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/vhpet.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/lock.h>
   39 #include <sys/mutex.h>
   40 #include <sys/kernel.h>
   41 #include <sys/malloc.h>
   42 #include <sys/systm.h>
   43 
   44 #include <dev/acpica/acpi_hpet.h>
   45 
   46 #include <machine/vmm.h>
   47 #include <machine/vmm_dev.h>
   48 #include <machine/vmm_snapshot.h>
   49 
   50 #include "vmm_lapic.h"
   51 #include "vatpic.h"
   52 #include "vioapic.h"
   53 #include "vhpet.h"
   54 
   55 #include "vmm_ktr.h"
   56 
   57 static MALLOC_DEFINE(M_VHPET, "vhpet", "bhyve virtual hpet");
   58 
   59 #define HPET_FREQ       16777216                /* 16.7 (2^24) Mhz */
   60 #define FS_PER_S        1000000000000000ul
   61 
   62 /* Timer N Configuration and Capabilities Register */
   63 #define HPET_TCAP_RO_MASK       (HPET_TCAP_INT_ROUTE    |               \
   64                                  HPET_TCAP_FSB_INT_DEL  |               \
   65                                  HPET_TCAP_SIZE         |               \
   66                                  HPET_TCAP_PER_INT)
   67 /*
   68  * HPET requires at least 3 timers and up to 32 timers per block.
   69  */
   70 #define VHPET_NUM_TIMERS        8
   71 CTASSERT(VHPET_NUM_TIMERS >= 3 && VHPET_NUM_TIMERS <= 32);
   72 
   73 struct vhpet_callout_arg {
   74         struct vhpet *vhpet;
   75         int timer_num;
   76 };
   77 
   78 struct vhpet {
   79         struct vm       *vm;
   80         struct mtx      mtx;
   81         sbintime_t      freq_sbt;
   82 
   83         uint64_t        config;         /* Configuration */
   84         uint64_t        isr;            /* Interrupt Status */
   85         uint32_t        countbase;      /* HPET counter base value */
   86         sbintime_t      countbase_sbt;  /* uptime corresponding to base value */
   87 
   88         struct {
   89                 uint64_t        cap_config;     /* Configuration */
   90                 uint64_t        msireg;         /* FSB interrupt routing */
   91                 uint32_t        compval;        /* Comparator */
   92                 uint32_t        comprate;
   93                 struct callout  callout;
   94                 sbintime_t      callout_sbt;    /* time when counter==compval */
   95                 struct vhpet_callout_arg arg;
   96         } timer[VHPET_NUM_TIMERS];
   97 };
   98 
   99 #define VHPET_LOCK(vhp)         mtx_lock(&((vhp)->mtx))
  100 #define VHPET_UNLOCK(vhp)       mtx_unlock(&((vhp)->mtx))
  101 
  102 static void vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter,
  103     sbintime_t now);
  104 
  105 static uint64_t
  106 vhpet_capabilities(void)
  107 {
  108         uint64_t cap = 0;
  109 
  110         cap |= 0x8086 << 16;                    /* vendor id */
  111         cap |= (VHPET_NUM_TIMERS - 1) << 8;     /* number of timers */
  112         cap |= 1;                               /* revision */
  113         cap &= ~HPET_CAP_COUNT_SIZE;            /* 32-bit timer */
  114 
  115         cap &= 0xffffffff;
  116         cap |= (FS_PER_S / HPET_FREQ) << 32;    /* tick period in fs */
  117 
  118         return (cap);
  119 }
  120 
  121 static __inline bool
  122 vhpet_counter_enabled(struct vhpet *vhpet)
  123 {
  124 
  125         return ((vhpet->config & HPET_CNF_ENABLE) ? true : false);
  126 }
  127 
  128 static __inline bool
  129 vhpet_timer_msi_enabled(struct vhpet *vhpet, int n)
  130 {
  131         const uint64_t msi_enable = HPET_TCAP_FSB_INT_DEL | HPET_TCNF_FSB_EN;
  132 
  133         if ((vhpet->timer[n].cap_config & msi_enable) == msi_enable)
  134                 return (true);
  135         else
  136                 return (false);
  137 }
  138 
  139 static __inline int
  140 vhpet_timer_ioapic_pin(struct vhpet *vhpet, int n)
  141 {
  142         /*
  143          * If the timer is configured to use MSI then treat it as if the
  144          * timer is not connected to the ioapic.
  145          */
  146         if (vhpet_timer_msi_enabled(vhpet, n))
  147                 return (0);
  148 
  149         return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ROUTE) >> 9);
  150 }
  151 
  152 static uint32_t
  153 vhpet_counter(struct vhpet *vhpet, sbintime_t *nowptr)
  154 {
  155         uint32_t val;
  156         sbintime_t now, delta;
  157 
  158         val = vhpet->countbase;
  159         if (vhpet_counter_enabled(vhpet)) {
  160                 now = sbinuptime();
  161                 delta = now - vhpet->countbase_sbt;
  162                 KASSERT(delta >= 0, ("vhpet_counter: uptime went backwards: "
  163                     "%#lx to %#lx", vhpet->countbase_sbt, now));
  164                 val += delta / vhpet->freq_sbt;
  165                 if (nowptr != NULL)
  166                         *nowptr = now;
  167         } else {
  168                 /*
  169                  * The sbinuptime corresponding to the 'countbase' is
  170                  * meaningless when the counter is disabled. Make sure
  171                  * that the caller doesn't want to use it.
  172                  */
  173                 KASSERT(nowptr == NULL, ("vhpet_counter: nowptr must be NULL"));
  174         }
  175         return (val);
  176 }
  177 
  178 static void
  179 vhpet_timer_clear_isr(struct vhpet *vhpet, int n)
  180 {
  181         int pin;
  182 
  183         if (vhpet->isr & (1 << n)) {
  184                 pin = vhpet_timer_ioapic_pin(vhpet, n);
  185                 KASSERT(pin != 0, ("vhpet timer %d irq incorrectly routed", n));
  186                 vioapic_deassert_irq(vhpet->vm, pin);
  187                 vhpet->isr &= ~(1 << n);
  188         }
  189 }
  190 
  191 static __inline bool
  192 vhpet_periodic_timer(struct vhpet *vhpet, int n)
  193 {
  194 
  195         return ((vhpet->timer[n].cap_config & HPET_TCNF_TYPE) != 0);
  196 }
  197 
  198 static __inline bool
  199 vhpet_timer_interrupt_enabled(struct vhpet *vhpet, int n)
  200 {
  201 
  202         return ((vhpet->timer[n].cap_config & HPET_TCNF_INT_ENB) != 0);
  203 }
  204 
  205 static __inline bool
  206 vhpet_timer_edge_trig(struct vhpet *vhpet, int n)
  207 {
  208 
  209         KASSERT(!vhpet_timer_msi_enabled(vhpet, n), ("vhpet_timer_edge_trig: "
  210             "timer %d is using MSI", n));
  211 
  212         if ((vhpet->timer[n].cap_config & HPET_TCNF_INT_TYPE) == 0)
  213                 return (true);
  214         else
  215                 return (false);
  216 }
  217 
  218 static void
  219 vhpet_timer_interrupt(struct vhpet *vhpet, int n)
  220 {
  221         int pin;
  222 
  223         /* If interrupts are not enabled for this timer then just return. */
  224         if (!vhpet_timer_interrupt_enabled(vhpet, n))
  225                 return;
  226 
  227         /*
  228          * If a level triggered interrupt is already asserted then just return.
  229          */
  230         if ((vhpet->isr & (1 << n)) != 0) {
  231                 VM_CTR1(vhpet->vm, "hpet t%d intr is already asserted", n);
  232                 return;
  233         }
  234 
  235         if (vhpet_timer_msi_enabled(vhpet, n)) {
  236                 lapic_intr_msi(vhpet->vm, vhpet->timer[n].msireg >> 32,
  237                     vhpet->timer[n].msireg & 0xffffffff);
  238                 return;
  239         }       
  240 
  241         pin = vhpet_timer_ioapic_pin(vhpet, n);
  242         if (pin == 0) {
  243                 VM_CTR1(vhpet->vm, "hpet t%d intr is not routed to ioapic", n);
  244                 return;
  245         }
  246 
  247         if (vhpet_timer_edge_trig(vhpet, n)) {
  248                 vioapic_pulse_irq(vhpet->vm, pin);
  249         } else {
  250                 vhpet->isr |= 1 << n;
  251                 vioapic_assert_irq(vhpet->vm, pin);
  252         }
  253 }
  254 
  255 static void
  256 vhpet_adjust_compval(struct vhpet *vhpet, int n, uint32_t counter)
  257 {
  258         uint32_t compval, comprate, compnext;
  259 
  260         KASSERT(vhpet->timer[n].comprate != 0, ("hpet t%d is not periodic", n));
  261 
  262         compval = vhpet->timer[n].compval;
  263         comprate = vhpet->timer[n].comprate;
  264 
  265         /*
  266          * Calculate the comparator value to be used for the next periodic
  267          * interrupt.
  268          *
  269          * This function is commonly called from the callout handler.
  270          * In this scenario the 'counter' is ahead of 'compval'. To find
  271          * the next value to program into the accumulator we divide the
  272          * number space between 'compval' and 'counter' into 'comprate'
  273          * sized units. The 'compval' is rounded up such that is "ahead"
  274          * of 'counter'.
  275          */
  276         compnext = compval + ((counter - compval) / comprate + 1) * comprate;
  277 
  278         vhpet->timer[n].compval = compnext;
  279 }
  280 
  281 static void
  282 vhpet_handler(void *a)
  283 {
  284         int n;
  285         uint32_t counter;
  286         sbintime_t now;
  287         struct vhpet *vhpet;
  288         struct callout *callout;
  289         struct vhpet_callout_arg *arg;
  290 
  291         arg = a;
  292         vhpet = arg->vhpet;
  293         n = arg->timer_num;
  294         callout = &vhpet->timer[n].callout;
  295 
  296         VM_CTR1(vhpet->vm, "hpet t%d fired", n);
  297 
  298         VHPET_LOCK(vhpet);
  299 
  300         if (callout_pending(callout))           /* callout was reset */
  301                 goto done;
  302 
  303         if (!callout_active(callout))           /* callout was stopped */
  304                 goto done;
  305 
  306         callout_deactivate(callout);
  307 
  308         if (!vhpet_counter_enabled(vhpet))
  309                 panic("vhpet(%p) callout with counter disabled", vhpet);
  310 
  311         counter = vhpet_counter(vhpet, &now);
  312         vhpet_start_timer(vhpet, n, counter, now);
  313         vhpet_timer_interrupt(vhpet, n);
  314 done:
  315         VHPET_UNLOCK(vhpet);
  316         return;
  317 }
  318 
  319 static void
  320 vhpet_stop_timer(struct vhpet *vhpet, int n, sbintime_t now)
  321 {
  322 
  323         VM_CTR1(vhpet->vm, "hpet t%d stopped", n);
  324         callout_stop(&vhpet->timer[n].callout);
  325 
  326         /*
  327          * If the callout was scheduled to expire in the past but hasn't
  328          * had a chance to execute yet then trigger the timer interrupt
  329          * here. Failing to do so will result in a missed timer interrupt
  330          * in the guest. This is especially bad in one-shot mode because
  331          * the next interrupt has to wait for the counter to wrap around.
  332          */
  333         if (vhpet->timer[n].callout_sbt < now) {
  334                 VM_CTR1(vhpet->vm, "hpet t%d interrupt triggered after "
  335                     "stopping timer", n);
  336                 vhpet_timer_interrupt(vhpet, n);
  337         }
  338 }
  339 
  340 static void
  341 vhpet_start_timer(struct vhpet *vhpet, int n, uint32_t counter, sbintime_t now)
  342 {
  343         sbintime_t delta, precision;
  344 
  345         if (vhpet->timer[n].comprate != 0)
  346                 vhpet_adjust_compval(vhpet, n, counter);
  347         else {
  348                 /*
  349                  * In one-shot mode it is the guest's responsibility to make
  350                  * sure that the comparator value is not in the "past". The
  351                  * hardware doesn't have any belt-and-suspenders to deal with
  352                  * this so we don't either.
  353                  */
  354         }
  355 
  356         delta = (vhpet->timer[n].compval - counter) * vhpet->freq_sbt;
  357         precision = delta >> tc_precexp;
  358         vhpet->timer[n].callout_sbt = now + delta;
  359         callout_reset_sbt(&vhpet->timer[n].callout, vhpet->timer[n].callout_sbt,
  360             precision, vhpet_handler, &vhpet->timer[n].arg, C_ABSOLUTE);
  361 }
  362 
  363 static void
  364 vhpet_start_counting(struct vhpet *vhpet)
  365 {
  366         int i;
  367 
  368         vhpet->countbase_sbt = sbinuptime();
  369         for (i = 0; i < VHPET_NUM_TIMERS; i++) {
  370                 /*
  371                  * Restart the timers based on the value of the main counter
  372                  * when it stopped counting.
  373                  */
  374                 vhpet_start_timer(vhpet, i, vhpet->countbase,
  375                     vhpet->countbase_sbt);
  376         }
  377 }
  378 
  379 static void
  380 vhpet_stop_counting(struct vhpet *vhpet, uint32_t counter, sbintime_t now)
  381 {
  382         int i;
  383 
  384         vhpet->countbase = counter;
  385         for (i = 0; i < VHPET_NUM_TIMERS; i++)
  386                 vhpet_stop_timer(vhpet, i, now);
  387 }
  388 
  389 static __inline void
  390 update_register(uint64_t *regptr, uint64_t data, uint64_t mask)
  391 {
  392 
  393         *regptr &= ~mask;
  394         *regptr |= (data & mask);
  395 }
  396 
  397 static void
  398 vhpet_timer_update_config(struct vhpet *vhpet, int n, uint64_t data,
  399     uint64_t mask)
  400 {
  401         bool clear_isr;
  402         int old_pin, new_pin;
  403         uint32_t allowed_irqs;
  404         uint64_t oldval, newval;
  405 
  406         if (vhpet_timer_msi_enabled(vhpet, n) ||
  407             vhpet_timer_edge_trig(vhpet, n)) {
  408                 if (vhpet->isr & (1 << n))
  409                         panic("vhpet timer %d isr should not be asserted", n);
  410         }
  411         old_pin = vhpet_timer_ioapic_pin(vhpet, n);
  412         oldval = vhpet->timer[n].cap_config;
  413 
  414         newval = oldval;
  415         update_register(&newval, data, mask);
  416         newval &= ~(HPET_TCAP_RO_MASK | HPET_TCNF_32MODE);
  417         newval |= oldval & HPET_TCAP_RO_MASK;
  418 
  419         if (newval == oldval)
  420                 return;
  421 
  422         vhpet->timer[n].cap_config = newval;
  423         VM_CTR2(vhpet->vm, "hpet t%d cap_config set to 0x%016x", n, newval);
  424 
  425         /*
  426          * Validate the interrupt routing in the HPET_TCNF_INT_ROUTE field.
  427          * If it does not match the bits set in HPET_TCAP_INT_ROUTE then set
  428          * it to the default value of 0.
  429          */
  430         allowed_irqs = vhpet->timer[n].cap_config >> 32;
  431         new_pin = vhpet_timer_ioapic_pin(vhpet, n);
  432         if (new_pin != 0 && (allowed_irqs & (1 << new_pin)) == 0) {
  433                 VM_CTR3(vhpet->vm, "hpet t%d configured invalid irq %d, "
  434                     "allowed_irqs 0x%08x", n, new_pin, allowed_irqs);
  435                 new_pin = 0;
  436                 vhpet->timer[n].cap_config &= ~HPET_TCNF_INT_ROUTE;
  437         }
  438 
  439         if (!vhpet_periodic_timer(vhpet, n))
  440                 vhpet->timer[n].comprate = 0;
  441 
  442         /*
  443          * If the timer's ISR bit is set then clear it in the following cases:
  444          * - interrupt is disabled
  445          * - interrupt type is changed from level to edge or fsb.
  446          * - interrupt routing is changed
  447          *
  448          * This is to ensure that this timer's level triggered interrupt does
  449          * not remain asserted forever.
  450          */
  451         if (vhpet->isr & (1 << n)) {
  452                 KASSERT(old_pin != 0, ("timer %d isr asserted to ioapic pin %d",
  453                     n, old_pin));
  454                 if (!vhpet_timer_interrupt_enabled(vhpet, n))
  455                         clear_isr = true;
  456                 else if (vhpet_timer_msi_enabled(vhpet, n))
  457                         clear_isr = true;
  458                 else if (vhpet_timer_edge_trig(vhpet, n))
  459                         clear_isr = true;
  460                 else if (vhpet_timer_ioapic_pin(vhpet, n) != old_pin)
  461                         clear_isr = true;
  462                 else
  463                         clear_isr = false;
  464 
  465                 if (clear_isr) {
  466                         VM_CTR1(vhpet->vm, "hpet t%d isr cleared due to "
  467                             "configuration change", n);
  468                         vioapic_deassert_irq(vhpet->vm, old_pin);
  469                         vhpet->isr &= ~(1 << n);
  470                 }
  471         }
  472 }
  473 
  474 int
  475 vhpet_mmio_write(struct vcpu *vcpu, uint64_t gpa, uint64_t val, int size,
  476     void *arg)
  477 {
  478         struct vhpet *vhpet;
  479         uint64_t data, mask, oldval, val64;
  480         uint32_t isr_clear_mask, old_compval, old_comprate, counter;
  481         sbintime_t now, *nowptr;
  482         int i, offset;
  483 
  484         vhpet = vm_hpet(vcpu_vm(vcpu));
  485         offset = gpa - VHPET_BASE;
  486 
  487         VHPET_LOCK(vhpet);
  488 
  489         /* Accesses to the HPET should be 4 or 8 bytes wide */
  490         switch (size) {
  491         case 8:
  492                 mask = 0xffffffffffffffff;
  493                 data = val;
  494                 break;
  495         case 4:
  496                 mask = 0xffffffff;
  497                 data = val;
  498                 if ((offset & 0x4) != 0) {
  499                         mask <<= 32;
  500                         data <<= 32;
  501                 } 
  502                 break;
  503         default:
  504                 VM_CTR2(vhpet->vm, "hpet invalid mmio write: "
  505                     "offset 0x%08x, size %d", offset, size);
  506                 goto done;
  507         }
  508 
  509         /* Access to the HPET should be naturally aligned to its width */
  510         if (offset & (size - 1)) {
  511                 VM_CTR2(vhpet->vm, "hpet invalid mmio write: "
  512                     "offset 0x%08x, size %d", offset, size);
  513                 goto done;
  514         }
  515 
  516         if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
  517                 /*
  518                  * Get the most recent value of the counter before updating
  519                  * the 'config' register. If the HPET is going to be disabled
  520                  * then we need to update 'countbase' with the value right
  521                  * before it is disabled.
  522                  */
  523                 nowptr = vhpet_counter_enabled(vhpet) ? &now : NULL;
  524                 counter = vhpet_counter(vhpet, nowptr);
  525                 oldval = vhpet->config;
  526                 update_register(&vhpet->config, data, mask);
  527 
  528                 /*
  529                  * LegacyReplacement Routing is not supported so clear the
  530                  * bit explicitly.
  531                  */
  532                 vhpet->config &= ~HPET_CNF_LEG_RT;
  533 
  534                 if ((oldval ^ vhpet->config) & HPET_CNF_ENABLE) {
  535                         if (vhpet_counter_enabled(vhpet)) {
  536                                 vhpet_start_counting(vhpet);
  537                                 VM_CTR0(vhpet->vm, "hpet enabled");
  538                         } else {
  539                                 vhpet_stop_counting(vhpet, counter, now);
  540                                 VM_CTR0(vhpet->vm, "hpet disabled");
  541                         }
  542                 }
  543                 goto done;
  544         }
  545 
  546         if (offset == HPET_ISR || offset == HPET_ISR + 4) {
  547                 isr_clear_mask = vhpet->isr & data;
  548                 for (i = 0; i < VHPET_NUM_TIMERS; i++) {
  549                         if ((isr_clear_mask & (1 << i)) != 0) {
  550                                 VM_CTR1(vhpet->vm, "hpet t%d isr cleared", i);
  551                                 vhpet_timer_clear_isr(vhpet, i);
  552                         }
  553                 }
  554                 goto done;
  555         }
  556 
  557         if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
  558                 /* Zero-extend the counter to 64-bits before updating it */
  559                 val64 = vhpet_counter(vhpet, NULL);
  560                 update_register(&val64, data, mask);
  561                 vhpet->countbase = val64;
  562                 if (vhpet_counter_enabled(vhpet))
  563                         vhpet_start_counting(vhpet);
  564                 goto done;
  565         }
  566 
  567         for (i = 0; i < VHPET_NUM_TIMERS; i++) {
  568                 if (offset == HPET_TIMER_CAP_CNF(i) ||
  569                     offset == HPET_TIMER_CAP_CNF(i) + 4) {
  570                         vhpet_timer_update_config(vhpet, i, data, mask);
  571                         break;
  572                 }
  573 
  574                 if (offset == HPET_TIMER_COMPARATOR(i) ||
  575                     offset == HPET_TIMER_COMPARATOR(i) + 4) {
  576                         old_compval = vhpet->timer[i].compval;
  577                         old_comprate = vhpet->timer[i].comprate;
  578                         if (vhpet_periodic_timer(vhpet, i)) {
  579                                 /*
  580                                  * In periodic mode writes to the comparator
  581                                  * change the 'compval' register only if the
  582                                  * HPET_TCNF_VAL_SET bit is set in the config
  583                                  * register.
  584                                  */
  585                                 val64 = vhpet->timer[i].comprate;
  586                                 update_register(&val64, data, mask);
  587                                 vhpet->timer[i].comprate = val64;
  588                                 if ((vhpet->timer[i].cap_config &
  589                                     HPET_TCNF_VAL_SET) != 0) {
  590                                         vhpet->timer[i].compval = val64;
  591                                 }
  592                         } else {
  593                                 KASSERT(vhpet->timer[i].comprate == 0,
  594                                     ("vhpet one-shot timer %d has invalid "
  595                                     "rate %u", i, vhpet->timer[i].comprate));
  596                                 val64 = vhpet->timer[i].compval;
  597                                 update_register(&val64, data, mask);
  598                                 vhpet->timer[i].compval = val64;
  599                         }
  600                         vhpet->timer[i].cap_config &= ~HPET_TCNF_VAL_SET;
  601 
  602                         if (vhpet->timer[i].compval != old_compval ||
  603                             vhpet->timer[i].comprate != old_comprate) {
  604                                 if (vhpet_counter_enabled(vhpet)) {
  605                                         counter = vhpet_counter(vhpet, &now);
  606                                         vhpet_start_timer(vhpet, i, counter,
  607                                             now);
  608                                 }
  609                         }
  610                         break;
  611                 }
  612 
  613                 if (offset == HPET_TIMER_FSB_VAL(i) ||
  614                     offset == HPET_TIMER_FSB_ADDR(i)) {
  615                         update_register(&vhpet->timer[i].msireg, data, mask);
  616                         break;
  617                 }
  618         }
  619 done:
  620         VHPET_UNLOCK(vhpet);
  621         return (0);
  622 }
  623 
  624 int
  625 vhpet_mmio_read(struct vcpu *vcpu, uint64_t gpa, uint64_t *rval, int size,
  626     void *arg)
  627 {
  628         int i, offset;
  629         struct vhpet *vhpet;
  630         uint64_t data;
  631 
  632         vhpet = vm_hpet(vcpu_vm(vcpu));
  633         offset = gpa - VHPET_BASE;
  634 
  635         VHPET_LOCK(vhpet);
  636 
  637         /* Accesses to the HPET should be 4 or 8 bytes wide */
  638         if (size != 4 && size != 8) {
  639                 VM_CTR2(vhpet->vm, "hpet invalid mmio read: "
  640                     "offset 0x%08x, size %d", offset, size);
  641                 data = 0;
  642                 goto done;
  643         }
  644 
  645         /* Access to the HPET should be naturally aligned to its width */
  646         if (offset & (size - 1)) {
  647                 VM_CTR2(vhpet->vm, "hpet invalid mmio read: "
  648                     "offset 0x%08x, size %d", offset, size);
  649                 data = 0;
  650                 goto done;
  651         }
  652 
  653         if (offset == HPET_CAPABILITIES || offset == HPET_CAPABILITIES + 4) {
  654                 data = vhpet_capabilities();
  655                 goto done;      
  656         }
  657 
  658         if (offset == HPET_CONFIG || offset == HPET_CONFIG + 4) {
  659                 data = vhpet->config;
  660                 goto done;
  661         }
  662 
  663         if (offset == HPET_ISR || offset == HPET_ISR + 4) {
  664                 data = vhpet->isr;
  665                 goto done;
  666         }
  667 
  668         if (offset == HPET_MAIN_COUNTER || offset == HPET_MAIN_COUNTER + 4) {
  669                 data = vhpet_counter(vhpet, NULL);
  670                 goto done;
  671         }
  672 
  673         for (i = 0; i < VHPET_NUM_TIMERS; i++) {
  674                 if (offset == HPET_TIMER_CAP_CNF(i) ||
  675                     offset == HPET_TIMER_CAP_CNF(i) + 4) {
  676                         data = vhpet->timer[i].cap_config;
  677                         break;
  678                 }
  679 
  680                 if (offset == HPET_TIMER_COMPARATOR(i) ||
  681                     offset == HPET_TIMER_COMPARATOR(i) + 4) {
  682                         data = vhpet->timer[i].compval;
  683                         break;
  684                 }
  685 
  686                 if (offset == HPET_TIMER_FSB_VAL(i) ||
  687                     offset == HPET_TIMER_FSB_ADDR(i)) {
  688                         data = vhpet->timer[i].msireg;
  689                         break;
  690                 }
  691         }
  692 
  693         if (i >= VHPET_NUM_TIMERS)
  694                 data = 0;
  695 done:
  696         VHPET_UNLOCK(vhpet);
  697 
  698         if (size == 4) {
  699                 if (offset & 0x4)
  700                         data >>= 32;
  701         }
  702         *rval = data;
  703         return (0);
  704 }
  705 
  706 struct vhpet *
  707 vhpet_init(struct vm *vm)
  708 {
  709         int i, pincount;
  710         struct vhpet *vhpet;
  711         uint64_t allowed_irqs;
  712         struct vhpet_callout_arg *arg;
  713         struct bintime bt;
  714 
  715         vhpet = malloc(sizeof(struct vhpet), M_VHPET, M_WAITOK | M_ZERO);
  716         vhpet->vm = vm;
  717         mtx_init(&vhpet->mtx, "vhpet lock", NULL, MTX_DEF);
  718 
  719         FREQ2BT(HPET_FREQ, &bt);
  720         vhpet->freq_sbt = bttosbt(bt);
  721 
  722         pincount = vioapic_pincount(vm);
  723         if (pincount >= 32)
  724                 allowed_irqs = 0xff000000;      /* irqs 24-31 */
  725         else if (pincount >= 20)
  726                 allowed_irqs = 0xf << (pincount - 4);   /* 4 upper irqs */
  727         else
  728                 allowed_irqs = 0;
  729 
  730         /*
  731          * Initialize HPET timer hardware state.
  732          */
  733         for (i = 0; i < VHPET_NUM_TIMERS; i++) {
  734                 vhpet->timer[i].cap_config = allowed_irqs << 32;
  735                 vhpet->timer[i].cap_config |= HPET_TCAP_PER_INT;
  736                 vhpet->timer[i].cap_config |= HPET_TCAP_FSB_INT_DEL;
  737 
  738                 vhpet->timer[i].compval = 0xffffffff;
  739                 callout_init(&vhpet->timer[i].callout, 1);
  740 
  741                 arg = &vhpet->timer[i].arg;
  742                 arg->vhpet = vhpet;
  743                 arg->timer_num = i;
  744         }
  745 
  746         return (vhpet);
  747 }
  748 
  749 void
  750 vhpet_cleanup(struct vhpet *vhpet)
  751 {
  752         int i;
  753 
  754         for (i = 0; i < VHPET_NUM_TIMERS; i++)
  755                 callout_drain(&vhpet->timer[i].callout);
  756 
  757         mtx_destroy(&vhpet->mtx);
  758         free(vhpet, M_VHPET);
  759 }
  760 
  761 int
  762 vhpet_getcap(struct vm_hpet_cap *cap)
  763 {
  764 
  765         cap->capabilities = vhpet_capabilities();
  766         return (0);
  767 }
  768 
  769 #ifdef BHYVE_SNAPSHOT
  770 int
  771 vhpet_snapshot(struct vhpet *vhpet, struct vm_snapshot_meta *meta)
  772 {
  773         int i, ret;
  774         uint32_t countbase;
  775 
  776         SNAPSHOT_VAR_OR_LEAVE(vhpet->freq_sbt, meta, ret, done);
  777         SNAPSHOT_VAR_OR_LEAVE(vhpet->config, meta, ret, done);
  778         SNAPSHOT_VAR_OR_LEAVE(vhpet->isr, meta, ret, done);
  779 
  780         /* at restore time the countbase should have the value it had when the
  781          * snapshot was created; since the value is not directly kept in
  782          * vhpet->countbase, but rather computed relative to the current system
  783          * uptime using countbase_sbt, save the value retured by vhpet_counter
  784          */
  785         if (meta->op == VM_SNAPSHOT_SAVE)
  786                 countbase = vhpet_counter(vhpet, NULL);
  787         SNAPSHOT_VAR_OR_LEAVE(countbase, meta, ret, done);
  788         if (meta->op == VM_SNAPSHOT_RESTORE)
  789                 vhpet->countbase = countbase;
  790 
  791         for (i = 0; i < nitems(vhpet->timer); i++) {
  792                 SNAPSHOT_VAR_OR_LEAVE(vhpet->timer[i].cap_config,
  793                                       meta, ret, done);
  794                 SNAPSHOT_VAR_OR_LEAVE(vhpet->timer[i].msireg, meta, ret, done);
  795                 SNAPSHOT_VAR_OR_LEAVE(vhpet->timer[i].compval, meta, ret, done);
  796                 SNAPSHOT_VAR_OR_LEAVE(vhpet->timer[i].comprate, meta, ret, done);
  797                 SNAPSHOT_VAR_OR_LEAVE(vhpet->timer[i].callout_sbt,
  798                                       meta, ret, done);
  799         }
  800 
  801 done:
  802         return (ret);
  803 }
  804 
  805 int
  806 vhpet_restore_time(struct vhpet *vhpet)
  807 {
  808         if (vhpet_counter_enabled(vhpet))
  809                 vhpet_start_counting(vhpet);
  810 
  811         return (0);
  812 }
  813 #endif

Cache object: 8a88ad72cd249d6e324a6e8699d4674d


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