Hallo zusammen, ich spiele gerade ein wenig zum Thema Bootloader und FW-Update rum. Fange gerade mit einem primitiven Bootloader an und habe mich an dem Beispiel von IAR orientiert: https://www.iar.com/support/tech-notes/general/creating-a-bootloader-for-cortex-m/ Wie sehen kann, steht ja im ersten DWORD die Speicheradresse des Stack und im zweiten DWORD dann die Sprungadresse zum Programm. Letzteres wird ja vom Linker gesetzt und hängt von der Speicherkonfiguration des Linkerfiles ab. Mein FW-Update-Konzept sieht eine Dreiteilung des internen Flashspeichers vor: Bootloader | Image 1 | Image 2 Die Speicherbereich Image 1 und Image 2 sollen abwechselnd mit der aktuellen FW beschrieben werden. Im EEPROM des Controllers stehen dann Sachen, welches Image gerade aktuell ist (später auch Prüfsummen und ähnliches). Das Problem ist jetzt eben, dass ich zu Compile-Zeit nicht weiß in welchem Speicherbereich (1 oder 2) das FW-Image dann kommt. Wie mache ich dass dann mit der Sprungadresse? Und vermutlich nicht nur die! - Sämtliche Funktionsadressen im FW-Image zeigen ja dann auf den falschen Speicherbereich, oder nicht!? Funktioniert das überhaupt so, wie ich mir das vorstelle? Wäre super, wenn ihr mir helfen könnt! Danke & Grüße Martin
Martin Lohmeyer schrieb: > Sämtliche Funktionsadressen im FW-Image > zeigen ja dann auf den falschen Speicherbereich, oder nicht!? So sehe ich das auch. Zur Compile-Zeit existiert die Adresse die im Linker-Script oder in den Configs hinterlegt ist. Du könntest deine FW für beide Configs erstellen und falls der Bootloader auch das flashen übernimmt, kann er entscheiden, welche er empfangen möchte und flasht diese an die richtige Stelle. Die Gültigkeit der Firmware müsstest du nicht einmal extra im EEPROM ablegen. Ich lösche die 4 Byte des RESET_VECTORS, falls beim Flashen der Firmware etwas nicht funktioniert hat. Somit erkenne ich ob die FW die dort liegt gültig ist. Sonst springt der Bootloader erst gar nicht an die Stelle.
1 | /*******************************************************************************
|
2 | * Prüft ob eine Firmware im Flash vorhanden ist.
|
3 | *
|
4 | * param void
|
5 | *
|
6 | * return bool true Firmware gefunden
|
7 | * false keine Firmware vorhanden
|
8 | */
|
9 | static bool fw_flash_bootable(void) |
10 | {
|
11 | uint32_t *p_flash = (uint32_t *)(BL_FW_FLASH_START + 4); |
12 | |
13 | /* Firmware gefunden */
|
14 | if((*p_flash != 0x00000000) && (*p_flash != 0xFFFFFFFF)) |
15 | {
|
16 | return true; |
17 | }
|
18 | /* keine Firmware */
|
19 | else
|
20 | {
|
21 | return false; |
22 | }
|
23 | }
|
Warum möchtest du überhaupt die FW 2x im Flash halten (alt / & neu)? Gruß
Adam P. schrieb: > So sehe ich das auch. Zur Compile-Zeit existiert die Adresse die im > Linker-Script oder in den Configs hinterlegt ist. Verdammt! Adam P. schrieb: > Warum möchtest du überhaupt die FW 2x im Flash halten (alt / & neu)? Weil das die für mich einfachste und schönste Lösung ist. Ansonsten müsste ich im Bootloader nach einem Update immer Image 2 an die Stelle von Image 1 kopieren und dann das neue Image booten. Direkt an die Stelle von Image 1 kann ich nicht schreiben, da die Daten über UART in einem etwas komplexen Protokoll rein kommen für dessen Abhandlung Timer, und die RTOS Laufzeitumgebung benötigt werden. Das kann man nicht in den RAM oder so auslagern, zumal der Platz da eh eng bemessen ist. Deshalb war die Idee alles gemütlich an die Speicherstelle 2 zu schreiben, zu prüfen und neu zu starten ...
Also soll die FW-Flash-Funktionalität in der FW selbst sein: FW 1 flasht FW 2 oder FW 2 flasht FW 1? Richtig verstanden? Und der Bootloader soll dann nur entscheiden welche er beim Restart anspringt? Aber dann müsste das doch auch gehen. - Beide FW erstellen - Aktive FW teilt mit welche Sie ist oder welche FW sie empfangen möchte - Übertragen & abspeichern - Falls CRC(Prüfung) + Flashen OK, dann löscht aktive FW ihren reset vector Eintrag im Flash und erzeugt ein Reset (oder halt eine Info im EEPROM) - Bootloader prüft an beiden Basis-Adr. welcher reset vector noch gültig ist oder halt den Eintrag im EEPROM und springt die FW an. Denke das müsste machbar sein.
:
Bearbeitet durch User
Adam P. schrieb: > Also soll die FW-Flash-Funktionalität in der FW selbst sein: > FW 1 flasht FW 2 > oder > FW 2 flasht FW 1? Richtig verstanden? Und der Bootloader soll dann nur > entscheiden welche er beim Restart anspringt? Richtig. Adam P. schrieb: > Aber dann müsste das doch auch gehen. > - Beide FW erstellen > - Aktive FW teilt mit welche Sie ist oder welche FW sie empfangen möchte > - Übertragen & abspeichern > - Falls CRC(Prüfung) + Flashen OK, dann löscht aktive FW ihren reset > vector Eintrag im Flash und erzeugt ein Reset (oder halt eine Info im > EEPROM) > - Bootloader prüft an beiden Basis-Adr. welcher reset vector noch gültig > ist und springt die FW an. > > Denke das müsste machbar sein. Eigentlich ja. Hat aber drei Nachteile: 1. Das Update-Image ist immer doppelt so groß, da ja beide Versionen enthalten sein müssen 2. Es müssen dementsprechend auch immer zwei Versionen des Projekts kompiliert werden. 3. Im Protokoll müsste diese "Abhandlung" hinterlegt werden ... Ein "Suchen und Ersetzen" während dem Flashen, bei dem die Adressen von einen in den anderen Adressbereich über einen Offset geändert werden, geht vermutlich nicht, oder? (Ist vermutlich eine bescheuerte Idee ... ! - Ich vermute ich muss mit den drei Nachteilen leben müssen!)
Martin Lohmeyer schrieb: > Funktioniert das überhaupt so, wie ich mir das vorstelle? Es gibt controller (z.b. STM32L4...) die haben 2 flash Bänke, die sich auf HW-Ebene umschalten lassen. Alternativ müsstest Du gucken, ob Du Deinen Compiler dazu bringen kannst, relocatable code zu erzeugen. Dann müsstest Du nur die interrupt vector table entsprechend anpassen. mfg Torsten
Martin Lohmeyer schrieb: > Direkt an die Stelle von Image 1 kann ich nicht schreiben, da die Daten > über UART in einem etwas komplexen Protokoll rein kommen für dessen > Abhandlung Timer, und die RTOS Laufzeitumgebung benötigt werden. Das > kann man nicht in den RAM oder so auslagern, zumal der Platz da eh eng > bemessen ist. Warum das nicht alles in den Bootloader mit reinpacken? Wenn Dein Bootloader dieses Protokoll beherrscht, dann kann er komplett unabhängig von der alten Firmware-Version agieren, was deutlich sicherer ist. Und das braucht immer noch weniger Platz, als ein komplettes 2. FW-Image zu speichern. Welchen mc benutzt Du? Viele Grüße, Stefan
Martin Lohmeyer schrieb: > Das Update-Image ist immer doppelt so groß, da ja beide Versionen > enthalten sein müssen Nein, sehe ich nicht so. Du hast in deinem Projekt 2 Configs und erstellst diese beiden, dann hast du zwei FW z.b: - Release_FW_1 - Release_FW_2 Und per Protokollabsprache wird dann das richtige übertragen.
Martin Lohmeyer schrieb: > Ein "Suchen und Ersetzen" während dem Flashen, bei dem die Adressen von > einen in den anderen Adressbereich über einen Offset geändert werden, > geht vermutlich nicht, oder? > (Ist vermutlich eine bescheuerte Idee ... ! - Ich vermute ich muss mit > den drei Nachteilen leben müssen!) Das ist nicht unüblich. Letztendlich ist bei jedem Programm, dass auf einem modernem OS läuft zur Linkzeit nicht klar, an welche Adresse es beim Start geladen wird: https://en.wikipedia.org/wiki/Relocation_(computing) Diese Tabelle müsstet Du idealerweise dann im RAM halten können, bzw. so über das Image verteilen, dass der Teil, den Du im RAM halten müsstest klein genug.
Stefan K. schrieb: > Warum das nicht alles in den Bootloader mit reinpacken? Wenn Dein > Bootloader dieses Protokoll beherrscht, dann kann er komplett unabhängig > von der alten Firmware-Version agieren, Weil das echt zuviel für ein Bootloader wird ... Grob überschlagen würde er auf 30k Flash anwachsen. Stefan K. schrieb: > Welchen mc benutzt Du? STM32L151RD
Martin Lohmeyer schrieb: > Weil das echt zuviel für ein Bootloader wird ... Grob überschlagen würde > er auf 30k Flash anwachsen. Der STM32L151RD hat doch 384kb Flash, wo ist das Problem? Bei einem 32kb Bootloader bleibt Dir 352kb für die Firmware. Bei einem Minimal-Bootloader, der 2* die Firmware vorhalten muss, bleibt Dir (384kb - 8kb) / 2 = 188kb für jedes Firmware-Image. Viele Grüße, Stefan
Wenn die Update Routine im booter ist, kann sie nicht so einfach ausgetauscht werden. Das ist ggf ein schwerwiegenden Nachteil. Weiter wird er dadurch komplexer und hat mehr Fehler. Ich bin auch generell dafür den booter so schlank wie möglich zu gestalten. Je weniger Code um so weniger Fehler. Und Software ist nie fehlerfrei. Bei Update Routinen im Image immer überprüfen ob das da Update aus beiden danach noch tut.
Mein Bootloader hat ca. 46K. Aber wie ABC schon schrieb, beide Seiten haben ihre Vor-& Nachteile. Sollte sich die Update Routine wirklich mal ändern, wäre ich dann wohl auch für die Variante von ABC. Hast halt bissel mehr Aufwand beim erzeugen der FW (2x Compilieren) aber dafür wäre es flexibel.
Stefan K. schrieb: > (384kb - 8kb) / 2 = 188kb für jedes Firmware-Image. Ein wenig muss man auch auf die Sectoren achten... Im aktuellen Stand ist die FW knapp 153k groß... ABC schrieb: > Ich bin auch generell dafür den booter so schlank wie möglich zu > gestalten. Je weniger Code um so weniger Fehler. > > Und Software ist nie fehlerfrei. Noch ein Punkt, was gegen die Sache mit "alles im Bootloader" spricht: Das Protokoll darf sich nie ändern! Liegt alles in der FW selbst, kann man das mit einem FW-Update eben beheben. Im Bootloader wird schwer ...
Hmmm ... Ganz anderer Vorschlag! - Gar kein Bootloader verwenden, sondern die zwei Bänke des Controllers verwenden: http://www.st.com/content/ccc/resource/technical/document/application_note/group0/ab/6a/0f/b7/1a/84/40/c3/DM00230416/files/DM00230416.pdf/jcr:content/translations/en.DM00230416.pdf Das heißt: 1. Aktuelle FW flasht neues Image in die andere Bank. 2. Aktuelle FW prüft die Prüfsumme 3. Falls alles passt wird das Boot bit (BFB2 gesetzt, oder gelöscht) Nachtrag: Habe eben erst gesehen, dass da noch zwei Antworten kamen, während ich geantwortet habe! Adam P. schrieb: > Nein, sehe ich nicht so. Du hast in deinem Projekt 2 Configs und > erstellst diese beiden, dann hast du zwei FW z.b: > - Release_FW_1 > - Release_FW_2 > > Und per Protokollabsprache wird dann das richtige übertragen. Ja, das meine ich ja. Aber Release_FW_1 und Release_FW_2 müssten dann halte beide beim Host sein, damit das richtige übertragen werden kann. Das meine ich mit doppelter Größe... - Also beim Host. Torsten R. schrieb: > Das ist nicht unüblich. Letztendlich ist bei jedem Programm, dass auf > einem modernem OS läuft zur Linkzeit nicht klar, an welche Adresse es > beim Start geladen wird: > > https://en.wikipedia.org/wiki/Relocation_(computing) > > Diese Tabelle müsstet Du idealerweise dann im RAM halten können, bzw. so > über das Image verteilen, dass der Teil, den Du im RAM halten müsstest > klein genug. Funktioniert dieses Reallocation auch bei einem embedded Controller (Cortex-M3)?!?
Martin Lohmeyer schrieb: > Funktioniert dieses Reallocation auch bei einem embedded Controller > (Cortex-M3)?!? Wenn Du eine tool chain hast, die Dir entsprechende binaries für deine Plattform generiert, wüste ich nicht, warum es nicht funktionieren sollte. Gff. müsstest Du das Format noch etwas anpassen.
Martin Lohmeyer schrieb: >> Warum das nicht alles in den Bootloader mit reinpacken? Wenn Dein >> Bootloader dieses Protokoll beherrscht, dann kann er komplett unabhängig >> von der alten Firmware-Version agieren, > > Weil das echt zuviel für ein Bootloader wird ... Grob überschlagen würde > er auf 30k Flash anwachsen. Dann macht man halt nur einen Flash-Kopierer als Bootloader. Die Firmware selbst packt ihr Update in den freien Flash Bereich (der groß genug sein muss) und ruft den Bootloader erst am Schluss zum Umkopieren der Daten auf, d.h. beim Update werden einfach die Programmdaten von einer höheren auf eine niedrigere Addresse kopiert. Damit es keine Probleme bei plötzlichen Stromausfällen gibt muss der Bootloader eine oder zwei Flash Pages für Status Infos benutzen. Dafür ist er selbst aber schön klein.
Naja ... Das der Linker entsprechend die Adressen anpassen kann ist ja klar. Deshalb auch zwei Images. Meine Idee war immer nur eine FW für Image 1 zu kompilieren. Wenn das Gerät dann erkennt, dass es das ganze in Flash 2 schreiben muss, sucht es im übertragenen Image nach den Sprung-Adressen und ersetzt sie durch die richtigen. Was aber auch bescheuert ist, da nämlich dann auf jeden Fall die Prüfsumme falsch ist ... Alles doof! :-(
Ich kenne das so: - Firmware lädt Update in einen separaten Flashbereich, z.B. obere Hälfte und checkt mit CRC, ob das Image OK ist. Wenn nicht, gibt's ne Fehlermeldung im Protokoll, und es wird abgebrochen. - Firmware setzt irgendwo im Flash ein "hallo, Update ist da"-Flag. - Firmware rebootet. - Bootloader guckt auf das Update-Flag, ob es gesetzt ist. - Bootloader prüft selber nochmal das Update-Image per CRC. - Bootloader kopiert das Update in den Firmware-Bereich. - Bootloader checkt CRC der Firmware - wenn OK, wird das "Update ist da"-Flag resettet, sonst nochmal kopiert. - Bootloader startet Firmware. Mit so einem Design kann auch zu jedem Zeitpunkt der Strom abgedreht werden. Wenn das beim Kopieren der Firmware im Bootloader passiert, wiederholt er die Nummer beim nächsten Start eben. Man kann auch z.B. noch das runtergeladene Update komprimieren und auch so komprimiert speichern. Dann braucht man nicht das Doppelte an Flash. Dafür wird der Bootloader aufwendiger, weil er dekomprimieren können muß. Außerdem kann der Bootloader auch ohne gesetztes Update-Flag die eigentliche Firmware vor dem Starten per CRC checken, und wenn das fehlschlägt, nachsehen, ob das letzte Update vielleicht noch verfügbar ist zum Drüberkopieren. Zur Sicherheit sollte außerdem im Bootloader noch ein rudimentäres Protokoll sein, mit dem man ein Hexfile in den Updatebereich laden kann. Das ist, wenn alles schiefgeht, als Notnagel. Also wenn die Firmware kaputt ist und auch kein letztes Update rumliegt. Das sollte nur auf einem UART mit geringer Baudrate sein. Hauptsächliche Einschränkung, weswegen das nicht für das reguläre Update gedacht ist: remote Update geht so nicht.
Martin Lohmeyer schrieb: > sucht es im übertragenen Image nach den Sprung-Adressen und > ersetzt sie durch die richtigen. ...wie hast du dir das angedacht? Wenn in einem Update Funktionen verändert wurden bzw. neue hinzukamen, woher soll der Bootloader wissen was er wie zu ersetzen hat. Und die Funktionen sind doch nicht immer gleich groß oder liegen immer an der selben stelle, was mit einem Offset zu lösen wäre - aber bezweifel, dass es dafür eine Lösung gibt. Lasse mich jedoch gern eines besseren belehren.
Nop schrieb: > Ich kenne das so: > > - Firmware lädt Update in einen separaten Flashbereich, z.B. obere > Hälfte und checkt mit CRC, ob das Image OK ist. Wenn nicht, gibt's ne > Fehlermeldung im Protokoll, und es wird abgebrochen. > - Firmware setzt irgendwo im Flash ein "hallo, Update ist da"-Flag. > - Firmware rebootet. > - Bootloader guckt auf das Update-Flag, ob es gesetzt ist. > - Bootloader prüft selber nochmal das Update-Image per CRC. > - Bootloader kopiert das Update in den Firmware-Bereich. > - Bootloader checkt CRC der Firmware - wenn OK, wird das "Update ist > da"-Flag resettet, sonst nochmal kopiert. > - Bootloader startet Firmware. Entspricht so in etwa dem bestehenden Speicherkonzept. Nur das der Bootloader immer Image 1 lädt und bei einem update zuvor die aktuelle FW von Image 2 Speicherbereich in den Image 1 Speicher bereich kopiert. D.h. ein Backup wäre nicht da, außer man hat genug Speicher für beide 8ggf. komprimierte) Images. Sind Sie komprimiert darf aber beim Entpacken (und Schreiben in das Flash) nichts schief laufen ... Adam P. schrieb: > ...wie hast du dir das angedacht? > Wenn in einem Update Funktionen verändert wurden bzw. neue hinzukamen, > woher soll der Bootloader wissen was er wie zu ersetzen hat. > > Und die Funktionen sind doch nicht immer gleich groß oder liegen immer > an der selben stelle, was mit einem Offset zu lösen wäre - aber > bezweifel, dass es dafür eine Lösung gibt. > Lasse mich jedoch gern eines besseren belehren. Ja ... war ja nur ein Gedanke, wenn quasi ein Jump immer im Hex-Datenstrom durch einen eindeutigen Identifier und einer nachfolgenden Adresse mit 0x080????? beginnend steht ...
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0493h/CHDECFHF.html http://www.keil.com/support/man/docs/armclang_link/armclang_link_pge1362075483299.htm mfg
Martin Lohmeyer schrieb: > D.h. ein Backup wäre nicht da, außer man hat genug Speicher für beide > 8ggf. komprimierte) Images. Ich würde mir von Kompression nicht al zu viel versprechen. Versuch mal bin-files zu packen. Zumindest der ARM thumb code enthält so wenig Redundanz, dass der sich nur schlecht kompremieren läßt. > Ja ... war ja nur ein Gedanke, wenn quasi ein Jump immer im > Hex-Datenstrom durch einen eindeutigen Identifier und einer > nachfolgenden Adresse mit 0x080????? beginnend steht ... Nein, Du must Dir , die Informationen, an welcher Stelle Korrekturen vorzunehmen sind, von der tool chain geben lassen. Es sind ja nicht nur Sprünge, die korregiert werden müssen (auch Adressen auf Konstanten, Sprungtabellen, virtual function tables, etc.)
Hallo, Nop schrieb: > Ich kenne das so: > > - Firmware lädt Update in einen separaten Flashbereich, z.B. obere > Hälfte und checkt mit CRC, ob das Image OK ist. Wenn nicht, gibt's ne > Fehlermeldung im Protokoll, und es wird abgebrochen. > - Firmware setzt irgendwo im Flash ein "hallo, Update ist da"-Flag. Unnötig > - Firmware rebootet. > - Bootloader guckt auf das Update-Flag, ob es gesetzt ist. > - Bootloader prüft selber nochmal das Update-Image per CRC. nochmal?? > - Bootloader kopiert das Update in den Firmware-Bereich. > - Bootloader checkt CRC der Firmware - wenn OK, wird das "Update ist > da"-Flag resettet, sonst nochmal kopiert. > - Bootloader startet Firmware. Also ich habe so etwas mit einem Bekannten mal implementiert. Ganz Grob -3 Teile 1. Bootloader (8k) 2. Low Flash (Flash-8k)/2 3. High Flash (Rest) Der Bootloader berechnet den CRC von Low und High Dann wird der CRC Wert mit dem CRC abgespeicherten CRC verglichen Anhand der 4 Werte kann man nun entscheiden, was gemacht werden soll. Beide CRC gleich und Valid das Programm wird gestartet High Flash Valid und ungleich Low CRC High Flash wird runter kopiert. Nun gibt es noch das Problem, dass man nicht weiß. wie groß das Bin File ist, darum haben wir die ersten 2 Words ans ende von den Bin File gepackt (Reset Vektor und Vektor Tabelle) um einmal den CRC Wert zu speichern und danach die Länge vom Binary file. Der Bootloader schaut dann beim starten ans Ende und läd die 2 words in die passenden Register Dazu kommt noch eine AES CBC Verschlüsselung über das gesamte Binary File (bis auf die ersten 2 Words) welche vom Bootloader beim Flashen rückgängig gemacht wird. Laufen tut das auf einem STM32F1 AES in Assambler und in ca. 2ms wird eine Page Entschlüsselt und geflasht. Der gesamte Bootloader benötigt etwa 6k bis 7k. Jens
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.