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(默认配置)。为了不影响物理机的网络,搭建网络环境如下:

Trustzone安全内核Open Virtualization SierraTEE向Xilinx ZC702移植手册_第1张图片

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和一些安全客户端应用程序。下图是开发板上列出的客户端应用程序,和一些运行结果。

Trustzone安全内核Open Virtualization SierraTEE向Xilinx ZC702移植手册_第2张图片     

Trustzone安全内核Open Virtualization SierraTEE向Xilinx ZC702移植手册_第3张图片

Trustzone安全内核Open Virtualization SierraTEE向Xilinx ZC702移植手册_第4张图片

 

存在问题

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指令调用互相转换。

 Trustzone安全内核Open Virtualization SierraTEE向Xilinx ZC702移植手册_第5张图片

开机后的引导过程

通过阅读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的流程。

 Trustzone安全内核Open Virtualization SierraTEE向Xilinx ZC702移植手册_第6张图片Trustzone安全内核Open Virtualization SierraTEE向Xilinx ZC702移植手册_第7张图片

 

注意:此图与实际有差别,但是流程差不多

 

中断向量表: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指令调度安全世界中的任务。

 

Trustzone安全内核Open Virtualization SierraTEE向Xilinx ZC702移植手册_第8张图片Trustzone安全内核Open Virtualization SierraTEE向Xilinx ZC702移植手册_第9张图片

 

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 ,, )

这是一个循环语句,用于将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

解释:将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

 

 

 

 

 

你可能感兴趣的:(Trustzone)