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