3 * Copyright (c) 2011, ARM Limited. All rights reserved.
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
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.
15 #include "BdsInternal.h"
16 #include "BdsLinuxLoader.h"
18 #include <Library/PcdLib.h>
19 #include <Library/ArmLib.h>
20 #include <Library/HobLib.h>
24 GetARMLinuxMachineType (
25 IN BOOLEAN FdtSupported
,
26 OUT UINT32
*MachineType
30 // FDT requires that the machine type is set to the maximum 32-bit number.
31 *MachineType
= 0xFFFFFFFF;
35 // Non-FDT requires a specific machine type.
36 // This OS Boot loader supports just one machine type,
37 // but that could change in the future.
38 *MachineType
= PcdGet32(PcdArmMachineType
);
46 SetupCoreTag( IN UINT32 PageSize
)
48 Params
->header
.size
= tag_size(atag_core
);
49 Params
->header
.type
= ATAG_CORE
;
51 Params
->body
.core_tag
.flags
= 1; /* ensure read-only */
52 Params
->body
.core_tag
.pagesize
= PageSize
; /* systems PageSize (4k) */
53 Params
->body
.core_tag
.rootdev
= 0; /* zero root device (typically overridden from kernel command line )*/
55 Params
= next_tag_address(Params
); /* move pointer to next tag */
60 SetupMemTag( IN UINTN StartAddress
, IN UINT32 Size
)
62 Params
->header
.size
= tag_size(atag_mem
);
63 Params
->header
.type
= ATAG_MEM
;
65 Params
->body
.mem_tag
.start
= StartAddress
; /* Start of memory chunk for AtagMem */
66 Params
->body
.mem_tag
.size
= Size
; /* Size of memory chunk for AtagMem */
68 Params
= next_tag_address(Params
); /* move pointer to next tag */
73 SetupCmdlineTag( IN CONST CHAR8
*CmdLine
)
77 // Increment the line length by 1 to account for the null string terminator character
78 LineLength
= AsciiStrLen(CmdLine
) + 1;
80 /* Check for NULL strings.
81 * Do not insert a tag for an empty CommandLine, don't even modify the tag address pointer.
82 * Remember, you have at least one null string terminator character.
86 Params
->header
.size
= ((UINT32
)sizeof(struct atag_header
) + LineLength
+ (UINT32
)3) >> 2;
87 Params
->header
.type
= ATAG_CMDLINE
;
89 /* place CommandLine into tag */
90 AsciiStrCpy(Params
->body
.cmdline_tag
.cmdline
, CmdLine
);
92 Params
= next_tag_address(Params
); /* move pointer to next tag */
100 // Empty tag ends list; this has zero length and no body
101 Params
->header
.type
= ATAG_NONE
;
102 Params
->header
.size
= 0;
104 /* We can not calculate the next address by using the standard macro:
105 * Params = next_tag_address(Params);
106 * because it relies on the header.size, which here it is 0 (zero).
107 * The easiest way is to add the sizeof(Params->header).
109 Params
= (struct atag
*)((UINT32
)Params
+ sizeof(Params
->header
));
115 IN OUT
struct atag
**AtagStartAddress
,
116 IN CONST CHAR8
* CommandLineString
,
119 LIST_ENTRY
*ResourceLink
;
120 LIST_ENTRY ResourceList
;
121 BDS_SYSTEM_MEMORY_RESOURCE
*Resource
;
123 // If no address supplied then this function will decide where to put it
124 if( *AtagStartAddress
== 0 )
126 /* WARNING: At the time of writing (2010-July-30) the linux kernel expects
127 * the atag list it in the first 1MB of memory and preferably at address 0x100.
128 * This has a very high risk of overwriting UEFI code, but as
129 * the linux kernel does not expect any runtime services from uefi
130 * and there is no afterlife section following the linux kernel termination,
131 * it does not matter if we stamp over that memory area.
133 * The proposed workaround is to create the atag list somewhere in boot services memory
134 * and then transfer it to address 0x100 (or to runtime services memory) immediately
135 * before starting the kernel.
136 * An additional benefit of this is that when we copy the ATAG list to it's final place,
137 * we can trim down the memory allocation size. Before we create the list we don't know
138 * how much space it is going to take, so we are over-allocating space.
140 *AtagStartAddress
= (struct atag
*) AllocatePool(ATAG_MAX_SIZE
);
143 // Ensure the pointer is not NULL.
144 ASSERT( *AtagStartAddress
!= (struct atag
*)NULL
);
146 // Ready to setup the atag list
147 Params
= *AtagStartAddress
;
149 // Standard core tag 4k PageSize
150 SetupCoreTag( (UINT32
)SIZE_4KB
);
152 // Physical memory setup
153 GetSystemMemoryResources(&ResourceList
);
154 ResourceLink
= ResourceList
.ForwardLink
;
155 while (ResourceLink
!= NULL
&& ResourceLink
!= &ResourceList
) {
156 Resource
= (BDS_SYSTEM_MEMORY_RESOURCE
*)ResourceList
.ForwardLink
;
157 SetupMemTag( (UINT32
)Resource
->PhysicalStart
, (UINT32
)Resource
->ResourceLength
);
158 ResourceLink
= ResourceLink
->ForwardLink
;
161 // CommandLine setting root device
162 SetupCmdlineTag( CommandLineString
);
167 // Calculate atag list size
168 *AtagSize
= (UINT32
)Params
- (UINT32
)*AtagStartAddress
+ 1;
175 PreparePlatformHardware( VOID
)
177 //Note: Interrupts will be disabled by the GIC driver when ExitBootServices() will be called.
179 // clean, invalidate, disable data cache
180 ArmCleanInvalidateDataCache();
181 ArmDisableDataCache();
183 // Invalidate and disable the Instruction cache
184 ArmInvalidateInstructionCache ();
185 ArmDisableInstructionCache ();
194 /*************************************************
195 * R0, R1, R2 correspond to registers R0, R1, R2
196 *************************************************/
199 StartLinuxKernel( IN VOID
* KernelAddress
, IN UINTN R0
, IN UINTN R1
, IN UINTN R2
)
201 VOID (*Kernel
)(UINT32 Zero
, UINT32 Arch
, UINTN AtagListParams
);
203 // set the kernel address
204 Kernel
= (VOID (*)(UINT32
, UINT32
, UINTN
)) KernelAddress
;
206 // Outside BootServices, so can't use Print();
207 DEBUG((EFI_D_ERROR
, "\nStarting the kernel:\n\n"));
209 // jump to kernel with register set
210 Kernel( R0
, R1
, R2
);
212 // Kernel should never exit
213 // After Life services are not provided
219 EFI_STATUS
BdsBootLinux(
220 IN CONST CHAR16
* LinuxKernel
,
221 IN CONST CHAR8
* ATag
,
224 BDS_FILE LinuxKernelFile
;
229 UINT32 KernelParamsSize
;
230 VOID
* KernelParamsAddress
= NULL
;
231 UINTN KernelParamsNewAddress
;
234 BOOLEAN FdtSupported
= FALSE
;
235 EFI_HOB_RESOURCE_DESCRIPTOR
*ResHob
;
237 // Load the Linux kernel from a device path
238 Status
= BdsLoadFilePath(LinuxKernel
, &LinuxKernelFile
);
239 if (EFI_ERROR(Status
)) {
240 DEBUG ((EFI_D_ERROR
, "ERROR: Do not find Linux kernel %s\n",LinuxKernel
));
244 // Copy the Linux Kernel from the raw file to Runtime memory
245 Status
= BdsCopyRawFileToRuntimeMemory(&LinuxKernelFile
,&LinuxImage
,NULL
);
246 if (EFI_ERROR(Status
)) {
250 // Load the FDT binary from a device path
251 Status
= BdsLoadFilePath(Fdt
, &FdtFile
);
252 if (!EFI_ERROR(Status
)) {
253 // Copy the FDT binary from the raw file to Runtime memory
254 Status
= BdsCopyRawFileToRuntimeMemory(&FdtFile
,&KernelParamsAddress
,&KernelParamsSize
);
255 if (EFI_ERROR(Status
)) {
262 /**********************************************************
263 * Setup the platform type
264 **********************************************************/
265 Status
= GetARMLinuxMachineType(FdtSupported
, &MachineType
);
266 if(EFI_ERROR(Status
))
268 Print(L
"ERROR : Can not prepare ARM Linux machine type. Status=0x%X\n", Status
);
273 /**********************************************************
274 * Setup the ATAG list
275 **********************************************************/
276 // By setting address=0 we leave the memory allocation to the function
278 Status
= PrepareAtagList( (struct atag
**)&AtagAddress
, ATag
, &KernelParamsSize
);
279 KernelParamsAddress
= (VOID
*)AtagAddress
;
280 if(EFI_ERROR(Status
))
282 Print(L
"ERROR : Can not prepare ATAG list. Status=0x%X\n", Status
);
287 /**********************************************************
288 * Switch off interrupts, caches, mmu, etc
289 **********************************************************/
290 Status
= PreparePlatformHardware();
291 if(EFI_ERROR(Status
))
293 Print(L
"ERROR : Can not prepare platform hardware. Status=0x%X\n", Status
);
297 // Initialize the ATag destination
298 KernelParamsNewAddress
= 0x100;
300 // Update the ATag destination by finding the start address of the first System Memory Resource Descriptor Hob
301 ResHob
= (EFI_HOB_RESOURCE_DESCRIPTOR
*)GetFirstHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
);
302 while (ResHob
!= NULL
) {
303 if (ResHob
->ResourceType
== EFI_RESOURCE_SYSTEM_MEMORY
) {
304 KernelParamsNewAddress
= (UINTN
)ResHob
->PhysicalStart
+ 0x100;
307 ResHob
= (EFI_HOB_RESOURCE_DESCRIPTOR
*)GetNextHob (EFI_HOB_TYPE_RESOURCE_DESCRIPTOR
, (VOID
*)((UINTN
)ResHob
+ ResHob
->Header
.HobLength
));
310 // Shut down UEFI boot services. ExitBootServices() will notify every driver that created an event on
311 // ExitBootServices event. Example the Interrupt DXE driver will disable the interrupts on this event.
312 Status
= ShutdownUefiBootServices();
313 if(EFI_ERROR(Status
))
315 Print(L
"ERROR : Can not shutdown UEFI boot services. Status=0x%X\n", Status
);
319 // Move the kernel parameters to any address inside the first 1MB.
320 // This is necessary because the ARM Linux kernel requires
321 // the FTD / ATAG List to reside entirely inside the first 1MB of
323 CopyMem((VOID
*)KernelParamsNewAddress
, KernelParamsAddress
, KernelParamsSize
);
325 //**********************************************************
326 // * Start the Linux Kernel
327 // **********************************************************
329 Status
= StartLinuxKernel(LinuxImage
, (UINTN
)0, (UINTN
)MachineType
, KernelParamsNewAddress
);
331 // Only be here if we fail to start Linux
332 DEBUG((EFI_D_ERROR
, "ERROR : Can not start the kernel. Status=0x%X\n", Status
));
335 // Free Runtimee Memory (kernel and FDT)