Forum: Compiler & IDEs Softwareversion im Flash hinterlegen.


von Thorsten M. (mightyshite)


Lesenswert?

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.

von Bauform B. (bauformb)


Angehängte Dateien:

Lesenswert?

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...

von Nop (Gast)


Lesenswert?

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).

von Andreas B. (bitverdreher)


Lesenswert?

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.

von Purzel H. (hacky)


Lesenswert?

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
von Thorsten M. (mightyshite)


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

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

von Bauform B. (bauformb)


Lesenswert?

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");

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

von Thorsten M. (mightyshite)


Lesenswert?

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
Noch kein Account? Hier anmelden.