From 4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b Mon Sep 17 00:00:00 2001 From: manuel Date: Tue, 27 Mar 2012 11:51:08 +0200 Subject: reorganize file structure to match the upstream requirements --- threads/loader.S | 263 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 263 insertions(+) create mode 100644 threads/loader.S (limited to 'threads/loader.S') diff --git a/threads/loader.S b/threads/loader.S new file mode 100644 index 0000000..dd87ea1 --- /dev/null +++ b/threads/loader.S @@ -0,0 +1,263 @@ +#include "threads/loader.h" + +#### Kernel loader. + +#### This code should be stored in the first sector of a hard disk. +#### When the BIOS runs, it loads this code at physical address +#### 0x7c00-0x7e00 (512 bytes) and jumps to the beginning of it, +#### in real mode. The loader loads the kernel into memory and jumps +#### to its entry point, which is the start function in start.S. +#### +#### The BIOS passes in the drive that the loader was read from as +#### DL, with floppy drives numbered 0x00, 0x01, ... and hard drives +#### numbered 0x80, 0x81, ... We want to support booting a kernel on +#### a different drive from the loader, so we don't take advantage of +#### this. + +# Runs in real mode, which is a 16-bit segment. + .code16 + +# Set up segment registers. +# Set stack to grow downward from 60 kB (after boot, the kernel +# continues to use this stack for its initial thread). + + sub %ax, %ax + mov %ax, %ds + mov %ax, %ss + mov $0xf000, %esp + +# Configure serial port so we can report progress without connected VGA. +# See [IntrList] for details. + sub %dx, %dx # Serial port 0. + mov $0xe3, %al # 9600 bps, N-8-1. + # AH is already 0 (Initialize Port). + int $0x14 # Destroys AX. + + call puts + .string "PiLo" + +#### Read the partition table on each system hard disk and scan for a +#### partition of type 0x20, which is the type that we use for a +#### Pintos kernel. +#### +#### Read [Partitions] for a description of the partition table format +#### that we parse. +#### +#### We print out status messages to show the disk and partition being +#### scanned, e.g. hda1234 as we scan four partitions on the first +#### hard disk. + + mov $0x80, %dl # Hard disk 0. +read_mbr: + sub %ebx, %ebx # Sector 0. + mov $0x2000, %ax # Use 0x20000 for buffer. + mov %ax, %es + call read_sector + jc no_such_drive + + # Print hd[a-z]. + call puts + .string " hd" + mov %dl, %al + add $'a' - 0x80, %al + call putc + + # Check for MBR signature--if not present, it's not a + # partitioned hard disk. + cmpw $0xaa55, %es:510 + jne next_drive + + mov $446, %si # Offset of partition table entry 1. + mov $'1', %al +check_partition: + # Is it an unused partition? + cmpl $0, %es:(%si) + je next_partition + + # Print [1-4]. + call putc + + # Is it a Pintos kernel partition? + cmpb $0x20, %es:4(%si) + jne next_partition + + # Is it a bootable partition? + cmpb $0x80, %es:(%si) + je load_kernel + +next_partition: + # No match for this partition, go on to the next one. + add $16, %si # Offset to next partition table entry. + inc %al + cmp $510, %si + jb check_partition + +next_drive: + # No match on this drive, go on to the next one. + inc %dl + jnc read_mbr + +no_such_drive: +no_boot_partition: + # Didn't find a Pintos kernel partition anywhere, give up. + call puts + .string "\rNot found\r" + + # Notify BIOS that boot failed. See [IntrList]. + int $0x18 + +#### We found a kernel. The kernel's drive is in DL. The partition +#### table entry for the kernel's partition is at ES:SI. Our job now +#### is to read the kernel from disk and jump to its start address. + +load_kernel: + call puts + .string "\rLoading" + + # Figure out number of sectors to read. A Pintos kernel is + # just an ELF format object, which doesn't have an + # easy-to-read field to identify its own size (see [ELF1]). + # But we limit Pintos kernels to 512 kB for other reasons, so + # it's easy enough to just read the entire contents of the + # partition or 512 kB from disk, whichever is smaller. + mov %es:12(%si), %ecx # EBP = number of sectors + cmp $1024, %ecx # Cap size at 512 kB + jbe 1f + mov $1024, %cx +1: + + mov %es:8(%si), %ebx # EBX = first sector + mov $0x2000, %ax # Start load address: 0x20000 + +next_sector: + # Read one sector into memory. + mov %ax, %es # ES:0000 -> load address + call read_sector + jc read_failed + + # Print '.' as progress indicator once every 16 sectors == 8 kB. + test $15, %bl + jnz 1f + call puts + .string "." +1: + + # Advance memory pointer and disk sector. + add $0x20, %ax + inc %bx + loop next_sector + + call puts + .string "\r" + +#### Transfer control to the kernel that we loaded. We read the start +#### address out of the ELF header (see [ELF1]) and convert it from a +#### 32-bit linear address into a 16:16 segment:offset address for +#### real mode, then jump to the converted address. The 80x86 doesn't +#### have an instruction to jump to an absolute segment:offset kept in +#### registers, so in fact we store the address in a temporary memory +#### location, then jump indirectly through that location. To save 4 +#### bytes in the loader, we reuse 4 bytes of the loader's code for +#### this temporary pointer. + + mov $0x2000, %ax + mov %ax, %es + mov %es:0x18, %dx + mov %dx, start + movw $0x2000, start + 2 + ljmp *start + +read_failed: +start: + # Disk sector read failed. + call puts +1: .string "\rBad read\r" + + # Notify BIOS that boot failed. See [IntrList]. + int $0x18 + +#### Print string subroutine. To save space in the loader, this +#### subroutine takes its null-terminated string argument from the +#### code stream just after the call, and then returns to the byte +#### just after the terminating null. This subroutine preserves all +#### general-purpose registers. + +puts: xchg %si, %ss:(%esp) + push %ax +next_char: + mov %cs:(%si), %al + inc %si + test %al, %al + jz 1f + call putc + jmp next_char +1: pop %ax + xchg %si, %ss:(%esp) + ret + +#### Character output subroutine. Prints the character in AL to the +#### VGA display and serial port 0, using BIOS services (see +#### [IntrList]). Preserves all general-purpose registers. +#### +#### If called upon to output a carriage return, this subroutine +#### automatically supplies the following line feed. + +putc: pusha + +1: sub %bh, %bh # Page 0. + mov $0x0e, %ah # Teletype output service. + int $0x10 + + mov $0x01, %ah # Serial port output service. + sub %dx, %dx # Serial port 0. +2: int $0x14 # Destroys AH. + test $0x80, %ah # Output timed out? + jz 3f + movw $0x9090, 2b # Turn "int $0x14" above into NOPs. + +3: + cmp $'\r', %al + jne popa_ret + mov $'\n', %al + jmp 1b + +#### Sector read subroutine. Takes a drive number in DL (0x80 = hard +#### disk 0, 0x81 = hard disk 1, ...) and a sector number in EBX, and +#### reads the specified sector into memory at ES:0000. Returns with +#### carry set on error, clear otherwise. Preserves all +#### general-purpose registers. + +read_sector: + pusha + sub %ax, %ax + push %ax # LBA sector number [48:63] + push %ax # LBA sector number [32:47] + push %ebx # LBA sector number [0:31] + push %es # Buffer segment + push %ax # Buffer offset (always 0) + push $1 # Number of sectors to read + push $16 # Packet size + mov $0x42, %ah # Extended read + mov %sp, %si # DS:SI -> packet + int $0x13 # Error code in CF + popa # Pop 16 bytes, preserve flags +popa_ret: + popa + ret # Error code still in CF + +#### Command-line arguments and their count. +#### This is written by the `pintos' utility and read by the kernel. +#### The loader itself does not do anything with the command line. + .org LOADER_ARG_CNT - LOADER_BASE + .fill LOADER_ARG_CNT_LEN, 1, 0 + + .org LOADER_ARGS - LOADER_BASE + .fill LOADER_ARGS_LEN, 1, 0 + +#### Partition table. + .org LOADER_PARTS - LOADER_BASE + .fill LOADER_PARTS_LEN, 1, 0 + +#### Boot-sector signature for BIOS inspection. + .org LOADER_SIG - LOADER_BASE + .word 0xaa55 -- cgit v1.2.3