即日起在codingBlog上分享您的技术经验即可获得积分,积分可兑换现金哦。

Android启动流程解析之二:内核的引导

编程语言 ffmxnjm 9℃ 0评论
本文目录
[隐藏]

http://blog.csdn.net/ly890700/article/details/54586465


继续以c6(mido)的代码为例

由于目前大部分手机不再使用nand flash,取而代之的是emmc,因此启动内核的实现以boot_linux_from_mmc为例分析。

 

1. 一 boot_linux_from_mmc

boot_linux_from_mmc函数主要负责根据boot_into_xxx从对应的分区内读取相关信息并传给kernel,然后引导kernel。

boot_linux_from_mmc()函数的工作主要有: 

1).程序会从boot分区或者recovery分区的header中读取地址等信息,然后把kernel、ramdisk加载到内存中。

2).程序会从misc分区中读取bootloader_message结构体,如果有boot-recovery,则进入recovery模式

3).更新cmdline,然后把cmdline写到tags_addr地址,把参数传给kernel,kernel起来以后会到这个地址读取参数。

执行到boot_linux_from_mmc函数这里,说明这次启动不进入fastboot模式,可能的情况有:正常启动,进入recovery,开机闹钟启动。

bootable/bootloader/lk/app/aboot/aboot.c Collapse
source
int boot_linux_from_mmc(void)
{
    struct boot_img_hdr
*hdr = (
void*)
buf;
    struct boot_img_hdr
*uhdr;
    unsigned
offset = 0;
    int rcode;
    unsigned long long ptn
= 0;
    int index
= INVALID_PTN;
    unsigned char *image_addr
= 0;
    unsigned
kernel_actual;
    unsigned
ramdisk_actual;
    unsigned
imagesize_actual;
    unsigned
second_actual = 0;
    unsigned int dtb_size
= 0;
    unsigned int out_len
= 0;
    unsigned int out_avai_len
= 0;
    unsigned char *out_addr
= NULL;
    uint32_t
dtb_offset = 0;
    unsigned char *kernel_start_addr
= NULL;
    unsigned int kernel_size
= 0;
    int rc;
#if
DEVICE_TREE
    struct dt_table
*table;
    struct dt_entry
dt_entry;
    unsigned
dt_table_offset;
    uint32_t
dt_actual;
    uint32_t
dt_hdr_size;
    unsigned char *best_match_dt_addr
= NULL;
#endif
    struct kernel64_hdr
*kptr = NULL;
    if (check_format_bit())
        boot_into_recovery
= 1;
    if (!boot_into_recovery)
{
        memset(ffbm_mode_string, '\0'sizeof(ffbm_mode_string));
        rcode
= get_ffbm(ffbm_mode_string, 
sizeof(ffbm_mode_string));
        if (rcode
<= 0) {
            boot_into_ffbm
false;
            if (rcode
< 0)
                dprintf(CRITICAL,"failed
to get ffbm cookie"
);
        else
            boot_into_ffbm
true;
    else
        boot_into_ffbm
false;
    uhdr
= (
struct boot_img_hdr
*)EMMC_BOOT_IMG_HEADER_ADDR;
    if (!memcmp(uhdr->magic,
BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
        dprintf(INFO, "Unified
boot method!\n"
);
        hdr
= uhdr;
        goto unified_boot;
    }
    if (!boot_into_recovery)
{
        index
= partition_get_index(
"boot");
        ptn
= partition_get_offset(index);
        if(ptn
== 0) {
            dprintf(CRITICAL, "ERROR:
No boot partition found\n"
);
                    return -1;
        }
    }
    else {
        index
= partition_get_index(
"recovery");
        ptn
= partition_get_offset(index);
        if(ptn
== 0) {
            dprintf(CRITICAL, "ERROR:
No recovery partition found\n"
);
                    return -1;
        }
    }
    /*
Set Lun for boot & recovery partitions */
    mmc_set_lun(partition_get_lun(index));
    if (mmc_read(ptn
+ offset, (uint32_t *) buf, page_size)) {
        dprintf(CRITICAL, "ERROR:
Cannot read boot image header\n"
);
                return -1;
    }
    if (memcmp(hdr->magic,
BOOT_MAGIC, BOOT_MAGIC_SIZE)) {
        dprintf(CRITICAL, "ERROR:
Invalid boot image header\n"
);
                return -1;
    }
    if (hdr->page_size
&& (hdr->page_size != page_size)) {
        if (hdr->page_size
> BOOT_IMG_MAX_PAGE_SIZE) {
            dprintf(CRITICAL, "ERROR:
Invalid page size\n"
);
            return -1;
        }
        page_size
= hdr->page_size;
        page_mask
= page_size - 1;
    }
    /*
ensure commandline is terminated */
    hdr->cmdline[BOOT_ARGS_SIZE-1]
= 0;
    kernel_actual 
= ROUND_TO_PAGE(hdr->kernel_size,  page_mask);
    ramdisk_actual
= ROUND_TO_PAGE(hdr->ramdisk_size, page_mask);
    image_addr
= (unsigned 
char *)target_get_scratch_address();
#if
DEVICE_TREE
    dt_actual
= ROUND_TO_PAGE(hdr->dt_size, page_mask);
    if (UINT_MAX
< ((uint64_t)kernel_actual + (uint64_t)ramdisk_actual+ (uint64_t)dt_actual + page_size)) {
        dprintf(CRITICAL, "Integer
overflow detected in bootimage header fields at %u in %s\n"
,__LINE__,__FILE__);
        return -1;
    }
    imagesize_actual
= (page_size + kernel_actual + ramdisk_actual + dt_actual);
#else
    if (UINT_MAX
< ((uint64_t)kernel_actual + (uint64_t)ramdisk_actual + page_size)) {
        dprintf(CRITICAL, "Integer
overflow detected in bootimage header fields at %u in %s\n"
,__LINE__,__FILE__);
        return -1;
    }
    imagesize_actual
= (page_size + kernel_actual + ramdisk_actual);
#endif
#if
VERIFIED_BOOT
    boot_verifier_init();
#endif
    if (check_aboot_addr_range_overlap((uintptr_t)
image_addr, imagesize_actual))
    {
        dprintf(CRITICAL, "Boot
image buffer address overlaps with aboot addresses.\n"
);
        return -1;
    }
    /*
     *
Update loading flow of bootimage to support compressed/uncompressed
     *
bootimage on both 64bit and 32bit platform.
     *
1. Load bootimage from emmc partition onto DDR.
     *
2. Check if bootimage is gzip format. If yes, decompress compressed kernel
     *
3. Check kernel header and update kernel load addr for 64bit and 32bit
     *   
platform accordingly.
     *
4. Sanity Check on kernel_addr and ramdisk_addr and copy data.
     */
    dprintf(INFO, "Loading
(%s) image (%d): start\n"
,
            (!boot_into_recovery
"boot" "recovery"),imagesize_actual);
    bs_set_timestamp(BS_KERNEL_LOAD_START);
    if ((target_get_max_flash_size()
- page_size) < imagesize_actual)
    {
        dprintf(CRITICAL, "booimage 
size is greater than DDR can hold\n"
);
        return -1;
    }
    /*
Read image without signature */
    if (mmc_read(ptn
+ offset, (
void *)image_addr,
imagesize_actual))
    {
        dprintf(CRITICAL, "ERROR:
Cannot read boot image\n"
);
        return -1;
    }
    dprintf(INFO, "Loading
(%s) image (%d): done\n"
,
            (!boot_into_recovery
"boot" "recovery"),imagesize_actual);
    bs_set_timestamp(BS_KERNEL_LOAD_DONE);
    /*
Authenticate Kernel */
    dprintf(INFO, "use_signed_kernel=%d,
is_unlocked=%d, is_tampered=%d.\n"
,
        (int)
target_use_signed_kernel(),
        device.is_unlocked,
        device.is_tampered);
    /*
Change the condition a little bit to include the test framework support.
     *
We would never reach this point if device is in fastboot mode, even if we did
     *
that means we are in test mode, so execute kernel authentication part for the
     *
tests */
    if((target_use_signed_kernel()
&& (!device.is_unlocked)) || is_test_mode_enabled())
    {
        offset
= imagesize_actual;
        if (check_aboot_addr_range_overlap((uintptr_t)image_addr
+ offset, page_size))
        {
            dprintf(CRITICAL, "Signature
read buffer address overlaps with aboot addresses.\n"
);
            return -1;
        }
        /*
Read signature */
        if(mmc_read(ptn
+ offset, (
void *)(image_addr
+ offset), page_size))
        {
            dprintf(CRITICAL, "ERROR:
Cannot read boot image signature\n"
);
            return -1;
        }
        verify_signed_bootimg((uint32_t)image_addr,
imagesize_actual);
        /*
The purpose of our test is done here */
        if(is_test_mode_enabled()
&& auth_kernel_img)
            return 0;
    else {
        second_actual 
= ROUND_TO_PAGE(hdr->second_size,  page_mask);
        #ifdef
TZ_SAVE_KERNEL_HASH
        aboot_save_boot_hash_mmc((uint32_t)
image_addr, imagesize_actual);
        #endif
/* TZ_SAVE_KERNEL_HASH */
#ifdef
MDTP_SUPPORT
        {
            /*
Verify MDTP lock.
             *
For boot & recovery partitions, MDTP will use boot_verifier APIs,
             *
since verification was skipped in aboot. The signature is not part of the loaded image.
             */
            mdtp_ext_partition_verification_t
ext_partition;
            ext_partition.partition
= boot_into_recovery ? MDTP_PARTITION_RECOVERY : MDTP_PARTITION_BOOT;
            ext_partition.integrity_state
= MDTP_PARTITION_STATE_UNSET;
            ext_partition.page_size
= page_size;
            ext_partition.image_addr
= (uint32)image_addr;
            ext_partition.image_size
= imagesize_actual;
            ext_partition.sig_avail
= FALSE;
            mdtp_fwlock_verify_lock(&ext_partition);
        }
#endif
/* MDTP_SUPPORT */
    }
#if
VERIFIED_BOOT
    if(boot_verify_get_state()
== ORANGE)
    {
#if
FBCON_DISPLAY_MSG
        display_bootverify_menu(DISPLAY_MENU_ORANGE);
        wait_for_users_action();
#else
        dprintf(CRITICAL,
            "Your
device has been unlocked and can't be trusted.\nWait for 5 seconds before proceeding\n"
);
#endif
    }
#endif
#if
VERIFIED_BOOT
#if
!VBOOT_MOTA
    //
send root of trust
    if(!send_rot_command((uint32_t)device.is_unlocked))
        ASSERT(0);
#endif
#endif
    /*
     *
Check if the kernel image is a gzip package. If yes, need to decompress it.
     *
If not, continue booting.
     */
    if (is_gzip_package((unsigned char *)(image_addr
+ page_size), hdr->kernel_size))
    {
        out_addr
= (unsigned 
char *)(image_addr
+ imagesize_actual + page_size);
        out_avai_len
= target_get_max_flash_size() - imagesize_actual - page_size;
        dprintf(INFO, "decompressing
kernel image: start\n"
);
        rc
= decompress((unsigned 
char *)(image_addr
+ page_size),
                hdr->kernel_size,
out_addr, out_avai_len,
                &dtb_offset,
&out_len);
        if (rc)
        {
            dprintf(CRITICAL, "decompressing
kernel image failed!!!\n"
);
            ASSERT(0);
        }
        dprintf(INFO, "decompressing
kernel image: done\n"
);
        kptr
= (
struct kernel64_hdr
*)out_addr;
        kernel_start_addr
= out_addr;
        kernel_size
= out_len;
    else {
        kptr
= (
struct kernel64_hdr
*)(image_addr + page_size);
        kernel_start_addr
= (unsigned 
char *)(image_addr
+ page_size);
        kernel_size
= hdr->kernel_size;
    }
    /*
     *
Update the kernel/ramdisk/tags address if the boot image header
     *
has default values, these default values come from mkbootimg when
     *
the boot image is flashed using fastboot flash:raw
     */
    update_ker_tags_rdisk_addr(hdr,
IS_ARM64(kptr));
    /*
Get virtual addresses since the hdr saves physical addresses. */
    hdr->kernel_addr
= VA((addr_t)(hdr->kernel_addr));
    hdr->ramdisk_addr
= VA((addr_t)(hdr->ramdisk_addr));
    hdr->tags_addr
= VA((addr_t)(hdr->tags_addr));
    kernel_size
= ROUND_TO_PAGE(kernel_size,  page_mask);
    /*
Check if the addresses in the header are valid. */
    if (check_aboot_addr_range_overlap(hdr->kernel_addr,
kernel_size) ||
        check_aboot_addr_range_overlap(hdr->ramdisk_addr,
ramdisk_actual))
    {
        dprintf(CRITICAL, "kernel/ramdisk
addresses overlap with aboot addresses.\n"
);
        return -1;
    }
#ifndef
DEVICE_TREE
    if (check_aboot_addr_range_overlap(hdr->tags_addr,
MAX_TAGS_SIZE))
    {
        dprintf(CRITICAL, "Tags
addresses overlap with aboot addresses.\n"
);
        return -1;
    }
#endif
    /*
Move kernel, ramdisk and device tree to correct address */
    memmove((void*)
hdr->kernel_addr, kernel_start_addr, kernel_size);
    memmove((void*)
hdr->ramdisk_addr, (
char *)(image_addr
+ page_size + kernel_actual), hdr->ramdisk_size);
    #if
DEVICE_TREE
    if(hdr->dt_size)
{
        dt_table_offset
= ((uint32_t)image_addr + page_size + kernel_actual + ramdisk_actual + second_actual);
        table
= (
struct dt_table*)
dt_table_offset;
        if (dev_tree_validate(table,
hdr->page_size, &dt_hdr_size) != 0) {
            dprintf(CRITICAL, "ERROR:
Cannot validate Device Tree Table \n"
);
            return -1;
        }
        /*
Its Error if, dt_hdr_size (table->num_entries * dt_entry size + Dev_Tree Header)
        goes
beyound hdr->dt_size*/
        if (dt_hdr_size
> ROUND_TO_PAGE(hdr->dt_size,hdr->page_size)) {
            dprintf(CRITICAL, "ERROR:
Invalid Device Tree size \n"
);
            return -1;
        }
        /*
Find index of device tree within device tree table */
        if(dev_tree_get_entry_info(table,
&dt_entry) != 0){
            dprintf(CRITICAL, "ERROR:
Getting device tree address failed\n"
);
            return -1;
        }
        if(dt_entry.offset
> (UINT_MAX - dt_entry.size)) {
            dprintf(CRITICAL, "ERROR:
Device tree contents are Invalid\n"
);
            return -1;
        }
        /*
Ensure we are not overshooting dt_size with the dt_entry selected */
        if ((dt_entry.offset
+ dt_entry.size) > hdr->dt_size) {
            dprintf(CRITICAL, "ERROR:
Device tree contents are Invalid\n"
);
            return -1;
        }
        if (is_gzip_package((unsigned char *)dt_table_offset
+ dt_entry.offset, dt_entry.size))
        {
            unsigned int compressed_size
= 0;
            out_addr
+= out_len;
            out_avai_len
-= out_len;
            dprintf(INFO, "decompressing
dtb: start\n"
);
            rc
= decompress((unsigned 
char *)dt_table_offset
+ dt_entry.offset,
                    dt_entry.size,
out_addr, out_avai_len,
                    &compressed_size,
&dtb_size);
            if (rc)
            {
                dprintf(CRITICAL, "decompressing
dtb failed!!!\n"
);
                ASSERT(0);
            }
            dprintf(INFO, "decompressing
dtb: done\n"
);
            best_match_dt_addr
= out_addr;
        else {
            best_match_dt_addr
= (unsigned 
char *)dt_table_offset
+ dt_entry.offset;
            dtb_size
= dt_entry.size;
        }
        /*
Validate and Read device device tree in the tags_addr */
        if (check_aboot_addr_range_overlap(hdr->tags_addr,
dtb_size))
        {
            dprintf(CRITICAL, "Device
tree addresses overlap with aboot addresses.\n"
);
            return -1;
        }
        memmove((void *)hdr->tags_addr,
(
char *)best_match_dt_addr,
dtb_size);
    else {
        /*
Validate the tags_addr */
        if (check_aboot_addr_range_overlap(hdr->tags_addr,
kernel_actual))
        {
            dprintf(CRITICAL, "Device
tree addresses overlap with aboot addresses.\n"
);
            return -1;
        }
        /*
         *
If appended dev tree is found, update the atags with
         *
memory address to the DTB appended location on RAM.
         *
Else update with the atags address in the kernel header
         */
        void *dtb;
        dtb
= dev_tree_appended((
void*)(image_addr
+ page_size),
                    hdr->kernel_size,
dtb_offset,
                    (void *)hdr->tags_addr);
        if (!dtb)
{
            dprintf(CRITICAL, "ERROR:
Appended Device Tree Blob not found\n"
);
            return -1;
        }
    }
    #endif
    if (boot_into_recovery
&& !device.is_unlocked && !device.is_tampered)
        target_load_ssd_keystore();
unified_boot:
    boot_linux((void *)hdr->kernel_addr,
(
void *)hdr->tags_addr,
           (const char *)hdr->cmdline,
board_machtype(),
           (void *)hdr->ramdisk_addr,
hdr->ramdisk_size);
    return 0;
}

进入到boot_linux_from_mmc函数后,

1 首先创建一个用来保存boot.img文件头信息的变量hdr,buf是一个4096byte的数组,hdr和hdr指向了同一个内存地址。

2 执行check_format_bit,根据bootselect分区信息判断是否进入recovery模式

3 如果不是recovery模式,此时有两种可能,正常开机/进入ffbm工厂测试模式,进入工厂测试模式是正行启动,但是向kernel传参会多一个字符串"androidboot.mode='ffbm_mode_string'"。因此这里还要调用get_ffbm,根据misc分区信息判断是否进入ffbm模式。

4 如果boot_into_recovery为true,就boot_into_ffbm置为false

5 uhdr = (struct boot_img_hdr *)EMMC_BOOT_IMG_HEADER_ADDR,计算uhdr,uhdr指向boot分区header地址

6 检查uhdr->magic 是否等于 "Android!",如果是就直接跳转到kernel,这是非正常路径

7 如果不是recovery模式,可能是正常启动或者进入ffbm,这种情况下调用partition_get_index获取boot分区索引,调用partition_get_offset获取boot分区便宜。 如果是recovery模式,读取recovery分区,并获得recovery分区的偏移量。

8 调用mmc_set_lun设置boot或recovery分区的lun号

9 调用mmc_read,从boot或者recovery分区读取1字节的内容到buf(hdr)中,我们知道在boot/recovery中开始的1字节存放的是hdr的内容。

10 调用memcmp,判断boot.img头结构体的魔数是否正确。

11 根据hdr->page_size,判断是否需要更新页大小。

12 如果有DEVICE_TREE,计算出dt所占的页的大小 dt_actual = ROUND_TO_PAGE(hdr->dt_size, page_mask); image占的页的总大小为imagesize_actual = (page_size + kernel_actual + ramdisk_actual + dt_actual);

如果没有DEVICE_TREE,imagesize_actual = (page_size + kernel_actual + ramdisk_actual);

13 检查boot.img是否与aboot的内存空间有重叠

14 调用函数update_ker_tags_rdisk_addr更新boot.img头结构体

15 将hdr中保存的物理地址转化为虚拟地址

hdr->kernel_addr = VA((addr_t)(hdr->kernel_addr));


hdr->ramdisk_addr = VA((addr_t)(hdr->ramdisk_addr));


hdr->tags_addr = VA((addr_t)(hdr->tags_addr));

16 kernel大小向上页对齐 :kernel_actual  = ROUND_TO_PAGE(hdr->kernel_size,  page_mask), ramdisk大小向上页对齐:kernel_actual  = ROUND_TO_PAGE(hdr->kernel_size,  page_mask) 

17 调用函数check_aboot_addr_range_overlap,检查kernel和ramdisk的是否与aboot的内存空间有重叠

18 将内核,randisk,和device tree在内存中移动到正确的地址

19 最后在函数返回前,将会执行到boot_linux,在boot_linux中将会完成跳转到内核的操作。



2. 二 boot_linux

boot_linux的实现同样位于bootable/bootloader/lk/app/aboot/aboot.c

bootable/bootloader/lk/app/aboot/aboot.c Collapse
source
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));
    uint32_t
tags_phys = PA((addr_t)tags);
    struct kernel64_hdr
*kptr = ((
struct kernel64_hdr*)(PA((addr_t)kernel)));
    ramdisk
= (
void *)PA((addr_t)ramdisk);
    final_cmdline
= update_cmdline((
const char*)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);
    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);
#endif
    free(final_cmdline);
#if
VERIFIED_BOOT
#if
!VBOOT_MOTA
    if (device.verity_mode
== 0) {
#if
FBCON_DISPLAY_MSG
        display_bootverify_menu(DISPLAY_MENU_LOGGING);
        wait_for_users_action();
#else
        dprintf(CRITICAL,
            "The
dm-verity is not started in enforcing mode.\nWait for 5 seconds before proceeding\n"
);
        mdelay(5000);
#endif
    }
#endif
#endif
#if
0//VERIFIED_BOOT
    /*
Write protect the device info */
    if (!boot_into_recovery
&& target_build_variant_user() && devinfo_present && mmc_write_protect(
"devinfo",
1))
    {
        dprintf(INFO, "Failed
to write protect dev info\n"
);
        //ASSERT(0);
    }
#endif
    /*
Turn off splash screen if enabled */
#if
DISPLAY_SPLASH_SCREEN
    target_display_shutdown();
#endif
    /*
Perform target specific cleanup */
    target_uninit();
    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);
#if
ARM_WITH_MMU
    arch_disable_mmu();
#endif
    bs_set_timestamp(BS_KERNEL_ENTRY);
    if (IS_ARM64(kptr))
        /*
Jump to a 64bit kernel */
        scm_elexec_call((paddr_t)kernel,
tags_phys);
    else
        /*
Jump to a 32bit kernel */
        entry(0,
machtype, (unsigned*)tags_phys);
}

1 首先将kernel的起始内存地址转化成entry_func_ptr函数类型:void (*entry)(unsigned, unsigned, unsigned*) = (entry_func_ptr*)(PA((addr_t)kernel));

2 更新cmdline:final_cmdline = update_cmdline((const char*)cmdline);

3 更新device tree内容,主要是三部分:memory,cmdline,ramdisk:ret = update_device_tree((void *)tags,(const char *)final_cmdline, ramdisk, ramdisk_size);

4  由于cmdline内容已经打包进device tree中,这里可以释放cmdline临时占用的内存:free(final_cmdline);

5 对devinfo分区写保护:mmc_write_protect("devinfo", 1)

6 完成目标板级清除动作:target_uninit()

7 关闭lcd:target_display_shutdown()

8 关闭中断:enter_critical_section();

9 完成平台级清除动作:platform_uninit();

10 禁用cache:arch_disable_cache(UCACHE),如果有mmu还要关闭mmu:arch_disable_mmu()

11 设置kernel入口时间戳:bs_set_timestamp(BS_KERNEL_ENTRY);

12 判断是32位还是64位内核,执行不同的跳转操作。对于32bit kernel,直接跳转到kernel入口函数执行。对于64bit kernel,执行scm_elexec_call完成跳转。假设为64位内核,下面继续分析scm_elexec_call函数

 

3. 三 scm_elexec_call

scm_elexec_call定义在bootable/bootloader/lk/platform/msm_shared/scm.c

bootable/bootloader/lk/platform/msm_shared/scm.c Collapse
source
void scm_elexec_call(paddr_t
kernel_entry, paddr_t dtb_offset)
{
    uint32_t
svc_id = SCM_SVC_MILESTONE_32_64_ID;
    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;
    param.el1_elr
= kernel_entry;
    /*
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);
    }
    else
    {
        scm_arg.x0
= MAKE_SIP_SCM_CMD(SCM_SVC_MILESTONE_32_64_ID, SCM_SVC_MILESTONE_CMD_ID);
        scm_arg.x1
= MAKE_SCM_ARGS(0x2, SMC_PARAM_TYPE_BUFFER_READ);
        scm_arg.x2
= (uint32_t ) ¶m;
        scm_arg.x3
sizeof(el1_system_param);
        scm_call2(&scm_arg,
NULL);
    }
    /*
Assert if execution ever reaches here */
    dprintf(CRITICAL, "Failed
to jump to kernel\n"
);
    ASSERT(0);
}

在scm_elexec_call中先获取到device tree的物理内存地址param.el1_x0 = dtb_offset, kernel入口的物理内存地址param.el1_elr = kernel_entry,最终通过scm_call或者scm_call2完成跳转到内核的操作。


转载请注明:CodingBlog » Android启动流程解析之二:内核的引导

喜欢 (0)or分享 (0)
发表我的评论
取消评论

*

表情