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