]>
Commit | Line | Data |
---|---|---|
1bfda055 | 1 | /** @file |
2 | * | |
995d9676 | 3 | * Copyright (c) 2011-2012, ARM Limited. All rights reserved. |
1bfda055 | 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 "BdsInternal.h" | |
b34e4db3 | 16 | #include "BdsLinuxLoader.h" |
1bfda055 | 17 | |
a355a365 | 18 | #define ALIGN32_BELOW(addr) ALIGN_POINTER(addr - 32,32) |
1bfda055 | 19 | |
1bfda055 | 20 | STATIC |
21 | EFI_STATUS | |
a355a365 | 22 | PreparePlatformHardware ( |
23 | VOID | |
24 | ) | |
1bfda055 | 25 | { |
26 | //Note: Interrupts will be disabled by the GIC driver when ExitBootServices() will be called. | |
27 | ||
76d17c31 | 28 | // Clean, invalidate, disable data cache |
1bfda055 | 29 | ArmDisableDataCache(); |
ed71a22c | 30 | ArmCleanInvalidateDataCache(); |
1bfda055 | 31 | |
32 | // Invalidate and disable the Instruction cache | |
1bfda055 | 33 | ArmDisableInstructionCache (); |
ed71a22c | 34 | ArmInvalidateInstructionCache (); |
1bfda055 | 35 | |
76d17c31 | 36 | // Turn off MMU |
1bfda055 | 37 | ArmDisableMmu(); |
38 | ||
39 | return EFI_SUCCESS; | |
40 | } | |
41 | ||
76d17c31 | 42 | STATIC |
a355a365 | 43 | EFI_STATUS |
76d17c31 | 44 | StartLinux ( |
45 | IN EFI_PHYSICAL_ADDRESS LinuxImage, | |
46 | IN UINTN LinuxImageSize, | |
47 | IN EFI_PHYSICAL_ADDRESS KernelParamsAddress, | |
48 | IN UINTN KernelParamsSize, | |
49 | IN UINT32 MachineType | |
a355a365 | 50 | ) |
51 | { | |
52 | EFI_STATUS Status; | |
a355a365 | 53 | LINUX_KERNEL LinuxKernel; |
1bfda055 | 54 | |
a355a365 | 55 | // Shut down UEFI boot services. ExitBootServices() will notify every driver that created an event on |
56 | // ExitBootServices event. Example the Interrupt DXE driver will disable the interrupts on this event. | |
57 | Status = ShutdownUefiBootServices (); | |
58 | if(EFI_ERROR(Status)) { | |
59 | DEBUG((EFI_D_ERROR,"ERROR: Can not shutdown UEFI boot services. Status=0x%X\n", Status)); | |
60 | goto Exit; | |
61 | } | |
1bfda055 | 62 | |
a355a365 | 63 | // Move the kernel parameters to any address inside the first 1MB. |
64 | // This is necessary because the ARM Linux kernel requires | |
65 | // the FTD / ATAG List to reside entirely inside the first 1MB of | |
66 | // physical memory. | |
0a6653bc | 67 | //Note: There is no requirement on the alignment |
68 | if (MachineType != ARM_FDT_MACHINE_TYPE) { | |
69 | if (((UINTN)KernelParamsAddress > LINUX_ATAG_MAX_OFFSET) && (KernelParamsSize < PcdGet32(PcdArmLinuxAtagMaxOffset))) { | |
70 | KernelParamsAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)CopyMem (ALIGN32_BELOW(LINUX_ATAG_MAX_OFFSET - KernelParamsSize), (VOID*)(UINTN)KernelParamsAddress, KernelParamsSize); | |
71 | } | |
72 | } else { | |
73 | if (((UINTN)KernelParamsAddress > LINUX_FDT_MAX_OFFSET) && (KernelParamsSize < PcdGet32(PcdArmLinuxFdtMaxOffset))) { | |
74 | KernelParamsAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)CopyMem (ALIGN32_BELOW(LINUX_FDT_MAX_OFFSET - KernelParamsSize), (VOID*)(UINTN)KernelParamsAddress, KernelParamsSize); | |
75 | } | |
a355a365 | 76 | } |
1bfda055 | 77 | |
a355a365 | 78 | if ((UINTN)LinuxImage > LINUX_KERNEL_MAX_OFFSET) { |
79 | //Note: There is no requirement on the alignment | |
80 | LinuxKernel = (LINUX_KERNEL)CopyMem (ALIGN32_BELOW(LINUX_KERNEL_MAX_OFFSET - LinuxImageSize), (VOID*)(UINTN)LinuxImage, LinuxImageSize); | |
76d17c31 | 81 | } else { |
82 | LinuxKernel = (LINUX_KERNEL)(UINTN)LinuxImage; | |
a355a365 | 83 | } |
1bfda055 | 84 | |
995d9676 | 85 | // Check if the Linux Image is a uImage |
86 | if (*(UINT32*)LinuxKernel == LINUX_UIMAGE_SIGNATURE) { | |
87 | // Assume the Image Entry Point is just after the uImage header (64-byte size) | |
88 | LinuxKernel = (LINUX_KERNEL)((UINTN)LinuxKernel + 64); | |
1093e307 | 89 | LinuxImageSize -= 64; |
995d9676 | 90 | } |
91 | ||
a355a365 | 92 | //TODO: Check there is no overlapping between kernel and Atag |
1bfda055 | 93 | |
a355a365 | 94 | // |
95 | // Switch off interrupts, caches, mmu, etc | |
96 | // | |
97 | Status = PreparePlatformHardware (); | |
98 | ASSERT_EFI_ERROR(Status); | |
1bfda055 | 99 | |
a355a365 | 100 | // Register and print out performance information |
101 | PERF_END (NULL, "BDS", NULL, 0); | |
102 | if (PerformanceMeasurementEnabled ()) { | |
103 | PrintPerformance (); | |
104 | } | |
1bfda055 | 105 | |
a355a365 | 106 | // |
107 | // Start the Linux Kernel | |
108 | // | |
1bfda055 | 109 | |
a355a365 | 110 | // Outside BootServices, so can't use Print(); |
111 | DEBUG((EFI_D_ERROR, "\nStarting the kernel:\n\n")); | |
1bfda055 | 112 | |
995d9676 | 113 | // Jump to kernel with register set |
76d17c31 | 114 | LinuxKernel ((UINTN)0, MachineType, (UINTN)KernelParamsAddress); |
1bfda055 | 115 | |
a355a365 | 116 | // Kernel should never exit |
117 | // After Life services are not provided | |
118 | ASSERT(FALSE); | |
1bfda055 | 119 | |
120 | Exit: | |
a355a365 | 121 | // Only be here if we fail to start Linux |
122 | Print (L"ERROR : Can not start the kernel. Status=0x%X\n", Status); | |
123 | ||
124 | // Free Runtimee Memory (kernel and FDT) | |
125 | return Status; | |
1bfda055 | 126 | } |
76d17c31 | 127 | |
128 | /** | |
129 | Start a Linux kernel from a Device Path | |
130 | ||
131 | @param LinuxKernel Device Path to the Linux Kernel | |
b34e4db3 | 132 | @param Parameters Linux kernel arguments |
76d17c31 | 133 | @param Fdt Device Path to the Flat Device Tree |
134 | ||
135 | @retval EFI_SUCCESS All drivers have been connected | |
136 | @retval EFI_NOT_FOUND The Linux kernel Device Path has not been found | |
137 | @retval EFI_OUT_OF_RESOURCES There is not enough resource memory to store the matching results. | |
138 | ||
139 | **/ | |
140 | EFI_STATUS | |
141 | BdsBootLinuxAtag ( | |
142 | IN EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath, | |
143 | IN EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath, | |
0a6653bc | 144 | IN CONST CHAR8* CommandLineArguments |
76d17c31 | 145 | ) |
146 | { | |
147 | EFI_STATUS Status; | |
148 | UINT32 LinuxImageSize; | |
149 | UINT32 InitrdImageSize = 0; | |
0a6653bc | 150 | UINT32 AtagSize; |
151 | EFI_PHYSICAL_ADDRESS AtagBase; | |
76d17c31 | 152 | EFI_PHYSICAL_ADDRESS LinuxImage; |
153 | EFI_PHYSICAL_ADDRESS InitrdImage; | |
154 | ||
155 | PERF_START (NULL, "BDS", NULL, 0); | |
156 | ||
157 | // Load the Linux kernel from a device path | |
158 | LinuxImage = LINUX_KERNEL_MAX_OFFSET; | |
159 | Status = BdsLoadImage (LinuxKernelDevicePath, AllocateMaxAddress, &LinuxImage, &LinuxImageSize); | |
160 | if (EFI_ERROR(Status)) { | |
161 | Print (L"ERROR: Did not find Linux kernel.\n"); | |
162 | return Status; | |
163 | } | |
164 | ||
165 | if (InitrdDevicePath) { | |
4671d15d | 166 | // Load the initrd near to the Linux kernel |
167 | InitrdImage = LINUX_KERNEL_MAX_OFFSET; | |
168 | Status = BdsLoadImage (InitrdDevicePath, AllocateMaxAddress, &InitrdImage, &InitrdImageSize); | |
169 | if (Status == EFI_OUT_OF_RESOURCES) { | |
170 | Status = BdsLoadImage (InitrdDevicePath, AllocateAnyPages, &InitrdImage, &InitrdImageSize); | |
171 | } | |
76d17c31 | 172 | if (EFI_ERROR(Status)) { |
173 | Print (L"ERROR: Did not find initrd image.\n"); | |
174 | return Status; | |
175 | } | |
c0a1f777 | 176 | |
177 | // Check if the initrd is a uInitrd | |
178 | if (*(UINT32*)((UINTN)InitrdImage) == LINUX_UIMAGE_SIGNATURE) { | |
179 | // Skip the 64-byte image header | |
180 | InitrdImage = (EFI_PHYSICAL_ADDRESS)((UINTN)InitrdImage + 64); | |
181 | InitrdImageSize -= 64; | |
182 | } | |
76d17c31 | 183 | } |
184 | ||
185 | // | |
186 | // Setup the Linux Kernel Parameters | |
187 | // | |
188 | ||
189 | // By setting address=0 we leave the memory allocation to the function | |
0a6653bc | 190 | Status = PrepareAtagList (CommandLineArguments, InitrdImage, InitrdImageSize, &AtagBase, &AtagSize); |
76d17c31 | 191 | if (EFI_ERROR(Status)) { |
192 | Print(L"ERROR: Can not prepare ATAG list. Status=0x%X\n", Status); | |
193 | return Status; | |
194 | } | |
195 | ||
0a6653bc | 196 | return StartLinux (LinuxImage, LinuxImageSize, AtagBase, AtagSize, PcdGet32(PcdArmMachineType)); |
76d17c31 | 197 | } |
198 | ||
199 | /** | |
200 | Start a Linux kernel from a Device Path | |
201 | ||
202 | @param LinuxKernel Device Path to the Linux Kernel | |
b34e4db3 | 203 | @param Parameters Linux kernel arguments |
76d17c31 | 204 | @param Fdt Device Path to the Flat Device Tree |
205 | ||
206 | @retval EFI_SUCCESS All drivers have been connected | |
207 | @retval EFI_NOT_FOUND The Linux kernel Device Path has not been found | |
208 | @retval EFI_OUT_OF_RESOURCES There is not enough resource memory to store the matching results. | |
209 | ||
210 | **/ | |
211 | EFI_STATUS | |
212 | BdsBootLinuxFdt ( | |
213 | IN EFI_DEVICE_PATH_PROTOCOL* LinuxKernelDevicePath, | |
214 | IN EFI_DEVICE_PATH_PROTOCOL* InitrdDevicePath, | |
0a6653bc | 215 | IN CONST CHAR8* CommandLineArguments, |
76d17c31 | 216 | IN EFI_DEVICE_PATH_PROTOCOL* FdtDevicePath |
217 | ) | |
218 | { | |
219 | EFI_STATUS Status; | |
220 | UINT32 LinuxImageSize; | |
221 | UINT32 InitrdImageSize = 0; | |
0a6653bc | 222 | UINT32 FdtBlobSize; |
223 | EFI_PHYSICAL_ADDRESS FdtBlobBase; | |
76d17c31 | 224 | EFI_PHYSICAL_ADDRESS LinuxImage; |
225 | EFI_PHYSICAL_ADDRESS InitrdImage; | |
226 | ||
76d17c31 | 227 | PERF_START (NULL, "BDS", NULL, 0); |
228 | ||
229 | // Load the Linux kernel from a device path | |
230 | LinuxImage = LINUX_KERNEL_MAX_OFFSET; | |
231 | Status = BdsLoadImage (LinuxKernelDevicePath, AllocateMaxAddress, &LinuxImage, &LinuxImageSize); | |
232 | if (EFI_ERROR(Status)) { | |
233 | Print (L"ERROR: Did not find Linux kernel.\n"); | |
234 | return Status; | |
235 | } | |
236 | ||
237 | if (InitrdDevicePath) { | |
4671d15d | 238 | InitrdImage = LINUX_KERNEL_MAX_OFFSET; |
239 | Status = BdsLoadImage (InitrdDevicePath, AllocateMaxAddress, &InitrdImage, &InitrdImageSize); | |
240 | if (Status == EFI_OUT_OF_RESOURCES) { | |
241 | Status = BdsLoadImage (InitrdDevicePath, AllocateAnyPages, &InitrdImage, &InitrdImageSize); | |
242 | } | |
76d17c31 | 243 | if (EFI_ERROR(Status)) { |
244 | Print (L"ERROR: Did not find initrd image.\n"); | |
245 | return Status; | |
246 | } | |
c0a1f777 | 247 | |
248 | // Check if the initrd is a uInitrd | |
249 | if (*(UINT32*)((UINTN)InitrdImage) == LINUX_UIMAGE_SIGNATURE) { | |
250 | // Skip the 64-byte image header | |
251 | InitrdImage = (EFI_PHYSICAL_ADDRESS)((UINTN)InitrdImage + 64); | |
252 | InitrdImageSize -= 64; | |
253 | } | |
76d17c31 | 254 | } |
255 | ||
0a6653bc | 256 | // Load the FDT binary from a device path. The FDT will be reloaded later to a more appropriate location for the Linux kernel. |
257 | FdtBlobBase = 0; | |
258 | Status = BdsLoadImage (FdtDevicePath, AllocateAnyPages, &FdtBlobBase, &FdtBlobSize); | |
76d17c31 | 259 | if (EFI_ERROR(Status)) { |
260 | Print (L"ERROR: Did not find Device Tree blob.\n"); | |
261 | return Status; | |
262 | } | |
0a6653bc | 263 | |
264 | // Update the Fdt with the Initrd information. The FDT will increase in size. | |
265 | // By setting address=0 we leave the memory allocation to the function | |
266 | Status = PrepareFdt (CommandLineArguments, InitrdImage, InitrdImageSize, &FdtBlobBase, &FdtBlobSize); | |
267 | if (EFI_ERROR(Status)) { | |
268 | Print(L"ERROR: Can not load kernel with FDT. Status=%r\n", Status); | |
269 | return Status; | |
270 | } | |
271 | ||
272 | return StartLinux (LinuxImage, LinuxImageSize, FdtBlobBase, FdtBlobSize, ARM_FDT_MACHINE_TYPE); | |
76d17c31 | 273 | } |
274 |