Home | History | Annotate | Line # | Download | only in acpi
      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