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