The patch implements secure booting for the mvebu architecture. This includes: - The addition of secure headers and all needed signatures and keys in mkimage - Commands capable of writing the board's efuses to both write the needed cryptographic data and enable the secure booting mechanism - The creation of convenience text files containing the necessary commands to write the efuses The KAK and CSK keys are expected to reside in the files kwb_kak.key and kwb_csk.key (OpenSSL 2048 bit private keys) in the top-level directory. Signed-off-by: Reinhard Pfau <reinhard.pfau@gdsys.cc> Signed-off-by: Mario Six <mario.six@gdsys.cc> Reviewed-by: Stefan Roese <sr@denx.de> Reviewed-by: Simon Glass <sjg@chromium.org> Signed-off-by: Stefan Roese <sr@denx.de>master
parent
4991b4f7f1
commit
a1b6b0a9c1
@ -0,0 +1,264 @@ |
||||
/*
|
||||
* Copyright (C) 2015-2016 Reinhard Pfau <reinhard.pfau@gdsys.cc> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#include <config.h> |
||||
#include <common.h> |
||||
#include <errno.h> |
||||
#include <asm/io.h> |
||||
#include <asm/arch/cpu.h> |
||||
#include <asm/arch/efuse.h> |
||||
#include <asm/arch/soc.h> |
||||
#include <linux/mbus.h> |
||||
|
||||
#if defined(CONFIG_MVEBU_EFUSE_FAKE) |
||||
#define DRY_RUN |
||||
#else |
||||
#undef DRY_RUN |
||||
#endif |
||||
|
||||
#define MBUS_EFUSE_BASE 0xF6000000 |
||||
#define MBUS_EFUSE_SIZE BIT(20) |
||||
|
||||
#define MVEBU_EFUSE_CONTROL (MVEBU_REGISTER(0xE4008)) |
||||
|
||||
enum { |
||||
MVEBU_EFUSE_CTRL_PROGRAM_ENABLE = (1 << 31), |
||||
}; |
||||
|
||||
struct mvebu_hd_efuse { |
||||
u32 bits_31_0; |
||||
u32 bits_63_32; |
||||
u32 bit64; |
||||
u32 reserved0; |
||||
}; |
||||
|
||||
#ifndef DRY_RUN |
||||
static struct mvebu_hd_efuse *efuses = |
||||
(struct mvebu_hd_efuse *)(MBUS_EFUSE_BASE + 0xF9000); |
||||
#else |
||||
static struct mvebu_hd_efuse efuses[EFUSE_LINE_MAX + 1]; |
||||
#endif |
||||
|
||||
static int efuse_initialised; |
||||
|
||||
static struct mvebu_hd_efuse *get_efuse_line(int nr) |
||||
{ |
||||
if (nr < 0 || nr > 63 || !efuse_initialised) |
||||
return NULL; |
||||
|
||||
return efuses + nr; |
||||
} |
||||
|
||||
static void enable_efuse_program(void) |
||||
{ |
||||
#ifndef DRY_RUN |
||||
setbits_le32(MVEBU_EFUSE_CONTROL, MVEBU_EFUSE_CTRL_PROGRAM_ENABLE); |
||||
#endif |
||||
} |
||||
|
||||
static void disable_efuse_program(void) |
||||
{ |
||||
#ifndef DRY_RUN |
||||
clrbits_le32(MVEBU_EFUSE_CONTROL, MVEBU_EFUSE_CTRL_PROGRAM_ENABLE); |
||||
#endif |
||||
} |
||||
|
||||
static int do_prog_efuse(struct mvebu_hd_efuse *efuse, |
||||
struct efuse_val *new_val, u32 mask0, u32 mask1) |
||||
{ |
||||
struct efuse_val val; |
||||
|
||||
val.dwords.d[0] = readl(&efuse->bits_31_0); |
||||
val.dwords.d[1] = readl(&efuse->bits_63_32); |
||||
val.lock = readl(&efuse->bit64); |
||||
|
||||
if (val.lock & 1) |
||||
return -EPERM; |
||||
|
||||
val.dwords.d[0] |= (new_val->dwords.d[0] & mask0); |
||||
val.dwords.d[1] |= (new_val->dwords.d[1] & mask1); |
||||
val.lock |= new_val->lock; |
||||
|
||||
writel(val.dwords.d[0], &efuse->bits_31_0); |
||||
mdelay(1); |
||||
writel(val.dwords.d[1], &efuse->bits_63_32); |
||||
mdelay(1); |
||||
writel(val.lock, &efuse->bit64); |
||||
mdelay(5); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int prog_efuse(int nr, struct efuse_val *new_val, u32 mask0, u32 mask1) |
||||
{ |
||||
struct mvebu_hd_efuse *efuse; |
||||
int res = 0; |
||||
|
||||
res = mvebu_efuse_init_hw(); |
||||
if (res) |
||||
return res; |
||||
|
||||
efuse = get_efuse_line(nr); |
||||
if (!efuse) |
||||
return -ENODEV; |
||||
|
||||
if (!new_val) |
||||
return -EINVAL; |
||||
|
||||
/* only write a fuse line with lock bit */ |
||||
if (!new_val->lock) |
||||
return -EINVAL; |
||||
|
||||
/* according to specs ECC protection bits must be 0 on write */ |
||||
if (new_val->bytes.d[7] & 0xFE) |
||||
return -EINVAL; |
||||
|
||||
if (!new_val->dwords.d[0] && !new_val->dwords.d[1] && (mask0 | mask1)) |
||||
return 0; |
||||
|
||||
enable_efuse_program(); |
||||
|
||||
res = do_prog_efuse(efuse, new_val, mask0, mask1); |
||||
|
||||
disable_efuse_program(); |
||||
|
||||
return res; |
||||
} |
||||
|
||||
int mvebu_efuse_init_hw(void) |
||||
{ |
||||
int ret; |
||||
|
||||
if (efuse_initialised) |
||||
return 0; |
||||
|
||||
ret = mvebu_mbus_add_window_by_id( |
||||
CPU_TARGET_SATA23_DFX, 0xA, MBUS_EFUSE_BASE, MBUS_EFUSE_SIZE); |
||||
|
||||
if (ret) |
||||
return ret; |
||||
|
||||
efuse_initialised = 1; |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
int mvebu_read_efuse(int nr, struct efuse_val *val) |
||||
{ |
||||
struct mvebu_hd_efuse *efuse; |
||||
int res; |
||||
|
||||
res = mvebu_efuse_init_hw(); |
||||
if (res) |
||||
return res; |
||||
|
||||
efuse = get_efuse_line(nr); |
||||
if (!efuse) |
||||
return -ENODEV; |
||||
|
||||
if (!val) |
||||
return -EINVAL; |
||||
|
||||
val->dwords.d[0] = readl(&efuse->bits_31_0); |
||||
val->dwords.d[1] = readl(&efuse->bits_63_32); |
||||
val->lock = readl(&efuse->bit64); |
||||
return 0; |
||||
} |
||||
|
||||
int mvebu_write_efuse(int nr, struct efuse_val *val) |
||||
{ |
||||
return prog_efuse(nr, val, ~0, ~0); |
||||
} |
||||
|
||||
int mvebu_lock_efuse(int nr) |
||||
{ |
||||
struct efuse_val val = { |
||||
.lock = 1, |
||||
}; |
||||
|
||||
return prog_efuse(nr, &val, 0, 0); |
||||
} |
||||
|
||||
/*
|
||||
* wrapper funcs providing the fuse API |
||||
* |
||||
* we use the following mapping: |
||||
* "bank" -> eFuse line |
||||
* "word" -> 0: bits 0-31 |
||||
* 1: bits 32-63 |
||||
* 2: bit 64 (lock) |
||||
*/ |
||||
|
||||
static struct efuse_val prog_val; |
||||
static int valid_prog_words; |
||||
|
||||
int fuse_read(u32 bank, u32 word, u32 *val) |
||||
{ |
||||
struct efuse_val fuse_line; |
||||
int res; |
||||
|
||||
if (bank < EFUSE_LINE_MIN || bank > EFUSE_LINE_MAX || word > 2) |
||||
return -EINVAL; |
||||
|
||||
res = mvebu_read_efuse(bank, &fuse_line); |
||||
if (res) |
||||
return res; |
||||
|
||||
if (word < 2) |
||||
*val = fuse_line.dwords.d[word]; |
||||
else |
||||
*val = fuse_line.lock; |
||||
|
||||
return res; |
||||
} |
||||
|
||||
int fuse_sense(u32 bank, u32 word, u32 *val) |
||||
{ |
||||
/* not supported */ |
||||
return -ENOSYS; |
||||
} |
||||
|
||||
int fuse_prog(u32 bank, u32 word, u32 val) |
||||
{ |
||||
int res = 0; |
||||
|
||||
/*
|
||||
* NOTE: Fuse line should be written as whole. |
||||
* So how can we do that with this API? |
||||
* For now: remember values for word == 0 and word == 1 and write the |
||||
* whole line when word == 2. |
||||
* This implies that we always require all 3 fuse prog cmds (one for |
||||
* for each word) to write a single fuse line. |
||||
* Exception is a single write to word 2 which will lock the fuse line. |
||||
* |
||||
* Hope that will be OK. |
||||
*/ |
||||
|
||||
if (bank < EFUSE_LINE_MIN || bank > EFUSE_LINE_MAX || word > 2) |
||||
return -EINVAL; |
||||
|
||||
if (word < 2) { |
||||
prog_val.dwords.d[word] = val; |
||||
valid_prog_words |= (1 << word); |
||||
} else if ((valid_prog_words & 3) == 0 && val) { |
||||
res = mvebu_lock_efuse(bank); |
||||
valid_prog_words = 0; |
||||
} else if ((valid_prog_words & 3) != 3 || !val) { |
||||
res = -EINVAL; |
||||
} else { |
||||
prog_val.lock = val != 0; |
||||
res = mvebu_write_efuse(bank, &prog_val); |
||||
valid_prog_words = 0; |
||||
} |
||||
|
||||
return res; |
||||
} |
||||
|
||||
int fuse_override(u32 bank, u32 word, u32 val) |
||||
{ |
||||
/* not supported */ |
||||
return -ENOSYS; |
||||
} |
@ -0,0 +1,69 @@ |
||||
/*
|
||||
* Copyright (C) 2015 Reinhard Pfau <reinhard.pfau@gdsys.cc> |
||||
* |
||||
* SPDX-License-Identifier: GPL-2.0+ |
||||
*/ |
||||
|
||||
#ifndef _MVEBU_EFUSE_H |
||||
#define _MVEBU_EFUSE_H |
||||
|
||||
#include <common.h> |
||||
|
||||
struct efuse_val { |
||||
union { |
||||
struct { |
||||
u8 d[8]; |
||||
} bytes; |
||||
struct { |
||||
u16 d[4]; |
||||
} words; |
||||
struct { |
||||
u32 d[2]; |
||||
} dwords; |
||||
}; |
||||
u32 lock; |
||||
}; |
||||
|
||||
#if defined(CONFIG_ARMADA_38X) |
||||
|
||||
enum efuse_line { |
||||
EFUSE_LINE_SECURE_BOOT = 24, |
||||
EFUSE_LINE_PUBKEY_DIGEST_0 = 26, |
||||
EFUSE_LINE_PUBKEY_DIGEST_1 = 27, |
||||
EFUSE_LINE_PUBKEY_DIGEST_2 = 28, |
||||
EFUSE_LINE_PUBKEY_DIGEST_3 = 29, |
||||
EFUSE_LINE_PUBKEY_DIGEST_4 = 30, |
||||
EFUSE_LINE_CSK_0_VALID = 31, |
||||
EFUSE_LINE_CSK_1_VALID = 32, |
||||
EFUSE_LINE_CSK_2_VALID = 33, |
||||
EFUSE_LINE_CSK_3_VALID = 34, |
||||
EFUSE_LINE_CSK_4_VALID = 35, |
||||
EFUSE_LINE_CSK_5_VALID = 36, |
||||
EFUSE_LINE_CSK_6_VALID = 37, |
||||
EFUSE_LINE_CSK_7_VALID = 38, |
||||
EFUSE_LINE_CSK_8_VALID = 39, |
||||
EFUSE_LINE_CSK_9_VALID = 40, |
||||
EFUSE_LINE_CSK_10_VALID = 41, |
||||
EFUSE_LINE_CSK_11_VALID = 42, |
||||
EFUSE_LINE_CSK_12_VALID = 43, |
||||
EFUSE_LINE_CSK_13_VALID = 44, |
||||
EFUSE_LINE_CSK_14_VALID = 45, |
||||
EFUSE_LINE_CSK_15_VALID = 46, |
||||
EFUSE_LINE_FLASH_ID = 47, |
||||
EFUSE_LINE_BOX_ID = 48, |
||||
|
||||
EFUSE_LINE_MIN = 0, |
||||
EFUSE_LINE_MAX = 63, |
||||
}; |
||||
|
||||
#endif |
||||
|
||||
int mvebu_efuse_init_hw(void); |
||||
|
||||
int mvebu_read_efuse(int nr, struct efuse_val *val); |
||||
|
||||
int mvebu_write_efuse(int nr, struct efuse_val *val); |
||||
|
||||
int mvebu_lock_efuse(int nr); |
||||
|
||||
#endif |
@ -0,0 +1,373 @@ |
||||
The trusted boot framework on Marvell Armada 38x |
||||
================================================ |
||||
|
||||
Contents: |
||||
|
||||
1. Overview of the trusted boot |
||||
2. Terminology |
||||
3. Boot image layout |
||||
4. The secured header |
||||
5. The secured boot flow |
||||
6. Usage example |
||||
7. Work to be done |
||||
8. Bibliography |
||||
|
||||
1. Overview of the trusted boot |
||||
------------------------------- |
||||
|
||||
The Armada's trusted boot framework enables the SoC to cryptographically verify |
||||
a specially prepared boot image. This can be used to establish a chain of trust |
||||
from the boot firmware all the way to the OS. |
||||
|
||||
To achieve this, the Armada SoC requires a specially prepared boot image, which |
||||
contains the relevant cryptographic data, as well as other information |
||||
pertaining to the boot process. Furthermore, a eFuse structure (a |
||||
one-time-writeable memory) need to be configured in the correct way. |
||||
|
||||
Roughly, the secure boot process works as follows: |
||||
|
||||
* Load the header block of the boot image, extract a special "root" public RSA |
||||
key from it, and verify its SHA-256 hash against a SHA-256 stored in a eFuse |
||||
field. |
||||
* Load an array of code signing public RSA keys from the header block, and |
||||
verify its RSA signature (contained in the header block as well) using the |
||||
"root" RSA key. |
||||
* Choose a code signing key, and use it to verify the header block (excluding |
||||
the key array). |
||||
* Verify the binary image's signature (contained in the header block) using the |
||||
code signing key. |
||||
* If all checks pass successfully, boot the image. |
||||
|
||||
The chain of trust is thus as follows: |
||||
|
||||
* The SHA-256 value in the eFuse field verifies the "root" public key. |
||||
* The "root" public key verifies the code signing key array. |
||||
* The selected code signing key verifies the header block and the binary image. |
||||
|
||||
In the special case of building a boot image containing U-Boot as the binary |
||||
image, which employs this trusted boot framework, the following tasks need to |
||||
be addressed: |
||||
|
||||
1. Creation of the needed cryptographic key material. |
||||
2. Creation of a conforming boot image containing the U-Boot image as binary |
||||
image. |
||||
3. Burning the necessary eFuse values. |
||||
|
||||
(1) will be addressed later, (2) will be taken care of by U-Boot's build |
||||
system (some user configuration is required, though), and for (3) the necessary |
||||
data (essentially a series of U-Boot commands to be entered at the U-Boot |
||||
command prompt) will be created by the build system as well. |
||||
|
||||
The documentation of the trusted boot mode is contained in part 1, chapter |
||||
7.2.5 in the functional specification [1], and in application note [2]. |
||||
|
||||
2. Terminology |
||||
-------------- |
||||
|
||||
CSK - Code Signing Key(s): An array of RSA key pairs, which |
||||
are used to sign and verify the secured header and the |
||||
boot loader image. |
||||
KAK - Key Authentication Key: A RSA key pair, which is used |
||||
to sign and verify the array of CSKs. |
||||
Header block - The first part of the boot image, which contains the |
||||
image's headers (also known as "headers block", "boot |
||||
header", and "image header") |
||||
eFuse - A one-time-writeable memory. |
||||
BootROM - The Armada's built-in boot firmware, which is |
||||
responsible for verifying and starting secure images. |
||||
Boot image - The complete image the SoC's boot firmware loads |
||||
(contains the header block and the binary image) |
||||
Main header - The header in the header block containing information |
||||
and data pertaining to the boot process (used for both |
||||
the regular and secured boot processes) |
||||
Binary image - The binary code payload of the boot image; in this |
||||
case the U-Boot's code (also known as "source image", |
||||
or just "image") |
||||
Secured header - The specialized header in the header block that |
||||
contains information and data pertaining to the |
||||
trusted boot (also known as "security header") |
||||
Secured boot mode - A special boot mode of the Armada SoC in which secured |
||||
images are verified (non-secure images won't boot); |
||||
the mode is activated by setting a eFuse field. |
||||
Trusted debug mode - A special mode for the trusted boot that allows |
||||
debugging of devices employing the trusted boot |
||||
framework in a secure manner (untested in the current |
||||
implementation). |
||||
Trusted boot framework - The ARMADA SoC's implementation of a secure verified |
||||
boot process. |
||||
|
||||
3. Boot image layout |
||||
-------------------- |
||||
|
||||
+-- Boot image --------------------------------------------+ |
||||
| | |
||||
| +-- Header block --------------------------------------+ | |
||||
| | Main header | | |
||||
| +------------------------------------------------------+ | |
||||
| | Secured header | | |
||||
| +------------------------------------------------------+ | |
||||
| | BIN header(s) | | |
||||
| +------------------------------------------------------+ | |
||||
| | REG header(s) | | |
||||
| +------------------------------------------------------+ | |
||||
| | Padding | | |
||||
| +------------------------------------------------------+ | |
||||
| | |
||||
| +------------------------------------------------------+ | |
||||
| | Binary image + checksum | | |
||||
| +------------------------------------------------------+ | |
||||
+----------------------------------------------------------+ |
||||
|
||||
4. The secured header |
||||
--------------------- |
||||
|
||||
For the trusted boot framework, a additional header is added to the boot image. |
||||
The following data are relevant for the secure boot: |
||||
|
||||
KAK: The KAK is contained in the secured header in the form |
||||
of a RSA-2048 public key in DER format with a length of |
||||
524 bytes. |
||||
Header block signature: The RSA signature of the header block (excluding the |
||||
CSK array), created using the selected CSK. |
||||
Binary image signature: The RSA signature of the binary image, created using |
||||
the selected CSK. |
||||
CSK array: The array of the 16 CSKs as RSA-2048 public keys in DER |
||||
format with a length of 8384 = 16 * 524 bytes. |
||||
CSK block signature: The RSA signature of the CSK array, created using the |
||||
KAK. |
||||
|
||||
NOTE: The JTAG delay, Box ID, and Flash ID header fields do play a role in the |
||||
trusted boot process to enable and configure secure debugging, but they were |
||||
not tested in the current implementation of the trusted boot in U-Boot. |
||||
|
||||
5. The secured boot flow |
||||
------------------------ |
||||
|
||||
The steps in the boot flow that are relevant for the trusted boot framework |
||||
proceed as follows: |
||||
|
||||
1) Check if trusted boot is enabled, and perform regular boot if it is not. |
||||
2) Load the secured header, and verify its checksum. |
||||
3) Select the lowest valid CSK from CSK0 to CSK15. |
||||
4) Verify the SHA-256 hash of the KAK embedded in the secured header. |
||||
5) Verify the RSA signature of the CSK block from the secured header with the |
||||
KAK. |
||||
6) Verify the header block signature (which excludes the CSK block) from the |
||||
secured header with the selected CSK. |
||||
7) Load the binary image to the main memory and verify its checksum. |
||||
8) Verify the binary image's RSA signature from the secured header with the |
||||
selected CSK. |
||||
9) Continue the boot process as in the case of the regular boot. |
||||
|
||||
NOTE: All RSA signatures are verified according to the PKCS #1 v2.1 standard |
||||
described in [3]. |
||||
|
||||
NOTE: The Box ID and Flash ID are checked after step 6, and the trusted debug |
||||
mode may be entered there, but since this mode is untested in the current |
||||
implementation, it is not described further. |
||||
|
||||
6. Usage example |
||||
---------------- |
||||
|
||||
### Create key material |
||||
|
||||
To employ the trusted boot framework, cryptographic key material needs to be |
||||
created. In the current implementation, two keys are needed to build a valid |
||||
secured boot image: The KAK private key and a CSK private key (both have to be |
||||
2048 bit RSA keys in PEM format). Note that the usage of more than one CSK is |
||||
currently not supported. |
||||
|
||||
NOTE: Since the public key can be generated from the private key, it is |
||||
sufficient to store the private key for each key pair. |
||||
|
||||
OpenSSL can be used to generate the needed files kwb_kak.key and kwb_csk.key |
||||
(the names of these files have to be configured, see the next section on |
||||
kwbimage.cfg settings): |
||||
|
||||
openssl genrsa -out kwb_kak.key 2048 |
||||
openssl genrsa -out kwb_csk.key 2048 |
||||
|
||||
The generated files have to be placed in the U-Boot root directory. |
||||
|
||||
Alternatively, instead of copying the files, symlinks to the private keys can |
||||
be placed in the U-Boot root directory. |
||||
|
||||
WARNING: Knowledge of the KAK or CSK private key would enable an attacker to |
||||
generate secured boot images containing arbitrary code. Hence, the private keys |
||||
should be carefully guarded. |
||||
|
||||
### Create/Modifiy kwbimage.cfg |
||||
|
||||
The Kirkwook architecture in U-Boot employs a special board-specific |
||||
configuration file (kwbimage.cfg), which controls various boot image settings |
||||
that are interpreted by the BootROM, such as the boot medium. The support the |
||||
trusted boot framework, several new options were added to faciliate |
||||
configuration of the secured boot. |
||||
|
||||
The configuration file's layout has been retained, only the following new |
||||
options were added: |
||||
|
||||
KAK - The name of the KAK RSA private key file in the U-Boot |
||||
root directory, without the trailing extension of ".key". |
||||
CSK - The name of the (active) CSK RSA private key file in the |
||||
U-Boot root directory, without the trailing extension of |
||||
".key". |
||||
BOX_ID - The BoxID to be used for trusted debugging (a integer |
||||
value). |
||||
FLASH_ID - The FlashID to be used for trusted debugging (a integer |
||||
value). |
||||
JTAG_DELAY - The JTAG delay to be used for trusted debugging (a |
||||
integer value). |
||||
CSK_INDEX - The index of the active CSK (a integer value). |
||||
SEC_SPECIALIZED_IMG - Flag to indicate whether to include the BoxID and FlashID |
||||
in the image (that is, whether to use the trusted debug |
||||
mode or not); no parameters. |
||||
SEC_BOOT_DEV - The boot device from which the trusted boot is allowed to |
||||
proceed, identified via a numeric ID. The tested values |
||||
are 0x34 = NOR flash, 0x31 = SDIO/MMC card; for |
||||
additional ID values, consult the documentation in [1]. |
||||
SEC_FUSE_DUMP - Dump the "fuse prog" commands necessary for writing the |
||||
correct eFuse values to a text file in the U-Boot root |
||||
directory. The parameter is the architecture for which to |
||||
dump the commands (currently only "a38x" is supported). |
||||
|
||||
The parameter values may be hardcoded into the file, but it is also possible to |
||||
employ a dynamic approach of creating a Autoconf-like kwbimage.cfg.in, then |
||||
reading configuration values from Kconfig options or from the board config |
||||
file, and generating the actual kwbimage.cfg from this template using Makefile |
||||
mechanisms (see board/gdsys/a38x/Makefile as an example for this approach). |
||||
|
||||
### Set config options |
||||
|
||||
To enable the generation of trusted boot images, the corresponding support |
||||
needs to be activated, and a index for the active CSK needs to be selected as |
||||
well. |
||||
|
||||
Furthermore, eFuse writing support has to be activated in order to burn the |
||||
eFuse structure's values (this option is just needed for programming the eFuse |
||||
structure; production boot images may disable it). |
||||
|
||||
ARM architecture |
||||
-> [*] Build image for trusted boot |
||||
(0) Index of active CSK |
||||
-> [*] Enable eFuse support |
||||
[ ] Fake eFuse access (dry run) |
||||
|
||||
### Build and test boot image |
||||
|
||||
The creation of the boot image is done via the usual invocation of make (with a |
||||
suitably set CROSS_COMPILE environment variable, of course). The resulting boot |
||||
image u-boot-spl.kwb can then be tested, if so desired. The hdrparser from [5] |
||||
can be used for this purpose. To build the tool, invoke make in the |
||||
'tools/marvell/doimage_mv' directory of [5], which builds a stand-alone |
||||
hdrparser executable. A test can be conducted by calling hdrparser with the |
||||
produced boot image and the following (mandatory) parameters: |
||||
|
||||
./hdrparser -k 0 -t u-boot-spl.kwb |
||||
|
||||
Here we assume that the CSK index is 0 and the boot image file resides in the |
||||
same directory (adapt accordingly if needed). The tool should report that all |
||||
checksums are valid ("GOOD"), that all signature verifications succeed |
||||
("PASSED"), and, finally, that the overall test was successful |
||||
("T E S T S U C C E E D E D" in the last line of output). |
||||
|
||||
### Burn eFuse structure |
||||
|
||||
+----------------------------------------------------------+ |
||||
| WARNING: Burning the eFuse structure is a irreversible | |
||||
| operation! Should wrong or corrupted values be used, the | |
||||
| board won't boot anymore, and recovery is likely | |
||||
| impossible! | |
||||
+----------------------------------------------------------+ |
||||
|
||||
After the build process has finished, and the SEC_FUSE_DUMP option was set in |
||||
the kwbimage.cfg was set, a text file kwb_fuses_a38x.txt should be present in |
||||
the U-Boot top-level directory. It contains all the necessary commands to set |
||||
the eFuse structure to the values needed for the used KAK digest, as well as |
||||
the CSK index, Flash ID and Box ID that were selected in kwbimage.cfg. |
||||
|
||||
Sequentially executing the commands in this file at the U-Boot command prompt |
||||
will write these values to the eFuse structure. |
||||
|
||||
If the SEC_FUSE_DUMP option was not set, the commands needed to burn the fuses |
||||
have to be crafted by hand. The needed fuse lines can be looked up in [1]; a |
||||
rough overview of the process is: |
||||
|
||||
* Burn the KAK public key hash. The hash itself can be found in the file |
||||
pub_kak_hash.txt in the U-Boot top-level directory; be careful to account for |
||||
the endianness! |
||||
* Burn the CSK selection, BoxID, and FlashID |
||||
* Enable trusted boot by burning the corresponding fuse (WARNING: this must be |
||||
the last fuse line written!) |
||||
* Lock the unused fuse lines |
||||
|
||||
The command to employ is the "fuse prog" command previously enabled by setting |
||||
the corresponding configuration option. |
||||
|
||||
For the trusted boot, the fuse prog command has a special syntax, since the |
||||
ARMADA SoC demands that whole fuse lines (64 bit values) have to be written as |
||||
a whole. The fuse prog command itself allows lists of 32 bit words to be |
||||
written at a time, but this is translated to a series of single 32 bit write |
||||
operations to the fuse line, where the individual 32 bit words are identified |
||||
by a "word" counter that is increased for each write. |
||||
|
||||
To work around this restriction, we interpret each line to have three "words" |
||||
(0-2): The first and second words are the values to be written to the fuse |
||||
line, and the third is a lock flag, which is supposed to lock the fuse line |
||||
when set to 1. Writes to the first and second words are memoized between |
||||
function calls, and the fuse line is only really written and locked (on writing |
||||
the third word) if both words were previously set, so that "incomplete" writes |
||||
are prevented. An exception to this is a single write to the third word (index |
||||
2) without previously writing neither the first nor the second word, which |
||||
locks the fuse line without setting any value; this is needed to lock the |
||||
unused fuse lines. |
||||
|
||||
As an example, to write the value 0011223344556677 to fuse line 10, we would |
||||
use the following command: |
||||
|
||||
fuse prog -y 10 0 00112233 44556677 1 |
||||
|
||||
Here 10 is the fuse line number, 0 is the index of the first word to be |
||||
written, 00112233 and 44556677 are the values to be written to the fuse line |
||||
(first and second word) and the trailing 1 is the value for the third word |
||||
responsible for locking the line. |
||||
|
||||
A "lock-only" command would look like this: |
||||
|
||||
fuse prog -y 11 2 1 |
||||
|
||||
Here 11 is the fuse number, 2 is the index of the first word to be written |
||||
(notice that we only write to word 2 here; the third word for fuse line |
||||
locking), and the 1 is the value for the word we are writing to. |
||||
|
||||
WARNING: According to application note [4], the VHV pin of the SoC must be |
||||
connected to a 1.8V source during eFuse programming, but *must* be disconnected |
||||
for normal operation. The AN [4] describes a software-controlled circuit (based |
||||
on a N-channel or P-channel FET and a free GPIO pin of the SoC) to achieve |
||||
this, but a jumper-based circuit should suffice as well. Regardless of the |
||||
chosen circuit, the issue needs to be addressed accordingly! |
||||
|
||||
7. Work to be done |
||||
------------------ |
||||
|
||||
* Add the ability to populate more than one CSK |
||||
* Test secure debug |
||||
* Test on Armada XP |
||||
|
||||
8. Bibliography |
||||
--------------- |
||||
|
||||
[1] ARMADA(R) 38x Family High-Performance Single/Dual CPU System on Chip |
||||
Functional Specification; MV-S109094-00, Rev. C; August 2, 2015, |
||||
Preliminary |
||||
[2] AN-383: ARMADA(R) 38x Families Secure Boot Mode Support; MV-S302501-00 |
||||
Rev. A; March 11, 2015, Preliminary |
||||
[3] Public-Key Cryptography Standards (PKCS) #1: RSA Cryptography |
||||
Specifications Version 2.1; February 2003; |
||||
https://www.ietf.org/rfc/rfc3447.txt |
||||
[4] AN-389: ARMADA(R) VHV Power; MV-S302545-00 Rev. B; January 28, 2016, |
||||
Released |
||||
[5] Marvell Armada 38x U-Boot support; November 25, 2015; |
||||
https://github.com/MarvellEmbeddedProcessors/u-boot-marvell |
||||
|
||||
2017-01-05, Mario Six <mario.six@gdsys.cc> |
Loading…
Reference in new issue