[PXE] Improve PnP/BBS detection
[people/mdeck/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 STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
12 #define PNP_GET_BBS_VERSION 0x60
13
14         .text
15         .code16
16         .arch i386
17         .section ".prefix", "ax", @progbits
18         
19         .org    0x00
20 romheader:
21         .word   0xAA55                  /* BIOS extension signature */
22 romheader_size: .byte _load_size_sect   /* Size in 512-byte blocks */
23         jmp     init                    /* Initialisation vector */
24 checksum:
25         .byte   0
26         .org    0x16
27         .word   undiheader
28         .org    0x18
29         .word   pciheader
30         .org    0x1a
31         .word   pnpheader
32         .size romheader, . - romheader
33         
34         .section ".zinfo.fixup", "a"    /* Compressor fixup information */
35         .ascii  "SUBB"
36         .long   romheader_size
37         .long   512
38         .long   0
39         .previous
40
41 pciheader:
42         .ascii  "PCIR"                  /* Signature */
43         .word   pci_vendor_id           /* Vendor ID */ 
44         .word   pci_device_id           /* Device ID */
45         .word   0x0000                  /* pointer to vital product data */
46         .word   pciheader_len           /* PCI data structure length */
47         .byte   0x00                    /* PCI data structure revision */
48         .byte   0x02                    /* Device Base Type code */
49         .byte   0x00                    /* Device Sub-Type code */
50         .byte   0x00                    /* Device Interface Type code */
51 pciheader_size: .word _load_size_sect   /* Image length same as offset 02h */
52         .word   0x0001                  /* revision level of code/data */
53         .byte   0x00                    /* code type */
54         .byte   0x80                    /* Flags (last PCI data structure) */
55         .word   0x0000                  /* reserved */
56         .equ pciheader_len, . - pciheader
57         .size pciheader, . - pciheader
58         
59         .section ".zinfo.fixup", "a"    /* Compressor fixup information */
60         .ascii  "SUBW"
61         .long   pciheader_size
62         .long   512
63         .long   0
64         .previous
65
66 pnpheader:
67         .ascii  "$PnP"                  /* Signature */
68         .byte   0x01                    /* Structure revision */
69         .byte   ( pnpheader_len / 16 )  /* Length (in 16 byte increments) */
70         .word   0x0000                  /* Offset of next header */
71         .byte   0x00                    /* Reserved */
72         .byte   0x00                    /* Checksum */
73         .long   0x00000000              /* Device identifier */
74         .word   mfgstr                  /* Manufacturer string */
75         .word   prodstr                 /* Product name */
76         .byte   0x02                    /* Device base type code */
77         .byte   0x00                    /* Device sub-type code */
78         .byte   0x00                    /* Device interface type code */
79         .byte   0x54                    /* Device indicator */
80         .word   0x0000                  /* Boot connection vector */
81         .word   0x0000                  /* Disconnect vector */
82         .word   bev_entry               /* Boot execution vector */
83         .word   0x0000                  /* Reserved */
84         .word   0x0000                  /* Static resource information vector*/
85         .equ pnpheader_len, . - pnpheader
86         .size pnpheader, . - pnpheader
87
88 mfgstr:
89         .asciz  "http://etherboot.org"
90         .size mfgstr, . - mfgstr
91 prodstr:
92         .asciz  "gPXE"
93         .size prodstr, . - prodstr
94         
95 undiheader:
96         .ascii  "UNDI"                  /* Signature */
97         .byte   undiheader_len          /* Length of structure */
98         .byte   0                       /* Checksum */
99         .byte   0                       /* Structure revision */
100         .byte   0,1,2                   /* PXE version: 2.1.0 */
101         .word   undiloader              /* Offset to loader routine */
102         .word   _data16_size            /* Stack segment size */
103         .word   _data16_size            /* Data segment size */
104         .word   _text16_size            /* Code segment size */
105         .equ undiheader_len, . - undiheader
106         .size undiheader, . - undiheader
107
108 /* Initialisation (called once during POST)
109  *
110  * Determine whether or not this is a PnP system via a signature
111  * check.  If it is PnP, return to the PnP BIOS indicating that we are
112  * a boot-capable device; the BIOS will call our boot execution vector
113  * if it wants to boot us.  If it is not PnP, hook INT 19.
114  */
115 init:
116         /* Preserve registers, clear direction flag, set %ds=%cs */
117         pushaw
118         pushw   %ds
119         pushw   %es
120         cld
121         pushw   %cs
122         popw    %ds
123         /* Print message as early as possible */
124         movw    $init_message, %si
125         call    print_message
126         /* Check for PnP BIOS */
127         testw   $0x0f, %di      /* PnP signature must be aligned - bochs    */
128         jnz     hook_int19      /* uses unalignment to indicate 'fake' PnP. */
129         cmpl    $PNP_SIGNATURE, %es:0(%di)
130         jne     hook_int19
131         /* Is PnP: print PnP message */
132         movw    $init_message_pnp, %si
133         call    print_message
134         xchgw   %bx, %bx
135         /* Check for BBS */
136         pushw   %es:0x1b(%di)   /* Real-mode data segment */
137         pushw   %ds             /* &(bbs_version) */
138         pushw   $bbs_version
139         pushw   $PNP_GET_BBS_VERSION
140         lcall   *%es:0xd(%di)
141         addw    $16, %sp
142         testw   %ax, %ax
143         jne     hook_int19
144         movw    $init_message_bbs, %si
145         call    print_message
146         jmp     hook_bbs
147         /* Not BBS-compliant - must hook INT 19 */
148 hook_int19:
149         movw    $init_message_int19, %si
150         call    print_message
151         xorw    %ax, %ax
152         movw    %ax, %es
153         pushw   %cs
154         pushw   $int19_entry
155         popl    %es:( 0x19 * 4 )
156 hook_bbs:
157         /* Check for PMM */
158         movw    $( 0xe000 - 1 ), %di
159 pmm_scan:
160         incw    %di
161         jz      no_pmm
162         movw    %di, %es
163         cmpl    $PMM_SIGNATURE, %es:0
164         jne     pmm_scan
165         xorw    %bx, %bx
166         xorw    %si, %si
167         movzbw  %es:5, %cx
168 1:      es lodsb
169         addb    %al, %bl
170         loop    1b
171         jnz     pmm_scan
172         /* PMM found: print PMM message */
173         movw    $init_message_pmm, %si
174         call    print_message
175         /* Try to allocate 2MB block via PMM */
176         pushw   $0x0006         /* Aligned, extended memory */
177         pushl   $0xffffffff     /* No handle */
178         pushl   $( 0x00200000 / 16 ) /* 2MB in paragraphs */
179         pushw   $0x0000         /* pmmAllocate */
180         lcall   *%es:7
181         addw    $12, %sp
182         testw   %dx, %dx        /* %ax==0 even on success, since align=2MB */
183         jnz     gotpmm
184         movw    $init_message_pmm_failed, %si
185         call    print_message
186         jmp     no_pmm
187 gotpmm: /* PMM allocation succeeded: copy ROM to PMM block */
188         pushal                  /* PMM presence implies 1kB stack */
189         movw    %ax, %es        /* %ax=0 already - see above */
190         pushw   %dx
191         pushw   %ax
192         popl    %edi
193         movl    %edi, image_source
194         xorl    %esi, %esi
195         movzbl  romheader_size, %ecx
196         shll    $9, %ecx
197         addr32 rep movsb        /* PMM presence implies flat real mode */
198         movl    %edi, decompress_to
199         /* Shrink ROM and update checksum */
200         xorw    %bx, %bx
201         xorw    %si, %si
202         movw    $_prefix_size_sect, %cx
203         movb    %cl, romheader_size
204         shlw    $9, %cx
205 1:      lodsb
206         addb    %al, %bl
207         loop    1b
208         subb    %bl, checksum
209         popal
210 no_pmm:
211         /* Print CRLF to terminate messages */
212         movw    $'\n', %ax
213         call    print_character
214         /* Restore registers */
215         popw    %es
216         popw    %ds
217         popaw
218         /* Indicate boot capability to PnP BIOS, if present */
219         movw    $0x20, %ax
220         lret
221         .size init, . - init
222
223 init_message:
224         .asciz  "gPXE (http://etherboot.org) -"
225         .size   init_message, . - init_message
226 init_message_pnp:
227         .asciz  " PnP"
228         .size init_message_pnp, . - init_message_pnp
229 init_message_bbs:
230         .asciz  " BBS"
231         .size init_message_bbs, . - init_message_bbs
232 init_message_pmm:
233         .asciz  " PMM"
234         .size init_message_pmm, . - init_message_pmm
235 init_message_pmm_failed:
236         .asciz  "(failed)"
237         .size init_message_pmm_failed, . - init_message_pmm_failed
238 init_message_int19:
239         .asciz  " INT19"
240         .size init_message_int19, . - init_message_int19
241
242 /* ROM image location
243  *
244  * May be either within option ROM space, or within PMM-allocated block.
245  */
246 image_source:
247         .long   0
248         .size   image_source, . - image_source
249
250 /* Temporary decompression area
251  *
252  * May be either at HIGHMEM_LOADPOINT, or within PMM-allocated block.
253  */
254 decompress_to:
255         .long   HIGHMEM_LOADPOINT
256         .size   decompress_to, . - decompress_to
257
258 /* BBS version
259  *
260  * Filled in by BBS BIOS.  We ignore the value.
261  */
262 bbs_version:
263         .word   0
264
265 /* Boot Execution Vector entry point
266  *
267  * Called by the PnP BIOS when it wants to boot us.
268  */
269 bev_entry:
270         pushw   %cs
271         call    exec
272         lret
273         .size   bev_entry, . - bev_entry
274
275 /* INT19 entry point
276  *
277  * Called via the hooked INT 19 if we detected a non-PnP BIOS.
278  */
279 int19_entry:
280         pushw   %cs
281         call    exec
282         /* No real way to return from INT19 */
283         int     $0x18
284         .size   int19_entry, . - int19_entry
285
286 /* Execute as a boot device
287  *
288  */
289 exec:   /* Set %ds = %cs */
290         pushw   %cs
291         popw    %ds
292
293         /* Print message as soon as possible */
294         movw    $exec_message, %si
295         call    print_message
296
297         /* Store magic word on BIOS stack and remember BIOS %ss:sp */
298         pushl   $STACK_MAGIC
299         movw    %ss, %dx
300         movw    %sp, %bp
301
302         /* Obtain a reasonably-sized temporary stack */
303         xorw    %ax, %ax
304         movw    %ax, %ss
305         movw    $0x7c00, %sp
306
307         /* Install gPXE */
308         movl    image_source, %esi
309         movl    decompress_to, %edi
310         call    alloc_basemem
311         call    install_prealloc
312
313         /* Set up real-mode stack */
314         movw    %bx, %ss
315         movw    $_estack16, %sp
316
317         /* Jump to .text16 segment */
318         pushw   %ax
319         pushw   $1f
320         lret
321         .section ".text16", "awx", @progbits
322 1:      /* Call main() */
323         pushl   $main
324         pushw   %cs
325         call    prot_call
326         /* No need to clean up stack; we are about to reload %ss:sp */
327         
328         /* Restore BIOS stack */
329         movw    %dx, %ss
330         movw    %bp, %sp
331
332         /* Check magic word on BIOS stack */
333         popl    %eax
334         cmpl    $STACK_MAGIC, %eax
335         jne     1f
336         /* BIOS stack OK: return to caller */
337         lret
338 1:      /* BIOS stack corrupt: use INT 18 */
339         int     $0x18
340         .previous
341
342 exec_message:
343         .asciz  "gPXE starting boot\n"
344         .size exec_message, . - exec_message
345
346 /* UNDI loader
347  *
348  * Called by an external program to load our PXE stack.
349  */
350 undiloader:
351         /* Save registers */
352         pushl   %esi
353         pushl   %edi
354         pushw   %es
355         pushw   %bx
356         /* UNDI loader parameter structure address into %es:%di */
357         movw    %sp, %bx
358         movw    %ss:12(%bx), %di
359         movw    %ss:14(%bx), %es
360         /* Install to specified real-mode addresses */
361         pushw   %di
362         movw    %es:12(%di), %bx
363         movw    %es:14(%di), %ax
364         movl    %cs:image_source, %esi
365         movl    %cs:decompress_to, %edi
366         call    install_prealloc
367         popw    %di
368         /* Call UNDI loader C code */
369         pushl   $pxe_loader_call
370         pushw   %cs
371         pushw   $1f
372         pushw   %ax
373         pushw   $prot_call
374         lret
375 1:      popw    %bx     /* discard */
376         popw    %bx     /* discard */
377         /* Restore registers and return */
378         popw    %bx
379         popw    %es
380         popl    %edi
381         popl    %esi
382         lret
383         .size undiloader, . - undiloader