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 }