1 #include "threads/loader.h"
5 #### This code should be stored in the first sector of a hard disk.
6 #### When the BIOS runs, it loads this code at physical address
7 #### 0x7c00-0x7e00 (512 bytes) and jumps to the beginning of it,
8 #### in real mode. The loader loads the kernel into memory and jumps
9 #### to its entry point, which is the start function in start.S.
11 #### The BIOS passes in the drive that the loader was read from as
12 #### DL, with floppy drives numbered 0x00, 0x01, ... and hard drives
13 #### numbered 0x80, 0x81, ... We want to support booting a kernel on
14 #### a different drive from the loader, so we don't take advantage of
17 # Runs in real mode, which is a 16-bit segment.
20 # Set up segment registers.
21 # Set stack to grow downward from 60 kB (after boot, the kernel
22 # continues to use this stack for its initial thread).
29 # Configure serial port so we can report progress without connected VGA.
30 # See [IntrList] for details.
31 sub %dx, %dx # Serial port 0.
32 mov $0x00e3, %ax # 9600 bps, N-8-1.
33 int $0x14 # Destroys AX.
36 .string "Pintos loader"
38 #### Read the partition table on each system hard disk and scan for a
39 #### partition of type 0x20, which is the type that we use for a
42 #### Read [Partitions] for a description of the partition table format
45 #### We print out status messages to show the disk and partition being
46 #### scanned, e.g. hda1234 as we scan four partitions on the first
49 mov $0x80, %dl # Hard disk 0.
51 sub %ebx, %ebx # Sector 0.
52 mov $0x2000, %ax # Use 0x20000 for buffer.
64 # Check for MBR signature--if not present, it's not a
65 # partitioned hard disk.
69 mov $446, %si # Offset of partition table entry 1.
72 # Is it an unused partition?
79 # Is it a Pintos kernel partition?
80 cmpb $0x20, %es:4(%si)
83 # Is it a bootable partition?
88 # No match for this partition, go on to the next one.
89 add $16, %si # Offset to next partition table entry.
95 # No match on this drive, go on to the next one.
101 # Didn't find a Pintos kernel partition anywhere, give up.
103 .string "\rNot found\r"
105 # Notify BIOS that boot failed. See [IntrList].
108 #### We found a kernel. The kernel's drive is in DL. The partition
109 #### table entry for the kernel's partition is at ES:SI. Our job now
110 #### is to read the kernel from disk and jump to its start address.
116 # Figure out number of sectors to read. A Pintos kernel is
117 # just an ELF format object, which doesn't have an
118 # easy-to-read field to identify its own size (see [ELF1]).
119 # But we limit Pintos kernels to 512 kB for other reasons, so
120 # it's easy enough to just read the entire contents of the
121 # partition or 512 kB from disk, whichever is smaller.
122 mov %es:12(%si), %ecx # EBP = number of sectors
123 cmp $1024, %ecx # Cap size at 512 kB
128 mov %es:8(%si), %ebx # EBX = first sector
129 mov $0x2000, %ax # Start load address: 0x20000
132 # Read one sector into memory.
133 mov %ax, %es # ES:0000 -> load address
137 # Print '.' as progress indicator once every 16 sectors == 8 kB.
144 # Advance memory pointer and disk sector.
152 #### Transfer control to the kernel that we loaded. We read the start
153 #### address out of the ELF header (see [ELF1]) and convert it from a
154 #### 32-bit linear address into a 16:16 segment:offset address for
155 #### real mode, then jump to the converted address. The 80x86 doesn't
156 #### have an instruction to jump to an absolute segment:offset kept in
157 #### registers, so in fact we store the address in a temporary memory
158 #### location, then jump indirectly through that location. To save 4
159 #### bytes in the loader, we reuse 4 bytes of the loader's code for
160 #### this temporary pointer.
166 movw $0x2000, start + 2
171 # Disk sector read failed.
173 1: .string "\rBad read\r"
175 # Notify BIOS that boot failed. See [IntrList].
178 #### Print string subroutine. To save space in the loader, this
179 #### subroutine takes its null-terminated string argument from the
180 #### code stream just after the call, and then returns to the byte
181 #### just after the terminating null. This subroutine preserves all
182 #### general-purpose registers.
184 puts: xchg %si, %ss:(%esp)
197 #### Character output subroutine. Prints the character in AL to the
198 #### VGA display and serial port 0, using BIOS services (see
199 #### [IntrList]). Preserves all general-purpose registers.
201 #### If called upon to output a carriage return, this subroutine
202 #### automatically supplies the following line feed.
206 1: sub %bh, %bh # Page 0.
207 mov $0x0e, %ah # Teletype output service.
210 mov $0x01, %ah # Serial port output service.
211 sub %dx, %dx # Serial port 0.
212 int $0x14 # Destroys AH.
219 #### Sector read subroutine. Takes a drive number in DL (0x80 = hard
220 #### disk 0, 0x81 = hard disk 1, ...) and a sector number in EBX, and
221 #### reads the specified sector into memory at ES:0000. Returns with
222 #### carry set on error, clear otherwise. Preserves all
223 #### general-purpose registers.
228 push %eax # LBA sector number [32:63]
229 push %ebx # LBA sector number [0:31]
230 push %es # Buffer segment
231 push %ax # Buffer offset (always 0)
232 push $1 # Number of sectors to read
233 push $16 # Packet size
234 mov $0x42, %ah # Extended read
235 mov %sp, %si # DS:SI -> packet
236 int $0x13 # Error code in CF
237 popa # Pop 16 bytes, preserve flags
240 ret # Error code still in CF
242 #### Command-line arguments and their count.
243 #### This is written by the `pintos' utility and read by the kernel.
244 #### The loader itself does not do anything with the command line.
245 .org LOADER_ARG_CNT - LOADER_BASE
246 .fill LOADER_ARG_CNT_LEN, 1, 0
248 .org LOADER_ARGS - LOADER_BASE
249 .fill LOADER_ARGS_LEN, 1, 0
251 #### Partition table.
252 .org LOADER_PARTS - LOADER_BASE
253 .fill LOADER_PARTS_LEN, 1, 0
255 #### Boot-sector signature for BIOS inspection.
256 .org LOADER_SIG - LOADER_BASE