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