FastBoot協議版本0.4
fastboot協議是一種通過USB連接與bootloaders通訊的機制。它被設計的非常容易實現,能夠用於多種設備和運行Linux、Windows或者OSX的主機。
基本需求(Basic Requirements)
- 兩個端點,一個輸入端,一個輸出端。
- 對於全速(full-speed)USB,最大包尺寸必須是64個字節;
對於高速(hign-speed)USB,最大包尺寸必須是512個字節。 - 協議完全是主機驅動(SSW注:相對於設備客戶端而言),並且同步的。
這與多通道、雙向、異步的ADB協議不同。
傳輸和組幀(Transport and Framing)
步驟1、主機發送命令(Command)。
一個命令是一個ASCII字符串,並且只能包含在不大於64個字節的單個包內。
步驟2、客戶端(SSW注:設備)用一個單個的不大於64個字節的包響應。
響應包開頭四個字節是“OKAY”、“FAIL”、“DATA”或者“INFO”。
響應包剩餘的字節可以包含ASCII格式的說明性信息。
a、INFO -> 剩餘的60個字節包含說明信息(提供進度或者診斷信息)。
這些說明信息應該被顯示,然後重複步驟2。
b、FAIL -> 指示請求的命令失敗。
剩餘的60個字節可以提供一個文本形式的失敗原因呈現給用戶。交互停止。
c、OKAY -> 指示請求的命令成功完成。跳轉到步驟5。
d、DATA -> 請求的命令已經爲數據階段做好準備。
一個數據響應包是12個字節長,組織形式爲DATA00000000,
其中8位十六進制的數字表示所傳輸數據的總大小。
步驟3、數據階段。
根據命令的不同,主機或者客戶端將發送指定大小的數據。
比指定長度短的包總是可接受的,零長度的包將被忽略。
這個階段會一直持續,直到客戶端已經發送或接收了上面數據響應包中指定大小的字節數爲止。
步驟4、客戶端用一個單個的不大於64個字節的包響應。
a、INFO -> 顯示剩餘的60個字節,然後返回到步驟4。
b、FAIL -> 顯示剩餘的60個字節(如果有的話)作爲失敗原因,命令失敗,停止交互。
c、OKAY -> 成功。跳轉到步驟5。
步驟5、命令執行成功。
結束交互。
示例會話(Example Session)
Host:主機 Client:客戶端(設備)
Host: “getvar:version” 請求版本號
Client: “OKAY0.4” 返回版本爲”0.4”
Host: “getvar:nonexistant” 請求未定義的變量
Client: “OKAY” 返回值爲”“
Host: “download:00001234” 請求發送0x1234大小的字節數據
Client: “DATA00001234” 準備好接收數據
Host: < 0x1234 bytes > 發送數據
Client: “OKAY” 數據接收成功完成
Host: “flash:bootloader” 請求刷新數據到bootloader
Client: “INFOerasing flash” 指示狀態/進度爲“擦除flash”
“INFOwriting flash” 指示狀態/進度爲“寫入flash”
“OKAY” 刷新成功完成
Host: “powerdown” 發送“關機”命令
Client: “FAILunknown command” 命令執行失敗
命令參考(Command Reference)
- 命令參數以printf風格的轉義序列表示。
- 命令是ASCII字符串,發送時不用引號(下面命令外使用引號僅僅爲了在此文檔中清楚的表達命令),
發送時也不以0字節結尾。
- 以小寫字母開頭的命令是爲本規範保留的,OEM特定的命令不應該以小寫字母開頭,以防和規範的未來版本不兼容。
"getvar:%s" 從bootloader讀取配置或版本變量。
變量的值在OKAY響應的後面返回。
"download:x" 寫入數據到內存,供下面闡述的”boot“、”randisk“、”flash“等命令使用。
如果RAM有足夠的空間,客戶端將用”DATAx“迴應;否則,將回應”FAIL“。
下載數據的大小會被記下來。
"verify:x" 發送一個數字簽名去驗證下載的數據。
如果bootloader是”secure(安全的)“,那麼簽名驗證是必須的;
如果bootloader不是”secure“,”flash“和”boot“命令會忽略簽名驗證。
"flash:%s" 將之前下載的影像寫入到指定的分區(如果可能的話)。
"erase:%s" 擦除指定的分區(將分區全部寫成0xFFs)。
"boot" 之前下載的數據一個boot.img,應該按照boot.img的正常步驟被啓動。
"continue" 繼續正常啓動工作(如果可能的話)。
"reboot" 重新啓動設備。
"reboot-bootloader" 重新啓動進入bootloader。
對於升級bootloader之後,用新的bootloader去升級其他分區的升級過程,
這個命令是很有用的。
"powerdown" 設備關機。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
客戶端變量(Client Variables)
命令”getvar:%s”用來讀取客戶端變量,客戶端變量代表關於設備和運行於設備之上軟件的各種信息。
當前已經定義的變量名稱如下:
version FastBoot協議所支持的版本。
version-bootloader Bootloader的版本字符串。
version-baseband 基帶(Baseband)軟件的版本字符串。
product 產品名稱。
serialno 產品序列號。
secure 如果值是”yes“,說明這是一個安全的bootloader,在它安裝或啓動映像之前,需要一個簽名。
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
以小寫字母開頭的變量名被本規範保留,OEM特定的變量名不應該以小寫字母開頭。
ADB (Android Debug Bridge)
ADB是Android系統的調試協議,所有android系統都支持它。
解析源碼:
lk-refs-heads-master\app\aboot下的rules.mk可知
OBJS += \
$(LOCAL_DIR)/aboot.o \
$(LOCAL_DIR)/fastboot.o
- 1
- 2
- 3
- 1
- 2
- 3
主要包含aboot.c 和 fastboot.c
在aboot.c 中
APP_START(aboot)
.init = aboot_init,
APP_END
- 1
- 2
- 3
- 1
- 2
- 3
aboot_init的實現如下:
可知只要用戶按下BACK按鍵就跳過boot_linux_from_flash
,進入到fastboot.
void aboot_init(const struct app_descriptor *app)
{
if (keys_get_state(KEY_BACK) != 0)
goto fastboot;
boot_linux_from_flash();
dprintf(CRITICAL, "ERROR: Could not do normal boot. Reverting "
"to fastboot mode.\n");
fastboot:
udc_init(&surf_udc_device);
fastboot_register("boot", cmd_boot);
fastboot_register("erase:", cmd_erase);
fastboot_register("flash:", cmd_flash);
fastboot_register("continue", cmd_continue);
fastboot_publish("product", "swordfish");
fastboot_publish("kernel", "lk");
fastboot_init((void*) SCRATCH_ADDR, 100 * 1024 * 1024);
udc_start();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
調用usb初始化,並註冊fastboot的boot/erase/flash/continue/product/kernel等命令,並fastboot_init
,新建thread來接受pc發過來的命令
int fastboot_init(void *base, unsigned size)
{
thread_t *thr;
dprintf(INFO, "fastboot_init()\n");
download_base = base;
download_max = size;
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)
goto fail_alloc_in;
out = udc_endpoint_alloc(UDC_TYPE_BULK_OUT, 512);
if (!out)
goto fail_alloc_out;
fastboot_endpoints[0] = in;
fastboot_endpoints[1] = out;
req = udc_request_alloc();
if (!req)
goto fail_alloc_req;
if (udc_register_gadget(&fastboot_gadget))
goto fail_udc_register;
fastboot_register("getvar:", cmd_getvar);
fastboot_register("download:", cmd_download);
fastboot_publish("version", "0.5");
thr = thread_create("fastboot", fastboot_handler, 0, DEFAULT_PRIORITY, 4096);
thread_resume(thr);
return 0;
fail_udc_register:
udc_request_free(req);
fail_alloc_req:
udc_endpoint_free(out);
fail_alloc_out:
udc_endpoint_free(in);
fail_alloc_in:
return -1;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
這個函數又註冊getvar/download命令,並建立thread接受pc發過來的執行,thread的callback函數是fastboot_handler
:
static int fastboot_handler(void *arg)
{
for (;;) {
event_wait(&usb_online);
fastboot_command_loop();
}
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
這個函數是循環,如果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");
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
所有的命令都在cmdlist 這個列表中,調用memcmp來比較pc發的命令和fastboot的命令是否相等,如果相等就調用handle處理,
也就是我們fastboot_register
時候的第二個參數.
在fastboot中我們一般通過fastboot_register
來註冊命令
fastboot_register("erase:", cmd_erase);
我們來看看fastboot_register
的實現
static struct fastboot_cmd *cmdlist;
void fastboot_register(const char *prefix,
void (*handle)(const char *arg, void *data, unsigned sz))
{
struct fastboot_cmd *cmd;
cmd = malloc(sizeof(*cmd));
if (cmd) {
cmd->prefix = prefix;
cmd->prefix_len = strlen(prefix);
cmd->handle = handle;
cmd->next = cmdlist;
cmdlist = cmd;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
在fastboot.c 中定義一個fastboot_cmd
類型的靜態變量cmdlist,在調用fastboot_register
註冊時會先通過
malloc 申請一個fastboot_cmd
,然後分貝給prefix賦值命令的名稱,如本例中的erase:,handle是收到命令後
要處理的函數,本例中賦值爲cmd_erase
。然後將這個新建的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);
- 1
- 2
- 1
- 2
我們會調用fastboot_publish
來註冊常量,例如下例中定義version=0.5
fastboot_publish("version", "0.5");
我們看看fastboot_publish
的實現,和上面講的fastboot_register
類似,也是有一個常量的varlist,通過fastboot_publish
註冊的常量都在這個varlist上.
static struct fastboot_var *varlist;
void fastboot_publish(const char *name, const char *value)
{
struct fastboot_var *var;
var = malloc(sizeof(*var));
if (var) {
var->name = name;
var->value = value;
var->next = varlist;
varlist = var;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
而我們又是通過 fastboot_register("getvar:", cmd_getvar);
來註冊如果獲取常量的
所以如果發過來的命令是getvar,就調用cmd_getva
。
而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("");
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
fastboot init
\lk-refs-heads-master\arch\arm\crt0.s
_start:
b reset
b arm_undefined
b arm_syscall
b arm_prefetch_abort
b arm_data_abort
b arm_reserved
b arm_irq
b arm_fiq
cmp r0, r1
strlt r2, [r0], #4
blt .L__bss_loop
......
bl kmain
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
lk的第一行語句是從crt0.s 中的_start開始,在這個函數的最後會調用kmain 進入到c code的執行
void kmain(void)
{
....
// 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();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
在這個函數的最後會new 一個thread,回調函數是bootstrap2
static int bootstrap2(void *arg)
{
dprintf(SPEW, "calling apps_init()\n");
apps_init();
....
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
這個函數最後會調用apps_init
。在lk中所有的應該都是以app的形式出現的,其中每個app對應一個thread,就像前面講過的fastboot,我們看看在apps_init
中怎麼給每個app一個thread來運行的
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);
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
這個函數會對放在__apps_start
和__apps_end
的apps如果有init函數,就全部調用init:
APP_START(aboot)
.init = aboot_init,
APP_END
- 1
- 2
- 3
- 1
- 2
- 3
以fastboot 爲例,apps_init
中的app->init=aboot_init
。我們在aboot_init
會爲fastboot new一個thread來運行.
如果在__apps_start
和__apps_end
的apps 有定義entry 且flags沒有APP_FLAG_DONT_START_ON_BOOT
例如shell.c中
APP_START(shell)
.init = shell_init,
.entry = shell_entry,
APP_END
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
的話,就在apps_init
中就調用start_app
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));
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
就會爲這個apps new 一個thread,其回調函數是app_thread_entry
static int app_thread_entry(void *arg)
{
const struct app_descriptor *app = (const struct app_descriptor *)arg;
app->entry(app, NULL);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
在app_thread_entry
中直接調用app->entry
會運行在一個新的thread中,在shell.c中就是shell_entry
會在一個new thread中運行.
lk thread
在lk中我們一般通過thread_create
來新建一個thread,但這個thread 是THREAD_SUSPENDED
,必須要調用thread_resume
才能開始運行
enum thread_state {
THREAD_SUSPENDED = 0,
THREAD_READY,
THREAD_RUNNING,
THREAD_BLOCKED,
THREAD_SLEEPING,
THREAD_DEATH,
};
thread_resume(thread_create(app->name, &app_thread_entry, (void *)app, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE));
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
我們來看看thread_create
的實現
thread_t *thread_create(const char *name, thread_start_routine entry, void *arg, int priority, size_t stack_size)
{
thread_t *t;
t = malloc(sizeof(thread_t));
if (!t)
return NULL;
init_thread_struct(t, name);
t->entry = entry;
t->arg = arg;
t->priority = priority;
t->saved_critical_section_count = 1; /* we always start inside a critical section */
t->state = THREAD_SUSPENDED;
t->blocking_wait_queue = NULL;
t->wait_queue_block_ret = NO_ERROR;
/* create the stack */
t->stack = malloc(stack_size);
if (!t->stack) {
free(t);
return NULL;
}
t->stack_size = stack_size;
/* inheirit thread local storage from the parent */
int i;
for (i=0; i < MAX_TLS_ENTRY; i++)
t->tls[i] = current_thread->tls[i];
/* set up the initial stack frame */
arch_thread_initialize(t);
/* add it to the global thread list */
enter_critical_section();
list_add_head(&thread_list, &t->thread_list_node);
exit_critical_section();
return t;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
這個函數但大部分在做新thread_t
結構體的初始化。在最後調用list_add_head
將這個新建的thread加入到thread_list
中.
/* global thread list */
static struct list_node thread_list;
所有的thread 都是放在這個全局的thread_list
中
現在爲止我們的thread 已經建好了,但是其狀態是t->state = THREAD_SUSPENDED;
我們需要調用thread_resume
來讓其運行
status_t thread_resume(thread_t *t)
{
#if THREAD_CHECKS
ASSERT(t->magic == THREAD_MAGIC);
ASSERT(t->state != THREAD_DEATH);
#endif
if (t->state == THREAD_READY || t->state == THREAD_RUNNING)
return ERR_NOT_SUSPENDED;
enter_critical_section();
t->state = THREAD_READY;
insert_in_run_queue_head(t);
thread_yield();
exit_critical_section();
return NO_ERROR;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
這個函數會先判斷t->state
是不是THREAD_READY
或者 THREAD_RUNNING
,如果是的話就退出,說明出錯了,由於我們是
新建一個thread,其狀態是THREAD_SUSPENDED
,所以繼續往下走
將thread的狀態設成THREAD_READY
。
然後調用insert_in_run_queue_head
插入到將要運行thread的list中
/* run queue manipulation */
static void insert_in_run_queue_head(thread_t *t)
{
#if THREAD_CHECKS
ASSERT(t->magic == THREAD_MAGIC);
ASSERT(t->state == THREAD_READY);
ASSERT(!list_in_list(&t->queue_node));
ASSERT(in_critical_section());
#endif
list_add_head(&run_queue[t->priority], &t->queue_node);
run_queue_bitmap |= (1<<t->priority);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
其中
static struct list_node run_queue[NUM_PRIORITIES];
#define NUM_PRIORITIES 32
可以run_queue
是一個數組,每個優先級對應數組中的一樣。其中每一項又是一個list. thread_resume
在把這個thread 放到run_queue
後,繼續調用thread_yield
來運行
void thread_yield(void)
{
#if THREAD_CHECKS
ASSERT(current_thread->magic == THREAD_MAGIC);
ASSERT(current_thread->state == THREAD_RUNNING);
#endif
enter_critical_section();
#if THREAD_STATS
thread_stats.yields++;
#endif
/* we are yielding the cpu, so stick ourselves into the tail of the run queue and reschedule */
current_thread->state = THREAD_READY;
current_thread->remaining_quantum = 0;
insert_in_run_queue_tail(current_thread);
thread_resched();
exit_critical_section();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
這個函數將當前正在運行的thread的狀態設成THREAD_READY
,然後調用insert_in_run_queue_tail
將當前thread 插入到run_queue
的最後面然後調用thread_resched
來進程thread 切換
void thread_resched(void)
{
thread_t *oldthread;
thread_t *newthread;
oldthread = current_thread;
int next_queue = HIGHEST_PRIORITY - __builtin_clz(run_queue_bitmap) - (32 - NUM_PRIORITIES);
//dprintf(SPEW, "bitmap 0x%x, next %d\n", run_queue_bitmap, next_queue);
newthread = list_remove_head_type(&run_queue[next_queue], thread_t, queue_node);
if (list_is_empty(&run_queue[next_queue]))
run_queue_bitmap &= ~(1<<next_queue);
newthread->state = THREAD_RUNNING;
if (newthread == oldthread)
return;
/* set up quantum for the new thread if it was consumed */
if (newthread->remaining_quantum <= 0) {
newthread->remaining_quantum = 5; // XXX make this smarter
}
/* do the switch */
oldthread->saved_critical_section_count = critical_section_count;
current_thread = newthread;
critical_section_count = newthread->saved_critical_section_count;
arch_context_switch(oldthread, newthread);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
通過list_remove_head_type
異常並返回在run_queu
頭部的thread,由於這次切換的時候是找優先級高的thread的運行,因此如果當前還有比我們優先級高的thread,可能這次運行的不是我們前面
新建的thread
int next_queue = HIGHEST_PRIORITY - __builtin_clz(run_queue_bitmap) - (32 - NUM_PRIORITIES);
將這個thread的狀態切換成newthread->state = THREAD_RUNNING
最後調用arch_context_switch
來進行thread 切換
void arch_context_switch(thread_t *oldthread, thread_t *newthread)
{
// dprintf("arch_context_switch: old %p (%s), new %p (%s)\n", oldthread, oldthread->name, newthread, newthread->name);
arm_context_switch(&oldthread->arch.sp, newthread->arch.sp);
}
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
由於當前cpu是arm,因此調用arm_context_switch
來切換
/* context switch frame is as follows:
* ulr
* usp
* lr
* r11
* r10
* r9
* r8
* r7
* r6
* r5
* r4
*/
/* arm_context_switch(addr_t *old_sp, addr_t new_sp) */
FUNCTION(arm_context_switch)
/* save all the usual registers + user regs */
/* the spsr is saved and restored in the iframe by exceptions.S */
sub r3, sp, #(11*4)
/* can't use sp in user mode stm */
mov r12, lr
stmia r3, { r4-r11, r12, r13, r14 }^
/* save old sp */
str r3, [r0]
/* clear any exlusive locks that the old thread holds */
#if ARM_ISA_ARMV7
/* can clear it directly */
.word 0xf57ff01f // clrex
#elif ARM_ISA_ARMV6
/* have to do a fake strex to clear it */
ldr r0, =strex_spot
strex r3, r2, [r0]
#endif
/* load new regs */
ldmia r1, { r4-r11, r12, r13, r14 }^
mov lr, r12
/* restore lr */
add sp, r1, #(11*4) /* restore sp */
bx lr
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
其實現比較簡單就是保存當前thread的寄存器,然後將新thread的寄存器從stack中load到寄存器中,並執行bx lr
跳到新thread來運行
lk的thread切換
lk是按照時間片來運行thread,每個thread 默認運行5個時鐘中斷,如果時間到了就在timer的中斷中做thread 切換。下來我們看看具體的代碼。
當我們通過thread_resume
來讓thread 運行時.會調用thread_yiled
void thread_yield(void)
{
enter_critical_section();
/* we are yielding the cpu, so stick ourselves into the tail of the run queue and reschedule */
current_thread->state = THREAD_READY;
current_thread->remaining_quantum = 0;
insert_in_run_queue_tail(current_thread);
thread_resched();
exit_critical_section();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
這個函數會將current_thread->remaining_quantum = 0;
而remaining_quantum
就是表示thread 可以運行的timer 中斷的個數
繼續看thread_resched
void thread_resched(void)
{
thread_t *oldthread;
thread_t *newthread;
/* set up quantum for the new thread if it was consumed */
if (newthread->remaining_quantum <= 0) {
newthread->remaining_quantum = 5; // XXX make this smarter
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
這個函數會將新thread的remaining_quantum
設定爲5,也就是運行5個time 中斷,如果一個timer中斷是10ms的話,每個thread 就默認運行50ms
明白這點後我們看看怎麼在時鐘中斷中切換thread
在arm_irq
中斷中會根據中斷的返回值來決定是否進行thread切換
FUNCTION(arm_irq)
/* XXX only deals with interrupting supervisor mode */
/* call into higher level code */
mov r0, sp /* iframe */
bl platform_irq
/* reschedule if the handler returns nonzero */
cmp r0, #0
blne thread_preempt
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
先看看platform_irq
的實現
enum handler_return platform_irq(struct arm_iframe *frame)
{
ret = handler[num].func(handler[num].arg);
return ret;
}
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
會根據中斷號,調用對應的函數而handler中的func是通過register_int_handler
來註冊的.
void register_int_handler(unsigned int vector, int_handler func, void *arg)
{
if (vector >= NR_IRQS)
return;
enter_critical_section();
handler[vector].func = func;
handler[vector].arg = arg;
exit_critical_section();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
看下面的函數handler是platform_tick
,對應的timer中斷號是INT_PIT
void platform_init_timer(void)
{
register_int_handler(INT_PIT, &platform_tick, NULL);
}
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
繼續看platform_tick
的實現
static enum handler_return platform_tick(void *arg)
{
*REG(PIT_CLEAR_INT) = 1;
if (t_callback) {
return t_callback(arg, current_time());
} else {
return INT_NO_RESCHEDULE;
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
調用t_callback
,通過下面的函數設定t_callback
status_t platform_set_periodic_timer(platform_timer_callback callback, void *arg, time_t interval)
{
enter_critical_section();
t_callback = callback;
return NO_ERROR;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
所以t_callback == timer_tick
void timer_init(void)
{
list_initialize(&timer_queue);
/* register for a periodic timer tick */
platform_set_periodic_timer(timer_tick, NULL, 10); /* 10ms */
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
timer_tick
中繼續call thread_timer_tick
來決定是否要進行thread 切換
static enum handler_return timer_tick(void *arg, time_t now)
{
timer_t *timer;
enum handler_return ret = INT_NO_RESCHEDULE;
#if THREAD_STATS
thread_stats.timer_ints++;
#endif
for (;;) {
/* let the scheduler have a shot to do quantum expiration, etc */
if (thread_timer_tick() == INT_RESCHEDULE)
ret = INT_RESCHEDULE;
return INT_RESCHEDULE;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
而在thread_timer_tick
就是簡單的判定當前thread的時間片是否用完,如果用完就切換
enum handler_return thread_timer_tick(void)
{
if (current_thread == idle_thread)
return INT_NO_RESCHEDULE;
current_thread->remaining_quantum--;
if (current_thread->remaining_quantum <= 0)
return INT_RESCHEDULE;
else
return INT_NO_RESCHEDULE;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
如果返回INT_RESCHEDULE
也就是不等於0 ,也就是要切換thread,則在arm_irq
中繼續調用thread_preempt
進行thread 切換。
可以看到當前remaining_quantum
不等於0,就說明當前有更高優先級的thread搶佔了當前的thread, 所以講當前thread 插在run queue的前面等下一次運行。否則的話,就是當前thread時間片運行完,加到run queue的後面
void thread_preempt(void)
{
#if THREAD_CHECKS
ASSERT(current_thread->magic == THREAD_MAGIC);
ASSERT(current_thread->state == THREAD_RUNNING);
#endif
enter_critical_section();
#if THREAD_STATS
if (current_thread != idle_thread)
thread_stats.preempts++; /* only track when a meaningful preempt happens */
#endif
/* we are being preempted, so we get to go back into the front of the run queue if we have quantum left */
current_thread->state = THREAD_READY;
if (current_thread->remaining_quantum > 0)
insert_in_run_queue_head(current_thread);
else
insert_in_run_queue_tail(current_thread); /* if we're out of quantum, go to the tail of the queue */
thread_resched();
exit_critical_section();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
kl中的memory
在lk中通過我們一般通過malloc來申請內存
void *malloc(size_t size)
{
return heap_alloc(size, 0);
}
- 1
- 2
- 3
- 4
- 1
- 2
- 3
- 4
malloc 直接調用heap_alloc
來申請。 lk_main
函數中通過調用heap_init
來初始由於malloc申請的heap
void heap_init(void)
{
LTRACE_ENTRY;
// set the heap range
theheap.base = (void *)HEAP_START;
theheap.len = HEAP_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));
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
可以看到這個函數一開始就要設置heap 需要管理的memory的start 和 size
這兩個宏定義如下:
#define HEAP_START ((unsigned long)&_end)
#define HEAP_LEN ((size_t)&_end_of_ram - (size_t)&_end)
_end
和_end_of_ram
又是在下面的文件中定義的
system-onesegment.ld
__data_end = .;
/* unintialized data (in same segment as writable data) */
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss .bss.*) }
. = ALIGN(4);
_end = .;
. = %MEMBASE% + %MEMSIZE%;
_end_of_ram = .;
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
可以看到需要用戶自己定義MEMBASE
和MEMSIZE
變量
我們繼續看heap_init
會調用下面的函數初始free list
// initialize the free list
list_initialize(&theheap.free_list);
可見這個時候free list
是空的
static inline void list_initialize(struct list_node *list)
{
list->prev = list->next = list;
}
繼續看是如何插入chunk的
// create an initial free chunk
heap_insert_free_chunk(heap_create_free_chunk(theheap.base, theheap.len));
其中heap_create_free_chunk
的實現如下,
struct free_heap_chunk *heap_create_free_chunk(void *ptr, size_t len)
{
DEBUG_ASSERT((len % sizeof(void *)) == 0); /*size must be aligned on pointer boundary*/
struct free_heap_chunk *chunk = (struct free_heap_chunk *)ptr;
chunk->len = len;
return chunk;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
可知lk中是用chunk 來管理memory的,剛開始的時候只有一個很大的chunk heap_insert_free_chunk
的實現如下:
static struct free_heap_chunk *heap_insert_free_chunk(struct free_heap_chunk *chunk)
{
#if DEBUGLEVEL > INFO
vaddr_t chunk_end = (vaddr_t)chunk + chunk->len;
#endif
// dprintf("%s: chunk ptr %p, size 0x%lx, chunk_end 0x%x\n", __FUNCTION__, chunk, chunk->len, chunk_end);
struct free_heap_chunk *next_chunk;
struct free_heap_chunk *last_chunk;
// walk through the list, finding the node to insert before
list_for_every_entry(&theheap.free_list, next_chunk, struct free_heap_chunk, node) {
if (chunk < next_chunk) {
DEBUG_ASSERT(chunk_end <= (vaddr_t)next_chunk);
list_add_before(&next_chunk->node, &chunk->node);
goto try_merge;
}
}
// walked off the end of the list, add it at the tail
list_add_tail(&theheap.free_list, &chunk->node);
// try to merge with the previous chunk
try_merge:
last_chunk = list_prev_type(&theheap.free_list, &chunk->node, struct free_heap_chunk, node);
if (last_chunk) {
if ((vaddr_t)last_chunk + last_chunk->len == (vaddr_t)chunk) {
// easy, just extend the previous chunk
last_chunk->len += chunk->len;
// remove ourself from the list
list_delete(&chunk->node);
// set the chunk pointer to the newly extended chunk, in case
// it needs to merge with the next chunk below
chunk = last_chunk;
}
}
// try to merge with the next chunk
if (next_chunk) {
if ((vaddr_t)chunk + chunk->len == (vaddr_t)next_chunk) {
// extend our chunk
chunk->len += next_chunk->len;
// remove them from the list
list_delete(&next_chunk->node);
}
}
return chunk;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
這個函數首先會根據chunk的地址,來將chunk插入到free list的位置中,這裏也可以知道free list 中的chunk 是按地址排序的這個函數後面的部分會試圖對已存在的chunk進行合併,類似kernel中的buddy system前面講過malloc 直接調用heap_alloc
來申請內存
void *heap_alloc(size_t size, unsigned int alignment)
{
void *ptr;
LTRACEF("size %zd, align %d\n", size, alignment);
// alignment must be power of 2
if (alignment & (alignment - 1))
return NULL;
/*we always put a size field + base pointer + magic in front of the allocation*/
size += sizeof(struct alloc_struct_begin);
/*make sure we allocate at least the size of a struct free_heap_chunk so that when we free it, we can create a struct free_heap_chunk struct and stick it*/
// in the spot
if (size < sizeof(struct free_heap_chunk))
size = sizeof(struct free_heap_chunk);
// round up size to a multiple of native pointer size
size = ROUNDUP(size, sizeof(void *));
// deal with nonzero alignments
if (alignment > 0) {
if (alignment < 16)
alignment = 16;
// add alignment for worst case fit
size += alignment;
}
// critical section
enter_critical_section();
// walk through the list
ptr = NULL;
struct free_heap_chunk *chunk;
list_for_every_entry(&theheap.free_list, chunk, struct free_heap_chunk, node) {
DEBUG_ASSERT((chunk->len % sizeof(void *)) == 0); // len should always be a multiple of pointer size
// is it big enough to service our allocation?
if (chunk->len >= size) {
ptr = chunk;
// remove it from the list
struct list_node *next_node = list_next(&theheap.free_list, &chunk->node);
list_delete(&chunk->node);
if (chunk->len > size + sizeof(struct free_heap_chunk)) {
/*here's enough space in this chunk to create a new one after the allocation*/
struct free_heap_chunk *newchunk = heap_create_free_chunk((uint8_t *)ptr + size, chunk->len - size);
// truncate this chunk
chunk->len -= chunk->len - size;
// add the new one where chunk used to be
if (next_node)
list_add_before(next_node, &newchunk->node);
else
list_add_tail(&theheap.free_list, &newchunk->node);
}
/*he allocated size is actually the length of this chunk, not the size requested*/
DEBUG_ASSERT(chunk->len >= size);
size = chunk->len;
ptr = (void *)((addr_t)ptr + sizeof(struct alloc_struct_begin));
// align the output if requested
if (alignment > 0) {
ptr = (void *)ROUNDUP((addr_t)ptr, alignment);
}
struct alloc_struct_begin *as = (struct alloc_struct_begin *)ptr;
as--;
as->magic = HEAP_MAGIC;
as->ptr = (void *)chunk;
as->size = size;
break;
}
}
LTRACEF("returning ptr %p\n", ptr);
// heap_dump();
exit_critical_section();
return ptr;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
這個函數前一部分size,例如需要在size基礎上加上alloc_struct_begin
的size,用於管理chunk size 要至少4byte對其等後辦法通過list_for_every_entry
遍歷free list 找到size滿足用戶申請的size後,將chunk的起始地址返回用戶。
LK是什麼
LK 是 Little Kernel 它是 appsbl (Applications ARM Boot Loader)流程代碼 ,little kernel 是小內核小操作系統。
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()
體系架構相關的初始化我們一般用的 ARM 體系
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
apps_init 是關鍵,對 LK 中所謂 app 初始化並運行起來,而 aboot_init 就將在這裏開始被運行,Android Linux 內核的加載工作就在 aboot_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的啓動過程就結束了。
http://blog.csdn.net/viewsky11/article/details/53334219