GDB Remote Stub KlasseDiese Klasse ermöglicht ein entferntes Debuggen des Betriebssystems mit GDB auf echter Hardware (aber auch im Emulator), in dem sie einen Teil des GDB Remote Serial Protocols (RSP) implementiert, eigene Unterbrechungsbehandlungsroutinen für Traps installiert und über die serielle Schnittstelle kommuniziert.
Mehr ...
#include <debug/gdb/stub.h>
|
struct | state |
| Struktur mit dem Systemzustands nach Traps. Mehr ...
|
|
|
enum | Registers {
REG_EAX = 0,
REG_ECX = 1,
REG_EDX = 2,
REG_EBX = 3,
REG_ESP = 4,
REG_EBP = 5,
REG_ESI = 6,
REG_EDI = 7,
REG_PC = 8,
REG_PS = 9,
REG_CS = 10,
REG_SS = 11,
REG_DS = 12,
REG_ES = 13,
REG_FS = 14,
REG_GS = 15,
NUM_REGISTERS = 16
} |
| zur Verfügung stehende (gesicherte) Register
|
|
typedef unsigned int | address |
| Datentyp für Speicheradresse (mit Zeigerarithmetik)
|
|
typedef unsigned int | reg |
| Datentyp für Register.
|
|
|
void | handle (void) |
| Behandlung eines Traps. Mehr ...
|
|
int | writeString (const char *buf, size_t len) |
| Sende eine Zeichenkette über die serielle Schnittstelle. Mehr ...
|
|
int | readString (char *buf, size_t buf_len, size_t len) |
| Empfange eine Zeichenkette über die serielle Schnittstelle. Mehr ...
|
|
int | send_packet (const char *pkt, size_t pkt_len) |
| Sende ein Datenpaket. Mehr ...
|
|
int | recv_packet (char *pkt_buf, size_t pkt_buf_len, size_t *pkt_len) |
| Empfange ein Datenpaket. Mehr ...
|
|
int | checksum (const char *buf, size_t len) |
| Berechne die Prüfsumme. Mehr ...
|
|
int | recv_ack (void) |
| Empfange ein Bestätigung für ein Paket. Mehr ...
|
|
int | send_ok_packet () |
| Erstelle und sende ein OK Paket. Mehr ...
|
|
int | send_signal_packet (char *buf, size_t buf_len, char signal) |
| Erstelle und sende ein Signalpaket. Mehr ...
|
|
int | send_error_packet (char *buf, size_t buf_len, char error) |
| Erstelle und sende ein Fehlerpaket (E Errorcode) Mehr ...
|
|
int | mem_read (char *buf, size_t buf_len, address addr, size_t len, bool hex) |
| Lese den Speicherinhalt einer Adresse in den Puffer. Mehr ...
|
|
int | mem_write (const char *buf, size_t buf_len, address addr, size_t len, bool hex) |
| Schreibe den Pufferinhalt an eine Adresse. Mehr ...
|
|
void | sys_continue (void) |
| Setze das Programm am aktuellen Instruktionszeiger fort.
|
|
void | sys_step (void) |
| Springe zur nächsten Instruktion.
|
|
bool | install_handler (int vector) |
| Installiere Unterbrechungsbehandlung für Trap-Vektoren. Mehr ...
|
|
|
enum | comPort { COM1 = 0x3f8,
COM2 = 0x2f8,
COM3 = 0x3e8,
COM4 = 0x238
} |
| COM-Port. Mehr ...
|
|
enum | baudRate {
BAUD_300 = 384,
BAUD_600 = 192,
BAUD_1200 = 96,
BAUD_2400 = 48,
BAUD_4800 = 24,
BAUD_9600 = 12,
BAUD_19200 = 6,
BAUD_38400 = 3,
BAUD_57600 = 2,
BAUD_115200 = 1
} |
| Übertragungsgeschwindigkeit. Mehr ...
|
|
enum | dataBits { DATA_5BIT = 0,
DATA_6BIT = 1,
DATA_7BIT = 2,
DATA_8BIT = 3
} |
| Anzahl der Datenbits pro Zeichen.
|
|
enum | stopBits { STOP_1BIT = 0,
STOP_1_5BIT = 4,
STOP_2BIT = 4
} |
| Anzahl der Stopbits pro Zeichen.
|
|
enum | parity {
PARITY_NONE = 0,
PARITY_ODD = 8,
PARITY_EVEN = 24,
PARITY_MARK = 40,
PARITY_SPACE = 56
} |
| Paritätsbit.
|
|
GDB Remote Stub Klasse
Diese Klasse ermöglicht ein entferntes Debuggen des Betriebssystems mit GDB auf echter Hardware (aber auch im Emulator), in dem sie einen Teil des GDB Remote Serial Protocols (RSP) implementiert, eigene Unterbrechungsbehandlungsroutinen für Traps installiert und über die serielle Schnittstelle kommuniziert.
Für den Einsatz muss GDB mit der selben Binärdatei wie das Betriebssystem auf der Hardware gestartet werden – idealerweise im Quelltextverzeichnis, denn dann kann der Quelltext in die Ausgabe eingebettet werden. Zudem müssen die Einstellungen für die seriellen Übertragung sowohl auf der Hardware als auch im GDB identisch sein.
Beispiel:
~> ssh faui04a
faui04a:~> cd mpstubs
faui04a:~/mpstubs> make netboot
faui04a:~/mpstubs> gdb build/system
GNU gdb (Debian 7.12-6) 7.12.0.20161007-git
[...]
Reading symbols from /proj/i4bs/student/uj66ojab/kernel...done.
(gdb) set serial baud 9600
(gdb) target remote /dev/ttyBS1
Remote debugging using /dev/ttyBS1
main () at main.cc:87
- Zu beachten
- GDB liegt bereits eine i386-stub.c bei, welche jedoch hässlich, schlecht gewartet und nicht sonderlich gut in unser objektorientiertes Betriebssystem integrierbar ist. Deshalb verwenden wir eine überarbeitete Version von Matt Borgersons gdbstub (veröffentlicht 2016 unter der GPL v2 Lizenz).
◆ GDB_Stub()
Konstruktor.
Konfiguriert die serielle Schnittstelle (als 8N1
) sowie Unterbrechungsbehandlungen
- Zu beachten
- Damit wir uns beim Interruptbetrieb der seriellen Schnittstelle keine Gedanken machen brauchen, initialisieren wir Serial im IOAPIC wie gehabt, machen den GDB_Stub jedoch nicht zu einem Gate – sondern biegen die Interruptbehandlung einfach um: statt guardian durch
irq_entry_SERIALVEKTOR
nehmen wir den GDB_Stub::debug_handler durch die Routine dbg_irq_entry_SERIALVEKTOR
. Dazu müssen wir lediglich sicher stellen, dass NUM_HANDLERS
in der debug/gdb/handler.asm
ausreichend groß ist, damit auch eine Routine für den Vektor Plugbox::serial erstellt wird, und einen entsprechenden Aufruf an GDB_Stub::install_handler tätigen.
- Optional:
- Unterbrechungsbehandlung für Empfangsunterbrechung der seriellen Verbindung installieren
- Parameter
-
wait | Warte nach der Konfiguration auf eine GDB-Verbindung |
debugOutput | Debugge den GDB-Stub durch Bildschirmausgaben der Kommunikation (hilfreich bei Erweiterung des RSP) |
port | COM-Port für die serielle Verbindung |
baudrate | Baud Rate, Standard bei GDB ist 9600 (kann aber zu Engpässen führen) |
◆ checksum()
int GDB_Stub::checksum |
( |
const char * |
buf, |
|
|
size_t |
len |
|
) |
| |
|
protected |
Berechne die Prüfsumme.
- Parameter
-
buf | Zeiger auf den Puffer |
len | Größe des Puffers |
- Rückgabe
- 8-Bit Prüfsumme des Puffers
◆ handle()
void GDB_Stub::handle |
( |
void |
| ) |
|
|
protected |
Behandlung eines Traps.
Diese Funktion wird nach der Speicherung des aktuellen CPU Zustandes von der generischen Debug-Unterbrechungsbehandlungsroutine aufgerufen. Sie ist das Herzstück und übernimmt die Kommunikation mit dem GDB Host.
◆ install_handler()
bool GDB_Stub::install_handler |
( |
int |
vector | ) |
|
|
protected |
Installiere Unterbrechungsbehandlung für Trap-Vektoren.
Der Code für die spezifischen Debugger-Unterbrechungsbehandlungen der Traps ist in Assembler in der debug/gdb/handler.asm
verfasst. Diese ähneln den ebenfalls in Assembler verfassten Einsprungsfunktionen in der boot/startup.asm
für den guardian , sichern jedoch explizit den kompletten Zustand des Systems (inklusive callee save und Segment-Register) und springen eine eigene generische Debug-Unterbrechungsbehandlung an: den GDB_Stub::debug_handler
Da der GDB Stub dynamisch (zur Laufzeit) aktivierbar sein soll, wird vor dem Initialisieren der Klasse die zentrale Unterbrechungsbehandlungsroutine guardian für den Vektor verwendet. Erst durch Aufruf dieser Methode wird die spezifische Debug- Unterbrechungsbehandlung für den Vektor eingestellt.
Hierfür muss die Interrupt Deskriptortabelle (IDT) modifiziert werden: Beim Eintrag an der für den Vektor entsprechenden Stelle (Gate) wird die Adresse der Behandlungsfunktion geändert. Diese zeigt ursprünglich auf irq_entry_VEKTORNUMMER
(definiert in der boot/startup.asm
), wird nun jedoch auf die Adresse des debug/gdb/handler.asm
-Äquivalents dbg_irq_entry_VEKTORNUMMER
geändert.
- Parameter
-
vector | der zu ändernde Trap-Vektor |
- Rückgabewerte
-
true | bei erfolgreicher Änderung der Unterbrechungsbehandlungsroutine |
false | bei Fehler (z.B. ungültige Vektornummer) |
◆ mem_read()
int GDB_Stub::mem_read |
( |
char * |
buf, |
|
|
size_t |
buf_len, |
|
|
address |
addr, |
|
|
size_t |
len, |
|
|
bool |
hex |
|
) |
| |
|
protected |
Lese den Speicherinhalt einer Adresse in den Puffer.
- Parameter
-
buf | Zeiger auf den Puffer |
buf_len | Größe des puffers |
addr | Startadresse des zu lesenden Speichers |
len | Größe des zu lesenden Speichers |
hex | Speichere als Hexadezimal (true ) oder binär (false ) in den Puffer |
- Rückgabe
- Anzahl der gelsenen Bytes oder
-1
falls der Puffer zu klein
◆ mem_write()
int GDB_Stub::mem_write |
( |
const char * |
buf, |
|
|
size_t |
buf_len, |
|
|
address |
addr, |
|
|
size_t |
len, |
|
|
bool |
hex |
|
) |
| |
|
protected |
Schreibe den Pufferinhalt an eine Adresse.
- Parameter
-
buf | Zeiger auf den Puffer |
buf_len | Größe des puffers |
addr | Startadresse des zu schreibenden Speichers |
len | Größe des zu schreibenden Speichers |
hex | Interpretiere den Pufferinhalt als Hexadezimal (true ) oder binär (false ) |
- Rückgabe
- Anzahl der geschrieben Bytes oder
-1
falls der Puffer zu gross
◆ readString()
int GDB_Stub::readString |
( |
char * |
buf, |
|
|
size_t |
buf_len, |
|
|
size_t |
len |
|
) |
| |
|
protected |
Empfange eine Zeichenkette über die serielle Schnittstelle.
- Parameter
-
buf | Zeiger auf den Puffer |
buf_len | Größe des Puffers |
len | Anzahl der zu empfangenden Bytes |
- Rückgabewerte
-
0 | falls erfolgreich, |
-1 | falls kein/nicht alle Bytes empfangen werden konnten |
◆ recv_ack()
int GDB_Stub::recv_ack |
( |
void |
| ) |
|
|
protected |
Empfange ein Bestätigung für ein Paket.
- Zu beachten
- Durch die Tastenkombination
[ctrl]+[c]
im GDB kann das laufende Betriebssystem gestoppt werden. GDB sendet dabei einfach den Wert 3
(nicht ASCII, auch kein gültiges Paket). Unser Betriebssystem ist beim Empfang dieses Codes normalerweise in dieser Funktion. Wir können in diesem Fall einfach den Empfang des Werts ignorieren und auf das nächste Zeichen – die tatsächliche Bestätigung – warten.
- Optional:
- Methode so erweitern, dass ein Zeichen
0x3
ignoriert wird
- Rückgabewerte
-
0 | eine [positive] Bestätigung (ACK, + ) wurde empfangen |
1 | eine negative Bestätigung (NACK, - ) wurde empfangen |
-1 | andernfalls |
◆ recv_packet()
int GDB_Stub::recv_packet |
( |
char * |
pkt_buf, |
|
|
size_t |
pkt_buf_len, |
|
|
size_t * |
pkt_len |
|
) |
| |
|
protected |
Empfange ein Datenpaket.
Bedingt eine fehlerfreie Verbindung zur Übertragung von 7-Bit ANSI Zeichen
- Parameter
-
pkt_buf | Zeiger auf den Paketpuffer |
pkt_buf_len | Größe des Paketpuffers |
pkt_len | Größe des zu empfangenden Pakets |
- Rückgabewerte
-
0 | falls Paket erfolgreich empfangen wurde, |
-1 | andernfalls |
◆ send_error_packet()
int GDB_Stub::send_error_packet |
( |
char * |
buf, |
|
|
size_t |
buf_len, |
|
|
char |
error |
|
) |
| |
|
protected |
Erstelle und sende ein Fehlerpaket (E
Errorcode)
- Rückgabe
- Statuscode von send_packet
◆ send_ok_packet()
int GDB_Stub::send_ok_packet |
( |
| ) |
|
|
protected |
Erstelle und sende ein OK
Paket.
- Rückgabe
- Statuscode von send_packet
◆ send_packet()
int GDB_Stub::send_packet |
( |
const char * |
pkt, |
|
|
size_t |
pkt_len |
|
) |
| |
|
protected |
Sende ein Datenpaket.
Datenpakete haben die Form
- Parameter
-
pkt | Zeiger auf den Paketpuffer |
pkt_len | Größe des Paketpuffers |
- Rückgabewerte
-
0 | falls Paket erfolgreich gesendet und bestätigt wurde, |
1 | falls Paket zwar gesendet aber nicht bestätigt wurde, |
-1 | andernfalls |
◆ send_signal_packet()
int GDB_Stub::send_signal_packet |
( |
char * |
buf, |
|
|
size_t |
buf_len, |
|
|
char |
signal |
|
) |
| |
|
protected |
Erstelle und sende ein Signalpaket.
beinhaltet aktuellen Interruptvektor (S
Vektor)
- Rückgabe
- Statuscode von send_packet
◆ writeString()
int GDB_Stub::writeString |
( |
const char * |
buf, |
|
|
size_t |
len |
|
) |
| |
|
protected |
Sende eine Zeichenkette über die serielle Schnittstelle.
- Parameter
-
buf | Zeiger auf den Puffer |
len | Größe des Puffers |
- Rückgabewerte
-
0 | falls erfolgreich, |
-1 | falls kein/nicht alle Bytes gesendet werden konnten |
◆ debug_handler
Erlaube der generischen Unterbrechungsbehandlung Zugriff auf die geschützten Methoden dieser Klasse.
Debug-Unterbrechungsbehandlungsroutine des Systems, analog zu guardian . Aufruf dieser Funktion durch den Assembler Teil der Debug- Unterbrechungsbehandlung (dbg_irq_entry_*
in der debug/gbd/handler.asm
) – diese Routinen müssen zuvor mittels GDB_Stub::install_handler installiert werden. Nach dem vorbereiten der Daten wird GDB_Stub::handle aufgerufen, welches mit dem Host über die serielle Schnittstelle kommuniziert.
- Zu beachten
- Für korrekte Funktionalität im Multiprozessorfall wird eine korrekte Implementierung der Klasse Ticketlock vorausgesetzt!
- Optional:
- Da diese Funktion nun auch die serielle Empfangsunterbrechung behandelt, muss die abgeschlossene Abarbeitung der Unterbrechung entsprechend über den LAPIC signalisiert werden.
- Parameter
-
context | beinhaltet einen Pointer auf den eigenen Stack, über den auf den Debug-Unterbrechungskontext zugegriffen werden kann. |
◆ state
Speicher für den aktuelle CPU Zustand.
Wird am Beginn einer Unterbrechungsbehandlung aktualisiert und an deren Ende wieder zurückgeschrieben (mit ggf. modifizierten Werten)
Die Dokumentation für diese Klasse wurde erzeugt aufgrund der Dateien:
- debug/gdb/stub.h
- debug/gdb/handler.cc
- debug/gdb/init.cc
- debug/gdb/protocol.cc