-/* This file is derived from source code used in MIT's 6.828
- course. The original copyright notice is reproduced in full
- below. */
-
-/*
- * Copyright (C) 1997 Massachusetts Institute of Technology
- *
- * This software is being provided by the copyright holders under the
- * following license. By obtaining, using and/or copying this software,
- * you agree that you have read, understood, and will comply with the
- * following terms and conditions:
- *
- * Permission to use, copy, modify, distribute, and sell this software
- * and its documentation for any purpose and without fee or royalty is
- * hereby granted, provided that the full text of this NOTICE appears on
- * ALL copies of the software and documentation or portions thereof,
- * including modifications, that you make.
- *
- * THIS SOFTWARE IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO
- * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE,
- * BUT NOT LIMITATION, COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR
- * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR
- * THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY
- * THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. COPYRIGHT
- * HOLDERS WILL BEAR NO LIABILITY FOR ANY USE OF THIS SOFTWARE OR
- * DOCUMENTATION.
- *
- * The name and trademarks of copyright holders may NOT be used in
- * advertising or publicity pertaining to the software without specific,
- * written prior permission. Title to copyright in this software and any
- * associated documentation will at all times remain with copyright
- * holders. See the file AUTHORS which should have accompanied this software
- * for a list of all copyright holders.
- *
- * This file may be derived from previously copyrighted software. This
- * copyright applies only to those changes made by the copyright
- * holders listed in the AUTHORS file. The rest of this file is covered by
- * the copyright notices, if any, listed below.
- */
-
#include "threads/loader.h"
+ .intel_syntax noprefix
+
#### 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. */
-#define CR0_EM 0x00000004 /* (Floating-point) Emulation. */
-#define CR0_PG 0x80000000 /* Paging. */
-#define CR0_WP 0x00010000 /* Write-Protect enable in kernel mode. */
+# Runs in real mode, which is a 16-bit segment.
+ .code16
-# Code runs in real mode, which is a 16-bit segment.
+# Set up segment registers.
+# Stack grows downward starting from us.
.globl start
start:
- .code16
-
-# Disable interrupts.
-# String instructions go upward.
-
- cli
+ sub ax, ax
+ mov ds, ax
+ mov ss, ax
+ mov sp, 0x7c00
cld
-# Set up data segments and stack.
-
- subw %ax, %ax
- movw %ax, %es
- movw %ax, %ds
-
-# Stack grows downward starting from us.
-# We don't ever use the stack so this is strictly speaking
-# unnecessary.
-
- movw %ax, %ss
- movw $0x7c00, %sp
+ mov ax, 0x00e3
+ sub dx, dx
+ int 0x14
+
+# 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 outw
+ call scan_partitions
+ inc dl
+ jnc 1b
+
+1: jmp 1b
+
+scan_partitions:
+ # EBX = sector number of partition table
+ # DL = drive number
+ # ES:0000 -> buffer for partition table
+ # Returns CF set if drive does not exist, CF clear otherwise.
+ call read_sector
+ jc no_such_drive
-#### Enable A20. Address line 20 is tied to low when the machine
-#### boots, which prevents addressing memory about 1 MB. This code
-#### fixes it.
+2: pusha
+ mov edx, [es:508]
+ call outw
+ popa
-# 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. We cap memory at 64 MB because that's all we
-#### prepare page tables for, below.
+ cmp word ptr [es:510], 0xaa55
+ jnz no_boot_partition
- movb $0x88, %ah
- int $0x15
- jc panic
- 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
- movl %eax, ram_pages
-
-#### 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
-
-# Add PDEs to point to PTEs for the first 64 MB of RAM.
-# Also add identical PDEs starting at LOADER_PHYS_BASE.
-# See [IA32-v3] section 3.7.6 for a description of the bits in %eax.
-
- movl $0x11007, %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
+ 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 up one-to-map linear to physical map for the first 64 MB of RAM.
-# See [IA32-v3] section 3.7.6 for a description of the bits in %eax.
-
- movw $0x1100, %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
+ ljmp 0x1000, 0
-# 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
+ # ebx: sector number
+ # dl: drive number
+ # es:0000: destination buffer
+ # returns error flag in CF
+read_sector:
+ pusha
+ or dl, dl # Floppy drives: DL >= 0
+ jns read_floppy_sector
+
+read_harddrv_sector:
+ sub eax, eax
+ push eax # LBA sector number [32:63]
+ push ebx # LBA sector number [0:31]
+ push es
+ push 0
+ 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
+ lahf
+ add sp, 16
+ sahf
+read_sector_done:
+ 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
+ mov di, 3 # Number of tries left.
-# 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
+try_read_floppy:
+ pusha
+ int 0x13
+ popa
+ jnc read_sector_done
+ dec di
+ jnz 1f
+ stc
+ jmp read_sector_done
+
+1: # Reset floppy drive motor, try again.
+ pusha
+ sub ah, ah
+ int 0x13
+ popa
+ jmp try_read_floppy
+
+outw: pushf
+ pusha
-# 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: movw $SEL_KDSEG, %ax
- movw %ax, %ds
- movw %ax, %es
- movw %ax, %fs
- movw %ax, %gs
- movw %ax, %ss
- movl $LOADER_PHYS_BASE + 0x30000, %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
-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
+ mov cx, 8
+ mov ebx, edx
+ mov dx, 0xe9
+1: rol ebx, 4
+ mov al, bl
+ and al, 15
+ add al, '0'
+ cmp al, '9'
+ jbe 2f
+ add al, 'a' - '0' - 10
+2: out dx, al
loop 1b
-# READ command to command register.
-
- incw %dx
- movb $0x20, %al
- outb %al, %dx
+ mov al, '\n'
+ out dx, al
-# 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 $256, %ecx
- movl $0x1f0, %edx
- rep insw
-
-# 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
+ popa
+ popf
+ ret
+
+#### The partition table goes here.
+ .org 446
+part_tbl:
-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
#### Boot-sector signature for BIOS inspection.
- .org LOADER_BIOS_SIG - LOADER_BASE
+ .org 510
.word 0xaa55