arm平台
/*
* Kernel startup entry point.
* ---------------------------
*
* This is normally called from the decompressor code. The requirements
* are: MMU = off, D-cache = off, I-cache = dont care, r0 = 0,
* r1 = machine nr, r2 = atags or dtb pointer.
*
* This code is mostly position independent, so if you link the kernel at
* 0xc0008000, you call this at __pa(0xc0008000).
*
* See linux/arch/arm/tools/mach-types for the complete list of machine
* numbers for r1.
*
* We're trying to keep crap to a minimum; DO NOT add any machine specific
* crap here - that's what the boot loader (or in extreme, well justified
* circumstances, zImage) is for.
*/
arch/arm/kernel/head.S文件定义了bootloader和kernel的参数传递要求:
MMU = off, D-cache = off, I-cache = dont care, r0 = 0, r1 = machine nr, r2 = atags or dtb pointer.
因此在内核的主入口函数,需要传递3个参数,r0/r1/r2。
arm64平台
/*
* Kernel startup entry point.
* ---------------------------
*
* The requirements are:
* MMU = off, D-cache = off, I-cache = on or off,
* x0 = physical address to the FDT blob.
*
* This code is mostly position independent so you call this at
* __pa(PAGE_OFFSET + TEXT_OFFSET).
*
* Note that the callee-saved registers are used for storing variables
* that are useful before the MMU is enabled. The allocations are described
* in the entry routines.
*/
ARM64对启动时的x0~x3这四个寄存器有严格的限制:x0是dtb的物理地址,x1~x3必须是0。因此在内核的主入口函数,需要传递4个参数,x0/x1/x2/x3。
dtb传递
目前新版的kernel支持device tree的启动方式,同时保持了对旧的tag list的支持。对于arm32平台,按照使用dtb进行设备描述的平台,r0/r1都应该传递0,r2传递dtb的内存地址;对于arm64平台,x0为dtb的物理地址,而x1/x2/x3寄存器都是0。
我们以UEFI启动的最后部分来分析:
uefi code:
if (BootParamlistPtr.BootingWith32BitKernel) {
// Booting into 32 bit kernel.
LinuxKernel32 = (LINUX_KERNEL32) (UINT64)BootParamlistPtr.KernelLoadAddr;
LinuxKernel32 (0, 0, (UINTN)BootParamlistPtr.DeviceTreeLoadAddr);
// Should never reach here. After life support is not available
DEBUG ((EFI_D_ERROR, "After Life support not available\n"));
goto Exit;
}
LinuxKernel = (LINUX_KERNEL) (UINT64)BootParamlistPtr.KernelLoadAddr;
LinuxKernel ((UINT64)BootParamlistPtr.DeviceTreeLoadAddr, 0, 0, 0);
kernel code:
start_kernel()
|setup_arch()
|setup_machine_fdt()//select machine description according to DT info
tag list传递
这是linux内核最早的板级信息传递的方式,最早期的时候这个tag list地址并不需要传递,而是通过代码中写死,这就要求bootloader中和kernel中对于tag list保存地址定义要相同。
后面新版的内核做了改进,可以通过一个寄存器传递对应的tag list地址,对于arm就是r2寄存器。如果系统使用了taglist来传递信息,那么必须要指定一个machine type,用于匹配对应的板型。r1 = machine nr指向__machine_arch_type,而r2 = atags的指针被保存在变量__atags_pointer中。