]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - usr.sbin/bhyve/pit_8254.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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 "ioapic.h"
48 #include "mevent.h"
49 #include "pit_8254.h"
50
51 #define TIMER_SEL_MASK          0xc0
52 #define TIMER_RW_MASK           0x30
53 #define TIMER_MODE_MASK         0x0f
54 #define TIMER_SEL_READBACK      0xc0
55
56 #define TIMER_DIV(freq, hz)     (((freq) + (hz) / 2) / (hz))
57
58 #define PIT_8254_FREQ           1193182
59 static const int nsecs_per_tick = 1000000000 / PIT_8254_FREQ;
60
61 struct counter {
62         struct vmctx    *ctx;
63         struct mevent   *tevp;  
64         struct timeval  tv;             /* uptime when counter was loaded */
65         int             mode;
66         uint16_t        initial;        /* initial counter value */
67         uint8_t         cr[2];
68         uint8_t         ol[2];
69         int             crbyte;
70         int             olbyte;
71         int             frbyte;
72 };
73
74
75 static void
76 timevalfix(struct timeval *t1)
77 {
78
79         if (t1->tv_usec < 0) {
80                 t1->tv_sec--;
81                 t1->tv_usec += 1000000;
82         }
83         if (t1->tv_usec >= 1000000) {
84                 t1->tv_sec++;
85                 t1->tv_usec -= 1000000;
86         }
87 }
88
89 static void
90 timevalsub(struct timeval *t1, const struct timeval *t2)
91 {
92
93         t1->tv_sec -= t2->tv_sec;
94         t1->tv_usec -= t2->tv_usec;
95         timevalfix(t1);
96 }
97
98 static uint64_t pit_mev_count;
99
100 static void
101 pit_mevent_cb(int fd, enum ev_type type, void *param)
102 {
103         struct counter *c;
104
105         c = param;
106
107         pit_mev_count++;
108
109         ioapic_assert_pin(c->ctx, 0);
110         ioapic_deassert_pin(c->ctx, 0);
111
112         /*
113          * Delete the timer for one-shots
114          */
115         if (c->mode != TIMER_RATEGEN) {
116                 mevent_delete(c->tevp);
117                 c->tevp = NULL;
118         }
119 }
120
121 static void
122 pit_timer_start(struct vmctx *ctx, struct counter *c)
123 {
124         int msecs;
125
126         if (c->initial != 0) {
127                 msecs = c->initial * nsecs_per_tick / 1000000;
128                 if (msecs == 0)
129                         msecs = 1;
130
131                 if (c->tevp == NULL)
132                         c->tevp = mevent_add(msecs, EVF_TIMER, pit_mevent_cb,
133                             c);
134         }
135 }
136
137 static uint16_t
138 pit_update_counter(struct counter *c, int latch)
139 {
140         struct timeval tv2;
141         uint16_t lval;
142         uint64_t delta_nsecs, delta_ticks;
143
144         /* cannot latch a new value until the old one has been consumed */
145         if (latch && c->olbyte != 0)
146                 return (0);
147
148         if (c->initial == 0 || c->initial == 1) {
149                 /*
150                  * XXX the program that runs the VM can be stopped and
151                  * restarted at any time. This means that state that was
152                  * created by the guest is destroyed between invocations
153                  * of the program.
154                  *
155                  * If the counter's initial value is not programmed we
156                  * assume a value that would be set to generate 100
157                  * interrupts per second.
158                  */
159                 c->initial = TIMER_DIV(PIT_8254_FREQ, 100);
160                 gettimeofday(&c->tv, NULL);
161         }
162         
163         (void)gettimeofday(&tv2, NULL);
164         timevalsub(&tv2, &c->tv);
165         delta_nsecs = tv2.tv_sec * 1000000000 + tv2.tv_usec * 1000;
166         delta_ticks = delta_nsecs / nsecs_per_tick;
167
168         lval = c->initial - delta_ticks % c->initial;
169
170         if (latch) {
171                 c->olbyte = 2;
172                 c->ol[1] = lval;                /* LSB */
173                 c->ol[0] = lval >> 8;           /* MSB */
174         }
175
176         return (lval);
177 }
178
179 static int
180 pit_8254_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
181                  uint32_t *eax, void *arg)
182 {
183         int sel, rw, mode;
184         uint8_t val;
185         struct counter *c;
186
187         static struct counter counter[3];
188
189         if (bytes != 1)
190                 return (-1);
191
192         val = *eax;
193
194         if (port == TIMER_MODE) {
195                 assert(in == 0);
196                 sel = val & TIMER_SEL_MASK;
197                 rw = val & TIMER_RW_MASK;
198                 mode = val & TIMER_MODE_MASK;
199
200                 if (sel == TIMER_SEL_READBACK)
201                         return (-1);
202                 if (rw != TIMER_LATCH && rw != TIMER_16BIT)
203                         return (-1);
204
205                 if (rw != TIMER_LATCH) {
206                         /*
207                          * Counter mode is not affected when issuing a
208                          * latch command.
209                          */
210                         if (mode != TIMER_INTTC &&
211                             mode != TIMER_RATEGEN &&
212                             mode != TIMER_SQWAVE &&
213                             mode != TIMER_SWSTROBE)
214                                 return (-1);
215                 }
216                 
217                 c = &counter[sel >> 6];
218                 c->ctx = ctx;
219                 c->mode = mode;
220                 if (rw == TIMER_LATCH)
221                         pit_update_counter(c, 1);
222                 else
223                         c->olbyte = 0;  /* reset latch after reprogramming */
224                 
225                 return (0);
226         }
227
228         /* counter ports */
229         assert(port >= TIMER_CNTR0 && port <= TIMER_CNTR2);
230         c = &counter[port - TIMER_CNTR0];
231
232         if (in) {
233                 /*
234                  * The spec says that once the output latch is completely
235                  * read it should revert to "following" the counter. Use
236                  * the free running counter for this case (i.e. Linux
237                  * TSC calibration). Assuming the access mode is 16-bit,
238                  * toggle the MSB/LSB bit on each read.
239                  */
240                 if (c->olbyte == 0) {
241                         uint16_t tmp;
242
243                         tmp = pit_update_counter(c, 0);
244                         if (c->frbyte)
245                                 tmp >>= 8;
246                         tmp &= 0xff;
247                         *eax = tmp;
248                         c->frbyte ^= 1;
249                 }  else
250                         *eax = c->ol[--c->olbyte];
251         } else {
252                 c->cr[c->crbyte++] = *eax;
253                 if (c->crbyte == 2) {
254                         c->frbyte = 0;
255                         c->crbyte = 0;
256                         c->initial = c->cr[0] | (uint16_t)c->cr[1] << 8;
257                         /* Start an interval timer for counter 0 */
258                         if (port == 0x40)
259                                 pit_timer_start(ctx, c);
260                         if (c->initial == 0)
261                                 c->initial = 0xffff;
262                         gettimeofday(&c->tv, NULL);
263                 }
264         }
265
266         return (0);
267 }
268
269 INOUT_PORT(8254, TIMER_MODE, IOPORT_F_OUT, pit_8254_handler);
270 INOUT_PORT(8254, TIMER_CNTR0, IOPORT_F_INOUT, pit_8254_handler);
271 INOUT_PORT(8254, TIMER_CNTR1, IOPORT_F_INOUT, pit_8254_handler);
272 INOUT_PORT(8254, TIMER_CNTR2, IOPORT_F_INOUT, pit_8254_handler);