1 | /* NOTE: this boot sector contains instructions that need at least an 80186. |
---|
2 | * Yes, as86 has a bug somewhere in the valid instruction set checks. |
---|
3 | * |
---|
4 | */ |
---|
5 | |
---|
6 | /* floppyload.S Copyright (C) 1991, 1992 Linus Torvalds |
---|
7 | * modified by Drew Eckhardt |
---|
8 | * modified by Bruce Evans (bde) |
---|
9 | * |
---|
10 | * floppyprefix.S is loaded at 0x0000:0x7c00 by the bios-startup routines. |
---|
11 | * |
---|
12 | * It then loads the system at SYSSEG<<4, using BIOS interrupts. |
---|
13 | * |
---|
14 | * The loader has been made as simple as possible, and continuous read errors |
---|
15 | * will result in a unbreakable loop. Reboot by hand. It loads pretty fast by |
---|
16 | * getting whole tracks at a time whenever possible. |
---|
17 | */ |
---|
18 | |
---|
19 | FILE_LICENCE ( GPL2_ONLY ) |
---|
20 | |
---|
21 | .equ BOOTSEG, 0x07C0 /* original address of boot-sector */ |
---|
22 | |
---|
23 | .equ SYSSEG, 0x1000 /* system loaded at SYSSEG<<4 */ |
---|
24 | |
---|
25 | .org 0 |
---|
26 | .arch i386 |
---|
27 | .text |
---|
28 | .section ".prefix", "ax", @progbits |
---|
29 | .code16 |
---|
30 | |
---|
31 | jmp $BOOTSEG, $go /* reload cs:ip to match relocation addr */ |
---|
32 | go: |
---|
33 | movw $0x2000-12, %di /* 0x2000 is arbitrary value >= length */ |
---|
34 | /* of bootsect + room for stack + 12 for */ |
---|
35 | /* saved disk parm block */ |
---|
36 | |
---|
37 | movw $BOOTSEG, %ax |
---|
38 | movw %ax,%ds |
---|
39 | movw %ax,%es |
---|
40 | movw %ax,%ss /* put stack at BOOTSEG:0x4000-12. */ |
---|
41 | movw %di,%sp |
---|
42 | |
---|
43 | /* Many BIOS's default disk parameter tables will not recognize multi-sector |
---|
44 | * reads beyond the maximum sector number specified in the default diskette |
---|
45 | * parameter tables - this may mean 7 sectors in some cases. |
---|
46 | * |
---|
47 | * Since single sector reads are slow and out of the question, we must take care |
---|
48 | * of this by creating new parameter tables (for the first disk) in RAM. We |
---|
49 | * will set the maximum sector count to 36 - the most we will encounter on an |
---|
50 | * ED 2.88. High doesn't hurt. Low does. |
---|
51 | * |
---|
52 | * Segments are as follows: ds=es=ss=cs - BOOTSEG |
---|
53 | */ |
---|
54 | |
---|
55 | xorw %cx,%cx |
---|
56 | movw %cx,%es /* access segment 0 */ |
---|
57 | movw $0x78, %bx /* 0:bx is parameter table address */ |
---|
58 | pushw %ds /* save ds */ |
---|
59 | /* 0:bx is parameter table address */ |
---|
60 | ldsw %es:(%bx),%si /* loads ds and si */ |
---|
61 | |
---|
62 | movw %ax,%es /* ax is BOOTSECT (loaded above) */ |
---|
63 | movb $6, %cl /* copy 12 bytes */ |
---|
64 | cld |
---|
65 | pushw %di /* keep a copy for later */ |
---|
66 | rep |
---|
67 | movsw /* ds:si is source, es:di is dest */ |
---|
68 | popw %di |
---|
69 | |
---|
70 | movb $36,%es:4(%di) |
---|
71 | |
---|
72 | movw %cx,%ds /* access segment 0 */ |
---|
73 | xchgw %di,(%bx) |
---|
74 | movw %es,%si |
---|
75 | xchgw %si,2(%bx) |
---|
76 | popw %ds /* restore ds */ |
---|
77 | movw %di, dpoff /* save old parameters */ |
---|
78 | movw %si, dpseg /* to restore just before finishing */ |
---|
79 | pushw %ds |
---|
80 | popw %es /* reload es */ |
---|
81 | |
---|
82 | /* Note that es is already set up. Also cx is 0 from rep movsw above. */ |
---|
83 | |
---|
84 | xorb %ah,%ah /* reset FDC */ |
---|
85 | xorb %dl,%dl |
---|
86 | int $0x13 |
---|
87 | |
---|
88 | /* Get disk drive parameters, specifically number of sectors/track. |
---|
89 | * |
---|
90 | * It seems that there is no BIOS call to get the number of sectors. Guess |
---|
91 | * 36 sectors if sector 36 can be read, 18 sectors if sector 18 can be read, |
---|
92 | * 15 if sector 15 can be read. Otherwise guess 9. |
---|
93 | */ |
---|
94 | |
---|
95 | movw $disksizes, %si /* table of sizes to try */ |
---|
96 | |
---|
97 | probe_loop: |
---|
98 | lodsb |
---|
99 | cbtw /* extend to word */ |
---|
100 | movw %ax, sectors |
---|
101 | cmpw $disksizes+4, %si |
---|
102 | jae got_sectors /* if all else fails, try 9 */ |
---|
103 | xchgw %cx,%ax /* cx = track and sector */ |
---|
104 | xorw %dx,%dx /* drive 0, head 0 */ |
---|
105 | movw $0x0200, %bx /* address after boot sector */ |
---|
106 | /* (512 bytes from origin, es = cs) */ |
---|
107 | movw $0x0201, %ax /* service 2, 1 sector */ |
---|
108 | int $0x13 |
---|
109 | jc probe_loop /* try next value */ |
---|
110 | |
---|
111 | got_sectors: |
---|
112 | movw $msg1end-msg1, %cx |
---|
113 | movw $msg1, %si |
---|
114 | call print_str |
---|
115 | |
---|
116 | /* ok, we've written the Loading... message, now we want to load the system */ |
---|
117 | |
---|
118 | movw $SYSSEG, %ax |
---|
119 | movw %ax,%es /* segment of SYSSEG<<4 */ |
---|
120 | pushw %es |
---|
121 | call read_it |
---|
122 | |
---|
123 | /* This turns off the floppy drive motor, so that we enter the kernel in a |
---|
124 | * known state, and don't have to worry about it later. |
---|
125 | */ |
---|
126 | movw $0x3f2, %dx |
---|
127 | xorb %al,%al |
---|
128 | outb %al,%dx |
---|
129 | |
---|
130 | call print_nl |
---|
131 | pop %es /* = SYSSEG */ |
---|
132 | |
---|
133 | /* Restore original disk parameters */ |
---|
134 | movw $0x78, %bx |
---|
135 | movw dpoff, %di |
---|
136 | movw dpseg, %si |
---|
137 | xorw %ax,%ax |
---|
138 | movw %ax,%ds |
---|
139 | movw %di,(%bx) |
---|
140 | movw %si,2(%bx) |
---|
141 | |
---|
142 | /* Everything now loaded. %es = SYSSEG, so %es:0000 points to |
---|
143 | * start of loaded image. |
---|
144 | */ |
---|
145 | |
---|
146 | /* Jump to loaded copy */ |
---|
147 | ljmp $SYSSEG, $start_runtime |
---|
148 | |
---|
149 | endseg: .word SYSSEG |
---|
150 | .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ |
---|
151 | .ascii "ADDW" |
---|
152 | .long endseg |
---|
153 | .long 16 |
---|
154 | .long 0 |
---|
155 | .previous |
---|
156 | |
---|
157 | /* This routine loads the system at address SYSSEG<<4, making sure no 64kB |
---|
158 | * boundaries are crossed. We try to load it as fast as possible, loading whole |
---|
159 | * tracks whenever we can. |
---|
160 | * |
---|
161 | * in: es - starting address segment (normally SYSSEG) |
---|
162 | */ |
---|
163 | read_it: |
---|
164 | movw $0,sread /* load whole image including prefix */ |
---|
165 | movw %es,%ax |
---|
166 | testw $0x0fff, %ax |
---|
167 | die: jne die /* es must be at 64kB boundary */ |
---|
168 | xorw %bx,%bx /* bx is starting address within segment */ |
---|
169 | rp_read: |
---|
170 | movw %es,%ax |
---|
171 | movw %bx,%dx |
---|
172 | movb $4, %cl |
---|
173 | shrw %cl,%dx /* bx is always divisible by 16 */ |
---|
174 | addw %dx,%ax |
---|
175 | cmpw endseg, %ax /* have we loaded all yet? */ |
---|
176 | jb ok1_read |
---|
177 | ret |
---|
178 | ok1_read: |
---|
179 | movw sectors, %ax |
---|
180 | subw sread, %ax |
---|
181 | movw %ax,%cx |
---|
182 | shlw $9, %cx |
---|
183 | addw %bx,%cx |
---|
184 | jnc ok2_read |
---|
185 | je ok2_read |
---|
186 | xorw %ax,%ax |
---|
187 | subw %bx,%ax |
---|
188 | shrw $9, %ax |
---|
189 | ok2_read: |
---|
190 | call read_track |
---|
191 | movw %ax,%cx |
---|
192 | addw sread, %ax |
---|
193 | cmpw sectors, %ax |
---|
194 | jne ok3_read |
---|
195 | movw $1, %ax |
---|
196 | subw head, %ax |
---|
197 | jne ok4_read |
---|
198 | incw track |
---|
199 | ok4_read: |
---|
200 | movw %ax, head |
---|
201 | xorw %ax,%ax |
---|
202 | ok3_read: |
---|
203 | movw %ax, sread |
---|
204 | shlw $9, %cx |
---|
205 | addw %cx,%bx |
---|
206 | jnc rp_read |
---|
207 | movw %es,%ax |
---|
208 | addb $0x10, %ah |
---|
209 | movw %ax,%es |
---|
210 | xorw %bx,%bx |
---|
211 | jmp rp_read |
---|
212 | |
---|
213 | read_track: |
---|
214 | pusha |
---|
215 | pushw %ax |
---|
216 | pushw %bx |
---|
217 | pushw %bp /* just in case the BIOS is buggy */ |
---|
218 | movw $0x0e2e, %ax /* 0x2e = . */ |
---|
219 | movw $0x0007, %bx |
---|
220 | int $0x10 |
---|
221 | popw %bp |
---|
222 | popw %bx |
---|
223 | popw %ax |
---|
224 | |
---|
225 | movw track, %dx |
---|
226 | movw sread, %cx |
---|
227 | incw %cx |
---|
228 | movb %dl,%ch |
---|
229 | movw head, %dx |
---|
230 | movb %dl,%dh |
---|
231 | andw $0x0100, %dx |
---|
232 | movb $2, %ah |
---|
233 | |
---|
234 | pushw %dx /* save for error dump */ |
---|
235 | pushw %cx |
---|
236 | pushw %bx |
---|
237 | pushw %ax |
---|
238 | |
---|
239 | int $0x13 |
---|
240 | jc bad_rt |
---|
241 | addw $8, %sp |
---|
242 | popa |
---|
243 | ret |
---|
244 | |
---|
245 | bad_rt: pushw %ax /* save error code */ |
---|
246 | call print_all /* ah = error, al = read */ |
---|
247 | |
---|
248 | xorb %ah,%ah |
---|
249 | xorb %dl,%dl |
---|
250 | int $0x13 |
---|
251 | |
---|
252 | addw $10, %sp |
---|
253 | popa |
---|
254 | jmp read_track |
---|
255 | |
---|
256 | /* print_all is for debugging purposes. It will print out all of the registers. |
---|
257 | * The assumption is that this is called from a routine, with a stack frame like |
---|
258 | * dx |
---|
259 | * cx |
---|
260 | * bx |
---|
261 | * ax |
---|
262 | * error |
---|
263 | * ret <- sp |
---|
264 | */ |
---|
265 | |
---|
266 | print_all: |
---|
267 | call print_nl /* nl for readability */ |
---|
268 | movw $5, %cx /* error code + 4 registers */ |
---|
269 | movw %sp,%bp |
---|
270 | |
---|
271 | print_loop: |
---|
272 | pushw %cx /* save count left */ |
---|
273 | |
---|
274 | cmpb $5, %cl |
---|
275 | jae no_reg /* see if register name is needed */ |
---|
276 | |
---|
277 | movw $0x0007, %bx /* page 0, attribute 7 (normal) */ |
---|
278 | movw $0xe05+0x41-1, %ax |
---|
279 | subb %cl,%al |
---|
280 | int $0x10 |
---|
281 | |
---|
282 | movb $0x58, %al /* 'X' */ |
---|
283 | int $0x10 |
---|
284 | |
---|
285 | movb $0x3A, %al /* ':' */ |
---|
286 | int $0x10 |
---|
287 | |
---|
288 | no_reg: |
---|
289 | addw $2, %bp /* next register */ |
---|
290 | call print_hex /* print it */ |
---|
291 | movb $0x20, %al /* print a space */ |
---|
292 | int $0x10 |
---|
293 | popw %cx |
---|
294 | loop print_loop |
---|
295 | call print_nl /* nl for readability */ |
---|
296 | ret |
---|
297 | |
---|
298 | print_str: |
---|
299 | movw $0x0007, %bx /* page 0, attribute 7 (normal) */ |
---|
300 | movb $0x0e, %ah /* write char, tty mode */ |
---|
301 | prloop: |
---|
302 | lodsb |
---|
303 | int $0x10 |
---|
304 | loop prloop |
---|
305 | ret |
---|
306 | |
---|
307 | print_nl: |
---|
308 | movw $0x0007, %bx /* page 0, attribute 7 (normal) */ |
---|
309 | movw $0xe0d, %ax /* CR */ |
---|
310 | int $0x10 |
---|
311 | movb $0xa, %al /* LF */ |
---|
312 | int $0x10 |
---|
313 | ret |
---|
314 | |
---|
315 | /* print_hex prints the word pointed to by ss:bp in hexadecimal. */ |
---|
316 | |
---|
317 | print_hex: |
---|
318 | movw (%bp),%dx /* load word into dx */ |
---|
319 | movb $4, %cl |
---|
320 | movb $0x0e, %ah /* write char, tty mode */ |
---|
321 | movw $0x0007, %bx /* page 0, attribute 7 (normal) */ |
---|
322 | call print_digit |
---|
323 | call print_digit |
---|
324 | call print_digit |
---|
325 | /* fall through */ |
---|
326 | print_digit: |
---|
327 | rol %cl,%dx /* rotate so that lowest 4 bits are used */ |
---|
328 | movb $0x0f, %al /* mask for nybble */ |
---|
329 | andb %dl,%al |
---|
330 | addb $0x90, %al /* convert al to ascii hex (four instructions) */ |
---|
331 | daa |
---|
332 | adcb $0x40, %al |
---|
333 | daa |
---|
334 | int $0x10 |
---|
335 | ret |
---|
336 | |
---|
337 | sread: .word 0 /* sectors read of current track */ |
---|
338 | head: .word 0 /* current head */ |
---|
339 | track: .word 0 /* current track */ |
---|
340 | |
---|
341 | sectors: |
---|
342 | .word 0 |
---|
343 | |
---|
344 | dpseg: .word 0 |
---|
345 | dpoff: .word 0 |
---|
346 | |
---|
347 | disksizes: |
---|
348 | .byte 36,18,15,9 |
---|
349 | |
---|
350 | msg1: |
---|
351 | .ascii "Loading ROM image" |
---|
352 | msg1end: |
---|
353 | |
---|
354 | .org 510, 0 |
---|
355 | .word 0xAA55 |
---|
356 | |
---|
357 | start_runtime: |
---|
358 | /* Install gPXE */ |
---|
359 | call install |
---|
360 | |
---|
361 | /* Set up real-mode stack */ |
---|
362 | movw %bx, %ss |
---|
363 | movw $_estack16, %sp |
---|
364 | |
---|
365 | /* Jump to .text16 segment */ |
---|
366 | pushw %ax |
---|
367 | pushw $1f |
---|
368 | lret |
---|
369 | .section ".text16", "awx", @progbits |
---|
370 | 1: |
---|
371 | pushl $main |
---|
372 | pushw %cs |
---|
373 | call prot_call |
---|
374 | popl %ecx /* discard */ |
---|
375 | |
---|
376 | /* Uninstall gPXE */ |
---|
377 | call uninstall |
---|
378 | |
---|
379 | /* Boot next device */ |
---|
380 | int $0x18 |
---|
381 | |
---|