1 of 55

TF-A, TBBRopen source bootloader for RPi3

PaulLiu (Ying-Chun Liu)COSCUP 2019

2 of 55

About me

  • PaulLiu (Ying-Chun Liu)
  • Debian Developer since 2010
  • Linaro Engineer
  • 純自由軟體實踐者
    • 只使用 Debian main

3 of 55

Reverse Engineering

  • 逆向工程 - 最後也是最爛的辦法
  • 耗時
    • 研究/破解時間
    • 實做時間
  • 憑運氣

4 of 55

案例 - WDL

  • 政府委託民間公司開發的某號稱跨平台 portable 格式
  • 但沒 open source
  • 也不是 open format
  • Debian 中還有這個程式
  • 大概花了一年破解+開發時間
  • 現已被 PDF 取代
  • wdl 已經很少見

5 of 55

et1009-tools

  • 當年 (2002) NOVA 裡面最便宜的錄音筆
  • 要破解兩個部份
    • 把檔案透過 USB 傳到電腦上的協定
    • 檔案 vwd 轉成 wav
  • 一年半左右的破解+開發時間
  • 程式寫完沒多久, 錄音筆就開始有壞磁區.
  • 現在早就沒這款錄音筆可買了

6 of 55

AnyX

  • 電視訊號轉 mpeg4 TCP stream.
  • 破解三個部份
    • http stream 格式
    • multimedia stream 格式 (video/audio)
    • 如何控制轉台
  • 運氣好三個月搞定
  • 用了五年以上. 但後來有線電視升級數位頻道後再也無法使用

7 of 55

RaspberryPi3

  • 2019-01-22 獲得重要進展
    • rpi-open-firmware 載入 TF-A BL1/BL2
    • BL2 以 SDHost driver 後續載入 BL3x
  • 2019-06-03 COSCUP 社群議程入選通知
  • 2019-06-24 RPi4 出了.
    • 而且還聽說不用 non-free firmware

8 of 55

Raspbian boot partition

  • bcm2708-rpi-b.dtb bcm2711-rpi-4-b.dtb fixup_cd.dat overlays
  • bcm2708-rpi-b-plus.dtb bootcode.bin fixup.dat start4cd.elf
  • bcm2708-rpi-cm.dtb cmdline.txt fixup_db.dat start4db.elf
  • bcm2708-rpi-zero.dtb config.txt fixup_x.dat start4.elf
  • bcm2708-rpi-zero-w.dtb COPYING.linux issue.txt start4x.elf
  • bcm2709-rpi-2-b.dtb fixup4cd.dat kernel7.img start_cd.elf
  • bcm2710-rpi-3-b.dtb fixup4.dat kernel7l.img start_db.elf
  • bcm2710-rpi-3-b-plus.dtb fixup4db.dat kernel.img start.elf
  • bcm2710-rpi-cm3.dtb fixup4x.dat LICENCE.broadcom start_x.elf
  • 更多檔案 https://github.com/raspberrypi/firmware

9 of 55

  • $ file start4.elf
  • start4.elf: ELF 32-bit LSB executable, Broadcom VideoCore III, version 1 (SYSV), statically linked, stripped

10 of 55

  • bcm2837

ARM Cortex-A53

VideoCore 4

11 of 55

Videocore 4

  • Videocore 4 本身有個 bootrom
  • bootrom 會載入 FAT partition 中的 bootcode.bin
  • bootcode.bin init SDRAM 後, 載入後續的 start*.elf
  • start*.elf 把 kernel*.img 載入記憶體 如果 config.txt 沒指定位置, 預設 0x08000 (32-bit kernel)
  • start*.elf 依照板型把對應的 *.dtb 載入記憶體. 如果 config.txt 沒指定位置, 預設載入 0x100
  • 將一段 default stub 放到 0x0 以跳至 kernel 起始位置
  • 啟動 ARM core.

12 of 55

目前的 Memory Map

  • 0x0

Boot code

dtb

kernel

  • 0x100
  • 0x8000

13 of 55

Videocore 4

  • Videocore 4 本身有個 bootrom
  • bootrom 會載入 FAT partition 中的 bootcode.bin
  • bootcode.bin init SDRAM 後, 載入後續的 start*.elf
  • start*.elf 把 kernel*.img 載入記憶體 如果 config.txt 沒指定位置, 預設 0x08000 (32-bit kernel)
  • start*.elf 依照板型把對應的 *.dtb 載入記憶體. 如果 config.txt 沒指定位置, 預設載入 0x100
  • 如果 FAT 裡面有 armstub8.bin 就放到 0x0
  • 以 64-bit mode 啟動 ARM core.

14 of 55

TF-A

  • Trusted-firmware A.
  • 以前叫 Arm Trusted Firmware (ATF)

15 of 55

Architecture

16 of 55

調整一下記憶體使用方式

  • 0x0

BL1

dtb

kernel

  • 0x1000000
  • 0x2000000

FIP

  • 0x20000
  • 0x200000
  • Loaded by VC4
  • Loaded by VC4

BL2/BL31

BL32(OPTEE)

BL33(U-Boot)

  • 0x10000000
  • 0x10100000
  • 0x11000000

17 of 55

修改 config.txt

  • enable_uart=1
  • kernel_address=0x02000000
  • device_tree_address=0x01000000

18 of 55

Build u-boot

  • make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- rpi_3_32b_defconfig
  • 用 menuconfig 修改CONFIG_SYS_TEXT_BASE=0x11000000
  • make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf-

19 of 55

Build optee

make \

CROSS_COMPILE_ta_arm32=arm-linux-gnueabihf- \

CROSS_COMPILE_core=aarch64-linux-gnu- \

PLATFORM=rpi3 CFG_ARM64_core=y \

CFG_DT=y CFG_DT_ADDR=0x14000000 \

CFG_TEE_CORE_LOG_LEVEL=4 \

CFG_EXTERNAL_DTB_OVERLAY=y

20 of 55

Compile TF-A

env CROSS_COMPILE=aarch64-linux-gnu- make \

PLAT=rpi3 RPI3_BL33_IN_AARCH32=1 \

BL33=u-boot.bin NEED_BL32=yes \

BL32=tee-header_v2.bin \

BL32_EXTRA1=tee-pager_v2.bin \

BL32_EXTRA2=tee-pageable_v2.bin LOG_LEVEL=40 CRASH_REPORTING=1 \

GENERATE_COT=1 TRUSTED_BOARD_BOOT=1 \

USE_TBBR_DEFS=1 SPD=opteed MBEDTLS_DIR=../mbedtls \

RPI3_PRELOADED_DTB_BASE=0x01000000 \

RPI3_RUNTIME_UART=1 all

21 of 55

  • 然後就得到 armstub8.bin 了
  • 如果沒事先產生並指定的話, 還會得到 rot_key.pem��

22 of 55

Boot log

NOTICE: Booting Trusted Firmware

NOTICE: BL1: v2.1(release):atf-pr1768-748-g17e93a8-dirty

NOTICE: BL1: Built : 15:04:14, Aug 7 2019

INFO: BL1: RAM 0x100ee000 - 0x100f9000

INFO: Using crypto library 'mbed TLS'

NOTICE: rpi3: Detected: Raspberry Pi 3 Model B (1GB, Embest, China) [0x00a22082]

INFO: BL1: Loading BL2

INFO: Loading image id=6 at address 0x100b4000

INFO: Image id=6 loaded: 0x100b4000 - 0x100b446f

INFO: Loading image id=1 at address 0x100b4000

INFO: Image id=1 loaded: 0x100b4000 - 0x100c75b0

NOTICE: BL1: Booting BL2

INFO: Entry point address = 0x100b4000

INFO: SPSR = 0x3c5

23 of 55

Boot log

NOTICE: BL2: v2.1(release):atf-pr1768-748-g17e93a8-dirty

NOTICE: BL2: Built : 15:04:09, Aug 7 2019

INFO: Using crypto library 'mbed TLS'

INFO: BL2: Doing platform setup

INFO: BL2: Loading image id 3

INFO: Loading image id=7 at address 0x100e0000

INFO: Image id=7 loaded: 0x100e0000 - 0x100e060e

INFO: Loading image id=9 at address 0x100e0000

INFO: Image id=9 loaded: 0x100e0000 - 0x100e04da

INFO: Loading image id=13 at address 0x100e0000

INFO: Image id=13 loaded: 0x100e0000 - 0x100e0430

INFO: Loading image id=3 at address 0x100e0000

INFO: Image id=3 loaded: 0x100e0000 - 0x100e8070

INFO: BL2: Loading image id 4

INFO: Loading image id=10 at address 0x10100000

INFO: Image id=10 loaded: 0x10100000 - 0x101004e8

INFO: Loading image id=14 at address 0x10100000

INFO: Image id=14 loaded: 0x10100000 - 0x101004ce

INFO: Loading image id=4 at address 0x10100000

INFO: Image id=4 loaded: 0x10100000 - 0x1010001c

24 of 55

Boot log

INFO: OPTEE ep=0x10100000

INFO: OPTEE header info:

INFO: magic=0x4554504f

INFO: version=0x2

INFO: arch=0x1

INFO: flags=0x0

INFO: nb_images=0x1

INFO: BL2: Loading image id 21

INFO: Loading image id=21 at address 0x10100000

INFO: Image id=21 loaded: 0x10100000 - 0x1014f370

INFO: BL2: Skip loading image id 22

INFO: BL2: Loading image id 5

INFO: Loading image id=11 at address 0x11000000

INFO: Image id=11 loaded: 0x11000000 - 0x110004ea

INFO: Loading image id=15 at address 0x11000000

INFO: Image id=15 loaded: 0x11000000 - 0x11000440

INFO: Loading image id=5 at address 0x11000000

INFO: Image id=5 loaded: 0x11000000 - 0x1107bae8

25 of 55

Boot log

INFO: BL33 will boot in Non-secure AArch32 Hypervisor mode

NOTICE: BL1: Booting BL31

INFO: Entry point address = 0x100e0000

INFO: SPSR = 0x3cd

NOTICE: BL31: v2.1(release):atf-pr1768-748-g17e93a8-dirty

NOTICE: BL31: Built : 15:04:12, Aug 7 2019

INFO: rpi3: Checking DTB...

INFO: rpi3: Reserved 0x10000000 - 0x10100000 in DTB

INFO: BL31: Initializing runtime services

INFO: BL31: Initializing BL32

INFO: BL31: Preparing for EL3 exit to normal world

INFO: Entry point address = 0x11000000

INFO: SPSR = 0x1da

26 of 55

Boot log

U-Boot 2019.01 (Aug 07 2019 - 07:43:43 +0000)

DRAM: 948 MiB

RPI 3 Model B (0xa22082)

MMC: mmc@7e202000: 0, sdhci@7e300000: 1

Loading Environment from FAT... *** Warning - bad CRC, using default environment

In: serial

Out: vidconsole

Err: vidconsole

Net: No ethernet found.

starting USB...

USB0: scanning bus 0 for devices... 3 USB Device(s) found

scanning usb for storage devices... 0 Storage Device(s) found

Hit any key to stop autoboot: 2

27 of 55

armstub8.bin 內容

  • 131072

BL1

FIP

28 of 55

  • $ /tmp/rpi3-cst/split_rpi3_bl1_fip.sh --bl1 bl1.bin --fip fip.bin armstub8.bin
  • $ ls -l *.bin
  • -rw-r--r-- 1 paulliu paulliu 1064895 5月 16 02:58 armstub8.bin
  • -rw-r--r-- 1 paulliu paulliu 131072 8月 18 07:37 bl1.bin
  • -rw-r--r-- 1 paulliu paulliu 933823 8月 18 07:37 fip.bin

29 of 55

$ ./tools/fiptool/fiptool info fip.bin  

Trusted Boot Firmware BL2: offset=0x268, size=0x115B0, cmdline="--tb-fw"

EL3 Runtime Firmware BL31: offset=0x11818, size=0x7070, cmdline="--soc-fw"

Secure Payload BL32 (Trusted OS): offset=0x18888, size=0x1C, cmdline="--tos-fw"

Secure Payload BL32 Extra1 (Trusted OS Extra1): offset=0x188A4, size=0x4CC00, cmdline="--tos-fw-extra1"

Secure Payload BL32 Extra2 (Trusted OS Extra2): offset=0x654A4, size=0x0, cmdline="--tos-fw-extra2"

Non-Trusted Firmware BL33: offset=0x654A4, size=0x7C4B4, cmdline="--nt-fw"

Trusted key certificate: offset=0xE1958, size=0x60E, cmdline="--trusted-key-cert"

SoC Firmware key certificate: offset=0xE1F66, size=0x4DA, cmdline="--soc-fw-key-

cert"

Trusted OS Firmware key certificate: offset=0xE2440, size=0x4E8, cmdline="--tos-

fw-key-cert"

Non-Trusted Firmware key certificate: offset=0xE2928, size=0x4EA, cmdline="--nt-

fw-key-cert"

30 of 55

FIP

  • tb-fw.bin: BL2
  • soc-fw.bin: BL31 (secure monitor)
  • tos-fw.bin: BL32 (OPTEE)
    • tos-fw-extra1.bin
    • tos-fw-extra2.bin
  • nt-fw.bin: BL33 (U-boot or Kernel)

31 of 55

FIP(Cont)

  • trusted-key-cert.bin: 2 public keys([non-]tw-key)
  • soc-fw-key-cert.bin: public key for soc-fw
  • tos-fw-key-cert.bin: pub. key for tos-fw
  • nt-fw-key-cert.bin: public key for nt-fw
  • tb-fw-cert.bin: BL2 certificate
  • soc-fw-cert.bin: BL31 certificate
  • tos-fw-cert.bin: BL32(OPTEE) certificate
  • nt-fw-cert.bin: BL33(U-boot) certificate

32 of 55

ROT Key

Trusted world key

Non-Trusted world key

BL31 key

BL32 key

BL33 key

BL31 content hash

BL32 content hash

BL33 content hash

BL2 content hash

33 of 55

  • 預設只會保留 rot_key.pem. 可透過參數保留更多 key.
  • 實務上可以只保留 rot_key.pem. 每次都重新產生所有 子 keys 並重 sign.
  • fiptool
  • cert_create

34 of 55

Trusted Boot?

  • TF-A 文件有特別提到 RPi3 上面沒有所謂的 secure.

35 of 55

Example: How BL2 verifies BL33

BL2

ROTPK_Hash

BL33 (U-boot)

BL33 content hash

BL33 key

Trusted world key

Non-Trusted world key

36 of 55

How BL1 verifies BL2?

BL2

ROTPK_Hash

BL1

ROTPK_Hash

BL2 content hash

37 of 55

Who verifies BL1??

BL1

ROTPK_Hash

  • 沒有人. 因為 BL1 本來應該是 bootROM
  1. 把後續的 boot loader 送給擁有 ROT key 的人簽
  2. 簽 NDA 拿到一個 close source 程式來簽

38 of 55

  • BL1 雖然是 ROM
  • 但有一次性寫入的 FUSE 自己填 ROTPK_HASH

BL1

FUSE

39 of 55

RaspberryPi3

  • BL1 不是 唯讀 ROM.
  • BL1 也不會從 FUSE 裡面讀資料. 因為根本沒有 FUSE.
  • 也沒有人 Verify BL1
  • 所以只要能改 BL1 裡面存的 ROTPK_HASH 就能破解 Trusted Boot.
  • https://gitlab.com/grandpaul/rpi3-cst

40 of 55

Conclusion

  • ARM core 這邊已經完全開源
  • 也沒有辦法實做 Trusted Boot 鎖機

41 of 55

VideoCore4 ?

42 of 55

vc4-toolchain

  • 不明原因. gcc upstream 沒收
  • Build-Depends on guile-1.8
  • Debian old-oldstable (jessie) 也沒有.
  • 只能在 Ubuntu Xenial (16.04 LTS) 裡面編譯

43 of 55

rpi-open-firmware

  • 可產生 bootcode.bin
  • bootcode.bin 使用 vc4 init SDRAM.
  • 然後將 bootcode.bin 裡面的 padding 往 0x0 放.
  • 然後啟動 ARM core.
  • 預設的 padding 是一個很小的 ARM boot loader (32-bit).
  • VC4 bootloader 會去填一些數值到 ARM boot loader 裡面

44 of 55

修改 rpi-open-firmware

  • 需要修改成啟動 ARM core 為 64-bit value.
  • 避免修改 bootloader. 會破壞 TF-A (BL1)

45 of 55

  • 仍然有問題.
  • 因為 bootcode.bin 是由 VC4 Bootrom 載入.
  • bootcode.bin padding armstub8.bin 太大. Bootrom 無法載入這麼大的 bootcode.bin

46 of 55

修改 TF-A

  • BL1+FIP(BL2-only) 為 armstub8.bin
  • BL2 中實做 SDHost driver. 然後從 SDCard raw sector 載入另外一個 FIP (BL31 BL32 BL33).
  • 為了節省 size, 去掉 TBBR

47 of 55

修改後的 repository

48 of 55

Boot log

Booting Raspberry Pi....

Copyright 2016-2017 rpi-open-firmware authors  

BUILDATE  : Aug 17 2019 01:28:13  

__cxx_init: calling 5 static constructors (0x14000 - 0x14014) ...

BCM2708UsbPhy::registerDriver(): driver registered on platform IO plane

BCM2708ArmControl::registerDriver(): driver registered on platform IO plane

BCM2708PLLB::init(): PLLB VCO registered

BCM2708PLLB::registerDriver(): driver registered on platform IO plane

BCM2708Gpio::registerDriver(): driver registered on platform IO plane

[SDRAM:sdram_init]: (0) SD_CS = 0x794200

[SDRAM:switch_to_cprman_clock]: switching sdram to cprman clock (src=1, div=1),  

waiting for busy (4011) ...

[SDRAM:switch_to_cprman_clock]: busy set, switch complete!

[SDRAM:reset_phy]: reset_phy: resetting SDRAM PHY ...

[SDRAM:reset_phy]: reset_phy: resetting DPHY CTRL ...

[SDRAM:reset_phy_dll]: resetting aphy and dphy dlls ...

[SDRAM:reset_phy_dll]: waiting for dphy master dll to lock ...

[SDRAM:reset_phy_dll]: dphy master dll locked!

[SDRAM:sdram_init]: waiting for SDUP (210242) ...

49 of 55

Boot log

[SDRAM:sdram_init]: SDRAM controller has arrived! (218E42)

[SDRAM:calibrate_pvt_early]: DPHY_CSR_DQ_PAD_DRV_SLEW_CTRL = 0x223

[SDRAM:calibrate_pvt_early]: waiting for address PVT calibration ...

[SDRAM:calibrate_pvt_early]: waiting for data PVT calibration ...

[SDRAM:calibrate_pvt_early]: waiting for SDRAM calibration command ...

[SDRAM:sdram_init]: SDRAM Type: Elpida 1GB LPDDR2 (BC=0x58)

[SDRAM:reset_with_timing]: waiting for SDRAM controller to go down (218E4A) ...

[SDRAM:reset_with_timing]: SDRAM controller down!

[SDRAM:reset_with_timing]: SDRAM clock disabled!

[SDRAM:reset_with_timing]: waiting for master ddr pll to lock ...

[SDRAM:reset_with_timing]: master ddr pll locked!

[SDRAM:reset_with_timing]: SDRAM Addressing Mode: Bank=3 Row=3 Col=3 SB=0xFF

[SDRAM:reset_phy_dll]: resetting aphy and dphy dlls ...

[SDRAM:reset_phy_dll]: waiting for dphy master dll to lock ...

[SDRAM:reset_phy_dll]: dphy master dll locked!

[SDRAM:reset_with_timing]: waiting for address dll to lock ...

[SDRAM:reset_with_timing]: address dll locked!

[SDRAM:selftest]: Starting self test ...

50 of 55

Boot log

[SDRAM:selftest_at]: Testing region at 0xC0000000 ...

[SDRAM:selftest_at]: Testing region at 0xCFF00000 ...

[SDRAM:selftest_at]: Testing region at 0xDFF00000 ...

[SDRAM:selftest_at]: Testing region at 0xEFF00000 ...

[SDRAM:selftest_at]: Testing region at 0xFFF00000 ...

[SDRAM:selftest]: Self test successful!

SDRAM initialization completed successfully!

BCM2708PowerDomainUSB::stop(): stopping ...

BCM2708PowerDomainUSB::stop(): stopped

BCM2708PowerDomainImage::powerOn(): powering on, current PM_IMAGE state: 0x1000

BCM2708PowerDomainImage::beginPowerUpSequence(): starting power up sequence ...

BCM2708PowerDomainImage::waitForPOWOK(): waiting for POWOK ...

BCM2708PowerDomainImage::waitForPOWOK(): got POWOK with CFG=0x10000

BCM2708PowerDomainImage::powerOn(): stopping power up sequence ...

BCM2708PowerDomainImage::beginPowerUpSequence(): starting power up sequence ...

BCM2708PowerDomainImage::waitForPOWOK(): waiting for POWOK ...

BCM2708PowerDomainImage::waitForPOWOK(): got POWOK with CFG=0x30000

BCM2708PowerDomainImage::completePowerUpSequence(): waiting for MRDONE ...

51 of 55

Boot Log

BCM2708PowerDomainImage::resetPeripheralsUngated(): calling v16mov gated ...

BCM2708PowerDomainImage::resetPeripheralsUngated(): ungating and resetting ...

BCM2708PowerDomainImage::resetPeripheralsUngated(): done

BCM2708PowerDomainImage::start(): CM_PERIICTL = 0x40

BCM2708PowerDomainImage::start(): CM_PERIIDIV = 0x0

BCM2708PowerDomainImage::start(): PM_IMAGE    = 0x3107F

BCM2708PowerDomainImage::start(): started

BCM2708PowerDomainUSB::start(): starting ...

BCM2708PowerDomainUSB::start(): started

BCM2708UsbPhy::start(): starting ...

BCM2708UsbPhy::start(): LAN reset

BCM2708UsbPhy::start(): started

BCM2708ArmControl::start(): starting ...

BCM2708ArmControl::loadInitialCode(): copied 57412 bytes to 0xC0000000!

BCM2708ArmControl::start(): original memstart: 0xEA000039

BCM2708ArmControl::start(): mapped VC 0xC0000000 to ARM 0x0

BCM2708ArmControl::start(): mapped peripherals VC 0x7E000000 to ARM 0x3F000000

BCM2708ArmControl::start(): ARM ID: 0x364D5241 C0: 0x0

BCM2708ArmControl::start(): using C0: 0xA24B

52 of 55

Boot log

BCM2708ArmControl::setupClock(): initializing PLLB ...

BCM2708PLLB::setDigValues(): setting DIG values for this VCO ...

BCM2708ArmControl::setupClock(): KAIP  = 0x228

BCM2708ArmControl::setupClock(): MULTI = 0x613277

BCM2708ArmControl::setupClock(): ARM clock succesfully initialized!

BCM2708PowerDomainARM::start(): starting ...

BCM2708PowerDomainARM::powerOn(): powering on (rstnMask=0xFFFFFFBF) ...

BCM2708PowerDomainARM::beginPowerUpSequence(): starting power up sequence ...

BCM2708PowerDomainARM::waitForPOWOK(): waiting for POWOK ...

BCM2708PowerDomainARM::waitForPOWOK(): got POWOK with CFG=0x0

BCM2708PowerDomainARM::completePowerUpSequence(): waiting for MRDONE ...

BCM2708PowerDomainARM::powerOn(): domain powered on succesfully

BCM2708PowerDomainARM::start(): started

BCM2708ArmControl::bridgeStart(): setting up async bridge ...

BCM2708PowerDomainARM::setReset(): setting RSTN bits to 0x40 ...

BCM2708ArmControl::bridgeCycleBresp(): cycling through bresp bits ...

BCM2708PowerDomainARM::setReset(): setting RSTN bits to 0x40 ...

BCM2708ArmControl::bridgeStart(): starting async bridge now!

BCM2708ArmControl::bridgeStart(): bridge init done, PM_PROC is now: 0x107F!

Starting IPC monitor ...

53 of 55

TODO

  • 研究為何 VC4 toolchain 沒有被上游接受
  • Transition: guile-1.8 → guile-2.0
  • 與其在 TF-A 裡面實做 SDHost driver. 不如在 VC4 上實做 SDHost driver?

54 of 55

New TODO

  • 扔掉 RPi3
  • 快去買 RPi4

55 of 55

Q&A?