Für sicherheitsrelevante Steuerungssysteme werden von der FuSi Maßnahmen zur Erhöhung der Gerätesicherheit gefordert. Abhängig von der ASIL-Klassifikation können neben Anforderungen wie Architektur, Coding und Tests auch funktionale Tests gefordert werden. In diesem zweiteiligen Beitrag soll ein Überblick über mögliche FuSi Maßnahmen in embedded Systemen gegeben werden. Im ersten Teil werden FuSi Maßnahmen zur Validierung des Programmspeichers (ROM), Erkennung fehlerhafter Arbeitsspeicher (RAM) und Integrität des Kellerspeichers (Stack) erörtert.
ROM Test:
Grundsätzlich kann zwischen zwei Vorgehensweisen beim ROM Test unterschieden werden:
Beiden Prüfmethoden ist gemein, dass eine Checksumme berechnet und mit einer zuvor bestimmten Referenzchecksumme verglichen wird. Am Ende der Checksummenberechnung wird je nach Ergebnis eine Reaktion (fail/pass) veranlasst. Beide Methoden können auch miteinander kombiniert werden.
Bei der Prüfung zum Systemstart muss man die dafür benötigte Verarbeitungszeit berücksichtigen. Das kann z. B. kritisch für Steuerungen sein, die innerhalb kürzester Zeit auf Signale reagieren müssen.
Die Prüfung zur Laufzeit fällt dagegen erfahrungsgemäß kaum ins Gewicht, weil die Checksumme zyklisch in kleinen Tranchen über den Programmspeicher gerechnet wird. Am Ende einer kompletten Checksummenberechnung beginnt der ROM Test von vorn. Wie oft und in welcher Größe die Tranchen berechnet werden ist abhängig von den FuSi Anforderungen.
Die Art der Checksumme ergibt sich aus den FuSi Anforderungen. Das kann eine CRC32, CRC16 oder CRC8 mit einem entsprechenden Polynom sein. Eine einfache Aufsummierung des Speicherinhalts ist im allgemeinen nicht ausreichend. Die Referenzchecksumme wird beim Build-Prozess über das Firmware Image gerechnet und an eine für das Projekt definierte Stelle in das Firmware Image geschrieben. Neben der Referenzchecksumme werden weitere Informationen wie z. B. Startadresse, Endadresse und Versionsinformationen im Firmware Image abgelegt. Diese Informationen können von anderen Tools zur Plausibilisierung des Images, z. B. beim Firmware Update Prozess, verwendet werden.
RAM Test:
Auch beim RAM Test kann man bei der Vorgehensweise unterscheiden zwischen:
Hinzu kommt die Unterscheidung zwischen:
Die Theorie des RAM Tests ist ein weites Feld. Es gibt viele unterschiedliche Ansätze, um diverse Fehlermöglichkeiten im RAM zu erkennen. Eine nähere Betrachtung sprengt den Rahmen dieser kleinen Ausführung.
Die zu verwendende Art des RAM Tests ist abhängig von den Anforderungen und den Rahmenbedingungen.
Ein Beispiel für einen destruktiven RAM Test ist der March Test, wie er in der AN11208 von NXP im Rahmen der IEC60335 Class B realisiert ist. Dieser Test kann direct coupling, stuck-at faults und retention faults erkennen. Er wird beim Systemstart durchgeführt und kann, je nach Größe des Speichers, auch schon mal etwas längere Zeit in Anspruch nehmen.
Zur Laufzeit empfiehlt sich ein kurzer, nicht destruktiver Checker Board Test. Der Test wird zyklisch ausgeführt und prüft jeweils eine kleine Tranche des Speichers. Dabei wird der zu prüfende Bereich nacheinander mit 0xAA bzw. 0x55 beschrieben und jeweils auf den korrekten Inhalt geprüft. Zuvor wird der ursprüngliche Inhalt zwischengespeichert und nach erfolgtem Test wieder hergestellt. Um Kollisionen mit asynchronen Ereignissen zu vermeiden, werden während des Tests alle Interrupts gesperrt. Wie oft und in welcher Größe die Tranchen berechnet werden ist abhängig von den FuSi Anforderungen.
Der Prüfalgorithmus wird in Assembler implementiert, damit er unabhängig von Compilereinstellungen oder neueren Compilerversionen immer identisch ist. Änderungen am Programmablauf durch z. B. Optimierungen des Compilers wären fatal. Die korrekte Funktion müsste nach jeder Änderung erneut nachgewiesen werden.
Bei der Assembler Implementierung ist darauf zu achten, dass alle temporären Variablen in Registern gehalten und nicht in Stackvariablen abgelegt werden. Das würde sonst zu Kollisionen beim RAM Test im Stackbereich führen.
Je nach Ergebnis wird sofort nach Beendigung der laufenden Prüfung eine Reaktion (fail/pass) veranlasst.
Stack Check
Ein zyklischer Stack Check erscheint eher esoterisch, kann aber durch die FuSi gefordert werden. Esoterisch deshalb, weil eine zyklische Prüfung gerne den Hintergrundaktivitäten des Betriebssystems zugeordnet wird, aber eine Verletzung des Stacks eher durch Interrupts zu erwarten ist, wenn der Stack zu gering bemessen ist.
Mögliche Maßnahmen sind:
Diese Maßnahmen sind in einem einfachen System mit ein bis zwei Stacks relativ leicht umzusetzen. Schwieriger wird es, wenn ein Betriebssystem verwendet wird, das viele kleine Stacks für Tasks und Interrupts verwendet. In einem solchen System sollten aber entsprechende Maßnahmen schon vorbereitet sein.
Für die Überprüfung von Prüfmustern wird jeweils ein Bereich vor und nach dem Stackbereich reserviert und mit einem Prüfmuster gefüllt. Das Prüfmuster wird zyklisch auf Veränderung geprüft. Sollte der Stackpointer aus dem vorgesehenen Bereich hinauslaufen, so wird das Prüfmuster überschrieben. So die Theorie. Das funktioniert aber nur so lange, wie der Stackpointer Schritte nur innerhalb des überprüften Bereichs macht.
Die Prüfung des Stackpointers wird zyklisch durchgeführt. Die benötigten Adressen können aus den Adressen der Prüfmusterbereiche hergeleitet werden. Verletzungen der Stackgrenzen durch Interrupt Service Routinen können auf diese Art nicht erfasst werden (s. o.).
Um die Auslastung des Stacks während der Entwicklungsphase zu ermitteln, kann man den Stackbereich bei der Initialisierung mit einem einheitlichen Muster füllen und bei Bedarf mittels Debugger inspizieren.
Je nach Ergebnis wird sofort nach Beendigung der laufenden Prüfung eine Reaktion (fail/pass) veranlasst.
Wie oft die Stack Check Prüfungen durchgeführt werden und wie groß das Prüfmuster sein soll ist abhängig von den FuSi Anforderungen.