VirtualBox

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

Last change on this file since 14759 was 14742, checked in by vboxsync, 16 years ago

finally fixed timner starvation for real, also REM improvments
by putting external events check into right places and reverted
PIT DRAM refresh rate frequency code to correct one

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

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