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 2022 Oxide Computer Company 14 */ 15 16 /* 17 * A companion to zen_udf(4D) 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 df_reg_def_t def; 79 80 if (cmd != ZEN_UDF_READ32 && cmd != ZEN_UDF_READ64) { 81 return (ENOTTY); 82 } 83 84 dfno = getminor(dev); 85 if (dfno >= zen_udf->zudf_ndfs) { 86 return (ENXIO); 87 } 88 89 if (crgetzoneid(credp) != GLOBAL_ZONEID || 90 secpolicy_hwmanip(credp) != 0) { 91 return (EPERM); 92 } 93 94 if (ddi_copyin((void *)arg, &zui, sizeof (zui), mode & FKIOCTL) != 0) { 95 return (EFAULT); 96 } 97 98 /* 99 * Cons up a register definition based on the user request. We set the 100 * gen to our current one. 101 */ 102 def.drd_gens = amdzen_c_df_rev(); 103 def.drd_func = zui.zui_func; 104 def.drd_reg = zui.zui_reg; 105 106 if (cmd == ZEN_UDF_READ32) { 107 int ret; 108 uint32_t data; 109 110 ret = amdzen_c_df_read32(dfno, zui.zui_inst, def, &data); 111 if (ret != 0) { 112 return (ret); 113 } 114 115 zui.zui_data = data; 116 } else { 117 int ret; 118 119 ret = amdzen_c_df_read64(dfno, zui.zui_inst, def, 120 &zui.zui_data); 121 if (ret != 0) { 122 return (ret); 123 } 124 } 125 126 if (ddi_copyout(&zui, (void *)arg, sizeof (zui), mode & FKIOCTL) != 0) { 127 return (EFAULT); 128 } 129 130 return (0); 131 } 132 133 static int 134 zen_udf_close(dev_t dev, int flag, int otyp, cred_t *credp) 135 { 136 return (0); 137 } 138 139 static void 140 zen_udf_cleanup(zen_udf_t *zen_udf) 141 { 142 ddi_remove_minor_node(zen_udf->zudf_dip, NULL); 143 zen_udf->zudf_ndfs = 0; 144 zen_udf->zudf_dip = NULL; 145 } 146 147 static int 148 zen_udf_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 149 { 150 zen_udf_t *zen_udf = &zen_udf_data; 151 152 if (cmd == DDI_RESUME) { 153 return (DDI_SUCCESS); 154 } else if (cmd != DDI_ATTACH) { 155 return (DDI_FAILURE); 156 } 157 158 if (zen_udf->zudf_dip != NULL) { 159 dev_err(dip, CE_WARN, "!zen_udf is already attached to a " 160 "dev_info_t: %p", zen_udf->zudf_dip); 161 return (DDI_FAILURE); 162 } 163 164 zen_udf->zudf_dip = dip; 165 zen_udf->zudf_ndfs = amdzen_c_df_count(); 166 for (uint_t i = 0; i < zen_udf->zudf_ndfs; i++) { 167 char buf[32]; 168 169 (void) snprintf(buf, sizeof (buf), "zen_udf.%u", i); 170 if (ddi_create_minor_node(dip, buf, S_IFCHR, i, DDI_PSEUDO, 171 0) != DDI_SUCCESS) { 172 dev_err(dip, CE_WARN, "!failed to create minor %s", 173 buf); 174 goto err; 175 } 176 } 177 178 return (DDI_SUCCESS); 179 180 err: 181 zen_udf_cleanup(zen_udf); 182 return (DDI_FAILURE); 183 } 184 185 static int 186 zen_udf_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) 187 { 188 zen_udf_t *zen_udf = &zen_udf_data; 189 minor_t m; 190 191 switch (cmd) { 192 case DDI_INFO_DEVT2DEVINFO: 193 m = getminor((dev_t)arg); 194 if (m >= zen_udf->zudf_ndfs) { 195 return (DDI_FAILURE); 196 } 197 *resultp = (void *)zen_udf->zudf_dip; 198 break; 199 case DDI_INFO_DEVT2INSTANCE: 200 m = getminor((dev_t)arg); 201 if (m >= zen_udf->zudf_ndfs) { 202 return (DDI_FAILURE); 203 } 204 *resultp = (void *)(uintptr_t)ddi_get_instance( 205 zen_udf->zudf_dip); 206 break; 207 default: 208 return (DDI_FAILURE); 209 } 210 return (DDI_SUCCESS); 211 } 212 213 static int 214 zen_udf_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 215 { 216 zen_udf_t *zen_udf = &zen_udf_data; 217 218 if (cmd == DDI_SUSPEND) { 219 return (DDI_SUCCESS); 220 } else if (cmd != DDI_DETACH) { 221 return (DDI_FAILURE); 222 } 223 224 if (zen_udf->zudf_dip != dip) { 225 dev_err(dip, CE_WARN, "!asked to detach zen_udf, but dip " 226 "doesn't match"); 227 return (DDI_FAILURE); 228 } 229 230 zen_udf_cleanup(zen_udf); 231 return (DDI_SUCCESS); 232 } 233 234 static struct cb_ops zen_udf_cb_ops = { 235 .cb_open = zen_udf_open, 236 .cb_close = zen_udf_close, 237 .cb_strategy = nodev, 238 .cb_print = nodev, 239 .cb_dump = nodev, 240 .cb_read = nodev, 241 .cb_write = nodev, 242 .cb_ioctl = zen_udf_ioctl, 243 .cb_devmap = nodev, 244 .cb_mmap = nodev, 245 .cb_segmap = nodev, 246 .cb_chpoll = nochpoll, 247 .cb_prop_op = ddi_prop_op, 248 .cb_flag = D_MP, 249 .cb_rev = CB_REV, 250 .cb_aread = nodev, 251 .cb_awrite = nodev 252 }; 253 254 static struct dev_ops zen_udf_dev_ops = { 255 .devo_rev = DEVO_REV, 256 .devo_refcnt = 0, 257 .devo_getinfo = zen_udf_getinfo, 258 .devo_identify = nulldev, 259 .devo_probe = nulldev, 260 .devo_attach = zen_udf_attach, 261 .devo_detach = zen_udf_detach, 262 .devo_reset = nodev, 263 .devo_quiesce = ddi_quiesce_not_needed, 264 .devo_cb_ops = &zen_udf_cb_ops 265 }; 266 267 static struct modldrv zen_udf_modldrv = { 268 .drv_modops = &mod_driverops, 269 .drv_linkinfo = "AMD User DF Access", 270 .drv_dev_ops = &zen_udf_dev_ops 271 }; 272 273 static struct modlinkage zen_udf_modlinkage = { 274 .ml_rev = MODREV_1, 275 .ml_linkage = { &zen_udf_modldrv, NULL } 276 }; 277 278 int 279 _init(void) 280 { 281 return (mod_install(&zen_udf_modlinkage)); 282 } 283 284 int 285 _info(struct modinfo *modinfop) 286 { 287 return (mod_info(&zen_udf_modlinkage, modinfop)); 288 } 289 290 int 291 _fini(void) 292 { 293 return (mod_remove(&zen_udf_modlinkage)); 294 } 295