Support simple exception handlers for General Protection (GP) and Page Fault (PF) faults. The handlers will report the issue and halt the machine.
Signed-off-by: Kevin O'Connor kevin@koconnor.net --- src/malloc.c | 2 ++ src/memmap.c | 66 +++++++++++++++++++++++++++++++++++++++++++-------------- src/memmap.h | 2 ++ src/romlayout.S | 21 +++++++++++++++--- src/x86.h | 22 ++++++++++++++++++- 5 files changed, 93 insertions(+), 20 deletions(-)
diff --git a/src/malloc.c b/src/malloc.c index 093b165..ec7a21f 100644 --- a/src/malloc.c +++ b/src/malloc.c @@ -445,6 +445,8 @@ malloc_preinit(void) alloc_add(&ZoneHigh, highram, highram + BUILD_MAX_HIGHTABLE); e820_add(highram, BUILD_MAX_HIGHTABLE, E820_RESERVED); } + + memmap_preinit(); }
void diff --git a/src/memmap.c b/src/memmap.c index 51a0123..b08313a 100644 --- a/src/memmap.c +++ b/src/memmap.c @@ -5,6 +5,10 @@ // This file may be distributed under the terms of the GNU LGPLv3 license.
#include "biosvar.h" // struct rmode_IVT +#include "malloc.h" // malloc_high +#include "memmap.h" // memmap_preinit +#include "string.h" // memset +#include "output.h" // panic #include "x86.h" // struct descloc_s
@@ -12,22 +16,6 @@ * GDT and IDT tables ****************************************************************/
-// Real mode IDT descriptor -struct descloc_s rmode_IDT_info VARFSEG = { - .length = sizeof(struct rmode_IVT) - 1, - .addr = (u32)MAKE_FLATPTR(SEG_IVT, 0), -}; - -// Dummy IDT that forces a machine shutdown if an irq happens in -// protected mode. -u8 dummy_IDT VARFSEG; - -// Protected mode IDT descriptor -struct descloc_s pmode_IDT_info VARFSEG = { - .length = sizeof(dummy_IDT) - 1, - .addr = (u32)&dummy_IDT, -}; - // GDT u64 rombios32_gdt[] VARFSEG __aligned(8) = { // First entry can't be used. @@ -51,3 +39,49 @@ struct descloc_s rombios32_gdt_48 VARFSEG = { .length = sizeof(rombios32_gdt) - 1, .addr = (u32)rombios32_gdt, }; + +// Real mode IDT descriptor +struct descloc_s rmode_IDT_info VARFSEG = { + .length = sizeof(struct rmode_IVT) - 1, + .addr = (u32)MAKE_FLATPTR(SEG_IVT, 0), +}; + +// Protected mode IDT descriptor +struct descloc_s pmode_IDT_info VARFSEG; + +void VISIBLE32FLAT +handle_gp(struct x86_exception_stack *data) +{ + panic("#GP exception: %x %x %x %x\n", data->error_code, data->eip + , data->cs, data->eflags); +} + +void VISIBLE32FLAT +handle_pf(struct x86_exception_stack *data) +{ + panic("#PF exception: %x %x %x %x %x\n", data->error_code, data->eip + , data->cs, data->eflags, cr2_read()); +} + +// Setup a 32bit General Protection (GP) and Page Fault (PF) handler +void +memmap_preinit(void) +{ + u32 ilen = sizeof(u64)*(IDT_VECTOR_PF+1); + u64 *pidt = malloc_high(ilen); + if (!pidt) { + warn_noalloc(); + return; + } + memset(pidt, 0, ilen); + extern void entry_gp(void); + extern void entry_pf(void); + pidt[IDT_VECTOR_GP] = IDT_IRQ | IDT_SEGMENT(SEG32_MODE32_CS) + | IDT_OFFSET((u32)&entry_gp); + pidt[IDT_VECTOR_PF] = IDT_IRQ | IDT_SEGMENT(SEG32_MODE32_CS) + | IDT_OFFSET((u32)&entry_pf); + pmode_IDT_info.addr = virt_to_phys(pidt); + pmode_IDT_info.length = ilen-1; + dprintf(1, "Installing pmode exception handler\n"); + lidt(&pmode_IDT_info); +} diff --git a/src/memmap.h b/src/memmap.h index c927ab9..d4f150a 100644 --- a/src/memmap.h +++ b/src/memmap.h @@ -7,6 +7,8 @@ #define PAGE_SIZE 4096 #define PAGE_SHIFT 12
+void memmap_preinit(void); + static inline u32 virt_to_phys(void *v) { return (u32)v; } diff --git a/src/romlayout.S b/src/romlayout.S index 823188b..acf0f32 100644 --- a/src/romlayout.S +++ b/src/romlayout.S @@ -42,8 +42,8 @@ transition32:
transition32_nmi_off: // Set segment descriptors - lidtw %cs:pmode_IDT_info - lgdtw %cs:rombios32_gdt_48 + lidtl %cs:pmode_IDT_info + lgdtl %cs:rombios32_gdt_48
// Enable protected mode movl %cr0, %ecx @@ -104,7 +104,7 @@ transition16big: ljmpw $SEG_BIOS, $2f
// restore IDT to normal real-mode defaults -2: lidtw %cs:rmode_IDT_info +2: lidtl %cs:rmode_IDT_info
// Clear segment registers xorw %cx, %cx @@ -412,6 +412,21 @@ __csm_return: popfw lretw
+// Entry point for protected mode exceptions + DECLFUNC entry_gp + .code32 +entry_gp: + movl %esp, %eax + calll _cfunc32flat_handle_gp - BUILD_BIOS_ADDR + .code16 + + DECLFUNC entry_pf + .code32 +entry_pf: + movl %esp, %eax + calll _cfunc32flat_handle_pf - BUILD_BIOS_ADDR + .code16 +
/**************************************************************** * Interrupt entry points diff --git a/src/x86.h b/src/x86.h index 53378e9..5de149a 100644 --- a/src/x86.h +++ b/src/x86.h @@ -92,6 +92,12 @@ static inline u16 cr0_vm86_read(void) { return cr0; }
+static inline u32 cr2_read(void) { + u32 cr2; + asm("movl %%cr2, %0" : "=r"(cr2)); + return cr2; +} + static inline u64 rdmsr(u32 index) { u64 ret; @@ -228,7 +234,7 @@ static inline u8 readb(const void *addr) {
// GDT bits #define GDT_CODE (0x9bULL << 40) // Code segment - P,R,A bits also set -#define GDT_DATA (0x93ULL << 40) // Data segment - W,A bits also set +#define GDT_DATA (0x93ULL << 40) // Data segment - P,W,A bits also set #define GDT_B (0x1ULL << 54) // Big flag #define GDT_G (0x1ULL << 55) // Granularity flag // GDT bits for segment base @@ -239,6 +245,17 @@ static inline u8 readb(const void *addr) { | (((u64)(v) & 0x0000ffff) << 0)) // GDT bits for segment limit (0-4Gig in 4K chunks) #define GDT_GRANLIMIT(v) (GDT_G | GDT_LIMIT((v) >> 12)) +// IDT bits +#define IDT_IRQ (0x8eULL << 40) // IRQ gate - P,D bits also set +#define IDT_OFFSET(v) ((((u64)(v) & 0xffff0000) << 32) \ + | ((u64)(v) & 0x0000ffff)) +#define IDT_SEGMENT(v) ((u64)(v) << 16) +#define IDT_VECTOR_GP 13 +#define IDT_VECTOR_PF 14 + +struct x86_exception_stack { + u32 error_code, eip, cs, eflags; +};
struct descloc_s { u16 length; @@ -251,6 +268,9 @@ static inline void sgdt(struct descloc_s *desc) { static inline void lgdt(struct descloc_s *desc) { asm("lgdtl %0" : : "m"(*desc) : "memory"); } +static inline void lidt(struct descloc_s *desc) { + asm("lidtl %0" : : "m"(*desc) : "memory"); +}
static inline u8 get_a20(void) { return (inb(PORT_A20) & A20_ENABLE_BIT) != 0;