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