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