summaryrefslogtreecommitdiff
path: root/sys/dev
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/fdt/files.fdt7
-rw-r--r--sys/dev/fdt/qcrtc.c37
-rw-r--r--sys/dev/fdt/qcscm.c783
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, &reg, 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, &reg, 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));
+}