[romprefix] On a PCI3.0, non-BBS system, use the correct %cs for INT19
[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
148         /* Shuffle some registers around.  We need %di available for
149          * the print_xxx functions, and in a register that's
150          * addressable from %es, so shuffle as follows:
151          *
152          *    %di (pointer to PnP structure) => %bx
153          *    %bx (runtime segment address, for PCI 3.0) => %gs
154          */
155         movw    %bx, %gs
156         movw    %di, %bx
157
158         /* Print message as early as possible */
159         movw    $init_message, %si
160         xorw    %di, %di
161         call    print_message
162         call    print_pci_busdevfn
163
164         /* Fill in product name string, if possible */
165         movw    $prodstr_pci_id, %di
166         call    print_pci_busdevfn
167         movb    $' ', prodstr_separator
168
169         /* Print segment address */
170         movb    $' ', %al
171         xorw    %di, %di
172         call    print_character
173         movw    %cs, %ax
174         call    print_hex_word
175
176         /* Check for PCI BIOS version */
177         pushl   %ebx
178         pushl   %edx
179         stc
180         movw    $0xb101, %ax
181         int     $0x1a
182         jc      1f
183         cmpl    $PCI_SIGNATURE, %edx
184         jne     1f
185         testb   %ah, %ah
186         jnz     1f
187         movw    $init_message_pci, %si
188         xorw    %di, %di
189         call    print_message
190         movb    %bh, %al
191         call    print_hex_nibble
192         movb    $'.', %al
193         call    print_character
194         movb    %bl, %al
195         call    print_hex_byte
196         cmpb    $3, %bh
197         jae     2f
198 1:      /* PCI <3.0: set %gs (runtime segment) = %cs (init-time segment) */
199         pushw   %cs
200         popw    %gs
201 2:      popl    %edx
202         popl    %ebx
203
204         /* Check for PnP BIOS */
205         testw   $0x0f, %bx      /* PnP signature must be aligned - bochs    */
206         jnz     hook_int19      /* uses unalignment to indicate 'fake' PnP. */
207         cmpl    $PNP_SIGNATURE, %es:0(%bx)
208         jne     hook_int19
209         /* Is PnP: print PnP message */
210         movw    $init_message_pnp, %si
211         xorw    %di, %di
212         call    print_message
213
214         /* Check for BBS */
215         pushw   %es:0x1b(%bx)   /* Real-mode data segment */
216         pushw   %ds             /* &(bbs_version) */
217         pushw   $bbs_version
218         pushw   $PNP_GET_BBS_VERSION
219         lcall   *%es:0xd(%bx)
220         addw    $8, %sp
221         testw   %ax, %ax
222         jne     hook_int19
223         movw    $init_message_bbs, %si
224         xorw    %di, %di
225         call    print_message
226         jmp     hook_bbs
227         /* Not BBS-compliant - must hook INT 19 */
228 hook_int19:
229         movw    $init_message_int19, %si
230         xorw    %di, %di
231         call    print_message
232         xorw    %ax, %ax
233         movw    %ax, %es
234         pushl   %es:( 0x19 * 4 )
235         popl    orig_int19
236         pushw   %gs /* %gs contains runtime %cs */
237         pushw   $int19_entry
238         popl    %es:( 0x19 * 4 )
239 hook_bbs:
240
241         /* Check for PMM */
242         movw    $( 0xe000 - 1 ), %bx
243 pmm_scan:
244         incw    %bx
245         jz      no_pmm
246         movw    %bx, %es
247         cmpl    $PMM_SIGNATURE, %es:0
248         jne     pmm_scan
249         xorw    %dx, %dx
250         xorw    %si, %si
251         movzbw  %es:5, %cx
252 1:      es lodsb
253         addb    %al, %dl
254         loop    1b
255         jnz     pmm_scan
256         /* PMM found: print PMM message */
257         movw    $init_message_pmm, %si
258         xorw    %di, %di
259         call    print_message
260         /* Try to allocate 2MB block via PMM */
261         pushw   $0x0006         /* Aligned, extended memory */
262         pushl   $0xffffffff     /* No handle */
263         pushl   $( 0x00200000 / 16 ) /* 2MB in paragraphs */
264         pushw   $0x0000         /* pmmAllocate */
265         lcall   *%es:7
266         addw    $12, %sp
267         testw   %dx, %dx        /* %ax==0 even on success, since align=2MB */
268         jnz     gotpmm
269         movb    $'-', %al
270         xorw    %di, %di
271         call    print_character
272         jmp     no_pmm
273 gotpmm: /* PMM allocation succeeded: copy ROM to PMM block */
274         pushal                  /* PMM presence implies 1kB stack */
275         movw    %ax, %es        /* %ax=0 already - see above */
276         pushw   %dx
277         pushw   %ax
278         popl    %edi
279         movl    %edi, image_source
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) - PCI "
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
423 /* Boot Execution Vector entry point
424  *
425  * Called by the PnP BIOS when it wants to boot us.
426  */
427 bev_entry:
428         pushw   %cs
429         call    exec
430         lret
431         .size   bev_entry, . - bev_entry
432
433 /* INT19 entry point
434  *
435  * Called via the hooked INT 19 if we detected a non-PnP BIOS.  We
436  * attempt to return via the original INT 19 vector (if we were able to
437  * store it).
438  */
439 int19_entry:
440         pushw   %cs
441         call    exec
442         movl    %cs:orig_int19, %eax
443         testl   %eax, %eax
444         je      1f
445         /* Chain to original INT 19 vector */
446         ljmp    *%cs:orig_int19
447 1:      /* No chained vector: issue INT 18 as a last resort */
448         int     $0x18
449         .size   int19_entry, . - int19_entry
450 orig_int19:
451         .long   0
452         .size   orig_int19, . - orig_int19
453
454 /* Execute as a boot device
455  *
456  */
457 exec:   /* Set %ds = %cs */
458         pushw   %cs
459         popw    %ds
460
461         /* Print message as soon as possible */
462         movw    $exec_message, %si
463         xorw    %di, %di
464         call    print_message
465
466         /* Store magic word on BIOS stack and remember BIOS %ss:sp */
467         pushl   $STACK_MAGIC
468         movw    %ss, %dx
469         movw    %sp, %bp
470
471         /* Obtain a reasonably-sized temporary stack */
472         xorw    %ax, %ax
473         movw    %ax, %ss
474         movw    $0x7c00, %sp
475
476         /* Install gPXE */
477         movl    image_source, %esi
478         movl    decompress_to, %edi
479         call    alloc_basemem
480         call    install_prealloc
481
482         /* Set up real-mode stack */
483         movw    %bx, %ss
484         movw    $_estack16, %sp
485
486         /* Jump to .text16 segment */
487         pushw   %ax
488         pushw   $1f
489         lret
490         .section ".text16", "awx", @progbits
491 1:      /* Call main() */
492         pushl   $main
493         pushw   %cs
494         call    prot_call
495         /* No need to clean up stack; we are about to reload %ss:sp */
496         
497         /* Restore BIOS stack */
498         movw    %dx, %ss
499         movw    %bp, %sp
500
501         /* Check magic word on BIOS stack */
502         popl    %eax
503         cmpl    $STACK_MAGIC, %eax
504         jne     1f
505         /* BIOS stack OK: return to caller */
506         lret
507 1:      /* BIOS stack corrupt: use INT 18 */
508         int     $0x18
509         .previous
510
511 exec_message:
512         .asciz  "Entering gPXE\n"
513         .size exec_message, . - exec_message
514
515 /* UNDI loader
516  *
517  * Called by an external program to load our PXE stack.
518  */
519 undiloader:
520         /* Save registers */
521         pushl   %esi
522         pushl   %edi
523         pushw   %es
524         pushw   %bx
525         /* UNDI loader parameter structure address into %es:%di */
526         movw    %sp, %bx
527         movw    %ss:12(%bx), %di
528         movw    %ss:14(%bx), %es
529         /* Install to specified real-mode addresses */
530         pushw   %di
531         movw    %es:12(%di), %bx
532         movw    %es:14(%di), %ax
533         movl    %cs:image_source, %esi
534         movl    %cs:decompress_to, %edi
535         call    install_prealloc
536         popw    %di
537         /* Call UNDI loader C code */
538         pushl   $pxe_loader_call
539         pushw   %cs
540         pushw   $1f
541         pushw   %ax
542         pushw   $prot_call
543         lret
544 1:      popw    %bx     /* discard */
545         popw    %bx     /* discard */
546         /* Restore registers and return */
547         popw    %bx
548         popw    %es
549         popl    %edi
550         popl    %esi
551         lret
552         .size undiloader, . - undiloader