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