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