]>
Commit | Line | Data |
---|---|---|
232cfd6d JW |
1 | // SPDX-License-Identifier: BSD-2-Clause |
2 | /* | |
3 | * Copyright (c) 2018 Linaro Limited | |
4 | */ | |
5 | ||
6 | #include <common.h> | |
7 | #include <dm.h> | |
8 | #include <log.h> | |
9 | #include <tee.h> | |
10 | #include <mmc.h> | |
336d4615 | 11 | #include <dm/device_compat.h> |
232cfd6d JW |
12 | |
13 | #include "optee_msg.h" | |
14 | #include "optee_private.h" | |
15 | ||
16 | /* | |
17 | * Request and response definitions must be in sync with the secure side of | |
18 | * OP-TEE. | |
19 | */ | |
20 | ||
21 | /* Request */ | |
22 | struct rpmb_req { | |
23 | u16 cmd; | |
24 | #define RPMB_CMD_DATA_REQ 0x00 | |
25 | #define RPMB_CMD_GET_DEV_INFO 0x01 | |
26 | u16 dev_id; | |
27 | u16 block_count; | |
28 | /* Optional data frames (rpmb_data_frame) follow */ | |
29 | }; | |
30 | ||
31 | #define RPMB_REQ_DATA(req) ((void *)((struct rpmb_req *)(req) + 1)) | |
32 | ||
33 | /* Response to device info request */ | |
34 | struct rpmb_dev_info { | |
35 | u8 cid[16]; | |
36 | u8 rpmb_size_mult; /* EXT CSD-slice 168: RPMB Size */ | |
37 | u8 rel_wr_sec_c; /* EXT CSD-slice 222: Reliable Write Sector */ | |
38 | /* Count */ | |
39 | u8 ret_code; | |
40 | #define RPMB_CMD_GET_DEV_INFO_RET_OK 0x00 | |
41 | #define RPMB_CMD_GET_DEV_INFO_RET_ERROR 0x01 | |
42 | }; | |
43 | ||
44 | static void release_mmc(struct optee_private *priv) | |
45 | { | |
46 | int rc; | |
47 | ||
48 | if (!priv->rpmb_mmc) | |
49 | return; | |
50 | ||
51 | rc = blk_select_hwpart_devnum(IF_TYPE_MMC, priv->rpmb_dev_id, | |
52 | priv->rpmb_original_part); | |
53 | if (rc) | |
54 | debug("%s: blk_select_hwpart_devnum() failed: %d\n", | |
55 | __func__, rc); | |
56 | ||
57 | priv->rpmb_mmc = NULL; | |
58 | } | |
59 | ||
60 | static struct mmc *get_mmc(struct optee_private *priv, int dev_id) | |
61 | { | |
62 | struct mmc *mmc; | |
63 | int rc; | |
64 | ||
65 | if (priv->rpmb_mmc && priv->rpmb_dev_id == dev_id) | |
66 | return priv->rpmb_mmc; | |
67 | ||
68 | release_mmc(priv); | |
69 | ||
70 | mmc = find_mmc_device(dev_id); | |
71 | if (!mmc) { | |
72 | debug("Cannot find RPMB device\n"); | |
73 | return NULL; | |
74 | } | |
75 | if (!(mmc->version & MMC_VERSION_MMC)) { | |
76 | debug("Device id %d is not an eMMC device\n", dev_id); | |
77 | return NULL; | |
78 | } | |
79 | if (mmc->version < MMC_VERSION_4_41) { | |
80 | debug("Device id %d: RPMB not supported before version 4.41\n", | |
81 | dev_id); | |
82 | return NULL; | |
83 | } | |
84 | ||
85 | priv->rpmb_original_part = mmc_get_blk_desc(mmc)->hwpart; | |
86 | ||
87 | rc = blk_select_hwpart_devnum(IF_TYPE_MMC, dev_id, MMC_PART_RPMB); | |
88 | if (rc) { | |
89 | debug("Device id %d: cannot select RPMB partition: %d\n", | |
90 | dev_id, rc); | |
91 | return NULL; | |
92 | } | |
93 | ||
94 | priv->rpmb_mmc = mmc; | |
95 | priv->rpmb_dev_id = dev_id; | |
96 | return mmc; | |
97 | } | |
98 | ||
99 | static u32 rpmb_get_dev_info(u16 dev_id, struct rpmb_dev_info *info) | |
100 | { | |
101 | struct mmc *mmc = find_mmc_device(dev_id); | |
2464b229 | 102 | int i; |
232cfd6d JW |
103 | |
104 | if (!mmc) | |
105 | return TEE_ERROR_ITEM_NOT_FOUND; | |
106 | ||
107 | if (!mmc->ext_csd) | |
108 | return TEE_ERROR_GENERIC; | |
109 | ||
2464b229 JRO |
110 | for (i = 0; i < ARRAY_SIZE(mmc->cid); i++) |
111 | ((u32 *) info->cid)[i] = cpu_to_be32(mmc->cid[i]); | |
112 | ||
232cfd6d JW |
113 | info->rel_wr_sec_c = mmc->ext_csd[222]; |
114 | info->rpmb_size_mult = mmc->ext_csd[168]; | |
115 | info->ret_code = RPMB_CMD_GET_DEV_INFO_RET_OK; | |
116 | ||
117 | return TEE_SUCCESS; | |
118 | } | |
119 | ||
120 | static u32 rpmb_process_request(struct optee_private *priv, void *req, | |
121 | ulong req_size, void *rsp, ulong rsp_size) | |
122 | { | |
123 | struct rpmb_req *sreq = req; | |
124 | struct mmc *mmc; | |
125 | ||
126 | if (req_size < sizeof(*sreq)) | |
127 | return TEE_ERROR_BAD_PARAMETERS; | |
128 | ||
129 | switch (sreq->cmd) { | |
130 | case RPMB_CMD_DATA_REQ: | |
131 | mmc = get_mmc(priv, sreq->dev_id); | |
132 | if (!mmc) | |
133 | return TEE_ERROR_ITEM_NOT_FOUND; | |
134 | if (mmc_rpmb_route_frames(mmc, RPMB_REQ_DATA(req), | |
135 | req_size - sizeof(struct rpmb_req), | |
136 | rsp, rsp_size)) | |
137 | return TEE_ERROR_BAD_PARAMETERS; | |
138 | return TEE_SUCCESS; | |
139 | ||
140 | case RPMB_CMD_GET_DEV_INFO: | |
141 | if (req_size != sizeof(struct rpmb_req) || | |
142 | rsp_size != sizeof(struct rpmb_dev_info)) { | |
143 | debug("Invalid req/rsp size\n"); | |
144 | return TEE_ERROR_BAD_PARAMETERS; | |
145 | } | |
146 | return rpmb_get_dev_info(sreq->dev_id, rsp); | |
147 | ||
148 | default: | |
149 | debug("Unsupported RPMB command: %d\n", sreq->cmd); | |
150 | return TEE_ERROR_BAD_PARAMETERS; | |
151 | } | |
152 | } | |
153 | ||
154 | void optee_suppl_cmd_rpmb(struct udevice *dev, struct optee_msg_arg *arg) | |
155 | { | |
156 | struct tee_shm *req_shm; | |
157 | struct tee_shm *rsp_shm; | |
158 | void *req_buf; | |
159 | void *rsp_buf; | |
160 | ulong req_size; | |
161 | ulong rsp_size; | |
162 | ||
163 | if (arg->num_params != 2 || | |
164 | arg->params[0].attr != OPTEE_MSG_ATTR_TYPE_RMEM_INPUT || | |
165 | arg->params[1].attr != OPTEE_MSG_ATTR_TYPE_RMEM_OUTPUT) { | |
166 | arg->ret = TEE_ERROR_BAD_PARAMETERS; | |
167 | return; | |
168 | } | |
169 | ||
170 | req_shm = (struct tee_shm *)(ulong)arg->params[0].u.rmem.shm_ref; | |
171 | req_buf = (u8 *)req_shm->addr + arg->params[0].u.rmem.offs; | |
172 | req_size = arg->params[0].u.rmem.size; | |
173 | ||
174 | rsp_shm = (struct tee_shm *)(ulong)arg->params[1].u.rmem.shm_ref; | |
175 | rsp_buf = (u8 *)rsp_shm->addr + arg->params[1].u.rmem.offs; | |
176 | rsp_size = arg->params[1].u.rmem.size; | |
177 | ||
178 | arg->ret = rpmb_process_request(dev_get_priv(dev), req_buf, req_size, | |
179 | rsp_buf, rsp_size); | |
180 | } | |
181 | ||
182 | void optee_suppl_rpmb_release(struct udevice *dev) | |
183 | { | |
184 | release_mmc(dev_get_priv(dev)); | |
185 | } |