]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe/ArmJunoDxe.c
ArmPlatformPkg/ArmJunoPkg: Create two default boot entries on first boot on Juno R1
[mirror_edk2.git] / ArmPlatformPkg / ArmJunoPkg / Drivers / ArmJunoDxe / ArmJunoDxe.c
1 /** @file
2 *
3 * Copyright (c) 2013-2015, ARM Limited. All rights reserved.
4 *
5 * This program and the accompanying materials
6 * are licensed and made available under the terms and conditions of the BSD License
7 * which accompanies this distribution. The full text of the license may be found at
8 * http://opensource.org/licenses/bsd-license.php
9 *
10 * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 *
13 **/
14
15 #include "ArmJunoDxeInternal.h"
16
17 #include <Protocol/DevicePathFromText.h>
18
19 #include <Guid/GlobalVariable.h>
20
21 #include <Library/ArmShellCmdLib.h>
22 #include <Library/AcpiLib.h>
23 #include <Library/BaseMemoryLib.h>
24 #include <Library/DevicePathLib.h>
25 #include <Library/MemoryAllocationLib.h>
26 #include <Library/UefiRuntimeServicesTableLib.h>
27
28 // This GUID must match the FILE_GUID in ArmPlatformPkg/ArmJunoPkg/AcpiTables/AcpiTables.inf
29 STATIC CONST EFI_GUID mJunoAcpiTableFile = { 0xa1dd808e, 0x1e95, 0x4399, { 0xab, 0xc0, 0x65, 0x3c, 0x82, 0xe8, 0x53, 0x0c } };
30
31 /**
32 * Build and Set UEFI Variable Boot####
33 *
34 * @param BootVariableName Name of the UEFI Variable
35 * @param Attributes 'Attributes' for the Boot#### variable as per UEFI spec
36 * @param BootDescription Description of the Boot#### variable
37 * @param DevicePath EFI Device Path of the EFI Application to boot
38 * @param OptionalData Parameters to pass to the EFI application
39 * @param OptionalDataSize Size of the parameters to pass to the EFI application
40 *
41 * @return EFI_OUT_OF_RESOURCES A memory allocation failed
42 * @return Return value of RT.SetVariable
43 */
44 STATIC
45 EFI_STATUS
46 BootOptionCreate (
47 IN CHAR16 BootVariableName[9],
48 IN UINT32 Attributes,
49 IN CHAR16* BootDescription,
50 IN EFI_DEVICE_PATH_PROTOCOL* DevicePath,
51 IN UINT8* OptionalData,
52 IN UINTN OptionalDataSize
53 )
54 {
55 UINTN VariableSize;
56 UINT8 *Variable;
57 UINT8 *VariablePtr;
58 UINTN FilePathListLength;
59 UINTN BootDescriptionSize;
60
61 FilePathListLength = GetDevicePathSize (DevicePath);
62 BootDescriptionSize = StrSize (BootDescription);
63
64 // Each Boot#### variable is built as follow:
65 // UINT32 Attributes
66 // UINT16 FilePathListLength
67 // CHAR16* Description
68 // EFI_DEVICE_PATH_PROTOCOL FilePathList[]
69 // UINT8 OptionalData[]
70 VariableSize = sizeof (UINT32) + sizeof (UINT16) +
71 BootDescriptionSize + FilePathListLength + OptionalDataSize;
72 Variable = AllocateZeroPool (VariableSize);
73 if (Variable == NULL) {
74 return EFI_OUT_OF_RESOURCES;
75 }
76
77 // 'Attributes' field
78 *(UINT32*)Variable = Attributes;
79 // 'FilePathListLength' field
80 VariablePtr = Variable + sizeof (UINT32);
81 *(UINT16*)VariablePtr = FilePathListLength;
82 // 'Description' field
83 VariablePtr += sizeof (UINT16);
84 CopyMem (VariablePtr, BootDescription, BootDescriptionSize);
85 // 'FilePathList' field
86 VariablePtr += BootDescriptionSize;
87 CopyMem (VariablePtr, DevicePath, FilePathListLength);
88 // 'OptionalData' field
89 VariablePtr += FilePathListLength;
90 CopyMem (VariablePtr, OptionalData, OptionalDataSize);
91
92 return gRT->SetVariable (
93 BootVariableName,
94 &gEfiGlobalVariableGuid,
95 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
96 VariableSize, Variable
97 );
98 }
99
100 EFI_STATUS
101 EFIAPI
102 ArmJunoEntryPoint (
103 IN EFI_HANDLE ImageHandle,
104 IN EFI_SYSTEM_TABLE *SystemTable
105 )
106 {
107 EFI_STATUS Status;
108 EFI_PHYSICAL_ADDRESS HypBase;
109 CHAR16 *TextDevicePath;
110 UINTN TextDevicePathSize;
111 VOID *Buffer;
112 UINT32 Midr;
113 UINT32 CpuType;
114 UINT32 CpuRev;
115 BOOLEAN IsJunoR1;
116
117 Status = PciEmulationEntryPoint ();
118 if (EFI_ERROR (Status)) {
119 return Status;
120 }
121
122 //
123 // If a hypervisor has been declared then we need to make sure its region is protected at runtime
124 //
125 // Note: This code is only a workaround for our dummy hypervisor (ArmPkg/Extra/AArch64ToAArch32Shim/)
126 // that does not set up (yet) the stage 2 translation table to hide its own memory to EL1.
127 //
128 if (FixedPcdGet32 (PcdHypFvSize) != 0) {
129 // Ensure the hypervisor region is strictly contained into a EFI_PAGE_SIZE-aligned region.
130 // The memory must be a multiple of EFI_PAGE_SIZE to ensure we do not reserve more memory than the hypervisor itself.
131 // A UEFI Runtime region size granularity cannot be smaller than EFI_PAGE_SIZE. If the hypervisor size is not rounded
132 // to this size then there is a risk some non-runtime memory could be visible to the OS view.
133 if (((FixedPcdGet32 (PcdHypFvSize) & EFI_PAGE_MASK) == 0) && ((FixedPcdGet32 (PcdHypFvBaseAddress) & EFI_PAGE_MASK) == 0)) {
134 // The memory needs to be declared because the DXE core marked it as reserved and removed it from the memory space
135 // as it contains the Firmware.
136 Status = gDS->AddMemorySpace (
137 EfiGcdMemoryTypeSystemMemory,
138 FixedPcdGet32 (PcdHypFvBaseAddress), FixedPcdGet32 (PcdHypFvSize),
139 EFI_MEMORY_WB | EFI_MEMORY_RUNTIME
140 );
141 if (!EFI_ERROR (Status)) {
142 // We allocate the memory to ensure it is marked as runtime memory
143 HypBase = FixedPcdGet32 (PcdHypFvBaseAddress);
144 Status = gBS->AllocatePages (AllocateAddress, EfiRuntimeServicesCode,
145 EFI_SIZE_TO_PAGES (FixedPcdGet32 (PcdHypFvSize)), &HypBase);
146 }
147 } else {
148 // The hypervisor must be contained into a EFI_PAGE_SIZE-aligned region and its size must also be aligned
149 // on a EFI_PAGE_SIZE boundary (ie: 4KB).
150 Status = EFI_UNSUPPORTED;
151 ASSERT_EFI_ERROR (Status);
152 }
153
154 if (EFI_ERROR (Status)) {
155 return Status;
156 }
157 }
158
159 // Install dynamic Shell command to run baremetal binaries.
160 Status = ShellDynCmdRunAxfInstall (ImageHandle);
161 if (EFI_ERROR (Status)) {
162 DEBUG ((EFI_D_ERROR, "ArmJunoDxe: Failed to install ShellDynCmdRunAxf\n"));
163 }
164
165 //
166 // Set up the device path to the FDT.
167 // We detect whether we are running on a Juno r0 or Juno r1 board at
168 // runtime by checking the value of the MIDR register.
169 //
170
171 Midr = ArmReadMidr ();
172 CpuType = (Midr >> ARM_CPU_TYPE_SHIFT) & ARM_CPU_TYPE_MASK;
173 CpuRev = Midr & ARM_CPU_REV_MASK;
174 TextDevicePath = NULL;
175 IsJunoR1 = FALSE;
176
177 switch (CpuType) {
178 case ARM_CPU_TYPE_A53:
179 if (CpuRev == ARM_CPU_REV (0, 0)) {
180 TextDevicePath = (CHAR16*)FixedPcdGetPtr (PcdJunoR0FdtDevicePath);
181 } else if (CpuRev == ARM_CPU_REV (0, 3)) {
182 TextDevicePath = (CHAR16*)FixedPcdGetPtr (PcdJunoR1A57x2FdtDevicePath);
183 IsJunoR1 = TRUE;
184 }
185 break;
186
187 case ARM_CPU_TYPE_A57:
188 if (CpuRev == ARM_CPU_REV (0, 0)) {
189 TextDevicePath = (CHAR16*)FixedPcdGetPtr (PcdJunoR0FdtDevicePath);
190 } else if (CpuRev == ARM_CPU_REV (1, 1)) {
191 TextDevicePath = (CHAR16*)FixedPcdGetPtr (PcdJunoR1A57x2FdtDevicePath);
192 IsJunoR1 = TRUE;
193 }
194 }
195
196 if (TextDevicePath != NULL) {
197 TextDevicePathSize = StrSize (TextDevicePath);
198 Buffer = PcdSetPtr (PcdFdtDevicePaths, &TextDevicePathSize, TextDevicePath);
199 Status = (Buffer != NULL) ? EFI_SUCCESS : EFI_BUFFER_TOO_SMALL;
200 } else {
201 Status = EFI_NOT_FOUND;
202 }
203
204 if (EFI_ERROR (Status)) {
205 DEBUG (
206 (EFI_D_ERROR,
207 "ArmJunoDxe: Setting of FDT device path in PcdFdtDevicePaths failed - %r\n", Status)
208 );
209 return Status;
210 }
211
212 // Try to install the ACPI Tables
213 Status = LocateAndInstallAcpiFromFv (&mJunoAcpiTableFile);
214
215 //
216 // If Juno R1 and it is the first boot then default boot entries will be created
217 //
218 if (IsJunoR1) {
219 CONST CHAR16* ExtraBootArgument = L" dtb=juno-r1-ca57x2_ca53x4.dtb";
220 UINTN Size;
221 EFI_DEVICE_PATH_FROM_TEXT_PROTOCOL* EfiDevicePathFromTextProtocol;
222 EFI_DEVICE_PATH* BootDevicePath;
223 CHAR16* DefaultBootArgument;
224 UINTN DefaultBootArgumentSize;
225 CHAR16* DefaultBootArgument2;
226 UINTN DefaultBootArgument2Size;
227 UINT16 BootOrder[2];
228
229 // Because the driver has a dependency on gEfiVariable(Write)ArchProtocolGuid (see [Depex]
230 // section of the INF file), we know we can safely access the UEFI Variable at that stage.
231 Size = 0;
232 Status = gRT->GetVariable (L"BootOrder", &gEfiGlobalVariableGuid, NULL, &Size, NULL);
233 if (Status == EFI_NOT_FOUND) {
234 Status = gBS->LocateProtocol (&gEfiDevicePathFromTextProtocolGuid, NULL, (VOID **)&EfiDevicePathFromTextProtocol);
235 if (EFI_ERROR (Status)) {
236 // You must provide an implementation of DevicePathFromTextProtocol in your firmware (eg: DevicePathDxe)
237 DEBUG ((EFI_D_ERROR, "Error: Require DevicePathFromTextProtocol\n"));
238 return Status;
239 }
240 // We use the same default kernel
241 BootDevicePath = EfiDevicePathFromTextProtocol->ConvertTextToDevicePath ((CHAR16*)PcdGetPtr (PcdDefaultBootDevicePath));
242
243 // Create the entry if the Default values are correct
244 if (BootDevicePath != NULL) {
245 DefaultBootArgument = (CHAR16*)PcdGetPtr (PcdDefaultBootArgument);
246 DefaultBootArgumentSize = StrSize (DefaultBootArgument);
247 DefaultBootArgument2Size = DefaultBootArgumentSize + StrSize (ExtraBootArgument);
248
249 DefaultBootArgument2 = AllocatePool (DefaultBootArgument2Size);
250 if (DefaultBootArgument2 == NULL) {
251 FreePool (BootDevicePath);
252 return EFI_OUT_OF_RESOURCES;
253 }
254 CopyMem (DefaultBootArgument2, DefaultBootArgument, DefaultBootArgumentSize);
255 CopyMem ((UINT8*)DefaultBootArgument2 + (StrLen (DefaultBootArgument2) * sizeof (CHAR16)), ExtraBootArgument, StrSize (ExtraBootArgument));
256
257 // Create Boot0001 environment variable
258 Status = BootOptionCreate (L"Boot0001", LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_BOOT,
259 L"Linux with A57x2", BootDevicePath,
260 (UINT8*)DefaultBootArgument, DefaultBootArgumentSize);
261 ASSERT_EFI_ERROR (Status);
262
263 // Create Boot0002 environment variable
264 Status = BootOptionCreate (L"Boot0002", LOAD_OPTION_ACTIVE | LOAD_OPTION_CATEGORY_BOOT,
265 L"Linux with A57x2_A53x4", BootDevicePath,
266 (UINT8*)DefaultBootArgument2, DefaultBootArgument2Size);
267 ASSERT_EFI_ERROR (Status);
268
269 // Add the new Boot Index to the list
270 BootOrder[0] = 1; // Boot0001
271 BootOrder[1] = 2; // Boot0002
272 Status = gRT->SetVariable (
273 L"BootOrder",
274 &gEfiGlobalVariableGuid,
275 EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
276 sizeof (BootOrder),
277 BootOrder
278 );
279
280 FreePool (BootDevicePath);
281 FreePool (DefaultBootArgument2);
282 } else {
283 Status = EFI_UNSUPPORTED;
284 }
285 }
286 }
287
288 return Status;
289 }