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(__amd64) && !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 }