[romprefix] Fix PMM detection start address
[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 /* Manufacturer string */
89 mfgstr:
90         .asciz  "http://etherboot.org"
91         .size mfgstr, . - mfgstr
92
93 /* Product string
94  *
95  * Defaults to "gPXE".  If the ROM image is writable at initialisation
96  * time, it will be filled in to include the PCI bus:dev.fn number of
97  * the card as well.
98  */
99 prodstr:
100         .ascii  "gPXE"
101 prodstr_separator:
102         .byte   0
103         .ascii  "(PCI "
104 prodstr_pci_id:
105         .asciz  "xx:xx.x)"              /* Filled in by init code */
106         .size prodstr, . - prodstr
107         
108 undiheader:
109         .ascii  "UNDI"                  /* Signature */
110         .byte   undiheader_len          /* Length of structure */
111         .byte   0                       /* Checksum */
112         .byte   0                       /* Structure revision */
113         .byte   0,1,2                   /* PXE version: 2.1.0 */
114         .word   undiloader              /* Offset to loader routine */
115         .word   _data16_size            /* Stack segment size */
116         .word   _data16_size            /* Data segment size */
117         .word   _text16_size            /* Code segment size */
118         .equ undiheader_len, . - undiheader
119         .size undiheader, . - undiheader
120
121 /* Initialisation (called once during POST)
122  *
123  * Determine whether or not this is a PnP system via a signature
124  * check.  If it is PnP, return to the PnP BIOS indicating that we are
125  * a boot-capable device; the BIOS will call our boot execution vector
126  * if it wants to boot us.  If it is not PnP, hook INT 19.
127  */
128 init:
129         /* Preserve registers, clear direction flag, set %ds=%cs */
130         pushaw
131         pushw   %ds
132         pushw   %es
133         pushw   %fs
134         cld
135         pushw   %cs
136         popw    %ds
137         pushw   $0x40
138         popw    %fs
139         movw    %di, %bx
140         xorw    %di, %di
141         /* Print message as early as possible */
142         movw    $init_message, %si
143         call    print_message
144         call    print_pci_busdevfn
145         /* Fill in product name string, if possible */
146         movw    $prodstr_pci_id, %di
147         call    print_pci_busdevfn
148         movb    $' ', prodstr_separator
149         xorw    %di, %di
150         /* Check for PnP BIOS */
151         testw   $0x0f, %bx      /* PnP signature must be aligned - bochs    */
152         jnz     hook_int19      /* uses unalignment to indicate 'fake' PnP. */
153         cmpl    $PNP_SIGNATURE, %es:0(%bx)
154         jne     hook_int19
155         /* Is PnP: print PnP message */
156         movw    $init_message_pnp, %si
157         call    print_message
158         /* Check for BBS */
159         pushw   %es:0x1b(%bx)   /* Real-mode data segment */
160         pushw   %ds             /* &(bbs_version) */
161         pushw   $bbs_version
162         pushw   $PNP_GET_BBS_VERSION
163         lcall   *%es:0xd(%bx)
164         addw    $8, %sp
165         testw   %ax, %ax
166         jne     hook_int19
167         movw    $init_message_bbs, %si
168         call    print_message
169         jmp     hook_bbs
170         /* Not BBS-compliant - must hook INT 19 */
171 hook_int19:
172         movw    $init_message_int19, %si
173         call    print_message
174         xorw    %ax, %ax
175         movw    %ax, %es
176         pushl   %es:( 0x19 * 4 )
177         popl    orig_int19
178         pushw   %cs
179         pushw   $int19_entry
180         popl    %es:( 0x19 * 4 )
181 hook_bbs:
182         /* Check for PMM */
183         movw    $( 0xe000 - 1 ), %bx
184 pmm_scan:
185         incw    %bx
186         jz      no_pmm
187         movw    %bx, %es
188         cmpl    $PMM_SIGNATURE, %es:0
189         jne     pmm_scan
190         xorw    %dx, %dx
191         xorw    %si, %si
192         movzbw  %es:5, %cx
193 1:      es lodsb
194         addb    %al, %dl
195         loop    1b
196         jnz     pmm_scan
197         /* PMM found: print PMM message */
198         movw    $init_message_pmm, %si
199         call    print_message
200         /* Try to allocate 2MB block via PMM */
201         pushw   $0x0006         /* Aligned, extended memory */
202         pushl   $0xffffffff     /* No handle */
203         pushl   $( 0x00200000 / 16 ) /* 2MB in paragraphs */
204         pushw   $0x0000         /* pmmAllocate */
205         lcall   *%es:7
206         addw    $12, %sp
207         testw   %dx, %dx        /* %ax==0 even on success, since align=2MB */
208         jnz     gotpmm
209         movw    $init_message_pmm_failed, %si
210         call    print_message
211         jmp     no_pmm
212 gotpmm: /* PMM allocation succeeded: copy ROM to PMM block */
213         pushal                  /* PMM presence implies 1kB stack */
214         movw    %ax, %es        /* %ax=0 already - see above */
215         pushw   %dx
216         pushw   %ax
217         popl    %edi
218         movl    %edi, image_source
219         xorl    %esi, %esi
220         movzbl  romheader_size, %ecx
221         shll    $9, %ecx
222         addr32 rep movsb        /* PMM presence implies flat real mode */
223         movl    %edi, decompress_to
224         /* Shrink ROM and update checksum */
225         xorw    %bx, %bx
226         xorw    %si, %si
227         movw    $_prefix_size_sect, %cx
228         movb    %cl, romheader_size
229         shlw    $9, %cx
230 1:      lodsb
231         addb    %al, %bl
232         loop    1b
233         subb    %bl, checksum
234         popal
235 no_pmm: /* Prompt for POST-time shell */
236         movw    $init_message_prompt, %si
237         call    print_message
238         /* Empty the keyboard buffer before waiting for input */
239 empty_keyboard_buffer:
240         movb    $0x01, %ah
241         int     $0x16
242         jz      1f
243         xorw    %ax, %ax
244         int     $0x16
245         jmp     empty_keyboard_buffer
246 1:      /* Wait for up to 3s for a key press */
247         movw    $(18 * 3), %cx  /* Approx 3s worth of timer ticks */
248 wait_for_key:
249         decw    %cx
250         jz      no_key_pressed
251         /* Wait for timer tick to be updated */
252         movl    %fs:(0x6c), %eax
253 1:      pushf
254         sti
255         hlt
256         popf
257         cmpl    %fs:(0x6c), %eax
258         je      1b
259         /* Check to see if a key was pressed */
260         movb    $0x01, %ah
261         int     $0x16
262         jz      wait_for_key
263         /* Check to see if key was Ctrl-B */
264         cmpb    $0x02, %al
265         je      1f
266         /* Key was not Ctrl-B: remove from buffer and stop waiting */
267         xorw    %ax, %ax
268         int     $0x16
269         jmp     no_key_pressed
270 1:      /* Key was Ctrl-B: leave in keyboard buffer and invoke gPXE.
271          * The keypress will be picked up by the initial shell
272          * prompt, and we will drop into a shell.
273          */
274         pushw   %cs
275         call    exec
276 no_key_pressed:
277         /* Print blank lines to terminate messages */
278         movw    $init_message_end, %si
279         call    print_message
280         /* Restore registers */
281         popw    %fs
282         popw    %es
283         popw    %ds
284         popaw
285         /* Indicate boot capability to PnP BIOS, if present */
286         movw    $0x20, %ax
287         lret
288         .size init, . - init
289
290 init_message:
291         .asciz  "gPXE (http://etherboot.org) - PCI "
292         .size   init_message, . - init_message
293 init_message_pnp:
294         .asciz  " PnP"
295         .size   init_message_pnp, . - init_message_pnp
296 init_message_bbs:
297         .asciz  " BBS"
298         .size   init_message_bbs, . - init_message_bbs
299 init_message_pmm:
300         .asciz  " PMM"
301         .size   init_message_pmm, . - init_message_pmm
302 init_message_pmm_failed:
303         .asciz  "(failed)"
304         .size   init_message_pmm_failed, . - init_message_pmm_failed
305 init_message_int19:
306         .asciz  " INT19"
307         .size   init_message_int19, . - init_message_int19
308 init_message_prompt:
309         .asciz  "\nPress Ctrl-B to configure gPXE..."
310         .size   init_message_prompt, . - init_message_prompt
311 init_message_end:
312         .asciz  "\n\n\n"
313         .size   init_message_end, . - init_message_end
314
315 /* ROM image location
316  *
317  * May be either within option ROM space, or within PMM-allocated block.
318  */
319 image_source:
320         .long   0
321         .size   image_source, . - image_source
322
323 /* Temporary decompression area
324  *
325  * May be either at HIGHMEM_LOADPOINT, or within PMM-allocated block.
326  */
327 decompress_to:
328         .long   HIGHMEM_LOADPOINT
329         .size   decompress_to, . - decompress_to
330
331 /* BBS version
332  *
333  * Filled in by BBS BIOS.  We ignore the value.
334  */
335 bbs_version:
336         .word   0
337
338 /* Boot Execution Vector entry point
339  *
340  * Called by the PnP BIOS when it wants to boot us.
341  */
342 bev_entry:
343         pushw   %cs
344         call    exec
345         lret
346         .size   bev_entry, . - bev_entry
347
348 /* INT19 entry point
349  *
350  * Called via the hooked INT 19 if we detected a non-PnP BIOS.  We
351  * attempt to return via the original INT 19 vector (if we were able to
352  * store it).
353  */
354 int19_entry:
355         pushw   %cs
356         call    exec
357         movl    %cs:orig_int19, %eax
358         testl   %eax, %eax
359         je      1f
360         /* Chain to original INT 19 vector */
361         ljmp    *%cs:orig_int19
362 1:      /* No chained vector: issue INT 18 as a last resort */
363         int     $0x18
364         .size   int19_entry, . - int19_entry
365 orig_int19:
366         .long   0
367         .size   orig_int19, . - orig_int19
368
369 /* Execute as a boot device
370  *
371  */
372 exec:   /* Set %ds = %cs */
373         pushw   %cs
374         popw    %ds
375
376         /* Print message as soon as possible */
377         movw    $exec_message, %si
378         xorw    %di, %di
379         call    print_message
380
381         /* Store magic word on BIOS stack and remember BIOS %ss:sp */
382         pushl   $STACK_MAGIC
383         movw    %ss, %dx
384         movw    %sp, %bp
385
386         /* Obtain a reasonably-sized temporary stack */
387         xorw    %ax, %ax
388         movw    %ax, %ss
389         movw    $0x7c00, %sp
390
391         /* Install gPXE */
392         movl    image_source, %esi
393         movl    decompress_to, %edi
394         call    alloc_basemem
395         call    install_prealloc
396
397         /* Set up real-mode stack */
398         movw    %bx, %ss
399         movw    $_estack16, %sp
400
401         /* Jump to .text16 segment */
402         pushw   %ax
403         pushw   $1f
404         lret
405         .section ".text16", "awx", @progbits
406 1:      /* Call main() */
407         pushl   $main
408         pushw   %cs
409         call    prot_call
410         /* No need to clean up stack; we are about to reload %ss:sp */
411         
412         /* Restore BIOS stack */
413         movw    %dx, %ss
414         movw    %bp, %sp
415
416         /* Check magic word on BIOS stack */
417         popl    %eax
418         cmpl    $STACK_MAGIC, %eax
419         jne     1f
420         /* BIOS stack OK: return to caller */
421         lret
422 1:      /* BIOS stack corrupt: use INT 18 */
423         int     $0x18
424         .previous
425
426 exec_message:
427         .asciz  "Entering gPXE\n"
428         .size exec_message, . - exec_message
429
430 /* UNDI loader
431  *
432  * Called by an external program to load our PXE stack.
433  */
434 undiloader:
435         /* Save registers */
436         pushl   %esi
437         pushl   %edi
438         pushw   %es
439         pushw   %bx
440         /* UNDI loader parameter structure address into %es:%di */
441         movw    %sp, %bx
442         movw    %ss:12(%bx), %di
443         movw    %ss:14(%bx), %es
444         /* Install to specified real-mode addresses */
445         pushw   %di
446         movw    %es:12(%di), %bx
447         movw    %es:14(%di), %ax
448         movl    %cs:image_source, %esi
449         movl    %cs:decompress_to, %edi
450         call    install_prealloc
451         popw    %di
452         /* Call UNDI loader C code */
453         pushl   $pxe_loader_call
454         pushw   %cs
455         pushw   $1f
456         pushw   %ax
457         pushw   $prot_call
458         lret
459 1:      popw    %bx     /* discard */
460         popw    %bx     /* discard */
461         /* Restore registers and return */
462         popw    %bx
463         popw    %es
464         popl    %edi
465         popl    %esi
466         lret
467         .size undiloader, . - undiloader