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