
分類:
Android Bootloader(68)
LK是什麼
LK 代碼 在 bootable/bootloadler/lk 目錄下
LK 代碼結構
+app // 應用相關
+arch // arm 體系
+dev // 裝置相關
+include // 頭檔案
+kernel // lk系統相關
+platform // 相關驅動
+projiect // makefile檔案
+scripts // Jtag 腳本
+target // 具體闆子相關
LK 流程分析
在 bootable/bootloadler/lk/arch/arm/ssystem-onesegment.ld 連接配接檔案中 ENTRY(_start)指定 LK 從_start 函數開始,_start
在 lk/arch/crt0.S中 。crt0.S 主要做一些基本的 CPU 的初始化再通過 bl kmain ;跳轉到 C 代碼中。
kmain 在 lk/kernel/main.c 中
kmain()
kmain 主要做兩件事:1、本身 lk 這個系統子產品的初始化;2、boot 的啟動初始化動作。
kmain 源碼分析:
void kmain()
{
1.初始化程序(lk
中的簡單程序)相關結構體。
thread_init_early();
2.做一些如 關閉 cache,使能 mmu 的 arm 相關工作。
arch_early_init();
3.相關平台的早期初始化
platform_early_init();
4.現在就一個函數跳轉,初始化UART(闆子相關)
target_early_init();
5.構造函數相關初始化
call_constructors();
6.lk系統相關的堆棧初始化
heap_init();
7.簡短的初始化定時器對象
thread_init();
8.lk系統控制器初始化(相關事件初始化)
dpc_init();
9.初始化lk中的定時器
timer_init();
10.建立線程入口函數 bootstrap2 用于boot 工作(重點)
thread_resume(thread_create("bootstrap2", &bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
}
以上與 boot 啟動初始化相關函數是 arch_early_init、 platform_early_init 、bootstrap2,這些是啟動的重點,我們下面慢慢來看。
arch_early_init()
1.關閉cache
arch_disable_cache(UCACHE);
2.設定向量基位址(中斷相關)
set_vector_base(MEMBASE);
3.初始化MMU
arm_mmu_init();
4.初始化MMU映射__平台相關
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.設定使能 fpexc 位 (中斷相關)
__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.使能循環計數寄存器
__asm__ volatile("mrc p15, 0, %0, c9, c12, 0" : "=r" (en));
en &= ~(1<<3); /*循環計算每個周期*/
en |= 1;
__asm__ volatile("mcr p15, 0, %0, c9, c12, 0" :: "r" (en));
9.使能循環計數器
en = (1<<31);
__asm__ volatile("mcr p15, 0, %0, c9, c12, 1" :: "r" (en));
platform_early_init()
平台相關初始化不同平台不同的初始化下面是msm7x30
1.初始化中斷
platform_init_interrupts();
2.初始化定時器
platform_init_timer();
bootstrap2
bootstrap2 在kmain的末尾以線程方式開啟。主要分三步:platform_init、target_init、apps_init。
1.platform_init
platform_init 中主要是函數 acpu_clock_init。
在 acpu_clock_init 對
arm11 進行系統時鐘設定,超頻
2.target_init
針對硬體平台進行設定。主要對 arm9 和 arm11 的分區表進行整合,初始化flash和讀取FLASH資訊
3.apps_init
aboot_init
1.設定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.讀取按鍵資訊,判斷是正常開機,還是進入 fastboot ,還是進入recovery 模式
。。。。。。。。。
通過一系列的 if (keys_get_state() == XXX) 判斷
3.從 nand 中加載 核心
boot_linux_from_flash();
partition_dump();
sz = target_get_max_flash_size();
fastboot_init(target_get_scratch_address(), sz);
udc_start(); // 開始 USB 協定
boot_linux_from_flash
主要是核心的加載過程,我們的 boot.img 包含:kernel 頭、kernel、ramdisk、second stage(可以沒有)。
1.讀取boot 頭部
flash_read(p, offset, raw_header, 2048)
offset += 2048;
2.讀取 核心
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.啟動核心,
boot_linux();//在boot_linux
中entry(0,machtype,tags);從kernel加載在核心中的位址開始運作了。
到這裡LK的啟動過程就結束了。
Android Boot loader 的 code 在 bootable/bootloader/lk 底下, LK 是 Little Kernel 的縮寫, 是 andriod bootloader 的核心精神.
入口函數在 kernel/main.c 中的 kmain(), 以下就來讀讀這一段 code.
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 的第一個參數是代表 debug level.
/* debug levels */
#define CRITICAL 0
#define ALWAYS 0
#define INFO 1
#define SPEW 2
In include/debug.h:
#define dprintf(level, x...) do { if ((level) <= DEBUGLEVEL) { _dprintf(x); } } while (0)
是以 dprintf 會依 DEBUGLEVEL 來判斷是否輸出資訊.
來看第一個 call 的函數: thread_init_early, define in thread.c
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
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 的函數是: arch_early_init() defined in arch/arm/arch.c
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();
/* 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));
現代作業系統普遍采用虛拟記憶體管理(Virtual Memory Management)機制,這需要處理器中的MMU(Memory Management Unit,
記憶體管理單元)提供支援。
CPU執行單元發出的記憶體位址将被MMU截獲,從CPU到MMU的位址稱為虛拟位址(Virtual Address,以下簡稱VA),而MMU将這個地
址翻譯成另一個位址發到CPU晶片的外部位址引腳上,也就是将VA映射成PA
MMU将VA映射到PA是以頁(Page)為機關的,32位處理器的頁尺寸通常是4KB。例如,MMU可以通過一個映射項将VA的一頁
0xb7001000~0xb7001fff映射到PA的一頁0x2000~0x2fff,如果CPU執行單元要通路虛拟位址0xb7001008,則實際通路到的實體地
址是0x2008。實體記憶體中的頁稱為實體頁面或者頁幀(Page Frame)。虛拟記憶體的哪個頁面映射到實體記憶體的哪個頁幀是通過頁
表(Page Table)來描述的,頁表儲存在實體記憶體中,MMU會查找頁表來确定一個VA應該映射到什麼PA。
作業系統和MMU是這樣配合的:
1. 作業系統在初始化或配置設定、釋放記憶體時會執行一些指令在實體記憶體中填寫頁表,然後用指令設定MMU,告訴MMU頁表在實體記憶體中
的什麼位置。
2. 設定好之後,CPU每次執行通路記憶體的指令都會自動引發MMU做查表和位址轉換操作,位址轉換操作由硬體自動完成,不需要用指令
控制MMU去做。
MMU除了做位址轉換之外,還提供記憶體保護機制。各種體系結構都有使用者模式(User Mode)和特權模式(Privileged Mode)之分,
作業系統可以在頁表中設定每個記憶體頁面的通路權限,有些頁面不允許通路,有些頁面隻有在CPU處于特權模式時才允許通路,有些頁面
在使用者模式和特權模式都可以通路,通路權限又分為可讀、可寫和可執行三種。這樣設定好之後,當CPU要通路一個VA時,MMU會檢查
CPU目前處于使用者模式還是特權模式,通路記憶體的目的是讀資料、寫資料還是取指令,如果和作業系統設定的頁面權限相符,就允許訪
問,把它轉換成PA,否則不允許通路,産生一個異常(Exception)
常見的 segmentation fault 産生的原因:
使用者程式要通路一段 VA, 經 MMU 檢查後無權通路, MMU 會産生異常, CPU 從使用者模式切換到特權模式, 跳轉到核心代碼中執行異常服務程式.
核心就會把這個異常解釋為 segmentation fault, 将引發異常的程式終止.
簡單的講一下 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
void platform_early_init(void)
uart_init();
platform_init_interrupts();
platform_init_timer();
uart_init.c defined in platform/<your-platform>/uart.c 所有用到的變數,也都定義在 uart.c
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);
/* TCXO/4 */
uwr(0xC0, UART_MREG);
uwr(0xAF, UART_NREG);
uwr(0x80, UART_DREG);
uwr(0x19, UART_MNDREG);
uwr(0x10, UART_CR); /* reset RX */
uwr(0x20, UART_CR); /* reset TX */
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
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.
platform_init_timer(): defined in platform/<your-platform>/timer.c
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
/*
* 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:
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:
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, 先記下.
void thread_init(void)
dpc_init() is defined in kernel/dpc.c:
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 延遲過程調用的縮寫.
timer_init() is defined in kernel/timer.c:
void timer_init(void)
list_initialize(&timer_queue);
/* register for a periodic timer tick */
platform_set_periodic_timer(timer_tick, NULL, 10); /* 10ms */
執行 thread_resume 或是 bootstrap_nandwrite
In kermel/main.c:
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, "calling nandwrite_init()/n");
nandwrite_init();
continue to see apps_init(): app/app.c:void apps_init(void)
#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 */
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);
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));
至于會有那些 app 被放入 boot thread section, 則定義在 include/app.h 中的 APP_START(appname)
#define APP_START(appname) struct app_descriptor _app_##appname __SECTION(".apps") = { .name = #appname,
#define APP_END };
在 app 中隻要像 app/aboot/aboot.c 指定就會在 bootloader bootup 時放入 thread section 中被執行.
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 皆有此聲明.
接下來關注: aboot.c 中的 aboot_init()
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();
/* 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())
init_vol_key();
if(voldown_press())
reboot_mode = check_reboot_mode();
if (reboot_mode == RECOVERY_MODE) {
} else if(reboot_mode == FASTBOOT_MODE) {
boot_linux_from_mmc();
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);
fastboot_register("flash:", cmd_flash_mmc);
fastboot_register("erase:", cmd_erase_mmc);
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 時的 flags
__WEAK int target_is_emmc_boot(void)
{
#if _EMMC_BOOT
return 1;
return 0;
check_reboot_mode is defined in target/<your-platform>/init.c:
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:
#define RECOVERY_MODE 0x77665502
#define FASTBOOT_MODE 0x77665500
再來就會執行 boot_linux_from_mmc():
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;
ptn = mmc_ptn_offset("recovery");
dprintf(CRITICAL, "ERROR: No recovery partition found/n");
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");
if (hdr->page_size && (hdr->page_size != page_size)) {
page_size = hdr->page_size;
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");
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");
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);
mmc_read() is defined in platform/<your-platform>/mmc.c:
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(): 啟動 Linux, 看看函數定義
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;
if (pause_at_bootup) {
src = battchg_pause;
ptr += (n / 4);
/* END */
*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_mmu();
#if DISPLAY_SPLASH_SCREEN
display_shutdown();
entry(0, machtype, tags);
配合 boot.img 來看會比較好了解.
由此可知 boot_img_hdr 中各成員值為:
TAGS_ADDR 如上 target/<your-platform>/rules.mk 所定義的 : 0x40200100, 是以 boot_linux(), 就是傳入TAGS_ADDR,
然後将資料寫入 tag, tag 的結構如下所示.
然後進入到 kernel 的入口函數: entry(0, machtype, tags)
【新浪微網誌】 張昺華--sky
【twitter】 @sky2030_
【facebook】 張昺華 zhangbinghua
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利.