0e0b6c00392c5079381fa71ecdf0db632a54b43b
[gpxe.git] / src / arch / i386 / prefix / pxeprefix.S
1 /* Offsets of words containing ROM's CS and size (in 512 byte blocks)
2  * from start of floppy boot block at 0x7c00
3  * Offsets must match those in etherboot.h
4  */
5 #define FLOPPY_SEGMENT  0x7c0
6
7 #define PXENV_UNDI_CLEANUP      0x02
8 #define PXENV_UNDI_SHUTDOWN     0x05
9 #define PXENV_STOP_UNDI         0x15
10 #define PXENV_UNLOAD_STACK      0x70
11 #define PXENV_STOP_BASE         0x76
12
13 #define PUSHA_SIZE              16
14 #define PXE_STACK_MAGIC         0x57ac  /* 'STac' */
15
16         .text
17         .code16
18         .arch i386
19         .org 0
20         .section ".prefix", "ax", @progbits
21 /*****************************************************************************
22  * Entry point: set cs, ds, bp, print welcome message
23  *****************************************************************************
24  */     
25 _prefix:
26         jmp     $FLOPPY_SEGMENT, $code_start-_prefix
27 10:     .asciz  "PXE->EB "
28 code_start:
29         pusha                           /* Preserve all registers */
30         push    %ds
31         movw    %sp, %bp                /* %bp must be preserved, hence do
32                                          * this after the pusha */
33         push    $PXE_STACK_MAGIC        /* PXE stack magic marker */
34
35         push    %cs                     /* Set up data segment */
36         pop     %ds
37         mov     $0x40, %cx              /* Set up %fs for access to 40:13 */
38         mov     %cx, %fs
39         movw    $10b-_prefix, %si       /* Print welcome message */
40         call    print_message
41
42 /*****************************************************************************
43  * Detect type of PXE available (!PXE, PXENV+ or none)
44  *****************************************************************************
45  */
46 detect_pxe:
47         les     4+PUSHA_SIZE+2(%bp), %di /* !PXE structure */
48         cmpl    $0x45585021, %es:(%di)  /* '!PXE' signature */
49         je      detected_pxe
50         mov     $0x5650, %ax
51         int     $0x1a
52         cmp     $0x564e, %ax
53         jne     detected_nothing
54         cmpl    $0x4e455850, %es:(%bx)  /* 'PXEN' signature */
55         jne     detected_nothing
56         cmpw    $0x2b56, %es:4(%bx)     /* 'V+' signature */
57         je      detected_pxenv
58
59 detected_nothing:
60         movw    $10f-_prefix, %si
61         call    print_message
62         jmp     finished_with_error
63 10:     .asciz  "No PXE "
64
65 detected_pxenv: /* es:bx points to PXENV+ structure */
66         push    %es
67         push    %bx
68         push    %es:0x24(%bx)           /* UNDI code segment */
69         push    %es:0x26(%bx)           /* UNDI code size */
70         push    %es:0x20(%bx)           /* UNDI data segment */
71         push    %es:0x22(%bx)           /* UNDI data size */
72         les     %es:0x0a(%bx), %di      /* Entry point to %es:%di */
73         movw    $10f-_prefix, %si
74         jmp     pxe_setup_done
75 10:     .asciz  "PXENV+ "
76
77 detected_pxe:   /* es:di points to !PXE structure */
78         push    %es
79         push    %di
80         push    %es:0x30(%di)           /* UNDI code segment */
81         push    %es:0x36(%di)           /* UNDI code size */
82         push    %es:0x28(%di)           /* UNDI data segment */
83         push    %es:0x2e(%di)           /* UNDI data size */
84         les     %es:0x10(%di), %di      /* Entry point to %es:%di */
85         movw    $10f-_prefix, %si
86         jmp     pxe_setup_done
87 10:     .asciz  "!PXE "
88
89 pxe_setup_done:
90         mov     %es, pxe_entry_segment - _prefix
91         mov     %di, pxe_entry_offset - _prefix
92         pop     %ax
93         mov     %ax, undi_data_size - _prefix
94         pop     %ax
95         mov     %ax, undi_data_segment - _prefix
96         pop     %ax
97         mov     %ax, undi_code_size - _prefix
98         pop     %ax
99         mov     %ax, undi_code_segment - _prefix
100         call    print_message
101         pop     %di
102         pop     %es     /* Exit with %es:%di containing structure address */
103
104 /*****************************************************************************
105  * Print information about located structure
106  *****************************************************************************
107  */
108 print_structure_information:
109         call    print_segoff    /* %es:%di contains address of structure */
110         les     %ds:(pxe_entry_segoff - _prefix), %di
111         call    print_segoff
112         les     %ds:(undi_code_segoff - _prefix), %di
113         call    print_segoff
114         les     %ds:(undi_data_segoff - _prefix), %di
115         call    print_segoff
116
117 /*****************************************************************************
118  * Unload PXE base code and UNDI driver
119  *****************************************************************************
120  */
121 #ifdef PXELOADER_KEEP_ALL
122         xor     %ax, %ax                /* Force zero flag to show success */
123         jmp     do_not_free_base_mem    /* Skip the unloading */
124 #endif /* PXELOADER_KEEP_ALL */
125         
126 unload_pxe:
127         mov     $PXENV_UNLOAD_STACK, %bx
128         call    pxe_call
129         mov     $PXENV_STOP_UNDI, %bx
130         call    pxe_call
131         pushfw                          /* Ignore PXENV_UNDI_CLEANUP errors */
132         mov     $PXENV_UNDI_CLEANUP, %bx
133         call    pxe_call
134         popfw
135         /* On exit, zero flag is set iff all calls were successful */
136                 
137 /*****************************************************************************
138  * Free base memory
139  *****************************************************************************
140  */
141 free_base_mem:
142         jnz     do_not_free_base_mem    /* Using zero flag from unload_pxe */
143
144         mov     undi_code_segment - _prefix, %bx
145         mov     undi_data_segment - _prefix, %cx
146         mov     undi_code_size - _prefix, %ax
147         cmp     %bx, %cx
148         jb      1f
149         mov     %cx, %bx
150         mov     undi_data_size - _prefix, %ax
151 1:      add     $0x0f, %ax              /* Round up to next segment */
152         shr     $4, %ax
153         add     %bx, %ax                /* Highest segment address into %ax */
154         add     $(1024 / 16 - 1), %ax   /* Round up to next kb */
155         shr     $6, %ax                 /* New free basemem size in %ax */
156         mov     %fs:(0x13), %bx         /* Old free base memory in %bx */
157         mov     %ax, %fs:(0x13)         /* Store new free base memory size */
158
159         /* Note that zero_mem_loop will also zero out our stack, so make
160          * sure the stack is empty at this point.
161          */
162         mov     %ax, %dx
163         sub     %bx, %dx                /* numberof kb to zero in %dx */
164         shl     $6, %bx                 /* Segment address into %bx */
165 zero_mem_loop:
166         mov     %bx, %es                /* kB boundary into %es:00 */
167         xor     %ax, %ax
168         xor     %di, %di
169         mov     $0x400, %cx
170         rep     stosb                   /* fill kB with zeroes */
171         add     $(1024 / 16), %bx
172         dec     %dx
173         jnz     zero_mem_loop
174         /* Will exit here with zero flag set, so no need to set it explicitly
175          * in order to indicate success.
176          */
177         
178 do_not_free_base_mem:
179         pushf                           /* Save success (zero) flag status */
180         mov     %fs:(0x13), %ax         /* Free base memory in %ax */
181         call    print_hex_word          /* Print free base memory */
182         popf                            /* Restore success (zero) flag */
183
184 /*****************************************************************************
185  * Exit point
186  * Jump to finished with the zero flag set to indicate success, or to
187  * finished_with_error to always report an error
188  *****************************************************************************
189  */     
190 finished:
191         movw    $10f-_prefix, %si
192         jz      1f
193 finished_with_error:
194         movw    $20f-_prefix, %si
195 1:
196         call    print_message
197         jmp     99f
198 10:     .asciz " ok\n"
199 20:     .asciz " err\n"
200
201         
202         /* We place a stack here.  It doesn't get used until after all
203          * the above code is finished, so we can happily write all
204          * over it.  Putting the stack here ensures that it doesn't
205          * accidentally go over the 512 byte threshold, which would
206          * cause problems when returning via start32's prefix
207          * relocation mechanism.
208          */
209 _estack:        
210 99:
211
212 /*****************************************************************************
213  * Run Etherboot main code
214  *****************************************************************************
215  */     
216 run_etherboot:
217         /* Very temporarily switch stacks to one internal to the
218          * prefix.  Do this because the stack provided by the PXE ROM
219          * could be absolutely anywhere, including in an area of
220          * memory that the call to prelocate will vapourise...
221          */
222         pushw   %ss                     /* PXE stack pointer to ES:DI */
223         popw    %es
224         movw    %sp, %di
225         pushw   %ds                     /* Set up stack in "safe" area */
226         popw    %ss
227         movw    $_estack-_prefix, %sp
228         pushw   %es                     /* Record PXE stack pointer */
229         pushw   %di
230         /* Relocate payload and stack to claimed base memory */
231         pushw   $4                      /* Preserve old PXE stack pointer */
232         call    prelocate
233         popw    %ax                     /* Remove parameter */
234         pushl   $4                      /* Preserve old PXE stack pointer */
235         pushw   $0                      /* Indicate prefix exit mechanism */
236         jmp     _start                  /* Run Etherboot */
237
238         .section ".text16", "ax", @progbits
239 prefix_exit:
240         pushw   %cs                     /* Set %ds, %bp for access to text */
241         popw    %ds
242         call    1f
243 1:      popw    %bp
244         popw    %di                     /* Old PXE stack to %es:di */
245         popw    %es
246         cmpw    $PXE_STACK_MAGIC, %es:0(%di)  /* See if PXE stack intact */
247         jne     exit_via_int18
248 exit_via_pxe:                           /* Stack OK, return to PXE */
249         push    %es                     /* Restore PXE stack pointer */
250         pop     %ss
251         mov     %di, %sp
252         pop     %ax                     /* Discard PXE_STACK_MAGIC marker */
253         leaw    (10f-1b)(%bp), %si
254         call    print_exit_message
255         pop     %ds                     /* Restore PXE's DS */
256         popa                            /* Restore PXE's other registers */
257         movw    $0, %ax                 /* Return PXENV_STATUS_SUCCESS */
258         lret                            /* Return control to PXE ROM */
259 10:     .asciz  "EB->PXE\r\n"
260 exit_via_int18:                         /* Stack damaged, do int 18 */
261         leaw    (10f-1b)(%bp), %si
262         call    print_exit_message
263         int     $0x18
264 10:     .asciz  "EB->BIOS\r\n"
265 print_exit_message:     
266         movw    $0x0007, %bx            /* page 0, attribute 7 (normal) */
267         movb    $0x0e, %ah              /* write char, tty mode */
268 1:      lodsb
269         testb   %al, %al
270         je      2f
271         int     $0x10
272         jmp     1b
273 2:      ret
274 prefix_exit_end:
275         .previous
276
277 /*****************************************************************************
278  * Subroutine: print character in %al (with LF -> LF,CR translation)
279  *****************************************************************************
280  */
281 print_character:
282         movw    $0x0007, %bx            /* page 0, attribute 7 (normal) */
283         movb    $0x0e, %ah              /* write char, tty mode */
284         cmpb    $0x0a, %al              /* '\n'? */
285         jne     1f
286         int     $0x10
287         movb    $0x0d, %al
288 1:      int     $0x10
289         ret
290         
291 /*****************************************************************************
292  * Subroutine: print a zero-terminated message starting at %si
293  *****************************************************************************
294  */     
295 print_message:
296 1:      lodsb
297         testb   %al, %al
298         je      2f
299         call    print_character
300         jmp     1b
301 2:      ret
302
303 /*****************************************************************************
304  * Subroutine: print hex word in %ax
305  *****************************************************************************
306  */
307 print_hex_word:
308         mov     $4, %cx
309 1:
310         push    %ax
311         shr     $12, %ax
312         /* Courtesy of Norbert Juffa <norbert.juffa@amd.com> */
313         cmp     $10, %al        
314         sbb     $0x69, %al
315         das
316         call    print_character
317         pop     %ax
318         shl     $4, %ax
319         loop    1b
320         ret
321         
322 /*****************************************************************************
323  * Subroutine: print segment:offset address in %es:%di
324  *****************************************************************************
325  */
326 print_segoff:
327         push    %di
328         push    %es
329         pop     %ax
330         call    print_hex_word
331         movb    $0x3a,%al                       /* ':' */
332         call    print_character
333         pop     %ax
334         call    print_hex_word
335         mov     $0x20, %al                      /* ' ' */
336         call    print_character
337         ret
338         
339 /*****************************************************************************
340  * Make a PXE API call.  Works with either !PXE or PXENV+ API.
341  * Opcode in %bx.  pxe_parameter_structure always used.
342  * Returns status code (not exit code) in %bx and prints it.
343  * ORs status code with overall status code in pxe_overall_status, returns
344  * with zero flag set iff all PXE API calls have been successful.
345  *****************************************************************************
346  */
347 pxe_call:
348         /* Set up registers for PXENV+ API.  %bx already set up */
349         push    %ds
350         pop     %es
351         mov     $pxe_parameter_structure - _prefix, %di
352         /* Set up stack for !PXE API */
353         pushw   %cs
354         pushw   %di
355         pushw   %bx
356         /* Make the API call */
357         lcall   *(pxe_entry_segoff - _prefix)
358         /* Reset the stack */
359         add     $6, %sp
360         mov     pxe_parameter_structure - _prefix, %ax
361         push    %ax
362         call    print_hex_word
363         mov     $0x20, %ax              /* ' ' */
364         call    print_character
365         pop     %bx
366         or      %bx, pxe_overall_status - _prefix
367         ret
368
369 /*****************************************************************************
370  * PXE data structures
371  *****************************************************************************
372  */
373
374 pxe_overall_status:     .word 0
375
376 pxe_entry_segoff:
377 pxe_entry_offset:       .word 0
378 pxe_entry_segment:      .word 0
379
380 undi_code_segoff:
381 undi_code_size:         .word 0
382 undi_code_segment:      .word 0
383
384 undi_data_segoff:
385 undi_data_size:         .word 0
386 undi_data_segment:      .word 0
387
388 pxe_parameter_structure:
389         .word   0
390         .word   0,0,0,0,0
391
392 end_of_pxeloader:
393
394         .balign 16, 0
395 payload: