mtk-lk代碼分析


本文簡單介紹了mtk-lk階段的啟動流程。

1.crt0.S的_start跳轉到kmain執行

.section ".text.boot"
.globl _start
_start:
        ......
    bl        kmain

2.kmain函數在kernel/main.c中定義

void kmain(void)
{
//初始化線程系統,初始化run_queue& thread鏈表,初始化bootstrap線程,並標記為當前線程  
    thread_init_early();

//設置異常向量表基地址,使能mmu和neon,neon為多媒體應用優化設計
    arch_early_init();

//平台相關初始化,中斷、uart、gpio、wdt、i2c、pmic等,很多在preloader階段已經完成的無需重復初始化    
    platform_early_init();

//目標板子前期初始化,這里並未定義
    target_early_init();
    
//調用構造函數,調用.ctors段中的函數,使用objdump反匯編lk文件,並不存在.ctors段,這里直接跳過
    call_constructors();

//初始化堆空間,插入空閑堆空間,並進行整合,減少碎片化
    heap_init();

//timer線程初始化,這里跳過這步
    thread_init();

//初始化dpc線程,調用dpc_queue將callback函數加入線程中,有時間觸發時,線程會順序執行回調函數
    dpc_init();
//初始化timer_queue,並設置每隔10ms產生中斷調用timer_tick函數 timer_init(); #if (!ENABLE_NANDWRITE) //創建並執行bootstrap2線程 dprintf(SPEW, "creating bootstrap completion thread\n"); thread_resume(thread_create("bootstrap2", &bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE)); //退出臨界區,使能中斷 exit_critical_section(); //標記當前線程為空閑狀態,即bootstrap線程空閑 thread_become_idle(); #else bootstrap_nandwrite(); #endif }

 

  • platform_early_init
void platform_early_init(void)
{
//初始化gic controller platform_init_interrupts();
//timer前期初始化 platform_early_init_timer();
//gpio設置為default狀態 mt_gpio_set_default();
//串口初始化,從bootarg中獲取串口和波特率並進行設置 uart_init_early();
//wdt初始化 mtk_wdt_init();
//i2c初始化,preloader階段已經完成,這里忽略 i2c_hw_init();
//pmic初始化,preloader階段完成,這里忽略 pmic_init(); }
  • call_constructors,遍歷.ctors(__ctor_list到__ctor_end)段中所有的函數,並執行,通過objdump反編譯lk並不存在.ctors段,這里直接忽略
static void call_constructors(void)
{
    void **ctor;
   
    ctor = &__ctor_list;
    while(ctor != &__ctor_end) {
        void (*func)(void);

        func = (void (*)())*ctor;

        func();
        ctor++;
    }
}
  • heap_init,堆初始化
void heap_init(void)
{
    LTRACE_ENTRY;
//設置堆空間范圍
    theheap.base = (void *)HEAP_START;
    theheap.len = HEAP_LEN;

//初始化空閑堆鏈表
    list_initialize(&theheap.free_list);

//創建空閑堆塊,並插入空閑堆鏈表中,並與前后相鄰空閑堆合並,初始化階段就一個空閑堆
    heap_insert_free_chunk(heap_create_free_chunk(theheap.base, theheap.len));
#ifdef MTK_3LEVEL_PAGETABLE
//標記堆初始化標志位為1
extern ld_tt_l2_info_t ld_tt_l2_info; ld_tt_l2_info.heap_init_done = 1; #endif }
  • dpc_init,初始化dpc線程,該線程負責順序執行dpc鏈表中的cb函數,thread_exit函數調用dpc_queue,負責處理線程退出時的清理工作,包括從線程鏈表中刪除節點並釋放線程的棧空間
void dpc_init(void)
{
 //初始化dpc_event  
    event_init(&dpc_event, false, 0);
//創建並啟用dpc線程,調用dpc_thread_routine函數,等待dpc_event事件發生
    thread_resume(thread_create("dpc", &dpc_thread_routine, NULL, DPC_PRIORITY, DEFAULT_STACK_SIZE));
}

static int dpc_thread_routine(void *arg)
{
//循環等待dpc事件發生
    for (;;) {
        event_wait(&dpc_event);
//關閉中斷,進入臨界區
        enter_critical_section();
//將該dpc從鏈表中刪除
        struct dpc *dpc = list_remove_head_type(&dpc_list, struct dpc, node);
        if (!dpc)
//dpc信號觸發標志清零
            event_unsignal(&dpc_event);
//打開中斷,退出臨界區
        exit_critical_section();

        if (dpc) {
//執行dpc的callback函數
            dpc->cb(dpc->arg);

            free(dpc);
        }
    }

    return 0;
}

//通過dpc_queue將需要執行的操作加入dpc鏈表中
status_t dpc_queue(dpc_callback cb, void *arg, uint flags)
{
    struct dpc *dpc;

    dpc = malloc(sizeof(struct dpc));

    dpc->cb = cb;
    dpc->arg = arg;
    enter_critical_section();
    list_add_tail(&dpc_list, &dpc->node);
    event_signal(&dpc_event, (flags & DPC_FLAG_NORESCHED) ? false : true);
    exit_critical_section();

    return NO_ERROR;
}
  • timer_init,初始化timer_queue,並且設置10ms的周期任務執行timer_tick來實現線程調度

void timer_init(void)
{
  list_initialize(&timer_queue);

  platform_set_periodic_timer(timer_tick, NULL, 10); /* 10ms */
}

 

 3.bootstrap2代碼分析

static int bootstrap2(void *arg)
{
//arch初始化,這里忽略直接返回
    arch_init();
//平台其余部分初始化
    platform_init();
//target初始化,這里忽略直接返回    
    target_init();
//調用app->init函數,加載並啟動kernel
    apps_init();

    return 0;
}
  • platform_init
void platform_init(void)
{
//mmc初始化 mmc_legacy_init(
1);
//led初始化 leds_init();
//env初始化 env_init();
//設置framebuffer的起始地址和大小 g_fb_size
= mt_disp_get_vram_size(); g_fb_base = mblock_reserve(&g_boot_arg->mblock_info, g_fb_size, 0x10000, 0xa0000000, RANKMAX);
//display&lcm初始化 mt_disp_init((
void *)g_fb_base);
//填充fb為黑畫面 mt_disp_fill_rect(
0, 0, CFG_DISPLAY_WIDTH, CFG_DISPLAY_HEIGHT, 0x0);
   mt_disp_update(
0, 0, CFG_DISPLAY_WIDTH, CFG_DISPLAY_HEIGHT);
//初始化console drv_video_init();
//這里直接返回 set_kpd_pmic_mode();
//啟動模式 boot_mode_select();
//加載開機動畫 mboot_common_load_logo((unsigned
long)mt_get_logo_db_addr_pa(), "logo");
//安全相關 sec_func_init(pl_start_addr); seclib_set_oemkey(g_oemkey, OEM_PUBK_SZ);
//pll設置 mt_pll_turn_off();
//battery,charge相關 mt65xx_bat_init();
//rtc開機檢測 rtc_boot_check(
false); //關機充電啟動則顯示充電圖標,否則顯示正常開機畫面 if (kernel_charging_boot() == 1) {   mt_disp_power(TRUE);   mt_disp_show_low_battery();   mt65xx_leds_brightness_set(6, 110); } else if (g_boot_mode != KERNEL_POWER_OFF_CHARGING_BOOT && g_boot_mode != LOW_POWER_OFF_CHARGING_BOOT) { if (g_boot_mode != ALARM_BOOT && (g_boot_mode != FASTBOOT)) { mt_disp_show_boot_logo(); } } //打開背光 mt65xx_backlight_on(); mt_disp_update(0, 0, CFG_DISPLAY_WIDTH, CFG_DISPLAY_HEIGHT);
//在LCM上顯示開機模式文字,正常開機顯示"=> NORMAL BOOT",user版本該文字不顯示 sw_env(); }
  • app_init
void apps_init(void)
{
    const struct app_descriptor *app;
//調用.apps段中的app_init函數
    for (app = &__apps_start; app != &__apps_end; app++) {
        if (app->init)
            app->init(app);
    }
    /* start any that want to start on boot */
    for (app = &__apps_start; app != &__apps_end; app++) {
        if (app->entry && (app->flags & APP_FLAG_DONT_START_ON_BOOT) == 0) {
            start_app(app);
        }
    }
}

mt_boot.c文件中定義了app,調用mt_boot_init函數,APP_START宏定義的內容放入.apps段中
APP_START(mt_boot)
.init = mt_boot_init,
 APP_END
#define APP_START(appname) struct app_descriptor _app_##appname __SECTION(".apps") = { .name = #appname,
#define APP_END };
  • mt_boot_init,主要調用boot_linux_from_storage函數加載並啟動內核
boot_linux_from_storage();
  • boot_linux_from_storage
int boot_linux_from_storage(void)
{
//加載bootimage header ret
= mboot_android_load_bootimg_hdr(PART_BOOTIMG, CFG_BOOTIMG_LOAD_ADDR);
//加載bootimage ret
= mboot_android_load_bootimg(PART_BOOTIMG, kimg_load_addr); #ifndef SKIP_LOADING_RAMDISK if (g_rimg_sz == 0) { if (g_boot_hdr != NULL) { g_rimg_sz = g_boot_hdr->ramdisk_size; } } #ifdef MTK_3LEVEL_PAGETABLE /* rootfs addr */ if (g_boot_hdr != NULL) { arch_mmu_map((uint64_t)g_boot_hdr->ramdisk_addr, (uint32_t)g_boot_hdr->ramdisk_addr, MMU_MEMORY_TYPE_NORMAL_WRITE_BACK | MMU_MEMORY_AP_P_RW_U_NA, ROUNDUP(g_rimg_sz, PAGE_SIZE)); } #endif /* relocate rootfs (ignore rootfs header) */ memcpy((g_boot_hdr!=NULL) ? (char *)g_boot_hdr->ramdisk_addr : (char *)CFG_RAMDISK_LOAD_ADDR, (char *)(g_rmem_off), g_rimg_sz); g_rmem_off = (g_boot_hdr!=NULL) ? g_boot_hdr->ramdisk_addr : CFG_RAMDISK_LOAD_ADDR; #endif // preloader傳遞給lk的參數中獲取uart口,並將其添加到cmdline中
  custom_port_in_kernel(g_boot_mode, cmdline_get());
//boot_linux,tags_addr作為fdt的加載地址 boot_linux((
void *)g_boot_hdr->kernel_addr, (unsigned *)g_boot_hdr->tags_addr, (char *)cmdline_get(), board_machtype(), (void *)g_boot_hdr->ramdisk_addr, g_rimg_sz); }
  •  boot_linux,啟動dts文件
void boot_linux(void *kernel, unsigned *tags,
                char *cmdline, unsigned machtype,
                void *ramdisk, unsigned ramdisk_size)
{
    #ifdef DEVICE_TREE_SUPPORT
    boot_linux_fdt((void *)kernel, (unsigned *)tags,
                   (char *)cmdline, machtype,
                   (void *)ramdisk, ramdisk_size);

    while (1) ;
#endif
}
  • boot_linux_fdt,加載並解析fdt,關閉cache&mmu,最終跳轉到kernel執行
int boot_linux_fdt(void *kernel, unsigned *tags,
                   char *cmdline, unsigned machtype,
                   void *ramdisk, unsigned ramdisk_size)
{
    zimage_size = (g_boot_hdr->kernel_size);
    addr = (unsigned int)(zimage_addr + zimage_size);
//從zImage后的地址依次尋址,根據MAGIC匹配fdt
for (dtb_size = 0; dtb_size < zimage_size; dtb_size++, addr--) { magic = (unsigned char *)addr;
//fdt MAGIC 0xd00dfeed
if ( *(magic + 3) == 0xED && *(magic + 2) == 0xFE && *(magic + 1) == 0x0D && *(magic + 0) == 0xD0) { dtb_addr = addr; dtb_size = (dtb_size + 0x3) & (~0x3);
//copy fdt到tags_addr(fdt)地址中 memcpy_u8(fdt, (
void *)dtb_addr, dtb_size); dtb_addr = (unsigned int)fdt; break; }    } if (dtb_size != zimage_size) { zimage_size -= dtb_size; }
//解壓zImage鏡像
if (decompress_kernel((unsigned char *)zimage_addr, (void *)g_boot_hdr->kernel_addr, (int)zimage_size, (int)0x4000000)) { while (1); } if (fdt32_to_cpu(*(unsigned int *)dtb_addr) == FDT_MAGIC) { dtb_size = fdt32_to_cpu(*(unsigned int *)(dtb_addr+0x4));   }
//copy fdt memcpy(fdt, (
void *)dtb_addr, dtb_size); strcpy(&buf[FDT_BUFF_SIZE], FDT_BUFF_PATTERN); setup_fdt(fdt, MIN(0x100000, (g_fb_base-(unsigned int)fdt)));
//加載modem鏡像
extern void load_modem_image(void)__attribute__((weak)); if (load_modem_image) { load_modem_image(); }

//根據是否為user版本決定kernel階段uart log是否打開

#ifdef USER_BUILD
    sprintf(cmdline,"%s%s",cmdline," printk.disable_uart=1");
#else
    sprintf(cmdline,"%s%s",cmdline," printk.disable_uart=0 ddebug_query=\"file *mediatek* +p ; file *gpu* =_\"");
#endif

//解析memory節點
    offset = fdt_path_offset(fdt, "/memory");
    extern int get_mblock_num(void) __attribute__((weak));
    ret = fdt_setprop(fdt, offset, "reg", mem_reg_property,
                      ((int)get_mblock_num? get_mblock_num(): g_nr_bank ) * sizeof(dt_dram_info));
//解析chosen節點 offset
= fdt_path_offset(fdt, "/chosen"); ret = fdt_setprop_cell(fdt, offset, "linux,initrd-start",(unsigned int) ramdisk); ret = fdt_setprop_cell(fdt, offset, "linux,initrd-end", (unsigned int)ramdisk + ramdisk_size);
//關閉cache&mmu arch_disable_cache(UCACHE); arch_disable_mmu();
//跳轉kernel執行,entry為kernel地址,tags為fdt地址 lk_jump64((u32)entry, (u32)tags,
0, KERNEL_64BITS); }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM