1 | /* At entry, the processor is in 16 bit real mode and the code is being |
---|
2 | * executed from an address it was not linked to. Code must be pic and |
---|
3 | * 32 bit sensitive until things are fixed up. |
---|
4 | * |
---|
5 | * Also be very careful as the stack is at the rear end of the interrupt |
---|
6 | * table so using a noticeable amount of stack space is a no-no. |
---|
7 | */ |
---|
8 | |
---|
9 | FILE_LICENCE ( GPL2_OR_LATER ) |
---|
10 | |
---|
11 | #include <config/general.h> |
---|
12 | |
---|
13 | #define PNP_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) ) |
---|
14 | #define PMM_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'M' << 16 ) + ( 'M' << 24 ) ) |
---|
15 | #define PCI_SIGNATURE ( 'P' + ( 'C' << 8 ) + ( 'I' << 16 ) + ( ' ' << 24 ) ) |
---|
16 | #define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) ) |
---|
17 | #define PNP_GET_BBS_VERSION 0x60 |
---|
18 | #define PMM_ALLOCATE 0x0000 |
---|
19 | #define PMM_DEALLOCATE 0x0002 |
---|
20 | |
---|
21 | /* ROM banner timeout. Based on the configurable BANNER_TIMEOUT in |
---|
22 | * config.h, but converted to a number of (18Hz) timer ticks, and |
---|
23 | * doubled to allow for BIOSes that switch video modes immediately |
---|
24 | * beforehand, so rendering the message almost invisible to the user. |
---|
25 | */ |
---|
26 | #define ROM_BANNER_TIMEOUT ( 2 * ( 18 * BANNER_TIMEOUT ) / 10 ) |
---|
27 | |
---|
28 | /* We can load a ROM in two ways: have the BIOS load all of it (.rom prefix) |
---|
29 | * or have the BIOS load a stub that loads the rest using PCI (.xrom prefix). |
---|
30 | * The latter is not as widely supported, but allows the use of large ROMs |
---|
31 | * on some systems with crowded option ROM space. |
---|
32 | */ |
---|
33 | |
---|
34 | #ifdef LOAD_ROM_FROM_PCI |
---|
35 | #define ROM_SIZE_VALUE _prefix_filesz_sect /* Amount to load in BIOS */ |
---|
36 | #else |
---|
37 | #define ROM_SIZE_VALUE 0 /* Load amount (before compr. fixup) */ |
---|
38 | #endif |
---|
39 | |
---|
40 | |
---|
41 | .text |
---|
42 | .code16 |
---|
43 | .arch i386 |
---|
44 | .section ".prefix", "ax", @progbits |
---|
45 | |
---|
46 | .org 0x00 |
---|
47 | romheader: |
---|
48 | .word 0xAA55 /* BIOS extension signature */ |
---|
49 | romheader_size: .byte ROM_SIZE_VALUE /* Size in 512-byte blocks */ |
---|
50 | jmp init /* Initialisation vector */ |
---|
51 | checksum: |
---|
52 | .byte 0, 0 |
---|
53 | real_size: |
---|
54 | .word 0 |
---|
55 | .org 0x16 |
---|
56 | .word undiheader |
---|
57 | .org 0x18 |
---|
58 | .word pciheader |
---|
59 | .org 0x1a |
---|
60 | .word pnpheader |
---|
61 | .size romheader, . - romheader |
---|
62 | |
---|
63 | .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ |
---|
64 | #ifndef LOAD_ROM_FROM_PCI |
---|
65 | .ascii "ADDB" |
---|
66 | .long romheader_size |
---|
67 | .long 512 |
---|
68 | .long 0 |
---|
69 | #endif |
---|
70 | .ascii "ADDB" |
---|
71 | .long real_size |
---|
72 | .long 512 |
---|
73 | .long 0 |
---|
74 | .previous |
---|
75 | |
---|
76 | pciheader: |
---|
77 | .ascii "PCIR" /* Signature */ |
---|
78 | .word pci_vendor_id /* Vendor identification */ |
---|
79 | .word pci_device_id /* Device identification */ |
---|
80 | .word 0x0000 /* Device list pointer */ |
---|
81 | .word pciheader_len /* PCI data structure length */ |
---|
82 | .byte 0x03 /* PCI data structure revision */ |
---|
83 | .byte 0x02, 0x00, 0x00 /* Class code */ |
---|
84 | pciheader_image_length: |
---|
85 | .word ROM_SIZE_VALUE /* Image length */ |
---|
86 | .word 0x0001 /* Revision level */ |
---|
87 | .byte 0x00 /* Code type */ |
---|
88 | .byte 0x80 /* Last image indicator */ |
---|
89 | pciheader_runtime_length: |
---|
90 | .word ROM_SIZE_VALUE /* Maximum run-time image length */ |
---|
91 | .word 0x0000 /* Configuration utility code header */ |
---|
92 | .word 0x0000 /* DMTF CLP entry point */ |
---|
93 | .equ pciheader_len, . - pciheader |
---|
94 | .size pciheader, . - pciheader |
---|
95 | |
---|
96 | #ifndef LOAD_ROM_FROM_PCI |
---|
97 | .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ |
---|
98 | .ascii "ADDW" |
---|
99 | .long pciheader_image_length |
---|
100 | .long 512 |
---|
101 | .long 0 |
---|
102 | .ascii "ADDW" |
---|
103 | .long pciheader_runtime_length |
---|
104 | .long 512 |
---|
105 | .long 0 |
---|
106 | .previous |
---|
107 | #endif |
---|
108 | |
---|
109 | pnpheader: |
---|
110 | .ascii "$PnP" /* Signature */ |
---|
111 | .byte 0x01 /* Structure revision */ |
---|
112 | .byte ( pnpheader_len / 16 ) /* Length (in 16 byte increments) */ |
---|
113 | .word 0x0000 /* Offset of next header */ |
---|
114 | .byte 0x00 /* Reserved */ |
---|
115 | .byte 0x00 /* Checksum */ |
---|
116 | .long 0x00000000 /* Device identifier */ |
---|
117 | .word mfgstr /* Manufacturer string */ |
---|
118 | .word prodstr /* Product name */ |
---|
119 | .byte 0x02 /* Device base type code */ |
---|
120 | .byte 0x00 /* Device sub-type code */ |
---|
121 | .byte 0x00 /* Device interface type code */ |
---|
122 | .byte 0xf4 /* Device indicator */ |
---|
123 | .word 0x0000 /* Boot connection vector */ |
---|
124 | .word 0x0000 /* Disconnect vector */ |
---|
125 | .word bev_entry /* Boot execution vector */ |
---|
126 | .word 0x0000 /* Reserved */ |
---|
127 | .word 0x0000 /* Static resource information vector*/ |
---|
128 | .equ pnpheader_len, . - pnpheader |
---|
129 | .size pnpheader, . - pnpheader |
---|
130 | |
---|
131 | /* Manufacturer string */ |
---|
132 | mfgstr: |
---|
133 | .asciz "http://etherboot.org" |
---|
134 | .size mfgstr, . - mfgstr |
---|
135 | |
---|
136 | /* Product string |
---|
137 | * |
---|
138 | * Defaults to PRODUCT_SHORT_NAME. If the ROM image is writable at |
---|
139 | * initialisation time, it will be filled in to include the PCI |
---|
140 | * bus:dev.fn number of the card as well. |
---|
141 | */ |
---|
142 | prodstr: |
---|
143 | .ascii PRODUCT_SHORT_NAME |
---|
144 | prodstr_separator: |
---|
145 | .byte 0 |
---|
146 | .ascii "(PCI " |
---|
147 | prodstr_pci_id: |
---|
148 | .asciz "xx:xx.x)" /* Filled in by init code */ |
---|
149 | .size prodstr, . - prodstr |
---|
150 | |
---|
151 | .globl undiheader |
---|
152 | .weak undiloader |
---|
153 | undiheader: |
---|
154 | .ascii "UNDI" /* Signature */ |
---|
155 | .byte undiheader_len /* Length of structure */ |
---|
156 | .byte 0 /* Checksum */ |
---|
157 | .byte 0 /* Structure revision */ |
---|
158 | .byte 0,1,2 /* PXE version: 2.1.0 */ |
---|
159 | .word undiloader /* Offset to loader routine */ |
---|
160 | .word _data16_memsz /* Stack segment size */ |
---|
161 | .word _data16_memsz /* Data segment size */ |
---|
162 | .word _text16_memsz /* Code segment size */ |
---|
163 | .ascii "PCIR" /* Bus type */ |
---|
164 | .equ undiheader_len, . - undiheader |
---|
165 | .size undiheader, . - undiheader |
---|
166 | |
---|
167 | /* Initialisation (called once during POST) |
---|
168 | * |
---|
169 | * Determine whether or not this is a PnP system via a signature |
---|
170 | * check. If it is PnP, return to the PnP BIOS indicating that we are |
---|
171 | * a boot-capable device; the BIOS will call our boot execution vector |
---|
172 | * if it wants to boot us. If it is not PnP, hook INT 19. |
---|
173 | */ |
---|
174 | init: |
---|
175 | /* Preserve registers, clear direction flag, set %ds=%cs */ |
---|
176 | pushaw |
---|
177 | pushw %ds |
---|
178 | pushw %es |
---|
179 | pushw %fs |
---|
180 | pushw %gs |
---|
181 | cld |
---|
182 | pushw %cs |
---|
183 | popw %ds |
---|
184 | |
---|
185 | /* Shuffle some registers around. We need %di available for |
---|
186 | * the print_xxx functions, and in a register that's |
---|
187 | * addressable from %es, so shuffle as follows: |
---|
188 | * |
---|
189 | * %di (pointer to PnP structure) => %bx |
---|
190 | * %bx (runtime segment address, for PCI 3.0) => %gs |
---|
191 | */ |
---|
192 | movw %bx, %gs |
---|
193 | movw %di, %bx |
---|
194 | |
---|
195 | /* Print message as early as possible */ |
---|
196 | movw $init_message, %si |
---|
197 | xorw %di, %di |
---|
198 | call print_message |
---|
199 | call print_pci_busdevfn |
---|
200 | |
---|
201 | #ifdef LOAD_ROM_FROM_PCI |
---|
202 | /* Save PCI bus:dev.fn for later use */ |
---|
203 | movw %ax, pci_busdevfn |
---|
204 | #endif |
---|
205 | |
---|
206 | /* Fill in product name string, if possible */ |
---|
207 | movw $prodstr_pci_id, %di |
---|
208 | call print_pci_busdevfn |
---|
209 | movb $( ' ' ), prodstr_separator |
---|
210 | |
---|
211 | /* Print segment address */ |
---|
212 | movb $( ' ' ), %al |
---|
213 | xorw %di, %di |
---|
214 | call print_character |
---|
215 | movw %cs, %ax |
---|
216 | call print_hex_word |
---|
217 | |
---|
218 | /* Check for PCI BIOS version */ |
---|
219 | pushl %ebx |
---|
220 | pushl %edx |
---|
221 | pushl %edi |
---|
222 | stc |
---|
223 | movw $0xb101, %ax |
---|
224 | int $0x1a |
---|
225 | jc no_pci3 |
---|
226 | cmpl $PCI_SIGNATURE, %edx |
---|
227 | jne no_pci3 |
---|
228 | testb %ah, %ah |
---|
229 | jnz no_pci3 |
---|
230 | #ifdef LOAD_ROM_FROM_PCI |
---|
231 | incb pcibios_present |
---|
232 | #endif |
---|
233 | movw $init_message_pci, %si |
---|
234 | xorw %di, %di |
---|
235 | call print_message |
---|
236 | movb %bh, %al |
---|
237 | call print_hex_nibble |
---|
238 | movb $( '.' ), %al |
---|
239 | call print_character |
---|
240 | movb %bl, %al |
---|
241 | call print_hex_byte |
---|
242 | cmpb $3, %bh |
---|
243 | jb no_pci3 |
---|
244 | /* PCI >=3.0: leave %gs as-is if sane */ |
---|
245 | movw %gs, %ax |
---|
246 | cmpw $0xa000, %ax /* Insane if %gs < 0xa000 */ |
---|
247 | jb pci3_insane |
---|
248 | movw %cs, %bx /* Sane if %cs == %gs */ |
---|
249 | cmpw %bx, %ax |
---|
250 | je 1f |
---|
251 | movzbw romheader_size, %cx /* Sane if %cs+len <= %gs */ |
---|
252 | shlw $5, %cx |
---|
253 | addw %cx, %bx |
---|
254 | cmpw %bx, %ax |
---|
255 | jae 1f |
---|
256 | movw %cs, %bx /* Sane if %gs+len <= %cs */ |
---|
257 | addw %cx, %ax |
---|
258 | cmpw %bx, %ax |
---|
259 | jbe 1f |
---|
260 | pci3_insane: /* PCI 3.0 with insane %gs value: print error and ignore %gs */ |
---|
261 | movb $( '!' ), %al |
---|
262 | call print_character |
---|
263 | movw %gs, %ax |
---|
264 | call print_hex_word |
---|
265 | no_pci3: |
---|
266 | /* PCI <3.0: set %gs (runtime segment) = %cs (init-time segment) */ |
---|
267 | pushw %cs |
---|
268 | popw %gs |
---|
269 | 1: popl %edi |
---|
270 | popl %edx |
---|
271 | popl %ebx |
---|
272 | |
---|
273 | /* Check for PnP BIOS. Although %es:di should point to the |
---|
274 | * PnP BIOS signature on entry, some BIOSes fail to do this. |
---|
275 | */ |
---|
276 | movw $( 0xf000 - 1 ), %bx |
---|
277 | pnp_scan: |
---|
278 | incw %bx |
---|
279 | jz no_pnp |
---|
280 | movw %bx, %es |
---|
281 | cmpl $PNP_SIGNATURE, %es:0 |
---|
282 | jne pnp_scan |
---|
283 | xorw %dx, %dx |
---|
284 | xorw %si, %si |
---|
285 | movzbw %es:5, %cx |
---|
286 | 1: es lodsb |
---|
287 | addb %al, %dl |
---|
288 | loop 1b |
---|
289 | jnz pnp_scan |
---|
290 | /* Is PnP: print PnP message */ |
---|
291 | movw $init_message_pnp, %si |
---|
292 | xorw %di, %di |
---|
293 | call print_message |
---|
294 | /* Check for BBS */ |
---|
295 | pushw %es:0x1b /* Real-mode data segment */ |
---|
296 | pushw %ds /* &(bbs_version) */ |
---|
297 | pushw $bbs_version |
---|
298 | pushw $PNP_GET_BBS_VERSION |
---|
299 | lcall *%es:0xd |
---|
300 | addw $8, %sp |
---|
301 | testw %ax, %ax |
---|
302 | je got_bbs |
---|
303 | no_pnp: /* Not PnP-compliant - therefore cannot be BBS-compliant */ |
---|
304 | no_bbs: /* Not BBS-compliant - must hook INT 19 */ |
---|
305 | movw $init_message_int19, %si |
---|
306 | xorw %di, %di |
---|
307 | call print_message |
---|
308 | xorw %ax, %ax |
---|
309 | movw %ax, %es |
---|
310 | pushl %es:( 0x19 * 4 ) |
---|
311 | popl orig_int19 |
---|
312 | pushw %gs /* %gs contains runtime %cs */ |
---|
313 | pushw $int19_entry |
---|
314 | popl %es:( 0x19 * 4 ) |
---|
315 | jmp bbs_done |
---|
316 | got_bbs: /* BBS compliant - no need to hook INT 19 */ |
---|
317 | movw $init_message_bbs, %si |
---|
318 | xorw %di, %di |
---|
319 | call print_message |
---|
320 | bbs_done: |
---|
321 | |
---|
322 | /* Check for PMM */ |
---|
323 | movw $( 0xe000 - 1 ), %bx |
---|
324 | pmm_scan: |
---|
325 | incw %bx |
---|
326 | jz no_pmm |
---|
327 | movw %bx, %es |
---|
328 | cmpl $PMM_SIGNATURE, %es:0 |
---|
329 | jne pmm_scan |
---|
330 | xorw %dx, %dx |
---|
331 | xorw %si, %si |
---|
332 | movzbw %es:5, %cx |
---|
333 | 1: es lodsb |
---|
334 | addb %al, %dl |
---|
335 | loop 1b |
---|
336 | jnz pmm_scan |
---|
337 | /* PMM found: print PMM message */ |
---|
338 | movw $init_message_pmm, %si |
---|
339 | xorw %di, %di |
---|
340 | call print_message |
---|
341 | /* We have PMM and so a 1kB stack: preserve upper register halves */ |
---|
342 | pushal |
---|
343 | /* Calculate required allocation size in %esi */ |
---|
344 | movzwl real_size, %eax |
---|
345 | shll $9, %eax |
---|
346 | addl $_textdata_memsz, %eax |
---|
347 | orw $0xffff, %ax /* Ensure allocation size is at least 64kB */ |
---|
348 | bsrl %eax, %ecx |
---|
349 | subw $15, %cx /* Round up and convert to 64kB count */ |
---|
350 | movw $1, %si |
---|
351 | shlw %cl, %si |
---|
352 | pmm_loop: |
---|
353 | /* Try to allocate block via PMM */ |
---|
354 | pushw $0x0006 /* Aligned, extended memory */ |
---|
355 | pushl $0xffffffff /* No handle */ |
---|
356 | movzwl %si, %eax |
---|
357 | shll $12, %eax |
---|
358 | pushl %eax /* Allocation size in paragraphs */ |
---|
359 | pushw $PMM_ALLOCATE |
---|
360 | lcall *%es:7 |
---|
361 | addw $12, %sp |
---|
362 | /* Abort if allocation fails */ |
---|
363 | testw %dx, %dx /* %ax==0 even on success, since align>=64kB */ |
---|
364 | jz pmm_fail |
---|
365 | /* If block has A20==1, free block and try again with twice |
---|
366 | * the allocation size (and hence alignment). |
---|
367 | */ |
---|
368 | testw $0x0010, %dx |
---|
369 | jz got_pmm |
---|
370 | pushw %dx |
---|
371 | pushw $0 |
---|
372 | pushw $PMM_DEALLOCATE |
---|
373 | lcall *%es:7 |
---|
374 | addw $6, %sp |
---|
375 | addw %si, %si |
---|
376 | jmp pmm_loop |
---|
377 | got_pmm: /* PMM allocation succeeded */ |
---|
378 | movw %dx, ( image_source + 2 ) |
---|
379 | movw %dx, %ax |
---|
380 | xorw %di, %di |
---|
381 | call print_hex_word |
---|
382 | movb $( '@' ), %al |
---|
383 | call print_character |
---|
384 | movw %si, %ax |
---|
385 | call print_hex_byte |
---|
386 | pmm_copy: |
---|
387 | /* Copy ROM to PMM block */ |
---|
388 | xorw %ax, %ax |
---|
389 | movw %ax, %es |
---|
390 | movl image_source, %edi |
---|
391 | xorl %esi, %esi |
---|
392 | movzbl romheader_size, %ecx |
---|
393 | shll $9, %ecx |
---|
394 | addr32 rep movsb /* PMM presence implies flat real mode */ |
---|
395 | movl %edi, decompress_to |
---|
396 | /* Shrink ROM */ |
---|
397 | movb $_prefix_memsz_sect, romheader_size |
---|
398 | #if defined(SHRINK_WITHOUT_PMM) || defined(LOAD_ROM_FROM_PCI) |
---|
399 | jmp pmm_done |
---|
400 | pmm_fail: |
---|
401 | /* Print marker and copy ourselves to high memory */ |
---|
402 | movl $HIGHMEM_LOADPOINT, image_source |
---|
403 | xorw %di, %di |
---|
404 | movb $( '!' ), %al |
---|
405 | call print_character |
---|
406 | jmp pmm_copy |
---|
407 | pmm_done: |
---|
408 | #else |
---|
409 | pmm_fail: |
---|
410 | #endif |
---|
411 | /* Restore upper register halves */ |
---|
412 | popal |
---|
413 | #if defined(LOAD_ROM_FROM_PCI) |
---|
414 | call load_from_pci |
---|
415 | jc load_err |
---|
416 | jmp load_ok |
---|
417 | no_pmm: |
---|
418 | /* Cannot continue without PMM - print error message */ |
---|
419 | xorw %di, %di |
---|
420 | movw $init_message_no_pmm, %si |
---|
421 | call print_message |
---|
422 | load_err: |
---|
423 | /* Wait for five seconds to let user see message */ |
---|
424 | movw $90, %cx |
---|
425 | 1: call wait_for_tick |
---|
426 | loop 1b |
---|
427 | /* Mark environment as invalid and return */ |
---|
428 | movl $0, decompress_to |
---|
429 | jmp out |
---|
430 | |
---|
431 | load_ok: |
---|
432 | #else |
---|
433 | no_pmm: |
---|
434 | #endif |
---|
435 | /* Update checksum */ |
---|
436 | xorw %bx, %bx |
---|
437 | xorw %si, %si |
---|
438 | movzbw romheader_size, %cx |
---|
439 | shlw $9, %cx |
---|
440 | 1: lodsb |
---|
441 | addb %al, %bl |
---|
442 | loop 1b |
---|
443 | subb %bl, checksum |
---|
444 | |
---|
445 | /* Copy self to option ROM space. Required for PCI3.0, which |
---|
446 | * loads us to a temporary location in low memory. Will be a |
---|
447 | * no-op for lower PCI versions. |
---|
448 | */ |
---|
449 | movb $( ' ' ), %al |
---|
450 | xorw %di, %di |
---|
451 | call print_character |
---|
452 | movw %gs, %ax |
---|
453 | call print_hex_word |
---|
454 | movzbw romheader_size, %cx |
---|
455 | shlw $9, %cx |
---|
456 | movw %ax, %es |
---|
457 | xorw %si, %si |
---|
458 | xorw %di, %di |
---|
459 | cs rep movsb |
---|
460 | |
---|
461 | /* Prompt for POST-time shell */ |
---|
462 | movw $init_message_prompt, %si |
---|
463 | xorw %di, %di |
---|
464 | call print_message |
---|
465 | movw $prodstr, %si |
---|
466 | call print_message |
---|
467 | movw $init_message_dots, %si |
---|
468 | call print_message |
---|
469 | /* Wait for Ctrl-B */ |
---|
470 | movw $0xff02, %bx |
---|
471 | call wait_for_key |
---|
472 | /* Clear prompt */ |
---|
473 | pushf |
---|
474 | xorw %di, %di |
---|
475 | call print_kill_line |
---|
476 | movw $init_message_done, %si |
---|
477 | call print_message |
---|
478 | popf |
---|
479 | jnz out |
---|
480 | /* Ctrl-B was pressed: invoke gPXE. The keypress will be |
---|
481 | * picked up by the initial shell prompt, and we will drop |
---|
482 | * into a shell. |
---|
483 | */ |
---|
484 | pushw %cs |
---|
485 | call exec |
---|
486 | out: |
---|
487 | /* Restore registers */ |
---|
488 | popw %gs |
---|
489 | popw %fs |
---|
490 | popw %es |
---|
491 | popw %ds |
---|
492 | popaw |
---|
493 | |
---|
494 | /* Indicate boot capability to PnP BIOS, if present */ |
---|
495 | movw $0x20, %ax |
---|
496 | lret |
---|
497 | .size init, . - init |
---|
498 | |
---|
499 | /* |
---|
500 | * Note to hardware vendors: |
---|
501 | * |
---|
502 | * If you wish to brand this boot ROM, please do so by defining the |
---|
503 | * strings PRODUCT_NAME and PRODUCT_SHORT_NAME in config/general.h. |
---|
504 | * |
---|
505 | * While nothing in the GPL prevents you from removing all references |
---|
506 | * to gPXE or http://etherboot.org, we prefer you not to do so. |
---|
507 | * |
---|
508 | * If you have an OEM-mandated branding requirement that cannot be |
---|
509 | * satisfied simply by defining PRODUCT_NAME and PRODUCT_SHORT_NAME, |
---|
510 | * please contact us. |
---|
511 | * |
---|
512 | * [ Including an ASCII NUL in PRODUCT_NAME is considered to be |
---|
513 | * bypassing the spirit of this request! ] |
---|
514 | */ |
---|
515 | init_message: |
---|
516 | .ascii "\n" |
---|
517 | .ascii PRODUCT_NAME |
---|
518 | .ascii "\n" |
---|
519 | .asciz "gPXE (http://etherboot.org) - " |
---|
520 | .size init_message, . - init_message |
---|
521 | init_message_pci: |
---|
522 | .asciz " PCI" |
---|
523 | .size init_message_pci, . - init_message_pci |
---|
524 | init_message_pnp: |
---|
525 | .asciz " PnP" |
---|
526 | .size init_message_pnp, . - init_message_pnp |
---|
527 | init_message_bbs: |
---|
528 | .asciz " BBS" |
---|
529 | .size init_message_bbs, . - init_message_bbs |
---|
530 | init_message_pmm: |
---|
531 | .asciz " PMM" |
---|
532 | .size init_message_pmm, . - init_message_pmm |
---|
533 | #ifdef LOAD_ROM_FROM_PCI |
---|
534 | init_message_no_pmm: |
---|
535 | .asciz "\nPMM required but not present!\n" |
---|
536 | .size init_message_no_pmm, . - init_message_no_pmm |
---|
537 | #endif |
---|
538 | init_message_int19: |
---|
539 | .asciz " INT19" |
---|
540 | .size init_message_int19, . - init_message_int19 |
---|
541 | init_message_prompt: |
---|
542 | .asciz "\nPress Ctrl-B to configure " |
---|
543 | .size init_message_prompt, . - init_message_prompt |
---|
544 | init_message_dots: |
---|
545 | .asciz "..." |
---|
546 | .size init_message_dots, . - init_message_dots |
---|
547 | init_message_done: |
---|
548 | .asciz "\n\n" |
---|
549 | .size init_message_done, . - init_message_done |
---|
550 | |
---|
551 | /* ROM image location |
---|
552 | * |
---|
553 | * May be either within option ROM space, or within PMM-allocated block. |
---|
554 | */ |
---|
555 | .globl image_source |
---|
556 | image_source: |
---|
557 | .long 0 |
---|
558 | .size image_source, . - image_source |
---|
559 | |
---|
560 | /* Temporary decompression area |
---|
561 | * |
---|
562 | * May be either at HIGHMEM_LOADPOINT, or within PMM-allocated block. |
---|
563 | * If a PCI ROM load fails, this will be set to zero. |
---|
564 | */ |
---|
565 | .globl decompress_to |
---|
566 | decompress_to: |
---|
567 | .long HIGHMEM_LOADPOINT |
---|
568 | .size decompress_to, . - decompress_to |
---|
569 | |
---|
570 | #ifdef LOAD_ROM_FROM_PCI |
---|
571 | |
---|
572 | /* Set if the PCI BIOS is present, even <3.0 */ |
---|
573 | pcibios_present: |
---|
574 | .byte 0 |
---|
575 | .byte 0 /* for alignment */ |
---|
576 | .size pcibios_present, . - pcibios_present |
---|
577 | |
---|
578 | /* PCI bus:device.function word |
---|
579 | * |
---|
580 | * Filled in by init in the .xrom case, so the remainder of the ROM |
---|
581 | * can be located. |
---|
582 | */ |
---|
583 | pci_busdevfn: |
---|
584 | .word 0 |
---|
585 | .size pci_busdevfn, . - pci_busdevfn |
---|
586 | |
---|
587 | #endif |
---|
588 | |
---|
589 | /* BBS version |
---|
590 | * |
---|
591 | * Filled in by BBS BIOS. We ignore the value. |
---|
592 | */ |
---|
593 | bbs_version: |
---|
594 | .word 0 |
---|
595 | .size bbs_version, . - bbs_version |
---|
596 | |
---|
597 | /* Boot Execution Vector entry point |
---|
598 | * |
---|
599 | * Called by the PnP BIOS when it wants to boot us. |
---|
600 | */ |
---|
601 | bev_entry: |
---|
602 | pushw %cs |
---|
603 | call exec |
---|
604 | lret |
---|
605 | .size bev_entry, . - bev_entry |
---|
606 | |
---|
607 | |
---|
608 | #ifdef LOAD_ROM_FROM_PCI |
---|
609 | |
---|
610 | #define PCI_ROM_ADDRESS 0x30 /* Bits 31:11 address, 10:1 reserved */ |
---|
611 | #define PCI_ROM_ADDRESS_ENABLE 0x00000001 |
---|
612 | #define PCI_ROM_ADDRESS_MASK 0xfffff800 |
---|
613 | |
---|
614 | #define PCIBIOS_READ_WORD 0xb109 |
---|
615 | #define PCIBIOS_READ_DWORD 0xb10a |
---|
616 | #define PCIBIOS_WRITE_WORD 0xb10c |
---|
617 | #define PCIBIOS_WRITE_DWORD 0xb10d |
---|
618 | |
---|
619 | /* Determine size of PCI BAR |
---|
620 | * |
---|
621 | * %bx : PCI bus:dev.fn to probe |
---|
622 | * %di : Address of BAR to find size of |
---|
623 | * %edx : Mask of address bits within BAR |
---|
624 | * |
---|
625 | * %ecx : Size for a memory resource, |
---|
626 | * 1 for an I/O resource (bit 0 set). |
---|
627 | * CF : Set on error or nonexistent device (all-ones read) |
---|
628 | * |
---|
629 | * All other registers saved. |
---|
630 | */ |
---|
631 | pci_bar_size: |
---|
632 | /* Save registers */ |
---|
633 | pushw %ax |
---|
634 | pushl %esi |
---|
635 | pushl %edx |
---|
636 | |
---|
637 | /* Read current BAR value */ |
---|
638 | movw $PCIBIOS_READ_DWORD, %ax |
---|
639 | int $0x1a |
---|
640 | |
---|
641 | /* Check for device existence and save it */ |
---|
642 | testb $1, %cl /* I/O bit? */ |
---|
643 | jz 1f |
---|
644 | andl $1, %ecx /* If so, exit with %ecx = 1 */ |
---|
645 | jmp 99f |
---|
646 | 1: notl %ecx |
---|
647 | testl %ecx, %ecx /* Set ZF iff %ecx was all-ones */ |
---|
648 | notl %ecx |
---|
649 | jnz 1f |
---|
650 | stc /* All ones - exit with CF set */ |
---|
651 | jmp 99f |
---|
652 | 1: movl %ecx, %esi /* Save in %esi */ |
---|
653 | |
---|
654 | /* Write all ones to BAR */ |
---|
655 | movl %edx, %ecx |
---|
656 | movw $PCIBIOS_WRITE_DWORD, %ax |
---|
657 | int $0x1a |
---|
658 | |
---|
659 | /* Read back BAR */ |
---|
660 | movw $PCIBIOS_READ_DWORD, %ax |
---|
661 | int $0x1a |
---|
662 | |
---|
663 | /* Find decode size from least set bit in mask BAR */ |
---|
664 | bsfl %ecx, %ecx /* Find least set bit, log2(decode size) */ |
---|
665 | jz 1f /* Mask BAR should not be zero */ |
---|
666 | xorl %edx, %edx |
---|
667 | incl %edx |
---|
668 | shll %cl, %edx /* %edx = decode size */ |
---|
669 | jmp 2f |
---|
670 | 1: xorl %edx, %edx /* Return zero size for mask BAR zero */ |
---|
671 | |
---|
672 | /* Restore old BAR value */ |
---|
673 | 2: movl %esi, %ecx |
---|
674 | movw $PCIBIOS_WRITE_DWORD, %ax |
---|
675 | int $0x1a |
---|
676 | |
---|
677 | movl %edx, %ecx /* Return size in %ecx */ |
---|
678 | |
---|
679 | /* Restore registers and return */ |
---|
680 | 99: popl %edx |
---|
681 | popl %esi |
---|
682 | popw %ax |
---|
683 | ret |
---|
684 | |
---|
685 | .size pci_bar_size, . - pci_bar_size |
---|
686 | |
---|
687 | /* PCI ROM loader |
---|
688 | * |
---|
689 | * Called from init in the .xrom case to load the non-prefix code |
---|
690 | * using the PCI ROM BAR. |
---|
691 | * |
---|
692 | * Returns with carry flag set on error. All registers saved. |
---|
693 | */ |
---|
694 | load_from_pci: |
---|
695 | /* |
---|
696 | * Use PCI BIOS access to config space. The calls take |
---|
697 | * |
---|
698 | * %ah : 0xb1 %al : function |
---|
699 | * %bx : bus/dev/fn |
---|
700 | * %di : config space address |
---|
701 | * %ecx : value to write (for writes) |
---|
702 | * |
---|
703 | * %ecx : value read (for reads) |
---|
704 | * %ah : return code |
---|
705 | * CF : error indication |
---|
706 | * |
---|
707 | * All registers not used for return are preserved. |
---|
708 | */ |
---|
709 | |
---|
710 | /* Save registers and set up %es for big real mode */ |
---|
711 | pushal |
---|
712 | pushw %es |
---|
713 | xorw %ax, %ax |
---|
714 | movw %ax, %es |
---|
715 | |
---|
716 | /* Check PCI BIOS presence */ |
---|
717 | cmpb $0, pcibios_present |
---|
718 | jz err_pcibios |
---|
719 | |
---|
720 | /* Load existing PCI ROM BAR */ |
---|
721 | movw $PCIBIOS_READ_DWORD, %ax |
---|
722 | movw pci_busdevfn, %bx |
---|
723 | movw $PCI_ROM_ADDRESS, %di |
---|
724 | int $0x1a |
---|
725 | |
---|
726 | /* Maybe it's already enabled? */ |
---|
727 | testb $PCI_ROM_ADDRESS_ENABLE, %cl |
---|
728 | jz 1f |
---|
729 | movb $1, %dl /* Flag indicating no deinit required */ |
---|
730 | movl %ecx, %ebp |
---|
731 | jmp check_rom |
---|
732 | |
---|
733 | /* Determine PCI BAR decode size */ |
---|
734 | 1: movl $PCI_ROM_ADDRESS_MASK, %edx |
---|
735 | call pci_bar_size /* Returns decode size in %ecx */ |
---|
736 | jc err_size_insane /* CF => no ROM BAR, %ecx == ffffffff */ |
---|
737 | |
---|
738 | /* Check sanity of decode size */ |
---|
739 | xorl %eax, %eax |
---|
740 | movw real_size, %ax |
---|
741 | shll $9, %eax /* %eax = ROM size */ |
---|
742 | cmpl %ecx, %eax |
---|
743 | ja err_size_insane /* Insane if decode size < ROM size */ |
---|
744 | cmpl $0x100000, %ecx |
---|
745 | jae err_size_insane /* Insane if decode size >= 1MB */ |
---|
746 | |
---|
747 | /* Find a place to map the BAR |
---|
748 | * In theory we should examine e820 and all PCI BARs to find a |
---|
749 | * free region. However, we run at POST when e820 may not be |
---|
750 | * available, and memory reads of an unmapped location are |
---|
751 | * de facto standardized to return all-ones. Thus, we can get |
---|
752 | * away with searching high memory (0xf0000000 and up) on |
---|
753 | * multiples of the ROM BAR decode size for a sufficiently |
---|
754 | * large all-ones region. |
---|
755 | */ |
---|
756 | movl %ecx, %edx /* Save ROM BAR size in %edx */ |
---|
757 | movl $0xf0000000, %ebp |
---|
758 | xorl %eax, %eax |
---|
759 | notl %eax /* %eax = all ones */ |
---|
760 | bar_search: |
---|
761 | movl %ebp, %edi |
---|
762 | movl %edx, %ecx |
---|
763 | shrl $2, %ecx |
---|
764 | addr32 repe scasl /* Scan %es:edi for anything not all-ones */ |
---|
765 | jz bar_found |
---|
766 | addl %edx, %ebp |
---|
767 | testl $0x80000000, %ebp |
---|
768 | jz err_no_bar |
---|
769 | jmp bar_search |
---|
770 | |
---|
771 | bar_found: |
---|
772 | movl %edi, %ebp |
---|
773 | /* Save current BAR value on stack to restore later */ |
---|
774 | movw $PCIBIOS_READ_DWORD, %ax |
---|
775 | movw $PCI_ROM_ADDRESS, %di |
---|
776 | int $0x1a |
---|
777 | pushl %ecx |
---|
778 | |
---|
779 | /* Map the ROM */ |
---|
780 | movw $PCIBIOS_WRITE_DWORD, %ax |
---|
781 | movl %ebp, %ecx |
---|
782 | orb $PCI_ROM_ADDRESS_ENABLE, %cl |
---|
783 | int $0x1a |
---|
784 | |
---|
785 | xorb %dl, %dl /* %dl = 0 : ROM was not already mapped */ |
---|
786 | check_rom: |
---|
787 | /* Check and copy ROM - enter with %dl set to skip unmapping, |
---|
788 | * %ebp set to mapped ROM BAR address. |
---|
789 | * We check up to prodstr_separator for equality, since anything past |
---|
790 | * that may have been modified. Since our check includes the checksum |
---|
791 | * byte over the whole ROM stub, that should be sufficient. |
---|
792 | */ |
---|
793 | xorb %dh, %dh /* %dh = 0 : ROM did not fail integrity check */ |
---|
794 | |
---|
795 | /* Verify ROM integrity */ |
---|
796 | xorl %esi, %esi |
---|
797 | movl %ebp, %edi |
---|
798 | movl $prodstr_separator, %ecx |
---|
799 | addr32 repe cmpsb |
---|
800 | jz copy_rom |
---|
801 | incb %dh /* ROM failed integrity check */ |
---|
802 | movl %ecx, %ebp /* Save number of bytes left */ |
---|
803 | jmp skip_load |
---|
804 | |
---|
805 | copy_rom: |
---|
806 | /* Print BAR address and indicate whether we mapped it ourselves */ |
---|
807 | movb $( ' ' ), %al |
---|
808 | xorw %di, %di |
---|
809 | call print_character |
---|
810 | movl %ebp, %eax |
---|
811 | call print_hex_dword |
---|
812 | movb $( '-' ), %al /* '-' for self-mapped */ |
---|
813 | subb %dl, %al |
---|
814 | subb %dl, %al /* '+' = '-' - 2 for BIOS-mapped */ |
---|
815 | call print_character |
---|
816 | |
---|
817 | /* Copy ROM at %ebp to PMM or highmem block */ |
---|
818 | movl %ebp, %esi |
---|
819 | movl image_source, %edi |
---|
820 | movzwl real_size, %ecx |
---|
821 | shll $9, %ecx |
---|
822 | addr32 es rep movsb |
---|
823 | movl %edi, decompress_to |
---|
824 | skip_load: |
---|
825 | testb %dl, %dl /* Was ROM already mapped? */ |
---|
826 | jnz skip_unmap |
---|
827 | |
---|
828 | /* Unmap the ROM by restoring old ROM BAR */ |
---|
829 | movw $PCIBIOS_WRITE_DWORD, %ax |
---|
830 | movw $PCI_ROM_ADDRESS, %di |
---|
831 | popl %ecx |
---|
832 | int $0x1a |
---|
833 | |
---|
834 | skip_unmap: |
---|
835 | /* Error handling */ |
---|
836 | testb %dh, %dh |
---|
837 | jnz err_rom_invalid |
---|
838 | clc |
---|
839 | jmp 99f |
---|
840 | |
---|
841 | err_pcibios: /* No PCI BIOS available */ |
---|
842 | movw $load_message_no_pcibios, %si |
---|
843 | xorl %eax, %eax /* "error code" is zero */ |
---|
844 | jmp 1f |
---|
845 | err_size_insane: /* BAR has size (%ecx) that is insane */ |
---|
846 | movw $load_message_size_insane, %si |
---|
847 | movl %ecx, %eax |
---|
848 | jmp 1f |
---|
849 | err_no_bar: /* No space of sufficient size (%edx) found */ |
---|
850 | movw $load_message_no_bar, %si |
---|
851 | movl %edx, %eax |
---|
852 | jmp 1f |
---|
853 | err_rom_invalid: /* Loaded ROM does not match (%ebp bytes left) */ |
---|
854 | movw $load_message_rom_invalid, %si |
---|
855 | movzbl romheader_size, %eax |
---|
856 | shll $9, %eax |
---|
857 | subl %ebp, %eax |
---|
858 | decl %eax /* %eax is now byte index of failure */ |
---|
859 | |
---|
860 | 1: /* Error handler - print message at %si and dword in %eax */ |
---|
861 | xorw %di, %di |
---|
862 | call print_message |
---|
863 | call print_hex_dword |
---|
864 | stc |
---|
865 | 99: popw %es |
---|
866 | popal |
---|
867 | ret |
---|
868 | |
---|
869 | .size load_from_pci, . - load_from_pci |
---|
870 | |
---|
871 | load_message_no_pcibios: |
---|
872 | .asciz "\nNo PCI BIOS found! " |
---|
873 | .size load_message_no_pcibios, . - load_message_no_pcibios |
---|
874 | |
---|
875 | load_message_size_insane: |
---|
876 | .asciz "\nROM resource has invalid size " |
---|
877 | .size load_message_size_insane, . - load_message_size_insane |
---|
878 | |
---|
879 | load_message_no_bar: |
---|
880 | .asciz "\nNo memory hole of sufficient size " |
---|
881 | .size load_message_no_bar, . - load_message_no_bar |
---|
882 | |
---|
883 | load_message_rom_invalid: |
---|
884 | .asciz "\nLoaded ROM is invalid at " |
---|
885 | .size load_message_rom_invalid, . - load_message_rom_invalid |
---|
886 | |
---|
887 | #endif /* LOAD_ROM_FROM_PCI */ |
---|
888 | |
---|
889 | |
---|
890 | /* INT19 entry point |
---|
891 | * |
---|
892 | * Called via the hooked INT 19 if we detected a non-PnP BIOS. We |
---|
893 | * attempt to return via the original INT 19 vector (if we were able |
---|
894 | * to store it). |
---|
895 | */ |
---|
896 | int19_entry: |
---|
897 | pushw %cs |
---|
898 | popw %ds |
---|
899 | /* Prompt user to press B to boot */ |
---|
900 | movw $int19_message_prompt, %si |
---|
901 | xorw %di, %di |
---|
902 | call print_message |
---|
903 | movw $prodstr, %si |
---|
904 | call print_message |
---|
905 | movw $int19_message_dots, %si |
---|
906 | call print_message |
---|
907 | movw $0xdf4e, %bx |
---|
908 | call wait_for_key |
---|
909 | pushf |
---|
910 | xorw %di, %di |
---|
911 | call print_kill_line |
---|
912 | movw $int19_message_done, %si |
---|
913 | call print_message |
---|
914 | popf |
---|
915 | jz 1f |
---|
916 | /* Leave keypress in buffer and start gPXE. The keypress will |
---|
917 | * cause the usual initial Ctrl-B prompt to be skipped. |
---|
918 | */ |
---|
919 | pushw %cs |
---|
920 | call exec |
---|
921 | 1: /* Try to call original INT 19 vector */ |
---|
922 | movl %cs:orig_int19, %eax |
---|
923 | testl %eax, %eax |
---|
924 | je 2f |
---|
925 | ljmp *%cs:orig_int19 |
---|
926 | 2: /* No chained vector: issue INT 18 as a last resort */ |
---|
927 | int $0x18 |
---|
928 | .size int19_entry, . - int19_entry |
---|
929 | orig_int19: |
---|
930 | .long 0 |
---|
931 | .size orig_int19, . - orig_int19 |
---|
932 | |
---|
933 | int19_message_prompt: |
---|
934 | .asciz "Press N to skip booting from " |
---|
935 | .size int19_message_prompt, . - int19_message_prompt |
---|
936 | int19_message_dots: |
---|
937 | .asciz "..." |
---|
938 | .size int19_message_dots, . - int19_message_dots |
---|
939 | int19_message_done: |
---|
940 | .asciz "\n\n" |
---|
941 | .size int19_message_done, . - int19_message_done |
---|
942 | |
---|
943 | /* Execute as a boot device |
---|
944 | * |
---|
945 | */ |
---|
946 | exec: /* Set %ds = %cs */ |
---|
947 | pushw %cs |
---|
948 | popw %ds |
---|
949 | |
---|
950 | #ifdef LOAD_ROM_FROM_PCI |
---|
951 | /* Don't execute if load was invalid */ |
---|
952 | cmpl $0, decompress_to |
---|
953 | jne 1f |
---|
954 | lret |
---|
955 | 1: |
---|
956 | #endif |
---|
957 | |
---|
958 | /* Print message as soon as possible */ |
---|
959 | movw $prodstr, %si |
---|
960 | xorw %di, %di |
---|
961 | call print_message |
---|
962 | movw $exec_message, %si |
---|
963 | call print_message |
---|
964 | |
---|
965 | /* Store magic word on BIOS stack and remember BIOS %ss:sp */ |
---|
966 | pushl $STACK_MAGIC |
---|
967 | movw %ss, %dx |
---|
968 | movw %sp, %bp |
---|
969 | |
---|
970 | /* Obtain a reasonably-sized temporary stack */ |
---|
971 | xorw %ax, %ax |
---|
972 | movw %ax, %ss |
---|
973 | movw $0x7c00, %sp |
---|
974 | |
---|
975 | /* Install gPXE */ |
---|
976 | movl image_source, %esi |
---|
977 | movl decompress_to, %edi |
---|
978 | call alloc_basemem |
---|
979 | call install_prealloc |
---|
980 | |
---|
981 | /* Set up real-mode stack */ |
---|
982 | movw %bx, %ss |
---|
983 | movw $_estack16, %sp |
---|
984 | |
---|
985 | /* Jump to .text16 segment */ |
---|
986 | pushw %ax |
---|
987 | pushw $1f |
---|
988 | lret |
---|
989 | .section ".text16", "awx", @progbits |
---|
990 | 1: /* Call main() */ |
---|
991 | pushl $main |
---|
992 | pushw %cs |
---|
993 | call prot_call |
---|
994 | popl %ecx /* discard */ |
---|
995 | |
---|
996 | /* Uninstall gPXE */ |
---|
997 | call uninstall |
---|
998 | |
---|
999 | /* Restore BIOS stack */ |
---|
1000 | movw %dx, %ss |
---|
1001 | movw %bp, %sp |
---|
1002 | |
---|
1003 | /* Check magic word on BIOS stack */ |
---|
1004 | popl %eax |
---|
1005 | cmpl $STACK_MAGIC, %eax |
---|
1006 | jne 1f |
---|
1007 | /* BIOS stack OK: return to caller */ |
---|
1008 | lret |
---|
1009 | 1: /* BIOS stack corrupt: use INT 18 */ |
---|
1010 | int $0x18 |
---|
1011 | .previous |
---|
1012 | |
---|
1013 | exec_message: |
---|
1014 | .asciz " starting execution\n" |
---|
1015 | .size exec_message, . - exec_message |
---|
1016 | |
---|
1017 | /* Wait for key press specified by %bl (masked by %bh) |
---|
1018 | * |
---|
1019 | * Used by init and INT19 code when prompting user. If the specified |
---|
1020 | * key is pressed, it is left in the keyboard buffer. |
---|
1021 | * |
---|
1022 | * Returns with ZF set iff specified key is pressed. |
---|
1023 | */ |
---|
1024 | wait_for_key: |
---|
1025 | /* Preserve registers */ |
---|
1026 | pushw %cx |
---|
1027 | pushw %ax |
---|
1028 | 1: /* Empty the keyboard buffer before waiting for input */ |
---|
1029 | movb $0x01, %ah |
---|
1030 | int $0x16 |
---|
1031 | jz 2f |
---|
1032 | xorw %ax, %ax |
---|
1033 | int $0x16 |
---|
1034 | jmp 1b |
---|
1035 | 2: /* Wait for a key press */ |
---|
1036 | movw $ROM_BANNER_TIMEOUT, %cx |
---|
1037 | 3: decw %cx |
---|
1038 | js 99f /* Exit with ZF clear */ |
---|
1039 | /* Wait for timer tick to be updated */ |
---|
1040 | call wait_for_tick |
---|
1041 | /* Check to see if a key was pressed */ |
---|
1042 | movb $0x01, %ah |
---|
1043 | int $0x16 |
---|
1044 | jz 3b |
---|
1045 | /* Check to see if key was the specified key */ |
---|
1046 | andb %bh, %al |
---|
1047 | cmpb %al, %bl |
---|
1048 | je 99f /* Exit with ZF set */ |
---|
1049 | /* Not the specified key: remove from buffer and stop waiting */ |
---|
1050 | pushfw |
---|
1051 | xorw %ax, %ax |
---|
1052 | int $0x16 |
---|
1053 | popfw /* Exit with ZF clear */ |
---|
1054 | 99: /* Restore registers and return */ |
---|
1055 | popw %ax |
---|
1056 | popw %cx |
---|
1057 | ret |
---|
1058 | .size wait_for_key, . - wait_for_key |
---|
1059 | |
---|
1060 | /* Wait for timer tick |
---|
1061 | * |
---|
1062 | * Used by wait_for_key |
---|
1063 | */ |
---|
1064 | wait_for_tick: |
---|
1065 | pushl %eax |
---|
1066 | pushw %fs |
---|
1067 | movw $0x40, %ax |
---|
1068 | movw %ax, %fs |
---|
1069 | movl %fs:(0x6c), %eax |
---|
1070 | 1: pushf |
---|
1071 | sti |
---|
1072 | hlt |
---|
1073 | popf |
---|
1074 | cmpl %fs:(0x6c), %eax |
---|
1075 | je 1b |
---|
1076 | popw %fs |
---|
1077 | popl %eax |
---|
1078 | ret |
---|
1079 | .size wait_for_tick, . - wait_for_tick |
---|