1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #include <sys/types.h> 28 #include <sys/file.h> 29 #include <sys/errno.h> 30 #include <sys/open.h> 31 #include <sys/stat.h> 32 #include <sys/cred.h> 33 #include <sys/modctl.h> 34 #include <sys/conf.h> 35 #include <sys/devops.h> 36 #include <sys/ddi.h> 37 #include <sys/x86_archext.h> 38 39 #include <sys/amd_iommu.h> 40 #include "amd_iommu_impl.h" 41 #include "amd_iommu_acpi.h" 42 43 44 #define AMD_IOMMU_MINOR2INST(x) (x) 45 #define AMD_IOMMU_INST2MINOR(x) (x) 46 #define AMD_IOMMU_NODETYPE "ddi_iommu" 47 #define AMD_IOMMU_MINOR_NAME "amd-iommu" 48 49 static int amd_iommu_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, 50 void **result); 51 static int amd_iommu_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); 52 static int amd_iommu_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); 53 static int amd_iommu_open(dev_t *devp, int flag, int otyp, cred_t *credp); 54 static int amd_iommu_close(dev_t dev, int flag, int otyp, cred_t *credp); 55 static int amd_iommu_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, 56 cred_t *credp, int *rvalp); 57 58 static struct cb_ops amd_iommu_cb_ops = { 59 amd_iommu_open, /* cb_open */ 60 amd_iommu_close, /* cb_close */ 61 nodev, /* cb_strategy */ 62 nodev, /* cb_print */ 63 nodev, /* cb_dump */ 64 nodev, /* cb_read */ 65 nodev, /* cb_write */ 66 amd_iommu_ioctl, /* cb_ioctl */ 67 nodev, /* cb_devmap */ 68 nodev, /* cb_mmap */ 69 nodev, /* cb_segmap */ 70 nochpoll, /* cb_chpoll */ 71 ddi_prop_op, /* cb_prop_op */ 72 NULL, /* cb_str */ 73 D_NEW | D_MP, /* cb_flag */ 74 CB_REV, /* cb_rev */ 75 nodev, /* cb_aread */ 76 nodev /* cb_awrite */ 77 }; 78 79 static struct dev_ops amd_iommu_dev_ops = { 80 DEVO_REV, /* devo_rev */ 81 0, /* devo_refcnt */ 82 amd_iommu_getinfo, /* devo_getinfo */ 83 nulldev, /* devo_identify */ 84 nulldev, /* devo_probe */ 85 amd_iommu_attach, /* devo_attach */ 86 amd_iommu_detach, /* devo_detach */ 87 nodev, /* devo_reset */ 88 &amd_iommu_cb_ops, /* devo_cb_ops */ 89 NULL, /* devo_bus_ops */ 90 nulldev /* devo_power */ 91 }; 92 93 static struct modldrv modldrv = { 94 &mod_driverops, 95 "AMD IOMMU 0.1", 96 &amd_iommu_dev_ops 97 }; 98 99 static struct modlinkage modlinkage = { 100 MODREV_1, 101 (void *)&modldrv, 102 NULL 103 }; 104 105 amd_iommu_debug_t amd_iommu_debug; 106 kmutex_t amd_iommu_global_lock; 107 const char *amd_iommu_modname = "amd_iommu"; 108 amd_iommu_alias_t **amd_iommu_alias; 109 amd_iommu_page_table_hash_t amd_iommu_page_table_hash; 110 static void *amd_iommu_statep; 111 int amd_iommu_64bit_bug; 112 int amd_iommu_unity_map; 113 int amd_iommu_no_RW_perms; 114 int amd_iommu_no_unmap; 115 int amd_iommu_pageva_inval_all; 116 int amd_iommu_disable; /* disable IOMMU */ 117 char *amd_iommu_disable_list; /* list of drivers bypassing IOMMU */ 118 119 int 120 _init(void) 121 { 122 int error = ENOTSUP; 123 124 #if defined(__amd64) && !defined(__xpv) 125 126 if (get_hwenv() != HW_NATIVE) 127 return (ENOTSUP); 128 129 error = ddi_soft_state_init(&amd_iommu_statep, 130 sizeof (struct amd_iommu_state), 1); 131 if (error) { 132 cmn_err(CE_WARN, "%s: _init: failed to init soft state.", 133 amd_iommu_modname); 134 return (error); 135 } 136 137 if (amd_iommu_acpi_init() != DDI_SUCCESS) { 138 if (amd_iommu_debug) { 139 cmn_err(CE_WARN, "%s: _init: ACPI init failed.", 140 amd_iommu_modname); 141 } 142 ddi_soft_state_fini(&amd_iommu_statep); 143 return (ENOTSUP); 144 } 145 146 amd_iommu_read_boot_props(); 147 148 if (amd_iommu_page_table_hash_init(&amd_iommu_page_table_hash) 149 != DDI_SUCCESS) { 150 cmn_err(CE_WARN, "%s: _init: Page table hash init failed.", 151 amd_iommu_modname); 152 if (amd_iommu_disable_list) { 153 kmem_free(amd_iommu_disable_list, 154 strlen(amd_iommu_disable_list) + 1); 155 amd_iommu_disable_list = NULL; 156 } 157 amd_iommu_acpi_fini(); 158 ddi_soft_state_fini(&amd_iommu_statep); 159 amd_iommu_statep = NULL; 160 return (EFAULT); 161 } 162 163 error = mod_install(&modlinkage); 164 if (error) { 165 cmn_err(CE_WARN, "%s: _init: mod_install failed.", 166 amd_iommu_modname); 167 amd_iommu_page_table_hash_fini(&amd_iommu_page_table_hash); 168 if (amd_iommu_disable_list) { 169 kmem_free(amd_iommu_disable_list, 170 strlen(amd_iommu_disable_list) + 1); 171 amd_iommu_disable_list = NULL; 172 } 173 amd_iommu_acpi_fini(); 174 ddi_soft_state_fini(&amd_iommu_statep); 175 amd_iommu_statep = NULL; 176 return (error); 177 } 178 error = 0; 179 #endif 180 181 return (error); 182 } 183 184 int 185 _info(struct modinfo *modinfop) 186 { 187 return (mod_info(&modlinkage, modinfop)); 188 } 189 190 int 191 _fini(void) 192 { 193 int error; 194 195 error = mod_remove(&modlinkage); 196 if (error) 197 return (error); 198 199 amd_iommu_page_table_hash_fini(&amd_iommu_page_table_hash); 200 if (amd_iommu_disable_list) { 201 kmem_free(amd_iommu_disable_list, 202 strlen(amd_iommu_disable_list) + 1); 203 amd_iommu_disable_list = NULL; 204 } 205 amd_iommu_acpi_fini(); 206 ddi_soft_state_fini(&amd_iommu_statep); 207 amd_iommu_statep = NULL; 208 209 return (0); 210 } 211 212 /*ARGSUSED*/ 213 static int 214 amd_iommu_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 215 { 216 struct amd_iommu_state *statep; 217 218 ASSERT(result); 219 220 *result = NULL; 221 222 switch (cmd) { 223 case DDI_INFO_DEVT2DEVINFO: 224 statep = ddi_get_soft_state(amd_iommu_statep, 225 AMD_IOMMU_MINOR2INST(getminor((dev_t)arg))); 226 if (statep) { 227 *result = statep->aioms_devi; 228 return (DDI_SUCCESS); 229 } 230 break; 231 case DDI_INFO_DEVT2INSTANCE: 232 *result = (void *)(uintptr_t) 233 AMD_IOMMU_MINOR2INST(getminor((dev_t)arg)); 234 return (DDI_SUCCESS); 235 } 236 237 return (DDI_FAILURE); 238 } 239 240 static int 241 amd_iommu_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 242 { 243 int instance = ddi_get_instance(dip); 244 const char *driver = ddi_driver_name(dip); 245 struct amd_iommu_state *statep; 246 247 ASSERT(instance >= 0); 248 ASSERT(driver); 249 250 switch (cmd) { 251 case DDI_ATTACH: 252 if (ddi_soft_state_zalloc(amd_iommu_statep, instance) 253 != DDI_SUCCESS) { 254 cmn_err(CE_WARN, "Unable to allocate soft state for " 255 "%s%d", driver, instance); 256 return (DDI_FAILURE); 257 } 258 259 statep = ddi_get_soft_state(amd_iommu_statep, instance); 260 if (statep == NULL) { 261 cmn_err(CE_WARN, "Unable to get soft state for " 262 "%s%d", driver, instance); 263 ddi_soft_state_free(amd_iommu_statep, instance); 264 return (DDI_FAILURE); 265 } 266 267 if (ddi_create_minor_node(dip, AMD_IOMMU_MINOR_NAME, S_IFCHR, 268 AMD_IOMMU_INST2MINOR(instance), AMD_IOMMU_NODETYPE, 269 0) != DDI_SUCCESS) { 270 cmn_err(CE_WARN, "Unable to create minor node for " 271 "%s%d", driver, instance); 272 ddi_remove_minor_node(dip, NULL); 273 ddi_soft_state_free(amd_iommu_statep, instance); 274 return (DDI_FAILURE); 275 } 276 277 statep->aioms_devi = dip; 278 statep->aioms_instance = instance; 279 statep->aioms_iommu_start = NULL; 280 statep->aioms_iommu_end = NULL; 281 282 amd_iommu_lookup_conf_props(dip); 283 284 if (amd_iommu_disable_list) { 285 cmn_err(CE_NOTE, "AMD IOMMU disabled for the following" 286 " drivers:\n%s", amd_iommu_disable_list); 287 } 288 289 if (amd_iommu_disable) { 290 cmn_err(CE_NOTE, "AMD IOMMU disabled by user"); 291 } else if (amd_iommu_setup(dip, statep) != DDI_SUCCESS) { 292 cmn_err(CE_WARN, "Unable to initialize AMD IOMMU " 293 "%s%d", driver, instance); 294 ddi_remove_minor_node(dip, NULL); 295 ddi_soft_state_free(amd_iommu_statep, instance); 296 return (DDI_FAILURE); 297 } 298 299 ddi_report_dev(dip); 300 301 return (DDI_SUCCESS); 302 303 case DDI_RESUME: 304 return (DDI_SUCCESS); 305 default: 306 return (DDI_FAILURE); 307 } 308 } 309 310 static int 311 amd_iommu_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 312 { 313 int instance = ddi_get_instance(dip); 314 const char *driver = ddi_driver_name(dip); 315 struct amd_iommu_state *statep; 316 317 ASSERT(instance >= 0); 318 ASSERT(driver); 319 320 switch (cmd) { 321 case DDI_DETACH: 322 statep = ddi_get_soft_state(amd_iommu_statep, instance); 323 if (statep == NULL) { 324 cmn_err(CE_WARN, "%s%d: Cannot get soft state", 325 driver, instance); 326 return (DDI_FAILURE); 327 } 328 return (DDI_FAILURE); 329 case DDI_SUSPEND: 330 return (DDI_SUCCESS); 331 default: 332 return (DDI_FAILURE); 333 } 334 } 335 336 /*ARGSUSED*/ 337 static int 338 amd_iommu_open(dev_t *devp, int flag, int otyp, cred_t *credp) 339 { 340 int instance = AMD_IOMMU_MINOR2INST(getminor(*devp)); 341 struct amd_iommu_state *statep; 342 const char *f = "amd_iommu_open"; 343 344 if (instance < 0) { 345 cmn_err(CE_WARN, "%s: invalid instance %d", 346 f, instance); 347 return (ENXIO); 348 } 349 350 if (!(flag & (FREAD|FWRITE))) { 351 cmn_err(CE_WARN, "%s: invalid flags %d", f, flag); 352 return (EINVAL); 353 } 354 355 if (otyp != OTYP_CHR) { 356 cmn_err(CE_WARN, "%s: invalid otyp %d", f, otyp); 357 return (EINVAL); 358 } 359 360 statep = ddi_get_soft_state(amd_iommu_statep, instance); 361 if (statep == NULL) { 362 cmn_err(CE_WARN, "%s: cannot get soft state: instance %d", 363 f, instance); 364 return (ENXIO); 365 } 366 367 ASSERT(statep->aioms_instance == instance); 368 369 return (0); 370 } 371 372 /*ARGSUSED*/ 373 static int 374 amd_iommu_close(dev_t dev, int flag, int otyp, cred_t *credp) 375 { 376 int instance = AMD_IOMMU_MINOR2INST(getminor(dev)); 377 struct amd_iommu_state *statep; 378 const char *f = "amd_iommu_close"; 379 380 if (instance < 0) { 381 cmn_err(CE_WARN, "%s: invalid instance %d", f, instance); 382 return (ENXIO); 383 } 384 385 if (!(flag & (FREAD|FWRITE))) { 386 cmn_err(CE_WARN, "%s: invalid flags %d", f, flag); 387 return (EINVAL); 388 } 389 390 if (otyp != OTYP_CHR) { 391 cmn_err(CE_WARN, "%s: invalid otyp %d", f, otyp); 392 return (EINVAL); 393 } 394 395 statep = ddi_get_soft_state(amd_iommu_statep, instance); 396 if (statep == NULL) { 397 cmn_err(CE_WARN, "%s: cannot get soft state: instance %d", 398 f, instance); 399 return (ENXIO); 400 } 401 402 ASSERT(statep->aioms_instance == instance); 403 return (0); 404 405 } 406 407 /*ARGSUSED*/ 408 static int 409 amd_iommu_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 410 int *rvalp) 411 { 412 int instance = AMD_IOMMU_MINOR2INST(getminor(dev)); 413 struct amd_iommu_state *statep; 414 const char *f = "amd_iommu_ioctl"; 415 416 ASSERT(*rvalp); 417 418 if (instance < 0) { 419 cmn_err(CE_WARN, "%s: invalid instance %d", f, instance); 420 return (ENXIO); 421 } 422 423 424 if (!(mode & (FREAD|FWRITE))) { 425 cmn_err(CE_WARN, "%s: invalid mode %d", f, mode); 426 return (EINVAL); 427 } 428 429 if (mode & FKIOCTL) { 430 cmn_err(CE_WARN, "%s: FKIOCTL unsupported mode %d", f, mode); 431 return (EINVAL); 432 } 433 434 statep = ddi_get_soft_state(amd_iommu_statep, instance); 435 if (statep == NULL) { 436 cmn_err(CE_WARN, "%s: cannot get soft state: instance %d", 437 f, instance); 438 return (ENXIO); 439 } 440 441 ASSERT(statep->aioms_instance == instance); 442 443 return (ENOTTY); 444 } 445