本文档描述将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 $@`/ > $@