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/io/vioapic.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) 2013 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
    5  * Copyright (c) 2013 Neel Natu <neel@freebsd.org>
    6  * All rights reserved.
    7  *
    8  * Redistribution and use in source and binary forms, with or without
    9  * modification, are permitted provided that the following conditions
   10  * are met:
   11  * 1. Redistributions of source code must retain the above copyright
   12  *    notice, this list of conditions and the following 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 NETAPP, INC ``AS IS'' AND
   18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   20  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
   21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   27  * SUCH DAMAGE.
   28  *
   29  * $FreeBSD$
   30  */
   31 
   32 #include <sys/cdefs.h>
   33 __FBSDID("$FreeBSD$");
   34 
   35 #include "opt_bhyve_snapshot.h"
   36 
   37 #include <sys/param.h>
   38 #include <sys/queue.h>
   39 #include <sys/lock.h>
   40 #include <sys/mutex.h>
   41 #include <sys/systm.h>
   42 #include <sys/kernel.h>
   43 #include <sys/malloc.h>
   44 
   45 #include <x86/apicreg.h>
   46 #include <machine/vmm.h>
   47 #include <machine/vmm_snapshot.h>
   48 
   49 #include "vmm_ktr.h"
   50 #include "vmm_lapic.h"
   51 #include "vlapic.h"
   52 #include "vioapic.h"
   53 
   54 #define IOREGSEL        0x00
   55 #define IOWIN           0x10
   56 
   57 #define REDIR_ENTRIES   32
   58 #define RTBL_RO_BITS    ((uint64_t)(IOART_REM_IRR | IOART_DELIVS))
   59 
   60 struct vioapic {
   61         struct vm       *vm;
   62         struct mtx      mtx;
   63         uint32_t        id;
   64         uint32_t        ioregsel;
   65         struct {
   66                 uint64_t reg;
   67                 int      acnt;  /* sum of pin asserts (+1) and deasserts (-1) */
   68         } rtbl[REDIR_ENTRIES];
   69 };
   70 
   71 #define VIOAPIC_LOCK(vioapic)           mtx_lock_spin(&((vioapic)->mtx))
   72 #define VIOAPIC_UNLOCK(vioapic)         mtx_unlock_spin(&((vioapic)->mtx))
   73 #define VIOAPIC_LOCKED(vioapic)         mtx_owned(&((vioapic)->mtx))
   74 
   75 static MALLOC_DEFINE(M_VIOAPIC, "vioapic", "bhyve virtual ioapic");
   76 
   77 #define VIOAPIC_CTR1(vioapic, fmt, a1)                                  \
   78         VM_CTR1((vioapic)->vm, fmt, a1)
   79 
   80 #define VIOAPIC_CTR2(vioapic, fmt, a1, a2)                              \
   81         VM_CTR2((vioapic)->vm, fmt, a1, a2)
   82 
   83 #define VIOAPIC_CTR3(vioapic, fmt, a1, a2, a3)                          \
   84         VM_CTR3((vioapic)->vm, fmt, a1, a2, a3)
   85 
   86 #define VIOAPIC_CTR4(vioapic, fmt, a1, a2, a3, a4)                      \
   87         VM_CTR4((vioapic)->vm, fmt, a1, a2, a3, a4)
   88 
   89 #ifdef KTR
   90 static const char *
   91 pinstate_str(bool asserted)
   92 {
   93 
   94         if (asserted)
   95                 return ("asserted");
   96         else
   97                 return ("deasserted");
   98 }
   99 #endif
  100 
  101 static void
  102 vioapic_send_intr(struct vioapic *vioapic, int pin)
  103 {
  104         int vector, delmode;
  105         uint32_t low, high, dest;
  106         bool level, phys;
  107 
  108         KASSERT(pin >= 0 && pin < REDIR_ENTRIES,
  109             ("vioapic_set_pinstate: invalid pin number %d", pin));
  110 
  111         KASSERT(VIOAPIC_LOCKED(vioapic),
  112             ("vioapic_set_pinstate: vioapic is not locked"));
  113 
  114         low = vioapic->rtbl[pin].reg;
  115         high = vioapic->rtbl[pin].reg >> 32;
  116 
  117         if ((low & IOART_INTMASK) == IOART_INTMSET) {
  118                 VIOAPIC_CTR1(vioapic, "ioapic pin%d: masked", pin);
  119                 return;
  120         }
  121 
  122         phys = ((low & IOART_DESTMOD) == IOART_DESTPHY);
  123         delmode = low & IOART_DELMOD;
  124         level = low & IOART_TRGRLVL ? true : false;
  125         if (level) {
  126                 if ((low & IOART_REM_IRR) != 0) {
  127                         VIOAPIC_CTR1(vioapic, "ioapic pin%d: irr pending",
  128                             pin);
  129                         return;
  130                 }
  131                 vioapic->rtbl[pin].reg |= IOART_REM_IRR;
  132         }
  133 
  134         vector = low & IOART_INTVEC;
  135         dest = high >> APIC_ID_SHIFT;
  136         vlapic_deliver_intr(vioapic->vm, level, dest, phys, delmode, vector);
  137 }
  138 
  139 static void
  140 vioapic_set_pinstate(struct vioapic *vioapic, int pin, bool newstate)
  141 {
  142         int oldcnt, newcnt;
  143         bool needintr;
  144 
  145         KASSERT(pin >= 0 && pin < REDIR_ENTRIES,
  146             ("vioapic_set_pinstate: invalid pin number %d", pin));
  147 
  148         KASSERT(VIOAPIC_LOCKED(vioapic),
  149             ("vioapic_set_pinstate: vioapic is not locked"));
  150 
  151         oldcnt = vioapic->rtbl[pin].acnt;
  152         if (newstate)
  153                 vioapic->rtbl[pin].acnt++;
  154         else
  155                 vioapic->rtbl[pin].acnt--;
  156         newcnt = vioapic->rtbl[pin].acnt;
  157 
  158         if (newcnt < 0) {
  159                 VIOAPIC_CTR2(vioapic, "ioapic pin%d: bad acnt %d",
  160                     pin, newcnt);
  161         }
  162 
  163         needintr = false;
  164         if (oldcnt == 0 && newcnt == 1) {
  165                 needintr = true;
  166                 VIOAPIC_CTR1(vioapic, "ioapic pin%d: asserted", pin);
  167         } else if (oldcnt == 1 && newcnt == 0) {
  168                 VIOAPIC_CTR1(vioapic, "ioapic pin%d: deasserted", pin);
  169         } else {
  170                 VIOAPIC_CTR3(vioapic, "ioapic pin%d: %s, ignored, acnt %d",
  171                     pin, pinstate_str(newstate), newcnt);
  172         }
  173 
  174         if (needintr)
  175                 vioapic_send_intr(vioapic, pin);
  176 }
  177 
  178 enum irqstate {
  179         IRQSTATE_ASSERT,
  180         IRQSTATE_DEASSERT,
  181         IRQSTATE_PULSE
  182 };
  183 
  184 static int
  185 vioapic_set_irqstate(struct vm *vm, int irq, enum irqstate irqstate)
  186 {
  187         struct vioapic *vioapic;
  188 
  189         if (irq < 0 || irq >= REDIR_ENTRIES)
  190                 return (EINVAL);
  191 
  192         vioapic = vm_ioapic(vm);
  193 
  194         VIOAPIC_LOCK(vioapic);
  195         switch (irqstate) {
  196         case IRQSTATE_ASSERT:
  197                 vioapic_set_pinstate(vioapic, irq, true);
  198                 break;
  199         case IRQSTATE_DEASSERT:
  200                 vioapic_set_pinstate(vioapic, irq, false);
  201                 break;
  202         case IRQSTATE_PULSE:
  203                 vioapic_set_pinstate(vioapic, irq, true);
  204                 vioapic_set_pinstate(vioapic, irq, false);
  205                 break;
  206         default:
  207                 panic("vioapic_set_irqstate: invalid irqstate %d", irqstate);
  208         }
  209         VIOAPIC_UNLOCK(vioapic);
  210 
  211         return (0);
  212 }
  213 
  214 int
  215 vioapic_assert_irq(struct vm *vm, int irq)
  216 {
  217 
  218         return (vioapic_set_irqstate(vm, irq, IRQSTATE_ASSERT));
  219 }
  220 
  221 int
  222 vioapic_deassert_irq(struct vm *vm, int irq)
  223 {
  224 
  225         return (vioapic_set_irqstate(vm, irq, IRQSTATE_DEASSERT));
  226 }
  227 
  228 int
  229 vioapic_pulse_irq(struct vm *vm, int irq)
  230 {
  231 
  232         return (vioapic_set_irqstate(vm, irq, IRQSTATE_PULSE));
  233 }
  234 
  235 /*
  236  * Reset the vlapic's trigger-mode register to reflect the ioapic pin
  237  * configuration.
  238  */
  239 static void
  240 vioapic_update_tmr(struct vcpu *vcpu, void *arg)
  241 {
  242         struct vioapic *vioapic;
  243         struct vlapic *vlapic;
  244         uint32_t low, high, dest;
  245         int delmode, pin, vector;
  246         bool level, phys;
  247 
  248         vlapic = vm_lapic(vcpu);
  249         vioapic = vm_ioapic(vcpu_vm(vcpu));
  250 
  251         VIOAPIC_LOCK(vioapic);
  252         /*
  253          * Reset all vectors to be edge-triggered.
  254          */
  255         vlapic_reset_tmr(vlapic);
  256         for (pin = 0; pin < REDIR_ENTRIES; pin++) {
  257                 low = vioapic->rtbl[pin].reg;
  258                 high = vioapic->rtbl[pin].reg >> 32;
  259 
  260                 level = low & IOART_TRGRLVL ? true : false;
  261                 if (!level)
  262                         continue;
  263 
  264                 /*
  265                  * For a level-triggered 'pin' let the vlapic figure out if
  266                  * an assertion on this 'pin' would result in an interrupt
  267                  * being delivered to it. If yes, then it will modify the
  268                  * TMR bit associated with this vector to level-triggered.
  269                  */
  270                 phys = ((low & IOART_DESTMOD) == IOART_DESTPHY);
  271                 delmode = low & IOART_DELMOD;
  272                 vector = low & IOART_INTVEC;
  273                 dest = high >> APIC_ID_SHIFT;
  274                 vlapic_set_tmr_level(vlapic, dest, phys, delmode, vector);
  275         }
  276         VIOAPIC_UNLOCK(vioapic);
  277 }
  278 
  279 static uint32_t
  280 vioapic_read(struct vioapic *vioapic, struct vcpu *vcpu, uint32_t addr)
  281 {
  282         int regnum, pin, rshift;
  283 
  284         regnum = addr & 0xff;
  285         switch (regnum) {
  286         case IOAPIC_ID:
  287                 return (vioapic->id);
  288                 break;
  289         case IOAPIC_VER:
  290                 return (((REDIR_ENTRIES - 1) << MAXREDIRSHIFT) | 0x11);
  291                 break;
  292         case IOAPIC_ARB:
  293                 return (vioapic->id);
  294                 break;
  295         default:
  296                 break;
  297         }
  298 
  299         /* redirection table entries */
  300         if (regnum >= IOAPIC_REDTBL &&
  301             regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
  302                 pin = (regnum - IOAPIC_REDTBL) / 2;
  303                 if ((regnum - IOAPIC_REDTBL) % 2)
  304                         rshift = 32;
  305                 else
  306                         rshift = 0;
  307 
  308                 return (vioapic->rtbl[pin].reg >> rshift);
  309         }
  310 
  311         return (0);
  312 }
  313 
  314 static void
  315 vioapic_write(struct vioapic *vioapic, struct vcpu *vcpu, uint32_t addr,
  316     uint32_t data)
  317 {
  318         uint64_t data64, mask64;
  319         uint64_t last, changed;
  320         int regnum, pin, lshift;
  321         cpuset_t allvcpus;
  322 
  323         regnum = addr & 0xff;
  324         switch (regnum) {
  325         case IOAPIC_ID:
  326                 vioapic->id = data & APIC_ID_MASK;
  327                 break;
  328         case IOAPIC_VER:
  329         case IOAPIC_ARB:
  330                 /* readonly */
  331                 break;
  332         default:
  333                 break;
  334         }
  335 
  336         /* redirection table entries */
  337         if (regnum >= IOAPIC_REDTBL &&
  338             regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
  339                 pin = (regnum - IOAPIC_REDTBL) / 2;
  340                 if ((regnum - IOAPIC_REDTBL) % 2)
  341                         lshift = 32;
  342                 else
  343                         lshift = 0;
  344 
  345                 last = vioapic->rtbl[pin].reg;
  346 
  347                 data64 = (uint64_t)data << lshift;
  348                 mask64 = (uint64_t)0xffffffff << lshift;
  349                 vioapic->rtbl[pin].reg &= ~mask64 | RTBL_RO_BITS;
  350                 vioapic->rtbl[pin].reg |= data64 & ~RTBL_RO_BITS;
  351 
  352                 /*
  353                  * Switching from level to edge triggering will clear the IRR
  354                  * bit. This is what FreeBSD will do in order to EOI an
  355                  * interrupt when the IO-APIC doesn't support targeted EOI (see
  356                  * _ioapic_eoi_source).
  357                  */
  358                 if ((vioapic->rtbl[pin].reg & IOART_TRGRMOD) == IOART_TRGREDG &&
  359                     (vioapic->rtbl[pin].reg & IOART_REM_IRR) != 0)
  360                         vioapic->rtbl[pin].reg &= ~IOART_REM_IRR;
  361 
  362                 VIOAPIC_CTR2(vioapic, "ioapic pin%d: redir table entry %#lx",
  363                     pin, vioapic->rtbl[pin].reg);
  364 
  365                 /*
  366                  * If any fields in the redirection table entry (except mask
  367                  * or polarity) have changed then rendezvous all the vcpus
  368                  * to update their vlapic trigger-mode registers.
  369                  */
  370                 changed = last ^ vioapic->rtbl[pin].reg;
  371                 if (changed & ~(IOART_INTMASK | IOART_INTPOL)) {
  372                         VIOAPIC_CTR1(vioapic, "ioapic pin%d: recalculate "
  373                             "vlapic trigger-mode register", pin);
  374                         VIOAPIC_UNLOCK(vioapic);
  375                         allvcpus = vm_active_cpus(vioapic->vm);
  376                         (void)vm_smp_rendezvous(vcpu, allvcpus,
  377                             vioapic_update_tmr, NULL);
  378                         VIOAPIC_LOCK(vioapic);
  379                 }
  380 
  381                 /*
  382                  * Generate an interrupt if the following conditions are met:
  383                  * - pin trigger mode is level
  384                  * - pin level is asserted
  385                  */
  386                 if ((vioapic->rtbl[pin].reg & IOART_TRGRMOD) == IOART_TRGRLVL &&
  387                     (vioapic->rtbl[pin].acnt > 0)) {
  388                         VIOAPIC_CTR2(vioapic, "ioapic pin%d: asserted at rtbl "
  389                             "write, acnt %d", pin, vioapic->rtbl[pin].acnt);
  390                         vioapic_send_intr(vioapic, pin);
  391                 }
  392         }
  393 }
  394 
  395 static int
  396 vioapic_mmio_rw(struct vioapic *vioapic, struct vcpu *vcpu, uint64_t gpa,
  397     uint64_t *data, int size, bool doread)
  398 {
  399         uint64_t offset;
  400 
  401         offset = gpa - VIOAPIC_BASE;
  402 
  403         /*
  404          * The IOAPIC specification allows 32-bit wide accesses to the
  405          * IOREGSEL (offset 0) and IOWIN (offset 16) registers.
  406          */
  407         if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) {
  408                 if (doread)
  409                         *data = 0;
  410                 return (0);
  411         }
  412 
  413         VIOAPIC_LOCK(vioapic);
  414         if (offset == IOREGSEL) {
  415                 if (doread)
  416                         *data = vioapic->ioregsel;
  417                 else
  418                         vioapic->ioregsel = *data;
  419         } else {
  420                 if (doread) {
  421                         *data = vioapic_read(vioapic, vcpu,
  422                             vioapic->ioregsel);
  423                 } else {
  424                         vioapic_write(vioapic, vcpu, vioapic->ioregsel,
  425                             *data);
  426                 }
  427         }
  428         VIOAPIC_UNLOCK(vioapic);
  429 
  430         return (0);
  431 }
  432 
  433 int
  434 vioapic_mmio_read(struct vcpu *vcpu, uint64_t gpa, uint64_t *rval,
  435     int size, void *arg)
  436 {
  437         int error;
  438         struct vioapic *vioapic;
  439 
  440         vioapic = vm_ioapic(vcpu_vm(vcpu));
  441         error = vioapic_mmio_rw(vioapic, vcpu, gpa, rval, size, true);
  442         return (error);
  443 }
  444 
  445 int
  446 vioapic_mmio_write(struct vcpu *vcpu, uint64_t gpa, uint64_t wval,
  447     int size, void *arg)
  448 {
  449         int error;
  450         struct vioapic *vioapic;
  451 
  452         vioapic = vm_ioapic(vcpu_vm(vcpu));
  453         error = vioapic_mmio_rw(vioapic, vcpu, gpa, &wval, size, false);
  454         return (error);
  455 }
  456 
  457 void
  458 vioapic_process_eoi(struct vm *vm, int vector)
  459 {
  460         struct vioapic *vioapic;
  461         int pin;
  462 
  463         KASSERT(vector >= 0 && vector < 256,
  464             ("vioapic_process_eoi: invalid vector %d", vector));
  465 
  466         vioapic = vm_ioapic(vm);
  467         VIOAPIC_CTR1(vioapic, "ioapic processing eoi for vector %d", vector);
  468 
  469         /*
  470          * XXX keep track of the pins associated with this vector instead
  471          * of iterating on every single pin each time.
  472          */
  473         VIOAPIC_LOCK(vioapic);
  474         for (pin = 0; pin < REDIR_ENTRIES; pin++) {
  475                 if ((vioapic->rtbl[pin].reg & IOART_REM_IRR) == 0)
  476                         continue;
  477                 if ((vioapic->rtbl[pin].reg & IOART_INTVEC) != vector)
  478                         continue;
  479                 vioapic->rtbl[pin].reg &= ~IOART_REM_IRR;
  480                 if (vioapic->rtbl[pin].acnt > 0) {
  481                         VIOAPIC_CTR2(vioapic, "ioapic pin%d: asserted at eoi, "
  482                             "acnt %d", pin, vioapic->rtbl[pin].acnt);
  483                         vioapic_send_intr(vioapic, pin);
  484                 }
  485         }
  486         VIOAPIC_UNLOCK(vioapic);
  487 }
  488 
  489 struct vioapic *
  490 vioapic_init(struct vm *vm)
  491 {
  492         int i;
  493         struct vioapic *vioapic;
  494 
  495         vioapic = malloc(sizeof(struct vioapic), M_VIOAPIC, M_WAITOK | M_ZERO);
  496 
  497         vioapic->vm = vm;
  498         mtx_init(&vioapic->mtx, "vioapic lock", NULL, MTX_SPIN);
  499 
  500         /* Initialize all redirection entries to mask all interrupts */
  501         for (i = 0; i < REDIR_ENTRIES; i++)
  502                 vioapic->rtbl[i].reg = 0x0001000000010000UL;
  503 
  504         return (vioapic);
  505 }
  506 
  507 void
  508 vioapic_cleanup(struct vioapic *vioapic)
  509 {
  510 
  511         mtx_destroy(&vioapic->mtx);
  512         free(vioapic, M_VIOAPIC);
  513 }
  514 
  515 int
  516 vioapic_pincount(struct vm *vm)
  517 {
  518 
  519         return (REDIR_ENTRIES);
  520 }
  521 
  522 #ifdef BHYVE_SNAPSHOT
  523 int
  524 vioapic_snapshot(struct vioapic *vioapic, struct vm_snapshot_meta *meta)
  525 {
  526         int ret;
  527         int i;
  528 
  529         SNAPSHOT_VAR_OR_LEAVE(vioapic->ioregsel, meta, ret, done);
  530 
  531         for (i = 0; i < nitems(vioapic->rtbl); i++) {
  532                 SNAPSHOT_VAR_OR_LEAVE(vioapic->rtbl[i].reg, meta, ret, done);
  533                 SNAPSHOT_VAR_OR_LEAVE(vioapic->rtbl[i].acnt, meta, ret, done);
  534         }
  535 
  536 done:
  537         return (ret);
  538 }
  539 #endif

Cache object: 99d5d6d0f344ab30732bc633b683b690


[ 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.