The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/amd64/vmm/amd/amdvi_hw.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 /*-
    2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
    3  *
    4  * Copyright (c) 2016, Anish Gupta (anish@freebsd.org)
    5  * All rights reserved.
    6  *
    7  * Redistribution and use in source and binary forms, with or without
    8  * modification, are permitted provided that the following conditions
    9  * are met:
   10  * 1. Redistributions of source code must retain the above copyright
   11  *    notice unmodified, this list of conditions, and the following
   12  *    disclaimer.
   13  * 2. Redistributions in binary form must reproduce the above copyright
   14  *    notice, this list of conditions and the following disclaimer in the
   15  *    documentation and/or other materials provided with the distribution.
   16  *
   17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
   18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
   20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
   21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
   22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
   23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
   24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
   25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
   26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   27  */
   28 
   29 #include <sys/cdefs.h>
   30 __FBSDID("$FreeBSD$");
   31 
   32 #include <sys/param.h>
   33 #include <sys/systm.h>
   34 #include <sys/bus.h>
   35 #include <sys/kernel.h>
   36 #include <sys/module.h>
   37 #include <sys/malloc.h>
   38 #include <sys/pcpu.h>
   39 #include <sys/rman.h>
   40 #include <sys/smp.h>
   41 #include <sys/sysctl.h>
   42 
   43 #include <vm/vm.h>
   44 #include <vm/pmap.h>
   45 
   46 #include <dev/pci/pcivar.h>
   47 #include <dev/pci/pcireg.h>
   48 
   49 #include <machine/resource.h>
   50 #include <machine/vmm.h>
   51 #include <machine/pmap.h>
   52 #include <machine/vmparam.h>
   53 #include <machine/pci_cfgreg.h>
   54 
   55 #include "ivhd_if.h"
   56 #include "pcib_if.h"
   57 
   58 #include "io/iommu.h"
   59 #include "amdvi_priv.h"
   60 
   61 SYSCTL_DECL(_hw_vmm);
   62 SYSCTL_NODE(_hw_vmm, OID_AUTO, amdvi, CTLFLAG_RW | CTLFLAG_MPSAFE, NULL,
   63     NULL);
   64 
   65 #define MOD_INC(a, s, m) (((a) + (s)) % ((m) * (s)))
   66 #define MOD_DEC(a, s, m) (((a) - (s)) % ((m) * (s)))
   67 
   68 /* Print RID or device ID in PCI string format. */
   69 #define RID2PCI_STR(d) PCI_RID2BUS(d), PCI_RID2SLOT(d), PCI_RID2FUNC(d)
   70 
   71 static void amdvi_dump_cmds(struct amdvi_softc *softc, int count);
   72 static void amdvi_print_dev_cap(struct amdvi_softc *softc);
   73 
   74 MALLOC_DEFINE(M_AMDVI, "amdvi", "amdvi");
   75 
   76 extern device_t *ivhd_devs;
   77 
   78 extern int ivhd_count;
   79 SYSCTL_INT(_hw_vmm_amdvi, OID_AUTO, count, CTLFLAG_RDTUN, &ivhd_count,
   80     0, NULL);
   81 
   82 static int amdvi_enable_user = 0;
   83 SYSCTL_INT(_hw_vmm_amdvi, OID_AUTO, enable, CTLFLAG_RDTUN,
   84     &amdvi_enable_user, 0, NULL);
   85 TUNABLE_INT("hw.vmm.amdvi_enable", &amdvi_enable_user);
   86 
   87 #ifdef AMDVI_ATS_ENABLE
   88 /* XXX: ATS is not tested. */
   89 static int amdvi_enable_iotlb = 1;
   90 SYSCTL_INT(_hw_vmm_amdvi, OID_AUTO, iotlb_enabled, CTLFLAG_RDTUN,
   91     &amdvi_enable_iotlb, 0, NULL);
   92 TUNABLE_INT("hw.vmm.enable_iotlb", &amdvi_enable_iotlb);
   93 #endif
   94 
   95 static int amdvi_host_ptp = 1;  /* Use page tables for host. */
   96 SYSCTL_INT(_hw_vmm_amdvi, OID_AUTO, host_ptp, CTLFLAG_RDTUN,
   97     &amdvi_host_ptp, 0, NULL);
   98 TUNABLE_INT("hw.vmm.amdvi.host_ptp", &amdvi_host_ptp);
   99 
  100 /* Page table level used <= supported by h/w[v1=7]. */
  101 int amdvi_ptp_level = 4;
  102 SYSCTL_INT(_hw_vmm_amdvi, OID_AUTO, ptp_level, CTLFLAG_RDTUN,
  103     &amdvi_ptp_level, 0, NULL);
  104 TUNABLE_INT("hw.vmm.amdvi.ptp_level", &amdvi_ptp_level);
  105 
  106 /* Disable fault event reporting. */
  107 static int amdvi_disable_io_fault = 0;
  108 SYSCTL_INT(_hw_vmm_amdvi, OID_AUTO, disable_io_fault, CTLFLAG_RDTUN,
  109     &amdvi_disable_io_fault, 0, NULL);
  110 TUNABLE_INT("hw.vmm.amdvi.disable_io_fault", &amdvi_disable_io_fault);
  111 
  112 static uint32_t amdvi_dom_id = 0;       /* 0 is reserved for host. */
  113 SYSCTL_UINT(_hw_vmm_amdvi, OID_AUTO, domain_id, CTLFLAG_RD,
  114     &amdvi_dom_id, 0, NULL);
  115 /*
  116  * Device table entry.
  117  * Bus(256) x Dev(32) x Fun(8) x DTE(256 bits or 32 bytes).
  118  *      = 256 * 2 * PAGE_SIZE.
  119  */
  120 static struct amdvi_dte amdvi_dte[PCI_NUM_DEV_MAX] __aligned(PAGE_SIZE);
  121 CTASSERT(PCI_NUM_DEV_MAX == 0x10000);
  122 CTASSERT(sizeof(amdvi_dte) == 0x200000);
  123 
  124 static SLIST_HEAD (, amdvi_domain) dom_head;
  125 
  126 static inline uint32_t
  127 amdvi_pci_read(struct amdvi_softc *softc, int off)
  128 {
  129 
  130         return (pci_cfgregread(PCI_RID2BUS(softc->pci_rid),
  131             PCI_RID2SLOT(softc->pci_rid), PCI_RID2FUNC(softc->pci_rid),
  132             off, 4));
  133 }
  134 
  135 #ifdef AMDVI_ATS_ENABLE
  136 /* XXX: Should be in pci.c */
  137 /*
  138  * Check if device has ATS capability and its enabled.
  139  * If ATS is absent or disabled, return (-1), otherwise ATS
  140  * queue length.
  141  */
  142 static int
  143 amdvi_find_ats_qlen(uint16_t devid)
  144 {
  145         device_t dev;
  146         uint32_t off, cap;
  147         int qlen = -1;
  148 
  149         dev = pci_find_bsf(PCI_RID2BUS(devid), PCI_RID2SLOT(devid),
  150                            PCI_RID2FUNC(devid));
  151 
  152         if (!dev) {
  153                 return (-1);
  154         }
  155 #define PCIM_ATS_EN     BIT(31)
  156 
  157         if (pci_find_extcap(dev, PCIZ_ATS, &off) == 0) {
  158                 cap = pci_read_config(dev, off + 4, 4);
  159                 qlen = (cap & 0x1F);
  160                 qlen = qlen ? qlen : 32;
  161                 printf("AMD-Vi: PCI device %d.%d.%d ATS %s qlen=%d\n",
  162                        RID2PCI_STR(devid),
  163                        (cap & PCIM_ATS_EN) ? "enabled" : "Disabled",
  164                        qlen);
  165                 qlen = (cap & PCIM_ATS_EN) ? qlen : -1;
  166         }
  167 
  168         return (qlen);
  169 }
  170 
  171 /*
  172  * Check if an endpoint device support device IOTLB or ATS.
  173  */
  174 static inline bool
  175 amdvi_dev_support_iotlb(struct amdvi_softc *softc, uint16_t devid)
  176 {
  177         struct ivhd_dev_cfg *cfg;
  178         int qlen, i;
  179         bool pci_ats, ivhd_ats;
  180 
  181         qlen = amdvi_find_ats_qlen(devid);
  182         if (qlen < 0)
  183                 return (false);
  184 
  185         KASSERT(softc, ("softc is NULL"));
  186         cfg = softc->dev_cfg;
  187 
  188         ivhd_ats = false;
  189         for (i = 0; i < softc->dev_cfg_cnt; i++) {
  190                 if ((cfg->start_id <= devid) && (cfg->end_id >= devid)) {
  191                         ivhd_ats = cfg->enable_ats;
  192                         break;
  193                 }
  194                 cfg++;
  195         }
  196 
  197         pci_ats = (qlen < 0) ? false : true;
  198         if (pci_ats != ivhd_ats)
  199                 device_printf(softc->dev,
  200                     "BIOS bug: mismatch in ATS setting for %d.%d.%d,"
  201                     "ATS inv qlen = %d\n", RID2PCI_STR(devid), qlen);
  202 
  203         /* Ignore IVRS setting and respect PCI setting. */
  204         return (pci_ats);
  205 }
  206 #endif
  207 
  208 /* Enable IOTLB support for IOMMU if its supported. */
  209 static inline void
  210 amdvi_hw_enable_iotlb(struct amdvi_softc *softc)
  211 {
  212 #ifndef AMDVI_ATS_ENABLE
  213         softc->iotlb = false;
  214 #else
  215         bool supported;
  216 
  217         supported = (softc->ivhd_flag & IVHD_FLAG_IOTLB) ? true : false;
  218 
  219         if (softc->pci_cap & AMDVI_PCI_CAP_IOTLB) {
  220                 if (!supported)
  221                         device_printf(softc->dev, "IOTLB disabled by BIOS.\n");
  222 
  223                 if (supported && !amdvi_enable_iotlb) {
  224                         device_printf(softc->dev, "IOTLB disabled by user.\n");
  225                         supported = false;
  226                 }
  227         } else
  228                 supported = false;
  229 
  230         softc->iotlb = supported;
  231 
  232 #endif
  233 }
  234 
  235 static int
  236 amdvi_init_cmd(struct amdvi_softc *softc)
  237 {
  238         struct amdvi_ctrl *ctrl = softc->ctrl;
  239 
  240         ctrl->cmd.len = 8;      /* Use 256 command buffer entries. */
  241         softc->cmd_max = 1 << ctrl->cmd.len;
  242 
  243         softc->cmd = malloc(sizeof(struct amdvi_cmd) *
  244             softc->cmd_max, M_AMDVI, M_WAITOK | M_ZERO);
  245 
  246         if ((uintptr_t)softc->cmd & PAGE_MASK)
  247                 panic("AMDVi: Command buffer not aligned on page boundary.");
  248 
  249         ctrl->cmd.base = vtophys(softc->cmd) / PAGE_SIZE;
  250         /*
  251          * XXX: Reset the h/w pointers in case IOMMU is restarting,
  252          * h/w doesn't clear these pointers based on empirical data.
  253          */
  254         ctrl->cmd_tail = 0;
  255         ctrl->cmd_head = 0;
  256 
  257         return (0);
  258 }
  259 
  260 /*
  261  * Note: Update tail pointer after we have written the command since tail
  262  * pointer update cause h/w to execute new commands, see section 3.3
  263  * of AMD IOMMU spec ver 2.0.
  264  */
  265 /* Get the command tail pointer w/o updating it. */
  266 static struct amdvi_cmd *
  267 amdvi_get_cmd_tail(struct amdvi_softc *softc)
  268 {
  269         struct amdvi_ctrl *ctrl;
  270         struct amdvi_cmd *tail;
  271 
  272         KASSERT(softc, ("softc is NULL"));
  273         KASSERT(softc->cmd != NULL, ("cmd is NULL"));
  274 
  275         ctrl = softc->ctrl;
  276         KASSERT(ctrl != NULL, ("ctrl is NULL"));
  277 
  278         tail = (struct amdvi_cmd *)((uint8_t *)softc->cmd +
  279             ctrl->cmd_tail);
  280 
  281         return (tail);
  282 }
  283 
  284 /*
  285  * Update the command tail pointer which will start command execution.
  286  */
  287 static void
  288 amdvi_update_cmd_tail(struct amdvi_softc *softc)
  289 {
  290         struct amdvi_ctrl *ctrl;
  291         int size;
  292 
  293         size = sizeof(struct amdvi_cmd);
  294         KASSERT(softc->cmd != NULL, ("cmd is NULL"));
  295 
  296         ctrl = softc->ctrl;
  297         KASSERT(ctrl != NULL, ("ctrl is NULL"));
  298 
  299         ctrl->cmd_tail = MOD_INC(ctrl->cmd_tail, size, softc->cmd_max);
  300         softc->total_cmd++;
  301 
  302 #ifdef AMDVI_DEBUG_CMD
  303         device_printf(softc->dev, "cmd_tail: %s Tail:0x%x, Head:0x%x.\n",
  304             ctrl->cmd_tail,
  305             ctrl->cmd_head);
  306 #endif
  307 
  308 }
  309 
  310 /*
  311  * Various commands supported by IOMMU.
  312  */
  313 
  314 /* Completion wait command. */
  315 static void
  316 amdvi_cmd_cmp(struct amdvi_softc *softc, const uint64_t data)
  317 {
  318         struct amdvi_cmd *cmd;
  319         uint64_t pa;
  320 
  321         cmd = amdvi_get_cmd_tail(softc);
  322         KASSERT(cmd != NULL, ("Cmd is NULL"));
  323 
  324         pa = vtophys(&softc->cmp_data);
  325         cmd->opcode = AMDVI_CMP_WAIT_OPCODE;
  326         cmd->word0 = (pa & 0xFFFFFFF8) | AMDVI_CMP_WAIT_STORE;
  327         cmd->word1 = (pa >> 32) & 0xFFFFF;
  328         cmd->addr = data;
  329 
  330         amdvi_update_cmd_tail(softc);
  331 }
  332 
  333 /* Invalidate device table entry. */
  334 static void
  335 amdvi_cmd_inv_dte(struct amdvi_softc *softc, uint16_t devid)
  336 {
  337         struct amdvi_cmd *cmd;
  338 
  339         cmd = amdvi_get_cmd_tail(softc);
  340         KASSERT(cmd != NULL, ("Cmd is NULL"));
  341         cmd->opcode = AMDVI_INVD_DTE_OPCODE;
  342         cmd->word0 = devid;
  343         amdvi_update_cmd_tail(softc);
  344 #ifdef AMDVI_DEBUG_CMD
  345         device_printf(softc->dev, "Invalidated DTE:0x%x\n", devid);
  346 #endif
  347 }
  348 
  349 /* Invalidate IOMMU page, use for invalidation of domain. */
  350 static void
  351 amdvi_cmd_inv_iommu_pages(struct amdvi_softc *softc, uint16_t domain_id,
  352                           uint64_t addr, bool guest_nested,
  353                           bool pde, bool page)
  354 {
  355         struct amdvi_cmd *cmd;
  356 
  357         cmd = amdvi_get_cmd_tail(softc);
  358         KASSERT(cmd != NULL, ("Cmd is NULL"));
  359 
  360         cmd->opcode = AMDVI_INVD_PAGE_OPCODE;
  361         cmd->word1 = domain_id;
  362         /*
  363          * Invalidate all addresses for this domain.
  364          */
  365         cmd->addr = addr;
  366         cmd->addr |= pde ? AMDVI_INVD_PAGE_PDE : 0;
  367         cmd->addr |= page ? AMDVI_INVD_PAGE_S : 0;
  368 
  369         amdvi_update_cmd_tail(softc);
  370 }
  371 
  372 #ifdef AMDVI_ATS_ENABLE
  373 /* Invalidate device IOTLB. */
  374 static void
  375 amdvi_cmd_inv_iotlb(struct amdvi_softc *softc, uint16_t devid)
  376 {
  377         struct amdvi_cmd *cmd;
  378         int qlen;
  379 
  380         if (!softc->iotlb)
  381                 return;
  382 
  383         qlen = amdvi_find_ats_qlen(devid);
  384         if (qlen < 0) {
  385                 panic("AMDVI: Invalid ATS qlen(%d) for device %d.%d.%d\n",
  386                       qlen, RID2PCI_STR(devid));
  387         }
  388         cmd = amdvi_get_cmd_tail(softc);
  389         KASSERT(cmd != NULL, ("Cmd is NULL"));
  390 
  391 #ifdef AMDVI_DEBUG_CMD
  392         device_printf(softc->dev, "Invalidate IOTLB devID 0x%x"
  393                       " Qlen:%d\n", devid, qlen);
  394 #endif
  395         cmd->opcode = AMDVI_INVD_IOTLB_OPCODE;
  396         cmd->word0 = devid;
  397         cmd->word1 = qlen;
  398         cmd->addr = AMDVI_INVD_IOTLB_ALL_ADDR |
  399                 AMDVI_INVD_IOTLB_S;
  400         amdvi_update_cmd_tail(softc);
  401 }
  402 #endif
  403 
  404 #ifdef notyet                           /* For Interrupt Remap. */
  405 static void
  406 amdvi_cmd_inv_intr_map(struct amdvi_softc *softc,
  407                        uint16_t devid)
  408 {
  409         struct amdvi_cmd *cmd;
  410 
  411         cmd = amdvi_get_cmd_tail(softc);
  412         KASSERT(cmd != NULL, ("Cmd is NULL"));
  413         cmd->opcode = AMDVI_INVD_INTR_OPCODE;
  414         cmd->word0 = devid;
  415         amdvi_update_cmd_tail(softc);
  416 #ifdef AMDVI_DEBUG_CMD
  417         device_printf(softc->dev, "Invalidate INTR map of devID 0x%x\n", devid);
  418 #endif
  419 }
  420 #endif
  421 
  422 /* Invalidate domain using INVALIDATE_IOMMU_PAGES command. */
  423 static void
  424 amdvi_inv_domain(struct amdvi_softc *softc, uint16_t domain_id)
  425 {
  426         struct amdvi_cmd *cmd __diagused;
  427 
  428         cmd = amdvi_get_cmd_tail(softc);
  429         KASSERT(cmd != NULL, ("Cmd is NULL"));
  430 
  431         /*
  432          * See section 3.3.3 of IOMMU spec rev 2.0, software note
  433          * for invalidating domain.
  434          */
  435         amdvi_cmd_inv_iommu_pages(softc, domain_id, AMDVI_INVD_PAGE_ALL_ADDR,
  436                                 false, true, true);
  437 
  438 #ifdef AMDVI_DEBUG_CMD
  439         device_printf(softc->dev, "Invalidate domain:0x%x\n", domain_id);
  440 
  441 #endif
  442 }
  443 
  444 static  bool
  445 amdvi_cmp_wait(struct amdvi_softc *softc)
  446 {
  447 #ifdef AMDVI_DEBUG_CMD
  448         struct amdvi_ctrl *ctrl = softc->ctrl;
  449 #endif
  450         const uint64_t VERIFY = 0xA5A5;
  451         volatile uint64_t *read;
  452         int i;
  453         bool status;
  454 
  455         read = &softc->cmp_data;
  456         *read = 0;
  457         amdvi_cmd_cmp(softc, VERIFY);
  458         /* Wait for h/w to update completion data. */
  459         for (i = 0; i < 100 && (*read != VERIFY); i++) {
  460                 DELAY(1000);            /* 1 ms */
  461         }
  462         status = (VERIFY == softc->cmp_data) ? true : false;
  463 
  464 #ifdef AMDVI_DEBUG_CMD
  465         if (status)
  466                 device_printf(softc->dev, "CMD completion DONE Tail:0x%x, "
  467                               "Head:0x%x, loop:%d.\n", ctrl->cmd_tail,
  468                               ctrl->cmd_head, loop);
  469 #endif
  470         return (status);
  471 }
  472 
  473 static void
  474 amdvi_wait(struct amdvi_softc *softc)
  475 {
  476         struct amdvi_ctrl *ctrl;
  477         int i;
  478 
  479         KASSERT(softc, ("softc is NULL"));
  480 
  481         ctrl = softc->ctrl;
  482         KASSERT(ctrl != NULL, ("ctrl is NULL"));
  483         /* Don't wait if h/w is not enabled. */
  484         if ((ctrl->control & AMDVI_CTRL_EN) == 0)
  485                 return;
  486 
  487         for (i = 0; i < 10; i++) {
  488                 if (amdvi_cmp_wait(softc))
  489                         return;
  490         }
  491 
  492         device_printf(softc->dev, "Error: completion failed"
  493                       " tail:0x%x, head:0x%x.\n",
  494                       ctrl->cmd_tail, ctrl->cmd_head);
  495         /* Dump the last command. */
  496         amdvi_dump_cmds(softc, 1);
  497 }
  498 
  499 static void
  500 amdvi_dump_cmds(struct amdvi_softc *softc, int count)
  501 {
  502         struct amdvi_ctrl *ctrl;
  503         struct amdvi_cmd *cmd;
  504         int off, i;
  505 
  506         ctrl = softc->ctrl;
  507         device_printf(softc->dev, "Dump last %d command(s):\n", count);
  508         /*
  509          * If h/w is stuck in completion, it is the previous command,
  510          * start dumping from previous command onward.
  511          */
  512         off = MOD_DEC(ctrl->cmd_head, sizeof(struct amdvi_cmd),
  513             softc->cmd_max);
  514         for (i = 0; off != ctrl->cmd_tail && i < count; i++) {
  515                 cmd = (struct amdvi_cmd *)((uint8_t *)softc->cmd + off);
  516                 printf("  [CMD%d, off:0x%x] opcode= 0x%x 0x%x"
  517                     " 0x%x 0x%lx\n", i, off, cmd->opcode,
  518                     cmd->word0, cmd->word1, cmd->addr);
  519                 off = MOD_INC(off, sizeof(struct amdvi_cmd), softc->cmd_max);
  520         }
  521 }
  522 
  523 static int
  524 amdvi_init_event(struct amdvi_softc *softc)
  525 {
  526         struct amdvi_ctrl *ctrl;
  527 
  528         ctrl = softc->ctrl;
  529         ctrl->event.len = 8;
  530         softc->event_max = 1 << ctrl->event.len;
  531         softc->event = malloc(sizeof(struct amdvi_event) *
  532             softc->event_max, M_AMDVI, M_WAITOK | M_ZERO);
  533         if ((uintptr_t)softc->event & PAGE_MASK) {
  534                 device_printf(softc->dev, "Event buffer not aligned on page.");
  535                 return (false);
  536         }
  537         ctrl->event.base = vtophys(softc->event) / PAGE_SIZE;
  538 
  539         /* Reset the pointers. */
  540         ctrl->evt_head = 0;
  541         ctrl->evt_tail = 0;
  542 
  543         return (0);
  544 }
  545 
  546 static inline void
  547 amdvi_decode_evt_flag(uint16_t flag)
  548 {
  549 
  550         flag &= AMDVI_EVENT_FLAG_MASK;
  551         printf(" 0x%b]\n", flag,
  552                 "\020"
  553                 "\001GN"
  554                 "\002NX"
  555                 "\003US"
  556                 "\004I"
  557                 "\005PR"
  558                 "\006RW"
  559                 "\007PE"
  560                 "\010RZ"
  561                 "\011TR"
  562                 );
  563 }
  564 
  565 /* See section 2.5.4 of AMD IOMMU spec ver 2.62.*/
  566 static inline void
  567 amdvi_decode_evt_flag_type(uint8_t type)
  568 {
  569 
  570         switch (AMDVI_EVENT_FLAG_TYPE(type)) {
  571         case 0:
  572                 printf("RSVD\n");
  573                 break;
  574         case 1:
  575                 printf("Master Abort\n");
  576                 break;
  577         case 2:
  578                 printf("Target Abort\n");
  579                 break;
  580         case 3:
  581                 printf("Data Err\n");
  582                 break;
  583         default:
  584                 break;
  585         }
  586 }
  587 
  588 static void
  589 amdvi_decode_inv_dte_evt(uint16_t devid, uint16_t domid, uint64_t addr,
  590     uint16_t flag)
  591 {
  592 
  593         printf("\t[IO_PAGE_FAULT EVT: devId:0x%x DomId:0x%x"
  594             " Addr:0x%lx",
  595             devid, domid, addr);
  596         amdvi_decode_evt_flag(flag);
  597 }
  598 
  599 static void
  600 amdvi_decode_pf_evt(uint16_t devid, uint16_t domid, uint64_t addr,
  601     uint16_t flag)
  602 {
  603 
  604         printf("\t[IO_PAGE_FAULT EVT: devId:0x%x DomId:0x%x"
  605             " Addr:0x%lx",
  606             devid, domid, addr);
  607         amdvi_decode_evt_flag(flag);
  608 }
  609 
  610 static void
  611 amdvi_decode_dte_hwerr_evt(uint16_t devid, uint16_t domid,
  612     uint64_t addr, uint16_t flag)
  613 {
  614 
  615         printf("\t[DEV_TAB_HW_ERR EVT: devId:0x%x DomId:0x%x"
  616             " Addr:0x%lx", devid, domid, addr);
  617         amdvi_decode_evt_flag(flag);
  618         amdvi_decode_evt_flag_type(flag);
  619 }
  620 
  621 static void
  622 amdvi_decode_page_hwerr_evt(uint16_t devid, uint16_t domid, uint64_t addr,
  623     uint16_t flag)
  624 {
  625 
  626         printf("\t[PAGE_TAB_HW_ERR EVT: devId:0x%x DomId:0x%x"
  627             " Addr:0x%lx", devid, domid, addr);
  628         amdvi_decode_evt_flag(flag);
  629         amdvi_decode_evt_flag_type(AMDVI_EVENT_FLAG_TYPE(flag));
  630 }
  631 
  632 static void
  633 amdvi_decode_evt(struct amdvi_event *evt)
  634 {
  635         struct amdvi_cmd *cmd;
  636 
  637         switch (evt->opcode) {
  638         case AMDVI_EVENT_INVALID_DTE:
  639                 amdvi_decode_inv_dte_evt(evt->devid, evt->pasid_domid,
  640                     evt->addr, evt->flag);
  641                 break;
  642 
  643         case AMDVI_EVENT_PFAULT:
  644                 amdvi_decode_pf_evt(evt->devid, evt->pasid_domid,
  645                     evt->addr, evt->flag);
  646                 break;
  647 
  648         case AMDVI_EVENT_DTE_HW_ERROR:
  649                 amdvi_decode_dte_hwerr_evt(evt->devid, evt->pasid_domid,
  650                     evt->addr, evt->flag);
  651                 break;
  652 
  653         case AMDVI_EVENT_PAGE_HW_ERROR:
  654                 amdvi_decode_page_hwerr_evt(evt->devid, evt->pasid_domid,
  655                     evt->addr, evt->flag);
  656                 break;
  657 
  658         case AMDVI_EVENT_ILLEGAL_CMD:
  659                 /* FALL THROUGH */
  660         case AMDVI_EVENT_CMD_HW_ERROR:
  661                 printf("\t[%s EVT]\n", (evt->opcode == AMDVI_EVENT_ILLEGAL_CMD) ?
  662                     "ILLEGAL CMD" : "CMD HW ERR");
  663                 cmd = (struct amdvi_cmd *)PHYS_TO_DMAP(evt->addr);
  664                 printf("\tCMD opcode= 0x%x 0x%x 0x%x 0x%lx\n",
  665                     cmd->opcode, cmd->word0, cmd->word1, cmd->addr);
  666                 break;
  667 
  668         case AMDVI_EVENT_IOTLB_TIMEOUT:
  669                 printf("\t[IOTLB_INV_TIMEOUT devid:0x%x addr:0x%lx]\n",
  670                     evt->devid, evt->addr);
  671                 break;
  672 
  673         case AMDVI_EVENT_INVALID_DTE_REQ:
  674                 printf("\t[INV_DTE devid:0x%x addr:0x%lx type:0x%x tr:%d]\n",
  675                     evt->devid, evt->addr, evt->flag >> 9,
  676                     (evt->flag >> 8) & 1);
  677                 break;
  678 
  679         case AMDVI_EVENT_INVALID_PPR_REQ:
  680         case AMDVI_EVENT_COUNTER_ZERO:
  681                 printf("AMD-Vi: v2 events.\n");
  682                 break;
  683 
  684         default:
  685                 printf("Unsupported AMD-Vi event:%d\n", evt->opcode);
  686         }
  687 }
  688 
  689 static void
  690 amdvi_print_events(struct amdvi_softc *softc)
  691 {
  692         struct amdvi_ctrl *ctrl;
  693         struct amdvi_event *event;
  694         int i, size;
  695 
  696         ctrl = softc->ctrl;
  697         size = sizeof(struct amdvi_event);
  698         for (i = 0; i < softc->event_max; i++) {
  699                 event = &softc->event[ctrl->evt_head / size];
  700                 if (!event->opcode)
  701                         break;
  702                 device_printf(softc->dev, "\t[Event%d: Head:0x%x Tail:0x%x]\n",
  703                     i, ctrl->evt_head, ctrl->evt_tail);
  704                 amdvi_decode_evt(event);
  705                 ctrl->evt_head = MOD_INC(ctrl->evt_head, size,
  706                     softc->event_max);
  707         }
  708 }
  709 
  710 static int
  711 amdvi_init_dte(struct amdvi_softc *softc)
  712 {
  713         struct amdvi_ctrl *ctrl;
  714 
  715         ctrl = softc->ctrl;
  716         ctrl->dte.base = vtophys(amdvi_dte) / PAGE_SIZE;
  717         ctrl->dte.size = 0x1FF;         /* 2MB device table. */
  718 
  719         return (0);
  720 }
  721 
  722 /*
  723  * Not all capabilities of IOMMU are available in ACPI IVHD flag
  724  * or EFR entry, read directly from device.
  725  */
  726 static int
  727 amdvi_print_pci_cap(device_t dev)
  728 {
  729         struct amdvi_softc *softc;
  730         uint32_t off, cap;
  731 
  732         softc = device_get_softc(dev);
  733         off = softc->cap_off;
  734 
  735         /*
  736          * Section 3.7.1 of IOMMU sepc rev 2.0.
  737          * Read capability from device.
  738          */
  739         cap = amdvi_pci_read(softc, off);
  740 
  741         /* Make sure capability type[18:16] is 3. */
  742         KASSERT((((cap >> 16) & 0x7) == 0x3),
  743             ("Not a IOMMU capability 0x%x@0x%x", cap, off));
  744 
  745         softc->pci_cap = cap >> 24;
  746         device_printf(softc->dev, "PCI cap 0x%x@0x%x feature:%b\n",
  747             cap, off, softc->pci_cap,
  748             "\2\1IOTLB\2HT\3NPCache\4EFR\5CapExt");
  749 
  750         return (0);
  751 }
  752 
  753 static void
  754 amdvi_event_intr(void *arg)
  755 {
  756         struct amdvi_softc *softc;
  757         struct amdvi_ctrl *ctrl;
  758 
  759         softc = (struct amdvi_softc *)arg;
  760         ctrl = softc->ctrl;
  761         device_printf(softc->dev, "EVT INTR %ld Status:0x%x"
  762             " EVT Head:0x%x Tail:0x%x]\n", softc->event_intr_cnt++,
  763             ctrl->status, ctrl->evt_head, ctrl->evt_tail);
  764         printf("  [CMD Total 0x%lx] Tail:0x%x, Head:0x%x.\n",
  765             softc->total_cmd, ctrl->cmd_tail, ctrl->cmd_head);
  766 
  767         amdvi_print_events(softc);
  768         ctrl->status &= AMDVI_STATUS_EV_OF | AMDVI_STATUS_EV_INTR;
  769 }
  770 
  771 static void
  772 amdvi_free_evt_intr_res(device_t dev)
  773 {
  774 
  775         struct amdvi_softc *softc;
  776         device_t mmio_dev;
  777 
  778         softc = device_get_softc(dev);
  779         mmio_dev = softc->pci_dev;
  780 
  781         IVHD_TEARDOWN_INTR(mmio_dev);
  782 }
  783 
  784 static bool
  785 amdvi_alloc_intr_resources(struct amdvi_softc *softc)
  786 {
  787         struct amdvi_ctrl *ctrl;
  788         device_t dev, mmio_dev;
  789         int err;
  790 
  791         dev = softc->dev;
  792         mmio_dev = softc->pci_dev;
  793 
  794         /* Clear interrupt status bits. */
  795         ctrl = softc->ctrl;
  796         ctrl->status &= AMDVI_STATUS_EV_OF | AMDVI_STATUS_EV_INTR;
  797 
  798         err = IVHD_SETUP_INTR(mmio_dev, amdvi_event_intr, softc, "fault");
  799         if (err)
  800                 device_printf(dev, "Interrupt setup failed on %s\n",
  801                     device_get_nameunit(mmio_dev));
  802         return (err);
  803 }
  804 
  805 static void
  806 amdvi_print_dev_cap(struct amdvi_softc *softc)
  807 {
  808         struct ivhd_dev_cfg *cfg;
  809         int i;
  810 
  811         cfg = softc->dev_cfg;
  812         for (i = 0; i < softc->dev_cfg_cnt; i++) {
  813                 device_printf(softc->dev, "device [0x%x - 0x%x] "
  814                     "config:%b%s\n", cfg->start_id, cfg->end_id,
  815                     cfg->data,
  816                     "\020\001INIT\002ExtInt\003NMI"
  817                     "\007LINT0\010LINT1",
  818                     cfg->enable_ats ? "ATS enabled" : "");
  819                 cfg++;
  820         }
  821 }
  822 
  823 static int
  824 amdvi_handle_sysctl(SYSCTL_HANDLER_ARGS)
  825 {
  826         struct amdvi_softc *softc;
  827         int result, type, error = 0;
  828 
  829         softc = (struct amdvi_softc *)arg1;
  830         type = arg2;
  831 
  832         switch (type) {
  833         case 0:
  834                 result = softc->ctrl->cmd_head;
  835                 error = sysctl_handle_int(oidp, &result, 0,
  836                     req);
  837                 break;
  838         case 1:
  839                 result = softc->ctrl->cmd_tail;
  840                 error = sysctl_handle_int(oidp, &result, 0,
  841                     req);
  842                 break;
  843         case 2:
  844                 result = softc->ctrl->evt_head;
  845                 error = sysctl_handle_int(oidp, &result, 0,
  846                     req);
  847                 break;
  848         case 3:
  849                 result = softc->ctrl->evt_tail;
  850                 error = sysctl_handle_int(oidp, &result, 0,
  851                     req);
  852                 break;
  853 
  854         default:
  855                 device_printf(softc->dev, "Unknown sysctl:%d\n", type);
  856         }
  857 
  858         return (error);
  859 }
  860 
  861 static void
  862 amdvi_add_sysctl(struct amdvi_softc *softc)
  863 {
  864         struct sysctl_oid_list *child;
  865         struct sysctl_ctx_list *ctx;
  866         device_t dev;
  867 
  868         dev = softc->dev;
  869         ctx = device_get_sysctl_ctx(dev);
  870         child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev));
  871 
  872         SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "event_intr_count", CTLFLAG_RD,
  873             &softc->event_intr_cnt, "Event interrupt count");
  874         SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "command_count", CTLFLAG_RD,
  875             &softc->total_cmd, "Command submitted count");
  876         SYSCTL_ADD_U16(ctx, child, OID_AUTO, "pci_rid", CTLFLAG_RD,
  877             &softc->pci_rid, 0, "IOMMU RID");
  878         SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "command_head",
  879             CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_MPSAFE, softc, 0,
  880             amdvi_handle_sysctl, "IU", "Command head");
  881         SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "command_tail",
  882             CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_MPSAFE, softc, 1,
  883             amdvi_handle_sysctl, "IU", "Command tail");
  884         SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "event_head",
  885             CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_MPSAFE, softc, 2,
  886             amdvi_handle_sysctl, "IU", "Command head");
  887         SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "event_tail",
  888             CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_MPSAFE, softc, 3,
  889             amdvi_handle_sysctl, "IU", "Command tail");
  890 }
  891 
  892 int
  893 amdvi_setup_hw(struct amdvi_softc *softc)
  894 {
  895         device_t dev;
  896         int status;
  897 
  898         dev = softc->dev;
  899 
  900         amdvi_hw_enable_iotlb(softc);
  901 
  902         amdvi_print_dev_cap(softc);
  903 
  904         if ((status = amdvi_print_pci_cap(dev)) != 0) {
  905                 device_printf(dev, "PCI capability.\n");
  906                 return (status);
  907         }
  908         if ((status = amdvi_init_cmd(softc)) != 0) {
  909                 device_printf(dev, "Couldn't configure command buffer.\n");
  910                 return (status);
  911         }
  912         if ((status = amdvi_init_event(softc)) != 0) {
  913                 device_printf(dev, "Couldn't configure event buffer.\n");
  914                 return (status);
  915         }
  916         if ((status = amdvi_init_dte(softc)) != 0) {
  917                 device_printf(dev, "Couldn't configure device table.\n");
  918                 return (status);
  919         }
  920         if ((status = amdvi_alloc_intr_resources(softc)) != 0) {
  921                 return (status);
  922         }
  923         amdvi_add_sysctl(softc);
  924         return (0);
  925 }
  926 
  927 int
  928 amdvi_teardown_hw(struct amdvi_softc *softc)
  929 {
  930         device_t dev;
  931 
  932         dev = softc->dev;
  933 
  934         /* 
  935          * Called after disable, h/w is stopped by now, free all the resources. 
  936          */
  937         amdvi_free_evt_intr_res(dev);
  938 
  939         if (softc->cmd)
  940                 free(softc->cmd, M_AMDVI);
  941 
  942         if (softc->event)
  943                 free(softc->event, M_AMDVI);
  944 
  945         return (0);
  946 }
  947 
  948 /*********** bhyve interfaces *********************/
  949 static int
  950 amdvi_init(void)
  951 {
  952         if (!ivhd_count) {
  953                 return (EIO);
  954         }
  955         if (!amdvi_enable_user && ivhd_count) {
  956                 printf("bhyve: Found %d AMD-Vi/IOMMU device(s), "
  957                         "use hw.vmm.amdvi.enable=1 to enable pass-through.\n",
  958                     ivhd_count);
  959                 return (EINVAL);
  960         }
  961         return (0);
  962 }
  963 
  964 static void
  965 amdvi_cleanup(void)
  966 {
  967         /* Nothing. */
  968 }
  969 
  970 static uint16_t
  971 amdvi_domainId(void)
  972 {
  973 
  974         /*
  975          * If we hit maximum domain limit, rollover leaving host
  976          * domain(0).
  977          * XXX: make sure that this domain is not used.
  978          */
  979         if (amdvi_dom_id == AMDVI_MAX_DOMAIN)
  980                 amdvi_dom_id = 1;
  981 
  982         return ((uint16_t)amdvi_dom_id++);
  983 }
  984 
  985 static void
  986 amdvi_do_inv_domain(uint16_t domain_id, bool create)
  987 {
  988         struct amdvi_softc *softc;
  989         int i;
  990 
  991         for (i = 0; i < ivhd_count; i++) {
  992                 softc = device_get_softc(ivhd_devs[i]);
  993                 KASSERT(softc, ("softc is NULL"));
  994                 /*
  995                  * If not present pages are cached, invalidate page after
  996                  * creating domain.
  997                  */
  998 #if 0
  999                 if (create && ((softc->pci_cap & AMDVI_PCI_CAP_NPCACHE) == 0))
 1000                         continue;
 1001 #endif
 1002                 amdvi_inv_domain(softc, domain_id);
 1003                 amdvi_wait(softc);
 1004         }
 1005 }
 1006 
 1007 static void *
 1008 amdvi_create_domain(vm_paddr_t maxaddr)
 1009 {
 1010         struct amdvi_domain *dom;
 1011 
 1012         dom = malloc(sizeof(struct amdvi_domain), M_AMDVI, M_ZERO | M_WAITOK);
 1013         dom->id = amdvi_domainId();
 1014         //dom->maxaddr = maxaddr;
 1015 #ifdef AMDVI_DEBUG_CMD
 1016         printf("Created domain #%d\n", dom->id);
 1017 #endif
 1018         /*
 1019          * Host domain(#0) don't create translation table.
 1020          */
 1021         if (dom->id || amdvi_host_ptp)
 1022                 dom->ptp = malloc(PAGE_SIZE, M_AMDVI, M_WAITOK | M_ZERO);
 1023 
 1024         dom->ptp_level = amdvi_ptp_level;
 1025 
 1026         amdvi_do_inv_domain(dom->id, true);
 1027         SLIST_INSERT_HEAD(&dom_head, dom, next);
 1028 
 1029         return (dom);
 1030 }
 1031 
 1032 static void
 1033 amdvi_free_ptp(uint64_t *ptp, int level)
 1034 {
 1035         int i;
 1036 
 1037         if (level < 1)
 1038                 return;
 1039 
 1040         for (i = 0; i < NPTEPG ; i++) {
 1041                 if ((ptp[i] & AMDVI_PT_PRESENT) == 0)
 1042                         continue;
 1043                 /* XXX: Add super-page or PTE mapping > 4KB. */
 1044 #ifdef notyet
 1045                 /* Super-page mapping. */
 1046                 if (AMDVI_PD_SUPER(ptp[i]))
 1047                         continue;
 1048 #endif
 1049 
 1050                 amdvi_free_ptp((uint64_t *)PHYS_TO_DMAP(ptp[i]
 1051                     & AMDVI_PT_MASK), level - 1);
 1052         }
 1053 
 1054         free(ptp, M_AMDVI);
 1055 }
 1056 
 1057 static void
 1058 amdvi_destroy_domain(void *arg)
 1059 {
 1060         struct amdvi_domain *domain;
 1061 
 1062         domain = (struct amdvi_domain *)arg;
 1063         KASSERT(domain, ("domain is NULL"));
 1064 #ifdef AMDVI_DEBUG_CMD
 1065         printf("Destroying domain %d\n", domain->id);
 1066 #endif
 1067         if (domain->ptp)
 1068                 amdvi_free_ptp(domain->ptp, domain->ptp_level);
 1069 
 1070         amdvi_do_inv_domain(domain->id, false);
 1071         SLIST_REMOVE(&dom_head, domain, amdvi_domain, next);
 1072         free(domain, M_AMDVI);
 1073 }
 1074 
 1075 static uint64_t
 1076 amdvi_set_pt(uint64_t *pt, int level, vm_paddr_t gpa,
 1077     vm_paddr_t hpa, uint64_t pg_size, bool create)
 1078 {
 1079         uint64_t *page, pa;
 1080         int shift, index;
 1081         const int PT_SHIFT = 9;
 1082         const int PT_INDEX_MASK = (1 << PT_SHIFT) - 1;  /* Based on PT_SHIFT */
 1083 
 1084         if (!pg_size)
 1085                 return (0);
 1086 
 1087         if (hpa & (pg_size - 1)) {
 1088                 printf("HPA is not size aligned.\n");
 1089                 return (0);
 1090         }
 1091         if (gpa & (pg_size - 1)) {
 1092                 printf("HPA is not size aligned.\n");
 1093                 return (0);
 1094         }
 1095         shift = PML4SHIFT;
 1096         while ((shift > PAGE_SHIFT) && (pg_size < (1UL << shift))) {
 1097                 index = (gpa >> shift) & PT_INDEX_MASK;
 1098 
 1099                 if ((pt[index] == 0) && create) {
 1100                         page = malloc(PAGE_SIZE, M_AMDVI, M_WAITOK | M_ZERO);
 1101                         pa = vtophys(page);
 1102                         pt[index] = pa | AMDVI_PT_PRESENT | AMDVI_PT_RW |
 1103                             ((level - 1) << AMDVI_PD_LEVEL_SHIFT);
 1104                 }
 1105 #ifdef AMDVI_DEBUG_PTE
 1106                 if ((gpa % 0x1000000) == 0)
 1107                         printf("[level%d, shift = %d]PTE:0x%lx\n",
 1108                             level, shift, pt[index]);
 1109 #endif
 1110 #define PTE2PA(x)       ((uint64_t)(x) & AMDVI_PT_MASK)
 1111                 pa = PTE2PA(pt[index]);
 1112                 pt = (uint64_t *)PHYS_TO_DMAP(pa);
 1113                 shift -= PT_SHIFT;
 1114                 level--;
 1115         }
 1116 
 1117         /* Leaf entry. */
 1118         index = (gpa >> shift) & PT_INDEX_MASK;
 1119 
 1120         if (create) {
 1121                 pt[index] = hpa | AMDVI_PT_RW | AMDVI_PT_PRESENT;
 1122         } else
 1123                 pt[index] = 0;
 1124 
 1125 #ifdef AMDVI_DEBUG_PTE
 1126         if ((gpa % 0x1000000) == 0)
 1127                 printf("[Last level%d, shift = %d]PTE:0x%lx\n",
 1128                     level, shift, pt[index]);
 1129 #endif
 1130         return (1ULL << shift);
 1131 }
 1132 
 1133 static uint64_t
 1134 amdvi_update_mapping(struct amdvi_domain *domain, vm_paddr_t gpa,
 1135     vm_paddr_t hpa, uint64_t size, bool create)
 1136 {
 1137         uint64_t mapped, *ptp, len;
 1138         int level;
 1139 
 1140         KASSERT(domain, ("domain is NULL"));
 1141         level = domain->ptp_level;
 1142         KASSERT(level, ("Page table level is 0"));
 1143 
 1144         ptp = domain->ptp;
 1145         KASSERT(ptp, ("PTP is NULL"));
 1146         mapped = 0;
 1147         while (mapped < size) {
 1148                 len = amdvi_set_pt(ptp, level, gpa + mapped, hpa + mapped,
 1149                     PAGE_SIZE, create);
 1150                 if (!len) {
 1151                         printf("Error: Couldn't map HPA:0x%lx GPA:0x%lx\n",
 1152                             hpa, gpa);
 1153                         return (0);
 1154                 }
 1155                 mapped += len;
 1156         }
 1157 
 1158         return (mapped);
 1159 }
 1160 
 1161 static uint64_t
 1162 amdvi_create_mapping(void *arg, vm_paddr_t gpa, vm_paddr_t hpa,
 1163     uint64_t len)
 1164 {
 1165         struct amdvi_domain *domain;
 1166 
 1167         domain = (struct amdvi_domain *)arg;
 1168 
 1169         if (domain->id && !domain->ptp) {
 1170                 printf("ptp is NULL");
 1171                 return (-1);
 1172         }
 1173 
 1174         /*
 1175          * If host domain is created w/o page table, skip IOMMU page
 1176          * table set-up.
 1177          */
 1178         if (domain->ptp)
 1179                 return (amdvi_update_mapping(domain, gpa, hpa, len, true));
 1180         else
 1181                 return (len);
 1182 }
 1183 
 1184 static uint64_t
 1185 amdvi_remove_mapping(void *arg, vm_paddr_t gpa, uint64_t len)
 1186 {
 1187         struct amdvi_domain *domain;
 1188 
 1189         domain = (struct amdvi_domain *)arg;
 1190         /*
 1191          * If host domain is created w/o page table, skip IOMMU page
 1192          * table set-up.
 1193          */
 1194         if (domain->ptp)
 1195                 return (amdvi_update_mapping(domain, gpa, 0, len, false));
 1196         return
 1197             (len);
 1198 }
 1199 
 1200 static struct amdvi_softc *
 1201 amdvi_find_iommu(uint16_t devid)
 1202 {
 1203         struct amdvi_softc *softc;
 1204         int i, j;
 1205 
 1206         for (i = 0; i < ivhd_count; i++) {
 1207                 softc = device_get_softc(ivhd_devs[i]);
 1208                 for (j = 0; j < softc->dev_cfg_cnt; j++)
 1209                         if ((devid >= softc->dev_cfg[j].start_id) &&
 1210                             (devid <= softc->dev_cfg[j].end_id))
 1211                                 return (softc);
 1212         }
 1213 
 1214         return (NULL);
 1215 }
 1216 
 1217 /*
 1218  * Set-up device table entry.
 1219  * IOMMU spec Rev 2.0, section 3.2.2.2, some of the fields must
 1220  * be set concurrently, e.g. read and write bits.
 1221  */
 1222 static void
 1223 amdvi_set_dte(struct amdvi_domain *domain, struct amdvi_softc *softc,
 1224     uint16_t devid, bool enable)
 1225 {
 1226         struct amdvi_dte* temp;
 1227 
 1228         KASSERT(domain, ("domain is NULL for pci_rid:0x%x\n", devid));
 1229         KASSERT(softc, ("softc is NULL for pci_rid:0x%x\n", devid));
 1230 
 1231         temp = &amdvi_dte[devid];
 1232 
 1233 #ifdef AMDVI_ATS_ENABLE
 1234         /* If IOMMU and device support IOTLB, enable it. */
 1235         if (amdvi_dev_support_iotlb(softc, devid) && softc->iotlb)
 1236                 temp->iotlb_enable = 1;
 1237 #endif
 1238 
 1239         /* Avoid duplicate I/O faults. */
 1240         temp->sup_second_io_fault = 1;
 1241         temp->sup_all_io_fault = amdvi_disable_io_fault;
 1242 
 1243         temp->dt_valid = 1;
 1244         temp->domain_id = domain->id;
 1245 
 1246         if (enable) {
 1247                 if (domain->ptp) {
 1248                         temp->pt_base = vtophys(domain->ptp) >> 12;
 1249                         temp->pt_level = amdvi_ptp_level;
 1250                 }
 1251                 /*
 1252                  * XXX: Page table valid[TV] bit must be set even if host domain
 1253                  * page tables are not enabled.
 1254                  */
 1255                 temp->pt_valid = 1;
 1256                 temp->read_allow = 1;
 1257                 temp->write_allow = 1;
 1258         }
 1259 }
 1260 
 1261 static void
 1262 amdvi_inv_device(struct amdvi_softc *softc, uint16_t devid)
 1263 {
 1264         KASSERT(softc, ("softc is NULL"));
 1265 
 1266         amdvi_cmd_inv_dte(softc, devid);
 1267 #ifdef AMDVI_ATS_ENABLE
 1268         if (amdvi_dev_support_iotlb(softc, devid))
 1269                 amdvi_cmd_inv_iotlb(softc, devid);
 1270 #endif
 1271         amdvi_wait(softc);
 1272 }
 1273 
 1274 static void
 1275 amdvi_add_device(void *arg, uint16_t devid)
 1276 {
 1277         struct amdvi_domain *domain;
 1278         struct amdvi_softc *softc;
 1279 
 1280         domain = (struct amdvi_domain *)arg;
 1281         KASSERT(domain != NULL, ("domain is NULL"));
 1282 #ifdef AMDVI_DEBUG_CMD
 1283         printf("Assigning device(%d.%d.%d) to domain:%d\n",
 1284             RID2PCI_STR(devid), domain->id);
 1285 #endif
 1286         softc = amdvi_find_iommu(devid);
 1287         if (softc == NULL)
 1288                 return;
 1289         amdvi_set_dte(domain, softc, devid, true);
 1290         amdvi_inv_device(softc, devid);
 1291 }
 1292 
 1293 static void
 1294 amdvi_remove_device(void *arg, uint16_t devid)
 1295 {
 1296         struct amdvi_domain *domain;
 1297         struct amdvi_softc *softc;
 1298 
 1299         domain = (struct amdvi_domain *)arg;
 1300 #ifdef AMDVI_DEBUG_CMD
 1301         printf("Remove device(0x%x) from domain:%d\n",
 1302                devid, domain->id);
 1303 #endif
 1304         softc = amdvi_find_iommu(devid);
 1305         if (softc == NULL)
 1306                 return;
 1307         amdvi_set_dte(domain, softc, devid, false);
 1308         amdvi_inv_device(softc, devid);
 1309 }
 1310 
 1311 static void
 1312 amdvi_enable(void)
 1313 {
 1314         struct amdvi_ctrl *ctrl;
 1315         struct amdvi_softc *softc;
 1316         uint64_t val;
 1317         int i;
 1318 
 1319         for (i = 0; i < ivhd_count; i++) {
 1320                 softc = device_get_softc(ivhd_devs[i]);
 1321                 KASSERT(softc, ("softc is NULL\n"));
 1322                 ctrl = softc->ctrl;
 1323                 KASSERT(ctrl, ("ctrl is NULL\n"));
 1324 
 1325                 val = ( AMDVI_CTRL_EN           |
 1326                         AMDVI_CTRL_CMD          |
 1327                         AMDVI_CTRL_ELOG         |
 1328                         AMDVI_CTRL_ELOGINT      |
 1329                         AMDVI_CTRL_INV_TO_1S);
 1330 
 1331                 if (softc->ivhd_flag & IVHD_FLAG_COH)
 1332                         val |= AMDVI_CTRL_COH;
 1333                 if (softc->ivhd_flag & IVHD_FLAG_HTT)
 1334                         val |= AMDVI_CTRL_HTT;
 1335                 if (softc->ivhd_flag & IVHD_FLAG_RPPW)
 1336                         val |= AMDVI_CTRL_RPPW;
 1337                 if (softc->ivhd_flag & IVHD_FLAG_PPW)
 1338                         val |= AMDVI_CTRL_PPW;
 1339                 if (softc->ivhd_flag & IVHD_FLAG_ISOC)
 1340                         val |= AMDVI_CTRL_ISOC;
 1341 
 1342                 ctrl->control = val;
 1343         }
 1344 }
 1345 
 1346 static void
 1347 amdvi_disable(void)
 1348 {
 1349         struct amdvi_ctrl *ctrl;
 1350         struct amdvi_softc *softc;
 1351         int i;
 1352 
 1353         for (i = 0; i < ivhd_count; i++) {
 1354                 softc = device_get_softc(ivhd_devs[i]);
 1355                 KASSERT(softc, ("softc is NULL\n"));
 1356                 ctrl = softc->ctrl;
 1357                 KASSERT(ctrl, ("ctrl is NULL\n"));
 1358 
 1359                 ctrl->control = 0;
 1360         }
 1361 }
 1362 
 1363 static void
 1364 amdvi_invalidate_tlb(void *arg)
 1365 {
 1366         struct amdvi_domain *domain;
 1367 
 1368         domain = (struct amdvi_domain *)arg;
 1369         KASSERT(domain, ("domain is NULL"));
 1370         amdvi_do_inv_domain(domain->id, false);
 1371 }
 1372 
 1373 const struct iommu_ops iommu_ops_amd = {
 1374         .init = amdvi_init,
 1375         .cleanup = amdvi_cleanup,
 1376         .enable = amdvi_enable,
 1377         .disable = amdvi_disable,
 1378         .create_domain = amdvi_create_domain,
 1379         .destroy_domain = amdvi_destroy_domain,
 1380         .create_mapping = amdvi_create_mapping,
 1381         .remove_mapping = amdvi_remove_mapping,
 1382         .add_device = amdvi_add_device,
 1383         .remove_device = amdvi_remove_device,
 1384         .invalidate_tlb = amdvi_invalidate_tlb
 1385 };

Cache object: 189a0c447f6d74098527922393b35241


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.