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/vatpit.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  * Copyright (c) 2014 Tycho Nightingale <tycho.nightingale@pluribusnetworks.com>
    3  * Copyright (c) 2011 NetApp, Inc.
    4  * All rights reserved.
    5  * Copyright (c) 2018 Joyent, Inc.
    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, this list of conditions and the following disclaimer.
   12  * 2. Redistributions in binary form must reproduce the above copyright
   13  *    notice, this list of conditions and the following disclaimer in the
   14  *    documentation and/or other materials provided with the distribution.
   15  *
   16  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
   17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   19  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
   20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   26  * SUCH DAMAGE.
   27  */
   28 
   29 #include <sys/cdefs.h>
   30 __FBSDID("$FreeBSD$");
   31 
   32 #include "opt_bhyve_snapshot.h"
   33 
   34 #include <sys/param.h>
   35 #include <sys/types.h>
   36 #include <sys/queue.h>
   37 #include <sys/kernel.h>
   38 #include <sys/lock.h>
   39 #include <sys/malloc.h>
   40 #include <sys/mutex.h>
   41 #include <sys/systm.h>
   42 
   43 #include <machine/vmm.h>
   44 #include <machine/vmm_snapshot.h>
   45 
   46 #include "vmm_ktr.h"
   47 #include "vatpic.h"
   48 #include "vioapic.h"
   49 #include "vatpit.h"
   50 
   51 static MALLOC_DEFINE(M_VATPIT, "atpit", "bhyve virtual atpit (8254)");
   52 
   53 #define VATPIT_LOCK(vatpit)             mtx_lock_spin(&((vatpit)->mtx))
   54 #define VATPIT_UNLOCK(vatpit)           mtx_unlock_spin(&((vatpit)->mtx))
   55 #define VATPIT_LOCKED(vatpit)           mtx_owned(&((vatpit)->mtx))
   56 
   57 #define TIMER_SEL_MASK          0xc0
   58 #define TIMER_RW_MASK           0x30
   59 #define TIMER_MODE_MASK         0x0f
   60 #define TIMER_SEL_READBACK      0xc0
   61 
   62 #define TIMER_STS_OUT           0x80
   63 #define TIMER_STS_NULLCNT       0x40
   64 
   65 #define TIMER_RB_LCTR           0x20
   66 #define TIMER_RB_LSTATUS        0x10
   67 #define TIMER_RB_CTR_2          0x08
   68 #define TIMER_RB_CTR_1          0x04
   69 #define TIMER_RB_CTR_0          0x02
   70 
   71 #define TMR2_OUT_STS            0x20
   72 
   73 #define PIT_8254_FREQ           1193182
   74 #define TIMER_DIV(freq, hz)     (((freq) + (hz) / 2) / (hz))
   75 
   76 struct vatpit_callout_arg {
   77         struct vatpit   *vatpit;
   78         int             channel_num;
   79 };
   80 
   81 struct channel {
   82         int             mode;
   83         uint16_t        initial;        /* initial counter value */
   84         struct bintime  now_bt;         /* uptime when counter was loaded */
   85         uint8_t         cr[2];
   86         uint8_t         ol[2];
   87         bool            slatched;       /* status latched */
   88         uint8_t         status;
   89         int             crbyte;
   90         int             olbyte;
   91         int             frbyte;
   92         struct callout  callout;
   93         struct bintime  callout_bt;     /* target time */
   94         struct vatpit_callout_arg callout_arg;
   95 };
   96 
   97 struct vatpit {
   98         struct vm       *vm;
   99         struct mtx      mtx;
  100 
  101         struct bintime  freq_bt;
  102 
  103         struct channel  channel[3];
  104 };
  105 
  106 static void pit_timer_start_cntr0(struct vatpit *vatpit);
  107 
  108 static uint64_t
  109 vatpit_delta_ticks(struct vatpit *vatpit, struct channel *c)
  110 {
  111         struct bintime delta;
  112         uint64_t result;
  113 
  114         binuptime(&delta);
  115         bintime_sub(&delta, &c->now_bt);
  116 
  117         result = delta.sec * PIT_8254_FREQ;
  118         result += delta.frac / vatpit->freq_bt.frac;
  119 
  120         return (result);
  121 }
  122 
  123 static int
  124 vatpit_get_out(struct vatpit *vatpit, int channel)
  125 {
  126         struct channel *c;
  127         uint64_t delta_ticks;
  128         int out;
  129 
  130         c = &vatpit->channel[channel];
  131 
  132         switch (c->mode) {
  133         case TIMER_INTTC:
  134                 delta_ticks = vatpit_delta_ticks(vatpit, c);
  135                 out = (delta_ticks >= c->initial);
  136                 break;
  137         default:
  138                 out = 0;
  139                 break;
  140         }
  141 
  142         return (out);
  143 }
  144 
  145 static void
  146 vatpit_callout_handler(void *a)
  147 {
  148         struct vatpit_callout_arg *arg = a;
  149         struct vatpit *vatpit;
  150         struct callout *callout;
  151         struct channel *c;
  152 
  153         vatpit = arg->vatpit;
  154         c = &vatpit->channel[arg->channel_num];
  155         callout = &c->callout;
  156 
  157         VM_CTR1(vatpit->vm, "atpit t%d fired", arg->channel_num);
  158 
  159         VATPIT_LOCK(vatpit);
  160 
  161         if (callout_pending(callout))           /* callout was reset */
  162                 goto done;
  163 
  164         if (!callout_active(callout))           /* callout was stopped */
  165                 goto done;
  166 
  167         callout_deactivate(callout);
  168 
  169         if (c->mode == TIMER_RATEGEN) {
  170                 pit_timer_start_cntr0(vatpit);
  171         }
  172 
  173         vatpic_pulse_irq(vatpit->vm, 0);
  174         vioapic_pulse_irq(vatpit->vm, 2);
  175 
  176 done:
  177         VATPIT_UNLOCK(vatpit);
  178         return;
  179 }
  180 
  181 static void
  182 pit_timer_start_cntr0(struct vatpit *vatpit)
  183 {
  184         struct channel *c;
  185         struct bintime now, delta;
  186         sbintime_t precision;
  187 
  188         c = &vatpit->channel[0];
  189         if (c->initial != 0) {
  190                 delta.sec = 0;
  191                 delta.frac = vatpit->freq_bt.frac * c->initial;
  192                 bintime_add(&c->callout_bt, &delta);
  193                 precision = bttosbt(delta) >> tc_precexp;
  194 
  195                 /*
  196                  * Reset 'callout_bt' if the time that the callout
  197                  * was supposed to fire is more than 'c->initial'
  198                  * ticks in the past.
  199                  */
  200                 binuptime(&now);
  201                 if (bintime_cmp(&c->callout_bt, &now, <)) {
  202                         c->callout_bt = now;
  203                         bintime_add(&c->callout_bt, &delta);
  204                 }
  205 
  206                 callout_reset_sbt(&c->callout, bttosbt(c->callout_bt),
  207                     precision, vatpit_callout_handler, &c->callout_arg,
  208                     C_ABSOLUTE);
  209         }
  210 }
  211 
  212 static uint16_t
  213 pit_update_counter(struct vatpit *vatpit, struct channel *c, bool latch)
  214 {
  215         uint16_t lval;
  216         uint64_t delta_ticks;
  217 
  218         /* cannot latch a new value until the old one has been consumed */
  219         if (latch && c->olbyte != 0)
  220                 return (0);
  221 
  222         if (c->initial == 0) {
  223                 /*
  224                  * This is possibly an o/s bug - reading the value of
  225                  * the timer without having set up the initial value.
  226                  *
  227                  * The original user-space version of this code set
  228                  * the timer to 100hz in this condition; do the same
  229                  * here.
  230                  */
  231                 c->initial = TIMER_DIV(PIT_8254_FREQ, 100);
  232                 binuptime(&c->now_bt);
  233                 c->status &= ~TIMER_STS_NULLCNT;
  234         }
  235 
  236         delta_ticks = vatpit_delta_ticks(vatpit, c);
  237         lval = c->initial - delta_ticks % c->initial;
  238 
  239         if (latch) {
  240                 c->olbyte = 2;
  241                 c->ol[1] = lval;                /* LSB */
  242                 c->ol[0] = lval >> 8;           /* MSB */
  243         }
  244 
  245         return (lval);
  246 }
  247 
  248 static int
  249 pit_readback1(struct vatpit *vatpit, int channel, uint8_t cmd)
  250 {
  251         struct channel *c;
  252 
  253         c = &vatpit->channel[channel];
  254 
  255         /*
  256          * Latch the count/status of the timer if not already latched.
  257          * N.B. that the count/status latch-select bits are active-low.
  258          */
  259         if (!(cmd & TIMER_RB_LCTR) && !c->olbyte) {
  260                 (void) pit_update_counter(vatpit, c, true);
  261         }
  262 
  263         if (!(cmd & TIMER_RB_LSTATUS) && !c->slatched) {
  264                 c->slatched = true;
  265                 /*
  266                  * For mode 0, see if the elapsed time is greater
  267                  * than the initial value - this results in the
  268                  * output pin being set to 1 in the status byte.
  269                  */
  270                 if (c->mode == TIMER_INTTC && vatpit_get_out(vatpit, channel))
  271                         c->status |= TIMER_STS_OUT;
  272                 else
  273                         c->status &= ~TIMER_STS_OUT;
  274         }
  275 
  276         return (0);
  277 }
  278 
  279 static int
  280 pit_readback(struct vatpit *vatpit, uint8_t cmd)
  281 {
  282         int error;
  283 
  284         /*
  285          * The readback command can apply to all timers.
  286          */
  287         error = 0;
  288         if (cmd & TIMER_RB_CTR_0)
  289                 error = pit_readback1(vatpit, 0, cmd);
  290         if (!error && cmd & TIMER_RB_CTR_1)
  291                 error = pit_readback1(vatpit, 1, cmd);
  292         if (!error && cmd & TIMER_RB_CTR_2)
  293                 error = pit_readback1(vatpit, 2, cmd);
  294 
  295         return (error);
  296 }
  297 
  298 static int
  299 vatpit_update_mode(struct vatpit *vatpit, uint8_t val)
  300 {
  301         struct channel *c;
  302         int sel, rw, mode;
  303 
  304         sel = val & TIMER_SEL_MASK;
  305         rw = val & TIMER_RW_MASK;
  306         mode = val & TIMER_MODE_MASK;
  307 
  308         if (sel == TIMER_SEL_READBACK)
  309                 return (pit_readback(vatpit, val));
  310 
  311         if (rw != TIMER_LATCH && rw != TIMER_16BIT)
  312                 return (-1);
  313 
  314         if (rw != TIMER_LATCH) {
  315                 /*
  316                  * Counter mode is not affected when issuing a
  317                  * latch command.
  318                  */
  319                 if (mode != TIMER_INTTC &&
  320                     mode != TIMER_RATEGEN &&
  321                     mode != TIMER_SQWAVE &&
  322                     mode != TIMER_SWSTROBE)
  323                         return (-1);
  324         }
  325 
  326         c = &vatpit->channel[sel >> 6];
  327         if (rw == TIMER_LATCH)
  328                 pit_update_counter(vatpit, c, true);
  329         else {
  330                 c->mode = mode;
  331                 c->olbyte = 0;  /* reset latch after reprogramming */
  332                 c->status |= TIMER_STS_NULLCNT;
  333         }
  334 
  335         return (0);
  336 }
  337 
  338 int
  339 vatpit_handler(struct vm *vm, bool in, int port, int bytes, uint32_t *eax)
  340 {
  341         struct vatpit *vatpit;
  342         struct channel *c;
  343         uint8_t val;
  344         int error;
  345 
  346         vatpit = vm_atpit(vm);
  347 
  348         if (bytes != 1)
  349                 return (-1);
  350 
  351         val = *eax;
  352 
  353         if (port == TIMER_MODE) {
  354                 if (in) {
  355                         VM_CTR0(vatpit->vm, "vatpit attempt to read mode");
  356                         return (-1);
  357                 }
  358 
  359                 VATPIT_LOCK(vatpit);
  360                 error = vatpit_update_mode(vatpit, val);
  361                 VATPIT_UNLOCK(vatpit);
  362 
  363                 return (error);
  364         }
  365 
  366         /* counter ports */
  367         KASSERT(port >= TIMER_CNTR0 && port <= TIMER_CNTR2,
  368             ("invalid port 0x%x", port));
  369         c = &vatpit->channel[port - TIMER_CNTR0];
  370 
  371         VATPIT_LOCK(vatpit);
  372         if (in && c->slatched) {
  373                 /*
  374                  * Return the status byte if latched
  375                  */
  376                 *eax = c->status;
  377                 c->slatched = false;
  378                 c->status = 0;
  379         } else if (in) {
  380                 /*
  381                  * The spec says that once the output latch is completely
  382                  * read it should revert to "following" the counter. Use
  383                  * the free running counter for this case (i.e. Linux
  384                  * TSC calibration). Assuming the access mode is 16-bit,
  385                  * toggle the MSB/LSB bit on each read.
  386                  */
  387                 if (c->olbyte == 0) {
  388                         uint16_t tmp;
  389 
  390                         tmp = pit_update_counter(vatpit, c, false);
  391                         if (c->frbyte)
  392                                 tmp >>= 8;
  393                         tmp &= 0xff;
  394                         *eax = tmp;
  395                         c->frbyte ^= 1;
  396                 }  else
  397                         *eax = c->ol[--c->olbyte];
  398         } else {
  399                 c->cr[c->crbyte++] = *eax;
  400                 if (c->crbyte == 2) {
  401                         c->status &= ~TIMER_STS_NULLCNT;
  402                         c->frbyte = 0;
  403                         c->crbyte = 0;
  404                         c->initial = c->cr[0] | (uint16_t)c->cr[1] << 8;
  405                         binuptime(&c->now_bt);
  406                         /* Start an interval timer for channel 0 */
  407                         if (port == TIMER_CNTR0) {
  408                                 c->callout_bt = c->now_bt;
  409                                 pit_timer_start_cntr0(vatpit);
  410                         }
  411                         if (c->initial == 0)
  412                                 c->initial = 0xffff;
  413                 }
  414         }
  415         VATPIT_UNLOCK(vatpit);
  416 
  417         return (0);
  418 }
  419 
  420 int
  421 vatpit_nmisc_handler(struct vm *vm, bool in, int port, int bytes,
  422     uint32_t *eax)
  423 {
  424         struct vatpit *vatpit;
  425 
  426         vatpit = vm_atpit(vm);
  427 
  428         if (in) {
  429                         VATPIT_LOCK(vatpit);
  430                         if (vatpit_get_out(vatpit, 2))
  431                                 *eax = TMR2_OUT_STS;
  432                         else
  433                                 *eax = 0;
  434 
  435                         VATPIT_UNLOCK(vatpit);
  436         }
  437 
  438         return (0);
  439 }
  440 
  441 struct vatpit *
  442 vatpit_init(struct vm *vm)
  443 {
  444         struct vatpit *vatpit;
  445         struct vatpit_callout_arg *arg;
  446         int i;
  447 
  448         vatpit = malloc(sizeof(struct vatpit), M_VATPIT, M_WAITOK | M_ZERO);
  449         vatpit->vm = vm;
  450 
  451         mtx_init(&vatpit->mtx, "vatpit lock", NULL, MTX_SPIN);
  452 
  453         FREQ2BT(PIT_8254_FREQ, &vatpit->freq_bt);
  454 
  455         for (i = 0; i < 3; i++) {
  456                 callout_init(&vatpit->channel[i].callout, 1);
  457                 arg = &vatpit->channel[i].callout_arg;
  458                 arg->vatpit = vatpit;
  459                 arg->channel_num = i;
  460         }
  461 
  462         return (vatpit);
  463 }
  464 
  465 void
  466 vatpit_cleanup(struct vatpit *vatpit)
  467 {
  468         int i;
  469 
  470         for (i = 0; i < 3; i++)
  471                 callout_drain(&vatpit->channel[i].callout);
  472 
  473         mtx_destroy(&vatpit->mtx);
  474         free(vatpit, M_VATPIT);
  475 }
  476 
  477 #ifdef BHYVE_SNAPSHOT
  478 int
  479 vatpit_snapshot(struct vatpit *vatpit, struct vm_snapshot_meta *meta)
  480 {
  481         int ret;
  482         int i;
  483         struct channel *channel;
  484 
  485         SNAPSHOT_VAR_OR_LEAVE(vatpit->freq_bt.sec, meta, ret, done);
  486         SNAPSHOT_VAR_OR_LEAVE(vatpit->freq_bt.frac, meta, ret, done);
  487 
  488         /* properly restore timers; they will NOT work currently */
  489         printf("%s: snapshot restore does not reset timers!\r\n", __func__);
  490 
  491         for (i = 0; i < nitems(vatpit->channel); i++) {
  492                 channel = &vatpit->channel[i];
  493 
  494                 SNAPSHOT_VAR_OR_LEAVE(channel->mode, meta, ret, done);
  495                 SNAPSHOT_VAR_OR_LEAVE(channel->initial, meta, ret, done);
  496                 SNAPSHOT_VAR_OR_LEAVE(channel->now_bt.sec, meta, ret, done);
  497                 SNAPSHOT_VAR_OR_LEAVE(channel->now_bt.frac, meta, ret, done);
  498                 SNAPSHOT_BUF_OR_LEAVE(channel->cr, sizeof(channel->cr),
  499                         meta, ret, done);
  500                 SNAPSHOT_BUF_OR_LEAVE(channel->ol, sizeof(channel->ol),
  501                         meta, ret, done);
  502                 SNAPSHOT_VAR_OR_LEAVE(channel->slatched, meta, ret, done);
  503                 SNAPSHOT_VAR_OR_LEAVE(channel->status, meta, ret, done);
  504                 SNAPSHOT_VAR_OR_LEAVE(channel->crbyte, meta, ret, done);
  505                 SNAPSHOT_VAR_OR_LEAVE(channel->frbyte, meta, ret, done);
  506                 SNAPSHOT_VAR_OR_LEAVE(channel->callout_bt.sec, meta, ret, done);
  507                 SNAPSHOT_VAR_OR_LEAVE(channel->callout_bt.frac, meta, ret,
  508                         done);
  509         }
  510 
  511 done:
  512         return (ret);
  513 }
  514 #endif

Cache object: 845ad542721161907e5699dc6c9f5600


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