]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/Csm/LegacyBiosDxe/LegacyBios.c
OvmfPkg: Apply uncrustify changes
[mirror_edk2.git] / OvmfPkg / Csm / LegacyBiosDxe / LegacyBios.c
1 /** @file
2
3 Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.<BR>
4
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 **/
8
9 #include "LegacyBiosInterface.h"
10
11 #define PHYSICAL_ADDRESS_TO_POINTER(Address) ((VOID *) ((UINTN) Address))
12
13 //
14 // define maximum number of HDD system supports
15 //
16 #define MAX_HDD_ENTRIES 0x30
17
18 //
19 // Module Global:
20 // Since this driver will only ever produce one instance of the Private Data
21 // protocol you are not required to dynamically allocate the PrivateData.
22 //
23 LEGACY_BIOS_INSTANCE mPrivateData;
24
25 //
26 // The SMBIOS table in EfiRuntimeServicesData memory
27 //
28 VOID *mRuntimeSmbiosEntryPoint = NULL;
29
30 //
31 // The SMBIOS table in EfiReservedMemoryType memory
32 //
33 EFI_PHYSICAL_ADDRESS mReserveSmbiosEntryPoint = 0;
34 EFI_PHYSICAL_ADDRESS mStructureTableAddress = 0;
35 UINTN mStructureTablePages = 0;
36 BOOLEAN mEndOfDxe = FALSE;
37
38 /**
39 Allocate memory for legacy usage. The memory is executable.
40
41 @param AllocateType The type of allocation to perform.
42 @param MemoryType The type of memory to allocate.
43 @param StartPageAddress Start address of range
44 @param Pages Number of pages to allocate
45 @param Result Result of allocation
46
47 @retval EFI_SUCCESS Legacy memory is allocated successfully.
48 @retval Other Legacy memory is not allocated.
49
50 **/
51 EFI_STATUS
52 AllocateLegacyMemory (
53 IN EFI_ALLOCATE_TYPE AllocateType,
54 IN EFI_MEMORY_TYPE MemoryType,
55 IN EFI_PHYSICAL_ADDRESS StartPageAddress,
56 IN UINTN Pages,
57 OUT EFI_PHYSICAL_ADDRESS *Result
58 )
59 {
60 EFI_STATUS Status;
61 EFI_PHYSICAL_ADDRESS MemPage;
62 EFI_GCD_MEMORY_SPACE_DESCRIPTOR MemDesc;
63
64 //
65 // Allocate Pages of memory less <= StartPageAddress
66 //
67 MemPage = (EFI_PHYSICAL_ADDRESS)(UINTN)StartPageAddress;
68 Status = gBS->AllocatePages (
69 AllocateType,
70 MemoryType,
71 Pages,
72 &MemPage
73 );
74 //
75 // Do not ASSERT on Status error but let caller decide since some cases
76 // memory is already taken but that is ok.
77 //
78 if (!EFI_ERROR (Status)) {
79 if (MemoryType != EfiBootServicesCode) {
80 //
81 // Make sure that the buffer can be used to store code.
82 //
83 Status = gDS->GetMemorySpaceDescriptor (MemPage, &MemDesc);
84 if (!EFI_ERROR (Status) && ((MemDesc.Attributes & EFI_MEMORY_XP) != 0)) {
85 Status = gDS->SetMemorySpaceAttributes (
86 MemPage,
87 EFI_PAGES_TO_SIZE (Pages),
88 MemDesc.Attributes & (~EFI_MEMORY_XP)
89 );
90 }
91
92 if (EFI_ERROR (Status)) {
93 gBS->FreePages (MemPage, Pages);
94 }
95 }
96 }
97
98 if (!EFI_ERROR (Status)) {
99 *Result = (EFI_PHYSICAL_ADDRESS)(UINTN)MemPage;
100 }
101
102 return Status;
103 }
104
105 /**
106 This function is called when EFI needs to reserve an area in the 0xE0000 or 0xF0000
107 64 KB blocks.
108
109 Note: inconsistency with the Framework CSM spec. Per the spec, this function may be
110 invoked only once. This limitation is relaxed to allow multiple calls in this implementation.
111
112 @param This Protocol instance pointer.
113 @param LegacyMemorySize Size of required region
114 @param Region Region to use. 00 = Either 0xE0000 or 0xF0000
115 block Bit0 = 1 0xF0000 block Bit1 = 1 0xE0000
116 block
117 @param Alignment Address alignment. Bit mapped. First non-zero
118 bit from right is alignment.
119 @param LegacyMemoryAddress Region Assigned
120
121 @retval EFI_SUCCESS Region assigned
122 @retval EFI_ACCESS_DENIED Procedure previously invoked
123 @retval Other Region not assigned
124
125 **/
126 EFI_STATUS
127 EFIAPI
128 LegacyBiosGetLegacyRegion (
129 IN EFI_LEGACY_BIOS_PROTOCOL *This,
130 IN UINTN LegacyMemorySize,
131 IN UINTN Region,
132 IN UINTN Alignment,
133 OUT VOID **LegacyMemoryAddress
134 )
135 {
136 LEGACY_BIOS_INSTANCE *Private;
137 EFI_IA32_REGISTER_SET Regs;
138 EFI_STATUS Status;
139 UINT32 Granularity;
140
141 Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
142 Private->LegacyRegion->UnLock (Private->LegacyRegion, 0xE0000, 0x20000, &Granularity);
143
144 ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));
145 Regs.X.AX = Legacy16GetTableAddress;
146 Regs.X.BX = (UINT16)Region;
147 Regs.X.CX = (UINT16)LegacyMemorySize;
148 Regs.X.DX = (UINT16)Alignment;
149 Private->LegacyBios.FarCall86 (
150 &Private->LegacyBios,
151 Private->Legacy16CallSegment,
152 Private->Legacy16CallOffset,
153 &Regs,
154 NULL,
155 0
156 );
157
158 if (Regs.X.AX == 0) {
159 *LegacyMemoryAddress = (VOID *)(((UINTN)Regs.X.DS << 4) + Regs.X.BX);
160 Status = EFI_SUCCESS;
161 } else {
162 Status = EFI_OUT_OF_RESOURCES;
163 }
164
165 Private->Cpu->FlushDataCache (Private->Cpu, 0xE0000, 0x20000, EfiCpuFlushTypeWriteBackInvalidate);
166 Private->LegacyRegion->Lock (Private->LegacyRegion, 0xE0000, 0x20000, &Granularity);
167
168 return Status;
169 }
170
171 /**
172 This function is called when copying data to the region assigned by
173 EFI_LEGACY_BIOS_PROTOCOL.GetLegacyRegion().
174
175 @param This Protocol instance pointer.
176 @param LegacyMemorySize Size of data to copy
177 @param LegacyMemoryAddress Legacy Region destination address Note: must
178 be in region assigned by
179 LegacyBiosGetLegacyRegion
180 @param LegacyMemorySourceAddress Source of data
181
182 @retval EFI_SUCCESS The data was copied successfully.
183 @retval EFI_ACCESS_DENIED Either the starting or ending address is out of bounds.
184 **/
185 EFI_STATUS
186 EFIAPI
187 LegacyBiosCopyLegacyRegion (
188 IN EFI_LEGACY_BIOS_PROTOCOL *This,
189 IN UINTN LegacyMemorySize,
190 IN VOID *LegacyMemoryAddress,
191 IN VOID *LegacyMemorySourceAddress
192 )
193 {
194 LEGACY_BIOS_INSTANCE *Private;
195 UINT32 Granularity;
196
197 if ((LegacyMemoryAddress < (VOID *)(UINTN)0xE0000) ||
198 ((UINTN)LegacyMemoryAddress + LegacyMemorySize > (UINTN)0x100000)
199 )
200 {
201 return EFI_ACCESS_DENIED;
202 }
203
204 //
205 // There is no protection from writes over lapping if this function is
206 // called multiple times.
207 //
208 Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
209 Private->LegacyRegion->UnLock (Private->LegacyRegion, 0xE0000, 0x20000, &Granularity);
210 CopyMem (LegacyMemoryAddress, LegacyMemorySourceAddress, LegacyMemorySize);
211
212 Private->Cpu->FlushDataCache (Private->Cpu, 0xE0000, 0x20000, EfiCpuFlushTypeWriteBackInvalidate);
213 Private->LegacyRegion->Lock (Private->LegacyRegion, 0xE0000, 0x20000, &Granularity);
214
215 return EFI_SUCCESS;
216 }
217
218 /**
219 Find Legacy16 BIOS image in the FLASH device and shadow it into memory. Find
220 the $EFI table in the shadow area. Thunk into the Legacy16 code after it had
221 been shadowed.
222
223 @param Private Legacy BIOS context data
224
225 @retval EFI_SUCCESS Legacy16 code loaded
226 @retval Other No protocol installed, unload driver.
227
228 **/
229 EFI_STATUS
230 ShadowAndStartLegacy16 (
231 IN LEGACY_BIOS_INSTANCE *Private
232 )
233 {
234 EFI_STATUS Status;
235 UINT8 *Ptr;
236 UINT8 *PtrEnd;
237 BOOLEAN Done;
238 EFI_COMPATIBILITY16_TABLE *Table;
239 UINT8 CheckSum;
240 EFI_IA32_REGISTER_SET Regs;
241 EFI_TO_COMPATIBILITY16_INIT_TABLE *EfiToLegacy16InitTable;
242 EFI_TO_COMPATIBILITY16_BOOT_TABLE *EfiToLegacy16BootTable;
243 VOID *LegacyBiosImage;
244 UINTN LegacyBiosImageSize;
245 UINTN E820Size;
246 UINT32 *ClearPtr;
247 BBS_TABLE *BbsTable;
248 LEGACY_EFI_HDD_TABLE *LegacyEfiHddTable;
249 UINTN Index;
250 UINT32 TpmPointer;
251 VOID *TpmBinaryImage;
252 UINTN TpmBinaryImageSize;
253 UINTN Location;
254 UINTN Alignment;
255 UINTN TempData;
256 EFI_PHYSICAL_ADDRESS Address;
257 UINT16 OldMask;
258 UINT16 NewMask;
259 UINT32 Granularity;
260 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;
261
262 Location = 0;
263 Alignment = 0;
264
265 //
266 // we allocate the C/D/E/F segment as RT code so no one will use it any more.
267 //
268 Address = 0xC0000;
269 gDS->GetMemorySpaceDescriptor (Address, &Descriptor);
270 if (Descriptor.GcdMemoryType == EfiGcdMemoryTypeSystemMemory) {
271 //
272 // If it is already reserved, we should be safe, or else we allocate it.
273 //
274 Status = gBS->AllocatePages (
275 AllocateAddress,
276 EfiRuntimeServicesCode,
277 0x40000/EFI_PAGE_SIZE,
278 &Address
279 );
280 if (EFI_ERROR (Status)) {
281 //
282 // Bugbug: need to figure out whether C/D/E/F segment should be marked as reserved memory.
283 //
284 DEBUG ((DEBUG_ERROR, "Failed to allocate the C/D/E/F segment Status = %r", Status));
285 }
286 }
287
288 //
289 // start testtest
290 // GetTimerValue (&Ticker);
291 //
292 // gRT->SetVariable (L"StartLegacy",
293 // &gEfiGlobalVariableGuid,
294 // EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
295 // sizeof (UINT64),
296 // (VOID *)&Ticker
297 // );
298 // end testtest
299 //
300 EfiToLegacy16BootTable = &Private->IntThunk->EfiToLegacy16BootTable;
301 Status = Private->LegacyBiosPlatform->GetPlatformInfo (
302 Private->LegacyBiosPlatform,
303 EfiGetPlatformBinarySystemRom,
304 &LegacyBiosImage,
305 &LegacyBiosImageSize,
306 &Location,
307 &Alignment,
308 0,
309 0
310 );
311 if (EFI_ERROR (Status)) {
312 return Status;
313 }
314
315 Private->BiosStart = (UINT32)(0x100000 - LegacyBiosImageSize);
316 Private->OptionRom = 0xc0000;
317 Private->LegacyBiosImageSize = (UINT32)LegacyBiosImageSize;
318
319 //
320 // Can only shadow into memory allocated for legacy usage.
321 //
322 ASSERT (Private->BiosStart > Private->OptionRom);
323
324 //
325 // Shadow Legacy BIOS. Turn on memory and copy image
326 //
327 Private->LegacyRegion->UnLock (Private->LegacyRegion, 0xc0000, 0x40000, &Granularity);
328
329 ClearPtr = (VOID *)((UINTN)0xc0000);
330
331 //
332 // Initialize region from 0xc0000 to start of BIOS to all ffs. This allows unused
333 // regions to be used by EMM386 etc.
334 //
335 SetMem ((VOID *)ClearPtr, (UINTN)(0x40000 - LegacyBiosImageSize), 0xff);
336
337 TempData = Private->BiosStart;
338
339 CopyMem (
340 (VOID *)TempData,
341 LegacyBiosImage,
342 (UINTN)LegacyBiosImageSize
343 );
344
345 Private->Cpu->FlushDataCache (Private->Cpu, 0xc0000, 0x40000, EfiCpuFlushTypeWriteBackInvalidate);
346
347 //
348 // Search for Legacy16 table in Shadowed ROM
349 //
350 Done = FALSE;
351 Table = NULL;
352 for (Ptr = (UINT8 *)TempData; Ptr < (UINT8 *)((UINTN)0x100000) && !Done; Ptr += 0x10) {
353 if (*(UINT32 *)Ptr == SIGNATURE_32 ('I', 'F', 'E', '$')) {
354 Table = (EFI_COMPATIBILITY16_TABLE *)Ptr;
355 PtrEnd = Ptr + Table->TableLength;
356 for (CheckSum = 0; Ptr < PtrEnd; Ptr++) {
357 CheckSum = (UINT8)(CheckSum +*Ptr);
358 }
359
360 Done = TRUE;
361 }
362 }
363
364 if (Table == NULL) {
365 DEBUG ((DEBUG_ERROR, "No Legacy16 table found\n"));
366 return EFI_NOT_FOUND;
367 }
368
369 if (!Done) {
370 //
371 // Legacy16 table header checksum error.
372 //
373 DEBUG ((DEBUG_ERROR, "Legacy16 table found with bad talbe header checksum\n"));
374 }
375
376 //
377 // Remember location of the Legacy16 table
378 //
379 Private->Legacy16Table = Table;
380 Private->Legacy16CallSegment = Table->Compatibility16CallSegment;
381 Private->Legacy16CallOffset = Table->Compatibility16CallOffset;
382 EfiToLegacy16InitTable = &Private->IntThunk->EfiToLegacy16InitTable;
383 Private->Legacy16InitPtr = EfiToLegacy16InitTable;
384 Private->Legacy16BootPtr = &Private->IntThunk->EfiToLegacy16BootTable;
385 Private->InternalIrqRoutingTable = NULL;
386 Private->NumberIrqRoutingEntries = 0;
387 Private->BbsTablePtr = NULL;
388 Private->LegacyEfiHddTable = NULL;
389 Private->DiskEnd = 0;
390 Private->Disk4075 = 0;
391 Private->HddTablePtr = &Private->IntThunk->EfiToLegacy16BootTable.HddInfo;
392 Private->NumberHddControllers = MAX_IDE_CONTROLLER;
393 Private->Dump[0] = 'D';
394 Private->Dump[1] = 'U';
395 Private->Dump[2] = 'M';
396 Private->Dump[3] = 'P';
397
398 ZeroMem (
399 Private->Legacy16BootPtr,
400 sizeof (EFI_TO_COMPATIBILITY16_BOOT_TABLE)
401 );
402
403 //
404 // Store away a copy of the EFI System Table
405 //
406 Table->EfiSystemTable = (UINT32)(UINTN)gST;
407
408 //
409 // IPF CSM integration -Bug
410 //
411 // Construct the Legacy16 boot memory map. This sets up number of
412 // E820 entries.
413 //
414 LegacyBiosBuildE820 (Private, &E820Size);
415 //
416 // Initialize BDA and EBDA standard values needed to load Legacy16 code
417 //
418 LegacyBiosInitBda (Private);
419 LegacyBiosInitCmos (Private);
420
421 //
422 // All legacy interrupt should be masked when do initialization work from legacy 16 code.
423 //
424 Private->Legacy8259->GetMask (Private->Legacy8259, &OldMask, NULL, NULL, NULL);
425 NewMask = 0xFFFF;
426 Private->Legacy8259->SetMask (Private->Legacy8259, &NewMask, NULL, NULL, NULL);
427
428 //
429 // Call into Legacy16 code to do an INIT
430 //
431 ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));
432 Regs.X.AX = Legacy16InitializeYourself;
433 Regs.X.ES = EFI_SEGMENT (*((UINT32 *)&EfiToLegacy16InitTable));
434 Regs.X.BX = EFI_OFFSET (*((UINT32 *)&EfiToLegacy16InitTable));
435
436 Private->LegacyBios.FarCall86 (
437 &Private->LegacyBios,
438 Table->Compatibility16CallSegment,
439 Table->Compatibility16CallOffset,
440 &Regs,
441 NULL,
442 0
443 );
444
445 //
446 // Restore original legacy interrupt mask value
447 //
448 Private->Legacy8259->SetMask (Private->Legacy8259, &OldMask, NULL, NULL, NULL);
449
450 if (Regs.X.AX != 0) {
451 return EFI_DEVICE_ERROR;
452 }
453
454 //
455 // start testtest
456 // GetTimerValue (&Ticker);
457 //
458 // gRT->SetVariable (L"BackFromInitYourself",
459 // &gEfiGlobalVariableGuid,
460 // EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
461 // sizeof (UINT64),
462 // (VOID *)&Ticker
463 // );
464 // end testtest
465 //
466 // Copy E820 table after InitializeYourself is completed
467 //
468 ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));
469 Regs.X.AX = Legacy16GetTableAddress;
470 Regs.X.CX = (UINT16)E820Size;
471 Regs.X.DX = 1;
472 Private->LegacyBios.FarCall86 (
473 &Private->LegacyBios,
474 Table->Compatibility16CallSegment,
475 Table->Compatibility16CallOffset,
476 &Regs,
477 NULL,
478 0
479 );
480
481 Table->E820Pointer = (UINT32)(Regs.X.DS * 16 + Regs.X.BX);
482 Table->E820Length = (UINT32)E820Size;
483 if (Regs.X.AX != 0) {
484 DEBUG ((DEBUG_ERROR, "Legacy16 E820 length insufficient\n"));
485 } else {
486 TempData = Table->E820Pointer;
487 CopyMem ((VOID *)TempData, Private->E820Table, E820Size);
488 }
489
490 //
491 // Get PnPInstallationCheck Info.
492 //
493 Private->PnPInstallationCheckSegment = Table->PnPInstallationCheckSegment;
494 Private->PnPInstallationCheckOffset = Table->PnPInstallationCheckOffset;
495
496 //
497 // Check if PCI Express is supported. If yes, Save base address.
498 //
499 Status = Private->LegacyBiosPlatform->GetPlatformInfo (
500 Private->LegacyBiosPlatform,
501 EfiGetPlatformPciExpressBase,
502 NULL,
503 NULL,
504 &Location,
505 &Alignment,
506 0,
507 0
508 );
509 if (!EFI_ERROR (Status)) {
510 Private->Legacy16Table->PciExpressBase = (UINT32)Location;
511 Location = 0;
512 }
513
514 //
515 // Check if TPM is supported. If yes get a region in E0000,F0000 to copy it
516 // into, copy it and update pointer to binary image. This needs to be
517 // done prior to any OPROM for security purposes.
518 //
519 Status = Private->LegacyBiosPlatform->GetPlatformInfo (
520 Private->LegacyBiosPlatform,
521 EfiGetPlatformBinaryTpmBinary,
522 &TpmBinaryImage,
523 &TpmBinaryImageSize,
524 &Location,
525 &Alignment,
526 0,
527 0
528 );
529 if (!EFI_ERROR (Status)) {
530 ZeroMem (&Regs, sizeof (EFI_IA32_REGISTER_SET));
531 Regs.X.AX = Legacy16GetTableAddress;
532 Regs.X.CX = (UINT16)TpmBinaryImageSize;
533 Regs.X.DX = 1;
534 Private->LegacyBios.FarCall86 (
535 &Private->LegacyBios,
536 Table->Compatibility16CallSegment,
537 Table->Compatibility16CallOffset,
538 &Regs,
539 NULL,
540 0
541 );
542
543 TpmPointer = (UINT32)(Regs.X.DS * 16 + Regs.X.BX);
544 if (Regs.X.AX != 0) {
545 DEBUG ((DEBUG_ERROR, "TPM cannot be loaded\n"));
546 } else {
547 CopyMem ((VOID *)(UINTN)TpmPointer, TpmBinaryImage, TpmBinaryImageSize);
548 Table->TpmSegment = Regs.X.DS;
549 Table->TpmOffset = Regs.X.BX;
550 }
551 }
552
553 //
554 // Lock the Legacy BIOS region
555 //
556 Private->Cpu->FlushDataCache (Private->Cpu, Private->BiosStart, (UINT32)LegacyBiosImageSize, EfiCpuFlushTypeWriteBackInvalidate);
557 Private->LegacyRegion->Lock (Private->LegacyRegion, Private->BiosStart, (UINT32)LegacyBiosImageSize, &Granularity);
558
559 //
560 // Get the BbsTable from LOW_MEMORY_THUNK
561 //
562 BbsTable = (BBS_TABLE *)(UINTN)Private->IntThunk->BbsTable;
563 ZeroMem ((VOID *)BbsTable, sizeof (Private->IntThunk->BbsTable));
564
565 EfiToLegacy16BootTable->BbsTable = (UINT32)(UINTN)BbsTable;
566 Private->BbsTablePtr = (VOID *)BbsTable;
567
568 //
569 // Populate entire table with BBS_IGNORE_ENTRY
570 //
571 EfiToLegacy16BootTable->NumberBbsEntries = MAX_BBS_ENTRIES;
572
573 for (Index = 0; Index < MAX_BBS_ENTRIES; Index++) {
574 BbsTable[Index].BootPriority = BBS_IGNORE_ENTRY;
575 }
576
577 //
578 // Allocate space for Legacy HDD table
579 //
580 LegacyEfiHddTable = (LEGACY_EFI_HDD_TABLE *)AllocateZeroPool ((UINTN)MAX_HDD_ENTRIES * sizeof (LEGACY_EFI_HDD_TABLE));
581 ASSERT (LegacyEfiHddTable);
582
583 Private->LegacyEfiHddTable = LegacyEfiHddTable;
584 Private->LegacyEfiHddTableIndex = 0x00;
585
586 //
587 // start testtest
588 // GetTimerValue (&Ticker);
589 //
590 // gRT->SetVariable (L"EndOfLoadFv",
591 // &gEfiGlobalVariableGuid,
592 // EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
593 // sizeof (UINT64),
594 // (VOID *)&Ticker
595 // );
596 // end testtest
597 //
598 return EFI_SUCCESS;
599 }
600
601 /**
602 Shadow all legacy16 OPROMs that haven't been shadowed.
603 Warning: Use this with caution. This routine disconnects all EFI
604 drivers. If used externally then caller must re-connect EFI
605 drivers.
606
607 @param This Protocol instance pointer.
608
609 @retval EFI_SUCCESS OPROMs shadowed
610
611 **/
612 EFI_STATUS
613 EFIAPI
614 LegacyBiosShadowAllLegacyOproms (
615 IN EFI_LEGACY_BIOS_PROTOCOL *This
616 )
617 {
618 LEGACY_BIOS_INSTANCE *Private;
619
620 //
621 // EFI_LEGACY_BIOS_PLATFORM_PROTOCOL *LegacyBiosPlatform;
622 // EFI_LEGACY16_TABLE *Legacy16Table;
623 //
624 Private = LEGACY_BIOS_INSTANCE_FROM_THIS (This);
625
626 //
627 // LegacyBiosPlatform = Private->LegacyBiosPlatform;
628 // Legacy16Table = Private->Legacy16Table;
629 //
630 // Shadow PCI ROMs. We must do this near the end since this will kick
631 // of Native EFI drivers that may be needed to collect info for Legacy16
632 //
633 // WARNING: PciIo is gone after this call.
634 //
635 PciProgramAllInterruptLineRegisters (Private);
636
637 PciShadowRoms (Private);
638
639 //
640 // Shadow PXE base code, BIS etc.
641 //
642 // LegacyBiosPlatform->ShadowServiceRoms (LegacyBiosPlatform,
643 // &Private->OptionRom,
644 // Legacy16Table);
645 //
646 return EFI_SUCCESS;
647 }
648
649 /**
650 Get the PCI BIOS interface version.
651
652 @param Private Driver private data.
653
654 @return The PCI interface version number in Binary Coded Decimal (BCD) format.
655 E.g.: 0x0210 indicates 2.10, 0x0300 indicates 3.00
656
657 **/
658 UINT16
659 GetPciInterfaceVersion (
660 IN LEGACY_BIOS_INSTANCE *Private
661 )
662 {
663 EFI_IA32_REGISTER_SET Reg;
664 BOOLEAN ThunkFailed;
665 UINT16 PciInterfaceVersion;
666
667 PciInterfaceVersion = 0;
668
669 Reg.X.AX = 0xB101;
670 Reg.E.EDI = 0;
671
672 ThunkFailed = Private->LegacyBios.Int86 (&Private->LegacyBios, 0x1A, &Reg);
673 if (!ThunkFailed) {
674 //
675 // From PCI Firmware 3.0 Specification:
676 // If the CARRY FLAG [CF] is cleared and AH is set to 00h, it is still necessary to examine the
677 // contents of [EDX] for the presence of the string "PCI" + (trailing space) to fully validate the
678 // presence of the PCI function set. [BX] will further indicate the version level, with enough
679 // granularity to allow for incremental changes in the code that don't affect the function interface.
680 // Version numbers are stored as Binary Coded Decimal (BCD) values. For example, Version 2.10
681 // would be returned as a 02h in the [BH] registers and 10h in the [BL] registers.
682 //
683 if ((Reg.X.Flags.CF == 0) && (Reg.H.AH == 0) && (Reg.E.EDX == SIGNATURE_32 ('P', 'C', 'I', ' '))) {
684 PciInterfaceVersion = Reg.X.BX;
685 }
686 }
687
688 return PciInterfaceVersion;
689 }
690
691 /**
692 Callback function to calculate SMBIOS table size, and allocate memory for SMBIOS table.
693 SMBIOS table will be copied into EfiReservedMemoryType memory in legacy boot path.
694
695 @param Event Event whose notification function is being invoked.
696 @param Context The pointer to the notification function's context,
697 which is implementation-dependent.
698
699 **/
700 VOID
701 EFIAPI
702 InstallSmbiosEventCallback (
703 IN EFI_EVENT Event,
704 IN VOID *Context
705 )
706 {
707 EFI_STATUS Status;
708 SMBIOS_TABLE_ENTRY_POINT *EntryPointStructure;
709
710 //
711 // Get SMBIOS table from EFI configuration table
712 //
713 Status = EfiGetSystemConfigurationTable (
714 &gEfiSmbiosTableGuid,
715 &mRuntimeSmbiosEntryPoint
716 );
717 if ((EFI_ERROR (Status)) || (mRuntimeSmbiosEntryPoint == NULL)) {
718 return;
719 }
720
721 EntryPointStructure = (SMBIOS_TABLE_ENTRY_POINT *)mRuntimeSmbiosEntryPoint;
722
723 //
724 // Allocate memory for SMBIOS Entry Point Structure.
725 // CSM framework spec requires SMBIOS table below 4GB in EFI_TO_COMPATIBILITY16_BOOT_TABLE.
726 //
727 if (mReserveSmbiosEntryPoint == 0) {
728 //
729 // Entrypoint structure with fixed size is allocated only once.
730 //
731 mReserveSmbiosEntryPoint = SIZE_4GB - 1;
732 Status = gBS->AllocatePages (
733 AllocateMaxAddress,
734 EfiReservedMemoryType,
735 EFI_SIZE_TO_PAGES ((UINTN)(EntryPointStructure->EntryPointLength)),
736 &mReserveSmbiosEntryPoint
737 );
738 if (EFI_ERROR (Status)) {
739 mReserveSmbiosEntryPoint = 0;
740 return;
741 }
742
743 DEBUG ((DEBUG_INFO, "Allocate memory for Smbios Entry Point Structure\n"));
744 }
745
746 if ((mStructureTableAddress != 0) &&
747 (mStructureTablePages < EFI_SIZE_TO_PAGES ((UINT32)EntryPointStructure->TableLength)))
748 {
749 //
750 // If original buffer is not enough for the new SMBIOS table, free original buffer and re-allocate
751 //
752 gBS->FreePages (mStructureTableAddress, mStructureTablePages);
753 mStructureTableAddress = 0;
754 mStructureTablePages = 0;
755 DEBUG ((DEBUG_INFO, "Original size is not enough. Re-allocate the memory.\n"));
756 }
757
758 if (mStructureTableAddress == 0) {
759 //
760 // Allocate reserved memory below 4GB.
761 // Smbios spec requires the structure table is below 4GB.
762 //
763 mStructureTableAddress = SIZE_4GB - 1;
764 mStructureTablePages = EFI_SIZE_TO_PAGES (EntryPointStructure->TableLength);
765 Status = gBS->AllocatePages (
766 AllocateMaxAddress,
767 EfiReservedMemoryType,
768 mStructureTablePages,
769 &mStructureTableAddress
770 );
771 if (EFI_ERROR (Status)) {
772 gBS->FreePages (
773 mReserveSmbiosEntryPoint,
774 EFI_SIZE_TO_PAGES ((UINTN)(EntryPointStructure->EntryPointLength))
775 );
776 mReserveSmbiosEntryPoint = 0;
777 mStructureTableAddress = 0;
778 mStructureTablePages = 0;
779 return;
780 }
781
782 DEBUG ((DEBUG_INFO, "Allocate memory for Smbios Structure Table\n"));
783 }
784 }
785
786 /**
787 Callback function to toggle EndOfDxe status. NULL pointer detection needs
788 this status to decide if it's necessary to change attributes of page 0.
789
790 @param Event Event whose notification function is being invoked.
791 @param Context The pointer to the notification function's context,
792 which is implementation-dependent.
793
794 **/
795 VOID
796 EFIAPI
797 ToggleEndOfDxeStatus (
798 IN EFI_EVENT Event,
799 IN VOID *Context
800 )
801 {
802 mEndOfDxe = TRUE;
803 return;
804 }
805
806 /**
807 Install Driver to produce Legacy BIOS protocol.
808
809 @param ImageHandle Handle of driver image.
810 @param SystemTable Pointer to system table.
811
812 @retval EFI_SUCCESS Legacy BIOS protocol installed
813 @retval No protocol installed, unload driver.
814
815 **/
816 EFI_STATUS
817 EFIAPI
818 LegacyBiosInstall (
819 IN EFI_HANDLE ImageHandle,
820 IN EFI_SYSTEM_TABLE *SystemTable
821 )
822 {
823 EFI_STATUS Status;
824 LEGACY_BIOS_INSTANCE *Private;
825 EFI_TO_COMPATIBILITY16_INIT_TABLE *EfiToLegacy16InitTable;
826 EFI_PHYSICAL_ADDRESS MemoryAddress;
827 EFI_PHYSICAL_ADDRESS EbdaReservedBaseAddress;
828 VOID *MemoryPtr;
829 EFI_PHYSICAL_ADDRESS MemoryAddressUnder1MB;
830 UINTN Index;
831 UINT32 *BaseVectorMaster;
832 EFI_PHYSICAL_ADDRESS StartAddress;
833 UINT32 *ClearPtr;
834 EFI_PHYSICAL_ADDRESS MemStart;
835 UINT32 IntRedirCode;
836 UINT32 Granularity;
837 BOOLEAN DecodeOn;
838 UINT32 MemorySize;
839 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;
840 UINT64 Length;
841 UINT8 *SecureBoot;
842 EFI_EVENT InstallSmbiosEvent;
843 EFI_EVENT EndOfDxeEvent;
844
845 //
846 // Load this driver's image to memory
847 //
848 Status = RelocateImageUnder4GIfNeeded (ImageHandle, SystemTable);
849 if (EFI_ERROR (Status)) {
850 return Status;
851 }
852
853 //
854 // When UEFI Secure Boot is enabled, CSM module will not start any more.
855 //
856 SecureBoot = NULL;
857 GetEfiGlobalVariable2 (EFI_SECURE_BOOT_MODE_NAME, (VOID **)&SecureBoot, NULL);
858 if ((SecureBoot != NULL) && (*SecureBoot == SECURE_BOOT_MODE_ENABLE)) {
859 FreePool (SecureBoot);
860 return EFI_SECURITY_VIOLATION;
861 }
862
863 if (SecureBoot != NULL) {
864 FreePool (SecureBoot);
865 }
866
867 Private = &mPrivateData;
868 ZeroMem (Private, sizeof (LEGACY_BIOS_INSTANCE));
869
870 //
871 // Grab a copy of all the protocols we depend on. Any error would
872 // be a dispatcher bug!.
873 //
874 Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&Private->Cpu);
875 ASSERT_EFI_ERROR (Status);
876
877 Status = gBS->LocateProtocol (&gEfiTimerArchProtocolGuid, NULL, (VOID **)&Private->Timer);
878 ASSERT_EFI_ERROR (Status);
879
880 Status = gBS->LocateProtocol (&gEfiLegacyRegion2ProtocolGuid, NULL, (VOID **)&Private->LegacyRegion);
881 ASSERT_EFI_ERROR (Status);
882
883 Status = gBS->LocateProtocol (&gEfiLegacyBiosPlatformProtocolGuid, NULL, (VOID **)&Private->LegacyBiosPlatform);
884 ASSERT_EFI_ERROR (Status);
885
886 Status = gBS->LocateProtocol (&gEfiLegacy8259ProtocolGuid, NULL, (VOID **)&Private->Legacy8259);
887 ASSERT_EFI_ERROR (Status);
888
889 Status = gBS->LocateProtocol (&gEfiLegacyInterruptProtocolGuid, NULL, (VOID **)&Private->LegacyInterrupt);
890 ASSERT_EFI_ERROR (Status);
891
892 //
893 // Locate Memory Test Protocol if exists
894 //
895 Status = gBS->LocateProtocol (
896 &gEfiGenericMemTestProtocolGuid,
897 NULL,
898 (VOID **)&Private->GenericMemoryTest
899 );
900 ASSERT_EFI_ERROR (Status);
901
902 //
903 // Make sure all memory from 0-640K is tested
904 //
905 for (StartAddress = 0; StartAddress < 0xa0000; ) {
906 gDS->GetMemorySpaceDescriptor (StartAddress, &Descriptor);
907 if (Descriptor.GcdMemoryType != EfiGcdMemoryTypeReserved) {
908 StartAddress = Descriptor.BaseAddress + Descriptor.Length;
909 continue;
910 }
911
912 Length = MIN (Descriptor.Length, 0xa0000 - StartAddress);
913 Private->GenericMemoryTest->CompatibleRangeTest (
914 Private->GenericMemoryTest,
915 StartAddress,
916 Length
917 );
918 StartAddress = StartAddress + Length;
919 }
920
921 //
922 // Make sure all memory from 1MB to 16MB is tested and added to memory map
923 //
924 for (StartAddress = BASE_1MB; StartAddress < BASE_16MB; ) {
925 gDS->GetMemorySpaceDescriptor (StartAddress, &Descriptor);
926 if (Descriptor.GcdMemoryType != EfiGcdMemoryTypeReserved) {
927 StartAddress = Descriptor.BaseAddress + Descriptor.Length;
928 continue;
929 }
930
931 Length = MIN (Descriptor.Length, BASE_16MB - StartAddress);
932 Private->GenericMemoryTest->CompatibleRangeTest (
933 Private->GenericMemoryTest,
934 StartAddress,
935 Length
936 );
937 StartAddress = StartAddress + Length;
938 }
939
940 Private->Signature = LEGACY_BIOS_INSTANCE_SIGNATURE;
941
942 Private->LegacyBios.Int86 = LegacyBiosInt86;
943 Private->LegacyBios.FarCall86 = LegacyBiosFarCall86;
944 Private->LegacyBios.CheckPciRom = LegacyBiosCheckPciRom;
945 Private->LegacyBios.InstallPciRom = LegacyBiosInstallPciRom;
946 Private->LegacyBios.LegacyBoot = LegacyBiosLegacyBoot;
947 Private->LegacyBios.UpdateKeyboardLedStatus = LegacyBiosUpdateKeyboardLedStatus;
948 Private->LegacyBios.GetBbsInfo = LegacyBiosGetBbsInfo;
949 Private->LegacyBios.ShadowAllLegacyOproms = LegacyBiosShadowAllLegacyOproms;
950 Private->LegacyBios.PrepareToBootEfi = LegacyBiosPrepareToBootEfi;
951 Private->LegacyBios.GetLegacyRegion = LegacyBiosGetLegacyRegion;
952 Private->LegacyBios.CopyLegacyRegion = LegacyBiosCopyLegacyRegion;
953 Private->LegacyBios.BootUnconventionalDevice = LegacyBiosBootUnconventionalDevice;
954
955 Private->ImageHandle = ImageHandle;
956
957 //
958 // Enable read attribute of legacy region.
959 //
960 DecodeOn = TRUE;
961 Private->LegacyRegion->Decode (
962 Private->LegacyRegion,
963 0xc0000,
964 0x40000,
965 &Granularity,
966 &DecodeOn
967 );
968 //
969 // Set Cachebility for legacy region
970 // BUGBUG: Comments about this legacy region cacheability setting
971 // This setting will make D865GCHProduction CSM Unhappy
972 //
973 if (PcdGetBool (PcdLegacyBiosCacheLegacyRegion)) {
974 gDS->SetMemorySpaceAttributes (
975 0x0,
976 0xA0000,
977 EFI_MEMORY_WB
978 );
979 gDS->SetMemorySpaceAttributes (
980 0xc0000,
981 0x40000,
982 EFI_MEMORY_WB
983 );
984 }
985
986 gDS->SetMemorySpaceAttributes (
987 0xA0000,
988 0x20000,
989 EFI_MEMORY_UC
990 );
991
992 //
993 // Allocate 0 - 4K for real mode interrupt vectors and BDA.
994 //
995 AllocateLegacyMemory (
996 AllocateAddress,
997 EfiReservedMemoryType,
998 0,
999 1,
1000 &MemoryAddress
1001 );
1002 ASSERT (MemoryAddress == 0x000000000);
1003
1004 ClearPtr = (VOID *)((UINTN)0x0000);
1005
1006 //
1007 // Initialize region from 0x0000 to 4k. This initializes interrupt vector
1008 // range.
1009 //
1010 ACCESS_PAGE0_CODE (
1011 gBS->SetMem ((VOID *)ClearPtr, 0x400, INITIAL_VALUE_BELOW_1K);
1012 ZeroMem ((VOID *)((UINTN)ClearPtr + 0x400), 0xC00);
1013 );
1014
1015 //
1016 // Allocate pages for OPROM usage
1017 //
1018 MemorySize = PcdGet32 (PcdEbdaReservedMemorySize);
1019 ASSERT ((MemorySize & 0xFFF) == 0);
1020
1021 Status = AllocateLegacyMemory (
1022 AllocateAddress,
1023 EfiReservedMemoryType,
1024 CONVENTIONAL_MEMORY_TOP - MemorySize,
1025 EFI_SIZE_TO_PAGES (MemorySize),
1026 &MemoryAddress
1027 );
1028 ASSERT_EFI_ERROR (Status);
1029
1030 ZeroMem ((VOID *)((UINTN)MemoryAddress), MemorySize);
1031
1032 //
1033 // Allocate all 32k chunks from 0x60000 ~ 0x88000 for Legacy OPROMs that
1034 // don't use PMM but look for zeroed memory. Note that various non-BBS
1035 // OpROMs expect different areas to be free
1036 //
1037 EbdaReservedBaseAddress = MemoryAddress;
1038 MemoryAddress = PcdGet32 (PcdOpromReservedMemoryBase);
1039 MemorySize = PcdGet32 (PcdOpromReservedMemorySize);
1040 //
1041 // Check if base address and size for reserved memory are 4KB aligned.
1042 //
1043 ASSERT ((MemoryAddress & 0xFFF) == 0);
1044 ASSERT ((MemorySize & 0xFFF) == 0);
1045 //
1046 // Check if the reserved memory is below EBDA reserved range.
1047 //
1048 ASSERT ((MemoryAddress < EbdaReservedBaseAddress) && ((MemoryAddress + MemorySize - 1) < EbdaReservedBaseAddress));
1049 for (MemStart = MemoryAddress; MemStart < MemoryAddress + MemorySize; MemStart += 0x1000) {
1050 Status = AllocateLegacyMemory (
1051 AllocateAddress,
1052 EfiBootServicesCode,
1053 MemStart,
1054 1,
1055 &StartAddress
1056 );
1057 if (!EFI_ERROR (Status)) {
1058 MemoryPtr = (VOID *)((UINTN)StartAddress);
1059 ZeroMem (MemoryPtr, 0x1000);
1060 } else {
1061 DEBUG ((DEBUG_ERROR, "WARNING: Allocate legacy memory fail for SCSI card - %x\n", MemStart));
1062 }
1063 }
1064
1065 //
1066 // Allocate low PMM memory and zero it out
1067 //
1068 MemorySize = PcdGet32 (PcdLowPmmMemorySize);
1069 ASSERT ((MemorySize & 0xFFF) == 0);
1070 Status = AllocateLegacyMemory (
1071 AllocateMaxAddress,
1072 EfiBootServicesCode,
1073 CONVENTIONAL_MEMORY_TOP,
1074 EFI_SIZE_TO_PAGES (MemorySize),
1075 &MemoryAddressUnder1MB
1076 );
1077 ASSERT_EFI_ERROR (Status);
1078
1079 ZeroMem ((VOID *)((UINTN)MemoryAddressUnder1MB), MemorySize);
1080
1081 //
1082 // Allocate space for thunker and Init Thunker
1083 //
1084 Status = AllocateLegacyMemory (
1085 AllocateMaxAddress,
1086 EfiReservedMemoryType,
1087 CONVENTIONAL_MEMORY_TOP,
1088 (sizeof (LOW_MEMORY_THUNK) / EFI_PAGE_SIZE) + 2,
1089 &MemoryAddress
1090 );
1091 ASSERT_EFI_ERROR (Status);
1092 Private->IntThunk = (LOW_MEMORY_THUNK *)(UINTN)MemoryAddress;
1093 EfiToLegacy16InitTable = &Private->IntThunk->EfiToLegacy16InitTable;
1094 EfiToLegacy16InitTable->ThunkStart = (UINT32)(EFI_PHYSICAL_ADDRESS)(UINTN)MemoryAddress;
1095 EfiToLegacy16InitTable->ThunkSizeInBytes = (UINT32)(sizeof (LOW_MEMORY_THUNK));
1096
1097 Status = LegacyBiosInitializeThunk (Private);
1098 ASSERT_EFI_ERROR (Status);
1099
1100 //
1101 // Init the legacy memory map in memory < 1 MB.
1102 //
1103 EfiToLegacy16InitTable->BiosLessThan1MB = (UINT32)MemoryAddressUnder1MB;
1104 EfiToLegacy16InitTable->LowPmmMemory = (UINT32)MemoryAddressUnder1MB;
1105 EfiToLegacy16InitTable->LowPmmMemorySizeInBytes = MemorySize;
1106
1107 MemorySize = PcdGet32 (PcdHighPmmMemorySize);
1108 ASSERT ((MemorySize & 0xFFF) == 0);
1109 //
1110 // Allocate high PMM Memory under 16 MB
1111 //
1112 Status = AllocateLegacyMemory (
1113 AllocateMaxAddress,
1114 EfiBootServicesCode,
1115 0x1000000,
1116 EFI_SIZE_TO_PAGES (MemorySize),
1117 &MemoryAddress
1118 );
1119 if (EFI_ERROR (Status)) {
1120 //
1121 // If it fails, allocate high PMM Memory under 4GB
1122 //
1123 Status = AllocateLegacyMemory (
1124 AllocateMaxAddress,
1125 EfiBootServicesCode,
1126 0xFFFFFFFF,
1127 EFI_SIZE_TO_PAGES (MemorySize),
1128 &MemoryAddress
1129 );
1130 }
1131
1132 if (!EFI_ERROR (Status)) {
1133 EfiToLegacy16InitTable->HiPmmMemory = (UINT32)(EFI_PHYSICAL_ADDRESS)(UINTN)MemoryAddress;
1134 EfiToLegacy16InitTable->HiPmmMemorySizeInBytes = MemorySize;
1135 }
1136
1137 //
1138 // ShutdownAPs();
1139 //
1140 // Start the Legacy BIOS;
1141 //
1142 Status = ShadowAndStartLegacy16 (Private);
1143 if (EFI_ERROR (Status)) {
1144 return Status;
1145 }
1146
1147 //
1148 // Initialize interrupt redirection code and entries;
1149 // IDT Vectors 0x68-0x6f must be redirected to IDT Vectors 0x08-0x0f.
1150 //
1151 CopyMem (
1152 Private->IntThunk->InterruptRedirectionCode,
1153 (VOID *)(UINTN)InterruptRedirectionTemplate,
1154 sizeof (Private->IntThunk->InterruptRedirectionCode)
1155 );
1156
1157 //
1158 // Save Unexpected interrupt vector so can restore it just prior to boot
1159 //
1160 ACCESS_PAGE0_CODE (
1161 BaseVectorMaster = (UINT32 *)(sizeof (UINT32) * PROTECTED_MODE_BASE_VECTOR_MASTER);
1162 Private->BiosUnexpectedInt = BaseVectorMaster[0];
1163 IntRedirCode = (UINT32)(UINTN)Private->IntThunk->InterruptRedirectionCode;
1164 for (Index = 0; Index < 8; Index++) {
1165 BaseVectorMaster[Index] = (EFI_SEGMENT (IntRedirCode + Index * 4) << 16) | EFI_OFFSET (IntRedirCode + Index * 4);
1166 }
1167
1168 );
1169
1170 //
1171 // Save EFI value
1172 //
1173 Private->ThunkSeg = (UINT16)(EFI_SEGMENT (IntRedirCode));
1174
1175 //
1176 // Allocate reserved memory for SMBIOS table used in legacy boot if SMBIOS table exists
1177 //
1178 InstallSmbiosEventCallback (NULL, NULL);
1179
1180 //
1181 // Create callback function to update the size of reserved memory after LegacyBiosDxe starts
1182 //
1183 Status = gBS->CreateEventEx (
1184 EVT_NOTIFY_SIGNAL,
1185 TPL_NOTIFY,
1186 InstallSmbiosEventCallback,
1187 NULL,
1188 &gEfiSmbiosTableGuid,
1189 &InstallSmbiosEvent
1190 );
1191 ASSERT_EFI_ERROR (Status);
1192
1193 //
1194 // Create callback to update status of EndOfDxe, which is needed by NULL
1195 // pointer detection
1196 //
1197 Status = gBS->CreateEventEx (
1198 EVT_NOTIFY_SIGNAL,
1199 TPL_NOTIFY,
1200 ToggleEndOfDxeStatus,
1201 NULL,
1202 &gEfiEndOfDxeEventGroupGuid,
1203 &EndOfDxeEvent
1204 );
1205 ASSERT_EFI_ERROR (Status);
1206
1207 //
1208 // Make a new handle and install the protocol
1209 //
1210 Private->Handle = NULL;
1211 Status = gBS->InstallProtocolInterface (
1212 &Private->Handle,
1213 &gEfiLegacyBiosProtocolGuid,
1214 EFI_NATIVE_INTERFACE,
1215 &Private->LegacyBios
1216 );
1217 Private->Csm16PciInterfaceVersion = GetPciInterfaceVersion (Private);
1218
1219 DEBUG ((
1220 DEBUG_INFO,
1221 "CSM16 PCI BIOS Interface Version: %02x.%02x\n",
1222 (UINT8)(Private->Csm16PciInterfaceVersion >> 8),
1223 (UINT8)Private->Csm16PciInterfaceVersion
1224 ));
1225 ASSERT (Private->Csm16PciInterfaceVersion != 0);
1226 return Status;
1227 }