starfive-tech / Tools

19 stars 6 forks source link

document OTP fuses and secure boot flow #8

Open orangecms opened 10 months ago

orangecms commented 10 months ago

In #1, we discussed the basics of creating a valid header for a simple boot.

Still open is

For reference:

I would like to provision my JH7110 SoCs with secure boot and need this documentation. I can write the tools for that myself and would integrate support in oreboot.

orangecms commented 10 months ago

From the forum post:

  • The recovery tool has (unsurprisingly) a header that is similar to that added by the spl_tool. However, it is not exact. For example the version field is 0x01010001 instead of 0x01010101. Is there any significance to that? There is also some further data at offset 0x2d4, that is not specified in the spl_tool source. Does anybody have a more complete header description at hand?

  • The recovery tool has a menu option to blow OTP fuses. Is there any documentation about these fuses?

  • The spl_tool source file says that the header is discussed on the page “Creating SPL File 1”. However, on this page there is no discussion of the header and its contents

orangecms commented 10 months ago

dd if=jh7110-devkits-recovery-20230918.bin bs=1024 count=1 | hexdump -C

00000000  40 02 00 00 00 00 20 00  00 00 00 00 00 00 00 00  |@..... .........|
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000280  00 00 00 00 01 00 01 01  10 72 03 00 00 04 00 00  |.........r......|
00000290  a0 3a d2 53 00 00 00 00  00 00 00 00 00 00 00 00  |.:.S............|
000002a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000002d0  00 00 00 00 10 72 03 00  01 00 00 00 00 00 00 00  |.....r..........|
000002e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
orangecms commented 10 months ago

dd bs=1024 count=1 if=jh7110-recovery-20230322.bin | hexdump -C

00000000  40 02 00 00 00 00 20 00  00 00 00 00 00 00 00 00  |@..... .........|
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000280  00 00 00 00 01 00 01 01  a0 81 02 00 00 04 00 00  |................|
00000290  30 43 4d 78 00 00 00 00  00 00 00 00 00 00 00 00  |0CMx............|
000002a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000002d0  00 00 00 00 a0 81 02 00  01 00 00 00 00 00 00 00  |................|
000002e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
orangecms commented 10 months ago

dd bs=1024 count=1 if=jh7110-recovery-20221205.bin | hexdump -C

00000000  40 02 00 00 00 00 20 00  00 00 00 00 00 00 00 00  |@..... .........|
00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000280  00 00 00 00 01 00 01 01  f0 81 02 00 00 04 00 00  |................|
00000290  f5 dc e1 a4 00 00 00 00  00 00 00 00 00 00 00 00  |................|
000002a0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
000002d0  00 00 00 00 f0 81 02 00  01 00 00 00 00 00 00 00  |................|
000002e0  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
orangecms commented 10 months ago

Related forum post: https://forum.rvspace.org/t/is-the-source-code-for-the-boot-rom-for-the-visionfive-2-available/1918/46

orangecms commented 10 months ago

The manual says that something called OTPC is here (table 2-4 System Memory Map):

Start Address End Address Size Attribute Device/Description
0x00_1705_0000 0x00_1705_FFFF 64KB RW A OTPC
orangecms commented 9 months ago

I was able to get a bunch of data into the create_hdr tool. It outputs a "common header" and the "sbl header". A configuration file is needed to hold all the data.

A full header is 0x400 in site; I am not sure why the common and sbl header are split as 0x240 + 0x160 0x180 bytes, What is in the sbl header starts at 0x280 in a real image. Those 0x40 in between might be taken from the end of the sbl header, or those last 0x40 bytes there are meaningless... that's an open thing to figure out. EDIT: Those 0x40 in between come from something called sig(nature?) padding, and so 0x240 + 0x040 + 0x180 add up to 0x400.

Here is my config; not everything shows up though, maybe the tool wasn't that complete?

create_hdr.cfg

# path of sbl firmware, can be overrided by "--bin file" argument
SBL_BIN = test/test1.bin

# version of sbl firmware, can be overrided by "--version val" argument
# Endian: Little
SBL_VER = 0x01010001

# Offset of backup SBL from Flash info start
# Endian: Little
SBL_BAK_OFFSET = 0x200000

OUTPUT_COMMON_HDR_FILENAME = test/common.hdr.out
OUTPUT_SBL_HDR_FILENAME = test/sbl.hdr
OUTPUT_SBL_HASH_FILENAME = test/sbl.hash

# unused ?
OUTPUT_SBL_BIN_CIPHER_FILENAME = test/sbl.bin.cipher
OUTPUT_SBL_SIG_FILENAME = test/sbl.sig
OUTPUT_SBL_VER_CIPHER_FILENAME = test/sbl_ver.cipher
OUTPUT_EC_KEY_REVOKE_CIPHER_FILENAME = test/ec_key_revoke.cipher
OUTPUT_AES_IV_FILENAME = test/aes_iv
# OTP secret?
OUTPUT_OTPSCR_FILENAME = test/otp_scr

# TODO
CHIP_UID = 0x01234567

# TODO
DBG_KEY      = 0x1337
CHIP_MODE    = 1
FL_SECBT     = 2

# TODO
AES_KEY = "abcd"
AES_IV  = 0x00001234,0x00001234,0x00001234,0x00001234

# Key select for multiple pri(vate) keys?
EC_KEY_SEL   = 3

# TODO
EC_KEY_REVOKE = 2,1,4,3

EC_PARAM_P   = 0x22002020,0x22002020,0x22002020,0x22002020,0x22002020,0x22002020,0x22002020,0x22002020
EC_PARAM_A   = 0x33003030,0x33003030,0x33003030,0x33003030,0x33003030,0x33003030,0x33003030,0x33003030
EC_PARAM_B   = 0x44004040,0x44004040,0x44004040,0x44004040,0x44004040,0x44004040,0x44004040,0x44004040
EC_PARAM_GX  = 0x55005050,0x55005050,0x55005050,0x55005050,0x55005050,0x55005050,0x55005050,0x55005050
EC_PARAM_GY  = 0x66006060,0x66006060,0x66006060,0x66006060,0x66006060,0x66006060,0x66006060,0x66006060
EC_PARAM_N   = 0x77007070,0x77007070,0x77007070,0x77007070,0x77007070,0x77007070,0x77007070,0x77007070
EC_SIGN_KINV = 0x88008080,0x88008080,0x88008080,0x88008080,0x88008080,0x88008080,0x88008080,0x88008080

# ??
EC_SIGN_RP   = 0x99009090,0x99009090,0x99009090,0x99009090,0x99009090,0x99009090,0x99009090,0x99009090

# How does this work?
# https://developers.yubico.com/PIV/Guides/Generating_keys_using_OpenSSL.html
# openssl ecparam -name prime256v1 -genkey -noout -out key.pem
EC_PRI_KEYS  = "test/ec_key.pem","test/ec_key.pem","test/ec_key.pem","test/ec_key.pem"

invoked like this: create_hdr create_hdr.cfg

common

the funny numbers are the EC parameters

the last 4x 0x40 are the EC keys, which the tool reads from PEM files; to create them: openssl ecparam -name prime256v1 -genkey -noout -out key.pem

xxd common.hdr.out
00000000: 4002 0000 0000 2000 0000 0000 0000 0000  @..... .........
00000010: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000040: 2020 0022 2020 0022 2020 0022 2020 0022    ."  ."  ."  ."
00000050: 2020 0022 2020 0022 2020 0022 2020 0022    ."  ."  ."  ."
00000060: 3030 0033 3030 0033 3030 0033 3030 0033  00.300.300.300.3
00000070: 3030 0033 3030 0033 3030 0033 3030 0033  00.300.300.300.3
00000080: 4040 0044 4040 0044 4040 0044 4040 0044  @@.D@@.D@@.D@@.D
00000090: 4040 0044 4040 0044 4040 0044 4040 0044  @@.D@@.D@@.D@@.D
000000a0: 5050 0055 5050 0055 5050 0055 5050 0055  PP.UPP.UPP.UPP.U
000000b0: 5050 0055 5050 0055 5050 0055 5050 0055  PP.UPP.UPP.UPP.U
000000c0: 6060 0066 6060 0066 6060 0066 6060 0066  ``.f``.f``.f``.f
000000d0: 6060 0066 6060 0066 6060 0066 6060 0066  ``.f``.f``.f``.f
000000e0: 7070 0077 7070 0077 7070 0077 7070 0077  pp.wpp.wpp.wpp.w
000000f0: 7070 0077 7070 0077 7070 0077 7070 0077  pp.wpp.wpp.wpp.w
00000100: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000110: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000120: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000130: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000140: cae7 0af0 8dcf 9381 5549 2d2a 0f85 f57d  ........UI-*...}
00000150: 6cd1 8252 76df f475 2319 9913 4f98 fdc9  l..Rv..u#...O...
00000160: 7677 fa40 5b21 777c 96c6 d85e 905d cf17  vw.@[!w|...^.]..
00000170: 0abc 68fe 6bc3 231e bd98 bb49 224c a2c4  ..h.k.#....I"L..
00000180: cae7 0af0 8dcf 9381 5549 2d2a 0f85 f57d  ........UI-*...}
00000190: 6cd1 8252 76df f475 2319 9913 4f98 fdc9  l..Rv..u#...O...
000001a0: 7677 fa40 5b21 777c 96c6 d85e 905d cf17  vw.@[!w|...^.]..
000001b0: 0abc 68fe 6bc3 231e bd98 bb49 224c a2c4  ..h.k.#....I"L..
000001c0: cae7 0af0 8dcf 9381 5549 2d2a 0f85 f57d  ........UI-*...}
000001d0: 6cd1 8252 76df f475 2319 9913 4f98 fdc9  l..Rv..u#...O...
000001e0: 7677 fa40 5b21 777c 96c6 d85e 905d cf17  vw.@[!w|...^.]..
000001f0: 0abc 68fe 6bc3 231e bd98 bb49 224c a2c4  ..h.k.#....I"L..
00000200: cae7 0af0 8dcf 9381 5549 2d2a 0f85 f57d  ........UI-*...}
00000210: 6cd1 8252 76df f475 2319 9913 4f98 fdc9  l..Rv..u#...O...
00000220: 7677 fa40 5b21 777c 96c6 d85e 905d cf17  vw.@[!w|...^.]..
00000230: 0abc 68fe 6bc3 231e bd98 bb49 224c a2c4  ..h.k.#....I"L..

sbl

The first field here is the EC key to use; it can be 0 (none) or some value of 1, 2, 3, 4; otherwise, the mask ROM loader would reject, from what I can tell. This would be at 0x280 in a full image.

xxd sbl.hdr
00000000: 0300 0000 0100 0101 0000 4000 0004 0000  ..........@.....
00000010: fab3 4d89 0000 0000 0000 0000 0000 0000  ..M.............
00000020: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000030: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000040: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000050: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000060: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000070: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000080: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000090: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000a0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000b0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000c0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000d0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000e0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
000000f0: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000100: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000110: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000120: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000130: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000140: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000150: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000160: 0000 0000 0000 0000 0000 0000 0000 0000  ................
00000170: 0000 0000 0000 0000 0000 0000 0000 0000  ................
orangecms commented 9 months ago

addendum to the sbl part:

the next fields after the checksum are

orangecms commented 9 months ago

On the EC keys

creating a key is also described in the config file I provided above; taken from

https://developers.yubico.com/PIV/Guides/Generating_keys_using_OpenSSL.html

openssl ecparam -name prime256v1 -genkey -noout -out key.pem

Anyway, that gets us a PEM format file like this:

-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIFpLCSboXWMteMtlhSgFneRRdizkaIKOSfTXhE1VapDxoAoGCCqGSM49
AwEHoUQDQgAEyf2YTxOZGSN19N92UoLRbH31hQ8qLUlVgZPPjfAK58rEokwiSbuY
vR4jw2v+aLwKF89dkF7YxpZ8dyFbQPp3dg==
-----END EC PRIVATE KEY-----

let's quick-hack that to a hex representation

https://gchq.github.io/CyberChef/#recipe=From_Base64('A-Za-z0-9%2B/%3D',true,false)To_Hex('Space',0)

I've marked the relevant parts

30  77  02  01  01  04  20  5a  4b  09  26  e8  5d  63  2d  78
cb  65  85  28  05  9d  e4  51  76  2c  e4  68  82  8e  49  f4
d7  84  4d  55  6a  90  f1  a0  0a  06  08  2a  86  48  ce  3d
03  01  07  a1  44  03  42  00  04 [c9  fd  98  4f  13  99  19
23  75  f4  df  76  52  82  d1  6c  7d  f5  85  0f  2a  2d  49
55  81  93  cf  8d  f0  0a  e7  ca][c4  a2  4c  22  49  bb  98
bd  1e  23  c3  6b  fe  68  bc  0a  17  cf  5d  90  5e  d8  c6
96  7c  77  21  5b  40  fa  77  76]

looking at the resulting header file, it appears that the last two parts are just reversed, respectively, which becomes

cae7 0af0 8dcf 9381 5549 2d2a 0f85 f57d  ........UI-*...}
6cd1 8252 76df f475 2319 9913 4f98 fdc9  l..Rv..u#...O...
7677 fa40 5b21 777c 96c6 d85e 905d cf17  vw.@[!w|...^.]..
0abc 68fe 6bc3 231e bd98 bb49 224c a2c4  ..h.k.#....I"L..

now I have no clue what exactly the pieces are; just look at then OpenSSL manual or whatever or recap your crypto intro 101 on elliptic curves to get an idea :-)

Addendum: okay so OpenSSL 101... the pub key has a fixed 04 at the beginning generate multiple keys to check:

openssl ecparam -name prime256v1 -genkey -text | tail -n 5 | openssl ec -text

Note that the format is not too excellent for human processing; it is 15 bytes per line. Now, here is our key, just as we got it with CyberChef:

openssl ec -in test/ec_key.pem -text
read EC key
Private-Key: (256 bit)
priv:
    5a:4b:09:26:e8:5d:63:2d:78:cb:65:85:28:05:9d:
    e4:51:76:2c:e4:68:82:8e:49:f4:d7:84:4d:55:6a:
    90:f1
pub:
    04:c9:fd:98:4f:13:99:19:23:75:f4:df:76:52:82:
    d1:6c:7d:f5:85:0f:2a:2d:49:55:81:93:cf:8d:f0:
    0a:e7:ca:c4:a2:4c:22:49:bb:98:bd:1e:23:c3:6b:
    fe:68:bc:0a:17:cf:5d:90:5e:d8:c6:96:7c:77:21:
    5b:40:fa:77:76
ASN1 OID: prime256v1
NIST CURVE: P-256
writing EC key
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIFpLCSboXWMteMtlhSgFneRRdizkaIKOSfTXhE1VapDxoAoGCCqGSM49
AwEHoUQDQgAEyf2YTxOZGSN19N92UoLRbH31hQ8qLUlVgZPPjfAK58rEokwiSbuY
vR4jw2v+aLwKF89dkF7YxpZ8dyFbQPp3dg==
-----END EC PRIVATE KEY-----
orangecms commented 9 months ago

On the OTPC block again

OTPC = One-Time Programmable Configuration

0x1705_0000 64k

I read this as blocks of 64x 32 bytes each, which has repeating entries for me on a Milk-V Mars.

This here is repeated 64 times, the first block

0b040200 03000000  00000000 00000000  00000000 21000000  00000000 ffffffff

Note: 0x21000000 is the QSPI XIP memory; this might be coincidental, here it'd be BE

then comes 64 times all ffffffff...

and then a block of this 64 times

0b040200 03000000  00000000 00000000  00000000 ff010000  00000000 ffffffff

then all ffffffff... again, and then the above block again, all-ffffffff again, and those repeat to the end.

Note: From the mask ROM, I can tell that there is a fixed function just writing the value 0x0002040b to the OTPC base address which you see repeated in the beginning as pointed out above. That function is called as part of the OTP init, right in the beginning after GPIO, UART and CLINT init, before evaluating the boot source.

boot modes

If XIP flash is disabled in OTP configuration, system cannot boot from XIP flash.

The On-Chip boot ROM is 32 KB.

The boot mode and boot options could be loaded from the SYSCON status registers.

from the manual, Table 2-2 System Boot Process

Processor Boot Mode (PAD_RGPIO2) Boot Vector Boot Selection (PAD_RGPIO[1:0])
U74MC 0x1 0x2100_0000 XIP Flash
0x0 0x2A00_0000 0x0: Boot from 1-bit QSPI instead of flash
0x1: Boot from SDIO3.0
0x2: Boot from EMMC5.1
0x3: Boot from UART
Audio DSP Defined by U74MC or E24
E24 Defined by U74MC

my OTP config in AON_SYSCON (24 onwards):

   00000000000000000000000000000111
   11111111111111111111111111111111
   11111111111111111111111111111111
   11111111111111111111111111111111
   00000000000000000000000010110010
first line: u0_otpc_chip_mode, u0_otpc_crc_pass and u0_otpc_dbg_enable are all set second: u0_otpc_fl_func_lock (all 0 by default) third: u0_otpc_fl_pll0_lock (all 0 by default) fourth: u0_otpc_fl_pll1_lock (all 0 by default) last: bit name mode default
[1] u0_otpc_fl_xip RO 0x0
[2] u0_otpc_load_busy RO 0x0
[3] u0_reset_ctrl_clr_reset_status WR 0x0
[4] u0_reset_ctrl_pll_timecnt_finish RO 0x0
[5] u0_reset_ctrl_rstn_sw WR 0x1
[9:6] u0_reset_ctrl_sys_reset_status RO 0x0
orangecms commented 9 months ago

@MichaelZhuxx any comment on this so far? This is very tedious... would be great to have some support here. :grimacing: Thank you!