Long Mode. long-mode
authorJack Miller <jack@codezen.org>
Sun, 2 Sep 2012 15:55:55 +0000 (10:55 -0500)
committerJack Miller <jack@codezen.org>
Sat, 19 Mar 2016 19:02:48 +0000 (14:02 -0500)
- 64-bit Long Mode Active (EFER.LMA set)
- 64-bit CS
- GDT setup, IDT 64-bit stubbed
- 4-level paging inited, 4k pages
- Jumped into C code

Signed-off-by: Jack Miller <jack@codezen.org>
Makefile
asm/gdt.asm [new file with mode: 0644]
asm/head.asm [new file with mode: 0644]
asm/idt.asm [new file with mode: 0644]
include/early.h [new file with mode: 0644]
include/grub.h [new file with mode: 0644]
include/kernel.h [new file with mode: 0644]
kernel/main.c [new file with mode: 0644]
linker.ld
main.c [deleted file]

index 3718eef..1bf4a46 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,23 +1,42 @@
 CC = gcc
 LD = ld
-CFLAGS = -std=c99 -pedantic -Wall -nostdlib -ffreestanding -mcmodel=kernel
+NASM=nasm
+CPP=cpp
+
+INCLUDE_DIRS = include/
+
+CPPFLAGS = -I ${INCLUDE_DIRS}
+CFLAGS = -c -std=gnu99 -pedantic -Wall -nostdlib -ffreestanding -mcmodel=kernel -I ${INCLUDE_DIRS}
 LDFLAGS = -T linker.ld -nostdlib -n
+NASMFLAGS = -f elf64
+
+SRC_C = $(wildcard kernel/*.c)
+OBJ_C = ${SRC_C:.c=.o}
 
-SRC = main.c
-OBJ = ${SRC:.c=.o}
+SRC_ASM = $(wildcard asm/*.asm)
+OBJ_ASM = ${SRC_ASM:.asm=.o}
 
-all: kernel
+SRC = ${SRC_C} ${SRC_ASM}
+OBJ = ${OBJ_C} ${OBJ_ASM}
 
-.c.o:
+all: viridis
+
+%.o : %.c
        @echo CC $<
-       @${CC} -c ${CFLAGS} $<
+       @${CC} ${CFLAGS} $< -o $@
+
+%.o : %.asm
+       @echo NASM $<
+       @${CPP} ${CPPFLAGS} $< > $@.tmp
+       @${NASM} ${NASMFLAGS} $@.tmp -o $@
+       @rm $@.tmp
 
-kernel: ${OBJ} linker.ld
+viridis: ${OBJ} linker.ld
        @echo CC -o $@
-       @${LD} ${LDFLAGS} -o kernel ${OBJ}
+       @${LD} ${LDFLAGS} -o viridis ${OBJ}
 
 clean:
        @echo cleaning
-       @rm -f ${OBJ} kernel
+       @rm -f ${OBJ} viridis
 
 .PHONY: all
diff --git a/asm/gdt.asm b/asm/gdt.asm
new file mode 100644 (file)
index 0000000..a8052cb
--- /dev/null
@@ -0,0 +1,141 @@
+BITS 32
+SECTION .text_early
+
+#include <early.h>
+
+/* AMD64 Architecture Programmer's Manual v2 4.8
+ *
+ * This structure is a jigsaw of values in 32-bit.
+ *
+ *     DW     SEG_LIMIT_LOW
+ *     DW     BASE_ADDRESS_LOW
+ *     DB     BASE_ADDRESS_MID
+ *     DW     FLAGS
+ *     DB     BASE_ADDRESS_HIGH
+ *
+ * Base Address is obviously the 32-bit address starting the segment.
+ *
+ * Segment Limit is 20-bits of size.
+ *
+ * FLAGS = 15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
+ *         G  D  R  A  \         / P  \DPL/ S  \         /
+ *               S  V   SEGMENT HI                TYPE
+ *               V  L
+ *
+ * For code descriptors, type is
+ *     8 - Execute Only
+ *     A - Executable / Readable
+ *
+ * There's also a concept of "conforming" with the higher bits we don't care
+ * about.
+ *
+ * For data descriptors, type is
+ *     0 - Read Only
+ *     2 - Read / Write
+ *     4 - Expand Down RO
+ *     6 - Expand Down RW
+ *
+ * Each of these types for code and data descriptors has an odd version to
+ * indicating the accessed bit that we don't care about either.
+ *
+ * DPL is a two bit field indicating which privilege level the descriptor is
+ * in. 0 is the higher priority, 3 is the low priority. The kernel is in the
+ * highest priority.
+ *
+ * P is the present bit.
+ *
+ * AVL is available to us, but we don't use it.
+ *
+ * D/B Default Operand Size, for Long Mode this must be 0, for non-Long mode
+ * 1 for 32-bit, 0 for 16-but segment.
+ *
+ * G - granularity, whether segment limit describes the size in bytes (G=0)
+ * or 4k pages (G=1).
+ */
+
+#define FLAG_CODE   0xa        //Read/Execute
+#define FLAG_DATA   0x2        //Read/Write
+
+#define FLAG_USER   (1 << 4)
+#define FLAG_SYSTEM (0 << 4)
+
+#define FLAG_R0                (0 << 5)    // Rings 0 - 3
+#define FLAG_R1                (1 << 5)
+#define FLAG_R2                (2 << 5)
+#define FLAG_R3                (3 << 5)
+
+#define FLAG_P         (1 << 7)    // Present
+
+#define FLAG_32                (1 << 14)   // 1 for 32-bit compat
+#define FLAG_4k                (1 << 15)   // 4k page granularity
+
+#define FLAGS_COMMON_32 (FLAG_USER | FLAG_R0 | FLAG_P | FLAG_32 | FLAG_4k)
+#define FLAGS_CODE_32 (FLAG_CODE | FLAGS_COMMON_32)
+#define FLAGS_DATA_32 (FLAG_DATA | FLAGS_COMMON_32)
+
+/* Long mode descriptors have bit 13 as the "Long bit"
+ *
+ * A lot of the bits get ignored in long mode, but we'll set them anyway since
+ * we're not there yet.
+ */
+
+#define FLAG_L          (1 << 13) // 1 for 64-bit
+#define FLAGS_CODE_64 (FLAG_USER | FLAG_R0 | FLAG_P | FLAG_L | FLAG_4k | FLAG_CODE)
+
+ALIGN 8
+GDTR:
+DW (GDTEND - GDT - 1)
+
+/* 8 bytes for long-mode, high bytes ignored in legacy,
+ * it works out okay, since we define .gdt well under 4Gb
+ */
+
+DQ GDT
+
+/* 1 = FLAGS, 2 = BASE, 3 = LIMIT */
+
+%macro GDTENTRY 3
+    DW  ((%3) & 0xFFFF)
+    DW  ((%2) & 0xFFFF)
+    DB  (((%2) & 0xFF0000) >> 16)
+    DW  ((%1) | (((%3) & 0xF0000) >> 8))
+    DB  (((%2) & 0xFF000000) >> 24)
+%endmacro
+
+GLOBAL CODE_SEL_32
+GLOBAL DATA_SEL
+GLOBAL CODE_SEL_64
+
+ALIGN 8
+GDT:
+    /* NULL descriptor */
+    GDTENTRY    0, 0x0, 0x0
+    /* Code descriptor (Compat) */
+CODE_SEL_32 EQU $-GDT
+    GDTENTRY    FLAGS_CODE_32, 0x0, 0xFFFFF
+    /* Data descriptor (Compat) */
+DATA_SEL EQU $-GDT
+    GDTENTRY    FLAGS_DATA_32, 0x0, 0xFFFFF
+    /* Code descriptor (Long) */
+CODE_SEL_64 EQU $-GDT
+    GDTENTRY    FLAGS_CODE_64, 0x0, 0xFFFFF
+GDTEND:
+
+GLOBAL populate_gdt
+populate_gdt:
+    lgdt[GDTR]
+    ret
+
+BITS 64
+GLOBAL fixup_gdtr
+fixup_gdtr:
+    mov rax, VIRT_BASE + GDTR + 2
+
+    mov rbx, VIRT_BASE + GDT
+
+    mov qword[rax], rbx
+
+    sub rax, 2
+    lgdt[rax]
+
+    ret
diff --git a/asm/head.asm b/asm/head.asm
new file mode 100644 (file)
index 0000000..2253003
--- /dev/null
@@ -0,0 +1,378 @@
+BITS 32
+
+#include <early.h>
+
+/* main.c */
+EXTERN main
+
+/* gdt.asm */
+EXTERN populate_gdt
+EXTERN fixup_gdtr
+EXTERN DATA_SEL
+EXTERN CODE_SEL_32
+EXTERN CODE_SEL_64
+
+/* idt.asm */
+EXTERN populate_idt
+EXTERN fixup_idtr
+
+/* linker.ld */
+EXTERN kernel_size
+
+/* We specially link this at our load address to simplify jmps. */
+SECTION .text_early
+
+GLOBAL entry
+
+entry:
+
+    /* Disable interrupts while we move memory descriptors over from
+     * whatever is left of GRUB.
+     */
+
+    cli
+
+    /* Move the GRUB information pointer into EDI, which we won't
+     * use for anything else until we call main()
+     */
+
+    mov edi, ebx
+
+    /* Assume that GRUB setup enough stack for us to do a call. */
+    /* This only does lgdt */
+
+    call populate_gdt
+
+    /* Setup data segments to new offset in our own GDT */
+    mov eax, DATA_SEL
+    mov ds, eax
+    mov es, eax
+    mov fs, eax
+    mov gs, eax
+    mov ss, eax
+
+    /* Setup our stack, paging code will start to use it. */
+    mov esp, (STACK_PAGES_PHYS + S_PAGES * PAGE_SIZE)
+    mov ebp, esp
+
+    /* Reload code segment, jumping to host_gdt load (not link) */
+    jmp CODE_SEL_32:host_gdt
+host_gdt:
+
+    call populate_idt
+
+    /* Calculate size of early structures */
+
+    /* eax = size of kernel, rounded up to page size */
+    mov eax, kernel_size
+    add eax, PAGE_MASK
+    and eax, ~PAGE_MASK
+
+    /* ebx = end of kernel address, rounded up*/
+    mov ebx, eax
+    add ebx, KERNEL_START
+
+    /* Now we want a count of how many page table pages it will take to map
+     * this. Because of our chosen target address, we get half of the first page table
+     * page
+     *
+     * PTE(KERNEL_START) = 256 of 512 entries
+     */
+
+    /* We do get an entire page directory though, so we can count on the number
+     * of PD/PDP/PML4 pages being * one a piece because I'm fairly confident that our
+     * kernel will always be * under 1Gb (the amount of memory mappable in a single
+     * page directory).
+     */
+
+    /* ecx = pte index of first kernel page */
+    mov ecx, PTE(KERNEL_START)
+
+    /* edx = page structure count, initial PT and  PD/PDP/PML4 already counted. */
+    mov edx, 4
+
+count_early:
+    sub eax, PAGE_SIZE
+
+    /* If this would be the 512th PTE, it's actually PTE 0 of another page
+     * table page. Roughly:
+
+        if (pte_idx == 512) {
+            reserved_pages++;
+            pte_idx = 0;
+        }
+        else {
+            pte_idx++;
+        }
+     */
+
+    /* if */
+    cmp ecx, 512
+    jne no_new_pt
+
+    /* then */
+    add edx, 1
+    mov ecx, 0
+    jmp ce_loop_end
+
+    /* else */
+no_new_pt:
+    add ecx, 1
+
+    /* remaining loop */
+ce_loop_end:
+
+    cmp eax, 0
+    jne count_early
+
+    /* Summary:
+     * ebx = page aligned end kernel address
+     * edx = number of pages tables needed to fully map kernel
+     *  + 4 for initial PT and PD/PDP/PML4
+     */
+
+    /* ecx = end of kernel paging addresses */
+    mov ecx, edx
+    shl ecx, PAGE_SHIFT
+    add ecx, ebx
+    add ecx, PAGE_SIZE
+
+    mov eax, ebx
+zero_page_mem:
+    mov dword [eax], 0x0
+    add eax, 4
+    cmp eax, ecx
+    jne zero_page_mem
+
+    /* Summary:
+     * ebx = page aligned end kernel address, now PML4 address
+     * ecx = end of kernel and page structures
+     */
+
+    /* Now we've counted and cleared the memory for our page structures, so
+     * now let's set them up.
+     */
+
+    /* First, map the PML4 into itself (see early.h) */
+
+    /* eax = address of PML4[510]*/
+    mov eax, ebx
+    add eax, (8 * 510)
+
+    /* edx = PML4 address + flags */
+    mov edx, ebx
+    or  edx, (PF_RW | PF_P)
+
+    mov dword [eax], edx
+
+    /* Now, map two PDPs, one where we want our kernel, and one
+     * where we'll end up after we start paging but before we jump to our
+     * kernel address. Here's the break down of our two addresses:
+     *
+     * KERNEL_START (0x100000) - where we are running when paging turns on
+     * PML4E:  0
+     * PDPE:   0
+     * PDE:    0
+     * PTE:    256
+     *
+     * VIRT_BASE | KERNEL_START - where we want to run
+     * PML4E:  511
+     * PDPE:   510
+     * PDE:    0
+     * PTE:    256
+     *
+     * We're going to be lazy and merge these together because we're mapping
+     * the identical content and because we'll clean up immediately after
+     * paging is enabled. Looking at the above it's clear that we should
+     * eliminate PML4E[0] and PDPE[0], but leave the identical PDEs and PTEs
+     * in place.
+     *
+     * We can also see that, if we are flexible in the number of PTs, we'd
+     * have to have 512 of them before we'd have to allocate another PD. Since
+     * 512 PTs can map 1GB of memory, I don't think that's an issue for our
+     * kernel, thus we're safe hardcoding 1 PML4/PDP/PD page.
+     */
+
+    /* First, the scrap mapping */
+
+    mov eax, ebx
+    add eax, 8 * PML4E(KERNEL_START)
+
+    mov edx, ebx
+    add edx, PAGE_SIZE
+    or  edx, (PF_RW | PF_P)
+
+    mov dword [eax], edx
+
+    /* Now, the real entry */
+
+    mov eax, ebx
+    add eax, 8 * PML4E(VIRT_BASE | KERNEL_START)
+
+    mov dword [eax], edx
+
+    /* Onto the PDP */
+
+    mov eax, ebx
+    add eax, (8 * PDPE(KERNEL_START)) + PAGE_SIZE
+
+    mov edx, ebx
+    add edx, 2*PAGE_SIZE
+    or  edx, (PF_RW | PF_P)
+
+    mov dword [eax], edx
+
+    mov eax, ebx
+    add eax, (8 * PDPE(VIRT_BASE | KERNEL_START)) + PAGE_SIZE
+
+    mov edx, ebx
+    add edx, 2*PAGE_SIZE
+    or  edx, (PF_RW | PF_P)
+
+    mov dword [eax], edx
+
+    /* Now the PD, which is the same for both */
+
+    mov eax, ebx
+    add eax, (8 * PDE(KERNEL_START)) + (2*PAGE_SIZE)
+
+    mov edx, ebx
+    add edx, 3*PAGE_SIZE
+
+    /* Remember ecx? It's end address of paging structures, use it to know when
+     * we've mapped all the PTs
+     */
+
+write_next_pde:
+    mov esi, edx
+    or  esi, (PF_RW | PF_P)
+
+    mov dword [eax], esi
+
+    add eax, 8
+    add edx, PAGE_SIZE
+    cmp edx, ecx
+    jne write_next_pde
+
+    /* Fill out all of the PTEs */
+
+    /* Start with the stack */
+    mov eax, ebx
+    add eax, (8 * PTE(STACK_PAGES_START)) + (3*PAGE_SIZE)
+
+%assign i 0
+%rep S_PAGES
+    mov dword [eax], ((STACK_PAGES_PHYS + PAGE_SIZE * i) | PF_RW | PF_P)
+    add eax, 8
+%assign i (i+1)
+%endrep
+
+    /* Whose PTEs are adjacent to the kernel's so we don't need to mess with
+     * eax
+     */
+
+    mov edx, KERNEL_START
+
+write_next_pte:
+    mov esi, edx
+    or  esi, (PF_RW | PF_P)
+
+    mov dword [eax], esi
+
+    add eax, 8
+    add edx, PAGE_SIZE
+    cmp edx, ebx
+    jne write_next_pte
+
+    /* While we have a close value handy, put the end of paging structures in esi to
+     * pass to main
+     */
+
+    mov esi, eax
+    add esi, PAGE_MASK
+    and esi, ~PAGE_MASK
+
+    /* Enable CR4.PAE (bit 5) */
+    mov eax, cr4
+    or  eax, (1 << 5)
+    mov cr4, eax
+
+    /* Put PML4 address in CR3 */
+    mov cr3, ebx
+
+    /* Set EFER.LME (bit 8) to enable Long Mode
+     * EFER is a model specific register (MSR).
+     * To access MSRs, you place their "address" (not memory)
+     * into call rdmsr to read it into edx:eax. wrmsr will 
+     * also take edx:eax and write it to the msr
+     */
+
+    mov ecx, 0xc0000080
+    rdmsr
+    or  eax, (1 << 8)
+    wrmsr
+
+    /* Set CR0.PG (bit 31) to enable paging */
+    mov eax, cr0
+    or  eax, (1 << 31)
+    mov cr0, eax
+
+    /* Get into 64-bit to perform jump to 0xFFFF8...*/
+    jmp        0x18:cleanup_32
+cleanup_32:
+
+    /* Jump to proper 0xF... address of cleanup_64 */
+BITS 64
+
+    mov rax, VIRT_BASE
+    add rax, cleanup_64
+    jmp rax
+
+cleanup_64:
+
+    /* Hooray! EFER.LMA should be set (long mode active),
+     * and our code segment is 64-bit!
+     */
+
+    /* Update our stacks to the new paged mapping, resetting the stack */
+
+    mov rax, (VIRT_BASE + (KERNEL_START - 8))
+    mov rsp, rax
+    mov rbp, rax
+
+    /* Move GDTR / IDTR addresses to new 0xFFFF8....... */
+    call fixup_gdtr
+    call fixup_idtr
+
+    /* Now that we are executing in 0xF... space, and our
+     * stacks are there too, we can clean up the 0x0 kernel
+     * mapping that had to be in place for us to successfully
+     * return from setting CR0.PG
+     */
+
+    /* Unfortunately I can't just use the C macros directly */
+
+    /* Also note that it's important to do this from the bottom up, on qemu (if
+     * not hardware), the PDPE mapping disappears with the PML4E mapping, despite
+     * cr3 not being reset
+     */
+
+    /* mov rax, PDPE_ADDR(KERNEL_START) */
+    mov rax, 0xffffff7fbfc00000
+    mov dword [rax], 0x0
+
+    /* mov rax, PML4E_ADDR(KERNEL_START) */
+    mov rax, 0xffffff7fbfdfe000
+    mov dword [rax], 0x0
+
+    /* Reset CR3 to update. */
+    mov rax, cr3
+    mov cr3, rax
+
+    sti
+
+    /* Move to rax first to avoid linker relocation truncation. */
+    mov rax, main
+
+    call rax
+    jmp $   // We should never get here.
diff --git a/asm/idt.asm b/asm/idt.asm
new file mode 100644 (file)
index 0000000..8528cb3
--- /dev/null
@@ -0,0 +1,122 @@
+BITS 32
+SECTION .text_early
+
+#include <early.h>
+
+/* gdt.asm */
+EXTERN CODE_SEL_64
+
+/* The IDT holds a set of descriptors similar to the GDT, called
+ * Interrupt Gates. Call and Trap Gates are also valid here.
+ */
+
+#define FLAG_INTERRUPT  0xe
+
+/* These two are common with GDT descriptors */
+
+#define FLAG_R0                (0 << 5)    // Rings 0 - 3
+#define FLAG_P          (1 << 7)
+
+#define IDT_ENTRIES     256
+
+/* Generate 256 interrupt routines. */
+
+/* 1 = ISR # */
+
+%macro ISR 1
+isr%1:
+    jmp $
+%endmacro
+
+ISRS:
+%assign i 0
+%rep IDT_ENTRIES
+ISR i
+%assign i (i+1)
+%endrep
+
+#define ISR_SIZE (isr1 - isr0)
+
+/* IDTR, just like GDTR */
+
+ALIGN 8
+IDTR:
+DW (IDTEND - IDT - 1)
+DQ IDT
+
+/* Use 64-bit IDTE format, even though we assume 32-bit addresses since 
+ * we're defining them here.
+ */
+
+%macro IDTENTRY 0
+    DD 0xabcdefab
+    DD 0xabcdefab
+    DD 0xabcdefab
+    DD 0xabcdefab
+%endmacro
+
+ALIGN 8
+IDT:
+%assign i 0
+%rep IDT_ENTRIES
+IDTENTRY
+%assign i (i+1)
+%endrep
+IDTEND:
+
+GLOBAL populate_idt
+populate_idt:
+    mov eax, IDT
+    mov ebx, isr0
+    or ebx, (VIRT_BASE & 0xFFFFFFFF)
+
+idt_init_one:
+    /* Target Low (word) */
+    mov ecx, ebx
+    mov word [eax], cx
+    add eax, 2
+
+    /* Code Selector (word) */
+    mov word[eax], CODE_SEL_64
+    add eax, 2
+
+    /* IST (byte) */
+    mov byte[eax], 0
+    add eax, 1
+
+    /* Flags (byte) */
+    mov byte[eax], (FLAG_P|FLAG_R0|FLAG_INTERRUPT)
+    add eax, 1
+
+    /* Target High (word) */
+    shr ecx, 16
+    mov word[eax], cx
+    add eax, 2
+
+    /* Long Mode Target High 32 */
+    mov dword[eax], (VIRT_BASE >> 32)
+    add eax, 4
+
+    mov dword[eax], 0
+    add eax, 4
+
+    add ebx, ISR_SIZE
+
+    cmp eax, IDTEND
+    jl idt_init_one
+
+    lidt[IDTR]
+    ret
+
+BITS 64
+GLOBAL fixup_idtr
+fixup_idtr:
+    mov rax, VIRT_BASE + IDTR + 2
+
+    mov rbx, VIRT_BASE + IDT
+
+    mov qword[rax], rbx
+
+    sub rax, 2
+    lidt[rax]
+    ret
diff --git a/include/early.h b/include/early.h
new file mode 100644 (file)
index 0000000..a1fcbb5
--- /dev/null
@@ -0,0 +1,109 @@
+#ifndef EARLY_H
+#define EARLY_H
+
+#define VIRT_BASE 0xFFFFFFFF80000000
+
+/* 4k page size */
+#define PAGE_SHIFT      12
+#define PAGE_SIZE       (1 << PAGE_SHIFT)
+#define PAGE_MASK      (PAGE_SIZE - 1)
+
+#define PTE_SHIFT      (PAGE_SHIFT + 9*0)
+#define PDE_SHIFT      (PAGE_SHIFT + 9*1)
+#define PDPE_SHIFT     (PAGE_SHIFT + 9*2)
+#define PML4E_SHIFT    (PAGE_SHIFT + 9*3)
+
+/* Find index based on virtual address */
+#define PTE(x)         (((x) >> PTE_SHIFT) & 0x1FF)
+#define PDE(x)         (((x) >> PDE_SHIFT) & 0x1FF)
+#define PDPE(x)                (((x) >> PDPE_SHIFT) & 0x1FF)
+#define PML4E(x)       (((x) >> PML4E_SHIFT) & 0x1FF)
+
+/* Find page structure based on virtual address */
+
+/* Because we mapped the PML4 page into itself as the second to last entry, and
+ * the structure of the PML4/PDP/PD/PTs are all compatible, we can then use the
+ * bits in the virtual address to locate its relevant page structures.
+ *
+ * With the standard 4-level page tables, the "leaves" are data pages.
+ *
+ * With the PML4 mapped into itself at 0x1fe, if you have an address with PML4E
+ * = 0x1fe, then the PML4 becomes the PDP, the PDP becomes the PD, the PD
+ * becomes the PT and PTs are now the leaves and accessible.
+ *
+ * If you create an address with PML4E = 0x1fe and PDP = 0x1fe, then the PDs
+ * are the leaves and accessible.
+ *
+ * So if we want to access, for example, the page directory for a certain
+ * address, we can set PML4E = 0x1fe and PDP = 0x1fe... but what are the rest
+ * of the bits? The look ups that the MMU already does! The PML4E in the
+ * address becomes the PDE index and the PDPE in the address becomes the PTE
+ * index.
+ */
+
+/* The BASE definitions are merely handling setting the entries we know need to
+ * be 0x1FE for each form of lookup. P_VIRT_BASE is just the sign extension we
+ * know must be present for a valid address.
+ *
+ * As we layed out above, to get the PTs to be the leaf nodes (accessible
+ * data), just the PML4E has to be 0x1fe, and so on
+ */
+
+#define P_VIRT_BASE    (0xFFFF000000000000)
+#define PT_VIRT_BASE   (P_VIRT_BASE | ((long) 0x1FE << PML4E_SHIFT))
+#define PD_VIRT_BASE   (PT_VIRT_BASE | ((long) 0x1FE << PDPE_SHIFT))
+#define PDP_VIRT_BASE  (PD_VIRT_BASE | ((long) 0x1FE << PDE_SHIFT))
+#define PML4_VIRT_BASE (PDP_VIRT_BASE | ((long) 0x1FE << PTE_SHIFT))
+
+/*
+ * Derive addresses of these entries. This macro is complicated so let's break
+ * it down.
+ *
+ * Let's say I'm trying to find PTE address for 0x12345678, which in binary is:
+ *
+ * 00 | 01 0010 001 | 1 0100 0101 | 0110 0111 1000
+ *    | PDE         | PTE         | PAGE OFFSET
+ *
+ * There's no sign extension, but nonetheless (x & ~P_VIRT_BASE) doesn't hurt.
+ * Then we shift it down by 9, we get
+ *
+ * 0000 0 | 000 0000 00 | 01 0010 001 | 1 0100 0101 011
+ *        | PDE         | PTE         | PAGE OFFSET
+ *
+ * As you can see, the new PTE is the old PDE, the new PDE is the old PDP (all
+ * zeroes) and so forth, but the top three bits of the original page offset are
+ * now the bottom three bits of it, and are irrelevant, so we zero them with an
+ * (& ~7).
+ *
+ * 0000 0 | 000 0000 00 | 01 0010 001 | 1 0100 0101 000
+ *        | PDE         | PTE         | PAGE OFFSET
+ *
+ * Now that we've properly shifted all of our lookups, and discarded the
+ * unnecessary page offset and sign extension bits, we can and in the BASE,
+ * which as explained above is a constructed value where all of the unset
+ * lookups are set to 0x1fe. In this case, our PML4E which is now 0 thanks to
+ * shifting, needs to be set to 0x1fe, which is exactly what PT_VIRT_BASE
+ * includes, along with the sign extension required due to the top bit of the
+ * PML4E being 1
+ */
+
+#define PTE_ADDR(x)      (PT_VIRT_BASE  | (((x & ~P_VIRT_BASE) >> 9)  & ~7))
+#define PDE_ADDR(x)      (PD_VIRT_BASE  | (((x & ~P_VIRT_BASE) >> 18) & ~7))
+#define PDPE_ADDR(x)     (PDP_VIRT_BASE  | (((x & ~P_VIRT_BASE) >> 27) & ~7))
+#define PML4E_ADDR(x)    (PML4_VIRT_BASE | (((x & ~P_VIRT_BASE) >> 36) & ~7))
+
+#define PF_P                (1 << 0)
+#define PF_RW               (1 << 1)
+#define PF_USER             (1 << 2)
+#define PF_WRITETHRU        (1 << 3)
+#define PF_DISABLE_CACHE    (1 << 4)
+
+/* Early physical addresses */
+#define KERNEL_START 0x100000
+
+#define S_PAGES                        2       // 8k stack, for now
+#define STACK_PAGES_PHYS       0       // Starting at 0
+#define STACK_PAGES_START (KERNEL_START - (S_PAGES * PAGE_SIZE))
+
+
+#endif
diff --git a/include/grub.h b/include/grub.h
new file mode 100644 (file)
index 0000000..c5c5e47
--- /dev/null
@@ -0,0 +1,17 @@
+#ifndef GRUB_H
+#define GRUB_H
+
+struct grub_signature {
+       unsigned int magic;
+       unsigned int flags;
+       unsigned int checksum;
+};
+
+#define GRUB_MAGIC 0x1BADB002
+#define GRUB_FLAGS 0x0
+#define GRUB_CHECKSUM (-1 * (GRUB_MAGIC + GRUB_FLAGS))
+
+struct grub_signature gs __attribute__ ((section (".grub_sig"))) =
+       { GRUB_MAGIC, GRUB_FLAGS, GRUB_CHECKSUM };
+
+#endif
diff --git a/include/kernel.h b/include/kernel.h
new file mode 100644 (file)
index 0000000..70ee2c1
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef KERNEL_H
+#define KERNEL_H
+
+#include <early.h>
+
+#endif
diff --git a/kernel/main.c b/kernel/main.c
new file mode 100644 (file)
index 0000000..e2ce019
--- /dev/null
@@ -0,0 +1,7 @@
+#include <kernel.h>
+#include <grub.h>
+
+void main (void)
+{
+    asm ("hlt" : :);
+}
index 9584ff7..96035fa 100644 (file)
--- a/linker.ld
+++ b/linker.ld
@@ -1,12 +1,18 @@
 OUTPUT_FORMAT("elf64-x86-64")
-ENTRY(main)
+ENTRY(entry)
 SECTIONS
 {
-    .grub_sig 0xFFFFFFFF80100000 : AT(0x100000)
+    kernel_start = 0xFFFFFFFF80100000;
+
+    .grub_sig 0x100000 :
     {
         *(.grub_sig)
     }
-    .text :
+    .text_early 0x100080 :
+    {
+        *(.text_early)
+    }
+    .text 0xFFFFFFFF80102000 : AT(0x102000)
     {
         *(.text)
     }
@@ -23,4 +29,6 @@ SECTIONS
         *(.comment)
         *(.eh_frame)
     }
+    kernel_end = . ;
+    kernel_size = kernel_end - kernel_start;
 }
diff --git a/main.c b/main.c
deleted file mode 100644 (file)
index aed4eb7..0000000
--- a/main.c
+++ /dev/null
@@ -1,17 +0,0 @@
-struct grub_signature {
-       unsigned int magic;
-       unsigned int flags;
-       unsigned int checksum;
-};
-
-#define GRUB_MAGIC 0x1BADB002
-#define GRUB_FLAGS 0x0
-#define GRUB_CHECKSUM (-1 * (GRUB_MAGIC + GRUB_FLAGS))
-
-struct grub_signature gs __attribute__ ((section (".grub_sig"))) =
-       { GRUB_MAGIC, GRUB_FLAGS, GRUB_CHECKSUM };
-
-void main (void)
-{
-    while(1) {}
-}