summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/ufm.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/io/ufm.c')
-rw-r--r--usr/src/uts/common/io/ufm.c104
1 files changed, 104 insertions, 0 deletions
diff --git a/usr/src/uts/common/io/ufm.c b/usr/src/uts/common/io/ufm.c
index 49a98c101a..d124485e65 100644
--- a/usr/src/uts/common/io/ufm.c
+++ b/usr/src/uts/common/io/ufm.c
@@ -11,6 +11,7 @@
/*
* Copyright 2019 Joyent, Inc.
+ * Copyright 2020 Oxide Computer Company
*/
/*
@@ -27,6 +28,9 @@
#include <sys/file.h>
#include <sys/kmem.h>
#include <sys/stat.h>
+#include <sys/sysmacros.h>
+
+#define UFM_READ_SIZE (1 * 1024 * 1024)
#define UFMTEST_IOC ('u' << 24) | ('f' << 16) | ('t' << 8)
#define UFMTEST_IOC_SETFW (UFMTEST_IOC | 1)
@@ -145,6 +149,7 @@ ufm_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
if (devi != NULL)
ddi_remove_minor_node(devi, NULL);
+ ufm_devi = NULL;
return (DDI_SUCCESS);
}
@@ -454,6 +459,101 @@ ufm_do_report(intptr_t data, int mode)
}
static int
+ufm_do_readimg(intptr_t data, int mode)
+{
+ int ret;
+ uint_t model;
+ ufm_ioc_readimg_t ufri;
+ char devpath[MAXPATHLEN];
+ ddi_ufm_handle_t *ufmh;
+ dev_info_t *dip;
+#ifdef _MULTI_DATAMODEL
+ ufm_ioc_readimg32_t ufri32;
+#endif
+
+ model = ddi_model_convert_from(mode);
+ switch (model) {
+#ifdef _MULTI_DATAMODEL
+ case DDI_MODEL_ILP32:
+ if (ddi_copyin((void *)data, &ufri32, sizeof (ufri32),
+ mode) != 0) {
+ return (EFAULT);
+ }
+ ufri.ufri_version = ufri32.ufri_version;
+ ufri.ufri_imageno = ufri32.ufri_imageno;
+ ufri.ufri_slotno = ufri32.ufri_slotno;
+ ufri.ufri_offset = ufri32.ufri_offset;
+ ufri.ufri_len = ufri32.ufri_len;
+ ufri.ufri_nread = ufri32.ufri_nread;
+
+ if (strlcpy(ufri.ufri_devpath, ufri32.ufri_devpath,
+ MAXPATHLEN) >= MAXPATHLEN) {
+ return (EOVERFLOW);
+ }
+ ufri.ufri_buf = (caddr_t)(uintptr_t)ufri32.ufri_buf;
+ break;
+#endif /* _MULTI_DATAMODEL */
+ case DDI_MODEL_NONE:
+ default:
+ if (ddi_copyin((void *)data, &ufri, sizeof (ufri), mode) != 0) {
+ return (EFAULT);
+ }
+ }
+
+ if (strlcpy(devpath, ufri.ufri_devpath, MAXPATHLEN) >= MAXPATHLEN)
+ return (EOVERFLOW);
+
+ if ((dip = e_ddi_hold_devi_by_path(devpath, 0)) == NULL) {
+ return (ENOTSUP);
+ }
+ if ((ufmh = ufm_find(devpath)) == NULL) {
+ ddi_release_devi(dip);
+ return (ENOTSUP);
+ }
+ ASSERT(MUTEX_HELD(&ufmh->ufmh_lock));
+
+ if (!ufm_driver_ready(ufmh)) {
+ ret = EAGAIN;
+ goto out;
+ }
+
+ if (ufri.ufri_version != ufmh->ufmh_version) {
+ ret = ENOTSUP;
+ goto out;
+ }
+
+ ret = ufm_read_img(ufmh, ufri.ufri_imageno, ufri.ufri_slotno,
+ ufri.ufri_len, ufri.ufri_offset, (uintptr_t)ufri.ufri_buf,
+ &ufri.ufri_nread, mode);
+
+out:
+ mutex_exit(&ufmh->ufmh_lock);
+ ddi_release_devi(dip);
+
+ if (ret == 0) {
+ switch (model) {
+#ifdef _MULTI_DATAMODEL
+ case DDI_MODEL_ILP32:
+ ufri32.ufri_nread = ufri.ufri_nread;
+ if (ddi_copyout(&ufri32, (void *)data, sizeof (ufri32),
+ mode) != 0) {
+ return (EFAULT);
+ }
+ break;
+#endif /* _MULTI_DATAMODEL */
+ case DDI_MODEL_NONE:
+ default:
+ if (ddi_copyout(&ufri, (void *)data, sizeof (ufri),
+ mode) != 0) {
+ return (EFAULT);
+ }
+ }
+ }
+
+ return (ret);
+}
+
+static int
ufm_ioctl(dev_t dev, int cmd, intptr_t data, int mode, cred_t *credp,
int *rvalp)
{
@@ -474,6 +574,10 @@ ufm_ioctl(dev_t dev, int cmd, intptr_t data, int mode, cred_t *credp,
case UFM_IOC_REPORT:
ret = ufm_do_report(data, mode);
break;
+
+ case UFM_IOC_READIMG:
+ ret = ufm_do_readimg(data, mode);
+ break;
default:
return (ENOTTY);
}