90ddd1067d2a63d163ae2ff5e5e695a58edef066
[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         .ascii  "PCIR"                  /* Bus type */
127         .equ undiheader_len, . - undiheader
128         .size undiheader, . - undiheader
129
130 /* Initialisation (called once during POST)
131  *
132  * Determine whether or not this is a PnP system via a signature
133  * check.  If it is PnP, return to the PnP BIOS indicating that we are
134  * a boot-capable device; the BIOS will call our boot execution vector
135  * if it wants to boot us.  If it is not PnP, hook INT 19.
136  */
137 init:
138         /* Preserve registers, clear direction flag, set %ds=%cs */
139         pushaw
140         pushw   %ds
141         pushw   %es
142         pushw   %fs
143         pushw   %gs
144         cld
145         pushw   %cs
146         popw    %ds
147         pushw   $0x40
148         popw    %fs
149
150         /* Shuffle some registers around.  We need %di available for
151          * the print_xxx functions, and in a register that's
152          * addressable from %es, so shuffle as follows:
153          *
154          *    %di (pointer to PnP structure) => %bx
155          *    %bx (runtime segment address, for PCI 3.0) => %gs
156          */
157         movw    %bx, %gs
158         movw    %di, %bx
159
160         /* Print message as early as possible */
161         movw    $init_message, %si
162         xorw    %di, %di
163         call    print_message
164         call    print_pci_busdevfn
165
166         /* Fill in product name string, if possible */
167         movw    $prodstr_pci_id, %di
168         call    print_pci_busdevfn
169         movb    $' ', prodstr_separator
170
171         /* Print segment address */
172         movb    $' ', %al
173         xorw    %di, %di
174         call    print_character
175         movw    %cs, %ax
176         call    print_hex_word
177
178         /* Check for PCI BIOS version */
179         pushl   %ebx
180         pushl   %edx
181         pushl   %edi
182         stc
183         movw    $0xb101, %ax
184         int     $0x1a
185         jc      1f
186         cmpl    $PCI_SIGNATURE, %edx
187         jne     1f
188         testb   %ah, %ah
189         jnz     1f
190         movw    $init_message_pci, %si
191         xorw    %di, %di
192         call    print_message
193         movb    %bh, %al
194         call    print_hex_nibble
195         movb    $'.', %al
196         call    print_character
197         movb    %bl, %al
198         call    print_hex_byte
199         cmpb    $3, %bh
200         jae     2f
201 1:      /* PCI <3.0: set %gs (runtime segment) = %cs (init-time segment) */
202         pushw   %cs
203         popw    %gs
204 2:      popl    %edi
205         popl    %edx
206         popl    %ebx
207
208         /* Check for PnP BIOS */
209         testw   $0x0f, %bx      /* PnP signature must be aligned - bochs    */
210         jnz     no_bbs          /* uses unalignment to indicate 'fake' PnP. */
211         cmpl    $PNP_SIGNATURE, %es:0(%bx)
212         jne     no_bbs
213         /* Is PnP: print PnP message */
214         movw    $init_message_pnp, %si
215         xorw    %di, %di
216         call    print_message
217         /* Check for BBS */
218         pushw   %es:0x1b(%bx)   /* Real-mode data segment */
219         pushw   %ds             /* &(bbs_version) */
220         pushw   $bbs_version
221         pushw   $PNP_GET_BBS_VERSION
222         lcall   *%es:0xd(%bx)
223         addw    $8, %sp
224         testw   %ax, %ax
225         je      got_bbs
226 no_bbs: /* Not BBS-compliant - must hook INT 19 */
227         movw    $init_message_int19, %si
228         xorw    %di, %di
229         call    print_message
230         xorw    %ax, %ax
231         movw    %ax, %es
232         pushl   %es:( 0x19 * 4 )
233         popl    orig_int19
234         pushw   %gs /* %gs contains runtime %cs */
235         pushw   $int19_entry
236         popl    %es:( 0x19 * 4 )
237         jmp     bbs_done
238 got_bbs: /* BBS compliant - no need to hook INT 19 */
239         movw    $init_message_bbs, %si
240         xorw    %di, %di
241         call    print_message
242 bbs_done:
243
244         /* Check for PMM */
245         movw    $( 0xe000 - 1 ), %bx
246 pmm_scan:
247         incw    %bx
248         jz      no_pmm
249         movw    %bx, %es
250         cmpl    $PMM_SIGNATURE, %es:0
251         jne     pmm_scan
252         xorw    %dx, %dx
253         xorw    %si, %si
254         movzbw  %es:5, %cx
255 1:      es lodsb
256         addb    %al, %dl
257         loop    1b
258         jnz     pmm_scan
259         /* PMM found: print PMM message */
260         movw    $init_message_pmm, %si
261         xorw    %di, %di
262         call    print_message
263         /* Try to allocate 2MB block via PMM */
264         pushw   $0x0006         /* Aligned, extended memory */
265         pushl   $0xffffffff     /* No handle */
266         pushl   $( 0x00200000 / 16 ) /* 2MB in paragraphs */
267         pushw   $PMM_ALLOCATE
268         lcall   *%es:7
269         addw    $12, %sp
270         movw    %dx, %ax
271         xorw    %di, %di
272         call    print_hex_word
273         movw    %dx, ( image_source + 2 )
274         testw   %dx, %dx        /* %ax==0 even on success, since align=2MB */
275         jz      no_pmm
276         /* PMM allocation succeeded: copy ROM to PMM block */
277         pushal                  /* PMM presence implies 1kB stack */
278         xorw    %ax, %ax
279         movw    %ax, %es
280         movl    image_source, %edi
281         xorl    %esi, %esi
282         movzbl  romheader_size, %ecx
283         shll    $9, %ecx
284         addr32 rep movsb        /* PMM presence implies flat real mode */
285         movl    %edi, decompress_to
286         /* Shrink ROM and update checksum */
287         xorw    %bx, %bx
288         xorw    %si, %si
289         movw    $_prefix_size_sect, %cx
290         movb    %cl, romheader_size
291         shlw    $9, %cx
292 1:      lodsb
293         addb    %al, %bl
294         loop    1b
295         subb    %bl, checksum
296         popal
297 no_pmm:
298
299         /* Copy self to option ROM space.  Required for PCI3.0, which
300          * loads us to a temporary location in low memory.  Will be a
301          * no-op for lower PCI versions.
302          */
303         movb    $' ', %al
304         xorw    %di, %di
305         call    print_character
306         movw    %gs, %ax
307         call    print_hex_word
308         movzbw  romheader_size, %cx
309         shlw    $9, %cx
310         movw    %ax, %es
311         xorw    %si, %si
312         xorw    %di, %di
313         cs rep  movsb
314
315         /* Prompt for POST-time shell */
316         movw    $init_message_prompt, %si
317         xorw    %di, %di
318         call    print_message
319         /* Empty the keyboard buffer before waiting for input */
320 empty_keyboard_buffer:
321         movb    $0x01, %ah
322         int     $0x16
323         jz      1f
324         xorw    %ax, %ax
325         int     $0x16
326         jmp     empty_keyboard_buffer
327 1:      /* Wait for up to 3s for a key press */
328         movw    $(18 * 3), %cx  /* Approx 3s worth of timer ticks */
329 wait_for_key:
330         decw    %cx
331         jz      no_key_pressed
332         /* Wait for timer tick to be updated */
333         movl    %fs:(0x6c), %eax
334 1:      pushf
335         sti
336         hlt
337         popf
338         cmpl    %fs:(0x6c), %eax
339         je      1b
340         /* Check to see if a key was pressed */
341         movb    $0x01, %ah
342         int     $0x16
343         jz      wait_for_key
344         /* Check to see if key was Ctrl-B */
345         cmpb    $0x02, %al
346         je      1f
347         /* Key was not Ctrl-B: remove from buffer and stop waiting */
348         xorw    %ax, %ax
349         int     $0x16
350         jmp     no_key_pressed
351 1:      /* Key was Ctrl-B: leave in keyboard buffer and invoke gPXE.
352          * The keypress will be picked up by the initial shell
353          * prompt, and we will drop into a shell.
354          */
355         pushw   %cs
356         call    exec
357 no_key_pressed:
358
359         /* Print blank lines to terminate messages */
360         movw    $init_message_end, %si
361         xorw    %di, %di
362         call    print_message
363
364         /* Restore registers */
365         popw    %gs
366         popw    %fs
367         popw    %es
368         popw    %ds
369         popaw
370
371         /* Indicate boot capability to PnP BIOS, if present */
372         movw    $0x20, %ax
373         lret
374         .size init, . - init
375
376 init_message:
377         .asciz  "gPXE (http://etherboot.org) - "
378         .size   init_message, . - init_message
379 init_message_pci:
380         .asciz  " PCI"
381         .size   init_message_pci, . - init_message_pci
382 init_message_pnp:
383         .asciz  " PnP"
384         .size   init_message_pnp, . - init_message_pnp
385 init_message_bbs:
386         .asciz  " BBS"
387         .size   init_message_bbs, . - init_message_bbs
388 init_message_pmm:
389         .asciz  " PMM"
390         .size   init_message_pmm, . - init_message_pmm
391 init_message_int19:
392         .asciz  " INT19"
393         .size   init_message_int19, . - init_message_int19
394 init_message_prompt:
395         .asciz  "\nPress Ctrl-B to configure gPXE..."
396         .size   init_message_prompt, . - init_message_prompt
397 init_message_end:
398         .asciz  "\n\n\n"
399         .size   init_message_end, . - init_message_end
400
401 /* ROM image location
402  *
403  * May be either within option ROM space, or within PMM-allocated block.
404  */
405 image_source:
406         .long   0
407         .size   image_source, . - image_source
408
409 /* Temporary decompression area
410  *
411  * May be either at HIGHMEM_LOADPOINT, or within PMM-allocated block.
412  */
413 decompress_to:
414         .long   HIGHMEM_LOADPOINT
415         .size   decompress_to, . - decompress_to
416
417 /* BBS version
418  *
419  * Filled in by BBS BIOS.  We ignore the value.
420  */
421 bbs_version:
422         .word   0
423         .size   bbs_version, . - bbs_version
424
425 /* Boot Execution Vector entry point
426  *
427  * Called by the PnP BIOS when it wants to boot us.
428  */
429 bev_entry:
430         pushw   %cs
431         call    exec
432         lret
433         .size   bev_entry, . - bev_entry
434
435 /* INT19 entry point
436  *
437  * Called via the hooked INT 19 if we detected a non-PnP BIOS.  We
438  * attempt to return via the original INT 19 vector (if we were able to
439  * store it).
440  */
441 int19_entry:
442         pushw   %cs
443         call    exec
444         movl    %cs:orig_int19, %eax
445         testl   %eax, %eax
446         je      1f
447         /* Chain to original INT 19 vector */
448         ljmp    *%cs:orig_int19
449 1:      /* No chained vector: issue INT 18 as a last resort */
450         int     $0x18
451         .size   int19_entry, . - int19_entry
452 orig_int19:
453         .long   0
454         .size   orig_int19, . - orig_int19
455
456 /* Execute as a boot device
457  *
458  */
459 exec:   /* Set %ds = %cs */
460         pushw   %cs
461         popw    %ds
462
463         /* Print message as soon as possible */
464         movw    $prodstr, %si
465         xorw    %di, %di
466         call    print_message
467         movw    $exec_message, %si
468         call    print_message
469
470         /* Store magic word on BIOS stack and remember BIOS %ss:sp */
471         pushl   $STACK_MAGIC
472         movw    %ss, %dx
473         movw    %sp, %bp
474
475         /* Obtain a reasonably-sized temporary stack */
476         xorw    %ax, %ax
477         movw    %ax, %ss
478         movw    $0x7c00, %sp
479
480         /* Install gPXE */
481         movl    image_source, %esi
482         movl    decompress_to, %edi
483         call    alloc_basemem
484         call    install_prealloc
485
486         /* Set up real-mode stack */
487         movw    %bx, %ss
488         movw    $_estack16, %sp
489
490         /* Jump to .text16 segment */
491         pushw   %ax
492         pushw   $1f
493         lret
494         .section ".text16", "awx", @progbits
495 1:      /* Call main() */
496         pushl   $main
497         pushw   %cs
498         call    prot_call
499         /* No need to clean up stack; we are about to reload %ss:sp */
500         
501         /* Restore BIOS stack */
502         movw    %dx, %ss
503         movw    %bp, %sp
504
505         /* Check magic word on BIOS stack */
506         popl    %eax
507         cmpl    $STACK_MAGIC, %eax
508         jne     1f
509         /* BIOS stack OK: return to caller */
510         lret
511 1:      /* BIOS stack corrupt: use INT 18 */
512         int     $0x18
513         .previous
514
515 exec_message:
516         .asciz  " starting execution\n"
517         .size exec_message, . - exec_message
518
519 /* UNDI loader
520  *
521  * Called by an external program to load our PXE stack.
522  */
523 undiloader:
524         /* Save registers */
525         pushl   %esi
526         pushl   %edi
527         pushw   %es
528         pushw   %bx
529         /* UNDI loader parameter structure address into %es:%di */
530         movw    %sp, %bx
531         movw    %ss:16(%bx), %di
532         movw    %ss:18(%bx), %es
533         /* Install to specified real-mode addresses */
534         pushw   %di
535         movw    %es:12(%di), %bx
536         movw    %es:14(%di), %ax
537         movl    %cs:image_source, %esi
538         movl    %cs:decompress_to, %edi
539         call    install_prealloc
540         popw    %di
541         /* Call UNDI loader C code */
542         pushl   $pxe_loader_call
543         pushw   %cs
544         pushw   $1f
545         pushw   %ax
546         pushw   $prot_call
547         lret
548 1:      popw    %bx     /* discard */
549         popw    %bx     /* discard */
550         /* Restore registers and return */
551         popw    %bx
552         popw    %es
553         popl    %edi
554         popl    %esi
555         lret
556         .size undiloader, . - undiloader