Copy pxelinux's shutdown sequence: use UNLOAD_STACK without STOP_BASE,
[people/xl0/gpxe.git] / src / arch / i386 / prefix / pxeprefix.S
1 #define PXENV_UNDI_SHUTDOWN     0x05
2 #define PXENV_STOP_UNDI         0x15
3 #define PXENV_UNLOAD_STACK      0x70
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  * Leave NIC in a safe state
143  *****************************************************************************
144  */
145 shutdown_nic:
146         movw    $PXENV_UNDI_SHUTDOWN, %bx
147         call    pxe_call
148
149 /*****************************************************************************
150  * Unload PXE base code
151  *****************************************************************************
152  */     
153 unload_base_code:
154         movw    $PXENV_UNLOAD_STACK, %bx
155         call    pxe_call
156         jnz     do_not_free_base_code
157 free_base_code:
158         movw    %fs:(0x13), %si
159         movw    undi_fbms_start, %di
160         call    free_basemem
161 do_not_free_base_code:
162
163 /*****************************************************************************
164  * Unload UNDI driver
165  *****************************************************************************
166  */
167 unload_undi:
168         movw    $PXENV_STOP_UNDI, %bx
169         call    pxe_call
170 #ifndef PXELOADER_KEEP_UNDI
171         jnz     do_not_free_undi
172 free_undi:
173         movw    undi_fbms_start, %si
174         movw    undi_fbms_end, %di
175         call    free_basemem
176 do_not_free_undi:
177 #endif /* PXELOADER_KEEP_UNDI */
178
179 /*****************************************************************************
180  * Exit point
181  *****************************************************************************
182  */     
183 finished:
184         movw    $10f, %si
185         movw    pxe_overall_status, %ax
186         testw   %ax, %ax
187         jz      1f
188 finished_with_error:
189         movw    $20f, %si
190 1:
191         call    print_message
192         jmp     run_etherboot
193 10:     .asciz "ok\n"
194 20:     .asciz "err\n"
195
196 /*****************************************************************************
197  * Subroutine: print character in %al (with LF -> LF,CR translation)
198  *****************************************************************************
199  */
200 print_character:
201         movw    $0x0007, %bx            /* page 0, attribute 7 (normal) */
202         movb    $0x0e, %ah              /* write char, tty mode */
203         cmpb    $0x0a, %al              /* '\n'? */
204         jne     1f
205         int     $0x10
206         movb    $0x0d, %al
207 1:      int     $0x10
208         ret
209         
210 /*****************************************************************************
211  * Subroutine: print a zero-terminated message starting at %si
212  *****************************************************************************
213  */     
214 print_message:
215 1:      lodsb
216         testb   %al, %al
217         je      2f
218         call    print_character
219         jmp     1b
220 2:      ret
221
222 /*****************************************************************************
223  * Subroutine: print hex word in %ax
224  *****************************************************************************
225  */
226 print_hex_word:
227         movw    $4, %cx
228 1:
229         pushw   %ax
230         shrw    $12, %ax
231         /* Courtesy of Norbert Juffa <norbert.juffa@amd.com> */
232         cmpb    $10, %al        
233         sbbb    $0x69, %al
234         das
235         call    print_character
236         popw    %ax
237         shlw    $4, %ax
238         loop    1b
239         ret
240         
241 /*****************************************************************************
242  * Subroutine: print segment:offset address in %es:%di
243  *****************************************************************************
244  */
245 print_segoff:
246         pushw   %di
247         pushw   %es
248         popw    %ax
249         call    print_hex_word
250         movb    $0x3a,%al                       /* ':' */
251         call    print_character
252         popw    %ax
253         call    print_hex_word
254         movb    $0x20, %al                      /* ' ' */
255         call    print_character
256         ret
257
258 /*****************************************************************************
259  * Subroutine: free and zero base memory from %si kB to %di kB
260  *****************************************************************************
261  */
262 free_basemem:
263         movw    %fs:(0x13), %ax         /* Current FBMS to %ax */
264         cmpw    %ax, %si                /* Update FBMS only if "old" value  */
265         jne     1f                      /* is correct                       */
266         movw    %di, %fs:(0x13)
267 1:      movw    %di, %bx
268 zero_kb:
269         movw    %si, %ax                /* Zero kB at %si */
270         shlw    $6, %ax
271         movw    %ax, %es
272         xorw    %ax, %ax
273         xorw    %di, %di
274         movw    $0x400, %cx
275         rep stosb
276         incw    %si                     /* Move to next kB */
277         cmpw    %si, %bx
278         jne     zero_kb                 /* Loop until done */
279         movw    %fs:(0x13), %ax         /* Print free base memory */
280         call    print_hex_word
281         movb    $0x20, %al                      /* ' ' */
282         call    print_character
283         ret
284
285 /*****************************************************************************
286  * Make a PXE API call.  Works with either !PXE or PXENV+ API.
287  * Opcode in %bx.  pxe_parameter_structure always used.
288  *
289  * Returns status code (not exit code) in %bx and prints it.  Returns
290  * with zero flag set if status code is zero (PXENV_STATUS_SUCCESS).
291  *****************************************************************************
292  */
293 pxe_call:
294         /* Set up registers for PXENV+ API.  %bx already set up */
295         pushw   %ds
296         popw    %es
297         movw    $pxe_parameter_structure, %di
298         /* Set up stack for !PXE API */
299         pushw   %cs
300         pushw   %di
301         pushw   %bx
302         /* Make the API call */
303         lcall   *pxe_entry_segoff
304         /* Reset the stack */
305         addw    $6, %sp
306         movw    pxe_parameter_structure, %ax
307         pushw   %ax
308         call    print_hex_word
309         movw    $0x20, %ax              /* ' ' */
310         call    print_character
311         popw    %bx
312         orw     %bx, pxe_overall_status
313         testw   %bx, %bx
314         ret
315
316 /*****************************************************************************
317  * PXE data structures
318  *****************************************************************************
319  */
320
321 pxe_entry_segoff:
322 pxe_entry_offset:       .word 0
323 pxe_entry_segment:      .word 0
324
325 undi_code_segoff:
326 undi_code_size:         .word 0
327 undi_code_segment:      .word 0
328
329 undi_data_segoff:
330 undi_data_size:         .word 0
331 undi_data_segment:      .word 0
332
333 undi_fbms_start:        .word 0
334 undi_fbms_end:          .word 0
335
336 pxe_parameter_structure:
337         .word   0
338         .word   0,0,0,0,0
339
340 pxe_overall_status:     .word 0
341
342 /*****************************************************************************
343  * Run Etherboot main code
344  *****************************************************************************
345  */     
346 run_etherboot:
347         /* Install Etherboot */
348         call    install
349
350         /* Jump to .text16 segment with %ds pointing to .data16*/
351         movw    %bx, %ds
352         pushw   %ax
353         pushw   $1f
354         lret
355         .section ".text16", "ax", @progbits
356 1:
357         /* Original PXE stack pointer to es:di.  We must hold it in
358          * registers, because our current stack may be vapourised by
359          * the time main() returns.  (main() will still be able to
360          * return, because prot_call() transfers the return address to
361          * the internal stack and back again).
362          */
363         popw    %di
364         popw    %es
365
366         /* Run main program */
367         pushl   $main
368         pushw   %cs
369         call    prot_call
370         popl    %eax /* discard */
371
372         /* If original PXE stack is intact, return via PXE, else via INT 18 */
373         cmpw    $PXE_STACK_MAGIC, %es:0(%di)
374         jne     exit_via_int18
375 exit_via_pxe:                           /* Stack OK, return to PXE */
376         movw    $exit_via_pxe_message, %si
377         call    print_exit_message
378         pushw   %es                     /* Restore original PXE stack */
379         popw    %ss
380         movw    %di, %sp
381         popw    %ax /* discard PXE_STACK_MAGIC */
382         popw    %ax /* discard %cs */
383         popw    %ax /* discard %ss */
384         popw    %ds
385         popw    %es
386         popw    %fs
387         popw    %gs
388         popal
389         popfl
390         xorw    %ax, %ax                /* Return PXENV_STATUS_SUCCESS */
391         lret
392 exit_via_int18:                         /* Stack damaged, do int 18 */
393         movw    $exit_via_int18_message, %si
394         call    print_exit_message
395         int     $0x18
396
397 print_exit_message:     
398         movw    $0x0007, %bx            /* page 0, attribute 7 (normal) */
399         movb    $0x0e, %ah              /* write char, tty mode */
400 1:      lodsb
401         testb   %al, %al
402         je      2f
403         int     $0x10
404         jmp     1b
405 2:      ret
406
407         .section ".data16", "aw", @progbits
408 exit_via_pxe_message:
409         .asciz  "EB->PXE\r\n"
410 exit_via_int18_message:
411         .asciz  "EB->BIOS\r\n"