[pxeprefix] Add .kkpxe image type and ability to return via PXE stack
[people/lynusvaz/gpxe.git] / src / arch / i386 / prefix / pxeprefix.S
1 #define PXENV_UNDI_SHUTDOWN             0x0005
2 #define PXENV_UNDI_GET_NIC_TYPE         0x0012
3 #define PXENV_STOP_UNDI                 0x0015
4 #define PXENV_UNLOAD_STACK              0x0070
5
6         .text
7         .arch i386
8         .org 0
9         .code16
10
11 #include <undi.h>
12
13 #define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
14
15 /*****************************************************************************
16  * Entry point: set operating context, print welcome message
17  *****************************************************************************
18  */
19         .section ".prefix", "ax", @progbits
20         jmp     $0x7c0, $1f
21 1:
22         /* Preserve registers for possible return to PXE */
23         pushfl
24         pushal
25         pushw   %gs
26         pushw   %fs
27         pushw   %es
28         pushw   %ds
29
30         /* Store magic word on PXE stack and remember PXE %ss:esp */
31         pushl   $STACK_MAGIC
32         movw    %ss, %cs:pxe_ss
33         movl    %esp, %cs:pxe_esp
34         movw    %sp, %bp
35         movl    (10*4+4*2+4)(%bp),%ebp  /* !PXE address */
36
37         /* Set up %ds */
38         movw    %cs, %ax
39         movw    %ax, %ds
40         /* Record PXENV+ and !PXE nominal addresses */
41         movw    %es, pxenv_segment      /* PXENV+ address */
42         movw    %bx, pxenv_offset
43         movl    %ebp, ppxe_segoff       /* !PXE address */
44         /* Set up %es and %fs */
45         movw    %ax, %es
46         movw    $0x40, %ax              /* BIOS data segment access */
47         movw    %ax, %fs
48         /* Set up stack just below 0x7c00 */
49         xorw    %ax, %ax
50         movw    %ax, %ss
51         movl    $0x7c00, %esp
52         /* Clear direction flag, for the sake of sanity */
53         cld
54         /* Print welcome message */
55         movw    $10f, %si
56         xorw    %di, %di
57         call    print_message
58         .section ".prefix.data", "aw", @progbits
59 10:     .asciz  "PXE->EB:"
60         .previous
61
62 /*****************************************************************************
63  * Verify PXENV+ structure and record parameters of interest
64  *****************************************************************************
65  */
66 detect_pxenv:
67         /* Signature check */
68         les     pxenv_segoff, %bx
69         cmpl    $0x4e455850, %es:(%bx)  /* 'PXEN' signature */
70         jne     no_pxenv
71         cmpw    $0x2b56, %es:4(%bx)     /* 'V+' signature */
72         jne     no_pxenv
73         /* Record entry point and UNDI segments */
74         pushl   %es:0x0a(%bx)           /* Entry point */
75         popl    entry_segoff
76         pushw   %es:0x24(%bx)           /* UNDI code segment */
77         pushw   %es:0x26(%bx)           /* UNDI code size */
78         popl    undi_code_segoff
79         pushw   %es:0x20(%bx)           /* UNDI data segment */
80         pushw   %es:0x22(%bx)           /* UNDI data size */
81         popl    undi_data_segoff
82         /* Print "PXENV+ at <address>" */
83         movw    $10f, %si
84         call    print_message
85         call    print_segoff
86         movb    $( ',' ), %al
87         call    print_character
88         jmp     99f
89         .section ".prefix.data", "aw", @progbits
90 10:     .asciz  " PXENV+ at "
91         .previous
92
93 no_pxenv:
94         xorl    %eax, %eax
95         movl    %eax, pxenv_segoff
96         
97 99:
98         
99 /*****************************************************************************
100  * Verify !PXE structure and record parameters of interest
101  *****************************************************************************
102  */
103 detect_ppxe:
104         /* Signature check */
105         les     ppxe_segoff, %bx
106         cmpl    $0x45585021, %es:(%bx)  /* '!PXE' signature */
107         jne     no_ppxe
108         /* Record structure address, entry point, and UNDI segments */
109         pushw   %es
110         popw    ppxe_segment
111         movw    %bx, ppxe_offset
112         pushl   %es:0x10(%bx)           /* Entry point */
113         popl    entry_segoff
114         pushw   %es:0x30(%bx)           /* UNDI code segment */
115         pushw   %es:0x36(%bx)           /* UNDI code size */
116         popl    undi_code_segoff
117         pushw   %es:0x28(%bx)           /* UNDI data segment */
118         pushw   %es:0x2e(%bx)           /* UNDI data size */
119         popl    undi_data_segoff
120         /* Print "!PXE at <address>" */
121         movw    $10f, %si
122         call    print_message
123         call    print_segoff
124         movb    $( ',' ), %al
125         call    print_character
126         jmp     99f
127         .section ".prefix.data", "aw", @progbits
128 10:     .asciz  " !PXE at "
129         .previous
130
131 no_ppxe:
132         xorl    %eax, %eax
133         movl    %eax, ppxe_segoff
134
135 99:
136
137 /*****************************************************************************
138  * Sanity check: we must have an entry point
139  *****************************************************************************
140  */
141 check_have_stack:
142         /* Check for entry point */
143         movl    entry_segoff, %eax
144         testl   %eax, %eax
145         jnz     99f
146         /* No entry point: print message and skip everything else */
147         movw    $10f, %si
148         call    print_message
149         jmp     finished
150         .section ".prefix.data", "aw", @progbits
151 10:     .asciz  " No PXE stack found!\n"
152         .previous
153 99:     
154
155 /*****************************************************************************
156  * Calculate base memory usage by UNDI
157  *****************************************************************************
158  */
159 find_undi_basemem_usage:
160         movw    undi_code_segment, %ax
161         movw    undi_code_size, %bx
162         movw    undi_data_segment, %cx
163         movw    undi_data_size, %dx
164         cmpw    %ax, %cx
165         ja      1f
166         xchgw   %ax, %cx
167         xchgw   %bx, %dx
168 1:      /* %ax:%bx now describes the lower region, %cx:%dx the higher */
169         shrw    $6, %ax                 /* Round down to nearest kB */
170         movw    %ax, undi_fbms_start
171         addw    $0x0f, %dx              /* Round up to next segment */
172         shrw    $4, %dx
173         addw    %dx, %cx
174         addw    $((1024 / 16) - 1), %cx /* Round up to next kB */
175         shrw    $6, %cx
176         movw    %cx, undi_fbms_end
177
178 /*****************************************************************************
179  * Print information about detected PXE stack
180  *****************************************************************************
181  */
182 print_structure_information:
183         /* Print entry point */
184         movw    $10f, %si
185         call    print_message
186         les     entry_segoff, %bx
187         call    print_segoff
188         .section ".prefix.data", "aw", @progbits
189 10:     .asciz  " entry point at "
190         .previous
191         /* Print UNDI code segment */
192         movw    $10f, %si
193         call    print_message
194         les     undi_code_segoff, %bx
195         call    print_segoff
196         .section ".prefix.data", "aw", @progbits
197 10:     .asciz  "\n         UNDI code segment "
198         .previous
199         /* Print UNDI data segment */
200         movw    $10f, %si
201         call    print_message
202         les     undi_data_segoff, %bx
203         call    print_segoff
204         .section ".prefix.data", "aw", @progbits
205 10:     .asciz  ", data segment "
206         .previous
207         /* Print UNDI memory usage */
208         movw    $10f, %si
209         call    print_message
210         movw    undi_fbms_start, %ax
211         call    print_word
212         movb    $( '-' ), %al
213         call    print_character
214         movw    undi_fbms_end, %ax
215         call    print_word
216         movw    $20f, %si
217         call    print_message
218         .section ".prefix.data", "aw", @progbits
219 10:     .asciz  " ("
220 20:     .asciz  "kB)\n"
221         .previous
222
223 /*****************************************************************************
224  * Determine physical device
225  *****************************************************************************
226  */
227 get_physical_device:
228         /* Issue PXENV_UNDI_GET_NIC_TYPE */
229         movw    $PXENV_UNDI_GET_NIC_TYPE, %bx
230         call    pxe_call
231         jnc     1f
232         call    print_pxe_error
233         jmp     no_physical_device
234 1:      /* Determine physical device type */
235         movb    ( pxe_parameter_structure + 0x02 ), %al
236         cmpb    $2, %al
237         je      pci_physical_device
238         jmp     no_physical_device
239
240 pci_physical_device:
241         /* Record PCI bus:dev.fn and vendor/device IDs */
242         movl    ( pxe_parameter_structure + 0x03 ), %eax
243         movl    %eax, pci_vendor
244         movw    ( pxe_parameter_structure + 0x0b ), %ax
245         movw    %ax, pci_busdevfn
246         movw    $10f, %si
247         call    print_message
248         call    print_pci_busdevfn
249         movb    $0x0a, %al
250         call    print_character
251         jmp     99f
252         .section ".prefix.data", "aw", @progbits
253 10:     .asciz  "         UNDI device is PCI "
254         .previous
255
256 no_physical_device:
257         /* No device found, or device type not understood */
258         movw    $10f, %si
259         call    print_message
260         .section ".prefix.data", "aw", @progbits
261 10:     .asciz  "         Unable to determine UNDI physical device\n"
262         .previous
263
264 99:
265
266 /*****************************************************************************
267  * Leave NIC in a safe state
268  *****************************************************************************
269  */
270 #ifndef PXELOADER_KEEP_PXE
271 shutdown_nic:
272         /* Issue PXENV_UNDI_SHUTDOWN */
273         movw    $PXENV_UNDI_SHUTDOWN, %bx
274         call    pxe_call
275         jnc     1f
276         call    print_pxe_error
277 1:
278 unload_base_code:
279         /* Issue PXENV_UNLOAD_STACK */
280         movw    $PXENV_UNLOAD_STACK, %bx
281         call    pxe_call
282         jnc     1f
283         call    print_pxe_error
284         jmp     99f
285 1:      /* Free base memory used by PXE base code */
286         movw    undi_fbms_start, %ax
287         movw    %fs:(0x13), %bx
288         call    free_basemem
289 99:
290         andw    $~( UNDI_FL_INITIALIZED | UNDI_FL_KEEP_ALL ), flags
291 #endif /* PXELOADER_KEEP_PXE */
292
293 /*****************************************************************************
294  * Unload UNDI driver
295  *****************************************************************************
296  */
297 #ifndef PXELOADER_KEEP_UNDI
298 unload_undi:
299         /* Issue PXENV_STOP_UNDI */
300         movw    $PXENV_STOP_UNDI, %bx
301         call    pxe_call
302         jnc     1f
303         call    print_pxe_error
304         jmp     99f
305 1:      /* Free base memory used by UNDI */
306         movw    undi_fbms_end, %ax
307         movw    undi_fbms_start, %bx
308         call    free_basemem
309         /* Clear UNDI_FL_STARTED */
310         andw    $~UNDI_FL_STARTED, flags
311 99:     
312 #endif /* PXELOADER_KEEP_UNDI */
313
314 /*****************************************************************************
315  * Print remaining free base memory
316  *****************************************************************************
317  */
318 print_free_basemem:
319         movw    $10f, %si
320         call    print_message
321         movw    %fs:(0x13), %ax
322         call    print_word
323         movw    $20f, %si
324         call    print_message
325         .section ".prefix.data", "aw", @progbits
326 10:     .asciz  "         "
327 20:     .asciz  "kB free base memory after PXE unload\n"
328         .previous
329         
330 /*****************************************************************************
331  * Exit point
332  *****************************************************************************
333  */     
334 finished:
335         jmp     run_gpxe
336
337 /*****************************************************************************
338  * Subroutine: print segment:offset address
339  *
340  * Parameters:
341  *   %es:%bx : segment:offset address to print
342  *   %ds:di : output buffer (or %di=0 to print to console)
343  * Returns:
344  *   %ds:di : next character in output buffer (if applicable)
345  *****************************************************************************
346  */
347 print_segoff:
348         /* Preserve registers */
349         pushw   %ax
350         /* Print "<segment>:offset" */
351         movw    %es, %ax
352         call    print_hex_word
353         movb    $( ':' ), %al
354         call    print_character
355         movw    %bx, %ax
356         call    print_hex_word
357         /* Restore registers and return */
358         popw    %ax
359         ret
360
361 /*****************************************************************************
362  * Subroutine: print decimal word
363  *
364  * Parameters:
365  *   %ax : word to print
366  *   %ds:di : output buffer (or %di=0 to print to console)
367  * Returns:
368  *   %ds:di : next character in output buffer (if applicable)
369  *****************************************************************************
370  */
371 print_word:
372         /* Preserve registers */
373         pushw   %ax
374         pushw   %bx
375         pushw   %cx
376         pushw   %dx
377         /* Build up digit sequence on stack */
378         movw    $10, %bx
379         xorw    %cx, %cx
380 1:      xorw    %dx, %dx
381         divw    %bx, %ax
382         pushw   %dx
383         incw    %cx
384         testw   %ax, %ax
385         jnz     1b
386         /* Print digit sequence */
387 1:      popw    %ax
388         call    print_hex_nibble
389         loop    1b
390         /* Restore registers and return */
391         popw    %dx
392         popw    %cx
393         popw    %bx
394         popw    %ax
395         ret
396         
397 /*****************************************************************************
398  * Subroutine: zero 1kB block of base memory
399  *
400  * Parameters:
401  *   %bx : block to zero (in kB)
402  * Returns:
403  *   Nothing
404  *****************************************************************************
405  */
406 zero_kb:
407         /* Preserve registers */
408         pushw   %ax
409         pushw   %cx
410         pushw   %di
411         pushw   %es
412         /* Zero block */
413         movw    %bx, %ax
414         shlw    $6, %ax
415         movw    %ax, %es
416         movw    $0x400, %cx
417         xorw    %di, %di
418         xorw    %ax, %ax
419         rep stosb
420         /* Restore registers and return */
421         popw    %es
422         popw    %di
423         popw    %cx
424         popw    %ax
425         ret
426         
427 /*****************************************************************************
428  * Subroutine: free and zero base memory
429  *
430  * Parameters:
431  *   %ax : Desired new free base memory counter (in kB)
432  *   %bx : Expected current free base memory counter (in kB)
433  *   %fs : BIOS data segment (0x40)
434  * Returns:
435  *   None
436  *
437  * The base memory from %bx kB to %ax kB is unconditionally zeroed.
438  * It will be freed if and only if the expected current free base
439  * memory counter (%bx) matches the actual current free base memory
440  * counter in 0x40:0x13; if this does not match then the memory will
441  * be leaked.
442  *****************************************************************************
443  */
444 free_basemem:
445         /* Zero base memory */
446         pushw   %bx
447 1:      cmpw    %bx, %ax
448         je      2f
449         call    zero_kb
450         incw    %bx
451         jmp     1b
452 2:      popw    %bx
453         /* Free base memory */
454         cmpw    %fs:(0x13), %bx         /* Update FBMS only if "old" value  */
455         jne     1f                      /* is correct                       */
456 1:      movw    %ax, %fs:(0x13)
457         ret
458
459 /*****************************************************************************
460  * Subroutine: make a PXE API call.  Works with either !PXE or PXENV+ API.
461  *
462  * Parameters:
463  *   %bx : PXE API call number
464  *   %ds:pxe_parameter_structure : Parameters for PXE API call
465  * Returns:
466  *   %ax : PXE status code (not exit code)
467  *   CF set if %ax is non-zero
468  *****************************************************************************
469  */
470 pxe_call:
471         /* Preserve registers */
472         pushw   %di
473         pushw   %es
474         /* Set up registers for PXENV+ API.  %bx already set up */
475         pushw   %ds
476         popw    %es
477         movw    $pxe_parameter_structure, %di
478         /* Set up stack for !PXE API */
479         pushw   %es
480         pushw   %di
481         pushw   %bx
482         /* Make the API call */
483         lcall   *entry_segoff
484         /* Reset the stack */
485         addw    $6, %sp
486         movw    pxe_parameter_structure, %ax
487         clc
488         testw   %ax, %ax
489         jz      1f
490         stc
491 1:      /* Restore registers and return */
492         popw    %es
493         popw    %di
494         ret
495
496 /*****************************************************************************
497  * Subroutine: print PXE API call error message
498  *
499  * Parameters:
500  *   %ax : PXE status code
501  *   %bx : PXE API call number
502  * Returns:
503  *   Nothing
504  *****************************************************************************
505  */
506 print_pxe_error:
507         pushw   %si
508         movw    $10f, %si
509         call    print_message
510         xchgw   %ax, %bx
511         call    print_hex_word
512         movw    $20f, %si
513         call    print_message
514         xchgw   %ax, %bx
515         call    print_hex_word
516         movw    $30f, %si
517         call    print_message
518         popw    %si
519         ret
520         .section ".prefix.data", "aw", @progbits
521 10:     .asciz  "         UNDI API call "
522 20:     .asciz  " failed: status code "
523 30:     .asciz  "\n"
524         .previous
525
526 /*****************************************************************************
527  * PXE data structures
528  *****************************************************************************
529  */
530         .section ".prefix.data"
531
532 pxe_ss:                 .word 0
533 pxe_esp:                .long 0
534
535 pxe_parameter_structure: .fill 20
536
537 undi_code_segoff:
538 undi_code_size:         .word 0
539 undi_code_segment:      .word 0
540
541 undi_data_segoff:
542 undi_data_size:         .word 0
543 undi_data_segment:      .word 0
544
545 /* The following fields are part of a struct undi_device */
546
547 undi_device:
548
549 pxenv_segoff:
550 pxenv_offset:           .word 0
551 pxenv_segment:          .word 0
552
553 ppxe_segoff:
554 ppxe_offset:            .word 0
555 ppxe_segment:           .word 0
556         
557 entry_segoff:
558 entry_offset:           .word 0
559 entry_segment:          .word 0
560
561 undi_fbms_start:        .word 0
562 undi_fbms_end:          .word 0
563
564 pci_busdevfn:           .word UNDI_NO_PCI_BUSDEVFN
565 isapnp_csn:             .word UNDI_NO_ISAPNP_CSN
566 isapnp_read_port:       .word UNDI_NO_ISAPNP_READ_PORT
567
568 pci_vendor:             .word 0
569 pci_device:             .word 0
570 flags:
571         .word ( UNDI_FL_INITIALIZED | UNDI_FL_STARTED | UNDI_FL_KEEP_ALL )
572
573         .equ undi_device_size, ( . - undi_device )
574
575 /*****************************************************************************
576  * Run gPXE main code
577  *****************************************************************************
578  */
579         .section ".prefix"
580 run_gpxe:
581         /* Install gPXE */
582         call    install
583
584         /* Set up real-mode stack */
585         movw    %bx, %ss
586         movw    $_estack16, %sp
587
588 #ifdef PXELOADER_KEEP_UNDI
589         /* Copy our undi_device structure to the preloaded_undi variable */
590         movw    %bx, %es
591         movw    $preloaded_undi, %di
592         movw    $undi_device, %si
593         movw    $undi_device_size, %cx
594         rep movsb
595 #endif
596
597         /* Retrieve PXE %ss:esp */
598         movw    pxe_ss, %di
599         movl    pxe_esp, %ebp
600
601         /* Jump to .text16 segment with %ds pointing to .data16 */
602         movw    %bx, %ds
603         pushw   %ax
604         pushw   $1f
605         lret
606         .section ".text16", "ax", @progbits
607 1:
608         /* Run main program */
609         pushl   $main
610         pushw   %cs
611         call    prot_call
612         popl    %ecx /* discard */
613
614         /* Uninstall gPXE */
615         call    uninstall
616
617         /* Restore PXE stack */
618         movw    %di, %ss
619         movl    %ebp, %esp
620
621         /* Check PXE stack magic */
622         popl    %eax
623         cmpl    $STACK_MAGIC, %eax
624         jne     1f
625
626         /* PXE stack OK: return to caller */
627         popw    %ds
628         popw    %es
629         popw    %fs
630         popw    %gs
631         popal
632         popfl
633         xorw    %ax, %ax        /* Return success */
634         lret
635
636 1:      /* PXE stack corrupt or removed: use INT 18 */
637         int     $0x18
638         .previous