start_kernel
在 4.2 上,start_kernel
frominit/main.c
是一个相当大的初始化过程,可以比作一个main
函数。
它是第一个运行的独立于架构的代码,并设置了内核的很大一部分。很像main
,start_kernel
之前是一些较低级别的设置代码(crt*
在 userland 的对象中完成main
),然后运行“主要”通用 C 代码。
如何start_kernel
在 x86_64 中调用
arch/x86/kernel/vmlinux.lds.S
,链接描述文件,设置:
ENTRY(phys_startup_64)
和
phys_startup_64 = startup_64 - LOAD_OFFSET;
和:
#define LOAD_OFFSET __START_KERNEL_map
arch/x86/include/asm/page_64_types.h
定义__START_KERNEL_map
为:
#define __START_KERNEL_map _AC(0xffffffff80000000, UL)
这是内核入口地址。TODO 该地址是如何准确到达的?我必须了解 Linux 向引导加载程序公开的接口。
arch/x86/kernel/vmlinux.lds.S
将第一个引导加载程序部分设置为:
.text : AT(ADDR(.text) - LOAD_OFFSET) {
_text = .;
/* bootstrapping code */
HEAD_TEXT
include/asm-generic/vmlinux.lds.h
定义HEAD_TEXT
:
#define HEAD_TEXT *(.head.text)
arch/x86/kernel/head_64.S
定义startup_64
. 这是第一个运行的 x86 内核代码。它做了很多低级设置,包括分段和分页。
那是运行的第一件事,因为文件以:
.text
__HEAD
.code64
.globl startup_64
并include/linux/init.h
定义__HEAD
为:
#define __HEAD .section ".head.text","ax"
所以与链接描述文件的第一件事相同。
最后它用 and 调用x86_64_start_kernel
有点尴尬lretq
:
movq initial_code(%rip),%rax
pushq $0 # fake return address to stop unwinder
pushq $__KERNEL_CS # set correct cs
pushq %rax # target address in negative space
lretq
和:
.balign 8
GLOBAL(initial_code)
.quad x86_64_start_kernel
arch/x86/kernel/head64.c
定义x86_64_start_kernel
哪些调用x86_64_start_reservations
哪些调用start_kernel
。
arm64 入口点
第一个在 v5.7 未压缩内核上运行的 arm64 定义在https://github.com/cirosantilli/linux/blob/v5.7/arch/arm64/kernel/head.S#L72所以add x13, x18, #0x16
要么b stext
上CONFIG_EFI
:
__HEAD
_head:
/*
* DO NOT MODIFY. Image header expected by Linux boot-loaders.
*/
#ifdef CONFIG_EFI
/*
* This add instruction has no meaningful effect except that
* its opcode forms the magic "MZ" signature required by UEFI.
*/
add x13, x18, #0x16
b stext
#else
b stext // branch to kernel start, magic
.long 0 // reserved
#endif
le64sym _kernel_offset_le // Image load offset from start of RAM, little-endian
le64sym _kernel_size_le // Effective size of kernel image, little-endian
le64sym _kernel_flags_le // Informative flags, little-endian
.quad 0 // reserved
.quad 0 // reserved
.quad 0 // reserved
.ascii ARM64_IMAGE_MAGIC // Magic number
#ifdef CONFIG_EFI
.long pe_header - _head // Offset to the PE header.
这也是未压缩内核映像的第一个字节。
这两种情况都会跳转到stext
开始“真实”动作的地方。
如评论中所述,这两条指令是记录在案的标头的前 64 个字节,如下所述:https ://github.com/cirosantilli/linux/blob/v5.7/Documentation/arm64/booting.rst#4-call -内核映像
arm64第一个MMU启用指令:__primary_switched
我认为它__primary_switched
在 head.S 中:
/*
* The following fragment of code is executed with the MMU enabled.
*
* x0 = __PHYS_OFFSET
*/
__primary_switched:
此时,内核似乎创建了页表 + 可能会重新定位自己,以便 PC 地址与 vmlinux ELF 文件的符号匹配。因此,此时您应该能够在 GDB 中看到有意义的函数名称,而无需额外的魔法。
arm64 辅助 CPU 入口点
secondary_holding_pen
定义于:https ://github.com/cirosantilli/linux/blob/v5.7/arch/arm64/kernel/head.S#L691
进入程序进一步描述在:https ://github.com/cirosantilli/linux/blob/v5.7/arch/arm64/kernel/head.S#L691