* [U-Boot] [PATCH 0/2] eMMC: support for Read Protected Memory Block (RPMB)
@ 2014-04-11 12:12 Pierre Aubert
2014-04-11 12:12 ` [U-Boot] [PATCH 1/2] eMMC: add support for operations in RPMB partition Pierre Aubert
` (5 more replies)
0 siblings, 6 replies; 31+ messages in thread
From: Pierre Aubert @ 2014-04-11 12:12 UTC (permalink / raw)
To: u-boot
This serie of patches adds some functions and a sub-command of 'mmc' for programming the authentication key and for reading and writing the RPMB partition of an eMMC according to the JEDEC standard No. 64-A441
The sub-command rpmb is enabled by the flag CONFIG_SUPPORT_EMMC_RPMB defined
in the board configuration file.
It has been tested on a SabreSDP iMX6 board.
Pierre Aubert (2):
eMMC: add support for operations in RPMB partition
eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command
common/cmd_mmc.c | 128 ++++++++++++++++++++-
drivers/mmc/Makefile | 1 +
drivers/mmc/rpmb.c | 317 ++++++++++++++++++++++++++++++++++++++++++++++++++
include/mmc.h | 10 ++-
lib/Makefile | 3 +
5 files changed, 457 insertions(+), 2 deletions(-)
create mode 100644 drivers/mmc/rpmb.c
--
1.7.6.5
^ permalink raw reply [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH 1/2] eMMC: add support for operations in RPMB partition
2014-04-11 12:12 [U-Boot] [PATCH 0/2] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
@ 2014-04-11 12:12 ` Pierre Aubert
2014-12-23 5:13 ` Roman Peniaev
2014-04-11 12:12 ` [U-Boot] [PATCH 2/2] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command Pierre Aubert
` (4 subsequent siblings)
5 siblings, 1 reply; 31+ messages in thread
From: Pierre Aubert @ 2014-04-11 12:12 UTC (permalink / raw)
To: u-boot
This patch adds functions for read, write and authentication
key programming for the Replay Protected Memory Block partition
in the eMMC.
Signed-off-by: Pierre Aubert <p.aubert@staubli.com>
CC: Pantelis Antoniou <panto@antoniou-consulting.com>
---
drivers/mmc/Makefile | 1 +
drivers/mmc/rpmb.c | 317 ++++++++++++++++++++++++++++++++++++++++++++++++++
include/mmc.h | 10 ++-
lib/Makefile | 3 +
4 files changed, 330 insertions(+), 1 deletions(-)
create mode 100644 drivers/mmc/rpmb.c
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 931922b..4c6ab9e 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_DWMMC) += dw_mmc.o
obj-$(CONFIG_EXYNOS_DWMMC) += exynos_dw_mmc.o
obj-$(CONFIG_ZYNQ_SDHCI) += zynq_sdhci.o
obj-$(CONFIG_SOCFPGA_DWMMC) += socfpga_dw_mmc.o
+obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o
ifdef CONFIG_SPL_BUILD
obj-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o
else
diff --git a/drivers/mmc/rpmb.c b/drivers/mmc/rpmb.c
new file mode 100644
index 0000000..7f9dffc
--- /dev/null
+++ b/drivers/mmc/rpmb.c
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2014, Staubli Faverges
+ * Pierre Aubert
+ *
+ * eMMC- Replay Protected Memory Block
+ * According to JEDEC Standard No. 84-A441
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <config.h>
+#include <common.h>
+#include <mmc.h>
+#include <sha256.h>
+#include "mmc_private.h"
+
+/* Request codes */
+#define RPMB_REQ_KEY 1
+#define RPMB_REQ_WCOUNTER 2
+#define RPMB_REQ_WRITE_DATA 3
+#define RPMB_REQ_READ_DATA 4
+#define RPMB_REQ_STATUS 5
+
+/* Response code */
+#define RPMB_RESP_KEY 0x0100
+#define RPMB_RESP_WCOUNTER 0x0200
+#define RPMB_RESP_WRITE_DATA 0x0300
+#define RPMB_RESP_READ_DATA 0x0400
+
+/* Error codes */
+#define RPMB_OK 0
+#define RPMB_ERR_GENERAL 1
+#define RPMB_ERR_AUTH 2
+#define RPMB_ERR_COUNTER 3
+#define RPMB_ERR_ADDRESS 4
+#define RPMB_ERR_WRITE 5
+#define RPMB_ERR_READ 6
+#define RPMB_ERR_KEY 7
+#define RPMB_ERR_CNT_EXPIRED 0x80
+#define RPMB_ERR_MSK 0x7
+
+/* Sizes of RPMB data frame */
+#define RPMB_SZ_STUFF 196
+#define RPMB_SZ_MAC 32
+#define RPMB_SZ_DATA 256
+#define RPMB_SZ_NONCE 16
+
+#define SHA256_BLOCK_SIZE 64
+
+/* Error messages */
+static const char * const rpmb_err_msg[] = {
+ "",
+ "General failure",
+ "Authentication failure",
+ "Counter failure",
+ "Address failure",
+ "Write failure",
+ "Read failure",
+ "Authentication key not yet programmed",
+};
+
+
+/* Structure of RPMB data frame. */
+static struct s_rpmb {
+ unsigned char stuff[RPMB_SZ_STUFF];
+ unsigned char mac[RPMB_SZ_MAC];
+ unsigned char data[RPMB_SZ_DATA];
+ unsigned char nonce[RPMB_SZ_NONCE];
+ unsigned long write_counter;
+ unsigned short address;
+ unsigned short block_count;
+ unsigned short result;
+ unsigned short request;
+} __aligned(32) rpmb_frame;
+
+static int mmc_set_blockcount(struct mmc *mmc, unsigned int blockcount,
+ bool is_rel_write)
+{
+ struct mmc_cmd cmd = {0};
+
+ cmd.cmdidx = MMC_CMD_SET_BLOCK_COUNT;
+ cmd.cmdarg = blockcount & 0x0000FFFF;
+ if (is_rel_write)
+ cmd.cmdarg |= 1 << 31;
+ cmd.resp_type = MMC_RSP_R1;
+
+ return mmc_send_cmd(mmc, &cmd, NULL);
+}
+static int mmc_rpmb_request(struct mmc *mmc, const struct s_rpmb *s,
+ unsigned int count, bool is_rel_write)
+{
+ struct mmc_cmd cmd = {0};
+ struct mmc_data data;
+ int ret;
+
+ ret = mmc_set_blockcount(mmc, count, is_rel_write);
+ if (ret) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:mmc_set_blockcount-> %d\n", __func__, ret);
+#endif
+ return 1;
+ }
+
+ cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
+ cmd.cmdarg = 0;
+ cmd.resp_type = MMC_RSP_R1b;
+
+ data.src = (const char *)s;
+ data.blocks = 1;
+ data.blocksize = MMC_MAX_BLOCK_LEN;
+ data.flags = MMC_DATA_WRITE;
+
+ ret = mmc_send_cmd(mmc, &cmd, &data);
+ if (ret) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:mmc_send_cmd-> %d\n", __func__, ret);
+#endif
+ return 1;
+ }
+ return 0;
+}
+static int mmc_rpmb_response(struct mmc *mmc, struct s_rpmb *s,
+ unsigned short expected)
+{
+ struct mmc_cmd cmd = {0};
+ struct mmc_data data;
+ int ret;
+
+ ret = mmc_set_blockcount(mmc, 1, false);
+ if (ret) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:mmc_set_blockcount-> %d\n", __func__, ret);
+#endif
+ return -1;
+ }
+ cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
+ cmd.cmdarg = 0;
+ cmd.resp_type = MMC_RSP_R1;
+
+ data.dest = (char *)s;
+ data.blocks = 1;
+ data.blocksize = MMC_MAX_BLOCK_LEN;
+ data.flags = MMC_DATA_READ;
+
+ ret = mmc_send_cmd(mmc, &cmd, &data);
+ if (ret) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:mmc_send_cmd-> %d\n", __func__, ret);
+#endif
+ return -1;
+ }
+ /* Check the response and the status */
+ if (be16_to_cpu(rpmb_frame.request) != expected) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:response= %x\n", __func__,
+ be16_to_cpu(rpmb_frame.request));
+#endif
+ return -1;
+ }
+ ret = be16_to_cpu(rpmb_frame.result);
+ if (ret) {
+ printf("%s %s\n", rpmb_err_msg[ret & RPMB_ERR_MSK],
+ (ret & RPMB_ERR_CNT_EXPIRED) ?
+ "Write counter has expired" : "");
+ }
+
+ /* Return the status of the command */
+ return ret;
+}
+static int mmc_rpmb_status(struct mmc *mmc, unsigned short expected)
+{
+ memset(&rpmb_frame, 0, sizeof(rpmb_frame));
+ rpmb_frame.request = cpu_to_be16(RPMB_REQ_STATUS);
+ if (mmc_rpmb_request(mmc, &rpmb_frame, 1, false))
+ return -1;
+
+ /* Read the result */
+ return mmc_rpmb_response(mmc, &rpmb_frame, expected);
+}
+static void rpmb_hmac(unsigned char *key, unsigned char *buff, int len,
+ unsigned char *output)
+{
+ sha256_context ctx;
+ int i;
+ unsigned char k_ipad[SHA256_BLOCK_SIZE];
+ unsigned char k_opad[SHA256_BLOCK_SIZE];
+
+ sha256_starts(&ctx);
+
+ /* According to RFC 4634, the HMAC transform looks like:
+ SHA(K XOR opad, SHA(K XOR ipad, text))
+
+ where K is an n byte key.
+ ipad is the byte 0x36 repeated blocksize times
+ opad is the byte 0x5c repeated blocksize times
+ and text is the data being protected.
+ */
+
+ for (i = 0; i < RPMB_SZ_MAC; i++) {
+ k_ipad[i] = key[i] ^ 0x36;
+ k_opad[i] = key[i] ^ 0x5c;
+ }
+ /* remaining pad bytes are '\0' XOR'd with ipad and opad values */
+ for ( ; i < SHA256_BLOCK_SIZE; i++) {
+ k_ipad[i] = 0x36;
+ k_opad[i] = 0x5c;
+ }
+ sha256_update(&ctx, k_ipad, SHA256_BLOCK_SIZE);
+ sha256_update(&ctx, buff, len);
+ sha256_finish(&ctx, output);
+
+ /* Init context for second pass */
+ sha256_starts(&ctx);
+
+ /* start with outer pad */
+ sha256_update(&ctx, k_opad, SHA256_BLOCK_SIZE);
+
+ /* then results of 1st hash */
+ sha256_update(&ctx, output, RPMB_SZ_MAC);
+
+ /* finish up 2nd pass */
+ sha256_finish(&ctx, output);
+}
+int mmc_rpmb_get_counter(struct mmc *mmc, unsigned long *pcounter)
+{
+ int ret;
+
+ /* Fill the request */
+ memset(&rpmb_frame, 0, sizeof(rpmb_frame));
+ rpmb_frame.request = cpu_to_be16(RPMB_REQ_WCOUNTER);
+ if (mmc_rpmb_request(mmc, &rpmb_frame, 1, false))
+ return -1;
+
+ /* Read the result */
+ ret = mmc_rpmb_response(mmc, &rpmb_frame, RPMB_RESP_WCOUNTER);
+ if (ret)
+ return ret;
+
+ *pcounter = be32_to_cpu(rpmb_frame.write_counter);
+ return 0;
+}
+int mmc_rpmb_set_key(struct mmc *mmc, void *key)
+{
+ /* Fill the request */
+ memset(&rpmb_frame, 0, sizeof(rpmb_frame));
+ rpmb_frame.request = cpu_to_be16(RPMB_REQ_KEY);
+ memcpy(&rpmb_frame.mac, key, sizeof(rpmb_frame.mac));
+
+ if (mmc_rpmb_request(mmc, &rpmb_frame, 1, true))
+ return -1;
+
+ /* read the operation status */
+ return mmc_rpmb_status(mmc, RPMB_RESP_KEY);
+}
+int mmc_rpmb_read(struct mmc *mmc, void *addr, unsigned short blk,
+ unsigned short cnt, unsigned char *key)
+{
+ int i;
+
+ for (i = 0; i < cnt; i++) {
+ /* Fill the request */
+ memset(&rpmb_frame, 0, sizeof(rpmb_frame));
+ rpmb_frame.address = cpu_to_be16(blk + i);
+ rpmb_frame.request = cpu_to_be16(RPMB_REQ_READ_DATA);
+ if (mmc_rpmb_request(mmc, &rpmb_frame, 1, false))
+ break;
+
+ /* Read the result */
+ if (mmc_rpmb_response(mmc, &rpmb_frame, RPMB_RESP_READ_DATA))
+ break;
+
+ /* Check the HMAC if key is provided */
+ if (key) {
+ unsigned char ret_hmac[RPMB_SZ_MAC];
+
+ rpmb_hmac(key, rpmb_frame.data, 284, ret_hmac);
+ if (memcmp(ret_hmac, rpmb_frame.mac, RPMB_SZ_MAC)) {
+ printf("MAC error on block #%d\n", i);
+ break;
+ }
+ }
+ /* Copy data */
+ memcpy(addr + i * RPMB_SZ_DATA, rpmb_frame.data, RPMB_SZ_DATA);
+ }
+ return i;
+}
+int mmc_rpmb_write(struct mmc *mmc, void *addr, unsigned short blk,
+ unsigned short cnt, unsigned char *key)
+{
+ unsigned long wcount;
+ int i;
+
+ for (i = 0; i < cnt; i++) {
+ if (mmc_rpmb_get_counter(mmc, &wcount)) {
+ printf("Cannot read RPMB write counter\n");
+ break;
+ }
+
+ /* Fill the request */
+ memset(&rpmb_frame, 0, sizeof(rpmb_frame));
+ memcpy(rpmb_frame.data, addr + i * RPMB_SZ_DATA, RPMB_SZ_DATA);
+ rpmb_frame.address = cpu_to_be16(blk + i);
+ rpmb_frame.block_count = cpu_to_be16(1);
+ rpmb_frame.write_counter = cpu_to_be32(wcount);
+ rpmb_frame.request = cpu_to_be16(RPMB_REQ_WRITE_DATA);
+ /* Computes HMAC */
+ rpmb_hmac(key, rpmb_frame.data, 284, rpmb_frame.mac);
+
+ if (mmc_rpmb_request(mmc, &rpmb_frame, 1, true))
+ break;
+
+ /* Get status */
+ if (mmc_rpmb_status(mmc, RPMB_RESP_WRITE_DATA))
+ break;
+ }
+ return i;
+}
diff --git a/include/mmc.h b/include/mmc.h
index 42d0125..14d296c 100644
--- a/include/mmc.h
+++ b/include/mmc.h
@@ -69,6 +69,7 @@
#define MMC_CMD_SET_BLOCKLEN 16
#define MMC_CMD_READ_SINGLE_BLOCK 17
#define MMC_CMD_READ_MULTIPLE_BLOCK 18
+#define MMC_CMD_SET_BLOCK_COUNT 23
#define MMC_CMD_WRITE_SINGLE_BLOCK 24
#define MMC_CMD_WRITE_MULTIPLE_BLOCK 25
#define MMC_CMD_ERASE_GROUP_START 35
@@ -224,6 +225,7 @@
* boot partitions (2), general purpose partitions (4) in MMC v4.4.
*/
#define MMC_NUM_BOOT_PARTITION 2
+#define MMC_PART_RPMB 3 /* RPMB partition number */
struct mmc_cid {
unsigned long psn;
@@ -335,7 +337,13 @@ int mmc_set_part_conf(struct mmc *mmc, u8 ack, u8 part_num, u8 access);
int mmc_set_boot_bus_width(struct mmc *mmc, u8 width, u8 reset, u8 mode);
/* Function to modify the RST_n_FUNCTION field of EXT_CSD */
int mmc_set_rst_n_function(struct mmc *mmc, u8 enable);
-
+/* Functions to read / write the RPMB partition */
+int mmc_rpmb_set_key(struct mmc *mmc, void *key);
+int mmc_rpmb_get_counter(struct mmc *mmc, unsigned long *counter);
+int mmc_rpmb_read(struct mmc *mmc, void *addr, unsigned short blk,
+ unsigned short cnt, unsigned char *key);
+int mmc_rpmb_write(struct mmc *mmc, void *addr, unsigned short blk,
+ unsigned short cnt, unsigned char *key);
/**
* Start device initialization and return immediately; it does not block on
* polling OCR (operation condition register) status. Then you should call
diff --git a/lib/Makefile b/lib/Makefile
index 27e4f78..2bf90f3 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -35,6 +35,9 @@ obj-y += net_utils.o
obj-$(CONFIG_PHYSMEM) += physmem.o
obj-y += qsort.o
obj-$(CONFIG_SHA1) += sha1.o
+ifdef CONFIG_SUPPORT_EMMC_RPMB
+CONFIG_SHA256 := y
+endif
obj-$(CONFIG_SHA256) += sha256.o
obj-y += strmhz.o
obj-$(CONFIG_TPM) += tpm.o
--
1.7.6.5
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH 2/2] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command
2014-04-11 12:12 [U-Boot] [PATCH 0/2] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
2014-04-11 12:12 ` [U-Boot] [PATCH 1/2] eMMC: add support for operations in RPMB partition Pierre Aubert
@ 2014-04-11 12:12 ` Pierre Aubert
2014-04-17 15:10 ` [U-Boot] [PATCH V2 0/2] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
` (3 subsequent siblings)
5 siblings, 0 replies; 31+ messages in thread
From: Pierre Aubert @ 2014-04-11 12:12 UTC (permalink / raw)
To: u-boot
This sub-command adds support for the RPMB partition of an eMMC:
* mmc rpmb key <address of the authentication key>
Programs the authentication key in the eMMC This key can not
be overwritten.
* mmc rpmb read <address> <block> <#count> [address of key]
Reads <#count> blocks of 256 bytes in the RPMB partition
beginning at block number <block>. If the optionnal
address of the authentication key is provided, the
Message Authentication Code (MAC) is verified on each
block.
* mmc rpmb write <address> <block> <#count> <address of key>
Writes <#count> blocks of 256 bytes in the RPMB partition
beginning at block number <block>. The datas are signed
with the key provided.
* mmc rpmb counter
Returns the 'Write counter' of the RPMB partition.
The sub-command is conditional on compilation flag CONFIG_SUPPORT_EMMC_RPMB
Signed-off-by: Pierre Aubert <p.aubert@staubli.com>
CC: Pantelis Antoniou <panto@antoniou-consulting.com>
---
common/cmd_mmc.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 127 insertions(+), 1 deletions(-)
diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c
index c1916c9..3cf11e7 100644
--- a/common/cmd_mmc.c
+++ b/common/cmd_mmc.c
@@ -130,7 +130,123 @@ U_BOOT_CMD(
"display MMC info",
"- display info of the current MMC device"
);
+#ifdef CONFIG_SUPPORT_EMMC_RPMB
+static int confirm_key_prog(void)
+{
+ puts("Warning: Programming authentication key can be done only once !\n"
+ " Use this command only if you are sure of what you are doing,\n"
+ "Really perform the key programming ? ");
+ if (getc() == 'y') {
+ int c;
+
+ putc('y');
+ c = getc();
+ putc('\n');
+ if (c == '\r')
+ return 1;
+ }
+ puts("Authentication key programming aborted\n");
+ return 0;
+}
+static int do_mmcrpmb(int argc, char * const argv[])
+{
+ enum rpmb_state {
+ RPMB_INVALID,
+ RPMB_READ,
+ RPMB_WRITE,
+ RPMB_KEY,
+ RPMB_COUNTER,
+ } state;
+
+ state = RPMB_INVALID;
+ if (argc == 4 && strcmp(argv[2], "key") == 0)
+ state = RPMB_KEY;
+ if ((argc == 6 || argc == 7) && strcmp(argv[2], "read") == 0)
+ state = RPMB_READ;
+ else if (argc == 7 && strcmp(argv[2], "write") == 0)
+ state = RPMB_WRITE;
+ else if (argc == 3 && strcmp(argv[2], "counter") == 0)
+ state = RPMB_COUNTER;
+
+ if (state != RPMB_INVALID) {
+ struct mmc *mmc = find_mmc_device(curr_device);
+ void *key_addr;
+ char original_part;
+ int ret;
+
+ if (!mmc) {
+ printf("no mmc device at slot %x\n", curr_device);
+ return CMD_RET_FAILURE;
+ }
+ mmc_init(mmc);
+ if (IS_SD(mmc)) {
+ printf("It is not a EMMC device\n");
+ return CMD_RET_FAILURE;
+ }
+ /* Switch to the RPMB partition */
+ original_part = mmc->part_num;
+ if (mmc->part_num != MMC_PART_RPMB) {
+ if (mmc_switch_part(curr_device, MMC_PART_RPMB) != 0)
+ return CMD_RET_FAILURE;
+ mmc->part_num = MMC_PART_RPMB;
+ }
+ ret = CMD_RET_SUCCESS;
+ if (state == RPMB_KEY) {
+ key_addr = (void *)simple_strtoul(argv[3], NULL, 16);
+ if (confirm_key_prog()) {
+ if (mmc_rpmb_set_key(mmc, key_addr)) {
+ printf("ERROR - Key already programmed ?\n");
+ ret = CMD_RET_FAILURE;
+ }
+ } else {
+ ret = CMD_RET_FAILURE;
+ }
+ } else if (state == RPMB_COUNTER) {
+ unsigned long counter;
+ if (mmc_rpmb_get_counter(mmc, &counter))
+ ret = CMD_RET_FAILURE;
+ else
+ printf("Write counter= %lx\n", counter);
+ } else {
+ u16 blk, cnt;
+ void *addr;
+ int n;
+
+ addr = (void *)simple_strtoul(argv[3], NULL, 16);
+ blk = simple_strtoul(argv[4], NULL, 16);
+ cnt = simple_strtoul(argv[5], NULL, 16);
+
+ if (state == RPMB_READ) {
+ key_addr = (argc == 7) ?
+ (void *)simple_strtoul(argv[6],
+ NULL, 16) :
+ NULL;
+ n = mmc_rpmb_read(mmc, addr, blk, cnt,
+ key_addr);
+ } else {
+ key_addr = (void *)simple_strtoul(argv[6],
+ NULL, 16);
+ n = mmc_rpmb_write(mmc, addr, blk, cnt,
+ key_addr);
+ }
+ printf("%d RPMB blocks %s: %s\n",
+ n, argv[2], (n == cnt) ? "OK" : "ERROR");
+ if (n != cnt)
+ ret = CMD_RET_FAILURE;
+ }
+
+ /* Return to orginal partition */
+ if (mmc->part_num != original_part) {
+ if (mmc_switch_part(curr_device, original_part) != 0)
+ return CMD_RET_FAILURE;
+ mmc->part_num = original_part;
+ }
+ return ret;
+ } else
+ return CMD_RET_USAGE;
+}
+#endif
static int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
enum mmc_state state;
@@ -365,6 +481,10 @@ static int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
return mmc_set_rst_n_function(mmc, enable);
#endif /* CONFIG_SUPPORT_EMMC_BOOT */
+#ifdef CONFIG_SUPPORT_EMMC_RPMB
+ } else if (strcmp(argv[1], "rpmb") == 0) {
+ return do_mmcrpmb(argc, argv);
+#endif /* CONFIG_SUPPORT_EMMC_RPMB */
}
else if (argc == 3 && strcmp(argv[1], "setdsr") == 0) {
@@ -454,7 +574,7 @@ static int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
}
U_BOOT_CMD(
- mmc, 6, 1, do_mmcops,
+ mmc, 7, 1, do_mmcops,
"MMC sub system",
"read addr blk# cnt\n"
"mmc write addr blk# cnt\n"
@@ -474,6 +594,12 @@ U_BOOT_CMD(
" - Change the RST_n_FUNCTION field of the specified device\n"
" WARNING: This is a write-once field and 0 / 1 / 2 are the only valid values.\n"
#endif
+#ifdef CONFIG_SUPPORT_EMMC_RPMB
+ "mmc rpmb read addr blk# cnt [address of auth-key] - block size is 256 bytes\n"
+ "mmc rpmb write addr blk# cnt <address of auth-key> - block size is 256 bytes\n"
+ "mmc rpmb key <address of auth-key> - program the RPMB authentication key.\n"
+ "mmc rpmb counter - read the value of the write counter\n"
+#endif
"mmc setdsr - set DSR register value\n"
);
#endif /* !CONFIG_GENERIC_MMC */
--
1.7.6.5
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V2 0/2] eMMC: support for Read Protected Memory Block (RPMB)
2014-04-11 12:12 [U-Boot] [PATCH 0/2] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
2014-04-11 12:12 ` [U-Boot] [PATCH 1/2] eMMC: add support for operations in RPMB partition Pierre Aubert
2014-04-11 12:12 ` [U-Boot] [PATCH 2/2] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command Pierre Aubert
@ 2014-04-17 15:10 ` Pierre Aubert
2014-04-17 15:10 ` [U-Boot] [PATCH V2 1/2] eMMC: add support for operations in RPMB partition Pierre Aubert
2014-04-17 15:10 ` [U-Boot] [PATCH V2 2/2] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command Pierre Aubert
2014-04-22 15:54 ` [U-Boot] [PATCH V3 0/3] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
` (2 subsequent siblings)
5 siblings, 2 replies; 31+ messages in thread
From: Pierre Aubert @ 2014-04-17 15:10 UTC (permalink / raw)
To: u-boot
This serie of patches adds some functions and a sub-command of 'mmc' for programming the authentication key and for reading and writing the RPMB partition of an eMMC according to the JEDEC standard No. 64-A441
The sub-command rpmb is enabled by the flag CONFIG_SUPPORT_EMMC_RPMB defined
in the board configuration file.
It has been tested on a SabreSDP iMX6 board.
Changes in V2:
- use ALLOC_CACHE_ALIGN_BUFFER in rpmb.c instead of a static buffer for the
RPMB frames.
Pierre Aubert (2):
eMMC: add support for operations in RPMB partition
eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command
common/cmd_mmc.c | 128 ++++++++++++++++++++-
drivers/mmc/Makefile | 1 +
drivers/mmc/rpmb.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++++++
include/mmc.h | 10 ++-
lib/Makefile | 3 +
5 files changed, 463 insertions(+), 2 deletions(-)
create mode 100644 drivers/mmc/rpmb.c
--
1.7.6.5
^ permalink raw reply [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V2 1/2] eMMC: add support for operations in RPMB partition
2014-04-17 15:10 ` [U-Boot] [PATCH V2 0/2] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
@ 2014-04-17 15:10 ` Pierre Aubert
2014-04-17 15:10 ` [U-Boot] [PATCH V2 2/2] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command Pierre Aubert
1 sibling, 0 replies; 31+ messages in thread
From: Pierre Aubert @ 2014-04-17 15:10 UTC (permalink / raw)
To: u-boot
This patch adds functions for read, write and authentication
key programming for the Replay Protected Memory Block partition
in the eMMC.
Signed-off-by: Pierre Aubert <p.aubert@staubli.com>
CC: Pantelis Antoniou <panto@antoniou-consulting.com>
---
drivers/mmc/Makefile | 1 +
drivers/mmc/rpmb.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++++++
include/mmc.h | 10 ++-
lib/Makefile | 3 +
4 files changed, 336 insertions(+), 1 deletions(-)
create mode 100644 drivers/mmc/rpmb.c
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 931922b..4c6ab9e 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_DWMMC) += dw_mmc.o
obj-$(CONFIG_EXYNOS_DWMMC) += exynos_dw_mmc.o
obj-$(CONFIG_ZYNQ_SDHCI) += zynq_sdhci.o
obj-$(CONFIG_SOCFPGA_DWMMC) += socfpga_dw_mmc.o
+obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o
ifdef CONFIG_SPL_BUILD
obj-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o
else
diff --git a/drivers/mmc/rpmb.c b/drivers/mmc/rpmb.c
new file mode 100644
index 0000000..05936f5
--- /dev/null
+++ b/drivers/mmc/rpmb.c
@@ -0,0 +1,323 @@
+/*
+ * Copyright 2014, Staubli Faverges
+ * Pierre Aubert
+ *
+ * eMMC- Replay Protected Memory Block
+ * According to JEDEC Standard No. 84-A441
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <config.h>
+#include <common.h>
+#include <mmc.h>
+#include <sha256.h>
+#include "mmc_private.h"
+
+/* Request codes */
+#define RPMB_REQ_KEY 1
+#define RPMB_REQ_WCOUNTER 2
+#define RPMB_REQ_WRITE_DATA 3
+#define RPMB_REQ_READ_DATA 4
+#define RPMB_REQ_STATUS 5
+
+/* Response code */
+#define RPMB_RESP_KEY 0x0100
+#define RPMB_RESP_WCOUNTER 0x0200
+#define RPMB_RESP_WRITE_DATA 0x0300
+#define RPMB_RESP_READ_DATA 0x0400
+
+/* Error codes */
+#define RPMB_OK 0
+#define RPMB_ERR_GENERAL 1
+#define RPMB_ERR_AUTH 2
+#define RPMB_ERR_COUNTER 3
+#define RPMB_ERR_ADDRESS 4
+#define RPMB_ERR_WRITE 5
+#define RPMB_ERR_READ 6
+#define RPMB_ERR_KEY 7
+#define RPMB_ERR_CNT_EXPIRED 0x80
+#define RPMB_ERR_MSK 0x7
+
+/* Sizes of RPMB data frame */
+#define RPMB_SZ_STUFF 196
+#define RPMB_SZ_MAC 32
+#define RPMB_SZ_DATA 256
+#define RPMB_SZ_NONCE 16
+
+#define SHA256_BLOCK_SIZE 64
+
+/* Error messages */
+static const char * const rpmb_err_msg[] = {
+ "",
+ "General failure",
+ "Authentication failure",
+ "Counter failure",
+ "Address failure",
+ "Write failure",
+ "Read failure",
+ "Authentication key not yet programmed",
+};
+
+
+/* Structure of RPMB data frame. */
+struct s_rpmb {
+ unsigned char stuff[RPMB_SZ_STUFF];
+ unsigned char mac[RPMB_SZ_MAC];
+ unsigned char data[RPMB_SZ_DATA];
+ unsigned char nonce[RPMB_SZ_NONCE];
+ unsigned long write_counter;
+ unsigned short address;
+ unsigned short block_count;
+ unsigned short result;
+ unsigned short request;
+};
+
+static int mmc_set_blockcount(struct mmc *mmc, unsigned int blockcount,
+ bool is_rel_write)
+{
+ struct mmc_cmd cmd = {0};
+
+ cmd.cmdidx = MMC_CMD_SET_BLOCK_COUNT;
+ cmd.cmdarg = blockcount & 0x0000FFFF;
+ if (is_rel_write)
+ cmd.cmdarg |= 1 << 31;
+ cmd.resp_type = MMC_RSP_R1;
+
+ return mmc_send_cmd(mmc, &cmd, NULL);
+}
+static int mmc_rpmb_request(struct mmc *mmc, const struct s_rpmb *s,
+ unsigned int count, bool is_rel_write)
+{
+ struct mmc_cmd cmd = {0};
+ struct mmc_data data;
+ int ret;
+
+ ret = mmc_set_blockcount(mmc, count, is_rel_write);
+ if (ret) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:mmc_set_blockcount-> %d\n", __func__, ret);
+#endif
+ return 1;
+ }
+
+ cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
+ cmd.cmdarg = 0;
+ cmd.resp_type = MMC_RSP_R1b;
+
+ data.src = (const char *)s;
+ data.blocks = 1;
+ data.blocksize = MMC_MAX_BLOCK_LEN;
+ data.flags = MMC_DATA_WRITE;
+
+ ret = mmc_send_cmd(mmc, &cmd, &data);
+ if (ret) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:mmc_send_cmd-> %d\n", __func__, ret);
+#endif
+ return 1;
+ }
+ return 0;
+}
+static int mmc_rpmb_response(struct mmc *mmc, struct s_rpmb *s,
+ unsigned short expected)
+{
+ struct mmc_cmd cmd = {0};
+ struct mmc_data data;
+ int ret;
+
+ ret = mmc_set_blockcount(mmc, 1, false);
+ if (ret) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:mmc_set_blockcount-> %d\n", __func__, ret);
+#endif
+ return -1;
+ }
+ cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
+ cmd.cmdarg = 0;
+ cmd.resp_type = MMC_RSP_R1;
+
+ data.dest = (char *)s;
+ data.blocks = 1;
+ data.blocksize = MMC_MAX_BLOCK_LEN;
+ data.flags = MMC_DATA_READ;
+
+ ret = mmc_send_cmd(mmc, &cmd, &data);
+ if (ret) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:mmc_send_cmd-> %d\n", __func__, ret);
+#endif
+ return -1;
+ }
+ /* Check the response and the status */
+ if (be16_to_cpu(s->request) != expected) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:response= %x\n", __func__,
+ be16_to_cpu(s->request));
+#endif
+ return -1;
+ }
+ ret = be16_to_cpu(s->result);
+ if (ret) {
+ printf("%s %s\n", rpmb_err_msg[ret & RPMB_ERR_MSK],
+ (ret & RPMB_ERR_CNT_EXPIRED) ?
+ "Write counter has expired" : "");
+ }
+
+ /* Return the status of the command */
+ return ret;
+}
+static int mmc_rpmb_status(struct mmc *mmc, unsigned short expected)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
+
+ memset(rpmb_frame, 0, sizeof(struct s_rpmb));
+ rpmb_frame->request = cpu_to_be16(RPMB_REQ_STATUS);
+ if (mmc_rpmb_request(mmc, rpmb_frame, 1, false))
+ return -1;
+
+ /* Read the result */
+ return mmc_rpmb_response(mmc, rpmb_frame, expected);
+}
+static void rpmb_hmac(unsigned char *key, unsigned char *buff, int len,
+ unsigned char *output)
+{
+ sha256_context ctx;
+ int i;
+ unsigned char k_ipad[SHA256_BLOCK_SIZE];
+ unsigned char k_opad[SHA256_BLOCK_SIZE];
+
+ sha256_starts(&ctx);
+
+ /* According to RFC 4634, the HMAC transform looks like:
+ SHA(K XOR opad, SHA(K XOR ipad, text))
+
+ where K is an n byte key.
+ ipad is the byte 0x36 repeated blocksize times
+ opad is the byte 0x5c repeated blocksize times
+ and text is the data being protected.
+ */
+
+ for (i = 0; i < RPMB_SZ_MAC; i++) {
+ k_ipad[i] = key[i] ^ 0x36;
+ k_opad[i] = key[i] ^ 0x5c;
+ }
+ /* remaining pad bytes are '\0' XOR'd with ipad and opad values */
+ for ( ; i < SHA256_BLOCK_SIZE; i++) {
+ k_ipad[i] = 0x36;
+ k_opad[i] = 0x5c;
+ }
+ sha256_update(&ctx, k_ipad, SHA256_BLOCK_SIZE);
+ sha256_update(&ctx, buff, len);
+ sha256_finish(&ctx, output);
+
+ /* Init context for second pass */
+ sha256_starts(&ctx);
+
+ /* start with outer pad */
+ sha256_update(&ctx, k_opad, SHA256_BLOCK_SIZE);
+
+ /* then results of 1st hash */
+ sha256_update(&ctx, output, RPMB_SZ_MAC);
+
+ /* finish up 2nd pass */
+ sha256_finish(&ctx, output);
+}
+int mmc_rpmb_get_counter(struct mmc *mmc, unsigned long *pcounter)
+{
+ int ret;
+ ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
+
+ /* Fill the request */
+ memset(rpmb_frame, 0, sizeof(struct s_rpmb));
+ rpmb_frame->request = cpu_to_be16(RPMB_REQ_WCOUNTER);
+ if (mmc_rpmb_request(mmc, rpmb_frame, 1, false))
+ return -1;
+
+ /* Read the result */
+ ret = mmc_rpmb_response(mmc, rpmb_frame, RPMB_RESP_WCOUNTER);
+ if (ret)
+ return ret;
+
+ *pcounter = be32_to_cpu(rpmb_frame->write_counter);
+ return 0;
+}
+int mmc_rpmb_set_key(struct mmc *mmc, void *key)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
+ /* Fill the request */
+ memset(rpmb_frame, 0, sizeof(struct s_rpmb));
+ rpmb_frame->request = cpu_to_be16(RPMB_REQ_KEY);
+ memcpy(rpmb_frame->mac, key, RPMB_SZ_MAC);
+
+ if (mmc_rpmb_request(mmc, rpmb_frame, 1, true))
+ return -1;
+
+ /* read the operation status */
+ return mmc_rpmb_status(mmc, RPMB_RESP_KEY);
+}
+int mmc_rpmb_read(struct mmc *mmc, void *addr, unsigned short blk,
+ unsigned short cnt, unsigned char *key)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
+ int i;
+
+ for (i = 0; i < cnt; i++) {
+ /* Fill the request */
+ memset(rpmb_frame, 0, sizeof(struct s_rpmb));
+ rpmb_frame->address = cpu_to_be16(blk + i);
+ rpmb_frame->request = cpu_to_be16(RPMB_REQ_READ_DATA);
+ if (mmc_rpmb_request(mmc, rpmb_frame, 1, false))
+ break;
+
+ /* Read the result */
+ if (mmc_rpmb_response(mmc, rpmb_frame, RPMB_RESP_READ_DATA))
+ break;
+
+ /* Check the HMAC if key is provided */
+ if (key) {
+ unsigned char ret_hmac[RPMB_SZ_MAC];
+
+ rpmb_hmac(key, rpmb_frame->data, 284, ret_hmac);
+ if (memcmp(ret_hmac, rpmb_frame->mac, RPMB_SZ_MAC)) {
+ printf("MAC error on block #%d\n", i);
+ break;
+ }
+ }
+ /* Copy data */
+ memcpy(addr + i * RPMB_SZ_DATA, rpmb_frame->data, RPMB_SZ_DATA);
+ }
+ return i;
+}
+int mmc_rpmb_write(struct mmc *mmc, void *addr, unsigned short blk,
+ unsigned short cnt, unsigned char *key)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
+ unsigned long wcount;
+ int i;
+
+ for (i = 0; i < cnt; i++) {
+ if (mmc_rpmb_get_counter(mmc, &wcount)) {
+ printf("Cannot read RPMB write counter\n");
+ break;
+ }
+
+ /* Fill the request */
+ memset(rpmb_frame, 0, sizeof(struct s_rpmb));
+ memcpy(rpmb_frame->data, addr + i * RPMB_SZ_DATA, RPMB_SZ_DATA);
+ rpmb_frame->address = cpu_to_be16(blk + i);
+ rpmb_frame->block_count = cpu_to_be16(1);
+ rpmb_frame->write_counter = cpu_to_be32(wcount);
+ rpmb_frame->request = cpu_to_be16(RPMB_REQ_WRITE_DATA);
+ /* Computes HMAC */
+ rpmb_hmac(key, rpmb_frame->data, 284, rpmb_frame->mac);
+
+ if (mmc_rpmb_request(mmc, rpmb_frame, 1, true))
+ break;
+
+ /* Get status */
+ if (mmc_rpmb_status(mmc, RPMB_RESP_WRITE_DATA))
+ break;
+ }
+ return i;
+}
diff --git a/include/mmc.h b/include/mmc.h
index 42d0125..14d296c 100644
--- a/include/mmc.h
+++ b/include/mmc.h
@@ -69,6 +69,7 @@
#define MMC_CMD_SET_BLOCKLEN 16
#define MMC_CMD_READ_SINGLE_BLOCK 17
#define MMC_CMD_READ_MULTIPLE_BLOCK 18
+#define MMC_CMD_SET_BLOCK_COUNT 23
#define MMC_CMD_WRITE_SINGLE_BLOCK 24
#define MMC_CMD_WRITE_MULTIPLE_BLOCK 25
#define MMC_CMD_ERASE_GROUP_START 35
@@ -224,6 +225,7 @@
* boot partitions (2), general purpose partitions (4) in MMC v4.4.
*/
#define MMC_NUM_BOOT_PARTITION 2
+#define MMC_PART_RPMB 3 /* RPMB partition number */
struct mmc_cid {
unsigned long psn;
@@ -335,7 +337,13 @@ int mmc_set_part_conf(struct mmc *mmc, u8 ack, u8 part_num, u8 access);
int mmc_set_boot_bus_width(struct mmc *mmc, u8 width, u8 reset, u8 mode);
/* Function to modify the RST_n_FUNCTION field of EXT_CSD */
int mmc_set_rst_n_function(struct mmc *mmc, u8 enable);
-
+/* Functions to read / write the RPMB partition */
+int mmc_rpmb_set_key(struct mmc *mmc, void *key);
+int mmc_rpmb_get_counter(struct mmc *mmc, unsigned long *counter);
+int mmc_rpmb_read(struct mmc *mmc, void *addr, unsigned short blk,
+ unsigned short cnt, unsigned char *key);
+int mmc_rpmb_write(struct mmc *mmc, void *addr, unsigned short blk,
+ unsigned short cnt, unsigned char *key);
/**
* Start device initialization and return immediately; it does not block on
* polling OCR (operation condition register) status. Then you should call
diff --git a/lib/Makefile b/lib/Makefile
index 27e4f78..2bf90f3 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -35,6 +35,9 @@ obj-y += net_utils.o
obj-$(CONFIG_PHYSMEM) += physmem.o
obj-y += qsort.o
obj-$(CONFIG_SHA1) += sha1.o
+ifdef CONFIG_SUPPORT_EMMC_RPMB
+CONFIG_SHA256 := y
+endif
obj-$(CONFIG_SHA256) += sha256.o
obj-y += strmhz.o
obj-$(CONFIG_TPM) += tpm.o
--
1.7.6.5
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V2 2/2] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command
2014-04-17 15:10 ` [U-Boot] [PATCH V2 0/2] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
2014-04-17 15:10 ` [U-Boot] [PATCH V2 1/2] eMMC: add support for operations in RPMB partition Pierre Aubert
@ 2014-04-17 15:10 ` Pierre Aubert
2014-04-17 19:56 ` Wolfgang Denk
1 sibling, 1 reply; 31+ messages in thread
From: Pierre Aubert @ 2014-04-17 15:10 UTC (permalink / raw)
To: u-boot
This sub-command adds support for the RPMB partition of an eMMC:
* mmc rpmb key <address of the authentication key>
Programs the authentication key in the eMMC This key can not
be overwritten.
* mmc rpmb read <address> <block> <#count> [address of key]
Reads <#count> blocks of 256 bytes in the RPMB partition
beginning at block number <block>. If the optionnal
address of the authentication key is provided, the
Message Authentication Code (MAC) is verified on each
block.
* mmc rpmb write <address> <block> <#count> <address of key>
Writes <#count> blocks of 256 bytes in the RPMB partition
beginning at block number <block>. The datas are signed
with the key provided.
* mmc rpmb counter
Returns the 'Write counter' of the RPMB partition.
The sub-command is conditional on compilation flag CONFIG_SUPPORT_EMMC_RPMB
Signed-off-by: Pierre Aubert <p.aubert@staubli.com>
CC: Pantelis Antoniou <panto@antoniou-consulting.com>
---
common/cmd_mmc.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 127 insertions(+), 1 deletions(-)
diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c
index c1916c9..3cf11e7 100644
--- a/common/cmd_mmc.c
+++ b/common/cmd_mmc.c
@@ -130,7 +130,123 @@ U_BOOT_CMD(
"display MMC info",
"- display info of the current MMC device"
);
+#ifdef CONFIG_SUPPORT_EMMC_RPMB
+static int confirm_key_prog(void)
+{
+ puts("Warning: Programming authentication key can be done only once !\n"
+ " Use this command only if you are sure of what you are doing,\n"
+ "Really perform the key programming ? ");
+ if (getc() == 'y') {
+ int c;
+
+ putc('y');
+ c = getc();
+ putc('\n');
+ if (c == '\r')
+ return 1;
+ }
+ puts("Authentication key programming aborted\n");
+ return 0;
+}
+static int do_mmcrpmb(int argc, char * const argv[])
+{
+ enum rpmb_state {
+ RPMB_INVALID,
+ RPMB_READ,
+ RPMB_WRITE,
+ RPMB_KEY,
+ RPMB_COUNTER,
+ } state;
+
+ state = RPMB_INVALID;
+ if (argc == 4 && strcmp(argv[2], "key") == 0)
+ state = RPMB_KEY;
+ if ((argc == 6 || argc == 7) && strcmp(argv[2], "read") == 0)
+ state = RPMB_READ;
+ else if (argc == 7 && strcmp(argv[2], "write") == 0)
+ state = RPMB_WRITE;
+ else if (argc == 3 && strcmp(argv[2], "counter") == 0)
+ state = RPMB_COUNTER;
+
+ if (state != RPMB_INVALID) {
+ struct mmc *mmc = find_mmc_device(curr_device);
+ void *key_addr;
+ char original_part;
+ int ret;
+
+ if (!mmc) {
+ printf("no mmc device at slot %x\n", curr_device);
+ return CMD_RET_FAILURE;
+ }
+ mmc_init(mmc);
+ if (IS_SD(mmc)) {
+ printf("It is not a EMMC device\n");
+ return CMD_RET_FAILURE;
+ }
+ /* Switch to the RPMB partition */
+ original_part = mmc->part_num;
+ if (mmc->part_num != MMC_PART_RPMB) {
+ if (mmc_switch_part(curr_device, MMC_PART_RPMB) != 0)
+ return CMD_RET_FAILURE;
+ mmc->part_num = MMC_PART_RPMB;
+ }
+ ret = CMD_RET_SUCCESS;
+ if (state == RPMB_KEY) {
+ key_addr = (void *)simple_strtoul(argv[3], NULL, 16);
+ if (confirm_key_prog()) {
+ if (mmc_rpmb_set_key(mmc, key_addr)) {
+ printf("ERROR - Key already programmed ?\n");
+ ret = CMD_RET_FAILURE;
+ }
+ } else {
+ ret = CMD_RET_FAILURE;
+ }
+ } else if (state == RPMB_COUNTER) {
+ unsigned long counter;
+ if (mmc_rpmb_get_counter(mmc, &counter))
+ ret = CMD_RET_FAILURE;
+ else
+ printf("Write counter= %lx\n", counter);
+ } else {
+ u16 blk, cnt;
+ void *addr;
+ int n;
+
+ addr = (void *)simple_strtoul(argv[3], NULL, 16);
+ blk = simple_strtoul(argv[4], NULL, 16);
+ cnt = simple_strtoul(argv[5], NULL, 16);
+
+ if (state == RPMB_READ) {
+ key_addr = (argc == 7) ?
+ (void *)simple_strtoul(argv[6],
+ NULL, 16) :
+ NULL;
+ n = mmc_rpmb_read(mmc, addr, blk, cnt,
+ key_addr);
+ } else {
+ key_addr = (void *)simple_strtoul(argv[6],
+ NULL, 16);
+ n = mmc_rpmb_write(mmc, addr, blk, cnt,
+ key_addr);
+ }
+ printf("%d RPMB blocks %s: %s\n",
+ n, argv[2], (n == cnt) ? "OK" : "ERROR");
+ if (n != cnt)
+ ret = CMD_RET_FAILURE;
+ }
+
+ /* Return to orginal partition */
+ if (mmc->part_num != original_part) {
+ if (mmc_switch_part(curr_device, original_part) != 0)
+ return CMD_RET_FAILURE;
+ mmc->part_num = original_part;
+ }
+ return ret;
+ } else
+ return CMD_RET_USAGE;
+}
+#endif
static int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
enum mmc_state state;
@@ -365,6 +481,10 @@ static int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
return mmc_set_rst_n_function(mmc, enable);
#endif /* CONFIG_SUPPORT_EMMC_BOOT */
+#ifdef CONFIG_SUPPORT_EMMC_RPMB
+ } else if (strcmp(argv[1], "rpmb") == 0) {
+ return do_mmcrpmb(argc, argv);
+#endif /* CONFIG_SUPPORT_EMMC_RPMB */
}
else if (argc == 3 && strcmp(argv[1], "setdsr") == 0) {
@@ -454,7 +574,7 @@ static int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
}
U_BOOT_CMD(
- mmc, 6, 1, do_mmcops,
+ mmc, 7, 1, do_mmcops,
"MMC sub system",
"read addr blk# cnt\n"
"mmc write addr blk# cnt\n"
@@ -474,6 +594,12 @@ U_BOOT_CMD(
" - Change the RST_n_FUNCTION field of the specified device\n"
" WARNING: This is a write-once field and 0 / 1 / 2 are the only valid values.\n"
#endif
+#ifdef CONFIG_SUPPORT_EMMC_RPMB
+ "mmc rpmb read addr blk# cnt [address of auth-key] - block size is 256 bytes\n"
+ "mmc rpmb write addr blk# cnt <address of auth-key> - block size is 256 bytes\n"
+ "mmc rpmb key <address of auth-key> - program the RPMB authentication key.\n"
+ "mmc rpmb counter - read the value of the write counter\n"
+#endif
"mmc setdsr - set DSR register value\n"
);
#endif /* !CONFIG_GENERIC_MMC */
--
1.7.6.5
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V2 2/2] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command
2014-04-17 15:10 ` [U-Boot] [PATCH V2 2/2] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command Pierre Aubert
@ 2014-04-17 19:56 ` Wolfgang Denk
2014-04-18 6:39 ` Pierre AUBERT
0 siblings, 1 reply; 31+ messages in thread
From: Wolfgang Denk @ 2014-04-17 19:56 UTC (permalink / raw)
To: u-boot
Dear Pierre Aubert,
In message <1397747435-24042-3-git-send-email-p.aubert@staubli.com> you wrote:
> This sub-command adds support for the RPMB partition of an eMMC:
> * mmc rpmb key <address of the authentication key>
> Programs the authentication key in the eMMC This key can not
> be overwritten.
> * mmc rpmb read <address> <block> <#count> [address of key]
> Reads <#count> blocks of 256 bytes in the RPMB partition
> beginning at block number <block>. If the optionnal
> address of the authentication key is provided, the
> Message Authentication Code (MAC) is verified on each
> block.
> * mmc rpmb write <address> <block> <#count> <address of key>
> Writes <#count> blocks of 256 bytes in the RPMB partition
> beginning at block number <block>. The datas are signed
> with the key provided.
> * mmc rpmb counter
> Returns the 'Write counter' of the RPMB partition.
>
> The sub-command is conditional on compilation flag CONFIG_SUPPORT_EMMC_RPMB
Such new options must be documented in the README.
> Signed-off-by: Pierre Aubert <p.aubert@staubli.com>
> CC: Pantelis Antoniou <panto@antoniou-consulting.com>
> ---
> common/cmd_mmc.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
> 1 files changed, 127 insertions(+), 1 deletions(-)
>
> diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c
> index c1916c9..3cf11e7 100644
> --- a/common/cmd_mmc.c
> +++ b/common/cmd_mmc.c
> @@ -130,7 +130,123 @@ U_BOOT_CMD(
> "display MMC info",
> "- display info of the current MMC device"
> );
> +#ifdef CONFIG_SUPPORT_EMMC_RPMB
> +static int confirm_key_prog(void)
> +{
> + puts("Warning: Programming authentication key can be done only once !\n"
> + " Use this command only if you are sure of what you are doing,\n"
> + "Really perform the key programming ? ");
> + if (getc() == 'y') {
Would it not makes sense to flush the input before reading the char,
so that you don;t react on any type-ahead that might already be
buffered?
> + int c;
> +
> + putc('y');
> + c = getc();
> + putc('\n');
> + if (c == '\r')
> + return 1;
> + }
Should we allow for 'Y"? And for "yes" / "Yes"?
We have getenv_yesno() - maybe we should provide a similar function
that can be used in other places where such interactive confirmation
is needed?
> + if (state != RPMB_INVALID) {
Change this into
if (state == RPMB_INVALID)
return CMD_RET_USAGE;
and avoid one level of indentation; this will make the code much
easier to read.
> + if (IS_SD(mmc)) {
Is IS_SD() a reliable test for eMMC devics, or would that also return
true in other cases?
> + if (confirm_key_prog()) {
> + if (mmc_rpmb_set_key(mmc, key_addr)) {
> + printf("ERROR - Key already programmed ?\n");
> + ret = CMD_RET_FAILURE;
> + }
> + } else {
> + ret = CMD_RET_FAILURE;
> + }
You should really avoid deep nesting and take early exits.
You can write this as:
if (!confirm_key_prog())
return CMD_RET_FAILURE;
if (mmc_rpmb_set_key(mmc, key_addr)) {
printf("ERROR - Key already programmed ?\n");
ret = CMD_RET_FAILURE;
}
Please fix globally.
> + } else if (state == RPMB_COUNTER) {
> + unsigned long counter;
> + if (mmc_rpmb_get_counter(mmc, &counter))
Please insert a blank line between declarations and code.
> + printf("%d RPMB blocks %s: %s\n",
> + n, argv[2], (n == cnt) ? "OK" : "ERROR");
As the input is in hex, it is usually also a good idea to (also) print
the count in hex.
> #endif /* CONFIG_SUPPORT_EMMC_BOOT */
> +#ifdef CONFIG_SUPPORT_EMMC_RPMB
> + } else if (strcmp(argv[1], "rpmb") == 0) {
> + return do_mmcrpmb(argc, argv);
> +#endif /* CONFIG_SUPPORT_EMMC_RPMB */
I think that now, with more subcommands being added, we should
convert the mmc code to proper subcommand handling. [It might even
make sense to do so for "mmc rpmb", too.]
Best regards,
Wolfgang Denk
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de
"The computer programmer is a creator of universes for which he alone
is responsible. Universes of virtually unlimited complexity can be
created in the form of computer programs." - Joseph Weizenbaum,
_Computer Power and Human Reason_
^ permalink raw reply [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V2 2/2] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command
2014-04-17 19:56 ` Wolfgang Denk
@ 2014-04-18 6:39 ` Pierre AUBERT
2014-04-22 17:41 ` Wolfgang Denk
0 siblings, 1 reply; 31+ messages in thread
From: Pierre AUBERT @ 2014-04-18 6:39 UTC (permalink / raw)
To: u-boot
Hello Wolfgang,
Le 17/04/2014 21:56, Wolfgang Denk a ?crit :
> Dear Pierre Aubert,
>
> In message <1397747435-24042-3-git-send-email-p.aubert@staubli.com> you wrote:
>> This sub-command adds support for the RPMB partition of an eMMC:
>> * mmc rpmb key <address of the authentication key>
>> Programs the authentication key in the eMMC This key can not
>> be overwritten.
>> * mmc rpmb read <address> <block> <#count> [address of key]
>> Reads <#count> blocks of 256 bytes in the RPMB partition
>> beginning at block number <block>. If the optionnal
>> address of the authentication key is provided, the
>> Message Authentication Code (MAC) is verified on each
>> block.
>> * mmc rpmb write <address> <block> <#count> <address of key>
>> Writes <#count> blocks of 256 bytes in the RPMB partition
>> beginning at block number <block>. The datas are signed
>> with the key provided.
>> * mmc rpmb counter
>> Returns the 'Write counter' of the RPMB partition.
>>
>> The sub-command is conditional on compilation flag CONFIG_SUPPORT_EMMC_RPMB
> Such new options must be documented in the README.
I will add it in a V3
>
>> Signed-off-by: Pierre Aubert <p.aubert@staubli.com>
>> CC: Pantelis Antoniou <panto@antoniou-consulting.com>
>> ---
>> common/cmd_mmc.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
>> 1 files changed, 127 insertions(+), 1 deletions(-)
>>
>> diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c
>> index c1916c9..3cf11e7 100644
>> --- a/common/cmd_mmc.c
>> +++ b/common/cmd_mmc.c
>> @@ -130,7 +130,123 @@ U_BOOT_CMD(
>> "display MMC info",
>> "- display info of the current MMC device"
>> );
>> +#ifdef CONFIG_SUPPORT_EMMC_RPMB
>> +static int confirm_key_prog(void)
>> +{
>> + puts("Warning: Programming authentication key can be done only once !\n"
>> + " Use this command only if you are sure of what you are doing,\n"
>> + "Really perform the key programming ? ");
>> + if (getc() == 'y') {
> Would it not makes sense to flush the input before reading the char,
> so that you don;t react on any type-ahead that might already be
> buffered?
>
>> + int c;
>> +
>> + putc('y');
>> + c = getc();
>> + putc('\n');
>> + if (c == '\r')
>> + return 1;
>> + }
> Should we allow for 'Y"? And for "yes" / "Yes"?
>
> We have getenv_yesno() - maybe we should provide a similar function
> that can be used in other places where such interactive confirmation
> is needed?
I have found such a confirmation in cmd_fuse, cmd_otp and cmd_mmc. It
makes sense to provide
a global function. I will try to submit a patch for that.
>> + if (state != RPMB_INVALID) {
> Change this into
>
> if (state == RPMB_INVALID)
> return CMD_RET_USAGE;
>
> and avoid one level of indentation; this will make the code much
> easier to read.
>
>> + if (IS_SD(mmc)) {
> Is IS_SD() a reliable test for eMMC devics, or would that also return
> true in other cases?
You're right, the test must be more restrictive. The RPMB partition is
available only since the release 4.41 of the Jedec
standard. I will fix it in a V3.
>
>> + if (confirm_key_prog()) {
>> + if (mmc_rpmb_set_key(mmc, key_addr)) {
>> + printf("ERROR - Key already programmed ?\n");
>> + ret = CMD_RET_FAILURE;
>> + }
>> + } else {
>> + ret = CMD_RET_FAILURE;
>> + }
> You should really avoid deep nesting and take early exits.
> You can write this as:
>
> if (!confirm_key_prog())
> return CMD_RET_FAILURE;
>
> if (mmc_rpmb_set_key(mmc, key_addr)) {
> printf("ERROR - Key already programmed ?\n");
> ret = CMD_RET_FAILURE;
> }
>
> Please fix globally.
No problem, it will be fixed globally in V3
>
>> + } else if (state == RPMB_COUNTER) {
>> + unsigned long counter;
>> + if (mmc_rpmb_get_counter(mmc, &counter))
> Please insert a blank line between declarations and code.
Ok
>
>> + printf("%d RPMB blocks %s: %s\n",
>> + n, argv[2], (n == cnt) ? "OK" : "ERROR");
> As the input is in hex, it is usually also a good idea to (also) print
> the count in hex.
For coherency with the mmc read and mmc write, I kept the same output.
But it can be changed, of course.
>
>> #endif /* CONFIG_SUPPORT_EMMC_BOOT */
>> +#ifdef CONFIG_SUPPORT_EMMC_RPMB
>> + } else if (strcmp(argv[1], "rpmb") == 0) {
>> + return do_mmcrpmb(argc, argv);
>> +#endif /* CONFIG_SUPPORT_EMMC_RPMB */
> I think that now, with more subcommands being added, we should
> convert the mmc code to proper subcommand handling. [It might even
> make sense to do so for "mmc rpmb", too.]
Do you think about the use of the macro U_BOOT_CMD_MKENT ?
>
> Best regards,
>
> Wolfgang Denk
>
Best regards
Pierre Aubert
^ permalink raw reply [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V3 0/3] eMMC: support for Read Protected Memory Block (RPMB)
2014-04-11 12:12 [U-Boot] [PATCH 0/2] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
` (2 preceding siblings ...)
2014-04-17 15:10 ` [U-Boot] [PATCH V2 0/2] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
@ 2014-04-22 15:54 ` Pierre Aubert
2014-04-22 15:54 ` [U-Boot] [PATCH V3 1/3] eMMC: add support for operations in RPMB partition Pierre Aubert
` (2 more replies)
2014-04-24 6:40 ` [U-Boot] [PATCH V4 0/3] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
2014-04-24 8:30 ` [U-Boot] [PATCH V5 0/3] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
5 siblings, 3 replies; 31+ messages in thread
From: Pierre Aubert @ 2014-04-22 15:54 UTC (permalink / raw)
To: u-boot
This serie of patches adds some functions and a sub-command of 'mmc' for programming the authentication key and for reading and writing the RPMB partition of an eMMC according to the JEDEC standard No. 64-A441
The sub-command rpmb is enabled by the flag CONFIG_SUPPORT_EMMC_RPMB defined
in the board configuration file.
It has been tested on a SabreSDP iMX6 board.
Changes in V3:
- add entries in README for configuration options related to eMMC.
- new patch for adding the 'confirm_yesno' function as suggested by W.Denk
- improved test for existence of RPMB partition.
- fix of coding issues.
Changes in V2:
- use ALLOC_CACHE_ALIGN_BUFFER in rpmb.c instead of a static buffer for the
RPMB frames.
Pierre Aubert (3):
eMMC: add support for operations in RPMB partition
Add the function 'confirm_yesno' for interactive confirmation.
eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command
README | 10 ++
common/cmd_fuse.c | 11 +--
common/cmd_mmc.c | 123 +++++++++++++++++++-
common/cmd_nand.c | 16 +--
common/cmd_otp.c | 18 +---
common/console.c | 28 ++++-
drivers/mmc/Makefile | 1 +
drivers/mmc/rpmb.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++++++
include/common.h | 2 +-
include/mmc.h | 10 ++-
lib/Makefile | 3 +
11 files changed, 506 insertions(+), 39 deletions(-)
create mode 100644 drivers/mmc/rpmb.c
--
1.7.6.5
^ permalink raw reply [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V3 1/3] eMMC: add support for operations in RPMB partition
2014-04-22 15:54 ` [U-Boot] [PATCH V3 0/3] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
@ 2014-04-22 15:54 ` Pierre Aubert
2014-04-22 15:54 ` [U-Boot] [PATCH V3 2/3] Add the function 'confirm_yesno' for interactive confirmation Pierre Aubert
2014-04-22 15:54 ` [U-Boot] [PATCH V3 3/3] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command Pierre Aubert
2 siblings, 0 replies; 31+ messages in thread
From: Pierre Aubert @ 2014-04-22 15:54 UTC (permalink / raw)
To: u-boot
This patch adds functions for read, write and authentication
key programming for the Replay Protected Memory Block partition
in the eMMC.
Signed-off-by: Pierre Aubert <p.aubert@staubli.com>
CC: Pantelis Antoniou <panto@antoniou-consulting.com>
---
drivers/mmc/Makefile | 1 +
drivers/mmc/rpmb.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++++++
include/mmc.h | 10 ++-
lib/Makefile | 3 +
4 files changed, 336 insertions(+), 1 deletions(-)
create mode 100644 drivers/mmc/rpmb.c
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 931922b..4c6ab9e 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_DWMMC) += dw_mmc.o
obj-$(CONFIG_EXYNOS_DWMMC) += exynos_dw_mmc.o
obj-$(CONFIG_ZYNQ_SDHCI) += zynq_sdhci.o
obj-$(CONFIG_SOCFPGA_DWMMC) += socfpga_dw_mmc.o
+obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o
ifdef CONFIG_SPL_BUILD
obj-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o
else
diff --git a/drivers/mmc/rpmb.c b/drivers/mmc/rpmb.c
new file mode 100644
index 0000000..05936f5
--- /dev/null
+++ b/drivers/mmc/rpmb.c
@@ -0,0 +1,323 @@
+/*
+ * Copyright 2014, Staubli Faverges
+ * Pierre Aubert
+ *
+ * eMMC- Replay Protected Memory Block
+ * According to JEDEC Standard No. 84-A441
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <config.h>
+#include <common.h>
+#include <mmc.h>
+#include <sha256.h>
+#include "mmc_private.h"
+
+/* Request codes */
+#define RPMB_REQ_KEY 1
+#define RPMB_REQ_WCOUNTER 2
+#define RPMB_REQ_WRITE_DATA 3
+#define RPMB_REQ_READ_DATA 4
+#define RPMB_REQ_STATUS 5
+
+/* Response code */
+#define RPMB_RESP_KEY 0x0100
+#define RPMB_RESP_WCOUNTER 0x0200
+#define RPMB_RESP_WRITE_DATA 0x0300
+#define RPMB_RESP_READ_DATA 0x0400
+
+/* Error codes */
+#define RPMB_OK 0
+#define RPMB_ERR_GENERAL 1
+#define RPMB_ERR_AUTH 2
+#define RPMB_ERR_COUNTER 3
+#define RPMB_ERR_ADDRESS 4
+#define RPMB_ERR_WRITE 5
+#define RPMB_ERR_READ 6
+#define RPMB_ERR_KEY 7
+#define RPMB_ERR_CNT_EXPIRED 0x80
+#define RPMB_ERR_MSK 0x7
+
+/* Sizes of RPMB data frame */
+#define RPMB_SZ_STUFF 196
+#define RPMB_SZ_MAC 32
+#define RPMB_SZ_DATA 256
+#define RPMB_SZ_NONCE 16
+
+#define SHA256_BLOCK_SIZE 64
+
+/* Error messages */
+static const char * const rpmb_err_msg[] = {
+ "",
+ "General failure",
+ "Authentication failure",
+ "Counter failure",
+ "Address failure",
+ "Write failure",
+ "Read failure",
+ "Authentication key not yet programmed",
+};
+
+
+/* Structure of RPMB data frame. */
+struct s_rpmb {
+ unsigned char stuff[RPMB_SZ_STUFF];
+ unsigned char mac[RPMB_SZ_MAC];
+ unsigned char data[RPMB_SZ_DATA];
+ unsigned char nonce[RPMB_SZ_NONCE];
+ unsigned long write_counter;
+ unsigned short address;
+ unsigned short block_count;
+ unsigned short result;
+ unsigned short request;
+};
+
+static int mmc_set_blockcount(struct mmc *mmc, unsigned int blockcount,
+ bool is_rel_write)
+{
+ struct mmc_cmd cmd = {0};
+
+ cmd.cmdidx = MMC_CMD_SET_BLOCK_COUNT;
+ cmd.cmdarg = blockcount & 0x0000FFFF;
+ if (is_rel_write)
+ cmd.cmdarg |= 1 << 31;
+ cmd.resp_type = MMC_RSP_R1;
+
+ return mmc_send_cmd(mmc, &cmd, NULL);
+}
+static int mmc_rpmb_request(struct mmc *mmc, const struct s_rpmb *s,
+ unsigned int count, bool is_rel_write)
+{
+ struct mmc_cmd cmd = {0};
+ struct mmc_data data;
+ int ret;
+
+ ret = mmc_set_blockcount(mmc, count, is_rel_write);
+ if (ret) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:mmc_set_blockcount-> %d\n", __func__, ret);
+#endif
+ return 1;
+ }
+
+ cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
+ cmd.cmdarg = 0;
+ cmd.resp_type = MMC_RSP_R1b;
+
+ data.src = (const char *)s;
+ data.blocks = 1;
+ data.blocksize = MMC_MAX_BLOCK_LEN;
+ data.flags = MMC_DATA_WRITE;
+
+ ret = mmc_send_cmd(mmc, &cmd, &data);
+ if (ret) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:mmc_send_cmd-> %d\n", __func__, ret);
+#endif
+ return 1;
+ }
+ return 0;
+}
+static int mmc_rpmb_response(struct mmc *mmc, struct s_rpmb *s,
+ unsigned short expected)
+{
+ struct mmc_cmd cmd = {0};
+ struct mmc_data data;
+ int ret;
+
+ ret = mmc_set_blockcount(mmc, 1, false);
+ if (ret) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:mmc_set_blockcount-> %d\n", __func__, ret);
+#endif
+ return -1;
+ }
+ cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
+ cmd.cmdarg = 0;
+ cmd.resp_type = MMC_RSP_R1;
+
+ data.dest = (char *)s;
+ data.blocks = 1;
+ data.blocksize = MMC_MAX_BLOCK_LEN;
+ data.flags = MMC_DATA_READ;
+
+ ret = mmc_send_cmd(mmc, &cmd, &data);
+ if (ret) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:mmc_send_cmd-> %d\n", __func__, ret);
+#endif
+ return -1;
+ }
+ /* Check the response and the status */
+ if (be16_to_cpu(s->request) != expected) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:response= %x\n", __func__,
+ be16_to_cpu(s->request));
+#endif
+ return -1;
+ }
+ ret = be16_to_cpu(s->result);
+ if (ret) {
+ printf("%s %s\n", rpmb_err_msg[ret & RPMB_ERR_MSK],
+ (ret & RPMB_ERR_CNT_EXPIRED) ?
+ "Write counter has expired" : "");
+ }
+
+ /* Return the status of the command */
+ return ret;
+}
+static int mmc_rpmb_status(struct mmc *mmc, unsigned short expected)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
+
+ memset(rpmb_frame, 0, sizeof(struct s_rpmb));
+ rpmb_frame->request = cpu_to_be16(RPMB_REQ_STATUS);
+ if (mmc_rpmb_request(mmc, rpmb_frame, 1, false))
+ return -1;
+
+ /* Read the result */
+ return mmc_rpmb_response(mmc, rpmb_frame, expected);
+}
+static void rpmb_hmac(unsigned char *key, unsigned char *buff, int len,
+ unsigned char *output)
+{
+ sha256_context ctx;
+ int i;
+ unsigned char k_ipad[SHA256_BLOCK_SIZE];
+ unsigned char k_opad[SHA256_BLOCK_SIZE];
+
+ sha256_starts(&ctx);
+
+ /* According to RFC 4634, the HMAC transform looks like:
+ SHA(K XOR opad, SHA(K XOR ipad, text))
+
+ where K is an n byte key.
+ ipad is the byte 0x36 repeated blocksize times
+ opad is the byte 0x5c repeated blocksize times
+ and text is the data being protected.
+ */
+
+ for (i = 0; i < RPMB_SZ_MAC; i++) {
+ k_ipad[i] = key[i] ^ 0x36;
+ k_opad[i] = key[i] ^ 0x5c;
+ }
+ /* remaining pad bytes are '\0' XOR'd with ipad and opad values */
+ for ( ; i < SHA256_BLOCK_SIZE; i++) {
+ k_ipad[i] = 0x36;
+ k_opad[i] = 0x5c;
+ }
+ sha256_update(&ctx, k_ipad, SHA256_BLOCK_SIZE);
+ sha256_update(&ctx, buff, len);
+ sha256_finish(&ctx, output);
+
+ /* Init context for second pass */
+ sha256_starts(&ctx);
+
+ /* start with outer pad */
+ sha256_update(&ctx, k_opad, SHA256_BLOCK_SIZE);
+
+ /* then results of 1st hash */
+ sha256_update(&ctx, output, RPMB_SZ_MAC);
+
+ /* finish up 2nd pass */
+ sha256_finish(&ctx, output);
+}
+int mmc_rpmb_get_counter(struct mmc *mmc, unsigned long *pcounter)
+{
+ int ret;
+ ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
+
+ /* Fill the request */
+ memset(rpmb_frame, 0, sizeof(struct s_rpmb));
+ rpmb_frame->request = cpu_to_be16(RPMB_REQ_WCOUNTER);
+ if (mmc_rpmb_request(mmc, rpmb_frame, 1, false))
+ return -1;
+
+ /* Read the result */
+ ret = mmc_rpmb_response(mmc, rpmb_frame, RPMB_RESP_WCOUNTER);
+ if (ret)
+ return ret;
+
+ *pcounter = be32_to_cpu(rpmb_frame->write_counter);
+ return 0;
+}
+int mmc_rpmb_set_key(struct mmc *mmc, void *key)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
+ /* Fill the request */
+ memset(rpmb_frame, 0, sizeof(struct s_rpmb));
+ rpmb_frame->request = cpu_to_be16(RPMB_REQ_KEY);
+ memcpy(rpmb_frame->mac, key, RPMB_SZ_MAC);
+
+ if (mmc_rpmb_request(mmc, rpmb_frame, 1, true))
+ return -1;
+
+ /* read the operation status */
+ return mmc_rpmb_status(mmc, RPMB_RESP_KEY);
+}
+int mmc_rpmb_read(struct mmc *mmc, void *addr, unsigned short blk,
+ unsigned short cnt, unsigned char *key)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
+ int i;
+
+ for (i = 0; i < cnt; i++) {
+ /* Fill the request */
+ memset(rpmb_frame, 0, sizeof(struct s_rpmb));
+ rpmb_frame->address = cpu_to_be16(blk + i);
+ rpmb_frame->request = cpu_to_be16(RPMB_REQ_READ_DATA);
+ if (mmc_rpmb_request(mmc, rpmb_frame, 1, false))
+ break;
+
+ /* Read the result */
+ if (mmc_rpmb_response(mmc, rpmb_frame, RPMB_RESP_READ_DATA))
+ break;
+
+ /* Check the HMAC if key is provided */
+ if (key) {
+ unsigned char ret_hmac[RPMB_SZ_MAC];
+
+ rpmb_hmac(key, rpmb_frame->data, 284, ret_hmac);
+ if (memcmp(ret_hmac, rpmb_frame->mac, RPMB_SZ_MAC)) {
+ printf("MAC error on block #%d\n", i);
+ break;
+ }
+ }
+ /* Copy data */
+ memcpy(addr + i * RPMB_SZ_DATA, rpmb_frame->data, RPMB_SZ_DATA);
+ }
+ return i;
+}
+int mmc_rpmb_write(struct mmc *mmc, void *addr, unsigned short blk,
+ unsigned short cnt, unsigned char *key)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
+ unsigned long wcount;
+ int i;
+
+ for (i = 0; i < cnt; i++) {
+ if (mmc_rpmb_get_counter(mmc, &wcount)) {
+ printf("Cannot read RPMB write counter\n");
+ break;
+ }
+
+ /* Fill the request */
+ memset(rpmb_frame, 0, sizeof(struct s_rpmb));
+ memcpy(rpmb_frame->data, addr + i * RPMB_SZ_DATA, RPMB_SZ_DATA);
+ rpmb_frame->address = cpu_to_be16(blk + i);
+ rpmb_frame->block_count = cpu_to_be16(1);
+ rpmb_frame->write_counter = cpu_to_be32(wcount);
+ rpmb_frame->request = cpu_to_be16(RPMB_REQ_WRITE_DATA);
+ /* Computes HMAC */
+ rpmb_hmac(key, rpmb_frame->data, 284, rpmb_frame->mac);
+
+ if (mmc_rpmb_request(mmc, rpmb_frame, 1, true))
+ break;
+
+ /* Get status */
+ if (mmc_rpmb_status(mmc, RPMB_RESP_WRITE_DATA))
+ break;
+ }
+ return i;
+}
diff --git a/include/mmc.h b/include/mmc.h
index 42d0125..14d296c 100644
--- a/include/mmc.h
+++ b/include/mmc.h
@@ -69,6 +69,7 @@
#define MMC_CMD_SET_BLOCKLEN 16
#define MMC_CMD_READ_SINGLE_BLOCK 17
#define MMC_CMD_READ_MULTIPLE_BLOCK 18
+#define MMC_CMD_SET_BLOCK_COUNT 23
#define MMC_CMD_WRITE_SINGLE_BLOCK 24
#define MMC_CMD_WRITE_MULTIPLE_BLOCK 25
#define MMC_CMD_ERASE_GROUP_START 35
@@ -224,6 +225,7 @@
* boot partitions (2), general purpose partitions (4) in MMC v4.4.
*/
#define MMC_NUM_BOOT_PARTITION 2
+#define MMC_PART_RPMB 3 /* RPMB partition number */
struct mmc_cid {
unsigned long psn;
@@ -335,7 +337,13 @@ int mmc_set_part_conf(struct mmc *mmc, u8 ack, u8 part_num, u8 access);
int mmc_set_boot_bus_width(struct mmc *mmc, u8 width, u8 reset, u8 mode);
/* Function to modify the RST_n_FUNCTION field of EXT_CSD */
int mmc_set_rst_n_function(struct mmc *mmc, u8 enable);
-
+/* Functions to read / write the RPMB partition */
+int mmc_rpmb_set_key(struct mmc *mmc, void *key);
+int mmc_rpmb_get_counter(struct mmc *mmc, unsigned long *counter);
+int mmc_rpmb_read(struct mmc *mmc, void *addr, unsigned short blk,
+ unsigned short cnt, unsigned char *key);
+int mmc_rpmb_write(struct mmc *mmc, void *addr, unsigned short blk,
+ unsigned short cnt, unsigned char *key);
/**
* Start device initialization and return immediately; it does not block on
* polling OCR (operation condition register) status. Then you should call
diff --git a/lib/Makefile b/lib/Makefile
index 27e4f78..2bf90f3 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -35,6 +35,9 @@ obj-y += net_utils.o
obj-$(CONFIG_PHYSMEM) += physmem.o
obj-y += qsort.o
obj-$(CONFIG_SHA1) += sha1.o
+ifdef CONFIG_SUPPORT_EMMC_RPMB
+CONFIG_SHA256 := y
+endif
obj-$(CONFIG_SHA256) += sha256.o
obj-y += strmhz.o
obj-$(CONFIG_TPM) += tpm.o
--
1.7.6.5
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V3 2/3] Add the function 'confirm_yesno' for interactive confirmation.
2014-04-22 15:54 ` [U-Boot] [PATCH V3 0/3] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
2014-04-22 15:54 ` [U-Boot] [PATCH V3 1/3] eMMC: add support for operations in RPMB partition Pierre Aubert
@ 2014-04-22 15:54 ` Pierre Aubert
2014-04-22 15:54 ` [U-Boot] [PATCH V3 3/3] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command Pierre Aubert
2 siblings, 0 replies; 31+ messages in thread
From: Pierre Aubert @ 2014-04-22 15:54 UTC (permalink / raw)
To: u-boot
User's confirmation is asked in different commands. This commit adds a
function for such confirmation.
Signed-off-by: Pierre Aubert <p.aubert@staubli.com>
---
common/cmd_fuse.c | 11 ++---------
common/cmd_nand.c | 16 +++++-----------
common/cmd_otp.c | 18 +++---------------
common/console.c | 28 +++++++++++++++++++++++++++-
include/common.h | 2 +-
5 files changed, 38 insertions(+), 37 deletions(-)
diff --git a/common/cmd_fuse.c b/common/cmd_fuse.c
index 0df57db..abab978 100644
--- a/common/cmd_fuse.c
+++ b/common/cmd_fuse.c
@@ -33,15 +33,8 @@ static int confirm_prog(void)
"what you are doing!\n"
"\nReally perform this fuse programming? <y/N>\n");
- if (getc() == 'y') {
- int c;
-
- putc('y');
- c = getc();
- putc('\n');
- if (c == '\r')
- return 1;
- }
+ if (confirm_yesno())
+ return 1;
puts("Fuse programming aborted\n");
return 0;
diff --git a/common/cmd_nand.c b/common/cmd_nand.c
index 04ab0f1..a84f7dc 100644
--- a/common/cmd_nand.c
+++ b/common/cmd_nand.c
@@ -605,22 +605,16 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
opts.spread = spread;
if (scrub) {
- if (!scrub_yes)
- puts(scrub_warn);
-
- if (scrub_yes)
+ if (scrub_yes) {
opts.scrub = 1;
- else if (getc() == 'y') {
- puts("y");
- if (getc() == '\r')
+ } else {
+ puts(scrub_warn);
+ if (confirm_yesno()) {
opts.scrub = 1;
- else {
+ } else {
puts("scrub aborted\n");
return 1;
}
- } else {
- puts("scrub aborted\n");
- return 1;
}
}
ret = nand_erase_opts(nand, &opts);
diff --git a/common/cmd_otp.c b/common/cmd_otp.c
index 67808aa..593bb8c 100644
--- a/common/cmd_otp.c
+++ b/common/cmd_otp.c
@@ -158,21 +158,9 @@ int do_otp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
lowup(half + count - 1), page + (half + count - 1) / 2,
half + count
);
-
- i = 0;
- while (1) {
- if (tstc()) {
- const char exp_ans[] = "YES\r";
- char c;
- putc(c = getc());
- if (exp_ans[i++] != c) {
- printf(" Aborting\n");
- return 1;
- } else if (!exp_ans[i]) {
- puts("\n");
- break;
- }
- }
+ if (!confirm_yesno()) {
+ printf(" Aborting\n");
+ return 1;
}
}
diff --git a/common/console.c b/common/console.c
index 2dfb788..5453726 100644
--- a/common/console.c
+++ b/common/console.c
@@ -537,7 +537,33 @@ int ctrlc(void)
}
return 0;
}
-
+/* Reads user's confirmation.
+ Returns 1 if user's input is "y", "Y", "yes" or "YES"
+*/
+int confirm_yesno(void)
+{
+ int i;
+ char str_input[5];
+
+ /* Flush input */
+ while (tstc())
+ getc();
+ i = 0;
+ while (i < sizeof(str_input)) {
+ str_input[i] = getc();
+ putc(str_input[i]);
+ if (str_input[i] == '\r')
+ break;
+ i++;
+ }
+ putc('\n');
+ if (strncmp(str_input, "y\r", 2) == 0 ||
+ strncmp(str_input, "Y\r", 2) == 0 ||
+ strncmp(str_input, "yes\r", 4) == 0 ||
+ strncmp(str_input, "YES\r", 4) == 0)
+ return 1;
+ return 0;
+}
/* pass 1 to disable ctrlc() checking, 0 to enable.
* returns previous state
*/
diff --git a/include/common.h b/include/common.h
index baf361b..1d922b9 100644
--- a/include/common.h
+++ b/include/common.h
@@ -838,7 +838,7 @@ int ctrlc (void);
int had_ctrlc (void); /* have we had a Control-C since last clear? */
void clear_ctrlc (void); /* clear the Control-C condition */
int disable_ctrlc (int); /* 1 to disable, 0 to enable Control-C detect */
-
+int confirm_yesno(void); /* 1 if input is "y", "Y", "yes" or "YES" */
/*
* STDIO based functions (can always be used)
*/
--
1.7.6.5
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V3 3/3] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command
2014-04-22 15:54 ` [U-Boot] [PATCH V3 0/3] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
2014-04-22 15:54 ` [U-Boot] [PATCH V3 1/3] eMMC: add support for operations in RPMB partition Pierre Aubert
2014-04-22 15:54 ` [U-Boot] [PATCH V3 2/3] Add the function 'confirm_yesno' for interactive confirmation Pierre Aubert
@ 2014-04-22 15:54 ` Pierre Aubert
2014-04-22 17:48 ` Wolfgang Denk
2 siblings, 1 reply; 31+ messages in thread
From: Pierre Aubert @ 2014-04-22 15:54 UTC (permalink / raw)
To: u-boot
This sub-command adds support for the RPMB partition of an eMMC:
* mmc rpmb key <address of the authentication key>
Programs the authentication key in the eMMC This key can not
be overwritten.
* mmc rpmb read <address> <block> <#count> [address of key]
Reads <#count> blocks of 256 bytes in the RPMB partition
beginning at block number <block>. If the optionnal
address of the authentication key is provided, the
Message Authentication Code (MAC) is verified on each
block.
* mmc rpmb write <address> <block> <#count> <address of key>
Writes <#count> blocks of 256 bytes in the RPMB partition
beginning at block number <block>. The datas are signed
with the key provided.
* mmc rpmb counter
Returns the 'Write counter' of the RPMB partition.
The sub-command is conditional on compilation flag CONFIG_SUPPORT_EMMC_RPMB
Signed-off-by: Pierre Aubert <p.aubert@staubli.com>
CC: Pantelis Antoniou <panto@antoniou-consulting.com>
---
README | 10 ++++
common/cmd_mmc.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 132 insertions(+), 1 deletions(-)
diff --git a/README b/README
index 52a92e7..518e209 100644
--- a/README
+++ b/README
@@ -1493,6 +1493,16 @@ The following options need to be configured:
CONFIG_SH_MMCIF_CLK
Define the clock frequency for MMCIF
+ CONFIG_GENERIC_MMC
+ Enable the generic MMC driver
+
+ CONFIG_SUPPORT_EMMC_BOOT
+ Enable some additional features of the eMMC boot partitions.
+
+ CONFIG_SUPPORT_EMMC_RPMB
+ Enable the commands for reading, writing and programming the
+ key for the Replay Protection Memory Block partition in eMMC.
+
- USB Device Firmware Update (DFU) class support:
CONFIG_DFU_FUNCTION
This enables the USB portion of the DFU USB class
diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c
index c1916c9..da8a243 100644
--- a/common/cmd_mmc.c
+++ b/common/cmd_mmc.c
@@ -130,7 +130,118 @@ U_BOOT_CMD(
"display MMC info",
"- display info of the current MMC device"
);
+#ifdef CONFIG_SUPPORT_EMMC_RPMB
+static int confirm_key_prog(void)
+{
+ puts("Warning: Programming authentication key can be done only once !\n"
+ " Use this command only if you are sure of what you are doing,\n"
+ "Really perform the key programming? <y/N> ");
+ if (confirm_yesno())
+ return 1;
+
+ puts("Authentication key programming aborted\n");
+ return 0;
+}
+static int do_mmcrpmb(int argc, char * const argv[])
+{
+ enum rpmb_state {
+ RPMB_INVALID,
+ RPMB_READ,
+ RPMB_WRITE,
+ RPMB_KEY,
+ RPMB_COUNTER,
+ } state;
+ struct mmc *mmc = find_mmc_device(curr_device);
+ void *key_addr;
+ char original_part;
+ int ret;
+
+ state = RPMB_INVALID;
+ if (argc == 4 && strcmp(argv[2], "key") == 0)
+ state = RPMB_KEY;
+ if ((argc == 6 || argc == 7) && strcmp(argv[2], "read") == 0)
+ state = RPMB_READ;
+ else if (argc == 7 && strcmp(argv[2], "write") == 0)
+ state = RPMB_WRITE;
+ else if (argc == 3 && strcmp(argv[2], "counter") == 0)
+ state = RPMB_COUNTER;
+
+ if (state == RPMB_INVALID)
+ return CMD_RET_USAGE;
+ if (!mmc) {
+ printf("no mmc device at slot %x\n", curr_device);
+ return CMD_RET_FAILURE;
+ }
+ mmc_init(mmc);
+ if (!(mmc->version & MMC_VERSION_MMC)) {
+ printf("It is not a EMMC device\n");
+ return CMD_RET_FAILURE;
+ }
+ if (mmc->version < MMC_VERSION_4_41) {
+ printf("RPMB not supported before version 4.41\n");
+ return CMD_RET_FAILURE;
+ }
+ /* Switch to the RPMB partition */
+ original_part = mmc->part_num;
+ if (mmc->part_num != MMC_PART_RPMB) {
+ if (mmc_switch_part(curr_device, MMC_PART_RPMB) != 0)
+ return CMD_RET_FAILURE;
+ mmc->part_num = MMC_PART_RPMB;
+ }
+ ret = CMD_RET_SUCCESS;
+ if (state == RPMB_KEY) {
+ key_addr = (void *)simple_strtoul(argv[3], NULL, 16);
+ if (!confirm_key_prog())
+ return CMD_RET_FAILURE;
+ if (mmc_rpmb_set_key(mmc, key_addr)) {
+ printf("ERROR - Key already programmed ?\n");
+ ret = CMD_RET_FAILURE;
+ }
+ } else if (state == RPMB_COUNTER) {
+ unsigned long counter;
+
+ if (mmc_rpmb_get_counter(mmc, &counter))
+ ret = CMD_RET_FAILURE;
+ else
+ printf("Write counter= %lx\n", counter);
+ } else {
+ u16 blk, cnt;
+ void *addr;
+ int n;
+
+ addr = (void *)simple_strtoul(argv[3], NULL, 16);
+ blk = simple_strtoul(argv[4], NULL, 16);
+ cnt = simple_strtoul(argv[5], NULL, 16);
+
+ if (state == RPMB_READ) {
+ key_addr = (argc == 7) ?
+ (void *)simple_strtoul(argv[6],
+ NULL, 16) :
+ NULL;
+ n = mmc_rpmb_read(mmc, addr, blk, cnt,
+ key_addr);
+ } else {
+ key_addr = (void *)simple_strtoul(argv[6],
+ NULL, 16);
+ n = mmc_rpmb_write(mmc, addr, blk, cnt,
+ key_addr);
+ }
+ printf("%d RPMB blocks %s: %s\n",
+ n, argv[2], (n == cnt) ? "OK" : "ERROR");
+ if (n != cnt)
+ ret = CMD_RET_FAILURE;
+ }
+
+ /* Return to orginal partition */
+ if (mmc->part_num != original_part) {
+ if (mmc_switch_part(curr_device, original_part) != 0)
+ return CMD_RET_FAILURE;
+ mmc->part_num = original_part;
+ }
+ return ret;
+}
+#endif
static int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
enum mmc_state state;
@@ -365,6 +476,10 @@ static int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
return mmc_set_rst_n_function(mmc, enable);
#endif /* CONFIG_SUPPORT_EMMC_BOOT */
+#ifdef CONFIG_SUPPORT_EMMC_RPMB
+ } else if (strcmp(argv[1], "rpmb") == 0) {
+ return do_mmcrpmb(argc, argv);
+#endif /* CONFIG_SUPPORT_EMMC_RPMB */
}
else if (argc == 3 && strcmp(argv[1], "setdsr") == 0) {
@@ -454,7 +569,7 @@ static int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
}
U_BOOT_CMD(
- mmc, 6, 1, do_mmcops,
+ mmc, 7, 1, do_mmcops,
"MMC sub system",
"read addr blk# cnt\n"
"mmc write addr blk# cnt\n"
@@ -474,6 +589,12 @@ U_BOOT_CMD(
" - Change the RST_n_FUNCTION field of the specified device\n"
" WARNING: This is a write-once field and 0 / 1 / 2 are the only valid values.\n"
#endif
+#ifdef CONFIG_SUPPORT_EMMC_RPMB
+ "mmc rpmb read addr blk# cnt [address of auth-key] - block size is 256 bytes\n"
+ "mmc rpmb write addr blk# cnt <address of auth-key> - block size is 256 bytes\n"
+ "mmc rpmb key <address of auth-key> - program the RPMB authentication key.\n"
+ "mmc rpmb counter - read the value of the write counter\n"
+#endif
"mmc setdsr - set DSR register value\n"
);
#endif /* !CONFIG_GENERIC_MMC */
--
1.7.6.5
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V2 2/2] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command
2014-04-18 6:39 ` Pierre AUBERT
@ 2014-04-22 17:41 ` Wolfgang Denk
0 siblings, 0 replies; 31+ messages in thread
From: Wolfgang Denk @ 2014-04-22 17:41 UTC (permalink / raw)
To: u-boot
Dear Pierre,
In message <5350C8BC.9000607@staubli.com> you wrote:
>
> > I think that now, with more subcommands being added, we should
> > convert the mmc code to proper subcommand handling. [It might even
> > make sense to do so for "mmc rpmb", too.]
> Do you think about the use of the macro U_BOOT_CMD_MKENT ?
Yes - see for example how the "env" sub-commands are implemented.
Best regards,
Wolfgang Denk
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de
Never underestimate the power of human stupidity when it comes to
using technology they don't understand.
^ permalink raw reply [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V3 3/3] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command
2014-04-22 15:54 ` [U-Boot] [PATCH V3 3/3] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command Pierre Aubert
@ 2014-04-22 17:48 ` Wolfgang Denk
0 siblings, 0 replies; 31+ messages in thread
From: Wolfgang Denk @ 2014-04-22 17:48 UTC (permalink / raw)
To: u-boot
Dear Pierre Aubert,
In message <1398182087-14913-4-git-send-email-p.aubert@staubli.com> you wrote:
> This sub-command adds support for the RPMB partition of an eMMC:
> * mmc rpmb key <address of the authentication key>
> Programs the authentication key in the eMMC This key can not
> be overwritten.
> * mmc rpmb read <address> <block> <#count> [address of key]
> Reads <#count> blocks of 256 bytes in the RPMB partition
> beginning at block number <block>. If the optionnal
> address of the authentication key is provided, the
> Message Authentication Code (MAC) is verified on each
> block.
> * mmc rpmb write <address> <block> <#count> <address of key>
> Writes <#count> blocks of 256 bytes in the RPMB partition
> beginning at block number <block>. The datas are signed
> with the key provided.
> * mmc rpmb counter
> Returns the 'Write counter' of the RPMB partition.
>
> The sub-command is conditional on compilation flag CONFIG_SUPPORT_EMMC_RPMB
>
> Signed-off-by: Pierre Aubert <p.aubert@staubli.com>
> CC: Pantelis Antoniou <panto@antoniou-consulting.com>
> ---
> README | 10 ++++
> common/cmd_mmc.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 132 insertions(+), 1 deletions(-)
Unfortunatley there is no changelog here. Please note that this is
mandatory for updated patch versions, see [1]
[1] http://www.denx.de/wiki/view/U-Boot/Patches#Sending_updated_patch_versions
A changelog in the cover letter is useless, as this does not get
recorded in patchwork. The changelog must always be in the patch
itself.
...
> + if (argc == 4 && strcmp(argv[2], "key") == 0)
> + state = RPMB_KEY;
> + if ((argc == 6 || argc == 7) && strcmp(argv[2], "read") == 0)
> + state = RPMB_READ;
> + else if (argc == 7 && strcmp(argv[2], "write") == 0)
> + state = RPMB_WRITE;
> + else if (argc == 3 && strcmp(argv[2], "counter") == 0)
> + state = RPMB_COUNTER;
As mentiuoned before, these repeated strcmp() should probably be
replaced by a subcommand table.
> + ret = CMD_RET_SUCCESS;
> + if (state == RPMB_KEY) {
> + key_addr = (void *)simple_strtoul(argv[3], NULL, 16);
> + if (!confirm_key_prog())
> + return CMD_RET_FAILURE;
> + if (mmc_rpmb_set_key(mmc, key_addr)) {
> + printf("ERROR - Key already programmed ?\n");
> + ret = CMD_RET_FAILURE;
Do a return here, or use some "goto errout" and place the label errout
before the needed cleanup acrtions.
> + }
> + } else if (state == RPMB_COUNTER) {
You can then get rid of this "else" level.
> + unsigned long counter;
> +
> + if (mmc_rpmb_get_counter(mmc, &counter))
> + ret = CMD_RET_FAILURE;
> + else
Ditto.
> + printf("Write counter= %lx\n", counter);
Should this be a debug() ?
> +#ifdef CONFIG_SUPPORT_EMMC_RPMB
> + } else if (strcmp(argv[1], "rpmb") == 0) {
> + return do_mmcrpmb(argc, argv);
> +#endif /* CONFIG_SUPPORT_EMMC_RPMB */
> }
Again, it would be better to switch the code to regular subcommand
processing.
Best regards,
Wolfgang Denk
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de
"I knew then (in 1970) that a 4-kbyte minicomputer would cost as much
as a house. So I reasoned that after college, I'd have to live
cheaply in an apartment and put all my money into owning a computer."
- Apple co-founder Steve Wozniak, EE Times, June 6, 1988, pg 45
^ permalink raw reply [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V4 0/3] eMMC: support for Read Protected Memory Block (RPMB)
2014-04-11 12:12 [U-Boot] [PATCH 0/2] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
` (3 preceding siblings ...)
2014-04-22 15:54 ` [U-Boot] [PATCH V3 0/3] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
@ 2014-04-24 6:40 ` Pierre Aubert
2014-04-24 6:40 ` [U-Boot] [PATCH V4 1/3] eMMC: add support for operations in RPMB partition Pierre Aubert
` (2 more replies)
2014-04-24 8:30 ` [U-Boot] [PATCH V5 0/3] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
5 siblings, 3 replies; 31+ messages in thread
From: Pierre Aubert @ 2014-04-24 6:40 UTC (permalink / raw)
To: u-boot
This serie of patches adds some functions and a sub-command of 'mmc' for programming the authentication key and for reading and writing the RPMB partition of an eMMC according to the JEDEC standard No. 64-A441
The sub-command rpmb is enabled by the flag CONFIG_SUPPORT_EMMC_RPMB defined
in the board configuration file.
It has been tested on a SabreSDP iMX6 board.
Changes in V4:
- use subcommand tables for 'mmc' command and 'mmc rpmb' subcommand.
- add 'mmc info' subcommand, which is the same as mmcinfo.
mmcinfo is kept for compatibility.
- add changelog in commit comments.
Changes in V3:
- add entries in README for configuration options related to eMMC.
- new patch for adding the 'confirm_yesno' function as suggested by W.Denk
- improved test for existence of RPMB partition.
- fix of coding issues.
Changes in V2:
- use ALLOC_CACHE_ALIGN_BUFFER in rpmb.c instead of a static buffer for the
RPMB frames.
Pierre Aubert (3):
eMMC: add support for operations in RPMB partition
Add the function 'confirm_yesno' for interactive
eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command
README | 10 +
common/cmd_fuse.c | 11 +-
common/cmd_mmc.c | 806 +++++++++++++++++++++++++++++++------------------
common/cmd_nand.c | 16 +-
common/cmd_otp.c | 18 +-
common/console.c | 28 ++-
drivers/mmc/Makefile | 1 +
drivers/mmc/rpmb.c | 323 ++++++++++++++++++++
include/common.h | 2 +-
include/mmc.h | 10 +-
lib/Makefile | 3 +
11 files changed, 894 insertions(+), 334 deletions(-)
create mode 100644 drivers/mmc/rpmb.c
--
1.7.6.5
^ permalink raw reply [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V4 1/3] eMMC: add support for operations in RPMB partition
2014-04-24 6:40 ` [U-Boot] [PATCH V4 0/3] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
@ 2014-04-24 6:40 ` Pierre Aubert
2014-04-24 6:55 ` Wolfgang Denk
2014-04-24 6:59 ` Wolfgang Denk
2014-04-24 6:40 ` [U-Boot] [PATCH V4 2/3] Add the function 'confirm_yesno' for interactive Pierre Aubert
2014-04-24 6:40 ` [U-Boot] [PATCH V4 3/3] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command Pierre Aubert
2 siblings, 2 replies; 31+ messages in thread
From: Pierre Aubert @ 2014-04-24 6:40 UTC (permalink / raw)
To: u-boot
This patch adds functions for read, write and authentication
key programming for the Replay Protected Memory Block partition
in the eMMC.
Changes in V2:
- use ALLOC_CACHE_ALIGN_BUFFER in rpmb.c instead of a static buffer for the
RPMB frames.
Signed-off-by: Pierre Aubert <p.aubert@staubli.com>
CC: Pantelis Antoniou <panto@antoniou-consulting.com>
---
drivers/mmc/Makefile | 1 +
drivers/mmc/rpmb.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++++++
include/mmc.h | 10 ++-
lib/Makefile | 3 +
4 files changed, 336 insertions(+), 1 deletions(-)
create mode 100644 drivers/mmc/rpmb.c
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 931922b..4c6ab9e 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_DWMMC) += dw_mmc.o
obj-$(CONFIG_EXYNOS_DWMMC) += exynos_dw_mmc.o
obj-$(CONFIG_ZYNQ_SDHCI) += zynq_sdhci.o
obj-$(CONFIG_SOCFPGA_DWMMC) += socfpga_dw_mmc.o
+obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o
ifdef CONFIG_SPL_BUILD
obj-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o
else
diff --git a/drivers/mmc/rpmb.c b/drivers/mmc/rpmb.c
new file mode 100644
index 0000000..05936f5
--- /dev/null
+++ b/drivers/mmc/rpmb.c
@@ -0,0 +1,323 @@
+/*
+ * Copyright 2014, Staubli Faverges
+ * Pierre Aubert
+ *
+ * eMMC- Replay Protected Memory Block
+ * According to JEDEC Standard No. 84-A441
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <config.h>
+#include <common.h>
+#include <mmc.h>
+#include <sha256.h>
+#include "mmc_private.h"
+
+/* Request codes */
+#define RPMB_REQ_KEY 1
+#define RPMB_REQ_WCOUNTER 2
+#define RPMB_REQ_WRITE_DATA 3
+#define RPMB_REQ_READ_DATA 4
+#define RPMB_REQ_STATUS 5
+
+/* Response code */
+#define RPMB_RESP_KEY 0x0100
+#define RPMB_RESP_WCOUNTER 0x0200
+#define RPMB_RESP_WRITE_DATA 0x0300
+#define RPMB_RESP_READ_DATA 0x0400
+
+/* Error codes */
+#define RPMB_OK 0
+#define RPMB_ERR_GENERAL 1
+#define RPMB_ERR_AUTH 2
+#define RPMB_ERR_COUNTER 3
+#define RPMB_ERR_ADDRESS 4
+#define RPMB_ERR_WRITE 5
+#define RPMB_ERR_READ 6
+#define RPMB_ERR_KEY 7
+#define RPMB_ERR_CNT_EXPIRED 0x80
+#define RPMB_ERR_MSK 0x7
+
+/* Sizes of RPMB data frame */
+#define RPMB_SZ_STUFF 196
+#define RPMB_SZ_MAC 32
+#define RPMB_SZ_DATA 256
+#define RPMB_SZ_NONCE 16
+
+#define SHA256_BLOCK_SIZE 64
+
+/* Error messages */
+static const char * const rpmb_err_msg[] = {
+ "",
+ "General failure",
+ "Authentication failure",
+ "Counter failure",
+ "Address failure",
+ "Write failure",
+ "Read failure",
+ "Authentication key not yet programmed",
+};
+
+
+/* Structure of RPMB data frame. */
+struct s_rpmb {
+ unsigned char stuff[RPMB_SZ_STUFF];
+ unsigned char mac[RPMB_SZ_MAC];
+ unsigned char data[RPMB_SZ_DATA];
+ unsigned char nonce[RPMB_SZ_NONCE];
+ unsigned long write_counter;
+ unsigned short address;
+ unsigned short block_count;
+ unsigned short result;
+ unsigned short request;
+};
+
+static int mmc_set_blockcount(struct mmc *mmc, unsigned int blockcount,
+ bool is_rel_write)
+{
+ struct mmc_cmd cmd = {0};
+
+ cmd.cmdidx = MMC_CMD_SET_BLOCK_COUNT;
+ cmd.cmdarg = blockcount & 0x0000FFFF;
+ if (is_rel_write)
+ cmd.cmdarg |= 1 << 31;
+ cmd.resp_type = MMC_RSP_R1;
+
+ return mmc_send_cmd(mmc, &cmd, NULL);
+}
+static int mmc_rpmb_request(struct mmc *mmc, const struct s_rpmb *s,
+ unsigned int count, bool is_rel_write)
+{
+ struct mmc_cmd cmd = {0};
+ struct mmc_data data;
+ int ret;
+
+ ret = mmc_set_blockcount(mmc, count, is_rel_write);
+ if (ret) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:mmc_set_blockcount-> %d\n", __func__, ret);
+#endif
+ return 1;
+ }
+
+ cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
+ cmd.cmdarg = 0;
+ cmd.resp_type = MMC_RSP_R1b;
+
+ data.src = (const char *)s;
+ data.blocks = 1;
+ data.blocksize = MMC_MAX_BLOCK_LEN;
+ data.flags = MMC_DATA_WRITE;
+
+ ret = mmc_send_cmd(mmc, &cmd, &data);
+ if (ret) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:mmc_send_cmd-> %d\n", __func__, ret);
+#endif
+ return 1;
+ }
+ return 0;
+}
+static int mmc_rpmb_response(struct mmc *mmc, struct s_rpmb *s,
+ unsigned short expected)
+{
+ struct mmc_cmd cmd = {0};
+ struct mmc_data data;
+ int ret;
+
+ ret = mmc_set_blockcount(mmc, 1, false);
+ if (ret) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:mmc_set_blockcount-> %d\n", __func__, ret);
+#endif
+ return -1;
+ }
+ cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
+ cmd.cmdarg = 0;
+ cmd.resp_type = MMC_RSP_R1;
+
+ data.dest = (char *)s;
+ data.blocks = 1;
+ data.blocksize = MMC_MAX_BLOCK_LEN;
+ data.flags = MMC_DATA_READ;
+
+ ret = mmc_send_cmd(mmc, &cmd, &data);
+ if (ret) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:mmc_send_cmd-> %d\n", __func__, ret);
+#endif
+ return -1;
+ }
+ /* Check the response and the status */
+ if (be16_to_cpu(s->request) != expected) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:response= %x\n", __func__,
+ be16_to_cpu(s->request));
+#endif
+ return -1;
+ }
+ ret = be16_to_cpu(s->result);
+ if (ret) {
+ printf("%s %s\n", rpmb_err_msg[ret & RPMB_ERR_MSK],
+ (ret & RPMB_ERR_CNT_EXPIRED) ?
+ "Write counter has expired" : "");
+ }
+
+ /* Return the status of the command */
+ return ret;
+}
+static int mmc_rpmb_status(struct mmc *mmc, unsigned short expected)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
+
+ memset(rpmb_frame, 0, sizeof(struct s_rpmb));
+ rpmb_frame->request = cpu_to_be16(RPMB_REQ_STATUS);
+ if (mmc_rpmb_request(mmc, rpmb_frame, 1, false))
+ return -1;
+
+ /* Read the result */
+ return mmc_rpmb_response(mmc, rpmb_frame, expected);
+}
+static void rpmb_hmac(unsigned char *key, unsigned char *buff, int len,
+ unsigned char *output)
+{
+ sha256_context ctx;
+ int i;
+ unsigned char k_ipad[SHA256_BLOCK_SIZE];
+ unsigned char k_opad[SHA256_BLOCK_SIZE];
+
+ sha256_starts(&ctx);
+
+ /* According to RFC 4634, the HMAC transform looks like:
+ SHA(K XOR opad, SHA(K XOR ipad, text))
+
+ where K is an n byte key.
+ ipad is the byte 0x36 repeated blocksize times
+ opad is the byte 0x5c repeated blocksize times
+ and text is the data being protected.
+ */
+
+ for (i = 0; i < RPMB_SZ_MAC; i++) {
+ k_ipad[i] = key[i] ^ 0x36;
+ k_opad[i] = key[i] ^ 0x5c;
+ }
+ /* remaining pad bytes are '\0' XOR'd with ipad and opad values */
+ for ( ; i < SHA256_BLOCK_SIZE; i++) {
+ k_ipad[i] = 0x36;
+ k_opad[i] = 0x5c;
+ }
+ sha256_update(&ctx, k_ipad, SHA256_BLOCK_SIZE);
+ sha256_update(&ctx, buff, len);
+ sha256_finish(&ctx, output);
+
+ /* Init context for second pass */
+ sha256_starts(&ctx);
+
+ /* start with outer pad */
+ sha256_update(&ctx, k_opad, SHA256_BLOCK_SIZE);
+
+ /* then results of 1st hash */
+ sha256_update(&ctx, output, RPMB_SZ_MAC);
+
+ /* finish up 2nd pass */
+ sha256_finish(&ctx, output);
+}
+int mmc_rpmb_get_counter(struct mmc *mmc, unsigned long *pcounter)
+{
+ int ret;
+ ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
+
+ /* Fill the request */
+ memset(rpmb_frame, 0, sizeof(struct s_rpmb));
+ rpmb_frame->request = cpu_to_be16(RPMB_REQ_WCOUNTER);
+ if (mmc_rpmb_request(mmc, rpmb_frame, 1, false))
+ return -1;
+
+ /* Read the result */
+ ret = mmc_rpmb_response(mmc, rpmb_frame, RPMB_RESP_WCOUNTER);
+ if (ret)
+ return ret;
+
+ *pcounter = be32_to_cpu(rpmb_frame->write_counter);
+ return 0;
+}
+int mmc_rpmb_set_key(struct mmc *mmc, void *key)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
+ /* Fill the request */
+ memset(rpmb_frame, 0, sizeof(struct s_rpmb));
+ rpmb_frame->request = cpu_to_be16(RPMB_REQ_KEY);
+ memcpy(rpmb_frame->mac, key, RPMB_SZ_MAC);
+
+ if (mmc_rpmb_request(mmc, rpmb_frame, 1, true))
+ return -1;
+
+ /* read the operation status */
+ return mmc_rpmb_status(mmc, RPMB_RESP_KEY);
+}
+int mmc_rpmb_read(struct mmc *mmc, void *addr, unsigned short blk,
+ unsigned short cnt, unsigned char *key)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
+ int i;
+
+ for (i = 0; i < cnt; i++) {
+ /* Fill the request */
+ memset(rpmb_frame, 0, sizeof(struct s_rpmb));
+ rpmb_frame->address = cpu_to_be16(blk + i);
+ rpmb_frame->request = cpu_to_be16(RPMB_REQ_READ_DATA);
+ if (mmc_rpmb_request(mmc, rpmb_frame, 1, false))
+ break;
+
+ /* Read the result */
+ if (mmc_rpmb_response(mmc, rpmb_frame, RPMB_RESP_READ_DATA))
+ break;
+
+ /* Check the HMAC if key is provided */
+ if (key) {
+ unsigned char ret_hmac[RPMB_SZ_MAC];
+
+ rpmb_hmac(key, rpmb_frame->data, 284, ret_hmac);
+ if (memcmp(ret_hmac, rpmb_frame->mac, RPMB_SZ_MAC)) {
+ printf("MAC error on block #%d\n", i);
+ break;
+ }
+ }
+ /* Copy data */
+ memcpy(addr + i * RPMB_SZ_DATA, rpmb_frame->data, RPMB_SZ_DATA);
+ }
+ return i;
+}
+int mmc_rpmb_write(struct mmc *mmc, void *addr, unsigned short blk,
+ unsigned short cnt, unsigned char *key)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
+ unsigned long wcount;
+ int i;
+
+ for (i = 0; i < cnt; i++) {
+ if (mmc_rpmb_get_counter(mmc, &wcount)) {
+ printf("Cannot read RPMB write counter\n");
+ break;
+ }
+
+ /* Fill the request */
+ memset(rpmb_frame, 0, sizeof(struct s_rpmb));
+ memcpy(rpmb_frame->data, addr + i * RPMB_SZ_DATA, RPMB_SZ_DATA);
+ rpmb_frame->address = cpu_to_be16(blk + i);
+ rpmb_frame->block_count = cpu_to_be16(1);
+ rpmb_frame->write_counter = cpu_to_be32(wcount);
+ rpmb_frame->request = cpu_to_be16(RPMB_REQ_WRITE_DATA);
+ /* Computes HMAC */
+ rpmb_hmac(key, rpmb_frame->data, 284, rpmb_frame->mac);
+
+ if (mmc_rpmb_request(mmc, rpmb_frame, 1, true))
+ break;
+
+ /* Get status */
+ if (mmc_rpmb_status(mmc, RPMB_RESP_WRITE_DATA))
+ break;
+ }
+ return i;
+}
diff --git a/include/mmc.h b/include/mmc.h
index 42d0125..14d296c 100644
--- a/include/mmc.h
+++ b/include/mmc.h
@@ -69,6 +69,7 @@
#define MMC_CMD_SET_BLOCKLEN 16
#define MMC_CMD_READ_SINGLE_BLOCK 17
#define MMC_CMD_READ_MULTIPLE_BLOCK 18
+#define MMC_CMD_SET_BLOCK_COUNT 23
#define MMC_CMD_WRITE_SINGLE_BLOCK 24
#define MMC_CMD_WRITE_MULTIPLE_BLOCK 25
#define MMC_CMD_ERASE_GROUP_START 35
@@ -224,6 +225,7 @@
* boot partitions (2), general purpose partitions (4) in MMC v4.4.
*/
#define MMC_NUM_BOOT_PARTITION 2
+#define MMC_PART_RPMB 3 /* RPMB partition number */
struct mmc_cid {
unsigned long psn;
@@ -335,7 +337,13 @@ int mmc_set_part_conf(struct mmc *mmc, u8 ack, u8 part_num, u8 access);
int mmc_set_boot_bus_width(struct mmc *mmc, u8 width, u8 reset, u8 mode);
/* Function to modify the RST_n_FUNCTION field of EXT_CSD */
int mmc_set_rst_n_function(struct mmc *mmc, u8 enable);
-
+/* Functions to read / write the RPMB partition */
+int mmc_rpmb_set_key(struct mmc *mmc, void *key);
+int mmc_rpmb_get_counter(struct mmc *mmc, unsigned long *counter);
+int mmc_rpmb_read(struct mmc *mmc, void *addr, unsigned short blk,
+ unsigned short cnt, unsigned char *key);
+int mmc_rpmb_write(struct mmc *mmc, void *addr, unsigned short blk,
+ unsigned short cnt, unsigned char *key);
/**
* Start device initialization and return immediately; it does not block on
* polling OCR (operation condition register) status. Then you should call
diff --git a/lib/Makefile b/lib/Makefile
index 27e4f78..2bf90f3 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -35,6 +35,9 @@ obj-y += net_utils.o
obj-$(CONFIG_PHYSMEM) += physmem.o
obj-y += qsort.o
obj-$(CONFIG_SHA1) += sha1.o
+ifdef CONFIG_SUPPORT_EMMC_RPMB
+CONFIG_SHA256 := y
+endif
obj-$(CONFIG_SHA256) += sha256.o
obj-y += strmhz.o
obj-$(CONFIG_TPM) += tpm.o
--
1.7.6.5
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V4 2/3] Add the function 'confirm_yesno' for interactive
2014-04-24 6:40 ` [U-Boot] [PATCH V4 0/3] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
2014-04-24 6:40 ` [U-Boot] [PATCH V4 1/3] eMMC: add support for operations in RPMB partition Pierre Aubert
@ 2014-04-24 6:40 ` Pierre Aubert
2014-04-24 6:40 ` [U-Boot] [PATCH V4 3/3] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command Pierre Aubert
2 siblings, 0 replies; 31+ messages in thread
From: Pierre Aubert @ 2014-04-24 6:40 UTC (permalink / raw)
To: u-boot
User's confirmation is asked in different commands. This commit adds a
function for such confirmation.
Signed-off-by: Pierre Aubert <p.aubert@staubli.com>
---
common/cmd_fuse.c | 11 ++---------
common/cmd_nand.c | 16 +++++-----------
common/cmd_otp.c | 18 +++---------------
common/console.c | 28 +++++++++++++++++++++++++++-
include/common.h | 2 +-
5 files changed, 38 insertions(+), 37 deletions(-)
diff --git a/common/cmd_fuse.c b/common/cmd_fuse.c
index 0df57db..abab978 100644
--- a/common/cmd_fuse.c
+++ b/common/cmd_fuse.c
@@ -33,15 +33,8 @@ static int confirm_prog(void)
"what you are doing!\n"
"\nReally perform this fuse programming? <y/N>\n");
- if (getc() == 'y') {
- int c;
-
- putc('y');
- c = getc();
- putc('\n');
- if (c == '\r')
- return 1;
- }
+ if (confirm_yesno())
+ return 1;
puts("Fuse programming aborted\n");
return 0;
diff --git a/common/cmd_nand.c b/common/cmd_nand.c
index 04ab0f1..a84f7dc 100644
--- a/common/cmd_nand.c
+++ b/common/cmd_nand.c
@@ -605,22 +605,16 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
opts.spread = spread;
if (scrub) {
- if (!scrub_yes)
- puts(scrub_warn);
-
- if (scrub_yes)
+ if (scrub_yes) {
opts.scrub = 1;
- else if (getc() == 'y') {
- puts("y");
- if (getc() == '\r')
+ } else {
+ puts(scrub_warn);
+ if (confirm_yesno()) {
opts.scrub = 1;
- else {
+ } else {
puts("scrub aborted\n");
return 1;
}
- } else {
- puts("scrub aborted\n");
- return 1;
}
}
ret = nand_erase_opts(nand, &opts);
diff --git a/common/cmd_otp.c b/common/cmd_otp.c
index 67808aa..593bb8c 100644
--- a/common/cmd_otp.c
+++ b/common/cmd_otp.c
@@ -158,21 +158,9 @@ int do_otp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
lowup(half + count - 1), page + (half + count - 1) / 2,
half + count
);
-
- i = 0;
- while (1) {
- if (tstc()) {
- const char exp_ans[] = "YES\r";
- char c;
- putc(c = getc());
- if (exp_ans[i++] != c) {
- printf(" Aborting\n");
- return 1;
- } else if (!exp_ans[i]) {
- puts("\n");
- break;
- }
- }
+ if (!confirm_yesno()) {
+ printf(" Aborting\n");
+ return 1;
}
}
diff --git a/common/console.c b/common/console.c
index 2dfb788..5453726 100644
--- a/common/console.c
+++ b/common/console.c
@@ -537,7 +537,33 @@ int ctrlc(void)
}
return 0;
}
-
+/* Reads user's confirmation.
+ Returns 1 if user's input is "y", "Y", "yes" or "YES"
+*/
+int confirm_yesno(void)
+{
+ int i;
+ char str_input[5];
+
+ /* Flush input */
+ while (tstc())
+ getc();
+ i = 0;
+ while (i < sizeof(str_input)) {
+ str_input[i] = getc();
+ putc(str_input[i]);
+ if (str_input[i] == '\r')
+ break;
+ i++;
+ }
+ putc('\n');
+ if (strncmp(str_input, "y\r", 2) == 0 ||
+ strncmp(str_input, "Y\r", 2) == 0 ||
+ strncmp(str_input, "yes\r", 4) == 0 ||
+ strncmp(str_input, "YES\r", 4) == 0)
+ return 1;
+ return 0;
+}
/* pass 1 to disable ctrlc() checking, 0 to enable.
* returns previous state
*/
diff --git a/include/common.h b/include/common.h
index baf361b..1d922b9 100644
--- a/include/common.h
+++ b/include/common.h
@@ -838,7 +838,7 @@ int ctrlc (void);
int had_ctrlc (void); /* have we had a Control-C since last clear? */
void clear_ctrlc (void); /* clear the Control-C condition */
int disable_ctrlc (int); /* 1 to disable, 0 to enable Control-C detect */
-
+int confirm_yesno(void); /* 1 if input is "y", "Y", "yes" or "YES" */
/*
* STDIO based functions (can always be used)
*/
--
1.7.6.5
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V4 3/3] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command
2014-04-24 6:40 ` [U-Boot] [PATCH V4 0/3] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
2014-04-24 6:40 ` [U-Boot] [PATCH V4 1/3] eMMC: add support for operations in RPMB partition Pierre Aubert
2014-04-24 6:40 ` [U-Boot] [PATCH V4 2/3] Add the function 'confirm_yesno' for interactive Pierre Aubert
@ 2014-04-24 6:40 ` Pierre Aubert
2 siblings, 0 replies; 31+ messages in thread
From: Pierre Aubert @ 2014-04-24 6:40 UTC (permalink / raw)
To: u-boot
This sub-command adds support for the RPMB partition of an eMMC:
* mmc rpmb key <address of the authentication key>
Programs the authentication key in the eMMC This key can not
be overwritten.
* mmc rpmb read <address> <block> <#count> [address of key]
Reads <#count> blocks of 256 bytes in the RPMB partition
beginning at block number <block>. If the optionnal
address of the authentication key is provided, the
Message Authentication Code (MAC) is verified on each
block.
* mmc rpmb write <address> <block> <#count> <address of key>
Writes <#count> blocks of 256 bytes in the RPMB partition
beginning at block number <block>. The datas are signed
with the key provided.
* mmc rpmb counter
Returns the 'Write counter' of the RPMB partition.
The sub-command is conditional on compilation flag CONFIG_SUPPORT_EMMC_RPMB
Changes in V4:
- use subcommand table for 'mmc' command and 'mmc rpmb' subcommand.
- add 'mmc info' subcommand, which is the same as mmcinfo.
mmcinfo is kept for compatibility.
- add changelog in commit comments.
Changes in V3:
- add entries in README for configuration options related to eMMC.
- improved test for existence of RPMB partition.
- fix of coding issues.
Signed-off-by: Pierre Aubert <p.aubert@staubli.com>
CC: Pantelis Antoniou <panto@antoniou-consulting.com>
CC: Wolfgang Denk <wd@denx.de>
---
README | 10 +
common/cmd_mmc.c | 806 ++++++++++++++++++++++++++++++++++--------------------
2 files changed, 520 insertions(+), 296 deletions(-)
diff --git a/README b/README
index 52a92e7..518e209 100644
--- a/README
+++ b/README
@@ -1493,6 +1493,16 @@ The following options need to be configured:
CONFIG_SH_MMCIF_CLK
Define the clock frequency for MMCIF
+ CONFIG_GENERIC_MMC
+ Enable the generic MMC driver
+
+ CONFIG_SUPPORT_EMMC_BOOT
+ Enable some additional features of the eMMC boot partitions.
+
+ CONFIG_SUPPORT_EMMC_RPMB
+ Enable the commands for reading, writing and programming the
+ key for the Replay Protection Memory Block partition in eMMC.
+
- USB Device Firmware Update (DFU) class support:
CONFIG_DFU_FUNCTION
This enables the USB portion of the DFU USB class
diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c
index c1916c9..55af295 100644
--- a/common/cmd_mmc.c
+++ b/common/cmd_mmc.c
@@ -71,12 +71,6 @@ U_BOOT_CMD(
);
#else /* !CONFIG_GENERIC_MMC */
-enum mmc_state {
- MMC_INVALID,
- MMC_READ,
- MMC_WRITE,
- MMC_ERASE,
-};
static void print_mmcinfo(struct mmc *mmc)
{
printf("Device: %s\n", mmc->cfg->name);
@@ -98,7 +92,18 @@ static void print_mmcinfo(struct mmc *mmc)
printf("Bus Width: %d-bit\n", mmc->bus_width);
}
-
+static struct mmc *init_mmc_device(int dev)
+{
+ struct mmc *mmc;
+ mmc = find_mmc_device(dev);
+ if (!mmc) {
+ printf("no mmc device at slot %x\n", dev);
+ return NULL;
+ }
+ if (mmc_init(mmc))
+ return NULL;
+ return mmc;
+}
static int do_mmcinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
struct mmc *mmc;
@@ -112,351 +117,546 @@ static int do_mmcinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
}
}
- mmc = find_mmc_device(curr_device);
+ mmc = init_mmc_device(curr_device);
+ if (!mmc)
+ return CMD_RET_FAILURE;
- if (mmc) {
- mmc_init(mmc);
+ print_mmcinfo(mmc);
+ return CMD_RET_SUCCESS;
+}
- print_mmcinfo(mmc);
- return 0;
- } else {
- printf("no mmc device at slot %x\n", curr_device);
+#ifdef CONFIG_SUPPORT_EMMC_RPMB
+static int confirm_key_prog(void)
+{
+ puts("Warning: Programming authentication key can be done only once !\n"
+ " Use this command only if you are sure of what you are doing,\n"
+ "Really perform the key programming? <y/N> ");
+ if (confirm_yesno())
return 1;
+
+ puts("Authentication key programming aborted\n");
+ return 0;
+}
+static int do_mmcrpmb_key(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ void *key_addr;
+ struct mmc *mmc = find_mmc_device(curr_device);
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ key_addr = (void *)simple_strtoul(argv[1], NULL, 16);
+ if (!confirm_key_prog())
+ return CMD_RET_FAILURE;
+ if (mmc_rpmb_set_key(mmc, key_addr)) {
+ printf("ERROR - Key already programmed ?\n");
+ return CMD_RET_FAILURE;
}
+ return CMD_RET_SUCCESS;
}
+static int do_mmcrpmb_read(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ u16 blk, cnt;
+ void *addr;
+ int n;
+ void *key_addr = NULL;
+ struct mmc *mmc = find_mmc_device(curr_device);
-U_BOOT_CMD(
- mmcinfo, 1, 0, do_mmcinfo,
- "display MMC info",
- "- display info of the current MMC device"
-);
+ if (argc < 4)
+ return CMD_RET_USAGE;
-static int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+ addr = (void *)simple_strtoul(argv[1], NULL, 16);
+ blk = simple_strtoul(argv[2], NULL, 16);
+ cnt = simple_strtoul(argv[3], NULL, 16);
+
+ if (argc == 5)
+ key_addr = (void *)simple_strtoul(argv[4], NULL, 16);
+
+ printf("\nMMC RPMB read: dev # %d, block # %d, count %d ... ",
+ curr_device, blk, cnt);
+ n = mmc_rpmb_read(mmc, addr, blk, cnt, key_addr);
+
+ printf("%d RPMB blocks read: %s\n", n, (n == cnt) ? "OK" : "ERROR");
+ if (n != cnt)
+ return CMD_RET_FAILURE;
+ return CMD_RET_SUCCESS;
+}
+static int do_mmcrpmb_write(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
{
- enum mmc_state state;
+ u16 blk, cnt;
+ void *addr;
+ int n;
+ void *key_addr;
+ struct mmc *mmc = find_mmc_device(curr_device);
- if (argc < 2)
+ if (argc != 5)
return CMD_RET_USAGE;
- if (curr_device < 0) {
- if (get_mmc_num() > 0)
- curr_device = 0;
- else {
- puts("No MMC device available\n");
- return 1;
- }
+ addr = (void *)simple_strtoul(argv[1], NULL, 16);
+ blk = simple_strtoul(argv[2], NULL, 16);
+ cnt = simple_strtoul(argv[3], NULL, 16);
+ key_addr = (void *)simple_strtoul(argv[4], NULL, 16);
+
+ printf("\nMMC RPMB write: dev # %d, block # %d, count %d ... ",
+ curr_device, blk, cnt);
+ n = mmc_rpmb_write(mmc, addr, blk, cnt, key_addr);
+
+ printf("%d RPMB blocks written: %s\n", n, (n == cnt) ? "OK" : "ERROR");
+ if (n != cnt)
+ return CMD_RET_FAILURE;
+ return CMD_RET_SUCCESS;
+}
+static int do_mmcrpmb_counter(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ unsigned long counter;
+ struct mmc *mmc = find_mmc_device(curr_device);
+
+ if (mmc_rpmb_get_counter(mmc, &counter))
+ return CMD_RET_FAILURE;
+ printf("RPMB Write counter= %lx\n", counter);
+ return CMD_RET_SUCCESS;
+}
+
+static cmd_tbl_t cmd_rpmb[] = {
+ U_BOOT_CMD_MKENT(key, 2, 0, do_mmcrpmb_key, "", ""),
+ U_BOOT_CMD_MKENT(read, 5, 1, do_mmcrpmb_read, "", ""),
+ U_BOOT_CMD_MKENT(write, 5, 0, do_mmcrpmb_write, "", ""),
+ U_BOOT_CMD_MKENT(counter, 1, 1, do_mmcrpmb_counter, "", ""),
+};
+
+static int do_mmcrpmb(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ cmd_tbl_t *cp;
+ struct mmc *mmc;
+ char original_part;
+ int ret;
+
+ cp = find_cmd_tbl(argv[1], cmd_rpmb, ARRAY_SIZE(cmd_rpmb));
+
+ /* Drop the rpmb subcommand */
+ argc--;
+ argv++;
+
+ if (cp == NULL || argc > cp->maxargs)
+ return CMD_RET_USAGE;
+ if (flag == CMD_FLAG_REPEAT && !cp->repeatable)
+ return CMD_RET_SUCCESS;
+
+ mmc = init_mmc_device(curr_device);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ if (!(mmc->version & MMC_VERSION_MMC)) {
+ printf("It is not a EMMC device\n");
+ return CMD_RET_FAILURE;
+ }
+ if (mmc->version < MMC_VERSION_4_41) {
+ printf("RPMB not supported before version 4.41\n");
+ return CMD_RET_FAILURE;
}
+ /* Switch to the RPMB partition */
+ original_part = mmc->part_num;
+ if (mmc->part_num != MMC_PART_RPMB) {
+ if (mmc_switch_part(curr_device, MMC_PART_RPMB) != 0)
+ return CMD_RET_FAILURE;
+ mmc->part_num = MMC_PART_RPMB;
+ }
+ ret = cp->cmd(cmdtp, flag, argc, argv);
- if (strcmp(argv[1], "rescan") == 0) {
- struct mmc *mmc;
+ /* Return to original partition */
+ if (mmc->part_num != original_part) {
+ if (mmc_switch_part(curr_device, original_part) != 0)
+ return CMD_RET_FAILURE;
+ mmc->part_num = original_part;
+ }
+ return ret;
+}
+#endif
- if (argc != 2)
- return CMD_RET_USAGE;
+static int do_mmc_read(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ struct mmc *mmc;
+ u32 blk, cnt, n;
+ void *addr;
- mmc = find_mmc_device(curr_device);
- if (!mmc) {
- printf("no mmc device at slot %x\n", curr_device);
- return 1;
- }
+ if (argc != 4)
+ return CMD_RET_USAGE;
- mmc->has_init = 0;
+ addr = (void *)simple_strtoul(argv[1], NULL, 16);
+ blk = simple_strtoul(argv[2], NULL, 16);
+ cnt = simple_strtoul(argv[3], NULL, 16);
- if (mmc_init(mmc))
- return 1;
- else
- return 0;
- } else if (strcmp(argv[1], "part") == 0) {
- block_dev_desc_t *mmc_dev;
- struct mmc *mmc;
+ mmc = init_mmc_device(curr_device);
+ if (!mmc)
+ return CMD_RET_FAILURE;
- if (argc != 2)
- return CMD_RET_USAGE;
+ printf("\nMMC read: dev # %d, block # %d, count %d ... ",
+ curr_device, blk, cnt);
- mmc = find_mmc_device(curr_device);
- if (!mmc) {
- printf("no mmc device at slot %x\n", curr_device);
- return 1;
- }
- mmc_init(mmc);
- mmc_dev = mmc_get_dev(curr_device);
- if (mmc_dev != NULL &&
- mmc_dev->type != DEV_TYPE_UNKNOWN) {
- print_part(mmc_dev);
- return 0;
- }
+ n = mmc->block_dev.block_read(curr_device, blk, cnt, addr);
+ /* flush cache after read */
+ flush_cache((ulong)addr, cnt * 512); /* FIXME */
+ printf("%d blocks read: %s\n", n, (n == cnt) ? "OK" : "ERROR");
- puts("get mmc type error!\n");
- return 1;
- } else if (strcmp(argv[1], "list") == 0) {
- if (argc != 2)
- return CMD_RET_USAGE;
- print_mmc_devices('\n');
- return 0;
- } else if (strcmp(argv[1], "dev") == 0) {
- int dev, part = -1;
- struct mmc *mmc;
-
- if (argc == 2)
- dev = curr_device;
- else if (argc == 3)
- dev = simple_strtoul(argv[2], NULL, 10);
- else if (argc == 4) {
- dev = (int)simple_strtoul(argv[2], NULL, 10);
- part = (int)simple_strtoul(argv[3], NULL, 10);
- if (part > PART_ACCESS_MASK) {
- printf("#part_num shouldn't be larger"
- " than %d\n", PART_ACCESS_MASK);
- return 1;
- }
- } else
- return CMD_RET_USAGE;
+ return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
+}
+static int do_mmc_write(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ struct mmc *mmc;
+ u32 blk, cnt, n;
+ void *addr;
- mmc = find_mmc_device(dev);
- if (!mmc) {
- printf("no mmc device at slot %x\n", dev);
- return 1;
- }
+ if (argc != 4)
+ return CMD_RET_USAGE;
- mmc_init(mmc);
- if (part != -1) {
- int ret;
- if (mmc->part_config == MMCPART_NOAVAILABLE) {
- printf("Card doesn't support part_switch\n");
- return 1;
- }
+ addr = (void *)simple_strtoul(argv[1], NULL, 16);
+ blk = simple_strtoul(argv[2], NULL, 16);
+ cnt = simple_strtoul(argv[3], NULL, 16);
- if (part != mmc->part_num) {
- ret = mmc_switch_part(dev, part);
- if (!ret)
- mmc->part_num = part;
+ mmc = init_mmc_device(curr_device);
+ if (!mmc)
+ return CMD_RET_FAILURE;
- printf("switch to partitions #%d, %s\n",
- part, (!ret) ? "OK" : "ERROR");
- }
- }
- curr_device = dev;
- if (mmc->part_config == MMCPART_NOAVAILABLE)
- printf("mmc%d is current device\n", curr_device);
- else
- printf("mmc%d(part %d) is current device\n",
- curr_device, mmc->part_num);
+ printf("\nMMC write: dev # %d, block # %d, count %d ... ",
+ curr_device, blk, cnt);
- return 0;
-#ifdef CONFIG_SUPPORT_EMMC_BOOT
- } else if (strcmp(argv[1], "partconf") == 0) {
- int dev;
- struct mmc *mmc;
- u8 ack, part_num, access;
-
- if (argc == 6) {
- dev = simple_strtoul(argv[2], NULL, 10);
- ack = simple_strtoul(argv[3], NULL, 10);
- part_num = simple_strtoul(argv[4], NULL, 10);
- access = simple_strtoul(argv[5], NULL, 10);
- } else {
- return CMD_RET_USAGE;
- }
+ if (mmc_getwp(mmc) == 1) {
+ printf("Error: card is write protected!\n");
+ return CMD_RET_FAILURE;
+ }
+ n = mmc->block_dev.block_write(curr_device, blk, cnt, addr);
+ printf("%d blocks written: %s\n", n, (n == cnt) ? "OK" : "ERROR");
- mmc = find_mmc_device(dev);
- if (!mmc) {
- printf("no mmc device at slot %x\n", dev);
- return 1;
- }
+ return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
+}
+static int do_mmc_erase(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ struct mmc *mmc;
+ u32 blk, cnt, n;
- if (IS_SD(mmc)) {
- puts("PARTITION_CONFIG only exists on eMMC\n");
- return 1;
- }
+ if (argc != 3)
+ return CMD_RET_USAGE;
- /* acknowledge to be sent during boot operation */
- return mmc_set_part_conf(mmc, ack, part_num, access);
- } else if (strcmp(argv[1], "bootbus") == 0) {
- int dev;
- struct mmc *mmc;
- u8 width, reset, mode;
-
- if (argc == 6) {
- dev = simple_strtoul(argv[2], NULL, 10);
- width = simple_strtoul(argv[3], NULL, 10);
- reset = simple_strtoul(argv[4], NULL, 10);
- mode = simple_strtoul(argv[5], NULL, 10);
- } else {
- return CMD_RET_USAGE;
- }
+ blk = simple_strtoul(argv[1], NULL, 16);
+ cnt = simple_strtoul(argv[2], NULL, 16);
- mmc = find_mmc_device(dev);
- if (!mmc) {
- printf("no mmc device at slot %x\n", dev);
- return 1;
- }
+ mmc = init_mmc_device(curr_device);
+ if (!mmc)
+ return CMD_RET_FAILURE;
- if (IS_SD(mmc)) {
- puts("BOOT_BUS_WIDTH only exists on eMMC\n");
- return 1;
- }
+ printf("\nMMC erase: dev # %d, block # %d, count %d ... ",
+ curr_device, blk, cnt);
- /* acknowledge to be sent during boot operation */
- return mmc_set_boot_bus_width(mmc, width, reset, mode);
- } else if (strcmp(argv[1], "bootpart-resize") == 0) {
- int dev;
- struct mmc *mmc;
- u32 bootsize, rpmbsize;
-
- if (argc == 5) {
- dev = simple_strtoul(argv[2], NULL, 10);
- bootsize = simple_strtoul(argv[3], NULL, 10);
- rpmbsize = simple_strtoul(argv[4], NULL, 10);
- } else {
- return CMD_RET_USAGE;
- }
+ if (mmc_getwp(mmc) == 1) {
+ printf("Error: card is write protected!\n");
+ return CMD_RET_FAILURE;
+ }
+ n = mmc->block_dev.block_erase(curr_device, blk, cnt);
+ printf("%d blocks erased: %s\n", n, (n == cnt) ? "OK" : "ERROR");
- mmc = find_mmc_device(dev);
- if (!mmc) {
- printf("no mmc device at slot %x\n", dev);
- return 1;
- }
+ return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
+}
+static int do_mmc_rescan(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ struct mmc *mmc;
- if (IS_SD(mmc)) {
- printf("It is not a EMMC device\n");
- return 1;
- }
+ mmc = find_mmc_device(curr_device);
+ if (!mmc) {
+ printf("no mmc device at slot %x\n", curr_device);
+ return CMD_RET_FAILURE;
+ }
- if (0 == mmc_boot_partition_size_change(mmc,
- bootsize, rpmbsize)) {
- printf("EMMC boot partition Size %d MB\n", bootsize);
- printf("EMMC RPMB partition Size %d MB\n", rpmbsize);
- return 0;
- } else {
- printf("EMMC boot partition Size change Failed.\n");
- return 1;
- }
- } else if (strcmp(argv[1], "rst-function") == 0) {
- /*
- * Set the RST_n_ENABLE bit of RST_n_FUNCTION
- * The only valid values are 0x0, 0x1 and 0x2 and writing
- * a value of 0x1 or 0x2 sets the value permanently.
- */
- int dev;
- struct mmc *mmc;
- u8 enable;
-
- if (argc == 4) {
- dev = simple_strtoul(argv[2], NULL, 10);
- enable = simple_strtoul(argv[3], NULL, 10);
- } else {
- return CMD_RET_USAGE;
- }
+ mmc->has_init = 0;
- if (enable > 2 || enable < 0) {
- puts("Invalid RST_n_ENABLE value\n");
- return CMD_RET_USAGE;
+ if (mmc_init(mmc))
+ return CMD_RET_FAILURE;
+ return CMD_RET_SUCCESS;
+}
+static int do_mmc_part(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ block_dev_desc_t *mmc_dev;
+ struct mmc *mmc;
+
+ mmc = init_mmc_device(curr_device);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ mmc_dev = mmc_get_dev(curr_device);
+ if (mmc_dev != NULL && mmc_dev->type != DEV_TYPE_UNKNOWN) {
+ print_part(mmc_dev);
+ return CMD_RET_SUCCESS;
+ }
+
+ puts("get mmc type error!\n");
+ return CMD_RET_FAILURE;
+}
+static int do_mmc_dev(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ int dev, part = -1;
+ struct mmc *mmc;
+
+ if (argc == 1) {
+ dev = curr_device;
+ } else if (argc == 2) {
+ dev = simple_strtoul(argv[1], NULL, 10);
+ } else if (argc == 3) {
+ dev = (int)simple_strtoul(argv[1], NULL, 10);
+ part = (int)simple_strtoul(argv[2], NULL, 10);
+ if (part > PART_ACCESS_MASK) {
+ printf("#part_num shouldn't be larger than %d\n",
+ PART_ACCESS_MASK);
+ return CMD_RET_FAILURE;
}
+ } else {
+ return CMD_RET_USAGE;
+ }
- mmc = find_mmc_device(dev);
- if (!mmc) {
- printf("no mmc device at slot %x\n", dev);
- return 1;
+ mmc = init_mmc_device(dev);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ if (part != -1) {
+ int ret;
+ if (mmc->part_config == MMCPART_NOAVAILABLE) {
+ printf("Card doesn't support part_switch\n");
+ return CMD_RET_FAILURE;
}
- if (IS_SD(mmc)) {
- puts("RST_n_FUNCTION only exists on eMMC\n");
- return 1;
+ if (part != mmc->part_num) {
+ ret = mmc_switch_part(dev, part);
+ if (!ret)
+ mmc->part_num = part;
+
+ printf("switch to partitions #%d, %s\n",
+ part, (!ret) ? "OK" : "ERROR");
}
+ }
+ curr_device = dev;
+ if (mmc->part_config == MMCPART_NOAVAILABLE)
+ printf("mmc%d is current device\n", curr_device);
+ else
+ printf("mmc%d(part %d) is current device\n",
+ curr_device, mmc->part_num);
- return mmc_set_rst_n_function(mmc, enable);
-#endif /* CONFIG_SUPPORT_EMMC_BOOT */
+ return CMD_RET_SUCCESS;
+}
+static int do_mmc_list(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ print_mmc_devices('\n');
+ return CMD_RET_SUCCESS;
+}
+#ifdef CONFIG_SUPPORT_EMMC_BOOT
+static int do_mmc_bootbus(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ int dev;
+ struct mmc *mmc;
+ u8 width, reset, mode;
+
+ if (argc != 5)
+ return CMD_RET_USAGE;
+ dev = simple_strtoul(argv[1], NULL, 10);
+ width = simple_strtoul(argv[2], NULL, 10);
+ reset = simple_strtoul(argv[3], NULL, 10);
+ mode = simple_strtoul(argv[4], NULL, 10);
+
+ mmc = init_mmc_device(dev);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ if (IS_SD(mmc)) {
+ puts("BOOT_BUS_WIDTH only exists on eMMC\n");
+ return CMD_RET_FAILURE;
}
- else if (argc == 3 && strcmp(argv[1], "setdsr") == 0) {
- struct mmc *mmc = find_mmc_device(curr_device);
- u32 val = simple_strtoul(argv[2], NULL, 16);
- int ret;
+ /* acknowledge to be sent during boot operation */
+ return mmc_set_boot_bus_width(mmc, width, reset, mode);
+}
+static int do_mmc_boot_resize(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ int dev;
+ struct mmc *mmc;
+ u32 bootsize, rpmbsize;
- if (!mmc) {
- printf("no mmc device at slot %x\n", curr_device);
- return 1;
- }
- ret = mmc_set_dsr(mmc, val);
- printf("set dsr %s\n", (!ret) ? "OK, force rescan" : "ERROR");
- if (!ret) {
- mmc->has_init = 0;
- if (mmc_init(mmc))
- return 1;
- else
- return 0;
- }
- return ret;
+ if (argc != 4)
+ return CMD_RET_USAGE;
+ dev = simple_strtoul(argv[1], NULL, 10);
+ bootsize = simple_strtoul(argv[2], NULL, 10);
+ rpmbsize = simple_strtoul(argv[3], NULL, 10);
+
+ mmc = init_mmc_device(dev);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ if (IS_SD(mmc)) {
+ printf("It is not a EMMC device\n");
+ return CMD_RET_FAILURE;
}
- state = MMC_INVALID;
- if (argc == 5 && strcmp(argv[1], "read") == 0)
- state = MMC_READ;
- else if (argc == 5 && strcmp(argv[1], "write") == 0)
- state = MMC_WRITE;
- else if (argc == 4 && strcmp(argv[1], "erase") == 0)
- state = MMC_ERASE;
-
- if (state != MMC_INVALID) {
- struct mmc *mmc = find_mmc_device(curr_device);
- int idx = 2;
- u32 blk, cnt, n;
- void *addr;
-
- if (state != MMC_ERASE) {
- addr = (void *)simple_strtoul(argv[idx], NULL, 16);
- ++idx;
- } else
- addr = NULL;
- blk = simple_strtoul(argv[idx], NULL, 16);
- cnt = simple_strtoul(argv[idx + 1], NULL, 16);
-
- if (!mmc) {
- printf("no mmc device at slot %x\n", curr_device);
- return 1;
- }
+ if (mmc_boot_partition_size_change(mmc, bootsize, rpmbsize)) {
+ printf("EMMC boot partition Size change Failed.\n");
+ return CMD_RET_FAILURE;
+ }
- printf("\nMMC %s: dev # %d, block # %d, count %d ... ",
- argv[1], curr_device, blk, cnt);
+ printf("EMMC boot partition Size %d MB\n", bootsize);
+ printf("EMMC RPMB partition Size %d MB\n", rpmbsize);
+ return CMD_RET_SUCCESS;
+}
+static int do_mmc_partconf(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ int dev;
+ struct mmc *mmc;
+ u8 ack, part_num, access;
- mmc_init(mmc);
+ if (argc != 5)
+ return CMD_RET_USAGE;
- if ((state == MMC_WRITE || state == MMC_ERASE)) {
- if (mmc_getwp(mmc) == 1) {
- printf("Error: card is write protected!\n");
- return 1;
- }
- }
+ dev = simple_strtoul(argv[1], NULL, 10);
+ ack = simple_strtoul(argv[2], NULL, 10);
+ part_num = simple_strtoul(argv[3], NULL, 10);
+ access = simple_strtoul(argv[4], NULL, 10);
- switch (state) {
- case MMC_READ:
- n = mmc->block_dev.block_read(curr_device, blk,
- cnt, addr);
- /* flush cache after read */
- flush_cache((ulong)addr, cnt * 512); /* FIXME */
- break;
- case MMC_WRITE:
- n = mmc->block_dev.block_write(curr_device, blk,
- cnt, addr);
- break;
- case MMC_ERASE:
- n = mmc->block_dev.block_erase(curr_device, blk, cnt);
- break;
- default:
- BUG();
- }
+ mmc = init_mmc_device(dev);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ if (IS_SD(mmc)) {
+ puts("PARTITION_CONFIG only exists on eMMC\n");
+ return CMD_RET_FAILURE;
+ }
+
+ /* acknowledge to be sent during boot operation */
+ return mmc_set_part_conf(mmc, ack, part_num, access);
+}
+static int do_mmc_rst_func(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ int dev;
+ struct mmc *mmc;
+ u8 enable;
+
+ /*
+ * Set the RST_n_ENABLE bit of RST_n_FUNCTION
+ * The only valid values are 0x0, 0x1 and 0x2 and writing
+ * a value of 0x1 or 0x2 sets the value permanently.
+ */
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ dev = simple_strtoul(argv[1], NULL, 10);
+ enable = simple_strtoul(argv[2], NULL, 10);
+
+ if (enable > 2 || enable < 0) {
+ puts("Invalid RST_n_ENABLE value\n");
+ return CMD_RET_USAGE;
+ }
+
+ mmc = init_mmc_device(dev);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ if (IS_SD(mmc)) {
+ puts("RST_n_FUNCTION only exists on eMMC\n");
+ return CMD_RET_FAILURE;
+ }
- printf("%d blocks %s: %s\n",
- n, argv[1], (n == cnt) ? "OK" : "ERROR");
- return (n == cnt) ? 0 : 1;
+ return mmc_set_rst_n_function(mmc, enable);
+}
+#endif
+static int do_mmc_setdsr(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ struct mmc *mmc;
+ u32 val;
+ int ret;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+ val = simple_strtoul(argv[2], NULL, 16);
+
+ mmc = find_mmc_device(curr_device);
+ if (!mmc) {
+ printf("no mmc device@slot %x\n", curr_device);
+ return CMD_RET_FAILURE;
+ }
+ ret = mmc_set_dsr(mmc, val);
+ printf("set dsr %s\n", (!ret) ? "OK, force rescan" : "ERROR");
+ if (!ret) {
+ mmc->has_init = 0;
+ if (mmc_init(mmc))
+ return CMD_RET_FAILURE;
+ else
+ return CMD_RET_SUCCESS;
}
+ return ret;
+}
+
+static cmd_tbl_t cmd_mmc[] = {
+ U_BOOT_CMD_MKENT(info, 1, 0, do_mmcinfo, "", ""),
+ U_BOOT_CMD_MKENT(read, 4, 1, do_mmc_read, "", ""),
+ U_BOOT_CMD_MKENT(write, 4, 0, do_mmc_write, "", ""),
+ U_BOOT_CMD_MKENT(erase, 3, 0, do_mmc_erase, "", ""),
+ U_BOOT_CMD_MKENT(rescan, 1, 1, do_mmc_rescan, "", ""),
+ U_BOOT_CMD_MKENT(part, 1, 1, do_mmc_part, "", ""),
+ U_BOOT_CMD_MKENT(dev, 3, 0, do_mmc_dev, "", ""),
+ U_BOOT_CMD_MKENT(list, 1, 1, do_mmc_list, "", ""),
+#ifdef CONFIG_SUPPORT_EMMC_BOOT
+ U_BOOT_CMD_MKENT(bootbus, 5, 0, do_mmc_bootbus, "", ""),
+ U_BOOT_CMD_MKENT(bootpart-resize, 3, 0, do_mmc_boot_resize, "", ""),
+ U_BOOT_CMD_MKENT(partconf, 5, 0, do_mmc_partconf, "", ""),
+ U_BOOT_CMD_MKENT(rst-function, 3, 0, do_mmc_rst_func, "", ""),
+#endif
+#ifdef CONFIG_SUPPORT_EMMC_RPMB
+ U_BOOT_CMD_MKENT(rpmb, CONFIG_SYS_MAXARGS, 1, do_mmcrpmb, "", ""),
+#endif
+ U_BOOT_CMD_MKENT(setdsr, 2, 0, do_mmc_setdsr, "", ""),
+};
+
+static int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ cmd_tbl_t *cp;
+
+ cp = find_cmd_tbl(argv[1], cmd_mmc, ARRAY_SIZE(cmd_mmc));
+
+ /* Drop the mmc command */
+ argc--;
+ argv++;
+
+ if (cp == NULL || argc > cp->maxargs)
+ return CMD_RET_USAGE;
+ if (flag == CMD_FLAG_REPEAT && !cp->repeatable)
+ return CMD_RET_SUCCESS;
- return CMD_RET_USAGE;
+ if (curr_device < 0) {
+ if (get_mmc_num() > 0) {
+ curr_device = 0;
+ } else {
+ puts("No MMC device available\n");
+ return CMD_RET_FAILURE;
+ }
+ }
+ return cp->cmd(cmdtp, flag, argc, argv);
}
U_BOOT_CMD(
- mmc, 6, 1, do_mmcops,
+ mmc, 7, 1, do_mmcops,
"MMC sub system",
- "read addr blk# cnt\n"
+ "info - display info of the current MMC device\n"
+ "mmc read addr blk# cnt\n"
"mmc write addr blk# cnt\n"
"mmc erase blk# cnt\n"
"mmc rescan\n"
@@ -474,6 +674,20 @@ U_BOOT_CMD(
" - Change the RST_n_FUNCTION field of the specified device\n"
" WARNING: This is a write-once field and 0 / 1 / 2 are the only valid values.\n"
#endif
- "mmc setdsr - set DSR register value\n"
+#ifdef CONFIG_SUPPORT_EMMC_RPMB
+ "mmc rpmb read addr blk# cnt [address of auth-key] - block size is 256 bytes\n"
+ "mmc rpmb write addr blk# cnt <address of auth-key> - block size is 256 bytes\n"
+ "mmc rpmb key <address of auth-key> - program the RPMB authentication key.\n"
+ "mmc rpmb counter - read the value of the write counter\n"
+#endif
+ "mmc setdsr <value> - set DSR register value\n"
);
+
+/* Old command kept for compatibility. Same as 'mmc info' */
+U_BOOT_CMD(
+ mmcinfo, 1, 0, do_mmcinfo,
+ "display MMC info",
+ "- display info of the current MMC device"
+);
+
#endif /* !CONFIG_GENERIC_MMC */
--
1.7.6.5
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V4 1/3] eMMC: add support for operations in RPMB partition
2014-04-24 6:40 ` [U-Boot] [PATCH V4 1/3] eMMC: add support for operations in RPMB partition Pierre Aubert
@ 2014-04-24 6:55 ` Wolfgang Denk
2014-04-24 7:16 ` Pierre AUBERT
2014-04-24 6:59 ` Wolfgang Denk
1 sibling, 1 reply; 31+ messages in thread
From: Wolfgang Denk @ 2014-04-24 6:55 UTC (permalink / raw)
To: u-boot
Dear Pierre Aubert,
In message <1398321641-7113-2-git-send-email-p.aubert@staubli.com> you wrote:
> This patch adds functions for read, write and authentication
> key programming for the Replay Protected Memory Block partition
> in the eMMC.
>
> Changes in V2:
> - use ALLOC_CACHE_ALIGN_BUFFER in rpmb.c instead of a static buffer for the
> RPMB frames.
The changelog goes into the comment section (i. e. below the "---"
line), not into the commit message. Also, this being V4 of the patch,
I wonder what was changed in V3 and V4?
> Signed-off-by: Pierre Aubert <p.aubert@staubli.com>
> CC: Pantelis Antoniou <panto@antoniou-consulting.com>
> ---
Add changelog here!
> drivers/mmc/Makefile | 1 +
> drivers/mmc/rpmb.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++++++
> include/mmc.h | 10 ++-
> lib/Makefile | 3 +
> 4 files changed, 336 insertions(+), 1 deletions(-)
> create mode 100644 drivers/mmc/rpmb.c
Best regards,
Wolfgang Denk
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de
It usually takes more than three weeks to prepare a good impromptu
speech. - Mark Twain
^ permalink raw reply [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V4 1/3] eMMC: add support for operations in RPMB partition
2014-04-24 6:40 ` [U-Boot] [PATCH V4 1/3] eMMC: add support for operations in RPMB partition Pierre Aubert
2014-04-24 6:55 ` Wolfgang Denk
@ 2014-04-24 6:59 ` Wolfgang Denk
2014-04-24 7:56 ` Pierre AUBERT
1 sibling, 1 reply; 31+ messages in thread
From: Wolfgang Denk @ 2014-04-24 6:59 UTC (permalink / raw)
To: u-boot
Dear Pierre.
In message <1398321641-7113-2-git-send-email-p.aubert@staubli.com> you wrote:
>
...
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -35,6 +35,9 @@ obj-y += net_utils.o
> obj-$(CONFIG_PHYSMEM) += physmem.o
> obj-y += qsort.o
> obj-$(CONFIG_SHA1) += sha1.o
> +ifdef CONFIG_SUPPORT_EMMC_RPMB
> +CONFIG_SHA256 := y
> +endif
This is a pretty ugly way to tweak the configuration. Why do we need
this?
I gess all you really want is
obj-$(CONFIG_SUPPORT_EMMC_RPMB) += sha256.o
?
Best regards,
Wolfgang Denk
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de
PLEASE NOTE: Some Quantum Physics Theories Suggest That When the Con-
sumer Is Not Directly Observing This Product, It May Cease to Exist
or Will Exist Only in a Vague and Undetermined State.
^ permalink raw reply [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V4 1/3] eMMC: add support for operations in RPMB partition
2014-04-24 6:55 ` Wolfgang Denk
@ 2014-04-24 7:16 ` Pierre AUBERT
2014-04-24 7:33 ` Wolfgang Denk
0 siblings, 1 reply; 31+ messages in thread
From: Pierre AUBERT @ 2014-04-24 7:16 UTC (permalink / raw)
To: u-boot
Hello Wolfgang,
Le 24/04/2014 08:55, Wolfgang Denk a ?crit :
> Dear Pierre Aubert,
>
> In message <1398321641-7113-2-git-send-email-p.aubert@staubli.com> you wrote:
>> This patch adds functions for read, write and authentication
>> key programming for the Replay Protected Memory Block partition
>> in the eMMC.
>>
>> Changes in V2:
>> - use ALLOC_CACHE_ALIGN_BUFFER in rpmb.c instead of a static buffer for the
>> RPMB frames.
> The changelog goes into the comment section (i. e. below the "---"
I will fix it in V5. I made the same mistake for the patch 3/3.
> line), not into the commit message. Also, this being V4 of the patch,
> I wonder what was changed in V3 and V4?
There's no change for this patch in V3 and V4. I have resubmitted all
the patchset.
>
>> Signed-off-by: Pierre Aubert <p.aubert@staubli.com>
>> CC: Pantelis Antoniou <panto@antoniou-consulting.com>
>> ---
> Add changelog here!
Ok
>
>> drivers/mmc/Makefile | 1 +
>> drivers/mmc/rpmb.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++++++
>> include/mmc.h | 10 ++-
>> lib/Makefile | 3 +
>> 4 files changed, 336 insertions(+), 1 deletions(-)
>> create mode 100644 drivers/mmc/rpmb.c
>
> Best regards,
>
> Wolfgang Denk
>
Best regards
Pierre Aubert
^ permalink raw reply [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V4 1/3] eMMC: add support for operations in RPMB partition
2014-04-24 7:16 ` Pierre AUBERT
@ 2014-04-24 7:33 ` Wolfgang Denk
2014-04-24 7:41 ` Pierre AUBERT
0 siblings, 1 reply; 31+ messages in thread
From: Wolfgang Denk @ 2014-04-24 7:33 UTC (permalink / raw)
To: u-boot
Dear Pierre,
In message <5358BA6A.3030604@staubli.com> you wrote:
>
> > The changelog goes into the comment section (i. e. below the "---"
> I will fix it in V5. I made the same mistake for the patch 3/3.
Thanks.
Actually I cannot see a V4 of patch 3/3 ?
> > line), not into the commit message. Also, this being V4 of the patch,
> > I wonder what was changed in V3 and V4?
> There's no change for this patch in V3 and V4. I have resubmitted all
> the patchset.
Then the changelog should say so (like: "V3, V4: no changes").
Best regards,
Wolfgang Denk
--
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
Phone: (+49)-8142-66989-10 Fax: (+49)-8142-66989-80 Email: wd at denx.de
"A little knowledge is a dangerous thing." - Doug Gwyn
^ permalink raw reply [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V4 1/3] eMMC: add support for operations in RPMB partition
2014-04-24 7:33 ` Wolfgang Denk
@ 2014-04-24 7:41 ` Pierre AUBERT
0 siblings, 0 replies; 31+ messages in thread
From: Pierre AUBERT @ 2014-04-24 7:41 UTC (permalink / raw)
To: u-boot
Hello Wolfgang,
Le 24/04/2014 09:33, Wolfgang Denk a ?crit :
> Dear Pierre,
>
> In message <5358BA6A.3030604@staubli.com> you wrote:
>>> The changelog goes into the comment section (i. e. below the "---"
>> I will fix it in V5. I made the same mistake for the patch 3/3.
> Thanks.
>
> Actually I cannot see a V4 of patch 3/3 ?
It's strange. On my side, I can see it in the mailing list and in patchwork.
>
>>> line), not into the commit message. Also, this being V4 of the patch,
>>> I wonder what was changed in V3 and V4?
>> There's no change for this patch in V3 and V4. I have resubmitted all
>> the patchset.
> Then the changelog should say so (like: "V3, V4: no changes").
Ok, I will fix it.
>
> Best regards,
>
> Wolfgang Denk
>
Best regards
Pierre
^ permalink raw reply [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V4 1/3] eMMC: add support for operations in RPMB partition
2014-04-24 6:59 ` Wolfgang Denk
@ 2014-04-24 7:56 ` Pierre AUBERT
0 siblings, 0 replies; 31+ messages in thread
From: Pierre AUBERT @ 2014-04-24 7:56 UTC (permalink / raw)
To: u-boot
Hello Wolfgang,
Le 24/04/2014 08:59, Wolfgang Denk a ?crit :
> Dear Pierre.
>
> In message <1398321641-7113-2-git-send-email-p.aubert@staubli.com> you wrote:
> ...
>> --- a/lib/Makefile
>> +++ b/lib/Makefile
>> @@ -35,6 +35,9 @@ obj-y += net_utils.o
>> obj-$(CONFIG_PHYSMEM) += physmem.o
>> obj-y += qsort.o
>> obj-$(CONFIG_SHA1) += sha1.o
>> +ifdef CONFIG_SUPPORT_EMMC_RPMB
>> +CONFIG_SHA256 := y
>> +endif
> This is a pretty ugly way to tweak the configuration. Why do we need
> this?
>
> I gess all you really want is
>
> obj-$(CONFIG_SUPPORT_EMMC_RPMB) += sha256.o
>
> ?
Yes. I will fix it.
> Best regards,
>
> Wolfgang Denk
>
Best regards
Pierre Aubert
^ permalink raw reply [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V5 0/3] eMMC: support for Read Protected Memory Block (RPMB)
2014-04-11 12:12 [U-Boot] [PATCH 0/2] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
` (4 preceding siblings ...)
2014-04-24 6:40 ` [U-Boot] [PATCH V4 0/3] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
@ 2014-04-24 8:30 ` Pierre Aubert
2014-04-24 8:30 ` [U-Boot] [PATCH V5 1/3] eMMC: add support for operations in RPMB partition Pierre Aubert
` (2 more replies)
5 siblings, 3 replies; 31+ messages in thread
From: Pierre Aubert @ 2014-04-24 8:30 UTC (permalink / raw)
To: u-boot
This serie of patches adds some functions and a sub-command of 'mmc' for programming the authentication key and for reading and writing the RPMB partition of an eMMC according to the JEDEC standard No. 64-A441
The sub-command rpmb is enabled by the flag CONFIG_SUPPORT_EMMC_RPMB defined
in the board configuration file.
It has been tested on a SabreSDP iMX6 board.
Changes in V5:
- move changelog to the right place for each patches
- add empty changelog for patches with no change
- change lib/Makefile for adding sha256 when CONFIG_SUPPORT_EMMC_RPMB is
defined
Changes in V4:
- use subcommand tables for 'mmc' command and 'mmc rpmb' subcommand.
- add 'mmc info' subcommand, which is the same as mmcinfo.
mmcinfo is kept for compatibility.
- add changelog in commit comments.
Changes in V3:
- add entries in README for configuration options related to eMMC.
- new patch for adding the 'confirm_yesno' function as suggested by W.Denk
- improved test for existence of RPMB partition.
- fix of coding issues.
Changes in V2:
- use ALLOC_CACHE_ALIGN_BUFFER in rpmb.c instead of a static buffer for the
RPMB frames.
Pierre Aubert (3):
eMMC: add support for operations in RPMB partition
Add the function 'confirm_yesno' for interactive
eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command
README | 10 +
common/cmd_fuse.c | 11 +-
common/cmd_mmc.c | 806 +++++++++++++++++++++++++++++++------------------
common/cmd_nand.c | 16 +-
common/cmd_otp.c | 18 +-
common/console.c | 28 ++-
drivers/mmc/Makefile | 1 +
drivers/mmc/rpmb.c | 323 ++++++++++++++++++++
include/common.h | 2 +-
include/mmc.h | 10 +-
lib/Makefile | 1 +
11 files changed, 892 insertions(+), 334 deletions(-)
create mode 100644 drivers/mmc/rpmb.c
--
1.7.6.5
^ permalink raw reply [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V5 1/3] eMMC: add support for operations in RPMB partition
2014-04-24 8:30 ` [U-Boot] [PATCH V5 0/3] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
@ 2014-04-24 8:30 ` Pierre Aubert
2014-05-23 8:50 ` Pantelis Antoniou
2014-04-24 8:30 ` [U-Boot] [PATCH V5 2/3] Add the function 'confirm_yesno' for interactive Pierre Aubert
2014-04-24 8:30 ` [U-Boot] [PATCH V5 3/3] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command Pierre Aubert
2 siblings, 1 reply; 31+ messages in thread
From: Pierre Aubert @ 2014-04-24 8:30 UTC (permalink / raw)
To: u-boot
This patch adds functions for read, write and authentication
key programming for the Replay Protected Memory Block partition
in the eMMC.
Signed-off-by: Pierre Aubert <p.aubert@staubli.com>
CC: Pantelis Antoniou <panto@antoniou-consulting.com>
---
Changes in V5:
- move changelog to the right place
- change lib/Makefile for adding sha256 when CONFIG_SUPPORT_EMMC_RPMB is
defined
V3, V4: no changes
Changes in V2:
- use ALLOC_CACHE_ALIGN_BUFFER in rpmb.c instead of a static buffer for the
RPMB frames.
drivers/mmc/Makefile | 1 +
drivers/mmc/rpmb.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++++++
include/mmc.h | 10 ++-
lib/Makefile | 1 +
4 files changed, 334 insertions(+), 1 deletions(-)
create mode 100644 drivers/mmc/rpmb.c
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index 931922b..4c6ab9e 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -30,6 +30,7 @@ obj-$(CONFIG_DWMMC) += dw_mmc.o
obj-$(CONFIG_EXYNOS_DWMMC) += exynos_dw_mmc.o
obj-$(CONFIG_ZYNQ_SDHCI) += zynq_sdhci.o
obj-$(CONFIG_SOCFPGA_DWMMC) += socfpga_dw_mmc.o
+obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o
ifdef CONFIG_SPL_BUILD
obj-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o
else
diff --git a/drivers/mmc/rpmb.c b/drivers/mmc/rpmb.c
new file mode 100644
index 0000000..05936f5
--- /dev/null
+++ b/drivers/mmc/rpmb.c
@@ -0,0 +1,323 @@
+/*
+ * Copyright 2014, Staubli Faverges
+ * Pierre Aubert
+ *
+ * eMMC- Replay Protected Memory Block
+ * According to JEDEC Standard No. 84-A441
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <config.h>
+#include <common.h>
+#include <mmc.h>
+#include <sha256.h>
+#include "mmc_private.h"
+
+/* Request codes */
+#define RPMB_REQ_KEY 1
+#define RPMB_REQ_WCOUNTER 2
+#define RPMB_REQ_WRITE_DATA 3
+#define RPMB_REQ_READ_DATA 4
+#define RPMB_REQ_STATUS 5
+
+/* Response code */
+#define RPMB_RESP_KEY 0x0100
+#define RPMB_RESP_WCOUNTER 0x0200
+#define RPMB_RESP_WRITE_DATA 0x0300
+#define RPMB_RESP_READ_DATA 0x0400
+
+/* Error codes */
+#define RPMB_OK 0
+#define RPMB_ERR_GENERAL 1
+#define RPMB_ERR_AUTH 2
+#define RPMB_ERR_COUNTER 3
+#define RPMB_ERR_ADDRESS 4
+#define RPMB_ERR_WRITE 5
+#define RPMB_ERR_READ 6
+#define RPMB_ERR_KEY 7
+#define RPMB_ERR_CNT_EXPIRED 0x80
+#define RPMB_ERR_MSK 0x7
+
+/* Sizes of RPMB data frame */
+#define RPMB_SZ_STUFF 196
+#define RPMB_SZ_MAC 32
+#define RPMB_SZ_DATA 256
+#define RPMB_SZ_NONCE 16
+
+#define SHA256_BLOCK_SIZE 64
+
+/* Error messages */
+static const char * const rpmb_err_msg[] = {
+ "",
+ "General failure",
+ "Authentication failure",
+ "Counter failure",
+ "Address failure",
+ "Write failure",
+ "Read failure",
+ "Authentication key not yet programmed",
+};
+
+
+/* Structure of RPMB data frame. */
+struct s_rpmb {
+ unsigned char stuff[RPMB_SZ_STUFF];
+ unsigned char mac[RPMB_SZ_MAC];
+ unsigned char data[RPMB_SZ_DATA];
+ unsigned char nonce[RPMB_SZ_NONCE];
+ unsigned long write_counter;
+ unsigned short address;
+ unsigned short block_count;
+ unsigned short result;
+ unsigned short request;
+};
+
+static int mmc_set_blockcount(struct mmc *mmc, unsigned int blockcount,
+ bool is_rel_write)
+{
+ struct mmc_cmd cmd = {0};
+
+ cmd.cmdidx = MMC_CMD_SET_BLOCK_COUNT;
+ cmd.cmdarg = blockcount & 0x0000FFFF;
+ if (is_rel_write)
+ cmd.cmdarg |= 1 << 31;
+ cmd.resp_type = MMC_RSP_R1;
+
+ return mmc_send_cmd(mmc, &cmd, NULL);
+}
+static int mmc_rpmb_request(struct mmc *mmc, const struct s_rpmb *s,
+ unsigned int count, bool is_rel_write)
+{
+ struct mmc_cmd cmd = {0};
+ struct mmc_data data;
+ int ret;
+
+ ret = mmc_set_blockcount(mmc, count, is_rel_write);
+ if (ret) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:mmc_set_blockcount-> %d\n", __func__, ret);
+#endif
+ return 1;
+ }
+
+ cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
+ cmd.cmdarg = 0;
+ cmd.resp_type = MMC_RSP_R1b;
+
+ data.src = (const char *)s;
+ data.blocks = 1;
+ data.blocksize = MMC_MAX_BLOCK_LEN;
+ data.flags = MMC_DATA_WRITE;
+
+ ret = mmc_send_cmd(mmc, &cmd, &data);
+ if (ret) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:mmc_send_cmd-> %d\n", __func__, ret);
+#endif
+ return 1;
+ }
+ return 0;
+}
+static int mmc_rpmb_response(struct mmc *mmc, struct s_rpmb *s,
+ unsigned short expected)
+{
+ struct mmc_cmd cmd = {0};
+ struct mmc_data data;
+ int ret;
+
+ ret = mmc_set_blockcount(mmc, 1, false);
+ if (ret) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:mmc_set_blockcount-> %d\n", __func__, ret);
+#endif
+ return -1;
+ }
+ cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
+ cmd.cmdarg = 0;
+ cmd.resp_type = MMC_RSP_R1;
+
+ data.dest = (char *)s;
+ data.blocks = 1;
+ data.blocksize = MMC_MAX_BLOCK_LEN;
+ data.flags = MMC_DATA_READ;
+
+ ret = mmc_send_cmd(mmc, &cmd, &data);
+ if (ret) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:mmc_send_cmd-> %d\n", __func__, ret);
+#endif
+ return -1;
+ }
+ /* Check the response and the status */
+ if (be16_to_cpu(s->request) != expected) {
+#ifdef CONFIG_MMC_RPMB_TRACE
+ printf("%s:response= %x\n", __func__,
+ be16_to_cpu(s->request));
+#endif
+ return -1;
+ }
+ ret = be16_to_cpu(s->result);
+ if (ret) {
+ printf("%s %s\n", rpmb_err_msg[ret & RPMB_ERR_MSK],
+ (ret & RPMB_ERR_CNT_EXPIRED) ?
+ "Write counter has expired" : "");
+ }
+
+ /* Return the status of the command */
+ return ret;
+}
+static int mmc_rpmb_status(struct mmc *mmc, unsigned short expected)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
+
+ memset(rpmb_frame, 0, sizeof(struct s_rpmb));
+ rpmb_frame->request = cpu_to_be16(RPMB_REQ_STATUS);
+ if (mmc_rpmb_request(mmc, rpmb_frame, 1, false))
+ return -1;
+
+ /* Read the result */
+ return mmc_rpmb_response(mmc, rpmb_frame, expected);
+}
+static void rpmb_hmac(unsigned char *key, unsigned char *buff, int len,
+ unsigned char *output)
+{
+ sha256_context ctx;
+ int i;
+ unsigned char k_ipad[SHA256_BLOCK_SIZE];
+ unsigned char k_opad[SHA256_BLOCK_SIZE];
+
+ sha256_starts(&ctx);
+
+ /* According to RFC 4634, the HMAC transform looks like:
+ SHA(K XOR opad, SHA(K XOR ipad, text))
+
+ where K is an n byte key.
+ ipad is the byte 0x36 repeated blocksize times
+ opad is the byte 0x5c repeated blocksize times
+ and text is the data being protected.
+ */
+
+ for (i = 0; i < RPMB_SZ_MAC; i++) {
+ k_ipad[i] = key[i] ^ 0x36;
+ k_opad[i] = key[i] ^ 0x5c;
+ }
+ /* remaining pad bytes are '\0' XOR'd with ipad and opad values */
+ for ( ; i < SHA256_BLOCK_SIZE; i++) {
+ k_ipad[i] = 0x36;
+ k_opad[i] = 0x5c;
+ }
+ sha256_update(&ctx, k_ipad, SHA256_BLOCK_SIZE);
+ sha256_update(&ctx, buff, len);
+ sha256_finish(&ctx, output);
+
+ /* Init context for second pass */
+ sha256_starts(&ctx);
+
+ /* start with outer pad */
+ sha256_update(&ctx, k_opad, SHA256_BLOCK_SIZE);
+
+ /* then results of 1st hash */
+ sha256_update(&ctx, output, RPMB_SZ_MAC);
+
+ /* finish up 2nd pass */
+ sha256_finish(&ctx, output);
+}
+int mmc_rpmb_get_counter(struct mmc *mmc, unsigned long *pcounter)
+{
+ int ret;
+ ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
+
+ /* Fill the request */
+ memset(rpmb_frame, 0, sizeof(struct s_rpmb));
+ rpmb_frame->request = cpu_to_be16(RPMB_REQ_WCOUNTER);
+ if (mmc_rpmb_request(mmc, rpmb_frame, 1, false))
+ return -1;
+
+ /* Read the result */
+ ret = mmc_rpmb_response(mmc, rpmb_frame, RPMB_RESP_WCOUNTER);
+ if (ret)
+ return ret;
+
+ *pcounter = be32_to_cpu(rpmb_frame->write_counter);
+ return 0;
+}
+int mmc_rpmb_set_key(struct mmc *mmc, void *key)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
+ /* Fill the request */
+ memset(rpmb_frame, 0, sizeof(struct s_rpmb));
+ rpmb_frame->request = cpu_to_be16(RPMB_REQ_KEY);
+ memcpy(rpmb_frame->mac, key, RPMB_SZ_MAC);
+
+ if (mmc_rpmb_request(mmc, rpmb_frame, 1, true))
+ return -1;
+
+ /* read the operation status */
+ return mmc_rpmb_status(mmc, RPMB_RESP_KEY);
+}
+int mmc_rpmb_read(struct mmc *mmc, void *addr, unsigned short blk,
+ unsigned short cnt, unsigned char *key)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
+ int i;
+
+ for (i = 0; i < cnt; i++) {
+ /* Fill the request */
+ memset(rpmb_frame, 0, sizeof(struct s_rpmb));
+ rpmb_frame->address = cpu_to_be16(blk + i);
+ rpmb_frame->request = cpu_to_be16(RPMB_REQ_READ_DATA);
+ if (mmc_rpmb_request(mmc, rpmb_frame, 1, false))
+ break;
+
+ /* Read the result */
+ if (mmc_rpmb_response(mmc, rpmb_frame, RPMB_RESP_READ_DATA))
+ break;
+
+ /* Check the HMAC if key is provided */
+ if (key) {
+ unsigned char ret_hmac[RPMB_SZ_MAC];
+
+ rpmb_hmac(key, rpmb_frame->data, 284, ret_hmac);
+ if (memcmp(ret_hmac, rpmb_frame->mac, RPMB_SZ_MAC)) {
+ printf("MAC error on block #%d\n", i);
+ break;
+ }
+ }
+ /* Copy data */
+ memcpy(addr + i * RPMB_SZ_DATA, rpmb_frame->data, RPMB_SZ_DATA);
+ }
+ return i;
+}
+int mmc_rpmb_write(struct mmc *mmc, void *addr, unsigned short blk,
+ unsigned short cnt, unsigned char *key)
+{
+ ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
+ unsigned long wcount;
+ int i;
+
+ for (i = 0; i < cnt; i++) {
+ if (mmc_rpmb_get_counter(mmc, &wcount)) {
+ printf("Cannot read RPMB write counter\n");
+ break;
+ }
+
+ /* Fill the request */
+ memset(rpmb_frame, 0, sizeof(struct s_rpmb));
+ memcpy(rpmb_frame->data, addr + i * RPMB_SZ_DATA, RPMB_SZ_DATA);
+ rpmb_frame->address = cpu_to_be16(blk + i);
+ rpmb_frame->block_count = cpu_to_be16(1);
+ rpmb_frame->write_counter = cpu_to_be32(wcount);
+ rpmb_frame->request = cpu_to_be16(RPMB_REQ_WRITE_DATA);
+ /* Computes HMAC */
+ rpmb_hmac(key, rpmb_frame->data, 284, rpmb_frame->mac);
+
+ if (mmc_rpmb_request(mmc, rpmb_frame, 1, true))
+ break;
+
+ /* Get status */
+ if (mmc_rpmb_status(mmc, RPMB_RESP_WRITE_DATA))
+ break;
+ }
+ return i;
+}
diff --git a/include/mmc.h b/include/mmc.h
index 42d0125..14d296c 100644
--- a/include/mmc.h
+++ b/include/mmc.h
@@ -69,6 +69,7 @@
#define MMC_CMD_SET_BLOCKLEN 16
#define MMC_CMD_READ_SINGLE_BLOCK 17
#define MMC_CMD_READ_MULTIPLE_BLOCK 18
+#define MMC_CMD_SET_BLOCK_COUNT 23
#define MMC_CMD_WRITE_SINGLE_BLOCK 24
#define MMC_CMD_WRITE_MULTIPLE_BLOCK 25
#define MMC_CMD_ERASE_GROUP_START 35
@@ -224,6 +225,7 @@
* boot partitions (2), general purpose partitions (4) in MMC v4.4.
*/
#define MMC_NUM_BOOT_PARTITION 2
+#define MMC_PART_RPMB 3 /* RPMB partition number */
struct mmc_cid {
unsigned long psn;
@@ -335,7 +337,13 @@ int mmc_set_part_conf(struct mmc *mmc, u8 ack, u8 part_num, u8 access);
int mmc_set_boot_bus_width(struct mmc *mmc, u8 width, u8 reset, u8 mode);
/* Function to modify the RST_n_FUNCTION field of EXT_CSD */
int mmc_set_rst_n_function(struct mmc *mmc, u8 enable);
-
+/* Functions to read / write the RPMB partition */
+int mmc_rpmb_set_key(struct mmc *mmc, void *key);
+int mmc_rpmb_get_counter(struct mmc *mmc, unsigned long *counter);
+int mmc_rpmb_read(struct mmc *mmc, void *addr, unsigned short blk,
+ unsigned short cnt, unsigned char *key);
+int mmc_rpmb_write(struct mmc *mmc, void *addr, unsigned short blk,
+ unsigned short cnt, unsigned char *key);
/**
* Start device initialization and return immediately; it does not block on
* polling OCR (operation condition register) status. Then you should call
diff --git a/lib/Makefile b/lib/Makefile
index 27e4f78..377ab13 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -35,6 +35,7 @@ obj-y += net_utils.o
obj-$(CONFIG_PHYSMEM) += physmem.o
obj-y += qsort.o
obj-$(CONFIG_SHA1) += sha1.o
+obj-$(CONFIG_SUPPORT_EMMC_RPMB) += sha256.o
obj-$(CONFIG_SHA256) += sha256.o
obj-y += strmhz.o
obj-$(CONFIG_TPM) += tpm.o
--
1.7.6.5
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V5 2/3] Add the function 'confirm_yesno' for interactive
2014-04-24 8:30 ` [U-Boot] [PATCH V5 0/3] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
2014-04-24 8:30 ` [U-Boot] [PATCH V5 1/3] eMMC: add support for operations in RPMB partition Pierre Aubert
@ 2014-04-24 8:30 ` Pierre Aubert
2014-05-23 8:52 ` Pantelis Antoniou
2014-04-24 8:30 ` [U-Boot] [PATCH V5 3/3] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command Pierre Aubert
2 siblings, 1 reply; 31+ messages in thread
From: Pierre Aubert @ 2014-04-24 8:30 UTC (permalink / raw)
To: u-boot
User's confirmation is asked in different commands. This commit adds a
function for such confirmation.
Signed-off-by: Pierre Aubert <p.aubert@staubli.com>
---
V3, V4, V5: no changes
Patch added in V2
common/cmd_fuse.c | 11 ++---------
common/cmd_nand.c | 16 +++++-----------
common/cmd_otp.c | 18 +++---------------
common/console.c | 28 +++++++++++++++++++++++++++-
include/common.h | 2 +-
5 files changed, 38 insertions(+), 37 deletions(-)
diff --git a/common/cmd_fuse.c b/common/cmd_fuse.c
index 0df57db..abab978 100644
--- a/common/cmd_fuse.c
+++ b/common/cmd_fuse.c
@@ -33,15 +33,8 @@ static int confirm_prog(void)
"what you are doing!\n"
"\nReally perform this fuse programming? <y/N>\n");
- if (getc() == 'y') {
- int c;
-
- putc('y');
- c = getc();
- putc('\n');
- if (c == '\r')
- return 1;
- }
+ if (confirm_yesno())
+ return 1;
puts("Fuse programming aborted\n");
return 0;
diff --git a/common/cmd_nand.c b/common/cmd_nand.c
index 04ab0f1..a84f7dc 100644
--- a/common/cmd_nand.c
+++ b/common/cmd_nand.c
@@ -605,22 +605,16 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
opts.spread = spread;
if (scrub) {
- if (!scrub_yes)
- puts(scrub_warn);
-
- if (scrub_yes)
+ if (scrub_yes) {
opts.scrub = 1;
- else if (getc() == 'y') {
- puts("y");
- if (getc() == '\r')
+ } else {
+ puts(scrub_warn);
+ if (confirm_yesno()) {
opts.scrub = 1;
- else {
+ } else {
puts("scrub aborted\n");
return 1;
}
- } else {
- puts("scrub aborted\n");
- return 1;
}
}
ret = nand_erase_opts(nand, &opts);
diff --git a/common/cmd_otp.c b/common/cmd_otp.c
index 67808aa..593bb8c 100644
--- a/common/cmd_otp.c
+++ b/common/cmd_otp.c
@@ -158,21 +158,9 @@ int do_otp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
lowup(half + count - 1), page + (half + count - 1) / 2,
half + count
);
-
- i = 0;
- while (1) {
- if (tstc()) {
- const char exp_ans[] = "YES\r";
- char c;
- putc(c = getc());
- if (exp_ans[i++] != c) {
- printf(" Aborting\n");
- return 1;
- } else if (!exp_ans[i]) {
- puts("\n");
- break;
- }
- }
+ if (!confirm_yesno()) {
+ printf(" Aborting\n");
+ return 1;
}
}
diff --git a/common/console.c b/common/console.c
index 2dfb788..5453726 100644
--- a/common/console.c
+++ b/common/console.c
@@ -537,7 +537,33 @@ int ctrlc(void)
}
return 0;
}
-
+/* Reads user's confirmation.
+ Returns 1 if user's input is "y", "Y", "yes" or "YES"
+*/
+int confirm_yesno(void)
+{
+ int i;
+ char str_input[5];
+
+ /* Flush input */
+ while (tstc())
+ getc();
+ i = 0;
+ while (i < sizeof(str_input)) {
+ str_input[i] = getc();
+ putc(str_input[i]);
+ if (str_input[i] == '\r')
+ break;
+ i++;
+ }
+ putc('\n');
+ if (strncmp(str_input, "y\r", 2) == 0 ||
+ strncmp(str_input, "Y\r", 2) == 0 ||
+ strncmp(str_input, "yes\r", 4) == 0 ||
+ strncmp(str_input, "YES\r", 4) == 0)
+ return 1;
+ return 0;
+}
/* pass 1 to disable ctrlc() checking, 0 to enable.
* returns previous state
*/
diff --git a/include/common.h b/include/common.h
index baf361b..1d922b9 100644
--- a/include/common.h
+++ b/include/common.h
@@ -838,7 +838,7 @@ int ctrlc (void);
int had_ctrlc (void); /* have we had a Control-C since last clear? */
void clear_ctrlc (void); /* clear the Control-C condition */
int disable_ctrlc (int); /* 1 to disable, 0 to enable Control-C detect */
-
+int confirm_yesno(void); /* 1 if input is "y", "Y", "yes" or "YES" */
/*
* STDIO based functions (can always be used)
*/
--
1.7.6.5
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V5 3/3] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command
2014-04-24 8:30 ` [U-Boot] [PATCH V5 0/3] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
2014-04-24 8:30 ` [U-Boot] [PATCH V5 1/3] eMMC: add support for operations in RPMB partition Pierre Aubert
2014-04-24 8:30 ` [U-Boot] [PATCH V5 2/3] Add the function 'confirm_yesno' for interactive Pierre Aubert
@ 2014-04-24 8:30 ` Pierre Aubert
2 siblings, 0 replies; 31+ messages in thread
From: Pierre Aubert @ 2014-04-24 8:30 UTC (permalink / raw)
To: u-boot
This sub-command adds support for the RPMB partition of an eMMC:
* mmc rpmb key <address of the authentication key>
Programs the authentication key in the eMMC This key can not
be overwritten.
* mmc rpmb read <address> <block> <#count> [address of key]
Reads <#count> blocks of 256 bytes in the RPMB partition
beginning at block number <block>. If the optionnal
address of the authentication key is provided, the
Message Authentication Code (MAC) is verified on each
block.
* mmc rpmb write <address> <block> <#count> <address of key>
Writes <#count> blocks of 256 bytes in the RPMB partition
beginning at block number <block>. The datas are signed
with the key provided.
* mmc rpmb counter
Returns the 'Write counter' of the RPMB partition.
The sub-command is conditional on compilation flag CONFIG_SUPPORT_EMMC_RPMB
Signed-off-by: Pierre Aubert <p.aubert@staubli.com>
CC: Pantelis Antoniou <panto@antoniou-consulting.com>
CC: Wolfgang Denk <wd@denx.de>
---
Changes in V5:
- move changelog to the right place
Changes in V4:
- use subcommand table for 'mmc' command and 'mmc rpmb' subcommand.
- add 'mmc info' subcommand, which is the same as mmcinfo.
mmcinfo is kept for compatibility.
- add changelog in commit comments.
Changes in V3:
- add entries in README for configuration options related to eMMC.
- improved test for existence of RPMB partition.
- fix of coding issues.
V2: no changes
README | 10 +
common/cmd_mmc.c | 806 ++++++++++++++++++++++++++++++++++--------------------
2 files changed, 520 insertions(+), 296 deletions(-)
diff --git a/README b/README
index 52a92e7..518e209 100644
--- a/README
+++ b/README
@@ -1493,6 +1493,16 @@ The following options need to be configured:
CONFIG_SH_MMCIF_CLK
Define the clock frequency for MMCIF
+ CONFIG_GENERIC_MMC
+ Enable the generic MMC driver
+
+ CONFIG_SUPPORT_EMMC_BOOT
+ Enable some additional features of the eMMC boot partitions.
+
+ CONFIG_SUPPORT_EMMC_RPMB
+ Enable the commands for reading, writing and programming the
+ key for the Replay Protection Memory Block partition in eMMC.
+
- USB Device Firmware Update (DFU) class support:
CONFIG_DFU_FUNCTION
This enables the USB portion of the DFU USB class
diff --git a/common/cmd_mmc.c b/common/cmd_mmc.c
index c1916c9..55af295 100644
--- a/common/cmd_mmc.c
+++ b/common/cmd_mmc.c
@@ -71,12 +71,6 @@ U_BOOT_CMD(
);
#else /* !CONFIG_GENERIC_MMC */
-enum mmc_state {
- MMC_INVALID,
- MMC_READ,
- MMC_WRITE,
- MMC_ERASE,
-};
static void print_mmcinfo(struct mmc *mmc)
{
printf("Device: %s\n", mmc->cfg->name);
@@ -98,7 +92,18 @@ static void print_mmcinfo(struct mmc *mmc)
printf("Bus Width: %d-bit\n", mmc->bus_width);
}
-
+static struct mmc *init_mmc_device(int dev)
+{
+ struct mmc *mmc;
+ mmc = find_mmc_device(dev);
+ if (!mmc) {
+ printf("no mmc device at slot %x\n", dev);
+ return NULL;
+ }
+ if (mmc_init(mmc))
+ return NULL;
+ return mmc;
+}
static int do_mmcinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
struct mmc *mmc;
@@ -112,351 +117,546 @@ static int do_mmcinfo(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
}
}
- mmc = find_mmc_device(curr_device);
+ mmc = init_mmc_device(curr_device);
+ if (!mmc)
+ return CMD_RET_FAILURE;
- if (mmc) {
- mmc_init(mmc);
+ print_mmcinfo(mmc);
+ return CMD_RET_SUCCESS;
+}
- print_mmcinfo(mmc);
- return 0;
- } else {
- printf("no mmc device at slot %x\n", curr_device);
+#ifdef CONFIG_SUPPORT_EMMC_RPMB
+static int confirm_key_prog(void)
+{
+ puts("Warning: Programming authentication key can be done only once !\n"
+ " Use this command only if you are sure of what you are doing,\n"
+ "Really perform the key programming? <y/N> ");
+ if (confirm_yesno())
return 1;
+
+ puts("Authentication key programming aborted\n");
+ return 0;
+}
+static int do_mmcrpmb_key(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ void *key_addr;
+ struct mmc *mmc = find_mmc_device(curr_device);
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+
+ key_addr = (void *)simple_strtoul(argv[1], NULL, 16);
+ if (!confirm_key_prog())
+ return CMD_RET_FAILURE;
+ if (mmc_rpmb_set_key(mmc, key_addr)) {
+ printf("ERROR - Key already programmed ?\n");
+ return CMD_RET_FAILURE;
}
+ return CMD_RET_SUCCESS;
}
+static int do_mmcrpmb_read(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ u16 blk, cnt;
+ void *addr;
+ int n;
+ void *key_addr = NULL;
+ struct mmc *mmc = find_mmc_device(curr_device);
-U_BOOT_CMD(
- mmcinfo, 1, 0, do_mmcinfo,
- "display MMC info",
- "- display info of the current MMC device"
-);
+ if (argc < 4)
+ return CMD_RET_USAGE;
-static int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+ addr = (void *)simple_strtoul(argv[1], NULL, 16);
+ blk = simple_strtoul(argv[2], NULL, 16);
+ cnt = simple_strtoul(argv[3], NULL, 16);
+
+ if (argc == 5)
+ key_addr = (void *)simple_strtoul(argv[4], NULL, 16);
+
+ printf("\nMMC RPMB read: dev # %d, block # %d, count %d ... ",
+ curr_device, blk, cnt);
+ n = mmc_rpmb_read(mmc, addr, blk, cnt, key_addr);
+
+ printf("%d RPMB blocks read: %s\n", n, (n == cnt) ? "OK" : "ERROR");
+ if (n != cnt)
+ return CMD_RET_FAILURE;
+ return CMD_RET_SUCCESS;
+}
+static int do_mmcrpmb_write(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
{
- enum mmc_state state;
+ u16 blk, cnt;
+ void *addr;
+ int n;
+ void *key_addr;
+ struct mmc *mmc = find_mmc_device(curr_device);
- if (argc < 2)
+ if (argc != 5)
return CMD_RET_USAGE;
- if (curr_device < 0) {
- if (get_mmc_num() > 0)
- curr_device = 0;
- else {
- puts("No MMC device available\n");
- return 1;
- }
+ addr = (void *)simple_strtoul(argv[1], NULL, 16);
+ blk = simple_strtoul(argv[2], NULL, 16);
+ cnt = simple_strtoul(argv[3], NULL, 16);
+ key_addr = (void *)simple_strtoul(argv[4], NULL, 16);
+
+ printf("\nMMC RPMB write: dev # %d, block # %d, count %d ... ",
+ curr_device, blk, cnt);
+ n = mmc_rpmb_write(mmc, addr, blk, cnt, key_addr);
+
+ printf("%d RPMB blocks written: %s\n", n, (n == cnt) ? "OK" : "ERROR");
+ if (n != cnt)
+ return CMD_RET_FAILURE;
+ return CMD_RET_SUCCESS;
+}
+static int do_mmcrpmb_counter(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ unsigned long counter;
+ struct mmc *mmc = find_mmc_device(curr_device);
+
+ if (mmc_rpmb_get_counter(mmc, &counter))
+ return CMD_RET_FAILURE;
+ printf("RPMB Write counter= %lx\n", counter);
+ return CMD_RET_SUCCESS;
+}
+
+static cmd_tbl_t cmd_rpmb[] = {
+ U_BOOT_CMD_MKENT(key, 2, 0, do_mmcrpmb_key, "", ""),
+ U_BOOT_CMD_MKENT(read, 5, 1, do_mmcrpmb_read, "", ""),
+ U_BOOT_CMD_MKENT(write, 5, 0, do_mmcrpmb_write, "", ""),
+ U_BOOT_CMD_MKENT(counter, 1, 1, do_mmcrpmb_counter, "", ""),
+};
+
+static int do_mmcrpmb(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ cmd_tbl_t *cp;
+ struct mmc *mmc;
+ char original_part;
+ int ret;
+
+ cp = find_cmd_tbl(argv[1], cmd_rpmb, ARRAY_SIZE(cmd_rpmb));
+
+ /* Drop the rpmb subcommand */
+ argc--;
+ argv++;
+
+ if (cp == NULL || argc > cp->maxargs)
+ return CMD_RET_USAGE;
+ if (flag == CMD_FLAG_REPEAT && !cp->repeatable)
+ return CMD_RET_SUCCESS;
+
+ mmc = init_mmc_device(curr_device);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ if (!(mmc->version & MMC_VERSION_MMC)) {
+ printf("It is not a EMMC device\n");
+ return CMD_RET_FAILURE;
+ }
+ if (mmc->version < MMC_VERSION_4_41) {
+ printf("RPMB not supported before version 4.41\n");
+ return CMD_RET_FAILURE;
}
+ /* Switch to the RPMB partition */
+ original_part = mmc->part_num;
+ if (mmc->part_num != MMC_PART_RPMB) {
+ if (mmc_switch_part(curr_device, MMC_PART_RPMB) != 0)
+ return CMD_RET_FAILURE;
+ mmc->part_num = MMC_PART_RPMB;
+ }
+ ret = cp->cmd(cmdtp, flag, argc, argv);
- if (strcmp(argv[1], "rescan") == 0) {
- struct mmc *mmc;
+ /* Return to original partition */
+ if (mmc->part_num != original_part) {
+ if (mmc_switch_part(curr_device, original_part) != 0)
+ return CMD_RET_FAILURE;
+ mmc->part_num = original_part;
+ }
+ return ret;
+}
+#endif
- if (argc != 2)
- return CMD_RET_USAGE;
+static int do_mmc_read(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ struct mmc *mmc;
+ u32 blk, cnt, n;
+ void *addr;
- mmc = find_mmc_device(curr_device);
- if (!mmc) {
- printf("no mmc device at slot %x\n", curr_device);
- return 1;
- }
+ if (argc != 4)
+ return CMD_RET_USAGE;
- mmc->has_init = 0;
+ addr = (void *)simple_strtoul(argv[1], NULL, 16);
+ blk = simple_strtoul(argv[2], NULL, 16);
+ cnt = simple_strtoul(argv[3], NULL, 16);
- if (mmc_init(mmc))
- return 1;
- else
- return 0;
- } else if (strcmp(argv[1], "part") == 0) {
- block_dev_desc_t *mmc_dev;
- struct mmc *mmc;
+ mmc = init_mmc_device(curr_device);
+ if (!mmc)
+ return CMD_RET_FAILURE;
- if (argc != 2)
- return CMD_RET_USAGE;
+ printf("\nMMC read: dev # %d, block # %d, count %d ... ",
+ curr_device, blk, cnt);
- mmc = find_mmc_device(curr_device);
- if (!mmc) {
- printf("no mmc device at slot %x\n", curr_device);
- return 1;
- }
- mmc_init(mmc);
- mmc_dev = mmc_get_dev(curr_device);
- if (mmc_dev != NULL &&
- mmc_dev->type != DEV_TYPE_UNKNOWN) {
- print_part(mmc_dev);
- return 0;
- }
+ n = mmc->block_dev.block_read(curr_device, blk, cnt, addr);
+ /* flush cache after read */
+ flush_cache((ulong)addr, cnt * 512); /* FIXME */
+ printf("%d blocks read: %s\n", n, (n == cnt) ? "OK" : "ERROR");
- puts("get mmc type error!\n");
- return 1;
- } else if (strcmp(argv[1], "list") == 0) {
- if (argc != 2)
- return CMD_RET_USAGE;
- print_mmc_devices('\n');
- return 0;
- } else if (strcmp(argv[1], "dev") == 0) {
- int dev, part = -1;
- struct mmc *mmc;
-
- if (argc == 2)
- dev = curr_device;
- else if (argc == 3)
- dev = simple_strtoul(argv[2], NULL, 10);
- else if (argc == 4) {
- dev = (int)simple_strtoul(argv[2], NULL, 10);
- part = (int)simple_strtoul(argv[3], NULL, 10);
- if (part > PART_ACCESS_MASK) {
- printf("#part_num shouldn't be larger"
- " than %d\n", PART_ACCESS_MASK);
- return 1;
- }
- } else
- return CMD_RET_USAGE;
+ return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
+}
+static int do_mmc_write(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ struct mmc *mmc;
+ u32 blk, cnt, n;
+ void *addr;
- mmc = find_mmc_device(dev);
- if (!mmc) {
- printf("no mmc device at slot %x\n", dev);
- return 1;
- }
+ if (argc != 4)
+ return CMD_RET_USAGE;
- mmc_init(mmc);
- if (part != -1) {
- int ret;
- if (mmc->part_config == MMCPART_NOAVAILABLE) {
- printf("Card doesn't support part_switch\n");
- return 1;
- }
+ addr = (void *)simple_strtoul(argv[1], NULL, 16);
+ blk = simple_strtoul(argv[2], NULL, 16);
+ cnt = simple_strtoul(argv[3], NULL, 16);
- if (part != mmc->part_num) {
- ret = mmc_switch_part(dev, part);
- if (!ret)
- mmc->part_num = part;
+ mmc = init_mmc_device(curr_device);
+ if (!mmc)
+ return CMD_RET_FAILURE;
- printf("switch to partitions #%d, %s\n",
- part, (!ret) ? "OK" : "ERROR");
- }
- }
- curr_device = dev;
- if (mmc->part_config == MMCPART_NOAVAILABLE)
- printf("mmc%d is current device\n", curr_device);
- else
- printf("mmc%d(part %d) is current device\n",
- curr_device, mmc->part_num);
+ printf("\nMMC write: dev # %d, block # %d, count %d ... ",
+ curr_device, blk, cnt);
- return 0;
-#ifdef CONFIG_SUPPORT_EMMC_BOOT
- } else if (strcmp(argv[1], "partconf") == 0) {
- int dev;
- struct mmc *mmc;
- u8 ack, part_num, access;
-
- if (argc == 6) {
- dev = simple_strtoul(argv[2], NULL, 10);
- ack = simple_strtoul(argv[3], NULL, 10);
- part_num = simple_strtoul(argv[4], NULL, 10);
- access = simple_strtoul(argv[5], NULL, 10);
- } else {
- return CMD_RET_USAGE;
- }
+ if (mmc_getwp(mmc) == 1) {
+ printf("Error: card is write protected!\n");
+ return CMD_RET_FAILURE;
+ }
+ n = mmc->block_dev.block_write(curr_device, blk, cnt, addr);
+ printf("%d blocks written: %s\n", n, (n == cnt) ? "OK" : "ERROR");
- mmc = find_mmc_device(dev);
- if (!mmc) {
- printf("no mmc device at slot %x\n", dev);
- return 1;
- }
+ return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
+}
+static int do_mmc_erase(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ struct mmc *mmc;
+ u32 blk, cnt, n;
- if (IS_SD(mmc)) {
- puts("PARTITION_CONFIG only exists on eMMC\n");
- return 1;
- }
+ if (argc != 3)
+ return CMD_RET_USAGE;
- /* acknowledge to be sent during boot operation */
- return mmc_set_part_conf(mmc, ack, part_num, access);
- } else if (strcmp(argv[1], "bootbus") == 0) {
- int dev;
- struct mmc *mmc;
- u8 width, reset, mode;
-
- if (argc == 6) {
- dev = simple_strtoul(argv[2], NULL, 10);
- width = simple_strtoul(argv[3], NULL, 10);
- reset = simple_strtoul(argv[4], NULL, 10);
- mode = simple_strtoul(argv[5], NULL, 10);
- } else {
- return CMD_RET_USAGE;
- }
+ blk = simple_strtoul(argv[1], NULL, 16);
+ cnt = simple_strtoul(argv[2], NULL, 16);
- mmc = find_mmc_device(dev);
- if (!mmc) {
- printf("no mmc device at slot %x\n", dev);
- return 1;
- }
+ mmc = init_mmc_device(curr_device);
+ if (!mmc)
+ return CMD_RET_FAILURE;
- if (IS_SD(mmc)) {
- puts("BOOT_BUS_WIDTH only exists on eMMC\n");
- return 1;
- }
+ printf("\nMMC erase: dev # %d, block # %d, count %d ... ",
+ curr_device, blk, cnt);
- /* acknowledge to be sent during boot operation */
- return mmc_set_boot_bus_width(mmc, width, reset, mode);
- } else if (strcmp(argv[1], "bootpart-resize") == 0) {
- int dev;
- struct mmc *mmc;
- u32 bootsize, rpmbsize;
-
- if (argc == 5) {
- dev = simple_strtoul(argv[2], NULL, 10);
- bootsize = simple_strtoul(argv[3], NULL, 10);
- rpmbsize = simple_strtoul(argv[4], NULL, 10);
- } else {
- return CMD_RET_USAGE;
- }
+ if (mmc_getwp(mmc) == 1) {
+ printf("Error: card is write protected!\n");
+ return CMD_RET_FAILURE;
+ }
+ n = mmc->block_dev.block_erase(curr_device, blk, cnt);
+ printf("%d blocks erased: %s\n", n, (n == cnt) ? "OK" : "ERROR");
- mmc = find_mmc_device(dev);
- if (!mmc) {
- printf("no mmc device at slot %x\n", dev);
- return 1;
- }
+ return (n == cnt) ? CMD_RET_SUCCESS : CMD_RET_FAILURE;
+}
+static int do_mmc_rescan(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ struct mmc *mmc;
- if (IS_SD(mmc)) {
- printf("It is not a EMMC device\n");
- return 1;
- }
+ mmc = find_mmc_device(curr_device);
+ if (!mmc) {
+ printf("no mmc device at slot %x\n", curr_device);
+ return CMD_RET_FAILURE;
+ }
- if (0 == mmc_boot_partition_size_change(mmc,
- bootsize, rpmbsize)) {
- printf("EMMC boot partition Size %d MB\n", bootsize);
- printf("EMMC RPMB partition Size %d MB\n", rpmbsize);
- return 0;
- } else {
- printf("EMMC boot partition Size change Failed.\n");
- return 1;
- }
- } else if (strcmp(argv[1], "rst-function") == 0) {
- /*
- * Set the RST_n_ENABLE bit of RST_n_FUNCTION
- * The only valid values are 0x0, 0x1 and 0x2 and writing
- * a value of 0x1 or 0x2 sets the value permanently.
- */
- int dev;
- struct mmc *mmc;
- u8 enable;
-
- if (argc == 4) {
- dev = simple_strtoul(argv[2], NULL, 10);
- enable = simple_strtoul(argv[3], NULL, 10);
- } else {
- return CMD_RET_USAGE;
- }
+ mmc->has_init = 0;
- if (enable > 2 || enable < 0) {
- puts("Invalid RST_n_ENABLE value\n");
- return CMD_RET_USAGE;
+ if (mmc_init(mmc))
+ return CMD_RET_FAILURE;
+ return CMD_RET_SUCCESS;
+}
+static int do_mmc_part(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ block_dev_desc_t *mmc_dev;
+ struct mmc *mmc;
+
+ mmc = init_mmc_device(curr_device);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ mmc_dev = mmc_get_dev(curr_device);
+ if (mmc_dev != NULL && mmc_dev->type != DEV_TYPE_UNKNOWN) {
+ print_part(mmc_dev);
+ return CMD_RET_SUCCESS;
+ }
+
+ puts("get mmc type error!\n");
+ return CMD_RET_FAILURE;
+}
+static int do_mmc_dev(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ int dev, part = -1;
+ struct mmc *mmc;
+
+ if (argc == 1) {
+ dev = curr_device;
+ } else if (argc == 2) {
+ dev = simple_strtoul(argv[1], NULL, 10);
+ } else if (argc == 3) {
+ dev = (int)simple_strtoul(argv[1], NULL, 10);
+ part = (int)simple_strtoul(argv[2], NULL, 10);
+ if (part > PART_ACCESS_MASK) {
+ printf("#part_num shouldn't be larger than %d\n",
+ PART_ACCESS_MASK);
+ return CMD_RET_FAILURE;
}
+ } else {
+ return CMD_RET_USAGE;
+ }
- mmc = find_mmc_device(dev);
- if (!mmc) {
- printf("no mmc device at slot %x\n", dev);
- return 1;
+ mmc = init_mmc_device(dev);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ if (part != -1) {
+ int ret;
+ if (mmc->part_config == MMCPART_NOAVAILABLE) {
+ printf("Card doesn't support part_switch\n");
+ return CMD_RET_FAILURE;
}
- if (IS_SD(mmc)) {
- puts("RST_n_FUNCTION only exists on eMMC\n");
- return 1;
+ if (part != mmc->part_num) {
+ ret = mmc_switch_part(dev, part);
+ if (!ret)
+ mmc->part_num = part;
+
+ printf("switch to partitions #%d, %s\n",
+ part, (!ret) ? "OK" : "ERROR");
}
+ }
+ curr_device = dev;
+ if (mmc->part_config == MMCPART_NOAVAILABLE)
+ printf("mmc%d is current device\n", curr_device);
+ else
+ printf("mmc%d(part %d) is current device\n",
+ curr_device, mmc->part_num);
- return mmc_set_rst_n_function(mmc, enable);
-#endif /* CONFIG_SUPPORT_EMMC_BOOT */
+ return CMD_RET_SUCCESS;
+}
+static int do_mmc_list(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ print_mmc_devices('\n');
+ return CMD_RET_SUCCESS;
+}
+#ifdef CONFIG_SUPPORT_EMMC_BOOT
+static int do_mmc_bootbus(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ int dev;
+ struct mmc *mmc;
+ u8 width, reset, mode;
+
+ if (argc != 5)
+ return CMD_RET_USAGE;
+ dev = simple_strtoul(argv[1], NULL, 10);
+ width = simple_strtoul(argv[2], NULL, 10);
+ reset = simple_strtoul(argv[3], NULL, 10);
+ mode = simple_strtoul(argv[4], NULL, 10);
+
+ mmc = init_mmc_device(dev);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ if (IS_SD(mmc)) {
+ puts("BOOT_BUS_WIDTH only exists on eMMC\n");
+ return CMD_RET_FAILURE;
}
- else if (argc == 3 && strcmp(argv[1], "setdsr") == 0) {
- struct mmc *mmc = find_mmc_device(curr_device);
- u32 val = simple_strtoul(argv[2], NULL, 16);
- int ret;
+ /* acknowledge to be sent during boot operation */
+ return mmc_set_boot_bus_width(mmc, width, reset, mode);
+}
+static int do_mmc_boot_resize(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ int dev;
+ struct mmc *mmc;
+ u32 bootsize, rpmbsize;
- if (!mmc) {
- printf("no mmc device at slot %x\n", curr_device);
- return 1;
- }
- ret = mmc_set_dsr(mmc, val);
- printf("set dsr %s\n", (!ret) ? "OK, force rescan" : "ERROR");
- if (!ret) {
- mmc->has_init = 0;
- if (mmc_init(mmc))
- return 1;
- else
- return 0;
- }
- return ret;
+ if (argc != 4)
+ return CMD_RET_USAGE;
+ dev = simple_strtoul(argv[1], NULL, 10);
+ bootsize = simple_strtoul(argv[2], NULL, 10);
+ rpmbsize = simple_strtoul(argv[3], NULL, 10);
+
+ mmc = init_mmc_device(dev);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ if (IS_SD(mmc)) {
+ printf("It is not a EMMC device\n");
+ return CMD_RET_FAILURE;
}
- state = MMC_INVALID;
- if (argc == 5 && strcmp(argv[1], "read") == 0)
- state = MMC_READ;
- else if (argc == 5 && strcmp(argv[1], "write") == 0)
- state = MMC_WRITE;
- else if (argc == 4 && strcmp(argv[1], "erase") == 0)
- state = MMC_ERASE;
-
- if (state != MMC_INVALID) {
- struct mmc *mmc = find_mmc_device(curr_device);
- int idx = 2;
- u32 blk, cnt, n;
- void *addr;
-
- if (state != MMC_ERASE) {
- addr = (void *)simple_strtoul(argv[idx], NULL, 16);
- ++idx;
- } else
- addr = NULL;
- blk = simple_strtoul(argv[idx], NULL, 16);
- cnt = simple_strtoul(argv[idx + 1], NULL, 16);
-
- if (!mmc) {
- printf("no mmc device at slot %x\n", curr_device);
- return 1;
- }
+ if (mmc_boot_partition_size_change(mmc, bootsize, rpmbsize)) {
+ printf("EMMC boot partition Size change Failed.\n");
+ return CMD_RET_FAILURE;
+ }
- printf("\nMMC %s: dev # %d, block # %d, count %d ... ",
- argv[1], curr_device, blk, cnt);
+ printf("EMMC boot partition Size %d MB\n", bootsize);
+ printf("EMMC RPMB partition Size %d MB\n", rpmbsize);
+ return CMD_RET_SUCCESS;
+}
+static int do_mmc_partconf(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ int dev;
+ struct mmc *mmc;
+ u8 ack, part_num, access;
- mmc_init(mmc);
+ if (argc != 5)
+ return CMD_RET_USAGE;
- if ((state == MMC_WRITE || state == MMC_ERASE)) {
- if (mmc_getwp(mmc) == 1) {
- printf("Error: card is write protected!\n");
- return 1;
- }
- }
+ dev = simple_strtoul(argv[1], NULL, 10);
+ ack = simple_strtoul(argv[2], NULL, 10);
+ part_num = simple_strtoul(argv[3], NULL, 10);
+ access = simple_strtoul(argv[4], NULL, 10);
- switch (state) {
- case MMC_READ:
- n = mmc->block_dev.block_read(curr_device, blk,
- cnt, addr);
- /* flush cache after read */
- flush_cache((ulong)addr, cnt * 512); /* FIXME */
- break;
- case MMC_WRITE:
- n = mmc->block_dev.block_write(curr_device, blk,
- cnt, addr);
- break;
- case MMC_ERASE:
- n = mmc->block_dev.block_erase(curr_device, blk, cnt);
- break;
- default:
- BUG();
- }
+ mmc = init_mmc_device(dev);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ if (IS_SD(mmc)) {
+ puts("PARTITION_CONFIG only exists on eMMC\n");
+ return CMD_RET_FAILURE;
+ }
+
+ /* acknowledge to be sent during boot operation */
+ return mmc_set_part_conf(mmc, ack, part_num, access);
+}
+static int do_mmc_rst_func(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ int dev;
+ struct mmc *mmc;
+ u8 enable;
+
+ /*
+ * Set the RST_n_ENABLE bit of RST_n_FUNCTION
+ * The only valid values are 0x0, 0x1 and 0x2 and writing
+ * a value of 0x1 or 0x2 sets the value permanently.
+ */
+ if (argc != 3)
+ return CMD_RET_USAGE;
+
+ dev = simple_strtoul(argv[1], NULL, 10);
+ enable = simple_strtoul(argv[2], NULL, 10);
+
+ if (enable > 2 || enable < 0) {
+ puts("Invalid RST_n_ENABLE value\n");
+ return CMD_RET_USAGE;
+ }
+
+ mmc = init_mmc_device(dev);
+ if (!mmc)
+ return CMD_RET_FAILURE;
+
+ if (IS_SD(mmc)) {
+ puts("RST_n_FUNCTION only exists on eMMC\n");
+ return CMD_RET_FAILURE;
+ }
- printf("%d blocks %s: %s\n",
- n, argv[1], (n == cnt) ? "OK" : "ERROR");
- return (n == cnt) ? 0 : 1;
+ return mmc_set_rst_n_function(mmc, enable);
+}
+#endif
+static int do_mmc_setdsr(cmd_tbl_t *cmdtp, int flag,
+ int argc, char * const argv[])
+{
+ struct mmc *mmc;
+ u32 val;
+ int ret;
+
+ if (argc != 2)
+ return CMD_RET_USAGE;
+ val = simple_strtoul(argv[2], NULL, 16);
+
+ mmc = find_mmc_device(curr_device);
+ if (!mmc) {
+ printf("no mmc device@slot %x\n", curr_device);
+ return CMD_RET_FAILURE;
+ }
+ ret = mmc_set_dsr(mmc, val);
+ printf("set dsr %s\n", (!ret) ? "OK, force rescan" : "ERROR");
+ if (!ret) {
+ mmc->has_init = 0;
+ if (mmc_init(mmc))
+ return CMD_RET_FAILURE;
+ else
+ return CMD_RET_SUCCESS;
}
+ return ret;
+}
+
+static cmd_tbl_t cmd_mmc[] = {
+ U_BOOT_CMD_MKENT(info, 1, 0, do_mmcinfo, "", ""),
+ U_BOOT_CMD_MKENT(read, 4, 1, do_mmc_read, "", ""),
+ U_BOOT_CMD_MKENT(write, 4, 0, do_mmc_write, "", ""),
+ U_BOOT_CMD_MKENT(erase, 3, 0, do_mmc_erase, "", ""),
+ U_BOOT_CMD_MKENT(rescan, 1, 1, do_mmc_rescan, "", ""),
+ U_BOOT_CMD_MKENT(part, 1, 1, do_mmc_part, "", ""),
+ U_BOOT_CMD_MKENT(dev, 3, 0, do_mmc_dev, "", ""),
+ U_BOOT_CMD_MKENT(list, 1, 1, do_mmc_list, "", ""),
+#ifdef CONFIG_SUPPORT_EMMC_BOOT
+ U_BOOT_CMD_MKENT(bootbus, 5, 0, do_mmc_bootbus, "", ""),
+ U_BOOT_CMD_MKENT(bootpart-resize, 3, 0, do_mmc_boot_resize, "", ""),
+ U_BOOT_CMD_MKENT(partconf, 5, 0, do_mmc_partconf, "", ""),
+ U_BOOT_CMD_MKENT(rst-function, 3, 0, do_mmc_rst_func, "", ""),
+#endif
+#ifdef CONFIG_SUPPORT_EMMC_RPMB
+ U_BOOT_CMD_MKENT(rpmb, CONFIG_SYS_MAXARGS, 1, do_mmcrpmb, "", ""),
+#endif
+ U_BOOT_CMD_MKENT(setdsr, 2, 0, do_mmc_setdsr, "", ""),
+};
+
+static int do_mmcops(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
+{
+ cmd_tbl_t *cp;
+
+ cp = find_cmd_tbl(argv[1], cmd_mmc, ARRAY_SIZE(cmd_mmc));
+
+ /* Drop the mmc command */
+ argc--;
+ argv++;
+
+ if (cp == NULL || argc > cp->maxargs)
+ return CMD_RET_USAGE;
+ if (flag == CMD_FLAG_REPEAT && !cp->repeatable)
+ return CMD_RET_SUCCESS;
- return CMD_RET_USAGE;
+ if (curr_device < 0) {
+ if (get_mmc_num() > 0) {
+ curr_device = 0;
+ } else {
+ puts("No MMC device available\n");
+ return CMD_RET_FAILURE;
+ }
+ }
+ return cp->cmd(cmdtp, flag, argc, argv);
}
U_BOOT_CMD(
- mmc, 6, 1, do_mmcops,
+ mmc, 7, 1, do_mmcops,
"MMC sub system",
- "read addr blk# cnt\n"
+ "info - display info of the current MMC device\n"
+ "mmc read addr blk# cnt\n"
"mmc write addr blk# cnt\n"
"mmc erase blk# cnt\n"
"mmc rescan\n"
@@ -474,6 +674,20 @@ U_BOOT_CMD(
" - Change the RST_n_FUNCTION field of the specified device\n"
" WARNING: This is a write-once field and 0 / 1 / 2 are the only valid values.\n"
#endif
- "mmc setdsr - set DSR register value\n"
+#ifdef CONFIG_SUPPORT_EMMC_RPMB
+ "mmc rpmb read addr blk# cnt [address of auth-key] - block size is 256 bytes\n"
+ "mmc rpmb write addr blk# cnt <address of auth-key> - block size is 256 bytes\n"
+ "mmc rpmb key <address of auth-key> - program the RPMB authentication key.\n"
+ "mmc rpmb counter - read the value of the write counter\n"
+#endif
+ "mmc setdsr <value> - set DSR register value\n"
);
+
+/* Old command kept for compatibility. Same as 'mmc info' */
+U_BOOT_CMD(
+ mmcinfo, 1, 0, do_mmcinfo,
+ "display MMC info",
+ "- display info of the current MMC device"
+);
+
#endif /* !CONFIG_GENERIC_MMC */
--
1.7.6.5
^ permalink raw reply related [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V5 1/3] eMMC: add support for operations in RPMB partition
2014-04-24 8:30 ` [U-Boot] [PATCH V5 1/3] eMMC: add support for operations in RPMB partition Pierre Aubert
@ 2014-05-23 8:50 ` Pantelis Antoniou
0 siblings, 0 replies; 31+ messages in thread
From: Pantelis Antoniou @ 2014-05-23 8:50 UTC (permalink / raw)
To: u-boot
Hi Pierre,
On Apr 24, 2014, at 11:30 AM, Pierre Aubert wrote:
> This patch adds functions for read, write and authentication
> key programming for the Replay Protected Memory Block partition
> in the eMMC.
>
> Signed-off-by: Pierre Aubert <p.aubert@staubli.com>
> CC: Pantelis Antoniou <panto@antoniou-consulting.com>
> ---
> Changes in V5:
> - move changelog to the right place
> - change lib/Makefile for adding sha256 when CONFIG_SUPPORT_EMMC_RPMB is
> defined
>
> V3, V4: no changes
>
> Changes in V2:
> - use ALLOC_CACHE_ALIGN_BUFFER in rpmb.c instead of a static buffer for the
> RPMB frames.
>
> drivers/mmc/Makefile | 1 +
> drivers/mmc/rpmb.c | 323 ++++++++++++++++++++++++++++++++++++++++++++++++++
> include/mmc.h | 10 ++-
> lib/Makefile | 1 +
> 4 files changed, 334 insertions(+), 1 deletions(-)
> create mode 100644 drivers/mmc/rpmb.c
>
> diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
> index 931922b..4c6ab9e 100644
> --- a/drivers/mmc/Makefile
> +++ b/drivers/mmc/Makefile
> @@ -30,6 +30,7 @@ obj-$(CONFIG_DWMMC) += dw_mmc.o
> obj-$(CONFIG_EXYNOS_DWMMC) += exynos_dw_mmc.o
> obj-$(CONFIG_ZYNQ_SDHCI) += zynq_sdhci.o
> obj-$(CONFIG_SOCFPGA_DWMMC) += socfpga_dw_mmc.o
> +obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o
> ifdef CONFIG_SPL_BUILD
> obj-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o
> else
> diff --git a/drivers/mmc/rpmb.c b/drivers/mmc/rpmb.c
> new file mode 100644
> index 0000000..05936f5
> --- /dev/null
> +++ b/drivers/mmc/rpmb.c
> @@ -0,0 +1,323 @@
> +/*
> + * Copyright 2014, Staubli Faverges
> + * Pierre Aubert
> + *
> + * eMMC- Replay Protected Memory Block
> + * According to JEDEC Standard No. 84-A441
> + *
> + * SPDX-License-Identifier: GPL-2.0+
> + */
> +
> +#include <config.h>
> +#include <common.h>
> +#include <mmc.h>
> +#include <sha256.h>
> +#include "mmc_private.h"
> +
> +/* Request codes */
> +#define RPMB_REQ_KEY 1
> +#define RPMB_REQ_WCOUNTER 2
> +#define RPMB_REQ_WRITE_DATA 3
> +#define RPMB_REQ_READ_DATA 4
> +#define RPMB_REQ_STATUS 5
> +
> +/* Response code */
> +#define RPMB_RESP_KEY 0x0100
> +#define RPMB_RESP_WCOUNTER 0x0200
> +#define RPMB_RESP_WRITE_DATA 0x0300
> +#define RPMB_RESP_READ_DATA 0x0400
> +
> +/* Error codes */
> +#define RPMB_OK 0
> +#define RPMB_ERR_GENERAL 1
> +#define RPMB_ERR_AUTH 2
> +#define RPMB_ERR_COUNTER 3
> +#define RPMB_ERR_ADDRESS 4
> +#define RPMB_ERR_WRITE 5
> +#define RPMB_ERR_READ 6
> +#define RPMB_ERR_KEY 7
> +#define RPMB_ERR_CNT_EXPIRED 0x80
> +#define RPMB_ERR_MSK 0x7
> +
> +/* Sizes of RPMB data frame */
> +#define RPMB_SZ_STUFF 196
> +#define RPMB_SZ_MAC 32
> +#define RPMB_SZ_DATA 256
> +#define RPMB_SZ_NONCE 16
> +
> +#define SHA256_BLOCK_SIZE 64
> +
> +/* Error messages */
> +static const char * const rpmb_err_msg[] = {
> + "",
> + "General failure",
> + "Authentication failure",
> + "Counter failure",
> + "Address failure",
> + "Write failure",
> + "Read failure",
> + "Authentication key not yet programmed",
> +};
> +
> +
> +/* Structure of RPMB data frame. */
> +struct s_rpmb {
> + unsigned char stuff[RPMB_SZ_STUFF];
> + unsigned char mac[RPMB_SZ_MAC];
> + unsigned char data[RPMB_SZ_DATA];
> + unsigned char nonce[RPMB_SZ_NONCE];
> + unsigned long write_counter;
> + unsigned short address;
> + unsigned short block_count;
> + unsigned short result;
> + unsigned short request;
> +};
> +
> +static int mmc_set_blockcount(struct mmc *mmc, unsigned int blockcount,
> + bool is_rel_write)
> +{
> + struct mmc_cmd cmd = {0};
> +
> + cmd.cmdidx = MMC_CMD_SET_BLOCK_COUNT;
> + cmd.cmdarg = blockcount & 0x0000FFFF;
> + if (is_rel_write)
> + cmd.cmdarg |= 1 << 31;
> + cmd.resp_type = MMC_RSP_R1;
> +
> + return mmc_send_cmd(mmc, &cmd, NULL);
> +}
> +static int mmc_rpmb_request(struct mmc *mmc, const struct s_rpmb *s,
> + unsigned int count, bool is_rel_write)
> +{
> + struct mmc_cmd cmd = {0};
> + struct mmc_data data;
> + int ret;
> +
> + ret = mmc_set_blockcount(mmc, count, is_rel_write);
> + if (ret) {
> +#ifdef CONFIG_MMC_RPMB_TRACE
> + printf("%s:mmc_set_blockcount-> %d\n", __func__, ret);
> +#endif
> + return 1;
> + }
> +
> + cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
> + cmd.cmdarg = 0;
> + cmd.resp_type = MMC_RSP_R1b;
> +
> + data.src = (const char *)s;
> + data.blocks = 1;
> + data.blocksize = MMC_MAX_BLOCK_LEN;
> + data.flags = MMC_DATA_WRITE;
> +
> + ret = mmc_send_cmd(mmc, &cmd, &data);
> + if (ret) {
> +#ifdef CONFIG_MMC_RPMB_TRACE
> + printf("%s:mmc_send_cmd-> %d\n", __func__, ret);
> +#endif
> + return 1;
> + }
> + return 0;
> +}
> +static int mmc_rpmb_response(struct mmc *mmc, struct s_rpmb *s,
> + unsigned short expected)
> +{
> + struct mmc_cmd cmd = {0};
> + struct mmc_data data;
> + int ret;
> +
> + ret = mmc_set_blockcount(mmc, 1, false);
> + if (ret) {
> +#ifdef CONFIG_MMC_RPMB_TRACE
> + printf("%s:mmc_set_blockcount-> %d\n", __func__, ret);
> +#endif
> + return -1;
> + }
> + cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
> + cmd.cmdarg = 0;
> + cmd.resp_type = MMC_RSP_R1;
> +
> + data.dest = (char *)s;
> + data.blocks = 1;
> + data.blocksize = MMC_MAX_BLOCK_LEN;
> + data.flags = MMC_DATA_READ;
> +
> + ret = mmc_send_cmd(mmc, &cmd, &data);
> + if (ret) {
> +#ifdef CONFIG_MMC_RPMB_TRACE
> + printf("%s:mmc_send_cmd-> %d\n", __func__, ret);
> +#endif
> + return -1;
> + }
> + /* Check the response and the status */
> + if (be16_to_cpu(s->request) != expected) {
> +#ifdef CONFIG_MMC_RPMB_TRACE
> + printf("%s:response= %x\n", __func__,
> + be16_to_cpu(s->request));
> +#endif
> + return -1;
> + }
> + ret = be16_to_cpu(s->result);
> + if (ret) {
> + printf("%s %s\n", rpmb_err_msg[ret & RPMB_ERR_MSK],
> + (ret & RPMB_ERR_CNT_EXPIRED) ?
> + "Write counter has expired" : "");
> + }
> +
> + /* Return the status of the command */
> + return ret;
> +}
> +static int mmc_rpmb_status(struct mmc *mmc, unsigned short expected)
> +{
> + ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
> +
> + memset(rpmb_frame, 0, sizeof(struct s_rpmb));
> + rpmb_frame->request = cpu_to_be16(RPMB_REQ_STATUS);
> + if (mmc_rpmb_request(mmc, rpmb_frame, 1, false))
> + return -1;
> +
> + /* Read the result */
> + return mmc_rpmb_response(mmc, rpmb_frame, expected);
> +}
> +static void rpmb_hmac(unsigned char *key, unsigned char *buff, int len,
> + unsigned char *output)
> +{
> + sha256_context ctx;
> + int i;
> + unsigned char k_ipad[SHA256_BLOCK_SIZE];
> + unsigned char k_opad[SHA256_BLOCK_SIZE];
> +
> + sha256_starts(&ctx);
> +
> + /* According to RFC 4634, the HMAC transform looks like:
> + SHA(K XOR opad, SHA(K XOR ipad, text))
> +
> + where K is an n byte key.
> + ipad is the byte 0x36 repeated blocksize times
> + opad is the byte 0x5c repeated blocksize times
> + and text is the data being protected.
> + */
> +
> + for (i = 0; i < RPMB_SZ_MAC; i++) {
> + k_ipad[i] = key[i] ^ 0x36;
> + k_opad[i] = key[i] ^ 0x5c;
> + }
> + /* remaining pad bytes are '\0' XOR'd with ipad and opad values */
> + for ( ; i < SHA256_BLOCK_SIZE; i++) {
> + k_ipad[i] = 0x36;
> + k_opad[i] = 0x5c;
> + }
> + sha256_update(&ctx, k_ipad, SHA256_BLOCK_SIZE);
> + sha256_update(&ctx, buff, len);
> + sha256_finish(&ctx, output);
> +
> + /* Init context for second pass */
> + sha256_starts(&ctx);
> +
> + /* start with outer pad */
> + sha256_update(&ctx, k_opad, SHA256_BLOCK_SIZE);
> +
> + /* then results of 1st hash */
> + sha256_update(&ctx, output, RPMB_SZ_MAC);
> +
> + /* finish up 2nd pass */
> + sha256_finish(&ctx, output);
> +}
> +int mmc_rpmb_get_counter(struct mmc *mmc, unsigned long *pcounter)
> +{
> + int ret;
> + ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
> +
> + /* Fill the request */
> + memset(rpmb_frame, 0, sizeof(struct s_rpmb));
> + rpmb_frame->request = cpu_to_be16(RPMB_REQ_WCOUNTER);
> + if (mmc_rpmb_request(mmc, rpmb_frame, 1, false))
> + return -1;
> +
> + /* Read the result */
> + ret = mmc_rpmb_response(mmc, rpmb_frame, RPMB_RESP_WCOUNTER);
> + if (ret)
> + return ret;
> +
> + *pcounter = be32_to_cpu(rpmb_frame->write_counter);
> + return 0;
> +}
> +int mmc_rpmb_set_key(struct mmc *mmc, void *key)
> +{
> + ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
> + /* Fill the request */
> + memset(rpmb_frame, 0, sizeof(struct s_rpmb));
> + rpmb_frame->request = cpu_to_be16(RPMB_REQ_KEY);
> + memcpy(rpmb_frame->mac, key, RPMB_SZ_MAC);
> +
> + if (mmc_rpmb_request(mmc, rpmb_frame, 1, true))
> + return -1;
> +
> + /* read the operation status */
> + return mmc_rpmb_status(mmc, RPMB_RESP_KEY);
> +}
> +int mmc_rpmb_read(struct mmc *mmc, void *addr, unsigned short blk,
> + unsigned short cnt, unsigned char *key)
> +{
> + ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
> + int i;
> +
> + for (i = 0; i < cnt; i++) {
> + /* Fill the request */
> + memset(rpmb_frame, 0, sizeof(struct s_rpmb));
> + rpmb_frame->address = cpu_to_be16(blk + i);
> + rpmb_frame->request = cpu_to_be16(RPMB_REQ_READ_DATA);
> + if (mmc_rpmb_request(mmc, rpmb_frame, 1, false))
> + break;
> +
> + /* Read the result */
> + if (mmc_rpmb_response(mmc, rpmb_frame, RPMB_RESP_READ_DATA))
> + break;
> +
> + /* Check the HMAC if key is provided */
> + if (key) {
> + unsigned char ret_hmac[RPMB_SZ_MAC];
> +
> + rpmb_hmac(key, rpmb_frame->data, 284, ret_hmac);
> + if (memcmp(ret_hmac, rpmb_frame->mac, RPMB_SZ_MAC)) {
> + printf("MAC error on block #%d\n", i);
> + break;
> + }
> + }
> + /* Copy data */
> + memcpy(addr + i * RPMB_SZ_DATA, rpmb_frame->data, RPMB_SZ_DATA);
> + }
> + return i;
> +}
> +int mmc_rpmb_write(struct mmc *mmc, void *addr, unsigned short blk,
> + unsigned short cnt, unsigned char *key)
> +{
> + ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
> + unsigned long wcount;
> + int i;
> +
> + for (i = 0; i < cnt; i++) {
> + if (mmc_rpmb_get_counter(mmc, &wcount)) {
> + printf("Cannot read RPMB write counter\n");
> + break;
> + }
> +
> + /* Fill the request */
> + memset(rpmb_frame, 0, sizeof(struct s_rpmb));
> + memcpy(rpmb_frame->data, addr + i * RPMB_SZ_DATA, RPMB_SZ_DATA);
> + rpmb_frame->address = cpu_to_be16(blk + i);
> + rpmb_frame->block_count = cpu_to_be16(1);
> + rpmb_frame->write_counter = cpu_to_be32(wcount);
> + rpmb_frame->request = cpu_to_be16(RPMB_REQ_WRITE_DATA);
> + /* Computes HMAC */
> + rpmb_hmac(key, rpmb_frame->data, 284, rpmb_frame->mac);
> +
> + if (mmc_rpmb_request(mmc, rpmb_frame, 1, true))
> + break;
> +
> + /* Get status */
> + if (mmc_rpmb_status(mmc, RPMB_RESP_WRITE_DATA))
> + break;
> + }
> + return i;
> +}
> diff --git a/include/mmc.h b/include/mmc.h
> index 42d0125..14d296c 100644
> --- a/include/mmc.h
> +++ b/include/mmc.h
> @@ -69,6 +69,7 @@
> #define MMC_CMD_SET_BLOCKLEN 16
> #define MMC_CMD_READ_SINGLE_BLOCK 17
> #define MMC_CMD_READ_MULTIPLE_BLOCK 18
> +#define MMC_CMD_SET_BLOCK_COUNT 23
> #define MMC_CMD_WRITE_SINGLE_BLOCK 24
> #define MMC_CMD_WRITE_MULTIPLE_BLOCK 25
> #define MMC_CMD_ERASE_GROUP_START 35
> @@ -224,6 +225,7 @@
> * boot partitions (2), general purpose partitions (4) in MMC v4.4.
> */
> #define MMC_NUM_BOOT_PARTITION 2
> +#define MMC_PART_RPMB 3 /* RPMB partition number */
>
> struct mmc_cid {
> unsigned long psn;
> @@ -335,7 +337,13 @@ int mmc_set_part_conf(struct mmc *mmc, u8 ack, u8 part_num, u8 access);
> int mmc_set_boot_bus_width(struct mmc *mmc, u8 width, u8 reset, u8 mode);
> /* Function to modify the RST_n_FUNCTION field of EXT_CSD */
> int mmc_set_rst_n_function(struct mmc *mmc, u8 enable);
> -
> +/* Functions to read / write the RPMB partition */
> +int mmc_rpmb_set_key(struct mmc *mmc, void *key);
> +int mmc_rpmb_get_counter(struct mmc *mmc, unsigned long *counter);
> +int mmc_rpmb_read(struct mmc *mmc, void *addr, unsigned short blk,
> + unsigned short cnt, unsigned char *key);
> +int mmc_rpmb_write(struct mmc *mmc, void *addr, unsigned short blk,
> + unsigned short cnt, unsigned char *key);
> /**
> * Start device initialization and return immediately; it does not block on
> * polling OCR (operation condition register) status. Then you should call
> diff --git a/lib/Makefile b/lib/Makefile
> index 27e4f78..377ab13 100644
> --- a/lib/Makefile
> +++ b/lib/Makefile
> @@ -35,6 +35,7 @@ obj-y += net_utils.o
> obj-$(CONFIG_PHYSMEM) += physmem.o
> obj-y += qsort.o
> obj-$(CONFIG_SHA1) += sha1.o
> +obj-$(CONFIG_SUPPORT_EMMC_RPMB) += sha256.o
> obj-$(CONFIG_SHA256) += sha256.o
> obj-y += strmhz.o
> obj-$(CONFIG_TPM) += tpm.o
> --
> 1.7.6.5
>
Looks good.
Applied
Thanks
-- Pantelis
Acked-by: Pantelis Antoniou <panto@antoniou-consulting.com>
> _______________________________________________
> U-Boot mailing list
> U-Boot at lists.denx.de
> http://lists.denx.de/mailman/listinfo/u-boot
^ permalink raw reply [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH V5 2/3] Add the function 'confirm_yesno' for interactive
2014-04-24 8:30 ` [U-Boot] [PATCH V5 2/3] Add the function 'confirm_yesno' for interactive Pierre Aubert
@ 2014-05-23 8:52 ` Pantelis Antoniou
0 siblings, 0 replies; 31+ messages in thread
From: Pantelis Antoniou @ 2014-05-23 8:52 UTC (permalink / raw)
To: u-boot
Hi Pierre,
On Apr 24, 2014, at 11:30 AM, Pierre Aubert wrote:
> User's confirmation is asked in different commands. This commit adds a
> function for such confirmation.
>
> Signed-off-by: Pierre Aubert <p.aubert@staubli.com>
> ---
>
> V3, V4, V5: no changes
>
> Patch added in V2
>
> common/cmd_fuse.c | 11 ++---------
> common/cmd_nand.c | 16 +++++-----------
> common/cmd_otp.c | 18 +++---------------
> common/console.c | 28 +++++++++++++++++++++++++++-
> include/common.h | 2 +-
> 5 files changed, 38 insertions(+), 37 deletions(-)
>
> diff --git a/common/cmd_fuse.c b/common/cmd_fuse.c
> index 0df57db..abab978 100644
> --- a/common/cmd_fuse.c
> +++ b/common/cmd_fuse.c
> @@ -33,15 +33,8 @@ static int confirm_prog(void)
> "what you are doing!\n"
> "\nReally perform this fuse programming? <y/N>\n");
>
> - if (getc() == 'y') {
> - int c;
> -
> - putc('y');
> - c = getc();
> - putc('\n');
> - if (c == '\r')
> - return 1;
> - }
> + if (confirm_yesno())
> + return 1;
>
> puts("Fuse programming aborted\n");
> return 0;
> diff --git a/common/cmd_nand.c b/common/cmd_nand.c
> index 04ab0f1..a84f7dc 100644
> --- a/common/cmd_nand.c
> +++ b/common/cmd_nand.c
> @@ -605,22 +605,16 @@ static int do_nand(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> opts.spread = spread;
>
> if (scrub) {
> - if (!scrub_yes)
> - puts(scrub_warn);
> -
> - if (scrub_yes)
> + if (scrub_yes) {
> opts.scrub = 1;
> - else if (getc() == 'y') {
> - puts("y");
> - if (getc() == '\r')
> + } else {
> + puts(scrub_warn);
> + if (confirm_yesno()) {
> opts.scrub = 1;
> - else {
> + } else {
> puts("scrub aborted\n");
> return 1;
> }
> - } else {
> - puts("scrub aborted\n");
> - return 1;
> }
> }
> ret = nand_erase_opts(nand, &opts);
> diff --git a/common/cmd_otp.c b/common/cmd_otp.c
> index 67808aa..593bb8c 100644
> --- a/common/cmd_otp.c
> +++ b/common/cmd_otp.c
> @@ -158,21 +158,9 @@ int do_otp(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
> lowup(half + count - 1), page + (half + count - 1) / 2,
> half + count
> );
> -
> - i = 0;
> - while (1) {
> - if (tstc()) {
> - const char exp_ans[] = "YES\r";
> - char c;
> - putc(c = getc());
> - if (exp_ans[i++] != c) {
> - printf(" Aborting\n");
> - return 1;
> - } else if (!exp_ans[i]) {
> - puts("\n");
> - break;
> - }
> - }
> + if (!confirm_yesno()) {
> + printf(" Aborting\n");
> + return 1;
> }
> }
>
> diff --git a/common/console.c b/common/console.c
> index 2dfb788..5453726 100644
> --- a/common/console.c
> +++ b/common/console.c
> @@ -537,7 +537,33 @@ int ctrlc(void)
> }
> return 0;
> }
> -
> +/* Reads user's confirmation.
> + Returns 1 if user's input is "y", "Y", "yes" or "YES"
> +*/
> +int confirm_yesno(void)
> +{
> + int i;
> + char str_input[5];
> +
> + /* Flush input */
> + while (tstc())
> + getc();
> + i = 0;
> + while (i < sizeof(str_input)) {
> + str_input[i] = getc();
> + putc(str_input[i]);
> + if (str_input[i] == '\r')
> + break;
> + i++;
> + }
> + putc('\n');
> + if (strncmp(str_input, "y\r", 2) == 0 ||
> + strncmp(str_input, "Y\r", 2) == 0 ||
> + strncmp(str_input, "yes\r", 4) == 0 ||
> + strncmp(str_input, "YES\r", 4) == 0)
> + return 1;
I guess you could convert to upper/lower case and reduce the comparisons but I'd
let it pass.
Maybe follow up later with a patch just for that.
> + return 0;
> +}
> /* pass 1 to disable ctrlc() checking, 0 to enable.
> * returns previous state
> */
> diff --git a/include/common.h b/include/common.h
> index baf361b..1d922b9 100644
> --- a/include/common.h
> +++ b/include/common.h
> @@ -838,7 +838,7 @@ int ctrlc (void);
> int had_ctrlc (void); /* have we had a Control-C since last clear? */
> void clear_ctrlc (void); /* clear the Control-C condition */
> int disable_ctrlc (int); /* 1 to disable, 0 to enable Control-C detect */
> -
> +int confirm_yesno(void); /* 1 if input is "y", "Y", "yes" or "YES" */
> /*
> * STDIO based functions (can always be used)
> */
> --
> 1.7.6.5
>
This is reasonable, and I've applied it to my mmc tree,
Applied,
Thanks
-- Pantelis
Acked-by: Pantelis Antoniou <panto@antoniou-consulting.com>
^ permalink raw reply [flat|nested] 31+ messages in thread
* [U-Boot] [PATCH 1/2] eMMC: add support for operations in RPMB partition
2014-04-11 12:12 ` [U-Boot] [PATCH 1/2] eMMC: add support for operations in RPMB partition Pierre Aubert
@ 2014-12-23 5:13 ` Roman Peniaev
0 siblings, 0 replies; 31+ messages in thread
From: Roman Peniaev @ 2014-12-23 5:13 UTC (permalink / raw)
To: u-boot
Hello, Pierre.
Could you please help me to understand why you used R1b with
MMC_CMD_WRITE_MULTIPLE_BLOCK in mmc_rpmb_request:
+ cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
+ cmd.cmdarg = 0;
+ cmd.resp_type = MMC_RSP_R1b;
Because according to the spec CMD25 has R1 response, also
RPMB section does not explicitly point on the response type,
maybe these words are the key:
"The busy signaling in the Dat0 line after the CRC status by the e?MMC
is indicating request busy."
But I am not sure.
--
Roman
^ permalink raw reply [flat|nested] 31+ messages in thread
end of thread, other threads:[~2014-12-23 5:13 UTC | newest]
Thread overview: 31+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-04-11 12:12 [U-Boot] [PATCH 0/2] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
2014-04-11 12:12 ` [U-Boot] [PATCH 1/2] eMMC: add support for operations in RPMB partition Pierre Aubert
2014-12-23 5:13 ` Roman Peniaev
2014-04-11 12:12 ` [U-Boot] [PATCH 2/2] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command Pierre Aubert
2014-04-17 15:10 ` [U-Boot] [PATCH V2 0/2] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
2014-04-17 15:10 ` [U-Boot] [PATCH V2 1/2] eMMC: add support for operations in RPMB partition Pierre Aubert
2014-04-17 15:10 ` [U-Boot] [PATCH V2 2/2] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command Pierre Aubert
2014-04-17 19:56 ` Wolfgang Denk
2014-04-18 6:39 ` Pierre AUBERT
2014-04-22 17:41 ` Wolfgang Denk
2014-04-22 15:54 ` [U-Boot] [PATCH V3 0/3] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
2014-04-22 15:54 ` [U-Boot] [PATCH V3 1/3] eMMC: add support for operations in RPMB partition Pierre Aubert
2014-04-22 15:54 ` [U-Boot] [PATCH V3 2/3] Add the function 'confirm_yesno' for interactive confirmation Pierre Aubert
2014-04-22 15:54 ` [U-Boot] [PATCH V3 3/3] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command Pierre Aubert
2014-04-22 17:48 ` Wolfgang Denk
2014-04-24 6:40 ` [U-Boot] [PATCH V4 0/3] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
2014-04-24 6:40 ` [U-Boot] [PATCH V4 1/3] eMMC: add support for operations in RPMB partition Pierre Aubert
2014-04-24 6:55 ` Wolfgang Denk
2014-04-24 7:16 ` Pierre AUBERT
2014-04-24 7:33 ` Wolfgang Denk
2014-04-24 7:41 ` Pierre AUBERT
2014-04-24 6:59 ` Wolfgang Denk
2014-04-24 7:56 ` Pierre AUBERT
2014-04-24 6:40 ` [U-Boot] [PATCH V4 2/3] Add the function 'confirm_yesno' for interactive Pierre Aubert
2014-04-24 6:40 ` [U-Boot] [PATCH V4 3/3] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command Pierre Aubert
2014-04-24 8:30 ` [U-Boot] [PATCH V5 0/3] eMMC: support for Read Protected Memory Block (RPMB) Pierre Aubert
2014-04-24 8:30 ` [U-Boot] [PATCH V5 1/3] eMMC: add support for operations in RPMB partition Pierre Aubert
2014-05-23 8:50 ` Pantelis Antoniou
2014-04-24 8:30 ` [U-Boot] [PATCH V5 2/3] Add the function 'confirm_yesno' for interactive Pierre Aubert
2014-05-23 8:52 ` Pantelis Antoniou
2014-04-24 8:30 ` [U-Boot] [PATCH V5 3/3] eMMC: cmd_mmc.c adds the 'rpmb' sub-command for the 'mmc' command Pierre Aubert
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox