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