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