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