All of lore.kernel.org
 help / color / mirror / Atom feed
* [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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.