Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Bernhard Heinloth
OOStuBS - WS18
Commits
0d0253e1
Commit
0d0253e1
authored
Nov 09, 2018
by
Bernhard Heinloth
Browse files
Vorgabe oostubs Aufgabe 2
parent
22d5e317
Changes
17
Show whitespace changes
Inline
Side-by-side
debug/gdb/handler.asm
0 → 100644
View file @
0d0253e1
;
; 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
db
g_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
db
g_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
DB
GIRQ
i
,
1
%else
; ohne ERROR CODE
DB
GIRQ
i
,
0
%endif
%assign i i+1
%endrep
debug/gdb/handler.cc
0 → 100644
View file @
0d0253e1
/*
* 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
();
}
}
debug/gdb/init.cc
0 → 100644
View file @
0d0253e1
// 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
;
}
debug/gdb/protocol.cc
0 → 100644
View file @
0d0253e1
/*
* 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
;
}
}
<