]> git.proxmox.com Git - mirror_edk2.git/blob - IntelFrameworkModulePkg/Csm/BiosThunk/Snp16Dxe/Misc.c
4750b2f99d9c2d8c55ab69f26d81832ef9a7fdcf
[mirror_edk2.git] / IntelFrameworkModulePkg / Csm / BiosThunk / Snp16Dxe / Misc.c
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@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 }