summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--usr/src/cmd/Makefile2
-rw-r--r--usr/src/cmd/amdzen/Makefile (renamed from usr/src/cmd/usmn/Makefile)2
-rw-r--r--usr/src/cmd/amdzen/udf.c118
-rw-r--r--usr/src/cmd/amdzen/usmn.c (renamed from usr/src/cmd/usmn/usmn.c)0
-rw-r--r--usr/src/man/man7d/Makefile1
-rw-r--r--usr/src/man/man7d/amdzen.7d4
-rw-r--r--usr/src/man/man7d/usmn.7d3
-rw-r--r--usr/src/man/man7d/zen_udf.7d36
-rw-r--r--usr/src/pkg/manifests/driver-developer-amd-zen.mf4
-rw-r--r--usr/src/uts/intel/Makefile.files1
-rw-r--r--usr/src/uts/intel/Makefile.intel2
-rw-r--r--usr/src/uts/intel/io/amdzen/amdzen.c70
-rw-r--r--usr/src/uts/intel/io/amdzen/amdzen.h3
-rw-r--r--usr/src/uts/intel/io/amdzen/amdzen_client.h4
-rw-r--r--usr/src/uts/intel/io/amdzen/zen_udf.c286
-rw-r--r--usr/src/uts/intel/io/amdzen/zen_udf.h44
-rw-r--r--usr/src/uts/intel/zen_udf/Makefile41
17 files changed, 611 insertions, 10 deletions
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index 2f35ac9412..dee86627e5 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -489,6 +489,7 @@ i386_SUBDIRS= \
acpihpd \
addbadsec \
ahciem \
+ amdzen \
bhyve \
bhyvectl \
biosdev \
@@ -499,7 +500,6 @@ i386_SUBDIRS= \
rdmsr \
rtc \
ucodeadm \
- usmn \
xhci \
xvm
diff --git a/usr/src/cmd/usmn/Makefile b/usr/src/cmd/amdzen/Makefile
index 4fc6e17bfe..c9f8b7778e 100644
--- a/usr/src/cmd/usmn/Makefile
+++ b/usr/src/cmd/amdzen/Makefile
@@ -13,7 +13,7 @@
# Copyright 2020 Oxide Computer Company
#
-PROG= usmn
+PROG= usmn udf
include ../Makefile.cmd
diff --git a/usr/src/cmd/amdzen/udf.c b/usr/src/cmd/amdzen/udf.c
new file mode 100644
index 0000000000..604e0b4802
--- /dev/null
+++ b/usr/src/cmd/amdzen/udf.c
@@ -0,0 +1,118 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2020 Oxide Computer Company
+ */
+
+/*
+ * Facilitate access to the AMD Zen data fabric
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <err.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <strings.h>
+#include <zen_udf.h>
+
+static void
+udf_readone(int fd, uint8_t inst, uint8_t func, uint16_t reg, boolean_t do64)
+{
+ int ret;
+ zen_udf_io_t zui;
+
+ bzero(&zui, sizeof (zui));
+ zui.zui_inst = inst;
+ zui.zui_func = func;
+ zui.zui_reg = reg;
+
+ ret = ioctl(fd, do64 ? ZEN_UDF_READ64 : ZEN_UDF_READ32, &zui);
+ if (ret != 0) {
+ err(EXIT_FAILURE, "failed to issue read ioctl");
+ }
+
+ (void) printf("ifr %x/%x/%x: 0x%" PRIx64 "\n",
+ inst, func, reg, zui.zui_data);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int c, fd;
+ const char *device = NULL;
+ const char *funcstr = NULL;
+ const char *inststr = NULL;
+ const char *regstr = NULL;
+ uint8_t func, inst;
+ uint16_t reg;
+ unsigned long lval;
+ char *eptr;
+ boolean_t do64 = B_FALSE;
+
+ while ((c = getopt(argc, argv, "d:f:i:r:l")) != -1) {
+ switch (c) {
+ case 'd':
+ device = optarg;
+ break;
+ case 'f':
+ funcstr = optarg;
+ break;
+ case 'i':
+ inststr = optarg;
+ break;
+ case 'l':
+ do64 = B_TRUE;
+ break;
+ case 'r':
+ regstr = optarg;
+ break;
+ }
+ }
+
+ if (device == NULL || funcstr == NULL || inststr == NULL ||
+ regstr == NULL) {
+ warnx("missing required arguments");
+ (void) fprintf(stderr, "Usage: udf [-l] -d device -f func -i "
+ "inst -r reg\n");
+ }
+
+ errno = 0;
+ lval = strtoul(funcstr, &eptr, 0);
+ if (errno != 0 || lval > UINT8_MAX || *eptr != '\0') {
+ errx(EXIT_FAILURE, "failed to parse -f: %s", funcstr);
+ }
+ func = (uint8_t)lval;
+
+ lval = strtoul(inststr, &eptr, 0);
+ if (errno != 0 || lval > UINT8_MAX || *eptr != '\0') {
+ errx(EXIT_FAILURE, "failed to parse -i: %s", inststr);
+ }
+ inst = (uint8_t)lval;
+
+ lval = strtoul(regstr, &eptr, 0);
+ if (errno != 0 || lval > UINT16_MAX || *eptr != '\0') {
+ errx(EXIT_FAILURE, "failed to parse -r: %s", regstr);
+ }
+ reg = (uint16_t)lval;
+
+ if ((fd = open(device, O_RDONLY)) < 0) {
+ err(EXIT_FAILURE, "failed to open %s", device);
+ }
+
+ udf_readone(fd, inst, func, reg, do64);
+ (void) close(fd);
+ return (0);
+}
diff --git a/usr/src/cmd/usmn/usmn.c b/usr/src/cmd/amdzen/usmn.c
index 3b9998f021..3b9998f021 100644
--- a/usr/src/cmd/usmn/usmn.c
+++ b/usr/src/cmd/amdzen/usmn.c
diff --git a/usr/src/man/man7d/Makefile b/usr/src/man/man7d/Makefile
index d8bbaaa77b..9da7d4b205 100644
--- a/usr/src/man/man7d/Makefile
+++ b/usr/src/man/man7d/Makefile
@@ -245,6 +245,7 @@ i386_MANFILES= ahci.7d \
virtio.7d \
wpi.7d \
xhci.7d \
+ zen_udf.7d \
zyd.7d
_MANLINKS= 1394.7d \
diff --git a/usr/src/man/man7d/amdzen.7d b/usr/src/man/man7d/amdzen.7d
index 0b0c531a99..171d36b0d6 100644
--- a/usr/src/man/man7d/amdzen.7d
+++ b/usr/src/man/man7d/amdzen.7d
@@ -45,4 +45,6 @@ drivers are limited to
.Sy x86
platforms with AMD Family 17h processors.
.Sh SEE ALSO
-.Xr smntemp 7D
+.Xr smntemp 7D ,
+.Xr usmn 7D ,
+.Xr zen_udf 7D
diff --git a/usr/src/man/man7d/usmn.7d b/usr/src/man/man7d/usmn.7d
index 016c85276c..b36da78be4 100644
--- a/usr/src/man/man7d/usmn.7d
+++ b/usr/src/man/man7d/usmn.7d
@@ -33,4 +33,5 @@ This driver is intended strictly for facilitating platform development
and is not recommended for systems that aren't doing kernel development
on AMD Zen platforms.
.Sh SEE ALSO
-.Xr amdzen 7D
+.Xr amdzen 7D ,
+.Xr zen_udf 7D
diff --git a/usr/src/man/man7d/zen_udf.7d b/usr/src/man/man7d/zen_udf.7d
new file mode 100644
index 0000000000..a2243ef67f
--- /dev/null
+++ b/usr/src/man/man7d/zen_udf.7d
@@ -0,0 +1,36 @@
+.\"
+.\" This file and its contents are supplied under the terms of the
+.\" Common Development and Distribution License ("CDDL"), version 1.0.
+.\" You may only use this file in accordance with the terms of version
+.\" 1.0 of the CDDL.
+.\"
+.\" A full copy of the text of the CDDL should have accompanied this
+.\" source. A copy of the CDDL is also available via the Internet at
+.\" http://www.illumos.org/license/CDDL.
+.\"
+.\"
+.\" Copyright 2020 Oxide Computer Company
+.\"
+.Dd October 7, 2020
+.Dt ZEN_UDF 7D
+.Os
+.Sh NAME
+.Nm zen_udf
+.Nd AMD data fabric user access driver
+.Sh SYNOPSIS
+.Pa /devices/pseudo/amdzen@0/zen_udf@3:zen_udf.*
+.Sh DESCRIPTION
+The
+.Nm
+driver provides the ability to read data from the AMD data fabric
+.Pq DF
+on AMD Family 17h
+.Pq Zen, Zen+, and Zen 2
+processors.
+.Pp
+This driver is intended strictly for facilitating platform development
+and is not recommended for systems that aren't doing kernel development
+on AMD Zen platforms.
+.Sh SEE ALSO
+.Xr amdzen 7D ,
+.Xr usmn 7D
diff --git a/usr/src/pkg/manifests/driver-developer-amd-zen.mf b/usr/src/pkg/manifests/driver-developer-amd-zen.mf
index 27d46083e5..7df997705f 100644
--- a/usr/src/pkg/manifests/driver-developer-amd-zen.mf
+++ b/usr/src/pkg/manifests/driver-developer-amd-zen.mf
@@ -27,7 +27,11 @@ dir path=usr/lib
dir path=usr/share/man
dir path=usr/share/man/man7d
driver name=usmn
+driver name=zen_udf
file path=kernel/drv/$(ARCH64)/usmn group=sys
+file path=kernel/drv/$(ARCH64)/zen_udf group=sys
+file path=usr/lib/udf mode=0555
file path=usr/lib/usmn mode=0555
file path=usr/share/man/man7d/usmn.7d
+file path=usr/share/man/man7d/zen_udf.7d
license lic_CDDL license=lic_CDDL
diff --git a/usr/src/uts/intel/Makefile.files b/usr/src/uts/intel/Makefile.files
index f6d2efc34c..336c25d739 100644
--- a/usr/src/uts/intel/Makefile.files
+++ b/usr/src/uts/intel/Makefile.files
@@ -350,3 +350,4 @@ AMDZEN_OBJS = amdzen.o
AMDZEN_STUB_OBJS = amdzen_stub.o
SMNTEMP_OBJS = smntemp.o
USMN_OBJS = usmn.o
+ZEN_UDF_OBJS = zen_udf.o
diff --git a/usr/src/uts/intel/Makefile.intel b/usr/src/uts/intel/Makefile.intel
index f24c591288..64e027fe15 100644
--- a/usr/src/uts/intel/Makefile.intel
+++ b/usr/src/uts/intel/Makefile.intel
@@ -776,4 +776,4 @@ DRV_KMODS += smntemp
#
DRV_KMODS += amdzen
DRV_KMODS += amdzen_stub
-DRV_KMODS += usmn
+DRV_KMODS += usmn zen_udf
diff --git a/usr/src/uts/intel/io/amdzen/amdzen.c b/usr/src/uts/intel/io/amdzen/amdzen.c
index 17a3c69b9a..25e38ee988 100644
--- a/usr/src/uts/intel/io/amdzen/amdzen.c
+++ b/usr/src/uts/intel/io/amdzen/amdzen.c
@@ -157,7 +157,8 @@ typedef struct {
static const amdzen_child_data_t amdzen_children[] = {
{ "smntemp", AMDZEN_C_SMNTEMP },
- { "usmn", AMDZEN_C_USMN }
+ { "usmn", AMDZEN_C_USMN },
+ { "zen_udf", AMDZEN_C_ZEN_UDF }
};
static uint32_t
@@ -166,6 +167,12 @@ amdzen_stub_get32(amdzen_stub_t *stub, off_t reg)
return (pci_config_get32(stub->azns_cfgspace, reg));
}
+static uint64_t
+amdzen_stub_get64(amdzen_stub_t *stub, off_t reg)
+{
+ return (pci_config_get64(stub->azns_cfgspace, reg));
+}
+
static void
amdzen_stub_put32(amdzen_stub_t *stub, off_t reg, uint32_t val)
{
@@ -189,6 +196,24 @@ amdzen_df_read32(amdzen_t *azn, amdzen_df_t *df, uint8_t inst, uint8_t func,
return (amdzen_stub_get32(df->adf_funcs[4], AMDZEN_DF_F4_FICAD_LO));
}
+/*
+ * Perform a targeted 64-bit indirect read to a specific instance and function.
+ */
+static uint64_t
+amdzen_df_read64(amdzen_t *azn, amdzen_df_t *df, uint8_t inst, uint8_t func,
+ uint16_t reg)
+{
+ uint32_t val;
+
+ VERIFY(MUTEX_HELD(&azn->azn_mutex));
+ val = AMDZEN_DF_F4_FICAA_TARG_INST | AMDZEN_DF_F4_FICAA_SET_REG(reg) |
+ AMDZEN_DF_F4_FICAA_SET_FUNC(func) |
+ AMDZEN_DF_F4_FICAA_SET_INST(inst) | AMDZEN_DF_F4_FICAA_SET_64B;
+ amdzen_stub_put32(df->adf_funcs[4], AMDZEN_DF_F4_FICAA, val);
+ return (amdzen_stub_get64(df->adf_funcs[4], AMDZEN_DF_F4_FICAD_LO));
+}
+
+
static uint32_t
amdzen_smn_read32(amdzen_t *azn, amdzen_df_t *df, uint32_t reg)
{
@@ -260,6 +285,45 @@ amdzen_c_df_count(void)
return (ret);
}
+int
+amdzen_c_df_read32(uint_t dfno, uint8_t inst, uint8_t func,
+ uint16_t reg, uint32_t *valp)
+{
+ amdzen_df_t *df;
+ amdzen_t *azn = amdzen_data;
+
+ mutex_enter(&azn->azn_mutex);
+ df = amdzen_df_find(azn, dfno);
+ if (df == NULL) {
+ mutex_exit(&azn->azn_mutex);
+ return (ENOENT);
+ }
+
+ *valp = amdzen_df_read32(azn, df, inst, func, reg);
+ mutex_exit(&azn->azn_mutex);
+
+ return (0);
+}
+
+int
+amdzen_c_df_read64(uint_t dfno, uint8_t inst, uint8_t func,
+ uint16_t reg, uint64_t *valp)
+{
+ amdzen_df_t *df;
+ amdzen_t *azn = amdzen_data;
+
+ mutex_enter(&azn->azn_mutex);
+ df = amdzen_df_find(azn, dfno);
+ if (df == NULL) {
+ mutex_exit(&azn->azn_mutex);
+ return (ENOENT);
+ }
+
+ *valp = amdzen_df_read64(azn, df, inst, func, reg);
+ mutex_exit(&azn->azn_mutex);
+
+ return (0);
+}
static boolean_t
amdzen_create_child(amdzen_t *azn, const amdzen_child_data_t *acd)
@@ -269,14 +333,14 @@ amdzen_create_child(amdzen_t *azn, const amdzen_child_data_t *acd)
if (ndi_devi_alloc(azn->azn_dip, acd->acd_name,
(pnode_t)DEVI_SID_NODEID, &child) != NDI_SUCCESS) {
- dev_err(azn->azn_dip, CE_WARN, "failed to allocate child "
+ dev_err(azn->azn_dip, CE_WARN, "!failed to allocate child "
"dip for %s", acd->acd_name);
return (B_FALSE);
}
ddi_set_parent_data(child, (void *)acd);
if ((ret = ndi_devi_online(child, 0)) != NDI_SUCCESS) {
- dev_err(azn->azn_dip, CE_WARN, "failed to online child "
+ dev_err(azn->azn_dip, CE_WARN, "!failed to online child "
"dip %s: %d", acd->acd_name, ret);
return (B_FALSE);
}
diff --git a/usr/src/uts/intel/io/amdzen/amdzen.h b/usr/src/uts/intel/io/amdzen/amdzen.h
index 79b0bc38f4..8150495911 100644
--- a/usr/src/uts/intel/io/amdzen/amdzen.h
+++ b/usr/src/uts/intel/io/amdzen/amdzen.h
@@ -289,7 +289,8 @@ typedef struct amdzen {
typedef enum {
AMDZEN_C_SMNTEMP = 1,
- AMDZEN_C_USMN
+ AMDZEN_C_USMN,
+ AMDZEN_C_ZEN_UDF
} amdzen_child_t;
/*
diff --git a/usr/src/uts/intel/io/amdzen/amdzen_client.h b/usr/src/uts/intel/io/amdzen/amdzen_client.h
index ebb942c40b..41ca3e8afd 100644
--- a/usr/src/uts/intel/io/amdzen/amdzen_client.h
+++ b/usr/src/uts/intel/io/amdzen/amdzen_client.h
@@ -26,8 +26,10 @@
extern "C" {
#endif
-extern int amdzen_c_smn_read32(uint_t df, uint32_t reg, uint32_t *);
+extern int amdzen_c_smn_read32(uint_t, uint32_t, uint32_t *);
extern uint_t amdzen_c_df_count(void);
+extern int amdzen_c_df_read32(uint_t, uint8_t, uint8_t, uint16_t, uint32_t *);
+extern int amdzen_c_df_read64(uint_t, uint8_t, uint8_t, uint16_t, uint64_t *);
#ifdef __cplusplus
}
diff --git a/usr/src/uts/intel/io/amdzen/zen_udf.c b/usr/src/uts/intel/io/amdzen/zen_udf.c
new file mode 100644
index 0000000000..61d1e79774
--- /dev/null
+++ b/usr/src/uts/intel/io/amdzen/zen_udf.c
@@ -0,0 +1,286 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2020 Oxide Computer Company
+ */
+
+/*
+ * A companion to zen_udf(7D) that allows user access to read the data fabric
+ * for development purposes.
+ */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/errno.h>
+#include <sys/open.h>
+#include <sys/cred.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/devops.h>
+#include <sys/cmn_err.h>
+#include <sys/policy.h>
+#include <amdzen_client.h>
+
+#include <zen_udf.h>
+
+typedef struct zen_udf {
+ dev_info_t *zudf_dip;
+ uint_t zudf_ndfs;
+} zen_udf_t;
+
+static zen_udf_t zen_udf_data;
+
+static int
+zen_udf_open(dev_t *devp, int flags, int otype, cred_t *credp)
+{
+ minor_t m;
+ zen_udf_t *zen_udf = &zen_udf_data;
+
+ if (crgetzoneid(credp) != GLOBAL_ZONEID ||
+ secpolicy_hwmanip(credp) != 0) {
+ return (EPERM);
+ }
+
+ if ((flags & (FEXCL | FNDELAY | FNONBLOCK)) != 0) {
+ return (EINVAL);
+ }
+
+ if (otype != OTYP_CHR) {
+ return (EINVAL);
+ }
+
+ m = getminor(*devp);
+ if (m >= zen_udf->zudf_ndfs) {
+ return (ENXIO);
+ }
+
+ return (0);
+}
+
+static int
+zen_udf_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
+ int *rvalp)
+{
+ uint_t dfno;
+ zen_udf_t *zen_udf = &zen_udf_data;
+ zen_udf_io_t zui;
+
+ if (cmd != ZEN_UDF_READ32 && cmd != ZEN_UDF_READ64) {
+ return (ENOTTY);
+ }
+
+ dfno = getminor(dev);
+ if (dfno >= zen_udf->zudf_ndfs) {
+ return (ENXIO);
+ }
+
+ if (crgetzoneid(credp) != GLOBAL_ZONEID ||
+ secpolicy_hwmanip(credp) != 0) {
+ return (EPERM);
+ }
+
+ if (ddi_copyin((void *)arg, &zui, sizeof (zui), mode & FKIOCTL) != 0) {
+ return (EFAULT);
+ }
+
+ if (cmd == ZEN_UDF_READ32) {
+ int ret;
+ uint32_t data;
+
+ ret = amdzen_c_df_read32(dfno, zui.zui_inst, zui.zui_func,
+ zui.zui_reg, &data);
+ if (ret != 0) {
+ return (ret);
+ }
+
+ zui.zui_data = data;
+ } else {
+ int ret;
+
+ ret = amdzen_c_df_read64(dfno, zui.zui_inst, zui.zui_func,
+ zui.zui_reg, &zui.zui_data);
+ if (ret != 0) {
+ return (ret);
+ }
+ }
+
+ if (ddi_copyout(&zui, (void *)arg, sizeof (zui), mode & FKIOCTL) != 0) {
+ return (EFAULT);
+ }
+
+ return (0);
+}
+
+static int
+zen_udf_close(dev_t dev, int flag, int otyp, cred_t *credp)
+{
+ return (0);
+}
+
+static void
+zen_udf_cleanup(zen_udf_t *zen_udf)
+{
+ ddi_remove_minor_node(zen_udf->zudf_dip, NULL);
+ zen_udf->zudf_ndfs = 0;
+ zen_udf->zudf_dip = NULL;
+}
+
+static int
+zen_udf_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ zen_udf_t *zen_udf = &zen_udf_data;
+
+ if (cmd == DDI_RESUME) {
+ return (DDI_SUCCESS);
+ } else if (cmd != DDI_ATTACH) {
+ return (DDI_FAILURE);
+ }
+
+ if (zen_udf->zudf_dip != NULL) {
+ dev_err(dip, CE_WARN, "!zen_udf is already attached to a "
+ "dev_info_t: %p", zen_udf->zudf_dip);
+ return (DDI_FAILURE);
+ }
+
+ zen_udf->zudf_dip = dip;
+ zen_udf->zudf_ndfs = amdzen_c_df_count();
+ for (uint_t i = 0; i < zen_udf->zudf_ndfs; i++) {
+ char buf[32];
+
+ (void) snprintf(buf, sizeof (buf), "zen_udf.%u", i);
+ if (ddi_create_minor_node(dip, buf, S_IFCHR, i, DDI_PSEUDO,
+ 0) != DDI_SUCCESS) {
+ dev_err(dip, CE_WARN, "!failed to create minor %s",
+ buf);
+ goto err;
+ }
+ }
+
+ return (DDI_SUCCESS);
+
+err:
+ zen_udf_cleanup(zen_udf);
+ return (DDI_FAILURE);
+}
+
+static int
+zen_udf_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp)
+{
+ zen_udf_t *zen_udf = &zen_udf_data;
+ minor_t m;
+
+ switch (cmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ m = getminor((dev_t)arg);
+ if (m >= zen_udf->zudf_ndfs) {
+ return (DDI_FAILURE);
+ }
+ *resultp = (void *)zen_udf->zudf_dip;
+ break;
+ case DDI_INFO_DEVT2INSTANCE:
+ m = getminor((dev_t)arg);
+ if (m >= zen_udf->zudf_ndfs) {
+ return (DDI_FAILURE);
+ }
+ *resultp = (void *)(uintptr_t)ddi_get_instance(
+ zen_udf->zudf_dip);
+ break;
+ default:
+ return (DDI_FAILURE);
+ }
+ return (DDI_SUCCESS);
+}
+
+static int
+zen_udf_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ zen_udf_t *zen_udf = &zen_udf_data;
+
+ if (cmd == DDI_SUSPEND) {
+ return (DDI_SUCCESS);
+ } else if (cmd != DDI_DETACH) {
+ return (DDI_FAILURE);
+ }
+
+ if (zen_udf->zudf_dip != dip) {
+ dev_err(dip, CE_WARN, "!asked to detach zen_udf, but dip "
+ "doesn't match");
+ return (DDI_FAILURE);
+ }
+
+ zen_udf_cleanup(zen_udf);
+ return (DDI_SUCCESS);
+}
+
+static struct cb_ops zen_udf_cb_ops = {
+ .cb_open = zen_udf_open,
+ .cb_close = zen_udf_close,
+ .cb_strategy = nodev,
+ .cb_print = nodev,
+ .cb_dump = nodev,
+ .cb_read = nodev,
+ .cb_write = nodev,
+ .cb_ioctl = zen_udf_ioctl,
+ .cb_devmap = nodev,
+ .cb_mmap = nodev,
+ .cb_segmap = nodev,
+ .cb_chpoll = nochpoll,
+ .cb_prop_op = ddi_prop_op,
+ .cb_flag = D_MP,
+ .cb_rev = CB_REV,
+ .cb_aread = nodev,
+ .cb_awrite = nodev
+};
+
+static struct dev_ops zen_udf_dev_ops = {
+ .devo_rev = DEVO_REV,
+ .devo_refcnt = 0,
+ .devo_getinfo = zen_udf_getinfo,
+ .devo_identify = nulldev,
+ .devo_probe = nulldev,
+ .devo_attach = zen_udf_attach,
+ .devo_detach = zen_udf_detach,
+ .devo_reset = nodev,
+ .devo_quiesce = ddi_quiesce_not_needed,
+ .devo_cb_ops = &zen_udf_cb_ops
+};
+
+static struct modldrv zen_udf_modldrv = {
+ .drv_modops = &mod_driverops,
+ .drv_linkinfo = "AMD User DF Access",
+ .drv_dev_ops = &zen_udf_dev_ops
+};
+
+static struct modlinkage zen_udf_modlinkage = {
+ .ml_rev = MODREV_1,
+ .ml_linkage = { &zen_udf_modldrv, NULL }
+};
+
+int
+_init(void)
+{
+ return (mod_install(&zen_udf_modlinkage));
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&zen_udf_modlinkage, modinfop));
+}
+
+int
+_fini(void)
+{
+ return (mod_remove(&zen_udf_modlinkage));
+}
diff --git a/usr/src/uts/intel/io/amdzen/zen_udf.h b/usr/src/uts/intel/io/amdzen/zen_udf.h
new file mode 100644
index 0000000000..ef5a3184d5
--- /dev/null
+++ b/usr/src/uts/intel/io/amdzen/zen_udf.h
@@ -0,0 +1,44 @@
+/*
+ * This file and its contents are supplied under the terms of the
+ * Common Development and Distribution License ("CDDL"), version 1.0.
+ * You may only use this file in accordance with the terms of version
+ * 1.0 of the CDDL.
+ *
+ * A full copy of the text of the CDDL should have accompanied this
+ * source. A copy of the CDDL is also available via the Internet at
+ * http://www.illumos.org/license/CDDL.
+ */
+
+/*
+ * Copyright 2020 Oxide Computer Company
+ */
+
+#ifndef _ZEN_UDF_H
+#define _ZEN_UDF_H
+
+/*
+ * Private ioctls for interfacing with the zen_udf driver.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ZEN_UDF_IOCTL (('u' << 24) | ('d' << 16) | ('f' << 8))
+
+#define ZEN_UDF_READ32 (ZEN_UDF_IOCTL | 0x01)
+#define ZEN_UDF_READ64 (ZEN_UDF_IOCTL | 0x02)
+
+typedef struct zen_udf_io {
+ uint8_t zui_inst;
+ uint8_t zui_func;
+ uint16_t zui_reg;
+ uint32_t zui_pad;
+ uint64_t zui_data;
+} zen_udf_io_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _ZEN_UDF_H */
diff --git a/usr/src/uts/intel/zen_udf/Makefile b/usr/src/uts/intel/zen_udf/Makefile
new file mode 100644
index 0000000000..5b312d7013
--- /dev/null
+++ b/usr/src/uts/intel/zen_udf/Makefile
@@ -0,0 +1,41 @@
+#
+# This file and its contents are supplied under the terms of the
+# Common Development and Distribution License ("CDDL"), version 1.0.
+# You may only use this file in accordance with the terms of version
+# 1.0 of the CDDL.
+#
+# A full copy of the text of the CDDL should have accompanied this
+# source. A copy of the CDDL is also available via the Internet at
+# http://www.illumos.org/license/CDDL.
+#
+
+#
+# Copyright 2020 Oxide Computer Company
+#
+
+UTSBASE = ../..
+
+MODULE = zen_udf
+OBJECTS = $(ZEN_UDF_OBJS:%=$(OBJS_DIR)/%)
+ROOTMODULE = $(ROOT_DRV_DIR)/$(MODULE)
+
+include $(UTSBASE)/intel/Makefile.intel
+
+ALL_TARGET = $(BINARY)
+INSTALL_TARGET = $(BINARY) $(ROOTMODULE)
+CPPFLAGS += -I$(UTSBASE)/intel/io/amdzen
+LDFLAGS += -dy -Ndrv/amdzen
+
+.KEEP_STATE:
+
+def: $(DEF_DEPS)
+
+all: $(ALL_DEPS)
+
+clean: $(CLEAN_DEPS)
+
+clobber: $(CLOBBER_DEPS)
+
+install: $(INSTALL_DEPS)
+
+include $(UTSBASE)/intel/Makefile.targ