From: Ben Pfaff Date: Sat, 11 Sep 2004 19:29:05 +0000 (+0000) Subject: Rewrite loader, improving comments greatly. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=843e338ee1a66bb6069ddf11d0d2650e6cb5c5f3;p=pintos-anon Rewrite loader, improving comments greatly. Also: load GDT relative to at LOADER_PHYS_BASE. --- diff --git a/src/threads/loader.S b/src/threads/loader.S index 9e2f377..3d440d2 100644 --- a/src/threads/loader.S +++ b/src/threads/loader.S @@ -41,16 +41,15 @@ #include "threads/loader.h" #include "threads/mmu.h" -############################################################################## -# Kernel loader. -# -# This code should be stored in the first sector of the hard disk. When the -# BIOS runs, it loads this code at physical address 0x7c00-0x7e00 (512 bytes). -# Then it jumps to the beginning of it, in real mode. -# This code switches into protected mode (32-bit mode) so that all of -# memory can accessed, loads the kernel into memory, and jumps to the -# first byte of the kernel, where start.S is linked. -############################################################################## +#### Kernel loader. + +#### This code should be stored in the first sector of the hard disk. +#### When the BIOS runs, it loads this code at physical address +#### 0x7c00-0x7e00 (512 bytes). Then it jumps to the beginning of it, +#### in real mode. This code switches into protected mode (32-bit +#### mode) so that all of memory can accessed, loads the kernel into +#### memory, and jumps to the first byte of the kernel, where start.S +#### is linked. /* Flags in control register 0 */ #define CR0_PE 0x00000001 /* Protection Enable. */ @@ -58,175 +57,213 @@ #define CR0_PG 0x80000000 /* Paging. */ #define CR0_WP 0x00010000 /* Write-Protect enable in kernel mode. */ -.globl start # Entry point -start: .code16 # This runs in real mode - cli # Disable interrupts - cld # String ops inc - xorw %ax,%ax # Zero - movw %ax,%es # Address - movw %ax,%ds # data - movw %ax,%ss # Set up - movw $start,%sp # stack (grows down) +# Code runs in real mode, which is a 16-bit segment. + + .code16 + +# Disable interrupts. +# String instructions go upward. + + cli + cld + +# Set up segment registers and stack. +# Stack grows downward starting from us. + + subw %ax, %ax + movw %ax, %es + movw %ax, %ds + movw %ax, %ss + movw $0x7c00, %sp -#### Enable A20: -#### Address line 20 is tied to low when the machine boots, -#### obviously this a bit of a drag, such as when trying to -#### address memory above 1MB. This code undoes this. +#### Enable A20. Address line 20 is tied to low when the machine +#### boots, which prevents addressing memory about 1 MB. This code +#### fixes it. -1: inb $0x64,%al # Get status - testb $0x2,%al # Busy? - jnz 1b # Yes - movb $0xd1,%al # Command: Write - outb %al,$0x64 # output port -2: inb $0x64,%al # Get status - testb $0x2,%al # Busy? - jnz 2b # Yes - movb $0xdf,%al # Enable - outb %al,$0x60 # A20 - -#### Get memory size, via interrupt 15h function 88h. -#### 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. +# 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 + +#### Get memory size, via interrupt 15h function 88h. 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. - movb $0x88,%ah + movb $0x88, %ah int $0x15 jc panic # Carry flag set on error -2: addl $1024,%eax # Total kB - shrl $2,%eax # Total 4 kB pages + addl $1024, %eax # Total kB + shrl $2, %eax # Total 4 kB pages movl %eax, ram_pages -#### switch from real to protected mode -#### The segments in GDT allow all of physical memory to be accessed. -#### Furthermore, the segments have base addresses of 0, so that the -#### segment translation is a NOP, ie. virtual addresses are identical to -#### their physical addresses. With this setup, it appears to this code -#### that it is running directly on physical memory. +#### Create temporary page directory and page table and set page +#### directory base register. + +# Create page directory at 64 kB and fill with zeroes. + + mov $0x1000, %ax + mov %ax, %es + subl %eax, %eax + subl %edi, %edi + movl $0x400, %ecx + rep stosl + +# Set PDEs for 0 and LOADER_PHYS_BASE to point to the page table. + + movl $0x11000 | PG_U | PG_W | PG_P, %eax + movl %eax, %es:0 + movl %eax, %es:LOADER_PHYS_BASE >> 20 + +# Initialize page table. + + movl $PG_U | PG_W | PG_P, %eax + movl $0x400, %ecx +1: stosl + addl $0x1000, %eax + loop 1b + +# Set page directory base register. + + movl $0x10000, %eax + movl %eax, %cr3 - cli # Mandatory since we dont set up an IDT - lgdt gdtdesc # load GDT -- mandatory in protected mode - movl %cr0, %eax # turn on protected mode - orl $CR0_PE, %eax # - movl %eax, %cr0 # - ### CPU magic: jump to relocation, flush prefetch queue, and - ### reload %cs Has the effect of just jmp to the next - ### instruction, but simultaneous loads CS with - ### $PROT_MODE_CSEG. - ljmp $SEL_KCSEG, $protcseg +#### Switch to protected mode. + +# First we turn off interrupts because we don't set up an IDT. + + cli + +# 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. -#### We are in protected mode in a 32-bit segment (hence the .code32) -protcseg: + 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 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 - movw $SEL_KDSEG, %ax # set up data segment registers + +# Reload all the other segment registers and the stack pointer to +# point into our new GDT. + +1: movw $SEL_KDSEG, %ax movw %ax, %ds movw %ax, %es movw %ax, %fs movw %ax, %gs movw %ax, %ss + movl $LOADER_PHYS_BASE + 0x20000, %esp #### Load kernel starting at physical address LOADER_KERN_BASE by #### frobbing the IDE controller directly. movl $1, %ebx - movl $LOADER_KERN_BASE, %edi + movl $LOADER_KERN_BASE + LOADER_PHYS_BASE, %edi read_sector: - ### Poll status register while controller busy. +# Poll status register while controller busy. + movl $0x1f7, %edx 1: inb %dx, %al testb $0x80, %al jnz 1b - ### Read a single sector. +# Read a single sector. + movl $0x1f2, %edx movb $1, %al outb %al, %dx - ### Sector number to write in low 28 bits. - ### LBA mode, device 0 in top 4 bits. +# Sector number to write in low 28 bits. +# LBA mode, device 0 in top 4 bits. + movl %ebx, %eax andl $0x0fffffff, %eax orl $0xe0000000, %eax - ### Dump %eax to ports 0x1f3...0x1f6. +# Dump %eax to ports 0x1f3...0x1f6. + movl $4, %ecx -2: incw %dx +1: incw %dx outb %al, %dx shrl $8, %eax - loop 2b + loop 1b + +# READ command to command register. - ### READ command to command register. incw %dx movb $0x20, %al outb %al, %dx - ### Poll status register while controller busy. +# Poll status register while controller busy. + 1: inb %dx, %al testb $0x80, %al jnz 1b - ### Poll status register until data ready. +# Poll status register until data ready. + 1: inb %dx, %al testb $0x08, %al jz 1b - ### Transfer sector. +# Transfer sector. + movl $512 / 4, %ecx movl $0x1f0, %edx rep insl - ### Next sector. +# Next sector. + incl %ebx cmpl $KERNEL_LOAD_PAGES*8 + 1, %ebx jnz read_sector -##### Create temporary page directory and page table, set page -##### directory pointer, and turn on paging. -##### FIXME? We could use a single 4 MB page instead of 1024 4 kB pages. - - # Create page directory at 64 kB. - movl $0x10000, %edi - movl %edi, %cr3 - - # Fill page directory with zeroes. - subl %eax, %eax - movl $0x400, %ecx - rep stosl - - # Set PDEs for 0 and LOADER_PHYS_BASE to point to the - # page table. - movl $0x11000 | PG_U | PG_W | PG_P, %eax - movl %eax, 0x10000 - movl %eax, 0x10000 | (LOADER_PHYS_BASE >> 20) - - # Initialize page table. - movl $PG_U | PG_W | PG_P, %eax - movl $0x400, %ecx -1: stosl - addl $0x1000, %eax - loop 1b - - # Turn on paging and kernel write-protect. - movl %cr0, %eax - orl $CR0_PG | CR0_WP, %eax - movl %eax, %cr0 - jmp 1f -1: - -##### Turn on EM bit in CR0, forcing most floating-point instructions -##### to trap. We don't support floating-point or MMX. - - movl %cr0, %eax - orl $CR0_EM, %eax - movl %eax, %cr0 - -##### Jump to kernel entry point. +#### Jump to kernel entry point. - movl $LOADER_PHYS_BASE + 0x20000, %esp movl $LOADER_PHYS_BASE + LOADER_KERN_BASE, %eax call *%eax jmp panic -##### GDT +#### GDT gdt: .quad 0x0000000000000000 # null seg @@ -235,13 +272,15 @@ gdt: gdtdesc: .word 0x17 # sizeof (gdt) - 1 - .long gdt # address gdt + .long gdt + LOADER_PHYS_BASE # address gdt + +#### Fatal error. +#### Print panicmsg (with help from the BIOS) and spin. -##### To panic, we print panicmsg (with help from the BIOS) and spin. panic: .code16 # We only panic in real mode. movw $panicmsg, %si movb $0xe, %ah - xorb %bh, %bh + subb %bh, %bh 1: lodsb test %al, %al 2: jz 2b # Spin. @@ -252,18 +291,18 @@ panicmsg: .ascii "Loader panic!\r\n" .byte 0 -##### Memory size in 4 kB pages. +#### Memory size in 4 kB pages. .org LOADER_RAM_PAGES - LOADER_BASE ram_pages: .long 0 -##### Command-line arguments inserted by another utility. -##### The loader doesn't use these, but we note their -##### location here for easy reference. +#### Command-line arguments inserted by another utility. +#### The loader doesn't use these, but we note their +#### location here for easy reference. .org LOADER_CMD_LINE - LOADER_BASE cmd_line: .fill 0x80, 1, 0 -##### Boot-sector signature for BIOS inspection. +#### Boot-sector signature for BIOS inspection. .org LOADER_BIOS_SIG - LOADER_BASE .word 0xaa55