本文簡單介紹了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); }