*/
#include "threads/loader.h"
-#include "threads/mmu.h"
+
+ .intel_syntax noprefix
#### Kernel loader.
#### memory, and jumps to the first byte of the kernel, where start.S
#### is linked.
-/* Flags in control register 0 */
+/* Flags in control register 0. */
#define CR0_PE 0x00000001 /* Protection Enable. */
#define CR0_EM 0x00000004 /* (Floating-point) Emulation. */
#define CR0_PG 0x80000000 /* Paging. */
# Code runs in real mode, which is a 16-bit segment.
+.globl start
+start:
.code16
-# Disable interrupts.
-# String instructions go upward.
-
- cli
- cld
-
-# Set up segment registers and stack.
+# Set up segment registers.
# 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, 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
-
-#### 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.
+ sub ax, ax
+ mov ds, ax
+ mov ss, ax
+ mov sp, 0x7c00
- movb $0x88, %ah
- int $0x15
- jc panic # Carry flag set on error
- addl $1024, %eax # Total kB
- shrl $2, %eax # Total 4 kB pages
- movl %eax, ram_pages
+# Scan floppy disks.
+
+ sub dl, dl
+ sub ebx, ebx
+ mov eax, 0x7e00
+ mov es, ax
+1: call scan_partitions
+ inc dl
+ jnc 1b
+
+# Scan hard disks.
+ mov dl, 0x80
+1: call scan_partitions
+ inc dl
+ jnc 1b
+
+ call panic
+ .asciz "No boot partition"
+
+scan_partitions:
+ # EBX = sector number of partition table
+ # DL = drive number
+ # ES:0000 -> buffer for partition table
+ # Returns CF set if drive exists, CF clear otherwise.
+ call read_sector
+ jnc no_such_drive
+2: cmp word ptr [es:510], 0xaa55
+ jnz no_boot_partition
-#### 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
+ mov si, 446
+1: mov al, [es:si+4]
+ cmp al, 0x20
+ jz found_boot_partition
+ cmp al, 0x05
+ jz found_extended_partition
+ cmp al, 0x0f
+ jz found_extended_partition
+ cmp al, 0x85
+ jz found_extended_partition
+ cmp al, 0xc5
+ jz found_extended_partition
+next_parttbl_entry:
+ add si, 16
+ cmp si, 510
+ jb 1b
+
+no_boot_partition:
+ clc
+ ret
+no_such_drive:
+ stc
+ ret
+
+found_extended_partition:
+ # DL = drive number.
+ # ES:SI -> partition table entry for extended partition.
+ # Recursively examine it.
+ pusha
+ mov ebx, es:[si+8]
+ mov ax, es
+ add ax, 0x20
+ mov es, ax
+ call scan_partitions
+ popa
+ jmp next_parttbl_entry
+
+found_boot_partition:
+ mov ebx, [es:si+8] # EBX = first sector
+ mov cx, [es:si+12] # CX = number of sectors
+ mov ax, 0x1000 # ES:0000 -> load address
+ mov es, ax
+1: call read_sector
+ add ax, 0x20
+ mov es, ax
loop 1b
-# Set page directory base register.
-
- movl $0x10000, %eax
- movl %eax, %cr3
-
-#### 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.
-
- 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
+ ljmp 0x1000, 0
-# 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 + LOADER_PHYS_BASE, %edi
+ # ebx: sector number
+ # dl: drive number
+ # es:0000: destination buffer
+ # returns error flag in CF
read_sector:
-
-# Poll status register while controller busy.
-
- movl $0x1f7, %edx
-1: inb %dx, %al
- testb $0x80, %al
- jnz 1b
-
-# 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.
-
- movl %ebx, %eax
- andl $0x0fffffff, %eax
- orl $0xe0000000, %eax
-
-# Dump %eax to ports 0x1f3...0x1f6.
-
- movl $4, %ecx
-1: incw %dx
- outb %al, %dx
- shrl $8, %eax
- loop 1b
-
-# READ command to command register.
-
- incw %dx
- movb $0x20, %al
- outb %al, %dx
-
-# Poll status register while controller busy.
-
-1: inb %dx, %al
- testb $0x80, %al
- jnz 1b
-
-# Poll status register until data ready.
-
-1: inb %dx, %al
- testb $0x08, %al
- jz 1b
-
-# Transfer sector.
-
- movl $512 / 4, %ecx
- movl $0x1f0, %edx
- rep insl
-
-# Next sector.
-
- incl %ebx
- cmpl $KERNEL_LOAD_PAGES*8 + 1, %ebx
- jnz read_sector
-
-#### Jump to kernel entry point.
-
- movl $LOADER_PHYS_BASE + LOADER_KERN_BASE, %eax
- call *%eax
- jmp panic
-
-#### GDT
-
-gdt:
- .quad 0x0000000000000000 # null seg
- .quad 0x00cf9a000000ffff # code seg
- .quad 0x00cf92000000ffff # data seg
+ pusha
+ or dl, dl # Floppy drives: DL < 0
+ js read_floppy_sector
+
+read_harddrv_sector:
+ sub eax, eax
+ push eax # LBA sector number [32:63]
+ push ebx # LBA sector number [0:31]
+ mov ax, es
+ shl eax, 4
+ push eax # Buffer linear address
+ push 1 # Transfer one sector
+ push 16 # Packet size
+ mov ah, 0x42 # Extended read
+ mov si, sp # DS:SI -> packet
+ int 0x13 # Error code in CF
+success:
+ popa
+ ret # Error code in CF
+
+read_floppy_sector:
+ #define HEADS 2
+ #define SECTORS 36
+
+ # In: BX = LBA sector number, DL = drive.
+ # Out: BL = drive, DX = cylinder, AL = head, AH = sector.
+ sub ax, ax
+ xchg dx, bx # DX = LBA sector number, BL = drive
+ mov cx, HEADS * SECTORS
+ div cx # AX = cyl, DX = hd + (sec-1) * SECTORS
+ xchg ax, dx # DX = cyl, AX = hd + (sec-1) * SECTORS
+ mov cl, SECTORS
+ div cl # AL = head, AH = sector - 1
+ inc ah
+
+ # Read sector.
+ mov ch, dl # CH = cylinder
+ mov cl, ah # CL = sector
+ mov dh, al # DH = head
+ mov dl, bl # DL = drive
+ mov ax, 0x0201 # AH = function, AL = sectors to read
+ sub bx, bx # ES:BX -> buffer
+ pusha
+ int 0x13
+ popa
+ jnc success
+
+ # Reset floppy drive motor, try again.
+ sub ah, ah
+ int 0x13
+ popa
+ jmp read_sector
-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.
- movw $panicmsg, %si
- movb $0xe, %ah
- subb %bh, %bh
-1: lodsb
- test %al, %al
-2: jz 2b # Spin.
- int $0x10
- jmp 1b
-
-panicmsg:
- .ascii "Loader panic!\r\n"
- .byte 0
-
-#### 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.
- .org LOADER_CMD_LINE - LOADER_BASE
-cmd_line:
- .fill 0x80, 1, 0
+#### The partition table goes here.
+ .org 446
+part_tbl:
#### Boot-sector signature for BIOS inspection.
- .org LOADER_BIOS_SIG - LOADER_BASE
+ .org 510
.word 0xaa55