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