8eee84e607e1f1cae8472f1290bc5f88b419a0e3
[people/xl0/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 DELAYED_INT when NO_DELAYED_INT is not defined.
10  * This allows positive tests instead of tests that contain
11  * double negatives, and become confusing.
12  */
13 #ifndef NO_DELAYED_INT
14 #define DELAYED_INT
15 #endif
16
17 /* We need some unique magic ID, if we defer startup thru the INT18H or INT19H
18  * handler. This way, we can check if we have already been installed.
19  */
20 #ifndef MAGIC
21 #define MAGIC           0xE44C
22 #endif
23
24 /* Hook into INT18H or INT19H handler */
25 #ifdef  BOOT_INT18H
26 #define BOOT_INT        0x18
27 #else
28 #define BOOT_INT        0x19
29 #endif
30
31 #define BOOT_INT_VEC    BOOT_INT*4
32 #define SCRATCHVEC      0x300
33
34 /* Prefix exit codes.  We store these on the stack so that we will
35  * know how to return control to the BIOS when Etherboot exits.
36  */
37 #define EXIT_VIA_LRET           0x0
38 #define EXIT_VIA_INT_18         0x1
39 #define EXIT_VIA_BOOT_INT       0x2
40
41         .text
42         .code16
43         .arch i386
44         .org 0
45         .section ".prefix", "ax", @progbits
46         .globl _prefix
47 _prefix: 
48         .word 0xAA55                    /* BIOS extension signature */
49 size:   .byte 0                         /* number of 512 byte blocks */
50                                         /* = number of 256 word blocks */
51                                         /* filled in by makerom program */
52         jmp     over                    /* skip over checksum */
53         .byte 0                         /* checksum */
54         jmp     legacyentry             /* alternate entry point +6 */
55                                         /* used by mknbi-rom */
56
57 #ifdef  PCI_PNP_HEADER
58 mfgstr:
59         .asciz  "Etherboot"
60         
61 #ifdef  PXE_EXPORT
62         .org    0x16
63         .word   UNDIROMID - _prefix
64 #endif  /* PXE_EXPORT */
65         
66         .org    0x18
67         .word   PCI - _prefix
68         .word   PnP - _prefix
69
70 PCI: 
71         .ascii  "PCIR"
72         .word 0x0000                    /* vendor ID, filled in by makerom */
73         .word 0x0000                    /* device ID, filled in by makerom */
74         .word 0x0000                    /* pointer to vital product data */
75         .word 0x0018                    /* PCI data structure length */
76         .byte 0x00                      /* PCI data structure revision */
77         .byte 0x02                      /* Device Base Type code */
78         .byte 0x00                      /* Device Sub-Type code */
79         .byte 0x00                      /* Device Interface Type code */
80         .word 0x0000                    /* Image length same as offset 02h */
81         .word 0x0001                    /* revision level of code/data */
82         .byte 0x00                      /* code type */
83         .byte 0x80                      /* indicator (last PCI data structure) */
84         .word 0x0000                    /* reserved */
85
86 PnP:
87         .ascii  "$PnP"
88         .byte 0x01                      /* structure revision */
89         .byte 0x02                      /* length (in 16 byte increments) */
90         .word 0x0000                    /* offset of next header */
91         .byte 0x00                      /* Reserved */
92         .byte 0x00                      /* checksum filled by makerom */
93         .long 0x00000000                /* Device identifier */
94         .word mfgstr - _prefix
95         .word 0x0                       /* pointer to product name */
96                                         /* filled by makerom */
97         .byte 0x02                      /* Device Base Type code */
98         .byte 0x00                      /* Device Sub-Type code */
99         .byte 0x00                      /* Device Interface Type code */
100         .byte 0x14                      /* device indicator */
101         .word 0x0000                    /* boot connection vector */
102         .word 0x0000                    /* disconnect vector */
103         .word pnpentry - _prefix
104         .word 0x0000                    /* reserved */
105         .word 0x0000                    /* static resource information vector */
106 #ifdef  PXE_EXPORT
107 UNDIROMID:
108         .ascii  "UNDI"
109         .byte   UNDIROMID_end - UNDIROMID /* length of structure */
110         .byte   0                       /* Checksum */
111         .byte   0                       /* Structure revision */
112         .byte   0,1,2                   /* PXE version 2.1.0 */
113         .word   UNDILoader - _prefix    /* Offset to loader routine */
114         .word   UNDIStackSize           /* Stack segment size */
115         .word   UNDIDataSize            /* Data segment size */
116         .word   UNDICodeSize            /* Code segment size */
117         .ascii  "PCIR"
118
119         /* The code segment contains our pxe_stack_t plus the PXE and
120          * RM callback interfaces.  We don't actually use a data
121          * segment, but we put a nonzero value here to avoid confusing
122          * things.  16k of stack space should be enough.
123          *
124          * When we claim our own memory, we fill out the data segment
125          * with the address and size of the real-mode stack, so that
126          * NBPs will free that area of memory for us.  When the UNDI
127          * loader is used to initialise us, we will never need a
128          * real-mode stack because we will only ever be called via the
129          * PXE API, hence our stack is already in base memory.
130          */
131         .equ    UNDICodeSize, _pxe_stack_size
132         .equ    UNDIDataSize, _real_mode_stack_size
133         .equ    UNDIStackSize, _real_mode_stack_size
134 UNDIROMID_end:  
135 #endif  /* PXE_EXPORT */
136         
137 #endif  /* PCI_PNP_HEADER */
138
139 /*
140  *      Explicitly specify DI is wrt ES to avoid problems with some BIOSes
141  *      Discovered by Eric Biederman
142  *      In addition, some BIOSes don't point DI to the string $PnP so
143  *      we need another #define to take care of that.
144  */
145 over:   
146 #ifdef  DEBUG_ROMPREFIX
147         call    print_bcv
148 #endif
149 /* Omit this test for ISA cards anyway */
150 #ifdef  PCI_PNP_HEADER
151 /* Accept old name too for backward compatibility */
152 #if     !defined(BBS_BUT_NOT_PNP_COMPLIANT) && !defined(PNP_BUT_NOT_BBS_COMPLIANT)
153         cmpw    $'$'+'P'*256,%es:0(%di)
154         jne     notpnp
155         cmpw    $'n'+'P'*256,%es:2(%di)
156         jne     notpnp
157 #endif  /* BBS_BUT_NOT_PNP_COMPLIANT */
158         movw    $0x20,%ax
159         lret
160 #endif  /* PCI_PNP_HEADER */
161 notpnp:
162 #ifdef  DEBUG_ROMPREFIX
163         call    print_notpnp
164 #endif
165 #ifdef  DELAYED_INT
166         pushw   %ax
167         pushw   %ds
168         xorw    %ax,%ax
169         movw    %ax,%ds                 /* access first 64kB segment */
170         movw    SCRATCHVEC+4, %ax       /* check if already installed */
171         cmpw    $MAGIC, %ax             /* check magic word */
172         jz      installed
173         movw    BOOT_INT_VEC, %ax       /* hook into INT18H or INT19H */
174         movw    %ax, SCRATCHVEC
175         movw    BOOT_INT_VEC+2, %ax
176         movw    %ax, SCRATCHVEC+2
177         movw    $start_int - _prefix, %ax
178         movw    %ax, BOOT_INT_VEC
179         movw    %cs,%ax
180         movw    %ax, BOOT_INT_VEC+2
181         movw    $MAGIC, %ax             /* set magic word */
182         movw    %ax, SCRATCHVEC+4
183 #ifdef  DEBUG_ROMPREFIX
184         call    print_installed
185 #endif
186 installed:
187         popw    %ds
188         popw    %ax
189         movw    $0x20,%ax
190         lret
191
192 start_int:                              /* clobber magic id, so that we will */
193 #ifdef  DEBUG_ROMPREFIX
194         call    print_start_int
195 #endif
196         xorw    %ax,%ax                 /* not inadvertendly end up in an */
197         movw    %ax,%ds                 /* endless loop */
198         movw    %ax, SCRATCHVEC+4
199         movw    SCRATCHVEC+2, %ax       /* restore original INT19h handler */
200         movw    %ax, BOOT_INT_VEC+2
201         movw    SCRATCHVEC, %ax
202         movw    %ax, BOOT_INT_VEC
203         pushl   %eax                    /* padding */
204         pushw   $EXIT_VIA_BOOT_INT
205         jmp     invoke
206 #endif  /* DELAYED_INT */
207
208
209
210         
211 legacyentry:
212 #ifdef  DEBUG_ROMPREFIX
213         call    print_legacyentry
214 #endif
215         pushw   $EXIT_VIA_LRET
216         jmp     invoke
217
218         
219         
220 #ifdef PCI_PNP_HEADER
221 pnpentry:
222 #ifdef  DEBUG_ROMPREFIX
223         call    print_bev
224 #endif
225         pushl   %eax                    /* padding */
226         pushw   $EXIT_VIA_INT_18
227         jmp     invoke
228 #endif /* PCI_PNP_HEADER */
229
230
231 invoke:
232         /* Store ROM segment and size on stack */
233         pushw   %ax
234         pushw   %ds
235         pushw   %cs
236         movzbw  %cs:(size-_prefix), %ax
237         shlw    $9, %ax                 /* 512-byte blocks */
238         pushw   %ax
239         /* Relocate to free base memory, switch stacks */
240         pushw   $12                     /* Preserve exit code & far ret addr */
241         call    prelocate
242         /* We are now running in RAM */
243         popw    %ax                     /* padding */
244         movw    %cs, %ax
245         movw    %ax, %ds
246         popw    %ds:(_prefix_rom+2)     /* ROM size */
247         popw    %ds:(_prefix_rom+0)     /* ROM segment */
248         popw    %ds                     /* Original %ds */
249         popw    %ax                     /* Original %ax */
250         pushw   %ax                     /* 4-byte alignment */
251         pushl   $8                      /* Preserve exit code & far ret addr */
252         pushw   $0                      /* Set null return address */
253         jmp     _start
254         
255
256         .section ".text16", "ax", @progbits
257         .globl  prefix_exit
258 prefix_exit:
259         popw    %ax                     /* padding */
260         popw    %ax                     /* %ax = exit code */
261         cmpw    $EXIT_VIA_LRET, %ax
262         jne     1f
263         /* Exit via LRET */
264         lret
265 1:      addw    $4, %sp                 /* Strip padding */
266         cmpw    $EXIT_VIA_BOOT_INT, %ax
267         jne     2f
268         /* Exit via int BOOT_INT */
269         int     $BOOT_INT               /* Try original vector */
270 2:      /* Exit via int $0x18 */
271         int     $0x18                   /* As per BIOS Boot Spec, next dev */
272         .globl  prefix_exit_end
273 prefix_exit_end:
274         .previous
275
276         
277
278 /* UNDI loader needs to be rewritten to use new mechanism */
279 #if 0
280                 
281 #ifdef  PXE_EXPORT
282
283 #define PXENV_UNDI_LOADER               0x104d
284
285         .section ".prefix"
286 UNDILoader:
287         /* Loader API is different to the usual PXE API; there is no
288          * opcode on the stack.  We arrange the stack to look like a
289          * normal PXE API call; this makes the Etherboot internals
290          * cleaner and avoids adding an extra API type just for the
291          * PXE loader.
292          */
293         pushw   %bx
294         movw    %sp, %ax                /* Store original %ss:sp */
295         pushw   %ss
296         pushw   %ax
297         pushl   %eax                    /* Space for loader structure ptr */
298         pushw   %bp
299         movw    %sp, %bp
300         movw    16(%bp), %ax            /* Copy loader structure ptr */
301         movw    %ax, 2(%bp)
302         movw    18(%bp), %ax
303         movw    %ax, 4(%bp)
304         popw    %bp
305         pushw   $PXENV_UNDI_LOADER      /* PXE 'opcode' */
306         pushl   %eax                    /* dummy return address */
307         /* Stack now looks like a normal PXE API call */
308         /* Store ROM segment and size on stack */
309         pushw   %ax
310         pushw   %cs
311         movzbw  %cs:(size-_prefix), %ax
312         shlw    $9, %ax                 /* 512-byte blocks */
313         pushw   %ax
314         /* Unpack Etherboot into temporarily claimed base memory */
315         pushw   $20                     /* Dummy ret, PXE params, orig ss:sp */
316         call    prelocate
317         popw    %ax                     /* discard */
318         popw    %cs:(_prefix_rom+2)     /* ROM size */
319         popw    %cs:(_prefix_rom+0)     /* ROM segment */
320         popw    %ax                     /* Original %ax */
321         /* Inhibit automatic deallocation of base memory */
322         movl    $0, %cs:_prefix_image_basemem
323         /* Make PXE API call to Etherboot */
324         pushl   $0x201                  /* PXE API version */
325         /* Need to USE_INTERNAL_STACK, since we will call relocate() */
326         pushl   $(EB_OPCODE_PXE|EB_USE_INTERNAL_STACK) /* PXE API call type */
327         call    _entry
328         addw    $18, %sp                /* discard */
329         popw    %bx                     /* Restore original %ss:sp */
330         popw    %ss
331         movw    %bx, %sp
332         popw    %bx
333         call    deprelocate
334         lret    $2                      /* Skip our PXE 'opcode' */
335 #endif  /* PXE_EXPORT */
336
337 #endif /* 0 */
338                         
339 #ifdef DEBUG_ROMPREFIX
340         .section ".prefix"
341
342 print_bcv:
343         pushw   %si
344         movw    $1f-_prefix, %si
345         call    print_message
346         popw    %si
347         ret
348 1:      .asciz  "ROM detected\r\n"
349
350 print_bev:
351         pushw   %si
352         movw    $1f-_prefix, %si
353         call    print_message
354         popw    %si
355         ret
356 1:      .asciz  "booting\r\n"
357
358 print_notpnp:
359         pushw   %si
360         movw    $1f-_prefix, %si
361         call    print_message
362         popw    %si
363         ret
364 1:      .asciz  ": Non-PnP BIOS detected!\r\n"
365
366 print_legacyentry:
367         pushw   %si
368         movw    $1f-_prefix, %si
369         call    print_message
370         popw    %si
371         ret
372 1:      .asciz  "ROM using legacy boot mechanism\r\n"
373
374 print_installed:
375         pushw   %si
376         movw    $1f-_prefix, %si
377         call    print_message
378         popw    %si
379         ret
380 1:      .ascii  "hooked boot via INT"
381 #ifdef  BOOT_INT18H
382         .asciz  "18\r\n"
383 #else
384         .asciz  "19\r\n"
385 #endif
386
387 print_start_int:
388         pushw   %si
389         movw    $1f-_prefix, %si
390         call    print_message
391         popw    %si
392         ret
393 1:      .asciz  "booting via hooked interrupt\r\n"
394
395 print_message:
396         pushaw
397         pushw   %ds
398         pushw   %cs
399         popw    %ds
400         pushw   %si
401         movw    $1f-_prefix, %si
402         call    print_string
403         popw    %si
404         call    print_string
405         popw    %ds
406         popaw
407         ret
408 1:      .asciz  "Etherboot "
409
410 print_string:
411 1:      lodsb
412         testb   %al,%al
413         je      2f
414         movw    $0x0007, %bx            /* page 0, attribute 7 (normal) */
415         movb    $0x0e, %ah              /* write char, tty mode */
416         int     $0x10
417         jmp     1b
418 2:      ret
419         
420 #endif