在现有的 ARMv9提供的 fvp代码包含多个部件的代码,主要包括 scp、atf、uefi、grub、kernel、busybox几个部分。模拟器的源码对于初学者可以不用理会,ARM V9主要用于服务器 SOC,其中 SCP则是包含了更多裸系统的外设控制。
SCP代码相对其它部件而言,代码框架并不复杂,毕竟是对外设的操作,用于执行 ATF和 BMC传递过来的指令。了解框架,我们可以先从链接脚本开始分析
目录
1. 找到入口函数
链接脚本: // scp\arch\arm\armv8-a\src\arch.ld.S
ENTRY(_entrypoint)
SECTIONS {
.text : {
*(.vectors)
*(.entrypoint) //入口函数
*(.text*)
*(.rodata*)
*(.note.gnu.build-id)
} > mem0
__text__ = .;
__sram_copy_start__ = .;
.system_ram : {
__system_ram_start__ = .;
*(.system_ram*)
*iic_dvfs.o(.rodata)
__system_ram_end__ = .;
} > sram AT> mem0
. = __text__ + SIZEOF(.system_ram);
/*
* Define a linker symbol to mark start of the RW memory area for this
* image.
*/
__RW_START__ = . ;
.data : {
. = ALIGN(4);
*(.data*)
. = ALIGN(4);
#if FMW_MEM_MODE == ARCH_MEM_MODE_SINGLE_REGION
} > mem0 /* .data follows .text in mem0 */
#elif FMW_MEM_MODE == ARCH_MEM_MODE_DUAL_REGION_NO_RELOCATION
} > mem1 /* .data is the first section in mem1 */
#elif FMW_MEM_MODE == ARCH_MEM_MODE_DUAL_REGION_RELOCATION
} > mem1 AT>mem0 /* Run-time image is at mem1, but loaded from mem0 */
#else
ASSERT(0, "Unrecognized FMW_MEM_MODE")
#endif
.bss : {
. = ALIGN(4);
*(.bss*)
. = ALIGN(4);
#if FMW_MEM_MODE == ARCH_MEM_MODE_SINGLE_REGION
} > mem0 /* Run-time image is at mem1, but loaded from mem0 */
#else
} > mem1 /* .bss follows .data in mem1 */
#endif
.stack (NOLOAD) : {
. = . + STACK_SIZE;
} > stack
__RW_END__ = .;
/*
* Define a linker symbol to mark end of the RW memory area for this
* image.
*/
__TEXT_START__ = LOADADDR(.text);
__TEXT_SIZE__ = SIZEOF(.text);
__TEXT_END__ = __TEXT_START__ + __TEXT_SIZE__;
__STACK_START__ = LOADADDR(.stack);
__STACK_SIZE__ = SIZEOF(.stack);
__STACK_END__ = __STACK_START__ + __STACK_SIZE__;
__STACK_TOP__ = __STACK_END__;
__STACK_SP3_SIZE__ = 0x400;
__STACK_SP0_TOP__ = __STACK_END__ - __STACK_SP3_SIZE__;
__DATA_LMA_START__ = LOADADDR(.data);
__DATA_START__ = ADDR(.data);
__DATA_SIZE__ = SIZEOF(.data);
__BSS_START__ = ADDR(.bss);
__BSS_SIZE__ = SIZEOF(.bss);
__BSS_END__ = __BSS_START__ + __BSS_SIZE__;
__HEAP_START__ = __BSS_START__ + __BSS_SIZE__;
__HEAP_END__ = __STACK_START__;
__HEAP_SIZE__ = __HEAP_END__ - __HEAP_START__;
}
从链接脚本看来,首先是初始化中断向量表,里面针对 arm64和 arm32做了区分,包括 freertos。这里只看入口函数: _entrypoint
// scp\arch\arm\armv8-a\src\arch_crt0.S
func _entrypoint
ldr w0, _boot_flag
ldr w1, =R_WARMBOOT
cmp w1, w0
beq _restore_system
ldr x0, =__STACK_TOP__
mov sp, x0 /* SP_EL3 */
adr x0, _vector_table
msr vbar_el3, x0
isb
msr spsel, #0
ldr x0, =__STACK_SP0_TOP__
mov sp, x0 /* SP_EL0 */
stp x29, x30, [sp, #-32]!
adr x0, __RW_START__
adr x1, __RW_END__
sub x1, x1, x0
bl inv_dcache_range
ldr x0, =__BSS_START__
ldr x1, =__BSS_SIZE__
bl zeromem
#if USE_COHERENT_MEM
ldr x0, =__COHERENT_RAM_START__
ldr x1, =__COHERENT_RAM_UNALIGNED_SIZE__
bl zeromem
#endif
mrs x0, scr_el3
/* RW[10]=1, HCE[8]=0, SMD[7]=0, EA[3]=1, FIQ[2]=1, IRQ[1]=1, NS[0]=0 */
mov x0, #(1<<10 | 0<<8 | 0<<7 | 1<<3 | 1<<2 | 1<<1 | 0<<0)
msr scr_el3, x0
/* --------------------------------------------------
* Initialize platform and jump to our c-entry point
* for this type of reset.
* --------------------------------------------------
*/
#ifdef BUILD_HAS_NOTIFICATION
bl __fwk_notification_reset
#endif
bl arm_main
mov x0, 1
ldp x29, x30, [sp], #32
ret
endfunc _entrypoint
从上面的汇编文件可以看出,构建了堆栈后直接跳到 C代码 arm_main
2. 入口函数分析
static struct fwk_arch_init_driver arch_init_driver = {
.interrupt = arm_gic_init,
};arm_main(void) // scp\arch\arm\armv8-a\src\arch_main.c
fwk_arch_init(&arch_init_driver); //这里传入的是架构 gic
2. 1 fwk_arch_init里面做了啥
架构 fwk主要是 module初始化, io初始化, 中断 gic初始化
int fwk_arch_init(const struct fwk_arch_init_driver *driver) // scp\framework\src\fwk_arch.c
fwk_module_init();
fwk_io_init();
fwk_arch_interrupt_init(driver->interrupt); //调用架构 gic相关代码
fwk_module_start();
上面的 module其实对应是 arm里面的硬件模块,我们可以在目录看到 scp\module
/scp/module
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 apcontext/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 apremap/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 armv7m_mpu/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 bootloader/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 clock/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 cmn600/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 cmn650/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 cmn700/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 cmn_booker/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 css_clock/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 ddr_phy500/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 debug/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 debugger_cli/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 dmc500/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 dmc620/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 dvfs/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 dw_apb_i2c/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 dwt_pmi/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 fip/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 gtimer/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 i2c/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 isys_rom/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 mhu/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 mhu2/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 mock_clock/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 mock_psu/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 mock_sensor/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 mock_voltage_domain/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 msys_rom/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 pcid/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 pcie_integ_ctrl/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 pik_clock/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 pl011/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 pmi/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 power_domain/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 ppu_v0/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 ppu_v1/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 psu/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 reg_sensor/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 reset_domain/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 resource_perms/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 scmi/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 scmi_apcore/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 scmi_clock/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 scmi_perf/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 scmi_power_domain/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 scmi_reset_domain/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 scmi_sensor/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 scmi_system_power/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 scmi_voltage_domain/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 sds/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 sensor/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 sid/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 smt/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 ssc/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 statistics/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 stdio/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 system_info/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 system_pll/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 system_power/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 timer/
drwxr-xr-x 1 Administrator 197121 0 Dec 15 09:34 voltage_domain/
2.2 fwk的 IO初始化
struct fwk_io_stream *fwk_io_stdin = &fwk_io_null;
struct fwk_io_stream *fwk_io_stdout = &fwk_io_null;int fwk_io_init(void)
{
fwk_io_open(&stdin_stream, FMW_IO_STDIN_ID, FWK_IO_MODE_READ);
fwk_io_stdin = &stdin_stream;fwk_io_open(&stdout_stream, FMW_IO_STDOUT_ID, FWK_IO_MODE_WRITE);
fwk_io_stdout = &stdout_stream;}
这里构建了一个全局的 fwk_io_stdin、 fwk_io_stdout, 在后面的终端输出有用
2.3 中断 gic初始化
gic 初始化相关代码: // scp\framework\src\fwk_arch.c
int fwk_arch_init(const struct fwk_arch_init_driver *driver)
fwk_arch_interrupt_init(driver->interrupt); //这个是main函数入口传入
interrupt_init_handler(&driver); //执行回调函数
fwk_interrupt_init(driver); // scp\framework\src\fwk_interrupt.c
fwk_interrupt_driver = driver; //这里构建以模块句柄
2.3.1 回调函数 arm_gic_init分析
static struct fwk_arch_init_driver arch_init_driver = {
.interrupt = arm_gic_init,
};// scp\arch\arm\armv8-a\src\arch_gic.c
arm_gic_init(const struct fwk_arch_interrupt_driver **driver)
gic_init(); // 这个以后再分析,主要还是初始化 gic相关寄存器
*driver = &arm_gic_driver; // 这里传入的是二级指针
2.3.2 gic提供的相关接口
从上上面的代码分析,可以看出经过 fwk_arch_interrupt_init函数初始化后,构建了一个全局句柄 fwk_interrupt_driver , 其提供的接口如下
// scp\arch\arm\armv8-a\src\arch_gic.c
static const struct fwk_arch_interrupt_driver arm_gic_driver = {
.global_enable = global_enable,
.global_disable = global_disable,
.is_enabled = is_enabled,
.enable = enable,
.disable = disable,
.is_pending = is_pending,
.set_pending = set_pending,
.clear_pending = clear_pending,
.set_isr_irq = set_isr_irq,
.set_isr_irq_param = set_isr_irq_param,
.set_isr_nmi = set_isr_dummy,
.set_isr_nmi_param = set_isr_dummy_param,
.set_isr_fault = set_isr_dummy,
.get_current = get_current,
};
对外接口如下: // scp\framework\include\fwk_interrupt.h
int fwk_interrupt_init(const struct fwk_arch_interrupt_driver *driver);
inline static void fwk_interrupt_global_enable(void)
inline static void fwk_interrupt_global_disable(void)
int fwk_interrupt_is_enabled(unsigned int interrupt, bool *enabled);
int fwk_interrupt_enable(unsigned int interrupt);
int fwk_interrupt_disable(unsigned int interrupt);
int fwk_interrupt_is_pending(unsigned int interrupt, bool *pending);
int fwk_interrupt_set_pending(unsigned int interrupt);
int fwk_interrupt_clear_pending(unsigned int interrupt);
int fwk_interrupt_set_isr(unsigned int interrupt, void (*isr)(void));
int fwk_interrupt_set_isr_param(unsigned int interrupt, void (*isr)(uintptr_t param),uintptr_t param);
int fwk_interrupt_get_current(unsigned int *interrupt);
这样以后,所有对中断操作,只需要使用上面的 API即可, 具体的都是对相关寄存器操作, 嵌入汇编代码实现。框架是比较简单,寄存器相关操作在后续章节分析。
3. 框架相关的初始化
先回到入口函数: // scp\framework\src\fwk_arch.c
int fwk_arch_init(const struct fwk_arch_init_driver *driver)
fwk_module_init(); // scp\framework\src\fwk_module.c
fwk_module_start();
3.1 fwk模块初始化
首先这里定义了一个本地全局的实例
static struct { // scp\framework\src\fwk_module.c
bool initialized;
struct fwk_module_ctx module_ctx_table[FWK_MODULE_IDX_COUNT];
enum fwk_module_stage stage;
fwk_id_t bind_id;
} fwk_module_ctx;
这个本地全局实体在本模块初始化
void fwk_module_init(void) // scp\framework\src\fwk_module.c
for (uint32_t i = 0U; i < (uint32_t)FWK_MODULE_IDX_COUNT; i++) {
struct fwk_module_ctx *ctx = &fwk_module_ctx.module_ctx_table[i];
fwk_id_t id = FWK_ID_MODULE(i);
const struct fwk_module *desc = module_table[i];
const struct fwk_module_config *config = module_config_table[i];
*ctx = (struct fwk_module_ctx){
.id = id,
.desc = desc,
.config = config,
};
// 上面通过匿名结构体赋值,
fwk_list_init(&ctx->delayed_response_list); //挂接到模块链表里
}
3.2 fwk模块核心启动
先看代码几个主要步骤
fwk_module_start(void) // scp\framework\src\fwk_module.c
__fwk_thread_init(FWK_MODULE_EVENT_COUNT);
fwk_module_init_modules();
start_modules();
__fwk_thread_run();
3.2.1 初始化所有 fwk_module
fwk_module_init_modules(); // scp\framework\src\fwk_module.c
for (unsigned int i = 0U; i < (unsigned int)FWK_MODULE_IDX_COUNT; i++) {
fwk_module_init_module(&fwk_module_ctx.module_ctx_table[i]);const struct fwk_module *desc = ctx->desc;
const struct fwk_module_config *config = ctx->config;desc->init(ctx->id, ctx->element_count, config->data); //调用模块初始化
fwk_module_init_elements(ctx); //后续分析
}start_modules();
for (module_idx = 0; module_idx < FWK_MODULE_IDX_COUNT; module_idx++) {
module_ctx = &fwk_module_ctx.module_ctx_table[module_idx];
fwk_module_start_module(module_ctx);module->start(module_ctx->id); //调用每个模块的 start函数
}
3.2.2 主线程的初始化和运行
int fwk_module_start(void) // scp\framework\src\fwk_thread.c
__fwk_thread_init(FWK_MODULE_EVENT_COUNT);
event_table = fwk_mm_calloc(event_count, sizeof(struct fwk_event));
fwk_list_init(&ctx.free_event_queue);
fwk_list_init(&ctx.event_queue);
fwk_list_init(&ctx.isr_event_queue);
for (event = event_table; event < (event_table + event_count); event++) {
fwk_list_push_tail(&ctx.free_event_queue, &event->slist_node);
}
__fwk_thread_run();
for (;;) {
while (!fwk_list_is_empty(&ctx.event_queue)) {
process_next_event();
}
if (process_isr()) {
continue;
}
(void)fwk_log_unbuffer();
}
主线程跑起来之后运行状态,从上面的代码可以看出,主要做两件事情
1. 处理队列里所有的事件
2. 处理所有的中断请求
具体事件和中断在后续进一步分析