FROM:https://blog.csdn.net/xichangbao/article/details/51484627
1 boot_linux()。
typedef void entry_func_ptr(unsigned, unsigned, unsigned*);
void boot_linux(void *kernel, unsigned *tags,
const char *cmdline, unsigned machtype,
void *ramdisk, unsigned ramdisk_size)
{
unsigned char *final_cmdline;
#if DEVICE_TREE
int ret = 0;
#endif
void (*entry)(unsigned, unsigned, unsigned*) = (entry_func_ptr*)(PA((addr_t)kernel)); // 将kernel的起始内存地址转化成entry_func_ptr函数类型; 在C语言中规定,一个函数总是占用一段连续的内存区,而函数名就是该函数所占内存区的首地址;对这一块c代码感到困惑,反汇编后看了下汇编代码,逻辑是没有问题的。
uint32_t tags_phys = PA((addr_t)tags);
struct kernel64_hdr *kptr = (struct kernel64_hdr*)kernel;
ramdisk = (void *)PA((addr_t)ramdisk);
final_cmdline = update_cmdline((const char*)cmdline); // 更新cmdline,这里可以动态增加修改默认cmdline的内容
#if DEVICE_TREE
dprintf(INFO, "Updating device tree: start\n");
/* Update the Device Tree */
ret = update_device_tree((void *)tags,(const char *)final_cmdline, ramdisk, ramdisk_size); // 更新device tree内容,主要是三部分:memory,cmdline,ramdisk
if(ret)
{
dprintf(CRITICAL, "ERROR: Updating Device Tree Failed \n");
ASSERT(0);
}
dprintf(INFO, "Updating device tree: done\n");
#else
/* Generating the Atags */
generate_atags(tags, final_cmdline, ramdisk, ramdisk_size); // Atags已经基本被废弃
#endif
free(final_cmdline); // 由于cmdline内容已经打包进device tree中,这里可以释放cmdline临时占用的内存
#if VERIFIED_BOOT
/* Write protect the device info */
if (target_build_variant_user() && mmc_write_protect("devinfo", 1)) // 对devinfo分区写保护,注意devinfo分区的起始地址和大小,避免写保护越界
{
dprintf(INFO, "Failed to write protect dev info\n");
ASSERT(0);
}
#endif
/* Perform target specific cleanup */
target_uninit(); // 完成目标板级清除动作
/* Turn off splash screen if enabled */
#if DISPLAY_SPLASH_SCREEN
target_display_shutdown(); // 关闭lcd
#endif
dprintf(INFO, "booting linux @ %p, ramdisk @ %p (%d), tags/device tree @ %p\n",
entry, ramdisk, ramdisk_size, (void *)tags_phys);
enter_critical_section(); // 关闭中断
/* do any platform specific cleanup before kernel entry */
platform_uninit(); // 完成平台级清除动作
arch_disable_cache(UCACHE); // 禁用cache
#if ARM_WITH_MMU
arch_disable_mmu(); // 关闭mmu
#endif
bs_set_timestamp(BS_KERNEL_ENTRY); // 设置kernel入口时间戳
if (IS_ARM64(kptr))
/* Jump to a 64bit kernel */
scm_elexec_call((paddr_t)kernel, tags_phys); // 64位kernel
else
/* Jump to a 32bit kernel */
entry(0, machtype, (unsigned*)tags_phys); // 32位kernel直接跳转到kernel入口函数执行;r0 = 0; r1 = machtype; r2 = device tree的物理内存地址
}
scm_elexec_call()。
void scm_elexec_call(paddr_t kernel_entry, paddr_t dtb_offset)
{
uint32_t svc_id = SCM_SVC_MILESTONE_32_64_ID; // 标志从lk的32位切换到kernel的64位
uint32_t cmd_id = SCM_SVC_MILESTONE_CMD_ID;
void *cmd_buf;
size_t cmd_len;
static el1_system_param param __attribute__((aligned(0x1000)));
scmcall_arg scm_arg = {0};
param.el1_x0 = dtb_offset; // device tree的物理内存地址
param.el1_elr = kernel_entry; // kernel入口的物理内存地址
/* Response Buffer = Null as no response expected */
dprintf(INFO, "Jumping to kernel via monitor\n");
if (!is_scm_armv8_support())
{
/* Command Buffer */
cmd_buf = (void *)¶m;
cmd_len = sizeof(el1_system_param);
scm_call(svc_id, cmd_id, cmd_buf, cmd_len, NULL, 0); // Secure Monitor Call
}
else
{
scm_arg.x0 = MAKE_SIP_SCM_CMD(SCM_SVC_MILESTONE_32_64_ID, SCM_SVC_MILESTONE_CMD_ID); // scm命令
scm_arg.x1 = MAKE_SCM_ARGS(0x2, SMC_PARAM_TYPE_BUFFER_READ); // scm参数
scm_arg.x2 = (uint32_t ) ¶m; // EL1的系统参数
scm_arg.x3 = sizeof(el1_system_param); // EL1的系统参数的大小
scm_call2(&scm_arg, NULL); // Secure Monitor Call;传递合适的参数,最终通过smc #0指令切换到TrustZone模式完成到kernel的跳转,由于tz没有源码,其具体实现就不清楚了
}
/* Assert if execution ever reaches here */
dprintf(CRITICAL, "Failed to jump to kernel\n");
ASSERT(0);
}
---------------------
作者:xichangbao
来源:CSDN
原文:https://blog.csdn.net/xichangbao/article/details/51484627
版权声明:本文为博主原创文章,转载请附上博文链接!