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