]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/Csm/LegacyBiosDxe/LegacyBios.c
OvmfPkg/LegacyBios: set NumberBbsEntries to the size of BbsTable
[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 //
570 // Populate entire table with BBS_IGNORE_ENTRY
571 //
572 EfiToLegacy16BootTable->NumberBbsEntries = MAX_BBS_ENTRIES;
573
574 for (Index = 0; Index < MAX_BBS_ENTRIES; Index++) {
575 BbsTable[Index].BootPriority = BBS_IGNORE_ENTRY;
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 return PciInterfaceVersion;
688 }
689
690 /**
691 Callback function to calculate SMBIOS table size, and allocate memory for SMBIOS table.
692 SMBIOS table will be copied into EfiReservedMemoryType memory in legacy boot path.
693
694 @param Event Event whose notification function is being invoked.
695 @param Context The pointer to the notification function's context,
696 which is implementation-dependent.
697
698 **/
699 VOID
700 EFIAPI
701 InstallSmbiosEventCallback (
702 IN EFI_EVENT Event,
703 IN VOID *Context
704 )
705 {
706 EFI_STATUS Status;
707 SMBIOS_TABLE_ENTRY_POINT *EntryPointStructure;
708
709 //
710 // Get SMBIOS table from EFI configuration table
711 //
712 Status = EfiGetSystemConfigurationTable (
713 &gEfiSmbiosTableGuid,
714 &mRuntimeSmbiosEntryPoint
715 );
716 if ((EFI_ERROR (Status)) || (mRuntimeSmbiosEntryPoint == NULL)) {
717 return;
718 }
719
720 EntryPointStructure = (SMBIOS_TABLE_ENTRY_POINT *) mRuntimeSmbiosEntryPoint;
721
722 //
723 // Allocate memory for SMBIOS Entry Point Structure.
724 // CSM framework spec requires SMBIOS table below 4GB in EFI_TO_COMPATIBILITY16_BOOT_TABLE.
725 //
726 if (mReserveSmbiosEntryPoint == 0) {
727 //
728 // Entrypoint structure with fixed size is allocated only once.
729 //
730 mReserveSmbiosEntryPoint = SIZE_4GB - 1;
731 Status = gBS->AllocatePages (
732 AllocateMaxAddress,
733 EfiReservedMemoryType,
734 EFI_SIZE_TO_PAGES ((UINTN) (EntryPointStructure->EntryPointLength)),
735 &mReserveSmbiosEntryPoint
736 );
737 if (EFI_ERROR (Status)) {
738 mReserveSmbiosEntryPoint = 0;
739 return;
740 }
741 DEBUG ((EFI_D_INFO, "Allocate memory for Smbios Entry Point Structure\n"));
742 }
743
744 if ((mStructureTableAddress != 0) &&
745 (mStructureTablePages < EFI_SIZE_TO_PAGES ((UINT32)EntryPointStructure->TableLength))) {
746 //
747 // If original buffer is not enough for the new SMBIOS table, free original buffer and re-allocate
748 //
749 gBS->FreePages (mStructureTableAddress, mStructureTablePages);
750 mStructureTableAddress = 0;
751 mStructureTablePages = 0;
752 DEBUG ((EFI_D_INFO, "Original size is not enough. Re-allocate the memory.\n"));
753 }
754
755 if (mStructureTableAddress == 0) {
756 //
757 // Allocate reserved memory below 4GB.
758 // Smbios spec requires the structure table is below 4GB.
759 //
760 mStructureTableAddress = SIZE_4GB - 1;
761 mStructureTablePages = EFI_SIZE_TO_PAGES (EntryPointStructure->TableLength);
762 Status = gBS->AllocatePages (
763 AllocateMaxAddress,
764 EfiReservedMemoryType,
765 mStructureTablePages,
766 &mStructureTableAddress
767 );
768 if (EFI_ERROR (Status)) {
769 gBS->FreePages (
770 mReserveSmbiosEntryPoint,
771 EFI_SIZE_TO_PAGES ((UINTN) (EntryPointStructure->EntryPointLength))
772 );
773 mReserveSmbiosEntryPoint = 0;
774 mStructureTableAddress = 0;
775 mStructureTablePages = 0;
776 return;
777 }
778 DEBUG ((EFI_D_INFO, "Allocate memory for Smbios Structure Table\n"));
779 }
780 }
781
782 /**
783 Callback function to toggle EndOfDxe status. NULL pointer detection needs
784 this status to decide if it's necessary to change attributes of page 0.
785
786 @param Event Event whose notification function is being invoked.
787 @param Context The pointer to the notification function's context,
788 which is implementation-dependent.
789
790 **/
791 VOID
792 EFIAPI
793 ToggleEndOfDxeStatus (
794 IN EFI_EVENT Event,
795 IN VOID *Context
796 )
797 {
798 mEndOfDxe = TRUE;
799 return;
800 }
801
802 /**
803 Install Driver to produce Legacy BIOS protocol.
804
805 @param ImageHandle Handle of driver image.
806 @param SystemTable Pointer to system table.
807
808 @retval EFI_SUCCESS Legacy BIOS protocol installed
809 @retval No protocol installed, unload driver.
810
811 **/
812 EFI_STATUS
813 EFIAPI
814 LegacyBiosInstall (
815 IN EFI_HANDLE ImageHandle,
816 IN EFI_SYSTEM_TABLE *SystemTable
817 )
818 {
819 EFI_STATUS Status;
820 LEGACY_BIOS_INSTANCE *Private;
821 EFI_TO_COMPATIBILITY16_INIT_TABLE *EfiToLegacy16InitTable;
822 EFI_PHYSICAL_ADDRESS MemoryAddress;
823 EFI_PHYSICAL_ADDRESS EbdaReservedBaseAddress;
824 VOID *MemoryPtr;
825 EFI_PHYSICAL_ADDRESS MemoryAddressUnder1MB;
826 UINTN Index;
827 UINT32 *BaseVectorMaster;
828 EFI_PHYSICAL_ADDRESS StartAddress;
829 UINT32 *ClearPtr;
830 EFI_PHYSICAL_ADDRESS MemStart;
831 UINT32 IntRedirCode;
832 UINT32 Granularity;
833 BOOLEAN DecodeOn;
834 UINT32 MemorySize;
835 EFI_GCD_MEMORY_SPACE_DESCRIPTOR Descriptor;
836 UINT64 Length;
837 UINT8 *SecureBoot;
838 EFI_EVENT InstallSmbiosEvent;
839 EFI_EVENT EndOfDxeEvent;
840
841 //
842 // Load this driver's image to memory
843 //
844 Status = RelocateImageUnder4GIfNeeded (ImageHandle, SystemTable);
845 if (EFI_ERROR (Status)) {
846 return Status;
847 }
848
849 //
850 // When UEFI Secure Boot is enabled, CSM module will not start any more.
851 //
852 SecureBoot = NULL;
853 GetEfiGlobalVariable2 (EFI_SECURE_BOOT_MODE_NAME, (VOID**)&SecureBoot, NULL);
854 if ((SecureBoot != NULL) && (*SecureBoot == SECURE_BOOT_MODE_ENABLE)) {
855 FreePool (SecureBoot);
856 return EFI_SECURITY_VIOLATION;
857 }
858
859 if (SecureBoot != NULL) {
860 FreePool (SecureBoot);
861 }
862
863 Private = &mPrivateData;
864 ZeroMem (Private, sizeof (LEGACY_BIOS_INSTANCE));
865
866 //
867 // Grab a copy of all the protocols we depend on. Any error would
868 // be a dispatcher bug!.
869 //
870 Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **) &Private->Cpu);
871 ASSERT_EFI_ERROR (Status);
872
873 Status = gBS->LocateProtocol (&gEfiTimerArchProtocolGuid, NULL, (VOID **) &Private->Timer);
874 ASSERT_EFI_ERROR (Status);
875
876 Status = gBS->LocateProtocol (&gEfiLegacyRegion2ProtocolGuid, NULL, (VOID **) &Private->LegacyRegion);
877 ASSERT_EFI_ERROR (Status);
878
879 Status = gBS->LocateProtocol (&gEfiLegacyBiosPlatformProtocolGuid, NULL, (VOID **) &Private->LegacyBiosPlatform);
880 ASSERT_EFI_ERROR (Status);
881
882 Status = gBS->LocateProtocol (&gEfiLegacy8259ProtocolGuid, NULL, (VOID **) &Private->Legacy8259);
883 ASSERT_EFI_ERROR (Status);
884
885 Status = gBS->LocateProtocol (&gEfiLegacyInterruptProtocolGuid, NULL, (VOID **) &Private->LegacyInterrupt);
886 ASSERT_EFI_ERROR (Status);
887
888 //
889 // Locate Memory Test Protocol if exists
890 //
891 Status = gBS->LocateProtocol (
892 &gEfiGenericMemTestProtocolGuid,
893 NULL,
894 (VOID **) &Private->GenericMemoryTest
895 );
896 ASSERT_EFI_ERROR (Status);
897
898 //
899 // Make sure all memory from 0-640K is tested
900 //
901 for (StartAddress = 0; StartAddress < 0xa0000; ) {
902 gDS->GetMemorySpaceDescriptor (StartAddress, &Descriptor);
903 if (Descriptor.GcdMemoryType != EfiGcdMemoryTypeReserved) {
904 StartAddress = Descriptor.BaseAddress + Descriptor.Length;
905 continue;
906 }
907 Length = MIN (Descriptor.Length, 0xa0000 - StartAddress);
908 Private->GenericMemoryTest->CompatibleRangeTest (
909 Private->GenericMemoryTest,
910 StartAddress,
911 Length
912 );
913 StartAddress = StartAddress + Length;
914 }
915 //
916 // Make sure all memory from 1MB to 16MB is tested and added to memory map
917 //
918 for (StartAddress = BASE_1MB; StartAddress < BASE_16MB; ) {
919 gDS->GetMemorySpaceDescriptor (StartAddress, &Descriptor);
920 if (Descriptor.GcdMemoryType != EfiGcdMemoryTypeReserved) {
921 StartAddress = Descriptor.BaseAddress + Descriptor.Length;
922 continue;
923 }
924 Length = MIN (Descriptor.Length, BASE_16MB - StartAddress);
925 Private->GenericMemoryTest->CompatibleRangeTest (
926 Private->GenericMemoryTest,
927 StartAddress,
928 Length
929 );
930 StartAddress = StartAddress + Length;
931 }
932
933 Private->Signature = LEGACY_BIOS_INSTANCE_SIGNATURE;
934
935 Private->LegacyBios.Int86 = LegacyBiosInt86;
936 Private->LegacyBios.FarCall86 = LegacyBiosFarCall86;
937 Private->LegacyBios.CheckPciRom = LegacyBiosCheckPciRom;
938 Private->LegacyBios.InstallPciRom = LegacyBiosInstallPciRom;
939 Private->LegacyBios.LegacyBoot = LegacyBiosLegacyBoot;
940 Private->LegacyBios.UpdateKeyboardLedStatus = LegacyBiosUpdateKeyboardLedStatus;
941 Private->LegacyBios.GetBbsInfo = LegacyBiosGetBbsInfo;
942 Private->LegacyBios.ShadowAllLegacyOproms = LegacyBiosShadowAllLegacyOproms;
943 Private->LegacyBios.PrepareToBootEfi = LegacyBiosPrepareToBootEfi;
944 Private->LegacyBios.GetLegacyRegion = LegacyBiosGetLegacyRegion;
945 Private->LegacyBios.CopyLegacyRegion = LegacyBiosCopyLegacyRegion;
946 Private->LegacyBios.BootUnconventionalDevice = LegacyBiosBootUnconventionalDevice;
947
948 Private->ImageHandle = ImageHandle;
949
950 //
951 // Enable read attribute of legacy region.
952 //
953 DecodeOn = TRUE;
954 Private->LegacyRegion->Decode (
955 Private->LegacyRegion,
956 0xc0000,
957 0x40000,
958 &Granularity,
959 &DecodeOn
960 );
961 //
962 // Set Cachebility for legacy region
963 // BUGBUG: Comments about this legacy region cacheability setting
964 // This setting will make D865GCHProduction CSM Unhappy
965 //
966 if (PcdGetBool (PcdLegacyBiosCacheLegacyRegion)) {
967 gDS->SetMemorySpaceAttributes (
968 0x0,
969 0xA0000,
970 EFI_MEMORY_WB
971 );
972 gDS->SetMemorySpaceAttributes (
973 0xc0000,
974 0x40000,
975 EFI_MEMORY_WB
976 );
977 }
978
979 gDS->SetMemorySpaceAttributes (
980 0xA0000,
981 0x20000,
982 EFI_MEMORY_UC
983 );
984
985 //
986 // Allocate 0 - 4K for real mode interupt vectors and BDA.
987 //
988 AllocateLegacyMemory (
989 AllocateAddress,
990 EfiReservedMemoryType,
991 0,
992 1,
993 &MemoryAddress
994 );
995 ASSERT (MemoryAddress == 0x000000000);
996
997 ClearPtr = (VOID *) ((UINTN) 0x0000);
998
999 //
1000 // Initialize region from 0x0000 to 4k. This initializes interrupt vector
1001 // range.
1002 //
1003 ACCESS_PAGE0_CODE (
1004 gBS->SetMem ((VOID *) ClearPtr, 0x400, INITIAL_VALUE_BELOW_1K);
1005 ZeroMem ((VOID *) ((UINTN)ClearPtr + 0x400), 0xC00);
1006 );
1007
1008 //
1009 // Allocate pages for OPROM usage
1010 //
1011 MemorySize = PcdGet32 (PcdEbdaReservedMemorySize);
1012 ASSERT ((MemorySize & 0xFFF) == 0);
1013
1014 Status = AllocateLegacyMemory (
1015 AllocateAddress,
1016 EfiReservedMemoryType,
1017 CONVENTIONAL_MEMORY_TOP - MemorySize,
1018 EFI_SIZE_TO_PAGES (MemorySize),
1019 &MemoryAddress
1020 );
1021 ASSERT_EFI_ERROR (Status);
1022
1023 ZeroMem ((VOID *) ((UINTN) MemoryAddress), MemorySize);
1024
1025 //
1026 // Allocate all 32k chunks from 0x60000 ~ 0x88000 for Legacy OPROMs that
1027 // don't use PMM but look for zeroed memory. Note that various non-BBS
1028 // OpROMs expect different areas to be free
1029 //
1030 EbdaReservedBaseAddress = MemoryAddress;
1031 MemoryAddress = PcdGet32 (PcdOpromReservedMemoryBase);
1032 MemorySize = PcdGet32 (PcdOpromReservedMemorySize);
1033 //
1034 // Check if base address and size for reserved memory are 4KB aligned.
1035 //
1036 ASSERT ((MemoryAddress & 0xFFF) == 0);
1037 ASSERT ((MemorySize & 0xFFF) == 0);
1038 //
1039 // Check if the reserved memory is below EBDA reserved range.
1040 //
1041 ASSERT ((MemoryAddress < EbdaReservedBaseAddress) && ((MemoryAddress + MemorySize - 1) < EbdaReservedBaseAddress));
1042 for (MemStart = MemoryAddress; MemStart < MemoryAddress + MemorySize; MemStart += 0x1000) {
1043 Status = AllocateLegacyMemory (
1044 AllocateAddress,
1045 EfiBootServicesCode,
1046 MemStart,
1047 1,
1048 &StartAddress
1049 );
1050 if (!EFI_ERROR (Status)) {
1051 MemoryPtr = (VOID *) ((UINTN) StartAddress);
1052 ZeroMem (MemoryPtr, 0x1000);
1053 } else {
1054 DEBUG ((EFI_D_ERROR, "WARNING: Allocate legacy memory fail for SCSI card - %x\n", MemStart));
1055 }
1056 }
1057
1058 //
1059 // Allocate low PMM memory and zero it out
1060 //
1061 MemorySize = PcdGet32 (PcdLowPmmMemorySize);
1062 ASSERT ((MemorySize & 0xFFF) == 0);
1063 Status = AllocateLegacyMemory (
1064 AllocateMaxAddress,
1065 EfiBootServicesCode,
1066 CONVENTIONAL_MEMORY_TOP,
1067 EFI_SIZE_TO_PAGES (MemorySize),
1068 &MemoryAddressUnder1MB
1069 );
1070 ASSERT_EFI_ERROR (Status);
1071
1072 ZeroMem ((VOID *) ((UINTN) MemoryAddressUnder1MB), MemorySize);
1073
1074 //
1075 // Allocate space for thunker and Init Thunker
1076 //
1077 Status = AllocateLegacyMemory (
1078 AllocateMaxAddress,
1079 EfiReservedMemoryType,
1080 CONVENTIONAL_MEMORY_TOP,
1081 (sizeof (LOW_MEMORY_THUNK) / EFI_PAGE_SIZE) + 2,
1082 &MemoryAddress
1083 );
1084 ASSERT_EFI_ERROR (Status);
1085 Private->IntThunk = (LOW_MEMORY_THUNK *) (UINTN) MemoryAddress;
1086 EfiToLegacy16InitTable = &Private->IntThunk->EfiToLegacy16InitTable;
1087 EfiToLegacy16InitTable->ThunkStart = (UINT32) (EFI_PHYSICAL_ADDRESS) (UINTN) MemoryAddress;
1088 EfiToLegacy16InitTable->ThunkSizeInBytes = (UINT32) (sizeof (LOW_MEMORY_THUNK));
1089
1090 Status = LegacyBiosInitializeThunk (Private);
1091 ASSERT_EFI_ERROR (Status);
1092
1093 //
1094 // Init the legacy memory map in memory < 1 MB.
1095 //
1096 EfiToLegacy16InitTable->BiosLessThan1MB = (UINT32) MemoryAddressUnder1MB;
1097 EfiToLegacy16InitTable->LowPmmMemory = (UINT32) MemoryAddressUnder1MB;
1098 EfiToLegacy16InitTable->LowPmmMemorySizeInBytes = MemorySize;
1099
1100 MemorySize = PcdGet32 (PcdHighPmmMemorySize);
1101 ASSERT ((MemorySize & 0xFFF) == 0);
1102 //
1103 // Allocate high PMM Memory under 16 MB
1104 //
1105 Status = AllocateLegacyMemory (
1106 AllocateMaxAddress,
1107 EfiBootServicesCode,
1108 0x1000000,
1109 EFI_SIZE_TO_PAGES (MemorySize),
1110 &MemoryAddress
1111 );
1112 if (EFI_ERROR (Status)) {
1113 //
1114 // If it fails, allocate high PMM Memory under 4GB
1115 //
1116 Status = AllocateLegacyMemory (
1117 AllocateMaxAddress,
1118 EfiBootServicesCode,
1119 0xFFFFFFFF,
1120 EFI_SIZE_TO_PAGES (MemorySize),
1121 &MemoryAddress
1122 );
1123 }
1124 if (!EFI_ERROR (Status)) {
1125 EfiToLegacy16InitTable->HiPmmMemory = (UINT32) (EFI_PHYSICAL_ADDRESS) (UINTN) MemoryAddress;
1126 EfiToLegacy16InitTable->HiPmmMemorySizeInBytes = MemorySize;
1127 }
1128
1129 //
1130 // ShutdownAPs();
1131 //
1132 // Start the Legacy BIOS;
1133 //
1134 Status = ShadowAndStartLegacy16 (Private);
1135 if (EFI_ERROR (Status)) {
1136 return Status;
1137 }
1138 //
1139 // Initialize interrupt redirection code and entries;
1140 // IDT Vectors 0x68-0x6f must be redirected to IDT Vectors 0x08-0x0f.
1141 //
1142 CopyMem (
1143 Private->IntThunk->InterruptRedirectionCode,
1144 (VOID *) (UINTN) InterruptRedirectionTemplate,
1145 sizeof (Private->IntThunk->InterruptRedirectionCode)
1146 );
1147
1148 //
1149 // Save Unexpected interrupt vector so can restore it just prior to boot
1150 //
1151 ACCESS_PAGE0_CODE (
1152 BaseVectorMaster = (UINT32 *) (sizeof (UINT32) * PROTECTED_MODE_BASE_VECTOR_MASTER);
1153 Private->BiosUnexpectedInt = BaseVectorMaster[0];
1154 IntRedirCode = (UINT32) (UINTN) Private->IntThunk->InterruptRedirectionCode;
1155 for (Index = 0; Index < 8; Index++) {
1156 BaseVectorMaster[Index] = (EFI_SEGMENT (IntRedirCode + Index * 4) << 16) | EFI_OFFSET (IntRedirCode + Index * 4);
1157 }
1158 );
1159
1160 //
1161 // Save EFI value
1162 //
1163 Private->ThunkSeg = (UINT16) (EFI_SEGMENT (IntRedirCode));
1164
1165 //
1166 // Allocate reserved memory for SMBIOS table used in legacy boot if SMBIOS table exists
1167 //
1168 InstallSmbiosEventCallback (NULL, NULL);
1169
1170 //
1171 // Create callback function to update the size of reserved memory after LegacyBiosDxe starts
1172 //
1173 Status = gBS->CreateEventEx (
1174 EVT_NOTIFY_SIGNAL,
1175 TPL_NOTIFY,
1176 InstallSmbiosEventCallback,
1177 NULL,
1178 &gEfiSmbiosTableGuid,
1179 &InstallSmbiosEvent
1180 );
1181 ASSERT_EFI_ERROR (Status);
1182
1183 //
1184 // Create callback to update status of EndOfDxe, which is needed by NULL
1185 // pointer detection
1186 //
1187 Status = gBS->CreateEventEx (
1188 EVT_NOTIFY_SIGNAL,
1189 TPL_NOTIFY,
1190 ToggleEndOfDxeStatus,
1191 NULL,
1192 &gEfiEndOfDxeEventGroupGuid,
1193 &EndOfDxeEvent
1194 );
1195 ASSERT_EFI_ERROR (Status);
1196
1197 //
1198 // Make a new handle and install the protocol
1199 //
1200 Private->Handle = NULL;
1201 Status = gBS->InstallProtocolInterface (
1202 &Private->Handle,
1203 &gEfiLegacyBiosProtocolGuid,
1204 EFI_NATIVE_INTERFACE,
1205 &Private->LegacyBios
1206 );
1207 Private->Csm16PciInterfaceVersion = GetPciInterfaceVersion (Private);
1208
1209 DEBUG ((EFI_D_INFO, "CSM16 PCI BIOS Interface Version: %02x.%02x\n",
1210 (UINT8) (Private->Csm16PciInterfaceVersion >> 8),
1211 (UINT8) Private->Csm16PciInterfaceVersion
1212 ));
1213 ASSERT (Private->Csm16PciInterfaceVersion != 0);
1214 return Status;
1215 }