blob: 659bb60951442627a3aaf21547c54a0c6cf605b4 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2019 MediaTek Inc.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/moduleparam.h>
#include <linux/slab.h>
#include <linux/unistd.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/version.h>
#include <linux/spinlock.h>
#include <linux/semaphore.h>
#include <linux/delay.h>
#include <linux/kthread.h>
#include <linux/errno.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/mutex.h>
#include <linux/string.h>
#include <linux/random.h>
#include <linux/memory.h>
#include <linux/io.h>
#include <linux/proc_fs.h>
#include <crypto/hash.h>
#include <linux/rpmb.h>
#include <linux/scatterlist.h>
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/sd.h>
#include "queue.h"
#include <linux/of.h>
/* #define __RPMB_MTK_DEBUG_MSG */
/* #define __RPMB_MTK_DEBUG_HMAC_VERIFY */
/* TEE usage */
#ifdef CONFIG_TRUSTONIC_TEE_SUPPORT
#include "mobicore_driver_api.h"
#include "drrpmb_gp_Api.h"
#include "drrpmb_Api.h"
static struct mc_uuid_t rpmb_gp_uuid = RPMB_GP_UUID;
static struct mc_session_handle rpmb_gp_session = {0};
static u32 rpmb_gp_devid = MC_DEVICE_ID_DEFAULT;
static struct dciMessage_t *rpmb_gp_dci;
#endif
/*
* Dummy definition for MAX_RPMB_TRANSFER_BLK.
*
* For UFS RPMB driver, MAX_RPMB_TRANSFER_BLK will be always
* used however it will NOT be defined in projects w/o Security
* OS. Thus we add a dummy definition here to avoid build errors.
*
* For eMMC RPMB driver, MAX_RPMB_TRANSFER_BLK will be used
* only if RPMB_MULTI_BLOCK_ACCESS is defined. thus
* build error will not happen on projects w/o Security OS.
*
* NOTE: This dummy definition shall be located after
* #include "drrpmb_Api.h" and
* #include "rpmb-mtk.h"
* since MAX_RPMB_TRANSFER_BLK will be defined in those
* header files if security OS is enabled.
*/
#ifndef MAX_RPMB_TRANSFER_BLK
#define MAX_RPMB_TRANSFER_BLK (1U)
#endif
#define RPMB_NAME "rpmb"
//extern struct msdc_host *mtk_msdc_host[];
#define RPMB_IOCTL_PROGRAM_KEY 1
#define RPMB_IOCTL_WRITE_DATA 3
#define RPMB_IOCTL_READ_DATA 4
struct rpmb_ioc_param {
unsigned char *keybytes;
unsigned char *databytes;
unsigned int data_len;
unsigned short addr;
unsigned char *hmac;
unsigned int hmac_len;
};
#define RPMB_SZ_MAC 32U
#define RPMB_SZ_DATA 256U
#define RPMB_SZ_STUFF 196U
#define RPMB_SZ_NONCE 16U
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 int write_counter;
unsigned short address;
unsigned short block_count;
unsigned short result;
unsigned short request;
};
enum {
RPMB_SUCCESS = 0,
RPMB_HMAC_ERROR,
RPMB_RESULT_ERROR,
RPMB_WC_ERROR,
RPMB_NONCE_ERROR,
RPMB_ALLOC_ERROR,
RPMB_TRANSFER_NOT_COMPLETE,
};
#define RPMB_REQ 1 /* RPMB request mark */
#define RPMB_RESP (1 << 1)/* RPMB response mark */
#define RPMB_AVAILABLE_SECTORS 8 /* 4K page size */
#define RPMB_TYPE_BEG 510
#define RPMB_RES_BEG 508
#define RPMB_BLKS_BEG 506
#define RPMB_ADDR_BEG 504
#define RPMB_WCOUNTER_BEG 500
#define RPMB_NONCE_BEG 484
#define RPMB_DATA_BEG 228
#define RPMB_MAC_BEG 196
struct emmc_rpmb_req {
__u16 type; /* RPMB request type */
__u16 *result; /* response or request result */
__u16 blk_cnt; /* Number of blocks(half sector 256B) */
__u16 addr; /* data address */
__u32 *wc; /* write counter */
__u8 *nonce; /* Ramdom number */
__u8 *data; /* Buffer of the user data */
__u8 *mac; /* Message Authentication Code */
__u8 *data_frame;
};
#define DEFAULT_HANDLES_NUM (64)
#define MAX_OPEN_SESSIONS (0xffffffffU - 1)
/* Debug message event */
#define DBG_EVT_NONE (0) /* No event */
#define DBG_EVT_CMD (1U << 0)/* SEC CMD related event */
#define DBG_EVT_FUNC (1U << 1U)/* SEC function event */
#define DBG_EVT_INFO (1U << 2U)/* SEC information event */
#define DBG_EVT_WRN (1U << 30U) /* Warning event */
#define DBG_EVT_ERR (0x80000000U) /* Error event, 1 << 31 */
#ifdef __RPMB_MTK_DEBUG_MSG
#define DBG_EVT_DBG_INFO (DBG_EVT_ERR) /* Error event */
#else
#define DBG_EVT_DBG_INFO (1U << 2U) /* Information event */
#endif
#define DBG_EVT_ALL (0xffffffffU)
static u32 dbg_evt = DBG_EVT_ERR;
#define DBG_EVT_MASK (dbg_evt)
#define MSG(evt, fmt, args...) \
do {\
if (((DBG_EVT_##evt) & DBG_EVT_MASK) != 0U) { \
(void)(pr_notice("[%s] "fmt, RPMB_NAME, ##args)); \
} \
} while (false)
static struct task_struct *open_th;
static struct task_struct *rpmb_gp_Dci_th;
static struct cdev rpmb_cdev;
static int hmac_sha256(const char *keybytes, u32 klen, const char *str,
size_t len, u8 *hmac)
{
struct shash_desc *shash;
struct crypto_shash *hmacsha256 = crypto_alloc_shash("hmac(sha256)",
0, 0);
size_t size = 0;
int err = 0;
int nbytes = (int)len;
if (IS_ERR(hmacsha256))
return -1;
size = sizeof(struct shash_desc) + crypto_shash_descsize(hmacsha256);
shash = kmalloc(size, GFP_KERNEL);
if (shash == NULL) {
err = -1;
goto malloc_err;
}
shash->tfm = hmacsha256;
shash->flags = 0x0;
err = crypto_shash_setkey(hmacsha256, keybytes, klen);
if (err != 0) {
err = -1;
goto hash_err;
}
err = crypto_shash_init(shash);
if (err != 0) {
err = -1;
goto hash_err;
}
if (nbytes != crypto_shash_update(shash, str, (unsigned int)nbytes)) {
err = -1;
goto hash_err;
}
err = crypto_shash_final(shash, hmac);
hash_err:
kfree(shash);
malloc_err:
crypto_free_shash(hmacsha256);
return err;
}
#ifdef __RPMB_MTK_DEBUG_HMAC_VERIFY
unsigned char rpmb_key[32] = {
0x64, 0x76, 0xEE, 0xF0, 0xF1, 0x6B, 0x30, 0x47,
0xE9, 0x79, 0x31, 0x58, 0xF6, 0x42, 0xDA, 0x46,
0xF7, 0x3B, 0x53, 0xFD, 0xC5, 0xF8, 0x84, 0xCE,
0x03, 0x73, 0x15, 0xBC, 0x54, 0x47, 0xD4, 0x6A
};
static int rpmb_cal_hmac(struct rpmb_frame *frame, int blk_cnt,
u8 *key, u8 *key_mac)
{
int i;
u8 *buf, *buf_start;
buf = buf_start = kzalloc(284 * blk_cnt, 0);
for (i = 0; i < blk_cnt; i++) {
memcpy(buf, frame[i].data, 284);
buf += 284;
}
if (hmac_sha256(key, 32, buf_start, 284UL * blk_cnt, key_mac) != 0)
MSG(ERR, "hmac_sha256() return error!\n");
kfree(buf_start);
return 0;
}
#endif
static void rpmb_dump_frame(u8 *data_frame)
{
MSG(DBG_INFO, "mac, frame[196] = 0x%x\n", data_frame[196]);
MSG(DBG_INFO, "mac, frame[197] = 0x%x\n", data_frame[197]);
MSG(DBG_INFO, "mac, frame[198] = 0x%x\n", data_frame[198]);
MSG(DBG_INFO, "data,frame[228] = 0x%x\n", data_frame[228]);
MSG(DBG_INFO, "data,frame[229] = 0x%x\n", data_frame[229]);
MSG(DBG_INFO, "nonce, frame[484] = 0x%x\n", data_frame[484]);
MSG(DBG_INFO, "nonce, frame[485] = 0x%x\n", data_frame[485]);
MSG(DBG_INFO, "nonce, frame[486] = 0x%x\n", data_frame[486]);
MSG(DBG_INFO, "nonce, frame[487] = 0x%x\n", data_frame[487]);
MSG(DBG_INFO, "wc, frame[500] = 0x%x\n", data_frame[500]);
MSG(DBG_INFO, "wc, frame[501] = 0x%x\n", data_frame[501]);
MSG(DBG_INFO, "wc, frame[502] = 0x%x\n", data_frame[502]);
MSG(DBG_INFO, "wc, frame[503] = 0x%x\n", data_frame[503]);
MSG(DBG_INFO, "addr, frame[504] = 0x%x\n", data_frame[504]);
MSG(DBG_INFO, "addr, frame[505] = 0x%x\n", data_frame[505]);
MSG(DBG_INFO, "blkcnt,frame[506] = 0x%x\n", data_frame[506]);
MSG(DBG_INFO, "blkcnt,frame[507] = 0x%x\n", data_frame[507]);
MSG(DBG_INFO, "result, frame[508] = 0x%x\n", data_frame[508]);
MSG(DBG_INFO, "result, frame[509] = 0x%x\n", data_frame[509]);
MSG(DBG_INFO, "type, frame[510] = 0x%x\n", data_frame[510]);
MSG(DBG_INFO, "type, frame[511] = 0x%x\n", data_frame[511]);
}
static struct rpmb_frame *rpmb_alloc_frames(unsigned int cnt)
{
return kzalloc(sizeof(struct rpmb_frame) * cnt, 0);
}
static int rpmb_req_get_wc(u8 *keybytes, u32 *wc, u8 *frame)
{
struct rpmb_data rpmbdata;
struct rpmb_dev *mmc_rpmb;
u8 nonce[RPMB_SZ_NONCE] = {0};
u8 hmac[RPMB_SZ_MAC];
int ret, i;
MSG(INFO, "%s start!!!\n", __func__);
mmc_rpmb = rpmb_dev_get_by_type(RPMB_TYPE_EMMC);
do {
/*
* Initial frame buffers
*/
if (frame != NULL) {
/*
* Use external frame if possible.
* External frame shall have below field ready,
*
* nonce
* req_resp
*/
rpmbdata.icmd.frames = (struct rpmb_frame *)frame;
rpmbdata.ocmd.frames = (struct rpmb_frame *)frame;
} else {
rpmbdata.icmd.frames = rpmb_alloc_frames(1);
if (rpmbdata.icmd.frames == NULL)
return RPMB_ALLOC_ERROR;
rpmbdata.ocmd.frames = rpmb_alloc_frames(1);
if (rpmbdata.ocmd.frames == NULL)
return RPMB_ALLOC_ERROR;
}
/*
* Prepare frame contents.
*
* Input frame (in view of device) only needs nonce
*/
rpmbdata.req_type = RPMB_GET_WRITE_COUNTER;
rpmbdata.icmd.nframes = 1;
/* Fill-in essential field in self-prepared frame */
if (frame == NULL) {
get_random_bytes(nonce, (int)RPMB_SZ_NONCE);
rpmbdata.icmd.frames->req_resp =
cpu_to_be16(RPMB_GET_WRITE_COUNTER);
memcpy(rpmbdata.icmd.frames->nonce, nonce,
RPMB_SZ_NONCE);
}
/* Output frame (in view of device) */
rpmbdata.ocmd.nframes = 1;
ret = rpmb_cmd_req(mmc_rpmb, &rpmbdata);
if (ret != 0) {
MSG(ERR, "%s, rpmb_cmd_req IO error!!!(0x%x)\n",
__func__, ret);
break;
}
/* Verify HMAC only if key is available */
if (keybytes != NULL) {
if (strlen(keybytes) != 32UL) {
MSG(ERR, "%s, error rpmb key len = 0x%x\n",
__func__, (unsigned int)strlen(keybytes));
ret = RPMB_WC_ERROR;
break;
}
/*
* Authenticate response write counter frame.
*/
if (hmac_sha256(keybytes, 32,
rpmbdata.ocmd.frames->data,
284UL, hmac) != 0)
MSG(ERR, "hmac_sha256() return error!\n");
if (memcmp(hmac, rpmbdata.ocmd.frames->key_mac,
RPMB_SZ_MAC) != 0) {
MSG(ERR, "%s, hmac compare error!!!\n",
__func__);
ret = RPMB_HMAC_ERROR;
}
/*
* DEVICE ISSUE:
* We found some devices will return hmac vale with
* all zeros.
* For this kind of device, bypass hmac comparison.
*/
if (ret == RPMB_HMAC_ERROR) {
for (i = 0; i < 32; i++) {
if (rpmbdata.ocmd.frames->key_mac[i] !=
0U) {
MSG(ERR,
"%s, dev hmac not NULL!\n",
__func__);
break;
}
}
MSG(ERR,
"%s, device hmac has all zero, bypassed!\n",
__func__);
ret = RPMB_SUCCESS;
}
}
/*
* Verify nonce and result only in self-prepared frame
* External frame shall be verified by frame provider,
* for example, TEE.
*/
if (frame == NULL) {
if (memcmp(nonce, rpmbdata.ocmd.frames->nonce,
RPMB_SZ_NONCE) != 0) {
MSG(ERR, "%s, nonce compare error!!!\n",
__func__);
rpmb_dump_frame((u8 *)rpmbdata.ocmd.frames);
ret = RPMB_NONCE_ERROR;
break;
}
if (rpmbdata.ocmd.frames->result != 0U) {
MSG(ERR, "%s, result error!!! (0x%x)\n",
__func__,
cpu_to_be16(rpmbdata.ocmd.frames->result));
ret = RPMB_RESULT_ERROR;
break;
}
}
if (wc != NULL) {
*wc = cpu_to_be32(rpmbdata.ocmd.frames->write_counter);
MSG(DBG_INFO, "%s: wc = %d (0x%x)\n",
__func__, *wc, *wc);
}
} while (false);
MSG(DBG_INFO, "%s: end\n", __func__);
if (frame == NULL) {
kfree(rpmbdata.icmd.frames);
kfree(rpmbdata.ocmd.frames);
}
return ret;
}
static int rpmb_req_read_data(u8 *frame, u32 blk_cnt)
{
struct rpmb_data rpmbdata;
struct rpmb_dev *mmc_rpmb;
int ret;
mmc_rpmb = rpmb_dev_get_by_type(RPMB_TYPE_EMMC);
MSG(DBG_INFO, "%s: blk_cnt: %d\n", __func__, blk_cnt);
rpmbdata.req_type = RPMB_READ_DATA;
rpmbdata.icmd.nframes = 1;
rpmbdata.icmd.frames = (struct rpmb_frame *)frame;
/*
* We need to fill-in block_count by ourselves for UFS case.
* TEE does not fill-in this field because eMMC spec specifiy it as 0.
*/
rpmbdata.icmd.frames->block_count = cpu_to_be16((u16)blk_cnt);
rpmbdata.ocmd.nframes = blk_cnt;
rpmbdata.ocmd.frames = (struct rpmb_frame *)frame;
ret = rpmb_cmd_req(mmc_rpmb, &rpmbdata);
if (ret != 0)
MSG(ERR, "%s: rpmb_cmd_req IO error, ret %d (0x%x)\n",
__func__, ret, ret);
MSG(DBG_INFO, "%s: result 0x%x\n", __func__,
rpmbdata.ocmd.frames->result);
MSG(DBG_INFO, "%s: ret 0x%x\n", __func__, ret);
return ret;
}
static int rpmb_req_write_data(u8 *frame, u32 blk_cnt)
{
struct rpmb_data rpmbdata;
struct rpmb_dev *mmc_rpmb;
int ret;
#ifdef __RPMB_MTK_DEBUG_HMAC_VERIFY
u8 *key_mac;
#endif
mmc_rpmb = rpmb_dev_get_by_type(RPMB_TYPE_EMMC);
MSG(DBG_INFO, "%s: blk_cnt: %d\n", __func__, blk_cnt);
/*
* Alloc output frame to avoid overwriting input frame buffer
* provided by TEE
*/
rpmbdata.ocmd.frames = rpmb_alloc_frames(1);
if (rpmbdata.ocmd.frames == NULL)
return RPMB_ALLOC_ERROR;
rpmbdata.ocmd.nframes = 1;
rpmbdata.req_type = RPMB_WRITE_DATA;
rpmbdata.icmd.nframes = blk_cnt;
rpmbdata.icmd.frames = (struct rpmb_frame *)frame;
#ifdef __RPMB_MTK_DEBUG_HMAC_VERIFY
key_mac = kzalloc(32, 0);
rpmb_cal_hmac((struct rpmb_frame *)frame, blk_cnt, rpmb_key, key_mac);
if (memcmp(key_mac, ((struct rpmb_frame *)frame)[blk_cnt - 1].key_mac,
32)) {
MSG(ERR, "%s, Key Mac is NOT matched!\n", __func__);
kfree(key_mac);
ret = 1;
goto out;
} else
MSG(ERR, "%s, Key Mac check passed.\n", __func__);
kfree(key_mac);
#endif
ret = rpmb_cmd_req(mmc_rpmb, &rpmbdata);
if (ret != 0)
MSG(ERR, "%s: rpmb_cmd_req IO error, ret %d (0x%x)\n",
__func__, ret, ret);
/*
* Microtrust TEE will check write counter in the first frame,
* thus we copy response frame to the first frame.
*/
memcpy(frame, rpmbdata.ocmd.frames, 512);
MSG(DBG_INFO, "%s: result 0x%x\n", __func__,
rpmbdata.ocmd.frames->result);
kfree(rpmbdata.ocmd.frames);
MSG(DBG_INFO, "%s: ret 0x%x\n", __func__, ret);
#ifdef __RPMB_MTK_DEBUG_HMAC_VERIFY
out:
#endif
return ret;
}
static int rpmb_req_ioctl_write_data(struct rpmb_ioc_param *param)
{
struct rpmb_data rpmbdata;
struct rpmb_dev *mmc_rpmb;
u32 i, tran_size, left_size = param->data_len;
u32 wc = 0xFFFFFFFFU;
u16 iCnt, total_blkcnt, tran_blkcnt, left_blkcnt;
u16 blkaddr;
u8 hmac[RPMB_SZ_MAC];
u8 *dataBuf, *dataBuf_start, *data_for_hmac;
size_t size_for_hmac;
int ret = 0;
u8 user_param_data;
MSG(DBG_INFO, "%s start!!!\n", __func__);
ret = get_user(user_param_data, param->databytes);
if (ret != 0)
return -EFAULT;
ret = get_user(user_param_data, param->keybytes);
if (ret != 0)
return -EFAULT;
mmc_rpmb = rpmb_dev_get_by_type(RPMB_TYPE_EMMC);
i = 0;
tran_blkcnt = 0;
dataBuf = NULL;
dataBuf_start = NULL;
total_blkcnt = (u16)(((param->data_len % RPMB_SZ_DATA) != 0U) ?
(param->data_len / RPMB_SZ_DATA + 1U) :
(param->data_len / RPMB_SZ_DATA));
left_blkcnt = total_blkcnt;
/*
* For RPMB write data, the elements we need in the input data frame is
* 1. address.
* 2. write counter.
* 3. data.
* 4. block count.
* 5. MAC
*/
blkaddr = param->addr;
while (left_blkcnt > 0U) {
if (left_blkcnt >= MAX_RPMB_TRANSFER_BLK)
tran_blkcnt = MAX_RPMB_TRANSFER_BLK;
else
tran_blkcnt = left_blkcnt;
MSG(DBG_INFO, "%s, total_blkcnt = 0x%x, tran_blkcnt = 0x%x\n",
__func__, left_blkcnt, tran_blkcnt);
ret = rpmb_req_get_wc(param->keybytes, &wc, NULL);
if (ret != 0) {
MSG(ERR, "%s, rpmb_req_get_wc error!!!(0x%x)\n",
__func__, ret);
return ret;
}
/*
* Initial frame buffers
*/
rpmbdata.icmd.frames = rpmb_alloc_frames(tran_blkcnt);
if (rpmbdata.icmd.frames == NULL)
return RPMB_ALLOC_ERROR;
rpmbdata.ocmd.frames = rpmb_alloc_frames(1);
if (rpmbdata.ocmd.frames == NULL)
return RPMB_ALLOC_ERROR;
/*
* Initial data buffer for HMAC computation.
* Since HAMC computation tool which we use needs
* consecutive data buffer.
* Pre-alloced it.
*/
dataBuf_start = dataBuf = kzalloc(284UL * tran_blkcnt, 0);
/*
* Prepare frame contents
*/
rpmbdata.req_type = RPMB_WRITE_DATA;
/* Output frames (in view of device) */
rpmbdata.ocmd.nframes = 1;
/*
* All input frames (in view of device) need below stuff,
* 1. address.
* 2. write counter.
* 3. data.
* 4. block count.
* 5. MAC
*/
rpmbdata.icmd.nframes = tran_blkcnt;
/* size for hmac calculation: 512 - 228 = 284 */
size_for_hmac = sizeof(struct rpmb_frame) -
offsetof(struct rpmb_frame, data);
for (iCnt = 0; iCnt < tran_blkcnt; iCnt++) {
/*
* Prepare write data frame. need addr, wc, blkcnt,
* data and mac.
*/
rpmbdata.icmd.frames[iCnt].req_resp =
cpu_to_be16(RPMB_WRITE_DATA);
rpmbdata.icmd.frames[iCnt].addr = cpu_to_be16(blkaddr);
rpmbdata.icmd.frames[iCnt].block_count =
cpu_to_be16(tran_blkcnt);
rpmbdata.icmd.frames[iCnt].write_counter =
cpu_to_be32(wc);
if (left_size >= RPMB_SZ_DATA)
tran_size = RPMB_SZ_DATA;
else
tran_size = left_size;
memcpy(rpmbdata.icmd.frames[iCnt].data,
param->databytes +
i * MAX_RPMB_TRANSFER_BLK * RPMB_SZ_DATA +
(iCnt * RPMB_SZ_DATA),
tran_size);
left_size -= tran_size;
data_for_hmac = rpmbdata.icmd.frames[iCnt].data;
/* copy data part */
memcpy(dataBuf, data_for_hmac, RPMB_SZ_DATA);
/* copy left part */
memcpy(dataBuf + RPMB_SZ_DATA,
data_for_hmac + RPMB_SZ_DATA,
size_for_hmac - RPMB_SZ_DATA);
dataBuf = dataBuf + size_for_hmac;
}
iCnt--;
if (hmac_sha256(param->keybytes, 32, dataBuf_start,
284UL * tran_blkcnt,
rpmbdata.icmd.frames[iCnt].key_mac) != 0)
MSG(ERR, "hmac_sha256() return error!\n");
/*
* Send write data request.
*/
ret = rpmb_cmd_req(mmc_rpmb, &rpmbdata);
if (ret != 0) {
MSG(ERR, "%s, rpmb_cmd_req IO error!!!(0x%x)\n",
__func__, ret);
break;
}
/*
* Authenticate write result response.
* 1. authenticate hmac.
* 2. check result.
* 3. compare write counter is increamented.
*/
if (hmac_sha256(param->keybytes, 32,
rpmbdata.ocmd.frames->data,
284UL, hmac) != 0)
MSG(ERR, "hmac_sha256() return error!\n");
if (memcmp(hmac, rpmbdata.ocmd.frames->key_mac,
RPMB_SZ_MAC) != 0) {
MSG(ERR, "%s, hmac compare error!!!\n", __func__);
ret = RPMB_HMAC_ERROR;
break;
}
if (rpmbdata.ocmd.frames->result != 0U) {
MSG(ERR, "%s, result error!!! (0x%x)\n", __func__,
cpu_to_be16(rpmbdata.ocmd.frames->result));
ret = RPMB_RESULT_ERROR;
break;
}
if (cpu_to_be32(rpmbdata.ocmd.frames->write_counter) !=
wc + 1U) {
MSG(ERR, "%s, write counter error!!! (0x%x)\n",
__func__,
cpu_to_be32(rpmbdata.ocmd.frames->write_counter));
ret = RPMB_WC_ERROR;
break;
}
blkaddr += tran_blkcnt;
left_blkcnt -= tran_blkcnt;
i++;
kfree(rpmbdata.icmd.frames);
kfree(rpmbdata.ocmd.frames);
kfree(dataBuf_start);
};
if (ret != 0) {
kfree(rpmbdata.icmd.frames);
kfree(rpmbdata.ocmd.frames);
kfree(dataBuf_start);
}
if (left_blkcnt != 0U || left_size != 0U) {
MSG(ERR, "left_blkcnt or left_size is not empty!!!!!!\n");
return RPMB_TRANSFER_NOT_COMPLETE;
}
MSG(DBG_INFO, "%s end!!!\n", __func__);
return ret;
}
static int rpmb_req_ioctl_read_data(struct rpmb_ioc_param *param)
{
struct rpmb_data rpmbdata;
struct rpmb_dev *mmc_rpmb;
u32 i, tran_size, left_size = param->data_len;
u16 iCnt, total_blkcnt, tran_blkcnt, left_blkcnt;
u16 blkaddr;
u8 nonce[RPMB_SZ_NONCE] = {0};
u8 hmac[RPMB_SZ_MAC];
u8 *dataBuf, *dataBuf_start, *data_for_hmac;
size_t size_for_hmac;
int ret = 0;
u8 user_param_data;
MSG(DBG_INFO, "%s start!!!\n", __func__);
ret = get_user(user_param_data, param->databytes);
if (ret != 0)
return -EFAULT;
ret = get_user(user_param_data, param->keybytes);
if (ret != 0)
return -EFAULT;
mmc_rpmb = rpmb_dev_get_by_type(RPMB_TYPE_EMMC);
i = 0;
tran_blkcnt = 0;
dataBuf = NULL;
dataBuf_start = NULL;
total_blkcnt = (u16)(((param->data_len % RPMB_SZ_DATA) != 0U) ?
(param->data_len / RPMB_SZ_DATA + 1U) :
(param->data_len / RPMB_SZ_DATA));
left_blkcnt = total_blkcnt;
blkaddr = param->addr;
while (left_blkcnt > 0U) {
if (left_blkcnt >= MAX_RPMB_TRANSFER_BLK)
tran_blkcnt = MAX_RPMB_TRANSFER_BLK;
else
tran_blkcnt = left_blkcnt;
MSG(DBG_INFO, "%s, left_blkcnt = 0x%x, tran_blkcnt = 0x%x\n",
__func__, left_blkcnt, tran_blkcnt);
/*
* initial frame buffers
*/
rpmbdata.icmd.frames = rpmb_alloc_frames(1);
if (rpmbdata.icmd.frames == NULL)
return RPMB_ALLOC_ERROR;
rpmbdata.ocmd.frames = rpmb_alloc_frames(tran_blkcnt);
if (rpmbdata.ocmd.frames == NULL)
return RPMB_ALLOC_ERROR;
/*
* Initial data buffer for HMAC computation.
* Since HAMC computation tool which we use needs
* consecutive data buffer.
* Pre-alloced it.
*/
dataBuf_start = dataBuf = kzalloc(284UL * tran_blkcnt, 0);
get_random_bytes(nonce, (int)RPMB_SZ_NONCE);
/*
* Prepare request read data frame.
*
* Input frame (in view of device) only needs addr and nonce.
*/
rpmbdata.req_type = RPMB_READ_DATA;
rpmbdata.icmd.nframes = 1;
rpmbdata.icmd.frames->req_resp = cpu_to_be16(RPMB_READ_DATA);
rpmbdata.icmd.frames->addr = cpu_to_be16(blkaddr);
rpmbdata.icmd.frames->block_count = cpu_to_be16(tran_blkcnt);
memcpy(rpmbdata.icmd.frames->nonce, nonce, RPMB_SZ_NONCE);
/* output frames (in view of device) */
rpmbdata.ocmd.nframes = tran_blkcnt;
ret = rpmb_cmd_req(mmc_rpmb, &rpmbdata);
if (ret != 0) {
MSG(ERR, "%s, rpmb_cmd_req IO error!!!(0x%x)\n",
__func__, ret);
break;
}
/*
* Retrieve every data frame one by one.
*/
/* size for hmac calculation: 512 - 228 = 284 */
size_for_hmac = sizeof(struct rpmb_frame) -
offsetof(struct rpmb_frame, data);
for (iCnt = 0; iCnt < tran_blkcnt; iCnt++) {
if (left_size >= RPMB_SZ_DATA)
tran_size = RPMB_SZ_DATA;
else
tran_size = left_size;
/*
* dataBuf used for hmac calculation. we need to
* aggregate each block's data till to type field.
* each block has 284 bytes (size_for_hmac) need
* aggregation.
*/
data_for_hmac = rpmbdata.ocmd.frames[iCnt].data;
/* copy data part */
memcpy(dataBuf, data_for_hmac, RPMB_SZ_DATA);
/* copy left part */
memcpy(dataBuf + RPMB_SZ_DATA,
data_for_hmac + RPMB_SZ_DATA,
size_for_hmac - RPMB_SZ_DATA);
dataBuf = dataBuf + size_for_hmac;
/*
* Sorry, I shouldn't copy read data to user's
* buffer now, it should be later
* after checking no problem,
* but for convenience...you know...
*/
memcpy(param->databytes +
i * MAX_RPMB_TRANSFER_BLK * RPMB_SZ_DATA +
(iCnt * RPMB_SZ_DATA),
rpmbdata.ocmd.frames[iCnt].data,
tran_size);
left_size -= tran_size;
}
iCnt--;
/*
* Authenticate response read data frame.
*/
if (hmac_sha256(param->keybytes, 32,
dataBuf_start, size_for_hmac * tran_blkcnt,
hmac) != 0)
MSG(ERR, "hmac_sha256() return error!\n");
if (memcmp(hmac, rpmbdata.ocmd.frames[iCnt].key_mac,
RPMB_SZ_MAC) != 0) {
MSG(ERR, "%s, hmac compare error!!!\n", __func__);
ret = RPMB_HMAC_ERROR;
break;
}
if (memcmp(nonce, rpmbdata.ocmd.frames[iCnt].nonce,
RPMB_SZ_NONCE) != 0) {
MSG(ERR, "%s, nonce compare error!!!\n", __func__);
ret = RPMB_NONCE_ERROR;
break;
}
if (rpmbdata.ocmd.frames[iCnt].result != 0U) {
MSG(ERR, "%s, result error!!! (0x%x)\n",
__func__,
cpu_to_be16p(&rpmbdata.ocmd.frames[iCnt].result));
ret = RPMB_RESULT_ERROR;
break;
}
blkaddr += tran_blkcnt;
left_blkcnt -= tran_blkcnt;
i++;
kfree(rpmbdata.icmd.frames);
kfree(rpmbdata.ocmd.frames);
kfree(dataBuf_start);
};
if (ret != 0) {
kfree(rpmbdata.icmd.frames);
kfree(rpmbdata.ocmd.frames);
kfree(dataBuf_start);
}
if (left_blkcnt != 0U || left_size != 0U) {
MSG(ERR, "left_blkcnt or left_size is not empty!!!!!!\n");
return RPMB_TRANSFER_NOT_COMPLETE;
}
MSG(DBG_INFO, "%s end!!!\n", __func__);
return ret;
}
/*
* End of above.
*
******************************************************************************/
#ifdef CONFIG_TRUSTONIC_TEE_SUPPORT
static enum mc_result rpmb_gp_execute(u32 cmdId)
{
int ret;
switch (cmdId) {
case DCI_RPMB_CMD_READ_DATA:
MSG(DBG_INFO, "%s: DCI_RPMB_CMD_READ_DATA\n", __func__);
ret = rpmb_req_read_data(rpmb_gp_dci->request.frame,
rpmb_gp_dci->request.blks);
break;
case DCI_RPMB_CMD_GET_WCNT:
MSG(DBG_INFO, "%s: DCI_RPMB_CMD_GET_WCNT\n", __func__);
ret = rpmb_req_get_wc(NULL, NULL, rpmb_gp_dci->request.frame);
break;
case DCI_RPMB_CMD_WRITE_DATA:
MSG(DBG_INFO, "%s: DCI_RPMB_CMD_WRITE_DATA\n", __func__);
ret = rpmb_req_write_data(rpmb_gp_dci->request.frame,
rpmb_gp_dci->request.blks);
break;
default:
MSG(ERR, "%s: receive an unknown command id(%d).\n",
__func__, cmdId);
break;
}
return MC_DRV_OK;
}
static int rpmb_gp_listenDci(void *arg)
{
enum mc_result mc_ret;
u32 cmdId;
MSG(INFO, "%s: DCI listener.\n", __func__);
for (;;) {
MSG(INFO, "%s: Waiting for notification\n", __func__);
/* Wait for notification from SWd */
mc_ret = mc_wait_notification(&rpmb_gp_session,
MC_INFINITE_TIMEOUT);
if (mc_ret != MC_DRV_OK) {
MSG(ERR, "%s: mcWaitNotification failed, mc_ret=%d\n",
__func__, mc_ret);
break;
}
cmdId = rpmb_gp_dci->command.header.commandId;
MSG(INFO, "%s: wait notification done!! cmdId = %x\n",
__func__, cmdId);
/* Received exception. */
mc_ret = rpmb_gp_execute(cmdId);
/* Notify the STH*/
mc_ret = mc_notify(&rpmb_gp_session);
if (mc_ret != MC_DRV_OK) {
MSG(ERR, "%s: mcNotify returned: %d\n",
__func__, mc_ret);
break;
}
}
return 0;
}
static enum mc_result rpmb_gp_open_session(void)
{
int cnt = 0;
enum mc_result mc_ret;
MSG(INFO, "%s start\n", __func__);
do {
msleep(6000);
/* open device */
mc_ret = mc_open_device(rpmb_gp_devid);
if (mc_ret != MC_DRV_OK) {
MSG(ERR, "%s, mc_open_device failed: %d\n",
__func__, mc_ret);
cnt++;
continue;
}
MSG(INFO, "%s, mc_open_device success.\n", __func__);
/* allocating WSM for DCI */
mc_ret = mc_malloc_wsm(rpmb_gp_devid, 0,
(u32)sizeof(struct dciMessage_t),
(uint8_t **)&rpmb_gp_dci, 0);
if (mc_ret != MC_DRV_OK) {
MSG(ERR, "%s, mc_malloc_wsm failed: %d\n",
__func__, mc_ret);
mc_ret = mc_close_device(rpmb_gp_devid);
cnt++;
continue;
}
MSG(INFO, "%s, mc_malloc_wsm success.\n", __func__);
MSG(INFO, "uuid[0]=%d, uuid[1]=%d, uuid[2]=%d, uuid[3]=%d\n",
rpmb_gp_uuid.value[0],
rpmb_gp_uuid.value[1],
rpmb_gp_uuid.value[2],
rpmb_gp_uuid.value[3]
);
rpmb_gp_session.device_id = rpmb_gp_devid;
/* open session */
mc_ret = mc_open_session(&rpmb_gp_session,
&rpmb_gp_uuid,
(uint8_t *) rpmb_gp_dci,
(u32)sizeof(struct dciMessage_t));
if (mc_ret != MC_DRV_OK) {
MSG(ERR,
"%s, mc_open_session failed, result(%d), cnt(%d)\n",
__func__, mc_ret, cnt);
mc_ret = mc_free_wsm(rpmb_gp_devid,
(uint8_t *)rpmb_gp_dci);
MSG(ERR, "%s, free wsm result (%d)\n",
__func__, mc_ret);
mc_ret = mc_close_device(rpmb_gp_devid);
MSG(ERR, "%s, try free wsm and close device\n",
__func__);
cnt++;
continue;
}
MSG(INFO, "%s, mc_open_session success.\n", __func__);
/* create a thread for listening DCI signals */
rpmb_gp_Dci_th = kthread_run(rpmb_gp_listenDci,
NULL, "rpmb_gp_Dci");
if (IS_ERR(rpmb_gp_Dci_th))
MSG(ERR, "%s, init kthread_run failed!\n", __func__);
else
break;
} while (cnt < 60);
if (cnt >= 60)
MSG(ERR, "%s, open session failed!!!\n", __func__);
MSG(ERR, "%s end, mc_ret = %x\n", __func__, mc_ret);
return mc_ret;
}
static int rpmb_thread(void *context)
{
enum mc_result ret;
MSG(INFO, "%s start\n", __func__);
ret = rpmb_gp_open_session();
MSG(INFO, "%s rpmb_gp_open_session, ret = %x\n", __func__, ret);
return 0;
}
#endif
static int rpmb_open(struct inode *pinode, struct file *pfile)
{
return 0;
}
static long rpmb_ioctl(struct file *pfile, unsigned int cmd, unsigned long arg)
{
int err = 0;
unsigned long n;
struct rpmb_ioc_param param;
n = copy_from_user(&param, (void *)arg, sizeof(param));
if (n != 0UL) {
MSG(ERR, "%s, copy from user failed: %x\n", __func__, err);
return -EFAULT;
}
switch (cmd) {
case RPMB_IOCTL_PROGRAM_KEY:
MSG(DBG_INFO, "%s, cmd = RPMB_IOCTL_PROGRAM_KEY not support\n",
__func__);
break;
case RPMB_IOCTL_READ_DATA:
MSG(DBG_INFO, "%s, cmd = RPMB_IOCTL_READ_DATA!!!!\n", __func__);
err = rpmb_req_ioctl_read_data(&param);
if (err != 0) {
MSG(ERR, "%s, rpmb_req_ioctl_read_data() error!(%x)\n",
__func__, err);
break;
}
n = copy_to_user((void *)arg, &param, sizeof(param));
if (n != 0UL) {
MSG(ERR, "%s, copy to user user failed: %x\n",
__func__, err);
err = -EFAULT;
break;
}
break;
case RPMB_IOCTL_WRITE_DATA:
MSG(DBG_INFO, "%s, cmd = RPMB_IOCTL_WRITE_DATA!!!\n", __func__);
err = rpmb_req_ioctl_write_data(&param);
if (err != 0)
MSG(ERR, "%s, rpmb_req_ioctl_write_data() error!(%x)\n",
__func__, err);
break;
default:
MSG(ERR, "%s, wrong ioctl code (%d)!!!\n", __func__, cmd);
err = -ENOTTY;
break;
}
return err;
}
static int rpmb_close(struct inode *pinode, struct file *pfile)
{
int ret = 0;
MSG(INFO, "%s, !!!!!!!!!!!!\n", __func__);
return ret;
}
static const struct file_operations rpmb_fops = {
.owner = THIS_MODULE,
.open = rpmb_open,
.release = rpmb_close,
.unlocked_ioctl = rpmb_ioctl,
.write = NULL,
.read = NULL,
};
static int __init rpmb_init(void)
{
int alloc_ret;
int cdev_ret = -1;
unsigned int major;
dev_t dev;
struct device_node *np;
np = of_find_compatible_node(NULL, NULL, "mediatek,rpmb-kinibi");
if (np == NULL)
goto out_err;
MSG(INFO, "%s start\n", __func__);
alloc_ret = alloc_chrdev_region(&dev, 0, 1, RPMB_NAME);
if (alloc_ret != 0) {
MSG(ERR, "%s, init alloc_chrdev_region failed!\n", __func__);
goto error;
}
major = MAJOR(dev);
cdev_init(&rpmb_cdev, &rpmb_fops);
rpmb_cdev.owner = THIS_MODULE;
cdev_ret = cdev_add(&rpmb_cdev, MKDEV(major, 0U), 1);
if (cdev_ret != 0) {
MSG(ERR, "%s, init cdev_add failed!\n", __func__);
goto error;
}
#ifdef CONFIG_TRUSTONIC_TEE_SUPPORT
open_th = kthread_run(rpmb_thread, NULL, "rpmb_open");
if (IS_ERR(open_th))
MSG(ERR, "%s, init kthread_run failed!\n", __func__);
#endif
MSG(INFO, "%s end!!!!\n", __func__);
return 0;
error:
if (cdev_ret == 0)
cdev_del(&rpmb_cdev);
if (alloc_ret == 0)
unregister_chrdev_region(dev, 1);
out_err:
return -1;
}
late_initcall(rpmb_init);