2 Helper Routines that use a PXE-enabled NIC option ROM.
4 Copyright (c) 1999 - 2017, Intel Corporation. All rights reserved.<BR>
6 This program and the accompanying materials
7 are licensed and made available under the terms and conditions
8 of the BSD License which accompanies this distribution. The
9 full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
17 #include "BiosSnp16.h"
19 #define TO_SEGMENT(x) ((UINT16) (RShiftU64 ((UINT32)(UINTN) (x), 4) & 0xF000))
20 #define TO_OFFSET(x) ((UINT16) ((UINT32)(UINTN) (x) & 0xFFFF))
21 #define PARAGRAPH_SIZE 0x10
22 #define IVT_BASE 0x00000000
26 UINT16 Signature
; ///< 0xaa55
27 UINT8 ROMlength
; ///< size of this ROM in 512 byte blocks
28 UINT8 InitEntryPoint
[4]; ///< a jump to the initialization routine
29 UINT8 Reserved
[0xf]; ///< various
30 UINT16 PxeRomIdOffset
; ///< offset of UNDI, $BC$, or BUSD ROM ID structure
31 UINT16 PcirHeaderOffset
; ///< offset of PCI Expansion Header
32 UINT16 PnpHeaderOffset
; ///< offset of Plug and Play Expansion Header
36 UINT32 CachedVectorAddress
[0x100];
39 Cache Interrupt verctor address converted from IVT number.
41 @param VectorNumber IVT number
43 @retval EFI_SUCCESS Success to operation.
52 Address
= (UINT32
*) ((UINTN
) IVT_BASE
+ VectorNumber
* 4);
53 CachedVectorAddress
[VectorNumber
] = *Address
;
58 Get interrupt vector address according to IVT number.
60 @param VectorNumber Given IVT number
62 @return cached interrupt vector address.
65 RestoreCachedVectorAddress (
71 Address
= (UINT32
*) ((UINTN
) IVT_BASE
+ VectorNumber
* 4);
72 *Address
= CachedVectorAddress
[VectorNumber
];
77 Print Undi loader table.
79 @param UndiLoaderStructure Point to Undi Loader table structure.
83 Print_Undi_Loader_Table (
84 VOID
*UndiLoaderStructure
87 UNDI_LOADER_T
*DisplayPointer
;
89 DisplayPointer
= (UNDI_LOADER_T
*) UndiLoaderStructure
;
91 DEBUG ((DEBUG_NET
, "Before Parsing the table contents, the table itself lives\n"));
92 DEBUG ((DEBUG_NET
, "\tat the address 0x%X\n\r", (UINT32
)(UINTN
) UndiLoaderStructure
));
94 DEBUG ((DEBUG_NET
, "\n\rStatus = 0x%X\n\r", DisplayPointer
->Status
));
95 DEBUG ((DEBUG_NET
, "\t_AX_= 0x%X\n\r", DisplayPointer
->Ax
));
96 DEBUG ((DEBUG_NET
, "\t_BX_= 0x%X\n\r", DisplayPointer
->Bx
));
97 DEBUG ((DEBUG_NET
, "\t_DX_= 0x%X\n\r", DisplayPointer
->Dx
));
98 DEBUG ((DEBUG_NET
, "\t_DI_= 0x%X\n\r", DisplayPointer
->Di
));
99 DEBUG ((DEBUG_NET
, "\t_ES_= 0x%X\n\r", DisplayPointer
->Es
));
100 DEBUG ((DEBUG_NET
, "\tUNDI_DS= 0x%X\n\r", DisplayPointer
->Undi_Ds
));
101 DEBUG ((DEBUG_NET
, "\tUNDI_CS= 0x%X\n\r", DisplayPointer
->Undi_Cs
));
102 DEBUG ((DEBUG_NET
, "\tPXEptr:SEG= 0x%X\n\r", (UINT16
) DisplayPointer
->PXEptr
.Segment
));
103 DEBUG ((DEBUG_NET
, "\tPXEptr:OFF= 0x%X\n\r", (UINT16
) DisplayPointer
->PXEptr
.Offset
));
104 DEBUG ((DEBUG_NET
, "\tPXENVptr:SEG= 0x%X\n\r", (UINT16
) DisplayPointer
->PXENVptr
.Segment
));
105 DEBUG ((DEBUG_NET
, "\tPXENVptr:OFF= 0x%X\n\r", (UINT16
) DisplayPointer
->PXENVptr
.Offset
));
109 Simple table dumper. The ROMID table is necessary in order to effect
110 the "Early UNDI" trick. Herein, the UNDI layer can be loaded in the
111 pre-boot phase without having to download a Network Boot Program
112 across the wire. It is required in the implementation in that we
115 @param RomIDStructure Point to RomID structure.
120 IN VOID
*RomIDStructure
123 UNDI_ROMID_T
*DisplayPointer
;
125 DisplayPointer
= (UNDI_ROMID_T
*) RomIDStructure
;
127 DEBUG ((DEBUG_NET
, "Before Parsing the table contents, the table itself lives\n"));
128 DEBUG ((DEBUG_NET
, "\tat the address 0x%X\n\r", (UINT32
)(UINTN
) RomIDStructure
));
132 "\n\rROMID %c%c%c%c\n\r",
133 DisplayPointer
->Signature
[0],
134 DisplayPointer
->Signature
[1],
135 DisplayPointer
->Signature
[2],
136 DisplayPointer
->Signature
[3])
141 "Length of this structure in bytes = 0x%X\n\r",
142 DisplayPointer
->StructLength
)
146 "Use to make byte checksum of this structure == zero is = 0x%X\n\r",
147 DisplayPointer
->StructCksum
)
151 "Structure format revision number= 0x%X\n\r",
152 DisplayPointer
->StructRev
)
156 "API Revision number = 0x%X 0x%X 0x%X\n\r",
157 DisplayPointer
->UNDI_Rev
[0],
158 DisplayPointer
->UNDI_Rev
[1],
159 DisplayPointer
->UNDI_Rev
[2])
163 "Offset of UNDI loader routine in the option ROM image= 0x%X\n\r",
164 DisplayPointer
->UNDI_Loader
)
166 DEBUG ((DEBUG_NET
, "From the data above, the absolute entry point of the UNDI loader is\n\r"));
169 "\tat address 0x%X\n\r",
170 (UINT32
) (DisplayPointer
->UNDI_Loader
+ ((UINT32
) (UINTN
)(DisplayPointer
- 0x20) & 0xFFFF0)))
172 DEBUG ((DEBUG_NET
, "Minimum stack segment size, in bytes,\n\r"));
175 "needed to load and run the UNDI= 0x%X \n\r",
176 DisplayPointer
->StackSize
)
180 "UNDI runtime code and data = 0x%X\n\r",
181 DisplayPointer
->DataSize
)
185 "Segment size = 0x%X\n\r",
186 DisplayPointer
->CodeSize
)
190 "\n\rBus Type = %c%c%c%c\n\r",
191 DisplayPointer
->BusType
[0],
192 DisplayPointer
->BusType
[1],
193 DisplayPointer
->BusType
[2],
194 DisplayPointer
->BusType
[3])
201 @param PxeTable Point to PXE table structure
209 PXE_T
*DisplayPointer
;
213 DisplayPointer
= (PXE_T
*) PxeTable
;
214 Dptr
= (UINT8
*) PxeTable
;
216 DEBUG ((DEBUG_NET
, "This is the PXE table at address 0x%X\n\r", PxeTable
));
218 DEBUG ((DEBUG_NET
, "A dump of the 0x%X bytes is:\n\r", sizeof (PXE_T
)));
220 for (Index
= 0; Index
< sizeof (PXE_T
); Index
++) {
221 if ((Index
% 0x10) == 0) {
222 DEBUG ((DEBUG_NET
, "\t\n\r"));
225 DEBUG ((DEBUG_NET
, " 0x%X ", *Dptr
++));
228 DEBUG ((DEBUG_NET
, "\n\r"));
231 "\n\rPXE %c%c%c%c%c%c\n\r",
232 DisplayPointer
->Signature
[0],
233 DisplayPointer
->Signature
[1],
234 DisplayPointer
->Signature
[2],
235 DisplayPointer
->Signature
[3])
239 "Length of this structure in bytes = 0x%X\n\r",
240 DisplayPointer
->StructLength
)
244 "Use to make byte checksum of this structure == zero is = 0x%X\n\r",
245 DisplayPointer
->StructCksum
)
249 "Structure format revision number = 0x%X\n\r",
250 DisplayPointer
->StructRev
)
254 "Must be zero, is equal to 0x%X\n\r",
255 DisplayPointer
->Reserved1
)
259 "Far pointer to UNDI ROMID = 0x%X\n\r",
260 (UINT32
) (DisplayPointer
->Undi
.Segment
<< 0x4 | DisplayPointer
->Undi
.Offset
))
264 "Far pointer to base-code ROMID = 0x%X\n\r",
265 (UINT32
) ((DisplayPointer
->Base
.Segment
<< 0x04) | DisplayPointer
->Base
.Offset
))
267 DEBUG ((DEBUG_NET
, "16bit stack segment API entry point. This will be seg:off in \n\r"));
270 "real mode and sel:off in 16:16 protected mode = 0x%X:0x%X\n\r",
271 DisplayPointer
->EntryPointSP
.Segment
,
272 DisplayPointer
->EntryPointSP
.Offset
)
275 DEBUG ((DEBUG_NET
, "\n\tNOTE to the implementer\n\tThis is the entry to use for call-ins\n\r"));
277 DEBUG ((DEBUG_NET
, "32bit stack Segment API entry point. This will be sel:off. \n\r"));
280 "In real mode, sel == 0 = 0x%X:0x%X\n\r",
281 DisplayPointer
->EntryPointESP
.Segment
,
282 DisplayPointer
->EntryPointESP
.Offset
)
286 "Reserved2 value, must be zero, is equal to 0x%X\n\r",
287 DisplayPointer
->Reserved2
)
291 "Number of segment descriptors in this structur = 0x%X\n\r",
292 (UINT8
) DisplayPointer
->SegDescCnt
)
296 "First segment descriptor in GDT assigned to PXE = 0x%X\n\r",
297 (UINT16
) DisplayPointer
->FirstSelector
)
301 "The Stack is \n\r\tSegment Addr = 0x%X\n\r\tPhysical Addr = 0x%X\n\r\tSeg Size = 0x%X\n\r",
302 (UINT16
) DisplayPointer
->Stack
.Seg_Addr
,
303 (UINT32
) DisplayPointer
->Stack
.Phy_Addr
,
304 (UINT16
) DisplayPointer
->Stack
.Seg_Size
)
308 "The UNDIData is \n\r\tSegment Addr = 0x%X\n\r\tPhysical Addr = 0x%X\n\r\tSeg Size = 0x%X\n\r",
309 (UINT16
) DisplayPointer
->UNDIData
.Seg_Addr
,
310 (UINT32
) DisplayPointer
->UNDIData
.Phy_Addr
,
311 (UINT16
) DisplayPointer
->UNDIData
.Seg_Size
)
315 "The UNDICodeWrite is \n\r\tSegment Addr = 0x%X\n\r\tPhysical Addr = 0x%X\n\r\tSeg Size = 0x%X\n\r",
316 (UINT16
) DisplayPointer
->UNDICode
.Seg_Addr
,
317 (UINT32
) DisplayPointer
->UNDICode
.Phy_Addr
,
318 (UINT16
) DisplayPointer
->UNDICode
.Seg_Size
)
322 "The Stack is \n\r\tSegment Addr = 0x%X\n\r\tPhysical Addr = 0x%X\n\r\tSeg Size = 0x%X\n\r",
323 (UINT16
) DisplayPointer
->UNDICodeWrite
.Seg_Addr
,
324 (UINT32
) DisplayPointer
->UNDICodeWrite
.Phy_Addr
,
325 (UINT16
) DisplayPointer
->UNDICodeWrite
.Seg_Size
)
329 "The BC_Data is \n\r\tSegment Addr = 0x%X\n\r\tPhysical Addr = 0x%X\n\r\tSeg Size = 0x%X\n\r",
330 (UINT16
) DisplayPointer
->BC_Data
.Seg_Addr
,
331 (UINT32
) DisplayPointer
->BC_Data
.Phy_Addr
,
332 (UINT16
) DisplayPointer
->BC_Data
.Seg_Size
)
336 "The BC_Code is \n\r\tSegment Addr = 0x%X\n\r\tPhysical Addr = 0x%X\n\r\tSeg Size = 0x%X\n\r",
337 (UINT16
) DisplayPointer
->BC_Code
.Seg_Addr
,
338 (UINT32
) DisplayPointer
->BC_Code
.Phy_Addr
,
339 (UINT16
) DisplayPointer
->BC_Code
.Seg_Size
)
343 "The BC_CodeWrite is \n\r\tSegment Addr = 0x%X\n\r\tPhysical Addr = 0x%X\n\r\tSeg Size = 0x%X\n\r",
344 (UINT16
) DisplayPointer
->BC_CodeWrite
.Seg_Addr
,
345 (UINT32
) DisplayPointer
->BC_CodeWrite
.Phy_Addr
,
346 (UINT16
) DisplayPointer
->BC_CodeWrite
.Seg_Size
)
353 @param PxenvTable Point to PXENV
361 PXENV_T
*DisplayPointer
;
363 DisplayPointer
= (PXENV_T
*) PxenvTable
;
367 "\n\rPXENV+ %c%c%c%c%c%c\n\r",
368 DisplayPointer
->Signature
[0],
369 DisplayPointer
->Signature
[1],
370 DisplayPointer
->Signature
[2],
371 DisplayPointer
->Signature
[3],
372 DisplayPointer
->Signature
[4],
373 DisplayPointer
->Signature
[5])
378 "PXE version number. \n\r\tLSB is minor version. \n\r\tMSB is major version = 0x%X\n\r",
379 DisplayPointer
->Version
)
383 "Length of PXE-2.0 Entry Point structure in bytes = 0x%X\n\r",
384 DisplayPointer
->StructLength
)
386 DEBUG ((DEBUG_NET
, "Used to make structure checksum equal zero is now = 0x%X\n\r", DisplayPointer
->StructCksum
));
387 DEBUG ((DEBUG_NET
, "Real mode API entry point segment:Offset. = 0x%X\n\r", DisplayPointer
->RMEntry
));
388 DEBUG ((DEBUG_NET
, "Protected mode API entry point = 0x%X\n\r", DisplayPointer
->PMEntryOff
));
389 DEBUG ((DEBUG_NET
, " segment:Offset. This will always be zero. \n\r"));
390 DEBUG ((DEBUG_NET
, "Protected mode API calls = 0x%X\n\r", DisplayPointer
->PMEntrySeg
));
391 DEBUG ((DEBUG_NET
, "Real mode stack segment = 0x%X\n\r", DisplayPointer
->StackSeg
));
392 DEBUG ((DEBUG_NET
, "Stack segment size in bytes = 0x%X\n\r", DisplayPointer
->StackSize
));
393 DEBUG ((DEBUG_NET
, "Real mode base-code code segment = 0x%X\n\r", DisplayPointer
->BaseCodeSeg
));
394 DEBUG ((DEBUG_NET
, "Base-code code segment size = 0x%X\n\r", DisplayPointer
->BaseCodeSize
));
395 DEBUG ((DEBUG_NET
, "Real mode base-code data segment = 0x%X\n\r", DisplayPointer
->BaseDataSeg
));
396 DEBUG ((DEBUG_NET
, "Base-code data segment size = 0x%X\n\r", DisplayPointer
->BaseDataSize
));
400 "UNDI code segment size in bytes = 0x%X\n\r",
401 DisplayPointer
->UNDICodeSize
)
405 "Real mode segment:Offset pointer \n\r\tto PXE Runtime ID structure, address = 0x%X\n\r",
406 DisplayPointer
->RuntimePtr
)
411 "From above, we have a linear address of 0x%X\n\r",
414 ((UINT32
)(UINTN
)(DisplayPointer
->RuntimePtr
) & 0xFFFF) +
415 (((UINT32
)(UINTN
)(DisplayPointer
->RuntimePtr
) & 0xFFFF0000) >> 12)
422 #define OPTION_ROM_PTR ((OPTION_ROM_HEADER *) RomAddress)
425 If available, launch the BaseCode from a NIC option ROM.
426 This should install the !PXE and PXENV+ structures in memory for
430 @param SimpleNetworkDevice Simple network device instance
431 @param RomAddress The ROM base address for NIC rom.
433 @retval EFI_NOT_FOUND The check sum does not match
434 @retval EFI_NOT_FOUND Rom ID offset is wrong
435 @retval EFI_NOT_FOUND No Rom ID structure is found
439 EFI_SIMPLE_NETWORK_DEV
*SimpleNetworkDevice
,
444 EFI_IA32_REGISTER_SET InOutRegs
;
445 UNDI_ROMID_T
*RomIdTableAddress
;
446 UNDI_LOADER_T
*UndiLoaderTable
;
448 UINT16
*StackPointer
;
459 DEBUG ((DEBUG_NET
, "\n\r\n\rCheck for the UNDI ROMID Signature\n\r"));
462 // paranoia - check structures for validity
464 RomLength
= OPTION_ROM_PTR
->ROMlength
<< 9;
465 if (CalculateSum8 ((UINT8
*) RomAddress
, RomLength
) != 0) {
466 DEBUG ((DEBUG_ERROR
, "ROM Header Checksum Error\n\r"));
467 return EFI_NOT_FOUND
;
470 RomIdTableAddress
= (UNDI_ROMID_T
*) (RomAddress
+ OPTION_ROM_PTR
->PxeRomIdOffset
);
472 if (((UINT32
)OPTION_ROM_PTR
->PxeRomIdOffset
+ RomIdTableAddress
->StructLength
) > RomLength
) {
473 DEBUG ((DEBUG_ERROR
, "ROM ID Offset Error\n\r"));
474 return EFI_NOT_FOUND
;
477 // see if this is a header for an UNDI ROM ID structure (vs. a $BC$ or BUSD type)
479 if (CompareMem (RomIdTableAddress
->Signature
, UNDI_ROMID_SIG
, sizeof RomIdTableAddress
->Signature
) != 0) {
480 DEBUG ((DEBUG_ERROR
, "No ROM ID Structure found....\n\r"));
481 return EFI_NOT_FOUND
;
483 // its not - keep looking
487 if (CalculateSum8 ((UINT8
*) RomIdTableAddress
, RomIdTableAddress
->StructLength
) != 0) {
488 DEBUG ((DEBUG_ERROR
, "ROM ID Checksum Error\n\r"));
489 return EFI_NOT_FOUND
;
492 Print_ROMID_Table (RomIdTableAddress
);
496 "The ROM ID is located at 0x%X\n\r",
502 "With an UNDI Loader located at 0x%X\n\r",
503 RomAddress
+ RomIdTableAddress
->UNDI_Loader
)
507 // found an UNDI ROM ID structure
509 SimpleNetworkDevice
->Nii
.ImageAddr
= RomAddress
;
510 SimpleNetworkDevice
->Nii
.ImageSize
= RomLength
;
511 SimpleNetworkDevice
->Nii
.MajorVer
= RomIdTableAddress
->UNDI_Rev
[2];
512 SimpleNetworkDevice
->Nii
.MinorVer
= RomIdTableAddress
->UNDI_Rev
[1];
514 DEBUG ((DEBUG_NET
, "Allocate area for the UNDI_LOADER_T structure\n\r"));
516 // Allocate 1 page below 1MB to put real mode thunk code in
518 // Undi Loader Table is a PXE Specification prescribed data structure
519 // that is used to transfer information into and out of the Undi layer.
520 // Note how it must be located below 1 MB.
522 SimpleNetworkDevice
->UndiLoaderTablePages
= EFI_SIZE_TO_PAGES (PARAGRAPH_SIZE
+ sizeof (UNDI_LOADER_T
));
523 Status
= BiosSnp16AllocatePagesBelowOneMb (
524 SimpleNetworkDevice
->UndiLoaderTablePages
,
525 &SimpleNetworkDevice
->UndiLoaderTable
527 if (EFI_ERROR (Status
)) {
528 DEBUG ((DEBUG_ERROR
, "We had a failure in AllocatePages, status code = 0x%X\n", Status
));
529 return EFI_OUT_OF_RESOURCES
;
532 UndiLoaderTable
= SimpleNetworkDevice
->UndiLoaderTable
;
534 DEBUG ((DEBUG_NET
, "Allocate area for the real-mode stack whose sole purpose\n\r"));
535 DEBUG ((DEBUG_NET
, "in life right now is to store a SEG:OFFSET combo pair that\n\r"));
536 DEBUG ((DEBUG_NET
, "points to an Undi_Loader_t table structure\n\r"));
539 Status
= gBS
->AllocatePool (EfiLoaderData
, Size
, &Buffer
);
540 if (EFI_ERROR (Status
)) {
544 // Now we want to put a pointer to the Under Loader Table in our MemPage
545 // Buffer. This will be the argument stack for the call into the Undi Loader
547 StackPointer
= (UINT16
*) Buffer
;
548 *StackPointer
++ = TO_OFFSET (UndiLoaderTable
);
552 *StackPointer
++ = TO_SEGMENT (UndiLoaderTable
);
556 StackPointer
= (UINT16
*) Buffer
;
558 // reset the stack pointer
562 "After the fixups, the stack pointer is 0x%X\n\r",
563 (UINT64
)(UINTN
) StackPointer
)
567 // Allocate memory for the Deployed UNDI.
568 // The UNDI is essentially telling us how much space it needs, and
569 // it is up to the EFI driver to allocate sufficient, boot-time
570 // persistent resources for the call
572 SimpleNetworkDevice
->DestinationDataSegmentPages
= EFI_SIZE_TO_PAGES (RomIdTableAddress
->DataSize
);
573 Status
= BiosSnp16AllocatePagesBelowOneMb (
574 SimpleNetworkDevice
->DestinationDataSegmentPages
,
575 &SimpleNetworkDevice
->DestinationDataSegment
577 if (EFI_ERROR (Status
)) {
578 DEBUG ((DEBUG_ERROR
, "We had a failure in AllocatePages, status code = 0x%X\n", Status
));
582 UndiLoaderTable
->Undi_Ds
= (UINT16
) ((UINTN
) SimpleNetworkDevice
->DestinationDataSegment
>> 4);
585 // Allocate memory for the Deployed UNDI stack
586 // The UNDI is essentially telling us how much space it needs, and
587 // it is up to the EFI driver to allocate sufficient, boot-time
588 // persistent resources for the call
590 SimpleNetworkDevice
->DestinationStackSegmentPages
= EFI_SIZE_TO_PAGES (RomIdTableAddress
->StackSize
);
591 Status
= BiosSnp16AllocatePagesBelowOneMb (
592 SimpleNetworkDevice
->DestinationStackSegmentPages
,
593 &SimpleNetworkDevice
->DestinationStackSegment
595 if (EFI_ERROR (Status
)) {
596 DEBUG ((DEBUG_ERROR
, "We had a failure in AllocatePages, status code = 0x%X\n", Status
));
600 // Allocate memory for the Deployed UNDI.
601 // The UNDI is essentially telling us how much space it needs, and
602 // it is up to the EFI driver to allocate sufficient, boot-time
603 // persistent resources for the call
605 SimpleNetworkDevice
->DestinationCodeSegmentPages
= EFI_SIZE_TO_PAGES (RomIdTableAddress
->CodeSize
);
606 Status
= BiosSnp16AllocatePagesBelowOneMb (
607 SimpleNetworkDevice
->DestinationCodeSegmentPages
,
608 &SimpleNetworkDevice
->DestinationCodeSegment
610 if (EFI_ERROR (Status
)) {
611 DEBUG ((DEBUG_ERROR
, "We had a failure in AllocatePages, status code = 0x%X\n", Status
));
615 UndiLoaderTable
->Undi_Cs
= (UINT16
) ((UINTN
) SimpleNetworkDevice
->DestinationCodeSegment
>> 4);
618 // these are in the Input and Output Parameter to be sent to the UNDI Loader code
620 UndiLoaderTable
->Status
= 0xAA55;
622 // -------------------- Changed by Michael_Huang@3Com.com -----------------
623 // UndiLoaderTable->_AX is AX value when UNDI ROM is initialized by BIOS, it is the PCI bus device
624 // function of the NIC. Please refer to PXE Spec for detail info.
626 // UndiLoaderTable->Ax = 0x0;
627 // -----------------------------------------------------------------------
629 SimpleNetworkDevice
->PciIo
->GetLocation (
630 SimpleNetworkDevice
->PciIo
,
636 UndiLoaderTable
->Ax
= (UINT16
) ((Bus
<< 0x8) | (Device
<< 0x3) | (Function
));
637 UndiLoaderTable
->Bx
= 0x0;
638 UndiLoaderTable
->Dx
= 0x0;
639 UndiLoaderTable
->Di
= 0x0;
640 UndiLoaderTable
->Es
= 0x0;
643 // set these OUT values to zero in order to ensure that
644 // uninitialized memory is not mistaken for display data
646 UndiLoaderTable
->PXEptr
.Offset
= 0;
647 UndiLoaderTable
->PXEptr
.Segment
= 0;
648 UndiLoaderTable
->PXENVptr
.Segment
= 0;
649 UndiLoaderTable
->PXENVptr
.Offset
= 0;
653 "The NIC is located at Bus 0x%X, Device 0x%X, Function 0x%X\n\r",
660 // These are the values that set up the ACTUAL IA32 machine state, whether in
661 // Real16 in EFI32 or the IVE for IA64
662 // register values are unused except for CS:IP and SS:SP
676 DEBUG ((DEBUG_NET
, "The way this game works is that the SS:SP +4 should point\n\r"));
677 DEBUG ((DEBUG_NET
, "to the contents of the UndiLoaderTable\n\r"));
680 "The Undi Loader Table is at address = 0x%X\n\r",
681 (UINT32
)(UINTN
) UndiLoaderTable
)
685 "The segment and offsets are 0x%X and 0x%X, resp\n",
686 TO_SEGMENT (UndiLoaderTable
),
687 TO_OFFSET (UndiLoaderTable
))
692 "The Linear Address of the UNDI Loader entry is 0x%X\n",
693 RomAddress
+ RomIdTableAddress
->UNDI_Loader
)
698 "The Address offset of the UNDI Loader entry is 0x%X\n",
699 RomIdTableAddress
->UNDI_Loader
)
702 DEBUG ((DEBUG_NET
, "Before the call, we have...\n\r"));
703 Print_Undi_Loader_Table (UndiLoaderTable
);
705 Segment
= ((UINT16
) (RShiftU64 (RomAddress
, 4) & 0xFFFF));
706 DEBUG ((DEBUG_NET
, "The Segment of the call is 0x%X\n\r", Segment
));
709 // make the call into the UNDI Code
711 DEBUG ((DEBUG_INIT
, "Make the call into the UNDI code now\n\r"));
713 DEBUG ((DEBUG_NET
, "\nThe 20-BIt address of the Call, and the location \n\r"));
714 DEBUG ((DEBUG_NET
, "\twhere we should be able to set a breakpoint is \n\r"));
717 "\t\t0x%X, from SEG:OFF 0x%X:0x%X\n\r\n\r",
718 Segment
* 0x10 + RomIdTableAddress
->UNDI_Loader
,
720 RomIdTableAddress
->UNDI_Loader
)
723 ThunkFailed
= SimpleNetworkDevice
->LegacyBios
->FarCall86 (
724 SimpleNetworkDevice
->LegacyBios
,
725 Segment
, // Input segment
726 (UINT16
) RomIdTableAddress
->UNDI_Loader
, // Offset
727 &InOutRegs
, // Ptr to Regs
728 Buffer
, // Reference to Stack
729 Size
// Size of the Stack
737 "The return code UndiLoaderTable->Status is = 0x%X\n\r",
738 UndiLoaderTable
->Status
)
742 "This error code should match eax, which is = 0x%X\n\r",
746 if ((UndiLoaderTable
->Status
!= 0) || (InOutRegs
.X
.AX
!= PXENV_EXIT_SUCCESS
)) {
747 DEBUG ((DEBUG_NET
, "LaunchBaseCode exits with error, RomAddress = 0x%X\n\r", RomAddress
));
751 DEBUG ((DEBUG_NET
, "Now returned from the UNDI code\n\r"));
753 DEBUG ((DEBUG_NET
, "After the call, we have...\n\r"));
754 Print_Undi_Loader_Table (UndiLoaderTable
);
756 DEBUG ((DEBUG_NET
, "Display the PXENV+ and !PXE tables exported by NIC\n\r"));
757 Print_PXENV_Table ((VOID
*)(((UINTN
)UndiLoaderTable
->PXENVptr
.Segment
<< 4) | UndiLoaderTable
->PXENVptr
.Offset
));
758 Print_PXE_Table ((VOID
*)(((UINTN
)UndiLoaderTable
->PXEptr
.Segment
<< 4) + UndiLoaderTable
->PXEptr
.Offset
));
760 Pxe
= (PXE_T
*)(((UINTN
)UndiLoaderTable
->PXEptr
.Segment
<< 4) + UndiLoaderTable
->PXEptr
.Offset
);
761 SimpleNetworkDevice
->Nii
.Id
= (UINT64
)(UINTN
) Pxe
;
763 gBS
->FreePool (Buffer
);
766 // paranoia - make sure a valid !PXE structure
768 if (CompareMem (Pxe
->Signature
, PXE_SIG
, sizeof Pxe
->Signature
) != 0) {
769 DEBUG ((DEBUG_ERROR
, "!PXE Structure not found....\n\r"));
770 return EFI_NOT_FOUND
;
772 // its not - keep looking
776 if (CalculateSum8 ((UINT8
*) Pxe
, Pxe
->StructLength
) != 0) {
777 DEBUG ((DEBUG_ERROR
, "!PXE Checksum Error\n\r"));
778 return EFI_NOT_FOUND
;
781 if (Pxe
->StructLength
< (UINT8
*) &Pxe
->FirstSelector
- (UINT8
*) Pxe
->Signature
) {
782 DEBUG ((DEBUG_ERROR
, "!PXE Length Error\n\r"));
783 return EFI_NOT_FOUND
;
786 if ((((UINTN
) Pxe
->Undi
.Segment
) << 4) + Pxe
->Undi
.Offset
!= (UINTN
) RomIdTableAddress
) {
787 DEBUG ((DEBUG_ERROR
, "!PXE RomId Address Error\n\r"));
788 return EFI_NOT_FOUND
;
791 // This is the magic to bind the global PXE interface
792 // This dirtiness is for non-protocol shrouded access
794 SimpleNetworkDevice
->PxeEntrySegment
= Pxe
->EntryPointSP
.Segment
;
796 if (SimpleNetworkDevice
->PxeEntrySegment
== 0) {
797 DEBUG ((DEBUG_ERROR
, "!PXE EntryPointSP segment Error\n\r"));
798 return EFI_NOT_FOUND
;
801 SimpleNetworkDevice
->PxeEntryOffset
= Pxe
->EntryPointSP
.Offset
;
805 DEBUG_NET
, "The entry point is 0x%X:0x%X\n\r", SimpleNetworkDevice
->PxeEntrySegment
, SimpleNetworkDevice
->
814 Effect the Far Call into the PXE Layer
816 Note: When using a 32-bit stack segment do not push 32-bit words onto the stack. The PXE API
817 services will not work, unless there are three 16-bit parameters pushed onto the stack.
818 push DS ;Far pointer to parameter structure
819 push offset pxe_data_call_struct ;is pushed onto stack.
820 push Index ;UINT16 is pushed onto stack.
821 call dword ptr (s_PXE ptr es:[di]).EntryPointSP
822 add sp, 6 ;Caller cleans up stack.
824 @param SimpleNetworkDevice Device instance for simple network
825 @param Table Point to parameter/retun value table for legacy far call
826 @param TableSize The size of parameter/return value table
827 @param CallIndex The index of legacy call.
833 EFI_SIMPLE_NETWORK_DEV
*SimpleNetworkDevice
,
840 EFI_IA32_REGISTER_SET InOutRegs
;
844 VOID
*MemPageAddress
;
848 DEBUG ((DEBUG_NET
, "MakePxeCall(CallIndex = %02x, Table = %X, TableSize = %d)\n", CallIndex
, Table
, TableSize
));
850 if (SimpleNetworkDevice
->PxeEntrySegment
== 0 && SimpleNetworkDevice
->PxeEntryOffset
== 0) {
851 return EFI_DEVICE_ERROR
;
854 Status
= EFI_SUCCESS
;
857 // Allocate a transient data structure for the argument table
858 // This table needs to have the input XXX_t structure copied into here.
859 // The PXE UNDI can only grab this table when it's below one-MB, and
860 // this implementation will not try to push this table on the stack
861 // (although this is a possible optimization path since EFI always allocates
862 // 4K as a minimum page size...............)
864 Status
= BiosSnp16AllocatePagesBelowOneMb (
865 TableSize
/ EFI_PAGE_SIZE
+ 1,
868 if (EFI_ERROR (Status
)) {
869 DEBUG ((DEBUG_ERROR
, "We had a failure in AllocatePages, status code = 0x%X\n", Status
));
873 // Copy the > 1MB pool table to a sub-1MB buffer
875 CopyMem (MemPageAddress
, Table
, TableSize
);
878 // Allocate space for IA-32 register context
880 ZeroMem (&InOutRegs
, sizeof (InOutRegs
));
881 InOutRegs
.X
.ES
= SimpleNetworkDevice
->PxeEntrySegment
;
882 InOutRegs
.X
.DI
= SimpleNetworkDevice
->PxeEntryOffset
;
885 // The game here is to build the stack which will subsequently
886 // get copied down below 1 MB by the FarCall primitive.
887 // This is now our working stack
890 Status
= gBS
->AllocatePool (
891 EfiRuntimeServicesData
,
895 if (EFI_ERROR (Status
)) {
899 BPtr
= (UINT16
*) Buffer
;
904 *BPtr
++ = TO_OFFSET (MemPageAddress
);
905 *BPtr
++ = TO_SEGMENT (MemPageAddress
);
907 DEBUG ((DEBUG_NET
, "State before FarCall86\n"));
908 DEBUG ((DEBUG_NET
, "The Buffer is at 0x%X\n\r", Buffer
));
909 BPtr
= (UINT16
*) Buffer
;
910 DEBUG ((DEBUG_NET
, " Buffer = %04X %04X %04X", *BPtr
, *(BPtr
+ 1), *(BPtr
+ 2)));
911 DEBUG ((DEBUG_NET
, " MemPage = "));
912 for (Index
= 0; Index
< TableSize
; Index
++) {
913 DEBUG ((DEBUG_NET
, " %02x", *((UINT8
*) MemPageAddress
+ Index
)));
916 DEBUG ((DEBUG_NET
, "\n"));
918 ThunkFailed
= SimpleNetworkDevice
->LegacyBios
->FarCall86 (
919 SimpleNetworkDevice
->LegacyBios
,
920 SimpleNetworkDevice
->PxeEntrySegment
, // Input segment
921 SimpleNetworkDevice
->PxeEntryOffset
,
922 &InOutRegs
, // Ptr to Regs
923 Buffer
, // Reference to Stack
924 6 // Size of the Stack
930 DEBUG ((DEBUG_NET
, "State after FarCall86\n"));
931 DEBUG ((DEBUG_NET
, "The Buffer is at 0x%X\n\r", Buffer
));
932 BPtr
= (UINT16
*) Buffer
;
933 DEBUG ((DEBUG_NET
, " Buffer = %04X %04X %04X", *BPtr
, *(BPtr
+ 1), *(BPtr
+ 2)));
934 DEBUG ((DEBUG_NET
, " MemPage = "));
935 for (Index
= 0; Index
< TableSize
; Index
++) {
936 DEBUG ((DEBUG_NET
, " %02x", *((UINT8
*) MemPageAddress
+ Index
)));
939 DEBUG ((DEBUG_NET
, "\n"));
942 // Copy the sub 1MB table to > 1MB table
944 CopyMem (Table
, MemPageAddress
, TableSize
);
947 // For PXE UNDI call, AX contains the return status.
948 // Convert the PXE UNDI Status to EFI_STATUS type
950 if (InOutRegs
.X
.AX
== PXENV_EXIT_SUCCESS
) {
951 Status
= EFI_SUCCESS
;
953 Status
= EFI_DEVICE_ERROR
;
958 gBS
->FreePool (Buffer
);
959 gBS
->FreePages ((EFI_PHYSICAL_ADDRESS
) (UINTN
) MemPageAddress
, TableSize
/ EFI_PAGE_SIZE
+ 1);