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
CacheVectorAddress(UINT8 VectorNumber)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
RestoreCachedVectorAddress(UINT8 VectorNumber)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
Print_Undi_Loader_Table(VOID * UndiLoaderStructure)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
Print_ROMID_Table(IN VOID * RomIDStructure)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
Print_PXE_Table(IN VOID * PxeTable)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
Print_PXENV_Table(IN VOID * PxenvTable)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
LaunchBaseCode(EFI_SIMPLE_NETWORK_DEV * SimpleNetworkDevice,UINTN RomAddress)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@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 paramter/return value table
827   @param CallIndex              The index of legacy call.
828 
829   @return EFI_STATUS
830 **/
831 EFI_STATUS
MakePxeCall(EFI_SIMPLE_NETWORK_DEV * SimpleNetworkDevice,IN OUT VOID * Table,IN UINTN TableSize,IN UINT16 CallIndex)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