Start work on kernel start-up code.
authorBen Pfaff <blp@cs.stanford.edu>
Sun, 30 Jan 2005 07:49:42 +0000 (07:49 +0000)
committerBen Pfaff <blp@cs.stanford.edu>
Sun, 30 Jan 2005 07:49:42 +0000 (07:49 +0000)
src/threads/init.c
src/threads/loader.S
src/threads/loader.h
src/threads/start.S

index 73a2e98305ffed99b683a9a350624e44c3f78717..b98e580feab1e59eae30775fd62f696cd2bd3f10 100644 (file)
@@ -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)));
 }
index aeb58bc9fd55b572d670fe5087fbd5bdbdb96c06..fd0f59eda57d40decd38b41c4113ea36b1f33319 100644 (file)
@@ -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
index b412af362f546f429324d60cee952cd6577ebf73..7b317f3989f449d62640e1d814dc95aeba77ccd0 100644 (file)
@@ -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.
 
    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. */
index 5a495c4651fecd6bf3d920130123f47f55f6588e..4c6ed2f84a7a05c59af52d44f405d23c1c167965 100644 (file)
 #### 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