X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?p=pintos-anon;a=blobdiff_plain;f=src%2Fthreads%2Fstart.S;h=29ffa7a42f3ed45a948befce9bceb9bd3c518ff5;hp=351b5e1532a95eb286d1c05c82509034cade63f2;hb=a03618133f7df0954802a470a4bee7674f7aed45;hpb=750d21936d284127e265d050ccbce76fca1ece1a diff --git a/src/threads/start.S b/src/threads/start.S index 351b5e1..29ffa7a 100644 --- a/src/threads/start.S +++ b/src/threads/start.S @@ -1,9 +1,204 @@ -# This module gets linked first, so that the kernel entry point -# is the very beginning of its binary image. All we need to do is -# jump to the real entry point. + #include "threads/loader.h" - .globl start -start: call main +#### 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 +.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 + +# 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. - # If main returns, 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 +