summaryrefslogtreecommitdiffstats
path: root/threads/loader.S
diff options
context:
space:
mode:
authormanuel <manuel@mausz.at>2012-03-27 11:51:08 +0200
committermanuel <manuel@mausz.at>2012-03-27 11:51:08 +0200
commit4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b (patch)
tree868c52e06f207b5ec8a3cc141f4b8b2bdfcc165c /threads/loader.S
parenteae0bd57f0a26314a94785061888d193d186944a (diff)
downloadprogos-4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b.tar.gz
progos-4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b.tar.bz2
progos-4f670845ff9ab6c48bcb5f7bf4d4ef6dc3c3064b.zip
reorganize file structure to match the upstream requirements
Diffstat (limited to 'threads/loader.S')
-rw-r--r--threads/loader.S263
1 files changed, 263 insertions, 0 deletions
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 @@
1#include "threads/loader.h"
2
3#### Kernel loader.
4
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.
10####
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
15#### this.
16
17# Runs in real mode, which is a 16-bit segment.
18 .code16
19
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).
23
24 sub %ax, %ax
25 mov %ax, %ds
26 mov %ax, %ss
27 mov $0xf000, %esp
28
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 $0xe3, %al # 9600 bps, N-8-1.
33 # AH is already 0 (Initialize Port).
34 int $0x14 # Destroys AX.
35
36 call puts
37 .string "PiLo"
38
39#### Read the partition table on each system hard disk and scan for a
40#### partition of type 0x20, which is the type that we use for a
41#### Pintos kernel.
42####
43#### Read [Partitions] for a description of the partition table format
44#### that we parse.
45####
46#### We print out status messages to show the disk and partition being
47#### scanned, e.g. hda1234 as we scan four partitions on the first
48#### hard disk.
49
50 mov $0x80, %dl # Hard disk 0.
51read_mbr:
52 sub %ebx, %ebx # Sector 0.
53 mov $0x2000, %ax # Use 0x20000 for buffer.
54 mov %ax, %es
55 call read_sector
56 jc no_such_drive
57
58 # Print hd[a-z].
59 call puts
60 .string " hd"
61 mov %dl, %al
62 add $'a' - 0x80, %al
63 call putc
64
65 # Check for MBR signature--if not present, it's not a
66 # partitioned hard disk.
67 cmpw $0xaa55, %es:510
68 jne next_drive
69
70 mov $446, %si # Offset of partition table entry 1.
71 mov $'1', %al
72check_partition:
73 # Is it an unused partition?
74 cmpl $0, %es:(%si)
75 je next_partition
76
77 # Print [1-4].
78 call putc
79
80 # Is it a Pintos kernel partition?
81 cmpb $0x20, %es:4(%si)
82 jne next_partition
83
84 # Is it a bootable partition?
85 cmpb $0x80, %es:(%si)
86 je load_kernel
87
88next_partition:
89 # No match for this partition, go on to the next one.
90 add $16, %si # Offset to next partition table entry.
91 inc %al
92 cmp $510, %si
93 jb check_partition
94
95next_drive:
96 # No match on this drive, go on to the next one.
97 inc %dl
98 jnc read_mbr
99
100no_such_drive:
101no_boot_partition:
102 # Didn't find a Pintos kernel partition anywhere, give up.
103 call puts
104 .string "\rNot found\r"
105
106 # Notify BIOS that boot failed. See [IntrList].
107 int $0x18
108
109#### We found a kernel. The kernel's drive is in DL. The partition
110#### table entry for the kernel's partition is at ES:SI. Our job now
111#### is to read the kernel from disk and jump to its start address.
112
113load_kernel:
114 call puts
115 .string "\rLoading"
116
117 # Figure out number of sectors to read. A Pintos kernel is
118 # just an ELF format object, which doesn't have an
119 # easy-to-read field to identify its own size (see [ELF1]).
120 # But we limit Pintos kernels to 512 kB for other reasons, so
121 # it's easy enough to just read the entire contents of the
122 # partition or 512 kB from disk, whichever is smaller.
123 mov %es:12(%si), %ecx # EBP = number of sectors
124 cmp $1024, %ecx # Cap size at 512 kB
125 jbe 1f
126 mov $1024, %cx
1271:
128
129 mov %es:8(%si), %ebx # EBX = first sector
130 mov $0x2000, %ax # Start load address: 0x20000
131
132next_sector:
133 # Read one sector into memory.
134 mov %ax, %es # ES:0000 -> load address
135 call read_sector
136 jc read_failed
137
138 # Print '.' as progress indicator once every 16 sectors == 8 kB.
139 test $15, %bl
140 jnz 1f
141 call puts
142 .string "."
1431:
144
145 # Advance memory pointer and disk sector.
146 add $0x20, %ax
147 inc %bx
148 loop next_sector
149
150 call puts
151 .string "\r"
152
153#### Transfer control to the kernel that we loaded. We read the start
154#### address out of the ELF header (see [ELF1]) and convert it from a
155#### 32-bit linear address into a 16:16 segment:offset address for
156#### real mode, then jump to the converted address. The 80x86 doesn't
157#### have an instruction to jump to an absolute segment:offset kept in
158#### registers, so in fact we store the address in a temporary memory
159#### location, then jump indirectly through that location. To save 4
160#### bytes in the loader, we reuse 4 bytes of the loader's code for
161#### this temporary pointer.
162
163 mov $0x2000, %ax
164 mov %ax, %es
165 mov %es:0x18, %dx
166 mov %dx, start
167 movw $0x2000, start + 2
168 ljmp *start
169
170read_failed:
171start:
172 # Disk sector read failed.
173 call puts
1741: .string "\rBad read\r"
175
176 # Notify BIOS that boot failed. See [IntrList].
177 int $0x18
178
179#### Print string subroutine. To save space in the loader, this
180#### subroutine takes its null-terminated string argument from the
181#### code stream just after the call, and then returns to the byte
182#### just after the terminating null. This subroutine preserves all
183#### general-purpose registers.
184
185puts: xchg %si, %ss:(%esp)
186 push %ax
187next_char:
188 mov %cs:(%si), %al
189 inc %si
190 test %al, %al
191 jz 1f
192 call putc
193 jmp next_char
1941: pop %ax
195 xchg %si, %ss:(%esp)
196 ret
197
198#### Character output subroutine. Prints the character in AL to the
199#### VGA display and serial port 0, using BIOS services (see
200#### [IntrList]). Preserves all general-purpose registers.
201####
202#### If called upon to output a carriage return, this subroutine
203#### automatically supplies the following line feed.
204
205putc: pusha
206
2071: sub %bh, %bh # Page 0.
208 mov $0x0e, %ah # Teletype output service.
209 int $0x10
210
211 mov $0x01, %ah # Serial port output service.
212 sub %dx, %dx # Serial port 0.
2132: int $0x14 # Destroys AH.
214 test $0x80, %ah # Output timed out?
215 jz 3f
216 movw $0x9090, 2b # Turn "int $0x14" above into NOPs.
217
2183:
219 cmp $'\r', %al
220 jne popa_ret
221 mov $'\n', %al
222 jmp 1b
223
224#### Sector read subroutine. Takes a drive number in DL (0x80 = hard
225#### disk 0, 0x81 = hard disk 1, ...) and a sector number in EBX, and
226#### reads the specified sector into memory at ES:0000. Returns with
227#### carry set on error, clear otherwise. Preserves all
228#### general-purpose registers.
229
230read_sector:
231 pusha
232 sub %ax, %ax
233 push %ax # LBA sector number [48:63]
234 push %ax # LBA sector number [32:47]
235 push %ebx # LBA sector number [0:31]
236 push %es # Buffer segment
237 push %ax # Buffer offset (always 0)
238 push $1 # Number of sectors to read
239 push $16 # Packet size
240 mov $0x42, %ah # Extended read
241 mov %sp, %si # DS:SI -> packet
242 int $0x13 # Error code in CF
243 popa # Pop 16 bytes, preserve flags
244popa_ret:
245 popa
246 ret # Error code still in CF
247
248#### Command-line arguments and their count.
249#### This is written by the `pintos' utility and read by the kernel.
250#### The loader itself does not do anything with the command line.
251 .org LOADER_ARG_CNT - LOADER_BASE
252 .fill LOADER_ARG_CNT_LEN, 1, 0
253
254 .org LOADER_ARGS - LOADER_BASE
255 .fill LOADER_ARGS_LEN, 1, 0
256
257#### Partition table.
258 .org LOADER_PARTS - LOADER_BASE
259 .fill LOADER_PARTS_LEN, 1, 0
260
261#### Boot-sector signature for BIOS inspection.
262 .org LOADER_SIG - LOADER_BASE
263 .word 0xaa55