什么是Trusty OS
為了應對開放系統的安全風險,Global Platform(GP)提出了可信執行環境的概念(TEE)。TEE要求在設備上有一個能夠與Rich OS(例如Android)並存,並運行於獨立空間的系統。這個系統的安全級別更高,可以為Rich OS提供密鑰存儲、加解密計算等服務。很明顯,這種系統的實現需要獲得從芯片到軟件的整套支持。很早以前ARM就推出了基於自己核心的解決方案---TrustZone。而各個下游廠商通常會在此基礎上各自實現自己的軟件系統,對Rich OS的接口也各不相同。這在一定程度上造成了開發成本的上升以及軟件系統的碎片化等問題。而Trusty OS就是谷歌針對這些問題提出的解決方案。
下圖是Trusty的一個大致構架,可以看出Trusty OS和Android是相對獨立運行的,他們之間通過特定的接口驅動進行通信。
圖1 Trusty OS 框架
此外,由圖中也可以看出Trusty OS可以直接接管硬件資源,這也在很大程度上提高了系統的安全性。關於這一點,在后面的應用場景中進行更詳細的討論。
有可能是因為Trusty不強制要求,目前的AOSP默認情況下似乎還沒有包含這部分內容。不過我們可以通過下面的命令直接下載代碼:
$ repo init -u https://android.googlesource.com/trusty/manifest
$ repo sync
啟動過程
Trusty的內核與Android Bootloader有一個同樣的名字---Little Kernel(LK),實際內容也差不多。我們先來看啟動過程。在目錄trusty/external/lk/arch/arm64中我們可以找到鏈接腳本system-onesegment.ld。其中的ENTRY(_start)指定了LK從_start函數開始。而_start的定義在同一目錄下的文件start.S中。start.S會做一些CPU的初始化工作,然后通過bl指令跳至lk_main函數。
前面提到的lk_main在trusty/external/lk/top/main.c中,它主要完成的工作如下:
1 void lk_main(ulong arg0, ulong arg1, ulong arg2, ulong arg3) 2 { 3 … … 4 thread_init_early();//初始化thread相關的結構體上下文 5 arch_early_init();//初始化MMU、向量表之類構架相關的東西 6 lk_primary_cpu_init_level(LK_INIT_LEVEL_ARCH_EARLY, LK_INIT_LEVEL_PLATFORM_EARLY - 1); 7 platform_early_init();//初始化中斷控制器、timer之類平台相關的東西 8 lk_primary_cpu_init_level(LK_INIT_LEVEL_PLATFORM_EARLY, LK_INIT_LEVEL_TARGET_EARLY - 1); 9 target_early_init();//初始化主板上的東西,比如gpio、各種外圍總線之類 10 call_constructors();//構造函數相關的初始化 11 lk_primary_cpu_init_level(LK_INIT_LEVEL_TARGET_EARLY, LK_INIT_LEVEL_HEAP - 1); 12 heap_init();//堆棧初始化 13 lk_primary_cpu_init_level(LK_INIT_LEVEL_HEAP, LK_INIT_LEVEL_KERNEL - 1); 14 kernel_init();//內核初始化 15 lk_primary_cpu_init_level(LK_INIT_LEVEL_KERNEL, LK_INIT_LEVEL_THREADING - 1); 16 thread_t *t = thread_create("bootstrap2", &bootstrap2, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);//創建一個thread,然后在調度中執行后面的初始化函數bootstrap2 17 … … 18 thread_become_idle();//將當前thread設為idle,並啟動調度過程 19 … … 20 } 21
在thread_become_idle中會調用thread_yield進行一次調度,然后在idle_thread_routine中通過ARM的wfi指令進入idle。此外,我們還可以看到lk_main中還多次執行了lk_primary_cpu_init_level,它的作用是按照enum lk_init_level執行通過宏LK_INIT_HOOK加入的回調函數。例如執行LK_INIT_HOOK(libtrusty_apps, start_apps, LK_INIT_LEVEL_APPS + 1);后會將相應的信息組合成一個結構體lk_init_struct然后放入section .lk_init。在初始化程序調用lk_primary_cpu_init_level 函數時,它會通過lk_init_level將lk_init區域中的內容取出來,找出相應的lk_init_level,並加以執行。至此啟動過程基本完成。
下面簡單說幾句調度過程。我們知道通常時間片輪詢的系統都是通過硬件上的tick中斷進入調度的。因此我們可以從中斷處理函數着手跟蹤代碼。中斷出現后后會首先進入trusty/ external/lk/arch/arm64/exceptions.S:
1 .macro irq_exception 2 regsave_short 3 msr daifclr, #1 /* reenable fiqs once elr and spsr have been saved */ 4 mov x0, sp 5 bl platform_irq 6 cbz x0, .Lirq_exception_no_preempt\@ 7 bl thread_preempt 8 .Lirq_exception_no_preempt\@: 9 msr daifset, #1 /* disable fiqs to protect elr and spsr restore */ 10 b arm64_exc_shared_restore_short 11 .endm 12
可以看到這會里通過bl指令跳入interrupts.c 中的platform_irq函數,它會返回下面的枚舉類型中的一個,具體判斷條件后面會提到:
1 enum handler_return { 2 INT_NO_RESCHEDULE = 0, 3 INT_RESCHEDULE, 4 }; 5
platform_irq執行完畢后,根據這個函數的返回值決定是否需要調度,如果需要調度就調用thread_preempt。通常情況下thread_preempt會先把當前thread放到隊列尾部,然后調用thread_resched進行切換。Thread_resched在隊列中找到要執行的thread,然后啟動一個回調為thread_timer_tick的10毫秒的tick中斷,以便再次進入platform_irq完成下一次循環。最后它會調用arch_context_switch完成新thread的上下文切換。
這里有一個非常重要的變量---remaining_quantum,這個變量實際上是標識當前thread輪詢次數的計數器,每個thread都有自己的remaining_quantum,初始值為5。tick中斷到達時會按照前面的描述首先調用platform_irq,platform_irq會先找到上面注冊的回調函數thread_timer_tick並加以執行。thread_timer_tick每次執行時都會將remaining_quantum減1,然后判斷它的值,如果大於0就返回INT_NO_RESCHEDULE,否則返回INT_RESCHEDULE。最終由platform_irq將這個結果傳入exceptions.S中,完成整個調度過程。
Trusty應用程序
Trusty OS中的MMU功能是打開的,因此的每個應用都可以運行在各自獨立的虛擬地址空間。整個系統采用時間片輪詢調度,所有應用享有同樣的優先級。開發者可以使用C/C++語言編寫應用程序。雖然它看上去是一個比較完整的操作系統,但是Trusty OS是不支持動態安裝第三方應用程序的。也就是說它是個封閉的系統,這也在一定程度上保障了系統的安全性。
所有的應用程序由一個不帶參數的main函數開始執行。此外每個應用還包含一個manifest.c文件,這個文件中會包含一些UUID等應用程序描述信息:
1 #define TRUSTY_APP_MANIFEST_ATTRS \ 2 __attribute((aligned(4))) __attribute((section(".trusty_app.manifest"))) 3 trusty_app_manifest_t TRUSTY_APP_MANIFEST_ATTRS trusty_app_manifest = 4 { 5 /* UUID : {5f902ace-5e5c-4cd8-ae54-87b88c22ddaf} */ 6 { 0x5f902ace, 0x5e5c, 0x4cd8, 7 { 0xae, 0x54, 0x87, 0xb8, 0x8c, 0x22, 0xdd, 0xaf } }, 8 … … 9 }; 10
可以看出這部分信息在鏈接后會和該應用程序放在一起,並形成一個完整的trusty_app image。
鏈接文件trusty_apps.ld中會指定兩個指針,__trusty_app_start及__trusty_app_end。這兩個指針一個指向app image的頭部一個指向尾部,由此系統啟動時可以通過它們找到所有的應用:
1 void trusty_app_init(void) 2 { 3 trusty_app_image_start = (char *)&__trusty_app_start; 4 trusty_app_image_end = (char *)&__trusty_app_end; 5 … … 6 for (i = 0, trusty_app = trusty_app_list; i < trusty_app_count; i++, trusty_app++) { 7 … … 8 } 9
Trusty應用通過端口(ports)和通道(channels)與Android中相應的應用程序進行交互。端口用於Trusty OS中的應用向Android暴露自己的服務。也就是說每個服務都有自己的端口ID,當Android中的某個應用需要與Trusty OS中相應的服務交互的時候,就作為客戶端向相應的端口ID發起請求。Trusty OS中的服務接受請求后會與客戶端協商建立兩個通道,用於全雙工通信。
轉載請注明出處: