]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/pit_8254.c
MFC
[FreeBSD/FreeBSD.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/time.h>
33
34 #include <machine/clock.h>
35
36 #include <stdio.h>
37 #include <assert.h>
38
39 #include "bhyverun.h"
40 #include "inout.h"
41 #include "pit_8254.h"
42
43 #define TIMER_SEL_MASK          0xc0
44 #define TIMER_RW_MASK           0x30
45 #define TIMER_MODE_MASK         0x0f
46 #define TIMER_SEL_READBACK      0xc0
47
48 #define TIMER_DIV(freq, hz)     (((freq) + (hz) / 2) / (hz))
49
50 #define PIT_8254_FREQ           1193182
51 static const int nsecs_per_tick = 1000000000 / PIT_8254_FREQ;
52
53 struct counter {
54         struct timeval  tv;             /* uptime when counter was loaded */
55         uint16_t        initial;        /* initial counter value */
56         uint8_t         cr[2];
57         uint8_t         ol[2];
58         int             crbyte;
59         int             olbyte;
60 };
61
62 static void
63 timevalfix(struct timeval *t1)
64 {
65
66         if (t1->tv_usec < 0) {
67                 t1->tv_sec--;
68                 t1->tv_usec += 1000000;
69         }
70         if (t1->tv_usec >= 1000000) {
71                 t1->tv_sec++;
72                 t1->tv_usec -= 1000000;
73         }
74 }
75
76 static void
77 timevalsub(struct timeval *t1, const struct timeval *t2)
78 {
79
80         t1->tv_sec -= t2->tv_sec;
81         t1->tv_usec -= t2->tv_usec;
82         timevalfix(t1);
83 }
84
85 static void
86 latch(struct counter *c)
87 {
88         struct timeval tv2;
89         uint16_t lval;
90         uint64_t delta_nsecs, delta_ticks;
91
92         /* cannot latch a new value until the old one has been consumed */
93         if (c->olbyte != 0)
94                 return;
95
96         if (c->initial == 0 || c->initial == 1) {
97                 /*
98                  * XXX the program that runs the VM can be stopped and
99                  * restarted at any time. This means that state that was
100                  * created by the guest is destroyed between invocations
101                  * of the program.
102                  *
103                  * If the counter's initial value is not programmed we
104                  * assume a value that would be set to generate 'guest_hz'
105                  * interrupts per second.
106                  */
107                 c->initial = TIMER_DIV(PIT_8254_FREQ, guest_hz);
108                 gettimeofday(&c->tv, NULL);
109         }
110         
111         (void)gettimeofday(&tv2, NULL);
112         timevalsub(&tv2, &c->tv);
113         delta_nsecs = tv2.tv_sec * 1000000000 + tv2.tv_usec * 1000;
114         delta_ticks = delta_nsecs / nsecs_per_tick;
115
116         lval = c->initial - delta_ticks % c->initial;
117         c->olbyte = 2;
118         c->ol[1] = lval;                /* LSB */
119         c->ol[0] = lval >> 8;           /* MSB */
120 }
121
122 static int
123 pit_8254_handler(struct vmctx *ctx, int vcpu, int in, int port, int bytes,
124                  uint32_t *eax, void *arg)
125 {
126         int sel, rw, mode;
127         uint8_t val;
128         struct counter *c;
129
130         static struct counter counter[3];
131
132         if (bytes != 1)
133                 return (-1);
134
135         val = *eax;
136
137         if (port == TIMER_MODE) {
138                 assert(in == 0);
139                 sel = val & TIMER_SEL_MASK;
140                 rw = val & TIMER_RW_MASK;
141                 mode = val & TIMER_MODE_MASK;
142
143                 if (sel == TIMER_SEL_READBACK)
144                         return (-1);
145                 if (rw != TIMER_LATCH && rw != TIMER_16BIT)
146                         return (-1);
147
148                 if (rw != TIMER_LATCH) {
149                         /*
150                          * Counter mode is not affected when issuing a
151                          * latch command.
152                          */
153                         if (mode != TIMER_RATEGEN && mode != TIMER_SQWAVE)
154                                 return (-1);
155                 }
156                 
157                 c = &counter[sel >> 6];
158                 if (rw == TIMER_LATCH)
159                         latch(c);
160                 else
161                         c->olbyte = 0;  /* reset latch after reprogramming */
162                 
163                 return (0);
164         }
165
166         /* counter ports */
167         assert(port >= TIMER_CNTR0 && port <= TIMER_CNTR2);
168         c = &counter[port - TIMER_CNTR0];
169
170         if (in) {
171                 /*
172                  * XXX
173                  * The spec says that once the output latch is completely
174                  * read it should revert to "following" the counter. We don't
175                  * do this because it is hard and any reasonable OS should
176                  * always latch the counter before trying to read it.
177                  */
178                 if (c->olbyte == 0)
179                         c->olbyte = 2;
180                 *eax = c->ol[--c->olbyte];
181         } else {
182                 c->cr[c->crbyte++] = *eax;
183                 if (c->crbyte == 2) {
184                         c->crbyte = 0;
185                         c->initial = c->cr[0] | (uint16_t)c->cr[1] << 8;
186                         if (c->initial == 0)
187                                 c->initial = 0xffff;
188                         gettimeofday(&c->tv, NULL);
189                 }
190         }
191
192         return (0);
193 }
194
195 INOUT_PORT(8254, TIMER_MODE, IOPORT_F_OUT, pit_8254_handler);
196 INOUT_PORT(8254, TIMER_CNTR0, IOPORT_F_INOUT, pit_8254_handler);
197 INOUT_PORT(8254, TIMER_CNTR1, IOPORT_F_INOUT, pit_8254_handler);
198 INOUT_PORT(8254, TIMER_CNTR2, IOPORT_F_INOUT, pit_8254_handler);