Trustzone安全內核Open Virtualization SierraTEE向Xilinx ZC702移植手冊

本文檔描述將OV項目向ZC702開發板移植的過程。

 

第一章.       環境搭建

ZC702開發板可以通過SD卡和網絡兩種方式啓動,但是從SD卡啓動方式在每次OS內核變化時就需要替換SD卡中的舊OS內核,需要重複插拔SD卡,太過繁瑣,不如從網絡方式啓動方便。這一章便描述如何建立ZC702的網絡啓動方式,方便以後修改OV內核。

網絡環境搭建

ZC702板和開發主機Host按照下圖進行連接。Host機是在VMware中安裝的Debian 6,網卡配置爲橋接方式,IP爲10.10.70.101。嵌入式開發板分配IP爲10.10.70.102(默認配置)。爲了不影響物理機的網絡,搭建網絡環境如下:


Host tftp nfs環境建立

所謂網絡啓動方式就是ZC702上的uboot通過tftp協議從Host上獲取內核鏡像。這種方式需要uboot支持tftp協議,Host支持tftp服務和nfs服務。ZC702的原始uboot就支持tftp,不需要改動,只需要在Host上安裝tftp和nfs即可。

 

Host tftp安裝

首先安裝tftp:apt-get install tftp tftpd xinetd

然後配置tftp:在/etc/xinet.d/目錄下創建文件tftp,並添加如下內容。

service tftp

{

      socket_type     = dgram

      protocol        = udp

       wait           = yes

       user           = zhao

      server          =/usr/sbin/in.tftpd

      server_args     = /home/zhao/xilinx/tftp   #替換爲你的目錄,注意權限

      disable         = no

       per_source     = 11

      cps             =1002

      flags           = IPv4

}

重啓tftp:

#/etc/init.d/xinetdreload

#/etc/init.d/xinetdrestart

測試tftp:首先在tftp的共享文件夾下創建一個文件test,添加內容abcdefgh;然後在另外一個目錄下運行

$tftp 10.10.70.101

tftp>get test

Received 10 bytes in 0.0 seconds

Host nfs安裝

參考自:http://billforums.station51.net/viewtopic.php?f=1&t=17

首先安裝nfs:apt-get install nfs-kernel-server portmap nfs-common

然後配置Host上的nfs服務。編輯/etc/exports文件,添加根文件系統路徑(rootpath):

/home/zhao/xilinx/tftp/fs        10.10.70.102(rw,fsid=0,insecure,no_subtree_check,async,no_root_squash)

/home/zhao/xilinx/tftp         10.10.70.102(rw,fsid=0,insecure,no_subtree_check,async)

網絡方式啓動OV

環境搭建好以後,就可以通過網絡方式啓動OV了。首先將編譯好的SierraTEE.bin複製到tftp服務器的目錄/home/zhao/xilinx/tftp/,然後在ZC702啓動後在uboot上運行如下命令:

tftp 0x3c000000SierraTEE.bin; go 0x3c000000;

這樣就將SierraTEE.bin啓動了。

SD卡啓動OV並且使用SD做根設備

OV自帶的N world Linux內核引導器將內核啓動的命令行參數寫在 裏,啓動方式爲虛擬盤作根文件系統。若改爲SD卡分區作根文件系統,需要以下改動

1.     給SD卡重新分區(之前需備份SD內容):使用fdisk命令給SD卡分區,分爲fat32(500M)+ext2(其餘SD卡空間)兩個分區,fat32爲第一個分區,供boot_rom讀取boot.in(fsbl+uboot)和uboot讀取sierratee.bin;第二個ext2分區作根文件系統,將OV提供的根文件系統內容複製到該分區。使用如下命令:

fdisk /dev/mmcblk0;

n(新建分區)

p(主分區)

1(主分區號1)

11(起始位置11柱面)

+500M(分區大小)

 

第二個分區類似建立

 

mkdosfs /dev/mmcblk0p1 (第一個分區格式化爲fat32類型)

把boot.bin和sierratee.bin等啓動必須得文件(sd卡中原來的所有內容)拷貝到第一個分區

mkfs.ext2 /dev/mmcblk0p2(第二個分區格式化爲ext2類型)

將虛擬盤中內容拷貝至該分區

2.     由於引導器的內核啓動參數已經寫死在程序裏,無法修改,因此對內核編譯配置項進行修改,設置自己的啓動參數,並忽略引導器傳遞過來的啓動參數,將根文件系統從虛擬盤改成SD卡的ext2分區。

修改Xilinx_dir/kernel/first/linux-xlnx/arch/arm/configs/zynq_base_trd_defconfig配置文件的CONFIG_CMDLINE配置項:

CONFIG_CMDLINE="console=tty0 console=ttyPS0,115200 noinitrd root=179:2rw ip=192.168.0.91:::255.255.255.0:ZC702:eth0 earlyprintk mem=512Mmemmap=128M$0x30000000 vmalloc=256M"

 

設置CONFIG_CMDLINE_FORCE項,使內核強制使用上面的啓動參數,忽略引導器傳遞的闡述

CONFIG_CMDLINE_FORCE=y

 

重新執行Xilinx_dir下的build.sh,生成新的sierratee.bin,放入sd卡的fat32分區中

 

啓動開發板,走到uboot shell界面下,運行下面的命令

mmcinfo

fatload mmc 0 0x3c000000 sierratee.bin

go 0x3c000000

之後ov啓動,至N world的LINUX系統啓動,此時的文件系統位於sd卡的ext2分區中,文件讀寫均會實際保存到sd卡中。

第二章.       編譯OV項目

編譯方式主要依據OV代碼中的README文檔。但是此README寫的太過簡略,甚至有的地方還有錯誤。本章主要簡略描述OV的編譯流程以及與README文檔編譯過程不同的步驟。本此編譯正常世界只有一個Guest OS的情況。

 

Sierraware公司提供的適用於ZC702板的OV源代碼編譯過程主要受build.sh腳本控制,這個腳本主要有如下幾個功能:

1.      編譯Linux內核,編譯過程中打上TrustZone補丁。

2.      將1的內核加上一個引導器,使其可以被TEE的安全OS引導。

3.      編譯TEE,即Monitor軟件和TEE OS,然後利用2編譯的Linux內核鏡像共同生成能夠在ZC702上運行的二進制代碼。此外,這一步還生成了一些可信應用客戶端,用於測試TEE OS的安全服務。

4.      生成根文件系統,生成過程會將3生成的可信應用客戶端放入根文件系統中的/root/otz/目錄下。

5.      利用4新生成的根文件系統重新生成Linux內核。

6.      利用4新生成的根文件系統重新生成運行在ZC702上的二進制文件。

 

除了按照README文檔修改以外,還需要對trustzone/tzone_sdk目錄下的Makefile做如下修改:

1. 修改根文件系統目錄,指向OV提供的ZC702專用根文件系統:

ROOT_FILE_SYSTEM_IMAGE:= /home/zhao/xilinx/Xilinx_dir/filesystem/first/ramdisk8M.image.gz

#ROOT_FILE_SYSTEM_IMAGE:= $(SDK_PATH)/../otz_linux/armv5t_min_EB_V6_V7.image

 

2. 修改板子參數:

#export BOARD:= VE

export BOARD:= zynq7

export ARM_CPU:=CORTEX_A9

#export ARM_CPU:=CORTEX_A15

 

完成上述修改就可以運行buid.sh生成運行在板子上的二進制代碼了。所有生成的二進制代碼都放在trustzone/tzone_sdk/bin目錄下,主要包括SierraTEE.bin和一些安全客戶端應用程序。下圖是開發板上列出的客戶端應用程序,和一些運行結果。

     

 

存在問題

OV生成了很多個客戶端,可以在cp_bin1.sh中將其他的客戶端app放入根文件系統。但是在普通世界OS中調用這些客戶端都沒有任何反應。應該是此OV版本的TEE OS沒有運行安全服務。

cp -f$TRUSTZONE_DIR/tzone_sdk/bin/otz_test_app.elf $TMP_MNT1/mnt/root/otz/

cp -f$TRUSTZONE_DIR/tzone_sdk/bin/otz_user_app.elf $TMP_MNT1/mnt/root/otz/

cp -f$TRUSTZONE_DIR/tzone_sdk/bin/otz_virtual_keyboard.elf $TMP_MNT1/mnt/root/otz/

cp -f$TRUSTZONE_DIR/tzone_sdk/bin/otz_play_media.elf $TMP_MNT1/mnt/root/otz/

cp -f$TRUSTZONE_DIR/tzone_sdk/otz_api/build/libotzapi.so    $TMP_MNT1/mnt/lib/

第三章.       源碼分析

編譯文件

build.sh負責總的調度,包括內核、TEE OS以及根文件系統的生成。在此主要分析TEE OS部分。buish.sh與trustzone中Makefile的設置使得trustzone目錄中本質執行如下MAKE命令:

       $(MAKE) -C $(SDK_PATH)/sierratee

       sudo rm -rf sierratee/mmc_fs

 

boot:

       $(MAKE) -C$(SDK_PATH)/sierratee boot

 

ENABLE_LIBCRYPT:是否產生openssl

 

TrustZone執行流程

此次編譯沒有讓安全內核運行在單獨的一個物理內核上,安全世界與正常世界共同使用CPU。下面是OV系統的啓動流程:

CPU:CPU.S進行一些初始化操作,然後跳轉到C語言的Secure_main函數。

|

Secure_main(Main_Secure.C):這個函數可以看作是安全內核的入口,首先進行一些系統初始化工作,然後將libc.o庫以及用戶的安全服務()映射到安全世界的內存空間(技術細節有待學習):

       /*Load 'C' library to memory */

       load_libc_to_memory();

       load_user_app_to_memory();

然後通過sa_create_entry_point創建dispatcher(OTZ_SVC_GLOBAL)和linux服務(OTZ_SVC_LINUX,不知道這個服務是幹什麼的,以後學習),成爲S世界第一次運行的服務。linux_task會調用invoke_ns_kernel將CPU切換到N世界。Invoke_ns_kernel函數將寄存器r0設置爲INVOKE_NON_SECURE_KERNEL,然後調用SMC指令進入Monitor模式。Monitor模式執行下面的宏定義代碼,然後跳轉到正常世界。

.macro mon_switchto_nsworld_ctx

       GET_CORE_CONTEXTs_sys_current

       bl    save_context

       GET_CORE_CONTEXTns_sys_current

       bl    restore_context

       @clear local monitor

       @-------------------

       clrex                                  

.endm

|

之後N、S世界通過SMC指令調用互相轉換。

 

開機後的引導過程

通過閱讀ug585-Zynq-7000-TRM.pdf和ug821-zynq-7000-swdev.pdf兩篇技術文檔,可知開發板加電開機之後至安全內核啓動共分三部分:boot_rom,第一階段引導程序fsbl,第二階段引導程序(uboot)

1.首先執行片上rom中的boot_rom引導程序,引導模式由板子上的strapping

Pins決定,引導模式包括Quad-SPI, SD card, NANDFlash,NOR Flash,JTAG.四種,決定boot_rom從哪種設備上加載用戶自定義的引導程序。

2.boot_rom根據boot_mode從相應設備上加載boot_image,boot_image包含第一,第二階段引導程序(fsbl和uboot)。boot_image必須包含一個滿足特定格式,能夠被boot_rom識別的boot header。根據說明文檔,廠商提供了一個boot_image的生成工具boot_gen和一個fsbl模板程序的源代碼,並已經燒寫到板子的flash中,具體的boot_image生成過程和boot_image的文件格式請參考上面兩篇說明文檔,由於flash中已存在廠商提供的boot_image,不用自己生成,細節不再深究。Boot_rom根據boot_header,首先將fsbl加載至片上ram(OCM)中,並將控制權移交給fsbl

3.fsbl加載第二階段引導程序至ddr內存中,廠商提供的boot_image中使用的第二階段引導程序是uboot,由於boot_image已經燒寫進flash中,因此我們開機後會直接走到uboot界面,之後在uboot下啓動OV。

libc庫編譯過程

OV的編譯工具提供了一個標準C庫的靜態庫libc.a(還有數學庫libm.a,以及一個不知道幹什麼用的libg.a庫)。OV利用此庫還有自身編寫的一些底層C函數爲運行在安全世界中的安全服務提供了C庫的支持,極大的方便了開發者開發自己的安全服務。下面描述OV對libc的編譯過程,可以借用他們的方法編譯其他的函數庫或者密碼學庫。

 

下面的Makefile語句就是編譯libc庫的基本過程,大體可以分爲如下幾個步驟:

1.     統計OV自身提供的libc庫二進制文件(.o文件)。其實這些文件最初已經在src/lib/usr, kernel和common目錄中的objects.mk文件統計了,下面的Makefile語句只是將這些二進制文件統計到變量libc-objs-y中。libc-objs-y記錄了需要編譯的二進制libc文件。

libc-objs-y = $(foreachobj,$(lib-common-objs-y),$(libc_build_dir)/lib/common/$(obj))

libc-objs-y += $(foreachobj,$(lib-user-objs-y),$(libc_build_dir)/lib/user/$(obj))

libc-objs-y +=$(foreach  obj, $(ulib-cpu-objs-y), $(libc_build_dir)/arch/arm/$(ARCH_DIR)/$(obj))

2.     編譯libc-objs-y記錄的二進制文件。

a)      首先爲每個.o文件生成.deps文件,即.o文件所依賴的源文件(包括.c和.s文件),可能是爲了調試使用。下面Makefile語句的意思就是爲$(src_dir)中的每個.S和.c文件在相應的$(libc_build_dir)目錄中產生.dep文件。$@以及$<等自動化變量的解釋參考附錄A。

$(libc_build_dir)/%.dep:$(src_dir)/%.S

  @echo"gen libc-dep file"

  $(V)mkdir -p `dirname $@`

  $(if$(V), @echo " (as-dep)    $(subst$(libc_build_dir)/,,$@)")

  $(V)echo-n `dirname $@`/ > $@

  $(as)$(libc_asflags) -I`dirname $<` -MM $< >> $@

 

$(libc_build_dir)/%.dep:$(src_dir)/%.c

  $(V)mkdir-p `dirname $@`

  $(if $(V), @echo " (cc-dep)    $(subst $(libc_build_dir)/,,$@)")

  $(V)echo -n `dirname $@`/ > $@

  $(V)$(cc) $(libc_cflags) -I`dirname $<` -MM$< >> $@

b)      利用src目錄下的源文件生成.o文件。將$(src_dir)中的.S和.c源文件編譯成.o文件,放到$(libc_build_dir)對應的目錄下。

$(libc_build_dir)/%.o:$(src_dir)/%.S

    $(V)mkdir-p `dirname $@`

    $(if$(V), @echo " (as)        $(subst$(libc_build_dir)/,,$@)")

    $(V)$(as)$(libc_asflags) -I`dirname $<` -c $< -o $@

 

$(libc_build_dir)/%.o:$(src_dir)/%.c

    $(V)mkdir-p `dirname $@`

    $(if $(V), @echo " (cc)        $(subst $(libc_build_dir)/,,$@)")

    $(V)$(cc) $(libc_cflags)-I`dirname $<` -c $< -o $@     

3.     利用libc.a以及上面編譯好的.o文件重新生成libc.o庫文件。下面語句首先利用Linux的ar –x命令將原libc.a靜態庫解壓,得到生成libc.a的所有.o文件,並刪除lib_a-syscalls.o文件;然後利用ar命令將剩下的.o文件重新打包生成newlibc.a庫文件;最後利用Linux鏈接器ld將步驟2生成的.o文件(libc-objs-y)和newlibc.a庫文件打包生成libc.o文件。libc.o文件是可以加載到安全內核的二進制文件。

@mkdir -p $(libc_build_dir)/tmp

    cp $(newlib_lib_dir)/libc.a$(libc_build_dir)/tmp/libc.a

    cd $(libc_build_dir)/tmp/; $(ar)x libc.a; //提取出libc.a中的.o文件

    if [ -f${libc_build_dir}/tmp/lib_a-syscalls.o ]; then \

    rm$(libc_build_dir)/tmp/lib_a-syscalls.o; \

    fi

cd $(libc_build_dir)/tmp/; $(ar)x libm.a

    cd $(libc_build_dir)/tmp/; $(ar)$(arflags) newlibc.a *.o //重新生成newlibc.a文件,刪掉了lib_a-syscalls.o

    $(lib_ld)  -o $(libc_build_dir)/libc.o$(libc-objs-y)  --whole-archive  $(libc_build_dir)/tmp/newlibc.a--no-whole-archive $(newlib_libgcc_file)  $(lib_ldflags)

    $(sstrip) --strip-debug$(libc_build_dir)/libc.o

Openssl編譯過程

Ov本身沒有提供openssl庫源文件 只提供了一個安全內核的openssl補丁,因此需要將openssl相應的tar.gz文件放到makefile中指定的路徑中。

編譯過程分爲兩個部分:

一.   安裝libssl.a和libcrypto.a庫:

下載openssl-1.0.1c.tar.gz安裝包,放至tzonesdk/../package/storage目錄中在package目錄中 執行make命令 完成兩個靜態庫的安裝

 

二.Ov編譯openssl後生成的庫名爲crypto.o,總的編譯命令位於sierratee/Makefile.in文件中:

 

ifeq ($(CONFIG_CRYPTO), y)

ifeq ($(CONFIG_SW_ELF_LOADER_SUPPORT),y)

modules_crypto: $(device-file)$(module-crypto-objs-y)

       $(lib_ld)$(app_ldflags) -o $(modules_build_dir)/crypto.o $(module-crypto-objs-y)$(crypto_libs)

       $(sstrip)--strip-debug $(modules_build_dir)/crypto.o

      

else

modules_crypto: $(device-file)

1.因此編譯之前需要將$(CONFIG_CRYPTO) $(CONFIG_SW_ELF_LOADER_SUPPORT兩個配置項打開 編譯後纔會生成crypto.o庫,package目錄的config中config_crypto設置。

2.此編譯目標的依賴文件在變量$(module-crypto-objs-y)中定義,$(module-crypto-objs-y)的定義如下:

sierratee/src/apps/objects.mk:

modules-crypto-objs-y+=crypto_task.o

sierratee/src/apps/objects.mk:

modules-crypto-objs-y+=crypto_tests.o

sierratee/src/lib/user/objects.mk:

lib-crypto-objs-y+=otz_tee_crypto_api.o

 

 

sierratee/Makefile.in:

ifeq ($(CONFIG_CRYPTO), y)

module-crypto-objs-y=$(foreachobj,$(modules-crypto-objs-y),$(modules_build_dir)/apps/$(obj))

module-crypto-objs-y+=$(foreachobj,$(lib-crypto-objs-y),$(modules_build_dir)/lib/user/$(obj))

endif

 

可知依賴文件爲crypto_task.ocrypto_tests.o otz_tee_crypto_api.o三個文件

3.$(crypto_libs)變量定義了目標文件依賴的庫文件,在sierratee/Makefile.in中定義如下

       crypto_libs= -L$(crypto_dir) -lcrypto

crypto_libs+=-L$(crypto_dir) –lssl

可知目標文件依賴與libcrypto.a   libssl.a兩個靜態庫,這兩個庫由安裝openssl-1.0.1c.tar.gz包後獲得。安裝過程如第一步所示。

在tzonesdk目錄下執行make 即可編譯得到crypto.o

 

Makefile.in:crypto_libs = -L$(crypto_dir)-lcrypto

Makefile.in:crypto_libs+= -L$(crypto_dir)–lssl

 

Makefile.in:extra_app_ldflags+=$(crypto_libs)

Makefile.in:extra_app_ldflags+=$(crypto_libs)

Makefile.in:extra_app_ldflags+=$(ffmpeg_libs)$(sdl_libs) $(math_lib)

Makefile.in:          $(lib_ld) $(app_ldflags) -o $(app_build_dir)/user_app.o$(user-apps-objs-y) $(extra_app_ldflags) ;

 

lib_ld = $(CROSS_COMPILE)ld    (包括libc)

 

Makefile.in:LD_SCRIPT     = $(drivers_dir)/linker.ld.S

 

Makefile.boot:LD_SCRIPT  = $(drivers_dir)/linker_boot.ld

Makefile.boot:ldflags+=-Wl,-T$(LD_SCRIPT)-nostartfiles

Makefile.in:LD_SCRIPT     = $(drivers_dir)/linker.ld.S

 

Makefile.in:linker.ld: $(LD_SCRIPT)

 

linker.ld: $(LD_SCRIPT):

$(cc) $(kern_cflags)-E -P -C -o $@ $<

 

ldflags+=-Wl,-T$(cur_dir)/linker.ld

lib_ld =$(CROSS_COMPILE)ld

 

$(build_dir)/SierraTEE.elf: sw_kernelKERNELS

       $(V)mkdir -p `dirname $@`

       $(if $(V), @echo " (ld)       $(subst $(build_dir)/,,$@)")

       $(V)$(ld)  $(ldflags)  -o $@

       $(V)cp -f $@ $(bin_dir)

       $(V)$(sstrip) -s $(bin_dir)/SierraTEE.elf

       $(V)rm -f $(KERNEL) $(KERNEL_2) $(INITRD)

 

 

安全內核服務架構

重要的數據結構:

 

struct sw_global global_val;

 

/**

 *@brief

 Secure API configuration details for task

 */

typedef struct sa_config_t

 

創建任務

以創建dispatcher任務爲例。

1. 初始化任務。dispatch_task_init函數初始化安全API配置結構體sa_config_t,主要初始化:

       a)服務信息:serviceid、service name。目前OV支持的服務類型見附錄C。

       b)stack和heap大小。一般stack4K、heap 64K,heap至少爲256字節。注意,此時並沒有申請空間,只是記錄堆棧的大小。

c) 初始化服務運行態、羣組、服務是否允許多個實例以及此次申請的任務號。服務運行態有用戶態和內核態兩種狀態,dispatcher爲內核態。dispatcher服務屬於一般組羣。值得注意的是,此次創建的任務號需要從全局變量global_val的任務池中申請。global_val有一個任務池global_val.task_id_pool,存儲了所有任務的任務號。

       d)填寫應用對此服務的訪問控制列表ACL。可以看出OV對服務進行了簡單的訪問控制。

       e)填寫此任務的服務入口,即服務函數地址。

       f)爲此服務申請一些私有數據,不同的服務有不同的定義。

2. 創建任務。

       a)檢查系統設定是否允許創建此任務。主要檢查訪問控制列表以及服務是否多個實例,如果檢查失敗,返回錯誤。

       b)爲此次任務申請heap空間。注意dispatcher這個任務的heap用的是全局變量global_val中的共享heap,不需要申請。

       c)初始化任務,即結構體structsw_task。包括task id、service UUID、服務入口、運行模式、guest no、任務名稱(即服務字符串)。

      

       d)爲此任務申請本地存儲空間tls/*!task local storage */,並將此空間映射到安全內存區域或任務指定的內存表中。然後進行將tls的private_date、process、堆棧大小、task id進行初始化。

       ((structsw_tls*)new_task->tls)->private_data = psa_config->data;

       ((structsw_tls*)new_task->tls)->process = psa_config->process;

       ((structsw_tls*)new_task->tls)->heap_size = psa_config->heap_size;

       ((structsw_tls*)new_task->tls)->min_alloc_size = psa_config->min_alloc_size;

       ((structsw_tls*)new_task->tls)->task_id = new_task->task_id;                  

       e)根據任務的運行模式(用戶空間、內核空間)申請相應的棧空間,並設置好任務的棧指針。

       new_task->task_sp_size= psa_config->stack_size;

       if(new_task->mode== TASK_USER_MODE) {

              new_task->task_sp= alloc_user_stack(new_task);

       }

       else{

              new_task->task_sp= alloc_kernel_stack(psa_config->stack_size);

       }

       f)根據任務的運行模式(用戶空間、內核空間)申請相應的堆空間,並初始化tls結構中的堆空間:目前heap使用數爲0,起始地址指向堆開始地址。

       heap_info->num_blocks_alloc= 0;

       heap_info->heap_vir_addr= heap_start;

       g)初始化任務鏈表中的各個鏈,然後用head指向自身,最後用head作爲索引添加到全局變量global_val中的全局任務列表裏。以後全局任務列表就能夠索引到本任務。

       link_init(&new_task->head);

       link_init(&new_task->ready_head);

       link_init(&new_task->wait_head);

       link_init(&new_task->file_dev_list);

       link_init(&new_task->task_wait_list);

       h)將任務狀態設置爲受阻:TASK_STATE_SUSPEND,表明此狀態還沒有準備運行。

3.初始化任務。安全內核的任務切換是直接從CPU寄存器進行切換的,就像是N、S世界直接的切換。爲了保存任務切換時任務的狀態,每個任務都有一個寄存器結構體,並且每個任務都有自己的堆棧,供任務運行時使用。初始化任務就是初始化寄存器結構體struct sw_task_cpu_regs 和堆棧的過程。

       a)將寄存器結構體r0指向任務的私有存儲空間tls,初始化其他通用寄存器r1-r12爲0, .lr寄存器設置爲0,pc寄存器設置爲服務的入口地址(即服務的函數入口地址)。

       b)設置棧指針sp爲棧頂。

       c)設置CPU狀態寄存器spsr。

       d) 將此任務添加到全局變量global_val中的準備運行任務列表(global_val.ready_to_run_list)。

 

       至此,任務已經加入到全局任務中的待運行列表,可以執行了。

Monitor模式切換至NS世界

 

在inttzhyp_init(void)

{

       interror;

      

       ns_sys_current= (struct system_context *)global_val.tzhyp_val.ns_world;

       s_sys_current= (struct system_context *)global_val.tzhyp_val.s_world;

。。。。

。。。。

}

 

global_val.tzhyp_val.ns_world爲max_cores*guests_no個struct system_context組成的數組如下: ns/s_sys_current指向數組頭

void global_init(void)

{

       global_val.tzhyp_val.ns_world= sw_malloc(MAX_CORES * GUESTS_NO

                     *sizeof(struct system_context));

       global_val.tzhyp_val.s_world= sw_malloc(MAX_CORES

                     *sizeof(struct system_context));

              。。。

              。。。

}

 

切換過程爲s_sys_current和n s_sys_current中cpu狀態的切換

 

n s_sys_current狀態的初始化

void mon_nscpu_context_init()

{

。。。。

。。。。

#ifdef LINUX_ATAG_BOOT_EN

       core_ctxt->r0 = 0;

       core_ctxt->r1 = LINUX_MACHINE_ID;

       core_ctxt->r2 = (sw_uint)NORMAL_WORLD_RAM_START+ 0x100;

#endif

 

       core_ctxt->spsr_mon= CPSR_RESET_VAL;

 

#ifdef OTZONE_ASYNC_NOTIFY_SUPPORT

       primary_ns_world->notify_data= NULL;

#endif

 

 

       /*

        * Save cp15 reset state

        */

       tzhyp_sysregs_save(cp15_ctxt);

}

 

.macro GET_CPU_ID rt

       mrc     p15,0, \rt, c0, c0, 5   @ Read CPU IDregister

       and     \rt, \rt, #0x03           @ Mask off, leaving the CPU ID field

.endm

extern struct system_context*ns_sys_current;

extern struct system_context*s_sys_current;

 

struct system_context {

       /*CPU context */

       structcore_context sysctxt_core;

       structcp15_context sysctxt_cp15;

#ifdef CONFIG_NEON_SUPPORT

       structvfp_context sysctxt_vfp;

#endif

       /*Devices */ generic interrupt context

       structgic_context sysctxt_gic;  

 

       sw_uintguest_no;

 

#ifdef OTZONE_ASYNC_NOTIFY_SUPPORT

       /*!Shared memory for notification */

       structotzc_notify_data *notify_data;

       sw_uintpending_notify;

#endif

      

       /*

        * to make the size a power of 2, so thatmultiplication can be acheived

        * by logical shift

        */

#ifndef CONFIG_NEON_SUPPORT 

       sw_uintpad[8]; 

#endif

} __attribute__ ((aligned(CACHELINE_SIZE)));

 

struct core_context {

       sw_uintr0;

       sw_uintr1;

       sw_uintr2;

       sw_uintr3;

       sw_uintr4;

       sw_uintr5;

       sw_uintr6;

       sw_uintr7;

       sw_uintr8;

       sw_uintr9;

       sw_uintr10;

       sw_uintr11;

       sw_uintr12;

       sw_uintspsr_mon; ///monitor模式沒有sp指針 所以恢復過程中棧指針不變

       sw_uintlr_mon;

       sw_uintspsr_svc;

       sw_uintr13_svc;

       sw_uintlr_svc;

       sw_uintr13_sys;

       sw_uintlr_sys;

       sw_uintspsr_abt;

       sw_uintr13_abt;

       sw_uintlr_abt;

       sw_uintspsr_undef;

       sw_uintr13_undef;

       sw_uintlr_undef;

       sw_uintspsr_irq;

       sw_uintr13_irq;

       sw_uintlr_irq;            

};

struct cp15_context {

       sw_uintc0_CSSELR;      /* Cache Size SelectionRegister */

       sw_uintc1_SCTLR;       /* System ControlRegister */

       sw_uintc1_ACTLR;       /* Auxilliary ControlRegister */

       sw_uintc2_TTBR0;       /* Translation Table BaseRegister 0 */

       sw_uintc2_TTBR1;       /* Translation Table BaseRegister 1 */

       sw_uintc2_TTBCR;       /* Translation Table BaseRegister Control */

       sw_uintc3_DACR;        /* Domain Access ControlRegister */

       sw_uintc5_DFSR;        /* Data Fault StatusRegister */

       sw_uintc5_IFSR;        /* Instruction FaultStatus Register */

       sw_uintc6_DFAR;        /* Data Fault AddressRegister */

       sw_uintc6_IFAR;        /* Instruction FaultAddress Register */

       sw_uintc7_PAR;         /* Physical AddressRegister */

       sw_uintc10_PRRR;       /* PRRR */

       sw_uintc10_NMRR;       /* NMRR */

       sw_uintc12_VBAR;       /* VBAR register */

       sw_uintc13_FCSEIDR;    /* FCSE PID Register */

       sw_uintc13_CONTEXTIDR; /* Context ID Register */

       sw_uintc13_TPIDRURW;   /* User Read/Write Threadand Process ID */

       sw_uintc13_TPIDRURO;   /* User Read-only Threadand Process ID */

       sw_uintc13_TPIDRPRW;   /* Privileged only Threadand Process ID */

};

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

call_non_secure_kernel:

#ifndef CONFIG_SW_DEDICATED_TEE

       push       {r4, lr} /* the corresponding pops happens from

                                                                save_context */

       push       {r0 - r3}

      

#ifndef CONFIG_BOOT_SVISOR

       b   mon_switchto_nsworld

#else

       mon_switchto_nsworld_ctx

       b   switch_to_hyp_mode

#endif /* CONFIG_BOOT_SVISOR */

 

#else  /* CONFIG_SW_DEDICATED_TEE */

       push       {lr}

       mov r0, #0

       bl    start_secondary_linux

       pop  {lr}

       movs      pc, lr

#endif

 

 

.func mon_switchto_nsworld

mon_switchto_nsworld:

       mon_switchto_nsworld_ctx

 

///此時lr已恢復成ns世界的lr

 

       push{r0}

       scr_nsbit_setr0    

      

       pop  {r0}

 

       dsb

       isb

 

       movs      pc, lr 

//到ns世界相應指令處執行(即 struct system_context*ns_sys_current中的monitor模式lr)

.endfunc

 

.macro mon_switchto_nsworld_ctx

       GET_CORE_CONTEXTs_sys_current

       bl    save_context

    ////執行完bl save_context後 s_sys_current中的monitor模式lr已保存爲smc指令之後的那條指令

       GET_CORE_CONTEXTns_sys_current

       bl    restore_context

       @clear local monitor

       @-------------------

       clrex                                  

.endm

 

任務切換

安全內核通過中斷swi進行任務的調度,執行全局任務列表中那些待運行的任務。每次執行一次swi中斷,CPU處理一個任務列表中的任務。swi中斷執行後,CPU跳轉到安全中斷向量表中的swi中斷處理函數,此函數執行如下步驟:

1. 保存當前運行的任務的CPU上下文,包括spsr、r0-r12、lr寄存器(此時lr指向被中斷任務正在運行的PC寄存器值)。這些上下文信息保存在SVC模式下的棧頂,形成一個swi_temp_regs結構體。如果此時不保存,那麼這個任務的CPU上下文就會丟失,無法恢復此任務。

2. 將處理器模式轉換爲SYS模式,然後進入C語言的任務上下文切換工作。將處理器轉換爲SYS模式的目的是防止任務上下文切換影響保存在SVC模式棧頂CPU上下文(即spsr、r0-r12、lr寄存器)。

       a)從ready to runlist獲取一個將要運行的任務,然後將當前正在運行的任務放入ready to run list。

       b)將即將運行的任務狀態修改爲TASK_STATE_RUNNING。

3. 任務之間的上下文切換:

       a)將被中斷任務的CPU上下文(在SYS模式下通過全局變量temp_swi_regs獲取對SVC模式中棧的訪問地址)存入任務自己的私有存儲空間的sw_task_cpu_regs結構體(此結構體除了spsr、r0-r12、lr,還有sp寄存器,不過sp應該沒有用):r0-r14、spsr、lr,注意lr賦值給任務CPU上下文存儲器中的pc寄存器,因爲lr目前存儲的就是任務中斷時下一條要運行的指令地址。

       b)即將運行的任務將自己的CPU上下文信息:r0-r12、spsr、pc替換SVC模式下棧頂的各個寄存器,返回到swi中斷處理函數,注意的是此時CPU已經被修改爲SYS模式,所以需要通過MSR指令手動修改模式:

         msr   CPSR_c, #(ARCH_SVC_MODE | IRQ_BIT)      

4. 從SVC模式下的棧頂彈出spsr寄存器,這個寄存器代表了即將運行任務的狀態寄存器,然後msr設置處理器,然後依次彈出棧頂的r0-r12,lc寄存器值分別賦值給r0-r12和pc寄存器。至此,處理器的狀態寄存器和pc寄存器都已經被設置爲新任務的CPU上下文,所以,下一步處理器就直接處理新任務。

 

/**

 * @brief Structureto store register in SWI handler

 */

struct swi_temp_regs {

         /*! spsr */

         sw_uintspsr;

         /*! regs r0- r12 */

         sw_uintregs[13];  

         /*! linkregister */

         sw_uintlr;        

};

 

/**

 * @brief Taskregisters context

 */

struct sw_task_cpu_regs {

         /*!Registers r0 -r12 */

         sw_uintregs[13];

         /*! Stackpointer of the task */

         sw_uint sp;

         /*! Linkregister of the task */

         sw_uint lr;

         /*! SPSR ofthe task */

         sw_uintspsr;

         /*! CurrentPC of the task */

         sw_uint pc;

#ifdef CONFIG_USER_PAGE_TABLE_ISOLATION

         /*! TTBR0of the current task */

         sw_uintttbr;

         /*! ASID ofthe current task */

         sw_uint asid;

#endif

};

 

安全內核的文件系統支持

目前OV的功能尚不完善,對與外設(SD卡)的數據交互支持的功能很少,目前只支持虛擬的文件系統,即數據的讀寫(open,read,write函數)實際上都是在內存中的虛擬文件系統中進行,SD卡的驅動函數,初始化函數都沒有實現,目前不能與SD卡進行數據交互。

在secure_main函數中,掛在文件系統的函數如下,

 

#ifdef CONFIG_FILESYSTEM_SUPPORT

#ifdef CONFIG_MMC_SUPPORT

       fs_ret= mount_file_system((sw_short_int*)read_from_disk());

#else

       fs_ret= mount_file_system((sw_short_int*)get_sw_fs_start());

#endif

       if(fs_ret!= SW_ERROR) {

              sw_printk("filesystem successfully mounted in FAT32 \n");

       }

#endif

CONFIG_FILESYSTEM_SUPPORT配置項開啓安全內核對文件系統的支持,允許內核以文件的形式讀寫數據,#ifdefCONFIG_MMC_SUPPORT配置項允許內核從SD中加載根文件系統,但是跟蹤代碼後發現,SD卡設備的初始化函數如下,

 

board_mmc_init()

{

       Return-1;

}

可見OV並沒有實現此函數,所以從SD卡加載根文件系統一定失敗,雖然有CONFIG_MMC_SUPPORT配置項,但實際上不支持從SD卡加載根文件系統。

如果CONFIG_MMC_SUPPORT配置項關閉,則在編譯安全內核的過程中,根文件系統也會一併編譯進來,所以內核加載完成後,文件系統已經在內存中了,外部變量_SW_FS_START(在linker.d.s中定義)表示其在內存中的地址。目前安全內核只支持fat32文件系統。mount_file_system函數讀取文件系統的引導塊,超級塊等信息,獲取扇區大小,簇大小,根目錄地址等統計信息,以此填充global_val.fs_context變量。

安全內核中的文件操作系統調用open,read,write,close函數,首先通過swi切換至supervisor模式,之後根據系統調用號調用file_open,file_read,file_write,file_close函數,這些函數根據global_val.fs_context中保存的文件系統的統計信息,計算出文件在文件系統中的位置,並讀寫數據。此時文件的讀寫均是在內存中的虛擬文件系統中進行,並沒有調用SD卡驅動程序與SD卡進行數據交互,實際上OV源碼中也並沒有相應的驅動程序。

安全內核實現了部分對SD卡的文件讀寫功能,實現在mmc.c中的mmc_bread,mmc_bwrite函數中。這些函數先填充mmc_cmd結構體,初始化給SD卡發送的讀寫指令,之後調用mmc->send_cmd函數,向SD卡發送讀寫請求。但是跟蹤代碼發現mmc的send_cmd函數並沒有實現。

安全服務調用

在普通世界裏,OV在內核層部署了一個通信代理,可以理解爲安全世界的驅動程序,負責將普通世界的用戶請求發送給安全世界中的安全服務。用戶層程序通過TEE Client API接口與通信代理通信。通信代理在用戶看來是一個硬件設備,以硬件驅動的方式來使用。下面首先描述用戶程序調用TEE Client API的流程。

 

 

注意:此圖與實際有差別,但是流程差不多

 

中斷向量表:monitor.S是monitor模式下的中斷向量表、cpu_start.S是安全世界的中斷向量表、正常世界的中斷向量表由Linux負責。

TEEC_InitializeContext:打開TEE設備。

 

TEEC_OpenSession:發送OTZ_CLIENT_IOCTL_SES_OPEN_REQ命令給TEE代理。

 

1. TEE代理:收到應用層發送的開啓會話請求後,TEE代理組裝SMC命令傳遞給TEE命令中的任務調度器(dispatcher)。儘管TEE代理收到的某個特定任務的服務,但是此次TEE組裝的命令service id爲OTZ_SVC_GLOBAL、service command爲OTZ_GLOBAL_CMD_ID_OPEN_SESSION,而真正的service id被封裝到了命令內容(req_buf_phys),sesssion id被封裝爲返回參數resp_buf_phys。組裝好命令之後,TEE代理利用寄存器r0-r2向S世界傳遞參數,

2. TEE代理調用SMC指令進行NS切換。

    register u32 r0asm("r0") = CALL_TRUSTZONE_API;

    register u32 r1asm("r1") = cmd_addr;

    register u32 r2asm("r2") = OTZ_CMD_TYPE_NS_TO_SECURE;

 

3. Monitor:將r0-r3寄存器存入全局變量params_stack,設置全局變量valid_params_flag=0x1,然後切換CPU上下文,跳轉到安全世界的調度器。

4. Dispatcher:調度器首先從params_stack中提取出TEE代理組裝的SMC命令結構,然後利用SMC命令結構體中真正的service id按照上面的創建任務流程創建任務,創建任務過程會把新創建的任務號作爲session id寫到SMC命令結構體中,最後調用SMC返回到正常世界。

   /* Service entrypoint */

   psa_config->entry_point= (sw_uint)&gp_internal_api_test_task;

   /* Service process */

   psa_config->process =&process_otz_gp_internal_svc;

 

TEEC_InvokeCommand:發送OTZ_CLIENT_IOCTL_SEND_CMD_REQ命令給TEE代理。

1. TEE代理:1) 組裝SMC命令傳遞給TEE,主要包括:service id、command id、session id、輸入數據、輸出數據 ; 2) 然後調用SMC指令跳轉到安全世界。其中service id爲對應的UUID(此處我們以OTZ_SVC_GP_INTERNAL爲例),session id爲上一步TEE傳遞回來的session id。

2. 任務調度器:將r0-r3寄存器中值作爲任務參數傳遞給任務,然後將此任務加入到準備運行的任務列表(ready to run list)。

3. 調度器通過swi指令調度安全世界中的任務。

 

 

TEE Client API測試

關鍵數據結構

/**

 *@brief The TEEC_Context structure is used to contain control information

 *related to the TEE

*/

struct TEEC_Context

{

       /*! Implementation-defined variables */

       /*! Device identifier */

       uint32_t fd;

       /*!Sessions count of the device */

       intsession_count;

       /*!Shared memory counter which got created for this context */

       uint32_tshared_mem_cnt;   

       /*!Shared memory list */

       structlink shared_mem_list;

       /*!Error number from the client driver */

       ints_errno;

};

 

/**

 *@brief The TEEC_Session structure is used to contain control information

 *related to a session between a client and a service.

*/

struct TEEC_Session

{

       /*!Implementation-defined variables */

       /*!Reference count of operations*/

       intoperation_cnt;

       /*!Session id obtained for the  service*/

       int session_id;

       /*! Unique service id */

       int service_id;

       /*! Device context */

       TEEC_Context*device;

       /*!Service error number */

       ints_errno;

};

API定義

TEE Client API規範定義了9個API,分別是:

TEEC_Result TEEC_InitializeContext(

              constchar*   name,

              TEEC_Context*context);

初始化TEE上下文,即開啓TEE設備,包括設置設備標誌符、會話計數器、共享內存計數器和共享內存鏈。

 

 

void TEEC_FinalizeContext(

              TEEC_Context*context);

關閉TEE設備,如果共享內存沒有被釋放掉,發出警告。

 

TEEC_Result TEEC_RegisterSharedMemory(

              TEEC_Context*      context,

              TEEC_SharedMemory*sharedMem);

 

TEEC_Result TEEC_AllocateSharedMemory(

              TEEC_Context*      context,

              TEEC_SharedMemory*sharedMem);

 

void TEEC_ReleaseSharedMemory(

              TEEC_SharedMemory*sharedMem);

 

TEEC_Result TEEC_OpenSession (

              TEEC_Context*    context,

              TEEC_Session*    session,

              const TEEC_UUID* destination,

              uint32_t         connectionMethod,

              constvoid*      connectionData,

              TEEC_Operation*operation,

              uint32_t*        returnOrigin);

指定與某個安全服務協商會話。TEEClient API提供了用戶認證機制,connectionMethod指定認證方式,connectionData指定認證口令。不過OV目前還沒有實現用戶認證機制。

 

void TEEC_CloseSession (

              TEEC_Session*    session);

 

TEEC_Result TEEC_InvokeCommand(

              TEEC_Session*     session,

              uint32_t          commandID,

              TEEC_Operation*   operation,

              uint32_t*         returnOrigin);

利用會話調用安全服務。session爲協商好的安全服務。

 

voidTEEC_RequestCancellation(

              TEEC_Operation*operation);

 

創建Hello測試服務

此次實驗在安全世界中創建一個hello安全服務,然後通過共享內存方式進行傳遞數據。實驗目的是測試打開設備、創建會話、數據共享等基本的TEE Client API。

 

客戶端應用

ns_client_apps目錄:

Objects.mk:添加app10-objs-y=otz_hello_app.o

Makefile:在對應位置添加如下三項

objs10-y=$(foreachobj,$(app9-objs-y),$(build_dir)/$(obj))

final10-objs-y=$(objs10-y)

$(build_dir)/otz_hello _app.elf: $(final10-objs-y)

       $(V)mkdir-p `dirname $@`

       $(if$(V), @echo " (ld)        $(subst$(build_dir)/,,$@)")

       $(V)$(ld)  $(ldflags) $(final10-objs-y) -o $@

       $(V)cp $@ $(bin_dir)

 

安全服務

 

 

 

 

附錄 A. Makefile知識

自動化變量

所謂自動化變量,指的是自動指代規則中的值的變量,主要有如下幾個:

$@ 表示規則中的目標文件集

$< 依賴目標中的第一個目標名字,如果依賴目標是以模式(即"%")定義的,那麼"$<"將是符合模式的一系列的文件集。注意,其是一個一個取出來的。

比如OV中下面的Makefile命令,目標是在$(libc_build_dir)目錄爲$(src_dir)下的每一個.S文件生成.dep文件。目標集合用%.dep代表,依賴集合用%.S代表。$@這個變量可以看作目標集合的索引變量,代表每一個目標集合的值。$<可以看作以來集合的索引變量,代表每一個依賴集合的值。

 

$(libc_build_dir)/%.dep: $(src_dir)/%.S

    @echo"gen libc-dep file"

    $(V)mkdir -p `dirname $@`

    $(if $(V), @echo "(as-dep)    $(subst$(libc_build_dir)/,,$@)")

    $(V)echo -n `dirname $@`/ >$@

    $(as) $(libc_asflags) -I`dirname$<` -MM $< >> $@

foreach函數

語法:$(foreach <var>,<list>, <text>)

這是一個循環語句,用於將list中的每個單詞取出賦值給臨時變量var,然後執行text語句。引用OV中的實例:

objs-y=$(foreachobj,$(cpu-objs-y),$(build_dir)/arch/arm/$(ARCH_DIR)/$(obj))

上述語句的意思是將cpu-objs-y中的每個值取出來形式$(build_dir)/…/$(obj)這種形式的值,然後複製給objs-y,最終結果是將cpu-objs-y的值形成$(build_dir)/…這種形式。

 

變量替換

語法:$(var: a=b)或 ${var: a=b}

將var變量中以a字符串結尾的單詞替換爲b。

OV中的實例:

libc-deps-y=$(libc-objs-y:.o=.dep)

上述語句將libc-objs-y變量中以.o結尾的單詞替換成.dep,然後將替換後的libc-objs-y賦值給libc-deps-y變量。

 

附錄 ARM彙編指令

SWI指令

格式:swi <number>

解釋:將CPU轉換到supervisor模式,number被處理器忽略,但是可以通過如下指令重新獲取:

   ldr   r0, [lr, #-4]

   bic   r0, r0, #0xff000000

 

ldr指令

格式:ldr rd, =value 或 ldr rd, [rs]

用法1:將value的地址賦給rd寄存器;

用法2:取地址rs處的內容賦值給rd寄存器。

 

 

附錄 常用Linux命令

grep

格式:grep –r “matchword” dir

解釋:在dir目錄下遞歸查找包含match word的文件。

 

find

格式:find dir-name match-word

解釋:在當前目錄下尋找文件名爲match-word的文件。-iname表示不區分大小寫。

 

sort

格式:sort –u

解釋:- u 對排序後認爲相同的行只留其中一行。

 

附錄 B. libc編譯腳本

export LIBC_DEPENDENCY_FILE=$(libc_build_dir)/.deps

 

export lib_user_dir=$(src_dir)/lib/user

export lib_kernel_dir=$(src_dir)/lib/kernel

export lib_common_dir=$(src_dir)/lib/common

 

 

lib-user-object-mks=$(shell if [[ -d $(lib_user_dir) ]]; then find$(lib_user_dir) -iname "objects.mk" | sort -r; fi)

lib-kernel-object-mks=$(shell if [[ -d $(lib_kernel_dir) ]]; then find$(lib_kernel_dir) -iname "objects.mk" | sort -r; fi)

lib-common-object-mks=$(shell if [[ -d $(lib_common_dir) ]]; then find$(lib_common_dir) -iname "objects.mk" | sort -r; fi)

 

libc-objs-y = $(foreachobj,$(lib-common-objs-y),$(libc_build_dir)/lib/common/$(obj))

libc-objs-y += $(foreach obj,$(lib-user-objs-y),$(libc_build_dir)/lib/user/$(obj))

libc-objs-y +=$(foreachobj,$(ulib-cpu-objs-y),$(libc_build_dir)/arch/arm/$(ARCH_DIR)/$(obj))

 

libc-deps-y=$(libc-objs-y:.o=.dep)

 

libc_includes=-I$(SDK_PATH)/include

libc_includes+=-I$(src_dir)/lib/common/include

libc_includes+=-I$(src_dir)/lib/user/include

 

libc_cflags=$(cflags) $(TARGET_CCFLAGS)

libc_cflags+=$(libc_includes)

 

 

libc_asflags=$(asflags) $(TARGET_ASMFLAGS)

libc_asflags+=$(libc_includes) -nostdlib

ifeq ($(CONFIG_NEWLIB), y)

libc_asflags +=  -DNEWLIB_SUPPORT

endif

 

//將libc-deps-y導入.deps文件

-include $(LIBC_DEPENDENCY_FILE)

$(LIBC_DEPENDENCY_FILE): $(libc-deps-y)

    @echo "where is libc-depfile"

    $(V)cat$(libc-deps-y) > $(LIBC_DEPENDENCY_FILE)

 

libc: $(device-file) $(libc-objs-y)

    @echo "generating lib"

ifeq ($(CONFIG_NEWLIB), y)

    @mkdir -p $(libc_build_dir)/tmp

    cp $(newlib_lib_dir)/libc.a$(libc_build_dir)/tmp/libc.a

ifeq ($(CONFIG_FFMPEG), y)

    cp $(newlib_lib_dir)/libm.a$(libc_build_dir)/tmp/libm.a 

endif  

    cd $(libc_build_dir)/tmp/; $(ar)x libc.a; //提取出libc.a中的.o文件

    if [ -f${libc_build_dir}/tmp/lib_a-syscalls.o ]; then \

    rm$(libc_build_dir)/tmp/lib_a-syscalls.o; \

    fi

ifeq ($(CONFIG_FFMPEG), y)        

    cd $(libc_build_dir)/tmp/; $(ar)x libm.a

endif  

    cd $(libc_build_dir)/tmp/; $(ar)$(arflags) newlibc.a *.o //重新生成newlibc.a文件,刪掉了lib_a-syscalls.o

               

    $(lib_ld)  -o $(libc_build_dir)/libc.o$(libc-objs-y)  --whole-archive  $(libc_build_dir)/tmp/newlibc.a--no-whole-archive $(newlib_libgcc_file)  $(lib_ldflags)

    $(sstrip) --strip-debug$(libc_build_dir)/libc.o

else

    $(lib_ld) $(lib_ldflags) -o$(libc_build_dir)/libc.o $(libc-objs-y)

    $(sstrip) --strip-debug$(libc_build_dir)/libc.o

endif  

 

//編譯src_dir目錄下的libc文件

$(libc_build_dir)/%.dep: $(src_dir)/%.S

    @echo "gen libc-depfile"

    $(V)mkdir -p `dirname $@`

    $(if $(V), @echo "(as-dep)    $(subst$(libc_build_dir)/,,$@)")

    $(V)echo -n `dirname $@`/ > $@

    $(as) $(libc_asflags) -I`dirname$<` -MM $< >> $@

 

$(libc_build_dir)/%.dep: $(src_dir)/%.c

    $(V)mkdir-p `dirname $@`

    $(if$(V), @echo " (cc-dep)    $(subst$(libc_build_dir)/,,$@)")

    $(V)echo-n `dirname $@`/ > $@

    $(V)$(cc)$(libc_cflags) -I`dirname $<` -MM $< >> $@

 

$(libc_build_dir)/%.o: $(src_dir)/%.S

    $(V)mkdir -p `dirname $@`

    $(if $(V), @echo " (as)        $(subst $(libc_build_dir)/,,$@)")

    $(V)$(as) $(libc_asflags) -I`dirname$<` -c $< -o $@

 

$(libc_build_dir)/%.o: $(src_dir)/%.c

    $(V)mkdir-p `dirname $@`

    $(if$(V), @echo " (cc)        $(subst$(libc_build_dir)/,,$@)")

    $(V)$(cc)$(libc_cflags) -I`dirname $<` -c $< -o $@

 

$(libc_build_dir)/%.o: $(libc_build_dir)/%.S

    $(V)mkdir -p `dirname $@`

    $(if $(V), @echo " (as)        $(subst $(libc_build_dir)/,,$@)")

    $(V)$(as) $(libc_asflags)-I`dirname $<` -c $< -o $@

 

$(libc_build_dir)/%.o: $(libc_build_dir)/%.c

    $(V)mkdir-p `dirname $@`

    $(if$(V), @echo " (cc)        $(subst$(libc_build_dir)/,,$@)")

    $(V)$(cc)$(libc_cflags) -I`dirname $<` -c $< -o $@

 

附錄 C. OV支持的服務種類

enum otz_svc_id {

       OTZ_SVC_INVALID = 0x0,

       OTZ_SVC_GLOBAL,

       OTZ_SVC_ECHO,

       OTZ_SVC_DRM,

       OTZ_SVC_CRYPT,

       OTZ_SVC_MUTEX_TEST,

       OTZ_SVC_VIRTUAL_KEYBOARD,

       OTZ_SVC_KERNEL_INTEGRITY_CHECK,

   OTZ_SVC_LINUX,

       OTZ_SVC_SHELL,

       OTZ_SVC_TEST_SUITE_KERNEL,

       OTZ_SVC_FFMPEG_TEST,

       OTZ_SVC_GP_INTERNAL,

       OTZ_SVC_TEST_SUITE_USER,

       OTZ_SVC_TEST_HEAP,

       OTZ_SVC_INT_CONTXT_SWITCH,

       OTZ_SVC_TEST_SHM

};

 

/home/zhao/xilinx/arm-2010q1/bin/arm-none-linux-gnueabi-

New_lib /home/zhao/xilinx/Xilinx_dir/trustzone/toolchain/sierra_toolchain/bin/arm-none-eabi

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章