]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - usr.sbin/bhyve/pit_8254.c
MFC 257422,257661,258075,258476,258494,258579,258609,258699:
[FreeBSD/stable/10.git] / usr.sbin / bhyve / pit_8254.c
1 /*-
2  * Copyright (c) 2011 NetApp, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/types.h>
33 #include <sys/time.h>
34 #include <machine/vmm.h>
35
36 #include <machine/clock.h>
37
38 #include <assert.h>
39 #include <stdio.h>
40 #include <string.h>
41 #include <unistd.h>
42
43 #include <vmmapi.h>
44
45 #include "bhyverun.h"
46 #include "inout.h"
47 #include "mevent.h"
48 #include "pit_8254.h"
49
50 #define TIMER_SEL_MASK          0xc0
51 #define TIMER_RW_MASK           0x30
52 #define TIMER_MODE_MASK         0x0f
53 #define TIMER_SEL_READBACK      0xc0
54
55 #define TIMER_DIV(freq, hz)     (((freq) + (hz) / 2) / (hz))
56
57 #define PIT_8254_FREQ           1193182
58 static const int nsecs_per_tick = 1000000000 / PIT_8254_FREQ;
59
60 struct counter {
61         struct vmctx    *ctx;
62         struct mevent   *tevp;  
63         struct timeval  tv;             /* uptime when counter was loaded */
64         int             mode;
65         uint16_t        initial;        /* initial counter value */
66         uint8_t         cr[2];
67         uint8_t         ol[2];
68         int             crbyte;
69         int             olbyte;
70         int             frbyte;
71 };
72
73
74 static void
75 timevalfix(struct timeval *t1)
76 {
77
78         if (t1->tv_usec < 0) {
79                 t1->tv_sec--;
80                 t1->tv_usec += 1000000;
81         }
82         if (t1->tv_usec >= 1000000) {
83                 t1->tv_sec++;
84                 t1->tv_usec -= 1000000;
85         }
86 }
87
88 static void
89 timevalsub(struct timeval *t1, const struct timeval *t2)
90 {
91
92         t1->tv_sec -= t2->tv_sec;
93         t1->tv_usec -= t2->tv_usec;
94         timevalfix(t1);
95 }
96
97 static uint64_t pit_mev_count;
98
99 static void
100 pit_mevent_cb(int fd, enum ev_type type, void *param)
101 {
102         struct counter *c;
103
104         c = param;
105
106         pit_mev_count++;
107
108         vm_ioapic_pulse_irq(c->ctx, 2);
109
110         /*
111          * Delete the timer for one-shots
112          */
113         if (c->mode != TIMER_RATEGEN) {
114                 mevent_delete(c->tevp);
115                 c->tevp = NULL;
116         }
117 }
118
119 static void
120 pit_timer_start(struct vmctx *ctx, struct counter *c)
121 {
122         int msecs;
123
124         if (c->initial != 0) {
125                 msecs = c->initial * nsecs_per_tick / 1000000;
126                 if (msecs == 0)
127                         msecs = 1;
128
129                 if (c->tevp == NULL)
130                         c->tevp = mevent_add(msecs, EVF_TIMER, pit_mevent_cb,
131                             c);
132         }
133 }
134
135 static uint16_t
136 pit_update_counter(struct counter *c, int latch)
137 {
138         struct timeval tv2;
139         uint16_t lval;
140         uint64_t delta_nsecs, delta_ticks;
141
142         /* cannot latch a new value until the old one has been consumed */
143         if (latch && c->olbyte != 0)
144                 return (0);
145
146         if (c->initial == 0 || c->initial == 1) {
147                 /*
148                  * XXX the program that runs the VM can be stopped and
149                  * restarted at any time. This means that state that was
150                  * created by the guest is destroyed between invocations
151                  * of the program.
152                  *
153                  * If the counter's initial value is not programmed we
154                  * assume a value that would be set to generate 100
155                  * interrupts per second.
156                  */
157                 c->initial = TIMER_DIV(PIT_8254_FREQ, 100);
158                 gettimeofday(&c->tv, NULL);
159         }
160         
161         (void)gettimeofday(&tv2, NULL);
162         timevalsub(&tv2, &c->tv);
163         delta_nsecs = tv2.tv_sec * 1000000000 + tv2.tv_usec * 1000;
164         delta_ticks = delta_nsecs / nsecs_per_tick;
165
166         lval = c->initial - delta_ticks % c->initial;
167
168         if (latch) {
169                 c->olbyte = 2;
170                 c->ol[1] = lval;                /* LSB */
171                 c->ol[0] = lval >> 8;           /* MSB */
172         }
173
174         return (lval);
175 }
176
177 static int
178 pit_8254_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
179                  uint32_t *eax, void *arg)
180 {
181         int sel, rw, mode;
182         uint8_t val;
183         struct counter *c;
184
185         static struct counter counter[3];
186
187         if (bytes != 1)
188                 return (-1);
189
190         val = *eax;
191
192         if (port == TIMER_MODE) {
193                 assert(in == 0);
194                 sel = val & TIMER_SEL_MASK;
195                 rw = val & TIMER_RW_MASK;
196                 mode = val & TIMER_MODE_MASK;
197
198                 if (sel == TIMER_SEL_READBACK)
199                         return (-1);
200                 if (rw != TIMER_LATCH && rw != TIMER_16BIT)
201                         return (-1);
202
203                 if (rw != TIMER_LATCH) {
204                         /*
205                          * Counter mode is not affected when issuing a
206                          * latch command.
207                          */
208                         if (mode != TIMER_INTTC &&
209                             mode != TIMER_RATEGEN &&
210                             mode != TIMER_SQWAVE &&
211                             mode != TIMER_SWSTROBE)
212                                 return (-1);
213                 }
214                 
215                 c = &counter[sel >> 6];
216                 c->ctx = ctx;
217                 c->mode = mode;
218                 if (rw == TIMER_LATCH)
219                         pit_update_counter(c, 1);
220                 else
221                         c->olbyte = 0;  /* reset latch after reprogramming */
222                 
223                 return (0);
224         }
225
226         /* counter ports */
227         assert(port >= TIMER_CNTR0 && port <= TIMER_CNTR2);
228         c = &counter[port - TIMER_CNTR0];
229
230         if (in) {
231                 /*
232                  * The spec says that once the output latch is completely
233                  * read it should revert to "following" the counter. Use
234                  * the free running counter for this case (i.e. Linux
235                  * TSC calibration). Assuming the access mode is 16-bit,
236                  * toggle the MSB/LSB bit on each read.
237                  */
238                 if (c->olbyte == 0) {
239                         uint16_t tmp;
240
241                         tmp = pit_update_counter(c, 0);
242                         if (c->frbyte)
243                                 tmp >>= 8;
244                         tmp &= 0xff;
245                         *eax = tmp;
246                         c->frbyte ^= 1;
247                 }  else
248                         *eax = c->ol[--c->olbyte];
249         } else {
250                 c->cr[c->crbyte++] = *eax;
251                 if (c->crbyte == 2) {
252                         c->frbyte = 0;
253                         c->crbyte = 0;
254                         c->initial = c->cr[0] | (uint16_t)c->cr[1] << 8;
255                         /* Start an interval timer for counter 0 */
256                         if (port == 0x40)
257                                 pit_timer_start(ctx, c);
258                         if (c->initial == 0)
259                                 c->initial = 0xffff;
260                         gettimeofday(&c->tv, NULL);
261                 }
262         }
263
264         return (0);
265 }
266
267 INOUT_PORT(8254, TIMER_MODE, IOPORT_F_OUT, pit_8254_handler);
268 INOUT_PORT(8254, TIMER_CNTR0, IOPORT_F_INOUT, pit_8254_handler);
269 INOUT_PORT(8254, TIMER_CNTR1, IOPORT_F_INOUT, pit_8254_handler);
270 INOUT_PORT(8254, TIMER_CNTR2, IOPORT_F_INOUT, pit_8254_handler);