高通LK(little kernel)。
little kernel是bootloader,其作用是硬件初始化,读取linux 内核和ramdisk到RAM里,设置初始寄存器以及内核命令行参数,并跳转到内核运行。
作用
- 硬件初始化:设置向量表,MMU,cache,初始化外设
- 从存储器加载boot.img
- 支持flash和recovery
即使在64bit架构,LK依然运行在32bit模式
kernel授权
- 签名的boot.img生成
a.使用用户的私有key生成加签名的boot镜像
b.计算原始boot.img的SHA256的哈希值,并使用用户的私有key(定义于device/qcom/common/common.mk)对哈希值镜像加签。将最终的哈希值和签名添加到原始的boot.img的末尾。
c.用户必须用PRODUCT_PRIVATE_KEY设置自己的私有key文件,当前该宏定义于device/qcom/common/qcom.key,目前使用的是一个测试key。
d.校验签名启动目前用于kernel和recovery镜像。
- LK授权kernel(boot.img)
a.如果在目标平台的BoardConfig.mk中设置了TARGET_BOOTIMG_SIGNED= true,LK先校验boot.img,然后在启动kernel。
b.在启动过程中,LK提取原始的boot.img镜像并计算其SHA26的哈希值,并将其编译生成的哈希值对比,如果相对,则成功授权。这一过程中涉及到的函数如下:
verify_signed_bootimg()→image_verify()
c.如果成功授权,则LK传递给kernel的命令行中有”androidboot.authorized_kernel=true“
d.用户需要用公钥放到bootable/bootloader/lk/platform/msm_shared/certificate.cfile,LK将会使用该公钥解密boot.img最后的签名哈希值。
LK调用流程
- 启动序列起始于arch/arm/crt0.S: _start:
a.设置cpu
b.如果需要调用__cpu_early_init(),
c.如果需要代码重定位
d.设置栈
e.调用kmain()
- kmain的调用流程如下:
- bootstrap2()的调用流程
a.arch/arm/arch.c –arch_init()
b.platform/<platform>/(platform.c)--platform_init()
c.target/<target>/(init.c) –target_init()
初始化SPMI
初始化keypad
设置驱动能力以及配置SDCpin脚功能
初始化SD主机控制器;确认MMC卡,设置时钟。
mmc_init()初始化
从emmc读取分区表
partition_read_table()
- app/init.c –apps_init()
初始化APP_START 和 APP_END两个宏定义的apps,aboot_init()被调用;
如果有.entry字段,则app运行在在一个独立的线程中。
- app/aboot/aboot.c –aboot_init()
根据设置和环境启动
- 常规启动
- fastboot模式接收镜像
- recovery模式恢复固件
LK常规启动
- recovery和fastboot标志没有设置
- 将boot.img从emmc中读取到内存中,读取地址由target/msmXXX/rules.mk的base address指定。
- 将kernel加载到KERNEL_ADDR(该地址源于boot image头)
- 类似kernel,加载RAM disk到RAMDISK_ADDR
- 找到正确的设备树,加载到TAGS_ADDR
- 跟新设备树
a.获得/memory和/chosen的偏移
b.添加HLOS内存范围
c.将cmdline添加到/chosen节点
d.禁止cache,中断,跳转到kernel
代码片段
- boot_linux_from_mmc() {
boot_linux_from_mmc() { structboot_img_hdr*hdr=(void*)buf; // boot image header /* Read boot image header from emmcpartition into buf*/ if(mmc_read(ptn+offset,(unsignedint*)buf,page_size)){... } /* Read image without signature to the scratch address */ if (mmc_read(ptn+ offset, (void *)image_addr, imagesize_actual)) { .... } /* Read signature to the scratch address */ if(mmc_read(ptn+ offset, (void *)(image_addr+ offset), page_size)) /* Kernel image authentication */ verify_signed_bootimg(image_addr, imagesize_actual); /* Move kernel, ramdiskand device tree to correct address */ memmove((void*) hdr->kernel_addr, (char *)(image_addr+ page_size), hdr->kernel_size); memmove((void*) hdr->ramdisk_addr, (char *)(image_addr+ page_size+kernel_actual), hdr->ramdisk_size); /* Find the DT table address */ dt_table_offset = ((uint32_t)image_addr + page_size + kernel_actual +ramdisk_actual + second_actual); table = (struct dt_table*) dt_table_offset; /* Calculate the index of device tree within device tree table */ if(dev_tree_get_entry_info(table, &dt_entry) != 0){ } /* boot_linux : update device tree and jump to kernel */ boot_linux((void *)hdr->kernel_addr, (unsigned *) hdr->tags_addr, (const char *)cmdline, board_machtype(), (void *)hdr->ramdisk_addr, hdr->ramdisk_size);
- void boot_linux()
update_device_tree((void *)tags, final_cmdline, ramdisk, ramdisk_size); /*
shown on the next slide */
.....
if (IS_ARM64(kptr))
scm_elexec_call((paddr_t)kernel, tags_phys); /* Jump to a 64bit kernel */
else
entry(0, machtype, (unsigned*)tags_phys); /* Jump to a 32 bitkernel */
}
- 跟新设备树
Int update_device_tree(constvoid*fdt,char*cmdline,
void*ramdisk,unsignedramdisk_size)
{ .........
uint32_t*memory_reg;
/*Checkthedevicetreeheader*/
offset=fdt_check_header(fdt);
..........
/*Getoffsetofthe”memory”node*/
offset=fdt_path_offset(fdt,”/memory”);
/* Update “memory” node* /
ret=target_dev_tree_mem(fdt,offset);
/*Getoffsetofthe”chosen”node*/
ret=fdt_path_offset(fdt,”/chosen”);
/*Addingthecmdlinetothe”chosen”node*/
ret=fdt_setprop_string(fdt,offset,(constchar*)”bootargs”,(constvoid*)cmdlin
e);
/*Addingtheinitrd-start tothechosennode*/
ret=fdt_setprop_u32(fdt,offset,”linux,initrd-start”,(uint32_t)ramdisk);
/*Addingtheinitrd-end tothechosennode*/
ret=fdt_setprop_u32(fdt,offset,”linux,initrd-
end”,((uint32_t)ramdisk+ramdisk_size));
.... }
LK fastboot模式
- aboot_init检查boot.img是否存在,音量减键是否被按住
- 检查reboot的原因--check_reboot_mode
- 注册fastboot模式的函数方法--fastboot_register(cmd_list[i].name,cmd_list[i].cb);
- 初始化fastboot
fastboot_init(void *base, unsigned size)
为 fastboot_handler()创建线程
线程等待USB事件
- 设置USB--udc_start()
fastboot命令
- 在用户模式fastboot命令被disable了,这出于安全考虑
ifeq($(TARGET_BUILD_VARIANT),user)
CFLAGS += -DDISABLE_FASTBOOT_CMDS=1
endif
- 启动user版本的镜像,如果要支持特定命令,需要按如下做
bootable/bootloader/lk/app/aboot/aboot.c
structfastboot_cmd_desccmd_list[] = {
/* By default the enabled list is empty. */
{““, NULL},
/* move commands enclosed within the below ifndefto here
* if they need to be enabled in user build.
*/
#ifndefDISABLE_FASTBOOT_CMDS
/* Register the following commands only for non-user builds */
{“flash:”, cmd_flash},
{“erase:”, cmd_erase},
{“boot”, cmd_boot},
{“continue”, cmd_continue},
{“reboot”, cmd_reboot},
{“reboot-bootloader”, cmd_reboot_bootloader},
{“oemunlock”, cmd_oem_unlock},
{“oemlock”, cmd_oem_lock},
{“oemverified”, cmd_oem_verified},
{“oemdevice-info”, cmd_oem_devinfo},
{“oemenable-charger-screen”, cmd_oem_enable_charger_screen},
{“oemdisable-charger-screen”, cmd_oem_disable_charger_screen},
{“oem-select-display-panel”, cmd_oem_select_display_panel},
#endif
};
LK recovery模式
- aboot_init检查KEY_HOME or VOLUME UP是否被按压了
- 检查重启的原因--check_reboot_mode(),如果是RECOVERY_MODE原因,则设置boot_into_recovery = 1.
- boot_linux_from_mmc检查
if (!boot_into_recovery) {
.........
...
else {
index = partition_get_index(“recovery”);
ptn = partition_get_offset(index);
...........
......
}
- 从recovery分区获取镜像
gned int target_freq, unsigned int relation);