Implement a proper block layer with partition support.
[pintos-anon] / src / threads / start.S
index 68c604cc276b33a6701dab250d16784de3d45bd0..29ffa7a42f3ed45a948befce9bceb9bd3c518ff5 100644 (file)
-#### The loader needs to have some way to know the kernel's entry
-#### point, that is, the address to which it should jump to start the
-#### kernel.  We handle this by writing the linker script kernel.lds.S
-#### so that this module appears at the very beginning of the kernel
-#### image, and then using that as the entry point.
-
-.section .start
-       
-.globl start
+       #include "threads/loader.h"
+
+#### Kernel startup code.
+
+#### The loader (in loader.S) loads the kernel at physical address
+#### 0x20000 (128 kB) and jumps to "start", defined here.  This code
+#### switches from real mode to 32-bit protected mode and calls
+#### main().
+
+/* Flags in control register 0. */
+#define CR0_PE 0x00000001      /* Protection Enable. */
+#define CR0_EM 0x00000004      /* (Floating-point) Emulation. */
+#define CR0_PG 0x80000000      /* Paging. */
+#define CR0_WP 0x00010000      /* Write-Protect enable in kernel mode. */
+
+       .section .start
+
+# The following code runs in real mode, which is a 16-bit code segment.
+       .code16
+
 .func start
-       # Terminate the backtrace that debug_backtrace() would output.
-       movl $0, %ebp
+.globl start
+start:
+
+# The loader called into us with CS = 0x2000, SS = 0x0000, ESP = 0xf000,
+# but we should initialize the other segment registers.
+
+       mov $0x2000, %ax
+       mov %ax, %ds
+       mov %ax, %es
+
+# Set string instructions to go upward.
+       cld
+
+#### Get memory size, via interrupt 15h function 88h (see [IntrList]),
+#### which returns AX = (kB of physical memory) - 1024.  This only
+#### works for memory sizes <= 65 MB, which should be fine for our
+#### purposes.  We cap memory at 64 MB because that's all we prepare
+#### page tables for, below.
+
+       movb $0x88, %ah
+       int $0x15
+       addl $1024, %eax        # Total kB memory
+       cmp $0x10000, %eax      # Cap at 64 MB
+       jbe 1f
+       mov $0x10000, %eax
+1:     shrl $2, %eax           # Total 4 kB pages
+       addr32 movl %eax, init_ram_pages - LOADER_PHYS_BASE - 0x20000
+
+#### Enable A20.  Address line 20 is tied low when the machine boots,
+#### which prevents addressing memory about 1 MB.  This code fixes it.
+
+# Poll status register while busy.
+
+1:     inb $0x64, %al
+       testb $0x2, %al
+       jnz 1b
+
+# Send command for writing output port.
+
+       movb $0xd1, %al
+       outb %al, $0x64
+
+# Poll status register while busy.
+
+1:     inb $0x64, %al
+       testb $0x2, %al
+       jnz 1b
+
+# Enable A20 line.
+
+       movb $0xdf, %al
+       outb %al, $0x60
+
+# Poll status register while busy.
+
+1:     inb $0x64, %al
+       testb $0x2, %al
+       jnz 1b
+
+#### Create temporary page directory and page table and set page
+#### directory base register.
+
+# Create page directory at 0xf000 (60 kB) and fill with zeroes.
+       mov $0xf00, %ax
+       mov %ax, %es
+       subl %eax, %eax
+       subl %edi, %edi
+       movl $0x400, %ecx
+       rep stosl
 
-       # Call main.
-start: call main
+# Add PDEs to point to page tables for the first 64 MB of RAM.
+# Also add identical PDEs starting at LOADER_PHYS_BASE.
+# See [IA32-v3a] section 3.7.6 "Page-Directory and Page-Table Entries"
+# for a description of the bits in %eax.
+
+       movl $0x10007, %eax
+       movl $0x11, %ecx
+       subl %edi, %edi
+1:     movl %eax, %es:(%di)
+       movl %eax, %es:LOADER_PHYS_BASE >> 20(%di)
+       addw $4, %di
+       addl $0x1000, %eax
+       loop 1b
+
+# Set up page tables for one-to-map linear to physical map for the
+# first 64 MB of RAM.
+# See [IA32-v3a] section 3.7.6 "Page-Directory and Page-Table Entries"
+# for a description of the bits in %eax.
+
+       movw $0x1000, %ax
+       movw %ax, %es
+       movl $0x7, %eax
+       movl $0x4000, %ecx
+       subl %edi, %edi
+1:     movl %eax, %es:(%di)
+       addw $4, %di
+       addl $0x1000, %eax
+       loop 1b
+
+# Set page directory base register.
+
+       movl $0xf000, %eax
+       movl %eax, %cr3
+
+#### Switch to protected mode.
+
+# First, disable interrupts.  We won't set up the IDT until we get
+# into C code, so any interrupt would blow us away.
+
+       cli
+
+# Protected mode requires a GDT, so point the GDTR to our 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).
+# The CPU doesn't need an addr32 prefix but ELF doesn't do 16-bit
+# relocations.
+
+       data32 addr32 lgdt gdtdesc - LOADER_PHYS_BASE - 0x20000
+
+# 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.
+
+       movl %cr0, %eax
+       orl $CR0_PE | CR0_PG | CR0_WP | CR0_EM, %eax
+       movl %eax, %cr0
+
+# 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 running in a 32-bit segment the data32 prefix is
+# needed to jump to a 32-bit offset in the target segment.
+
+       data32 ljmp $SEL_KCSEG, $1f
+
+# We're now in protected mode in a 32-bit segment.
+# Let the assembler know.
+
+       .code32
+
+# Reload all the other segment registers and the stack pointer to
+# point into our new GDT.
+
+1:     mov $SEL_KDSEG, %ax
+       mov %ax, %ds
+       mov %ax, %es
+       mov %ax, %fs
+       mov %ax, %gs
+       mov %ax, %ss
+       addl $LOADER_PHYS_BASE, %esp
+       movl $0, %ebp                   # Null-terminate main()'s backtrace
+
+#### Call main().
+
+       call main
+
+# main() shouldn't ever return.  If it does, spin.
 
-       # main() should not return, but if it does, spin.
 1:     jmp 1b
 .endfunc
+
+#### GDT
+
+       .align 8
+gdt:
+       .quad 0x0000000000000000        # Null segment.  Not used by CPU.
+       .quad 0x00cf9a000000ffff        # System code, base 0, limit 4 GB.
+       .quad 0x00cf92000000ffff        # System data, base 0, limit 4 GB.
+
+gdtdesc:
+       .word   gdtdesc - gdt - 1       # Size of the GDT, minus 1 byte.
+       .long   gdt                     # Address of the GDT.
+
+#### Physical memory size in 4 kB pages.  This is exported to the rest
+#### of the kernel.
+.globl init_ram_pages
+init_ram_pages:
+       .long 0
+