GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/vmd/i8253.c Lines: 0 175 0.0 %
Date: 2017-11-07 Branches: 0 68 0.0 %

Line Branch Exec Source
1
/* $OpenBSD: i8253.c,v 1.17 2017/08/14 19:46:44 jasper Exp $ */
2
/*
3
 * Copyright (c) 2016 Mike Larkin <mlarkin@openbsd.org>
4
 *
5
 * Permission to use, copy, modify, and distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 */
17
18
#include <sys/time.h>
19
#include <sys/types.h>
20
21
#include <dev/ic/i8253reg.h>
22
23
#include <machine/vmmvar.h>
24
25
#include <event.h>
26
#include <string.h>
27
#include <stddef.h>
28
#include <unistd.h>
29
30
#include "i8253.h"
31
#include "proc.h"
32
#include "vmm.h"
33
#include "atomicio.h"
34
35
extern char *__progname;
36
37
/*
38
 * Channel 0 is used to generate the legacy hardclock interrupt (HZ).
39
 * Channels 1 and 2 are not connected to any output (although someone
40
 * could hook channel 2 up to an emulated pcppi(4) at some point).
41
 */
42
struct i8253_channel i8253_channel[3];
43
44
/*
45
 * i8253_init
46
 *
47
 * Initialize the emulated i8253 PIT.
48
 *
49
 * Parameters:
50
 *  vm_id: vmm(4)-assigned ID of the VM
51
 */
52
void
53
i8253_init(uint32_t vm_id)
54
{
55
	memset(&i8253_channel, 0, sizeof(struct i8253_channel));
56
	gettimeofday(&i8253_channel[0].tv, NULL);
57
	i8253_channel[0].start = 0xFFFF;
58
	i8253_channel[0].mode = TIMER_INTTC;
59
	i8253_channel[0].last_r = 1;
60
	i8253_channel[0].vm_id = vm_id;
61
62
	i8253_channel[1].start = 0xFFFF;
63
	i8253_channel[1].mode = TIMER_INTTC;
64
	i8253_channel[1].last_r = 1;
65
	i8253_channel[1].vm_id = vm_id;
66
67
	i8253_channel[2].start = 0xFFFF;
68
	i8253_channel[2].mode = TIMER_INTTC;
69
	i8253_channel[2].last_r = 1;
70
	i8253_channel[2].vm_id = vm_id;
71
72
	evtimer_set(&i8253_channel[0].timer, i8253_fire, &i8253_channel[0]);
73
	evtimer_set(&i8253_channel[1].timer, i8253_fire, &i8253_channel[1]);
74
	evtimer_set(&i8253_channel[2].timer, i8253_fire, &i8253_channel[2]);
75
}
76
77
/*
78
 * i8253_do_readback
79
 *
80
 * Handles the readback status command. The readback status command latches
81
 * the current counter value plus various status bits.
82
 *
83
 * Parameters:
84
 *  data: The command word written by the guest VM
85
 */
86
void
87
i8253_do_readback(uint32_t data)
88
{
89
	struct timeval now, delta;
90
	uint64_t ns, ticks;
91
92
	/* bits are inverted here - !TIMER_RB_STATUS == enable chan readback */
93
	if (data & ~TIMER_RB_STATUS) {
94
		i8253_channel[0].rbs = (data & TIMER_RB_C0) ? 1 : 0;
95
		i8253_channel[1].rbs = (data & TIMER_RB_C1) ? 1 : 0;
96
		i8253_channel[2].rbs = (data & TIMER_RB_C2) ? 1 : 0;
97
	}
98
99
	/* !TIMER_RB_COUNT == enable counter readback */
100
	if (data & ~TIMER_RB_COUNT) {
101
		if (data & TIMER_RB_C0) {
102
			gettimeofday(&now, NULL);
103
			delta.tv_sec = now.tv_sec - i8253_channel[0].tv.tv_sec;
104
			delta.tv_usec = now.tv_usec -
105
			    i8253_channel[0].tv.tv_usec;
106
			if (delta.tv_usec < 0) {
107
				delta.tv_sec--;
108
				delta.tv_usec += 1000000;
109
			}
110
			if (delta.tv_usec > 1000000) {
111
				delta.tv_sec++;
112
				delta.tv_usec -= 1000000;
113
			}
114
			ns = delta.tv_usec * 1000 + delta.tv_sec * 1000000000;
115
			ticks = ns / NS_PER_TICK;
116
			if (i8253_channel[0].start)
117
				i8253_channel[0].olatch =
118
				    i8253_channel[0].start -
119
				    ticks % i8253_channel[0].start;
120
			else
121
				i8253_channel[0].olatch = 0;
122
		}
123
124
		if (data & TIMER_RB_C1) {
125
			gettimeofday(&now, NULL);
126
			delta.tv_sec = now.tv_sec - i8253_channel[1].tv.tv_sec;
127
			delta.tv_usec = now.tv_usec -
128
			    i8253_channel[1].tv.tv_usec;
129
			if (delta.tv_usec < 0) {
130
				delta.tv_sec--;
131
				delta.tv_usec += 1000000;
132
			}
133
			if (delta.tv_usec > 1000000) {
134
				delta.tv_sec++;
135
				delta.tv_usec -= 1000000;
136
			}
137
			ns = delta.tv_usec * 1000 + delta.tv_sec * 1000000000;
138
			ticks = ns / NS_PER_TICK;
139
			if (i8253_channel[1].start)
140
				i8253_channel[1].olatch =
141
				    i8253_channel[1].start -
142
				    ticks % i8253_channel[1].start;
143
			else
144
				i8253_channel[1].olatch = 0;
145
		}
146
147
		if (data & TIMER_RB_C2) {
148
			gettimeofday(&now, NULL);
149
			delta.tv_sec = now.tv_sec - i8253_channel[2].tv.tv_sec;
150
			delta.tv_usec = now.tv_usec -
151
			    i8253_channel[2].tv.tv_usec;
152
			if (delta.tv_usec < 0) {
153
				delta.tv_sec--;
154
				delta.tv_usec += 1000000;
155
			}
156
			if (delta.tv_usec > 1000000) {
157
				delta.tv_sec++;
158
				delta.tv_usec -= 1000000;
159
			}
160
			ns = delta.tv_usec * 1000 + delta.tv_sec * 1000000000;
161
			ticks = ns / NS_PER_TICK;
162
			if (i8253_channel[2].start)
163
				i8253_channel[2].olatch =
164
				    i8253_channel[2].start -
165
				    ticks % i8253_channel[2].start;
166
			else
167
				i8253_channel[2].olatch = 0;
168
		}
169
	}
170
}
171
172
/*
173
 * vcpu_exit_i8253
174
 *
175
 * Handles emulated i8253 PIT access (in/out instruction to PIT ports).
176
 *
177
 * Parameters:
178
 *  vrp: vm run parameters containing exit information for the I/O
179
 *      instruction being performed
180
 *
181
 * Return value:
182
 *  Interrupt to inject to the guest VM, or 0xFF if no interrupt should
183
 *      be injected.
184
 */
185
uint8_t
186
vcpu_exit_i8253(struct vm_run_params *vrp)
187
{
188
	uint32_t out_data;
189
	uint8_t sel, rw, data, mode;
190
	uint64_t ns, ticks;
191
	struct timeval now, delta;
192
	union vm_exit *vei = vrp->vrp_exit;
193
194
	get_input_data(vei, &out_data);
195
196
	if (vei->vei.vei_port == TIMER_CTRL) {
197
		if (vei->vei.vei_dir == VEI_DIR_OUT) { /* OUT instruction */
198
			sel = out_data &
199
			    (TIMER_SEL0 | TIMER_SEL1 | TIMER_SEL2);
200
			sel = sel >> 6;
201
202
			if (sel == 3) {
203
				i8253_do_readback(out_data);
204
				return (0xFF);
205
			}
206
207
			rw = out_data & (TIMER_LATCH | TIMER_16BIT);
208
209
			/*
210
			 * Since we don't truly emulate each tick of the PIT
211
			 * counter, when the guest asks for the timer to be
212
			 * latched, simulate what the counter would have been
213
			 * had we performed full emulation. We do this by
214
			 * calculating when the counter was reset vs how much
215
			 * time has elapsed, then bias by the counter tick
216
			 * rate.
217
			 */
218
			if (rw == TIMER_LATCH) {
219
				gettimeofday(&now, NULL);
220
				delta.tv_sec = now.tv_sec -
221
				    i8253_channel[sel].tv.tv_sec;
222
				delta.tv_usec = now.tv_usec -
223
				    i8253_channel[sel].tv.tv_usec;
224
				if (delta.tv_usec < 0) {
225
					delta.tv_sec--;
226
					delta.tv_usec += 1000000;
227
				}
228
				if (delta.tv_usec > 1000000) {
229
					delta.tv_sec++;
230
					delta.tv_usec -= 1000000;
231
				}
232
				ns = delta.tv_usec * 1000 +
233
				    delta.tv_sec * 1000000000;
234
				ticks = ns / NS_PER_TICK;
235
				if (i8253_channel[sel].start) {
236
					i8253_channel[sel].olatch =
237
					    i8253_channel[sel].start -
238
					    ticks % i8253_channel[sel].start;
239
				} else
240
					i8253_channel[sel].olatch = 0;
241
				goto ret;
242
			} else if (rw != TIMER_16BIT) {
243
				log_warnx("%s: i8253 PIT: unsupported counter "
244
				    "%d rw mode 0x%x selected", __func__,
245
				    sel, (rw & TIMER_16BIT));
246
			}
247
248
			goto ret;
249
		} else {
250
			log_warnx("%s: i8253 PIT: read from control port "
251
			    "unsupported", __progname);
252
			set_return_data(vei, 0);
253
		}
254
	} else {
255
		sel = vei->vei.vei_port - (TIMER_CNTR0 + TIMER_BASE);
256
257
		if (vei->vei.vei_dir == VEI_DIR_OUT) { /* OUT instruction */
258
			if (i8253_channel[sel].last_w == 0) {
259
				i8253_channel[sel].ilatch |= (out_data & 0xff);
260
				i8253_channel[sel].last_w = 1;
261
			} else {
262
				i8253_channel[sel].ilatch |= ((out_data & 0xff) << 8);
263
				i8253_channel[sel].start =
264
				    i8253_channel[sel].ilatch;
265
				i8253_channel[sel].last_w = 0;
266
				mode = (out_data & 0xe) >> 1;
267
268
				if (i8253_channel[sel].start == 0)
269
					i8253_channel[sel].start = 0xffff;
270
271
				log_debug("%s: channel %d reset, mode=%d, start=%d", __func__,
272
				    sel, mode, i8253_channel[sel].start);
273
				i8253_channel[sel].mode = mode;
274
				i8253_reset(sel);
275
			}
276
		} else {
277
			if (i8253_channel[sel].rbs) {
278
				i8253_channel[sel].rbs = 0;
279
				data = i8253_channel[sel].mode << 1;
280
				data |= TIMER_16BIT;
281
				set_return_data(vei, data);
282
				goto ret;
283
			}
284
285
			if (i8253_channel[sel].last_r == 0) {
286
				data = i8253_channel[sel].olatch >> 8;
287
				set_return_data(vei, data);
288
				i8253_channel[sel].last_r = 1;
289
			} else {
290
				data = i8253_channel[sel].olatch & 0xFF;
291
				set_return_data(vei, data);
292
				i8253_channel[sel].last_r = 0;
293
			}
294
		}
295
	}
296
297
ret:
298
	return (0xFF);
299
}
300
301
/*
302
 * i8253_reset
303
 *
304
 * Resets the i8253's counter timer
305
 *
306
 * Parameters:
307
 *  chn: counter ID. Only channel ID 0 is presently emulated.
308
 */
309
void
310
i8253_reset(uint8_t chn)
311
{
312
	struct timeval tv;
313
314
	evtimer_del(&i8253_channel[chn].timer);
315
	timerclear(&tv);
316
317
	i8253_channel[chn].in_use = 1;
318
	tv.tv_usec = (i8253_channel[chn].start * NS_PER_TICK) / 1000;
319
	evtimer_add(&i8253_channel[chn].timer, &tv);
320
}
321
322
/*
323
 * i8253_fire
324
 *
325
 * Callback invoked when the 8253 PIT timer fires. This will assert
326
 * IRQ0 on the legacy PIC attached to VCPU0.
327
 *
328
 * Parameters:
329
 *  fd: unused
330
 *  type: unused
331
 *  arg: VM ID
332
 */
333
void
334
i8253_fire(int fd, short type, void *arg)
335
{
336
	struct timeval tv;
337
	struct i8253_channel *ctr = (struct i8253_channel *)arg;
338
339
	timerclear(&tv);
340
	tv.tv_usec = (ctr->start * NS_PER_TICK) / 1000;
341
342
	vcpu_assert_pic_irq(ctr->vm_id, 0, 0);
343
344
	if (ctr->mode != TIMER_INTTC)
345
		evtimer_add(&ctr->timer, &tv);
346
}
347
348
int
349
i8253_dump(int fd)
350
{
351
	log_debug("%s: sending PIT", __func__);
352
	if (atomicio(vwrite, fd, &i8253_channel, sizeof(i8253_channel)) !=
353
	    sizeof(i8253_channel)) {
354
		log_warnx("%s: error writing PIT to fd", __func__);
355
		return (-1);
356
	}
357
	return (0);
358
}
359
360
int
361
i8253_restore(int fd, uint32_t vm_id)
362
{
363
	int i;
364
	log_debug("%s: restoring PIT", __func__);
365
	if (atomicio(read, fd, &i8253_channel, sizeof(i8253_channel)) !=
366
	    sizeof(i8253_channel)) {
367
		log_warnx("%s: error reading PIT from fd", __func__);
368
		return (-1);
369
	}
370
371
	for (i = 0; i < 3; i++) {
372
		memset(&i8253_channel[i].timer, 0, sizeof(struct event));
373
		i8253_channel[i].vm_id = vm_id;
374
		evtimer_set(&i8253_channel[i].timer, i8253_fire,
375
		    &i8253_channel[i]);
376
		i8253_reset(i);
377
	}
378
	return (0);
379
}
380
381
void
382
i8253_stop()
383
{
384
	int i;
385
	for (i = 0; i < 3; i++)
386
		evtimer_del(&i8253_channel[i].timer);
387
}
388
389
void
390
i8253_start()
391
{
392
	int i;
393
	for (i = 0; i < 3; i++)
394
		if(i8253_channel[i].in_use)
395
			i8253_reset(i);
396
}