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