Hallo zusammen, Problem: Ich möchte in meiner Software innerhalb der ersten paar Bytes über einen 2-Byte-Code die Versionsnummer der Software hinterlegen. Also, zum Beispiel, Byte 10 und 11 (oder ähnlich) ergeben zusammen die Version. Der Hintergrund ist der, dass ich meinen Bootloader entscheiden lassen möchte, ob er die neue Software aufspielen darf oder nicht. Umgebung: Ich programmiere in C (GNU C-Compiler) in Atmel Studio 7 auf einem Atmega1284p-au. Für Eure Unterstützung bedanke ich mich schon einmal herzlich.
Im Linker-Script kannst du für jede Quelldatei oder sogar für jede struct genau festlegen, auf welche Adresse die gelinkt werden soll. Für deine 2 Bytes scheint eine eigene struct etwas übertrieben, aber (nur?) so kannst du die genaue Reihenfolge der Bytes im Flash festlegen. Außerdem könnten es leicht mehr als 2 Bytes werden, z.B. eine Prüfsumme; Appetit kommt beim Essen ;) Ein Bootloader auf dem gleichen Chip könnte noch den gleichen Header mit "typedef struct" benutzen. Wenn man die hex-Datei mit einem PC-Programm verarbeiten möchte, ist das nicht mehr so einfach, deshalb habe ich gerne wirklich fixe Adressen. Thorsten M. schrieb: > Der Hintergrund ist der, dass ich meinen Bootloader entscheiden > lassen möchte, ob er die neue Software aufspielen darf oder nicht. Dazu sollte er eigentlich etwas mehr wissen, als nur die Firmware-Version. Ich hatte mal versucht, möglichst viel zu berücksichtigen und das in eine "Hardware-Signatur" zu packen. Zusätzlich zur Firmware-Version würde ich sowas in den Bootloader und die Anwendung einbauen und dann vergleichen
1 | typedef struct hw_compat_struct { |
2 | uint32_t rev : 5; // PCB-Revision, MBZ, außer im Bootloader (0, 'A'&31) |
3 | uint32_t dash : 7; // Sachnummer (-01) |
4 | uint32_t pcb : 20; // Sachnummer (100001 bis 999999) |
5 | uint32_t part; // Reference Designator; Bitmap |
6 | uint64_t vid; // Bestückungs-Variante und -Revision und ECO; Bitmap |
7 | } hw_compat_struct; |
Ein paar wirre Gedanken dazu findet man im Anhang...
Thorsten M. schrieb: > Problem: Ich möchte in meiner Software innerhalb der ersten paar Bytes > über einen 2-Byte-Code die Versionsnummer der Software hinterlegen. Sollte da nicht normalwerwise eine Interrupttabelle liegen? Eventuell direkt danach, denn deren Größe steht ja fest. Da könntest Du Dir ein geeignetes struct oder auch einen String definieren und über Dein Linkerscript in eine Section direkt nach der Interrupttabelle hinlegen. > Der Hintergrund ist der, dass ich meinen Bootloader entscheiden > lassen möchte, ob er die neue Software aufspielen darf oder nicht. Aber bitte nicht so, daß kein Downgrade mehr geht. Es gibt kaum etwas Blöderes, als wenn eine neue Software wegen neuer Bugs nicht geht, aber die Firmware auch kein Downgrade auf die Vorversion zuläßt (hallo, Asus).
Hier: Beitrag "MMC/SD Bootloader füt ATMega16" wurde so etwas schon gemacht. Nop schrieb: > Aber bitte nicht so, daß kein Downgrade mehr geht. Es gibt kaum etwas > Blöderes, als wenn eine neue Software wegen neuer Bugs nicht geht, aber > die Firmware auch kein Downgrade auf die Vorversion zuläßt (hallo, > Asus). Wenn man nur die Versionsnr. (wie im Link) kontrolliert, hindert einen niemand daran, dem Bootloader mit einer höheren Version wieder die alte SW unterzujubeln. Wenn einem das nicht gefällt, flasht man den Bootloader eben neu.
Ein downgrade generell zu verhindern ist eine maximal dumme Entscheidung. Da gibt's den Fall eines Devices, welches nach vielen Jahren ausfaellt. Die Umgebung wurde darauf angepasst und kann nicht mehr veraendert werden. Daselbe Device ist nicht mehr erhaeltlich, sondern nur das Identische mit neuer Firmware. und etwas anderer Funktionalitaet. Mit downgrade Moeglichkeit wurde das Neue passen, sonst eben nicht. Nicht, dass der Kunde das koennen muss, sondern der Produktmanager. Der Kunde bezahlt sicher einen geringen Aufpreis fuer eine alte Version, aber nicht, dass sich jemand Stunden lange damit rumschlagen muss. Und wenn der Kunde ein alte Version will, sollte man die liefern koennen und wollen.
:
Bearbeitet durch User
Vielen Dank schon einmal an Alle. Ein Downgrade verhindern möchte ich nicht, aber man kann den Kunden ja warnen und fragen, ob er das wirklich machen möchte. Bauform hat ja auch noch mehrere andere gute Gedanken genannt, was mit dieser Technik möglich ist. Was mir jetzt noch fehlt, ist die Beantwortung meiner Frage. Bauform schreibt, dass das mit einem Linkerscript möglich ist, aber ich weiß leider nicht wie? Die von allen vorgeschlagenen Links habe ich gelesen, aber keine Antwort gefunden.
Thorsten M. schrieb: > Was mir jetzt noch fehlt, ist die Beantwortung meiner Frage. Andreas B. schrieb: > Hier: > Beitrag "MMC/SD Bootloader füt ATMega16" > wurde so etwas schon gemacht. Oliver
Thorsten M. schrieb: > Die von allen vorgeschlagenen Links habe ich gelesen, > aber keine Antwort gefunden. Meinten Sie: habe zu viele Antworten gefunden? Und fast alle sind zu 99% richtig, auch wenn das linker script überall total unterschiedlich aussieht. Es muss nämlich nicht nur zum Linker passen, sondern zu Compiler, Startup, ggf. libc, Sprache und evt. sogar zur IDE. Der Linker bestimmt nur die Syntax und bietet sehr viele Möglichkeiten, die man (für uC) meist nicht braucht. Es kann sein, dass in deinem vorhandenen Script die Hälfte überflüssig ist, aber was genau? Du musst also dein vorhandenes Script behutsam erweitern und kannst nichts aus dem Internet einfach 1:1 übernehmen. Egal, hier ist eins von mir für ein Programm namens gps. Es ist etwas gekürzt und für einen STM32 (AVR kann ich nicht), aber im Prinzip funktioniert es so.
1 | /* gps.ld linker script */ |
2 | |
3 | MEMORY |
4 | { |
5 | /* STM32L412KBT (32 + 8K RAM, 128K Flash) */ |
6 | hw_flash (rx) : ORIGIN = 0x08000000, LENGTH = 128K |
7 | hw_sram1 (rx) : ORIGIN = 0x20000000, LENGTH = 32K |
8 | hw_sram2 (rw) : ORIGIN = 0x20008000, LENGTH = 8K |
9 | |
10 | flash_syslib (rx) : ORIGIN = 0x08000000, LENGTH = 31K |
11 | flash_gps (rx) : ORIGIN = 0x08008000, LENGTH = 15K |
12 | flash_tzdata (rx) : ORIGIN = 0x0801f000, LENGTH = 4K |
13 | |
14 | st_reserved (r) : ORIGIN = 0x20000000, LENGTH = 12544 |
15 | ram_syslib (rw) : ORIGIN = 0x20008800, LENGTH = 2K |
16 | ram_gps (rw) : ORIGIN = 0x20009800, LENGTH = 1K |
17 | } |
18 | |
19 | SECTIONS |
20 | { |
21 | /DISCARD/ : { |
22 | *($hardware_hdr) |
23 | } |
24 | |
25 | $image_header : { |
26 | *($image_hdr) |
27 | *($vectortable) |
28 | } > flash_gps |
29 | |
30 | .text : ALIGN(8) SUBALIGN(8) { |
31 | FILL (0xff) |
32 | *(.text) |
33 | *(.text.*) |
34 | *(.rodata) |
35 | *(.rodata.*) |
36 | *(.gcc*) |
37 | . = ALIGN(4); |
38 | text_end = .; |
39 | } > flash_gps |
40 | |
41 | $stack : { |
42 | stack = .; |
43 | . += 512; |
44 | tos = .; |
45 | } > ram_gps |
46 | |
47 | .data : ALIGN(4) SUBALIGN(4) { |
48 | data_start = .; |
49 | *(.data) |
50 | *(.data*) |
51 | data_end = .; |
52 | } > ram_gps AT > flash_gps |
53 | |
54 | .bss : ALIGN(4) SUBALIGN(4) { |
55 | bss_start = .; |
56 | *(COMMON) |
57 | *(.bss) |
58 | *(.bss*) |
59 | bss_end = .; |
60 | } > ram_gps |
61 | } |
Interessant sind eigentlich nur die Zeilen nach $image_header. Die section $image_hdr stammt aus dieser Quelle:
1 | // image-hdr.c
|
2 | |
3 | #include <stddef.h> |
4 | #include "image-hdr.h" |
5 | |
6 | extern crt1_type crt1; |
7 | extern uint32_t stack, tos; |
8 | |
9 | const image_struct __attribute__ ((section ("$image_hdr"))) |
10 | image_hdr = { |
11 | .top_of_stack = &tos, |
12 | .crt1 = crt1, |
13 | |
14 | .info_version = HDR_VERSION, |
15 | .image_type = IT_PROGRAM, |
16 | .dev_id = 0x464, |
17 | .crc = HDR_MAGIC, |
18 | .version = 2, |
19 | .revision = 11 |
20 | };
|
1 | // image-hdr.h
|
2 | |
3 | #pragma once
|
4 | #include <stddef.h> |
5 | #include <stdint.h> |
6 | |
7 | static const uint32_t HDR_MAGIC = 0xDEC11780; |
8 | |
9 | typedef enum header_version_enum { // die Version der image_struct |
10 | HDR_2014 = 'B', |
11 | HDR_NICE_TRY = 'C', |
12 | HDR_2020 = 'D', |
13 | HDR_VERSION = 'E' |
14 | } header_version_enum; |
15 | |
16 | typedef enum __attribute__ ((packed)) image_type_enum { |
17 | IT_UNDEFINED, |
18 | IT_BOOTLOADER, |
19 | IT_SYSLIB, |
20 | IT_PROGRAM, |
21 | IT_DATA
|
22 | } image_type_enum; |
23 | |
24 | typedef void (crt0_type)(void); // startup routine, bootloader |
25 | typedef int (crt1_type)(int); // application |
26 | |
27 | // 'info_version' sollte zwecks Kompatibilitaet nicht verschoben werden und
|
28 | // die ersten 4 Worte erst recht nicht.
|
29 | // Ein paar Felder werden nicht im Quelltext (image-hdr.c) initialisiert,
|
30 | // sondern vom ELF zu S19 Konverter; die sind mit 'S' markiert.
|
31 | // Der sollte meckern, falls sie schon initialisiert sind und muss dafür
|
32 | // sorgen, dass Strings mit '\0' terminiert sind.
|
33 | |
34 | typedef struct image_struct { |
35 | uint32_t *top_of_stack; // 00 initial SP - Offset |
36 | union { |
37 | crt0_type *crt0; // 04 initial PC(system) |
38 | crt1_type *crt1; // 04 initial PC(program) |
39 | };
|
40 | crt0_type *nmi; // 08 |
41 | crt0_type *hard_fault; // 0c |
42 | char info_version; // 10 'E' |
43 | image_type_enum image_type; // 11 bootloader, program, shm,... |
44 | uint16_t dev_id; // 12 DBGMCU_IDCODE.DEV_ID |
45 | uint32_t crc; // 14 S |
46 | uint32_t source_date_epoch; // 18 S unix time |
47 | uint16_t version; // 1c S |
48 | uint16_t revision; // 1e S |
49 | char name[9 + 1]; // 20 S |
50 | } image_struct; |
51 | |
52 | _Static_assert (sizeof(image_struct) == 42, " Bad struct size"); |
Man kann bei avr-gcc auch eine Section mit
1 | -Wl,--section-start=... |
als Option mitgeben. Ein Beispiel findet man zum Beispiel hier: https://www.mikrocontroller.net/articles/Konzept_f%C3%BCr_einen_ATtiny-Bootloader_in_C#Speicherlayout_des_Bootloaders Ist zwar für ATTiny, aber sollte auch für ATmega gelten.
Ich möchte mich bei Allen bedanken. Jetzt muss ich zunächst einmal die Informationen verarbeiten und versuchen, daraus etwas für meine Software zu stricken. Es gibt da noch viele andere Baustellen. Fühlt Euch alle gedanklich von mir in einen Biergarten eingeladen, mehr geht im Moment leider nicht...
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.