arm处理器uboot架构分析

标签:
u-bootarm |
分类: programming |
arm处理器u-boot运行从汇编文件arch\arm\cpu\armv8\start.S开始(链接脚本u-boot.lds中定义入口_start),
进入下述区域,第一行加载异常向量表vectors,将
vector
指向的地址放到X0寄存器;
之后调用
switch_el
, switch_el
是一个类似C函数的宏定义,======================================
---------------------------------------
=========================================
switch_el的定义如下,这是一个根据异常等级寄存器内容比较后跳转到不同处理分支的一段代码。
CurrentEL为当前异常等级寄存器,
其中EL0为非特权等级,即平时应用程序运行时的级别;EL1为特权等级,即操作系统运行时的级别;EL2为虚拟机监视器运行级别,即虚拟机的控制层运行的级别;EL3为切换EL1和EL2级别时需要进入的一个级别,为CPU的最高级别,arm64的异常级别示意图。=================================
.macro
.endm
=============================================
在检查完成异常等级后,调用lowlevel_init,对使能了宏CONFIG_GICV2或者CONFIG_GICV3情形下对中断GIC初始化
=================================
======================
之后跳转到arch/arm/lib/crt0.S开始(arm64是crt0_64.S),的_main执行,设置堆栈,建立c运行环境
========================
============================
_main函数中会调用board_init_f_alloc_reserve为gd在初始堆栈处位置预留空间
-----------------------------------------------
ulong board_init_f_alloc_reserve(ulong top)
{
#if CONFIG_VAL(SYS_MALLOC_F_LEN)
top -=
CONFIG_VAL(SYS_MALLOC_F_LEN);
#endif
top =
rounddown(top-sizeof(struct global_data), 16);
return
top;
}
{
#if CONFIG_VAL(SYS_MALLOC_F_LEN)
#endif
}
----------------------------------------------------------
最后调用c函数board_init_f进入u-boot主体
======================
========================
void board_init_f(ulong boot_flags)
{
gd->flags
= boot_flags;
gd->have_console = 0;
if
(initcall_run_list(init_sequence_f))
hang();
#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \
!defined(CONFIG_EFI_APP) && !CONFIG_IS_ENABLED(X86_64)
&& \
!defined(CONFIG_ARC)
hang();
#endif
}
{
#if !defined(CONFIG_ARM) && !defined(CONFIG_SANDBOX) && \
#endif
}
=================================================
上面的gd定义,在头文件global_data.h中,定义宏DECLARE_GLOBAL_DATA_PTR
========================================
#ifdef
CONFIG_ARM64
#define DECLARE_GLOBAL_DATA_PTR
register
volatile gd_t *gd asm ("x18")
#else
#define DECLARE_GLOBAL_DATA_PTR
register
volatile gd_t *gd asm ("r9")
#endif
#define DECLARE_GLOBAL_DATA_PTR
#else
#define DECLARE_GLOBAL_DATA_PTR
#endif
==========================================
而在board_f.c中,申明了DECLARE_GLOBAL_DATA_PTR,即gd初始化为新x18的内容,
-------------------------------------------
#ifdef
XTRN_DECLARE_GLOBAL_DATA_PTR
#undef
XTRN_DECLARE_GLOBAL_DATA_PTR
#define XTRN_DECLARE_GLOBAL_DATA_PTR
DECLARE_GLOBAL_DATA_PTR = (gd_t *)(CONFIG_SYS_INIT_GD_ADDR);
#else
DECLARE_GLOBAL_DATA_PTR;
#endif
#undef
#define XTRN_DECLARE_GLOBAL_DATA_PTR
DECLARE_GLOBAL_DATA_PTR = (gd_t *)(CONFIG_SYS_INIT_GD_ADDR);
#else
DECLARE_GLOBAL_DATA_PTR;
#endif
------------------------------------------------------
这样board_init_f中的gd的定义就找到了,函数在gd初始化后,运行initcall_run_list()函数,对init_sequence_f
中的函数表调用运行。
init_sequence_f函数表中,fdtdec_setup函数初始化fdt表的地址到gd->fdt_blob
fdt标的地址在arm结构中ATAG/FDT地址存放于r2/x0寄存器。
u-boot驱动列表构建和初始化扫描
====================================
#define U_BOOT_DRIVER(__name)
#define
ll_entry_declare(_type, _name,
_list)
\
_type
_u_boot_list_2_##_list##_2_##_name
__aligned(4)
\
__attribute__((unused,
\
section(".u_boot_list_2_"#_list"_2_"#_name)))
完全展开:
=====================================
#define U_BOOT_DRIVER(__name)
\
#define U_BOOT_DRIVER(__name)
struct driver
U_BOOT_DRIVER(eth_bcmgenet)
= {
.name =
"eth_bcmgenet",
.id
= UCLASS_ETH,
.of_match =
bcmgenet_eth_ids,
.of_to_plat
= bcmgenet_eth_of_to_plat,
.probe =
bcmgenet_eth_probe,
.ops
= &bcmgenet_gmac_eth_ops,
.priv_auto
= sizeof(struct bcmgenet_eth_priv),
.plat_auto
= sizeof(struct eth_pdata),
.flags =
DM_FLAG_ALLOC_PRIV_DMA,
};
};
得到:
struct driver _u_boot_list_2_driver_2_eth_bcmgenet
__aligned(4)
__attribute__((unused,
};
===================================
在u-boot.lds链接脚本文件中,有规则:
====================================
========================================
前缀为.u_boot_list的段都会放在.u_boot_list区域中,在文件driver/core/root.c,定义了驱动根节点信息
--------------------------------------------------------------
static struct
driver_info root_info = {
.name
=
"root_driver",
};
};
U_BOOT_DRIVER(root_driver)
= {
.name
= "root_driver",
.id
= UCLASS_ROOT,
ACPI_OPS_PTR(&root_acpi_ops)
};
UCLASS_DRIVER(root) = {
.name
= "root",
.id
= UCLASS_ROOT,
};
-------------------------------------------------------
};
UCLASS_DRIVER(root) = {
};
-------------------------------------------------------
在uboot的init_sequence_f初始化函数列表中,函数initf_dm用以扫描dtb文件,创建设备列表和驱动列表,关联设备和驱动。
----------------------------------------------
initf_dm
------------------------------------------------
dm_scan(bool
pre_reloc_only)
----------------------------------------------
在设备驱动模型初始化后,uboot继续初始化其他部分,包括串口初始化
==========================
===========================
以及内存初始化,获得内存大小,如raspberry,通过mailbox系统从GPU获得内存大小
=======================
int
dram_init(void)
{
ALLOC_CACHE_ALIGN_BUFFER(struct msg_get_arm_mem,
msg, 1);
int
ret;
BCM2835_MBOX_INIT_HDR(msg);
BCM2835_MBOX_INIT_TAG(&msg->get_arm_mem,
GET_ARM_MEMORY);
ret =
bcm2835_mbox_call_prop(BCM2835_MBOX_PROP_CHAN,
&msg->hdr);
if (ret)
{
printf("bcm2835: Could not query ARM memory size\n");
return -1;
}
gd->ram_size =
msg->get_arm_mem.body.resp.mem_size;
gd->ram_size &= ~MMU_SECTION_SIZE;
return
0;
}
{
}
===========================================
在内存初始化后,可以对内存进行测试
#if
defined(CONFIG_SYS_DRAM_TEST)
testdram,
#endif
在内存系统工作后,就可以进行uboot代码迁移,从rom复制到ram运行
#endif
在内存系统工作后,就可以进行uboot代码迁移,从rom复制到ram运行
调用setup_dest_addr设置uboot的各种内存基址,预留各种内存等,如图所示
到这里init_sequence_f结束,返回汇编程序
之后进行uboot迁移,(crt0_64.S
-----------------------------------
#if
!defined(CONFIG_SPL_BUILD)
ldr
x0, [x18,
#GD_START_ADDR_SP]
bic
sp, x0,
#0xf
ldr
x18, [x18,
#GD_NEW_GD]
adr
lr, relocation_return
#if CONFIG_POSITION_INDEPENDENT
adrp
x0, _start
add
x0, x0, #:lo12:_start
ldr
x9,
_TEXT_BASE
sub
x9, x9, x0
add
lr, lr, x9
#endif
ldr
x9, [x18,
#GD_RELOC_OFF]
add
lr, lr,
x9
ldr
x0, [x18,
#GD_RELOCADDR]
b
relocate_code
relocation_return:
#if CONFIG_POSITION_INDEPENDENT
#endif
relocation_return:
-------------------------------------
以relocation的目的地址(gd->relocaddr)为参数,调用relocate_code执行实际的relocation动作,就是将u-boot的代码段、data段、bss段等数据,拷贝到新的位置(gd->relocaddr)
relocate_code
定义在relocate_64.S中
在完成复制后,函数返回的地址就是新的ram中的uboot代码地址,建立运行时环境等,进行复制后的板级初始化,不再返回到汇编代码。
===============================
relocate完成之后,真正的C运行环境才算建立了起来,接下来会执行“后置的板级初始化操作
relocate完成之后,真正的C运行环境才算建立了起来,接下来会执行“后置的板级初始化操作
board_init_r
和board_init_f一样运行一个函数数组
中的函数,
其中包括,网络初始化
=============================
#ifdef
CONFIG_CMD_NET
INIT_FUNC_WATCHDOG_RESET
initr_net,
#endif
#endif
===================
initr_net->
#endif
=============================
int
genphy_init(void)
{
return
phy_register(&genphy_driver);
// 添加phy驱动到phy_drivers列表
}
=================================
{
}
=================================
static int
run_main_loop(void)
{
#ifdef CONFIG_SANDBOX
sandbox_main_loop_init();
#endif
for
(;;)
main_loop();
return
0;
}
{
#ifdef CONFIG_SANDBOX
#endif
}
最后调用run_main_loop进入主循环,初始化结束
前一篇:linux-wlan架构概述
后一篇:蓝牙协议架构分析之(一)