前提1:以arm64架构为例进行fiasco内核分析
前提2:编译生成arm64架构任意entry的镜像文件后,在编译路径下有个auto文件夹,其中就包含通过perl预处理后生成的内核源文件;通过rm *.ready命令删除临时文件;
通过sed -i '/#line/d' *.cc和sed -i '/#line/d' *.h命令去掉源文件中的临时注释行;就得到一个相对标准的C++实现的fiasco内核源代码。
前提3:下文中提到的源文件名皆以auto目录下的文件名为准。
首先找到kernel.arm64.ld链接文件,部分字段如下:
virt_address = 0xffff000040000000;
phys_offset = virt_address - kernel_load_addr;
OUTPUT_FORMAT("elf64-littleaarch64", "elf64-bigaarch64", "elf64-littleaarch64")
OUTPUT_ARCH(aarch64)
ENTRY(_start)
SECTIONS { …… |
可以看到,程序入口为“_start”。
在bootstrap-arm-64.cc文件中有段嵌入式汇编代码,指示_start的功能就是跳转到bootstrap_main执行。
asm ( …… ".global _start \n" "_start: \n" " ldr x9, =_stack \n" " mov sp, x9 \n" " bl bootstrap_main \n" ……); |
函数bootstrap_main在bootstrap.cc文件中。
在函数bootstrap_main中,会调用paging初始化函数,内容包括:刷EL2的TLB、设置SCR及monitor mode、配置gic、从EL3跳转到EL2等。最后调用全局变量bs_info.entry(),跳转到内核的入口,完成bootstrap功能。
那么内核的入口在哪里呢?或者说bs_info结构体在哪里初始化的呢?注意到其定义方式:
Bootstrap_info FIASCO_BOOT_PAGING_INFO bs_info; 而 #define FIASCO_BOOT_PAGING_INFO \ __attribute__((section(".bootstrap.info"), used)) |
将bs_info定义到了“.bootstrap.info”内存段中。全局搜索可以发现在编译目录下有个链接文件kernel.arm.lds中包含如下字段:
SECTIONS { /DISCARD/ : { *(.exitcall.exit) *(.bootstrap.dropped*) } . = kernel_load_addr + 0x1000; .text : { QUAD(_start_kernel) QUAD(my_kernel_info_page) KEEP(*(.bootstrap.info)) ASSERT (ABSOLUTE(.) == end_of_bootstrap_info, "invalid size of bootstrap.info"); . = ABSOLUTE(start_of_loader); *(.bootstrap.text) } :bstrap …… |
对的,就是这个_start_kernel就是内核入口(我们这里只讨论流程,不讨论具体实现细节)。
搜索源码目录,发现_start_kernel定义在crt0.S中(越来越像linux了):
.section .text.init,#alloc,#execinstr .type start,#function ENTRY(_start_kernel) ENTRY(start) ldr x9, =_stack; mov sp, x9 bl __main
/* never returns */
.section ".init.data" .p2align 4 .globl _sstack _sstack: .space 4096 .globl _stack .type _stack,#object _stack:
|
发现这其实也是个跳转,真正入口来了——“__main”在__main.cc中。
其中的核心函数kernel_main在main.cc中,下面具体分析kernel_main函数。
kernel_main:
打印版本号信息;
创建kernel线程;
创建kernel_task;
调用kernel线程的bind函数对线程和task进行绑定;
Tlb_flush;
切换到kernel的stack并调用“call_bootstrap”启动kernel线程。
注意到,kernel_thread.h中class的定义:
class Kernel_thread : public Thread_object { private: void free_initcall_section(); void bootstrap() asm ("call_bootstrap") FIASCO_FASTCALL; void bootstrap_arch(); …… |
所以,转到kernel_thread.cc中来看Kernel_thread::bootstrap()的实现,
Kernel_thread::bootstrap():
初始化每个cpu的数据;
设置当前cpu号;
设置自身ready;
初始化timer为0;
将当前线程加入调度链表;
让当前task运行;
在boot CPU上初始化系统时钟;
使能TLB;
调用bootstrap_arch函数,并在其中调用boot_app_cpus函数启动从核;
在boot CPU上使能系统时钟;
使能watchdog;
调用run函数,进入idle线程。
至此,fiasco的启动流程分析完毕,系统进入idle线程,通过schedule进行线程调度。
总结一下,整个启动调用关系为:
_start——》bootstrap_main——》_start_kernel——》__main——》kernel_main
——》Kernel_thread::bootstrap