xref: /openbmc/u-boot/drivers/mmc/rpmb.c (revision 4853ad3e)
183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
291fdabc6SPierre Aubert /*
391fdabc6SPierre Aubert  * Copyright 2014, Staubli Faverges
491fdabc6SPierre Aubert  * Pierre Aubert
591fdabc6SPierre Aubert  *
691fdabc6SPierre Aubert  * eMMC- Replay Protected Memory Block
791fdabc6SPierre Aubert  * According to JEDEC Standard No. 84-A441
891fdabc6SPierre Aubert  */
991fdabc6SPierre Aubert 
1091fdabc6SPierre Aubert #include <config.h>
1191fdabc6SPierre Aubert #include <common.h>
12cf92e05cSSimon Glass #include <memalign.h>
1391fdabc6SPierre Aubert #include <mmc.h>
142b9912e6SJeroen Hofstee #include <u-boot/sha256.h>
1591fdabc6SPierre Aubert #include "mmc_private.h"
1691fdabc6SPierre Aubert 
1791fdabc6SPierre Aubert /* Request codes */
1891fdabc6SPierre Aubert #define RPMB_REQ_KEY		1
1991fdabc6SPierre Aubert #define RPMB_REQ_WCOUNTER	2
2091fdabc6SPierre Aubert #define RPMB_REQ_WRITE_DATA	3
2191fdabc6SPierre Aubert #define RPMB_REQ_READ_DATA	4
2291fdabc6SPierre Aubert #define RPMB_REQ_STATUS		5
2391fdabc6SPierre Aubert 
2491fdabc6SPierre Aubert /* Response code */
2591fdabc6SPierre Aubert #define RPMB_RESP_KEY		0x0100
2691fdabc6SPierre Aubert #define RPMB_RESP_WCOUNTER	0x0200
2791fdabc6SPierre Aubert #define RPMB_RESP_WRITE_DATA	0x0300
2891fdabc6SPierre Aubert #define RPMB_RESP_READ_DATA	0x0400
2991fdabc6SPierre Aubert 
3091fdabc6SPierre Aubert /* Error codes */
3191fdabc6SPierre Aubert #define RPMB_OK			0
3291fdabc6SPierre Aubert #define RPMB_ERR_GENERAL	1
3391fdabc6SPierre Aubert #define RPMB_ERR_AUTH	2
3491fdabc6SPierre Aubert #define RPMB_ERR_COUNTER	3
3591fdabc6SPierre Aubert #define RPMB_ERR_ADDRESS	4
3691fdabc6SPierre Aubert #define RPMB_ERR_WRITE		5
3791fdabc6SPierre Aubert #define RPMB_ERR_READ		6
3891fdabc6SPierre Aubert #define RPMB_ERR_KEY		7
3991fdabc6SPierre Aubert #define RPMB_ERR_CNT_EXPIRED	0x80
4091fdabc6SPierre Aubert #define RPMB_ERR_MSK		0x7
4191fdabc6SPierre Aubert 
4291fdabc6SPierre Aubert /* Sizes of RPMB data frame */
4391fdabc6SPierre Aubert #define RPMB_SZ_STUFF		196
4491fdabc6SPierre Aubert #define RPMB_SZ_MAC		32
4591fdabc6SPierre Aubert #define RPMB_SZ_DATA		256
4691fdabc6SPierre Aubert #define RPMB_SZ_NONCE		16
4791fdabc6SPierre Aubert 
4891fdabc6SPierre Aubert #define SHA256_BLOCK_SIZE	64
4991fdabc6SPierre Aubert 
5091fdabc6SPierre Aubert /* Error messages */
5191fdabc6SPierre Aubert static const char * const rpmb_err_msg[] = {
5291fdabc6SPierre Aubert 	"",
5391fdabc6SPierre Aubert 	"General failure",
5491fdabc6SPierre Aubert 	"Authentication failure",
5591fdabc6SPierre Aubert 	"Counter failure",
5691fdabc6SPierre Aubert 	"Address failure",
5791fdabc6SPierre Aubert 	"Write failure",
5891fdabc6SPierre Aubert 	"Read failure",
5991fdabc6SPierre Aubert 	"Authentication key not yet programmed",
6091fdabc6SPierre Aubert };
6191fdabc6SPierre Aubert 
6291fdabc6SPierre Aubert 
6391fdabc6SPierre Aubert /* Structure of RPMB data frame. */
6491fdabc6SPierre Aubert struct s_rpmb {
6591fdabc6SPierre Aubert 	unsigned char stuff[RPMB_SZ_STUFF];
6691fdabc6SPierre Aubert 	unsigned char mac[RPMB_SZ_MAC];
6791fdabc6SPierre Aubert 	unsigned char data[RPMB_SZ_DATA];
6891fdabc6SPierre Aubert 	unsigned char nonce[RPMB_SZ_NONCE];
69343749c4SKever Yang 	unsigned int write_counter;
7091fdabc6SPierre Aubert 	unsigned short address;
7191fdabc6SPierre Aubert 	unsigned short block_count;
7291fdabc6SPierre Aubert 	unsigned short result;
7391fdabc6SPierre Aubert 	unsigned short request;
7491fdabc6SPierre Aubert };
7591fdabc6SPierre Aubert 
mmc_set_blockcount(struct mmc * mmc,unsigned int blockcount,bool is_rel_write)7691fdabc6SPierre Aubert static int mmc_set_blockcount(struct mmc *mmc, unsigned int blockcount,
7791fdabc6SPierre Aubert 			      bool is_rel_write)
7891fdabc6SPierre Aubert {
7991fdabc6SPierre Aubert 	struct mmc_cmd cmd = {0};
8091fdabc6SPierre Aubert 
8191fdabc6SPierre Aubert 	cmd.cmdidx = MMC_CMD_SET_BLOCK_COUNT;
8291fdabc6SPierre Aubert 	cmd.cmdarg = blockcount & 0x0000FFFF;
8391fdabc6SPierre Aubert 	if (is_rel_write)
8491fdabc6SPierre Aubert 		cmd.cmdarg |= 1 << 31;
8591fdabc6SPierre Aubert 	cmd.resp_type = MMC_RSP_R1;
8691fdabc6SPierre Aubert 
8791fdabc6SPierre Aubert 	return mmc_send_cmd(mmc, &cmd, NULL);
8891fdabc6SPierre Aubert }
mmc_rpmb_request(struct mmc * mmc,const struct s_rpmb * s,unsigned int count,bool is_rel_write)8991fdabc6SPierre Aubert static int mmc_rpmb_request(struct mmc *mmc, const struct s_rpmb *s,
9091fdabc6SPierre Aubert 			    unsigned int count, bool is_rel_write)
9191fdabc6SPierre Aubert {
9291fdabc6SPierre Aubert 	struct mmc_cmd cmd = {0};
9391fdabc6SPierre Aubert 	struct mmc_data data;
9491fdabc6SPierre Aubert 	int ret;
9591fdabc6SPierre Aubert 
9691fdabc6SPierre Aubert 	ret = mmc_set_blockcount(mmc, count, is_rel_write);
9791fdabc6SPierre Aubert 	if (ret) {
9891fdabc6SPierre Aubert #ifdef CONFIG_MMC_RPMB_TRACE
9991fdabc6SPierre Aubert 		printf("%s:mmc_set_blockcount-> %d\n", __func__, ret);
10091fdabc6SPierre Aubert #endif
10191fdabc6SPierre Aubert 		return 1;
10291fdabc6SPierre Aubert 	}
10391fdabc6SPierre Aubert 
10491fdabc6SPierre Aubert 	cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK;
10591fdabc6SPierre Aubert 	cmd.cmdarg = 0;
10691fdabc6SPierre Aubert 	cmd.resp_type = MMC_RSP_R1b;
10791fdabc6SPierre Aubert 
10891fdabc6SPierre Aubert 	data.src = (const char *)s;
10991fdabc6SPierre Aubert 	data.blocks = 1;
11091fdabc6SPierre Aubert 	data.blocksize = MMC_MAX_BLOCK_LEN;
11191fdabc6SPierre Aubert 	data.flags = MMC_DATA_WRITE;
11291fdabc6SPierre Aubert 
11391fdabc6SPierre Aubert 	ret = mmc_send_cmd(mmc, &cmd, &data);
11491fdabc6SPierre Aubert 	if (ret) {
11591fdabc6SPierre Aubert #ifdef CONFIG_MMC_RPMB_TRACE
11691fdabc6SPierre Aubert 		printf("%s:mmc_send_cmd-> %d\n", __func__, ret);
11791fdabc6SPierre Aubert #endif
11891fdabc6SPierre Aubert 		return 1;
11991fdabc6SPierre Aubert 	}
12091fdabc6SPierre Aubert 	return 0;
12191fdabc6SPierre Aubert }
mmc_rpmb_response(struct mmc * mmc,struct s_rpmb * s,unsigned short expected)12291fdabc6SPierre Aubert static int mmc_rpmb_response(struct mmc *mmc, struct s_rpmb *s,
12391fdabc6SPierre Aubert 			     unsigned short expected)
12491fdabc6SPierre Aubert {
12591fdabc6SPierre Aubert 	struct mmc_cmd cmd = {0};
12691fdabc6SPierre Aubert 	struct mmc_data data;
12791fdabc6SPierre Aubert 	int ret;
12891fdabc6SPierre Aubert 
12991fdabc6SPierre Aubert 	ret = mmc_set_blockcount(mmc, 1, false);
13091fdabc6SPierre Aubert 	if (ret) {
13191fdabc6SPierre Aubert #ifdef CONFIG_MMC_RPMB_TRACE
13291fdabc6SPierre Aubert 		printf("%s:mmc_set_blockcount-> %d\n", __func__, ret);
13391fdabc6SPierre Aubert #endif
13491fdabc6SPierre Aubert 		return -1;
13591fdabc6SPierre Aubert 	}
13691fdabc6SPierre Aubert 	cmd.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK;
13791fdabc6SPierre Aubert 	cmd.cmdarg = 0;
13891fdabc6SPierre Aubert 	cmd.resp_type = MMC_RSP_R1;
13991fdabc6SPierre Aubert 
14091fdabc6SPierre Aubert 	data.dest = (char *)s;
14191fdabc6SPierre Aubert 	data.blocks = 1;
14291fdabc6SPierre Aubert 	data.blocksize = MMC_MAX_BLOCK_LEN;
14391fdabc6SPierre Aubert 	data.flags = MMC_DATA_READ;
14491fdabc6SPierre Aubert 
14591fdabc6SPierre Aubert 	ret = mmc_send_cmd(mmc, &cmd, &data);
14691fdabc6SPierre Aubert 	if (ret) {
14791fdabc6SPierre Aubert #ifdef CONFIG_MMC_RPMB_TRACE
14891fdabc6SPierre Aubert 		printf("%s:mmc_send_cmd-> %d\n", __func__, ret);
14991fdabc6SPierre Aubert #endif
15091fdabc6SPierre Aubert 		return -1;
15191fdabc6SPierre Aubert 	}
15291fdabc6SPierre Aubert 	/* Check the response and the status */
15391fdabc6SPierre Aubert 	if (be16_to_cpu(s->request) != expected) {
15491fdabc6SPierre Aubert #ifdef CONFIG_MMC_RPMB_TRACE
15591fdabc6SPierre Aubert 		printf("%s:response= %x\n", __func__,
15691fdabc6SPierre Aubert 		       be16_to_cpu(s->request));
15791fdabc6SPierre Aubert #endif
15891fdabc6SPierre Aubert 		return -1;
15991fdabc6SPierre Aubert 	}
16091fdabc6SPierre Aubert 	ret = be16_to_cpu(s->result);
16191fdabc6SPierre Aubert 	if (ret) {
16291fdabc6SPierre Aubert 		printf("%s %s\n", rpmb_err_msg[ret & RPMB_ERR_MSK],
16391fdabc6SPierre Aubert 		       (ret & RPMB_ERR_CNT_EXPIRED) ?
16491fdabc6SPierre Aubert 		       "Write counter has expired" : "");
16591fdabc6SPierre Aubert 	}
16691fdabc6SPierre Aubert 
16791fdabc6SPierre Aubert 	/* Return the status of the command */
16891fdabc6SPierre Aubert 	return ret;
16991fdabc6SPierre Aubert }
mmc_rpmb_status(struct mmc * mmc,unsigned short expected)17091fdabc6SPierre Aubert static int mmc_rpmb_status(struct mmc *mmc, unsigned short expected)
17191fdabc6SPierre Aubert {
17291fdabc6SPierre Aubert 	ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
17391fdabc6SPierre Aubert 
17491fdabc6SPierre Aubert 	memset(rpmb_frame, 0, sizeof(struct s_rpmb));
17591fdabc6SPierre Aubert 	rpmb_frame->request = cpu_to_be16(RPMB_REQ_STATUS);
17691fdabc6SPierre Aubert 	if (mmc_rpmb_request(mmc, rpmb_frame, 1, false))
17791fdabc6SPierre Aubert 		return -1;
17891fdabc6SPierre Aubert 
17991fdabc6SPierre Aubert 	/* Read the result */
18091fdabc6SPierre Aubert 	return mmc_rpmb_response(mmc, rpmb_frame, expected);
18191fdabc6SPierre Aubert }
rpmb_hmac(unsigned char * key,unsigned char * buff,int len,unsigned char * output)18291fdabc6SPierre Aubert static void rpmb_hmac(unsigned char *key, unsigned char *buff, int len,
18391fdabc6SPierre Aubert 		      unsigned char *output)
18491fdabc6SPierre Aubert {
18591fdabc6SPierre Aubert 	sha256_context ctx;
18691fdabc6SPierre Aubert 	int i;
18791fdabc6SPierre Aubert 	unsigned char k_ipad[SHA256_BLOCK_SIZE];
18891fdabc6SPierre Aubert 	unsigned char k_opad[SHA256_BLOCK_SIZE];
18991fdabc6SPierre Aubert 
19091fdabc6SPierre Aubert 	sha256_starts(&ctx);
19191fdabc6SPierre Aubert 
19291fdabc6SPierre Aubert 	/* According to RFC 4634, the HMAC transform looks like:
19391fdabc6SPierre Aubert 	   SHA(K XOR opad, SHA(K XOR ipad, text))
19491fdabc6SPierre Aubert 
19591fdabc6SPierre Aubert 	   where K is an n byte key.
19691fdabc6SPierre Aubert 	   ipad is the byte 0x36 repeated blocksize times
19791fdabc6SPierre Aubert 	   opad is the byte 0x5c repeated blocksize times
19891fdabc6SPierre Aubert 	   and text is the data being protected.
19991fdabc6SPierre Aubert 	*/
20091fdabc6SPierre Aubert 
20191fdabc6SPierre Aubert 	for (i = 0; i < RPMB_SZ_MAC; i++) {
20291fdabc6SPierre Aubert 		k_ipad[i] = key[i] ^ 0x36;
20391fdabc6SPierre Aubert 		k_opad[i] = key[i] ^ 0x5c;
20491fdabc6SPierre Aubert 	}
20591fdabc6SPierre Aubert 	/* remaining pad bytes are '\0' XOR'd with ipad and opad values */
20691fdabc6SPierre Aubert 	for ( ; i < SHA256_BLOCK_SIZE; i++) {
20791fdabc6SPierre Aubert 		k_ipad[i] = 0x36;
20891fdabc6SPierre Aubert 		k_opad[i] = 0x5c;
20991fdabc6SPierre Aubert 	}
21091fdabc6SPierre Aubert 	sha256_update(&ctx, k_ipad, SHA256_BLOCK_SIZE);
21191fdabc6SPierre Aubert 	sha256_update(&ctx, buff, len);
21291fdabc6SPierre Aubert 	sha256_finish(&ctx, output);
21391fdabc6SPierre Aubert 
21491fdabc6SPierre Aubert 	/* Init context for second pass */
21591fdabc6SPierre Aubert 	sha256_starts(&ctx);
21691fdabc6SPierre Aubert 
21791fdabc6SPierre Aubert 	/* start with outer pad */
21891fdabc6SPierre Aubert 	sha256_update(&ctx, k_opad, SHA256_BLOCK_SIZE);
21991fdabc6SPierre Aubert 
22091fdabc6SPierre Aubert 	/* then results of 1st hash */
22191fdabc6SPierre Aubert 	sha256_update(&ctx, output, RPMB_SZ_MAC);
22291fdabc6SPierre Aubert 
22391fdabc6SPierre Aubert 	/* finish up 2nd pass */
22491fdabc6SPierre Aubert 	sha256_finish(&ctx, output);
22591fdabc6SPierre Aubert }
mmc_rpmb_get_counter(struct mmc * mmc,unsigned long * pcounter)22691fdabc6SPierre Aubert int mmc_rpmb_get_counter(struct mmc *mmc, unsigned long *pcounter)
22791fdabc6SPierre Aubert {
22891fdabc6SPierre Aubert 	int ret;
22991fdabc6SPierre Aubert 	ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
23091fdabc6SPierre Aubert 
23191fdabc6SPierre Aubert 	/* Fill the request */
23291fdabc6SPierre Aubert 	memset(rpmb_frame, 0, sizeof(struct s_rpmb));
23391fdabc6SPierre Aubert 	rpmb_frame->request = cpu_to_be16(RPMB_REQ_WCOUNTER);
23491fdabc6SPierre Aubert 	if (mmc_rpmb_request(mmc, rpmb_frame, 1, false))
23591fdabc6SPierre Aubert 		return -1;
23691fdabc6SPierre Aubert 
23791fdabc6SPierre Aubert 	/* Read the result */
23891fdabc6SPierre Aubert 	ret = mmc_rpmb_response(mmc, rpmb_frame, RPMB_RESP_WCOUNTER);
23991fdabc6SPierre Aubert 	if (ret)
24091fdabc6SPierre Aubert 		return ret;
24191fdabc6SPierre Aubert 
24291fdabc6SPierre Aubert 	*pcounter = be32_to_cpu(rpmb_frame->write_counter);
24391fdabc6SPierre Aubert 	return 0;
24491fdabc6SPierre Aubert }
mmc_rpmb_set_key(struct mmc * mmc,void * key)24591fdabc6SPierre Aubert int mmc_rpmb_set_key(struct mmc *mmc, void *key)
24691fdabc6SPierre Aubert {
24791fdabc6SPierre Aubert 	ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
24891fdabc6SPierre Aubert 	/* Fill the request */
24991fdabc6SPierre Aubert 	memset(rpmb_frame, 0, sizeof(struct s_rpmb));
25091fdabc6SPierre Aubert 	rpmb_frame->request = cpu_to_be16(RPMB_REQ_KEY);
25191fdabc6SPierre Aubert 	memcpy(rpmb_frame->mac, key, RPMB_SZ_MAC);
25291fdabc6SPierre Aubert 
25391fdabc6SPierre Aubert 	if (mmc_rpmb_request(mmc, rpmb_frame, 1, true))
25491fdabc6SPierre Aubert 		return -1;
25591fdabc6SPierre Aubert 
25691fdabc6SPierre Aubert 	/* read the operation status */
25791fdabc6SPierre Aubert 	return mmc_rpmb_status(mmc, RPMB_RESP_KEY);
25891fdabc6SPierre Aubert }
mmc_rpmb_read(struct mmc * mmc,void * addr,unsigned short blk,unsigned short cnt,unsigned char * key)25991fdabc6SPierre Aubert int mmc_rpmb_read(struct mmc *mmc, void *addr, unsigned short blk,
26091fdabc6SPierre Aubert 		  unsigned short cnt, unsigned char *key)
26191fdabc6SPierre Aubert {
26291fdabc6SPierre Aubert 	ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
26391fdabc6SPierre Aubert 	int i;
26491fdabc6SPierre Aubert 
26591fdabc6SPierre Aubert 	for (i = 0; i < cnt; i++) {
26691fdabc6SPierre Aubert 		/* Fill the request */
26791fdabc6SPierre Aubert 		memset(rpmb_frame, 0, sizeof(struct s_rpmb));
26891fdabc6SPierre Aubert 		rpmb_frame->address = cpu_to_be16(blk + i);
26991fdabc6SPierre Aubert 		rpmb_frame->request = cpu_to_be16(RPMB_REQ_READ_DATA);
27091fdabc6SPierre Aubert 		if (mmc_rpmb_request(mmc, rpmb_frame, 1, false))
27191fdabc6SPierre Aubert 			break;
27291fdabc6SPierre Aubert 
27391fdabc6SPierre Aubert 		/* Read the result */
27491fdabc6SPierre Aubert 		if (mmc_rpmb_response(mmc, rpmb_frame, RPMB_RESP_READ_DATA))
27591fdabc6SPierre Aubert 			break;
27691fdabc6SPierre Aubert 
27791fdabc6SPierre Aubert 		/* Check the HMAC if key is provided */
27891fdabc6SPierre Aubert 		if (key) {
27991fdabc6SPierre Aubert 			unsigned char ret_hmac[RPMB_SZ_MAC];
28091fdabc6SPierre Aubert 
28191fdabc6SPierre Aubert 			rpmb_hmac(key, rpmb_frame->data, 284, ret_hmac);
28291fdabc6SPierre Aubert 			if (memcmp(ret_hmac, rpmb_frame->mac, RPMB_SZ_MAC)) {
28391fdabc6SPierre Aubert 				printf("MAC error on block #%d\n", i);
28491fdabc6SPierre Aubert 				break;
28591fdabc6SPierre Aubert 			}
28691fdabc6SPierre Aubert 		}
28791fdabc6SPierre Aubert 		/* Copy data */
28891fdabc6SPierre Aubert 		memcpy(addr + i * RPMB_SZ_DATA, rpmb_frame->data, RPMB_SZ_DATA);
28991fdabc6SPierre Aubert 	}
29091fdabc6SPierre Aubert 	return i;
29191fdabc6SPierre Aubert }
mmc_rpmb_write(struct mmc * mmc,void * addr,unsigned short blk,unsigned short cnt,unsigned char * key)29291fdabc6SPierre Aubert int mmc_rpmb_write(struct mmc *mmc, void *addr, unsigned short blk,
29391fdabc6SPierre Aubert 		  unsigned short cnt, unsigned char *key)
29491fdabc6SPierre Aubert {
29591fdabc6SPierre Aubert 	ALLOC_CACHE_ALIGN_BUFFER(struct s_rpmb, rpmb_frame, 1);
29691fdabc6SPierre Aubert 	unsigned long wcount;
29791fdabc6SPierre Aubert 	int i;
29891fdabc6SPierre Aubert 
29991fdabc6SPierre Aubert 	for (i = 0; i < cnt; i++) {
30091fdabc6SPierre Aubert 		if (mmc_rpmb_get_counter(mmc, &wcount)) {
30191fdabc6SPierre Aubert 			printf("Cannot read RPMB write counter\n");
30291fdabc6SPierre Aubert 			break;
30391fdabc6SPierre Aubert 		}
30491fdabc6SPierre Aubert 
30591fdabc6SPierre Aubert 		/* Fill the request */
30691fdabc6SPierre Aubert 		memset(rpmb_frame, 0, sizeof(struct s_rpmb));
30791fdabc6SPierre Aubert 		memcpy(rpmb_frame->data, addr + i * RPMB_SZ_DATA, RPMB_SZ_DATA);
30891fdabc6SPierre Aubert 		rpmb_frame->address = cpu_to_be16(blk + i);
30991fdabc6SPierre Aubert 		rpmb_frame->block_count = cpu_to_be16(1);
31091fdabc6SPierre Aubert 		rpmb_frame->write_counter = cpu_to_be32(wcount);
31191fdabc6SPierre Aubert 		rpmb_frame->request = cpu_to_be16(RPMB_REQ_WRITE_DATA);
31291fdabc6SPierre Aubert 		/* Computes HMAC */
31391fdabc6SPierre Aubert 		rpmb_hmac(key, rpmb_frame->data, 284, rpmb_frame->mac);
31491fdabc6SPierre Aubert 
31591fdabc6SPierre Aubert 		if (mmc_rpmb_request(mmc, rpmb_frame, 1, true))
31691fdabc6SPierre Aubert 			break;
31791fdabc6SPierre Aubert 
31891fdabc6SPierre Aubert 		/* Get status */
31991fdabc6SPierre Aubert 		if (mmc_rpmb_status(mmc, RPMB_RESP_WRITE_DATA))
32091fdabc6SPierre Aubert 			break;
32191fdabc6SPierre Aubert 	}
32291fdabc6SPierre Aubert 	return i;
32391fdabc6SPierre Aubert }
324*4853ad3eSJens Wiklander 
send_write_mult_block(struct mmc * mmc,const struct s_rpmb * frm,unsigned short cnt)325*4853ad3eSJens Wiklander static int send_write_mult_block(struct mmc *mmc, const struct s_rpmb *frm,
326*4853ad3eSJens Wiklander 				 unsigned short cnt)
327*4853ad3eSJens Wiklander {
328*4853ad3eSJens Wiklander 	struct mmc_cmd cmd = {
329*4853ad3eSJens Wiklander 		.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK,
330*4853ad3eSJens Wiklander 		.resp_type = MMC_RSP_R1b,
331*4853ad3eSJens Wiklander 	};
332*4853ad3eSJens Wiklander 	struct mmc_data data = {
333*4853ad3eSJens Wiklander 		.src = (const void *)frm,
334*4853ad3eSJens Wiklander 		.blocks = cnt,
335*4853ad3eSJens Wiklander 		.blocksize = sizeof(*frm),
336*4853ad3eSJens Wiklander 		.flags = MMC_DATA_WRITE,
337*4853ad3eSJens Wiklander 	};
338*4853ad3eSJens Wiklander 
339*4853ad3eSJens Wiklander 	return mmc_send_cmd(mmc, &cmd, &data);
340*4853ad3eSJens Wiklander }
341*4853ad3eSJens Wiklander 
send_read_mult_block(struct mmc * mmc,struct s_rpmb * frm,unsigned short cnt)342*4853ad3eSJens Wiklander static int send_read_mult_block(struct mmc *mmc, struct s_rpmb *frm,
343*4853ad3eSJens Wiklander 				unsigned short cnt)
344*4853ad3eSJens Wiklander {
345*4853ad3eSJens Wiklander 	struct mmc_cmd cmd = {
346*4853ad3eSJens Wiklander 		.cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK,
347*4853ad3eSJens Wiklander 		.resp_type = MMC_RSP_R1,
348*4853ad3eSJens Wiklander 	};
349*4853ad3eSJens Wiklander 	struct mmc_data data = {
350*4853ad3eSJens Wiklander 		.dest = (void *)frm,
351*4853ad3eSJens Wiklander 		.blocks = cnt,
352*4853ad3eSJens Wiklander 		.blocksize = sizeof(*frm),
353*4853ad3eSJens Wiklander 		.flags = MMC_DATA_READ,
354*4853ad3eSJens Wiklander 	};
355*4853ad3eSJens Wiklander 
356*4853ad3eSJens Wiklander 	return mmc_send_cmd(mmc, &cmd, &data);
357*4853ad3eSJens Wiklander }
358*4853ad3eSJens Wiklander 
rpmb_route_write_req(struct mmc * mmc,struct s_rpmb * req,unsigned short req_cnt,struct s_rpmb * rsp,unsigned short rsp_cnt)359*4853ad3eSJens Wiklander static int rpmb_route_write_req(struct mmc *mmc, struct s_rpmb *req,
360*4853ad3eSJens Wiklander 				unsigned short req_cnt, struct s_rpmb *rsp,
361*4853ad3eSJens Wiklander 				unsigned short rsp_cnt)
362*4853ad3eSJens Wiklander {
363*4853ad3eSJens Wiklander 	int ret;
364*4853ad3eSJens Wiklander 
365*4853ad3eSJens Wiklander 	/*
366*4853ad3eSJens Wiklander 	 * Send the write request.
367*4853ad3eSJens Wiklander 	 */
368*4853ad3eSJens Wiklander 	ret = mmc_set_blockcount(mmc, req_cnt, true);
369*4853ad3eSJens Wiklander 	if (ret)
370*4853ad3eSJens Wiklander 		return ret;
371*4853ad3eSJens Wiklander 
372*4853ad3eSJens Wiklander 	ret = send_write_mult_block(mmc, req, req_cnt);
373*4853ad3eSJens Wiklander 	if (ret)
374*4853ad3eSJens Wiklander 		return ret;
375*4853ad3eSJens Wiklander 
376*4853ad3eSJens Wiklander 	/*
377*4853ad3eSJens Wiklander 	 * Read the result of the request.
378*4853ad3eSJens Wiklander 	 */
379*4853ad3eSJens Wiklander 	ret = mmc_set_blockcount(mmc, 1, false);
380*4853ad3eSJens Wiklander 	if (ret)
381*4853ad3eSJens Wiklander 		return ret;
382*4853ad3eSJens Wiklander 
383*4853ad3eSJens Wiklander 	memset(rsp, 0, sizeof(*rsp));
384*4853ad3eSJens Wiklander 	rsp->request = cpu_to_be16(RPMB_REQ_STATUS);
385*4853ad3eSJens Wiklander 	ret = send_write_mult_block(mmc, rsp, 1);
386*4853ad3eSJens Wiklander 	if (ret)
387*4853ad3eSJens Wiklander 		return ret;
388*4853ad3eSJens Wiklander 
389*4853ad3eSJens Wiklander 	ret = mmc_set_blockcount(mmc, 1, false);
390*4853ad3eSJens Wiklander 	if (ret)
391*4853ad3eSJens Wiklander 		return ret;
392*4853ad3eSJens Wiklander 
393*4853ad3eSJens Wiklander 	return send_read_mult_block(mmc, rsp, 1);
394*4853ad3eSJens Wiklander }
395*4853ad3eSJens Wiklander 
rpmb_route_read_req(struct mmc * mmc,struct s_rpmb * req,unsigned short req_cnt,struct s_rpmb * rsp,unsigned short rsp_cnt)396*4853ad3eSJens Wiklander static int rpmb_route_read_req(struct mmc *mmc, struct s_rpmb *req,
397*4853ad3eSJens Wiklander 			       unsigned short req_cnt, struct s_rpmb *rsp,
398*4853ad3eSJens Wiklander 			       unsigned short rsp_cnt)
399*4853ad3eSJens Wiklander {
400*4853ad3eSJens Wiklander 	int ret;
401*4853ad3eSJens Wiklander 
402*4853ad3eSJens Wiklander 	/*
403*4853ad3eSJens Wiklander 	 * Send the read request.
404*4853ad3eSJens Wiklander 	 */
405*4853ad3eSJens Wiklander 	ret = mmc_set_blockcount(mmc, 1, false);
406*4853ad3eSJens Wiklander 	if (ret)
407*4853ad3eSJens Wiklander 		return ret;
408*4853ad3eSJens Wiklander 
409*4853ad3eSJens Wiklander 	ret = send_write_mult_block(mmc, req, 1);
410*4853ad3eSJens Wiklander 	if (ret)
411*4853ad3eSJens Wiklander 		return ret;
412*4853ad3eSJens Wiklander 
413*4853ad3eSJens Wiklander 	/*
414*4853ad3eSJens Wiklander 	 * Read the result of the request.
415*4853ad3eSJens Wiklander 	 */
416*4853ad3eSJens Wiklander 
417*4853ad3eSJens Wiklander 	ret = mmc_set_blockcount(mmc, rsp_cnt, false);
418*4853ad3eSJens Wiklander 	if (ret)
419*4853ad3eSJens Wiklander 		return ret;
420*4853ad3eSJens Wiklander 
421*4853ad3eSJens Wiklander 	return send_read_mult_block(mmc, rsp, rsp_cnt);
422*4853ad3eSJens Wiklander }
423*4853ad3eSJens Wiklander 
rpmb_route_frames(struct mmc * mmc,struct s_rpmb * req,unsigned short req_cnt,struct s_rpmb * rsp,unsigned short rsp_cnt)424*4853ad3eSJens Wiklander static int rpmb_route_frames(struct mmc *mmc, struct s_rpmb *req,
425*4853ad3eSJens Wiklander 			     unsigned short req_cnt, struct s_rpmb *rsp,
426*4853ad3eSJens Wiklander 			     unsigned short rsp_cnt)
427*4853ad3eSJens Wiklander {
428*4853ad3eSJens Wiklander 	unsigned short n;
429*4853ad3eSJens Wiklander 
430*4853ad3eSJens Wiklander 	/*
431*4853ad3eSJens Wiklander 	 * If multiple request frames are provided, make sure that all are
432*4853ad3eSJens Wiklander 	 * of the same type.
433*4853ad3eSJens Wiklander 	 */
434*4853ad3eSJens Wiklander 	for (n = 1; n < req_cnt; n++)
435*4853ad3eSJens Wiklander 		if (req[n].request != req->request)
436*4853ad3eSJens Wiklander 			return -EINVAL;
437*4853ad3eSJens Wiklander 
438*4853ad3eSJens Wiklander 	switch (be16_to_cpu(req->request)) {
439*4853ad3eSJens Wiklander 	case RPMB_REQ_KEY:
440*4853ad3eSJens Wiklander 		if (req_cnt != 1 || rsp_cnt != 1)
441*4853ad3eSJens Wiklander 			return -EINVAL;
442*4853ad3eSJens Wiklander 		return rpmb_route_write_req(mmc, req, req_cnt, rsp, rsp_cnt);
443*4853ad3eSJens Wiklander 
444*4853ad3eSJens Wiklander 	case RPMB_REQ_WRITE_DATA:
445*4853ad3eSJens Wiklander 		if (!req_cnt || rsp_cnt != 1)
446*4853ad3eSJens Wiklander 			return -EINVAL;
447*4853ad3eSJens Wiklander 		return rpmb_route_write_req(mmc, req, req_cnt, rsp, rsp_cnt);
448*4853ad3eSJens Wiklander 
449*4853ad3eSJens Wiklander 	case RPMB_REQ_WCOUNTER:
450*4853ad3eSJens Wiklander 		if (req_cnt != 1 || rsp_cnt != 1)
451*4853ad3eSJens Wiklander 			return -EINVAL;
452*4853ad3eSJens Wiklander 		return rpmb_route_read_req(mmc, req, req_cnt, rsp, rsp_cnt);
453*4853ad3eSJens Wiklander 
454*4853ad3eSJens Wiklander 	case RPMB_REQ_READ_DATA:
455*4853ad3eSJens Wiklander 		if (req_cnt != 1 || !req_cnt)
456*4853ad3eSJens Wiklander 			return -EINVAL;
457*4853ad3eSJens Wiklander 		return rpmb_route_read_req(mmc, req, req_cnt, rsp, rsp_cnt);
458*4853ad3eSJens Wiklander 
459*4853ad3eSJens Wiklander 	default:
460*4853ad3eSJens Wiklander 		debug("Unsupported message type: %d\n",
461*4853ad3eSJens Wiklander 		      be16_to_cpu(req->request));
462*4853ad3eSJens Wiklander 		return -EINVAL;
463*4853ad3eSJens Wiklander 	}
464*4853ad3eSJens Wiklander }
465*4853ad3eSJens Wiklander 
mmc_rpmb_route_frames(struct mmc * mmc,void * req,unsigned long reqlen,void * rsp,unsigned long rsplen)466*4853ad3eSJens Wiklander int mmc_rpmb_route_frames(struct mmc *mmc, void *req, unsigned long reqlen,
467*4853ad3eSJens Wiklander 			  void *rsp, unsigned long rsplen)
468*4853ad3eSJens Wiklander {
469*4853ad3eSJens Wiklander 	/*
470*4853ad3eSJens Wiklander 	 * Whoever crafted the data supplied to this function knows how to
471*4853ad3eSJens Wiklander 	 * format the PRMB frames and which response is expected. If
472*4853ad3eSJens Wiklander 	 * there's some unexpected mismatch it's more helpful to report an
473*4853ad3eSJens Wiklander 	 * error immediately than trying to guess what was the intention
474*4853ad3eSJens Wiklander 	 * and possibly just delay an eventual error which will be harder
475*4853ad3eSJens Wiklander 	 * to track down.
476*4853ad3eSJens Wiklander 	 */
477*4853ad3eSJens Wiklander 
478*4853ad3eSJens Wiklander 	if (reqlen % sizeof(struct s_rpmb) || rsplen % sizeof(struct s_rpmb))
479*4853ad3eSJens Wiklander 		return -EINVAL;
480*4853ad3eSJens Wiklander 
481*4853ad3eSJens Wiklander 	return rpmb_route_frames(mmc, req, reqlen / sizeof(struct s_rpmb),
482*4853ad3eSJens Wiklander 				 rsp, rsplen / sizeof(struct s_rpmb));
483*4853ad3eSJens Wiklander }
484