blob: 00e2d26d7fa20d395ab46dd1038930db78c11080
1 | /* |
2 | * Copyright (C) 2015 The Android Open Source Project |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
5 | * you may not use this file except in compliance with the License. |
6 | * You may obtain a copy of the License at |
7 | * |
8 | * http://www.apache.org/licenses/LICENSE-2.0 |
9 | * |
10 | * Unless required by applicable law or agreed to in writing, software |
11 | * distributed under the License is distributed on an "AS IS" BASIS, |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
13 | * See the License for the specific language governing permissions and |
14 | * limitations under the License. |
15 | */ |
16 | |
17 | #define LOG_TAG "AmlogicKeymaster" |
18 | |
19 | // TODO: make this generic in libtrusty |
20 | |
21 | #include <errno.h> |
22 | #include <stdlib.h> |
23 | #include <string.h> |
24 | #include <sys/uio.h> |
25 | #include <unistd.h> |
26 | |
27 | #include <algorithm> |
28 | |
29 | #include <log/log.h> |
30 | #if !AMLOGIC_MODIFY |
31 | #include <trusty/tipc.h> |
32 | #endif |
33 | #include <amlogic_keymaster/ipc/keymaster_ipc.h> |
34 | #include <amlogic_keymaster/ipc/amlogic_keymaster_ipc.h> |
35 | |
36 | #if AMLOGIC_MODIFY |
37 | TEEC_Result aml_keymaster_connect(TEEC_Context *c, TEEC_Session *s) { |
38 | TEEC_Result result = TEEC_SUCCESS; |
39 | TEEC_UUID svc_id = TA_KEYMASTER_UUID; |
40 | TEEC_Operation operation; |
41 | uint32_t err_origin; |
42 | struct timespec time; |
43 | uint64_t millis = 0; |
44 | |
45 | memset(&operation, 0, sizeof(operation)); |
46 | |
47 | /* Initialize Context */ |
48 | result = TEEC_InitializeContext(NULL, c); |
49 | |
50 | if (result != TEEC_SUCCESS) { |
51 | ALOGD("TEEC_InitializeContext failed with error = %x\n", result); |
52 | return result; |
53 | } |
54 | /* Open Session */ |
55 | result = TEEC_OpenSession(c, s, &svc_id, |
56 | TEEC_LOGIN_PUBLIC, |
57 | NULL, NULL, |
58 | &err_origin); |
59 | |
60 | if (result != TEEC_SUCCESS) { |
61 | ALOGD("TEEC_Opensession failed with code 0x%x origin 0x%x",result, err_origin); |
62 | TEEC_FinalizeContext(c); |
63 | return result; |
64 | } |
65 | |
66 | int res = clock_gettime(CLOCK_BOOTTIME, &time); |
67 | if (res < 0) |
68 | millis = 0; |
69 | else |
70 | millis = (time.tv_sec * 1000) + (time.tv_nsec / 1000 / 1000); |
71 | |
72 | /* Init TA */ |
73 | operation.paramTypes = TEEC_PARAM_TYPES( |
74 | TEEC_VALUE_INPUT, TEEC_NONE, |
75 | TEEC_NONE, TEEC_NONE); |
76 | |
77 | operation.params[0].value.a = (millis >> 32); |
78 | operation.params[0].value.b = (millis & 0xffffffff); |
79 | |
80 | result = TEEC_InvokeCommand(s, |
81 | KM_TA_INIT, |
82 | &operation, |
83 | NULL); |
84 | |
85 | ALOGD("create id: %d, ctx: %p, ctx: %p\n", s->session_id, s->ctx, c); |
86 | return result; |
87 | } |
88 | |
89 | TEEC_Result aml_keymaster_call(TEEC_Session *s, uint32_t cmd, void* in, uint32_t in_size, uint8_t* out, |
90 | uint32_t* out_size) { |
91 | TEEC_Result res = TEEC_SUCCESS; |
92 | TEEC_Operation op; |
93 | uint32_t ret_orig; |
94 | |
95 | memset(&op, 0, sizeof(op)); |
96 | |
97 | op.params[0].tmpref.buffer = in; |
98 | op.params[0].tmpref.size = in_size; |
99 | op.params[1].tmpref.buffer = out; |
100 | op.params[1].tmpref.size = *out_size; |
101 | op.paramTypes = TEEC_PARAM_TYPES( |
102 | TEEC_MEMREF_TEMP_INPUT, |
103 | TEEC_MEMREF_TEMP_OUTPUT, |
104 | TEEC_VALUE_OUTPUT, |
105 | TEEC_NONE); |
106 | |
107 | ALOGD("id: %d, ctx: %p, cmd: %d\n", s->session_id, s->ctx, cmd); |
108 | res = TEEC_InvokeCommand(s, cmd, &op, &ret_orig); |
109 | if (res != TEEC_SUCCESS) { |
110 | ALOGE("Invoke cmd: %u failed with res(%x), ret_orig(%x), return(%d)\n", |
111 | cmd, res, ret_orig, op.params[2].value.a); |
112 | } else { |
113 | *out_size = op.params[2].value.b; |
114 | } |
115 | |
116 | return res; |
117 | } |
118 | |
119 | TEEC_Result aml_keymaster_disconnect(TEEC_Context *c, TEEC_Session *s) { |
120 | TEEC_Operation operation; |
121 | TEEC_Result result = TEEC_SUCCESS; |
122 | |
123 | operation.paramTypes = TEEC_PARAM_TYPES( |
124 | TEEC_NONE, TEEC_NONE, |
125 | TEEC_NONE, TEEC_NONE); |
126 | |
127 | result = TEEC_InvokeCommand(s, |
128 | KM_TA_TERM, |
129 | &operation, |
130 | NULL); |
131 | |
132 | TEEC_CloseSession(s); |
133 | TEEC_FinalizeContext(c); |
134 | |
135 | return result; |
136 | } |
137 | |
138 | keymaster_error_t aml_keymaster_send(TEEC_Session *s, uint32_t command, const keymaster::Serializable& req, |
139 | keymaster::KeymasterResponse* rsp) { |
140 | uint32_t req_size = req.SerializedSize(); |
141 | if (req_size > AMLOGIC_KEYMASTER_SEND_BUF_SIZE) { |
142 | ALOGE("Request too big: %u Max size: %u", req_size, AMLOGIC_KEYMASTER_SEND_BUF_SIZE); |
143 | return KM_ERROR_INVALID_INPUT_LENGTH; |
144 | } |
145 | |
146 | uint8_t send_buf[AMLOGIC_KEYMASTER_SEND_BUF_SIZE]; |
147 | keymaster::Eraser send_buf_eraser(send_buf, AMLOGIC_KEYMASTER_SEND_BUF_SIZE); |
148 | req.Serialize(send_buf, send_buf + req_size); |
149 | |
150 | // Send it |
151 | uint8_t recv_buf[AMLOGIC_KEYMASTER_RECV_BUF_SIZE]; |
152 | keymaster::Eraser recv_buf_eraser(recv_buf, AMLOGIC_KEYMASTER_RECV_BUF_SIZE); |
153 | uint32_t rsp_size = AMLOGIC_KEYMASTER_RECV_BUF_SIZE; |
154 | int rc = aml_keymaster_call(s, command, send_buf, req_size, recv_buf, &rsp_size); |
155 | if (rc < 0) { |
156 | ALOGE("tipc error: %d\n", rc); |
157 | // TODO(swillden): Distinguish permanent from transient errors and set error_ appropriately. |
158 | return translate_error(rc); |
159 | } else { |
160 | ALOGD("Received %d byte response\n", rsp_size); |
161 | } |
162 | |
163 | const keymaster_message* msg = (keymaster_message*)recv_buf; |
164 | const uint8_t* p = msg->payload; |
165 | |
166 | if (!rsp->Deserialize(&p, p + rsp_size)) { |
167 | ALOGE("Error deserializing response of size %d\n", (int)rsp_size); |
168 | return KM_ERROR_UNKNOWN_ERROR; |
169 | } else if (rsp->error != KM_ERROR_OK) { |
170 | ALOGE("Response of size %d contained error code %d\n", (int)rsp_size, (int)rsp->error); |
171 | return rsp->error; |
172 | } |
173 | return rsp->error; |
174 | } |
175 | #else |
176 | #define TRUSTY_DEVICE_NAME "/dev/trusty-ipc-dev0" |
177 | |
178 | static int handle_ = -1; |
179 | |
180 | int trusty_keymaster_connect() { |
181 | int rc = tipc_connect(TRUSTY_DEVICE_NAME, KEYMASTER_PORT); |
182 | if (rc < 0) { |
183 | return rc; |
184 | } |
185 | |
186 | handle_ = rc; |
187 | return 0; |
188 | } |
189 | |
190 | int trusty_keymaster_call(uint32_t cmd, void* in, uint32_t in_size, uint8_t* out, |
191 | uint32_t* out_size) { |
192 | if (handle_ < 0) { |
193 | ALOGE("not connected\n"); |
194 | return -EINVAL; |
195 | } |
196 | |
197 | size_t msg_size = in_size + sizeof(struct keymaster_message); |
198 | struct keymaster_message* msg = reinterpret_cast<struct keymaster_message*>(malloc(msg_size)); |
199 | if (!msg) { |
200 | ALOGE("failed to allocate msg buffer\n"); |
201 | return -EINVAL; |
202 | } |
203 | |
204 | msg->cmd = cmd; |
205 | memcpy(msg->payload, in, in_size); |
206 | |
207 | ssize_t rc = write(handle_, msg, msg_size); |
208 | free(msg); |
209 | |
210 | if (rc < 0) { |
211 | ALOGE("failed to send cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT, strerror(errno)); |
212 | return -errno; |
213 | } |
214 | size_t out_max_size = *out_size; |
215 | *out_size = 0; |
216 | struct iovec iov[2]; |
217 | struct keymaster_message header; |
218 | iov[0] = {.iov_base = &header, .iov_len = sizeof(struct keymaster_message)}; |
219 | while (true) { |
220 | iov[1] = {.iov_base = out + *out_size, |
221 | .iov_len = std::min<uint32_t>(KEYMASTER_MAX_BUFFER_LENGTH, |
222 | out_max_size - *out_size)}; |
223 | rc = readv(handle_, iov, 2); |
224 | if (rc < 0) { |
225 | ALOGE("failed to retrieve response for cmd (%d) to %s: %s\n", cmd, KEYMASTER_PORT, |
226 | strerror(errno)); |
227 | return -errno; |
228 | } |
229 | |
230 | if ((size_t)rc < sizeof(struct keymaster_message)) { |
231 | ALOGE("invalid response size (%d)\n", (int)rc); |
232 | return -EINVAL; |
233 | } |
234 | |
235 | if ((cmd | KEYMASTER_RESP_BIT) != (header.cmd & ~(KEYMASTER_STOP_BIT))) { |
236 | ALOGE("invalid command (%d)", header.cmd); |
237 | return -EINVAL; |
238 | } |
239 | *out_size += ((size_t)rc - sizeof(struct keymaster_message)); |
240 | if (header.cmd & KEYMASTER_STOP_BIT) { |
241 | break; |
242 | } |
243 | } |
244 | |
245 | return rc; |
246 | } |
247 | |
248 | void trusty_keymaster_disconnect() { |
249 | if (handle_ >= 0) { |
250 | tipc_close(handle_); |
251 | } |
252 | handle_ = -1; |
253 | } |
254 | |
255 | #endif // AMLOGIC_MODIFY |
256 | keymaster_error_t translate_error(int err) { |
257 | switch (err) { |
258 | case 0: |
259 | return KM_ERROR_OK; |
260 | case -EPERM: |
261 | case -EACCES: |
262 | return KM_ERROR_SECURE_HW_ACCESS_DENIED; |
263 | |
264 | case -ECANCELED: |
265 | return KM_ERROR_OPERATION_CANCELLED; |
266 | |
267 | case -ENODEV: |
268 | return KM_ERROR_UNIMPLEMENTED; |
269 | |
270 | case -ENOMEM: |
271 | return KM_ERROR_MEMORY_ALLOCATION_FAILED; |
272 | |
273 | case -EBUSY: |
274 | return KM_ERROR_SECURE_HW_BUSY; |
275 | |
276 | case -EIO: |
277 | return KM_ERROR_SECURE_HW_COMMUNICATION_FAILED; |
278 | |
279 | case -EOVERFLOW: |
280 | return KM_ERROR_INVALID_INPUT_LENGTH; |
281 | |
282 | default: |
283 | return KM_ERROR_UNKNOWN_ERROR; |
284 | } |
285 | } |
286 | #if !AMLOGIC_MODIFY |
287 | keymaster_error_t trusty_keymaster_send(uint32_t command, const keymaster::Serializable& req, |
288 | keymaster::KeymasterResponse* rsp) { |
289 | uint32_t req_size = req.SerializedSize(); |
290 | if (req_size > TRUSTY_KEYMASTER_SEND_BUF_SIZE) { |
291 | ALOGE("Request too big: %u Max size: %u", req_size, TRUSTY_KEYMASTER_SEND_BUF_SIZE); |
292 | return KM_ERROR_INVALID_INPUT_LENGTH; |
293 | } |
294 | |
295 | uint8_t send_buf[TRUSTY_KEYMASTER_SEND_BUF_SIZE]; |
296 | keymaster::Eraser send_buf_eraser(send_buf, TRUSTY_KEYMASTER_SEND_BUF_SIZE); |
297 | req.Serialize(send_buf, send_buf + req_size); |
298 | |
299 | // Send it |
300 | uint8_t recv_buf[TRUSTY_KEYMASTER_RECV_BUF_SIZE]; |
301 | keymaster::Eraser recv_buf_eraser(recv_buf, TRUSTY_KEYMASTER_RECV_BUF_SIZE); |
302 | uint32_t rsp_size = TRUSTY_KEYMASTER_RECV_BUF_SIZE; |
303 | int rc = trusty_keymaster_call(command, send_buf, req_size, recv_buf, &rsp_size); |
304 | if (rc < 0) { |
305 | // Reset the connection on tipc error |
306 | trusty_keymaster_disconnect(); |
307 | trusty_keymaster_connect(); |
308 | ALOGE("tipc error: %d\n", rc); |
309 | // TODO(swillden): Distinguish permanent from transient errors and set error_ appropriately. |
310 | return translate_error(rc); |
311 | } else { |
312 | ALOGV("Received %d byte response\n", rsp_size); |
313 | } |
314 | |
315 | const uint8_t* p = recv_buf; |
316 | if (!rsp->Deserialize(&p, p + rsp_size)) { |
317 | ALOGE("Error deserializing response of size %d\n", (int)rsp_size); |
318 | return KM_ERROR_UNKNOWN_ERROR; |
319 | } else if (rsp->error != KM_ERROR_OK) { |
320 | ALOGE("Response of size %d contained error code %d\n", (int)rsp_size, (int)rsp->error); |
321 | return rsp->error; |
322 | } |
323 | return rsp->error; |
324 | } |
325 | #endif //!AMLOGIC_MODIFY |
326 |