Commit 0d0253e1 authored by Bernhard Heinloth's avatar Bernhard Heinloth
Browse files

Vorgabe oostubs Aufgabe 2

parent 22d5e317
;
; Copyright (C) 2016 Matt Borgerson
; Copyright (C) 2018 Bernhard Heinloth
;
; This program is free software; you can redistribute it and/or modify
; it under the terms of the GNU General Public License as published by
; the Free Software Foundation; either version 2 of the License, or
; (at your option) any later version.
;
; This program is distributed in the hope that it will be useful,
; but WITHOUT ANY WARRANTY; without even the implied warranty of
; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
; GNU General Public License for more details.
;
; You should have received a copy of the GNU General Public License along
; with this program; if not, write to the Free Software Foundation, Inc.,
; 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
;
bits 32
%define NUM_HANDLERS 32
section .data
global dbg_irq_entries
; Erstelle eine Tabelle (Array) mit den Adressen der spezifischen Unterbrechungsbehandlungen
dbg_irq_entries:
%macro dbg_irq_addr 1
dd dbg_irq_entry_%1
%endmacro
%assign i 0
%rep NUM_HANDLERS
dbg_irq_addr i
%assign i i+1
%endrep
section .text
; Die generische Unterbrechungsbehandlung ist (analog zum guardian) eine C Funktion
extern debug_handler
; Debug-Unterbrechungsbehandlungen
; Analog zu den normalen Unterbrechungsbehandlungen in der startup.asm,
; jedoch müssen hier mehr (alle) Register gesichert werden.
;; DBGIRQ <irq-num> <error-code?>
%macro DBGIRQ 2
align 8
dbg_irq_entry_%1:
; CPU sichert
; - EFLAGS
; - CS
; - EIP
; - ERROR CODE, falls vorhanden (bei Vektor 8, 10-14 und 17)
%if %2 == 0
; Falls es keinen ERROR CODE gibt, setzen wir einen Platzhalter
; (damit der Stack identisch aufgebaut ist)
push 0
%endif
; - VECTOR (Interrupt-Nummer)
push %1
pushad
; sichert alle General-Purpose Register:
; - EAX
; - ECX
; - EDX
; - EBX
; - ESP
; - EBP
; - ESI
; - EDI
push ds
push es
push fs
push gs
push ss
; damit haben wir auch die Segment-Register:
; - DS
; - ES
; - FS
; - GS
; - SS
mov ebp, esp
push ebp
; statt guardian rufen wir unseren debug-Interrupthandler auf
call debug_handler
; CPU Kontext vom Stack nehmen
mov esp, ebp
pop ss
pop gs
pop fs
pop es
pop ds
popad
add esp, 8 ; wegen ERROR CODE und VECTOR
iret
%endmacro
; Macro für jeden Trap aufrufen (kann aber auch händisch gemacht werden, wie in der startup.asm)
%assign i 0
%rep NUM_HANDLERS
%if (i == 8) || ((i >= 10) && (i <= 14)) || (i == 17)
; mit ERROR CODE
DBGIRQ i, 1
%else
; ohne ERROR CODE
DBGIRQ i, 0
%endif
%assign i i+1
%endrep
/*
* Copyright (C) 2016 Matt Borgerson
* Copyright (C) 2018 Bernhard Heinloth
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "stub.h"
#include "debug/output.h"
#include "debug/assert.h"
/*! \brief GDB Instanz
*
* für Quasi-Singleton
*/
static GDB_Stub * instance = 0;
/*** Hilfsfunktion ***/
/*!\brief Setze einen Speicherbereich auf einen Wert
*
* \param ptr Zeiger zum Speicherbereich
* \param data Quelldaten mit roher Darstellung
* \param len Größe der Quelldaten
* \return Anzahl der in Puffer verwendeten Bytes oder \c -1 falls Puffer zu klein
*/
static void * memset(void *ptr, int data, size_t len){
char *p = (char*)ptr;
while (len--)
*p++ = (char)data;
return ptr;
}
/*** Debug-Unterbrechungsbehandlung (generischer Teil) ***/
extern "C" void debug_handler(struct debug_context *context){
// Benötigt eine aktive GDB Instanz
if (instance == 0)
return;
// Hole den internen Zustandsspeicher
struct GDB_Stub::state * state = &(instance->state);
// Lösche den vorherigen Inhalt
memset(&state->registers, 0, sizeof(state->registers));
// Setze den Interrupt Vektor
state->signum = context->vector;
// Übergebe den Zustand der Register
state->registers[GDB_Stub::REG_EAX] = context->eax;
state->registers[GDB_Stub::REG_ECX] = context->ecx;
state->registers[GDB_Stub::REG_EDX] = context->edx;
state->registers[GDB_Stub::REG_EBX] = context->ebx;
state->registers[GDB_Stub::REG_ESP] = context->esp;
state->registers[GDB_Stub::REG_EBP] = context->ebp;
state->registers[GDB_Stub::REG_ESI] = context->esi;
state->registers[GDB_Stub::REG_EDI] = context->edi;
state->registers[GDB_Stub::REG_PC] = context->eip;
state->registers[GDB_Stub::REG_CS] = context->cs;
state->registers[GDB_Stub::REG_PS] = context->eflags;
state->registers[GDB_Stub::REG_SS] = context->ss;
state->registers[GDB_Stub::REG_DS] = context->ds;
state->registers[GDB_Stub::REG_ES] = context->es;
state->registers[GDB_Stub::REG_FS] = context->fs;
state->registers[GDB_Stub::REG_GS] = context->gs;
// Verarbeite den aktuellen Zustand
instance->handle();
// Stelle die (ggf. geänderten) Register wieder her
context->eax = state->registers[GDB_Stub::REG_EAX];
context->ecx = state->registers[GDB_Stub::REG_ECX];
context->edx = state->registers[GDB_Stub::REG_EDX];
context->ebx = state->registers[GDB_Stub::REG_EBX];
context->esp = state->registers[GDB_Stub::REG_ESP];
context->ebp = state->registers[GDB_Stub::REG_EBP];
context->esi = state->registers[GDB_Stub::REG_ESI];
context->edi = state->registers[GDB_Stub::REG_EDI];
context->eip = state->registers[GDB_Stub::REG_PC];
context->cs = state->registers[GDB_Stub::REG_CS];
context->eflags = state->registers[GDB_Stub::REG_PS];
context->ss = state->registers[GDB_Stub::REG_SS];
context->ds = state->registers[GDB_Stub::REG_DS];
context->es = state->registers[GDB_Stub::REG_ES];
context->fs = state->registers[GDB_Stub::REG_FS];
context->gs = state->registers[GDB_Stub::REG_GS];
}
GDB_Stub::GDB_Stub(bool wait, bool debugOutput, Serial::comPort port, Serial::baudRate baudrate) : Serial(port, baudrate, DATA_8BIT, STOP_1BIT, PARITY_NONE), debug(debugOutput){
// Quasi-Singleton
assert(instance == 0);
// Instanzvariable setzen
instance = this;
// Debug-Unterbrechungsbehandlungen installieren
for (int i=0; i<17; i++)
if ( i != 2 // NMI
&& i != 15 // reserved
){
install_handler(i);
}
// Warte auf Verbindung
if (wait){
breakpoint();
}
}
// vim: set et ts=4 sw=4:
#include "stub.h"
/*!\brief Array mit Addressen der jeweiligen spezifischen Debug-Unterbrechungsbehandlungsroutinen
* aus der gdb_interrupt.asm ( dbg_irq_entry_%INDEX )
*/
extern void const * const dbg_irq_entries[];
bool GDB_Stub::install_handler(int vector){
//TODO: Implementieren. Falls du Lust hast. Ist wieder freiwillig.
// Braucht allerdings die Klasse Serial (welche in der vorherigen Aufgabe freiwillig war).
(void) vector;
return false;
}
/*
* Copyright (C) 2016 Matt Borgerson
* Copyright (C) 2018 Bernhard Heinloth
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "stub.h"
#include "debug/assert.h"
#include "debug/output.h"
// ASCII Zeichen für hexadezimale Darstellung
static const char digits[] = "0123456789abcdef";
/*** lokale Hilfsfunktionen fúr die Verarbeitung von Zeichen(ketten) ***/
/*!\brief Hexdarstellung (via ASCII) einer Zahl
*
* \param val Zahl
* \return Hexzeichen oder \c -1 falls ungültig
*/
static char get_digit(int val){
return (val >= 0) && (val <= 0xf) ? digits[val] : -1;
}
/*!\brief Umwandlung von diversen ASCII-Zahldarstellungen
*
* \param digit ASCII-Zeichen
* \param base Basis von 2 bis 16
* \return Zahl oder \c -1 falls ungültig
*/
static int get_val(char digit, int base) {
int value;
if ((digit >= '0') && (digit <= '9'))
value = digit-'0';
else if ((digit >= 'a') && (digit <= 'f'))
value = digit-'a'+0xa;
else if ((digit >= 'A') && (digit <= 'F'))
value = digit-'A'+0xa;
else
return -1;
return (value < base) ? value : -1;
}
/*!\brief Prüfe, ob c auf dem Bildschirm darstellbares ASCII-Zeichen ist
*
* \param ch zu prüfendes Zeichen
* \retval true druckbares Zeichen
* \retval false nicht-druckbares Zeichen
*/
static bool is_printable_char(char ch){
return (ch >= 0x20 && ch <= 0x7e);
}
/*!\brief Umwandlung einer Zeichenkette in eine Zahl
*
* \param str umzuwandelnde Zeichenkette. Sofern sie mit einem + oder - beginnt,
* wird das Vorzeichen der Zahl entsprechend gesetzt.
* \param len Länge der Zeichenkette
* \param base Basis für die Umwandlung. Bei Basis 0 wird automatisch zwischen
* hexadezimaler (Zeichenkette beginnt mit 0x oder 0X) oder
* dezimaler Darstellung entschieden.
* \param endptr Sofern nicht NULL, so zeigt es auf das erste Zeichen, das
* nicht zur Zahl gehört. Falls keine Zahl in der Zeichenkette
* erkannt wurde, wird der Inhalt auf NULL gesetzt
* \return erkannte Zahl
*/
static int strtol(const char *str, size_t len, int base, const char **endptr){
int value = 0;
size_t pos = 0;
int sign = 1;
int valid = 0;
if (endptr)
*endptr = NULL;
if (len < 1)
return 0;
if (str[pos] == '-') { // Negative Zahl
sign = -1;
pos += 1;
} else if (str[pos] == '+') { // Positive Zahl
sign = 1;
pos += 1;
}
// Hexadezimales Präfix
if ((pos + 2 < len) && (str[pos] == '0') &&
((str[pos+1] == 'x') || (str[pos+1] == 'X'))) {
base = 16;
pos += 2;
}
if (base == 0)
base = 10;
for (; (pos < len) && (str[pos] != '\x00'); pos++) {
int tmp = get_val(str[pos], base);
if (tmp == -1)
break;
value = value*base + tmp;
valid = 1; // Mindestens ein valides Zeichen
}
if (!valid)
return 0;
if (endptr)
*endptr = str+pos;
value *= sign;
return value;
}
/*** lokale Hilfsfunktionen zur Datenkodierung ***/
/*!\brief Darstellung als hexadezimale Werte
*
* \param buf Zielpuffer für kodierte Daten
* \param buf_len Größe des Puffers
* \param data Quelldaten mit roher Darstellung
* \param data_len Größe der Quelldaten
* \return Anzahl der in Puffer verwendeten Bytes oder \c -1 falls Puffer zu klein
*/
int enc_hex(char *buf, size_t buf_len, const char *data, size_t data_len){
// Puffer zu klein
if (buf_len < data_len*2)
return -1;
for (size_t pos = 0; pos < data_len; pos++) {
*buf++ = get_digit((data[pos] >> 4) & 0xf);
*buf++ = get_digit((data[pos] ) & 0xf);
}
return data_len*2;
}
/*!\brief Dekodiere Daten mit hexadezimale Darstellung
*
* \param buf Quellpuffer mit kodierten Daten
* \param buf_len Größe des Quellpuffers
* \param data Datenpuffer mit roher Darstellung
* \param data_len Größe des Datenpuffers
* \return Anzahl der in Datenpuffer verwendeten Bytes oder \c -1 falls Puffer zu klein
*/
static int dec_hex(const char *buf, size_t buf_len, char *data, size_t data_len){
// Puffer zu klein
if (buf_len != data_len*2)
return -1;
for (size_t pos = 0; pos < data_len; pos++) {
// Dekodiere höherwertiges Halbbyte
int tmp = get_val(*buf++, 16);
if (tmp == -1) {
// Puffer enthält ungültige Zeichen
assert(0);
return -1;
}
data[pos] = (tmp << 4);
// Dekodiere niederwertiges Halbbyte
tmp = get_val(*buf++, 16);
if (tmp == -1) {
// Puffer enthält ungültige Zeichen
assert(0);
return -1;
}
data[pos] |= tmp;
}
return 0;
}
/*!\brief Darstellung im Binärformat
*
* \param buf Zielpuffer für kodierte Daten
* \param buf_len Größe des Puffers
* \param data Quelldaten mit roher Darstellung
* \param data_len Größe der Quelldaten
* \return Anzahl der in Puffer verwendeten Bytes oder \c -1 falls Puffer zu klein
*/
static int enc_bin(char *buf, size_t buf_len, const char *data, size_t data_len){
size_t buf_pos = 0;
for (size_t data_pos = 0; data_pos < data_len; data_pos++) {
if (data[data_pos] == '$' || data[data_pos] == '#' || data[data_pos] == '}' || data[data_pos] == '*') {
if (buf_pos+1 >= buf_len) {
assert(0);
return -1;
}
buf[buf_pos++] = '}';
buf[buf_pos++] = data[data_pos] ^ 0x20;
} else {
if (buf_pos >= buf_len) {
assert(0);
return -1;
}
buf[buf_pos++] = data[data_pos];
}
}
return buf_pos;
}
/*!\brief Dekodiere Daten im Binärformat
*
* \param buf Quellpuffer mit kodierten Daten
* \param buf_len Größe des Quellpuffers
* \param data Datenpuffer mit roher Darstellung
* \param data_len Größe des Datenpuffers
* \return Anzahl der in Datenpuffer verwendeten Bytes oder \c -1 falls Puffer zu klein
*/
static int dec_bin(const char *buf, size_t buf_len, char *data, size_t data_len){
size_t data_pos = 0;
for (size_t buf_pos = 0; buf_pos < buf_len; buf_pos++) {
if (data_pos >= data_len) {
// Überlauf des Ausgabepuffer
assert(0);
return -1;
}
if (buf[buf_pos] == '}') {
// Nächstes Zeichen ist maskiert
if (buf_pos+1 >= buf_len) {
// Wider Erwarten folgt kein maskiertes Zeichen
assert(0);
return -1;
}
buf_pos += 1;
data[data_pos++] = buf[buf_pos] ^ 0x20;
} else {
data[data_pos++] = buf[buf_pos];
}
}
return data_pos;
}
/*** Paketmethoden ***/
int GDB_Stub::recv_ack(void){
// Warte auf Paketbestätigung
int response = read();
switch (response) {
case '+':
return 0; // positive Bestätigung
case '-':
return 1; // negative Bestätigung
default:
// Ungültige Antwort
DBG << "GDB: received bad packet response: " << hex << response << endl;
return -1;
}
}
int GDB_Stub::checksum(const char *buf, size_t len){
unsigned char csum = 0;
while (len--)
csum += *buf++;
return csum;
}
int GDB_Stub::send_packet(const char *pkt_data, size_t pkt_len){
// Sende Startsymbol
if (write('$') == -1)
return -1;
// lokale Debugausgabe
if (debug) {
DBG << "GDB: ->";
for (size_t p = 0; p < pkt_len; p++) {
if (is_printable_char(pkt_data[p]))
DBG << (char) pkt_data[p];
else
DBG << hex << (pkt_data[p] & 0xff);
}
DBG << endl;
}
// Sende Paketdaten
if (writeString(pkt_data, pkt_len) == -1)
return -1;
// Sende Prüfsumme
char buf[3];
buf[0] = '#';
char csum = checksum(pkt_data, pkt_len);
if ((enc_hex(buf+1, sizeof(buf)-1, &csum, 1) == -1) || (writeString(buf, sizeof(buf)) == -1)) {
return -1;
}
// Warte auf Bestätigung
return recv_ack();
}
int GDB_Stub::recv_packet(char *pkt_buf, size_t pkt_buf_len, size_t *pkt_len){
// Erkenne Paketbeginn
int data;
while ((data = read()) != '$');
// Lese bis zur Prüfsumme
*pkt_len = 0;
while (1) {
if ((data = read()) == -1) // Empfangsfehler
return -1;
else if (data == '#') // Paketende
break;
else {
if (*pkt_len >= pkt_buf_len) { // Prüfe auf Pufferüberlauf
DBG << "GDB: packet buffer overflow" << endl;
return -1;
}
// Speichere Zeichen und aktualisiere Vergleichsprüfsumme
pkt_buf[(*pkt_len)++] = (char) data;
}
}
// lokale Debugausgabe
if (debug){
DBG << "GDB: <- ";
for (size_t p = 0; p < *pkt_len; p++) {
if (is_printable_char(pkt_buf[p]))
DBG << (char)pkt_buf[p];
else
DBG << hex << (pkt_buf[p] & 0xff);
}
DBG << endl;;
}
// Empfange die Prüfsumme
char buf[2];
char actual_csum, expected_csum;
if ((readString(buf, sizeof(buf), 2) == -1) || (dec_hex(buf, 2, &expected_csum, 1) == -1))
return -1;
// Vergleiche Prüfsumme
else if ((actual_csum = checksum(pkt_buf, *pkt_len)) != expected_csum) {
// Warnung und negative Bestätigung senden
DBG << "GDB: received packet with bad checksum (" << (int)actual_csum << " instead of " << (int)expected_csum << ")" << endl;
write('-');
return -1;
} else {
// Sende positive Bestätigung
write('+');
return 0;
}
}
/*** Systemmethoden ***/
int GDB_Stub::mem_read(char *buf, size_t buf_len, address addr, size_t len, bool hex){
char data[64];
if (len > sizeof(data))
return -1;
// Lese aus dem Systemspeicher