mirror of
https://github.com/physwizz/a155-U-u1.git
synced 2024-11-19 13:27:49 +00:00
3495 lines
78 KiB
C
3495 lines
78 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* Copyright (c) 2021 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 <linux/of.h>
|
|
#include <net/sock.h>
|
|
#include <net/net_namespace.h>
|
|
#include <linux/netlink.h>
|
|
|
|
#if IS_ENABLED(CONFIG_SCSI_UFS_MEDIATEK)
|
|
#include "ufs-mediatek.h"
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_MMC_MTK_PRO)
|
|
#include <uapi/linux/mmc/ioctl.h>
|
|
#include "core.h"
|
|
#include "mmc_ops.h"
|
|
#include "mtk-mmc.h"
|
|
#include "queue.h"
|
|
#include "rpmb-mtk.h"
|
|
|
|
static struct mmc_host *mtk_mmc_host[] = {NULL};
|
|
#endif
|
|
|
|
/* #define __RPMB_MTK_DEBUG_MSG */
|
|
/* #define __RPMB_MTK_DEBUG_HMAC_VERIFY */
|
|
/* #define __RPMB_KERNEL_NL_SUPPORT */
|
|
#define __RPMB_IOCTL_SUPPORT
|
|
|
|
/* TEE usage */
|
|
#if IS_ENABLED(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
|
|
|
|
#if IS_ENABLED(CONFIG_TEEGRIS_TEE_SUPPORT)
|
|
#include "tee_client_api.h"
|
|
#include "tzdev.h"
|
|
#include "drrpmb_gp_Api.h"
|
|
#include "drrpmb_Api.h"
|
|
#include <core/iwsock.h>
|
|
|
|
static struct dciMessage_t *rpmb_gp_dci;
|
|
static DEFINE_MUTEX(rpmb_mutex);
|
|
|
|
#define RPMB_SOCKET_NAME "rpmb_socket"
|
|
|
|
#define RPMB_IPC_MAGIC 0x11111111
|
|
#define RPMB_REQUEST_MAGIC 0x44444444
|
|
#define RPMB_REPLY_MAGIC 0x66666666
|
|
|
|
struct rpmb_req {
|
|
uint16_t type;
|
|
uint16_t addr;
|
|
uint16_t blks;
|
|
uint8_t frame[0];
|
|
};
|
|
|
|
struct rpmb_ctx {
|
|
void *wsm_vaddr;
|
|
struct rpmb_req *req;
|
|
};
|
|
static struct task_struct *iwsock_th;
|
|
#endif
|
|
|
|
/* For nl socket */
|
|
#ifdef __RPMB_KERNEL_NL_SUPPORT
|
|
struct sock *rpmb_mtk_sock;
|
|
static u32 nl_pid = 100;
|
|
#define NETLINK_RPMB_MTK 30
|
|
|
|
wait_queue_head_t wait_rpmb;
|
|
static bool rpmb_done_flag;
|
|
struct mutex rpmb_lock;
|
|
|
|
|
|
/**
|
|
* struct storage_rpmb_req - request format for STORAGE_RPMB_SEND
|
|
* @reliable_write_size: size in bytes of reliable write region
|
|
* @write_size: size in bytes of write region
|
|
* @read_size: number of bytes to read for a read request
|
|
* @__reserved: unused, must be set to 0
|
|
* @payload: start of reliable write region, followed by
|
|
* write region.
|
|
* MAX_RPMB_REQUEST_SIZE(reliable write region)) +
|
|
* 512(write region)
|
|
*
|
|
*/
|
|
struct nl_rpmb_send_req {
|
|
u32 reliable_write_size;
|
|
u32 write_size;
|
|
u32 read_size;
|
|
u32 __reserved;
|
|
u8 payload[MAX_RPMB_REQUEST_SIZE+512];
|
|
};
|
|
|
|
static struct nl_rpmb_send_req nl_rpmb_req;
|
|
#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"
|
|
|
|
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
|
|
|
#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
|
|
#define RPMB_SZ_KEY 32U
|
|
#define RPMB_SZ_CAL_HMAC 284UL
|
|
#define RPMB_SZ_FRAME 512UL
|
|
|
|
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
|
|
|
|
#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_ALL;
|
|
#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;
|
|
#if IS_ENABLED(CONFIG_TRUSTONIC_TEE_SUPPORT)
|
|
static struct task_struct *rpmb_gp_Dci_th;
|
|
#endif
|
|
|
|
|
|
static struct cdev rpmb_cdev;
|
|
#ifdef __RPMB_IOCTL_SUPPORT
|
|
static struct class *mtk_rpmb_class;
|
|
#endif
|
|
|
|
#ifdef __RPMB_KERNEL_NL_SUPPORT
|
|
static int rpmb_mtk_snd_msg(void *pbuf, u16 len);
|
|
#endif
|
|
|
|
void rpmb_req_copy_data_for_hmac(u8 *buf, struct rpmb_frame *f)
|
|
{
|
|
u32 size;
|
|
|
|
/*
|
|
* Copy below members for HMAC calculation
|
|
* one by one with specifically assigning
|
|
* buf to each member to pass buffer-overrun checker.
|
|
*
|
|
* __u8 data[256];
|
|
* __u8 nonce[16];
|
|
* __be32 write_counter;
|
|
* __be16 addr;
|
|
* __be16 block_count;
|
|
* __be16 result;
|
|
* __be16 req_resp;
|
|
*/
|
|
|
|
memcpy(buf, f->data, RPMB_SZ_DATA);
|
|
buf += RPMB_SZ_DATA;
|
|
|
|
size = sizeof(f->nonce);
|
|
memcpy(buf, f->nonce, size);
|
|
buf += size;
|
|
|
|
size = sizeof(f->write_counter);
|
|
memcpy(buf, &f->write_counter, size);
|
|
buf += size;
|
|
|
|
size = sizeof(f->addr);
|
|
memcpy(buf, &f->addr, size);
|
|
buf += size;
|
|
|
|
size = sizeof(f->block_count);
|
|
memcpy(buf, &f->block_count, size);
|
|
buf += size;
|
|
|
|
size = sizeof(f->result);
|
|
memcpy(buf, &f->result, size);
|
|
buf += size;
|
|
|
|
size = sizeof(f->req_resp);
|
|
memcpy(buf, &f->req_resp, size);
|
|
buf += size;
|
|
}
|
|
|
|
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;
|
|
unsigned int nbytes = (unsigned 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;
|
|
|
|
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;
|
|
}
|
|
|
|
err = crypto_shash_update(shash, str, nbytes);
|
|
if (err != 0) {
|
|
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 g_rpmb_key[RPMB_SZ_KEY] = {
|
|
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, u16 blk_cnt,
|
|
u8 *key, u8 *key_mac)
|
|
{
|
|
int i;
|
|
u8 *buf, *buf_start;
|
|
|
|
buf = buf_start = kzalloc(RPMB_SZ_CAL_HMAC * blk_cnt, 0);
|
|
|
|
for (i = 0; i < blk_cnt; i++) {
|
|
memcpy(buf, frame[i].data, RPMB_SZ_CAL_HMAC);
|
|
buf += RPMB_SZ_CAL_HMAC;
|
|
}
|
|
|
|
if (hmac_sha256(key, RPMB_SZ_KEY, buf_start, RPMB_SZ_CAL_HMAC * 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);
|
|
}
|
|
|
|
#ifdef __RPMB_KERNEL_NL_SUPPORT
|
|
static int nl_rpmb_cmd_req(const struct rpmb_data *rpmbd)
|
|
{
|
|
int ret = 0;
|
|
|
|
u16 type, msg_len;
|
|
u32 cnt_in, cnt_out;
|
|
struct rpmb_frame *res_frame;
|
|
u8 *write_buf, *read_buf;
|
|
struct nl_rpmb_send_req *req = &nl_rpmb_req;
|
|
|
|
cnt_in = rpmbd->icmd.nframes;
|
|
cnt_out = rpmbd->ocmd.nframes;
|
|
type = rpmbd->req_type;
|
|
write_buf = req->payload;
|
|
|
|
mutex_lock(&rpmb_lock);
|
|
|
|
switch (type) {
|
|
case RPMB_PROGRAM_KEY:
|
|
cnt_in = 1;
|
|
cnt_out = 1;
|
|
fallthrough;
|
|
|
|
case RPMB_WRITE_DATA:
|
|
req->reliable_write_size = cnt_in * 512;
|
|
memcpy(write_buf, rpmbd->icmd.frames,
|
|
req->reliable_write_size);
|
|
write_buf += req->reliable_write_size;
|
|
|
|
res_frame = rpmbd->ocmd.frames;
|
|
memset(res_frame, 0, sizeof(*res_frame));
|
|
res_frame->req_resp = cpu_to_be16(RPMB_RESULT_READ);
|
|
req->write_size = 512;
|
|
memcpy(write_buf, res_frame, req->write_size);
|
|
write_buf += req->write_size;
|
|
|
|
req->read_size = cnt_out * 512;
|
|
break;
|
|
|
|
case RPMB_GET_WRITE_COUNTER:
|
|
cnt_in = 1;
|
|
cnt_out = 1;
|
|
fallthrough;
|
|
|
|
case RPMB_READ_DATA:
|
|
req->reliable_write_size = cnt_in * 512;
|
|
memcpy(write_buf, rpmbd->icmd.frames,
|
|
req->reliable_write_size);
|
|
write_buf += req->reliable_write_size;
|
|
|
|
req->write_size = 0;
|
|
req->read_size = cnt_out * 512;
|
|
break;
|
|
|
|
default:
|
|
pr_info("%s, -EINVAL in line %d!\n",
|
|
__func__, __LINE__);
|
|
mutex_unlock(&rpmb_lock);
|
|
return -EINVAL;
|
|
}
|
|
|
|
|
|
rpmb_done_flag = false; /* clear flag */
|
|
msg_len = 16 + req->reliable_write_size + req->write_size;
|
|
|
|
ret = rpmb_mtk_snd_msg(&nl_rpmb_req, msg_len);
|
|
if (ret != 0) {
|
|
MSG(ERR, "%s, rpmb message IO error!!!(0x%x)\n",
|
|
__func__, ret);
|
|
mutex_unlock(&rpmb_lock);
|
|
return -EINVAL;
|
|
}
|
|
|
|
ret = wait_event_timeout(wait_rpmb,
|
|
rpmb_done_flag,
|
|
msecs_to_jiffies(10000));
|
|
if (ret == 0) {
|
|
MSG(ERR, "[%s] rpmb operation timeout.", __func__);
|
|
ret = -ETIMEDOUT;
|
|
}
|
|
|
|
/* copy frame to rpmb_data */
|
|
read_buf = req->payload;
|
|
if (req->read_size)
|
|
memcpy(rpmbd->ocmd.frames, read_buf, req->read_size);
|
|
|
|
mutex_unlock(&rpmb_lock);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_SCSI_UFS_MEDIATEK)
|
|
static int rpmb_req_get_wc_ufs(u8 *keybytes, u32 *wc, u8 *frame)
|
|
{
|
|
struct rpmb_data rpmbdata;
|
|
struct rpmb_dev *rawdev_ufs_rpmb;
|
|
u8 nonce[RPMB_SZ_NONCE] = {0};
|
|
u8 hmac[RPMB_SZ_MAC];
|
|
int ret, i;
|
|
|
|
MSG(INFO, "%s start!!!\n", __func__);
|
|
|
|
rawdev_ufs_rpmb = ufs_mtk_rpmb_get_raw_dev();
|
|
|
|
do {
|
|
/*
|
|
* Initial frame buffers
|
|
*/
|
|
|
|
if (frame) {
|
|
|
|
/*
|
|
* 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) {
|
|
kfree(rpmbdata.icmd.frames);
|
|
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) {
|
|
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;
|
|
|
|
#ifdef __RPMB_KERNEL_NL_SUPPORT
|
|
ret = nl_rpmb_cmd_req(&rpmbdata);
|
|
#else
|
|
ret = rpmb_cmd_req(rawdev_ufs_rpmb, &rpmbdata);
|
|
#endif
|
|
|
|
if (ret) {
|
|
MSG(ERR, "%s, nl_rpmb_cmd_req IO error!!!(0x%x)\n",
|
|
__func__, ret);
|
|
break;
|
|
}
|
|
|
|
/* Verify HMAC only if key is available */
|
|
|
|
if (keybytes) {
|
|
/*
|
|
* Authenticate response write counter frame.
|
|
*/
|
|
if (hmac_sha256(keybytes, RPMB_SZ_KEY,
|
|
rpmbdata.ocmd.frames->data,
|
|
RPMB_SZ_CAL_HMAC, 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 < RPMB_SZ_MAC; 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) {
|
|
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) {
|
|
MSG(ERR, "%s, result error!!! (0x%x)\n",
|
|
__func__,
|
|
cpu_to_be16(rpmbdata.ocmd.frames->result));
|
|
ret = RPMB_RESULT_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (wc) {
|
|
*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) {
|
|
kfree(rpmbdata.icmd.frames);
|
|
kfree(rpmbdata.ocmd.frames);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rpmb_req_read_data_ufs(u8 *frame, u16 blk_cnt)
|
|
{
|
|
struct rpmb_data rpmbdata;
|
|
struct rpmb_dev *rawdev_ufs_rpmb;
|
|
int ret;
|
|
|
|
rawdev_ufs_rpmb = ufs_mtk_rpmb_get_raw_dev();
|
|
|
|
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;
|
|
|
|
#ifdef __RPMB_KERNEL_NL_SUPPORT
|
|
ret = nl_rpmb_cmd_req(&rpmbdata);
|
|
#else
|
|
ret = rpmb_cmd_req(rawdev_ufs_rpmb, &rpmbdata);
|
|
#endif
|
|
|
|
if (ret)
|
|
MSG(ERR, "%s: nl_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_ufs(u8 *frame, u16 blk_cnt)
|
|
{
|
|
struct rpmb_data rpmbdata;
|
|
struct rpmb_dev *rawdev_ufs_rpmb;
|
|
int ret;
|
|
#ifdef __RPMB_MTK_DEBUG_HMAC_VERIFY
|
|
u8 *key_mac;
|
|
#endif
|
|
|
|
rawdev_ufs_rpmb = ufs_mtk_rpmb_get_raw_dev();
|
|
|
|
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(RPMB_SZ_MAC, 0);
|
|
|
|
rpmb_cal_hmac((struct rpmb_frame *)frame, blk_cnt, g_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
|
|
|
|
#ifdef __RPMB_KERNEL_NL_SUPPORT
|
|
ret = nl_rpmb_cmd_req(&rpmbdata);
|
|
#else
|
|
ret = rpmb_cmd_req(rawdev_ufs_rpmb, &rpmbdata);
|
|
#endif
|
|
|
|
if (ret)
|
|
MSG(ERR, "%s: nl_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, RPMB_SZ_FRAME);
|
|
|
|
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;
|
|
}
|
|
|
|
int rpmb_req_program_key_ufs(u8 *frame, u16 blk_cnt)
|
|
{
|
|
struct rpmb_data data;
|
|
struct rpmb_dev *rawdev_ufs_rpmb;
|
|
int ret;
|
|
|
|
rawdev_ufs_rpmb = ufs_mtk_rpmb_get_raw_dev();
|
|
|
|
MSG(DBG_INFO, "%s: blk_cnt: %d\n", __func__, blk_cnt);
|
|
|
|
/*
|
|
* Alloc output frame to avoid overwriting input frame
|
|
* buffer provided by TEE
|
|
*/
|
|
data.ocmd.frames = rpmb_alloc_frames(1);
|
|
|
|
if (data.ocmd.frames == NULL)
|
|
return RPMB_ALLOC_ERROR;
|
|
|
|
data.ocmd.nframes = 1;
|
|
|
|
data.req_type = RPMB_PROGRAM_KEY;
|
|
data.icmd.nframes = 1;
|
|
data.icmd.frames = (struct rpmb_frame *)frame;
|
|
|
|
ret = rpmb_cmd_req(rawdev_ufs_rpmb, &data);
|
|
|
|
if (ret)
|
|
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, data.ocmd.frames, 512);
|
|
|
|
if (data.ocmd.frames->result) {
|
|
MSG(ERR, "%s, result error!!! (%x)\n", __func__,
|
|
cpu_to_be16(data.ocmd.frames->result));
|
|
ret = RPMB_RESULT_ERROR;
|
|
}
|
|
|
|
kfree(data.ocmd.frames);
|
|
|
|
MSG(DBG_INFO, "%s: ret 0x%x\n", __func__, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int rpmb_req_ioctl_write_data_ufs(struct rpmb_ioc_param *param)
|
|
{
|
|
struct rpmb_data rpmbdata;
|
|
struct rpmb_dev *rawdev_ufs_rpmb;
|
|
u32 i, tran_size, left_size = param->data_len;
|
|
u32 wc = 0xFFFFFFFFU;
|
|
u16 iCnt, tran_blkcnt, left_blkcnt;
|
|
u16 blkaddr;
|
|
u8 hmac[RPMB_SZ_MAC], rpmb_key[RPMB_SZ_KEY];
|
|
u8 *dataBuf, *dataBuf_start;
|
|
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;
|
|
|
|
rawdev_ufs_rpmb = ufs_mtk_rpmb_get_raw_dev();
|
|
|
|
/* copy key first */
|
|
ret = copy_from_user(rpmb_key, param->keybytes, RPMB_SZ_KEY);
|
|
if (ret != 0)
|
|
return -EFAULT;
|
|
|
|
i = 0;
|
|
tran_blkcnt = 0;
|
|
dataBuf = NULL;
|
|
dataBuf_start = NULL;
|
|
|
|
left_blkcnt = (u16)((param->data_len % RPMB_SZ_DATA) ?
|
|
(param->data_len / RPMB_SZ_DATA + 1) :
|
|
(param->data_len / RPMB_SZ_DATA));
|
|
|
|
/*
|
|
* 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) {
|
|
|
|
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_ufs(rpmb_key, &wc, NULL);
|
|
if (ret) {
|
|
MSG(ERR, "%s, rpmb_req_get_wc_ufs 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) {
|
|
kfree(rpmbdata.icmd.frames);
|
|
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(RPMB_SZ_CAL_HMAC * tran_blkcnt, 0);
|
|
if (!dataBuf_start) {
|
|
kfree(rpmbdata.icmd.frames);
|
|
kfree(rpmbdata.ocmd.frames);
|
|
return RPMB_ALLOC_ERROR;
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
|
|
ret = copy_from_user(rpmbdata.icmd.frames[iCnt].data,
|
|
param->databytes +
|
|
(i * MAX_RPMB_TRANSFER_BLK * RPMB_SZ_DATA +
|
|
(iCnt * RPMB_SZ_DATA)),
|
|
tran_size);
|
|
if (ret) {
|
|
MSG(ERR, "%s, copy from user failed: %x\n",
|
|
__func__, ret);
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
|
|
left_size -= tran_size;
|
|
|
|
rpmb_req_copy_data_for_hmac(
|
|
dataBuf, &rpmbdata.icmd.frames[iCnt]);
|
|
|
|
dataBuf += size_for_hmac;
|
|
}
|
|
|
|
iCnt--;
|
|
|
|
if (hmac_sha256(rpmb_key, RPMB_SZ_KEY, dataBuf_start,
|
|
RPMB_SZ_CAL_HMAC * tran_blkcnt,
|
|
rpmbdata.icmd.frames[iCnt].key_mac) != 0)
|
|
MSG(ERR, "hmac_sha256() return error!\n");
|
|
|
|
/*
|
|
* Send write data request.
|
|
*/
|
|
#ifdef __RPMB_KERNEL_NL_SUPPORT
|
|
ret = nl_rpmb_cmd_req(&rpmbdata);
|
|
#else
|
|
ret = rpmb_cmd_req(rawdev_ufs_rpmb, &rpmbdata);
|
|
#endif
|
|
|
|
if (ret) {
|
|
MSG(ERR, "%s, nl_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(rpmb_key, RPMB_SZ_KEY,
|
|
rpmbdata.ocmd.frames->data,
|
|
RPMB_SZ_CAL_HMAC, 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) {
|
|
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 + 1) {
|
|
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);
|
|
};
|
|
|
|
out:
|
|
if (ret) {
|
|
kfree(rpmbdata.icmd.frames);
|
|
kfree(rpmbdata.ocmd.frames);
|
|
kfree(dataBuf_start);
|
|
}
|
|
|
|
if (left_blkcnt || left_size) {
|
|
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_ufs(struct rpmb_ioc_param *param)
|
|
{
|
|
struct rpmb_data rpmbdata;
|
|
struct rpmb_dev *rawdev_ufs_rpmb;
|
|
u32 i, tran_size, left_size = param->data_len;
|
|
u16 iCnt, tran_blkcnt, left_blkcnt;
|
|
u16 blkaddr;
|
|
u8 nonce[RPMB_SZ_NONCE] = {0};
|
|
u8 hmac[RPMB_SZ_MAC], rpmb_key[RPMB_SZ_KEY];
|
|
u8 *dataBuf, *dataBuf_start;
|
|
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;
|
|
|
|
rawdev_ufs_rpmb = ufs_mtk_rpmb_get_raw_dev();
|
|
|
|
/* copy key first */
|
|
ret = copy_from_user(rpmb_key, param->keybytes, RPMB_SZ_KEY);
|
|
if (ret != 0)
|
|
return -EFAULT;
|
|
|
|
i = 0;
|
|
tran_blkcnt = 0;
|
|
dataBuf = NULL;
|
|
dataBuf_start = NULL;
|
|
|
|
left_blkcnt = (u16)((param->data_len % RPMB_SZ_DATA) ?
|
|
(param->data_len / RPMB_SZ_DATA + 1) :
|
|
(param->data_len / RPMB_SZ_DATA));
|
|
|
|
blkaddr = param->addr;
|
|
|
|
while (left_blkcnt) {
|
|
|
|
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) {
|
|
kfree(rpmbdata.icmd.frames);
|
|
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(RPMB_SZ_CAL_HMAC * tran_blkcnt, 0);
|
|
if (!dataBuf_start) {
|
|
kfree(rpmbdata.icmd.frames);
|
|
kfree(rpmbdata.ocmd.frames);
|
|
return RPMB_ALLOC_ERROR;
|
|
}
|
|
|
|
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;
|
|
|
|
#ifdef __RPMB_KERNEL_NL_SUPPORT
|
|
ret = nl_rpmb_cmd_req(&rpmbdata);
|
|
#else
|
|
ret = rpmb_cmd_req(rawdev_ufs_rpmb, &rpmbdata);
|
|
#endif
|
|
|
|
if (ret) {
|
|
MSG(ERR, "%s, nl_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.
|
|
*/
|
|
rpmb_req_copy_data_for_hmac(
|
|
dataBuf, &rpmbdata.ocmd.frames[iCnt]);
|
|
|
|
dataBuf += size_for_hmac;
|
|
|
|
ret = copy_to_user(param->databytes +
|
|
i * MAX_RPMB_TRANSFER_BLK * RPMB_SZ_DATA +
|
|
(iCnt * RPMB_SZ_DATA),
|
|
rpmbdata.ocmd.frames[iCnt].data, tran_size);
|
|
if (ret) {
|
|
ret = -EFAULT;
|
|
goto out;
|
|
}
|
|
|
|
left_size -= tran_size;
|
|
}
|
|
|
|
iCnt--;
|
|
|
|
/*
|
|
* Authenticate response read data frame.
|
|
*/
|
|
if (hmac_sha256(rpmb_key, RPMB_SZ_KEY,
|
|
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) {
|
|
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);
|
|
};
|
|
|
|
out:
|
|
if (ret) {
|
|
kfree(rpmbdata.icmd.frames);
|
|
kfree(rpmbdata.ocmd.frames);
|
|
kfree(dataBuf_start);
|
|
}
|
|
|
|
if (left_blkcnt || left_size) {
|
|
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.
|
|
*
|
|
******************************************************************************/
|
|
static void rpmb_gp_execute_ufs(u32 cmdId)
|
|
{
|
|
int ret = 0;
|
|
|
|
switch (cmdId) {
|
|
|
|
case DCI_RPMB_CMD_READ_DATA:
|
|
|
|
MSG(DBG_INFO, "%s: DCI_RPMB_CMD_READ_DATA\n", __func__);
|
|
|
|
ret = rpmb_req_read_data_ufs(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_ufs(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_ufs(rpmb_gp_dci->request.frame,
|
|
rpmb_gp_dci->request.blks);
|
|
|
|
break;
|
|
|
|
case DCI_RPMB_CMD_PROGRAM_KEY:
|
|
MSG(INFO, "%s: DCI_RPMB_CMD_PROGRAM_KEY.\n", __func__);
|
|
rpmb_dump_frame(rpmb_gp_dci->request.frame);
|
|
|
|
ret = rpmb_req_program_key_ufs(rpmb_gp_dci->request.frame, 1);
|
|
|
|
break;
|
|
|
|
default:
|
|
MSG(ERR, "%s: receive an unknown command id(%d).\n",
|
|
__func__, cmdId);
|
|
break;
|
|
|
|
}
|
|
|
|
if (ret)
|
|
MSG(ERR, "%s: Error ret(%d).\n", __func__, ret);
|
|
}
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_MMC_MTK_PRO)
|
|
/*
|
|
* CHECK THIS!!! Copy from block.c mmc_blk_data structure.
|
|
*/
|
|
struct emmc_rpmb_blk_data {
|
|
struct device *parent;
|
|
struct gendisk *disk;
|
|
struct mmc_queue queue;
|
|
struct list_head part;
|
|
struct list_head rpmbs;
|
|
|
|
unsigned int flags;
|
|
unsigned int usage;
|
|
unsigned int read_only;
|
|
unsigned int part_type;
|
|
unsigned int reset_done;
|
|
|
|
/*
|
|
* Only set in main mmc_blk_data associated
|
|
* with mmc_card with dev_set_drvdata, and keeps
|
|
* track of the current selected device partition.
|
|
*/
|
|
unsigned int part_curr;
|
|
struct device_attribute force_ro;
|
|
struct device_attribute power_ro_lock;
|
|
int area_type;
|
|
};
|
|
|
|
struct emmc_rpmb_data {
|
|
struct device dev;
|
|
struct cdev chrdev;
|
|
int id;
|
|
unsigned int part_index;
|
|
struct emmc_rpmb_blk_data *md;
|
|
struct list_head node;
|
|
};
|
|
|
|
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;
|
|
};
|
|
|
|
/*
|
|
* CHECK THIS!!! Copy from block.c mmc_blk_part_switch.
|
|
* Since it is static inline function, we cannot extern to use it.
|
|
* For syncing block data, this is the only way.
|
|
*/
|
|
int emmc_rpmb_switch(struct mmc_card *card, struct emmc_rpmb_blk_data *md)
|
|
{
|
|
int ret;
|
|
struct emmc_rpmb_blk_data *main_md = dev_get_drvdata(&card->dev);
|
|
|
|
if (main_md->part_curr == md->part_type)
|
|
return 0;
|
|
|
|
if (md->part_type == EXT_CSD_PART_CONFIG_ACC_RPMB) {
|
|
if (card->ext_csd.cmdq_en) {
|
|
ret = mmc_cmdq_disable(card);
|
|
if (ret) {
|
|
MSG(ERR, "CMDQ disabled failed!(%d)\n", ret);
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mmc_card_mmc(card)) {
|
|
u8 part_config = card->ext_csd.part_config;
|
|
|
|
part_config &= ~EXT_CSD_PART_CONFIG_ACC_MASK;
|
|
part_config |= md->part_type;
|
|
|
|
ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL,
|
|
EXT_CSD_PART_CONFIG, part_config,
|
|
card->ext_csd.part_time);
|
|
if (ret)
|
|
return ret;
|
|
|
|
card->ext_csd.part_config = part_config;
|
|
}
|
|
|
|
/* enable cmdq at user partition */
|
|
if (main_md->part_curr == EXT_CSD_PART_CONFIG_ACC_RPMB) {
|
|
if (card->reenable_cmdq && !card->ext_csd.cmdq_en) {
|
|
ret = mmc_cmdq_enable(card);
|
|
if (ret)
|
|
pr_notice("%s enable CMDQ error %d,so just work without CMDQ\n",
|
|
mmc_hostname(card->host), ret);
|
|
}
|
|
}
|
|
|
|
main_md->part_curr = md->part_type;
|
|
return 0;
|
|
}
|
|
|
|
static int emmc_rpmb_send_command(
|
|
struct mmc_card *card,
|
|
u8 *buf,
|
|
__u16 blks,
|
|
__u16 type,
|
|
u8 req_type
|
|
)
|
|
{
|
|
struct mmc_request mrq = {NULL};
|
|
struct mmc_command cmd = {0};
|
|
struct mmc_command sbc = {0};
|
|
struct mmc_data data = {0};
|
|
struct scatterlist sg;
|
|
u8 *transfer_buf = NULL;
|
|
|
|
if (blks == 0) {
|
|
MSG(ERR, "%s: Invalid blks: 0\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
mrq.sbc = &sbc;
|
|
mrq.cmd = &cmd;
|
|
mrq.data = &data;
|
|
mrq.stop = NULL;
|
|
transfer_buf = kzalloc(512 * blks, GFP_KERNEL);
|
|
if (!transfer_buf)
|
|
return -ENOMEM;
|
|
|
|
/*
|
|
* set CMD23
|
|
*/
|
|
sbc.opcode = MMC_SET_BLOCK_COUNT;
|
|
sbc.arg = blks;
|
|
if ((req_type == RPMB_REQ && type == RPMB_WRITE_DATA) ||
|
|
type == RPMB_PROGRAM_KEY)
|
|
sbc.arg |= 1 << 31;
|
|
sbc.flags = MMC_RSP_R1 | MMC_CMD_AC;
|
|
|
|
/*
|
|
* set CMD25/18
|
|
*/
|
|
sg_init_one(&sg, transfer_buf, 512 * blks);
|
|
if (req_type == RPMB_REQ) {
|
|
cmd.opcode = MMC_WRITE_MULTIPLE_BLOCK;
|
|
sg_copy_from_buffer(&sg, 1, buf, 512 * blks);
|
|
data.flags |= MMC_DATA_WRITE;
|
|
} else {
|
|
cmd.opcode = MMC_READ_MULTIPLE_BLOCK;
|
|
data.flags |= MMC_DATA_READ;
|
|
}
|
|
|
|
cmd.arg = 0;
|
|
cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
|
|
data.blksz = 512;
|
|
data.blocks = blks;
|
|
data.sg = &sg;
|
|
data.sg_len = 1;
|
|
|
|
mmc_set_data_timeout(&data, card);
|
|
|
|
mmc_wait_for_req(card->host, &mrq);
|
|
|
|
if (req_type != RPMB_REQ)
|
|
sg_copy_to_buffer(&sg, 1, buf, 512 * blks);
|
|
|
|
kfree(transfer_buf);
|
|
|
|
if (cmd.error)
|
|
return cmd.error;
|
|
if (data.error)
|
|
return data.error;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int emmc_rpmb_req_start(struct mmc_card *card, struct emmc_rpmb_req *req)
|
|
{
|
|
int err = 0;
|
|
u16 blks = req->blk_cnt;
|
|
u16 type = req->type;
|
|
u8 *data_frame = req->data_frame;
|
|
|
|
/* MSG(INFO, "%s, start\n", __func__); */
|
|
|
|
/*
|
|
* STEP 1: send request to RPMB partition.
|
|
*/
|
|
if (type == RPMB_WRITE_DATA)
|
|
err = emmc_rpmb_send_command(card, data_frame,
|
|
blks, type, RPMB_REQ);
|
|
else
|
|
err = emmc_rpmb_send_command(card, data_frame,
|
|
1, type, RPMB_REQ);
|
|
|
|
if (err) {
|
|
MSG(ERR, "%s step 1, request failed (%d)\n", __func__, err);
|
|
goto out;
|
|
}
|
|
|
|
/*
|
|
* STEP 2: check write result. Only for WRITE_DATA or Program key.
|
|
*/
|
|
memset(data_frame, 0, 512 * blks);
|
|
|
|
if (type == RPMB_WRITE_DATA || type == RPMB_PROGRAM_KEY) {
|
|
data_frame[RPMB_TYPE_BEG + 1] = RPMB_RESULT_READ;
|
|
err = emmc_rpmb_send_command(card, data_frame,
|
|
1, RPMB_RESULT_READ, RPMB_REQ);
|
|
if (err) {
|
|
MSG(ERR, "%s step 2, request result failed (%d)\n",
|
|
__func__, err);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* STEP 3: get response from RPMB partition
|
|
*/
|
|
data_frame[RPMB_TYPE_BEG] = 0;
|
|
data_frame[RPMB_TYPE_BEG + 1] = type;
|
|
|
|
if (type == RPMB_READ_DATA)
|
|
err = emmc_rpmb_send_command(card, data_frame, blks,
|
|
type, RPMB_RESP);
|
|
else
|
|
err = emmc_rpmb_send_command(card, data_frame, 1,
|
|
type, RPMB_RESP);
|
|
|
|
if (err)
|
|
MSG(ERR, "%s step 3, response failed (%d)\n", __func__, err);
|
|
|
|
/* MSG(INFO, "%s, end\n", __func__); */
|
|
|
|
out:
|
|
return err;
|
|
|
|
}
|
|
|
|
int emmc_rpmb_req_handle(struct mmc_card *card, struct emmc_rpmb_req *rpmb_req)
|
|
{
|
|
struct emmc_rpmb_blk_data *md = NULL, *part_md;
|
|
int ret;
|
|
struct emmc_rpmb_data *rpmb;
|
|
struct list_head *pos;
|
|
|
|
part_md = vzalloc(sizeof(struct emmc_rpmb_blk_data));
|
|
if (!part_md)
|
|
return -ENOMEM;
|
|
/* rpmb_dump_frame(rpmb_req->data_frame); */
|
|
md = dev_get_drvdata(&card->dev);
|
|
list_for_each(pos, &md->rpmbs) {
|
|
rpmb = list_entry(pos, struct emmc_rpmb_data, node);
|
|
if (rpmb) {
|
|
part_md->part_type = EXT_CSD_PART_CONFIG_ACC_RPMB;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* MSG(INFO, "%s start.\n", __func__); */
|
|
|
|
mmc_get_card(card, NULL);
|
|
|
|
/*
|
|
* STEP1: Switch to RPMB partition.
|
|
*/
|
|
ret = emmc_rpmb_switch(card, part_md);
|
|
if (ret) {
|
|
MSG(ERR, "%s emmc_rpmb_switch failed. (%x)\n", __func__, ret);
|
|
goto error;
|
|
}
|
|
|
|
/*
|
|
* STEP2: Start request. (CMD23, CMD25/18 procedure)
|
|
*/
|
|
ret = emmc_rpmb_req_start(card, rpmb_req);
|
|
if (ret) {
|
|
MSG(ERR, "%s emmc_rpmb_req_start failed!! (%x)\n",
|
|
__func__, ret);
|
|
goto error;
|
|
}
|
|
|
|
/* MSG(INFO, "%s end.\n", __func__); */
|
|
|
|
error:
|
|
ret = emmc_rpmb_switch(card, dev_get_drvdata(&card->dev));
|
|
if (ret)
|
|
MSG(ERR, "%s emmc_rpmb_switch main failed. (%x)\n",
|
|
__func__, ret);
|
|
|
|
mmc_put_card(card, NULL);
|
|
|
|
/* rpmb_dump_frame(rpmb_req->data_frame); */
|
|
vfree(part_md);
|
|
return ret;
|
|
}
|
|
|
|
int emmc_rpmb_req_set_key(struct mmc_card *card, u8 *key)
|
|
{
|
|
struct emmc_rpmb_req rpmb_req;
|
|
struct s_rpmb *rpmb_frame;
|
|
int ret;
|
|
u8 user_key;
|
|
|
|
if (get_user(user_key, key))
|
|
return -EFAULT;
|
|
|
|
MSG(INFO, "%s start!!!\n", __func__);
|
|
|
|
rpmb_frame = kzalloc(sizeof(struct s_rpmb), 0);
|
|
if (rpmb_frame == NULL)
|
|
return RPMB_ALLOC_ERROR;
|
|
|
|
memcpy(rpmb_frame->mac, key, RPMB_SZ_MAC);
|
|
|
|
rpmb_req.type = RPMB_PROGRAM_KEY;
|
|
rpmb_req.blk_cnt = 1;
|
|
rpmb_req.data_frame = (u8 *)rpmb_frame;
|
|
|
|
rpmb_frame->request = cpu_to_be16p(&rpmb_req.type);
|
|
|
|
ret = emmc_rpmb_req_handle(card, &rpmb_req);
|
|
if (ret) {
|
|
MSG(ERR, "%s, emmc_rpmb_req_handle IO error!!!(%x)\n",
|
|
__func__, ret);
|
|
goto free;
|
|
}
|
|
|
|
if (rpmb_frame->result) {
|
|
MSG(ERR, "%s, result error!!! (%x)\n",
|
|
__func__, cpu_to_be16p(&rpmb_frame->result));
|
|
ret = RPMB_RESULT_ERROR;
|
|
}
|
|
|
|
MSG(INFO, "%s end!!!\n", __func__);
|
|
|
|
free:
|
|
kfree(rpmb_frame);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void rpmb_gp_execute_emmc(u32 cmdId)
|
|
{
|
|
int ret = 0;
|
|
|
|
struct mmc_host *mmc = mtk_mmc_host[0];
|
|
struct mmc_card *card = mmc->card;
|
|
struct emmc_rpmb_req rpmb_req;
|
|
|
|
switch (cmdId) {
|
|
|
|
case DCI_RPMB_CMD_READ_DATA:
|
|
MSG(INFO, "%s: DCI_RPMB_CMD_READ_DATA.\n", __func__);
|
|
|
|
rpmb_req.type = RPMB_READ_DATA;
|
|
rpmb_req.blk_cnt = rpmb_gp_dci->request.blks;
|
|
rpmb_req.addr = rpmb_gp_dci->request.addr;
|
|
rpmb_req.data_frame = rpmb_gp_dci->request.frame;
|
|
|
|
ret = emmc_rpmb_req_handle(card, &rpmb_req);
|
|
if (ret)
|
|
MSG(ERR, "%s, emmc_rpmb_req_handle failed!!(%x)\n",
|
|
__func__, ret);
|
|
|
|
break;
|
|
|
|
case DCI_RPMB_CMD_GET_WCNT:
|
|
MSG(INFO, "%s: DCI_RPMB_CMD_GET_WCNT.\n", __func__);
|
|
|
|
rpmb_req.type = RPMB_GET_WRITE_COUNTER;
|
|
rpmb_req.blk_cnt = rpmb_gp_dci->request.blks;
|
|
rpmb_req.addr = rpmb_gp_dci->request.addr;
|
|
rpmb_req.data_frame = rpmb_gp_dci->request.frame;
|
|
|
|
ret = emmc_rpmb_req_handle(card, &rpmb_req);
|
|
if (ret)
|
|
MSG(ERR, "%s, emmc_rpmb_req_handle failed!!(%x)\n",
|
|
__func__, ret);
|
|
|
|
break;
|
|
|
|
case DCI_RPMB_CMD_WRITE_DATA:
|
|
MSG(INFO, "%s: DCI_RPMB_CMD_WRITE_DATA.\n", __func__);
|
|
|
|
rpmb_req.type = RPMB_WRITE_DATA;
|
|
rpmb_req.blk_cnt = rpmb_gp_dci->request.blks;
|
|
rpmb_req.addr = rpmb_gp_dci->request.addr;
|
|
rpmb_req.data_frame = rpmb_gp_dci->request.frame;
|
|
|
|
ret = emmc_rpmb_req_handle(card, &rpmb_req);
|
|
if (ret)
|
|
MSG(ERR, "%s, emmc_rpmb_req_handle failed!!(%x)\n",
|
|
__func__, ret);
|
|
|
|
break;
|
|
|
|
#ifdef CFG_RPMB_KEY_PROGRAMED_IN_KERNEL
|
|
case DCI_RPMB_CMD_PROGRAM_KEY:
|
|
MSG(INFO, "%s: DCI_RPMB_CMD_PROGRAM_KEY.\n", __func__);
|
|
rpmb_dump_frame(rpmb_gp_dci->request.frame);
|
|
|
|
rpmb_req.type = RPMB_PROGRAM_KEY;
|
|
rpmb_req.blk_cnt = rpmb_gp_dci->request.blks;
|
|
rpmb_req.addr = rpmb_gp_dci->request.addr;
|
|
rpmb_req.data_frame = rpmb_gp_dci->request.frame;
|
|
|
|
ret = emmc_rpmb_req_handle(card, &rpmb_req);
|
|
if (ret)
|
|
MSG(ERR, "%s, emmc_rpmb_req_handle failed!!(%x)\n",
|
|
__func__, ret);
|
|
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
MSG(ERR, "%s: receive an unknown command id(%d).\n",
|
|
__func__, cmdId);
|
|
break;
|
|
|
|
}
|
|
|
|
if (ret)
|
|
MSG(ERR, "%s: Error ret(%d).\n", __func__, ret);
|
|
}
|
|
|
|
int rpmb_req_get_wc_emmc(struct mmc_card *card, u8 *key, u32 *wc)
|
|
{
|
|
struct emmc_rpmb_req rpmb_req;
|
|
struct s_rpmb *rpmb_frame;
|
|
u8 nonce[RPMB_SZ_NONCE] = {0};
|
|
u8 hmac[RPMB_SZ_MAC];
|
|
int ret;
|
|
|
|
MSG(INFO, "%s start!!!\n", __func__);
|
|
|
|
do {
|
|
rpmb_frame = kzalloc(sizeof(struct s_rpmb), 0);
|
|
if (rpmb_frame == NULL)
|
|
return RPMB_ALLOC_ERROR;
|
|
|
|
get_random_bytes(nonce, RPMB_SZ_NONCE);
|
|
|
|
/*
|
|
* Prepare request. Get write counter.
|
|
*/
|
|
rpmb_req.type = RPMB_GET_WRITE_COUNTER;
|
|
rpmb_req.blk_cnt = 1;
|
|
rpmb_req.data_frame = (u8 *)rpmb_frame;
|
|
|
|
/*
|
|
* Prepare get write counter frame. only need nonce.
|
|
*/
|
|
rpmb_frame->request = cpu_to_be16p(&rpmb_req.type);
|
|
memcpy(rpmb_frame->nonce, nonce, RPMB_SZ_NONCE);
|
|
|
|
ret = emmc_rpmb_req_handle(card, &rpmb_req);
|
|
if (ret) {
|
|
MSG(ERR, "%s, emmc_rpmb_req_handle IO error!!!(%x)\n",
|
|
__func__, ret);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Authenticate response write counter frame.
|
|
*/
|
|
if (key) {
|
|
hmac_sha256(key, 32, rpmb_frame->data, 284, hmac);
|
|
if (memcmp(hmac, rpmb_frame->mac, RPMB_SZ_MAC) != 0) {
|
|
MSG(ERR, "%s, hmac compare error!!!\n",
|
|
__func__);
|
|
ret = RPMB_HMAC_ERROR;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (memcmp(nonce, rpmb_frame->nonce, RPMB_SZ_NONCE) != 0) {
|
|
MSG(ERR, "%s, nonce compare error!!!\n", __func__);
|
|
ret = RPMB_NONCE_ERROR;
|
|
break;
|
|
}
|
|
|
|
if (rpmb_frame->result) {
|
|
MSG(ERR, "%s, result error!!! (%x)\n", __func__,
|
|
cpu_to_be16p(&rpmb_frame->result));
|
|
ret = RPMB_RESULT_ERROR;
|
|
break;
|
|
}
|
|
|
|
*wc = cpu_to_be32p(&rpmb_frame->write_counter);
|
|
|
|
} while (0);
|
|
|
|
MSG(INFO, "%s end!!!\n", __func__);
|
|
|
|
kfree(rpmb_frame);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int rpmb_req_ioctl_write_data_emmc(struct mmc_card *card,
|
|
struct rpmb_ioc_param *param)
|
|
{
|
|
struct emmc_rpmb_req rpmb_req;
|
|
struct s_rpmb *rpmb_frame;
|
|
u32 tran_size, left_size = param->data_len;
|
|
u32 wc = 0xFFFFFFFF;
|
|
u16 iCnt, total_blkcnt, tran_blkcnt, left_blkcnt;
|
|
u16 blkaddr;
|
|
u8 hmac[RPMB_SZ_MAC] = {0};
|
|
u8 *dataBuf, *dataBuf_start;
|
|
int i, ret = 0;
|
|
#ifdef RPMB_MULTI_BLOCK_ACCESS
|
|
u8 write_blks_one_time = 0;
|
|
u32 size_for_hmac;
|
|
#endif
|
|
|
|
MSG(INFO, "%s start!!!\n", __func__);
|
|
|
|
i = 0;
|
|
tran_blkcnt = 0;
|
|
dataBuf = NULL;
|
|
dataBuf_start = NULL;
|
|
|
|
left_blkcnt = total_blkcnt = ((param->data_len % RPMB_SZ_DATA) ?
|
|
(param->data_len / RPMB_SZ_DATA + 1) :
|
|
(param->data_len / RPMB_SZ_DATA));
|
|
|
|
|
|
#ifdef RPMB_MULTI_BLOCK_ACCESS
|
|
|
|
/*
|
|
* For RPMB write data, the elements we need in the data frame is
|
|
* 1. address.
|
|
* 2. write counter.
|
|
* 3. data.
|
|
* 4. block count.
|
|
* 5. MAC
|
|
*
|
|
*/
|
|
|
|
blkaddr = param->addr;
|
|
write_blks_one_time = MIN(MAX_RPMB_TRANSFER_BLK,
|
|
card->ext_csd.rel_sectors * 2);
|
|
while (left_blkcnt) {
|
|
|
|
if (left_blkcnt > write_blks_one_time)
|
|
tran_blkcnt = write_blks_one_time;
|
|
else
|
|
tran_blkcnt = left_blkcnt;
|
|
|
|
MSG(INFO, "%s, total_blkcnt=%x, tran_blkcnt=%x\n",
|
|
__func__, left_blkcnt, tran_blkcnt);
|
|
|
|
ret = rpmb_req_get_wc_emmc(card, param->keybytes, &wc);
|
|
if (ret) {
|
|
MSG(ERR, "%s, rpmb_req_get_wc_emmc error!!!(%x)\n",
|
|
__func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
rpmb_frame = kzalloc(tran_blkcnt * sizeof(struct s_rpmb)
|
|
+ tran_blkcnt * 512, 0);
|
|
if (rpmb_frame == NULL)
|
|
return RPMB_ALLOC_ERROR;
|
|
|
|
dataBuf_start = dataBuf = (u8 *)(rpmb_frame + tran_blkcnt);
|
|
|
|
/*
|
|
* Prepare request. write data.
|
|
*/
|
|
rpmb_req.type = RPMB_WRITE_DATA;
|
|
rpmb_req.blk_cnt = tran_blkcnt;
|
|
rpmb_req.data_frame = (u8 *)rpmb_frame;
|
|
|
|
/*
|
|
* STEP 3(data), prepare every data frame one by one and
|
|
* hook HMAC to the last.
|
|
*/
|
|
|
|
/* 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.
|
|
*/
|
|
rpmb_frame[iCnt].request = cpu_to_be16p(&rpmb_req.type);
|
|
rpmb_frame[iCnt].address = cpu_to_be16p(&blkaddr);
|
|
rpmb_frame[iCnt].write_counter = cpu_to_be32p(&wc);
|
|
rpmb_frame[iCnt].block_count =
|
|
cpu_to_be16p(&rpmb_req.blk_cnt);
|
|
|
|
if (left_size >= RPMB_SZ_DATA)
|
|
tran_size = RPMB_SZ_DATA;
|
|
else
|
|
tran_size = left_size;
|
|
|
|
memcpy(rpmb_frame[iCnt].data,
|
|
param->databytes + (iCnt * RPMB_SZ_DATA)
|
|
+ i * write_blks_one_time * RPMB_SZ_DATA,
|
|
tran_size);
|
|
left_size -= tran_size;
|
|
|
|
rpmb_req_copy_data_for_hmac(dataBuf,
|
|
(struct rpmb_frame *) &rpmb_frame[iCnt]);
|
|
|
|
dataBuf += size_for_hmac;
|
|
}
|
|
|
|
iCnt--;
|
|
|
|
hmac_sha256(param->keybytes, 32, dataBuf_start, 284 * tran_blkcnt,
|
|
rpmb_frame[iCnt].mac);
|
|
|
|
/*
|
|
* STEP 4, send write data request.
|
|
*/
|
|
ret = emmc_rpmb_req_handle(card, &rpmb_req);
|
|
if (ret) {
|
|
MSG(ERR, "%s, emmc_rpmb_req_handle IO error!!!(%x)\n",
|
|
__func__, ret);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* STEP 5. authenticate write result response.
|
|
* 1. authenticate hmac.
|
|
* 2. check result.
|
|
* 3. compare write counter is increamented.
|
|
*/
|
|
hmac_sha256(param->keybytes, 32, rpmb_frame->data, 284, hmac);
|
|
|
|
if (memcmp(hmac, rpmb_frame->mac, RPMB_SZ_MAC) != 0) {
|
|
MSG(ERR, "%s, hmac compare error!!!\n", __func__);
|
|
ret = RPMB_HMAC_ERROR;
|
|
break;
|
|
}
|
|
|
|
if (rpmb_frame->result) {
|
|
MSG(ERR, "%s, result error!!! (%x)\n", __func__,
|
|
cpu_to_be16p(&rpmb_frame->result));
|
|
ret = RPMB_RESULT_ERROR;
|
|
break;
|
|
}
|
|
|
|
if (cpu_to_be32p(&rpmb_frame->write_counter) != wc + 1) {
|
|
MSG(ERR, "%s, write counter error!!! (%x)\n", __func__,
|
|
cpu_to_be32p(&rpmb_frame->write_counter));
|
|
ret = RPMB_WC_ERROR;
|
|
break;
|
|
}
|
|
|
|
blkaddr += tran_blkcnt;
|
|
left_blkcnt -= tran_blkcnt;
|
|
i++;
|
|
kfree(rpmb_frame);
|
|
};
|
|
|
|
if (ret)
|
|
kfree(rpmb_frame);
|
|
|
|
if (left_blkcnt || left_size) {
|
|
MSG(ERR, "left_blkcnt or left_size is not empty!!!!!!\n");
|
|
return RPMB_TRANSFER_NOT_COMPLETE;
|
|
}
|
|
|
|
#else
|
|
rpmb_frame = kzalloc(sizeof(struct s_rpmb), 0);
|
|
if (rpmb_frame == NULL)
|
|
return RPMB_ALLOC_ERROR;
|
|
|
|
blkaddr = param->addr;
|
|
|
|
for (iCnt = 0; iCnt < total_blkcnt; iCnt++) {
|
|
|
|
ret = rpmb_req_get_wc_emmc(card, param->keybytes, &wc);
|
|
if (ret)
|
|
break;
|
|
|
|
memset(rpmb_frame, 0, sizeof(struct s_rpmb));
|
|
|
|
/*
|
|
* Prepare request. write data.
|
|
*/
|
|
rpmb_req.type = RPMB_WRITE_DATA;
|
|
rpmb_req.blk_cnt = 1;
|
|
rpmb_req.data_frame = (u8 *)rpmb_frame;
|
|
|
|
/*
|
|
* Prepare write data frame. need addr, wc,
|
|
* blkcnt, data and mac.
|
|
*/
|
|
rpmb_frame->request = cpu_to_be16p(&rpmb_req.type);
|
|
rpmb_frame->address = cpu_to_be16p(&blkaddr);
|
|
rpmb_frame->write_counter = cpu_to_be32p(&wc);
|
|
rpmb_frame->block_count = cpu_to_be16p(&rpmb_req.blk_cnt);
|
|
|
|
if (left_size >= RPMB_SZ_DATA)
|
|
tran_size = RPMB_SZ_DATA;
|
|
else
|
|
tran_size = left_size;
|
|
|
|
memcpy(rpmb_frame->data,
|
|
param->databytes + iCnt * RPMB_SZ_DATA, tran_size);
|
|
|
|
hmac_sha256(param->keybytes, 32, rpmb_frame->data, 284,
|
|
rpmb_frame->mac);
|
|
|
|
ret = emmc_rpmb_req_handle(card, &rpmb_req);
|
|
if (ret) {
|
|
MSG(ERR, "%s, emmc_rpmb_req_handle IO error!!!(%x)\n",
|
|
__func__, ret);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Authenticate response write data frame.
|
|
*/
|
|
hmac_sha256(param->keybytes, 32, rpmb_frame->data, 284, hmac);
|
|
|
|
if (memcmp(hmac, rpmb_frame->mac, RPMB_SZ_MAC) != 0) {
|
|
MSG(ERR, "%s, hmac compare error!!!\n", __func__);
|
|
ret = RPMB_HMAC_ERROR;
|
|
break;
|
|
}
|
|
|
|
if (rpmb_frame->result) {
|
|
MSG(ERR, "%s, result error!!! (%x)\n", __func__,
|
|
cpu_to_be16p(&rpmb_frame->result));
|
|
ret = RPMB_RESULT_ERROR;
|
|
break;
|
|
}
|
|
|
|
if (cpu_to_be32p(&rpmb_frame->write_counter) != wc + 1) {
|
|
MSG(ERR, "%s, write counter error!!! (%x)\n", __func__,
|
|
cpu_to_be32p(&rpmb_frame->write_counter));
|
|
ret = RPMB_WC_ERROR;
|
|
break;
|
|
}
|
|
|
|
left_size -= tran_size;
|
|
blkaddr++;
|
|
}
|
|
|
|
kfree(rpmb_frame);
|
|
|
|
#endif
|
|
|
|
MSG(INFO, "%s end!!!\n", __func__);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int rpmb_req_ioctl_read_data_emmc(struct mmc_card *card,
|
|
struct rpmb_ioc_param *param)
|
|
{
|
|
struct emmc_rpmb_req rpmb_req;
|
|
/* if we put a large static buffer here, it will build fail.
|
|
* rpmb_frame[MAX_RPMB_TRANSFER_BLK];
|
|
* so I use dynamic alloc.
|
|
*/
|
|
struct s_rpmb *rpmb_frame;
|
|
u32 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;
|
|
int i, ret = 0;
|
|
#ifdef RPMB_MULTI_BLOCK_ACCESS
|
|
u32 size_for_hmac;
|
|
#endif
|
|
MSG(INFO, "%s start!!!\n", __func__);
|
|
|
|
i = 0;
|
|
tran_blkcnt = 0;
|
|
dataBuf = NULL;
|
|
dataBuf_start = NULL;
|
|
left_blkcnt = total_blkcnt = ((param->data_len % RPMB_SZ_DATA) ?
|
|
(param->data_len / RPMB_SZ_DATA + 1) :
|
|
(param->data_len / RPMB_SZ_DATA));
|
|
|
|
#ifdef RPMB_MULTI_BLOCK_ACCESS
|
|
|
|
blkaddr = param->addr;
|
|
|
|
while (left_blkcnt) {
|
|
|
|
if (left_blkcnt >= MAX_RPMB_TRANSFER_BLK)
|
|
tran_blkcnt = MAX_RPMB_TRANSFER_BLK;
|
|
else
|
|
tran_blkcnt = left_blkcnt;
|
|
|
|
MSG(INFO, "%s, left_blkcnt=%x, tran_blkcnt=%x\n", __func__,
|
|
left_blkcnt, tran_blkcnt);
|
|
|
|
/*
|
|
* initial buffer. (since HMAC computation of multi block needs
|
|
* multi buffer, pre-alloced it)
|
|
*/
|
|
rpmb_frame =
|
|
kzalloc(tran_blkcnt * sizeof(struct s_rpmb) + tran_blkcnt * 512, 0);
|
|
if (rpmb_frame == NULL)
|
|
return RPMB_ALLOC_ERROR;
|
|
|
|
dataBuf_start = dataBuf = (u8 *)(rpmb_frame + tran_blkcnt);
|
|
|
|
get_random_bytes(nonce, RPMB_SZ_NONCE);
|
|
|
|
/*
|
|
* Prepare request.
|
|
*/
|
|
rpmb_req.type = RPMB_READ_DATA;
|
|
rpmb_req.blk_cnt = tran_blkcnt;
|
|
rpmb_req.data_frame = (u8 *)rpmb_frame;
|
|
|
|
/*
|
|
* Prepare request read data frame. only need addr and nonce.
|
|
*/
|
|
rpmb_frame->request = cpu_to_be16p(&rpmb_req.type);
|
|
rpmb_frame->address = cpu_to_be16p(&blkaddr);
|
|
memcpy(rpmb_frame->nonce, nonce, RPMB_SZ_NONCE);
|
|
|
|
ret = emmc_rpmb_req_handle(card, &rpmb_req);
|
|
if (ret) {
|
|
MSG(ERR, "%s, emmc_rpmb_req_handle IO error!!!(%x)\n",
|
|
__func__, ret);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* STEP 3, 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 need to aggregate.
|
|
*/
|
|
rpmb_req_copy_data_for_hmac(dataBuf,
|
|
(struct rpmb_frame *) &rpmb_frame[iCnt]);
|
|
|
|
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),
|
|
rpmb_frame[iCnt].data,
|
|
tran_size);
|
|
left_size -= tran_size;
|
|
}
|
|
|
|
iCnt--;
|
|
|
|
/*
|
|
* Authenticate response read data frame.
|
|
*/
|
|
hmac_sha256(param->keybytes,
|
|
32, dataBuf_start, 284 * tran_blkcnt, hmac);
|
|
|
|
if (memcmp(hmac, rpmb_frame[iCnt].mac, RPMB_SZ_MAC) != 0) {
|
|
MSG(ERR, "%s, hmac compare error!!!\n", __func__);
|
|
ret = RPMB_HMAC_ERROR;
|
|
break;
|
|
}
|
|
|
|
if (memcmp(nonce, rpmb_frame[iCnt].nonce, RPMB_SZ_NONCE) != 0) {
|
|
MSG(ERR, "%s, nonce compare error!!!\n", __func__);
|
|
ret = RPMB_NONCE_ERROR;
|
|
break;
|
|
}
|
|
|
|
if (rpmb_frame[iCnt].result) {
|
|
MSG(ERR, "%s, result error!!! (%x)\n", __func__,
|
|
cpu_to_be16p(&rpmb_frame[iCnt].result));
|
|
ret = RPMB_RESULT_ERROR;
|
|
break;
|
|
}
|
|
|
|
blkaddr += tran_blkcnt;
|
|
left_blkcnt -= tran_blkcnt;
|
|
i++;
|
|
kfree(rpmb_frame);
|
|
};
|
|
|
|
if (ret)
|
|
kfree(rpmb_frame);
|
|
|
|
if (left_blkcnt || left_size) {
|
|
MSG(ERR, "left_blkcnt or left_size is not empty!!!!!!\n");
|
|
return RPMB_TRANSFER_NOT_COMPLETE;
|
|
}
|
|
|
|
#else
|
|
|
|
rpmb_frame = kzalloc(sizeof(struct s_rpmb), 0);
|
|
if (rpmb_frame == NULL)
|
|
return RPMB_ALLOC_ERROR;
|
|
|
|
blkaddr = param->addr;
|
|
|
|
for (iCnt = 0; iCnt < total_blkcnt; iCnt++) {
|
|
|
|
memset(rpmb_frame, 0, sizeof(struct s_rpmb));
|
|
get_random_bytes(nonce, RPMB_SZ_NONCE);
|
|
|
|
/*
|
|
* Prepare request.
|
|
*/
|
|
rpmb_req.type = RPMB_READ_DATA;
|
|
rpmb_req.blk_cnt = 1;
|
|
rpmb_req.data_frame = (u8 *)rpmb_frame;
|
|
|
|
/*
|
|
* Prepare request read data frame. only need addr and nonce.
|
|
*/
|
|
rpmb_frame->request = cpu_to_be16p(&rpmb_req.type);
|
|
rpmb_frame->address = cpu_to_be16p(&blkaddr);
|
|
memcpy(rpmb_frame->nonce, nonce, RPMB_SZ_NONCE);
|
|
|
|
ret = emmc_rpmb_req_handle(card, &rpmb_req);
|
|
if (ret) {
|
|
MSG(ERR, "%s, emmc_rpmb_req_handle IO error!!!(%x)\n",
|
|
__func__, ret);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Authenticate response read data frame.
|
|
*/
|
|
hmac_sha256(param->keybytes, 32, rpmb_frame->data, 284, hmac);
|
|
|
|
if (memcmp(hmac, rpmb_frame->mac, RPMB_SZ_MAC) != 0) {
|
|
MSG(ERR, "%s, hmac compare error!!!\n", __func__);
|
|
ret = RPMB_HMAC_ERROR;
|
|
break;
|
|
}
|
|
|
|
if (memcmp(nonce, rpmb_frame->nonce, RPMB_SZ_NONCE) != 0) {
|
|
MSG(ERR, "%s, nonce compare error!!!\n", __func__);
|
|
ret = RPMB_NONCE_ERROR;
|
|
break;
|
|
}
|
|
|
|
if (rpmb_frame->result) {
|
|
MSG(ERR, "%s, result error!!! (%x)\n", __func__,
|
|
cpu_to_be16p(&rpmb_frame->result));
|
|
ret = RPMB_RESULT_ERROR;
|
|
break;
|
|
}
|
|
|
|
if (left_size >= RPMB_SZ_DATA)
|
|
tran_size = RPMB_SZ_DATA;
|
|
else
|
|
tran_size = left_size;
|
|
|
|
memcpy(param->databytes + RPMB_SZ_DATA * iCnt,
|
|
rpmb_frame->data, tran_size);
|
|
|
|
left_size -= tran_size;
|
|
blkaddr++;
|
|
}
|
|
|
|
kfree(rpmb_frame);
|
|
|
|
#endif
|
|
|
|
MSG(INFO, "%s end!!!\n", __func__);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_TRUSTONIC_TEE_SUPPORT)
|
|
static int rpmb_gp_listenDci(void *arg)
|
|
{
|
|
enum mc_result mc_ret;
|
|
u32 cmdId;
|
|
struct mmc_host *mmc = mtk_mmc_host[0];
|
|
|
|
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);
|
|
|
|
#if IS_ENABLED(CONFIG_MMC_MTK_PRO)
|
|
/* Received exception. */
|
|
if (mmc && mmc->card)
|
|
rpmb_gp_execute_emmc(cmdId);
|
|
else
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_SCSI_UFS_MEDIATEK)
|
|
rpmb_gp_execute_ufs(cmdId);
|
|
#endif
|
|
/* 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 = MC_DRV_ERR_UNKNOWN;
|
|
|
|
MSG(INFO, "%s start\n", __func__);
|
|
|
|
do {
|
|
msleep(500);
|
|
|
|
/* open device */
|
|
mc_ret = mc_open_device(rpmb_gp_devid);
|
|
if (mc_ret == MC_DRV_ERR_NOT_IMPLEMENTED) {
|
|
MSG(INFO, "%s, mc_open_device not support\n", __func__);
|
|
break;
|
|
} else 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 < 240);
|
|
|
|
if (cnt >= 240)
|
|
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;
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_SCSI_UFS_MEDIATEK)
|
|
static long rpmb_ioctl_ufs(struct file *pfile, unsigned int cmd, unsigned long arg)
|
|
{
|
|
int err = 0;
|
|
unsigned long n;
|
|
struct rpmb_ioc_param param;
|
|
|
|
n = copy_from_user(¶m, (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_ufs(¶m);
|
|
|
|
if (err != 0) {
|
|
MSG(ERR, "%s, rpmb_req_ioctl_read_data_ufs() error!(%x)\n",
|
|
__func__, err);
|
|
break;
|
|
}
|
|
|
|
n = copy_to_user((void *)arg, ¶m, sizeof(param));
|
|
|
|
if (n != 0UL) {
|
|
MSG(ERR, "%s, copy to 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_ufs(¶m);
|
|
|
|
if (err != 0)
|
|
MSG(ERR, "%s, rpmb_req_ioctl_write_data_ufs() error!(%x)\n",
|
|
__func__, err);
|
|
|
|
break;
|
|
|
|
default:
|
|
MSG(ERR, "%s, wrong ioctl code (%d)!!!\n", __func__, cmd);
|
|
err = -ENOTTY;
|
|
break;
|
|
}
|
|
|
|
return err;
|
|
}
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_MMC_MTK_PRO)
|
|
long rpmb_ioctl_emmc(struct file *file, unsigned int cmd, unsigned long arg)
|
|
{
|
|
struct mmc_host *mmc = mtk_mmc_host[0];
|
|
struct mmc_card *card;
|
|
int ret = 0;
|
|
struct rpmb_ioc_param param;
|
|
unsigned char *ukey, *udata;
|
|
int err = 0;
|
|
|
|
if (!mmc || !mmc->card)
|
|
return -EFAULT;
|
|
|
|
card = mmc->card;
|
|
|
|
err = copy_from_user(¶m, (void *)arg, sizeof(param));
|
|
if (err) {
|
|
MSG(ERR, "%s, copy from user failed: %x\n", __func__, err);
|
|
return -EFAULT;
|
|
}
|
|
|
|
/* limit R/W arguments : less than RPMB area size
|
|
* follow block.c: limit transfer size 128K(don't use
|
|
* vmalloc for system performance)
|
|
*/
|
|
if ((param.data_len + param.addr * 256)
|
|
> card->ext_csd.raw_rpmb_size_mult * 128 * 1024 ||
|
|
param.data_len > MMC_IOC_MAX_BYTES)
|
|
return -EINVAL;
|
|
|
|
if (!param.keybytes || !param.databytes)
|
|
return -EFAULT;
|
|
|
|
ukey = param.keybytes;
|
|
udata = param.databytes;
|
|
param.keybytes = kmalloc(32, GFP_KERNEL);
|
|
|
|
/* follow block.c : at least one block(RPMB
|
|
* block size is:256) is allocated
|
|
*/
|
|
if (param.data_len < RPMB_SZ_DATA)
|
|
param.databytes = kmalloc(RPMB_SZ_DATA, GFP_KERNEL);
|
|
else
|
|
param.databytes = kmalloc(param.data_len, GFP_KERNEL);
|
|
if (param.keybytes) {
|
|
err = copy_from_user(param.keybytes, ukey, 32);
|
|
if (err != 0) {
|
|
MSG(ERR, "%s, err=%x\n", __func__, err);
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
} else {
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
|
|
if (param.databytes) {
|
|
err = copy_from_user(param.databytes, udata, param.data_len);
|
|
if (err != 0) {
|
|
MSG(ERR, "%s, err=%x\n", __func__, err);
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
} else {
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
|
|
switch (cmd) {
|
|
case RPMB_IOCTL_PROGRAM_KEY:
|
|
|
|
MSG(INFO, "%s, cmd = RPMB_IOCTL_PROGRAM_KEY!!!!!!!!!!!!!!\n",
|
|
__func__);
|
|
|
|
ret = emmc_rpmb_req_set_key(card, param.keybytes);
|
|
|
|
break;
|
|
|
|
case RPMB_IOCTL_READ_DATA:
|
|
|
|
MSG(INFO, "%s, cmd = RPMB_IOCTL_READ_DATA!!!!!!!!!!!!!!\n",
|
|
__func__);
|
|
|
|
ret = rpmb_req_ioctl_read_data_emmc(card, ¶m);
|
|
|
|
err = copy_to_user(udata, param.databytes, param.data_len);
|
|
if (err) {
|
|
MSG(ERR, "%s, err=%x\n", __func__, err);
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
|
|
break;
|
|
|
|
case RPMB_IOCTL_WRITE_DATA:
|
|
|
|
MSG(INFO, "%s, cmd = RPMB_IOCTL_WRITE_DATA!!!!!!!!!!!!!!\n",
|
|
__func__);
|
|
|
|
ret = rpmb_req_ioctl_write_data_emmc(card, ¶m);
|
|
|
|
break;
|
|
|
|
default:
|
|
MSG(ERR, "%s, wrong ioctl code (%d)!!!\n", __func__, cmd);
|
|
ret = -ENOTTY;
|
|
}
|
|
end:
|
|
kfree(param.databytes);
|
|
kfree(param.keybytes);
|
|
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static int rpmb_close(struct inode *pinode, struct file *pfile)
|
|
{
|
|
int ret = 0;
|
|
|
|
MSG(INFO, "%s, !!!!!!!!!!!!\n", __func__);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef __RPMB_KERNEL_NL_SUPPORT
|
|
static int rpmb_mtk_snd_msg(void *pbuf, u16 len)
|
|
{
|
|
struct sk_buff *skb = NULL;
|
|
struct nlmsghdr *nlh = NULL;
|
|
static int nlseq;
|
|
int ret;
|
|
|
|
skb = nlmsg_new(len, GFP_ATOMIC);
|
|
if (!skb) {
|
|
MSG(ERR, "%s netlink alloc failure\n", __func__);
|
|
ret = -ENOBUFS;
|
|
goto send_fail;
|
|
}
|
|
|
|
nlh = nlmsg_put(skb, nl_pid, nlseq, NETLINK_RPMB_MTK, len, 0);
|
|
if (!nlh) {
|
|
MSG(ERR, "%s nlmsg_put failure\n", __func__);
|
|
ret = -ENOBUFS;
|
|
nlmsg_free(skb);
|
|
goto send_fail_skb;
|
|
}
|
|
|
|
memcpy(nlmsg_data(nlh), pbuf, len);
|
|
|
|
ret = netlink_unicast(rpmb_mtk_sock, skb, nl_pid, MSG_DONTWAIT);
|
|
if (ret < 0) {
|
|
MSG(ERR, "%s send failed ret=%d, pid=%d\n",
|
|
__func__, ret, nl_pid);
|
|
goto send_fail;
|
|
}
|
|
|
|
return 0;
|
|
|
|
send_fail_skb:
|
|
kfree_skb(skb);
|
|
|
|
send_fail:
|
|
return ret;
|
|
}
|
|
|
|
static void rpmb_mtk_rcv_msg(struct sk_buff *skb)
|
|
{
|
|
struct nlmsghdr *nlh = NULL;
|
|
void *data;
|
|
struct nl_rpmb_send_req *req = &nl_rpmb_req;
|
|
|
|
if (skb->len >= NLMSG_HDRLEN) {
|
|
|
|
nlh = nlmsg_hdr(skb);
|
|
data = NLMSG_DATA(nlh);
|
|
|
|
/* Copy data to rpmb frame */
|
|
memcpy(req, data, 16);
|
|
if (req->read_size % 512 == 0 &&
|
|
req->read_size <= MAX_RPMB_REQUEST_SIZE) {
|
|
memcpy(&req->payload[0], (void *)(data + 16),
|
|
req->read_size);
|
|
}
|
|
|
|
/* Wakeup rpmb thread */
|
|
rpmb_done_flag = true;
|
|
wake_up(&wait_rpmb);
|
|
}
|
|
}
|
|
|
|
static int rpmb_create_netlink(void)
|
|
{
|
|
struct netlink_kernel_cfg cfg = {
|
|
.input = rpmb_mtk_rcv_msg,
|
|
};
|
|
|
|
rpmb_mtk_sock = netlink_kernel_create(&init_net,
|
|
NETLINK_RPMB_MTK, &cfg);
|
|
|
|
if (rpmb_mtk_sock == NULL) {
|
|
MSG(ERR, "netlink_kernel_create error\n");
|
|
return -EIO;
|
|
}
|
|
|
|
MSG(INFO, "%s netlink_kernel_create protocol = %d\n",
|
|
__func__, NETLINK_RPMB_MTK);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_SCSI_UFS_MEDIATEK)
|
|
static const struct file_operations rpmb_fops_ufs = {
|
|
.owner = THIS_MODULE,
|
|
.open = rpmb_open,
|
|
.release = rpmb_close,
|
|
.unlocked_ioctl = rpmb_ioctl_ufs,
|
|
.write = NULL,
|
|
.read = NULL,
|
|
};
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_MMC_MTK_PRO)
|
|
static const struct file_operations rpmb_fops_emmc = {
|
|
.owner = THIS_MODULE,
|
|
.open = rpmb_open,
|
|
.release = rpmb_close,
|
|
.unlocked_ioctl = rpmb_ioctl_emmc,
|
|
.write = NULL,
|
|
.read = NULL,
|
|
};
|
|
|
|
static int dt_get_boot_type(void)
|
|
{
|
|
struct tag_bootmode *tags = NULL;
|
|
struct device_node *node = NULL;
|
|
unsigned long size = 0;
|
|
int ret = BOOTDEV_UFS;
|
|
|
|
node = of_find_node_by_path("/chosen");
|
|
if (!node)
|
|
node = of_find_node_by_path("/chosen@0");
|
|
|
|
if (node) {
|
|
tags = (struct tag_bootmode *)of_get_property(node,
|
|
"atag,boot", (int *)&size);
|
|
} else
|
|
pr_notice("[%s] of_chosen not found\n", __func__);
|
|
|
|
if (tags) {
|
|
ret = tags->boottype;
|
|
if ((ret > 2) || (ret < 0))
|
|
ret = BOOTDEV_SDMMC;
|
|
} else {
|
|
pr_notice("[%s] 'atag,boot' is not found\n", __func__);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int mmc_rpmb_register(struct mmc_host *mmc)
|
|
{
|
|
int ret = 0;
|
|
|
|
if (!(mmc->caps2 & MMC_CAP2_NO_MMC))
|
|
mtk_mmc_host[0] = mmc;
|
|
else if (!(mmc->caps2 & MMC_CAP2_NO_SD))
|
|
return ret;
|
|
else
|
|
return -EINVAL;
|
|
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL_GPL(mmc_rpmb_register);
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_TEEGRIS_TEE_SUPPORT)
|
|
TEEC_UUID uuid_client = {
|
|
.timeLow = 0x00000000,
|
|
.timeMid = 0x4D54,
|
|
.timeHiAndVersion = 0x4B5F,
|
|
.clockSeqAndNode = {0x42, 0x46, 0x72, 0x70, 0x6D, 0x62, 0x74, 0x61},
|
|
};
|
|
|
|
enum cmd_invoke {
|
|
CMD_WAITING_NOTIFY,
|
|
CMD_DONE_NOTIFY,
|
|
CMD_ERROR_NOTIFY,
|
|
};
|
|
|
|
struct TEE_GP_SESSION_DATA {
|
|
TEEC_Context context;
|
|
TEEC_Session session;
|
|
void *wsm_buffer;
|
|
};
|
|
|
|
static struct TEE_GP_SESSION_DATA *rpmb_gp_session;
|
|
|
|
#define TEEGRIS_OPEN_SESSION_TMO 100
|
|
TEEC_Result teegris_rpmb_open_session(TEEC_Context *context,
|
|
TEEC_Operation *operation, TEEC_Session *session)
|
|
{
|
|
TEEC_Result res;
|
|
uint32_t returnOrigin;
|
|
int cnt = 0;
|
|
|
|
do {
|
|
res = TEEC_OpenSession(context, session, &uuid_client,
|
|
0, NULL, operation, &returnOrigin);
|
|
if (!res) {
|
|
MSG(INFO, "%s:open session success\n", __func__);
|
|
break;
|
|
}
|
|
cnt++;
|
|
if (cnt == TEEGRIS_OPEN_SESSION_TMO) {
|
|
MSG(ERR, "%s:open session fail,err:0x%x\n",
|
|
__func__, res);
|
|
break;
|
|
}
|
|
msleep(500);
|
|
} while (1);
|
|
|
|
return res;
|
|
}
|
|
|
|
static int check_tee_return(TEEC_Result res)
|
|
{
|
|
if (res == TEEC_ERROR_TARGET_DEAD) {
|
|
#ifdef CONFIG_MTK_UFS_DEBUG
|
|
BUG();
|
|
#endif
|
|
return 1;
|
|
} else if (res != TEEC_SUCCESS) {
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
TEEC_Result teegris_rpmb_run(TEEC_Context *context)
|
|
{
|
|
TEEC_Result res;
|
|
TEEC_Operation operation;
|
|
uint32_t returnOrigin;
|
|
u32 cmdId;
|
|
#if IS_ENABLED(CONFIG_MMC_MTK_PRO)
|
|
struct mmc_host *mmc = mtk_mmc_host[0];
|
|
#endif
|
|
memset(&operation, 0, sizeof(TEEC_Operation));
|
|
|
|
operation.paramTypes = TEEC_PARAM_TYPES(TEEC_VALUE_INOUT,
|
|
TEEC_NONE, TEEC_NONE, TEEC_NONE);
|
|
|
|
operation.params[0].value.a =
|
|
virt_to_phys(rpmb_gp_session->wsm_buffer) & 0xFFFFFFFF;
|
|
operation.params[0].value.b =
|
|
(virt_to_phys(rpmb_gp_session->wsm_buffer)) >> 32;
|
|
|
|
res = teegris_rpmb_open_session(context, &operation,
|
|
&rpmb_gp_session->session);
|
|
if (res)
|
|
goto exit;
|
|
while (1) {
|
|
MSG(ERR, "%s: wait swd notify\n", __func__);
|
|
/* wait swd notify */
|
|
res = TEEC_InvokeCommand(&rpmb_gp_session->session,
|
|
CMD_WAITING_NOTIFY, &operation, &returnOrigin);
|
|
if (res != TEEC_SUCCESS)
|
|
goto exit;
|
|
|
|
rpmb_gp_dci =
|
|
(struct dciMessage_t *)(rpmb_gp_session->wsm_buffer);
|
|
|
|
cmdId = rpmb_gp_dci->command.header.commandId;
|
|
|
|
MSG(ERR, "%s: cmd wait notify done!! cmdId = %x\n",
|
|
__func__, cmdId);
|
|
|
|
mutex_lock(&rpmb_mutex);
|
|
#if IS_ENABLED(CONFIG_MMC_MTK_PRO)
|
|
/* Received exception. */
|
|
if (mmc && mmc->card)
|
|
rpmb_gp_execute_emmc(cmdId);
|
|
else
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_SCSI_UFS_MEDIATEK)
|
|
rpmb_gp_execute_ufs(cmdId);
|
|
#endif
|
|
mutex_unlock(&rpmb_mutex);
|
|
|
|
switch (cmdId) {
|
|
case DCI_RPMB_CMD_READ_DATA:
|
|
case DCI_RPMB_CMD_GET_WCNT:
|
|
case DCI_RPMB_CMD_WRITE_DATA:
|
|
case DCI_RPMB_CMD_PROGRAM_KEY:
|
|
res = TEEC_InvokeCommand(&rpmb_gp_session->session,
|
|
CMD_DONE_NOTIFY,
|
|
&operation,
|
|
&returnOrigin);
|
|
break;
|
|
default:
|
|
res = TEEC_InvokeCommand(&rpmb_gp_session->session,
|
|
CMD_ERROR_NOTIFY,
|
|
&operation,
|
|
&returnOrigin);
|
|
break;
|
|
}
|
|
if (res != TEEC_SUCCESS)
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
TEEC_CloseSession(&rpmb_gp_session->session);
|
|
return res;
|
|
}
|
|
|
|
#define TEEGRIS_INIT_CONTEXT_TMO 100
|
|
#define ALLOCATED_DCI_MSG_SIZE (sizeof(struct dciMessage_t))
|
|
int teegris_rpmb_thread(void *data)
|
|
{
|
|
int cnt = 0;
|
|
int recovery_cnt = 0;
|
|
TEEC_Result res = -1;
|
|
|
|
recovery:
|
|
rpmb_gp_session =
|
|
kzalloc(sizeof(struct TEE_GP_SESSION_DATA), GFP_KERNEL);
|
|
if (!rpmb_gp_session) {
|
|
MSG(ERR, "failed to alloc rpmb_gp_session\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
do {
|
|
res = TEEC_InitializeContext(NULL, &rpmb_gp_session->context);
|
|
if (!res)
|
|
break;
|
|
cnt++;
|
|
if (cnt == TEEGRIS_INIT_CONTEXT_TMO) {
|
|
MSG(ERR, "failed to init context,err:0x%x\n", res);
|
|
return res;
|
|
}
|
|
msleep(500);
|
|
} while (1);
|
|
|
|
rpmb_gp_session->wsm_buffer =
|
|
kzalloc(ALLOCATED_DCI_MSG_SIZE, GFP_KERNEL);
|
|
if (!rpmb_gp_session->wsm_buffer) {
|
|
MSG(ERR, "failed to allocate wsm buffer\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
res = teegris_rpmb_run(&rpmb_gp_session->context);
|
|
if (check_tee_return(res)) {
|
|
kfree(rpmb_gp_session->wsm_buffer);
|
|
recovery_cnt++;
|
|
MSG(ERR, "RPMB recovery cnt %d\n", recovery_cnt);
|
|
}
|
|
|
|
MSG(ERR, "TEEC_FinalizeContex\n", res);
|
|
|
|
TEEC_FinalizeContext(&rpmb_gp_session->context);
|
|
|
|
kfree(rpmb_gp_session);
|
|
|
|
goto recovery;
|
|
|
|
return (res != TEEC_SUCCESS);
|
|
}
|
|
|
|
static struct rpmb_ctx *create_rpmb_ctx(void)
|
|
{
|
|
struct rpmb_ctx *ctx;
|
|
|
|
ctx = kzalloc(sizeof(struct rpmb_ctx), GFP_KERNEL);
|
|
if (!ctx) {
|
|
MSG(ERR, "%s rpmb context alloc failed\n", __func__);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
return ctx;
|
|
}
|
|
|
|
static int init_rpmb_wsm(struct rpmb_ctx *ctx)
|
|
{
|
|
ctx->wsm_vaddr = kzalloc(sizeof(struct rpmb_req) + MAX_RPMB_REQUEST_SIZE, GFP_KERNEL);
|
|
if (!ctx->wsm_vaddr) {
|
|
MSG(ERR, "%s failed to alloc rpmb wsm\n", __func__);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
MSG(INFO, "%s rpmb dma ddr : virt(%pK)\n",
|
|
__func__, (uint64_t)ctx->wsm_vaddr);
|
|
|
|
ctx->req = (struct rpmb_req *)ctx->wsm_vaddr;
|
|
return 0;
|
|
}
|
|
|
|
static struct sock_desc *accept_swd_connection(void)
|
|
{
|
|
int ret;
|
|
struct sock_desc *rpmb_conn;
|
|
struct sock_desc *srpmb_listen;
|
|
|
|
srpmb_listen = tz_iwsock_socket(1, 0);
|
|
if (IS_ERR(srpmb_listen)) {
|
|
MSG(ERR, "failed to create iwd socket, err = %ld\n", PTR_ERR(srpmb_listen));
|
|
return srpmb_listen;
|
|
}
|
|
MSG(INFO, "Created socket\n");
|
|
|
|
ret = tz_iwsock_listen(srpmb_listen, RPMB_SOCKET_NAME);
|
|
if (ret) {
|
|
MSG(ERR, "failed make iwd socket listening, err = %d\n", ret);
|
|
rpmb_conn = ERR_PTR(ret);
|
|
goto out;
|
|
}
|
|
MSG(INFO, "Make socket listening\n");
|
|
|
|
/* Accept connection */
|
|
rpmb_conn = tz_iwsock_accept(srpmb_listen);
|
|
if (IS_ERR(rpmb_conn)) {
|
|
MSG(ERR, "failed to accept connection, err = %ld\n", PTR_ERR(rpmb_conn));
|
|
goto out;
|
|
}
|
|
MSG(INFO, "Accepted connection\n");
|
|
out:
|
|
tz_iwsock_release(srpmb_listen);
|
|
|
|
return rpmb_conn;
|
|
}
|
|
|
|
static int register_rpmb_wsm(struct rpmb_ctx *ctx, struct sock_desc *rpmb_conn)
|
|
{
|
|
int ret;
|
|
ssize_t len;
|
|
uint64_t sock_data;
|
|
|
|
len = tz_iwsock_read(rpmb_conn, &sock_data, sizeof(sock_data), 0);
|
|
if (len > 0 && len != sizeof(sock_data)) {
|
|
MSG(ERR, "failed to receive request, invalid len = %zd\n", len);
|
|
return -EMSGSIZE;
|
|
} else if (!len) {
|
|
MSG(ERR, "connection was reset by peer\n");
|
|
return -ECONNRESET;
|
|
} else if (len < 0) {
|
|
ret = len;
|
|
MSG(ERR, "error while receiving request from SWd, err = %u\n", ret);
|
|
return ret;
|
|
}
|
|
if (sock_data != RPMB_IPC_MAGIC) {
|
|
MSG(ERR, "received invalied request data = %llx\n", sock_data);
|
|
return -EINVAL;
|
|
}
|
|
|
|
sock_data = virt_to_phys(ctx->wsm_vaddr);
|
|
len = tz_iwsock_write(rpmb_conn, &sock_data, sizeof(sock_data), 0);
|
|
if (len != sizeof(sock_data)) {
|
|
ret = len >= 0 ? -EMSGSIZE : len;
|
|
MSG(ERR, "failed to send reply, err = %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
MSG(INFO, "%s registered WSM success\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static int rpmb_wait_request(struct sock_desc *rpmb_conn)
|
|
{
|
|
int ret;
|
|
ssize_t len;
|
|
unsigned int sock_data;
|
|
|
|
MSG(INFO, "%s Read wait\n", __func__);
|
|
len = tz_iwsock_read(rpmb_conn, &sock_data, sizeof(sock_data), 0);
|
|
if (len > 0 && len != sizeof(sock_data)) {
|
|
MSG(ERR, "%s failed to receive request, invalid len = %zd\n", __func__, len);
|
|
return -EMSGSIZE;
|
|
} else if (!len) {
|
|
MSG(ERR, "%s connection was reset by peer\n", __func__);
|
|
return -ECONNRESET;
|
|
} else if (len < 0) {
|
|
ret = len;
|
|
MSG(ERR, "%s error while receiving request from SWd, err = %u\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
MSG(INFO, "%s Read request\n", __func__);
|
|
|
|
if (sock_data != RPMB_REQUEST_MAGIC) {
|
|
MSG(ERR, "%s received invalied request data = %u\n", __func__, sock_data);
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int rpmb_send_reply(struct sock_desc *rpmb_conn)
|
|
{
|
|
int ret;
|
|
ssize_t len;
|
|
unsigned int sock_data;
|
|
|
|
sock_data = RPMB_REPLY_MAGIC;
|
|
|
|
len = tz_iwsock_write(rpmb_conn, &sock_data, sizeof(sock_data), 0);
|
|
if (len != sizeof(sock_data)) {
|
|
ret = len >= 0 ? -EMSGSIZE : len;
|
|
MSG(ERR, "%s failed to send reply, err = %d\n", __func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
MSG(INFO, "%s Sent reply\n", __func__);
|
|
return 0;
|
|
}
|
|
|
|
static inline void release_rpmb_sock(struct sock_desc *rpmb_conn)
|
|
{
|
|
tz_iwsock_release(rpmb_conn);
|
|
}
|
|
|
|
static inline void release_rpmb_wsm(struct rpmb_ctx *ctx)
|
|
{
|
|
kfree(ctx->wsm_vaddr);
|
|
}
|
|
|
|
static inline void destroy_rpmb_ctx(struct rpmb_ctx *ctx)
|
|
{
|
|
kfree(ctx);
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_SCSI_UFS_MEDIATEK)
|
|
static void rpmb_iwsock_execute_ufs(struct rpmb_ctx *ctx)
|
|
{
|
|
int ret = 0;
|
|
|
|
switch (ctx->req->type) {
|
|
case RPMB_READ_DATA:
|
|
|
|
MSG(DBG_INFO, "%s: DCI_RPMB_CMD_READ_DATA\n", __func__);
|
|
|
|
ret = rpmb_req_read_data_ufs(ctx->req->frame,
|
|
ctx->req->blks);
|
|
break;
|
|
case RPMB_GET_WRITE_COUNTER:
|
|
|
|
MSG(DBG_INFO, "%s: DCI_RPMB_CMD_GET_WCNT\n", __func__);
|
|
|
|
ret = rpmb_req_get_wc_ufs(NULL, NULL,
|
|
ctx->req->frame);
|
|
break;
|
|
case RPMB_WRITE_DATA:
|
|
|
|
MSG(DBG_INFO, "%s: DCI_RPMB_CMD_WRITE_DATA\n", __func__);
|
|
|
|
ret = rpmb_req_write_data_ufs(ctx->req->frame,
|
|
ctx->req->blks);
|
|
|
|
break;
|
|
default:
|
|
MSG(ERR, "%s: receive an unknown command id(%d).\n",
|
|
__func__, ctx->req->type);
|
|
break;
|
|
}
|
|
|
|
if (ret)
|
|
MSG(ERR, "%s: Error ret(%d).\n", __func__, ret);
|
|
}
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_MMC_MTK_PRO)
|
|
static void rpmb_iwsock_execute_emmc(struct rpmb_ctx *ctx)
|
|
{
|
|
int ret = 0;
|
|
|
|
struct mmc_host *mmc = mtk_mmc_host[0];
|
|
struct mmc_card *card = mmc->card;
|
|
struct emmc_rpmb_req rpmb_req;
|
|
|
|
switch (ctx->req->type) {
|
|
case RPMB_READ_DATA:
|
|
MSG(INFO, "%s: DCI_RPMB_CMD_READ_DATA.\n", __func__);
|
|
|
|
rpmb_req.type = RPMB_READ_DATA;
|
|
rpmb_req.blk_cnt = ctx->req->blks;
|
|
rpmb_req.addr = ctx->req->addr;
|
|
rpmb_req.data_frame = ctx->req->frame;
|
|
|
|
ret = emmc_rpmb_req_handle(card, &rpmb_req);
|
|
if (ret)
|
|
MSG(ERR, "%s, emmc_rpmb_req_handle failed!!(%x)\n",
|
|
__func__, ret);
|
|
|
|
break;
|
|
|
|
case RPMB_GET_WRITE_COUNTER:
|
|
MSG(INFO, "%s: DCI_RPMB_CMD_GET_WCNT.\n", __func__);
|
|
|
|
rpmb_req.type = RPMB_GET_WRITE_COUNTER;
|
|
rpmb_req.blk_cnt = ctx->req->blks;
|
|
rpmb_req.addr = ctx->req->addr;
|
|
rpmb_req.data_frame = ctx->req->frame;
|
|
|
|
ret = emmc_rpmb_req_handle(card, &rpmb_req);
|
|
if (ret)
|
|
MSG(ERR, "%s, emmc_rpmb_req_handle failed!!(%x)\n",
|
|
__func__, ret);
|
|
|
|
break;
|
|
|
|
case RPMB_WRITE_DATA:
|
|
MSG(INFO, "%s: DCI_RPMB_CMD_WRITE_DATA.\n", __func__);
|
|
|
|
rpmb_req.type = RPMB_WRITE_DATA;
|
|
rpmb_req.blk_cnt = ctx->req->blks;
|
|
rpmb_req.addr = ctx->req->addr;
|
|
rpmb_req.data_frame = ctx->req->frame;
|
|
|
|
ret = emmc_rpmb_req_handle(card, &rpmb_req);
|
|
if (ret)
|
|
MSG(ERR, "%s, emmc_rpmb_req_handle failed!!(%x)\n",
|
|
__func__, ret);
|
|
|
|
break;
|
|
default:
|
|
MSG(ERR, "%s: receive an unknown command id(%d).\n",
|
|
__func__, ctx->req->type);
|
|
break;
|
|
}
|
|
|
|
if (ret)
|
|
MSG(ERR, "%s: Error ret(%d).\n", __func__, ret);
|
|
}
|
|
#endif
|
|
|
|
static int rpmb_iwsock_thread(void *context)
|
|
{
|
|
int ret;
|
|
struct rpmb_ctx *ctx;
|
|
struct sock_desc *rpmb_conn;
|
|
#if IS_ENABLED(CONFIG_MMC_MTK_PRO)
|
|
struct mmc_host *mmc = mtk_mmc_host[0];
|
|
#endif
|
|
|
|
MSG(INFO, "%s teegris iwsock thread start\n", __func__);
|
|
|
|
ctx = create_rpmb_ctx();
|
|
if (IS_ERR(ctx)) {
|
|
MSG(ERR, "%s failed to alloc context error:%ld\n", __func__, PTR_ERR(ctx));
|
|
return PTR_ERR(ctx);
|
|
}
|
|
|
|
ret = init_rpmb_wsm(ctx);
|
|
if (ret) {
|
|
MSG(ERR, "failed to initialize wsm\n");
|
|
goto destroy_ctx;
|
|
}
|
|
|
|
rpmb_conn = accept_swd_connection();
|
|
if (IS_ERR(rpmb_conn)) {
|
|
MSG(ERR, "%s failed to connect swd error:%ld\n", __func__, PTR_ERR(rpmb_conn));
|
|
ret = PTR_ERR(rpmb_conn);
|
|
goto release_wsm;
|
|
}
|
|
|
|
ret = register_rpmb_wsm(ctx, rpmb_conn);
|
|
if (ret) {
|
|
MSG(ERR, "%s failed to register wsm\n", __func__);
|
|
goto release_sock;
|
|
}
|
|
|
|
/* rpmb kthread main function */
|
|
while (!kthread_should_stop()) {
|
|
ret = rpmb_wait_request(rpmb_conn);
|
|
if (ret)
|
|
goto release_sock;
|
|
|
|
mutex_lock(&rpmb_mutex);
|
|
#if IS_ENABLED(CONFIG_MMC_MTK_PRO)
|
|
/* Received exception. */
|
|
if (mmc && mmc->card)
|
|
rpmb_iwsock_execute_emmc(ctx);
|
|
else
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_SCSI_UFS_MEDIATEK)
|
|
rpmb_iwsock_execute_ufs(ctx);
|
|
#endif
|
|
mutex_unlock(&rpmb_mutex);
|
|
|
|
ret = rpmb_send_reply(rpmb_conn);
|
|
if (ret) {
|
|
MSG(ERR, "rpmb send reply error : %d\n", ret);
|
|
goto release_sock;
|
|
}
|
|
}
|
|
|
|
release_sock:
|
|
release_rpmb_sock(rpmb_conn);
|
|
release_wsm:
|
|
release_rpmb_wsm(ctx);
|
|
destroy_ctx:
|
|
destroy_rpmb_ctx(ctx);
|
|
return ret;
|
|
}
|
|
#endif
|
|
|
|
static int __init rpmb_init(void)
|
|
{
|
|
int alloc_ret;
|
|
int cdev_ret = -1;
|
|
unsigned int major;
|
|
dev_t dev = 0;
|
|
struct device *device = NULL;
|
|
#if IS_ENABLED(CONFIG_TRUSTONIC_TEE_SUPPORT)
|
|
struct device_node *mobicore_node;
|
|
int ret = 0;
|
|
u32 real_drv;
|
|
#endif
|
|
|
|
MSG(INFO, "%s start\n", __func__);
|
|
|
|
alloc_ret = alloc_chrdev_region(&dev, 0, 1, RPMB_NAME);
|
|
|
|
if (alloc_ret) {
|
|
MSG(ERR, "%s, init alloc_chrdev_region failed!\n", __func__);
|
|
goto error;
|
|
}
|
|
|
|
major = MAJOR(dev);
|
|
|
|
#if IS_ENABLED(CONFIG_MMC_MTK_PRO)
|
|
if (dt_get_boot_type() == BOOTDEV_SDMMC)
|
|
cdev_init(&rpmb_cdev, &rpmb_fops_emmc);
|
|
else
|
|
#endif
|
|
#if IS_ENABLED(CONFIG_SCSI_UFS_MEDIATEK)
|
|
cdev_init(&rpmb_cdev, &rpmb_fops_ufs);
|
|
#else
|
|
return -EFAULT;
|
|
#endif
|
|
rpmb_cdev.owner = THIS_MODULE;
|
|
|
|
cdev_ret = cdev_add(&rpmb_cdev, MKDEV(major, 0U), 1);
|
|
if (cdev_ret) {
|
|
MSG(ERR, "%s, init cdev_add failed!\n", __func__);
|
|
goto error;
|
|
}
|
|
|
|
#ifdef __RPMB_IOCTL_SUPPORT
|
|
mtk_rpmb_class = class_create(THIS_MODULE, RPMB_NAME);
|
|
|
|
if (IS_ERR(mtk_rpmb_class)) {
|
|
MSG(ERR, "%s, init class_create failed!\n", __func__);
|
|
goto error;
|
|
}
|
|
|
|
device = device_create(mtk_rpmb_class, NULL, MKDEV(major, 0), NULL,
|
|
RPMB_NAME "%d", 0);
|
|
#endif
|
|
if (IS_ERR(device)) {
|
|
MSG(ERR, "%s, init device_create failed!\n", __func__);
|
|
goto error;
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_TEEGRIS_TEE_SUPPORT)
|
|
open_th = kthread_run(teegris_rpmb_thread, NULL, "teegris rpmb");
|
|
if (IS_ERR(open_th))
|
|
MSG(ERR, "%s, init kthread_run failed!\n", __func__);
|
|
|
|
iwsock_th = kthread_run(rpmb_iwsock_thread, NULL, "rpmb_iwsock");
|
|
if (IS_ERR(iwsock_th))
|
|
MSG(ERR, "%s, rpmb iwsock kthread_run failed!\n", __func__);
|
|
#endif
|
|
|
|
#if IS_ENABLED(CONFIG_TRUSTONIC_TEE_SUPPORT)
|
|
mobicore_node = of_find_compatible_node(NULL, NULL,
|
|
"trustonic,mobicore");
|
|
if (!mobicore_node) {
|
|
MSG(ERR, "find trustonic,mobicore fail\n");
|
|
goto fake_out;
|
|
}
|
|
|
|
ret = of_property_read_u32(mobicore_node,
|
|
"trustonic,real-drv", &real_drv);
|
|
if (ret || !real_drv) {
|
|
MSG(INFO, "MobiCore dummy driver, ret=%d, real_drv=%d",
|
|
ret, real_drv);
|
|
goto fake_out;
|
|
}
|
|
|
|
open_th = kthread_run(rpmb_thread, NULL, "rpmb_open");
|
|
if (IS_ERR(open_th))
|
|
MSG(ERR, "%s, init kthread_run failed!\n", __func__);
|
|
|
|
#ifdef __RPMB_KERNEL_NL_SUPPORT
|
|
if (rpmb_create_netlink()) {
|
|
MSG(ERR, "%s, init netlink failed!\n", __func__);
|
|
goto error;
|
|
}
|
|
|
|
init_waitqueue_head(&wait_rpmb);
|
|
mutex_init(&rpmb_lock);
|
|
#endif
|
|
|
|
fake_out:
|
|
#endif
|
|
|
|
MSG(INFO, "%s end!!!!\n", __func__);
|
|
|
|
return 0;
|
|
|
|
error:
|
|
#ifdef __RPMB_IOCTL_SUPPORT
|
|
if (mtk_rpmb_class)
|
|
class_destroy(mtk_rpmb_class);
|
|
#endif
|
|
if (cdev_ret == 0)
|
|
cdev_del(&rpmb_cdev);
|
|
|
|
if (alloc_ret == 0)
|
|
unregister_chrdev_region(dev, 1);
|
|
|
|
return 0;
|
|
}
|
|
|
|
late_initcall(rpmb_init);
|
|
|
|
MODULE_DESCRIPTION("RPMB class");
|
|
MODULE_LICENSE("GPL v2");
|