|TOC0_CHECK_SUM||0x0c||4B||Checksum, same algorithm as eGON.BT0 images|
|TOC0_NUM_ITEMS||0x18||4B||Number of following items|
|TOC0_LENGTH||0x1c||4B||Total length of the TOC0 Image|
|TOC0_BOOT_MEDIA||0x20||4B||Boot device, written by BROM|
|TOC0_ITEMn_ID||0x30 + n * 0x20 + 0x00||4B||
0x010101 = RoT certificate 0x010202 = TOC0 boot code
|TOC0_ITEMn_OFFSET||0x30 + n * 0x20 + 0x04||4B||Item data offset|
|TOC0_ITEMn_LENGTH||0x30 + n * 0x20 + 0x08||4B||Item data length|
|TOC0_ITEMn_STATUS||0x30 + n * 0x20 + 0x0c||4B|
|TOC0_ITEMn_TYPE||0x30 + n * 0x20 + 0x10||4B||
0x1 = certificate 0x2 = code
|TOC0_ITEMn_RUN_ADDRESS||0x30 + n * 0x20 + 0x14||4B||Address to execute code at|
|0x30 + n * 0x20 + 0x18||4B||reserved|
|TOC0_ITEMn_END||0x30 + n * 0x20 + 0x1c||4B||
RoT certificate (ID 0x010101)
Self-signed DER encoded X.509-like certificate containing a SHA256 hash of the boot code.
If a ROTPK_HASH is programmed only certificates with that key are accepted.
The certificate has a similar structure to standard X.509 certificates, but is different in details:
- the public key is stored in a non-standard way
- the hash is added at the place of extensions, but not as real extension
- the signature doesn't use a standard algorithm, misses the last 4 bytes (bug!) and is stored in a non-standard way
TODO: maybe add ASN.1 of the certificate
TOC0 boot code (ID 0x010202)
Similar to boot0, only "TOC0.GLH" magic instead of "eGON.BT0". After verifying against the SHA256 hash in the certificate, this item is copied at the run address given in the item header and executed in secure mode.
TOC0 images generation
There is a preliminary script: https://gist.github.com/jemk/2abcab1359c4bce793679c5854062331
- You may need to replace the ".sum" method with ".reduce(:+)" to run it with modern versions of Ruby
- You may need to change the entry point address from 0x0 to 0x10000 if you want to run it on A64/H64/H5 (the default 0x0 value is used for H3)
The experimental branch https://github.com/ssvb/sunxi-tools/commits/toc0 contains two scripts (egon2toc.rb and toc2egon.rb, based on Jemk's library). They can be used to convert SPL binaries back and forth between these two formats. Usage example:
git clone -b toc0 https://github.com/ssvb/sunxi-tools.git cd sunxi-tools ruby egon2toc.rb bin/uart0-helloworld-sdboot.sunxi bin/uart0-helloworld-sdboot.toc0 ruby toc2egon.rb bin/uart0-helloworld-sdboot.toc0 roundtrip-conversion-output.sunxi cmp -b bin/uart0-helloworld-sdboot.sunxi roundtrip-conversion-output.sunxi
The resulting file bin/uart0-helloworld-sdboot.toc0 can be written to an SD card or to SPI NOR flash and successfully booted on Allwinner H3/A64/H64 devices with secure mode bit burned in the eFUSE.
Doing conversion of the u-boot-sunxi-with-spl.bin files (not just SPL alone) back and forth between eGON and TOC0 formats requires a minor patch for U-Boot.
TOC0 vs. eGON.BT0
|Boot time||TBD||TBD (but worse than eGON)|
|SPL size limit||32 KiB (SRAM A1)||more than 100 KiB (SRAM A1 + SRAM C)|
|SPL load address||Loaded into SRAM A1 and executed there. This address is may be different on different SoC types, but the same position independent code can run on multiple SoC types (see uart0-helloworld-sdboot example)||The load & execute address is specified in the TOC0 item header and it should be set as SRAM A1 address of the SoC (or at least land into a valid SRAM address range). This is a little bit problematic, because it becomes difficult to boot the same image, for example, on H3 and A64 (but maybe we can load the SPL into SRAM A2 to overcome this?).|
|Can boot from||Works with SD card, eMMC, SPI. NAND is untested. Booting from eMMC boot partitions is untested too||Works with SD card, eMMC. SPI, NAND and eMMC boot partitions are still untested.|
|Boot source detection||A 8-bit boot media type identifier is written by the BROM at the offset 0x28 in SRAM A1||A 8-bit boot media type identifier is written by the BROM at the offset 0x20 in SRAM A1|
|Secure peripherals||Access to SID and the other secure-only resources is allowed from both secure and non-secure mode, SPC settings are ignored. Investigation for a possible fix or workaround is underway.|| Work correctly, SPC settings are respected.|
Note: some peripherals are switchable but the others are secure-only (such as SID). It means that secure boot is not a superset and the eGON-style non-secure boot configuration can't be replicated at the moment. This may cause inconveniences for the Linux kernel (reading SID is a perfect example) and some assistance from the firmware will be required.
The currently used eGON.BT0 images can be wrapped into TOC0 containers with just a little bit of adaptation. As discussed in the irc log, a small extra stub code added to the TOC0 container can ensure that the eGON.BT0 image lands at the start of SRAM A1 and also the boot media identifier can be patched there at the expected offset 0x28. This way the differences between eGON.BT0 and TOC0 can be fully hidden and we can continue using eGON.BT0 as a single unified format for the SPL on Allwinner devices (the 32K size restriction can be lifted though).
The task of converting from eGON.BT0 to TOC0 can be delegated to a smart flasher tool. For example, sunxi-fel can check whether the secure boot bit is set in LCJS and automatically do conversion into the TOC0 format when flashing SPI NOR. As an additional safety measure, it's also best if the tool can verify that the device is not locked down to only accept some particular key.
U-Boot build may produce images in both formats, but a smart flasher tool can interchangeably use either of them if the conversion is straightforward (this way a possible user error is eliminated).
This all is very similar in principle to what we have already done with FEL USB boot support (eliminate a special extra U-Boot build configuration and just use a single unified image for both SD card boot and FEL USB boot): https://lists.denx.de/pipermail/u-boot/2015-February/204388.html
Check if it is actually possible to configure secure peripherals after booting via eGON.BT0. Are there any other differences?
Tried to dump and compare HW registers with/without TOC0: https://gist.github.com/ssvb/88963c61c714068716412d828b9ff74e/revisions
There was a hope to maybe find some magic bit, which will allow restricting access to peripherals in non-secure mode.
This is a trivial quick and dirty test program, which can verify if such restrictions actually work as expected: https://github.com/ssvb/sunxi-tools/commit/3ffc4fbc3e5448d3390052ad2aa553afe69fefe6
Running it on Jide Remix Mini (secure bit is set in eFUSE) via "sunxi-fel spl" after "sunxi-fel smc":
Checking the current mode ... secure. Switching to non-secure ... done. Checking peripherals security ... SRAM A2 is not accessible (this is GOOD). Hello from Allwinner A64! Returning back to FEL.
Running on Pine64 (secure bit is not set in eFUSE) via "sunxi-fel spl":
Checking the current mode ... secure. Switching to non-secure ... done. Checking peripherals security ... SRAM A2 is accessible (this is BAD). Hello from Allwinner A64! Returning back to FEL.
None of the attempts to toggle various bits in various HW registers helped so far on Pine64, restricting access to SRAM A2 does not work.
Note: the test program does not work correctly on Jide Remix Mini when booting from SD card (deadlocks when trying to switch to non-secure, most likely the default settings are not the same as in FEL mode):
Checking the current mode ... secure. Switching to non-secure ...
Implement and test FEL boot for TOC0 bootloaders.
We can in principle boot TOC0 images via sunxi-fel even on boards without the secure bit set in eFUSE (and can boot oversized TOC0 images too). This just needs to be implemented.