如上篇分析,在fiasco系统启动的最后阶段——Kernel_thread::bootstrap中,会调用bootstrap_arch函数启动从核,同时启动每个核上的timer定时器。下面具体分析。
bootstrap_arch——》boot_app_cpus——》Platform_control::boot_ap_cpus(_tramp_mp_entry)
注意到定义:
extern char _tramp_mp_entry[]; |
全局搜索,发现_tramp_mp_entry只有在汇编文件中有定义——tramp-mp.S,为一个汇编函数变量:
.global _tramp_mp_entry _tramp_mp_entry: msr DAIFSet, #0xf bsp_early_init x0, x1 mrs x17, CurrentEL lsr x17, x17, #2 cmp x17, #3 // running at EL3 b.ne .Lnot_el3 …… .Lnot_el3: cmp x17, #2 #ifndef CONFIG_CPU_VIRT b.ne 2f mov x6, #((1 << 29) | (1<< 31)) // RW + HCD msr HCR_EL2, x6 movz x17, #((0xf << 6) | 5) // DAIF + EL1 / SPSEL msr SPSR_EL2, x17 …… ldr x0, 1f br x0 .align 3 1: .8byte _tramp_mp_virt …… // we run paged now _tramp_mp_virt: // spinlock on cpu-init adr x0, _tramp_mp_spinlock 1: ldr x1, [x0] cbz x1, 2f b 1b …… ldr x9, 3f br x9
.align 4 3: .8byte BOOT_AP_CPU |
可以看到,在_tramp_mp_entry函数末尾,调用了BOOT_AP_CPU函数。
在文件main.cc中有这样的定义:
int boot_ap_cpu() __asm__("BOOT_AP_CPU"); |
可见,绕来绕去,最终调用的是main.cc中的boot_ap_cpu()函数。
函数boot_ap_cpu()中会进行一系列初始化,其中就包括时钟初始化——Timer::init(_cpu),最后调用call_ap_bootstrap启动从核。
下面我们来看一下时钟初始化函数——Timer::init(Cpu_number cpu)
1. 首先判断cpu是否有通用定时器,如果没有,panic。
2. 在启动cpu上获取系统时钟频率,并设置定时器中断的_interval
一步步跟,可以发现在class Timer中有个定义:
typedef Generic_timer::Gtimer Gtimer; |
所以是用到了Generic_timer::Gtimer中的frequency函数,最终会调用嵌入汇编,通过msr指令来获取系统时钟:
static Unsigned32 frequency() { Unsigned32 v; asm volatile ("mrs %0, CNTFRQ_EL0": "=r" (v)); return v; } |
设置_interval
_interval = (Unsigned64)_freq0 * Config::Scheduler_granularity / 1000000; |
其中Config::Scheduler_granularity 在没设置oneshot模式下为1000,所以定时器中断时间为1ms(即HZ数为1000),否则为1us。
3. 设置定时器访问权限
static void setup_timer_access() { // CNTKCTL: allow access to virtual and physical counter from EL0 asm volatile("msr CNTKCTL_EL1, %0" : : "r"(0x3)); // CNTHCTL: forbid access to physical timer from EL0 and EL1 asm volatile("msr CNTHCTL_EL2, %0" : : "r"(0x0)); } |
4. 等待timer运行。
至此,fiasco的系统时钟初始化完毕。