diff options
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/fdt/files.fdt | 7 | ||||
-rw-r--r-- | sys/dev/fdt/qcrtc.c | 37 | ||||
-rw-r--r-- | sys/dev/fdt/qcscm.c | 783 |
3 files changed, 818 insertions, 9 deletions
diff --git a/sys/dev/fdt/files.fdt b/sys/dev/fdt/files.fdt index d5e55d0b9df..45a680b86a8 100644 --- a/sys/dev/fdt/files.fdt +++ b/sys/dev/fdt/files.fdt @@ -1,4 +1,4 @@ -# $OpenBSD: files.fdt,v 1.174 2023/01/01 01:34:33 jsg Exp $ +# $OpenBSD: files.fdt,v 1.175 2023/01/16 20:12:38 patrick Exp $ # # Config file and device description for machine-independent FDT code. # Included by ports that need it. @@ -643,6 +643,11 @@ file dev/fdt/qcgpio_fdt.c qcgpio attach qciic at fdt with qciic_fdt file dev/fdt/qciic_fdt.c qciic +# Qualcomm SCM +device qcscm +attach qcscm at fdt +file dev/fdt/qcscm.c qcscm + # Qualcomm SPMI controller device qcspmi: spmi attach qcspmi at fdt diff --git a/sys/dev/fdt/qcrtc.c b/sys/dev/fdt/qcrtc.c index 830181ebe8d..bc1e78dbc45 100644 --- a/sys/dev/fdt/qcrtc.c +++ b/sys/dev/fdt/qcrtc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: qcrtc.c,v 1.1 2022/11/08 19:47:05 patrick Exp $ */ +/* $OpenBSD: qcrtc.c,v 1.2 2023/01/16 20:12:38 patrick Exp $ */ /* * Copyright (c) 2022 Patrick Wildt <patrick@blueri.se> * @@ -61,6 +61,9 @@ int qcrtc_settime(struct todr_chip_handle *, struct timeval *); void qcrtc_tick(void *); +extern int qcscm_uefi_rtc_get(uint32_t *); +extern int qcscm_uefi_rtc_set(uint32_t); + int qcrtc_match(struct device *parent, void *match, void *aux) { @@ -100,9 +103,10 @@ int qcrtc_gettime(struct todr_chip_handle *handle, struct timeval *tv) { struct qcrtc_softc *sc = handle->cookie; - uint32_t reg; + uint32_t reg, off; int error; + /* Read current counting RTC value. */ error = spmi_cmd_read(sc->sc_tag, sc->sc_sid, SPMI_CMD_EXT_READL, sc->sc_addr + RTC_READ, ®, sizeof(reg)); if (error) { @@ -110,16 +114,33 @@ qcrtc_gettime(struct todr_chip_handle *handle, struct timeval *tv) return error; } - tv->tv_sec = reg; - tv->tv_usec = 0; + /* Retrieve RTC offset stored in UEFI. */ + if (qcscm_uefi_rtc_get(&off) != 0) + return EIO; - /* We need to offset. */ - return EIO; + /* Add RTC counter and 10y+1w to get seconds from epoch. */ + tv->tv_sec = off + (reg + (10 * 365 * 86400 + 7 * 86400)); + tv->tv_usec = 0; + return 0; } int qcrtc_settime(struct todr_chip_handle *handle, struct timeval *tv) { - /* XXX: We can't set the time for now, hardware reasons. */ - return EPERM; + struct qcrtc_softc *sc = handle->cookie; + uint32_t reg, off; + int error; + + error = spmi_cmd_read(sc->sc_tag, sc->sc_sid, SPMI_CMD_EXT_READL, + sc->sc_addr + RTC_READ, ®, sizeof(reg)); + if (error) { + printf("%s: error reading RTC\n", sc->sc_dev.dv_xname); + return error; + } + + /* Subtract RTC counter and 10y+1w to get offset for UEFI. */ + off = tv->tv_sec - (reg + (10 * 365 * 86400 + 7 * 86400)); + + /* Store offset in UEFI. */ + return qcscm_uefi_rtc_set(off); } diff --git a/sys/dev/fdt/qcscm.c b/sys/dev/fdt/qcscm.c new file mode 100644 index 00000000000..180a4b0f8a7 --- /dev/null +++ b/sys/dev/fdt/qcscm.c @@ -0,0 +1,783 @@ +/* $OpenBSD: qcscm.c,v 1.1 2023/01/16 20:12:38 patrick Exp $ */ +/* + * Copyright (c) 2022 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/queue.h> +#include <sys/malloc.h> +#include <sys/sysctl.h> +#include <sys/device.h> +#include <sys/evcount.h> +#include <sys/socket.h> +#include <sys/timeout.h> +#include <sys/atomic.h> + +#include <uvm/uvm_extern.h> + +#include <machine/intr.h> +#include <machine/bus.h> +#include <machine/fdt.h> + +#include <dev/efi/efi.h> + +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_misc.h> +#include <dev/ofw/fdt.h> + +/* #define QCSCM_DEBUG */ + +#define ARM_SMCCC_STD_CALL (0U << 31) +#define ARM_SMCCC_FAST_CALL (1U << 31) +#define ARM_SMCCC_LP64 (1U << 30) + +#define QCTEE_TZ_OWNER_TZ_APPS 48 +#define QCTEE_TZ_OWNER_QSEE_OS 50 + +#define QCTEE_TZ_SVC_APP_ID_PLACEHOLDER 0 +#define QCTEE_TZ_SVC_APP_MGR 1 + +#define QCTEE_OS_RESULT_SUCCESS 0 +#define QCTEE_OS_RESULT_INCOMPLETE 1 +#define QCTEE_OS_RESULT_BLOCKED_ON_LISTENER 2 +#define QCTEE_OS_RESULT_FAILURE 0xffffffff + +#define QCTEE_OS_SCM_RES_APP_ID 0xee01 +#define QCTEE_OS_SCM_RES_QSEOS_LISTENER_ID 0xee02 + +#define QCTEE_UEFI_GET_VARIABLE 0x8000 +#define QCTEE_UEFI_SET_VARIABLE 0x8001 +#define QCTEE_UEFI_GET_NEXT_VARIABLE 0x8002 +#define QCTEE_UEFI_QUERY_VARIABLE_INFO 0x8003 + +#define QCTEE_UEFI_SUCCESS 0 +#define QCTEE_UEFI_BUFFER_TOO_SMALL 0x80000005 +#define QCTEE_UEFI_DEVICE_ERROR 0x80000007 +#define QCTEE_UEFI_NOT_FOUND 0x8000000e + +#define QCSCM_INTERRUPTED 1 + +#define QCSCM_ARGINFO_NUM(x) (((x) & 0xf) << 0) +#define QCSCM_ARGINFO_TYPE(x, y) (((y) & 0x3) << (4 + 2 * (x))) +#define QCSCM_ARGINFO_TYPE_VAL 0 +#define QCSCM_ARGINFO_TYPE_RO 1 +#define QCSCM_ARGINFO_TYPE_RW 2 +#define QCSCM_ARGINFO_TYPE_BUFVAL 3 + +#define EFI_VARIABLE_NON_VOLATILE 0x00000001 +#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002 +#define EFI_VARIABLE_RUNTIME_ACCESS 0x00000004 + +struct qcscm_dmamem { + bus_dmamap_t qdm_map; + bus_dma_segment_t qdm_seg; + size_t qdm_size; + caddr_t qdm_kva; +}; + +#define QCSCM_DMA_MAP(_qdm) ((_qdm)->qdm_map) +#define QCSCM_DMA_LEN(_qdm) ((_qdm)->qdm_size) +#define QCSCM_DMA_DVA(_qdm) ((uint64_t)(_qdm)->qdm_map->dm_segs[0].ds_addr) +#define QCSCM_DMA_KVA(_qdm) ((void *)(_qdm)->qdm_kva) + +EFI_GUID qcscm_uefi_rtcinfo_guid = + { 0x882f8c2b, 0x9646, 0x435f, + { 0x8d, 0xe5, 0xf2, 0x08, 0xff, 0x80, 0xc1, 0xbd } }; + +struct qcscm_softc { + struct device sc_dev; + int sc_node; + bus_dma_tag_t sc_dmat; + + struct qcscm_dmamem *sc_extarg; + uint32_t sc_uefi_id; +}; + +int qcscm_match(struct device *, void *, void *); +void qcscm_attach(struct device *parent, struct device *self, void *args); + +const struct cfattach qcscm_ca = { + sizeof (struct qcscm_softc), qcscm_match, qcscm_attach +}; + +struct cfdriver qcscm_cd = { + NULL, "qcscm", DV_DULL +}; + +void qcscm_smc_exec(uint64_t *, uint64_t *); +int qcscm_smc_call(struct qcscm_softc *, uint8_t, uint8_t, uint8_t, + uint32_t, uint64_t *, int, uint64_t *); +int qcscm_tee_app_get_id(struct qcscm_softc *, const char *, uint32_t *); +int qcscm_tee_app_send(struct qcscm_softc *, uint32_t, uint64_t, uint64_t, + uint64_t, uint64_t); + +EFI_STATUS qcscm_uefi_get_variable(struct qcscm_softc *, CHAR16 *, + int, EFI_GUID *, uint32_t *, uint8_t *, int *); +EFI_STATUS qcscm_uefi_set_variable(struct qcscm_softc *, CHAR16 *, + int, EFI_GUID *, uint32_t, uint8_t *, int); +EFI_STATUS qcscm_uefi_get_next_variable(struct qcscm_softc *, + CHAR16 *, int *, EFI_GUID *); + +#ifdef QCSCM_DEBUG +void qcscm_uefi_dump_variables(struct qcscm_softc *); +void qcscm_uefi_dump_variable(struct qcscm_softc *, CHAR16 *, int, + EFI_GUID *); +#endif + +int qcscm_uefi_rtc_get(uint32_t *); +int qcscm_uefi_rtc_set(uint32_t); + +struct qcscm_dmamem * + qcscm_dmamem_alloc(struct qcscm_softc *, bus_size_t, bus_size_t); +void qcscm_dmamem_free(struct qcscm_softc *, struct qcscm_dmamem *); + +struct qcscm_softc *qcscm_sc; + +int +qcscm_match(struct device *parent, void *match, void *aux) +{ + struct fdt_attach_args *faa = aux; + + return OF_is_compatible(faa->fa_node, "qcom,scm"); +} + +void +qcscm_attach(struct device *parent, struct device *self, void *aux) +{ + struct qcscm_softc *sc = (struct qcscm_softc *)self; + struct fdt_attach_args *faa = aux; + + sc->sc_node = faa->fa_node; + sc->sc_dmat = faa->fa_dmat; + + sc->sc_extarg = qcscm_dmamem_alloc(sc, PAGE_SIZE, 8); + if (sc->sc_extarg == NULL) { + printf(": can't allocate memory for extended args\n"); + return; + } + + if (qcscm_tee_app_get_id(sc, "qcom.tz.uefisecapp", &sc->sc_uefi_id)) { + printf(": can't retrieve UEFI App\n"); + return; + } + + printf("\n"); + qcscm_sc = sc; + +#ifdef QCSCM_DEBUG + qcscm_uefi_dump_variables(sc); + qcscm_uefi_dump_variable(sc, u"RTCInfo", sizeof(u"RTCInfo"), + &qcscm_uefi_rtcinfo_guid); +#endif +} + +/* Expects an uint64_t[8] */ +void +qcscm_smc_exec(uint64_t *in, uint64_t *out) +{ + __asm( + "ldp x0, x1, [%0, %2]\n" + "ldp x2, x3, [%0, %3]\n" + "ldp x4, x5, [%0, %4]\n" + "ldp x6, x7, [%0, %5]\n" + "smc #0\n" + "stp x0, x1, [%1, %2]\n" + "stp x2, x3, [%1, %3]\n" + "stp x4, x5, [%1, %4]\n" + "stp x6, x7, [%1, %5]\n" :: + "r" (in), "r" (out), + "i"(0), "i"(16), "i"(32), "i"(48), + "m" (*in), "m" (*out) : + "x0", "x1", "x2", "x3", + "x4", "x5", "x6", "x7"); +} + +int +qcscm_smc_call(struct qcscm_softc *sc, uint8_t owner, uint8_t svc, uint8_t cmd, + uint32_t arginfo, uint64_t *args, int arglen, uint64_t *res) +{ + uint64_t smcreq[8] = { 0 }, smcres[8] = { 0 }; + uint64_t *smcextreq; + int i; + + /* 4 of our 10 possible args fit into x2-x5 */ + smcreq[0] = ARM_SMCCC_STD_CALL | ARM_SMCCC_LP64 | + owner << 24 | svc << 8 | cmd; + smcreq[1] = arginfo; + for (i = 0; i < min(arglen, 4); i++) + smcreq[2 + i] = args[i]; + + /* In case we have more than 4, use x5 as ptr to extra args */ + smcextreq = QCSCM_DMA_KVA(sc->sc_extarg); + if (arglen > 4) { + smcreq[5] = QCSCM_DMA_DVA(sc->sc_extarg); + smcextreq = QCSCM_DMA_KVA(sc->sc_extarg); + for (i = 0; i < min(arglen - 3, 7); i++) { + smcextreq[i] = args[3 + i]; + } + } + + for (;;) { + qcscm_smc_exec(smcreq, smcres); + /* If the call gets interrupted, try again and re-pass x0/x6 */ + if (smcres[0] == QCSCM_INTERRUPTED) { + smcreq[0] = smcres[0]; + smcreq[6] = smcres[6]; + continue; + } + break; + } + + if (res) { + res[0] = smcres[1]; + res[1] = smcres[2]; + res[2] = smcres[3]; + } + + return smcres[0]; +} + +/* Retrieve id of app running in TEE by name */ +int +qcscm_tee_app_get_id(struct qcscm_softc *sc, const char *name, uint32_t *id) +{ + struct qcscm_dmamem *qdm; + uint64_t res[3]; + uint64_t args[2]; + uint32_t arginfo; + int ret; + + /* Max name length is 64 */ + if (strlen(name) > 64) + return EINVAL; + + /* Alloc some phys mem to hold the name */ + qdm = qcscm_dmamem_alloc(sc, PAGE_SIZE, 8); + if (qdm == NULL) + return ENOMEM; + + /* Copy name of app we want to get an id for to page */ + memcpy(QCSCM_DMA_KVA(qdm), name, strlen(name)); + + /* Pass address of name and length */ + arginfo = QCSCM_ARGINFO_NUM(nitems(args)); + arginfo |= QCSCM_ARGINFO_TYPE(0, QCSCM_ARGINFO_TYPE_RW); + arginfo |= QCSCM_ARGINFO_TYPE(1, QCSCM_ARGINFO_TYPE_VAL); + args[0] = QCSCM_DMA_DVA(qdm); + args[1] = strlen(name); + + /* Make call into TEE */ + ret = qcscm_smc_call(sc, QCTEE_TZ_OWNER_QSEE_OS, QCTEE_TZ_SVC_APP_MGR, + 0x03, arginfo, args, nitems(args), res); + + /* If the call succeeded, check the response status */ + if (ret == 0) + ret = res[0]; + + /* If the response status is successful as well, retrieve data */ + if (ret == 0) + *id = res[2]; + + qcscm_dmamem_free(sc, qdm); + return ret; +} + +/* Message interface to app running in TEE */ +int +qcscm_tee_app_send(struct qcscm_softc *sc, uint32_t id, uint64_t req_phys, + uint64_t req_len, uint64_t rsp_phys, uint64_t rsp_len) +{ + uint64_t res[3]; + uint64_t args[5]; + uint32_t arginfo; + int ret; + + /* Pass id of app we target, plus request and response buffers */ + arginfo = QCSCM_ARGINFO_NUM(nitems(args)); + arginfo |= QCSCM_ARGINFO_TYPE(0, QCSCM_ARGINFO_TYPE_VAL); + arginfo |= QCSCM_ARGINFO_TYPE(1, QCSCM_ARGINFO_TYPE_RW); + arginfo |= QCSCM_ARGINFO_TYPE(2, QCSCM_ARGINFO_TYPE_VAL); + arginfo |= QCSCM_ARGINFO_TYPE(3, QCSCM_ARGINFO_TYPE_RW); + arginfo |= QCSCM_ARGINFO_TYPE(4, QCSCM_ARGINFO_TYPE_VAL); + args[0] = id; + args[1] = req_phys; + args[2] = req_len; + args[3] = rsp_phys; + args[4] = rsp_len; + + membar_sync(); + + /* Make call into TEE */ + ret = qcscm_smc_call(sc, QCTEE_TZ_OWNER_TZ_APPS, + QCTEE_TZ_SVC_APP_ID_PLACEHOLDER, 0x01, + arginfo, args, nitems(args), res); + + membar_sync(); + + /* If the call succeeded, check the response status */ + if (ret == 0) + ret = res[0]; + + return ret; +} + +struct qcscm_req_uefi_get_variable { + uint32_t command_id; + uint32_t length; + uint32_t name_offset; + uint32_t name_size; + uint32_t guid_offset; + uint32_t guid_size; + uint32_t data_size; +}; + +struct qcscm_rsp_uefi_get_variable { + uint32_t command_id; + uint32_t length; + uint32_t status; + uint32_t attributes; + uint32_t data_offset; + uint32_t data_size; +}; + +EFI_STATUS +qcscm_uefi_get_variable(struct qcscm_softc *sc, + CHAR16 *name, int name_size, EFI_GUID *guid, + uint32_t *attributes, uint8_t *data, int *data_size) +{ + struct qcscm_req_uefi_get_variable *req; + struct qcscm_rsp_uefi_get_variable *resp; + struct qcscm_dmamem *qdm; + size_t reqsize, respsize; + off_t reqoff, respoff; + int ret; + + reqsize = ALIGN(sizeof(*req)) + ALIGN(name_size) + ALIGN(sizeof(*guid)); + respsize = ALIGN(sizeof(*resp)) + ALIGN(*data_size); + + reqoff = 0; + respoff = reqsize; + + qdm = qcscm_dmamem_alloc(sc, round_page(reqsize + respsize), 8); + if (qdm == NULL) + return QCTEE_UEFI_DEVICE_ERROR; + + req = QCSCM_DMA_KVA(qdm) + reqoff; + req->command_id = QCTEE_UEFI_GET_VARIABLE; + req->data_size = *data_size; + req->name_offset = ALIGN(sizeof(*req)); + req->name_size = name_size; + req->guid_offset = ALIGN(req->name_offset + req->name_size); + req->guid_size = sizeof(*guid); + req->length = req->guid_offset + req->guid_size; + + memcpy((char *)req + req->guid_offset, guid, sizeof(*guid)); + memcpy((char *)req + req->name_offset, name, name_size); + + ret = qcscm_tee_app_send(sc, sc->sc_uefi_id, + QCSCM_DMA_DVA(qdm) + reqoff, reqsize, + QCSCM_DMA_DVA(qdm) + respoff, respsize); + if (ret) { + qcscm_dmamem_free(sc, qdm); + return QCTEE_UEFI_DEVICE_ERROR; + } + + resp = QCSCM_DMA_KVA(qdm) + respoff; + if (resp->command_id != QCTEE_UEFI_GET_VARIABLE || + resp->length < sizeof(*resp) || resp->length > respsize) { + qcscm_dmamem_free(sc, qdm); + return QCTEE_UEFI_DEVICE_ERROR; + } + + if (resp->status) { + if (resp->status == QCTEE_UEFI_BUFFER_TOO_SMALL) + *data_size = resp->data_size; + if (attributes) + *attributes = resp->attributes; + ret = resp->status; + qcscm_dmamem_free(sc, qdm); + return ret; + } + + if (resp->data_offset + resp->data_size > resp->length) { + qcscm_dmamem_free(sc, qdm); + return QCTEE_UEFI_DEVICE_ERROR; + } + + if (attributes) + *attributes = resp->attributes; + + if (*data_size == 0) { + *data_size = resp->data_size; + qcscm_dmamem_free(sc, qdm); + return QCTEE_UEFI_SUCCESS; + } + + if (resp->data_size > *data_size) { + *data_size = resp->data_size; + qcscm_dmamem_free(sc, qdm); + return QCTEE_UEFI_BUFFER_TOO_SMALL; + } + + memcpy(data, (char *)resp + resp->data_offset, resp->data_size); + *data_size = resp->data_size; + + qcscm_dmamem_free(sc, qdm); + return EFI_SUCCESS; +} + +struct qcscm_req_uefi_set_variable { + uint32_t command_id; + uint32_t length; + uint32_t name_offset; + uint32_t name_size; + uint32_t guid_offset; + uint32_t guid_size; + uint32_t attributes; + uint32_t data_offset; + uint32_t data_size; +}; + +struct qcscm_rsp_uefi_set_variable { + uint32_t command_id; + uint32_t length; + uint32_t status; + uint32_t unknown[2]; +}; + +EFI_STATUS +qcscm_uefi_set_variable(struct qcscm_softc *sc, + CHAR16 *name, int name_size, EFI_GUID *guid, + uint32_t attributes, uint8_t *data, int data_size) +{ + struct qcscm_req_uefi_set_variable *req; + struct qcscm_rsp_uefi_set_variable *resp; + struct qcscm_dmamem *qdm; + size_t reqsize, respsize; + off_t reqoff, respoff; + int ret; + + reqsize = ALIGN(sizeof(*req)) + ALIGN(name_size) + ALIGN(sizeof(*guid)) + + ALIGN(data_size); + respsize = ALIGN(sizeof(*resp)); + + reqoff = 0; + respoff = reqsize; + + qdm = qcscm_dmamem_alloc(sc, round_page(reqsize + respsize), 8); + if (qdm == NULL) + return QCTEE_UEFI_DEVICE_ERROR; + + req = QCSCM_DMA_KVA(qdm) + reqoff; + req->command_id = QCTEE_UEFI_SET_VARIABLE; + req->attributes = attributes; + req->name_offset = ALIGN(sizeof(*req)); + req->name_size = name_size; + req->guid_offset = ALIGN(req->name_offset + req->name_size); + req->guid_size = sizeof(*guid); + req->data_offset = ALIGN(req->guid_offset + req->guid_size); + req->data_size = data_size; + req->length = req->data_offset + req->data_size; + + memcpy((char *)req + req->name_offset, name, name_size); + memcpy((char *)req + req->guid_offset, guid, sizeof(*guid)); + memcpy((char *)req + req->data_offset, data, data_size); + + ret = qcscm_tee_app_send(sc, sc->sc_uefi_id, + QCSCM_DMA_DVA(qdm) + reqoff, reqsize, + QCSCM_DMA_DVA(qdm) + respoff, respsize); + if (ret) { + qcscm_dmamem_free(sc, qdm); + return QCTEE_UEFI_DEVICE_ERROR; + } + + resp = QCSCM_DMA_KVA(qdm) + respoff; + if (resp->command_id != QCTEE_UEFI_SET_VARIABLE || + resp->length < sizeof(*resp) || resp->length > respsize) { + qcscm_dmamem_free(sc, qdm); + return QCTEE_UEFI_DEVICE_ERROR; + } + + if (resp->status) { + ret = resp->status; + qcscm_dmamem_free(sc, qdm); + return ret; + } + + qcscm_dmamem_free(sc, qdm); + return QCTEE_UEFI_SUCCESS; +} + +struct qcscm_req_uefi_get_next_variable { + uint32_t command_id; + uint32_t length; + uint32_t guid_offset; + uint32_t guid_size; + uint32_t name_offset; + uint32_t name_size; +}; + +struct qcscm_rsp_uefi_get_next_variable { + uint32_t command_id; + uint32_t length; + uint32_t status; + uint32_t guid_offset; + uint32_t guid_size; + uint32_t name_offset; + uint32_t name_size; +}; + +EFI_STATUS +qcscm_uefi_get_next_variable(struct qcscm_softc *sc, + CHAR16 *name, int *name_size, EFI_GUID *guid) +{ + struct qcscm_req_uefi_get_next_variable *req; + struct qcscm_rsp_uefi_get_next_variable *resp; + struct qcscm_dmamem *qdm; + size_t reqsize, respsize; + off_t reqoff, respoff; + int ret; + + reqsize = ALIGN(sizeof(*req)) + ALIGN(sizeof(*guid)) + ALIGN(*name_size); + respsize = ALIGN(sizeof(*resp)) + ALIGN(sizeof(*guid)) + ALIGN(*name_size); + + reqoff = 0; + respoff = reqsize; + + qdm = qcscm_dmamem_alloc(sc, round_page(reqsize + respsize), 8); + if (qdm == NULL) + return QCTEE_UEFI_DEVICE_ERROR; + + req = QCSCM_DMA_KVA(qdm) + reqoff; + req->command_id = QCTEE_UEFI_GET_NEXT_VARIABLE; + req->guid_offset = ALIGN(sizeof(*req)); + req->guid_size = sizeof(*guid); + req->name_offset = ALIGN(req->guid_offset + req->guid_size); + req->name_size = *name_size; + req->length = req->name_offset + req->name_size; + + memcpy((char *)req + req->guid_offset, guid, sizeof(*guid)); + memcpy((char *)req + req->name_offset, name, *name_size); + + ret = qcscm_tee_app_send(sc, sc->sc_uefi_id, + QCSCM_DMA_DVA(qdm) + reqoff, reqsize, + QCSCM_DMA_DVA(qdm) + respoff, respsize); + if (ret) { + qcscm_dmamem_free(sc, qdm); + return QCTEE_UEFI_DEVICE_ERROR; + } + + resp = QCSCM_DMA_KVA(qdm) + respoff; + if (resp->command_id != QCTEE_UEFI_GET_NEXT_VARIABLE || + resp->length < sizeof(*resp) || resp->length > respsize) { + qcscm_dmamem_free(sc, qdm); + return QCTEE_UEFI_DEVICE_ERROR; + } + + if (resp->status) { + if (resp->status == QCTEE_UEFI_BUFFER_TOO_SMALL) + *name_size = resp->name_size; + ret = resp->status; + qcscm_dmamem_free(sc, qdm); + return ret; + } + + if (resp->guid_offset + resp->guid_size > resp->length || + resp->name_offset + resp->name_size > resp->length) { + qcscm_dmamem_free(sc, qdm); + return QCTEE_UEFI_DEVICE_ERROR; + } + + if (resp->guid_size != sizeof(*guid)) { + qcscm_dmamem_free(sc, qdm); + return QCTEE_UEFI_DEVICE_ERROR; + } + + if (resp->name_size > *name_size) { + *name_size = resp->name_size; + qcscm_dmamem_free(sc, qdm); + return QCTEE_UEFI_BUFFER_TOO_SMALL; + } + + memcpy(guid, (char *)resp + resp->guid_offset, sizeof(*guid)); + memcpy(name, (char *)resp + resp->name_offset, resp->name_size); + *name_size = resp->name_size; + + qcscm_dmamem_free(sc, qdm); + return QCTEE_UEFI_SUCCESS; +} + +#ifdef QCSCM_DEBUG +void +qcscm_uefi_dump_variables(struct qcscm_softc *sc) +{ + CHAR16 name[128]; + EFI_GUID guid; + int namesize = sizeof(name); + int i, ret; + + memset(name, 0, sizeof(name)); + memset(&guid, 0, sizeof(guid)); + + for (;;) { + ret = qcscm_uefi_get_next_variable(sc, name, &namesize, &guid); + if (ret == 0) { + printf("%s: ", sc->sc_dev.dv_xname); + for (i = 0; i < namesize / 2; i++) + printf("%c", name[i]); + printf(" { 0x%08x, 0x%04x, 0x%04x, { ", + guid.Data1, guid.Data2, guid.Data3); + for (i = 0; i < 8; i++) { + printf(" 0x%02x,", guid.Data4[i]); + } + printf(" }"); + printf("\n"); + namesize = sizeof(name); + continue; + } + break; + } +} + +void +qcscm_uefi_dump_variable(struct qcscm_softc *sc, CHAR16 *name, int namesize, + EFI_GUID *guid) +{ + uint8_t data[512]; + int datasize = sizeof(data); + int i, ret; + + ret = qcscm_uefi_get_variable(sc, name, namesize, guid, + NULL, data, &datasize); + if (ret != QCTEE_UEFI_SUCCESS) { + printf("%s: error reading ", sc->sc_dev.dv_xname); + for (i = 0; i < namesize / 2; i++) + printf("%c", name[i]); + printf("\n"); + return; + } + + printf("%s: ", sc->sc_dev.dv_xname); + for (i = 0; i < namesize / 2; i++) + printf("%c", name[i]); + printf(" = "); + for (i = 0; i < datasize; i++) + printf("%02x", data[i]); + printf("\n"); +} +#endif + +int +qcscm_uefi_rtc_get(uint32_t *off) +{ + struct qcscm_softc *sc = qcscm_sc; + uint32_t rtcinfo[3]; + int rtcinfosize = sizeof(rtcinfo); + + if (sc == NULL) + return ENXIO; + + if (qcscm_uefi_get_variable(sc, u"RTCInfo", sizeof(u"RTCInfo"), + &qcscm_uefi_rtcinfo_guid, NULL, (uint8_t *)rtcinfo, + &rtcinfosize) != 0) + return EIO; + + *off = rtcinfo[0]; + return 0; +} + +int +qcscm_uefi_rtc_set(uint32_t off) +{ + struct qcscm_softc *sc = qcscm_sc; + uint32_t rtcinfo[3]; + int rtcinfosize = sizeof(rtcinfo); + + if (sc == NULL) + return ENXIO; + + if (qcscm_uefi_get_variable(sc, u"RTCInfo", sizeof(u"RTCInfo"), + &qcscm_uefi_rtcinfo_guid, NULL, (uint8_t *)rtcinfo, + &rtcinfosize) != 0) + return EIO; + + /* No need to set if we're not changing anything */ + if (rtcinfo[0] == off) + return 0; + + rtcinfo[0] = off; + rtcinfo[1] = 0x10000; + rtcinfo[2] = 0; + + if (qcscm_uefi_set_variable(sc, u"RTCInfo", sizeof(u"RTCInfo"), + &qcscm_uefi_rtcinfo_guid, EFI_VARIABLE_NON_VOLATILE | + EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, + (uint8_t *)rtcinfo, sizeof(rtcinfo)) != 0) + return EIO; + + return 0; +} + +/* DMA code */ +struct qcscm_dmamem * +qcscm_dmamem_alloc(struct qcscm_softc *sc, bus_size_t size, bus_size_t align) +{ + struct qcscm_dmamem *qdm; + int nsegs; + + qdm = malloc(sizeof(*qdm), M_DEVBUF, M_WAITOK | M_ZERO); + qdm->qdm_size = size; + + if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, + BUS_DMA_WAITOK | BUS_DMA_ALLOCNOW, &qdm->qdm_map) != 0) + goto qdmfree; + + if (bus_dmamem_alloc(sc->sc_dmat, size, align, 0, &qdm->qdm_seg, 1, + &nsegs, BUS_DMA_WAITOK) != 0) + goto destroy; + + if (bus_dmamem_map(sc->sc_dmat, &qdm->qdm_seg, nsegs, size, + &qdm->qdm_kva, BUS_DMA_WAITOK | BUS_DMA_COHERENT) != 0) + goto free; + + if (bus_dmamap_load(sc->sc_dmat, qdm->qdm_map, qdm->qdm_kva, size, + NULL, BUS_DMA_WAITOK) != 0) + goto unmap; + + bzero(qdm->qdm_kva, size); + + return (qdm); + +unmap: + bus_dmamem_unmap(sc->sc_dmat, qdm->qdm_kva, size); +free: + bus_dmamem_free(sc->sc_dmat, &qdm->qdm_seg, 1); +destroy: + bus_dmamap_destroy(sc->sc_dmat, qdm->qdm_map); +qdmfree: + free(qdm, M_DEVBUF, sizeof(*qdm)); + + return (NULL); +} + +void +qcscm_dmamem_free(struct qcscm_softc *sc, struct qcscm_dmamem *qdm) +{ + bus_dmamem_unmap(sc->sc_dmat, qdm->qdm_kva, qdm->qdm_size); + bus_dmamem_free(sc->sc_dmat, &qdm->qdm_seg, 1); + bus_dmamap_destroy(sc->sc_dmat, qdm->qdm_map); + free(qdm, M_DEVBUF, sizeof(*qdm)); +} |