From ecd12507c871ad36c8f26d705d73ca367b5373ac Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Fri, 21 Jan 2005 22:17:37 +0000 Subject: [PATCH] Set up a frame pointer on interrupt entry, to improve backtraces of system calls, page faults, etc., whether they come from user space or the kernel. Get rid of intr-stubs.pl, replacing it by equivalent all-assembler + C preprocessor code. --- doc/threads.texi | 4 +- doc/tour.texi | 6 +- src/Makefile.build | 5 +- src/threads/interrupt.c | 4 +- src/threads/interrupt.h | 11 +- src/threads/intr-stubs.S | 205 ++++++++++++++++++++++++++++++++++++++ src/threads/intr-stubs.h | 7 +- src/threads/intr-stubs.pl | 66 ------------ src/userprog/process.c | 2 +- 9 files changed, 223 insertions(+), 87 deletions(-) create mode 100644 src/threads/intr-stubs.S delete mode 100755 src/threads/intr-stubs.pl diff --git a/doc/threads.texi b/doc/threads.texi index 6659732..bc2ccb5 100644 --- a/doc/threads.texi +++ b/doc/threads.texi @@ -157,9 +157,9 @@ the kernel. Basic interrupt handling and functions for turning interrupts on and off. -@item intr-stubs.pl +@item intr-stubs.S @itemx intr-stubs.h -A Perl program that outputs assembly for low-level interrupt handling. +Assembly code for low-level interrupt handling. @item synch.c @itemx synch.h diff --git a/doc/tour.texi b/doc/tour.texi index 39483a3..d1147dd 100644 --- a/doc/tour.texi +++ b/doc/tour.texi @@ -782,11 +782,7 @@ In Pintos, @func{intr_init} in @file{threads/interrupt.c} sets up the IDT so that each entry points to a unique entry point in @file{threads/intr-stubs.S} named @func{intr@var{NN}_stub}, where @var{NN} is the interrupt number in -hexadecimal.@footnote{@file{threads/intr-stubs.S} is so repetitive -that it is actually generated by a Perl script, -@file{threads/intr-stubs.pl}. Thus, you will actually find -@file{threads/intr-stubs.S} in your @file{threads/build/threads} -directory, not in plain @file{threads}.} Because the CPU doesn't give +hexadecimal. Because the CPU doesn't give us any other way to find out the interrupt number, this entry point pushes the interrupt number on the stack. Then it jumps to @func{intr_entry}, which pushes all the registers that the processor diff --git a/src/Makefile.build b/src/Makefile.build index aa8e650..d4c6d65 100644 --- a/src/Makefile.build +++ b/src/Makefile.build @@ -67,9 +67,6 @@ DEPENDS = $(patsubst %.o,%.d,$(OBJECTS)) all: os.dsk -threads/intr-stubs.S: threads/intr-stubs.pl threads/loader.h - $< > $@.tmp && mv $@.tmp $@ - threads/kernel.lds.s: CPPFLAGS += -P threads/kernel.lds.s: threads/kernel.lds.S threads/loader.h @@ -92,7 +89,7 @@ os.dsk: loader.bin kernel.bin clean: $(RM) -f $(OBJECTS) $(DEPENDS) - $(RM) -f threads/intr-stubs.S threads/loader.o + $(RM) -f threads/loader.o $(RM) -f kernel.o kernel.lds.s $(RM) -f kernel.bin loader.bin diff --git a/src/threads/interrupt.c b/src/threads/interrupt.c index 380544a..3e52228 100644 --- a/src/threads/interrupt.c +++ b/src/threads/interrupt.c @@ -310,8 +310,8 @@ make_idtr_operand (uint16_t limit, void *base) /* 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). FRAME describes the - interrupt and the interrupted thread's registers. */ + intr-stubs.S. FRAME describes the interrupt and the + interrupted thread's registers. */ void intr_handler (struct intr_frame *frame) { diff --git a/src/threads/interrupt.h b/src/threads/interrupt.h index 99abf8e..b805671 100644 --- a/src/threads/interrupt.h +++ b/src/threads/interrupt.h @@ -19,7 +19,7 @@ enum intr_level intr_disable (void); /* Interrupt stack frame. */ struct intr_frame { - /* Pushed by intr_entry in intr-stubs.S (see intr-stubs.pl). + /* Pushed by intr_entry in intr-stubs.S. These are the interrupted task's saved registers. */ uint32_t edi; /* Saved EDI. */ uint32_t esi; /* Saved ESI. */ @@ -34,13 +34,18 @@ struct intr_frame uint16_t es, :16; /* Saved ES segment register. */ uint16_t ds, :16; /* Saved DS segment register. */ - /* Pushed by intrXX_stub in intr-stubs.S (see intr-stubs.pl). */ + /* Pushed by intrNN_stub in intr-stubs.S. */ uint32_t vec_no; /* Interrupt vector number. */ /* Sometimes pushed by the CPU, - otherwise for consistency pushed as 0 by intrXX_stub. */ + otherwise for consistency pushed as 0 by intrNN_stub. + The CPU puts it just under `eip', but we move it here. */ uint32_t error_code; /* Error code. */ + /* Pushed by intrNN_stub in intr-stubs.S. + This frame pointer eases interpretation of backtraces. */ + void *frame_pointer; /* Saved EBP (frame pointer). */ + /* Pushed by the CPU. These are the interrupted task's saved registers. */ void (*eip) (void); /* Next instruction to execute. */ diff --git a/src/threads/intr-stubs.S b/src/threads/intr-stubs.S new file mode 100644 index 0000000..d250777 --- /dev/null +++ b/src/threads/intr-stubs.S @@ -0,0 +1,205 @@ +#include "threads/loader.h" + + .intel_syntax noprefix + .text + +/* Main interrupt entry point. + + An internal or external interrupt starts in one of the + intrNN_stub routines, which push the `struct intr_frame' + frame_pointer, error_code, and vec_no members on the stack, + then jump here. + + We save the rest of the `struct intr_frame' members to the + stack, set up some registers as needed by the kernel, and then + call intr_handler(), which actually handles the interrupt. + + We "fall through" to intr_exit to return from the interrupt. +*/ +.func intr_entry +intr_entry: + /* Save caller's registers. */ + push ds + push es + push fs + push gs + pusha + + /* Set up kernel environment. */ + cld /* String instructions go upward. */ + mov eax, SEL_KDSEG /* Initialize segment registers. */ + mov ds, eax + mov es, eax + lea ebp, [esp + 56] /* Set up frame pointer. */ + + /* Call interrupt handler. */ + push esp +.globl intr_handler + call intr_handler + add esp, 4 +.endfunc + +/* Interrupt exit. + + Restores the caller's registers, discards extra data on the + stack, and returns to the caller. + + This is a separate function because it is called directly when + we launch a new user process (see execute_thread() in + userprog/process.c). */ +.globl intr_exit +.func intr_exit +intr_exit: + /* Restore caller's registers. */ + popa + pop gs + pop fs + pop es + pop ds + + /* Discard `struct intr_frame' vec_no, error_code, + frame_pointer members. */ + add esp, 12 + + /* Return to caller. */ + iret +.endfunc + +/* Interrupt stubs. + + This defines 256 fragments of code, named `intr00_stub' + through `intrff_stub', each of which is used as the entry + point for the corresponding interrupt vector. It also puts + the address of each of these functions in the correct spot in + `intr_stubs', an array of function pointers. + + Most of the stubs do this: + + 1. Push EBP on the stack (frame_pointer in `struct intr_frame'). + + 2. Push 0 on the stack (error_code). + + 3. Push the interrupt number on the stack (vec_no). + + The CPU pushes an extra "error code" on the stack for a few + interrupts. Because we want EBP to be where the error code + is, we follow a different path: + + 1. Push a duplicate copy of the error code on the stack. + + 2. Replace the original copy of the error code by EBP. + + 3. Push the interrupt number on the stack. */ + + .data +.globl intr_stubs +intr_stubs: + +/* This implements steps 1 and 2, described above, in the common + case where we just push a 0 error code. */ +#define zero \ + push ebp; \ + push 0 + +/* This implements steps 1 and 2, described above, in the case + where the CPU already pushed an error code. */ +#define REAL \ + push dword ptr [esp]; \ + mov [esp + 4], ebp + +/* Emits a stub for interrupt vector NUMBER. + TYPE is `zero', for the case where we push a 0 error code, + or `REAL', if the CPU pushes an error code for us. */ +#define STUB(NUMBER, TYPE) \ + .text; \ +.globl intr##NUMBER##_stub; \ +.func intr##NUMBER##_stub; \ +intr##NUMBER##_stub: \ + TYPE; \ + push 0x##NUMBER; \ + jmp intr_entry; \ +.endfunc; \ + \ + .data; \ + .long intr##NUMBER##_stub; + +/* All the stubs. */ +STUB(00, zero) STUB(01, zero) STUB(02, zero) STUB(03, zero) +STUB(04, zero) STUB(05, zero) STUB(06, zero) STUB(07, zero) +STUB(08, REAL) STUB(09, zero) STUB(0a, REAL) STUB(0b, REAL) +STUB(0c, zero) STUB(0d, REAL) STUB(0e, REAL) STUB(0f, zero) + +STUB(10, zero) STUB(11, REAL) STUB(12, zero) STUB(13, zero) +STUB(14, zero) STUB(15, zero) STUB(16, zero) STUB(17, zero) +STUB(18, REAL) STUB(19, zero) STUB(1a, REAL) STUB(1b, REAL) +STUB(1c, zero) STUB(1d, REAL) STUB(1e, REAL) STUB(1f, zero) + +STUB(20, zero) STUB(21, zero) STUB(22, zero) STUB(23, zero) +STUB(24, zero) STUB(25, zero) STUB(26, zero) STUB(27, zero) +STUB(28, zero) STUB(29, zero) STUB(2a, zero) STUB(2b, zero) +STUB(2c, zero) STUB(2d, zero) STUB(2e, zero) STUB(2f, zero) + +STUB(30, zero) STUB(31, zero) STUB(32, zero) STUB(33, zero) +STUB(34, zero) STUB(35, zero) STUB(36, zero) STUB(37, zero) +STUB(38, zero) STUB(39, zero) STUB(3a, zero) STUB(3b, zero) +STUB(3c, zero) STUB(3d, zero) STUB(3e, zero) STUB(3f, zero) + +STUB(40, zero) STUB(41, zero) STUB(42, zero) STUB(43, zero) +STUB(44, zero) STUB(45, zero) STUB(46, zero) STUB(47, zero) +STUB(48, zero) STUB(49, zero) STUB(4a, zero) STUB(4b, zero) +STUB(4c, zero) STUB(4d, zero) STUB(4e, zero) STUB(4f, zero) + +STUB(50, zero) STUB(51, zero) STUB(52, zero) STUB(53, zero) +STUB(54, zero) STUB(55, zero) STUB(56, zero) STUB(57, zero) +STUB(58, zero) STUB(59, zero) STUB(5a, zero) STUB(5b, zero) +STUB(5c, zero) STUB(5d, zero) STUB(5e, zero) STUB(5f, zero) + +STUB(60, zero) STUB(61, zero) STUB(62, zero) STUB(63, zero) +STUB(64, zero) STUB(65, zero) STUB(66, zero) STUB(67, zero) +STUB(68, zero) STUB(69, zero) STUB(6a, zero) STUB(6b, zero) +STUB(6c, zero) STUB(6d, zero) STUB(6e, zero) STUB(6f, zero) + +STUB(70, zero) STUB(71, zero) STUB(72, zero) STUB(73, zero) +STUB(74, zero) STUB(75, zero) STUB(76, zero) STUB(77, zero) +STUB(78, zero) STUB(79, zero) STUB(7a, zero) STUB(7b, zero) +STUB(7c, zero) STUB(7d, zero) STUB(7e, zero) STUB(7f, zero) + +STUB(80, zero) STUB(81, zero) STUB(82, zero) STUB(83, zero) +STUB(84, zero) STUB(85, zero) STUB(86, zero) STUB(87, zero) +STUB(88, zero) STUB(89, zero) STUB(8a, zero) STUB(8b, zero) +STUB(8c, zero) STUB(8d, zero) STUB(8e, zero) STUB(8f, zero) + +STUB(90, zero) STUB(91, zero) STUB(92, zero) STUB(93, zero) +STUB(94, zero) STUB(95, zero) STUB(96, zero) STUB(97, zero) +STUB(98, zero) STUB(99, zero) STUB(9a, zero) STUB(9b, zero) +STUB(9c, zero) STUB(9d, zero) STUB(9e, zero) STUB(9f, zero) + +STUB(a0, zero) STUB(a1, zero) STUB(a2, zero) STUB(a3, zero) +STUB(a4, zero) STUB(a5, zero) STUB(a6, zero) STUB(a7, zero) +STUB(a8, zero) STUB(a9, zero) STUB(aa, zero) STUB(ab, zero) +STUB(ac, zero) STUB(ad, zero) STUB(ae, zero) STUB(af, zero) + +STUB(b0, zero) STUB(b1, zero) STUB(b2, zero) STUB(b3, zero) +STUB(b4, zero) STUB(b5, zero) STUB(b6, zero) STUB(b7, zero) +STUB(b8, zero) STUB(b9, zero) STUB(ba, zero) STUB(bb, zero) +STUB(bc, zero) STUB(bd, zero) STUB(be, zero) STUB(bf, zero) + +STUB(c0, zero) STUB(c1, zero) STUB(c2, zero) STUB(c3, zero) +STUB(c4, zero) STUB(c5, zero) STUB(c6, zero) STUB(c7, zero) +STUB(c8, zero) STUB(c9, zero) STUB(ca, zero) STUB(cb, zero) +STUB(cc, zero) STUB(cd, zero) STUB(ce, zero) STUB(cf, zero) + +STUB(d0, zero) STUB(d1, zero) STUB(d2, zero) STUB(d3, zero) +STUB(d4, zero) STUB(d5, zero) STUB(d6, zero) STUB(d7, zero) +STUB(d8, zero) STUB(d9, zero) STUB(da, zero) STUB(db, zero) +STUB(dc, zero) STUB(dd, zero) STUB(de, zero) STUB(df, zero) + +STUB(e0, zero) STUB(e1, zero) STUB(e2, zero) STUB(e3, zero) +STUB(e4, zero) STUB(e5, zero) STUB(e6, zero) STUB(e7, zero) +STUB(e8, zero) STUB(e9, zero) STUB(ea, zero) STUB(eb, zero) +STUB(ec, zero) STUB(ed, zero) STUB(ee, zero) STUB(ef, zero) + +STUB(f0, zero) STUB(f1, zero) STUB(f2, zero) STUB(f3, zero) +STUB(f4, zero) STUB(f5, zero) STUB(f6, zero) STUB(f7, zero) +STUB(f8, zero) STUB(f9, zero) STUB(fa, zero) STUB(fb, zero) +STUB(fc, zero) STUB(fd, zero) STUB(fe, zero) STUB(ff, zero) diff --git a/src/threads/intr-stubs.h b/src/threads/intr-stubs.h index 116b637..9ceba15 100644 --- a/src/threads/intr-stubs.h +++ b/src/threads/intr-stubs.h @@ -4,10 +4,9 @@ /* Interrupt stubs. These are little snippets of code in intr-stubs.S, one for - each of the 256 possible x86 interrupts. They just push the - interrupt vector number on the stack (and, for interrupts that - don't have an error code, a fake error code), then jump to - intr_entry(). + each of the 256 possible x86 interrupts. Each one does a + little bit of stack manipulation, then jumps to intr_entry(). + See intr-stubs.S for more information. This array points to each of the interrupt stub entry points so that intr_init() can easily find them. */ diff --git a/src/threads/intr-stubs.pl b/src/threads/intr-stubs.pl deleted file mode 100755 index 75cde7b..0000000 --- a/src/threads/intr-stubs.pl +++ /dev/null @@ -1,66 +0,0 @@ -#! /usr/bin/perl - -print <<'EOF'; -#include "threads/loader.h" - - .data - .intel_syntax noprefix -.globl intr_stubs -intr_stubs: -EOF - -for $i (0...255) { - $x = sprintf ("%02x", $i); - print "\t.long intr${x}_stub\n"; -} - -print <<'EOF'; - - .text -EOF - -for $i (0...255) { - $x = sprintf ("%02x", $i); - print ".globl intr${x}_stub\n"; - print "intr${x}_stub:\n"; - print "\tpush 0\n" - if ($i != 8 && $i != 10 && $i != 11 - && $i != 13 && $i != 14 && $i != 17); - print "\tpush 0x$x\n"; - print "\tjmp intr_entry\n"; -} - -print <<'EOF'; -intr_entry: - # Save caller's registers. - push ds - push es - push fs - push gs - pusha - - # Set up kernel environment. - cld - mov eax, SEL_KDSEG - mov ds, eax - mov es, eax - - # Call interrupt handler. - push esp -.globl intr_handler - call intr_handler - add esp, 4 - -.globl intr_exit -intr_exit: - # Restore caller's registers. - popa - pop gs - pop fs - pop es - pop ds - add esp, 8 - - # Return to caller. - iret -EOF diff --git a/src/userprog/process.c b/src/userprog/process.c index c0a6547..88c9d8d 100644 --- a/src/userprog/process.c +++ b/src/userprog/process.c @@ -73,7 +73,7 @@ execute_thread (void *filename_) /* Start the user process by simulating a return from an interrupt, implemented by intr_exit (in - threads/intr-stubs.pl). Because intr_exit takes all of its + threads/intr-stubs.S). Because intr_exit takes all of its arguments on the stack in the form of a `struct intr_frame', we just point the stack pointer (%esp) to our stack frame and jump to it. */ -- 2.30.2