qcomscm.c revision 1.1 1 /* $NetBSD: qcomscm.c,v 1.1 2024/12/30 12:31:10 jmcneill Exp $ */
2 /* $OpenBSD: qcscm.c,v 1.9 2024/08/04 15:30:08 kettenis Exp $ */
3 /*
4 * Copyright (c) 2022 Patrick Wildt <patrick (at) blueri.se>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/device.h>
22 #include <sys/kmem.h>
23 #include <sys/bus.h>
24 #include <sys/uuid.h>
25
26 #include <dev/efi/efi.h>
27 #include <dev/acpi/acpivar.h>
28 #include <dev/acpi/qcomscm.h>
29
30 #define ARM_SMCCC_STD_CALL (0U << 31)
31 #define ARM_SMCCC_FAST_CALL (1U << 31)
32 #define ARM_SMCCC_LP64 (1U << 30)
33 #define ARM_SMCCC_OWNER_SIP 2
34
35 #define QCTEE_TZ_OWNER_TZ_APPS 48
36 #define QCTEE_TZ_OWNER_QSEE_OS 50
37
38 #define QCTEE_TZ_SVC_APP_ID_PLACEHOLDER 0
39 #define QCTEE_TZ_SVC_APP_MGR 1
40
41 #define QCTEE_OS_RESULT_SUCCESS 0
42 #define QCTEE_OS_RESULT_INCOMPLETE 1
43 #define QCTEE_OS_RESULT_BLOCKED_ON_LISTENER 2
44 #define QCTEE_OS_RESULT_FAILURE 0xffffffff
45
46 #define QCTEE_OS_SCM_RES_APP_ID 0xee01
47 #define QCTEE_OS_SCM_RES_QSEOS_LISTENER_ID 0xee02
48
49 #define QCTEE_UEFI_GET_VARIABLE 0x8000
50 #define QCTEE_UEFI_SET_VARIABLE 0x8001
51 #define QCTEE_UEFI_GET_NEXT_VARIABLE 0x8002
52 #define QCTEE_UEFI_QUERY_VARIABLE_INFO 0x8003
53
54 #define QCTEE_UEFI_SUCCESS 0
55 #define QCTEE_UEFI_BUFFER_TOO_SMALL 0x80000005
56 #define QCTEE_UEFI_DEVICE_ERROR 0x80000007
57 #define QCTEE_UEFI_NOT_FOUND 0x8000000e
58
59 #define QCSCM_SVC_PIL 0x02
60 #define QCSCM_PIL_PAS_INIT_IMAGE 0x01
61 #define QCSCM_PIL_PAS_MEM_SETUP 0x02
62 #define QCSCM_PIL_PAS_AUTH_AND_RESET 0x05
63 #define QCSCM_PIL_PAS_SHUTDOWN 0x06
64 #define QCSCM_PIL_PAS_IS_SUPPORTED 0x07
65 #define QCSCM_PIL_PAS_MSS_RESET 0x0a
66
67 #define QCSCM_INTERRUPTED 1
68 #define QCSCM_EBUSY -22
69
70 #define QCSCM_ARGINFO_NUM(x) (((x) & 0xf) << 0)
71 #define QCSCM_ARGINFO_TYPE(x, y) (((y) & 0x3) << (4 + 2 * (x)))
72 #define QCSCM_ARGINFO_TYPE_VAL 0
73 #define QCSCM_ARGINFO_TYPE_RO 1
74 #define QCSCM_ARGINFO_TYPE_RW 2
75 #define QCSCM_ARGINFO_TYPE_BUFVAL 3
76
77 #define EFI_VARIABLE_NON_VOLATILE 0x00000001
78 #define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002
79 #define EFI_VARIABLE_RUNTIME_ACCESS 0x00000004
80
81 #define UNIX_GPS_EPOCH_OFFSET 315964800
82
83 #define EFI_VAR_RTCINFO __UNCONST(u"RTCInfo")
84
85 extern struct arm32_bus_dma_tag arm_generic_dma_tag;
86
87 struct qcscm_dmamem {
88 bus_dmamap_t qdm_map;
89 bus_dma_segment_t qdm_seg;
90 size_t qdm_size;
91 void *qdm_kva;
92 };
93
94 #define QCSCM_DMA_MAP(_qdm) ((_qdm)->qdm_map)
95 #define QCSCM_DMA_LEN(_qdm) ((_qdm)->qdm_size)
96 #define QCSCM_DMA_DVA(_qdm) ((uint64_t)(_qdm)->qdm_map->dm_segs[0].ds_addr)
97 #define QCSCM_DMA_KVA(_qdm, off) ((void *)((uintptr_t)(_qdm)->qdm_kva + (off)))
98
99 static struct uuid qcscm_uefi_rtcinfo_guid =
100 { 0x882f8c2b, 0x9646, 0x435f,
101 0x8d, 0xe5, { 0xf2, 0x08, 0xff, 0x80, 0xc1, 0xbd } };
102
103 struct qcscm_softc {
104 device_t sc_dev;
105 bus_dma_tag_t sc_dmat;
106
107 struct qcscm_dmamem *sc_extarg;
108 uint32_t sc_uefi_id;
109 };
110
111 static int qcscm_match(device_t, cfdata_t, void *);
112 static void qcscm_attach(device_t, device_t, void *);
113
114 CFATTACH_DECL_NEW(qcomscm, sizeof(struct qcscm_softc),
115 qcscm_match, qcscm_attach, NULL, NULL);
116
117 static inline void qcscm_smc_exec(uint64_t *, uint64_t *);
118 int qcscm_smc_call(struct qcscm_softc *, uint8_t, uint8_t, uint8_t,
119 uint32_t, uint64_t *, int, uint64_t *);
120 int qcscm_tee_app_get_id(struct qcscm_softc *, const char *, uint32_t *);
121 int qcscm_tee_app_send(struct qcscm_softc *, uint32_t, uint64_t, uint64_t,
122 uint64_t, uint64_t);
123
124 efi_status qcscm_uefi_get_variable(struct qcscm_softc *, efi_char *,
125 int, struct uuid *, uint32_t *, uint8_t *, int *);
126 efi_status qcscm_uefi_set_variable(struct qcscm_softc *, efi_char *,
127 int, struct uuid *, uint32_t, uint8_t *, int);
128 efi_status qcscm_uefi_get_next_variable(struct qcscm_softc *,
129 efi_char *, int *, struct uuid *);
130
131 efi_status qcscm_efi_get_variable(efi_char *, struct uuid *, uint32_t *,
132 u_long *, void *);
133 efi_status qcscm_efi_set_variable(efi_char *, struct uuid *, uint32_t,
134 u_long, void *);
135 efi_status qcscm_efi_get_next_variable_name(u_long *, efi_char *, struct uuid *);
136
137 #ifdef QCSCM_DEBUG
138 void qcscm_uefi_dump_variables(struct qcscm_softc *);
139 void qcscm_uefi_dump_variable(struct qcscm_softc *, efi_char *, int,
140 struct uuid *);
141 #endif
142
143 int qcscm_uefi_rtc_get(uint32_t *);
144 int qcscm_uefi_rtc_set(uint32_t);
145
146 struct qcscm_dmamem *
147 qcscm_dmamem_alloc(struct qcscm_softc *, bus_size_t, bus_size_t);
148 void qcscm_dmamem_free(struct qcscm_softc *, struct qcscm_dmamem *);
149
150 struct qcscm_softc *qcscm_sc;
151
152 static const struct device_compatible_entry compat_data[] = {
153 { .compat = "QCOM04DD" },
154 DEVICE_COMPAT_EOL
155 };
156
157 static int
158 qcscm_match(device_t parent, cfdata_t cf, void *aux)
159 {
160 struct acpi_attach_args *aa = aux;
161
162 return acpi_compatible_match(aa, compat_data);
163 }
164
165 static void
166 qcscm_attach(device_t parent, device_t self, void *aux)
167 {
168 struct qcscm_softc *sc = device_private(self);
169 int error;
170
171 sc->sc_dev = self;
172 error = bus_dmatag_subregion(&arm_generic_dma_tag,
173 0, __MASK(32), &sc->sc_dmat, 0);
174 KASSERT(error == 0);
175
176 sc->sc_extarg = qcscm_dmamem_alloc(sc, PAGE_SIZE, 8);
177 if (sc->sc_extarg == NULL) {
178 aprint_error(": can't allocate memory for extended args\n");
179 return;
180 }
181
182 aprint_naive("\n");
183 aprint_normal("\n");
184
185 #if notyet
186 error = qcscm_tee_app_get_id(sc, "qcom.tz.uefisecapp", &sc->sc_uefi_id);
187 if (error != 0) {
188 aprint_error_dev(self, "can't retrieve UEFI App: %d\n", error);
189 sc->sc_uefi_id = UINT32_MAX;
190 }
191 #else
192 sc->sc_uefi_id = UINT32_MAX;
193 #endif
194
195 qcscm_sc = sc;
196
197 #ifdef QCSCM_DEBUG
198 qcscm_uefi_dump_variables(sc);
199 qcscm_uefi_dump_variable(sc, EFI_VAR_RTCINFO, sizeof(EFI_VAR_RTCINFO),
200 &qcscm_uefi_rtcinfo_guid);
201 #endif
202 }
203
204 /* Expects an uint64_t[18] */
205 static inline void
206 qcscm_smc_exec(uint64_t *in, uint64_t *out)
207 {
208 asm volatile(
209 "ldp x0, x1, [%[in], #0]\n"
210 "ldp x2, x3, [%[in], #16]\n"
211 "ldp x4, x5, [%[in], #32]\n"
212 "ldp x6, x7, [%[in], #48]\n"
213 "ldp x8, x9, [%[in], #64]\n"
214 "ldp x10, x11, [%[in], #80]\n"
215 "ldp x12, x13, [%[in], #96]\n"
216 "ldp x14, x15, [%[in], #112]\n"
217 "ldp x16, x17, [%[in], #128]\n"
218 "smc #0\n"
219 "stp x0, x1, [%[out], #0]\n"
220 "stp x2, x3, [%[out], #16]\n"
221 "stp x4, x5, [%[out], #32]\n"
222 "stp x6, x7, [%[out], #48]\n"
223 "stp x8, x9, [%[out], #64]\n"
224 "stp x10, x11, [%[out], #80]\n"
225 "stp x12, x13, [%[out], #96]\n"
226 "stp x14, x15, [%[out], #112]\n"
227 "stp x16, x17, [%[out], #128]\n"
228 :
229 : [in] "r" (in), [out] "r" (out)
230 : "x0", "x1", "x2", "x3", "x4", "x5",
231 "x6", "x7", "x8", "x9", "x10", "x11",
232 "x12", "x13", "x14", "x15", "x16", "x17",
233 "memory");
234 }
235
236 int
237 qcscm_smc_call(struct qcscm_softc *sc, uint8_t owner, uint8_t svc, uint8_t cmd,
238 uint32_t arginfo, uint64_t *args, int arglen, uint64_t *res)
239 {
240 uint64_t smcreq[18] = { 0 }, smcres[18] = { 0 };
241 uint64_t *smcextreq;
242 int i, busy_retry;
243
244 /* 4 of our 10 possible args fit into x2-x5 */
245 smcreq[0] = ARM_SMCCC_STD_CALL | ARM_SMCCC_LP64 |
246 owner << 24 | svc << 8 | cmd;
247 #ifdef QCSCM_DEBUG_SMC
248 device_printf(sc->sc_dev, "owner %#x svc %#x cmd %#x req %#lx\n",
249 owner, svc, cmd, smcreq[0]);
250 #endif
251 smcreq[1] = arginfo;
252 for (i = 0; i < uimin(arglen, 4); i++)
253 smcreq[2 + i] = args[i];
254
255 /* In case we have more than 4, use x5 as ptr to extra args */
256 if (arglen > 4) {
257 smcreq[5] = QCSCM_DMA_DVA(sc->sc_extarg);
258 smcextreq = QCSCM_DMA_KVA(sc->sc_extarg, 0);
259 for (i = 0; i < uimin(arglen - 3, 7); i++) {
260 smcextreq[i] = args[3 + i];
261 }
262 cpu_drain_writebuf();
263 }
264
265 #ifdef QCSCM_DEBUG_SMC
266 device_printf(sc->sc_dev, "smcreq[before]:");
267 for (i = 0; i < __arraycount(smcreq); i++) {
268 printf(" %#lx", smcreq[i]);
269 }
270 printf("\n");
271 #endif
272 for (busy_retry = 20; busy_retry > 0; busy_retry--) {
273 int intr_retry = 1000000;
274 for (;;) {
275 qcscm_smc_exec(smcreq, smcres);
276 /* If the call gets interrupted, try again and re-pass x0/x6 */
277 if (smcres[0] == QCSCM_INTERRUPTED) {
278 if (--intr_retry == 0) {
279 break;
280 }
281 smcreq[0] = smcres[0];
282 smcreq[6] = smcres[6];
283 continue;
284 }
285 break;
286 }
287
288 if (smcres[0] != QCSCM_EBUSY) {
289 break;
290 }
291 delay(30000);
292 }
293
294 #ifdef QCSCM_DEBUG_SMC
295 device_printf(sc->sc_dev, "smcreq[after]:");
296 for (i = 0; i < __arraycount(smcreq); i++) {
297 printf(" %#lx", smcreq[i]);
298 }
299 printf("\n");
300 device_printf(sc->sc_dev, "smcres[after]:");
301 for (i = 0; i < __arraycount(smcres); i++) {
302 printf(" %#lx", smcres[i]);
303 }
304 printf("\n");
305 #endif
306
307 if (res) {
308 res[0] = smcres[1];
309 res[1] = smcres[2];
310 res[2] = smcres[3];
311 }
312
313 return smcres[0];
314 }
315
316 /* Retrieve id of app running in TEE by name */
317 int
318 qcscm_tee_app_get_id(struct qcscm_softc *sc, const char *name, uint32_t *id)
319 {
320 struct qcscm_dmamem *qdm;
321 uint64_t res[3];
322 uint64_t args[2];
323 uint32_t arginfo;
324 int ret;
325
326 /* Max name length is 64 */
327 if (strlen(name) > 64)
328 return EINVAL;
329
330 /* Alloc some phys mem to hold the name */
331 qdm = qcscm_dmamem_alloc(sc, PAGE_SIZE, 8);
332 if (qdm == NULL)
333 return ENOMEM;
334
335 /* Copy name of app we want to get an id for to page */
336 memcpy(QCSCM_DMA_KVA(qdm, 0), name, strlen(name));
337
338 /* Pass address of name and length */
339 arginfo = QCSCM_ARGINFO_NUM(__arraycount(args));
340 arginfo |= QCSCM_ARGINFO_TYPE(0, QCSCM_ARGINFO_TYPE_RW);
341 arginfo |= QCSCM_ARGINFO_TYPE(1, QCSCM_ARGINFO_TYPE_VAL);
342 args[0] = QCSCM_DMA_DVA(qdm);
343 args[1] = strlen(name);
344
345 cpu_drain_writebuf();
346
347 /* Make call into TEE */
348 ret = qcscm_smc_call(sc, QCTEE_TZ_OWNER_QSEE_OS, QCTEE_TZ_SVC_APP_MGR,
349 0x03, arginfo, args, __arraycount(args), res);
350
351 /* If the call succeeded, check the response status */
352 if (ret == 0)
353 ret = res[0];
354
355 /* If the response status is successful as well, retrieve data */
356 if (ret == 0)
357 *id = res[2];
358
359 qcscm_dmamem_free(sc, qdm);
360 return ret;
361 }
362
363 /* Message interface to app running in TEE */
364 int
365 qcscm_tee_app_send(struct qcscm_softc *sc, uint32_t id, uint64_t req_phys,
366 uint64_t req_len, uint64_t rsp_phys, uint64_t rsp_len)
367 {
368 uint64_t res[3];
369 uint64_t args[5];
370 uint32_t arginfo;
371 int ret;
372
373 /* Pass id of app we target, plus request and response buffers */
374 arginfo = QCSCM_ARGINFO_NUM(__arraycount(args));
375 arginfo |= QCSCM_ARGINFO_TYPE(0, QCSCM_ARGINFO_TYPE_VAL);
376 arginfo |= QCSCM_ARGINFO_TYPE(1, QCSCM_ARGINFO_TYPE_RW);
377 arginfo |= QCSCM_ARGINFO_TYPE(2, QCSCM_ARGINFO_TYPE_VAL);
378 arginfo |= QCSCM_ARGINFO_TYPE(3, QCSCM_ARGINFO_TYPE_RW);
379 arginfo |= QCSCM_ARGINFO_TYPE(4, QCSCM_ARGINFO_TYPE_VAL);
380 args[0] = id;
381 args[1] = req_phys;
382 args[2] = req_len;
383 args[3] = rsp_phys;
384 args[4] = rsp_len;
385
386 /* Make call into TEE */
387 ret = qcscm_smc_call(sc, QCTEE_TZ_OWNER_TZ_APPS,
388 QCTEE_TZ_SVC_APP_ID_PLACEHOLDER, 0x01,
389 arginfo, args, __arraycount(args), res);
390
391 /* If the call succeeded, check the response status */
392 if (ret == 0)
393 ret = res[0];
394
395 return ret;
396 }
397
398 struct qcscm_req_uefi_get_variable {
399 uint32_t command_id;
400 uint32_t length;
401 uint32_t name_offset;
402 uint32_t name_size;
403 uint32_t guid_offset;
404 uint32_t guid_size;
405 uint32_t data_size;
406 };
407
408 struct qcscm_rsp_uefi_get_variable {
409 uint32_t command_id;
410 uint32_t length;
411 uint32_t status;
412 uint32_t attributes;
413 uint32_t data_offset;
414 uint32_t data_size;
415 };
416
417 efi_status
418 qcscm_uefi_get_variable(struct qcscm_softc *sc,
419 efi_char *name, int name_size, struct uuid *guid,
420 uint32_t *attributes, uint8_t *data, int *data_size)
421 {
422 struct qcscm_req_uefi_get_variable *req;
423 struct qcscm_rsp_uefi_get_variable *resp;
424 struct qcscm_dmamem *qdm;
425 size_t reqsize, respsize;
426 off_t reqoff, respoff;
427 int ret;
428
429 if (sc->sc_uefi_id == UINT32_MAX)
430 return QCTEE_UEFI_DEVICE_ERROR;
431
432 reqsize = ALIGN(sizeof(*req)) + ALIGN(name_size) + ALIGN(sizeof(*guid));
433 respsize = ALIGN(sizeof(*resp)) + ALIGN(*data_size);
434
435 reqoff = 0;
436 respoff = reqsize;
437
438 qdm = qcscm_dmamem_alloc(sc, round_page(reqsize + respsize), 8);
439 if (qdm == NULL)
440 return QCTEE_UEFI_DEVICE_ERROR;
441
442 req = QCSCM_DMA_KVA(qdm, reqoff);
443 req->command_id = QCTEE_UEFI_GET_VARIABLE;
444 req->data_size = *data_size;
445 req->name_offset = ALIGN(sizeof(*req));
446 req->name_size = name_size;
447 req->guid_offset = ALIGN(req->name_offset + req->name_size);
448 req->guid_size = sizeof(*guid);
449 req->length = req->guid_offset + req->guid_size;
450
451 memcpy((char *)req + req->guid_offset, guid, sizeof(*guid));
452 memcpy((char *)req + req->name_offset, name, name_size);
453
454 cpu_drain_writebuf();
455
456 ret = qcscm_tee_app_send(sc, sc->sc_uefi_id,
457 QCSCM_DMA_DVA(qdm) + reqoff, reqsize,
458 QCSCM_DMA_DVA(qdm) + respoff, respsize);
459 if (ret) {
460 qcscm_dmamem_free(sc, qdm);
461 return QCTEE_UEFI_DEVICE_ERROR;
462 }
463
464 cpu_drain_writebuf();
465
466 resp = QCSCM_DMA_KVA(qdm, respoff);
467 if (resp->command_id != QCTEE_UEFI_GET_VARIABLE ||
468 resp->length < sizeof(*resp)) {
469 qcscm_dmamem_free(sc, qdm);
470 return QCTEE_UEFI_DEVICE_ERROR;
471 }
472
473 if (resp->status) {
474 if (resp->status == QCTEE_UEFI_BUFFER_TOO_SMALL)
475 *data_size = resp->data_size;
476 if (attributes)
477 *attributes = resp->attributes;
478 ret = resp->status;
479 qcscm_dmamem_free(sc, qdm);
480 return ret;
481 }
482
483 if (resp->length > respsize ||
484 resp->data_offset + resp->data_size > resp->length) {
485 qcscm_dmamem_free(sc, qdm);
486 return QCTEE_UEFI_DEVICE_ERROR;
487 }
488
489 if (attributes)
490 *attributes = resp->attributes;
491
492 if (*data_size == 0) {
493 *data_size = resp->data_size;
494 qcscm_dmamem_free(sc, qdm);
495 return QCTEE_UEFI_SUCCESS;
496 }
497
498 if (resp->data_size > *data_size) {
499 *data_size = resp->data_size;
500 qcscm_dmamem_free(sc, qdm);
501 return QCTEE_UEFI_BUFFER_TOO_SMALL;
502 }
503
504 memcpy(data, (char *)resp + resp->data_offset, resp->data_size);
505 *data_size = resp->data_size;
506
507 qcscm_dmamem_free(sc, qdm);
508 return EFI_SUCCESS;
509 }
510
511 struct qcscm_req_uefi_set_variable {
512 uint32_t command_id;
513 uint32_t length;
514 uint32_t name_offset;
515 uint32_t name_size;
516 uint32_t guid_offset;
517 uint32_t guid_size;
518 uint32_t attributes;
519 uint32_t data_offset;
520 uint32_t data_size;
521 };
522
523 struct qcscm_rsp_uefi_set_variable {
524 uint32_t command_id;
525 uint32_t length;
526 uint32_t status;
527 uint32_t unknown[2];
528 };
529
530 efi_status
531 qcscm_uefi_set_variable(struct qcscm_softc *sc,
532 efi_char *name, int name_size, struct uuid *guid,
533 uint32_t attributes, uint8_t *data, int data_size)
534 {
535 struct qcscm_req_uefi_set_variable *req;
536 struct qcscm_rsp_uefi_set_variable *resp;
537 struct qcscm_dmamem *qdm;
538 size_t reqsize, respsize;
539 off_t reqoff, respoff;
540 int ret;
541
542 if (sc->sc_uefi_id == UINT32_MAX)
543 return QCTEE_UEFI_DEVICE_ERROR;
544
545 reqsize = ALIGN(sizeof(*req)) + ALIGN(name_size) + ALIGN(sizeof(*guid)) +
546 ALIGN(data_size);
547 respsize = ALIGN(sizeof(*resp));
548
549 reqoff = 0;
550 respoff = reqsize;
551
552 qdm = qcscm_dmamem_alloc(sc, round_page(reqsize + respsize), 8);
553 if (qdm == NULL)
554 return QCTEE_UEFI_DEVICE_ERROR;
555
556 req = QCSCM_DMA_KVA(qdm, reqoff);
557 req->command_id = QCTEE_UEFI_SET_VARIABLE;
558 req->attributes = attributes;
559 req->name_offset = ALIGN(sizeof(*req));
560 req->name_size = name_size;
561 req->guid_offset = ALIGN(req->name_offset + req->name_size);
562 req->guid_size = sizeof(*guid);
563 req->data_offset = ALIGN(req->guid_offset + req->guid_size);
564 req->data_size = data_size;
565 req->length = req->data_offset + req->data_size;
566
567 memcpy((char *)req + req->name_offset, name, name_size);
568 memcpy((char *)req + req->guid_offset, guid, sizeof(*guid));
569 memcpy((char *)req + req->data_offset, data, data_size);
570
571 ret = qcscm_tee_app_send(sc, sc->sc_uefi_id,
572 QCSCM_DMA_DVA(qdm) + reqoff, reqsize,
573 QCSCM_DMA_DVA(qdm) + respoff, respsize);
574 if (ret) {
575 qcscm_dmamem_free(sc, qdm);
576 return QCTEE_UEFI_DEVICE_ERROR;
577 }
578
579 resp = QCSCM_DMA_KVA(qdm, respoff);
580 if (resp->command_id != QCTEE_UEFI_SET_VARIABLE ||
581 resp->length < sizeof(*resp) || resp->length > respsize) {
582 qcscm_dmamem_free(sc, qdm);
583 return QCTEE_UEFI_DEVICE_ERROR;
584 }
585
586 if (resp->status) {
587 ret = resp->status;
588 qcscm_dmamem_free(sc, qdm);
589 return ret;
590 }
591
592 qcscm_dmamem_free(sc, qdm);
593 return QCTEE_UEFI_SUCCESS;
594 }
595
596 struct qcscm_req_uefi_get_next_variable {
597 uint32_t command_id;
598 uint32_t length;
599 uint32_t guid_offset;
600 uint32_t guid_size;
601 uint32_t name_offset;
602 uint32_t name_size;
603 };
604
605 struct qcscm_rsp_uefi_get_next_variable {
606 uint32_t command_id;
607 uint32_t length;
608 uint32_t status;
609 uint32_t guid_offset;
610 uint32_t guid_size;
611 uint32_t name_offset;
612 uint32_t name_size;
613 };
614
615 efi_status
616 qcscm_uefi_get_next_variable(struct qcscm_softc *sc,
617 efi_char *name, int *name_size, struct uuid *guid)
618 {
619 struct qcscm_req_uefi_get_next_variable *req;
620 struct qcscm_rsp_uefi_get_next_variable *resp;
621 struct qcscm_dmamem *qdm;
622 size_t reqsize, respsize;
623 off_t reqoff, respoff;
624 int ret;
625
626 if (sc->sc_uefi_id == UINT32_MAX)
627 return QCTEE_UEFI_DEVICE_ERROR;
628
629 reqsize = ALIGN(sizeof(*req)) + ALIGN(sizeof(*guid)) + ALIGN(*name_size);
630 respsize = ALIGN(sizeof(*resp)) + ALIGN(sizeof(*guid)) + ALIGN(*name_size);
631
632 reqoff = 0;
633 respoff = reqsize;
634
635 qdm = qcscm_dmamem_alloc(sc, round_page(reqsize + respsize), 8);
636 if (qdm == NULL)
637 return QCTEE_UEFI_DEVICE_ERROR;
638
639 req = QCSCM_DMA_KVA(qdm, reqoff);
640 req->command_id = QCTEE_UEFI_GET_NEXT_VARIABLE;
641 req->guid_offset = ALIGN(sizeof(*req));
642 req->guid_size = sizeof(*guid);
643 req->name_offset = ALIGN(req->guid_offset + req->guid_size);
644 req->name_size = *name_size;
645 req->length = req->name_offset + req->name_size;
646
647 memcpy((char *)req + req->guid_offset, guid, sizeof(*guid));
648 memcpy((char *)req + req->name_offset, name, *name_size);
649
650 ret = qcscm_tee_app_send(sc, sc->sc_uefi_id,
651 QCSCM_DMA_DVA(qdm) + reqoff, reqsize,
652 QCSCM_DMA_DVA(qdm) + respoff, respsize);
653 if (ret) {
654 qcscm_dmamem_free(sc, qdm);
655 return QCTEE_UEFI_DEVICE_ERROR;
656 }
657
658 resp = QCSCM_DMA_KVA(qdm, respoff);
659 if (resp->command_id != QCTEE_UEFI_GET_NEXT_VARIABLE ||
660 resp->length < sizeof(*resp) || resp->length > respsize) {
661 qcscm_dmamem_free(sc, qdm);
662 return QCTEE_UEFI_DEVICE_ERROR;
663 }
664
665 if (resp->status) {
666 if (resp->status == QCTEE_UEFI_BUFFER_TOO_SMALL)
667 *name_size = resp->name_size;
668 ret = resp->status;
669 qcscm_dmamem_free(sc, qdm);
670 return ret;
671 }
672
673 if (resp->guid_offset + resp->guid_size > resp->length ||
674 resp->name_offset + resp->name_size > resp->length) {
675 qcscm_dmamem_free(sc, qdm);
676 return QCTEE_UEFI_DEVICE_ERROR;
677 }
678
679 if (resp->guid_size != sizeof(*guid)) {
680 qcscm_dmamem_free(sc, qdm);
681 return QCTEE_UEFI_DEVICE_ERROR;
682 }
683
684 if (resp->name_size > *name_size) {
685 *name_size = resp->name_size;
686 qcscm_dmamem_free(sc, qdm);
687 return QCTEE_UEFI_BUFFER_TOO_SMALL;
688 }
689
690 memcpy(guid, (char *)resp + resp->guid_offset, sizeof(*guid));
691 memcpy(name, (char *)resp + resp->name_offset, resp->name_size);
692 *name_size = resp->name_size;
693
694 qcscm_dmamem_free(sc, qdm);
695 return QCTEE_UEFI_SUCCESS;
696 }
697
698 efi_status
699 qcscm_efi_get_variable(efi_char *name, struct uuid *guid, uint32_t *attributes,
700 u_long *data_size, void *data)
701 {
702 struct qcscm_softc *sc = qcscm_sc;
703 efi_status status;
704 int name_size;
705 int size;
706
707 name_size = 0;
708 while (name[name_size])
709 name_size++;
710 name_size++;
711
712 size = *data_size;
713 status = qcscm_uefi_get_variable(sc, name, name_size * 2, guid,
714 attributes, data, &size);
715 *data_size = size;
716
717 /* Convert 32-bit status code to 64-bit. */
718 return ((status & 0xf0000000) << 32 | (status & 0x0fffffff));
719 }
720
721 efi_status
722 qcscm_efi_set_variable(efi_char *name, struct uuid *guid, uint32_t attributes,
723 u_long data_size, void *data)
724 {
725 struct qcscm_softc *sc = qcscm_sc;
726 efi_status status;
727 int name_size;
728
729 name_size = 0;
730 while (name[name_size])
731 name_size++;
732 name_size++;
733
734 status = qcscm_uefi_set_variable(sc, name, name_size * 2, guid,
735 attributes, data, data_size);
736
737 /* Convert 32-bit status code to 64-bit. */
738 return ((status & 0xf0000000) << 32 | (status & 0x0fffffff));
739 }
740
741 efi_status
742 qcscm_efi_get_next_variable_name(u_long *name_size, efi_char *name,
743 struct uuid *guid)
744 {
745 struct qcscm_softc *sc = qcscm_sc;
746 efi_status status;
747 int size;
748
749 size = *name_size;
750 status = qcscm_uefi_get_next_variable(sc, name, &size, guid);
751 *name_size = size;
752
753 /* Convert 32-bit status code to 64-bit. */
754 return ((status & 0xf0000000) << 32 | (status & 0x0fffffff));
755 }
756
757 #ifdef QCSCM_DEBUG
758
759 void
760 qcscm_uefi_dump_variables(struct qcscm_softc *sc)
761 {
762 efi_char name[128];
763 struct uuid guid;
764 int namesize = sizeof(name);
765 int i, ret;
766
767 memset(name, 0, sizeof(name));
768 memset(&guid, 0, sizeof(guid));
769
770 for (;;) {
771 ret = qcscm_uefi_get_next_variable(sc, name, &namesize, &guid);
772 if (ret == 0) {
773 printf("%s: ", device_xname(sc->sc_dev));
774 for (i = 0; i < namesize / 2; i++)
775 printf("%c", name[i]);
776 printf(" { 0x%08x, 0x%04x, 0x%04x, { ",
777 guid.time_low, guid.time_mid, guid.time_hi_and_version);
778 printf(" 0x%02x, 0x%02x,",
779 guid.clock_seq_hi_and_reserved,
780 guid.clock_seq_low);
781 for (i = 0; i < 6; i++) {
782 printf(" 0x%02x,", guid.node[i]);
783 }
784 printf(" }");
785 printf("\n");
786 namesize = sizeof(name);
787 continue;
788 }
789 break;
790 }
791 }
792
793 void
794 qcscm_uefi_dump_variable(struct qcscm_softc *sc, efi_char *name, int namesize,
795 struct uuid *guid)
796 {
797 uint8_t data[512];
798 int datasize = sizeof(data);
799 int i, ret;
800
801 ret = qcscm_uefi_get_variable(sc, name, namesize, guid,
802 NULL, data, &datasize);
803 if (ret != QCTEE_UEFI_SUCCESS) {
804 printf("%s: error reading ", device_xname(sc->sc_dev));
805 for (i = 0; i < namesize / 2; i++)
806 printf("%c", name[i]);
807 printf("\n");
808 return;
809 }
810
811 printf("%s: ", device_xname(sc->sc_dev));
812 for (i = 0; i < namesize / 2; i++)
813 printf("%c", name[i]);
814 printf(" = ");
815 for (i = 0; i < datasize; i++)
816 printf("%02x", data[i]);
817 printf("\n");
818 }
819
820 #endif
821
822 int
823 qcscm_uefi_rtc_get(uint32_t *off)
824 {
825 struct qcscm_softc *sc = qcscm_sc;
826 uint32_t rtcinfo[3];
827 int rtcinfosize = sizeof(rtcinfo);
828
829 if (sc == NULL)
830 return ENXIO;
831
832 if (qcscm_uefi_get_variable(sc, EFI_VAR_RTCINFO, sizeof(EFI_VAR_RTCINFO),
833 &qcscm_uefi_rtcinfo_guid, NULL, (uint8_t *)rtcinfo,
834 &rtcinfosize) != 0)
835 return EIO;
836
837 /* UEFI stores the offset based on GPS epoch */
838 *off = rtcinfo[0] + UNIX_GPS_EPOCH_OFFSET;
839 return 0;
840 }
841
842 int
843 qcscm_uefi_rtc_set(uint32_t off)
844 {
845 struct qcscm_softc *sc = qcscm_sc;
846 uint32_t rtcinfo[3];
847 int rtcinfosize = sizeof(rtcinfo);
848
849 if (sc == NULL)
850 return ENXIO;
851
852 if (qcscm_uefi_get_variable(sc, EFI_VAR_RTCINFO, sizeof(EFI_VAR_RTCINFO),
853 &qcscm_uefi_rtcinfo_guid, NULL, (uint8_t *)rtcinfo,
854 &rtcinfosize) != 0)
855 return EIO;
856
857 /* UEFI stores the offset based on GPS epoch */
858 off -= UNIX_GPS_EPOCH_OFFSET;
859
860 /* No need to set if we're not changing anything */
861 if (rtcinfo[0] == off)
862 return 0;
863
864 rtcinfo[0] = off;
865
866 if (qcscm_uefi_set_variable(sc, EFI_VAR_RTCINFO, sizeof(EFI_VAR_RTCINFO),
867 &qcscm_uefi_rtcinfo_guid, EFI_VARIABLE_NON_VOLATILE |
868 EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
869 (uint8_t *)rtcinfo, sizeof(rtcinfo)) != 0)
870 return EIO;
871
872 return 0;
873 }
874
875 int
876 qcscm_pas_init_image(uint32_t peripheral, paddr_t metadata)
877 {
878 struct qcscm_softc *sc = qcscm_sc;
879 uint64_t res[3];
880 uint64_t args[2];
881 uint32_t arginfo;
882 int ret;
883
884 if (sc == NULL)
885 return ENXIO;
886
887 arginfo = QCSCM_ARGINFO_NUM(__arraycount(args));
888 arginfo |= QCSCM_ARGINFO_TYPE(0, QCSCM_ARGINFO_TYPE_VAL);
889 arginfo |= QCSCM_ARGINFO_TYPE(1, QCSCM_ARGINFO_TYPE_RW);
890 args[0] = peripheral;
891 args[1] = metadata;
892
893 /* Make call into TEE */
894 ret = qcscm_smc_call(sc, ARM_SMCCC_OWNER_SIP, QCSCM_SVC_PIL,
895 QCSCM_PIL_PAS_INIT_IMAGE, arginfo, args, __arraycount(args), res);
896
897 /* If the call succeeded, check the response status */
898 if (ret == 0)
899 ret = res[0];
900
901 return ret;
902 }
903
904 int
905 qcscm_pas_mem_setup(uint32_t peripheral, paddr_t addr, size_t size)
906 {
907 struct qcscm_softc *sc = qcscm_sc;
908 uint64_t res[3];
909 uint64_t args[3];
910 uint32_t arginfo;
911 int ret;
912
913 if (sc == NULL)
914 return ENXIO;
915
916 arginfo = QCSCM_ARGINFO_NUM(__arraycount(args));
917 arginfo |= QCSCM_ARGINFO_TYPE(0, QCSCM_ARGINFO_TYPE_VAL);
918 arginfo |= QCSCM_ARGINFO_TYPE(1, QCSCM_ARGINFO_TYPE_VAL);
919 arginfo |= QCSCM_ARGINFO_TYPE(2, QCSCM_ARGINFO_TYPE_VAL);
920 args[0] = peripheral;
921 args[1] = addr;
922 args[2] = size;
923
924 /* Make call into TEE */
925 ret = qcscm_smc_call(sc, ARM_SMCCC_OWNER_SIP, QCSCM_SVC_PIL,
926 QCSCM_PIL_PAS_MEM_SETUP, arginfo, args, __arraycount(args), res);
927
928 /* If the call succeeded, check the response status */
929 if (ret == 0)
930 ret = res[0];
931
932 return ret;
933 }
934
935 int
936 qcscm_pas_auth_and_reset(uint32_t peripheral)
937 {
938 struct qcscm_softc *sc = qcscm_sc;
939 uint64_t res[3];
940 uint64_t args[1];
941 uint32_t arginfo;
942 int ret;
943
944 if (sc == NULL)
945 return ENXIO;
946
947 arginfo = QCSCM_ARGINFO_NUM(__arraycount(args));
948 arginfo |= QCSCM_ARGINFO_TYPE(0, QCSCM_ARGINFO_TYPE_VAL);
949 args[0] = peripheral;
950
951 /* Make call into TEE */
952 ret = qcscm_smc_call(sc, ARM_SMCCC_OWNER_SIP, QCSCM_SVC_PIL,
953 QCSCM_PIL_PAS_AUTH_AND_RESET, arginfo, args, __arraycount(args), res);
954
955 /* If the call succeeded, check the response status */
956 if (ret == 0)
957 ret = res[0];
958
959 return ret;
960 }
961
962 int
963 qcscm_pas_shutdown(uint32_t peripheral)
964 {
965 struct qcscm_softc *sc = qcscm_sc;
966 uint64_t res[3];
967 uint64_t args[1];
968 uint32_t arginfo;
969 int ret;
970
971 if (sc == NULL)
972 return ENXIO;
973
974 arginfo = QCSCM_ARGINFO_NUM(__arraycount(args));
975 arginfo |= QCSCM_ARGINFO_TYPE(0, QCSCM_ARGINFO_TYPE_VAL);
976 args[0] = peripheral;
977
978 /* Make call into TEE */
979 ret = qcscm_smc_call(sc, ARM_SMCCC_OWNER_SIP, QCSCM_SVC_PIL,
980 QCSCM_PIL_PAS_SHUTDOWN, arginfo, args, __arraycount(args), res);
981
982 /* If the call succeeded, check the response status */
983 if (ret == 0)
984 ret = res[0];
985
986 return ret;
987 }
988
989 /* DMA code */
990 struct qcscm_dmamem *
991 qcscm_dmamem_alloc(struct qcscm_softc *sc, bus_size_t size, bus_size_t align)
992 {
993 struct qcscm_dmamem *qdm;
994 int nsegs;
995
996 qdm = kmem_zalloc(sizeof(*qdm), KM_SLEEP);
997 qdm->qdm_size = size;
998
999 if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
1000 BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &qdm->qdm_map) != 0)
1001 goto qdmfree;
1002
1003 if (bus_dmamem_alloc(sc->sc_dmat, size, align, 0,
1004 &qdm->qdm_seg, 1, &nsegs, BUS_DMA_WAITOK) != 0)
1005 goto destroy;
1006
1007 if (bus_dmamem_map(sc->sc_dmat, &qdm->qdm_seg, nsegs, size,
1008 &qdm->qdm_kva, BUS_DMA_WAITOK | BUS_DMA_COHERENT) != 0)
1009 goto free;
1010
1011 if (bus_dmamap_load(sc->sc_dmat, qdm->qdm_map, qdm->qdm_kva, size,
1012 NULL, BUS_DMA_WAITOK) != 0)
1013 goto unmap;
1014
1015 memset(qdm->qdm_kva, 0, size);
1016
1017 return (qdm);
1018
1019 unmap:
1020 bus_dmamem_unmap(sc->sc_dmat, qdm->qdm_kva, size);
1021 free:
1022 bus_dmamem_free(sc->sc_dmat, &qdm->qdm_seg, 1);
1023 destroy:
1024 bus_dmamap_destroy(sc->sc_dmat, qdm->qdm_map);
1025 qdmfree:
1026 kmem_free(qdm, sizeof(*qdm));
1027
1028 return (NULL);
1029 }
1030
1031 void
1032 qcscm_dmamem_free(struct qcscm_softc *sc, struct qcscm_dmamem *qdm)
1033 {
1034 bus_dmamem_unmap(sc->sc_dmat, qdm->qdm_kva, qdm->qdm_size);
1035 bus_dmamem_free(sc->sc_dmat, &qdm->qdm_seg, 1);
1036 bus_dmamap_destroy(sc->sc_dmat, qdm->qdm_map);
1037 kmem_free(qdm, sizeof(*qdm));
1038 }
1039