diff options
author | Robert Mustacchi <rm@fingolfin.org> | 2020-10-07 12:00:39 -0700 |
---|---|---|
committer | Robert Mustacchi <rm@fingolfin.org> | 2020-10-21 14:05:11 -0700 |
commit | 549e0fd315406a4a97f9043f44860eed39a715da (patch) | |
tree | 7e8177502e3db10815f9c9d7eff2102000a2eb8e /usr/src/uts/intel/io | |
parent | becd642c1e97d1674cef9e3dccb159c20c6992ae (diff) | |
download | illumos-joyent-549e0fd315406a4a97f9043f44860eed39a715da.tar.gz |
13213 Want development driver for accessing AMD DF
Reviewed by: Patrick Mooney <pmooney@pfmooney.com>
Approved by: Richard Lowe <richlowe@richlowe.net>
Diffstat (limited to 'usr/src/uts/intel/io')
-rw-r--r-- | usr/src/uts/intel/io/amdzen/amdzen.c | 70 | ||||
-rw-r--r-- | usr/src/uts/intel/io/amdzen/amdzen.h | 3 | ||||
-rw-r--r-- | usr/src/uts/intel/io/amdzen/amdzen_client.h | 4 | ||||
-rw-r--r-- | usr/src/uts/intel/io/amdzen/zen_udf.c | 286 | ||||
-rw-r--r-- | usr/src/uts/intel/io/amdzen/zen_udf.h | 44 |
5 files changed, 402 insertions, 5 deletions
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 */ |