轉(zhuǎn)自:http://blog.csdn.net/loongembedded/article/details/41747523
2014-12-05 14:37?3599人閱讀? ?分類: Android Bootloader(68)?
Android?開發(fā)之 ---- bootloader (LK)
?
LK是什么
???????????LK 是 Little Kernel 它是 appsbl (Applications ARM Boot Loader)流程代碼? ,little kernel 是小內(nèi)核小操作系統(tǒng)。
?????????? LK 代碼 在 bootable/bootloadler/lk 目錄下
?????????? LK 代碼結(jié)構(gòu)
?????????? +app??????????? // 應(yīng)用相關(guān)
?????????? +arch?????????? // arm 體系?
?????????? +dev??????????? // 設(shè)備相關(guān)
?????????? +include????? // 頭文件
?????????? +kernel??????? // lk系統(tǒng)相關(guān)???
?????????? +platform??? //?相關(guān)驅(qū)動(dòng)
?????????? +projiect???? // makefile文件
?????????? +scripts????? // Jtag 腳本
?????????? +target??????? // 具體板子相關(guān)
LK 流程分析
????????? 在?bootable/bootloadler/lk/arch/arm/ssystem-onesegment.ld 連接文件中 ENTRY(_start)指定 LK 從_start 函數(shù)開始,_start 在 lk/arch/crt0.S中 。crt0.S 主要做一些基本的 CPU 的初始化再通過 bl? kmain ;跳轉(zhuǎn)到 C 代碼中。
????????? kmain 在 lk/kernel/main.c 中
kmain()
??????????? kmain 主要做兩件事:1、本身 lk 這個(gè)系統(tǒng)模塊的初始化;2、boot 的啟動(dòng)初始化動(dòng)作。
??????????? kmain 源碼分析:
???????????? void kmain()
????????? {
???????????1.初始化進(jìn)程(lk 中的簡單進(jìn)程)相關(guān)結(jié)構(gòu)體。
?????? ? ? ? thread_init_early();
???????????2.做一些如 關(guān)閉 cache,使能 mmu 的 arm 相關(guān)工作。
??????????? arch_early_init();
?????????? 3.相關(guān)平臺(tái)的早期初始化
??????????? platform_early_init();
???????????4.現(xiàn)在就一個(gè)函數(shù)跳轉(zhuǎn),初始化UART(板子相關(guān))
??????????? target_early_init();
???????????5.構(gòu)造函數(shù)相關(guān)初始化
??????? ? ? call_constructors();
???????????6.lk系統(tǒng)相關(guān)的堆棧初始化
??????????? heap_init();
?????????? 7.簡短的初始化定時(shí)器對(duì)象
??????????? thread_init();
???????????8.lk系統(tǒng)控制器初始化(相關(guān)事件初始化)
??????????? dpc_init();
???????????9.初始化lk中的定時(shí)器
??????????? timer_init();
???????????10.新建線程入口函數(shù)?bootstrap2 用于boot 工作(重點(diǎn))
?????????? thread_resume(thread_create("bootstrap2", &bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
?????????}
???以上與 boot 啟動(dòng)初始化相關(guān)函數(shù)是?arch_early_init、? platform_early_init 、bootstrap2,這些是啟動(dòng)的重點(diǎn),我們下面慢慢來看。
arch_early_init()
?????????體系架構(gòu)相關(guān)的初始化我們一般用的 ARM 體系
?????????1.關(guān)閉cache
???????? arch_disable_cache(UCACHE);
?????????2.設(shè)置向量基地址(中斷相關(guān))
???????? set_vector_base(MEMBASE);
?????????3.初始化MMU
???????? arm_mmu_init();
?????????4.初始化MMU映射__平臺(tái)相關(guān)
???????? platform_init_mmu_mappings();
? ? ? ???5.開啟cache ????????
???????? arch_enable_cache(UCACHE)
?????????6.使能 cp10 和 cp11
???????? __asm__ volatile("mrc?? ?p15, 0, %0, c1, c0, 2" : "=r" (val));
???????? val |= (3<<22)|(3<<20);
?????? ? __asm__ volatile("mcr?? ?p15, 0, %0, c1, c0, 2" :: "r" (val));
?
?? ? ???7.設(shè)置使能 fpexc 位?(中斷相關(guān))
??? ??? __asm__ volatile("mrc? p10, 7, %0, c8, c0, 0" : "=r" (val));
??????? val |= (1<<30);
??????? __asm__ volatile("mcr? p10, 7, %0, c8, c0, 0" :: "r" (val));
? ? ? ??8.使能循環(huán)計(jì)數(shù)寄存器
??????? __asm__ volatile("mrc?? ?p15, 0, %0, c9, c12, 0" : "=r" (en));
??????? en &= ~(1<<3);?/*循環(huán)計(jì)算每個(gè)周期*/
??????? en |= 1;?
??????? __asm__ volatile("mcr?? ?p15, 0, %0, c9, c12, 0" :: "r" (en));
???????9.使能循環(huán)計(jì)數(shù)器
?????? en = (1<<31);
?????? __asm__ volatile("mcr?? ?p15, 0, %0, c9, c12, 1" :: "r" (en));
platform_early_init()
???????平臺(tái)相關(guān)初始化不同平臺(tái)不同的初始化下面是msm7x30
? ? ? ??1.初始化中斷
?? ?? ??platform_init_interrupts();
????????2.初始化定時(shí)器
????????platform_init_timer();
bootstrap2?
?????????bootstrap2?在kmain的末尾以線程方式開啟。主要分三步:platform_init、target_init、apps_init。
????????1.platform_init
???????????????platform_init 中主要是函數(shù) acpu_clock_init。
?????????????? 在 acpu_clock_init?對(duì) arm11 進(jìn)行系統(tǒng)時(shí)鐘設(shè)置,超頻?
????????2.target_init
??????????? ? 針對(duì)硬件平臺(tái)進(jìn)行設(shè)置。主要對(duì) arm9 和 arm11 的分區(qū)表進(jìn)行整合,初始化flash和讀取FLASH信息
? ? ? ??3.apps_init??
???????????? apps_init 是關(guān)鍵,對(duì) LK 中所謂 app 初始化并運(yùn)行起來,而 aboot_init 就將在這里開始被運(yùn)行,android?Linux?內(nèi)核的加載工作就在 aboot_init 中完成的 。
aboot_init
????????1.設(shè)置NAND/?EMMC讀取信息頁面大小
????????if (target_is_emmc_boot())
??????? {
????????????????? page_size = 2048;
????????????????? page_mask = page_size - 1;
??????? }
?????? else
?????? {
???????????????? page_size = flash_page_size();
???????????????? page_mask = page_size - 1;
??????? }
????? 2.讀取按鍵信息,判斷是正常開機(jī),還是進(jìn)入 fastboot ,還是進(jìn)入recovery 模式
?????? 。。。。。。。。。
??????通過一系列的 if (keys_get_state() == XXX) 判斷
?????? 。。。。。。。。。
????? 3.從 nand 中加載 內(nèi)核
??????boot_linux_from_flash();
?
????? partition_dump();
????? sz = target_get_max_flash_size();
????? fastboot_init(target_get_scratch_address(), sz);
????? udc_start(); // 開始 USB 協(xié)議
?
boot_linux_from_flash
???????????? 主要是內(nèi)核的加載過程,我們的 boot.img 包含:kernel 頭、kernel、ramdisk、second stage(可以沒有)。
???????????1.讀取boot 頭部
?????????? flash_read(p, offset, raw_header, 2048)?
?????????? offset += 2048;
? ? ? ? ???2.讀取 內(nèi)核 ???
?????????? memcmp(hdr->magic, BOOT_MAGIC, BOOT_MAGIC_SIZE)
?????????? n = (hdr->kernel_size + (FLASH_PAGE_SIZE - 1)) & (~(FLASH_PAGE_SIZE - 1));
? ? ? ? ?? flash_read(p, offset, (void*) hdr->kernel_addr, n)
?? ? ? ? ? offset += n;
???????????3.讀取 ramdisk
? ? ? ? ?? n = (hdr->ramdisk_size + (FLASH_PAGE_SIZE - 1)) & (~(FLASH_PAGE_SIZE - 1));
?????????? flash_read(p, offset, (void*) hdr->ramdisk_addr, n)
?????????? offset += n;
????????????4.啟動(dòng)內(nèi)核,
????????????? ??boot_linux();//在boot_linux 中entry(0,machtype,tags);從kernel加載在內(nèi)核中的地址開始運(yùn)行了。
??????
????????到這里L(fēng)K的啟動(dòng)過程就結(jié)束了。
?
Android Kernel - Boot Loader
Android Boot loader 的 code 在 bootable/bootloader/lk 底下, LK 是 Little Kernel 的縮寫, 是 andriod bootloader 的核心精神.
?
入口函數(shù)在 kernel/main.c 中的 kmain(), 以下就來讀讀這一段 code.
?
?
[c-sharp]?view plaincopyprint?
void?kmain(void)??{??????//?get?us?into?some?sort?of?thread?context??? ????thread_init_early();??????//?early?arch?stuff??? ????arch_early_init();??????//?do?any?super?early?platform?initialization??? ????platform_early_init();??????//?do?any?super?early?target?initialization??? ????target_early_init();??????dprintf(INFO,?"welcome?to?lk/n/n");?? ??????????//?deal?with?any?static?constructors??? ????dprintf(SPEW,?"calling?constructors/n");?? ????call_constructors();??????//?bring?up?the?kernel?heap??? ????dprintf(SPEW,?"initializing?heap/n");?? ????heap_init();??????//?initialize?the?threading?system??? ????dprintf(SPEW,?"initializing?threads/n");?? ????thread_init();??????//?initialize?the?dpc?system??? ????dprintf(SPEW,?"initializing?dpc/n");?? ????dpc_init();??????//?initialize?kernel?timers??? ????dprintf(SPEW,?"initializing?timers/n");?? ????timer_init(); ?#if?(!ENABLE_NANDWRITE)???????//?create?a?thread?to?complete?system?initialization??? ????dprintf(SPEW,?"creating?bootstrap?completion?thread/n");?? ????thread_resume(thread_create("bootstrap2",?&bootstrap2,?NULL,?DEFAULT_PRIORITY,?DEFAULT_STACK_SIZE));?? ????//?enable?interrupts??? ????exit_critical_section();??????//?become?the?idle?thread??? ????thread_become_idle(); ?#else???????????bootstrap_nandwrite(); ?#endif???}?? ?
?
?
In include/debug.h: 我們可以看到 dprintf 的第一個(gè)參數(shù)是代表 debug level.
?
[c-sharp:nogutter]?view plaincopyprint?
/*?debug?levels?*/?#define?CRITICAL?0??#define?ALWAYS?0??#define?INFO?1??#define?SPEW?2?? ?
?
?
In include/debug.h:?
?
?
[c-sharp:nogutter]?view plaincopyprint?
#define?dprintf(level,?x...)?do?{?if?((level)?<=?DEBUGLEVEL)?{?_dprintf(x);?}?}?while?(0)?? ?
?
?
所以 dprintf 會(huì)依 DEBUGLEVEL 來判斷是否輸出信息.
?
來看第一個(gè) call 的函數(shù): thread_init_early, define in thread.c
?
[c-sharp:nogutter]?view plaincopyprint?
void?thread_init_early(void)??{??????????int?i;??????????/*?initialize?the?run?queues?*/?? ????????for?(i=0;?i?<?NUM_PRIORITIES;?i++)??????????????????list_initialize(&run_queue[i]);??????????/*?initialize?the?thread?list?*/?? ????????list_initialize(&thread_list);??????????/*?create?a?thread?to?cover?the?current?running?state?*/?? ????????thread_t?*t?=?&bootstrap_thread;??????????init_thread_struct(t,?"bootstrap");?? ????????/*?half?construct?this?thread,?since?we're?already?running?*/?? ????????t->priority?=?HIGHEST_PRIORITY;??????????t->state?=?THREAD_RUNNING;??????????t->saved_critical_section_count?=?1;??????????list_add_head(&thread_list,?&t->thread_list_node);??????????current_thread?=?t;??}?? ?
?
?
#define NUM_PRIORITIES 32 in include/kernel/thread.h
?
list_initialize() defined in include/list.h: initialized a list
?
?
[c-sharp:nogutter]?view plaincopyprint?
static?inline?void?list_initialize(struct?list_node?*list)??{??????????list->prev?=?list->next?=?list;??}?? ?
?
?
run_queue 是?static struct list_node run_queue[NUM_PRIORITIES]
?
thread_list 是?static struct list_node thread_list
?
再來要 call ?的函數(shù)是: arch_early_init() defined in?arch/arm/arch.c
?
[c-sharp:nogutter]?view plaincopyprint?
void?arch_early_init(void)??{??????/*?turn?off?the?cache?*/?? ????arch_disable_cache(UCACHE);??????/*?set?the?vector?base?to?our?exception?vectors?so?we?dont?need?to?double?map?at?0?*/? #if?ARM_CPU_CORTEX_A8???????set_vector_base(MEMBASE); ?#endif??#if?ARM_WITH_MMU???????arm_mmu_init();??????platform_init_mmu_mappings(); ?#endif???????/*?turn?the?cache?back?on?*/?? ????arch_enable_cache(UCACHE); ?#if?ARM_WITH_NEON???????/*?enable?cp10?and?cp11?*/?? ????uint32_t?val;??????__asm__?volatile("mrc???p15,?0,?%0,?c1,?c0,?2"?:?"=r"?(val));?? ????val?|=?(3<<22)|(3<<20);??????__asm__?volatile("mcr???p15,?0,?%0,?c1,?c0,?2"?::?"r"?(val));?? ????/*?set?enable?bit?in?fpexc?*/?? ????val?=?(1<<30);??????__asm__?volatile("mcr??p10,?7,?%0,?c8,?c0,?0"?::?"r"?(val)); ? #endif???}?? ?
?
?
現(xiàn)代操作系統(tǒng)普遍采用虛擬內(nèi)存管理(Virtual Memory Management)機(jī)制,這需要處理器中的MMU(Memory Management Unit,
?
內(nèi)存管理單元)提供支持。
?
CPU執(zhí)行單元發(fā)出的內(nèi)存地址將被MMU截獲,從CPU到MMU的地址稱為虛擬地址(Virtual Address,以下簡稱VA),而MMU將這個(gè)地
?
址翻譯成另一個(gè)地址發(fā)到CPU芯片的外部地址引腳上,也就是將VA映射成PA
?
MMU將VA映射到PA是以頁(Page)為單位的,32位處理器的頁尺寸通常是4KB。例如,MMU可以通過一個(gè)映射項(xiàng)將VA的一頁
?
0xb7001000~0xb7001fff映射到PA的一頁0x2000~0x2fff,如果CPU執(zhí)行單元要訪問虛擬地址0xb7001008,則實(shí)際訪問到的物理地
?
址是0x2008。物理內(nèi)存中的頁稱為物理頁面或者頁幀(Page Frame)。虛擬內(nèi)存的哪個(gè)頁面映射到物理內(nèi)存的哪個(gè)頁幀是通過頁
?
表(Page Table)來描述的,頁表保存在物理內(nèi)存中,MMU會(huì)查找頁表來確定一個(gè)VA應(yīng)該映射到什么PA。
?
?
?
操作系統(tǒng)和MMU是這樣配合的:
?
1. 操作系統(tǒng)在初始化或分配、釋放內(nèi)存時(shí)會(huì)執(zhí)行一些指令在物理內(nèi)存中填寫頁表,然后用指令設(shè)置MMU,告訴MMU頁表在物理內(nèi)存中
?
?? 的什么位置。
?
2. 設(shè)置好之后,CPU每次執(zhí)行訪問內(nèi)存的指令都會(huì)自動(dòng)引發(fā)MMU做查表和地址轉(zhuǎn)換操作,地址轉(zhuǎn)換操作由硬件自動(dòng)完成,不需要用指令
?
?? ?控制MMU去做。
?
?
MMU除了做地址轉(zhuǎn)換之外,還提供內(nèi)存保護(hù)機(jī)制。各種體系結(jié)構(gòu)都有用戶模式(User Mode)和特權(quán)模式(Privileged Mode)之分,
?
操作系統(tǒng)可以在頁表中設(shè)置每個(gè)內(nèi)存頁面的訪問權(quán)限,有些頁面不允許訪問,有些頁面只有在CPU處于特權(quán)模式時(shí)才允許訪問,有些頁面
?
在用戶模式和特權(quán)模式都可以訪問,訪問權(quán)限又分為可讀、可寫和可執(zhí)行三種。這樣設(shè)定好之后,當(dāng)CPU要訪問一個(gè)VA時(shí),MMU會(huì)檢查
?
CPU當(dāng)前處于用戶模式還是特權(quán)模式,訪問內(nèi)存的目的是讀數(shù)據(jù)、寫數(shù)據(jù)還是取指令,如果和操作系統(tǒng)設(shè)定的頁面權(quán)限相符,就允許訪
?
問,把它轉(zhuǎn)換成PA,否則不允許訪問,產(chǎn)生一個(gè)異常(Exception)
?
常見的 segmentation fault 產(chǎn)生的原因:
?
用戶程序要訪問一段 VA, 經(jīng) MMU 檢查后無權(quán)訪問, MMU 會(huì)產(chǎn)生異常, CPU 從用戶模式切換到特權(quán)模式, 跳轉(zhuǎn)到內(nèi)核代碼中執(zhí)行異常服務(wù)程序.
?
內(nèi)核就會(huì)把這個(gè)異常解釋為 segmentation fault, 將引發(fā)異常的程序終止.
?
簡單的講一下 NEON:?NEON technology can accelerate multimedia and signal processing algorithms such as video encode/decode,
?
2D/3D graphics, gaming, audio and speech processing,?image processing, telephony, and sound synthesis.
?
platform_early_init() defined in platform/<your-platform>/platform.c
?
?
[c-sharp:nogutter]?view plaincopyprint?
void?platform_early_init(void)??{??????uart_init();??????platform_init_interrupts();??????platform_init_timer();??}?? ?
?
?
uart_init.c defined in platform/<your-platform>/uart.c 所有用到的變數(shù),也都定義在 uart.c
?
[c-sharp:nogutter]?view plaincopyprint?
void?uart_init(void)??{??????uwr(0x0A,?UART_CR);??/*?disable?TX?and?RX?*/?? ??????????uwr(0x30,?UART_CR);??/*?reset?error?status?*/?? ????uwr(0x10,?UART_CR);??/*?reset?receiver?*/?? ????uwr(0x20,?UART_CR);??/*?reset?transmitter?*/? ?????#if?PLATFORM_QSD8K???????/*?TCXO?*/?? ????uwr(0x06,?UART_MREG);??????uwr(0xF1,?UART_NREG);??????uwr(0x0F,?UART_DREG);??????uwr(0x1A,?UART_MNDREG); ?#else???????/*?TCXO/4?*/?? ????uwr(0xC0,?UART_MREG);??????uwr(0xAF,?UART_NREG);??????uwr(0x80,?UART_DREG);??????uwr(0x19,?UART_MNDREG);???? ?#endif?????????????uwr(0x10,?UART_CR);??/*?reset?RX?*/?? ????uwr(0x20,?UART_CR);??/*?reset?TX?*/?? ????uwr(0x30,?UART_CR);??/*?reset?error?status?*/?? ????uwr(0x40,?UART_CR);??/*?reset?RX?break?*/?? ????uwr(0x70,?UART_CR);??/*?rest??*/?? ????uwr(0xD0,?UART_CR);??/*?reset?*/?? ??????????uwr(0x7BF,?UART_IPR);?/*?stale?timeout?=?630?*?bitrate?*/?? ????uwr(0,?UART_IMR);??????uwr(115,?UART_RFWR);?/*?RX?watermark?=?58?*?2?-?1?*/?? ????uwr(10,?UART_TFWR);??/*?TX?watermark?*/?? ??????????uwr(0,?UART_RFWR);?????????????uwr(UART_CSR_115200,?UART_CSR);??????uwr(0,?UART_IRDA);??????uwr(0x1E,?UART_HCR);??//??uwr(0x7F4,?UART_MR1);?/*?RFS/?CTS/?500chr?RFR?*/???????uwr(16,?UART_MR1);??????uwr(0x34,?UART_MR2);?/*?8N1?*/?? ??????????uwr(0x05,?UART_CR);?/*?enable?TX?&?RX?*/?? ????uart_ready?=?1;??}?? ?
?
?
platform_init_interrupts: defined in platform/msm8x60/interrupts.c
?
?
[c-sharp:nogutter]?view plaincopyprint?
void?platform_init_interrupts(void)??{??????platform_gic_dist_init();??????platform_gic_cpu_init();??}?? ?
?
?
GIC 指的是 Generic Interrupt Controller. The gic-cpu and gic-dist are two subcomponents of GIC.
?
Devices are wired to the?Git-dist which is in charge of distributing interrupts to the gic-cpu (per cpu IRQ IF).
?
platform_init_timer(): defined in platform/<your-platform>/timer.c
?
[c-sharp:nogutter]?view plaincopyprint?
void?platform_init_timer(void)??{??????writel(0,?DGT_ENABLE);??}?? ?
?
?
DGT: Digital Game Timer: presents the countdowns of two players, it is also called chess timer or chess clock.
?
target_early_init(): defined in target/init.c
?
?
[c-sharp:nogutter]?view plaincopyprint?
/*??*?default?implementations?of?these?routines,?if?the?target?code??*?chooses?not?to?implement.??*/??__WEAK?void?target_early_init(void)??{??}??__WEAK?void?target_init(void)??{??}?? ?
?
?
call_constructors() is defined in kernel/main.c:
?
[c-sharp:nogutter]?view plaincopyprint?
static?void?call_constructors(void)??{??????void?**ctor;???????????ctor?=?&__ctor_list;??????while(ctor?!=?&__ctor_end)?{??????????void?(*func)(void);??????????func?=?(void?(*)())*ctor;??????????func();??????????ctor++;??????}??}?? ?
?
?
?
heap_init is defined in lib/heap/heap.c:
?
?
[c-sharp:nogutter]?view plaincopyprint?
void?heap_init(void)??{??????LTRACE_ENTRY;??????//?set?the?heap?range??? ????theheap.base?=?(void?*)HEAP_START;??????theheap.len?=?HEAP_LEN;??????LTRACEF("base?%p?size?%zd?bytes/n",?theheap.base,?theheap.len);?? ????//?initialize?the?free?list??? ????list_initialize(&theheap.free_list);??????//?create?an?initial?free?chunk??? ????heap_insert_free_chunk(heap_create_free_chunk(theheap.base,?theheap.len));??????//?dump?heap?info??? //??heap_dump();???//??dprintf(INFO,?"running?heap?tests/n");???//??heap_test();???}?? ?
?
?
?
thread_init is defined in kernel/thread.c but nothing coded, 先記下.
?
[c-sharp:nogutter]?view plaincopyprint?
void?thread_init(void)??{??}?? ?
?
?
dpc_init() is defined in kernel/dpc.c:
?
?
?
[c-sharp:nogutter]?view plaincopyprint?
void?dpc_init(void)??{??????event_init(&dpc_event,?false,?0);??????thread_resume(thread_create("dpc",?&dpc_thread_routine,?NULL,?DPC_PRIORITY,?DEFAULT_STACK_SIZE));?? }?? ?
?
?
dpc 為 Delayed Procedure Call 延遲過程調(diào)用的縮寫.
?
timer_init() is defined in kernel/timer.c:
?
[c-sharp:nogutter]?view plaincopyprint?
void?timer_init(void)??{??????list_initialize(&timer_queue);??????/*?register?for?a?periodic?timer?tick?*/?? ????platform_set_periodic_timer(timer_tick,?NULL,?10);?/*?10ms?*/?? }?? ?
?
?
?
?
執(zhí)行 thread_resume 或是 bootstrap_nandwrite
?
?
?
[c-sharp:nogutter]?view plaincopyprint?
#if?(!ENABLE_NANDWRITE)???????//?create?a?thread?to?complete?system?initialization??? ????dprintf(SPEW,?"creating?bootstrap?completion?thread/n");?? ????thread_resume(thread_create("bootstrap2",?&bootstrap2,?NULL,?DEFAULT_PRIORITY,?DEFAULT_STACK_SIZE));?? ????//?enable?interrupts??? ????exit_critical_section();??????//?become?the?idle?thread??? ????thread_become_idle(); ?#else???????????bootstrap_nandwrite(); ?#endif?? ?
?
?
In kermel/main.c:
?
[c-sharp:nogutter]?view plaincopyprint?
static?int?bootstrap2(void?*arg)??{??????dprintf(SPEW,?"top?of?bootstrap2()/n");?? ????arch_init();??????//?initialize?the?rest?of?the?platform??? ????dprintf(SPEW,?"initializing?platform/n");?? ????platform_init();????????????//?initialize?the?target??? ????dprintf(SPEW,?"initializing?target/n");?? ????target_init();??????dprintf(SPEW,?"calling?apps_init()/n");?? ????apps_init();??????return?0;??} ?#if?(ENABLE_NANDWRITE)??? void?bootstrap_nandwrite(void)??{??????dprintf(SPEW,?"top?of?bootstrap2()/n");?? ????arch_init();??????//?initialize?the?rest?of?the?platform??? ????dprintf(SPEW,?"initializing?platform/n");?? ????platform_init();??????//?initialize?the?target??? ????dprintf(SPEW,?"initializing?target/n");?? ????target_init();??????dprintf(SPEW,?"calling?nandwrite_init()/n");?? ????nandwrite_init();??????return?0;??} ?#endif?? ?
?
?
continue to see apps_init():?app/app.c:void apps_init(void)
?
?
[c-sharp]?view plaincopyprint?
#include?<app.h>??#include?<kernel/thread.h>??? extern?const?struct?app_descriptor?__apps_start;?? extern?const?struct?app_descriptor?__apps_end;?? static?void?start_app(const?struct?app_descriptor?*app);??/*?one?time?setup?*/?? void?apps_init(void)??{??????const?struct?app_descriptor?*app;??????/*?call?all?the?init?routines?*/?? ????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);??????????}??????}??}?? static?int?app_thread_entry(void?*arg)??{??????const?struct?app_descriptor?*app?=?(const?struct?app_descriptor?*)arg;??????app->entry(app,?NULL);??????return?0;??}?? static?void?start_app(const?struct?app_descriptor?*app)??{??????printf("starting?app?%s/n",?app->name);?? ????thread_resume(thread_create(app->name,?&app_thread_entry,?(void?*)app,?DEFAULT_PRIORITY,?DEFAULT_STACK_SIZE));?????}?? ?
?
?
至于會(huì)有那些 app 被放入 boot thread section, 則定義在 include/app.h 中的 APP_START(appname)
?
[c-sharp:nogutter]?view plaincopyprint?
#define?APP_START(appname)?struct?app_descriptor?_app_##appname?__SECTION(".apps")?=?{?.name?=?#appname,?#define?APP_END?};?? ?
?
?
在 app 中只要像 app/aboot/aboot.c 指定就會(huì)在 bootloader bootup 時(shí)放入 thread section 中被執(zhí)行.
?
?
[c-sharp:nogutter]?view plaincopyprint?
APP_START(aboot)??????????.init?=?aboot_init,??APP_END?? ?
?
?
在我的 bootloader 中有 app/aboot/aboot.c, app/tests/tests.c, app/shell/shell.c, 及 app/stringtests/string_tests.c 皆有此聲明.
?
接下來關(guān)注: aboot.c 中的 aboot_init()
?
[c-sharp:nogutter]?view plaincopyprint?
void?aboot_init(const?struct?app_descriptor?*app)??{??????unsigned?reboot_mode?=?0;??????unsigned?disp_init?=?0;??????unsigned?usb_init?=?0;??????//test_ram();??? ????/*?Setup?page?size?information?for?nand/emmc?reads?*/?? ????if?(target_is_emmc_boot())??????{??????????page_size?=?2048;??????????page_mask?=?page_size?-?1;??????}??????else??????{??????????page_size?=?flash_page_size();??????????page_mask?=?page_size?-?1;??????}??????/*?Display?splash?screen?if?enabled?*/? ????#if?DISPLAY_SPLASH_SCREEN???????display_init();??????dprintf(INFO,?"Diplay?initialized/n");?? ????disp_init?=?1;??????diplay_image_on_screen(); ?????#endif???????/*?Check?if?we?should?do?something?other?than?booting?up?*/?? ????if?(keys_get_state(KEY_HOME)?!=?0)??????????boot_into_recovery?=?1;??????if?(keys_get_state(KEY_BACK)?!=?0)??????????goto?fastboot;??????if?(keys_get_state(KEY_CLEAR)?!=?0)??????????goto?fastboot; ?????#if?NO_KEYPAD_DRIVER???????/*?With?no?keypad?implementation,?check?the?status?of?USB?connection.?*/?? ????/*?If?USB?is?connected?then?go?into?fastboot?mode.?*/?? ????usb_init?=?1;??????udc_init(&surf_udc_device);??????if?(usb_cable_status())??????????goto?fastboot; ?????#endif???????init_vol_key();??????if(voldown_press())??????????goto?fastboot;??????reboot_mode?=?check_reboot_mode();??????if?(reboot_mode?==?RECOVERY_MODE)?{??????????boot_into_recovery?=?1;??????}?else?if(reboot_mode?==?FASTBOOT_MODE)?{??????????goto?fastboot;??????}??????if?(target_is_emmc_boot())??????{??????????boot_linux_from_mmc();??????}??????else??????{??????????recovery_init();??????????boot_linux_from_flash();??????}??????dprintf(CRITICAL,?"ERROR:?Could?not?do?normal?boot.?Reverting?"?? ????????"to?fastboot?mode./n");?? fastboot:??????if(!usb_init)??????????udc_init(&surf_udc_device);??????fastboot_register("boot",?cmd_boot);?? ????if?(target_is_emmc_boot())??????{??????????fastboot_register("flash:",?cmd_flash_mmc);?? ????????fastboot_register("erase:",?cmd_erase_mmc);?? ????}??????else??????{??????????fastboot_register("flash:",?cmd_flash);?? ????????fastboot_register("erase:",?cmd_erase);?? ????}??????fastboot_register("continue",?cmd_continue);?? ????fastboot_register("reboot",?cmd_reboot);?? ????fastboot_register("reboot-bootloader",?cmd_reboot_bootloader);?? ????fastboot_publish("product",?TARGET(BOARD));?? ????fastboot_publish("kernel",?"lk");?? ??????????fastboot_init(target_get_scratch_address(),?120?*?1024?*?1024);????????????udc_start();??????target_battery_charging_enable(1,?0);??}?? ?
?
?
?
target_is_emmc_boot() is defined in target/init.c: _EMMC_BOOT 是 compiler 時(shí)的 flags
?
?
[c-sharp:nogutter]?view plaincopyprint?
__WEAK?int?target_is_emmc_boot(void)??{ ?#if?_EMMC_BOOT???????return?1; ?#else???????return?0; ?#endif???}?? ?
?
?
check_reboot_mode is defined in target/<your-platform>/init.c:
?
[c-sharp:nogutter]?view plaincopyprint?
unsigned?check_reboot_mode(void)??{??????????unsigned?restart_reason?=?0;??????????void?*restart_reason_addr?=?0x401FFFFC;??????????/*?Read?reboot?reason?and?scrub?it?*/?? ????????restart_reason?=?readl(restart_reason_addr);??????????writel(0x00,?restart_reason_addr);??????????return?restart_reason;??}?? ?
?
?
reboot mode in bootloader:
?
?
[c-sharp:nogutter]?view plaincopyprint?
#define?RECOVERY_MODE???0x77665502??#define?FASTBOOT_MODE???0x77665500?? ?
?
?
?
再來就會(huì)執(zhí)行?boot_linux_from_mmc():
?
[c-sharp:nogutter]?view plaincopyprint?
int?boot_linux_from_mmc(void)??{??????struct?boot_img_hdr?*hdr?=?(void*)?buf;??????struct?boot_img_hdr?*uhdr;??????unsigned?offset?=?0;??????unsigned?long?long?ptn?=?0;??????unsigned?n?=?0;??????const?char?*cmdline;??????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)??????{??????????ptn?=?mmc_ptn_offset("boot");?? ????????if(ptn?==?0)?{??????????????dprintf(CRITICAL,?"ERROR:?No?boot?partition?found/n");?? ????????????????????return?-1;??????????}??????}??????else??????{??????????ptn?=?mmc_ptn_offset("recovery");?? ????????if(ptn?==?0)?{??????????????dprintf(CRITICAL,?"ERROR:?No?recovery?partition?found/n");?? ????????????????????return?-1;??????????}??????}??????if?(mmc_read(ptn?+?offset,?(unsigned?int?*)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:?Invaled?boot?image?header/n");?? ????????????????return?-1;??????}??????if?(hdr->page_size?&&?(hdr->page_size?!=?page_size))?{??????????page_size?=?hdr->page_size;??????????page_mask?=?page_size?-?1;??????}??????offset?+=?page_size;??????n?=?ROUND_TO_PAGE(hdr->kernel_size,?page_mask);??????if?(mmc_read(ptn?+?offset,?(void?*)hdr->kernel_addr,?n))?{??????????dprintf(CRITICAL,?"ERROR:?Cannot?read?kernel?image/n");?? ????????????????return?-1;??????}??????offset?+=?n;??????n?=?ROUND_TO_PAGE(hdr->ramdisk_size,?page_mask);??????if?(mmc_read(ptn?+?offset,?(void?*)hdr->ramdisk_addr,?n))?{??????????dprintf(CRITICAL,?"ERROR:?Cannot?read?ramdisk?image/n");?? ????????????????return?-1;??????}??????offset?+=?n;??unified_boot:??????dprintf(INFO,?"/nkernel??@?%x?(%d?bytes)/n",?hdr->kernel_addr,?? ????????hdr->kernel_size);??????dprintf(INFO,?"ramdisk?@?%x?(%d?bytes)/n",?hdr->ramdisk_addr,?? ????????hdr->ramdisk_size);??????if(hdr->cmdline[0])?{??????????cmdline?=?(char*)?hdr->cmdline;??????}?else?{??????????cmdline?=?DEFAULT_CMDLINE;??????}??????dprintf(INFO,?"cmdline?=?'%s'/n",?cmdline);?? ????dprintf(INFO,?"/nBooting?Linux/n");?? ????boot_linux((void?*)hdr->kernel_addr,?(void?*)TAGS_ADDR,?????????????(const?char?*)cmdline,?board_machtype(),?????????????(void?*)hdr->ramdisk_addr,?hdr->ramdisk_size);??????return?0;??}?? ?
?
?
mmc_read() is defined in platform/<your-platform>/mmc.c:
?
?
[c-sharp:nogutter]?view plaincopyprint?
unsigned?int?mmc_read?(unsigned?long?long?data_addr,?unsigned?int*?out,?unsigned?int?data_len)??{??????int?val?=?0;??????val?=?mmc_boot_read_from_card(?&mmc_host,?&mmc_card,?data_addr,?data_len,?out);??????return?val;??}?? ?
?
?
boot_linux(): 啟動(dòng) Linux, 看看函數(shù)定義
?
[c-sharp:nogutter]?view plaincopyprint?
void?boot_linux(void?*kernel,?unsigned?*tags,???????????const?char?*cmdline,?unsigned?machtype,??????????void?*ramdisk,?unsigned?ramdisk_size)??{??????unsigned?*ptr?=?tags;??????unsigned?pcount?=?0;??????void?(*entry)(unsigned,unsigned,unsigned*)?=?kernel;??????struct?ptable?*ptable;??????int?cmdline_len?=?0;??????int?have_cmdline?=?0;??????int?pause_at_bootup?=?0;??????/*?CORE?*/?? ????*ptr++?=?2;??????*ptr++?=?0x54410001;??????if?(ramdisk_size)?{??????????*ptr++?=?4;??????????*ptr++?=?0x54420005;??????????*ptr++?=?(unsigned)ramdisk;??????????*ptr++?=?ramdisk_size;??????}??????ptr?=?target_atag_mem(ptr);??????if?(!target_is_emmc_boot())?{??????????/*?Skip?NAND?partition?ATAGS?for?eMMC?boot?*/?? ????????if?((ptable?=?flash_get_ptable())?&&?(ptable->count?!=?0))?{??????????????int?i;??????????????for(i=0;?i?<?ptable->count;?i++)?{??????????????????struct?ptentry?*ptn;??????????????????ptn?=??ptable_get(ptable,?i);??????????????????if?(ptn->type?==?TYPE_APPS_PARTITION)??????????????????????pcount++;??????????????}??????????????*ptr++?=?2?+?(pcount?*?(sizeof(struct?atag_ptbl_entry)?/?????????????????????????????????sizeof(unsigned)));??????????????*ptr++?=?0x4d534d70;??????????????for?(i?=?0;?i?<?ptable->count;?++i)??????????????????ptentry_to_tag(&ptr,?ptable_get(ptable,?i));??????????}??????}??????if?(cmdline?&&?cmdline[0])?{??????????cmdline_len?=?strlen(cmdline);??????????have_cmdline?=?1;??????}??????if?(target_is_emmc_boot())?{??????????cmdline_len?+=?strlen(emmc_cmdline);??????}??????if?(target_pause_for_battery_charge())?{??????????pause_at_bootup?=?1;??????????cmdline_len?+=?strlen(battchg_pause);??????}??????if?(cmdline_len?>?0)?{??????????const?char?*src;??????????char?*dst;??????????unsigned?n;??????????/*?include?terminating?0?and?round?up?to?a?word?multiple?*/?? ????????n?=?(cmdline_len?+?4)?&?(~3);??????????*ptr++?=?(n?/?4)?+?2;??????????*ptr++?=?0x54410009;??????????dst?=?(char?*)ptr;??????????if?(have_cmdline)?{??????????????src?=?cmdline;??????????????while?((*dst++?=?*src++));??????????}??????????if?(target_is_emmc_boot())?{??????????????src?=?emmc_cmdline;??????????????if?(have_cmdline)?--dst;??????????????have_cmdline?=?1;??????????????while?((*dst++?=?*src++));??????????}??????????if?(pause_at_bootup)?{??????????????src?=?battchg_pause;??????????????if?(have_cmdline)?--dst;??????????????while?((*dst++?=?*src++));??????????}??????????ptr?+=?(n?/?4);??????}??????/*?END?*/?? ????*ptr++?=?0;??????*ptr++?=?0;??????dprintf(INFO,?"booting?linux?@?%p,?ramdisk?@?%p?(%d)/n",?? ????????kernel,?ramdisk,?ramdisk_size);??????if?(cmdline)??????????dprintf(INFO,?"cmdline:?%s/n",?cmdline);?? ????enter_critical_section();??????platform_uninit_timer();??????arch_disable_cache(UCACHE);??????arch_disable_mmu(); ?#if?DISPLAY_SPLASH_SCREEN???????display_shutdown(); ?#endif???????entry(0,?machtype,?tags);??}?? ?
?
?
配合 boot.img 來看會(huì)比較好理解.
?
?
?
由此可知?boot_img_hdr?中各成員值為:
?
?
?
TAGS_ADDR 如上 target/<your-platform>/rules.mk 所定義的 : 0x40200100,?所以 boot_linux(), 就是傳入TAGS_ADDR,
?
然后將資料寫入 tag, tag 的結(jié)構(gòu)如下所示.
?
?
然后進(jìn)入到 kernel 的入口函數(shù): entry(0, machtype, tags)
本文轉(zhuǎn)自張昺華-sky博客園博客,原文鏈接:http://www.cnblogs.com/sky-heaven/p/6077828.html,如需轉(zhuǎn)載請(qǐng)自行聯(lián)系原作者
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)
總結(jié)
以上是生活随笔為你收集整理的Android Bootloader LittleKernel的两篇文章 【转】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。