1 /** @file
2   Populate the BIOS_TABLES_TEST structure.
3 
4   Copyright (C) 2019, Red Hat, Inc.
5 
6   This program and the accompanying materials are licensed and made available
7   under the terms and conditions of the BSD License that accompanies this
8   distribution. The 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, WITHOUT
12   WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
13 **/
14 
15 #include <Guid/Acpi.h>
16 #include <Guid/BiosTablesTest.h>
17 #include <Library/BaseLib.h>
18 #include <Library/BaseMemoryLib.h>
19 #include <Library/MemoryAllocationLib.h>
20 #include <Library/UefiBootServicesTableLib.h>
21 #include <Library/UefiLib.h>
22 
23 /**
24   Wait for a keypress with a message that the application is about to exit.
25 **/
26 STATIC
27 VOID
28 WaitForExitKeyPress (
29   VOID
30   )
31 {
32   EFI_STATUS    Status;
33   UINTN         Idx;
34   EFI_INPUT_KEY Key;
35 
36   if (gST->ConIn == NULL) {
37     return;
38   }
39   AsciiPrint ("%a: press any key to exit\n", gEfiCallerBaseName);
40   Status = gBS->WaitForEvent (1, &gST->ConIn->WaitForKey, &Idx);
41   if (EFI_ERROR (Status)) {
42     return;
43   }
44   gST->ConIn->ReadKeyStroke (gST->ConIn, &Key);
45 }
46 
47 EFI_STATUS
48 EFIAPI
49 BiosTablesTestMain (
50   IN EFI_HANDLE       ImageHandle,
51   IN EFI_SYSTEM_TABLE *SystemTable
52   )
53 {
54   VOID                          *Pages;
55   volatile BIOS_TABLES_TEST     *BiosTablesTest;
56   CONST VOID                    *Rsdp10;
57   CONST VOID                    *Rsdp20;
58   CONST EFI_CONFIGURATION_TABLE *ConfigTable;
59   CONST EFI_CONFIGURATION_TABLE *ConfigTablesEnd;
60   volatile EFI_GUID             *InverseSignature;
61   UINTN                         Idx;
62 
63   Pages = AllocateAlignedPages (EFI_SIZE_TO_PAGES (sizeof *BiosTablesTest),
64             SIZE_1MB);
65   if (Pages == NULL) {
66     AsciiErrorPrint ("%a: AllocateAlignedPages() failed\n",
67       gEfiCallerBaseName);
68     //
69     // Assuming the application was launched by the boot manager as a boot
70     // loader, exiting with error will cause the boot manager to proceed with
71     // the remaining boot options. If there are no other boot options, the boot
72     // manager menu will be pulled up. Give the user a chance to read the error
73     // message.
74     //
75     WaitForExitKeyPress ();
76     return EFI_OUT_OF_RESOURCES;
77   }
78 
79   //
80   // Locate both gEfiAcpi10TableGuid and gEfiAcpi20TableGuid config tables in
81   // one go.
82   //
83   Rsdp10 = NULL;
84   Rsdp20 = NULL;
85   ConfigTable = gST->ConfigurationTable;
86   ConfigTablesEnd = gST->ConfigurationTable + gST->NumberOfTableEntries;
87   while ((Rsdp10 == NULL || Rsdp20 == NULL) && ConfigTable < ConfigTablesEnd) {
88     if (CompareGuid (&ConfigTable->VendorGuid, &gEfiAcpi10TableGuid)) {
89       Rsdp10 = ConfigTable->VendorTable;
90     } else if (CompareGuid (&ConfigTable->VendorGuid, &gEfiAcpi20TableGuid)) {
91       Rsdp20 = ConfigTable->VendorTable;
92     }
93     ++ConfigTable;
94   }
95 
96   AsciiPrint ("%a: BiosTablesTest=%p Rsdp10=%p Rsdp20=%p\n",
97     gEfiCallerBaseName, Pages, Rsdp10, Rsdp20);
98 
99   //
100   // Store the RSD PTR address(es) first, then the signature second.
101   //
102   BiosTablesTest = Pages;
103   BiosTablesTest->Rsdp10 = (UINTN)Rsdp10;
104   BiosTablesTest->Rsdp20 = (UINTN)Rsdp20;
105 
106   MemoryFence();
107 
108   InverseSignature = &BiosTablesTest->InverseSignatureGuid;
109   InverseSignature->Data1  = gBiosTablesTestGuid.Data1;
110   InverseSignature->Data1 ^= MAX_UINT32;
111   InverseSignature->Data2  = gBiosTablesTestGuid.Data2;
112   InverseSignature->Data2 ^= MAX_UINT16;
113   InverseSignature->Data3  = gBiosTablesTestGuid.Data3;
114   InverseSignature->Data3 ^= MAX_UINT16;
115   for (Idx = 0; Idx < sizeof InverseSignature->Data4; ++Idx) {
116     InverseSignature->Data4[Idx]  = gBiosTablesTestGuid.Data4[Idx];
117     InverseSignature->Data4[Idx] ^= MAX_UINT8;
118   }
119 
120   //
121   // The wait below has dual purpose. First, it blocks the application without
122   // wasting VCPU cycles while the hypervisor is scanning guest RAM. Second,
123   // assuming the application was launched by the boot manager as a boot
124   // loader, exiting the app with success causes the boot manager to pull up
125   // the boot manager menu at once (regardless of other boot options); the wait
126   // gives the user a chance to read the info printed above.
127   //
128   WaitForExitKeyPress ();
129   return EFI_SUCCESS;
130 }
131