TOC0

= TOC0 Header =

The TOC0 header is made up of a main header followed immediately by two or more item headers.

Item Header
The SBROM searches for items by name. This means item headers can appear in any order, and item headers with unknown IDs are ignored.

= TOC0 Items =

Certificate (ID 0x010101)
This is a DER-encoded X.509-like certificate containing a SHA256 hash of the firmware item. Only a few of the certificate fields are even read, and of those, some are ignored. The certificate has a similar structure to standard X.509 certificates, but is different in many important details:
 * The public key is stored in a non-standard way.
 * The hash is added at the place of extensions, but not as a real extension.
 * The last 4 bytes of the certificate (which are the last 4 bytes of the firmware digest) are not signed.
 * The signature algorithm is prepended to the signature, but the two objects are not inside a sequence.

Below is the structure of the minimal certificate that is accepted by the SBROM:

0:d=0 hl=4 l= 601 cons: SEQUENCE          # 4:d=1 hl=4 l= 330 cons:  SEQUENCE         # 8:d=2 hl=2 l=   3 cons:   cont [ 0 ]      # 10:d=3 hl=2 l=   1 prim:    INTEGER        # "version"      -- Up to 4 bytes are read (but ignored); contents must be exactly 2 bytes into the sequence 13:d=2 hl=2 l=   1 prim:   INTEGER         # "serialNumber" -- Up to 4 bytes are read (but ignored) 16:d=2 hl=2 l=   0 cons:   SEQUENCE        # "signature"    -- Up to 255 bytes are read (but ignored) 18:d=2 hl=2 l=   0 cons:   SEQUENCE        # "issuer"       -- The tag must be 0x30 (SEQUENCE), but the data is not read 20:d=2 hl=2 l=   0 cons:   SEQUENCE        # "validity"     -- The tag must be 0x30 (SEQUENCE), but the data is not read 22:d=2 hl=2 l=   0 cons:   SEQUENCE        # "subject"      -- The tag must be 0x30 (SEQUENCE), but the data is not read 24:d=2 hl=4 l= 272 cons:   SEQUENCE        # "subjectPublicKeyInfo" 28:d=3 hl=2 l=   0 cons:    SEQUENCE       # "algorithm"    -- Up to 255 bytes are read (but ignored); contents must follow exactly 6 bytes after "subject" 30:d=3 hl=4 l= 266 cons:    SEQUENCE       # 34:d=4 hl=4 l= 257 prim:     INTEGER       # Public key "n" -- Up to 400 bytes are read 295:d=4 hl=2 l=   3 prim:     INTEGER       # Public key "e" -- Up to 400 bytes are read 300:d=2 hl=2 l=  36 cons:   cont [ 3 ]      # 302:d=3 hl=2 l=  34 cons:    SEQUENCE       # 304:d=4 hl=2 l=  32 prim:     OCTET STRING  # Firmware hash  -- Contents must follow exactly 6 bytes after the key exponent 338:d=1 hl=4 l= 263 cons:  SEQUENCE         #                -- The tag must be 0x03 (BIT STRING), but this is really a sequence 342:d=2 hl=2 l=   0 cons:   SEQUENCE        #                -- The tag must be 0x30 (SEQUENCE) 344:d=2 hl=4 l= 257 prim:   BIT STRING      # RSA signature  -- Up to 400 bytes are read

When the SBROM reads a 256-byte or longer object such as the public key modulus or signature (i.e. when the first length byte is 0x82), if the length is odd, the first byte is ignored. This means a modulus with the high bit set can either be encoded as a negative INTEGER, or zero-extended to 257 bytes to remain positive. Both encodings are accepted. Similarly, the "bits remaining" field at the beginning of the inner signature BIT STRING is ignored. This does not apply to the outer signature (fake) "BIT STRING", which must not have a "bits remaining" field at all.

If the SBROM is configured to use a key item (this is the default on the H6; it is unavailable on older SoCs), the certificate must include (and be signed by) "KEY1" from the key item. Otherwise, it must include (and be signed by) the ROTPK. To make a TOC0 that will be accepted either way, fill both slots in the key item with the ROTPK.

If a ROTPK_HASH is programmed, only certificates (or key items) with that key are accepted. Otherwise, any key you generate can be the ROTPK.

Certificates generated by the BSP do not use padding when computing the signature. However, because the SBROM only looks at the least-significant 32 bytes of the decrypted signature, any padding algorithm can be used.

While the SBROM will read and parse a certificate containing 3072-bit RSA keys, it is hardcoded to perform 2048-bit modular arithmetic, so such a certificate will fail to verify.

Firmware Item (ID 0x010202)
This item has no specific format. After verifying against the SHA256 hash in the certificate, this item is memmove'd to the run address given in the item header and executed in secure mode. This run address can be anywhere in SRAM, except where it would overwrite the SBROM's stack. Note that the memmove happens after the SBROM stores the boot device in the main header, so if the run address is in the first 0x24 bytes of SRAM A1, that information will be lost.

The only format requirement is that the item, as stored in the TOC0 image, must start and end on a 32-byte boundary. This requirement comes from the SHA256 implementation, which expects full, aligned blocks.

Key Item (ID 0x10303)
This item links two public keys together. It is signed by the first key. If the VENDOR_ID eFUSE is programmed, the VENDOR_ID value in the key item is checked against it.

The key item has the following format:

= TOC0 images generation =

There is a preliminary script: https://gist.github.com/jemk/2abcab1359c4bce793679c5854062331

Some notes:
 * 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 =

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

U-Boot build generates a single unified "payload". And sunxi-tools provides a suitable "delivery" method :-)

= ToDo =

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.