VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/DevPit-i8254.cpp@ 400

Last change on this file since 400 was 1, checked in by vboxsync, 55 years ago

import

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 37.7 KB
Line 
1/** @file
2 *
3 * VBox basic PC devices:
4 * Intel 8254 programmable interval timer
5 */
6
7/*
8 * Copyright (C) 2006 InnoTek Systemberatung GmbH
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.virtualbox.org. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License as published by the Free Software Foundation,
14 * in version 2 as it comes in the "COPYING" file of the VirtualBox OSE
15 * distribution. VirtualBox OSE is distributed in the hope that it will
16 * be useful, but WITHOUT ANY WARRANTY of any kind.
17 *
18 * If you received this file as part of a commercial VirtualBox
19 * distribution, then only the terms of your commercial VirtualBox
20 * license agreement apply instead of the previous paragraph.
21 *
22 * --------------------------------------------------------------------
23 *
24 * This code is based on:
25 *
26 * QEMU 8253/8254 interval timer emulation
27 *
28 * Copyright (c) 2003-2004 Fabrice Bellard
29 *
30 * Permission is hereby granted, free of charge, to any person obtaining a copy
31 * of this software and associated documentation files (the "Software"), to deal
32 * in the Software without restriction, including without limitation the rights
33 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
34 * copies of the Software, and to permit persons to whom the Software is
35 * furnished to do so, subject to the following conditions:
36 *
37 * The above copyright notice and this permission notice shall be included in
38 * all copies or substantial portions of the Software.
39 *
40 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
41 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
42 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
43 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
44 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
45 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
46 * THE SOFTWARE.
47 */
48
49
50/*******************************************************************************
51* Header Files *
52*******************************************************************************/
53#define LOG_GROUP LOG_GROUP_DEV_PIT
54#include <VBox/pdm.h>
55
56#include <VBox/log.h>
57#include <iprt/assert.h>
58#include <VBox/stam.h>
59
60#include "vl_vbox.h"
61
62
63/*******************************************************************************
64* Defined Constants And Macros *
65*******************************************************************************/
66/** The PIT frequency. */
67#define PIT_FREQ 1193182
68
69#define RW_STATE_LSB 1
70#define RW_STATE_MSB 2
71#define RW_STATE_WORD0 3
72#define RW_STATE_WORD1 4
73
74/** The version of the saved state. */
75#define PIT_SAVED_STATE_VERSION 2
76
77
78/*******************************************************************************
79* Structures and Typedefs *
80*******************************************************************************/
81typedef struct PITChannelState
82{
83 /** Pointer to the instance data - HCPtr. */
84 HCPTRTYPE(struct PITState *) pPitHC;
85 /** The timer - HCPtr. */
86 PTMTIMERHC pTimerHC;
87 /** Pointer to the instance data - GCPtr. */
88 GCPTRTYPE(struct PITState *) pPitGC;
89 /** The timer - HCPtr. */
90 PTMTIMERGC pTimerGC;
91 /** The virtual time stamp at the last reload. (only used in mode 2 for now) */
92 uint64_t u64ReloadTS;
93 /** The actual time of the next tick.
94 * As apposed to the next_transition_time which contains the correct time of the next tick. */
95 uint64_t u64NextTS;
96 /** When to give up catching up. (negative number) */
97 int64_t i64MaxCatchupTS;
98
99 /** (count_load_time is only set by TMTimerGet() which returns uint64_t) */
100 uint64_t count_load_time;
101 /* irq handling */
102 int64_t next_transition_time;
103 int32_t irq;
104 uint32_t padding;
105
106 uint32_t count; /* can be 65536 */
107 uint16_t latched_count;
108 uint8_t count_latched;
109 uint8_t status_latched;
110
111 uint8_t status;
112 uint8_t read_state;
113 uint8_t write_state;
114 uint8_t write_latch;
115
116 uint8_t rw_mode;
117 uint8_t mode;
118 uint8_t bcd; /* not supported */
119 uint8_t gate; /* timer start */
120
121} PITChannelState;
122
123typedef struct PITState
124{
125 PITChannelState channels[3];
126 /** Speaker data. */
127 int32_t speaker_data_on;
128 /** Speaker dummy. */
129 int32_t dummy_refresh_clock;
130 /** Pointer to the device instance. */
131 HCPTRTYPE(PPDMDEVINS) pDevIns;
132 /** Number of IRQs that's been raised. */
133 STAMCOUNTER StatPITIrq;
134 /** Profiling the timer callback handler. */
135 STAMPROFILEADV StatPITHandler;
136 /** The number of times we've had to speed up the time because we lagged too far behind. */
137 STAMCOUNTER StatPITCatchup;
138 /** The number of times we've lagged too far behind for it to be worth trying to catch up. */
139 STAMCOUNTER StatPITGiveup;
140} PITState;
141
142
143/*******************************************************************************
144* Internal Functions *
145*******************************************************************************/
146__BEGIN_DECLS
147PDMBOTHCBDECL(int) pitIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
148PDMBOTHCBDECL(int) pitIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
149PDMBOTHCBDECL(int) pitIOPortSpeakerRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb);
150#ifdef IN_RING3
151PDMBOTHCBDECL(int) pitIOPortSpeakerWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb);
152static void pit_irq_timer_update(PITChannelState *s, uint64_t current_time);
153#endif
154__END_DECLS
155
156
157
158
159static int pit_get_count(PITChannelState *s)
160{
161 uint64_t d;
162 int counter;
163 PTMTIMER pTimer = s->CTXSUFF(pPit)->channels[0].CTXSUFF(pTimer);
164
165 if (s->mode == 2) /** @todo Implement proper virtual time and get rid of this hack.. */
166 {
167#if 0
168 d = TMTimerGet(pTimer);
169 d -= s->u64ReloadTS;
170 d = muldiv64(d, PIT_FREQ, TMTimerGetFreq(pTimer));
171#else /* variable time because of catch up */
172 if (s->u64NextTS == UINT64_MAX)
173 return 1; /** @todo check this value. */
174 d = TMTimerGet(pTimer);
175 d = muldiv64(d - s->u64ReloadTS, s->count, s->u64NextTS - s->u64ReloadTS);
176#endif
177 if (d >= s->count)
178 return 1;
179 return s->count - d;
180 }
181 d = muldiv64(TMTimerGet(pTimer) - s->count_load_time, PIT_FREQ, TMTimerGetFreq(pTimer));
182 switch(s->mode) {
183 case 0:
184 case 1:
185 case 4:
186 case 5:
187 counter = (s->count - d) & 0xffff;
188 break;
189 case 3:
190 /* XXX: may be incorrect for odd counts */
191 counter = s->count - ((2 * d) % s->count);
192 break;
193 default:
194 counter = s->count - (d % s->count);
195 break;
196 }
197 /** @todo check that we don't return 0, in most modes (all?) the counter shouldn't be zero. */
198 return counter;
199}
200
201/* get pit output bit */
202static int pit_get_out1(PITChannelState *s, int64_t current_time)
203{
204 uint64_t d;
205 PTMTIMER pTimer = s->CTXSUFF(pPit)->channels[0].CTXSUFF(pTimer);
206 int out;
207
208 d = muldiv64(current_time - s->count_load_time, PIT_FREQ, TMTimerGetFreq(pTimer));
209 switch(s->mode) {
210 default:
211 case 0:
212 out = (d >= s->count);
213 break;
214 case 1:
215 out = (d < s->count);
216 break;
217 case 2:
218 Log2(("pit_get_out1: d=%llx c=%x %x \n", d, s->count, (unsigned)(d % s->count)));
219 if ((d % s->count) == 0 && d != 0)
220 out = 1;
221 else
222 out = 0;
223 break;
224 case 3:
225 out = (d % s->count) < ((s->count + 1) >> 1);
226 break;
227 case 4:
228 case 5:
229 out = (d == s->count);
230 break;
231 }
232 return out;
233}
234
235
236static int pit_get_out(PITState *pit, int channel, int64_t current_time)
237{
238 PITChannelState *s = &pit->channels[channel];
239 return pit_get_out1(s, current_time);
240}
241
242
243static int pit_get_gate(PITState *pit, int channel)
244{
245 PITChannelState *s = &pit->channels[channel];
246 return s->gate;
247}
248
249
250/* if already latched, do not latch again */
251static void pit_latch_count(PITChannelState *s)
252{
253 if (!s->count_latched) {
254 s->latched_count = pit_get_count(s);
255 s->count_latched = s->rw_mode;
256 LogFlow(("pit_latch_count: latched_count=%#06x / %10RU64 ns (c=%#06x m=%d)\n",
257 s->latched_count, muldiv64(s->count - s->latched_count, 1000000000, PIT_FREQ), s->count, s->mode));
258 }
259}
260
261#ifdef IN_RING3
262
263/* val must be 0 or 1 */
264static void pit_set_gate(PITState *pit, int channel, int val)
265{
266 PITChannelState *s = &pit->channels[channel];
267 PTMTIMER pTimer = s->CTXSUFF(pPit)->channels[0].CTXSUFF(pTimer);
268
269 switch(s->mode) {
270 default:
271 case 0:
272 case 4:
273 /* XXX: just disable/enable counting */
274 break;
275 case 1:
276 case 5:
277 if (s->gate < val) {
278 /* restart counting on rising edge */
279 s->count_load_time = TMTimerGet(pTimer);
280 pit_irq_timer_update(s, s->count_load_time);
281 }
282 break;
283 case 2:
284 case 3:
285 if (s->gate < val) {
286 /* restart counting on rising edge */
287 s->count_load_time = s->u64ReloadTS = TMTimerGet(pTimer);
288 pit_irq_timer_update(s, s->count_load_time);
289 }
290 /* XXX: disable/enable counting */
291 break;
292 }
293 s->gate = val;
294}
295
296static inline void pit_load_count(PITChannelState *s, int val)
297{
298 PTMTIMER pTimer = s->CTXSUFF(pPit)->channels[0].CTXSUFF(pTimer);
299 if (val == 0)
300 val = 0x10000;
301 s->count_load_time = s->u64ReloadTS = TMTimerGet(pTimer);
302 s->count = val;
303 pit_irq_timer_update(s, s->count_load_time);
304}
305
306/* return -1 if no transition will occur. */
307static int64_t pit_get_next_transition_time(PITChannelState *s,
308 uint64_t current_time)
309{
310 PTMTIMER pTimer = s->CTXSUFF(pPit)->channels[0].CTXSUFF(pTimer);
311 uint64_t d, next_time, base;
312 uint32_t period2;
313
314 d = muldiv64(current_time - s->count_load_time, PIT_FREQ, TMTimerGetFreq(pTimer));
315 switch(s->mode) {
316 default:
317 case 0:
318 case 1:
319 if (d < s->count)
320 next_time = s->count;
321 else
322 return -1;
323 break;
324 /*
325 * Mode 2: The period is count + 1 PIT ticks.
326 * When the counter reaches 1 we sent the output low (for channel 0 that
327 * means raise an irq). On the next tick, where we should be decrementing
328 * from 1 to 0, the count is loaded and the output goes high (channel 0
329 * means clearing the irq).
330 *
331 * In VBox we simplify the tick cycle between 1 and 0 and immediately clears
332 * the irq. We also don't set it until we reach 0, which is a tick late - will
333 * try fix that later some day.
334 */
335 case 2:
336 base = (d / s->count) * s->count;
337#ifndef VBOX /* see above */
338 if ((d - base) == 0 && d != 0)
339 next_time = base + s->count;
340 else
341#endif
342 next_time = base + s->count + 1;
343 break;
344 case 3:
345 base = (d / s->count) * s->count;
346 period2 = ((s->count + 1) >> 1);
347 if ((d - base) < period2)
348 next_time = base + period2;
349 else
350 next_time = base + s->count;
351 break;
352 case 4:
353 case 5:
354 if (d < s->count)
355 next_time = s->count;
356 else if (d == s->count)
357 next_time = s->count + 1;
358 else
359 return -1;
360 break;
361 }
362 /* convert to timer units */
363 LogFlow(("PIT: next_time=%14RI64 %20RI64 mode=%#x count=%#06x\n", next_time,
364 muldiv64(next_time, TMTimerGetFreq(pTimer), PIT_FREQ), s->mode, s->count));
365 next_time = s->count_load_time + muldiv64(next_time, TMTimerGetFreq(pTimer), PIT_FREQ);
366 /* fix potential rounding problems */
367 /* XXX: better solution: use a clock at PIT_FREQ Hz */
368 if (next_time <= current_time)
369 next_time = current_time + 1;
370 return next_time;
371}
372
373static void pit_irq_timer_update(PITChannelState *s, uint64_t current_time)
374{
375 uint64_t now;
376 int64_t expire_time;
377 int irq_level;
378 PPDMDEVINS pDevIns;
379 PTMTIMER pTimer = s->CTXSUFF(pPit)->channels[0].CTXSUFF(pTimer);
380
381 if (!s->CTXSUFF(pTimer))
382 return;
383 expire_time = pit_get_next_transition_time(s, current_time);
384 irq_level = pit_get_out1(s, current_time);
385
386 /* We just flip-flop the irq level to save that extra timer call, which isn't generally required (we haven't served it for months). */
387 pDevIns = s->CTXSUFF(pPit)->pDevIns;
388 PDMDevHlpISASetIrq(pDevIns, s->irq, irq_level);
389 if (irq_level)
390 PDMDevHlpISASetIrq(pDevIns, s->irq, 0);
391 now = TMTimerGet(pTimer);
392 Log3(("pit_irq_timer_update: %lldns late\n", now - s->u64NextTS));
393 if (irq_level)
394 {
395 s->u64ReloadTS = now;
396 STAM_COUNTER_INC(&s->CTXSUFF(pPit)->StatPITIrq);
397 }
398
399 /* check if it expires too soon - move at 4x rate if it does. */
400 if (expire_time != -1)
401 {
402 int64_t delta = expire_time - now;
403 const int64_t quarter = (expire_time - s->next_transition_time) >> 2;
404 if (delta <= quarter && s->next_transition_time != -1)
405 {
406 if (delta >= s->i64MaxCatchupTS)
407 {
408 /* If we set the timer to 'expire_time' we could end up with flooding the guest
409 * with timer interrupts because the next interrupt(s) would probably raise
410 * immediately. Therefore we set the timer to 'now + quarter' with quarter>0.
411 * This delays the adaption a little bit. */
412 STAM_COUNTER_INC(&s->CTXSUFF(pPit)->StatPITCatchup);
413 s->u64NextTS = now + quarter;
414 LogFlow(("PIT: m=%d cnt=%#4x irq=%#x delay=%8RI64 next=%20RI64 now=%20RI64 load=%20RI64 %9RI64 delta=%9RI64\n",
415 s->mode, s->count, irq_level, quarter, s->u64NextTS, now, s->count_load_time,
416 muldiv64(s->u64NextTS - s->count_load_time, PIT_FREQ, TMTimerGetFreq(pTimer)), delta));
417 }
418 else
419 {
420 /* We are too far away from the real time. Hard synchronize. */
421 STAM_COUNTER_INC(&s->CTXSUFF(pPit)->StatPITGiveup);
422 s->u64NextTS = expire_time = pit_get_next_transition_time(s, now);
423 LogFlow(("PIT: m=%d cnt=%#4x irq=%#x delay=%8RI64 next=%20RI64 now=%20RI64 load=%20RI64 %9RI64 delta=%9RI64 giving up!\n",
424 s->mode, s->count, irq_level, quarter, s->u64NextTS, now, s->count_load_time,
425 muldiv64(s->u64NextTS - s->count_load_time, PIT_FREQ, TMTimerGetFreq(pTimer)), delta));
426 }
427 }
428 else
429 {
430 /* Everything is fine, just set the timer to the regular next expire_time. */
431 s->u64NextTS = expire_time;
432 LogFlow(("PIT: m=%d cnt=%#4x irq=%#x delay=%8RI64 next=%20RI64 now=%20RI64 load=%20RI64 %9RI64\n",
433 s->mode, s->count, irq_level, expire_time - now, expire_time, now, s->count_load_time,
434 muldiv64(expire_time - s->count_load_time, PIT_FREQ, TMTimerGetFreq(pTimer))));
435 }
436 TMTimerSet(s->CTXSUFF(pTimer), s->u64NextTS);
437 }
438 else
439 {
440 LogFlow(("PIT: m=%d count=%#4x irq_level=%#x stopped\n", s->mode, s->count, irq_level));
441 TMTimerStop(s->CTXSUFF(pTimer));
442 s->u64NextTS = UINT64_MAX;
443 }
444 s->next_transition_time = expire_time;
445}
446
447#endif /* IN_RING3 */
448
449
450/**
451 * Port I/O Handler for IN operations.
452 *
453 * @returns VBox status code.
454 *
455 * @param pDevIns The device instance.
456 * @param pvUser User argument - ignored.
457 * @param Port Port number used for the IN operation.
458 * @param pu32 Where to store the result.
459 * @param cb Number of bytes read.
460 */
461PDMBOTHCBDECL(int) pitIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
462{
463 Log2(("pitIOPortRead: Port=%#x cb=%x\n", Port, cb));
464 NOREF(pvUser);
465 Port &= 3;
466 if (cb != 1 || Port == 3)
467 {
468 Log(("pitIOPortRead: Port=%#x cb=%x *pu32=unused!\n", Port, cb));
469 return VERR_IOM_IOPORT_UNUSED;
470 }
471
472 PITState *pit = PDMINS2DATA(pDevIns, PITState *);
473 int ret;
474 PITChannelState *s = &pit->channels[Port];
475 if (s->status_latched)
476 {
477 s->status_latched = 0;
478 ret = s->status;
479 }
480 else if (s->count_latched)
481 {
482 switch (s->count_latched)
483 {
484 default:
485 case RW_STATE_LSB:
486 ret = s->latched_count & 0xff;
487 s->count_latched = 0;
488 break;
489 case RW_STATE_MSB:
490 ret = s->latched_count >> 8;
491 s->count_latched = 0;
492 break;
493 case RW_STATE_WORD0:
494 ret = s->latched_count & 0xff;
495 s->count_latched = RW_STATE_MSB;
496 break;
497 }
498 }
499 else
500 {
501 int count;
502 switch (s->read_state)
503 {
504 default:
505 case RW_STATE_LSB:
506 count = pit_get_count(s);
507 ret = count & 0xff;
508 break;
509 case RW_STATE_MSB:
510 count = pit_get_count(s);
511 ret = (count >> 8) & 0xff;
512 break;
513 case RW_STATE_WORD0:
514 count = pit_get_count(s);
515 ret = count & 0xff;
516 s->read_state = RW_STATE_WORD1;
517 break;
518 case RW_STATE_WORD1:
519 count = pit_get_count(s);
520 ret = (count >> 8) & 0xff;
521 s->read_state = RW_STATE_WORD0;
522 break;
523 }
524 }
525
526 *pu32 = ret;
527 Log2(("pitIOPortRead: Port=%#x cb=%x *pu32=%#04x\n", Port, cb, *pu32));
528 return VINF_SUCCESS;
529}
530
531
532/**
533 * Port I/O Handler for OUT operations.
534 *
535 * @returns VBox status code.
536 *
537 * @param pDevIns The device instance.
538 * @param pvUser User argument - ignored.
539 * @param Port Port number used for the IN operation.
540 * @param u32 The value to output.
541 * @param cb The value size in bytes.
542 */
543PDMBOTHCBDECL(int) pitIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
544{
545 Log2(("pitIOPortWrite: Port=%#x cb=%x u32=%#04x\n", Port, cb, u32));
546 NOREF(pvUser);
547 if (cb != 1)
548 return VINF_SUCCESS;
549
550 PITState *pit = PDMINS2DATA(pDevIns, PITState *);
551 Port &= 3;
552 if (Port == 3)
553 {
554 /*
555 * Port 43h - Mode/Command Register.
556 * 7 6 5 4 3 2 1 0
557 * * * . . . . . . Select channel: 0 0 = Channel 0
558 * 0 1 = Channel 1
559 * 1 0 = Channel 2
560 * 1 1 = Read-back command (8254 only)
561 * (Illegal on 8253)
562 * (Illegal on PS/2 {JAM})
563 * . . * * . . . . Command/Access mode: 0 0 = Latch count value command
564 * 0 1 = Access mode: lobyte only
565 * 1 0 = Access mode: hibyte only
566 * 1 1 = Access mode: lobyte/hibyte
567 * . . . . * * * . Operating mode: 0 0 0 = Mode 0, 0 0 1 = Mode 1,
568 * 0 1 0 = Mode 2, 0 1 1 = Mode 3,
569 * 1 0 0 = Mode 4, 1 0 1 = Mode 5,
570 * 1 1 0 = Mode 2, 1 1 1 = Mode 3
571 * . . . . . . . * BCD/Binary mode: 0 = 16-bit binary, 1 = four-digit BCD
572 */
573 unsigned channel = u32 >> 6;
574 if (channel == 3)
575 {
576 /* read-back command */
577 for (channel = 0; channel < ELEMENTS(pit->channels); channel++)
578 {
579 PITChannelState *s = &pit->channels[channel];
580 if (u32 & (2 << channel)) {
581 if (!(u32 & 0x20))
582 pit_latch_count(s);
583 if (!(u32 & 0x10) && !s->status_latched)
584 {
585 /* status latch */
586 /* XXX: add BCD and null count */
587 PTMTIMER pTimer = s->CTXSUFF(pPit)->channels[0].CTXSUFF(pTimer);
588 s->status = (pit_get_out1(s, TMTimerGet(pTimer)) << 7)
589 | (s->rw_mode << 4)
590 | (s->mode << 1)
591 | s->bcd;
592 s->status_latched = 1;
593 }
594 }
595 }
596 }
597 else
598 {
599 PITChannelState *s = &pit->channels[channel];
600 unsigned access = (u32 >> 4) & 3;
601 if (access == 0)
602 pit_latch_count(s);
603 else
604 {
605 s->rw_mode = access;
606 s->read_state = access;
607 s->write_state = access;
608
609 s->mode = (u32 >> 1) & 7;
610 s->bcd = u32 & 1;
611 /* XXX: update irq timer ? */
612 }
613 }
614 }
615 else
616 {
617#ifndef IN_RING3
618 return VINF_IOM_HC_IOPORT_WRITE;
619#else /* IN_RING3 */
620 /*
621 * Port 40-42h - Channel Data Ports.
622 */
623 PITChannelState *s = &pit->channels[Port];
624 switch(s->write_state)
625 {
626 default:
627 case RW_STATE_LSB:
628 pit_load_count(s, u32);
629 break;
630 case RW_STATE_MSB:
631 pit_load_count(s, u32 << 8);
632 break;
633 case RW_STATE_WORD0:
634 s->write_latch = u32;
635 s->write_state = RW_STATE_WORD1;
636 break;
637 case RW_STATE_WORD1:
638 pit_load_count(s, s->write_latch | (u32 << 8));
639 s->write_state = RW_STATE_WORD0;
640 break;
641 }
642#endif /* !IN_RING3 */
643 }
644 return VINF_SUCCESS;
645}
646
647
648/**
649 * Port I/O Handler for speaker IN operations.
650 *
651 * @returns VBox status code.
652 *
653 * @param pDevIns The device instance.
654 * @param pvUser User argument - ignored.
655 * @param Port Port number used for the IN operation.
656 * @param pu32 Where to store the result.
657 * @param cb Number of bytes read.
658 */
659PDMBOTHCBDECL(int) pitIOPortSpeakerRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
660{
661 NOREF(pvUser);
662 if (cb == 1)
663 {
664 PITState *pData = PDMINS2DATA(pDevIns, PITState *);
665 int out = pit_get_out(pData, 2, TMTimerGet(pData->channels[0].CTXSUFF(pTimer)));
666 pData->dummy_refresh_clock ^= 1;
667 *pu32 = (pData->speaker_data_on << 1) | pit_get_gate(pData, 2) | (out << 5) | (pData->dummy_refresh_clock << 4);
668 Log(("pitIOPortSpeakerRead: Port=%#x cb=%x *pu32=%#x\n", Port, cb, *pu32));
669 return VINF_SUCCESS;
670 }
671 Log(("pitIOPortSpeakerRead: Port=%#x cb=%x *pu32=unused!\n", Port, cb));
672 return VERR_IOM_IOPORT_UNUSED;
673}
674
675#ifdef IN_RING3
676
677/**
678 * Port I/O Handler for speaker OUT operations.
679 *
680 * @returns VBox status code.
681 *
682 * @param pDevIns The device instance.
683 * @param pvUser User argument - ignored.
684 * @param Port Port number used for the IN operation.
685 * @param u32 The value to output.
686 * @param cb The value size in bytes.
687 */
688PDMBOTHCBDECL(int) pitIOPortSpeakerWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
689{
690 NOREF(pvUser);
691 if (cb == 1)
692 {
693 PITState *pData = PDMINS2DATA(pDevIns, PITState *);
694 pData->speaker_data_on = (u32 >> 1) & 1;
695 pit_set_gate(pData, 2, u32 & 1);
696 }
697 Log(("pitIOPortSpeakerRead: Port=%#x cb=%x u32=%#x\n", Port, cb, u32));
698 return VINF_SUCCESS;
699}
700
701
702/**
703 * Saves a state of the programmable interval timer device.
704 *
705 * @returns VBox status code.
706 * @param pDevIns The device instance.
707 * @param pSSMHandle The handle to save the state to.
708 */
709static DECLCALLBACK(int) pitSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle)
710{
711 PITState *pData = PDMINS2DATA(pDevIns, PITState *);
712 unsigned i;
713
714 for (i = 0; i < ELEMENTS(pData->channels); i++)
715 {
716 PITChannelState *s = &pData->channels[i];
717 SSMR3PutU32(pSSMHandle, s->count);
718 SSMR3PutU16(pSSMHandle, s->latched_count);
719 SSMR3PutU8(pSSMHandle, s->count_latched);
720 SSMR3PutU8(pSSMHandle, s->status_latched);
721 SSMR3PutU8(pSSMHandle, s->status);
722 SSMR3PutU8(pSSMHandle, s->read_state);
723 SSMR3PutU8(pSSMHandle, s->write_state);
724 SSMR3PutU8(pSSMHandle, s->write_latch);
725 SSMR3PutU8(pSSMHandle, s->rw_mode);
726 SSMR3PutU8(pSSMHandle, s->mode);
727 SSMR3PutU8(pSSMHandle, s->bcd);
728 SSMR3PutU8(pSSMHandle, s->gate);
729 SSMR3PutU64(pSSMHandle, s->count_load_time);
730 SSMR3PutU64(pSSMHandle, s->u64NextTS);
731 SSMR3PutU64(pSSMHandle, s->u64ReloadTS);
732 SSMR3PutS64(pSSMHandle, s->next_transition_time);
733 if (s->CTXSUFF(pTimer))
734 TMR3TimerSave(s->CTXSUFF(pTimer), pSSMHandle);
735 }
736
737 SSMR3PutS32(pSSMHandle, pData->speaker_data_on);
738 return SSMR3PutS32(pSSMHandle, pData->dummy_refresh_clock);
739}
740
741
742/**
743 * Loads a saved programmable interval timer device state.
744 *
745 * @returns VBox status code.
746 * @param pDevIns The device instance.
747 * @param pSSMHandle The handle to the saved state.
748 * @param u32Version The data unit version number.
749 */
750static DECLCALLBACK(int) pitLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSMHandle, uint32_t u32Version)
751{
752 PITState *pData = PDMINS2DATA(pDevIns, PITState *);
753 unsigned i;
754
755 if (u32Version != PIT_SAVED_STATE_VERSION)
756 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
757
758 for (i = 0; i < ELEMENTS(pData->channels); i++)
759 {
760 PITChannelState *s = &pData->channels[i];
761 SSMR3GetU32(pSSMHandle, &s->count);
762 SSMR3GetU16(pSSMHandle, &s->latched_count);
763 SSMR3GetU8(pSSMHandle, &s->count_latched);
764 SSMR3GetU8(pSSMHandle, &s->status_latched);
765 SSMR3GetU8(pSSMHandle, &s->status);
766 SSMR3GetU8(pSSMHandle, &s->read_state);
767 SSMR3GetU8(pSSMHandle, &s->write_state);
768 SSMR3GetU8(pSSMHandle, &s->write_latch);
769 SSMR3GetU8(pSSMHandle, &s->rw_mode);
770 SSMR3GetU8(pSSMHandle, &s->mode);
771 SSMR3GetU8(pSSMHandle, &s->bcd);
772 SSMR3GetU8(pSSMHandle, &s->gate);
773 SSMR3GetU64(pSSMHandle, &s->count_load_time);
774 SSMR3GetU64(pSSMHandle, &s->u64NextTS);
775 SSMR3GetU64(pSSMHandle, &s->u64ReloadTS);
776 SSMR3GetS64(pSSMHandle, &s->next_transition_time);
777 if (s->CTXSUFF(pTimer))
778 TMR3TimerLoad(s->CTXSUFF(pTimer), pSSMHandle);
779 }
780
781 SSMR3GetS32(pSSMHandle, &pData->speaker_data_on);
782 return SSMR3GetS32(pSSMHandle, &pData->dummy_refresh_clock);
783}
784
785
786/**
787 * Device timer callback function.
788 *
789 * @param pDevIns Device instance of the device which registered the timer.
790 * @param pTimer The timer handle.
791 */
792static DECLCALLBACK(void) pitTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer)
793{
794 PITState *pData = PDMINS2DATA(pDevIns, PITState *);
795 PITChannelState *s = &pData->channels[0];
796 STAM_PROFILE_ADV_START(&s->CTXSUFF(pPit)->StatPITHandler, a);
797 pit_irq_timer_update(s, s->next_transition_time);
798 STAM_PROFILE_ADV_STOP(&s->CTXSUFF(pPit)->StatPITHandler, a);
799}
800
801
802/**
803 * Relocation notification.
804 *
805 * @returns VBox status.
806 * @param pDevIns The device instance data.
807 * @param offDelta The delta relative to the old address.
808 */
809static DECLCALLBACK(void) pitRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
810{
811 PITState *pData = PDMINS2DATA(pDevIns, PITState *);
812 unsigned i;
813 LogFlow(("pitRelocate: \n"));
814
815 for (i = 0; i < ELEMENTS(pData->channels); i++)
816 {
817 PITChannelState *pCh = &pData->channels[i];
818 if (pCh->pTimerHC)
819 pCh->pTimerGC = TMTimerGCPtr(pCh->pTimerHC);
820 pData->channels[i].pPitGC = PDMINS2DATA_GCPTR(pDevIns);
821 }
822}
823
824/** @todo remove this! */
825static DECLCALLBACK(void) pitInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs);
826
827/**
828 * Reset notification.
829 *
830 * @returns VBox status.
831 * @param pDevIns The device instance data.
832 */
833static DECLCALLBACK(void) pitReset(PPDMDEVINS pDevIns)
834{
835 PITState *pData = PDMINS2DATA(pDevIns, PITState *);
836 unsigned i;
837 LogFlow(("pitReset: \n"));
838
839 for (i = 0; i < ELEMENTS(pData->channels); i++)
840 {
841 PITChannelState *s = &pData->channels[i];
842
843#if 1 /* Set everything back to virgin state. (might not be strictly correct) */
844 s->latched_count = 0;
845 s->count_latched = 0;
846 s->status_latched = 0;
847 s->status = 0;
848 s->read_state = 0;
849 s->write_state = 0;
850 s->write_latch = 0;
851 s->rw_mode = 0;
852 s->bcd = 0;
853#endif
854 s->mode = 3;
855 s->gate = (i != 2);
856 pit_load_count(s, 0);
857 }
858/** @todo remove when #1589 is resolved. */
859pitInfo(pDevIns, DBGFR3InfoLogRelHlp(), NULL);
860}
861
862
863/**
864 * Info handler, device version.
865 *
866 * @param pDevIns Device instance which registered the info.
867 * @param pHlp Callback functions for doing output.
868 * @param pszArgs Argument string. Optional and specific to the handler.
869 */
870static DECLCALLBACK(void) pitInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
871{
872 PITState *pData = PDMINS2DATA(pDevIns, PITState *);
873 unsigned i;
874 for (i = 0; i < ELEMENTS(pData->channels); i++)
875 {
876 const PITChannelState *pCh = &pData->channels[i];
877
878 pHlp->pfnPrintf(pHlp,
879 "PIT (i8254) channel %d status: irq=%#x\n"
880 " count=%08x" " latched_count=%04x count_latched=%02x\n"
881 " status=%02x status_latched=%02x read_state=%02x\n"
882 " write_state=%02x write_latch=%02x rw_mode=%02x\n"
883 " mode=%02x bcd=%02x gate=%02x\n"
884 " count_load_time=%016RX64 next_transition_time=%016RX64\n"
885 " u64ReloadTS=%016RX64 u64NextTS=%016RX64\n"
886 ,
887 i, pCh->irq,
888 pCh->count, pCh->latched_count, pCh->count_latched,
889 pCh->status, pCh->status_latched, pCh->read_state,
890 pCh->write_state, pCh->write_latch, pCh->rw_mode,
891 pCh->mode, pCh->bcd, pCh->gate,
892 pCh->count_load_time, pCh->next_transition_time,
893 pCh->u64ReloadTS, pCh->u64NextTS);
894 }
895 pHlp->pfnPrintf(pHlp, "speaker_data_on=%#x dummy_refresh_clock=%#x\n",
896 pData->speaker_data_on, pData->dummy_refresh_clock);
897}
898
899
900/**
901 * Construct a device instance for a VM.
902 *
903 * @returns VBox status.
904 * @param pDevIns The device instance data.
905 * If the registration structure is needed, pDevIns->pDevReg points to it.
906 * @param iInstance Instance number. Use this to figure out which registers and such to use.
907 * The device number is also found in pDevIns->iInstance, but since it's
908 * likely to be freqently used PDM passes it as parameter.
909 * @param pCfgHandle Configuration node handle for the device. Use this to obtain the configuration
910 * of the device instance. It's also found in pDevIns->pCfgHandle, but like
911 * iInstance it's expected to be used a bit in this function.
912 */
913static DECLCALLBACK(int) pitConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfgHandle)
914{
915 PITState *pData = PDMINS2DATA(pDevIns, PITState *);
916 int rc;
917 uint8_t u8Irq;
918 uint16_t u16Base;
919 bool fSpeaker;
920 bool fGCEnabled;
921 bool fR0Enabled;
922 unsigned i;
923 Assert(iInstance == 0);
924
925 /*
926 * Validate configuration.
927 */
928 if (!CFGMR3AreValuesValid(pCfgHandle, "Irq\0Base\0Speaker\0GCEnabled\0R0Enabled"))
929 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
930
931 /*
932 * Init the data.
933 */
934 rc = CFGMR3QueryU8(pCfgHandle, "Irq", &u8Irq);
935 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
936 u8Irq = 0;
937 else if (VBOX_FAILURE(rc))
938 return PDMDEV_SET_ERROR(pDevIns, rc,
939 N_("Configuration error: Querying \"Irq\" as a uint8_t failed"));
940
941 rc = CFGMR3QueryU16(pCfgHandle, "Base", &u16Base);
942 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
943 u16Base = 0x40;
944 else if (VBOX_FAILURE(rc))
945 return PDMDEV_SET_ERROR(pDevIns, rc,
946 N_("Configuration error: Querying \"Base\" as a uint16_t failed"));
947
948 rc = CFGMR3QueryBool(pCfgHandle, "SpeakerEnabled", &fSpeaker);
949 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
950 fSpeaker = true;
951 else if (VBOX_FAILURE(rc))
952 return PDMDEV_SET_ERROR(pDevIns, rc,
953 N_("Configuration error: Querying \"SpeakerEnabled\" as a bool failed"));
954
955 rc = CFGMR3QueryBool(pCfgHandle, "GCEnabled", &fGCEnabled);
956 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
957 fGCEnabled = true;
958 else if (VBOX_FAILURE(rc))
959 return PDMDEV_SET_ERROR(pDevIns, rc,
960 N_("Configuration error: Querying \"GCEnabled\" as a bool failed"));
961
962 rc = CFGMR3QueryBool(pCfgHandle, "R0Enabled", &fR0Enabled);
963 if (rc == VERR_CFGM_VALUE_NOT_FOUND)
964 fR0Enabled = true;
965 else if (VBOX_FAILURE(rc))
966 return PDMDEV_SET_ERROR(pDevIns, rc,
967 N_("Configuration error: failed to read R0Enabled as boolean"));
968
969 pData->pDevIns = pDevIns;
970 pData->channels[0].irq = u8Irq;
971 for (i = 0; i < ELEMENTS(pData->channels); i++)
972 {
973 pData->channels[i].pPitHC = pData;
974 pData->channels[i].pPitGC = PDMINS2DATA_GCPTR(pDevIns);
975 }
976
977 /*
978 * Create timer, register I/O Ports and save state.
979 */
980 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL, pitTimer, "i8254 Programmable Interval Timer",
981 &pData->channels[0].CTXSUFF(pTimer));
982 if (VBOX_FAILURE(rc))
983 {
984 AssertMsgFailed(("pfnTMTimerCreate -> %Vrc\n", rc));
985 return rc;
986 }
987
988 rc = PDMDevHlpIOPortRegister(pDevIns, u16Base, 4, NULL, pitIOPortWrite, pitIOPortRead, NULL, NULL, "i8254 Programmable Interval Timer");
989 if (VBOX_FAILURE(rc))
990 return rc;
991 if (fGCEnabled)
992 {
993 rc = PDMDevHlpIOPortRegisterGC(pDevIns, u16Base, 4, 0, "pitIOPortWrite", "pitIOPortRead", NULL, NULL, "i8254 Programmable Interval Timer");
994 if (VBOX_FAILURE(rc))
995 return rc;
996 }
997 if (fR0Enabled)
998 {
999 rc = PDMDevHlpIOPortRegisterR0(pDevIns, u16Base, 4, 0, "pitIOPortWrite", "pitIOPortRead", NULL, NULL, "i8254 Programmable Interval Timer");
1000 if (VBOX_FAILURE(rc))
1001 return rc;
1002 }
1003
1004 if (fSpeaker)
1005 {
1006 rc = PDMDevHlpIOPortRegister(pDevIns, 0x61, 1, NULL, pitIOPortSpeakerWrite, pitIOPortSpeakerRead, NULL, NULL, "PC Speaker");
1007 if (VBOX_FAILURE(rc))
1008 return rc;
1009 if (fGCEnabled)
1010 {
1011 rc = PDMDevHlpIOPortRegisterGC(pDevIns, 0x61, 1, 0, NULL, "pitIOPortSpeakerRead", NULL, NULL, "PC Speaker");
1012 if (VBOX_FAILURE(rc))
1013 return rc;
1014 }
1015 }
1016
1017 rc = PDMDevHlpSSMRegister(pDevIns, pDevIns->pDevReg->szDeviceName, iInstance, PIT_SAVED_STATE_VERSION, sizeof(*pData),
1018 NULL, pitSaveExec, NULL,
1019 NULL, pitLoadExec, NULL);
1020 if (VBOX_FAILURE(rc))
1021 return rc;
1022
1023 /*
1024 * Calculate max catchup time.
1025 */
1026 pData->channels[0].i64MaxCatchupTS = pData->channels[1].i64MaxCatchupTS
1027 = pData->channels[2].i64MaxCatchupTS = -TMTimerFromMilli(pData->channels[0].CTXSUFF(pTimer), 1000*60*2); /* 2 min */
1028
1029 /*
1030 * Initialize the device state.
1031 */
1032 pitReset(pDevIns);
1033
1034 /*
1035 * Register statistics and debug info.
1036 */
1037 PDMDevHlpSTAMRegister(pDevIns, &pData->StatPITIrq, STAMTYPE_COUNTER, "/TM/PIT/Irq", STAMUNIT_OCCURENCES, "The number of times a timer interrupt was triggered.");
1038 PDMDevHlpSTAMRegister(pDevIns, &pData->StatPITHandler, STAMTYPE_PROFILE, "/TM/PIT/Handler", STAMUNIT_TICKS_PER_CALL, "Profiling timer callback handler.");
1039 PDMDevHlpSTAMRegister(pDevIns, &pData->StatPITCatchup, STAMTYPE_COUNTER, "/TM/PIT/Catchup", STAMUNIT_OCCURENCES, "The number of times we lagged too far behind.");
1040 PDMDevHlpSTAMRegister(pDevIns, &pData->StatPITGiveup, STAMTYPE_COUNTER, "/TM/PIT/Giveup", STAMUNIT_OCCURENCES, "The number of times we lagged so far behind that we simply gave up.");
1041
1042 PDMDevHlpDBGFInfoRegister(pDevIns, "pit", "Display PIT (i8254) status. (no arguments)", pitInfo);
1043
1044 return VINF_SUCCESS;
1045}
1046
1047
1048/**
1049 * The device registration structure.
1050 */
1051const PDMDEVREG g_DeviceI8254 =
1052{
1053 /* u32Version */
1054 PDM_DEVREG_VERSION,
1055 /* szDeviceName */
1056 "i8254",
1057 /* szGCMod */
1058 "VBoxDDGC.gc",
1059 /* szR0Mod */
1060 "VBoxDDR0.r0",
1061 /* pszDescription */
1062 "i8254 Programmable Interval Timer And Dummy Speaker",
1063 /* fFlags */
1064 PDM_DEVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DEVREG_FLAGS_GUEST_BITS_32_64 | PDM_DEVREG_FLAGS_PAE36 | PDM_DEVREG_FLAGS_GC | PDM_DEVREG_FLAGS_R0,
1065 /* fClass */
1066 PDM_DEVREG_CLASS_PIT,
1067 /* cMaxInstances */
1068 1,
1069 /* cbInstance */
1070 sizeof(PITState),
1071 /* pfnConstruct */
1072 pitConstruct,
1073 /* pfnDestruct */
1074 NULL,
1075 /* pfnRelocate */
1076 pitRelocate,
1077 /* pfnIOCtl */
1078 NULL,
1079 /* pfnPowerOn */
1080 NULL,
1081 /* pfnReset */
1082 pitReset,
1083 /* pfnSuspend */
1084 NULL,
1085 /* pfnResume */
1086 NULL,
1087 /* pfnAttach */
1088 NULL,
1089 /* pfnDetach */
1090 NULL,
1091 /* pfnQueryInterface. */
1092 NULL
1093};
1094
1095#endif /* IN_RING3 */
1096
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette