From 9869570326bb4e6d39fb41912a9076d53a82ad2d Mon Sep 17 00:00:00 2001 From: Ben Pfaff Date: Sun, 30 Jan 2005 07:49:42 +0000 Subject: [PATCH] Start work on kernel start-up code. --- src/threads/init.c | 28 +++---- src/threads/loader.S | 8 +- src/threads/loader.h | 17 +--- src/threads/start.S | 182 +++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 199 insertions(+), 36 deletions(-) diff --git a/src/threads/init.c b/src/threads/init.c index 73a2e98..b98e580 100644 --- a/src/threads/init.c +++ b/src/threads/init.c @@ -34,9 +34,6 @@ #include "filesys/fsutil.h" #endif -/* Amount of physical memory, in 4 kB pages. */ -size_t ram_pages; - /* Page directory with kernel mappings only. */ uint32_t *base_page_dir; @@ -81,9 +78,9 @@ main (void) argv_init (); /* Initialize memory system. */ + paging_init (); palloc_init (); malloc_init (); - paging_init (); /* Segmentation. */ #ifdef USERPROG @@ -154,9 +151,6 @@ ram_init (void) linker as _start_bss and _end_bss. See kernel.lds. */ extern char _start_bss, _end_bss; memset (&_start_bss, 0, &_end_bss - &_start_bss); - - /* Get RAM size from loader. See loader.S. */ - ram_pages = *(uint32_t *) ptov (LOADER_RAM_PAGES); } /* Populates the base page directory and page table with the @@ -165,8 +159,8 @@ ram_init (void) directory it creates. At the time this function is called, the active page table - (set up by loader.S) only maps the first 4 MB of RAM, so we - should not try to use extravagant amounts of memory. + (set up by start.S) only maps the first 4 MB of RAM, so we + should not try to access memory beyond that limit. Fortunately, there is no need to do so. */ static void paging_init (void) @@ -174,8 +168,8 @@ paging_init (void) uint32_t *pd, *pt; size_t page; - pd = base_page_dir = palloc_get_page (PAL_ASSERT | PAL_ZERO); - pt = NULL; + pd = base_page_dir = ptov (LOADER_PD_BASE); + pt = ptov (LOADER_PT_BASE); for (page = 0; page < ram_pages; page++) { uintptr_t paddr = page * PGSIZE; @@ -185,16 +179,22 @@ paging_init (void) if (pd[pde_idx] == 0) { - pt = palloc_get_page (PAL_ASSERT | PAL_ZERO); + pt += PGSIZE / sizeof *pt; + memset (pt, 0, PGSIZE); pd[pde_idx] = pde_create (pt); } pt[pte_idx] = pte_create_kernel (vaddr, true); } + /* start.S mapped the beginning of physical memory to virtual + address 0. We don't want that mapping anymore, so erase + it. */ + pd[0] = 0; + /* Store the physical address of the page directory into CR3 - aka PDBR (page directory base register). This activates our - new page tables immediately. See [IA32-v2a] "MOV--Move + aka PDBR (page directory base register). This flushes the + TLB to make sure . See [IA32-v2a] "MOV--Move to/from Control Registers" and [IA32-v3] 3.7.5. */ asm volatile ("mov %%cr3, %0" :: "r" (vtop (base_page_dir))); } diff --git a/src/threads/loader.S b/src/threads/loader.S index aeb58bc..fd0f59e 100644 --- a/src/threads/loader.S +++ b/src/threads/loader.S @@ -49,14 +49,14 @@ scan_partitions: call read_sector jc no_such_drive -2: pusha +2: cmp word ptr [es:510], 0xaa55 + jnz no_boot_partition + + pusha mov edx, [es:508] call outw popa - cmp word ptr [es:510], 0xaa55 - jnz no_boot_partition - mov si, 446 1: mov al, [es:si+4] cmp al, 0x20 diff --git a/src/threads/loader.h b/src/threads/loader.h index b412af3..7b317f3 100644 --- a/src/threads/loader.h +++ b/src/threads/loader.h @@ -1,12 +1,10 @@ #ifndef THREADS_LOADER_H #define THREADS_LOADER_H -/* Constants fixed by the PC BIOS. */ -#define LOADER_BASE 0x7c00 /* Physical address of loader's base. */ -#define LOADER_END 0x7e00 /* Physical address of end of loader. */ - -/* Physical address of kernel base. */ -#define LOADER_KERN_BASE 0x100000 /* 1 MB. */ +/* Physical addresses of important kernel components. */ +#define LOADER_PD_BASE 0x0f000 /* Page directory: 4 kB starting at 60 kB. */ +#define LOADER_PT_BASE 0x10000 /* Page tables: 64 kB starting at 64 kB. */ +#define LOADER_KERN_BASE 0x20000 /* Kernel: up to 512 kB starting at 128 kB. */ /* Kernel virtual address at which all physical memory is mapped. @@ -17,13 +15,6 @@ This must be aligned on a 4 MB boundary. */ #define LOADER_PHYS_BASE 0xc0000000 /* 3 GB. */ -/* Offsets within the loader. */ -#define LOADER_BIOS_SIG (LOADER_END - 2) /* 0xaa55 BIOS signature. */ -#define LOADER_CMD_LINE_LEN 0x80 /* Command line length. */ -#define LOADER_CMD_LINE (LOADER_BIOS_SIG - LOADER_CMD_LINE_LEN) - /* 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. */ diff --git a/src/threads/start.S b/src/threads/start.S index 5a495c4..4c6ed2f 100644 --- a/src/threads/start.S +++ b/src/threads/start.S @@ -4,13 +4,185 @@ #### so that this module appears at the very beginning of the kernel #### image, and then using that as the entry point. +#include "threads/loader.h" + .intel_syntax noprefix + .section .start +# Code runs in real mode, which is a 16-bit segment. + .code16 + .globl start -.func start - # Call main. -start: call main +start: + +# Disable interrupts. +# String instructions go upward. + + cli + cld + +# Set up data segments. + + sub ax, ax + mov ds, ax + mov es, ax + +#### Enable A20. Address line 20 is tied to low when the machine +#### boots, which prevents addressing memory about 1 MB. This code +#### fixes it. + +# Poll status register while busy. + +1: in al, 0x64 + test al, 0x2 + jnz 1b + +# Send command for writing output port. + + mov al, 0xd1 + outb 0x64, al + +# Poll status register while busy. + +1: in al, 0x64 + test al, 0x2 + jnz 1b + +# Enable A20 line. + + mov al, 0xdf + out 0x60, al + +#### Get memory size, via interrupt 15h function 88h, which returns CF +#### clear if successful, with AX = (kB of physical memory) - 1024. +#### This only works for memory sizes <= 65 MB, which should be fine +#### for our purposes. We only reserve enough memory for page tables +#### for 64 MB of RAM, so we cap it at that value. + + mov ah, 0x88 + int 0x15 + jc panic + cli # BIOS might have enabled interrupts + add eax, 1024 # Total kB memory + cmp eax, 64 * 1024 + jbe 1f + mov eax, 64 * 1024 +1: shr eax, 2 # Total 4 kB pages + mov [ram_pages], eax + +#### Create temporary page directory and page table and set page +#### directory base register. + +# Create page directory at 64 kB and fill with zeroes. + mov ax, LOADER_PD_BASE / 16 + mov es, ax + sub eax, eax + sub edi, edi + mov ecx, 0x400 + rep stosd + +# Add a PDE mapping both virtual addresses 0 and LOADER_PHYS_BASE +# to a single PTE. +# See [IA32-v3] section 3.7.6 for a description of the bits in eax. + + mov eax, LOADER_PT_BASE / 16 + 7 + mov es:[0], eax + mov es:[LOADER_PHYS_BASE / 1024 / 1024], eax + +# Set up one-to-map linear to physical map for the first 4 MB of RAM. +# See [IA32-v3] section 3.7.6 for a description of the bits in eax. - # main() should not return, but if it does, spin. + mov ax, LOADER_PT_BASE / 16 + mov es, ax + mov eax, 0x7 + mov cx, 0x400 +1: stosd + add eax, 0x1000 + loop 1b + +# Set page directory base register. + + mov eax, LOADER_PD_BASE + mov cr3, eax + +#### Switch to protected mode. + +# Then we point the GDTR to our GDT. Protected mode requires a GDT. +# We need a data32 prefix to ensure that all 32 bits of the GDT +# descriptor are loaded (default is to load only 24 bits). + + data32 lgdt gdtdesc + +# Then we turn on the following bits in CR0: +# PE (Protect Enable): this turns on protected mode. +# PG (Paging): turns on paging. +# WP (Write Protect): if unset, ring 0 code ignores +# write-protect bits in page tables (!). +# EM (Emulation): forces floating-point instructions to trap. +# We don't support floating point. + +#define CR0_PE 0x00000001 +#define CR0_EM 0x00000004 +#define CR0_PG 0x80000000 +#define CR0_WP 0x00010000 + + mov eax, cr0 + or eax, CR0_PE + CR0_PG + CR0_WP + CR0_EM + mov cr0, eax + +# We're now in protected mode in a 16-bit segment. The CPU still has +# the real-mode code segment cached in cs's segment descriptor. We +# need to reload cs, and the easiest way is to use a far jump. +# Because we're not in a 32-bit segment the data32 prefix is needed to +# jump to a 32-bit offset. + + data32 ljmp SEL_KCSEG, 1f + LOADER_PHYS_BASE + +# We're now in protected mode in a 32-bit segment. + + .code32 + +# Reload all the other segment registers and the stack pointer to +# point into our new GDT. + +1: mov ax, SEL_KDSEG + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + add esp, LOADER_PHYS_BASE + +#### Jump to kernel entry point. + + mov eax, main + call eax 1: jmp 1b -.endfunc + +#### GDT + +gdt: + .quad 0x0000000000000000 # null seg + .quad 0x00cf9a000000ffff # code seg + .quad 0x00cf92000000ffff # data seg + +gdtdesc: + .word 0x17 # sizeof (gdt) - 1 + .long gdt + LOADER_PHYS_BASE # address gdt + +#### Fatal error. +#### Print panicmsg (with help from the BIOS) and spin. + +panic: .code16 # We only panic in real mode. + mov si, offset panicmsg + mov ah, 0xe + sub bh, bh +1: lodsb + test al, al +2: jz 2b # Spin. + int 0x10 + jmp 1b + +panicmsg: + .ascii "Panic!" + .byte 0 -- 2.30.2