树莓派3B底层玩法:OP-TEE、Kernel module、JTAG

当年买树莓派的时候是为了研究ARM底层一些东西,比如Linux内核、JTAG、TEE啥的,却没成想配到的书籍都是教你用一些封装好的python库控制树莓派的GPIO,以控制外部一些简单的零部件、传感器啥的,很是失望,然后就把树莓派扔到一遍,买了讯为的4412开发板。后来随着自己平日研究的深入,还有也是遇到了纽创,发现其实树莓派可以胜任这些底层的学习与研究,只不过相关中文资料虽然有:从底层玩转树莓派,但是很少,也就更不会作为树莓派商家的配套教程了。本文按照前辈wellsleep之前的工作,构建了一个:① Ubuntu18.04和OP-TEE的混合系统 ② 并可以正常编译内核模块 ③ 开启了JTAG 的树莓派3B镜像。对未来的ARM底层研究,搭建好了一个可以调试的真实环境。当然学习ARM底层可以用qemu完成,但还是感觉在真实设备上更踏实。(我一直以为手中的是3B+,经过网友提醒发现是3B…之前坑人了,对不起大家…)

如果各位懒得搭建,可以直接烧录我做好的镜像(8G):ubuntu-18.04.5-optee-linux4.6.3-jtag-arm64-raspi3.img.tar.gz

  • 链接:https://pan.baidu.com/s/1zidfCV6ONKkTbqULSMOBOQ 密码:tnyb
  • 烧录:sudo dd if=./ubuntu-18.04.5-optee-linux4.6.3-jtag-arm64-raspi3.img of=/dev/disk2 bs=1m
  • 账户:ubuntu : admin123
  • 没有自动开dhcp,故树莓派需连接显示器及键盘,手动开启网卡的dhcp:sudo dhclient -i enxb827eb2d9c09

树莓派搭建OP-TEE

网上大部分的资料都是基于OP-TEE的官方文档的搭建方法:

这种搭建方法有个问题,或者说OPTEE官方的编译方法的问题:通过这种方式构建出的镜像,REE的linux的文件系统里基本是空空如也,啥工具都没有,也没有包管理工具,所以也基本没有办法使用这个系统,进行接下来的研究,故我们不采用这种方法。

ubuntu18.04与OP-TEE的混合系统

我们需要使用一个更丰富的文件系统和OPTEE,如:

这个工作交到我的手里时,基础环境以及镜像,前辈:wellsleep 都已经做完了,主要就是以Ubuntu 18.04.5 for Rpi为基础的,被OP-TEE 3.1.0 覆盖安全启动和内核的混合系统,方法参考:

其本质过程就是替换镜像中的启动、内核、库等文件:

#!/bin/sh

# This script will copy the contents to removable storage for
# Raspberry pi

# check lsblk output and make sure the device id, eg. /dev/sdbX

DEV_BOOT="/dev/sdb1"
DEV_ROOTFS="/dev/sdb2"

# OPTEE_ROOT="/home/mhasan/workspace/rpi_optee310"
OPTEE_ROOT="/home/mhasan/workspace/rt_security_io_tz"

# mount filesystem

echo "Mounting filesystem..."

sudo mkdir /media/boot
sudo mkdir /media/rootfs

sudo mount $DEV_BOOT /media/boot
sudo mount $DEV_ROOTFS /media/rootfs

echo "Copy library and examples ..."

# copy filesystme, library
cd /media
LOC="$OPTEE_ROOT/gen_rootfs/filesystem.cpio.gz"
sudo gunzip -cd $LOC | sudo cpio -iudmv "boot/*"

LOC="$OPTEE_ROOT/module_output/lib/*"
sudo cp -r $LOC rootfs/lib/

LOC="$OPTEE_ROOT/optee_client/out/export/*"
sudo cp -r $LOC rootfs/

# copy OPTEE examples
LOC="$OPTEE_ROOT/optee_examples/out/ca/*"
sudo cp -r $LOC  rootfs/bin/

# copy TA files
LOC="$OPTEE_ROOT/optee_examples/out/ta/*"
# create directory if doesn't exist
sudo mkdir rootfs/lib/optee_armtz/
sudo cp -r $LOC rootfs/lib/optee_armtz/

echo "Unmounting filesystem..."

# Unmount
sudo umount /media/boot/
sudo umount /media/rootfs/

# sudo rm -r /media/boot
# sudo rm -r /media/rootfs

echo "Script finished!"

所以也就是正常的树莓派sd卡中有两个分区:

  mount | grep sdb
/dev/sdb1 on /media/xuanxuan/system-boot type vfat 
/dev/sdb2 on /media/xuanxuan/writable type ext4
  • boot:包含了启动的bootloader,optee,内核等
  • rootfs:文件系统,包含了各种用户态的库,软件等

修改过程就是用OPTEE的编译安全启动部分以及内核替换了ubuntu18.04的树莓派镜像,至于其中到底是怎么配置跑起来TEE的,还需要理解OPTEE的树莓派适配部分。不过这种方式还是有个问题:

LOC="$OPTEE_ROOT/module_output/lib/*"
sudo cp -r $LOC rootfs/lib/

这种拷贝会直接拷贝软连接本身,导致内核模块编译的依赖文件夹/lib/modules/4.6.3/build只是个软链接,所以需要修复。

修复内核模块的编译依赖

修复方法非常简单,拷贝时使用-L参数即可将软链接指向的真正文件拷贝过来:

LOC="$OPTEE_ROOT/module_output/lib/*"
sudo cp -rL $LOC rootfs/lib/

但是因为之前刷入的是ubuntu18.04镜像,默认大小只有2.65G,空间是不够的,所以首先需要对刷入镜像的sd卡的文件系统使用空间扩大,可以将TF插入树莓派中并启动,ubuntu18.04的树莓派镜像中的程序会自动的扩大文件系统匹配到TF卡的大小,以前的方法都是磁盘工具对其进行扩大,这里使用linux的gparted进行扩大。

image

然后启动系统,发现还是无法正常编译内核模块,分析原因是因为居然是因为有的程序是x64的:

ubuntu@ubuntu:~$ file /lib/modules/4.6.3/build/scripts/recordmcount
/lib/modules/4.6.3/build/scripts/recordmcount: ELF 64-bit LSB executable, x86-64

遇到这个错误时真是百思不得其解,都是交叉编译出来的aarch64,为啥会有x64呢?后找到解决方案:树莓派实践例程(一)

$ cd /lib/modules/4.6.3/build/
$ sudo make scripts

就是在树莓派上自己make一下就可以修复了。

测试TEE

ubuntu@ubuntu:~$ ls /dev/tee*
/dev/tee0  /dev/teepriv0
ubuntu@ubuntu:~$ sudo tee-supplicant&
[1] 1443
ubuntu@ubuntu:~$ sudo optee_example_hello_world 
Invoking TA to increment 42
TA incremented value to 43

但看不到TEE侧打印,其实TEE的打印就在树莓派的串口输出,但是ubuntu侧应该是捕获不到OPTEE使用串口通信的数据(虽然是一个串口),所以还是需要把树莓派的串口接出来,那树莓派的串口在哪呢?

其实就是6、8、10三个引脚,接到一个串口转换器就行啦,然后重新执行一个CA程序:

ubuntu@ubuntu:~$ sudo optee_example_hello_world 
Invoking TA to increment 42
TA incremented value to 43

即可在串口看到TEE侧打印信息:

ubuntu@ubuntu:~$ D/TC:0 tee_ta_init_pseudo_ta_session:297 Lookup pseudo TA 8aaaf200-2450-11e4-abe2-0002a5d5c51b
D/TC:0 tee_ta_init_user_ta_session:632 Lookup user TA 8aaaf200-2450-11e4-abe2-0002a5d5c51b (Secure Storage TA)
D/TC:0 tee_ta_init_user_ta_session:632 Lookup user TA 8aaaf200-2450-11e4-abe2-0002a5d5c51b (REE)
D/TC:0 ta_load:317 ELF load address 0x40005000
D/TA:  TA_CreateEntryPoint:39 has been called
F/TA:  tee_user_mem_alloc:344: Allocate: link:[0x400170c0], buf:[0x400170e0:32]
D/TA:  TA_OpenSessionEntryPoint:68 has been called
I/TA:  Hello World!
D/TA:  inc_value:105 has been called
I/TA:  Got value: 42 from NW
I/TA:  Increase value to: 43
D/TC:0 tee_ta_close_session:403 tee_ta_close_session(0x8487ba0)
D/TC:0 tee_ta_close_session:422 Destroy session
I/TA:  Goodbye!
F/TA:  tee_user_mem_free:443: Free: link:[0x400170c0], buf:[0x400170e0:32]
D/TA:  TA_DestroyEntryPoint:50 has been called
D/TC:0 tee_ta_close_session:448 Destroy TA ctx

image

开启JTAG

参考:树莓派JTAG详细使用笔记

开启JTAG的方法非常简单,直接在树莓派的shell中操作就可以,打开/boot/firmware/config.txt配置项即可,经测试gpio=22-27=a4可以不写:

ubuntu@ubuntu:/boot$ cd /boot/firmware/
ubuntu@ubuntu:/boot/firmware$ sudo vi ./config.txt

enable_jtag_gpio=1

但是使用起来还是有点麻烦的,首先是连接线,参考上文的接线方法,不过还是要接GND的,所以总共6根线:

image

openocd没有自带树莓派3b的配置文件,需要自行寻找:

另外mac上brew安装的openocd对于此配置文件的解析有问题,需要下载一个官方版本

然后使用-f参数指定适配器型号(JLINK)以及目标配置(树莓派3B),使用-c将调试端口(默认127.0.0.1)开放在0.0.0.0以方便调试,可见开了4个gdb端口,分别对应CPU的四核:

  pwd
/Users/xuanxuan/Downloads/xpack-openocd-0.11.0-1
  ./bin/openocd -f ./scripts/interface/jlink.cfg -f ./r3.cfg -c "bindto 0.0.0.0"


xPack OpenOCD, x86_64 Open On-Chip Debugger 0.11.0-00155-ge392e485e (2021-03-15-18:44)
Licensed under GNU GPL v2
For bug reports, read
	http://openocd.org/doc/doxygen/bugs.html
DEPRECATED! use 'adapter speed' not 'adapter_khz'
Warn : DEPRECATED! use '-baseaddr' not '-ctibase'
Warn : DEPRECATED! use '-baseaddr' not '-ctibase'
Warn : DEPRECATED! use '-baseaddr' not '-ctibase'
Warn : DEPRECATED! use '-baseaddr' not '-ctibase'
Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : J-Link V9 compiled Dec 13 2019 11:14:50
Info : Hardware version: 9.60
Info : VTarget = 3.288 V
Info : clock speed 1000 kHz
Info : JTAG tap: rpi3.tap tap/device found: 0x4ba00477 (mfg: 0x23b (ARM Ltd), part: 0xba00, ver: 0x4)
Info : rpi3.a53.0: hardware has 6 breakpoints, 4 watchpoints
Info : rpi3.a53.1: hardware has 6 breakpoints, 4 watchpoints
Info : rpi3.a53.2: hardware has 6 breakpoints, 4 watchpoints
Info : rpi3.a53.3: hardware has 6 breakpoints, 4 watchpoints
Info : starting gdb server for rpi3.a53.0 on 3333
Info : Listening on port 3333 for gdb connections
Info : starting gdb server for rpi3.a53.1 on 3334
Info : Listening on port 3334 for gdb connections
Info : starting gdb server for rpi3.a53.2 on 3335
Info : Listening on port 3335 for gdb connections
Info : starting gdb server for rpi3.a53.3 on 3336
Info : Listening on port 3336 for gdb connections

然后使用gdb-multiarch进行连接(pwndbg貌似有解析问题):

$ gdb-multiarch -q
gdb-peda$ set architecture aarch64
The target architecture is assumed to be aarch64
gdb-peda$ set endian little
The target is assumed to be little endian
gdb-peda$ target remote 10.11.11.1:3333

调试截图如下,可以看到EL1、EL2、EL3相关寄存器:

image

另外还有用树莓派当JLINK去控制别的JTAG设备的:

完整流程

下载ubuntu-18.04镜像并烧录:

  wget http://cdimage.ubuntu.com/releases/bionic/release/ubuntu-18.04.5-preinstalled-server-arm64+raspi3.img.xz
  xzcat ./ubuntu-18.04.5-preinstalled-server-arm64+raspi3.img.xz | sudo dd of=/dev/disk2  bs=4M 

下载修复好的optee仓库,编译:

  git clone https://github.com/xuanxuanblingbling/RPi3_OPTEE_3.1.git
  cd RPi3_OPTEE_3.1/build 
  make -j2 toolchains
  make -j `nproc` 

扩容,修改脚本路径,复制optee文件到tf卡上

  cd ..
  vi ./copy_to_sdcard.sh 
  ./copy_to_sdcard.sh 

树莓派插卡,开机,连接网络,安装编译工具:

$ sudo dhclient -i enxb827eb2d9c09
$ sudo apt update
$ sudo apt install -y gcc make

修复编译内核模块的脚本:

$ cd /lib/modules/4.6.3/build/
$ sudo make scripts

开启JTAG:

$ cd /boot/firmware/
$ sudo vi ./config.txt

enable_jtag_gpio=1

测试编译内核模块:

$ cd ~
$ git clone https://github.com/xuanxuanblingbling/linux_kernel_module_exercise.git
$ cd ~/linux_kernel_module_exercise/01.hello/
$ make
$ sudo insmod ./hello.ko
$ dmesg | tail
[ 2049.439830] Hello, world!

测试TEE:

ubuntu@ubuntu:~$ ls /dev/tee*
/dev/tee0  /dev/teepriv0
ubuntu@ubuntu:~$ sudo tee-supplicant&
[1] 1443
ubuntu@ubuntu:~$ sudo optee_example_hello_world 
Invoking TA to increment 42
TA incremented value to 43