Thread init snapshot
authorJack Miller <jack@codezen.org>
Wed, 23 Mar 2016 02:32:42 +0000 (21:32 -0500)
committerJack Miller <jack@codezen.org>
Fri, 25 Mar 2016 21:59:17 +0000 (16:59 -0500)
- ACPI walked enough to detect LAPICs
- APIC used to SIPI sleeping threads into realmode stub
- realmode stub binary compiled and integrated, gets threads into
  protected mode, but not paging / 64 bit yet

Makefile
asm/realmode.asm-bin [new file with mode: 0644]
asm/rm.asm [new file with mode: 0644]
include/acpi.h [new file with mode: 0644]
include/apic.h
include/asm/realmode.h [new file with mode: 0644]
kernel/acpi.c
kernel/apic.c
kernel/main.c

index 06da764..7e86124 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -18,7 +18,10 @@ NASMFLAGS = -f elf64
 SRC_C = $(wildcard boot/*.c kernel/*.c mm/*.c)
 OBJ_C = ${SRC_C:.c=.o}
 
+BIN_ASM = $(wildcard asm/*.asm-bin)
 SRC_ASM = $(wildcard asm/*.asm)
+
+BIN_OBJ = ${BIN_ASM:.asm-bin=.bin}
 OBJ_ASM = ${SRC_ASM:.asm=.o}
 
 SRC = ${SRC_C} ${SRC_ASM}
@@ -30,7 +33,12 @@ all: viridis
        @echo CC $<
        @${CC} ${CFLAGS} $< -o $@
 
-%.o : %.asm
+%.bin : %.asm-bin
+       @echo NASM bin $<
+       @${CPP} ${CPPFLAGS} -D__ASSEMBLY__ $< > $@.tmp
+       @${NASM} -f bin $@.tmp -o $@
+
+%.o : %.asm ${BIN_OBJ}
        @echo NASM $<
        @${CPP} ${CPPFLAGS} -D__ASSEMBLY__ $< > $@.tmp
        @${NASM} ${NASMFLAGS} $@.tmp -o $@
@@ -40,8 +48,13 @@ viridis: ${OBJ} linker.ld
        @echo CC -o $@
        @${LD} ${LDFLAGS} -o viridis ${OBJ}
 
+realmode.elf: asm/realmode.asm-bin
+       @echo NASM $<
+       @${CPP} ${CPPFLAGS} -D__ASSEMBLY__ $< > $@.tmp
+       @${NASM} -f elf $@.tmp -o $@
+
 clean:
        @echo cleaning
-       @rm -f ${OBJ} viridis
+       @rm -f ${BIN_OBJ} ${OBJ} viridis
 
 .PHONY: all
diff --git a/asm/realmode.asm-bin b/asm/realmode.asm-bin
new file mode 100644 (file)
index 0000000..5b6f53c
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+   
+ This is a weird environment. The secondary threads are tossed here in 16 bit
+ real mode from a SIPI. Similar to Linux, this file is compiled separately, and
+ then INCBIN'd into an ELF file as a binary.
+
+ There a couple of reasons it makes sense to ditch ELF for this.
+
+ - This code won't be able to address anything in the kernel thanks to 16 bit
+ segmentation, so we gain nothing in this code from linking it into the kernel
+ proper. Meanwhile, everything this code needs has to be within reach of the
+ segmented memory, so it makes sense for this to be entirely standalone.
+
+ - The SIPI forces this code to be in the first 1M of memory, and page aligned.
+ This is hard to accomplish with the linker when your entire kernel starts at
+ 1M, without adding a bunch of padding or confusing our already complex linker
+ script. We get around this by copying the contents of this file to an
+ appropriate location before we SIPI. Because of this copy, any linker
+ relocation even within this file would be useless.
+
+ Now while binary compilation solves these issues, the cost is that we need the
+ boot CPU to communicate any addresses we're going to need at runtime and so,
+ again similar to Linux, we setup a struct in memory the boot CPU can fill in
+ for us.
+
+ */
+
+#include <memmap.h>
+
+#include <asm/gdt.h>
+
+/* Assumptions */
+
+#define REALMODE_PA 0x2000
+#define CODESEL_32  0x8
+#define DATASEL_32  0x10
+
+BITS 16
+
+    jmp main /* Skip our header data */
+
+gdtr:
+    DD 0
+crthree:
+    DD 0
+kernel_entry:
+    DQ 0
+
+main:
+    lgdt [cs:builtin_gdtr]
+
+    mov eax, cr0
+    or eax, 1
+    mov cr0, eax
+
+    jmp 0x8:host_local_gdt + REALMODE_PA
+
+BITS 32
+host_local_gdt:
+
+    mov eax, [cs:gdtr + REALMODE_PA]
+    lgdt [eax]
+
+    jmp CODESEL_32:kernel_gdt + REALMODE_PA
+kernel_gdt:
+    /* We now share a GDT with the rest of the kernel */
+
+    /* Initialize all of our selectors */
+    mov ax, DATASEL_32
+    mov ss, ax
+    mov ds, ax
+    mov es, ax
+    mov fs, ax
+    mov gs, ax
+
+    jmp kernel_gdt
+
+ALIGN 8
+builtin_gdtr:
+DW (builtin_gdt_end - builtin_gdt) - 1
+DQ builtin_gdt + REALMODE_PA
+
+builtin_gdt:
+GDTENTRY    0, 0x0, 0x0
+GDTENTRY    FLAGS_CODE_32, 0, 0xFFFFF
+GDTENTRY    FLAGS_DATA_32, 0, 0xFFFFF
+builtin_gdt_end:
diff --git a/asm/rm.asm b/asm/rm.asm
new file mode 100644 (file)
index 0000000..e771af6
--- /dev/null
@@ -0,0 +1,10 @@
+/* Graft in the realmode binary */
+
+GLOBAL realmode_bin
+GLOBAL end_realmode_bin
+
+realmode_bin:
+
+INCBIN "asm/realmode.bin"
+
+end_realmode_bin:
diff --git a/include/acpi.h b/include/acpi.h
new file mode 100644 (file)
index 0000000..f9edee1
--- /dev/null
@@ -0,0 +1,5 @@
+/* vim: set ts=4 sw=4 sts=4 noet : */
+
+#pragma once
+
+int acpi_init(void);
index 75286bd..b6778ea 100644 (file)
@@ -4,10 +4,13 @@
 struct apic {
     void (*init) (void);
     void (*eoi) (void);
-    void (*wake_secondary) (void);
+    void (*wake_secondary) (u8 apic_id);
     void (*start_timer) (void);
+       u8 (*get_id) (void);
        u32 (*read) (u32 offset);
        void (*write) (u32 offset, u32 val);
+       void (*write_icr) (u32 dest, u32 val);
+       void (*wait) (void);
 };
 
 int apic_init(void);
diff --git a/include/asm/realmode.h b/include/asm/realmode.h
new file mode 100644 (file)
index 0000000..a29487d
--- /dev/null
@@ -0,0 +1,15 @@
+#pragma once
+
+extern u64 realmode_bin;
+extern u64 end_realmode_bin;
+
+#define REALMODE_BIN_LEN (((u64) &end_realmode_bin) - ((u64) &realmode_bin))
+
+struct rm_header {
+       u8   jmp[2]; /* Two bytes for the jump over data */
+       u32  gdtr_pa;
+       u32  cr3;
+       u64  kernel_entry;
+} __attribute__((packed));
+
+extern u32 GDTR_32;
index 2492b16..69edc77 100644 (file)
@@ -5,6 +5,7 @@
 #include <memmap.h>
 #include <map.h>
 #include <console.h>
+#include <apic.h>
 
 struct rsdp {
        char sig[8];
@@ -59,9 +60,21 @@ struct apic_entry {
 
 #define APIC_FLAG_PCAT_COMPAT 1
 
+struct lapic_entry {
+       u8 type;
+       u8 len;
+       u8 apic_uid;
+       u8 apic_id;
+       u32 flags;
+} __attribute__((packed));
+
+#define LAPIC_FLAGS_ENABLED 1
+
 void acpi_apic_handler(struct description_header *hdr, void *entry_ptr)
 {
        struct apic_entry *entry = entry_ptr;
+       int my_lapic = apic->get_id();
+       int cpus = 0;
 
        u8 *intcons = (u8 *) (((u64) entry) + sizeof(struct apic_entry));
        int length = hdr->length - (sizeof(struct description_header) + sizeof(struct apic_entry));
@@ -70,13 +83,23 @@ void acpi_apic_handler(struct description_header *hdr, void *entry_ptr)
        while (cur_byte < length) {
                u8 type = intcons[cur_byte];
 
-               if (type < 16)
-                       printk("[%s]\n", apic_types[type]);
-               else
-                       printk("[%d]\n", type);
+               if (type == APIC_TYPE_LAPIC) {
+                       struct lapic_entry *lapic = (void *) &intcons[cur_byte];
+
+                       /* HACK -- working second thread has LAPIC disabled? */
+                       lapic->flags |= LAPIC_FLAGS_ENABLED;
+
+                       if (lapic->flags & LAPIC_FLAGS_ENABLED) {
+                               if (lapic->apic_id != my_lapic)
+                                       apic->wake_secondary(lapic->apic_id);
+                               cpus++;
+                       }
+               }
 
                cur_byte += intcons[cur_byte + 1];
        }
+
+       printk("%d CPUs detected\n", cpus);
 }
 
 struct acpi_section_handler {
index 624c40a..22144cb 100644 (file)
 #include <map.h>
 #include <apic.h>
 #include <mmio.h>
+#include <string.h>
+
+#include <asm/realmode.h>
+#include <asm/barrier.h>
 
 #define CPUID_X2APIC   (1 << 21)
 #define CPUID_LAPIC            (1 << 9)
 #define                XAPIC_ENABLE                    (1 << 11)
 #define                XAPIC_EXTD                              (1 << 10)
 
+#define APIC_ID                                                0x2
+#define                ID(x)                                   ((x) >> 24)
+
 #define APIC_EOI                                       0xb
 
+#define APIC_SVR                                       0xf
+#define                SVR_APIC_EN                             (1 << 8)
+
 #define APIC_ICRL                                      0x30
 #define                ICRL_DSH(x)                             ((x & 0x3) << 18)
 #define                        DSH_ALL_EX_SELF         0x3
 #define                        DSH_ALL_IN_SELF         0x2
 #define                ICRL_MT(x)                              ((x & 0x7) << 8)
+#define                        MT_INIT                         0x5
 #define                        MT_STARTUP                      0x6
 #define                ICRL_VEC(x)                             (x & 0xFF)
 #define                ICRL_DM                                 (1 << 11)
-#define                ICRL_DS                                 (1 << 12)
-#define                ICRL_L                                  (1 << 14)
-
+#define                ICRL_BUSY                               (1 << 12)
+#define                ICRL_ASSERT                             (1 << 14)
+#define                ICRL_TRIGGER_LEVEL              (1 << 15)
+#define APIC_ICRH                                      0x31
+#define                ICRH_DEST(x)                    ((x & 0xFF) << 24)
 #define APIC_LVT_TIMER                         0x32
 #define                LVT_TIMER_PERIODIC              (1 << 17)
 #define                LVT_VECTOR(x)                   (x & 0xff)
 #define                LVT_MASKED                              (1 << 16)
 
+#define APIC_LVT3                                      0x37
 #define        APIC_TIMER_INIT_CT                      0x38
 #define APIC_TIMER_CUR_CT                      0x39
 #define APIC_TIMER_DIV                         0x3e
 
 static volatile u64 *mmio_mem = NULL;
 
+static void apic_init_common(void)
+{
+       apic->write(APIC_SVR, SVR_APIC_EN);
+       apic->write(APIC_LVT3, LVT_VECTOR(34));
+}
+
 static void apic_start_timer(void)
 {
        u32 lvt;
@@ -87,11 +107,63 @@ static void apic_eoi(void)
        apic->write(APIC_EOI, 0);
 }
 
-static void apic_wake_secondary(void)
+static struct rm_header *realboot = NULL;
+
+static void apic_wake_secondary(u8 apic_id)
 {
-       u32 val = ICRL_DSH(DSH_ALL_IN_SELF) | ICRL_VEC(33) | ICRL_MT(MT_STARTUP) | ICRL_DM | ICRL_L;
+       if (!realboot) {
+               map_page(0x2000, 0x2000);
+               realboot = (void *) 0x2000;
+               memcpy(realboot, &realmode_bin, REALMODE_BIN_LEN);
+               realboot->gdtr_pa = (u64) &GDTR_32;
+               unmap_pages(0x2000, 1);
+               mb();
+       }
 
-       apic->write(APIC_ICRL, val);
+       u32 lo = ICRL_MT(MT_INIT) | ICRL_ASSERT | ICRL_TRIGGER_LEVEL;
+
+       printk("Waking %d\n", apic_id);
+
+       apic->write_icr(apic_id, lo);
+
+       apic->wait();
+
+       printk("INIT asserted\n");
+
+       lo = ICRL_MT(MT_INIT) | ICRL_TRIGGER_LEVEL;
+
+       apic->write_icr(apic_id, lo);
+
+       apic->wait();
+
+       printk("INIT deasserted\n");
+
+       mb();
+       for (int i = 0; i < 100000000; i++)
+
+       lo = ICRL_MT(MT_STARTUP) | ICRL_ASSERT | 2;
+
+       printk("lo: 0x%x\n", lo);
+
+       apic->write_icr(apic_id, lo);
+
+       apic->wait();
+
+       mb();
+       for (int i = 0; i < 100000000; i++)
+
+       lo = ICRL_MT(MT_STARTUP) | ICRL_ASSERT | 2;
+
+       printk("lo: 0x%x\n", lo);
+
+       apic->write_icr(apic_id, lo);
+
+       apic->wait();
+}
+
+static u8 apic_get_id(void)
+{
+       return ID(apic->read(APIC_ID));
 }
 
 static void xapic_init(void)
@@ -106,6 +178,8 @@ static void xapic_init(void)
        mmio_mem = vsalloc("APIC", VSALLOC_HEAP_VIRTUAL, PAGE_SIZE);
 
        map_page_nocache((u64) mmio_mem, msr & ~(0xFFFUL));
+
+       apic_init_common();
 }
 
 static void xapic_mmio_write(u32 offset, u32 val)
@@ -118,13 +192,30 @@ static u32 xapic_mmio_read(u32 offset)
        return mmio_read(mmio_mem, MMIO_OFFSET(offset));
 }
 
+static void xapic_ipi_wait(void)
+{
+       while(apic->read(APIC_ICRL) & ICRL_BUSY) {}
+}
+
+static void xapic_write_icr(u32 dest, u32 val)
+{
+       printk("ICRH 0x%x\n", ICRH_DEST(dest));
+       printk("ICRL 0x%x\n", val);
+
+       apic->write(APIC_ICRH, ICRH_DEST(dest));
+       apic->write(APIC_ICRL, val);
+}
+
 struct apic xapic = {
        .init = xapic_init,
        .eoi = apic_eoi,
        .wake_secondary = apic_wake_secondary,
        .start_timer = apic_start_timer,
+       .get_id = apic_get_id,
        .write = xapic_mmio_write,
+       .write_icr = xapic_write_icr,
        .read = xapic_mmio_read,
+       .wait = xapic_ipi_wait,
 };
 
 static void x2apic_init(void)
@@ -136,6 +227,8 @@ static void x2apic_init(void)
 
        /* Need EXTD flag to use MSR interface */
        wrmsr(MSR_IA32_APIC_BASE, msr | XAPIC_ENABLE | XAPIC_EXTD);
+
+       apic_init_common();
 }
 
 static void x2apic_msr_write(u32 offset, u32 val)
@@ -148,13 +241,26 @@ static u32 x2apic_msr_read(u32 offset)
        return rdmsr(MSR_OFFSET(offset));
 }
 
+static void x2apic_ipi_wait(void)
+{
+       return;
+}
+
+static void x2apic_write_icr(u32 dest, u32 val)
+{
+       apic->write(APIC_ICRL, ((u64) dest << 32) + val);
+}
+
 struct apic x2apic = {
        .init = x2apic_init,
        .eoi = apic_eoi,
        .wake_secondary = apic_wake_secondary,
        .start_timer = apic_start_timer,
+       .get_id = apic_get_id,
        .read = x2apic_msr_read,
        .write = x2apic_msr_write,
+       .write_icr = x2apic_write_icr,
+       .wait = x2apic_ipi_wait,
 };
 
 int apic_init(void)
@@ -176,9 +282,5 @@ int apic_init(void)
 
        apic->init();
 
-       apic->wake_secondary();
-
-       apic->start_timer();
-
     return 0;
 }
index 2e417d3..4b07863 100644 (file)
@@ -22,10 +22,10 @@ void main (void *info_phys, unsigned long end_structures)
 
        disable_pic();
 
-       acpi_init();
-
        apic_init();
 
+       acpi_init();
+
        asm ("sti" : : );
 
        asm ("hlt" : : );