# 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.
# 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
+++ /dev/null
-#include "gdt.h"
-#include "debug.h"
-#include "mmu.h"
-#include "palloc.h"
-#include "tss.h"
-
-/* The Global Descriptor Table (GDT).
-
- The GDT, an x86-specific structure, defines segments that can
- potentially be used by all processes in a system, subject to
- their permissions. There is also a per-process Local
- Descriptor Table (LDT) but that is not used by modern
- operating systems.
-
- Each entry in the GDT, which is known by its byte offset in
- the table, identifies a segment. For our purposes only three
- types of segments are of interest: code, data, and TSS or
- Task-State Segment descriptors. The former two types are
- exactly what they sound like. The TSS is used primarily for
- stack switching on interrupts.
-
- For more information on the GDT as used here, refer to
- [IA32-v3] sections 3.2 through 3.5. */
-static uint64_t gdt[SEL_CNT];
-
-/* Creating descriptors. */
-static uint64_t make_code_desc (int dpl);
-static uint64_t make_data_desc (int dpl);
-static uint64_t make_tss_desc (void *laddr);
-
-/* Sets up a proper GDT. The bootstrap loader's GDT didn't
- include user-mode selectors or a TSS, but we need both now. */
-void
-gdt_init (void)
-{
- uint64_t gdtr_operand;
-
- /* Initialize GDT. */
- gdt[SEL_NULL / sizeof *gdt] = 0;
- gdt[SEL_KCSEG / sizeof *gdt] = make_code_desc (0);
- gdt[SEL_KDSEG / sizeof *gdt] = make_data_desc (0);
- gdt[SEL_UCSEG / sizeof *gdt] = make_code_desc (3);
- gdt[SEL_UDSEG / sizeof *gdt] = make_data_desc (3);
- gdt[SEL_TSS / sizeof *gdt] = make_tss_desc (tss_get ());
-
- /* Load GDTR, TR. */
- gdtr_operand = make_dtr_operand (sizeof gdt - 1, gdt);
- asm volatile ("lgdt %0" :: "m" (gdtr_operand));
- asm volatile ("ltr %w0" :: "r" (SEL_TSS));
-}
-\f
-/* System segment or code/data segment? */
-enum seg_class
- {
- CLS_SYSTEM = 0, /* System segment. */
- CLS_CODE_DATA = 1 /* Code or data segment. */
- };
-
-/* Limit has byte or 4 kB page granularity? */
-enum seg_granularity
- {
- GRAN_BYTE = 0, /* Limit has 1-byte granularity. */
- GRAN_PAGE = 1 /* Limit has 4 kB granularity. */
- };
-
-/* Returns a segment descriptor with the given 32-bit BASE and
- 20-bit LIMIT (whose interpretation depends on GRANULARITY).
- The descriptor represents a system or code/data segment
- according to CLASS, and TYPE is its type (whose interpretation
- depends on the class).
-
- The segment has descriptor privilege level DPL, meaning that
- it can be used in rings numbered DPL or lower. In practice,
- DPL==3 means that user processes can use the segment and
- DPL==0 means that only the kernel can use the segment. See
- [IA32-v3] section 4.5 for further discussion. */
-static uint64_t
-make_seg_desc (uint32_t base,
- uint32_t limit,
- enum seg_class class,
- int type,
- int dpl,
- enum seg_granularity granularity)
-{
- uint32_t e0, e1;
-
- ASSERT (limit <= 0xfffff);
- ASSERT (class == CLS_SYSTEM || class == CLS_CODE_DATA);
- ASSERT (type >= 0 && type <= 15);
- ASSERT (dpl >= 0 && dpl <= 3);
- ASSERT (granularity == GRAN_BYTE || granularity == GRAN_PAGE);
-
- e0 = ((limit & 0xffff) /* Limit 15:0. */
- | (base << 16)); /* Base 15:0. */
-
- e1 = (((base >> 16) & 0xff) /* Base 23:16. */
- | (type << 8) /* Segment type. */
- | (class << 12) /* 0=system, 1=code/data. */
- | (dpl << 13) /* Descriptor privilege. */
- | (1 << 15) /* Present. */
- | (limit & 0xf0000) /* Limit 16:19. */
- | (1 << 22) /* 32-bit segment. */
- | (granularity << 23) /* Byte/page granularity. */
- | (base & 0xff000000)); /* Base 31:24. */
-
- return e0 | ((uint64_t) e1 << 32);
-}
-
-/* Returns a descriptor for a readable code segment with base at
- 0, a limit of 4 GB, and the given DPL. */
-static uint64_t
-make_code_desc (int dpl)
-{
- return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 10, dpl, GRAN_PAGE);
-}
-
-/* Returns a descriptor for a writable data segment with base at
- 0, a limit of 4 GB, and the given DPL. */
-static uint64_t
-make_data_desc (int dpl)
-{
- return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 2, dpl, GRAN_PAGE);
-}
-
-/* Returns a descriptor for an "available" 32-bit Task-State
- Segment with its base at the given linear address, a limit of
- 0x67 bytes (the size of a 32-bit TSS), and a DPL of 0. */
-static uint64_t
-make_tss_desc (void *laddr)
-{
- return make_seg_desc ((uint32_t) laddr, 0x67, CLS_SYSTEM, 9, 0, GRAN_BYTE);
-}
-
+++ /dev/null
-#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 <stdint.h>
-
-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 */
#include <stddef.h>
#include <limits.h>
#include "debug.h"
-#include "gdt.h"
#include "interrupt.h"
#include "io.h"
#include "kbd.h"
#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"
/* 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. */
intr_init ();
timer_init ();
kbd_init ();
+#ifdef USERPROG
+ exception_init ();
+#endif
/* Do everything else in a system thread. */
thread_init ();
thread_execute (initial_program);
else
PANIC ("no initial program specified");
+#else
+ PANIC ("boot successful");
#endif
}
\f
" -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]);
#include <stdint.h>
#include "intr-stubs.h"
#include "debug.h"
-#include "gdt.h"
#include "io.h"
#include "lib.h"
#include "mmu.h"
/* 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;
\f
/* Returns the current interrupt status. */
enum intr_level
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
{
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);
+}
\f
/* 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);
/* 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)
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));
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];
+}
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 */
#! /usr/bin/perl
print <<'EOF';
-#include "gdt.h"
+#include "loader.h"
.data
.globl intr_stubs
#include "loader.h"
#include "mmu.h"
-#include "gdt.h"
##############################################################################
# Kernel loader.
/* 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 */
#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
+++ /dev/null
-#include "tss.h"
-#include <stddef.h>
-#include "debug.h"
-#include "gdt.h"
-#include "mmu.h"
-#include "palloc.h"
-
-struct tss
- {
- uint16_t back_link, :16;
- void *esp0;
- uint16_t ss0, :16;
- void *esp1;
- uint16_t ss1, :16;
- void *esp2;
- uint16_t ss2, :16;
- uint32_t cr3;
- void (*eip) (void);
- uint32_t eflags;
- uint32_t eax, ecx, edx, ebx;
- uint32_t esp, ebp, esi, edi;
- uint16_t es, :16;
- uint16_t cs, :16;
- uint16_t ss, :16;
- uint16_t ds, :16;
- uint16_t fs, :16;
- uint16_t gs, :16;
- uint16_t ldt, :16;
- uint16_t trace, bitmap;
- };
-
-static struct tss *tss;
-
-void
-tss_init (void)
-{
- /* Our TSS is never used in a call gate or task gate, so only a
- few fields of it are ever referenced, and those are the only
- ones we initialize. */
- tss = palloc_get (PAL_ASSERT | PAL_ZERO);
- tss->esp0 = ptov(0x20000);
- tss->ss0 = SEL_KDSEG;
- tss->bitmap = 0xdfff;
-}
-
-struct tss *
-tss_get (void)
-{
- ASSERT (tss != NULL);
- return tss;
-}
-
-void
-tss_set_esp0 (uint8_t *esp0)
-{
- ASSERT (tss != NULL);
- tss->esp0 = esp0;
-}
+++ /dev/null
-#ifndef HEADER_TSS_H
-#define HEADER_TSS_H
-
-#include <stdint.h>
-
-struct tss;
-void tss_init (void);
-struct tss *tss_get (void);
-void tss_set_esp0 (uint8_t *);
-
-#endif /* tss.h */
--- /dev/null
+#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 ();
+ }
+}
+
--- /dev/null
+#ifndef HEADER_EXCEPTION_H
+#define HEADER_EXCEPTION_H 1
+
+void exception_init (void);
+
+#endif /* exception.h */
--- /dev/null
+#include "gdt.h"
+#include "debug.h"
+#include "mmu.h"
+#include "palloc.h"
+#include "tss.h"
+
+/* The Global Descriptor Table (GDT).
+
+ The GDT, an x86-specific structure, defines segments that can
+ potentially be used by all processes in a system, subject to
+ their permissions. There is also a per-process Local
+ Descriptor Table (LDT) but that is not used by modern
+ operating systems.
+
+ Each entry in the GDT, which is known by its byte offset in
+ the table, identifies a segment. For our purposes only three
+ types of segments are of interest: code, data, and TSS or
+ Task-State Segment descriptors. The former two types are
+ exactly what they sound like. The TSS is used primarily for
+ stack switching on interrupts.
+
+ For more information on the GDT as used here, refer to
+ [IA32-v3] sections 3.2 through 3.5. */
+static uint64_t gdt[SEL_CNT];
+
+/* 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. */
+void
+gdt_init (void)
+{
+ uint64_t gdtr_operand;
+
+ /* Initialize GDT. */
+ gdt[SEL_NULL / sizeof *gdt] = 0;
+ gdt[SEL_KCSEG / sizeof *gdt] = make_code_desc (0);
+ gdt[SEL_KDSEG / sizeof *gdt] = make_data_desc (0);
+ gdt[SEL_UCSEG / sizeof *gdt] = make_code_desc (3);
+ gdt[SEL_UDSEG / sizeof *gdt] = make_data_desc (3);
+ gdt[SEL_TSS / sizeof *gdt] = make_tss_desc (tss_get ());
+
+ /* Load GDTR, TR. */
+ gdtr_operand = make_gdtr_operand (sizeof gdt - 1, gdt);
+ asm volatile ("lgdt %0" :: "m" (gdtr_operand));
+ asm volatile ("ltr %w0" :: "r" (SEL_TSS));
+}
+\f
+/* System segment or code/data segment? */
+enum seg_class
+ {
+ CLS_SYSTEM = 0, /* System segment. */
+ CLS_CODE_DATA = 1 /* Code or data segment. */
+ };
+
+/* Limit has byte or 4 kB page granularity? */
+enum seg_granularity
+ {
+ GRAN_BYTE = 0, /* Limit has 1-byte granularity. */
+ GRAN_PAGE = 1 /* Limit has 4 kB granularity. */
+ };
+
+/* Returns a segment descriptor with the given 32-bit BASE and
+ 20-bit LIMIT (whose interpretation depends on GRANULARITY).
+ The descriptor represents a system or code/data segment
+ according to CLASS, and TYPE is its type (whose interpretation
+ depends on the class).
+
+ The segment has descriptor privilege level DPL, meaning that
+ it can be used in rings numbered DPL or lower. In practice,
+ DPL==3 means that user processes can use the segment and
+ DPL==0 means that only the kernel can use the segment. See
+ [IA32-v3] section 4.5 for further discussion. */
+static uint64_t
+make_seg_desc (uint32_t base,
+ uint32_t limit,
+ enum seg_class class,
+ int type,
+ int dpl,
+ enum seg_granularity granularity)
+{
+ uint32_t e0, e1;
+
+ ASSERT (limit <= 0xfffff);
+ ASSERT (class == CLS_SYSTEM || class == CLS_CODE_DATA);
+ ASSERT (type >= 0 && type <= 15);
+ ASSERT (dpl >= 0 && dpl <= 3);
+ ASSERT (granularity == GRAN_BYTE || granularity == GRAN_PAGE);
+
+ e0 = ((limit & 0xffff) /* Limit 15:0. */
+ | (base << 16)); /* Base 15:0. */
+
+ e1 = (((base >> 16) & 0xff) /* Base 23:16. */
+ | (type << 8) /* Segment type. */
+ | (class << 12) /* 0=system, 1=code/data. */
+ | (dpl << 13) /* Descriptor privilege. */
+ | (1 << 15) /* Present. */
+ | (limit & 0xf0000) /* Limit 16:19. */
+ | (1 << 22) /* 32-bit segment. */
+ | (granularity << 23) /* Byte/page granularity. */
+ | (base & 0xff000000)); /* Base 31:24. */
+
+ return e0 | ((uint64_t) e1 << 32);
+}
+
+/* Returns a descriptor for a readable code segment with base at
+ 0, a limit of 4 GB, and the given DPL. */
+static uint64_t
+make_code_desc (int dpl)
+{
+ return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 10, dpl, GRAN_PAGE);
+}
+
+/* Returns a descriptor for a writable data segment with base at
+ 0, a limit of 4 GB, and the given DPL. */
+static uint64_t
+make_data_desc (int dpl)
+{
+ return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 2, dpl, GRAN_PAGE);
+}
+
+/* Returns a descriptor for an "available" 32-bit Task-State
+ Segment with its base at the given linear address, a limit of
+ 0x67 bytes (the size of a 32-bit TSS), and a DPL of 0. */
+static uint64_t
+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);
+}
--- /dev/null
+#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 */
--- /dev/null
+#include "tss.h"
+#include <stddef.h>
+#include "debug.h"
+#include "gdt.h"
+#include "mmu.h"
+#include "palloc.h"
+
+struct tss
+ {
+ uint16_t back_link, :16;
+ void *esp0;
+ uint16_t ss0, :16;
+ void *esp1;
+ uint16_t ss1, :16;
+ void *esp2;
+ uint16_t ss2, :16;
+ uint32_t cr3;
+ void (*eip) (void);
+ uint32_t eflags;
+ uint32_t eax, ecx, edx, ebx;
+ uint32_t esp, ebp, esi, edi;
+ uint16_t es, :16;
+ uint16_t cs, :16;
+ uint16_t ss, :16;
+ uint16_t ds, :16;
+ uint16_t fs, :16;
+ uint16_t gs, :16;
+ uint16_t ldt, :16;
+ uint16_t trace, bitmap;
+ };
+
+static struct tss *tss;
+
+void
+tss_init (void)
+{
+ /* Our TSS is never used in a call gate or task gate, so only a
+ few fields of it are ever referenced, and those are the only
+ ones we initialize. */
+ tss = palloc_get (PAL_ASSERT | PAL_ZERO);
+ tss->esp0 = ptov(0x20000);
+ tss->ss0 = SEL_KDSEG;
+ tss->bitmap = 0xdfff;
+}
+
+struct tss *
+tss_get (void)
+{
+ ASSERT (tss != NULL);
+ return tss;
+}
+
+void
+tss_set_esp0 (uint8_t *esp0)
+{
+ ASSERT (tss != NULL);
+ tss->esp0 = esp0;
+}
--- /dev/null
+#ifndef HEADER_TSS_H
+#define HEADER_TSS_H
+
+#include <stdint.h>
+
+struct tss;
+void tss_init (void);
+struct tss *tss_get (void);
+void tss_set_esp0 (uint8_t *);
+
+#endif /* tss.h */