我很想深入研究 ARM 的 TrustZone,想要搭建一个可以模拟和调试 Trusted Application 的平台环境。我了解到 Open-TEE (ATF) 项目提供有一个 QEMU 模拟调试环境。虽然是针对于 Open-TEE 的 TrustZone 实现,但是也可以作为一个参考来学习。在阅读这个项目的代码时,我留意到了一个 QEMU 的启动参数:
经过一番查找后发现这个 Semihosting 功能颇有用处:他能够让 bare-metal 的 ARM 设备通过拦截指定的 SVC 指令,在连操作系统都没有的环境中实现 POSIX 中的许多标准函数,比如 printf、scanf、open、read、write 等等。这些 IO 操作将被 Semihosting 协议转发到 Host 主机上,然后由主机代为执行,所以在 ARM 模拟器中执行一个 printf 可以直接打印到 Host 主机上的终端窗口中;在 ARM 模拟器中写一个文件可以直接写到 Host 主机的当前目录下,等等。
如果要写一个 TrustZone Kernel 的 QEMU 模拟环境,如果使用了 Semihosting,则不需要去模拟 FLASH 存储设备就能直接从 Host 主机上加载文件;不需要模拟串口设备就能直接打印 Log 输出,将会大大减少开发工作量。Open-TEE 项目也正是利用了 Semihosting 非常简单地就加载了 Trusted Application。
为了今后能够在我自己的项目中使用 Semihosting 这一方便的功能,我想要写点 Hello World 程序看看如何使用这个功能。
Semihosting 其实是 ARM 官方定义的功能,所有 ARM 芯片都支持这个协议,只要连上硬件调试器就能使用。当然了,QEMU 模拟 ARM 也把 Semihosting 模拟了一遍,只不过不需要什么特殊的连接就能直接使用。这个是 ARM 的官方文档:
e52K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6V1k6i4k6W2L8r3!0H3k6i4u0Q4x3X3g2S2M7X3#2Q4x3X3g2U0L8$3#2Q4x3V1k6V1L8$3y4#2L8h3g2F1N6r3q4@1K9h3!0F1i4K6u0r3k6s2g2A6x3o6b7%4x3g2)9J5c8X3N6Q4x3V1k6e0k6h3#2A6K9r3!0K6N6r3W2F1k6#2)9J5c8W2c8Z5k6g2)9J5k6s2y4W2L8h3W2Z5L8%4y4@1K9h3&6Y4i4K6u0V1K9h3&6@1k6i4u0X3j5h3y4W2
可以看到 SVC 0x123456 是官方给 Semihosting 预留的指定命令。当这条 SVC 被执行时,R0 寄存器的值是一个 Operation ID,R1 寄存器的值是指向附加参数结构体的指针。这个页面列举了 Semihosting 支持的 Operations:
768K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6V1k6i4k6W2L8r3!0H3k6i4u0Q4x3X3g2S2M7X3#2Q4x3X3g2U0L8$3#2Q4x3V1k6V1L8$3y4#2L8h3g2F1N6r3q4@1K9h3!0F1i4K6u0r3k6s2g2A6x3o6b7%4x3g2)9J5c8X3N6Q4x3V1k6e0k6h3#2A6K9r3!0K6N6r3W2F1k6#2)9J5c8W2y4W2L8h3W2Z5L8%4y4@1K9h3&6Y4i4K6u0V1L8%4m8W2M7X3q4@1K9h3!0F1M7H3`.`.
比方说最简单的 puts 函数,对应的 Operation ID 是 SYS_WRITE0 (0x04),那我们就可以很简单地用 C 和汇编写一个 Hello World:
927K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6A6e0Y4k6q4M7U0N6Q4x3V1k6I4k6h3#2#2i4K6u0V1L8r3g2S2M7X3&6Q4x3V1k6T1L8r3!0T1i4K6u0r3L8h3q4K6N6r3g2J5i4K6u0r3M7$3g2E0K9h3S2G2M7%4c8A6L8X3M7`.
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课