[romprefix] Preserve %edi when issuing INT 1A,B101
[people/mcb30/gpxe.git] / src / arch / i386 / prefix / romprefix.S
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 #define PNP_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) )
10 #define PMM_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'M' << 16 ) + ( 'M' << 24 ) )
11 #define PCI_SIGNATURE ( 'P' + ( 'C' << 8 ) + ( 'I' << 16 ) + ( ' ' << 24 ) )
12 #define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
13 #define PNP_GET_BBS_VERSION 0x60
14 #define PMM_ALLOCATE 0x0000
15
16         .text
17         .code16
18         .arch i386
19         .section ".prefix", "ax", @progbits
20         
21         .org    0x00
22 romheader:
23         .word   0xAA55                  /* BIOS extension signature */
24 romheader_size: .byte _load_size_sect   /* Size in 512-byte blocks */
25         jmp     init                    /* Initialisation vector */
26 checksum:
27         .byte   0
28         .org    0x16
29         .word   undiheader
30         .org    0x18
31         .word   pciheader
32         .org    0x1a
33         .word   pnpheader
34         .size romheader, . - romheader
35         
36         .section ".zinfo.fixup", "a"    /* Compressor fixup information */
37         .ascii  "SUBB"
38         .long   romheader_size
39         .long   512
40         .long   0
41         .previous
42
43 pciheader:
44         .ascii  "PCIR"                  /* Signature */
45         .word   pci_vendor_id           /* Vendor identification */ 
46         .word   pci_device_id           /* Device identification */
47         .word   0x0000                  /* Device list pointer */
48         .word   pciheader_len           /* PCI data structure length */
49         .byte   0x03                    /* PCI data structure revision */
50         .byte   0x02, 0x00, 0x00        /* Class code */
51 pciheader_image_length:
52         .word   _load_size_sect         /* Image length */
53         .word   0x0001                  /* Revision level */
54         .byte   0x00                    /* Code type */
55         .byte   0x80                    /* Last image indicator */
56 pciheader_runtime_length:
57         .word   _load_size_sect         /* Maximum run-time image length */
58         .word   0x0000                  /* Configuration utility code header */
59         .word   0x0000                  /* DMTF CLP entry point */
60         .equ pciheader_len, . - pciheader
61         .size pciheader, . - pciheader
62         
63         .section ".zinfo.fixup", "a"    /* Compressor fixup information */
64         .ascii  "SUBW"
65         .long   pciheader_image_length
66         .long   512
67         .long   0
68         .ascii  "SUBW"
69         .long   pciheader_runtime_length
70         .long   512
71         .long   0
72         .previous
73
74 pnpheader:
75         .ascii  "$PnP"                  /* Signature */
76         .byte   0x01                    /* Structure revision */
77         .byte   ( pnpheader_len / 16 )  /* Length (in 16 byte increments) */
78         .word   0x0000                  /* Offset of next header */
79         .byte   0x00                    /* Reserved */
80         .byte   0x00                    /* Checksum */
81         .long   0x00000000              /* Device identifier */
82         .word   mfgstr                  /* Manufacturer string */
83         .word   prodstr                 /* Product name */
84         .byte   0x02                    /* Device base type code */
85         .byte   0x00                    /* Device sub-type code */
86         .byte   0x00                    /* Device interface type code */
87         .byte   0xf4                    /* Device indicator */
88         .word   0x0000                  /* Boot connection vector */
89         .word   0x0000                  /* Disconnect vector */
90         .word   bev_entry               /* Boot execution vector */
91         .word   0x0000                  /* Reserved */
92         .word   0x0000                  /* Static resource information vector*/
93         .equ pnpheader_len, . - pnpheader
94         .size pnpheader, . - pnpheader
95
96 /* Manufacturer string */
97 mfgstr:
98         .asciz  "http://etherboot.org"
99         .size mfgstr, . - mfgstr
100
101 /* Product string
102  *
103  * Defaults to "gPXE".  If the ROM image is writable at initialisation
104  * time, it will be filled in to include the PCI bus:dev.fn number of
105  * the card as well.
106  */
107 prodstr:
108         .ascii  "gPXE"
109 prodstr_separator:
110         .byte   0
111         .ascii  "(PCI "
112 prodstr_pci_id:
113         .asciz  "xx:xx.x)"              /* Filled in by init code */
114         .size prodstr, . - prodstr
115         
116 undiheader:
117         .ascii  "UNDI"                  /* Signature */
118         .byte   undiheader_len          /* Length of structure */
119         .byte   0                       /* Checksum */
120         .byte   0                       /* Structure revision */
121         .byte   0,1,2                   /* PXE version: 2.1.0 */
122         .word   undiloader              /* Offset to loader routine */
123         .word   _data16_size            /* Stack segment size */
124         .word   _data16_size            /* Data segment size */
125         .word   _text16_size            /* Code segment size */
126         .equ undiheader_len, . - undiheader
127         .size undiheader, . - undiheader
128
129 /* Initialisation (called once during POST)
130  *
131  * Determine whether or not this is a PnP system via a signature
132  * check.  If it is PnP, return to the PnP BIOS indicating that we are
133  * a boot-capable device; the BIOS will call our boot execution vector
134  * if it wants to boot us.  If it is not PnP, hook INT 19.
135  */
136 init:
137         /* Preserve registers, clear direction flag, set %ds=%cs */
138         pushaw
139         pushw   %ds
140         pushw   %es
141         pushw   %fs
142         pushw   %gs
143         cld
144         pushw   %cs
145         popw    %ds
146         pushw   $0x40
147         popw    %fs
148
149         /* Shuffle some registers around.  We need %di available for
150          * the print_xxx functions, and in a register that's
151          * addressable from %es, so shuffle as follows:
152          *
153          *    %di (pointer to PnP structure) => %bx
154          *    %bx (runtime segment address, for PCI 3.0) => %gs
155          */
156         movw    %bx, %gs
157         movw    %di, %bx
158
159         /* Print message as early as possible */
160         movw    $init_message, %si
161         xorw    %di, %di
162         call    print_message
163         call    print_pci_busdevfn
164
165         /* Fill in product name string, if possible */
166         movw    $prodstr_pci_id, %di
167         call    print_pci_busdevfn
168         movb    $' ', prodstr_separator
169
170         /* Print segment address */
171         movb    $' ', %al
172         xorw    %di, %di
173         call    print_character
174         movw    %cs, %ax
175         call    print_hex_word
176
177         /* Check for PCI BIOS version */
178         pushl   %ebx
179         pushl   %edx
180         pushl   %edi
181         stc
182         movw    $0xb101, %ax
183         int     $0x1a
184         jc      1f
185         cmpl    $PCI_SIGNATURE, %edx
186         jne     1f
187         testb   %ah, %ah
188         jnz     1f
189         movw    $init_message_pci, %si
190         xorw    %di, %di
191         call    print_message
192         movb    %bh, %al
193         call    print_hex_nibble
194         movb    $'.', %al
195         call    print_character
196         movb    %bl, %al
197         call    print_hex_byte
198         cmpb    $3, %bh
199         jae     2f
200 1:      /* PCI <3.0: set %gs (runtime segment) = %cs (init-time segment) */
201         pushw   %cs
202         popw    %gs
203 2:      popl    %edi
204         popl    %edx
205         popl    %ebx
206
207         /* Check for PnP BIOS */
208         testw   $0x0f, %bx      /* PnP signature must be aligned - bochs    */
209         jnz     no_bbs          /* uses unalignment to indicate 'fake' PnP. */
210         cmpl    $PNP_SIGNATURE, %es:0(%bx)
211         jne     no_bbs
212         /* Is PnP: print PnP message */
213         movw    $init_message_pnp, %si
214         xorw    %di, %di
215         call    print_message
216         /* Check for BBS */
217         pushw   %es:0x1b(%bx)   /* Real-mode data segment */
218         pushw   %ds             /* &(bbs_version) */
219         pushw   $bbs_version
220         pushw   $PNP_GET_BBS_VERSION
221         lcall   *%es:0xd(%bx)
222         addw    $8, %sp
223         testw   %ax, %ax
224         je      got_bbs
225 no_bbs: /* Not BBS-compliant - must hook INT 19 */
226         movw    $init_message_int19, %si
227         xorw    %di, %di
228         call    print_message
229         xorw    %ax, %ax
230         movw    %ax, %es
231         pushl   %es:( 0x19 * 4 )
232         popl    orig_int19
233         pushw   %gs /* %gs contains runtime %cs */
234         pushw   $int19_entry
235         popl    %es:( 0x19 * 4 )
236         jmp     bbs_done
237 got_bbs: /* BBS compliant - no need to hook INT 19 */
238         movw    $init_message_bbs, %si
239         xorw    %di, %di
240         call    print_message
241 bbs_done:
242
243         /* Check for PMM */
244         movw    $( 0xe000 - 1 ), %bx
245 pmm_scan:
246         incw    %bx
247         jz      no_pmm
248         movw    %bx, %es
249         cmpl    $PMM_SIGNATURE, %es:0
250         jne     pmm_scan
251         xorw    %dx, %dx
252         xorw    %si, %si
253         movzbw  %es:5, %cx
254 1:      es lodsb
255         addb    %al, %dl
256         loop    1b
257         jnz     pmm_scan
258         /* PMM found: print PMM message */
259         movw    $init_message_pmm, %si
260         xorw    %di, %di
261         call    print_message
262         /* Try to allocate 2MB block via PMM */
263         pushw   $0x0006         /* Aligned, extended memory */
264         pushl   $0xffffffff     /* No handle */
265         pushl   $( 0x00200000 / 16 ) /* 2MB in paragraphs */
266         pushw   $PMM_ALLOCATE
267         lcall   *%es:7
268         addw    $12, %sp
269         movw    %dx, %ax
270         xorw    %di, %di
271         call    print_hex_word
272         movw    %dx, ( image_source + 2 )
273         testw   %dx, %dx        /* %ax==0 even on success, since align=2MB */
274         jz      no_pmm
275         /* PMM allocation succeeded: copy ROM to PMM block */
276         pushal                  /* PMM presence implies 1kB stack */
277         xorw    %ax, %ax
278         movw    %ax, %es
279         movl    image_source, %edi
280         xorl    %esi, %esi
281         movzbl  romheader_size, %ecx
282         shll    $9, %ecx
283         addr32 rep movsb        /* PMM presence implies flat real mode */
284         movl    %edi, decompress_to
285         /* Shrink ROM and update checksum */
286         xorw    %bx, %bx
287         xorw    %si, %si
288         movw    $_prefix_size_sect, %cx
289         movb    %cl, romheader_size
290         shlw    $9, %cx
291 1:      lodsb
292         addb    %al, %bl
293         loop    1b
294         subb    %bl, checksum
295         popal
296 no_pmm:
297
298         /* Copy self to option ROM space.  Required for PCI3.0, which
299          * loads us to a temporary location in low memory.  Will be a
300          * no-op for lower PCI versions.
301          */
302         movb    $' ', %al
303         xorw    %di, %di
304         call    print_character
305         movw    %gs, %ax
306         call    print_hex_word
307         movzbw  romheader_size, %cx
308         shlw    $9, %cx
309         movw    %ax, %es
310         xorw    %si, %si
311         xorw    %di, %di
312         cs rep  movsb
313
314         /* Prompt for POST-time shell */
315         movw    $init_message_prompt, %si
316         xorw    %di, %di
317         call    print_message
318         /* Empty the keyboard buffer before waiting for input */
319 empty_keyboard_buffer:
320         movb    $0x01, %ah
321         int     $0x16
322         jz      1f
323         xorw    %ax, %ax
324         int     $0x16
325         jmp     empty_keyboard_buffer
326 1:      /* Wait for up to 3s for a key press */
327         movw    $(18 * 3), %cx  /* Approx 3s worth of timer ticks */
328 wait_for_key:
329         decw    %cx
330         jz      no_key_pressed
331         /* Wait for timer tick to be updated */
332         movl    %fs:(0x6c), %eax
333 1:      pushf
334         sti
335         hlt
336         popf
337         cmpl    %fs:(0x6c), %eax
338         je      1b
339         /* Check to see if a key was pressed */
340         movb    $0x01, %ah
341         int     $0x16
342         jz      wait_for_key
343         /* Check to see if key was Ctrl-B */
344         cmpb    $0x02, %al
345         je      1f
346         /* Key was not Ctrl-B: remove from buffer and stop waiting */
347         xorw    %ax, %ax
348         int     $0x16
349         jmp     no_key_pressed
350 1:      /* Key was Ctrl-B: leave in keyboard buffer and invoke gPXE.
351          * The keypress will be picked up by the initial shell
352          * prompt, and we will drop into a shell.
353          */
354         pushw   %cs
355         call    exec
356 no_key_pressed:
357
358         /* Print blank lines to terminate messages */
359         movw    $init_message_end, %si
360         xorw    %di, %di
361         call    print_message
362
363         /* Restore registers */
364         popw    %gs
365         popw    %fs
366         popw    %es
367         popw    %ds
368         popaw
369
370         /* Indicate boot capability to PnP BIOS, if present */
371         movw    $0x20, %ax
372         lret
373         .size init, . - init
374
375 init_message:
376         .asciz  "gPXE (http://etherboot.org) - "
377         .size   init_message, . - init_message
378 init_message_pci:
379         .asciz  " PCI"
380         .size   init_message_pci, . - init_message_pci
381 init_message_pnp:
382         .asciz  " PnP"
383         .size   init_message_pnp, . - init_message_pnp
384 init_message_bbs:
385         .asciz  " BBS"
386         .size   init_message_bbs, . - init_message_bbs
387 init_message_pmm:
388         .asciz  " PMM"
389         .size   init_message_pmm, . - init_message_pmm
390 init_message_int19:
391         .asciz  " INT19"
392         .size   init_message_int19, . - init_message_int19
393 init_message_prompt:
394         .asciz  "\nPress Ctrl-B to configure gPXE..."
395         .size   init_message_prompt, . - init_message_prompt
396 init_message_end:
397         .asciz  "\n\n\n"
398         .size   init_message_end, . - init_message_end
399
400 /* ROM image location
401  *
402  * May be either within option ROM space, or within PMM-allocated block.
403  */
404 image_source:
405         .long   0
406         .size   image_source, . - image_source
407
408 /* Temporary decompression area
409  *
410  * May be either at HIGHMEM_LOADPOINT, or within PMM-allocated block.
411  */
412 decompress_to:
413         .long   HIGHMEM_LOADPOINT
414         .size   decompress_to, . - decompress_to
415
416 /* BBS version
417  *
418  * Filled in by BBS BIOS.  We ignore the value.
419  */
420 bbs_version:
421         .word   0
422         .size   bbs_version, . - bbs_version
423
424 /* Boot Execution Vector entry point
425  *
426  * Called by the PnP BIOS when it wants to boot us.
427  */
428 bev_entry:
429         pushw   %cs
430         call    exec
431         lret
432         .size   bev_entry, . - bev_entry
433
434 /* INT19 entry point
435  *
436  * Called via the hooked INT 19 if we detected a non-PnP BIOS.  We
437  * attempt to return via the original INT 19 vector (if we were able to
438  * store it).
439  */
440 int19_entry:
441         pushw   %cs
442         call    exec
443         movl    %cs:orig_int19, %eax
444         testl   %eax, %eax
445         je      1f
446         /* Chain to original INT 19 vector */
447         ljmp    *%cs:orig_int19
448 1:      /* No chained vector: issue INT 18 as a last resort */
449         int     $0x18
450         .size   int19_entry, . - int19_entry
451 orig_int19:
452         .long   0
453         .size   orig_int19, . - orig_int19
454
455 /* Execute as a boot device
456  *
457  */
458 exec:   /* Set %ds = %cs */
459         pushw   %cs
460         popw    %ds
461
462         /* Print message as soon as possible */
463         movw    $prodstr, %si
464         xorw    %di, %di
465         call    print_message
466         movw    $exec_message, %si
467         call    print_message
468
469         /* Store magic word on BIOS stack and remember BIOS %ss:sp */
470         pushl   $STACK_MAGIC
471         movw    %ss, %dx
472         movw    %sp, %bp
473
474         /* Obtain a reasonably-sized temporary stack */
475         xorw    %ax, %ax
476         movw    %ax, %ss
477         movw    $0x7c00, %sp
478
479         /* Install gPXE */
480         movl    image_source, %esi
481         movl    decompress_to, %edi
482         call    alloc_basemem
483         call    install_prealloc
484
485         /* Set up real-mode stack */
486         movw    %bx, %ss
487         movw    $_estack16, %sp
488
489         /* Jump to .text16 segment */
490         pushw   %ax
491         pushw   $1f
492         lret
493         .section ".text16", "awx", @progbits
494 1:      /* Call main() */
495         pushl   $main
496         pushw   %cs
497         call    prot_call
498         /* No need to clean up stack; we are about to reload %ss:sp */
499         
500         /* Restore BIOS stack */
501         movw    %dx, %ss
502         movw    %bp, %sp
503
504         /* Check magic word on BIOS stack */
505         popl    %eax
506         cmpl    $STACK_MAGIC, %eax
507         jne     1f
508         /* BIOS stack OK: return to caller */
509         lret
510 1:      /* BIOS stack corrupt: use INT 18 */
511         int     $0x18
512         .previous
513
514 exec_message:
515         .asciz  " starting execution\n"
516         .size exec_message, . - exec_message
517
518 /* UNDI loader
519  *
520  * Called by an external program to load our PXE stack.
521  */
522 undiloader:
523         /* Save registers */
524         pushl   %esi
525         pushl   %edi
526         pushw   %es
527         pushw   %bx
528         /* UNDI loader parameter structure address into %es:%di */
529         movw    %sp, %bx
530         movw    %ss:16(%bx), %di
531         movw    %ss:18(%bx), %es
532         /* Install to specified real-mode addresses */
533         pushw   %di
534         movw    %es:12(%di), %bx
535         movw    %es:14(%di), %ax
536         movl    %cs:image_source, %esi
537         movl    %cs:decompress_to, %edi
538         call    install_prealloc
539         popw    %di
540         /* Call UNDI loader C code */
541         pushl   $pxe_loader_call
542         pushw   %cs
543         pushw   $1f
544         pushw   %ax
545         pushw   $prot_call
546         lret
547 1:      popw    %bx     /* discard */
548         popw    %bx     /* discard */
549         /* Restore registers and return */
550         popw    %bx
551         popw    %es
552         popl    %edi
553         popl    %esi
554         lret
555         .size undiloader, . - undiloader