Home | History | Annotate | Download | only in Snp16Dxe
      1 /** @file
      2   Helper Routines that use a PXE-enabled NIC option ROM.
      3 
      4 Copyright (c) 1999 - 2014, Intel Corporation. All rights reserved.<BR>
      5 
      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
     11 
     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.
     14 
     15 **/
     16 
     17 #include "BiosSnp16.h"
     18 
     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
     23 
     24 #pragma pack(1)
     25 typedef struct {
     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
     33 } OPTION_ROM_HEADER;
     34 #pragma pack()
     35 
     36 UINT32 CachedVectorAddress[0x100];
     37 
     38 /**
     39  Cache Interrupt verctor address converted from IVT number.
     40 
     41  @param VectorNumber  IVT number
     42 
     43  @retval EFI_SUCCESS Success to operation.
     44 **/
     45 EFI_STATUS
     46 CacheVectorAddress (
     47   UINT8   VectorNumber
     48   )
     49 {
     50   UINT32  *Address;
     51 
     52   Address                          = (UINT32 *)(UINTN) (IVT_BASE + VectorNumber * 4);
     53   CachedVectorAddress[VectorNumber] = *Address;
     54   return EFI_SUCCESS;
     55 }
     56 
     57 /**
     58  Get interrupt vector address according to IVT number.
     59 
     60  @param VectorNumber    Given IVT number
     61 
     62  @return cached interrupt vector address.
     63 **/
     64 EFI_STATUS
     65 RestoreCachedVectorAddress (
     66   UINT8   VectorNumber
     67   )
     68 {
     69   UINT32  *Address;
     70 
     71   Address  = (UINT32 *)(UINTN) (IVT_BASE + VectorNumber * 4);
     72   *Address = CachedVectorAddress[VectorNumber];
     73   return EFI_SUCCESS;
     74 }
     75 
     76 /**
     77  Print Undi loader table.
     78 
     79  @param UndiLoaderStructure Point to Undi Loader table structure.
     80 
     81 **/
     82 VOID
     83 Print_Undi_Loader_Table (
     84   VOID *UndiLoaderStructure
     85   )
     86 {
     87   UNDI_LOADER_T *DisplayPointer;
     88 
     89   DisplayPointer = (UNDI_LOADER_T *) UndiLoaderStructure;
     90 
     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));
     93 
     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));
    106 }
    107 
    108 /**
    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
    113   are not using PXE.
    114 
    115   @param RomIDStructure Point to RomID structure.
    116 
    117 **/
    118 VOID
    119 Print_ROMID_Table (
    120   IN VOID *RomIDStructure
    121   )
    122 {
    123   UNDI_ROMID_T  *DisplayPointer;
    124 
    125   DisplayPointer = (UNDI_ROMID_T *) RomIDStructure;
    126 
    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));
    129 
    130   DEBUG (
    131     (DEBUG_NET,
    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])
    137     );
    138 
    139   DEBUG (
    140     (DEBUG_NET,
    141     "Length of this structure in bytes = 0x%X\n\r",
    142     DisplayPointer->StructLength)
    143     );
    144   DEBUG (
    145     (DEBUG_NET,
    146     "Use to make byte checksum of this structure == zero is = 0x%X\n\r",
    147     DisplayPointer->StructCksum)
    148     );
    149   DEBUG (
    150     (DEBUG_NET,
    151     "Structure format revision number= 0x%X\n\r",
    152     DisplayPointer->StructRev)
    153     );
    154   DEBUG (
    155     (DEBUG_NET,
    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])
    160     );
    161   DEBUG (
    162     (DEBUG_NET,
    163     "Offset of UNDI loader routine in the option ROM image= 0x%X\n\r",
    164     DisplayPointer->UNDI_Loader)
    165     );
    166   DEBUG ((DEBUG_NET, "From the data above, the absolute entry point of the UNDI loader is\n\r"));
    167   DEBUG (
    168     (DEBUG_NET,
    169     "\tat address 0x%X\n\r",
    170     (UINT32) (DisplayPointer->UNDI_Loader + ((UINT32) (UINTN)(DisplayPointer - 0x20) & 0xFFFF0)))
    171     );
    172   DEBUG ((DEBUG_NET, "Minimum stack segment size, in bytes,\n\r"));
    173   DEBUG (
    174     (DEBUG_NET,
    175     "needed to load and run the UNDI= 0x%X \n\r",
    176     DisplayPointer->StackSize)
    177     );
    178   DEBUG (
    179     (DEBUG_NET,
    180     "UNDI runtime code and data = 0x%X\n\r",
    181     DisplayPointer->DataSize)
    182     );
    183   DEBUG (
    184     (DEBUG_NET,
    185     "Segment size = 0x%X\n\r",
    186     DisplayPointer->CodeSize)
    187     );
    188   DEBUG (
    189     (DEBUG_NET,
    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])
    195     );
    196 }
    197 
    198 /**
    199   Print PXE table.
    200 
    201   @param PxeTable Point to PXE table structure
    202 
    203 **/
    204 VOID
    205 Print_PXE_Table (
    206   IN VOID*  PxeTable
    207   )
    208 {
    209   PXE_T *DisplayPointer;
    210   UINTN Index;
    211   UINT8 *Dptr;
    212 
    213   DisplayPointer  = (PXE_T *) PxeTable;
    214   Dptr            = (UINT8 *) PxeTable;
    215 
    216   DEBUG ((DEBUG_NET, "This is the PXE table at address 0x%X\n\r", PxeTable));
    217 
    218   DEBUG ((DEBUG_NET, "A dump of the 0x%X bytes is:\n\r", sizeof (PXE_T)));
    219 
    220   for (Index = 0; Index < sizeof (PXE_T); Index++) {
    221     if ((Index % 0x10) == 0) {
    222       DEBUG ((DEBUG_NET, "\t\n\r"));
    223     }
    224 
    225     DEBUG ((DEBUG_NET, " 0x%X  ", *Dptr++));
    226   }
    227 
    228   DEBUG ((DEBUG_NET, "\n\r"));
    229   DEBUG (
    230     (DEBUG_NET,
    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])
    236     );
    237   DEBUG (
    238     (DEBUG_NET,
    239     "Length of this structure in bytes = 0x%X\n\r",
    240     DisplayPointer->StructLength)
    241     );
    242   DEBUG (
    243     (DEBUG_NET,
    244     "Use to make byte checksum of this  structure == zero is = 0x%X\n\r",
    245     DisplayPointer->StructCksum)
    246     );
    247   DEBUG (
    248     (DEBUG_NET,
    249     "Structure format revision number = 0x%X\n\r",
    250     DisplayPointer->StructRev)
    251     );
    252   DEBUG (
    253     (DEBUG_NET,
    254     "Must be zero, is equal to 0x%X\n\r",
    255     DisplayPointer->Reserved1)
    256     );
    257   DEBUG (
    258     (DEBUG_NET,
    259     "Far pointer to UNDI ROMID = 0x%X\n\r",
    260     (UINT32) (DisplayPointer->Undi.Segment << 0x4 | DisplayPointer->Undi.Offset))
    261     );
    262   DEBUG (
    263     (DEBUG_NET,
    264     "Far pointer to base-code ROMID = 0x%X\n\r",
    265     (UINT32) ((DisplayPointer->Base.Segment << 0x04) | DisplayPointer->Base.Offset))
    266     );
    267   DEBUG ((DEBUG_NET, "16bit stack segment API entry point.  This will be seg:off in \n\r"));
    268   DEBUG (
    269     (DEBUG_NET,
    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)
    273     );
    274 
    275   DEBUG ((DEBUG_NET, "\n\tNOTE to the implementer\n\tThis is the entry to use for call-ins\n\r"));
    276 
    277   DEBUG ((DEBUG_NET, "32bit stack Segment API entry point.  This will be sel:off. \n\r"));
    278   DEBUG (
    279     (DEBUG_NET,
    280     "In real mode, sel == 0 = 0x%X:0x%X\n\r",
    281     DisplayPointer->EntryPointESP.Segment,
    282     DisplayPointer->EntryPointESP.Offset)
    283     );
    284   DEBUG (
    285     (DEBUG_NET,
    286     "Reserved2 value, must be zero, is equal to 0x%X\n\r",
    287     DisplayPointer->Reserved2)
    288     );
    289   DEBUG (
    290     (DEBUG_NET,
    291     "Number of segment descriptors in this structur = 0x%X\n\r",
    292     (UINT8) DisplayPointer->SegDescCnt)
    293     );
    294   DEBUG (
    295     (DEBUG_NET,
    296     "First segment descriptor in GDT assigned to PXE = 0x%X\n\r",
    297     (UINT16) DisplayPointer->FirstSelector)
    298     );
    299   DEBUG (
    300     (DEBUG_NET,
    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)
    305     );
    306   DEBUG (
    307     (DEBUG_NET,
    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)
    312     );
    313   DEBUG (
    314     (DEBUG_NET,
    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)
    319     );
    320   DEBUG (
    321     (DEBUG_NET,
    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)
    326     );
    327   DEBUG (
    328     (DEBUG_NET,
    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)
    333     );
    334   DEBUG (
    335     (DEBUG_NET,
    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)
    340     );
    341   DEBUG (
    342     (DEBUG_NET,
    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)
    347     );
    348 }
    349 
    350 /**
    351   Print PXENV table.
    352 
    353   @param PxenvTable Point to PXENV
    354 
    355 **/
    356 VOID
    357 Print_PXENV_Table (
    358   IN VOID *PxenvTable
    359   )
    360 {
    361   PXENV_T *DisplayPointer;
    362 
    363   DisplayPointer = (PXENV_T *) PxenvTable;
    364 
    365   DEBUG (
    366     (DEBUG_NET,
    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])
    374     );
    375 
    376   DEBUG (
    377     (DEBUG_NET,
    378     "PXE version number.  \n\r\tLSB is minor version.  \n\r\tMSB is major version = 0x%X\n\r",
    379     DisplayPointer->Version)
    380     );
    381   DEBUG (
    382     (DEBUG_NET,
    383     "Length of PXE-2.0 Entry Point structure in bytes = 0x%X\n\r",
    384     DisplayPointer->StructLength)
    385     );
    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));
    397 
    398   DEBUG (
    399     (DEBUG_NET,
    400     "UNDI code segment size in bytes = 0x%X\n\r",
    401     DisplayPointer->UNDICodeSize)
    402     );
    403   DEBUG (
    404     (DEBUG_NET,
    405     "Real mode segment:Offset pointer \n\r\tto PXE Runtime ID structure, address = 0x%X\n\r",
    406     DisplayPointer->RuntimePtr)
    407     );
    408   DEBUG (
    409     (
    410     DEBUG_NET,
    411     "From above, we have a linear address of 0x%X\n\r",
    412     (UINT32)
    413     (
    414     ((UINT32)(UINTN)(DisplayPointer->RuntimePtr) & 0xFFFF) +
    415     (((UINT32)(UINTN)(DisplayPointer->RuntimePtr) & 0xFFFF0000) >> 12)
    416     )
    417     )
    418     );
    419 }
    420 
    421 
    422 #define OPTION_ROM_PTR  ((OPTION_ROM_HEADER *) RomAddress)
    423 
    424 /**
    425   If available, launch the BaseCode from a NIC option ROM.
    426   This should install the !PXE and PXENV+ structures in memory for
    427   subsequent use.
    428 
    429 
    430   @param SimpleNetworkDevice    Simple network device instance
    431   @param RomAddress             The ROM base address for NIC rom.
    432 
    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
    436 **/
    437 EFI_STATUS
    438 LaunchBaseCode (
    439   EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice,
    440   UINTN                   RomAddress
    441   )
    442 {
    443   EFI_STATUS            Status;
    444   EFI_IA32_REGISTER_SET InOutRegs;
    445   UNDI_ROMID_T          *RomIdTableAddress;
    446   UNDI_LOADER_T         *UndiLoaderTable;
    447   UINT16                Segment;
    448   UINT16                *StackPointer;
    449   VOID                  *Buffer;
    450   UINTN                 Size;
    451   PXE_T                 *Pxe;
    452   UINT32                RomLength;
    453   UINTN                 PciSegment;
    454   UINTN                 Bus;
    455   UINTN                 Device;
    456   UINTN                 Function;
    457   BOOLEAN               ThunkFailed;
    458 
    459   DEBUG ((DEBUG_NET, "\n\r\n\rCheck for the UNDI ROMID Signature\n\r"));
    460 
    461   //
    462   // paranoia - check structures for validity
    463   //
    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;
    468   }
    469 
    470   RomIdTableAddress = (UNDI_ROMID_T *) (RomAddress + OPTION_ROM_PTR->PxeRomIdOffset);
    471 
    472   if ((UINTN) (OPTION_ROM_PTR->PxeRomIdOffset + RomIdTableAddress->StructLength) > RomLength) {
    473     DEBUG ((DEBUG_ERROR, "ROM ID Offset Error\n\r"));
    474     return EFI_NOT_FOUND;
    475   }
    476   //
    477   // see if this is a header for an UNDI ROM ID structure (vs. a $BC$ or BUSD type)
    478   //
    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;
    482     //
    483     // its not - keep looking
    484     //
    485   }
    486 
    487   if (CalculateSum8 ((UINT8 *) RomIdTableAddress, RomIdTableAddress->StructLength) != 0) {
    488     DEBUG ((DEBUG_ERROR, "ROM ID Checksum Error\n\r"));
    489     return EFI_NOT_FOUND;
    490   }
    491 
    492   Print_ROMID_Table (RomIdTableAddress);
    493 
    494   DEBUG (
    495     (DEBUG_NET,
    496     "The ROM ID is located at 0x%X\n\r",
    497     RomIdTableAddress)
    498     );
    499 
    500   DEBUG (
    501     (DEBUG_NET,
    502     "With an UNDI Loader located at 0x%X\n\r",
    503     RomAddress + RomIdTableAddress->UNDI_Loader)
    504     );
    505 
    506   //
    507   // found an UNDI ROM ID structure
    508   //
    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];
    513 
    514   DEBUG ((DEBUG_NET, "Allocate area for the UNDI_LOADER_T structure\n\r"));
    515   //
    516   // Allocate 1 page below 1MB to put real mode thunk code in
    517   //
    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.
    521   //
    522   SimpleNetworkDevice->UndiLoaderTablePages = EFI_SIZE_TO_PAGES (PARAGRAPH_SIZE + sizeof (UNDI_LOADER_T));
    523   Status = BiosSnp16AllocatePagesBelowOneMb (
    524             SimpleNetworkDevice->UndiLoaderTablePages,
    525             &SimpleNetworkDevice->UndiLoaderTable
    526             );
    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;
    530   }
    531 
    532   UndiLoaderTable = SimpleNetworkDevice->UndiLoaderTable;
    533 
    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"));
    537 
    538   Size    = 0x100;
    539   Status  = gBS->AllocatePool (EfiLoaderData, Size, &Buffer);
    540   if (EFI_ERROR (Status)) {
    541     return Status;
    542   }
    543   //
    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
    546   //
    547   StackPointer    = (UINT16 *) Buffer;
    548   *StackPointer++ = TO_OFFSET (UndiLoaderTable);
    549   //
    550   // push the OFFSET
    551   //
    552   *StackPointer++ = TO_SEGMENT (UndiLoaderTable);
    553   //
    554   // push the SEGMENT
    555   //
    556   StackPointer = (UINT16 *) Buffer;
    557   //
    558   // reset the stack pointer
    559   //
    560   DEBUG (
    561     (DEBUG_NET,
    562     "After the fixups, the stack pointer is 0x%X\n\r",
    563     (UINT64)(UINTN) StackPointer)
    564     );
    565 
    566   //
    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
    571   //
    572   SimpleNetworkDevice->DestinationDataSegmentPages = EFI_SIZE_TO_PAGES (RomIdTableAddress->DataSize);
    573   Status = BiosSnp16AllocatePagesBelowOneMb (
    574             SimpleNetworkDevice->DestinationDataSegmentPages,
    575             &SimpleNetworkDevice->DestinationDataSegment
    576             );
    577   if (EFI_ERROR (Status)) {
    578     DEBUG ((DEBUG_ERROR, "We had a failure in AllocatePages, status code = 0x%X\n", Status));
    579     return Status;
    580   }
    581 
    582   UndiLoaderTable->Undi_Ds = (UINT16) ((UINTN) SimpleNetworkDevice->DestinationDataSegment >> 4);
    583 
    584   //
    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
    589   //
    590   SimpleNetworkDevice->DestinationStackSegmentPages = EFI_SIZE_TO_PAGES (RomIdTableAddress->StackSize);
    591   Status = BiosSnp16AllocatePagesBelowOneMb (
    592             SimpleNetworkDevice->DestinationStackSegmentPages,
    593             &SimpleNetworkDevice->DestinationStackSegment
    594             );
    595   if (EFI_ERROR (Status)) {
    596     DEBUG ((DEBUG_ERROR, "We had a failure in AllocatePages, status code = 0x%X\n", Status));
    597     return Status;
    598   }
    599   //
    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
    604   //
    605   SimpleNetworkDevice->DestinationCodeSegmentPages = EFI_SIZE_TO_PAGES (RomIdTableAddress->CodeSize);
    606   Status = BiosSnp16AllocatePagesBelowOneMb (
    607             SimpleNetworkDevice->DestinationCodeSegmentPages,
    608             &SimpleNetworkDevice->DestinationCodeSegment
    609             );
    610   if (EFI_ERROR (Status)) {
    611     DEBUG ((DEBUG_ERROR, "We had a failure in AllocatePages, status code = 0x%X\n", Status));
    612     return Status;
    613   }
    614 
    615   UndiLoaderTable->Undi_Cs = (UINT16) ((UINTN) SimpleNetworkDevice->DestinationCodeSegment >> 4);
    616 
    617   //
    618   // these are in the Input and Output Parameter to be sent to the UNDI Loader code
    619   //
    620   UndiLoaderTable->Status = 0xAA55;
    621   //
    622   // -------------------- Changed by Michael_Huang (at) 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.
    625   // old code is:
    626   // UndiLoaderTable->Ax       = 0x0;
    627   // -----------------------------------------------------------------------
    628   //
    629   SimpleNetworkDevice->PciIo->GetLocation (
    630                                 SimpleNetworkDevice->PciIo,
    631                                 &PciSegment,
    632                                 &Bus,
    633                                 &Device,
    634                                 &Function
    635                                 );
    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;
    641 
    642   //
    643   // set these OUT values to zero in order to ensure that
    644   // uninitialized memory is not mistaken for display data
    645   //
    646   UndiLoaderTable->PXEptr.Offset    = 0;
    647   UndiLoaderTable->PXEptr.Segment   = 0;
    648   UndiLoaderTable->PXENVptr.Segment = 0;
    649   UndiLoaderTable->PXENVptr.Offset  = 0;
    650 
    651   DEBUG (
    652     (DEBUG_INIT,
    653     "The NIC is located at Bus 0x%X, Device 0x%X, Function 0x%X\n\r",
    654     Bus,
    655     Device,
    656     Function)
    657     );
    658 
    659   //
    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
    663   //
    664   InOutRegs.X.AX  = 0;
    665   InOutRegs.X.BX  = 0;
    666   InOutRegs.X.CX  = 0;
    667   InOutRegs.X.DX  = 0;
    668   InOutRegs.X.SI  = 0;
    669   InOutRegs.X.DI  = 0;
    670   InOutRegs.X.BP  = 0;
    671   InOutRegs.X.DS  = 0;
    672   InOutRegs.X.ES  = 0;
    673   //
    674   // just to be clean
    675   //
    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"));
    678   DEBUG (
    679     (DEBUG_NET,
    680     "The Undi Loader Table is at address = 0x%X\n\r",
    681     (UINT32)(UINTN) UndiLoaderTable)
    682     );
    683   DEBUG (
    684     (DEBUG_NET,
    685     "The segment and offsets are 0x%X and 0x%X, resp\n",
    686     TO_SEGMENT (UndiLoaderTable),
    687     TO_OFFSET (UndiLoaderTable))
    688     );
    689 
    690   DEBUG (
    691     (DEBUG_NET,
    692     "The Linear Address of the UNDI Loader entry is 0x%X\n",
    693     RomAddress + RomIdTableAddress->UNDI_Loader)
    694     );
    695 
    696   DEBUG (
    697     (DEBUG_NET,
    698     "The Address offset of the UNDI Loader entry is 0x%X\n",
    699     RomIdTableAddress->UNDI_Loader)
    700     );
    701 
    702   DEBUG ((DEBUG_NET, "Before the call, we have...\n\r"));
    703   Print_Undi_Loader_Table (UndiLoaderTable);
    704 
    705   Segment = ((UINT16) (RShiftU64 (RomAddress, 4) & 0xFFFF));
    706   DEBUG ((DEBUG_NET, "The Segment of the call is 0x%X\n\r", Segment));
    707 
    708   //
    709   // make the call into the UNDI Code
    710   //
    711   DEBUG ((DEBUG_INIT, "Make the call into the UNDI code now\n\r"));
    712 
    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"));
    715   DEBUG (
    716     (DEBUG_NET,
    717     "\t\t0x%X, from SEG:OFF 0x%X:0x%X\n\r\n\r",
    718     Segment * 0x10 + RomIdTableAddress->UNDI_Loader,
    719     Segment,
    720     RomIdTableAddress->UNDI_Loader)
    721     );
    722 
    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
    730                                                    );
    731   if (ThunkFailed) {
    732     return EFI_ABORTED;
    733   }
    734 
    735   DEBUG (
    736     (DEBUG_NET,
    737     "The return code UndiLoaderTable->Status is = 0x%X\n\r",
    738     UndiLoaderTable->Status)
    739     );
    740   DEBUG (
    741     (DEBUG_NET,
    742     "This error code should match eax, which is = 0x%X\n\r",
    743     InOutRegs.X.AX)
    744     );
    745 
    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));
    748     return EFI_ABORTED;
    749   }
    750 
    751   DEBUG ((DEBUG_NET, "Now returned from the UNDI code\n\r"));
    752 
    753   DEBUG ((DEBUG_NET, "After the call, we have...\n\r"));
    754   Print_Undi_Loader_Table (UndiLoaderTable);
    755 
    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));
    759 
    760   Pxe = (PXE_T *)(UINTN)((UndiLoaderTable->PXEptr.Segment << 4) + UndiLoaderTable->PXEptr.Offset);
    761   SimpleNetworkDevice->Nii.Id = (UINT64)(UINTN) Pxe;
    762 
    763   gBS->FreePool (Buffer);
    764 
    765   //
    766   // paranoia - make sure a valid !PXE structure
    767   //
    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;
    771     //
    772     // its not - keep looking
    773     //
    774   }
    775 
    776   if (CalculateSum8 ((UINT8 *) Pxe, Pxe->StructLength) != 0) {
    777     DEBUG ((DEBUG_ERROR, "!PXE Checksum Error\n\r"));
    778     return EFI_NOT_FOUND;
    779   }
    780 
    781   if (Pxe->StructLength < (UINT8 *) &Pxe->FirstSelector - (UINT8 *) Pxe->Signature) {
    782     DEBUG ((DEBUG_ERROR, "!PXE Length Error\n\r"));
    783     return EFI_NOT_FOUND;
    784   }
    785 
    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;
    789   }
    790   //
    791   // This is the magic to bind the global PXE interface
    792   // This dirtiness is for non-protocol shrouded access
    793   //
    794   SimpleNetworkDevice->PxeEntrySegment = Pxe->EntryPointSP.Segment;
    795 
    796   if (SimpleNetworkDevice->PxeEntrySegment == 0) {
    797     DEBUG ((DEBUG_ERROR, "!PXE EntryPointSP segment Error\n\r"));
    798     return EFI_NOT_FOUND;
    799   }
    800 
    801   SimpleNetworkDevice->PxeEntryOffset = Pxe->EntryPointSP.Offset;
    802 
    803   DEBUG (
    804     (
    805     DEBUG_NET, "The entry point is 0x%X:0x%X\n\r", SimpleNetworkDevice->PxeEntrySegment, SimpleNetworkDevice->
    806     PxeEntryOffset
    807     )
    808     );
    809 
    810   return EFI_SUCCESS;
    811 }
    812 
    813 /**
    814   Effect the Far Call into the PXE Layer
    815 
    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.
    823 
    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.
    828 
    829   @return EFI_STATUS
    830 **/
    831 EFI_STATUS
    832 MakePxeCall (
    833   EFI_SIMPLE_NETWORK_DEV  *SimpleNetworkDevice,
    834   IN OUT VOID             *Table,
    835   IN UINTN                TableSize,
    836   IN UINT16               CallIndex
    837   )
    838 {
    839   EFI_STATUS            Status;
    840   EFI_IA32_REGISTER_SET InOutRegs;
    841   UINT16                *BPtr;
    842   VOID                  *Buffer;
    843   UINTN                 Size;
    844   VOID                  *MemPageAddress;
    845   UINTN                 Index;
    846   BOOLEAN               ThunkFailed;
    847 
    848   DEBUG ((DEBUG_NET, "MakePxeCall(CallIndex = %02x, Table = %X, TableSize = %d)\n", CallIndex, Table, TableSize));
    849 
    850   if (SimpleNetworkDevice->PxeEntrySegment == 0 && SimpleNetworkDevice->PxeEntryOffset == 0) {
    851     return EFI_DEVICE_ERROR;
    852   }
    853 
    854   Status = EFI_SUCCESS;
    855 
    856   //
    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...............)
    863   //
    864   Status = BiosSnp16AllocatePagesBelowOneMb (
    865             TableSize / EFI_PAGE_SIZE + 1,
    866             &MemPageAddress
    867             );
    868   if (EFI_ERROR (Status)) {
    869     DEBUG ((DEBUG_ERROR, "We had a failure in AllocatePages, status code = 0x%X\n", Status));
    870     return Status;
    871   }
    872   //
    873   // Copy the > 1MB pool table to a sub-1MB buffer
    874   //
    875   CopyMem (MemPageAddress, Table, TableSize);
    876 
    877   //
    878   // Allocate space for IA-32 register context
    879   //
    880   ZeroMem (&InOutRegs, sizeof (InOutRegs));
    881   InOutRegs.X.ES  = SimpleNetworkDevice->PxeEntrySegment;
    882   InOutRegs.X.DI  = SimpleNetworkDevice->PxeEntryOffset;
    883 
    884   //
    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
    888   //
    889   Size = 6;
    890   Status = gBS->AllocatePool (
    891                   EfiRuntimeServicesData,
    892                   Size,
    893                   &Buffer
    894                   );
    895   if (EFI_ERROR (Status)) {
    896     return Status;
    897   }
    898 
    899   BPtr    = (UINT16 *) Buffer;
    900   *BPtr++ = CallIndex;
    901   //
    902   // SP + 2
    903   //
    904   *BPtr++ = TO_OFFSET (MemPageAddress);
    905   *BPtr++ = TO_SEGMENT (MemPageAddress);
    906 
    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)));
    914   }
    915 
    916   DEBUG ((DEBUG_NET, "\n"));
    917 
    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
    925                                                    );
    926   if (ThunkFailed) {
    927     return EFI_ABORTED;
    928   }
    929 
    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)));
    937   }
    938 
    939   DEBUG ((DEBUG_NET, "\n"));
    940 
    941   //
    942   // Copy the sub 1MB table to > 1MB table
    943   //
    944   CopyMem (Table, MemPageAddress, TableSize);
    945 
    946   //
    947   // For PXE UNDI call, AX contains the return status.
    948   // Convert the PXE UNDI Status to EFI_STATUS type
    949   //
    950   if (InOutRegs.X.AX == PXENV_EXIT_SUCCESS) {
    951     Status = EFI_SUCCESS;
    952   } else {
    953     Status = EFI_DEVICE_ERROR;
    954   }
    955   //
    956   // Clean up house
    957   //
    958   gBS->FreePool (Buffer);
    959   gBS->FreePages ((EFI_PHYSICAL_ADDRESS) (UINTN) MemPageAddress, TableSize / EFI_PAGE_SIZE + 1);
    960 
    961   return Status;
    962 }
    963