一、进fastboot之前
bootloader入口文件为vendor\mediatek\proprietary\bootable\bootloader\lk\arch\arm arch\arm\crt0.S,前面的是一些环境和硬件的初始化,我们直接从kmain:”bl Kmain“开始,该函数位于main.c文件中。
kmain()—>bootstrap2()—>apps_init()
void kmain(void)
{
…
初始化环境的一系列的init
thread_t *thread_bs2 = thread_create("bootstrap2", &bootstrap2, NULL,
DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
…
}
static int bootstrap2(void *arg)
{
…
平台相关的init
apps_init();
return 0;
}
这里apps_init 是关键,对 LK 中所有的app 初始化并将其运行起来,而 aboot_init 就将在这里开始被运行,android linux 内核的加载工作就在 aboot_init 中完成的 。该函数中包含两个for函数,且循环条件一致:作用就是初始化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);
}
}
}
这段代码比较难理解,我们先看看__apps_start这个宏:
vendor\mediatek\proprietary\bootable\bootloader\lk\include\app.h中
APP_START和APP_END定义如下:
这个需要结合对应的连接脚本去理解:
vendor\mediatek\proprietary\bootable\bootloader\lk\arch\arm\system-onesegment.ld
.rodata : {
*(.rodata .rodata.* .gnu.linkonce.r.*)
. = ALIGN(4);
__commands_start = .;
KEEP (*(.commands))
__commands_end = .;
. = ALIGN(4);
__apps_start = .;
KEEP (*(.apps))
__apps_end = .;
. = ALIGN(4);
__rodata_end = . ;
}
这里的意思是将需要启动的apps括在了SECTIONS下的.rodata段中,且以__apps_start为开头,以__apps_end标志结束,指定的app就会在 bootloader bootup 时放入 thread section 中被执行,结合aboot_init()的定义就明确很多了 :
vendor\mediatek\proprietary\bootable\bootloader\lk\app\aboot\aboot.c最后
这里定义的aboot_init()就会在bootloader bootup是和其他的init app一样放入thread section中被执行。
再看aboot_init (const struct app_descriptor *app);
-
设置NAND/EMMC读取信息页面大小:
-
读取按键信息,判断是正常开机,还是进入 fastboot ,还是进入recovery 模式:
在函数体内,除了上半部分对按键进行判断以确定模式走向外,还有对BCB区域中command指令的读取来判断是否进入recovery 模式:
-
加载内核:如果是启动main system则执行boot_linux_from_mmc()进行加载,如果是启动Recovery模式则通过boot_linux_from_flash()加载。
-
启动内核:
boot_linux((void *)hdr->kernel_addr, (unsigned *) hdr->tags_addr,
(const char *)cmdline, board_machtype(),
(void *)hdr->ramdisk_addr, hdr->ramdisk_size);
二、fastboot分支
这里我们走fastboot分支,goto fastboot;
void aboot_init(const struct app_descriptor *app)
{
…
udc_init(&surf_udc_device);
…
udc_start();
}
调用usb初始化,并注册fastboot的boot/erase/flash/continue/product/kernel等命令,并fastboot_init,新建thread来接受pc发过来的命令
int fastboot_init(void *base, unsigned size)
{
…
/\*LXO: Download related command\*/
fastboot_register("download:", cmd_download, TRUE, FALSE);
fastboot_publish("max-download-size", dl_max_str);
/\*LXO: END!Download related command\*/
…
event_init(&usb_online, 0, EVENT_FLAG_AUTOUNSIGNAL);
event_init(&txn_done, 0, EVENT_FLAG_AUTOUNSIGNAL);
in = udc_endpoint_alloc(UDC_TYPE_BULK_IN, 512);
if (!in) {
ret = -1;
goto fail_alloc_in;
}
out = udc_endpoint_alloc(UDC_TYPE_BULK_OUT, 512);
if (!out) {
ret = -2;
goto fail_alloc_out;
}
fastboot_endpoints[0] = in;
fastboot_endpoints[1] = out;
req = udc_request_alloc();
if (!req) {
ret = -3;
goto fail_alloc_req;
}
if (udc_register_gadget(&fastboot_gadget)) {
ret = -4;
goto fail_udc_register;
}
thr = thread_create("fastboot", fastboot_handler, 0, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
if (!thr) {
ret = -1;
goto fail_alloc_in;
}
thread_resume(thr);
return 0;
…
}
这个函数又注册download命令,并建立thread接受pc发过来的执行,thread的callback函数是
fastboot_handler
static int fastboot_handler(void *arg)
{
for ( \;\; ) {
event_wait(&usb_online);
fastboot_command_loop();
}
return 0;
}
这个函数是循环,如果usb接收到命令,就调用fastboot_command_loop来解析命令并调用命令的执行函数
static void fastboot_command_loop(void)
{
struct fastboot_cmd *cmd;
int r;
dprintf(INFO,"fastboot: processing commands\n");
again:
while (fastboot_state != STATE_ERROR) {
r = usb_read(buffer, 64);
if (r < 0) break;
buffer[r] = 0;
dprintf(INFO,"fastboot: %s\n", buffer);
for (cmd = cmdlist; cmd; cmd = cmd->next) {
if (memcmp(buffer, cmd->prefix, cmd->prefix_len))
continue;
fastboot_state = STATE_COMMAND;
cmd->handle((const char*) buffer + cmd->prefix_len,
(void*) download_base, download_size);
if (fastboot_state == STATE_COMMAND)
fastboot_fail("unknown reason");
goto again;
}
fastboot_fail("unknown command");
}
fastboot_state = STATE_OFFLINE;
dprintf(INFO,"fastboot: oops!\n");
}
所有的命令都在cmdlist 这个列表中,调用memcmp来比较pc发的命令和fastboot的命令是否相等,如果相等就调用handle处理。
cmdlist:在调用fastboot_register 注册时会先通过malloc 申请一个fastboot_cmd,然后分配给prefix赋值命令的名称,然后将这个新建的cmd加到cmdlist中,这样在fastboot 处理函数中fastboot_command_loop,会比较buffer中接收到的命令和cmdlist->prefix 相比较是否相等(if (memcmp(buffer, cmd->prefix, cmd->prefix_len))),
如果相等就调用handle函数。
cmd->handle((const char*) buffer + cmd->prefix_len,
(void*) download_base, download_size);
我们会调用fastboot_publish 来注册常量,例如下例中定义version=0.5
fastboot_publish(“version”, “0.5”);
又是通过fastboot_register(“getvar:”, cmd_getvar)来注册如果获取常量的
所以如果发过来的命令是getvar,就调用cmd_getvar。
而cmd_getvar 就是将varlist中的所有产量通过fastboot_okay 发送给pc,显示出来.
static void cmd_getvar(const char *arg, void *data, unsigned sz)
{
struct fastboot_var *var;
for (var = varlist; var; var = var->next) {
if (!strcmp(var->name, arg)) {
fastboot_okay(var->value);
return;
}
}
fastboot_okay("");
}