1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2020 Oxide Computer Company 14 */ 15 16 /* 17 * A companion to zen_udf(7D) that allows user access to read the data fabric 18 * for development purposes. 19 */ 20 21 #include <sys/types.h> 22 #include <sys/file.h> 23 #include <sys/errno.h> 24 #include <sys/open.h> 25 #include <sys/cred.h> 26 #include <sys/ddi.h> 27 #include <sys/sunddi.h> 28 #include <sys/stat.h> 29 #include <sys/conf.h> 30 #include <sys/devops.h> 31 #include <sys/cmn_err.h> 32 #include <sys/policy.h> 33 #include <amdzen_client.h> 34 35 #include <zen_udf.h> 36 37 typedef struct zen_udf { 38 dev_info_t *zudf_dip; 39 uint_t zudf_ndfs; 40 } zen_udf_t; 41 42 static zen_udf_t zen_udf_data; 43 44 static int 45 zen_udf_open(dev_t *devp, int flags, int otype, cred_t *credp) 46 { 47 minor_t m; 48 zen_udf_t *zen_udf = &zen_udf_data; 49 50 if (crgetzoneid(credp) != GLOBAL_ZONEID || 51 secpolicy_hwmanip(credp) != 0) { 52 return (EPERM); 53 } 54 55 if ((flags & (FEXCL | FNDELAY | FNONBLOCK)) != 0) { 56 return (EINVAL); 57 } 58 59 if (otype != OTYP_CHR) { 60 return (EINVAL); 61 } 62 63 m = getminor(*devp); 64 if (m >= zen_udf->zudf_ndfs) { 65 return (ENXIO); 66 } 67 68 return (0); 69 } 70 71 static int 72 zen_udf_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 73 int *rvalp) 74 { 75 uint_t dfno; 76 zen_udf_t *zen_udf = &zen_udf_data; 77 zen_udf_io_t zui; 78 79 if (cmd != ZEN_UDF_READ32 && cmd != ZEN_UDF_READ64) { 80 return (ENOTTY); 81 } 82 83 dfno = getminor(dev); 84 if (dfno >= zen_udf->zudf_ndfs) { 85 return (ENXIO); 86 } 87 88 if (crgetzoneid(credp) != GLOBAL_ZONEID || 89 secpolicy_hwmanip(credp) != 0) { 90 return (EPERM); 91 } 92 93 if (ddi_copyin((void *)arg, &zui, sizeof (zui), mode & FKIOCTL) != 0) { 94 return (EFAULT); 95 } 96 97 if (cmd == ZEN_UDF_READ32) { 98 int ret; 99 uint32_t data; 100 101 ret = amdzen_c_df_read32(dfno, zui.zui_inst, zui.zui_func, 102 zui.zui_reg, &data); 103 if (ret != 0) { 104 return (ret); 105 } 106 107 zui.zui_data = data; 108 } else { 109 int ret; 110 111 ret = amdzen_c_df_read64(dfno, zui.zui_inst, zui.zui_func, 112 zui.zui_reg, &zui.zui_data); 113 if (ret != 0) { 114 return (ret); 115 } 116 } 117 118 if (ddi_copyout(&zui, (void *)arg, sizeof (zui), mode & FKIOCTL) != 0) { 119 return (EFAULT); 120 } 121 122 return (0); 123 } 124 125 static int 126 zen_udf_close(dev_t dev, int flag, int otyp, cred_t *credp) 127 { 128 return (0); 129 } 130 131 static void 132 zen_udf_cleanup(zen_udf_t *zen_udf) 133 { 134 ddi_remove_minor_node(zen_udf->zudf_dip, NULL); 135 zen_udf->zudf_ndfs = 0; 136 zen_udf->zudf_dip = NULL; 137 } 138 139 static int 140 zen_udf_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 141 { 142 zen_udf_t *zen_udf = &zen_udf_data; 143 144 if (cmd == DDI_RESUME) { 145 return (DDI_SUCCESS); 146 } else if (cmd != DDI_ATTACH) { 147 return (DDI_FAILURE); 148 } 149 150 if (zen_udf->zudf_dip != NULL) { 151 dev_err(dip, CE_WARN, "!zen_udf is already attached to a " 152 "dev_info_t: %p", zen_udf->zudf_dip); 153 return (DDI_FAILURE); 154 } 155 156 zen_udf->zudf_dip = dip; 157 zen_udf->zudf_ndfs = amdzen_c_df_count(); 158 for (uint_t i = 0; i < zen_udf->zudf_ndfs; i++) { 159 char buf[32]; 160 161 (void) snprintf(buf, sizeof (buf), "zen_udf.%u", i); 162 if (ddi_create_minor_node(dip, buf, S_IFCHR, i, DDI_PSEUDO, 163 0) != DDI_SUCCESS) { 164 dev_err(dip, CE_WARN, "!failed to create minor %s", 165 buf); 166 goto err; 167 } 168 } 169 170 return (DDI_SUCCESS); 171 172 err: 173 zen_udf_cleanup(zen_udf); 174 return (DDI_FAILURE); 175 } 176 177 static int 178 zen_udf_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) 179 { 180 zen_udf_t *zen_udf = &zen_udf_data; 181 minor_t m; 182 183 switch (cmd) { 184 case DDI_INFO_DEVT2DEVINFO: 185 m = getminor((dev_t)arg); 186 if (m >= zen_udf->zudf_ndfs) { 187 return (DDI_FAILURE); 188 } 189 *resultp = (void *)zen_udf->zudf_dip; 190 break; 191 case DDI_INFO_DEVT2INSTANCE: 192 m = getminor((dev_t)arg); 193 if (m >= zen_udf->zudf_ndfs) { 194 return (DDI_FAILURE); 195 } 196 *resultp = (void *)(uintptr_t)ddi_get_instance( 197 zen_udf->zudf_dip); 198 break; 199 default: 200 return (DDI_FAILURE); 201 } 202 return (DDI_SUCCESS); 203 } 204 205 static int 206 zen_udf_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 207 { 208 zen_udf_t *zen_udf = &zen_udf_data; 209 210 if (cmd == DDI_SUSPEND) { 211 return (DDI_SUCCESS); 212 } else if (cmd != DDI_DETACH) { 213 return (DDI_FAILURE); 214 } 215 216 if (zen_udf->zudf_dip != dip) { 217 dev_err(dip, CE_WARN, "!asked to detach zen_udf, but dip " 218 "doesn't match"); 219 return (DDI_FAILURE); 220 } 221 222 zen_udf_cleanup(zen_udf); 223 return (DDI_SUCCESS); 224 } 225 226 static struct cb_ops zen_udf_cb_ops = { 227 .cb_open = zen_udf_open, 228 .cb_close = zen_udf_close, 229 .cb_strategy = nodev, 230 .cb_print = nodev, 231 .cb_dump = nodev, 232 .cb_read = nodev, 233 .cb_write = nodev, 234 .cb_ioctl = zen_udf_ioctl, 235 .cb_devmap = nodev, 236 .cb_mmap = nodev, 237 .cb_segmap = nodev, 238 .cb_chpoll = nochpoll, 239 .cb_prop_op = ddi_prop_op, 240 .cb_flag = D_MP, 241 .cb_rev = CB_REV, 242 .cb_aread = nodev, 243 .cb_awrite = nodev 244 }; 245 246 static struct dev_ops zen_udf_dev_ops = { 247 .devo_rev = DEVO_REV, 248 .devo_refcnt = 0, 249 .devo_getinfo = zen_udf_getinfo, 250 .devo_identify = nulldev, 251 .devo_probe = nulldev, 252 .devo_attach = zen_udf_attach, 253 .devo_detach = zen_udf_detach, 254 .devo_reset = nodev, 255 .devo_quiesce = ddi_quiesce_not_needed, 256 .devo_cb_ops = &zen_udf_cb_ops 257 }; 258 259 static struct modldrv zen_udf_modldrv = { 260 .drv_modops = &mod_driverops, 261 .drv_linkinfo = "AMD User DF Access", 262 .drv_dev_ops = &zen_udf_dev_ops 263 }; 264 265 static struct modlinkage zen_udf_modlinkage = { 266 .ml_rev = MODREV_1, 267 .ml_linkage = { &zen_udf_modldrv, NULL } 268 }; 269 270 int 271 _init(void) 272 { 273 return (mod_install(&zen_udf_modlinkage)); 274 } 275 276 int 277 _info(struct modinfo *modinfop) 278 { 279 return (mod_info(&zen_udf_modlinkage, modinfop)); 280 } 281 282 int 283 _fini(void) 284 { 285 return (mod_remove(&zen_udf_modlinkage)); 286 } 287