[prefix] Add PCI bus:dev.fn to ROM product string
[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         xorw    %di, %di
41         call    print_message
42         .section ".prefix.data"
43 10:     .asciz  "PXE->EB:"
44         .previous
45
46 /*****************************************************************************
47  * Verify PXENV+ structure and record parameters of interest
48  *****************************************************************************
49  */
50 detect_pxenv:
51         /* Signature check */
52         les     pxenv_segoff, %bx
53         cmpl    $0x4e455850, %es:(%bx)  /* 'PXEN' signature */
54         jne     no_pxenv
55         cmpw    $0x2b56, %es:4(%bx)     /* 'V+' signature */
56         jne     no_pxenv
57         /* Record entry point and UNDI segments */
58         pushl   %es:0x0a(%bx)           /* Entry point */
59         popl    entry_segoff
60         pushw   %es:0x24(%bx)           /* UNDI code segment */
61         pushw   %es:0x26(%bx)           /* UNDI code size */
62         popl    undi_code_segoff
63         pushw   %es:0x20(%bx)           /* UNDI data segment */
64         pushw   %es:0x22(%bx)           /* UNDI data size */
65         popl    undi_data_segoff
66         /* Print "PXENV+ at <address>" */
67         movw    $10f, %si
68         call    print_message
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, %bx
90         cmpl    $0x45585021, %es:(%bx)  /* '!PXE' signature */
91         jne     no_ppxe
92         /* Record structure address, entry point, and UNDI segments */
93         pushw   %es
94         popw    ppxe_segment
95         movw    %bx, ppxe_offset
96         pushl   %es:0x10(%bx)           /* Entry point */
97         popl    entry_segoff
98         pushw   %es:0x30(%bx)           /* UNDI code segment */
99         pushw   %es:0x36(%bx)           /* UNDI code size */
100         popl    undi_code_segoff
101         pushw   %es:0x28(%bx)           /* UNDI data segment */
102         pushw   %es:0x2e(%bx)           /* 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, %bx
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, %bx
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, %bx
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    undi_fbms_start, %ax
275         movw    %fs:(0x13), %bx
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_end, %ax
293         movw    undi_fbms_start, %bx
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:%bx : segment:offset address to print
328  *   %ds:di : output buffer (or %di=0 to print to console)
329  * Returns:
330  *   %ds:di : next character in output buffer (if applicable)
331  *****************************************************************************
332  */
333 print_segoff:
334         /* Preserve registers */
335         pushw   %ax
336         /* Print "<segment>:offset" */
337         movw    %es, %ax
338         call    print_hex_word
339         movb    $':', %al
340         call    print_character
341         movw    %bx, %ax
342         call    print_hex_word
343         /* Restore registers and return */
344         popw    %ax
345         ret
346
347 /*****************************************************************************
348  * Subroutine: print decimal word
349  *
350  * Parameters:
351  *   %ax : word to print
352  *   %ds:di : output buffer (or %di=0 to print to console)
353  * Returns:
354  *   %ds:di : next character in output buffer (if applicable)
355  *****************************************************************************
356  */
357 print_word:
358         /* Preserve registers */
359         pushw   %ax
360         pushw   %bx
361         pushw   %cx
362         pushw   %dx
363         /* Build up digit sequence on stack */
364         movw    $10, %bx
365         xorw    %cx, %cx
366 1:      xorw    %dx, %dx
367         divw    %bx, %ax
368         pushw   %dx
369         incw    %cx
370         testw   %ax, %ax
371         jnz     1b
372         /* Print digit sequence */
373 1:      popw    %ax
374         call    print_hex_nibble
375         loop    1b
376         /* Restore registers and return */
377         popw    %dx
378         popw    %cx
379         popw    %bx
380         popw    %ax
381         ret
382         
383 /*****************************************************************************
384  * Subroutine: zero 1kB block of base memory
385  *
386  * Parameters:
387  *   %bx : block to zero (in kB)
388  * Returns:
389  *   Nothing
390  *****************************************************************************
391  */
392 zero_kb:
393         /* Preserve registers */
394         pushw   %ax
395         pushw   %cx
396         pushw   %di
397         pushw   %es
398         /* Zero block */
399         movw    %bx, %ax
400         shlw    $6, %ax
401         movw    %ax, %es
402         movw    $0x400, %cx
403         xorw    %di, %di
404         xorw    %ax, %ax
405         rep stosb
406         /* Restore registers and return */
407         popw    %es
408         popw    %di
409         popw    %cx
410         popw    %ax
411         ret
412         
413 /*****************************************************************************
414  * Subroutine: free and zero base memory
415  *
416  * Parameters:
417  *   %ax : Desired new free base memory counter (in kB)
418  *   %bx : Expected current free base memory counter (in kB)
419  *   %fs : BIOS data segment (0x40)
420  * Returns:
421  *   None
422  *
423  * The base memory from %bx kB to %ax kB is unconditionally zeroed.
424  * It will be freed if and only if the expected current free base
425  * memory counter (%bx) matches the actual current free base memory
426  * counter in 0x40:0x13; if this does not match then the memory will
427  * be leaked.
428  *****************************************************************************
429  */
430 free_basemem:
431         /* Zero base memory */
432         pushw   %bx
433 1:      cmpw    %bx, %ax
434         je      2f
435         call    zero_kb
436         incw    %bx
437         jmp     1b
438 2:      popw    %bx
439         /* Free base memory */
440         cmpw    %fs:(0x13), %bx         /* Update FBMS only if "old" value  */
441         jne     1f                      /* is correct                       */
442 1:      movw    %ax, %fs:(0x13)
443         ret
444
445 /*****************************************************************************
446  * Subroutine: make a PXE API call.  Works with either !PXE or PXENV+ API.
447  *
448  * Parameters:
449  *   %bx : PXE API call number
450  *   %ds:pxe_parameter_structure : Parameters for PXE API call
451  * Returns:
452  *   %ax : PXE status code (not exit code)
453  *   CF set if %ax is non-zero
454  *****************************************************************************
455  */
456 pxe_call:
457         /* Preserve registers */
458         pushw   %di
459         pushw   %es
460         /* Set up registers for PXENV+ API.  %bx already set up */
461         pushw   %ds
462         popw    %es
463         movw    $pxe_parameter_structure, %di
464         /* Set up stack for !PXE API */
465         pushw   %es
466         pushw   %di
467         pushw   %bx
468         /* Make the API call */
469         lcall   *entry_segoff
470         /* Reset the stack */
471         addw    $6, %sp
472         movw    pxe_parameter_structure, %ax
473         clc
474         testw   %ax, %ax
475         jz      1f
476         stc
477 1:      /* Restore registers and return */
478         popw    %es
479         popw    %di
480         ret
481
482 /*****************************************************************************
483  * Subroutine: print PXE API call error message
484  *
485  * Parameters:
486  *   %ax : PXE status code
487  *   %bx : PXE API call number
488  * Returns:
489  *   Nothing
490  *****************************************************************************
491  */
492 print_pxe_error:
493         pushw   %si
494         movw    $10f, %si
495         call    print_message
496         xchgw   %ax, %bx
497         call    print_hex_word
498         movw    $20f, %si
499         call    print_message
500         xchgw   %ax, %bx
501         call    print_hex_word
502         movw    $30f, %si
503         call    print_message
504         popw    %si
505         ret
506         .section ".prefix.data"
507 10:     .asciz  "         UNDI API call "
508 20:     .asciz  " failed: status code "
509 30:     .asciz  "\n"
510         .previous
511
512 /*****************************************************************************
513  * PXE data structures
514  *****************************************************************************
515  */
516
517 pxe_parameter_structure: .fill 20
518
519 undi_code_segoff:
520 undi_code_size:         .word 0
521 undi_code_segment:      .word 0
522
523 undi_data_segoff:
524 undi_data_size:         .word 0
525 undi_data_segment:      .word 0
526
527 /* The following fields are part of a struct undi_device */
528
529 undi_device:
530
531 pxenv_segoff:
532 pxenv_offset:           .word 0
533 pxenv_segment:          .word 0
534
535 ppxe_segoff:
536 ppxe_offset:            .word 0
537 ppxe_segment:           .word 0
538         
539 entry_segoff:
540 entry_offset:           .word 0
541 entry_segment:          .word 0
542
543 undi_fbms_start:        .word 0
544 undi_fbms_end:          .word 0
545
546 pci_busdevfn:           .word UNDI_NO_PCI_BUSDEVFN
547 isapnp_csn:             .word UNDI_NO_ISAPNP_CSN
548 isapnp_read_port:       .word UNDI_NO_ISAPNP_READ_PORT
549
550 pci_vendor:             .word 0
551 pci_device:             .word 0
552 flags:                  .word UNDI_FL_STARTED
553
554         .equ undi_device_size, ( . - undi_device )
555
556 /*****************************************************************************
557  * Run Etherboot main code
558  *****************************************************************************
559  */     
560 run_etherboot:
561         /* Install Etherboot */
562         call    install
563
564         /* Set up real-mode stack */
565         movw    %bx, %ss
566         movw    $_estack16, %sp
567
568 #ifdef PXELOADER_KEEP_UNDI
569         /* Copy our undi_device structure to the preloaded_undi variable */
570         movw    %bx, %es
571         movw    $preloaded_undi, %di
572         movw    $undi_device, %si
573         movw    $undi_device_size, %cx
574         rep movsb
575 #endif
576
577         /* Jump to .text16 segment with %ds pointing to .data16 */
578         movw    %bx, %ds
579         pushw   %ax
580         pushw   $1f
581         lret
582         .section ".text16", "ax", @progbits
583 1:
584         /* Run main program */
585         pushl   $main
586         pushw   %cs
587         call    prot_call
588         popl    %eax /* discard */
589
590         /* Boot next device */
591         int     $0x18
592         .previous