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 static int amd_iommu_quiesce(dev_info_t *dip); 58 59 static struct cb_ops amd_iommu_cb_ops = { 60 amd_iommu_open, /* cb_open */ 61 amd_iommu_close, /* cb_close */ 62 nodev, /* cb_strategy */ 63 nodev, /* cb_print */ 64 nodev, /* cb_dump */ 65 nodev, /* cb_read */ 66 nodev, /* cb_write */ 67 amd_iommu_ioctl, /* cb_ioctl */ 68 nodev, /* cb_devmap */ 69 nodev, /* cb_mmap */ 70 nodev, /* cb_segmap */ 71 nochpoll, /* cb_chpoll */ 72 ddi_prop_op, /* cb_prop_op */ 73 NULL, /* cb_str */ 74 D_NEW | D_MP, /* cb_flag */ 75 CB_REV, /* cb_rev */ 76 nodev, /* cb_aread */ 77 nodev /* cb_awrite */ 78 }; 79 80 static struct dev_ops amd_iommu_dev_ops = { 81 DEVO_REV, /* devo_rev */ 82 0, /* devo_refcnt */ 83 amd_iommu_getinfo, /* devo_getinfo */ 84 nulldev, /* devo_identify */ 85 nulldev, /* devo_probe */ 86 amd_iommu_attach, /* devo_attach */ 87 amd_iommu_detach, /* devo_detach */ 88 nodev, /* devo_reset */ 89 &amd_iommu_cb_ops, /* devo_cb_ops */ 90 NULL, /* devo_bus_ops */ 91 nulldev, /* devo_power */ 92 amd_iommu_quiesce, /* devo_quiesce */ 93 }; 94 95 static struct modldrv modldrv = { 96 &mod_driverops, 97 "AMD IOMMU 0.1", 98 &amd_iommu_dev_ops 99 }; 100 101 static struct modlinkage modlinkage = { 102 MODREV_1, 103 { (void *)&modldrv, NULL } 104 }; 105 106 amd_iommu_debug_t amd_iommu_debug; 107 kmutex_t amd_iommu_global_lock; 108 const char *amd_iommu_modname = "amd_iommu"; 109 amd_iommu_alias_t **amd_iommu_alias; 110 amd_iommu_page_table_hash_t amd_iommu_page_table_hash; 111 static void *amd_iommu_statep; 112 int amd_iommu_64bit_bug; 113 int amd_iommu_unity_map; 114 int amd_iommu_no_RW_perms; 115 int amd_iommu_no_unmap; 116 int amd_iommu_pageva_inval_all; 117 int amd_iommu_disable; /* disable IOMMU */ 118 char *amd_iommu_disable_list; /* list of drivers bypassing IOMMU */ 119 120 int 121 _init(void) 122 { 123 int error = ENOTSUP; 124 125 #if defined(__amd64) && !defined(__xpv) 126 127 if (get_hwenv() != HW_NATIVE) 128 return (ENOTSUP); 129 130 error = ddi_soft_state_init(&amd_iommu_statep, 131 sizeof (struct amd_iommu_state), 1); 132 if (error) { 133 cmn_err(CE_WARN, "%s: _init: failed to init soft state.", 134 amd_iommu_modname); 135 return (error); 136 } 137 138 if (amd_iommu_acpi_init() != DDI_SUCCESS) { 139 if (amd_iommu_debug) { 140 cmn_err(CE_WARN, "%s: _init: ACPI init failed.", 141 amd_iommu_modname); 142 } 143 ddi_soft_state_fini(&amd_iommu_statep); 144 return (ENOTSUP); 145 } 146 147 amd_iommu_read_boot_props(); 148 149 if (amd_iommu_page_table_hash_init(&amd_iommu_page_table_hash) 150 != DDI_SUCCESS) { 151 cmn_err(CE_WARN, "%s: _init: Page table hash init failed.", 152 amd_iommu_modname); 153 if (amd_iommu_disable_list) { 154 kmem_free(amd_iommu_disable_list, 155 strlen(amd_iommu_disable_list) + 1); 156 amd_iommu_disable_list = NULL; 157 } 158 amd_iommu_acpi_fini(); 159 ddi_soft_state_fini(&amd_iommu_statep); 160 amd_iommu_statep = NULL; 161 return (EFAULT); 162 } 163 164 error = mod_install(&modlinkage); 165 if (error) { 166 cmn_err(CE_WARN, "%s: _init: mod_install failed.", 167 amd_iommu_modname); 168 amd_iommu_page_table_hash_fini(&amd_iommu_page_table_hash); 169 if (amd_iommu_disable_list) { 170 kmem_free(amd_iommu_disable_list, 171 strlen(amd_iommu_disable_list) + 1); 172 amd_iommu_disable_list = NULL; 173 } 174 amd_iommu_acpi_fini(); 175 ddi_soft_state_fini(&amd_iommu_statep); 176 amd_iommu_statep = NULL; 177 return (error); 178 } 179 error = 0; 180 #endif 181 182 return (error); 183 } 184 185 int 186 _info(struct modinfo *modinfop) 187 { 188 return (mod_info(&modlinkage, modinfop)); 189 } 190 191 int 192 _fini(void) 193 { 194 int error; 195 196 error = mod_remove(&modlinkage); 197 if (error) 198 return (error); 199 200 amd_iommu_page_table_hash_fini(&amd_iommu_page_table_hash); 201 if (amd_iommu_disable_list) { 202 kmem_free(amd_iommu_disable_list, 203 strlen(amd_iommu_disable_list) + 1); 204 amd_iommu_disable_list = NULL; 205 } 206 amd_iommu_acpi_fini(); 207 ddi_soft_state_fini(&amd_iommu_statep); 208 amd_iommu_statep = NULL; 209 210 return (0); 211 } 212 213 /*ARGSUSED*/ 214 static int 215 amd_iommu_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 216 { 217 struct amd_iommu_state *statep; 218 219 ASSERT(result); 220 221 *result = NULL; 222 223 switch (cmd) { 224 case DDI_INFO_DEVT2DEVINFO: 225 statep = ddi_get_soft_state(amd_iommu_statep, 226 AMD_IOMMU_MINOR2INST(getminor((dev_t)arg))); 227 if (statep) { 228 *result = statep->aioms_devi; 229 return (DDI_SUCCESS); 230 } 231 break; 232 case DDI_INFO_DEVT2INSTANCE: 233 *result = (void *)(uintptr_t) 234 AMD_IOMMU_MINOR2INST(getminor((dev_t)arg)); 235 return (DDI_SUCCESS); 236 } 237 238 return (DDI_FAILURE); 239 } 240 241 static int 242 amd_iommu_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 243 { 244 int instance = ddi_get_instance(dip); 245 const char *driver = ddi_driver_name(dip); 246 struct amd_iommu_state *statep; 247 248 ASSERT(instance >= 0); 249 ASSERT(driver); 250 251 switch (cmd) { 252 case DDI_ATTACH: 253 if (ddi_soft_state_zalloc(amd_iommu_statep, instance) 254 != DDI_SUCCESS) { 255 cmn_err(CE_WARN, "Unable to allocate soft state for " 256 "%s%d", driver, instance); 257 return (DDI_FAILURE); 258 } 259 260 statep = ddi_get_soft_state(amd_iommu_statep, instance); 261 if (statep == NULL) { 262 cmn_err(CE_WARN, "Unable to get soft state for " 263 "%s%d", driver, instance); 264 ddi_soft_state_free(amd_iommu_statep, instance); 265 return (DDI_FAILURE); 266 } 267 268 if (ddi_create_minor_node(dip, AMD_IOMMU_MINOR_NAME, S_IFCHR, 269 AMD_IOMMU_INST2MINOR(instance), AMD_IOMMU_NODETYPE, 270 0) != DDI_SUCCESS) { 271 cmn_err(CE_WARN, "Unable to create minor node for " 272 "%s%d", driver, instance); 273 ddi_remove_minor_node(dip, NULL); 274 ddi_soft_state_free(amd_iommu_statep, instance); 275 return (DDI_FAILURE); 276 } 277 278 statep->aioms_devi = dip; 279 statep->aioms_instance = instance; 280 statep->aioms_iommu_start = NULL; 281 statep->aioms_iommu_end = NULL; 282 283 amd_iommu_lookup_conf_props(dip); 284 285 if (amd_iommu_disable_list) { 286 cmn_err(CE_NOTE, "AMD IOMMU disabled for the following" 287 " drivers:\n%s", amd_iommu_disable_list); 288 } 289 290 if (amd_iommu_disable) { 291 cmn_err(CE_NOTE, "AMD IOMMU disabled by user"); 292 } else if (amd_iommu_setup(dip, statep) != DDI_SUCCESS) { 293 cmn_err(CE_WARN, "Unable to initialize AMD IOMMU " 294 "%s%d", driver, instance); 295 ddi_remove_minor_node(dip, NULL); 296 ddi_soft_state_free(amd_iommu_statep, instance); 297 return (DDI_FAILURE); 298 } 299 300 ddi_report_dev(dip); 301 302 return (DDI_SUCCESS); 303 304 case DDI_RESUME: 305 return (DDI_SUCCESS); 306 default: 307 return (DDI_FAILURE); 308 } 309 } 310 311 static int 312 amd_iommu_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 313 { 314 int instance = ddi_get_instance(dip); 315 const char *driver = ddi_driver_name(dip); 316 struct amd_iommu_state *statep; 317 318 ASSERT(instance >= 0); 319 ASSERT(driver); 320 321 switch (cmd) { 322 case DDI_DETACH: 323 statep = ddi_get_soft_state(amd_iommu_statep, instance); 324 if (statep == NULL) { 325 cmn_err(CE_WARN, "%s%d: Cannot get soft state", 326 driver, instance); 327 return (DDI_FAILURE); 328 } 329 return (DDI_FAILURE); 330 case DDI_SUSPEND: 331 return (DDI_SUCCESS); 332 default: 333 return (DDI_FAILURE); 334 } 335 } 336 337 /*ARGSUSED*/ 338 static int 339 amd_iommu_open(dev_t *devp, int flag, int otyp, cred_t *credp) 340 { 341 int instance = AMD_IOMMU_MINOR2INST(getminor(*devp)); 342 struct amd_iommu_state *statep; 343 const char *f = "amd_iommu_open"; 344 345 if (instance < 0) { 346 cmn_err(CE_WARN, "%s: invalid instance %d", 347 f, instance); 348 return (ENXIO); 349 } 350 351 if (!(flag & (FREAD|FWRITE))) { 352 cmn_err(CE_WARN, "%s: invalid flags %d", f, flag); 353 return (EINVAL); 354 } 355 356 if (otyp != OTYP_CHR) { 357 cmn_err(CE_WARN, "%s: invalid otyp %d", f, otyp); 358 return (EINVAL); 359 } 360 361 statep = ddi_get_soft_state(amd_iommu_statep, instance); 362 if (statep == NULL) { 363 cmn_err(CE_WARN, "%s: cannot get soft state: instance %d", 364 f, instance); 365 return (ENXIO); 366 } 367 368 ASSERT(statep->aioms_instance == instance); 369 370 return (0); 371 } 372 373 /*ARGSUSED*/ 374 static int 375 amd_iommu_close(dev_t dev, int flag, int otyp, cred_t *credp) 376 { 377 int instance = AMD_IOMMU_MINOR2INST(getminor(dev)); 378 struct amd_iommu_state *statep; 379 const char *f = "amd_iommu_close"; 380 381 if (instance < 0) { 382 cmn_err(CE_WARN, "%s: invalid instance %d", f, instance); 383 return (ENXIO); 384 } 385 386 if (!(flag & (FREAD|FWRITE))) { 387 cmn_err(CE_WARN, "%s: invalid flags %d", f, flag); 388 return (EINVAL); 389 } 390 391 if (otyp != OTYP_CHR) { 392 cmn_err(CE_WARN, "%s: invalid otyp %d", f, otyp); 393 return (EINVAL); 394 } 395 396 statep = ddi_get_soft_state(amd_iommu_statep, instance); 397 if (statep == NULL) { 398 cmn_err(CE_WARN, "%s: cannot get soft state: instance %d", 399 f, instance); 400 return (ENXIO); 401 } 402 403 ASSERT(statep->aioms_instance == instance); 404 return (0); 405 406 } 407 408 /*ARGSUSED*/ 409 static int 410 amd_iommu_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, 411 int *rvalp) 412 { 413 int instance = AMD_IOMMU_MINOR2INST(getminor(dev)); 414 struct amd_iommu_state *statep; 415 const char *f = "amd_iommu_ioctl"; 416 417 ASSERT(*rvalp); 418 419 if (instance < 0) { 420 cmn_err(CE_WARN, "%s: invalid instance %d", f, instance); 421 return (ENXIO); 422 } 423 424 425 if (!(mode & (FREAD|FWRITE))) { 426 cmn_err(CE_WARN, "%s: invalid mode %d", f, mode); 427 return (EINVAL); 428 } 429 430 if (mode & FKIOCTL) { 431 cmn_err(CE_WARN, "%s: FKIOCTL unsupported mode %d", f, mode); 432 return (EINVAL); 433 } 434 435 statep = ddi_get_soft_state(amd_iommu_statep, instance); 436 if (statep == NULL) { 437 cmn_err(CE_WARN, "%s: cannot get soft state: instance %d", 438 f, instance); 439 return (ENXIO); 440 } 441 442 ASSERT(statep->aioms_instance == instance); 443 444 return (ENOTTY); 445 } 446 447 static int 448 amd_iommu_quiesce(dev_info_t *dip) 449 { 450 int instance = ddi_get_instance(dip); 451 struct amd_iommu_state *statep; 452 const char *f = "amd_iommu_quiesce"; 453 454 statep = ddi_get_soft_state(amd_iommu_statep, instance); 455 if (statep == NULL) { 456 cmn_err(CE_WARN, "%s: cannot get soft state: instance %d", 457 f, instance); 458 return (DDI_FAILURE); 459 } 460 461 if (amd_iommu_teardown(dip, statep, AMD_IOMMU_QUIESCE) != DDI_SUCCESS) { 462 cmn_err(CE_WARN, "%s: Unable to quiesce AMD IOMMU " 463 "%s%d", f, ddi_driver_name(dip), instance); 464 return (DDI_FAILURE); 465 } 466 467 return (DDI_SUCCESS); 468 }