1/** @file
2
3Copyright (c) 2006 - 2013, Intel Corporation. All rights reserved.<BR>
4
5This program and the accompanying materials
6are licensed and made available under the terms and conditions
7of the BSD License which accompanies this distribution.  The
8full text of the license may be found at
9http://opensource.org/licenses/bsd-license.php
10
11THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
12WITHOUT 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//
30LEGACY_BIOS_INSTANCE  mPrivateData;
31
32//
33// The SMBIOS table in EfiRuntimeServicesData memory
34//
35VOID                  *mRuntimeSmbiosEntryPoint = NULL;
36
37//
38// The SMBIOS table in EfiReservedMemoryType memory
39//
40EFI_PHYSICAL_ADDRESS  mReserveSmbiosEntryPoint = 0;
41EFI_PHYSICAL_ADDRESS  mStructureTableAddress   = 0;
42UINTN                 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**/
57EFI_STATUS
58AllocateLegacyMemory (
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**/
113EFI_STATUS
114EFIAPI
115LegacyBiosGetLegacyRegion (
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**/
174EFI_STATUS
175EFIAPI
176LegacyBiosCopyLegacyRegion (
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**/
218EFI_STATUS
219ShadowAndStartLegacy16 (
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**/
598EFI_STATUS
599EFIAPI
600LegacyBiosShadowAllLegacyOproms (
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**/
644UINT16
645GetPciInterfaceVersion (
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**/
685VOID
686EFIAPI
687InstallSmbiosEventCallback (
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**/
778EFI_STATUS
779EFIAPI
780LegacyBiosInstall (
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