SMP Booting, APIC, and IRQs
authorBarret Rhoden <brho@cs.berkeley.edu>
Mon, 23 Mar 2009 06:01:01 +0000 (23:01 -0700)
committerBarret Rhoden <brho@cs.berkeley.edu>
Mon, 23 Mar 2009 06:01:01 +0000 (23:01 -0700)
Lots of things in this, would have liked to split it up, but they are
rather woven together.  Primitive SMP booting, some basic APIC work,
basic IRQ work, and other x86 infrastructure.  Backtrace works too.

13 files changed:
inc/x86.h
kern/Makefrag
kern/apic.c [new file with mode: 0644]
kern/apic.h [new file with mode: 0644]
kern/entry.S
kern/env.c
kern/init.c
kern/pmap.c
kern/pmap.h
kern/smp_entry.S [new file with mode: 0644]
kern/trap.c
kern/trapentry.S
user/evilhello.c

index 689ef7a..2387e2f 100644 (file)
--- a/inc/x86.h
+++ b/inc/x86.h
@@ -46,6 +46,8 @@ static __inline void cpuid(uint32_t info, uint32_t *eaxp, uint32_t *ebxp, uint32
 static __inline uint64_t read_tsc(void) __attribute__((always_inline));
 static __inline uint64_t read_msr(uint32_t reg) __attribute__((always_inline));
 static __inline void write_msr(uint32_t reg, uint64_t val) __attribute__((always_inline));
+static __inline uint32_t read_mmreg32(uint32_t reg) __attribute__((always_inline));
+static __inline void write_mmreg32(uint32_t reg, uint32_t val) __attribute__((always_inline));
 
 static __inline void
 breakpoint(void)
@@ -287,19 +289,33 @@ read_tsc(void)
 }
 
 // Might need to mfence rdmsr.  supposedly wrmsr serializes, but not for x2APIC
-static __inline uint64_t 
+static __inline uint64_t
 read_msr(uint32_t reg)
 {
        uint32_t edx, eax;
-       asm volatile("rdmsr" : "=d"(edx), "=a"(eax) : "c"(reg));
+       asm volatile("rdmsr; mfence" : "=d"(edx), "=a"(eax) : "c"(reg));
        return (uint64_t)edx << 32 | eax;
 }
 
-static __inline void 
+static __inline void
 write_msr(uint32_t reg, uint64_t val)
 {
        asm volatile("wrmsr" : : "d"((uint32_t)(val >> 32)),
                                 "a"((uint32_t)(val & 0xFFFFFFFF)), 
                                 "c"(reg));
 }
+
+static __inline void
+write_mmreg32(uint32_t reg, uint32_t val)
+{
+       {TRUSTEDBLOCK *((volatile uint32_t*)reg) = val; }
+       //the C ends up producing better asm than this:
+       //asm volatile("movl %0, (%1)" : : "r"(val), "r"(reg));
+}
+
+static __inline uint32_t
+read_mmreg32(uint32_t reg)
+{
+       {TRUSTEDBLOCK return *((volatile uint32_t*)reg); }
+}
 #endif /* !JOS_INC_X86_H */
index 3b94979..d811522 100644 (file)
@@ -15,6 +15,7 @@ KERN_LDFLAGS := $(LDFLAGS) -Lobj/ivylib -T kern/kernel.ld -nostdlib
 # We also snatch the use of a couple handy source files
 # from the lib directory, to avoid gratuitous code duplication.
 KERN_SRCFILES :=       kern/entry.S \
+                       kern/smp_entry.S \
                        kern/init.c \
                        kern/console.c \
                        kern/monitor.c \
@@ -28,6 +29,7 @@ KERN_SRCFILES :=      kern/entry.S \
                        kern/sched.c \
                        kern/syscall.c \
                        kern/kdebug.c \
+                       kern/apic.c \
                        lib/printfmt.c \
                        lib/readline.c \
                        lib/string.c
diff --git a/kern/apic.c b/kern/apic.c
new file mode 100644 (file)
index 0000000..d8ba292
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2009 The Regents of the University of California
+ * See LICENSE for details.
+ */
+
+#include <inc/mmu.h>
+#include <inc/x86.h>
+
+#include <kern/apic.h>
+
+/*
+ * Remaps the Programmable Interrupt Controller to use IRQs 32-47
+ * http://wiki.osdev.org/PIC
+ */
+void remap_pic() 
+{
+       // start initialization
+       outb(PIC1_CMD, 0x11);
+       outb(PIC2_CMD, 0x11);
+       // set new offsets
+       outb(PIC1_DATA, PIC1_OFFSET);
+       outb(PIC2_DATA, PIC2_OFFSET);
+       // set up cascading
+       outb(PIC1_DATA, 0x04);
+       outb(PIC2_DATA, 0x02);
+       // other stuff (put in 8086/88 mode, or whatever)
+       outb(PIC1_DATA, 0x01);
+       outb(PIC2_DATA, 0x01);
+       // set masks, defaulting to all unmasked for now
+       outb(PIC1_DATA, 0x0);
+       outb(PIC2_DATA, 0x0);
+}
+
+/*
+ * Sets the LAPIC timer to go off after a certain number of ticks.  The primary
+ * clock freq is actually the bus clock, so we really will need to figure out
+ * the timing of the LAPIC timer via other timing.  For now, set it to a
+ * certain number of ticks, and specify an interrupt vector to send to the CPU.
+ * Unmasking is implied.  Ref SDM, 3A, 9.6.4
+ */
+void lapic_set_timer(uint32_t ticks, uint8_t vector, bool periodic)
+{
+       // divide the bus clock.  going with the max (128) for now (which is slow)
+       write_mmreg32(LAPIC_TIMER_DIVIDE, 0xa);
+       // set LVT with interrupt handling information
+       write_mmreg32(LAPIC_LVT_TIMER, vector | (periodic << 17));
+       write_mmreg32(LAPIC_TIMER_INIT, ticks);
+       // For debugging when we expand this
+       //cprintf("LAPIC Init Count: 0x%08x\n", read_mmreg32(LAPIC_TIMER_INIT));
+       //cprintf("LAPIC Current Count: 0x%08x\n", read_mmreg32(LAPIC_TIMER_CURRENT));
+}
+
+uint32_t lapic_get_default_id(void)
+{
+       uint32_t ebx;
+       cpuid(1, 0, &ebx, 0, 0);
+       // p6 family only uses 4 bits here, and 0xf is reserved for the IOAPIC
+       return (ebx & 0xFF000000) >> 24;
+}
diff --git a/kern/apic.h b/kern/apic.h
new file mode 100644 (file)
index 0000000..8555abd
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 2009 The Regents of the University of California
+ * See LICENSE for details.
+ */
+
+#ifndef ROS_KERN_APIC_H
+#define ROS_KERN_APIC_H
+
+/* 
+ * Functions and definitions for dealing with the APIC and PIC, specific to
+ * Intel.  Does not handle an x2APIC.
+ */
+
+#include <inc/mmu.h>
+#include <inc/x86.h>
+
+// PIC
+#define PIC1_CMD                                       0x20
+#define PIC1_DATA                                      0x21
+#define PIC2_CMD                                       0xA0
+#define PIC2_DATA                                      0xA1
+// These are also hardcoded into the IRQ_HANDLERs of kern/trapentry.S
+#define PIC1_OFFSET                                    0x20
+#define PIC2_OFFSET                                    0x28
+#define PIC_EOI                                                0x20
+
+// Local APIC
+#define LAPIC_BASE                                     0xfee00000 // this is the default, can be changed
+#define LAPIC_EOI                                      (LAPIC_BASE + 0x0b0)
+#define LAPIC_SPURIOUS                         (LAPIC_BASE + 0x0f0)
+#define LAPIC_VERSION                          (LAPIC_BASE + 0x030)
+#define LAPIC_ERROR                                    (LAPIC_BASE + 0x280)
+#define LAPIC_ID                                       (LAPIC_BASE + 0x020)
+// LAPIC Local Vector Table
+#define LAPIC_LVT_TIMER                                (LAPIC_BASE + 0x320)
+#define LAPIC_LVT_LINT0                                (LAPIC_BASE + 0x350)
+#define LAPIC_LVT_LINT1                                (LAPIC_BASE + 0x360)
+#define LAPIC_LVT_ERROR                                (LAPIC_BASE + 0x370)
+#define LAPIC_LVT_PERFMON                      (LAPIC_BASE + 0x340)
+#define LAPIC_LVT_THERMAL                      (LAPIC_BASE + 0x330)
+#define LAPIC_LVT_MASK                         0x00010000
+// LAPIC Timer
+#define LAPIC_TIMER_INIT                       (LAPIC_BASE + 0x380)
+#define LAPIC_TIMER_CURRENT                    (LAPIC_BASE + 0x390)
+#define LAPIC_TIMER_DIVIDE                     (LAPIC_BASE + 0x3e0)
+// IPI Interrupt Command Register
+#define LAPIC_IPI_ICR_LOWER                    (LAPIC_BASE + 0x300)
+#define LAPIC_IPI_ICR_UPPER                    (LAPIC_BASE + 0x310)
+
+// IOAPIC
+#define IOAPIC_BASE                                    0xfec00000 // this is the default, can be changed
+
+void remap_pic(void);
+void lapic_set_timer(uint32_t ticks, uint8_t vector, bool periodic);
+uint32_t lapic_get_default_id(void);
+
+static inline void pic_send_eoi(uint32_t irq);
+static inline void lapic_send_eoi(void);
+static inline uint32_t lapic_get_version(void);
+static inline uint32_t lapic_get_error(void);
+static inline uint32_t lapic_get_id(void);
+static inline void lapic_disable(void);
+static inline void lapic_enable(void);
+static inline void lapic_wait_to_send(void);
+static inline void send_init_ipi(void);
+static inline void send_startup_ipi(uint8_t vector);
+static inline void send_self_ipi(uint8_t vector);
+static inline void send_broadcast_ipi(uint8_t vector);
+static inline void send_all_others_ipi(uint8_t vector);
+static inline void send_ipi(uint8_t dest, bool logical_mode, uint8_t vector);
+
+#define mask_lapic_lvt(entry) \
+       write_mmreg32(entry, read_mmreg32(entry) | LAPIC_LVT_MASK)
+#define unmask_lapic_lvt(entry) \
+       write_mmreg32(entry, read_mmreg32(entry) & ~LAPIC_LVT_MASK)
+
+static inline void pic_send_eoi(uint32_t irq)
+{
+       // all irqs beyond the first seven need to be chained to the slave
+       if (irq > 7)
+               outb(PIC2_CMD, PIC_EOI);
+       outb(PIC1_CMD, PIC_EOI);
+}
+
+static inline void lapic_send_eoi(void)
+{
+       write_mmreg32(LAPIC_EOI, 0);
+}
+
+static inline uint32_t lapic_get_version(void)
+{
+       return read_mmreg32(LAPIC_VERSION);     
+}
+
+static inline uint32_t lapic_get_error(void)
+{
+       write_mmreg32(LAPIC_ERROR, 0xdeadbeef);
+       return read_mmreg32(LAPIC_ERROR);
+}
+
+static inline uint32_t lapic_get_id(void)
+{
+       return read_mmreg32(LAPIC_ID) >> 24;
+}
+
+/* There are a couple ways to do it.  The MSR route doesn't seem to work
+ * in KVM.  It's also a somewhat permanent thing
+ */
+static inline void lapic_disable(void)
+{
+       write_mmreg32(LAPIC_SPURIOUS, read_mmreg32(LAPIC_SPURIOUS) & 0xffffefff);
+       //write_msr(IA32_APIC_BASE, read_msr(IA32_APIC_BASE) & ~MSR_APIC_ENABLE);
+}
+
+/* Spins until previous IPIs are delivered.  Not sure if we want it inlined
+ * Also not sure when we really need to do this. 
+ */
+static inline void lapic_wait_to_send(void)
+{
+       while(read_mmreg32(LAPIC_IPI_ICR_LOWER) & 0x1000)
+               asm volatile("pause");
+}
+
+static inline void lapic_enable(void)
+{
+       write_mmreg32(LAPIC_SPURIOUS, read_mmreg32(LAPIC_SPURIOUS) | 0x00001000);
+}
+
+static inline void send_init_ipi(void)
+{
+       write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x000c4500);
+}
+
+static inline void send_startup_ipi(uint8_t vector)
+{
+       write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x000c4600 | vector);
+}
+
+static inline void send_self_ipi(uint8_t vector)
+{
+       write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x00044000 | vector);
+}
+
+static inline void send_broadcast_ipi(uint8_t vector)
+{
+       write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x00084000 | vector);
+}
+
+static inline void send_all_others_ipi(uint8_t vector)
+{
+       write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x000c4000 | vector);
+}
+
+static inline void send_ipi(uint8_t dest, bool logical_mode, uint8_t vector)
+{
+       write_mmreg32(LAPIC_IPI_ICR_UPPER, dest << 24);
+       write_mmreg32(LAPIC_IPI_ICR_LOWER, 0x00004000 | (logical_mode << 11) | vector);
+}
+
+/* To change the LAPIC Base (not recommended):
+       msr_val = read_msr(IA32_APIC_BASE);
+       msr_val = msr_val & ~MSR_APIC_BASE_ADDRESS | 0xfaa00000;
+       write_msr(IA32_APIC_BASE, msr_val);
+*/
+#endif /* ROS_KERN_APIC_H */
index ef4063a..7416408 100644 (file)
@@ -73,12 +73,12 @@ relocated:
        push    %ebx
 
        # now to C code
+       movl    $0x1,num_cpus           # init global var, for now
        call    kernel_init
 
        # Should never get here, but in case we do, just spin.
 spin:  jmp     spin
 
-
 ###################################################################    
 # See <inc/memlayout.h> for a complete description of these two symbols.
 ###################################################################
index 1c7257c..ea4b100 100644 (file)
@@ -192,6 +192,8 @@ env_alloc(struct Env **newenv_store, envid_t parent_id)
        e->env_tf.tf_esp = USTACKTOP;
        e->env_tf.tf_cs = GD_UT | 3;
        // You will set e->env_tf.tf_eip later.
+       // set the env's EFLAGSs to have interrupts enabled
+       e->env_tf.tf_eflags |= 0x00000200; // bit 9 is the interrupts-enabled
 
        // commit the allocation
        LIST_REMOVE(e, env_link);
index e4b72f1..859949a 100644 (file)
 #include <kern/kclock.h>
 #include <kern/env.h>
 #include <kern/trap.h>
+#include <kern/apic.h>
 
-void print_cpuinfo(void);
+volatile bool waiting = 1;
+volatile uint8_t num_cpus = 0xee;
+uintptr_t smp_stack_top;
+volatile bool smp_boot_lock = 0;
+
+static void print_cpuinfo(void);
+void smp_boot(void);
 
 void kernel_init(multiboot_info_t *mboot_info)
 {
@@ -45,12 +52,9 @@ void kernel_init(multiboot_info_t *mboot_info)
        env_init();
        idt_init();
 
-       // Temporary test code specific to LAB 3
-#if defined(TEST)
-       // Don't touch -- used by grading script!
-       ENV_CREATE2(TEST, TESTSIZE);
-#else
-       // Touch all you want.
+       // this returns when all other cores are done and ready to receive IPIs
+       smp_boot();
+
        //ENV_CREATE(user_faultread);
        //ENV_CREATE(user_faultreadkernel);
        //ENV_CREATE(user_faultwrite);
@@ -58,15 +62,74 @@ void kernel_init(multiboot_info_t *mboot_info)
        //ENV_CREATE(user_breakpoint);
        //ENV_CREATE(user_badsegment);
        //ENV_CREATE(user_divzero);
-       ENV_CREATE(user_hello);
        //ENV_CREATE(user_buggyhello);
+       ENV_CREATE(user_hello);
        //ENV_CREATE(user_evilhello);
-#endif // TEST*
 
        // We only have one user environment for now, so just run it.
        env_run(&envs[0]);
 }
 
+void smp_boot(void)
+{
+       struct Page* smp_stack;
+       // NEED TO GRAB A LOWMEM FREE PAGE FOR AP BOOTUP CODE
+       // page1 (2nd page) is reserved, hardcoded in pmap.c
+       extern smp_entry(), smp_entry_end();
+       memset(KADDR(0x00001000), 0, PGSIZE);           
+       memcpy(KADDR(0x00001000), &smp_entry, &smp_entry_end - &smp_entry);             
+
+       // This mapping allows access with paging on and off
+       page_insert(boot_pgdir, pa2page(0x00001000), (void*)0x00001000, PTE_W);
+
+       // Allocate a stack for the cores starting up.  One for all, must share
+       if (page_alloc(&smp_stack))
+               panic("No memory for SMP boot stack!");
+       smp_stack_top = (uintptr_t)(page2kva(smp_stack) + PGSIZE - SIZEOF_STRUCT_TRAPFRAME);
+
+       // set up the local APIC timer to fire 0x21 once.  hardcoded to break
+       // out of the spinloop on waiting.  really just want to wait a little
+       lapic_set_timer(0xffffffff, 0x21, 0);
+       cprintf("Num_Cpus: %d\n", num_cpus);
+       send_init_ipi();
+       asm volatile("sti"); // LAPIC timer will fire, extINTs are blocked at LINT0 now
+       while (waiting); // gets set in the lapic timer
+       send_startup_ipi(0x01);
+       // replace this with something that checks to see if smp_entrys are done
+       while(1); // want other cores to do stuff for now
+       
+       // Remove the mapping of the page used by the trampoline
+       page_remove(boot_pgdir, (void*)0x00001000);
+       // It had a refcount of 2 earlier, so we need to dec once more to free it
+       // TODO - double check that
+       page_decref(pa2page(0x00001000));
+       // Dealloc the temp shared stack
+       page_decref(smp_stack);
+}
+/* 
+ * This is called from smp_entry by each core to finish the core bootstrapping.
+ * There is a spinlock around this entire function in smp_entry, for a few reasons,
+ * the most important being that all cores use the same stack when entering here.
+ */
+void smp_main(void)
+{
+       cprintf("Good morning Vietnam!\n");
+
+       enable_pse();
+    cprintf("This core's Default APIC ID: 0x%08x\n", lapic_get_default_id());
+    cprintf("This core's Current APIC ID: 0x%08x\n", lapic_get_id());
+       
+       if (read_msr(IA32_APIC_BASE) & 0x00000100)
+               cprintf("I am the Boot Strap Processor\n");
+       else
+               cprintf("I am an Application Processor\n");
+       
+       // turn me on!
+       cprintf("Spurious Vector: 0x%08x\n", read_mmreg32(LAPIC_SPURIOUS));
+       cprintf("LINT0: 0x%08x\n", read_mmreg32(LAPIC_LVT_LINT0));
+       cprintf("LINT1: 0x%08x\n", read_mmreg32(LAPIC_LVT_LINT1));
+       cprintf("Num_Cpus: %d\n\n", num_cpus);
+}
 
 /*
  * Variable panicstr contains argument to first call to panic; used as flag
@@ -110,7 +173,7 @@ void _warn(const char *file, int line, const char *fmt,...)
        va_end(ap);
 }
 
-void print_cpuinfo(void) {
+static void print_cpuinfo(void) {
        uint32_t eax, ebx, ecx, edx;
        uint32_t model, family;
        uint64_t msr_val;
@@ -136,7 +199,7 @@ void print_cpuinfo(void) {
        cprintf("Model: %d\n", model);
        cprintf("Stepping: %d\n", eax & 0x0000000F);
        // eventually can fill this out with SDM Vol3B App B info, or 
-       // better yet with stepping info.  
+       // better yet with stepping info.  or cpuid 8000_000{2,3,4}
        switch ( family << 8 | model ) {
                case(0x060f):
                        cprintf("Processor: Core 2 Duo or Similar\n");
@@ -144,74 +207,27 @@ void print_cpuinfo(void) {
                default:
                        cprintf("Unknown or non-Intel CPU\n");
        }
-       if (edx & 0x00000010)
-               cprintf("Model Specific Registers supported\n");
-       else
+       if (!(edx & 0x00000010))
                panic("MSRs not supported!");
-       if (edx & 0x00000100)
-               cprintf("Local APIC Detected\n");
-       else
+       if (!(edx & 0x00001000))
+               panic("MTRRs not supported!");
+       if (!(edx & 0x00000100))
                panic("Local APIC Not Detected!");
-       
+       if (ecx & 0x00200000)
+               cprintf("x2APIC Detected\n");
+       else
+               cprintf("x2APIC Not Detected\n");
        cpuid(0x80000008, &eax, &ebx, &ecx, &edx);
        cprintf("Physical Address Bits: %d\n", eax & 0x000000FF);
-
-
-       /*
+       cprintf("Cores per Die: %d\n", (ecx & 0x000000FF) + 1);
+    cprintf("This core's Default APIC ID: 0x%08x\n", lapic_get_default_id());
        msr_val = read_msr(IA32_APIC_BASE);
-       msr_val & ~MSR_APIC_ENABLE;
-       write_msr(IA32_APIC_BASE, msr_val);
-       if (edx & 0x00000100)
-               cprintf("Local APIC Detected\n");
+       if (msr_val & MSR_APIC_ENABLE)
+               cprintf("Local APIC Enabled\n");
        else
-               panic("Local APIC Not Detected!");
-               */
+               cprintf("Local APIC Disabled\n");
+       if (msr_val & 0x00000100)
+               cprintf("I am the Boot Strap Processor\n");
+       else
+               cprintf("I am an Application Processor\n");
 }
-
-
-       /* Backup of old shit that i hoard for no reason
-        *
-        * all of this was in kernel_init
-       cprintf("6828 decimal is %o octal!\n", 6828);
-       cprintf("Symtab section should begin at: 0x%x \n", stab);
-
-       // double check
-       mboot_info = (multiboot_info_t*)(0xc0000000 + (char*)mboot_info);
-       cprintf("Mboot info address: %p\n", mboot_info);
-       elf_section_header_table_t *elf_head= &(mboot_info->u.elf_sec);
-       cprintf("elf sec info address: %p\n", elf_head);
-    cprintf ("elf_sec: num = %u, size = 0x%x, addr = 0x%x, shndx = 0x%x\n",
-            elf_head->num, elf_head->size,
-            elf_head->addr, elf_head->shndx);
-       
-       struct Secthdr *elf_sym = (struct Secthdr*)(0xc0000000 + elf_head->addr + elf_head->size * 3);
-
-       cprintf("Symtab multiboot struct address: %p\n", elf_sym);
-       cprintf("Symtab multiboot address: %p\n", elf_sym->sh_addr);
-
-       // this walks a symtable, but we don't have one...
-       Elf32_Sym* symtab = (Elf32_Sym*)stab;
-       Elf32_Sym* oldsymtab = symtab;
-       for (; symtab < oldsymtab + 10 ; symtab++) {
-               cprintf("Symbol name index = 0x%x\n", symtab->st_name);
-               //cprintf("Symbol name = %s\n", stabstr + symtab->st_name);
-               cprintf("Symbol vale = 0x%x\n", symtab->st_value);
-       }
-       */
-       /*
-       extern stab_t stab[], estab[];
-       extern char stabstr[];
-       stab_t* symtab;
-       // Spits out the stabs for functions
-       for (symtab = stab; symtab < estab; symtab++) {
-               // gives us only functions.  not really needed if we scan by address
-               if (symtab->n_type != 36)
-                       continue;
-               cprintf("Symbol name = %s\n", stabstr + symtab->n_strx);
-               cprintf("Symbol type = %d\n", symtab->n_type);
-               cprintf("Symbol value = 0x%x\n", symtab->n_value);
-               cprintf("\n");
-       }
-       */
-
-
index b2874af..3bf967f 100644 (file)
@@ -12,6 +12,7 @@
 #include <kern/pmap.h>
 #include <kern/kclock.h>
 #include <kern/env.h>
+#include <kern/apic.h>
 
 // These variables are set by i386_detect_memory()
 static physaddr_t maxpa;       // Maximum physical address
@@ -91,6 +92,19 @@ i386_detect_memory(void)
        cprintf("base = %dK, extended = %dK\n", (int)(basemem/1024), (int)(extmem/1024));
 }
 
+bool enable_pse(void)
+{
+       uint32_t edx, cr4;
+       cpuid(1, 0, 0, 0, &edx);
+       if (edx & CPUID_PSE_SUPPORT) {
+               cr4 = rcr4();
+               cr4 |= CR4_PSE;
+               lcr4(cr4);
+               return 1;
+       } else
+               return 0;
+}
+
 // --------------------------------------------------------------
 // Set up initial memory mappings and turn on MMU.
 // --------------------------------------------------------------
@@ -255,18 +269,9 @@ i386_vm_init(void)
        write_msr(IA32_MTRR_DEF_TYPE, 0x00000c06);
        // might need to set up MTRRS for the IO holes
 
-       // check for PSE support
-       cpuid(1, 0, 0, 0, &edx);
-       pse = edx & CPUID_PSE_SUPPORT;
-
-       // turn on PSE
-       if (pse) {
+       pse = enable_pse();
+       if (pse)
                cprintf("PSE capability detected.\n");
-               uint32_t cr4;
-               cr4 = rcr4();
-               cr4 |= CR4_PSE;
-               lcr4(cr4);
-       }
 
        /*
         * PSE status: 
@@ -341,8 +346,11 @@ i386_vm_init(void)
        else
                boot_map_segment(pgdir, KERNBASE, maxpa, 0, PTE_W );
 
-       // LAPIC mapping, in lieu of MTRRs for now.  TODO: remove when MTRRs are up
-       boot_map_segment(pgdir, (uintptr_t)0xfee00000, PGSIZE, 0xfee00000, PTE_W);
+       // APIC mapping, in lieu of MTRRs for now.  TODO: remove when MTRRs are up
+       // IOAPIC
+       boot_map_segment(pgdir, (uintptr_t)IOAPIC_BASE, PGSIZE, IOAPIC_BASE, PTE_PCD|PTE_W);
+       // Local APIC
+       boot_map_segment(pgdir, (uintptr_t)LAPIC_BASE, PGSIZE, LAPIC_BASE, PTE_PCD|PTE_W);
 
        //////////////////////////////////////////////////////////////////////
        // Make 'pages' point to an array of size 'npage' of 'struct Page'.
@@ -498,7 +506,7 @@ check_boot_pgdir(bool pse)
                case PDX(KSTACKTOP-1):
                case PDX(UPAGES):
                case PDX(UENVS):
-               case PDX(0xfee00000): // LAPIC mapping.  TODO: remove when MTRRs are up
+               case PDX(LAPIC_BASE): // LAPIC mapping.  TODO: remove when MTRRs are up
                        assert(pgdir[i]);
                        break;
                default:
@@ -621,7 +629,10 @@ page_init(void)
        LIST_INIT(&page_free_list);
 
        pages[0].pp_ref = 1;
-       for (i = 1; i < PPN(IOPHYSMEM); i++) {
+       // alloc the second page, since we will need it later to init the other cores
+       // probably need to be smarter about what page we use (make this dynamic) TODO
+       pages[1].pp_ref = 1;
+       for (i = 2; i < PPN(IOPHYSMEM); i++) {
                pages[i].pp_ref = 0;
                LIST_INSERT_HEAD(&page_free_list, &pages[i], pp_link);
        }
@@ -1070,7 +1081,6 @@ page_check(void)
        cprintf("page_check() succeeded!\n");
 }
 
-
 /* 
        // helpful if you want to manually walk with kvm / bochs
        //cprintf("pgdir va = %08p, pgdir pa = %08p\n\n", pgdir, PADDR(pgdir));
index dab44e3..b1c106e 100644 (file)
@@ -25,13 +25,13 @@ struct Env;
 })
 
 /* This macro takes a physical address and returns the corresponding kernel
- * virtual address.  It panics if you pass an invalid physical address. */
+ * virtual address.  It warns if you pass an invalid physical address. */
 #define KADDR(pa)                                              \
 ({                                                             \
        physaddr_t __m_pa = (pa);                               \
        uint32_t __m_ppn = PPN(__m_pa);                         \
        if (__m_ppn >= npage)                                   \
-               panic("KADDR called with invalid pa %08lx", __m_pa);\
+               warn("KADDR called with invalid pa %08lx", __m_pa);\
        (void*) (__m_pa + KERNBASE);                            \
 })
 
@@ -48,8 +48,9 @@ extern pde_t *boot_pgdir;
 extern struct Segdesc (COUNT(6) gdt)[];
 extern struct Pseudodesc gdt_pd;
 
-void   i386_vm_init();
-void   i386_detect_memory();
+void   i386_detect_memory(void);
+bool   enable_pse(void);
+void   i386_vm_init(void);
 
 void   page_init(void);
 void   page_check(void);
diff --git a/kern/smp_entry.S b/kern/smp_entry.S
new file mode 100644 (file)
index 0000000..bc1d72d
--- /dev/null
@@ -0,0 +1,86 @@
+#include <inc/mmu.h>
+#include <inc/memlayout.h>
+#include <inc/trap.h>
+
+#define        RELOC(x) ((x) - KERNBASE)
+
+.globl                 smp_entry
+smp_entry:             .code16
+       cli
+       cld
+       # Set up rudimentary segmentation
+       xorw    %ax, %ax                        # Segment number zero
+       movw    %ax, %ds                        # -> Data Segment
+       movw    %ax, %es                        # -> Extra Segment
+       movw    %ax, %ss                        # -> Stack Segment
+       # Would like to patch all of these 0x1000's at trampoline relocation time
+       # There's three of them, so we could patch the trampoline code when we load,
+       # once we're sure the entry code will not change anymore
+       # Note that this GDT is straight through, with no KERNBASE translation
+       lgdt    gdtdesc - smp_entry + 0x1000
+
+       # Turn on protected mode
+       movl    %cr0, %eax
+       orl             $CR0_PE, %eax
+       movl    %eax, %cr0
+       ljmp    $GD_KT, $(protcseg - smp_entry + 0x1000)
+       
+protcseg:      .code32
+       # Set up the protected-mode data segment registers
+       movw    $GD_KD, %ax             # Kernel segment selector
+       movw    %ax, %ds                # -> DS: Data Segment
+       movw    %ax, %es                # -> ES: Extra Segment
+       movw    %ax, %ss                # -> SS: Stack Segment
+       movw    %ax, %fs                # -> FS
+       movw    %ax, %gs                # -> GS
+
+       # Turn on Paging
+       movl    RELOC(boot_cr3), %eax
+       movl    %eax, %cr3
+       movl    %cr0, %eax      
+       # These cr0 flags are the same as in pmap.c.  Keep them in sync
+       orl             $(CR0_PE|CR0_PG|CR0_AM|CR0_WP|CR0_NE|CR0_MP), %eax  
+       andl    $(~(CR0_TS|CR0_EM)), %eax  
+       movl    %eax, %cr0
+
+       # Reload Segments, using the same gdt_pd as Core 0
+       lgdt    gdt_pd
+       movw    $GD_KD, %ax             # Kernel segment selector
+       movw    %ax, %ds                # -> DS: Data Segment
+       movw    %ax, %es                # -> ES: Extra Segment
+       movw    %ax, %ss                # -> SS: Stack Segment
+       movw    $GD_UD|3, %ax   # User segment selector, with RPL=3
+       movw    %ax, %fs                # -> FS
+       movw    %ax, %gs                # -> GS
+       ljmp    $GD_KT, $here   # jumping to original location of trampoline!
+here:
+       xorl    %eax, %eax
+       lldt    %ax
+spin_start:                                    # grab lock for smp_main
+       movl    $1, %eax
+       xchgl   %eax, smp_boot_lock
+       test    %eax, %eax
+       jne             spin_start
+
+       addl    $0x1, num_cpus
+       movl    (smp_stack_top), %esp
+       movl    $0, %ebp                # so backtrace works
+       call    smp_main
+       movl    $0, smp_boot_lock       # release lock
+
+       hlt                                             # does not work on kvm, so spin
+spinwait:
+       jmp             spinwait
+
+       # Below here is just data, stored with the code text
+       .p2align        2                                               # force 4 byte alignment
+gdt:
+       SEG_NULL                                                        # null seg
+       SEG(STA_X|STA_R, 0, 0xffffffff)         # code seg
+       SEG(STA_W, 0, 0xffffffff)                       # data seg
+gdtdesc:
+       .word   gdtdesc - gdt - 1                       # sizeof(gdt) - 1
+       .long   gdt - smp_entry + 0x1000        # address gdt
+
+.globl                 smp_entry_end
+smp_entry_end:
index b789e41..f586d76 100644 (file)
@@ -8,12 +8,14 @@
 #include <kern/monitor.h>
 #include <kern/env.h>
 #include <kern/syscall.h>
+#include <kern/apic.h>
 
 static struct Taskstate ts;
 
 /* Interrupt descriptor table.  (Must be built at run time because
  * shifted function addresses can't be represented in relocation records.)
  */
+// should align this on an 8 byte boundary (SDM V3A 5-13)
 struct Gatedesc idt[256] = { { 0 } };
 struct Pseudodesc idt_pd = {
        sizeof(idt) - 1, (uint32_t) idt
@@ -76,7 +78,9 @@ idt_init(void)
        // we need to stop short of the last one, since the last is the default
        // handler with a fake interrupt number (500) that is out of bounds of
        // the idt[]
-       for(i = 0; i < trap_tbl_size - 1; i++)
+       // if we set these to trap gates, be sure to handle the IRQs separately
+       // and we might need to break our pretty tables
+       for(i = 0; i < trap_tbl_size - 1; i++) 
                SETGATE(idt[trap_tbl[i].trapnumber], 0, GD_KT, trap_tbl[i].trapaddr, 0);
 
        // turn on syscall handling and other user-accessible ints
@@ -99,6 +103,14 @@ idt_init(void)
 
        // Load the IDT
        asm volatile("lidt idt_pd");
+
+       // This will go away when we start using the IOAPIC properly
+       remap_pic();
+       lapic_enable();
+       // set LINT0 to receive ExtINTs (KVM's default).  At reset they are 0x1000.
+       write_mmreg32(LAPIC_LVT_LINT0, 0x700); 
+       // mask it to shut it up for now
+       mask_lapic_lvt(LAPIC_LVT_LINT0);
 }
 
 void
@@ -198,6 +210,32 @@ void
         env_run(curenv);
 }
 
+void
+(IN_HANDLER irq_handler)(struct Trapframe *tf)
+{
+       // send EOI.  might want to do this later, and not earlier
+       // and probably in assembly.  just need to determine what's LAPIC or not
+       // this is set up to work with an old PIC for now
+       // and this is hardcoded to have only 32 be the PIC (LINT0 ExtINT)
+       if (tf->tf_trapno == 32) {
+               pic_send_eoi(tf->tf_trapno - PIC1_OFFSET);
+       } else // LAPIC style
+               lapic_send_eoi();
+
+       // consider doing James-style register_interrupt_handler instead
+       //trap(tf);
+       // this is just for handling the one time use of this in smp_boot
+       cprintf("Incoming IRQ, ISR = %d\n", tf->tf_trapno);
+       if (tf->tf_trapno == 33) {
+               cprintf("LAPIC TIMER!!!\n");
+               extern volatile bool waiting;
+               extern volatile uint8_t num_cpus;
+               {HANDLER_ATOMIC 
+                       waiting = 0;
+                       cprintf("Num_Cpus: %d\n", num_cpus);
+               }
+               }
+}
 
 void
 page_fault_handler(struct Trapframe *tf)
index 80c5f11..a4cc312 100644 (file)
        .long name;                                                     \
        .long num
 
+/* Same as NOEC, but for IRQs instead.  num is the ISR number it is mapped to */
+#define IRQ_HANDLER(name, num)                 \
+       .text;                                                          \
+       .globl name;                                            \
+       .type name, @function;                          \
+       .align 2;                                                       \
+       name:                                                           \
+       pushl $0;                                                       \
+       pushl $(num);                                           \
+       jmp _allirqs;                                           \
+       .data;                                                          \
+       .long name;                                                     \
+       .long num
+
 .data
-.globl trap_tbl;
+.globl trap_tbl
 trap_tbl:
 
 /*
  * Lab 3: Your code here for generating entry points for the different traps.
  */
-TRAPHANDLER_NOEC(ISR_divide_error, T_DIVIDE);
-TRAPHANDLER_NOEC(ISR_debug_exceptions, T_DEBUG);
-/* NMI is int 2 */
-TRAPHANDLER_NOEC(ISR_breakpoint, T_BRKPT);
-TRAPHANDLER_NOEC(ISR_overflow, T_OFLOW);
-TRAPHANDLER_NOEC(ISR_bounds_check, T_BOUND);
-TRAPHANDLER_NOEC(ISR_invalid_opcode, T_ILLOP);
-TRAPHANDLER_NOEC(ISR_device_not_available, T_DEVICE);
+TRAPHANDLER_NOEC(ISR_divide_error, T_DIVIDE)
+TRAPHANDLER_NOEC(ISR_debug_exceptions, T_DEBUG)
+TRAPHANDLER_NOEC(ISR_NMI, T_NMI)
+TRAPHANDLER_NOEC(ISR_breakpoint, T_BRKPT)
+TRAPHANDLER_NOEC(ISR_overflow, T_OFLOW)
+TRAPHANDLER_NOEC(ISR_bounds_check, T_BOUND)
+TRAPHANDLER_NOEC(ISR_invalid_opcode, T_ILLOP)
+TRAPHANDLER_NOEC(ISR_device_not_available, T_DEVICE)
 /* supposedly, DF generates an error code, but the one time we've had a DF so
- * far, it didn't eventually, this should probably be handled with a task gate
+ * far, it didn't.  eventually, this should probably be handled with a task gate
+ * it might have pushed a 0, but just the rest of the stack was corrupt
  */
-TRAPHANDLER_NOEC(ISR_double_fault, T_DBLFLT);
+TRAPHANDLER_NOEC(ISR_double_fault, T_DBLFLT)
 /* 9 reserved */
-TRAPHANDLER(ISR_invalid_TSS, T_TSS);
-TRAPHANDLER(ISR_segment_not_present, T_SEGNP);
-TRAPHANDLER(ISR_stack_exception, T_STACK);
-TRAPHANDLER(ISR_general_protection_fault, T_GPFLT);
-TRAPHANDLER(ISR_page_fault, T_PGFLT);
+TRAPHANDLER(ISR_invalid_TSS, T_TSS)
+TRAPHANDLER(ISR_segment_not_present, T_SEGNP)
+TRAPHANDLER(ISR_stack_exception, T_STACK)
+TRAPHANDLER(ISR_general_protection_fault, T_GPFLT)
+TRAPHANDLER(ISR_page_fault, T_PGFLT)
 /* 15 reserved */
-TRAPHANDLER_NOEC(ISR_floating_point_error, T_FPERR);
-TRAPHANDLER(ISR_alignment_check, T_ALIGN);
-TRAPHANDLER_NOEC(ISR_machine_check, T_MCHK);
-TRAPHANDLER_NOEC(ISR_simd_error, T_SIMDERR);
+TRAPHANDLER_NOEC(ISR_floating_point_error, T_FPERR)
+TRAPHANDLER(ISR_alignment_check, T_ALIGN)
+TRAPHANDLER_NOEC(ISR_machine_check, T_MCHK)
+TRAPHANDLER_NOEC(ISR_simd_error, T_SIMDERR)
+/* 20 - 31 reserved */
+IRQ_HANDLER(IRQ0, 32)
+IRQ_HANDLER(IRQ1, 33)
+IRQ_HANDLER(IRQ2, 34)
+IRQ_HANDLER(IRQ3, 35)
+IRQ_HANDLER(IRQ4, 36)
+IRQ_HANDLER(IRQ5, 37)
+IRQ_HANDLER(IRQ6, 38)
+IRQ_HANDLER(IRQ7, 39)
+IRQ_HANDLER(IRQ8, 40)
+IRQ_HANDLER(IRQ9, 41)
+IRQ_HANDLER(IRQ10, 42)
+IRQ_HANDLER(IRQ11, 43)
+IRQ_HANDLER(IRQ12, 44)
+IRQ_HANDLER(IRQ13, 45)
+IRQ_HANDLER(IRQ14, 46)
+IRQ_HANDLER(IRQ15, 47)
 
-TRAPHANDLER_NOEC(ISR_syscall, T_SYSCALL);
+TRAPHANDLER_NOEC(ISR_syscall, T_SYSCALL)
 /* Make sure default is last!! */
-TRAPHANDLER_NOEC(ISR_default, T_DEFAULT);
+TRAPHANDLER_NOEC(ISR_default, T_DEFAULT)
 
 .data
-.globl trap_tbl_end;
+.globl trap_tbl_end
 trap_tbl_end:
 
 .text
-/*
- * Lab 3: Your code here for _alltraps
- */
 _alltraps:
-       pushl %ds;
-       pushl %es;
-       pushal;
-       movw $GD_KD, %ax; /* data segments aren't accessible by default */
-       movw %ax, %ds;
-       movw %ax, %es;
-       pushl %esp;
-       call trap;
-       popl %esp;
-       popal;
-       popl %es;
-       popl %ds;
-       addl $0x8, %esp; /* skip trapno and err */
-       iret;
+       cld
+       pushl %ds
+       pushl %es
+       pushal
+       movw $GD_KD, %ax                # data segments aren't accessible by default
+       movw %ax, %ds
+       movw %ax, %es
+       pushl %esp
+       movl $0, %ebp                   # so we can backtrace to this point
+       call trap
+       popl %esp
+       popal
+       popl %es
+       popl %ds
+       addl $0x8, %esp                 # skip trapno and err
+       iret
+
+/* will need to think about when we reenable interrupts.  right now, iret does it,
+ * if the previous EFLAGS had interrupts enabled 
+ */
+_allirqs:
+       cld
+       pushl %ds
+       pushl %es
+       pushal
+       movw $GD_KD, %ax                # data segments aren't accessible by default
+       movw %ax, %ds
+       movw %ax, %es
+       pushl %esp
+       movl $0, %ebp                   # so we can backtrace to this point
+       call irq_handler
+       popl %esp
+       popal
+       popl %es
+       popl %ds
+       addl $0x8, %esp                 # skip IRQ number and err (which is 0)
+       iret
index fd9486a..5876672 100644 (file)
@@ -6,6 +6,7 @@
 void
 umain(void)
 {
+       while(1);
        // try to print the kernel entry point as a string!  mua ha ha!
        sys_cputs((char*SAFE)TC(0xc0100020), 100);
 }