From 6c5c6fdfe80bad40c90c19b67f00226610d59a38 Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Thu, 2 Sep 2004 03:25:54 +0000 Subject: [PATCH] Move user exception support into userprog. --- src/Makefile.inc | 7 +- src/threads/gdt.h | 25 ------ src/threads/init.c | 17 +++- src/threads/interrupt.c | 142 +++++++++++--------------------- src/threads/interrupt.h | 3 + src/threads/intr-stubs.pl | 2 +- src/threads/loader.S | 1 - src/threads/loader.h | 6 ++ src/threads/thread.c | 4 +- src/userprog/exception.c | 94 +++++++++++++++++++++ src/userprog/exception.h | 6 ++ src/{threads => userprog}/gdt.c | 13 ++- src/userprog/gdt.h | 15 ++++ src/{threads => userprog}/tss.c | 0 src/{threads => userprog}/tss.h | 0 15 files changed, 207 insertions(+), 128 deletions(-) delete mode 100644 src/threads/gdt.h create mode 100644 src/userprog/exception.c create mode 100644 src/userprog/exception.h rename src/{threads => userprog}/gdt.c (92%) create mode 100644 src/userprog/gdt.h rename src/{threads => userprog}/tss.c (100%) rename src/{threads => userprog}/tss.h (100%) diff --git a/src/Makefile.inc b/src/Makefile.inc index c125eba..982b5e8 100644 --- a/src/Makefile.inc +++ b/src/Makefile.inc @@ -17,8 +17,6 @@ ASFLAGS = -Wa,--gstabs+ $(INCLUDES) $(DEFINES) # Core kernel. THREADS_SRC = start.S # Must be linked first. THREADS_SRC += init.c # Main program. -THREADS_SRC += gdt.c # GDT initialization. -THREADS_SRC += tss.c # TSS management. THREADS_SRC += thread.c # Thread management core. THREADS_SRC += switch.S # Thread switch routine. THREADS_SRC += interrupt.c # Interrupt core. @@ -51,13 +49,16 @@ FILESYS_SRC += fsutil.c # Utilities. # User process code. USERPROG_SRC = addrspace.c # Address spaces. +USERPROG_SRC += exception.c # User exception handler. +USERPROG_SRC += gdt.c # GDT initialization. +USERPROG_SRC += tss.c # TSS management. # Objects. OBJECTS = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(SOURCES))) all: diskimage.bin -intr-stubs.S: $(TOP_SRCDIR)/threads/intr-stubs.pl $(TOP_SRCDIR)/threads/gdt.h +intr-stubs.S: $(TOP_SRCDIR)/threads/intr-stubs.pl $(TOP_SRCDIR)/threads/loader.h $< > $@ kernel.lds.s: $(TOP_SRCDIR)/threads/kernel.lds.S $(TOP_SRCDIR)/threads/loader.h diff --git a/src/threads/gdt.h b/src/threads/gdt.h deleted file mode 100644 index c32ceab..0000000 --- a/src/threads/gdt.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef HEADER_GDT_H -#define HEADER_GDT_H 1 - -/* Segment selectors. */ -#define SEL_NULL 0x00 /* Null selector. */ -#define SEL_KCSEG 0x08 /* Kernel code selector. */ -#define SEL_KDSEG 0x10 /* Kernel data selector. */ -#define SEL_UCSEG 0x1B /* User code selector. */ -#define SEL_UDSEG 0x23 /* User data selector. */ -#define SEL_TSS 0x28 /* Task-state segment. */ -#define SEL_CNT 6 /* Number of segments. */ - -#ifndef __ASSEMBLER__ -#include - -static inline uint64_t -make_dtr_operand (uint16_t limit, void *base) -{ - return limit | ((uint64_t) (uint32_t) base << 16); -} - -void gdt_init (void); -#endif - -#endif /* gdt.h */ diff --git a/src/threads/init.c b/src/threads/init.c index ae86498..ec71cec 100644 --- a/src/threads/init.c +++ b/src/threads/init.c @@ -3,7 +3,6 @@ #include #include #include "debug.h" -#include "gdt.h" #include "interrupt.h" #include "io.h" #include "kbd.h" @@ -17,8 +16,12 @@ #include "serial.h" #include "thread.h" #include "timer.h" -#include "tss.h" #include "vga.h" +#ifdef USERPROG +#include "exception.h" +#include "gdt.h" +#include "tss.h" +#endif #ifdef FILESYS #include "filesys.h" #include "disk.h" @@ -59,8 +62,10 @@ main (void) /* Initialize memory system, segments, paging. */ palloc_init (); paging_init (); +#ifdef USERPROG tss_init (); gdt_init (); +#endif malloc_init (); /* Set random seed if not already done. */ @@ -70,6 +75,9 @@ main (void) intr_init (); timer_init (); kbd_init (); +#ifdef USERPROG + exception_init (); +#endif /* Do everything else in a system thread. */ thread_init (); @@ -92,6 +100,8 @@ main_thread (void *aux UNUSED) thread_execute (initial_program); else PANIC ("no initial program specified"); +#else + PANIC ("boot successful"); #endif } @@ -174,8 +184,9 @@ argv_init (void) " -p FILENAME Print the contents of FILENAME\n" " -r FILENAME Delete FILENAME\n" " -ls List the files in the filesystem\n" - " -D Dump complete filesystem contents\n"); + " -D Dump complete filesystem contents\n" #endif + ); } else PANIC ("unknown option `%s'", argv[i]); diff --git a/src/threads/interrupt.c b/src/threads/interrupt.c index 7559447..8173ec1 100644 --- a/src/threads/interrupt.c +++ b/src/threads/interrupt.c @@ -3,7 +3,6 @@ #include #include "intr-stubs.h" #include "debug.h" -#include "gdt.h" #include "io.h" #include "lib.h" #include "mmu.h" @@ -40,11 +39,10 @@ static void pic_end_of_interrupt (int irq); /* Interrupt Descriptor Table helpers. */ static uint64_t make_intr_gate (void (*) (void), int dpl); static uint64_t make_trap_gate (void (*) (void), int dpl); +static inline uint64_t make_idtr_operand (uint16_t limit, void *base); /* Interrupt handlers. */ void intr_handler (struct intr_frame *args); -static intr_handler_func panic NO_RETURN; -static intr_handler_func kill NO_RETURN; /* Returns the current interrupt status. */ enum intr_level @@ -90,40 +88,34 @@ intr_init (void) uint64_t idtr_operand; int i; + /* Initialize interrupt controller. */ pic_init (); + /* Load IDT register. */ + idtr_operand = make_idtr_operand (sizeof idt - 1, idt); + asm volatile ("lidt %0" :: "m" (idtr_operand)); + /* Initialize intr_names. */ for (i = 0; i < INTR_CNT; i++) intr_names[i] = "unknown"; - - /* Most exceptions require ring 0. - Exceptions 3, 4, and 5 can be caused by ring 3 directly. */ - intr_register (0, 0, INTR_ON, kill, "#DE Divide Error"); - intr_register (1, 0, INTR_ON, kill, "#DB Debug Exception"); - intr_register (2, 0, INTR_ON, panic, "NMI Interrupt"); - intr_register (3, 3, INTR_ON, kill, "#BP Breakpoint Exception"); - intr_register (4, 3, INTR_ON, kill, "#OF Overflow Exception"); - intr_register (5, 3, INTR_ON, kill, "#BR BOUND Range Exceeded Exception"); - intr_register (6, 0, INTR_ON, kill, "#UD Invalid Opcode Exception"); - intr_register (7, 0, INTR_ON, kill, "#NM Device Not Available Exception"); - intr_register (8, 0, INTR_ON, panic, "#DF Double Fault Exception"); - intr_register (9, 0, INTR_ON, panic, "Coprocessor Segment Overrun"); - intr_register (10, 0, INTR_ON, panic, "#TS Invalid TSS Exception"); - intr_register (11, 0, INTR_ON, kill, "#NP Segment Not Present"); - intr_register (12, 0, INTR_ON, kill, "#SS Stack Fault Exception"); - intr_register (13, 0, INTR_ON, kill, "#GP General Protection Exception"); - intr_register (16, 0, INTR_ON, kill, "#MF x87 FPU Floating-Point Error"); - intr_register (17, 0, INTR_ON, panic, "#AC Alignment Check Exception"); - intr_register (18, 0, INTR_ON, panic, "#MC Machine-Check Exception"); - intr_register (19, 0, INTR_ON, kill, "#XF SIMD Floating-Point Exception"); - - /* Most exceptions can be handled with interrupts turned on. - We need to disable interrupts for page faults because the - fault address is stored in CR2 and needs to be preserved. */ - intr_register (14, 0, INTR_OFF, kill, "#PF Page-Fault Exception"); - - idtr_operand = make_dtr_operand (sizeof idt - 1, idt); - asm volatile ("lidt %0" :: "m" (idtr_operand)); + intr_names[0] = "#DE Divide Error"; + intr_names[1] = "#DB Debug Exception"; + intr_names[2] = "NMI Interrupt"; + intr_names[3] = "#BP Breakpoint Exception"; + intr_names[4] = "#OF Overflow Exception"; + intr_names[5] = "#BR BOUND Range Exceeded Exception"; + intr_names[6] = "#UD Invalid Opcode Exception"; + intr_names[7] = "#NM Device Not Available Exception"; + intr_names[8] = "#DF Double Fault Exception"; + intr_names[9] = "Coprocessor Segment Overrun"; + intr_names[10] = "#TS Invalid TSS Exception"; + intr_names[11] = "#NP Segment Not Present"; + intr_names[12] = "#SS Stack Fault Exception"; + intr_names[13] = "#GP General Protection Exception"; + intr_names[16] = "#MF x87 FPU Floating-Point Error"; + intr_names[17] = "#AC Alignment Check Exception"; + intr_names[18] = "#MC Machine-Check Exception"; + intr_names[19] = "#XF SIMD Floating-Point Exception"; } /* Registers interrupt VEC_NO to invoke HANDLER, which is named @@ -286,22 +278,28 @@ make_trap_gate (void (*function) (void), int dpl) { return make_gate (function, dpl, 15); } + +/* Returns a descriptor that yields the given LIMIT and BASE when + used as an operand for the LIDT instruction. */ +static inline uint64_t +make_idtr_operand (uint16_t limit, void *base) +{ + return limit | ((uint64_t) (uint32_t) base << 16); +} /* Interrupt handlers. */ -static void dump_intr_frame (struct intr_frame *); - /* Handler for all interrupts, faults, and exceptions. This function is called by the assembly language interrupt stubs in - intr-stubs.S (see intr-stubs.pl). ARGS describes the + intr-stubs.S (see intr-stubs.pl). FRAME describes the interrupt and the interrupted thread's registers. */ void -intr_handler (struct intr_frame *args) +intr_handler (struct intr_frame *frame) { bool external; intr_handler_func *handler; - external = args->vec_no >= 0x20 && args->vec_no < 0x30; + external = frame->vec_no >= 0x20 && frame->vec_no < 0x30; if (external) { ASSERT (intr_get_level () == INTR_OFF); @@ -314,10 +312,13 @@ intr_handler (struct intr_frame *args) /* Invoke the interrupt's handler. If there is no handler, invoke the unexpected interrupt handler. */ - handler = intr_handlers[args->vec_no]; + handler = intr_handlers[frame->vec_no]; if (handler == NULL) - handler = panic; - handler (args); + { + intr_dump_frame (frame); + PANIC ("Unexpected interrupt"); + } + handler (frame); /* Complete the processing of an external interrupt. */ if (external) @@ -326,65 +327,16 @@ intr_handler (struct intr_frame *args) ASSERT (intr_context ()); in_external_intr = false; - pic_end_of_interrupt (args->vec_no); + pic_end_of_interrupt (frame->vec_no); if (yield_on_return) thread_yield (); } } -/* Handler for an interrupt that should not have been invoked. */ -static void -panic (struct intr_frame *regs) -{ - dump_intr_frame (regs); - PANIC ("Panic!"); -} - -/* Handler for an exception (probably) caused by a user process. */ -static void -kill (struct intr_frame *f) -{ - /* This interrupt is one (probably) caused by a user process. - For example, the process might have tried to access unmapped - virtual memory (a page fault). For now, we simply kill the - user process. Later, we'll want to handle page faults in - the kernel. Real Unix-like operating systems pass most - exceptions back to the process via signals, but we don't - implement them. */ - - /* The interrupt frame's code segment value tells us where the - exception originated. */ - switch (f->cs) - { - case SEL_UCSEG: - /* User's code segment, so it's a user exception, as we - expected. */ - printk ("%s: dying due to interrupt %#04x (%s).\n", - thread_name (thread_current ()), - f->vec_no, intr_names[f->vec_no]); - dump_intr_frame (f); - thread_exit (); - - case SEL_KCSEG: - /* Kernel's code segment, which indicates a kernel bug. - Kernel code shouldn't throw exceptions. (Page faults - may cause kernel exceptions--but they shouldn't arrive - here.) */ - printk ("Kernel bug - unexpected interrupt in kernel\n"); - panic (f); - - default: - /* Some other code segment? Shouldn't happen. */ - printk ("Interrupt %#04x (%s) in unknown segment %04x\n", - f->vec_no, intr_names[f->vec_no], f->cs); - thread_exit (); - } -} - /* Dumps interrupt frame F to the console, for debugging. */ -static void -dump_intr_frame (struct intr_frame *f) +void +intr_dump_frame (const struct intr_frame *f) { uint32_t cr2, ss; asm ("movl %%cr2, %0" : "=r" (cr2)); @@ -401,3 +353,9 @@ dump_intr_frame (struct intr_frame *f) f->cs, f->ds, f->es, f->cs != SEL_KCSEG ? f->ss : ss); } +/* Returns the name of interrupt VEC. */ +const char * +intr_name (uint8_t vec) +{ + return intr_names[vec]; +} diff --git a/src/threads/interrupt.h b/src/threads/interrupt.h index fe26b3d..40d7b6e 100644 --- a/src/threads/interrupt.h +++ b/src/threads/interrupt.h @@ -56,4 +56,7 @@ void intr_register (uint8_t vec, int dpl, enum intr_level, intr_handler_func *, bool intr_context (void); void intr_yield_on_return (void); +void intr_dump_frame (const struct intr_frame *); +const char *intr_name (uint8_t vec); + #endif /* interrupt.h */ diff --git a/src/threads/intr-stubs.pl b/src/threads/intr-stubs.pl index d9e0dd3..22eaf59 100755 --- a/src/threads/intr-stubs.pl +++ b/src/threads/intr-stubs.pl @@ -1,7 +1,7 @@ #! /usr/bin/perl print <<'EOF'; -#include "gdt.h" +#include "loader.h" .data .globl intr_stubs diff --git a/src/threads/loader.S b/src/threads/loader.S index 41289d1..4e4fbb4 100644 --- a/src/threads/loader.S +++ b/src/threads/loader.S @@ -1,6 +1,5 @@ #include "loader.h" #include "mmu.h" -#include "gdt.h" ############################################################################## # Kernel loader. diff --git a/src/threads/loader.h b/src/threads/loader.h index 9507a6b..f5d7a9b 100644 --- a/src/threads/loader.h +++ b/src/threads/loader.h @@ -24,4 +24,10 @@ /* Kernel command line. */ #define LOADER_RAM_PAGES (LOADER_CMD_LINE - 4) /* # of pages of RAM. */ +/* GDT selectors defined by loader. + More selectors are defined by userprog/gdt.h. */ +#define SEL_NULL 0x00 /* Null selector. */ +#define SEL_KCSEG 0x08 /* Kernel code selector. */ +#define SEL_KDSEG 0x10 /* Kernel data selector. */ + #endif /* loader.h */ diff --git a/src/threads/thread.c b/src/threads/thread.c index c9bc8ee..d89edd6 100644 --- a/src/threads/thread.c +++ b/src/threads/thread.c @@ -4,11 +4,13 @@ #include "interrupt.h" #include "intr-stubs.h" #include "lib.h" -#include "gdt.h" #include "mmu.h" #include "palloc.h" #include "random.h" #include "switch.h" +#ifdef USERPROG +#include "gdt.h" +#endif #define THREAD_MAGIC 0x1234abcdu diff --git a/src/userprog/exception.c b/src/userprog/exception.c new file mode 100644 index 0000000..eaa0691 --- /dev/null +++ b/src/userprog/exception.c @@ -0,0 +1,94 @@ +#include "exception.h" +#include "lib.h" +#include "gdt.h" +#include "interrupt.h" +#include "thread.h" + +static void kill (struct intr_frame *); + +/* Registers handlers for interrupts that can be caused by user + programs. + + In a real Unix-like OS, most of these interrupts would be + passed along to the user process in the form of signals, but + we don't implement signals. Instead, we'll make them simply + kill the user process. + + Page faults are an exception. Here they are treated the same + way as other exceptions, but this will need to change to + implement virtual memory. + + Refer to [IA32-v3] section 5.14 for a description of each of + these exceptions. */ +void +exception_init (void) +{ + /* These exceptions can be raised explicitly by a user program, + e.g. via the INT, INT3, INTO, and BOUND instructions. Thus, + we set DPL==3, meaning that user programs are allowed to + invoke them via these instructions. */ + intr_register (3, 3, INTR_ON, kill, "#BP Breakpoint Exception"); + intr_register (4, 3, INTR_ON, kill, "#OF Overflow Exception"); + intr_register (5, 3, INTR_ON, kill, "#BR BOUND Range Exceeded Exception"); + + /* These exceptions have DPL==0, preventing user processes from + invoking them via the INT instruction. They can still be + caused indirectly, e.g. #DE can be caused by dividing by + 0. */ + intr_register (0, 0, INTR_ON, kill, "#DE Divide Error"); + intr_register (1, 0, INTR_ON, kill, "#DB Debug Exception"); + intr_register (6, 0, INTR_ON, kill, "#UD Invalid Opcode Exception"); + intr_register (7, 0, INTR_ON, kill, "#NM Device Not Available Exception"); + intr_register (11, 0, INTR_ON, kill, "#NP Segment Not Present"); + intr_register (12, 0, INTR_ON, kill, "#SS Stack Fault Exception"); + intr_register (13, 0, INTR_ON, kill, "#GP General Protection Exception"); + intr_register (16, 0, INTR_ON, kill, "#MF x87 FPU Floating-Point Error"); + intr_register (19, 0, INTR_ON, kill, "#XF SIMD Floating-Point Exception"); + + /* Most exceptions can be handled with interrupts turned on. + We need to disable interrupts for page faults because the + fault address is stored in CR2 and needs to be preserved. */ + intr_register (14, 0, INTR_OFF, kill, "#PF Page-Fault Exception"); +} + +/* Handler for an exception (probably) caused by a user process. */ +static void +kill (struct intr_frame *f) +{ + /* This interrupt is one (probably) caused by a user process. + For example, the process might have tried to access unmapped + virtual memory (a page fault). For now, we simply kill the + user process. Later, we'll want to handle page faults in + the kernel. Real Unix-like operating systems pass most + exceptions back to the process via signals, but we don't + implement them. */ + + /* The interrupt frame's code segment value tells us where the + exception originated. */ + switch (f->cs) + { + case SEL_UCSEG: + /* User's code segment, so it's a user exception, as we + expected. */ + printk ("%s: dying due to interrupt %#04x (%s).\n", + thread_name (thread_current ()), + f->vec_no, intr_name (f->vec_no)); + intr_dump_frame (f); + thread_exit (); + + case SEL_KCSEG: + /* Kernel's code segment, which indicates a kernel bug. + Kernel code shouldn't throw exceptions. (Page faults + may cause kernel exceptions--but they shouldn't arrive + here.) */ + intr_dump_frame (f); + PANIC ("Kernel bug - unexpected interrupt in kernel"); + + default: + /* Some other code segment? Shouldn't happen. */ + printk ("Interrupt %#04x (%s) in unknown segment %04x\n", + f->vec_no, intr_name (f->vec_no), f->cs); + thread_exit (); + } +} + diff --git a/src/userprog/exception.h b/src/userprog/exception.h new file mode 100644 index 0000000..37b6142 --- /dev/null +++ b/src/userprog/exception.h @@ -0,0 +1,6 @@ +#ifndef HEADER_EXCEPTION_H +#define HEADER_EXCEPTION_H 1 + +void exception_init (void); + +#endif /* exception.h */ diff --git a/src/threads/gdt.c b/src/userprog/gdt.c similarity index 92% rename from src/threads/gdt.c rename to src/userprog/gdt.c index 7a6005f..e3e1dde 100644 --- a/src/threads/gdt.c +++ b/src/userprog/gdt.c @@ -23,10 +23,11 @@ [IA32-v3] sections 3.2 through 3.5. */ static uint64_t gdt[SEL_CNT]; -/* Creating descriptors. */ +/* GDT helpers. */ static uint64_t make_code_desc (int dpl); static uint64_t make_data_desc (int dpl); static uint64_t make_tss_desc (void *laddr); +static uint64_t make_gdtr_operand (uint16_t limit, void *base); /* Sets up a proper GDT. The bootstrap loader's GDT didn't include user-mode selectors or a TSS, but we need both now. */ @@ -44,7 +45,7 @@ gdt_init (void) gdt[SEL_TSS / sizeof *gdt] = make_tss_desc (tss_get ()); /* Load GDTR, TR. */ - gdtr_operand = make_dtr_operand (sizeof gdt - 1, gdt); + gdtr_operand = make_gdtr_operand (sizeof gdt - 1, gdt); asm volatile ("lgdt %0" :: "m" (gdtr_operand)); asm volatile ("ltr %w0" :: "r" (SEL_TSS)); } @@ -131,3 +132,11 @@ make_tss_desc (void *laddr) return make_seg_desc ((uint32_t) laddr, 0x67, CLS_SYSTEM, 9, 0, GRAN_BYTE); } + +/* Returns a descriptor that yields the given LIMIT and BASE when + used as an operand for the LGDT instruction. */ +static uint64_t +make_gdtr_operand (uint16_t limit, void *base) +{ + return limit | ((uint64_t) (uint32_t) base << 16); +} diff --git a/src/userprog/gdt.h b/src/userprog/gdt.h new file mode 100644 index 0000000..ae89681 --- /dev/null +++ b/src/userprog/gdt.h @@ -0,0 +1,15 @@ +#ifndef HEADER_GDT_H +#define HEADER_GDT_H 1 + +#include "loader.h" + +/* Segment selectors. + More selectors are defined by the loader in loader.h. */ +#define SEL_UCSEG 0x1B /* User code selector. */ +#define SEL_UDSEG 0x23 /* User data selector. */ +#define SEL_TSS 0x28 /* Task-state segment. */ +#define SEL_CNT 6 /* Number of segments. */ + +void gdt_init (void); + +#endif /* gdt.h */ diff --git a/src/threads/tss.c b/src/userprog/tss.c similarity index 100% rename from src/threads/tss.c rename to src/userprog/tss.c diff --git a/src/threads/tss.h b/src/userprog/tss.h similarity index 100% rename from src/threads/tss.h rename to src/userprog/tss.h -- 2.30.2