VirtualBox

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

Last change on this file since 46845 was 45025, checked in by vboxsync, 12 years ago

Update PDMDEVREG initialization comment so they refer to pfnMemSetup instead of pfnIOCtl.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 45.2 KB
Line 
1/* $Id: DevPit-i8254.cpp 45025 2013-03-13 16:45:15Z vboxsync $ */
2/** @file
3 * DevPIT-i8254 - Intel 8254 Programmable Interval Timer (PIT) And Dummy Speaker Device.
4 */
5
6/*
7 * Copyright (C) 2006-2013 Oracle Corporation
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 *
18 * This code is based on:
19 *
20 * QEMU 8253/8254 interval timer emulation
21 *
22 * Copyright (c) 2003-2004 Fabrice Bellard
23 *
24 * Permission is hereby granted, free of charge, to any person obtaining a copy
25 * of this software and associated documentation files (the "Software"), to deal
26 * in the Software without restriction, including without limitation the rights
27 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
28 * copies of the Software, and to permit persons to whom the Software is
29 * furnished to do so, subject to the following conditions:
30 *
31 * The above copyright notice and this permission notice shall be included in
32 * all copies or substantial portions of the Software.
33 *
34 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
35 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
36 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
37 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
38 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
39 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
40 * THE SOFTWARE.
41 */
42
43/*******************************************************************************
44* Header Files *
45*******************************************************************************/
46#define LOG_GROUP LOG_GROUP_DEV_PIT
47#include <VBox/vmm/pdmdev.h>
48#include <VBox/log.h>
49#include <VBox/vmm/stam.h>
50#include <iprt/assert.h>
51#include <iprt/asm-math.h>
52
53#ifdef IN_RING3
54# include <iprt/alloc.h>
55# include <iprt/string.h>
56# include <iprt/uuid.h>
57#endif /* IN_RING3 */
58
59#include "VBoxDD.h"
60
61
62/*******************************************************************************
63* Defined Constants And Macros *
64*******************************************************************************/
65/** The PIT frequency. */
66#define PIT_FREQ 1193182
67
68#define RW_STATE_LSB 1
69#define RW_STATE_MSB 2
70#define RW_STATE_WORD0 3
71#define RW_STATE_WORD1 4
72
73/** The current saved state version. */
74#define PIT_SAVED_STATE_VERSION 4
75/** The saved state version used by VirtualBox 3.1 and earlier.
76 * This did not include disable by HPET flag. */
77#define PIT_SAVED_STATE_VERSION_VBOX_31 3
78/** The saved state version used by VirtualBox 3.0 and earlier.
79 * This did not include the config part. */
80#define PIT_SAVED_STATE_VERSION_VBOX_30 2
81
82/** @def FAKE_REFRESH_CLOCK
83 * Define this to flip the 15usec refresh bit on every read.
84 * If not defined, it will be flipped correctly. */
85/* #define FAKE_REFRESH_CLOCK */
86#ifdef DOXYGEN_RUNNING
87# define FAKE_REFRESH_CLOCK
88#endif
89
90/** The effective counter mode - if bit 1 is set, bit 2 is ignored. */
91#define EFFECTIVE_MODE(x) ((x) & ~(((x) & 2) << 1))
92
93
94/**
95 * Acquires the PIT lock or returns.
96 */
97#define DEVPIT_LOCK_RETURN(a_pThis, a_rcBusy) \
98 do { \
99 int rcLock = PDMCritSectEnter(&(a_pThis)->CritSect, (a_rcBusy)); \
100 if (rcLock != VINF_SUCCESS) \
101 return rcLock; \
102 } while (0)
103
104/**
105 * Releases the PIT lock.
106 */
107#define DEVPIT_UNLOCK(a_pThis) \
108 do { PDMCritSectLeave(&(a_pThis)->CritSect); } while (0)
109
110
111/**
112 * Acquires the TM lock and PIT lock, returns on failure.
113 */
114#define DEVPIT_LOCK_BOTH_RETURN(a_pThis, a_rcBusy) \
115 do { \
116 int rcLock = TMTimerLock((a_pThis)->channels[0].CTX_SUFF(pTimer), (a_rcBusy)); \
117 if (rcLock != VINF_SUCCESS) \
118 return rcLock; \
119 rcLock = PDMCritSectEnter(&(a_pThis)->CritSect, (a_rcBusy)); \
120 if (rcLock != VINF_SUCCESS) \
121 { \
122 TMTimerUnlock((a_pThis)->channels[0].CTX_SUFF(pTimer)); \
123 return rcLock; \
124 } \
125 } while (0)
126
127#if IN_RING3
128/**
129 * Acquires the TM lock and PIT lock, ignores failures.
130 */
131# define DEVPIT_R3_LOCK_BOTH(a_pThis) \
132 do { \
133 TMTimerLock((a_pThis)->channels[0].CTX_SUFF(pTimer), VERR_IGNORED); \
134 PDMCritSectEnter(&(a_pThis)->CritSect, VERR_IGNORED); \
135 } while (0)
136#endif /* IN_RING3 */
137
138/**
139 * Releases the PIT lock and TM lock.
140 */
141#define DEVPIT_UNLOCK_BOTH(a_pThis) \
142 do { \
143 PDMCritSectLeave(&(a_pThis)->CritSect); \
144 TMTimerUnlock((a_pThis)->channels[0].CTX_SUFF(pTimer)); \
145 } while (0)
146
147
148
149/*******************************************************************************
150* Structures and Typedefs *
151*******************************************************************************/
152/**
153 * The state of one PIT channel.
154 */
155typedef struct PITCHANNEL
156{
157 /** Pointer to the instance data - R3 Ptr. */
158 R3PTRTYPE(struct PITSTATE *) pPitR3;
159 /** The timer - R3 Ptr.
160 * @note Only channel 0 has a timer. */
161 PTMTIMERR3 pTimerR3;
162 /** Pointer to the instance data - R0 Ptr. */
163 R0PTRTYPE(struct PITSTATE *) pPitR0;
164 /** The timer - R0 Ptr.
165 * @note Only channel 0 has a timer. */
166 PTMTIMERR0 pTimerR0;
167 /** Pointer to the instance data - RC Ptr. */
168 RCPTRTYPE(struct PITSTATE *) pPitRC;
169 /** The timer - RC Ptr.
170 * @note Only channel 0 has a timer. */
171 PTMTIMERRC pTimerRC;
172 /** The virtual time stamp at the last reload. (only used in mode 2 for now) */
173 uint64_t u64ReloadTS;
174 /** The actual time of the next tick.
175 * As apposed to the next_transition_time which contains the correct time of the next tick. */
176 uint64_t u64NextTS;
177
178 /** (count_load_time is only set by TMTimerGet() which returns uint64_t) */
179 uint64_t count_load_time;
180 /* irq handling */
181 int64_t next_transition_time;
182 int32_t irq;
183 /** Number of release log entries. Used to prevent flooding. */
184 uint32_t cRelLogEntries;
185
186 uint32_t count; /* can be 65536 */
187 uint16_t latched_count;
188 uint8_t count_latched;
189 uint8_t status_latched;
190
191 uint8_t status;
192 uint8_t read_state;
193 uint8_t write_state;
194 uint8_t write_latch;
195
196 uint8_t rw_mode;
197 uint8_t mode;
198 uint8_t bcd; /* not supported */
199 uint8_t gate; /* timer start */
200
201} PITCHANNEL;
202/** Pointer to the state of one PIT channel. */
203typedef PITCHANNEL *PPITCHANNEL;
204
205/**
206 * The whole PIT state.
207 */
208typedef struct PITSTATE
209{
210 /** Channel state. Must come first? */
211 PITCHANNEL channels[3];
212 /** Speaker data. */
213 int32_t speaker_data_on;
214#ifdef FAKE_REFRESH_CLOCK
215 /** Speaker dummy. */
216 int32_t dummy_refresh_clock;
217#else
218 uint32_t Alignment1;
219#endif
220 /** Config: I/O port base. */
221 RTIOPORT IOPortBaseCfg;
222 /** Config: Speaker enabled. */
223 bool fSpeakerCfg;
224 bool fDisabledByHpet;
225 bool afAlignment0[HC_ARCH_BITS == 32 ? 4 : 4];
226 /** PIT port interface. */
227 PDMIHPETLEGACYNOTIFY IHpetLegacyNotify;
228 /** Pointer to the device instance. */
229 PPDMDEVINSR3 pDevIns;
230 /** Number of IRQs that's been raised. */
231 STAMCOUNTER StatPITIrq;
232 /** Profiling the timer callback handler. */
233 STAMPROFILEADV StatPITHandler;
234 /** Critical section protecting the state. */
235 PDMCRITSECT CritSect;
236} PITSTATE;
237/** Pointer to the PIT device state. */
238typedef PITSTATE *PPITSTATE;
239
240
241#ifndef VBOX_DEVICE_STRUCT_TESTCASE
242/*******************************************************************************
243* Internal Functions *
244*******************************************************************************/
245#ifdef IN_RING3
246static void pit_irq_timer_update(PPITCHANNEL pChan, uint64_t current_time, uint64_t now, bool in_timer);
247#endif
248
249
250
251static int pit_get_count(PPITCHANNEL pChan)
252{
253 uint64_t d;
254 PTMTIMER pTimer = pChan->CTX_SUFF(pPit)->channels[0].CTX_SUFF(pTimer);
255 Assert(TMTimerIsLockOwner(pTimer));
256
257 if (EFFECTIVE_MODE(pChan->mode) == 2)
258 {
259 if (pChan->u64NextTS == UINT64_MAX)
260 {
261 d = ASMMultU64ByU32DivByU32(TMTimerGet(pTimer) - pChan->count_load_time, PIT_FREQ, TMTimerGetFreq(pTimer));
262 return pChan->count - (d % pChan->count); /** @todo check this value. */
263 }
264 uint64_t Interval = pChan->u64NextTS - pChan->u64ReloadTS;
265 if (!Interval)
266 return pChan->count - 1; /** @todo This is WRONG! But I'm too tired to fix it properly and just want to shut up a DIV/0 trap now. */
267 d = TMTimerGet(pTimer);
268 d = ASMMultU64ByU32DivByU32(d - pChan->u64ReloadTS, pChan->count, Interval);
269 if (d >= pChan->count)
270 return 1;
271 return pChan->count - d;
272 }
273
274 d = ASMMultU64ByU32DivByU32(TMTimerGet(pTimer) - pChan->count_load_time, PIT_FREQ, TMTimerGetFreq(pTimer));
275 int counter;
276 switch (EFFECTIVE_MODE(pChan->mode))
277 {
278 case 0:
279 case 1:
280 case 4:
281 case 5:
282 counter = (pChan->count - d) & 0xffff;
283 break;
284 case 3:
285 /* XXX: may be incorrect for odd counts */
286 counter = pChan->count - ((2 * d) % pChan->count);
287 break;
288 default:
289 counter = pChan->count - (d % pChan->count);
290 break;
291 }
292 /** @todo check that we don't return 0, in most modes (all?) the counter shouldn't be zero. */
293 return counter;
294}
295
296/* get pit output bit */
297static int pit_get_out1(PPITCHANNEL pChan, int64_t current_time)
298{
299 PTMTIMER pTimer = pChan->CTX_SUFF(pPit)->channels[0].CTX_SUFF(pTimer);
300 uint64_t d;
301 int out;
302
303 d = ASMMultU64ByU32DivByU32(current_time - pChan->count_load_time, PIT_FREQ, TMTimerGetFreq(pTimer));
304 switch (EFFECTIVE_MODE(pChan->mode))
305 {
306 default:
307 case 0:
308 out = (d >= pChan->count);
309 break;
310 case 1:
311 out = (d < pChan->count);
312 break;
313 case 2:
314 Log2(("pit_get_out1: d=%llx c=%x %x \n", d, pChan->count, (unsigned)(d % pChan->count)));
315 if ((d % pChan->count) == 0 && d != 0)
316 out = 1;
317 else
318 out = 0;
319 break;
320 case 3:
321 out = (d % pChan->count) < ((pChan->count + 1) >> 1);
322 break;
323 case 4:
324 case 5:
325 out = (d != pChan->count);
326 break;
327 }
328 return out;
329}
330
331
332static int pit_get_out(PPITSTATE pThis, int channel, int64_t current_time)
333{
334 PPITCHANNEL pChan = &pThis->channels[channel];
335 return pit_get_out1(pChan, current_time);
336}
337
338
339static int pit_get_gate(PPITSTATE pThis, int channel)
340{
341 PPITCHANNEL pChan = &pThis->channels[channel];
342 return pChan->gate;
343}
344
345
346/* if already latched, do not latch again */
347static void pit_latch_count(PPITCHANNEL pChan)
348{
349 if (!pChan->count_latched)
350 {
351 pChan->latched_count = pit_get_count(pChan);
352 pChan->count_latched = pChan->rw_mode;
353 LogFlow(("pit_latch_count: latched_count=%#06x / %10RU64 ns (c=%#06x m=%d)\n",
354 pChan->latched_count, ASMMultU64ByU32DivByU32(pChan->count - pChan->latched_count, 1000000000, PIT_FREQ),
355 pChan->count, pChan->mode));
356 }
357}
358
359#ifdef IN_RING3
360
361/* val must be 0 or 1 */
362static void pit_set_gate(PPITSTATE pThis, int channel, int val)
363{
364 PPITCHANNEL pChan = &pThis->channels[channel];
365 PTMTIMER pTimer = pChan->CTX_SUFF(pPit)->channels[0].CTX_SUFF(pTimer);
366
367 Assert((val & 1) == val);
368 Assert(TMTimerIsLockOwner(pTimer));
369
370 switch (EFFECTIVE_MODE(pChan->mode))
371 {
372 default:
373 case 0:
374 case 4:
375 /* XXX: just disable/enable counting */
376 break;
377 case 1:
378 case 5:
379 if (pChan->gate < val)
380 {
381 /* restart counting on rising edge */
382 Log(("pit_set_gate: restarting mode %d\n", pChan->mode));
383 pChan->count_load_time = TMTimerGet(pTimer);
384 pit_irq_timer_update(pChan, pChan->count_load_time, pChan->count_load_time, false);
385 }
386 break;
387 case 2:
388 case 3:
389 if (pChan->gate < val)
390 {
391 /* restart counting on rising edge */
392 Log(("pit_set_gate: restarting mode %d\n", pChan->mode));
393 pChan->count_load_time = pChan->u64ReloadTS = TMTimerGet(pTimer);
394 pit_irq_timer_update(pChan, pChan->count_load_time, pChan->count_load_time, false);
395 }
396 /* XXX: disable/enable counting */
397 break;
398 }
399 pChan->gate = val;
400}
401
402static void pit_load_count(PPITCHANNEL pChan, int val)
403{
404 PTMTIMER pTimer = pChan->CTX_SUFF(pPit)->channels[0].CTX_SUFF(pTimer);
405 Assert(TMTimerIsLockOwner(pTimer));
406
407 if (val == 0)
408 val = 0x10000;
409 pChan->count_load_time = pChan->u64ReloadTS = TMTimerGet(pTimer);
410 pChan->count = val;
411 pit_irq_timer_update(pChan, pChan->count_load_time, pChan->count_load_time, false);
412
413 /* log the new rate (ch 0 only). */
414 if (pChan->pTimerR3 /* ch 0 */)
415 {
416 if (pChan->cRelLogEntries++ < 32)
417 LogRel(("PIT: mode=%d count=%#x (%u) - %d.%02d Hz (ch=0)\n",
418 pChan->mode, pChan->count, pChan->count, PIT_FREQ / pChan->count, (PIT_FREQ * 100 / pChan->count) % 100));
419 else
420 Log(("PIT: mode=%d count=%#x (%u) - %d.%02d Hz (ch=0)\n",
421 pChan->mode, pChan->count, pChan->count, PIT_FREQ / pChan->count, (PIT_FREQ * 100 / pChan->count) % 100));
422 TMTimerSetFrequencyHint(pChan->CTX_SUFF(pTimer), PIT_FREQ / pChan->count);
423 }
424 else
425 Log(("PIT: mode=%d count=%#x (%u) - %d.%02d Hz (ch=%d)\n",
426 pChan->mode, pChan->count, pChan->count, PIT_FREQ / pChan->count, (PIT_FREQ * 100 / pChan->count) % 100,
427 pChan - &pChan->CTX_SUFF(pPit)->channels[0]));
428}
429
430/* return -1 if no transition will occur. */
431static int64_t pit_get_next_transition_time(PPITCHANNEL pChan, uint64_t current_time)
432{
433 PTMTIMER pTimer = pChan->CTX_SUFF(pPit)->channels[0].CTX_SUFF(pTimer);
434 uint64_t d, next_time, base;
435 uint32_t period2;
436
437 d = ASMMultU64ByU32DivByU32(current_time - pChan->count_load_time, PIT_FREQ, TMTimerGetFreq(pTimer));
438 switch(EFFECTIVE_MODE(pChan->mode))
439 {
440 default:
441 case 0:
442 case 1:
443 if (d < pChan->count)
444 next_time = pChan->count;
445 else
446 return -1;
447 break;
448
449 /*
450 * Mode 2: The period is 'count' PIT ticks.
451 * When the counter reaches 1 we set the output low (for channel 0 that
452 * means lowering IRQ0). On the next tick, where we should be decrementing
453 * from 1 to 0, the count is loaded and the output goes high (channel 0
454 * means raising IRQ0 again and triggering timer interrupt).
455 *
456 * In VirtualBox we compress the pulse and flip-flop the IRQ line at the
457 * end of the period, which signals an interrupt at the exact same time.
458 */
459 case 2:
460 base = (d / pChan->count) * pChan->count;
461#ifndef VBOX /* see above */
462 if ((d - base) == 0 && d != 0)
463 next_time = base + pChan->count - 1;
464 else
465#endif
466 next_time = base + pChan->count;
467 break;
468 case 3:
469 base = (d / pChan->count) * pChan->count;
470 period2 = ((pChan->count + 1) >> 1);
471 if ((d - base) < period2)
472 next_time = base + period2;
473 else
474 next_time = base + pChan->count;
475 break;
476
477 /* Modes 4 and 5 generate a short pulse at the end of the time delay. This
478 * is similar to mode 2, except modes 4/5 aren't periodic. We use the same
479 * optimization - only use one timer callback and pulse the IRQ.
480 * Note: Tickless Linux kernels use PIT mode 4 with 'nolapic'.
481 */
482 case 4:
483 case 5:
484#ifdef VBOX
485 if (d <= pChan->count)
486 next_time = pChan->count;
487#else
488 if (d < pChan->count)
489 next_time = pChan->count;
490 else if (d == pChan->count)
491 next_time = pChan->count + 1;
492#endif
493 else
494 return -1;
495 break;
496 }
497
498 /* convert to timer units */
499 LogFlow(("PIT: next_time=%'14RU64 %'20RU64 mode=%#x count=%#06x\n", next_time,
500 ASMMultU64ByU32DivByU32(next_time, TMTimerGetFreq(pTimer), PIT_FREQ), pChan->mode, pChan->count));
501 next_time = pChan->count_load_time + ASMMultU64ByU32DivByU32(next_time, TMTimerGetFreq(pTimer), PIT_FREQ);
502
503 /* fix potential rounding problems */
504 if (next_time <= current_time)
505 next_time = current_time;
506
507 /* Add one to next_time; if we don't, integer truncation will cause
508 * the algorithm to think that at the end of each period, it'pChan still
509 * within the first one instead of at the beginning of the next one.
510 */
511 return next_time + 1;
512}
513
514static void pit_irq_timer_update(PPITCHANNEL pChan, uint64_t current_time, uint64_t now, bool in_timer)
515{
516 int64_t expire_time;
517 int irq_level;
518 PTMTIMER pTimer = pChan->CTX_SUFF(pPit)->channels[0].CTX_SUFF(pTimer);
519 Assert(TMTimerIsLockOwner(pTimer));
520
521 if (!pChan->CTX_SUFF(pTimer))
522 return;
523 expire_time = pit_get_next_transition_time(pChan, current_time);
524 irq_level = pit_get_out1(pChan, current_time) ? PDM_IRQ_LEVEL_HIGH : PDM_IRQ_LEVEL_LOW;
525
526 /* If PIT is disabled by HPET - simply disconnect ticks from interrupt controllers,
527 * but do not modify other aspects of device operation.
528 */
529 if (!pChan->pPitR3->fDisabledByHpet)
530 {
531 PPDMDEVINS pDevIns = pChan->CTX_SUFF(pPit)->pDevIns;
532
533 switch (EFFECTIVE_MODE(pChan->mode))
534 {
535 case 2:
536 case 4:
537 case 5:
538 /* We just flip-flop the IRQ line to save an extra timer call,
539 * which isn't generally required. However, the pulse is only
540 * generated when running on the timer callback (and thus on
541 * the trailing edge of the output signal pulse).
542 */
543 if (in_timer)
544 {
545 PDMDevHlpISASetIrq(pDevIns, pChan->irq, PDM_IRQ_LEVEL_FLIP_FLOP);
546 break;
547 }
548 /* Else fall through! */
549 default:
550 PDMDevHlpISASetIrq(pDevIns, pChan->irq, irq_level);
551 break;
552 }
553 }
554
555 if (irq_level)
556 {
557 pChan->u64ReloadTS = now;
558 STAM_COUNTER_INC(&pChan->CTX_SUFF(pPit)->StatPITIrq);
559 }
560
561 if (expire_time != -1)
562 {
563 Log3(("pit_irq_timer_update: next=%'RU64 now=%'RU64\n", expire_time, now));
564 pChan->u64NextTS = expire_time;
565 TMTimerSet(pChan->CTX_SUFF(pTimer), pChan->u64NextTS);
566 }
567 else
568 {
569 LogFlow(("PIT: m=%d count=%#4x irq_level=%#x stopped\n", pChan->mode, pChan->count, irq_level));
570 TMTimerStop(pChan->CTX_SUFF(pTimer));
571 pChan->u64NextTS = UINT64_MAX;
572 }
573 pChan->next_transition_time = expire_time;
574}
575
576#endif /* IN_RING3 */
577
578
579/**
580 * @callback_method_impl{FNIOMIOPORTIN}
581 */
582PDMBOTHCBDECL(int) pitIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
583{
584 Log2(("pitIOPortRead: Port=%#x cb=%x\n", Port, cb));
585 NOREF(pvUser);
586 Port &= 3;
587 if (cb != 1 || Port == 3)
588 {
589 Log(("pitIOPortRead: Port=%#x cb=%x *pu32=unused!\n", Port, cb));
590 return VERR_IOM_IOPORT_UNUSED;
591 }
592
593 PPITSTATE pThis = PDMINS_2_DATA(pDevIns, PPITSTATE);
594 PPITCHANNEL pChan = &pThis->channels[Port];
595 int ret;
596
597 DEVPIT_LOCK_RETURN(pThis, VINF_IOM_R3_IOPORT_READ);
598 if (pChan->status_latched)
599 {
600 pChan->status_latched = 0;
601 ret = pChan->status;
602 DEVPIT_UNLOCK(pThis);
603 }
604 else if (pChan->count_latched)
605 {
606 switch (pChan->count_latched)
607 {
608 default:
609 case RW_STATE_LSB:
610 ret = pChan->latched_count & 0xff;
611 pChan->count_latched = 0;
612 break;
613 case RW_STATE_MSB:
614 ret = pChan->latched_count >> 8;
615 pChan->count_latched = 0;
616 break;
617 case RW_STATE_WORD0:
618 ret = pChan->latched_count & 0xff;
619 pChan->count_latched = RW_STATE_MSB;
620 break;
621 }
622 DEVPIT_UNLOCK(pThis);
623 }
624 else
625 {
626 DEVPIT_UNLOCK(pThis);
627 DEVPIT_LOCK_BOTH_RETURN(pThis, VINF_IOM_R3_IOPORT_READ);
628 int count;
629 switch (pChan->read_state)
630 {
631 default:
632 case RW_STATE_LSB:
633 count = pit_get_count(pChan);
634 ret = count & 0xff;
635 break;
636 case RW_STATE_MSB:
637 count = pit_get_count(pChan);
638 ret = (count >> 8) & 0xff;
639 break;
640 case RW_STATE_WORD0:
641 count = pit_get_count(pChan);
642 ret = count & 0xff;
643 pChan->read_state = RW_STATE_WORD1;
644 break;
645 case RW_STATE_WORD1:
646 count = pit_get_count(pChan);
647 ret = (count >> 8) & 0xff;
648 pChan->read_state = RW_STATE_WORD0;
649 break;
650 }
651 DEVPIT_UNLOCK_BOTH(pThis);
652 }
653
654 *pu32 = ret;
655 Log2(("pitIOPortRead: Port=%#x cb=%x *pu32=%#04x\n", Port, cb, *pu32));
656 return VINF_SUCCESS;
657}
658
659
660/**
661 * @callback_method_impl{FNIOMIOPORTOUT}
662 */
663PDMBOTHCBDECL(int) pitIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
664{
665 Log2(("pitIOPortWrite: Port=%#x cb=%x u32=%#04x\n", Port, cb, u32));
666 NOREF(pvUser);
667 if (cb != 1)
668 return VINF_SUCCESS;
669
670 PPITSTATE pThis = PDMINS_2_DATA(pDevIns, PPITSTATE);
671 Port &= 3;
672 if (Port == 3)
673 {
674 /*
675 * Port 43h - Mode/Command Register.
676 * 7 6 5 4 3 2 1 0
677 * * * . . . . . . Select channel: 0 0 = Channel 0
678 * 0 1 = Channel 1
679 * 1 0 = Channel 2
680 * 1 1 = Read-back command (8254 only)
681 * (Illegal on 8253)
682 * (Illegal on PS/2 {JAM})
683 * . . * * . . . . Command/Access mode: 0 0 = Latch count value command
684 * 0 1 = Access mode: lobyte only
685 * 1 0 = Access mode: hibyte only
686 * 1 1 = Access mode: lobyte/hibyte
687 * . . . . * * * . Operating mode: 0 0 0 = Mode 0, 0 0 1 = Mode 1,
688 * 0 1 0 = Mode 2, 0 1 1 = Mode 3,
689 * 1 0 0 = Mode 4, 1 0 1 = Mode 5,
690 * 1 1 0 = Mode 2, 1 1 1 = Mode 3
691 * . . . . . . . * BCD/Binary mode: 0 = 16-bit binary, 1 = four-digit BCD
692 */
693 unsigned channel = u32 >> 6;
694 if (channel == 3)
695 {
696 /* read-back command */
697 DEVPIT_LOCK_BOTH_RETURN(pThis, VINF_IOM_R3_IOPORT_WRITE);
698 for (channel = 0; channel < RT_ELEMENTS(pThis->channels); channel++)
699 {
700 PPITCHANNEL pChan = &pThis->channels[channel];
701 if (u32 & (2 << channel)) {
702 if (!(u32 & 0x20))
703 pit_latch_count(pChan);
704 if (!(u32 & 0x10) && !pChan->status_latched)
705 {
706 /* status latch */
707 /* XXX: add BCD and null count */
708 PTMTIMER pTimer = pChan->CTX_SUFF(pPit)->channels[0].CTX_SUFF(pTimer);
709 pChan->status = (pit_get_out1(pChan, TMTimerGet(pTimer)) << 7)
710 | (pChan->rw_mode << 4)
711 | (pChan->mode << 1)
712 | pChan->bcd;
713 pChan->status_latched = 1;
714 }
715 }
716 }
717 DEVPIT_UNLOCK_BOTH(pThis);
718 }
719 else
720 {
721 PPITCHANNEL pChan = &pThis->channels[channel];
722 unsigned access = (u32 >> 4) & 3;
723 if (access == 0)
724 {
725 DEVPIT_LOCK_BOTH_RETURN(pThis, VINF_IOM_R3_IOPORT_WRITE);
726 pit_latch_count(pChan);
727 DEVPIT_UNLOCK_BOTH(pThis);
728 }
729 else
730 {
731 DEVPIT_LOCK_RETURN(pThis, VINF_IOM_R3_IOPORT_WRITE);
732 pChan->rw_mode = access;
733 pChan->read_state = access;
734 pChan->write_state = access;
735
736 pChan->mode = (u32 >> 1) & 7;
737 pChan->bcd = u32 & 1;
738 /* XXX: update irq timer ? */
739 DEVPIT_UNLOCK(pThis);
740 }
741 }
742 }
743 else
744 {
745#ifndef IN_RING3
746 /** @todo There is no reason not to do this in all contexts these
747 * days... */
748 return VINF_IOM_R3_IOPORT_WRITE;
749#else /* IN_RING3 */
750 /*
751 * Port 40-42h - Channel Data Ports.
752 */
753 PPITCHANNEL pChan = &pThis->channels[Port];
754 uint8_t const write_state = pChan->write_state;
755 DEVPIT_LOCK_BOTH_RETURN(pThis, VINF_IOM_R3_IOPORT_WRITE);
756 switch (pChan->write_state)
757 {
758 default:
759 case RW_STATE_LSB:
760 pit_load_count(pChan, u32);
761 break;
762 case RW_STATE_MSB:
763 pit_load_count(pChan, u32 << 8);
764 break;
765 case RW_STATE_WORD0:
766 pChan->write_latch = u32;
767 pChan->write_state = RW_STATE_WORD1;
768 break;
769 case RW_STATE_WORD1:
770 pit_load_count(pChan, pChan->write_latch | (u32 << 8));
771 pChan->write_state = RW_STATE_WORD0;
772 break;
773 }
774 DEVPIT_UNLOCK_BOTH(pThis);
775#endif /* !IN_RING3 */
776 }
777 return VINF_SUCCESS;
778}
779
780
781/**
782 * @callback_method_impl{FNIOMIOPORTIN, Speaker}
783 */
784PDMBOTHCBDECL(int) pitIOPortSpeakerRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
785{
786 NOREF(pvUser);
787 if (cb == 1)
788 {
789 PPITSTATE pThis = PDMINS_2_DATA(pDevIns, PPITSTATE);
790 DEVPIT_LOCK_BOTH_RETURN(pThis, VINF_IOM_R3_IOPORT_READ);
791
792 const uint64_t u64Now = TMTimerGet(pThis->channels[0].CTX_SUFF(pTimer));
793 Assert(TMTimerGetFreq(pThis->channels[0].CTX_SUFF(pTimer)) == 1000000000); /* lazy bird. */
794
795 /* bit 6,7 Parity error stuff. */
796 /* bit 5 - mirrors timer 2 output condition. */
797 const int fOut = pit_get_out(pThis, 2, u64Now);
798 /* bit 4 - toggled with each (DRAM?) refresh request, every 15.085 µpChan.
799 ASSUMES ns timer freq, see assertion above. */
800#ifndef FAKE_REFRESH_CLOCK
801 const int fRefresh = (u64Now / 15085) & 1;
802#else
803 pThis->dummy_refresh_clock ^= 1;
804 const int fRefresh = pThis->dummy_refresh_clock;
805#endif
806 /* bit 2,3 NMI / parity status stuff. */
807 /* bit 1 - speaker data status */
808 const int fSpeakerStatus = pThis->speaker_data_on;
809 /* bit 0 - timer 2 clock gate to speaker status. */
810 const int fTimer2GateStatus = pit_get_gate(pThis, 2);
811
812 DEVPIT_UNLOCK_BOTH(pThis);
813
814 *pu32 = fTimer2GateStatus
815 | (fSpeakerStatus << 1)
816 | (fRefresh << 4)
817 | (fOut << 5);
818 Log(("pitIOPortSpeakerRead: Port=%#x cb=%x *pu32=%#x\n", Port, cb, *pu32));
819 return VINF_SUCCESS;
820 }
821 Log(("pitIOPortSpeakerRead: Port=%#x cb=%x *pu32=unused!\n", Port, cb));
822 return VERR_IOM_IOPORT_UNUSED;
823}
824
825#ifdef IN_RING3
826
827/**
828 * @callback_method_impl{FNIOMIOPORTOUT, Speaker}
829 */
830PDMBOTHCBDECL(int) pitIOPortSpeakerWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
831{
832 NOREF(pvUser);
833 if (cb == 1)
834 {
835 PPITSTATE pThis = PDMINS_2_DATA(pDevIns, PPITSTATE);
836 DEVPIT_LOCK_BOTH_RETURN(pThis, VERR_IGNORED);
837
838 pThis->speaker_data_on = (u32 >> 1) & 1;
839 pit_set_gate(pThis, 2, u32 & 1);
840
841 DEVPIT_UNLOCK_BOTH(pThis);
842 }
843 Log(("pitIOPortSpeakerWrite: Port=%#x cb=%x u32=%#x\n", Port, cb, u32));
844 return VINF_SUCCESS;
845}
846
847
848/* -=-=-=-=-=- Saved state -=-=-=-=-=- */
849
850/**
851 * @callback_method_impl{FNSSMDEVLIVEEXEC}
852 */
853static DECLCALLBACK(int) pitLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
854{
855 PPITSTATE pThis = PDMINS_2_DATA(pDevIns, PPITSTATE);
856 SSMR3PutIOPort(pSSM, pThis->IOPortBaseCfg);
857 SSMR3PutU8( pSSM, pThis->channels[0].irq);
858 SSMR3PutBool( pSSM, pThis->fSpeakerCfg);
859 return VINF_SSM_DONT_CALL_AGAIN;
860}
861
862
863/**
864 * @callback_method_impl{FNSSMDEVSAVEEXEC}
865 */
866static DECLCALLBACK(int) pitSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
867{
868 PPITSTATE pThis = PDMINS_2_DATA(pDevIns, PPITSTATE);
869 PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
870
871 /* The config. */
872 pitLiveExec(pDevIns, pSSM, SSM_PASS_FINAL);
873
874 /* The state. */
875 for (unsigned i = 0; i < RT_ELEMENTS(pThis->channels); i++)
876 {
877 PPITCHANNEL pChan = &pThis->channels[i];
878 SSMR3PutU32(pSSM, pChan->count);
879 SSMR3PutU16(pSSM, pChan->latched_count);
880 SSMR3PutU8(pSSM, pChan->count_latched);
881 SSMR3PutU8(pSSM, pChan->status_latched);
882 SSMR3PutU8(pSSM, pChan->status);
883 SSMR3PutU8(pSSM, pChan->read_state);
884 SSMR3PutU8(pSSM, pChan->write_state);
885 SSMR3PutU8(pSSM, pChan->write_latch);
886 SSMR3PutU8(pSSM, pChan->rw_mode);
887 SSMR3PutU8(pSSM, pChan->mode);
888 SSMR3PutU8(pSSM, pChan->bcd);
889 SSMR3PutU8(pSSM, pChan->gate);
890 SSMR3PutU64(pSSM, pChan->count_load_time);
891 SSMR3PutU64(pSSM, pChan->u64NextTS);
892 SSMR3PutU64(pSSM, pChan->u64ReloadTS);
893 SSMR3PutS64(pSSM, pChan->next_transition_time);
894 if (pChan->CTX_SUFF(pTimer))
895 TMR3TimerSave(pChan->CTX_SUFF(pTimer), pSSM);
896 }
897
898 SSMR3PutS32(pSSM, pThis->speaker_data_on);
899#ifdef FAKE_REFRESH_CLOCK
900 SSMR3PutS32(pSSM, pThis->dummy_refresh_clock);
901#else
902 SSMR3PutS32(pSSM, 0);
903#endif
904
905 SSMR3PutBool(pSSM, pThis->fDisabledByHpet);
906
907 PDMCritSectLeave(&pThis->CritSect);
908 return VINF_SUCCESS;
909}
910
911
912/**
913 * @callback_method_impl{FNSSMDEVLOADEXEC}
914 */
915static DECLCALLBACK(int) pitLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
916{
917 PPITSTATE pThis = PDMINS_2_DATA(pDevIns, PPITSTATE);
918 int rc;
919
920 if ( uVersion != PIT_SAVED_STATE_VERSION
921 && uVersion != PIT_SAVED_STATE_VERSION_VBOX_30
922 && uVersion != PIT_SAVED_STATE_VERSION_VBOX_31)
923 return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
924
925 /* The config. */
926 if (uVersion > PIT_SAVED_STATE_VERSION_VBOX_30)
927 {
928 RTIOPORT IOPortBaseCfg;
929 rc = SSMR3GetIOPort(pSSM, &IOPortBaseCfg); AssertRCReturn(rc, rc);
930 if (IOPortBaseCfg != pThis->IOPortBaseCfg)
931 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - IOPortBaseCfg: saved=%RTiop config=%RTiop"),
932 IOPortBaseCfg, pThis->IOPortBaseCfg);
933
934 uint8_t u8Irq;
935 rc = SSMR3GetU8(pSSM, &u8Irq); AssertRCReturn(rc, rc);
936 if (u8Irq != pThis->channels[0].irq)
937 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - u8Irq: saved=%#x config=%#x"),
938 u8Irq, pThis->channels[0].irq);
939
940 bool fSpeakerCfg;
941 rc = SSMR3GetBool(pSSM, &fSpeakerCfg); AssertRCReturn(rc, rc);
942 if (fSpeakerCfg != pThis->fSpeakerCfg)
943 return SSMR3SetCfgError(pSSM, RT_SRC_POS, N_("Config mismatch - fSpeakerCfg: saved=%RTbool config=%RTbool"),
944 fSpeakerCfg, pThis->fSpeakerCfg);
945 }
946
947 if (uPass != SSM_PASS_FINAL)
948 return VINF_SUCCESS;
949
950 /* The state. */
951 for (unsigned i = 0; i < RT_ELEMENTS(pThis->channels); i++)
952 {
953 PPITCHANNEL pChan = &pThis->channels[i];
954 SSMR3GetU32(pSSM, &pChan->count);
955 SSMR3GetU16(pSSM, &pChan->latched_count);
956 SSMR3GetU8(pSSM, &pChan->count_latched);
957 SSMR3GetU8(pSSM, &pChan->status_latched);
958 SSMR3GetU8(pSSM, &pChan->status);
959 SSMR3GetU8(pSSM, &pChan->read_state);
960 SSMR3GetU8(pSSM, &pChan->write_state);
961 SSMR3GetU8(pSSM, &pChan->write_latch);
962 SSMR3GetU8(pSSM, &pChan->rw_mode);
963 SSMR3GetU8(pSSM, &pChan->mode);
964 SSMR3GetU8(pSSM, &pChan->bcd);
965 SSMR3GetU8(pSSM, &pChan->gate);
966 SSMR3GetU64(pSSM, &pChan->count_load_time);
967 SSMR3GetU64(pSSM, &pChan->u64NextTS);
968 SSMR3GetU64(pSSM, &pChan->u64ReloadTS);
969 SSMR3GetS64(pSSM, &pChan->next_transition_time);
970 if (pChan->CTX_SUFF(pTimer))
971 {
972 TMR3TimerLoad(pChan->CTX_SUFF(pTimer), pSSM);
973 LogRel(("PIT: mode=%d count=%#x (%u) - %d.%02d Hz (ch=%d) (restore)\n",
974 pChan->mode, pChan->count, pChan->count, PIT_FREQ / pChan->count, (PIT_FREQ * 100 / pChan->count) % 100, i));
975 PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
976 TMTimerSetFrequencyHint(pChan->CTX_SUFF(pTimer), PIT_FREQ / pChan->count);
977 PDMCritSectLeave(&pThis->CritSect);
978 }
979 pThis->channels[i].cRelLogEntries = 0;
980 }
981
982 SSMR3GetS32(pSSM, &pThis->speaker_data_on);
983#ifdef FAKE_REFRESH_CLOCK
984 SSMR3GetS32(pSSM, &pThis->dummy_refresh_clock);
985#else
986 int32_t u32Dummy;
987 SSMR3GetS32(pSSM, &u32Dummy);
988#endif
989 if (uVersion > PIT_SAVED_STATE_VERSION_VBOX_31)
990 SSMR3GetBool(pSSM, &pThis->fDisabledByHpet);
991
992 return VINF_SUCCESS;
993}
994
995
996/* -=-=-=-=-=- Timer -=-=-=-=-=- */
997
998/**
999 * @callback_method_impl{FNTMTIMERDEV}
1000 * @param pvUser Pointer to the PIT channel state.
1001 */
1002static DECLCALLBACK(void) pitTimer(PPDMDEVINS pDevIns, PTMTIMER pTimer, void *pvUser)
1003{
1004 PPITCHANNEL pChan = (PPITCHANNEL)pvUser;
1005 STAM_PROFILE_ADV_START(&pChan->CTX_SUFF(pPit)->StatPITHandler, a);
1006
1007 Log(("pitTimer\n"));
1008 Assert(PDMCritSectIsOwner(&PDMINS_2_DATA(pDevIns, PPITSTATE)->CritSect));
1009 Assert(TMTimerIsLockOwner(pTimer));
1010
1011 pit_irq_timer_update(pChan, pChan->next_transition_time, TMTimerGet(pTimer), true);
1012
1013 STAM_PROFILE_ADV_STOP(&pChan->CTX_SUFF(pPit)->StatPITHandler, a);
1014}
1015
1016
1017/* -=-=-=-=-=- Debug Info -=-=-=-=-=- */
1018
1019/**
1020 * @callback_method_impl{FNDBGFHANDLERDEV}
1021 */
1022static DECLCALLBACK(void) pitInfo(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
1023{
1024 PPITSTATE pThis = PDMINS_2_DATA(pDevIns, PPITSTATE);
1025 unsigned i;
1026 for (i = 0; i < RT_ELEMENTS(pThis->channels); i++)
1027 {
1028 const PITCHANNEL *pChan = &pThis->channels[i];
1029
1030 pHlp->pfnPrintf(pHlp,
1031 "PIT (i8254) channel %d status: irq=%#x\n"
1032 " count=%08x" " latched_count=%04x count_latched=%02x\n"
1033 " status=%02x status_latched=%02x read_state=%02x\n"
1034 " write_state=%02x write_latch=%02x rw_mode=%02x\n"
1035 " mode=%02x bcd=%02x gate=%02x\n"
1036 " count_load_time=%016RX64 next_transition_time=%016RX64\n"
1037 " u64ReloadTS=%016RX64 u64NextTS=%016RX64\n"
1038 ,
1039 i, pChan->irq,
1040 pChan->count, pChan->latched_count, pChan->count_latched,
1041 pChan->status, pChan->status_latched, pChan->read_state,
1042 pChan->write_state, pChan->write_latch, pChan->rw_mode,
1043 pChan->mode, pChan->bcd, pChan->gate,
1044 pChan->count_load_time, pChan->next_transition_time,
1045 pChan->u64ReloadTS, pChan->u64NextTS);
1046 }
1047#ifdef FAKE_REFRESH_CLOCK
1048 pHlp->pfnPrintf(pHlp, "speaker_data_on=%#x dummy_refresh_clock=%#x\n",
1049 pThis->speaker_data_on, pThis->dummy_refresh_clock);
1050#else
1051 pHlp->pfnPrintf(pHlp, "speaker_data_on=%#x\n", pThis->speaker_data_on);
1052#endif
1053 if (pThis->fDisabledByHpet)
1054 pHlp->pfnPrintf(pHlp, "Disabled by HPET\n");
1055}
1056
1057
1058/* -=-=-=-=-=- IHpetLegacyNotify -=-=-=-=-=- */
1059
1060/**
1061 * @interface_method_impl{PDMIHPETLEGACYNOTIFY,pfnModeChanged}
1062 */
1063static DECLCALLBACK(void) pitNotifyHpetLegacyNotify_ModeChanged(PPDMIHPETLEGACYNOTIFY pInterface, bool fActivated)
1064{
1065 PPITSTATE pThis = RT_FROM_MEMBER(pInterface, PITSTATE, IHpetLegacyNotify);
1066 PDMCritSectEnter(&pThis->CritSect, VERR_IGNORED);
1067
1068 pThis->fDisabledByHpet = fActivated;
1069
1070 PDMCritSectLeave(&pThis->CritSect);
1071}
1072
1073
1074/* -=-=-=-=-=- PDMDEVINS::IBase -=-=-=-=-=- */
1075
1076/**
1077 * @interface_method_impl{PDMIBASE,pfnQueryInterface}
1078 */
1079static DECLCALLBACK(void *) pitQueryInterface(PPDMIBASE pInterface, const char *pszIID)
1080{
1081 PPDMDEVINS pDevIns = RT_FROM_MEMBER(pInterface, PDMDEVINS, IBase);
1082 PPITSTATE pThis = PDMINS_2_DATA(pDevIns, PPITSTATE);
1083 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDevIns->IBase);
1084 PDMIBASE_RETURN_INTERFACE(pszIID, PDMIHPETLEGACYNOTIFY, &pThis->IHpetLegacyNotify);
1085 return NULL;
1086}
1087
1088
1089/* -=-=-=-=-=- PDMDEVREG -=-=-=-=-=- */
1090
1091/**
1092 * @interface_method_impl{PDMDEVREG,pfnRelocate}
1093 */
1094static DECLCALLBACK(void) pitRelocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
1095{
1096 PPITSTATE pThis = PDMINS_2_DATA(pDevIns, PPITSTATE);
1097 LogFlow(("pitRelocate: \n"));
1098
1099 for (unsigned i = 0; i < RT_ELEMENTS(pThis->channels); i++)
1100 {
1101 PPITCHANNEL pChan = &pThis->channels[i];
1102 if (pChan->pTimerR3)
1103 pChan->pTimerRC = TMTimerRCPtr(pChan->pTimerR3);
1104 pThis->channels[i].pPitRC = PDMINS_2_DATA_RCPTR(pDevIns);
1105 }
1106}
1107
1108
1109/**
1110 * @interface_method_impl{PDMDEVREG,pfnReset}
1111 */
1112static DECLCALLBACK(void) pitReset(PPDMDEVINS pDevIns)
1113{
1114 PPITSTATE pThis = PDMINS_2_DATA(pDevIns, PPITSTATE);
1115 LogFlow(("pitReset: \n"));
1116
1117 DEVPIT_R3_LOCK_BOTH(pThis);
1118
1119 pThis->fDisabledByHpet = false;
1120
1121 for (unsigned i = 0; i < RT_ELEMENTS(pThis->channels); i++)
1122 {
1123 PPITCHANNEL pChan = &pThis->channels[i];
1124
1125#if 1 /* Set everything back to virgin state. (might not be strictly correct) */
1126 pChan->latched_count = 0;
1127 pChan->count_latched = 0;
1128 pChan->status_latched = 0;
1129 pChan->status = 0;
1130 pChan->read_state = 0;
1131 pChan->write_state = 0;
1132 pChan->write_latch = 0;
1133 pChan->rw_mode = 0;
1134 pChan->bcd = 0;
1135#endif
1136 pChan->u64NextTS = UINT64_MAX;
1137 pChan->cRelLogEntries = 0;
1138 pChan->mode = 3;
1139 pChan->gate = (i != 2);
1140 pit_load_count(pChan, 0);
1141 }
1142
1143 DEVPIT_UNLOCK_BOTH(pThis);
1144}
1145
1146
1147/**
1148 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1149 */
1150static DECLCALLBACK(int) pitConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1151{
1152 PPITSTATE pThis = PDMINS_2_DATA(pDevIns, PPITSTATE);
1153 int rc;
1154 uint8_t u8Irq;
1155 uint16_t u16Base;
1156 bool fSpeaker;
1157 bool fGCEnabled;
1158 bool fR0Enabled;
1159 unsigned i;
1160 Assert(iInstance == 0);
1161
1162 /*
1163 * Validate configuration.
1164 */
1165 if (!CFGMR3AreValuesValid(pCfg, "Irq\0" "Base\0" "SpeakerEnabled\0" "GCEnabled\0" "R0Enabled\0"))
1166 return VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
1167
1168 /*
1169 * Init the data.
1170 */
1171 rc = CFGMR3QueryU8Def(pCfg, "Irq", &u8Irq, 0);
1172 if (RT_FAILURE(rc))
1173 return PDMDEV_SET_ERROR(pDevIns, rc,
1174 N_("Configuration error: Querying \"Irq\" as a uint8_t failed"));
1175
1176 rc = CFGMR3QueryU16Def(pCfg, "Base", &u16Base, 0x40);
1177 if (RT_FAILURE(rc))
1178 return PDMDEV_SET_ERROR(pDevIns, rc,
1179 N_("Configuration error: Querying \"Base\" as a uint16_t failed"));
1180
1181 rc = CFGMR3QueryBoolDef(pCfg, "SpeakerEnabled", &fSpeaker, true);
1182 if (RT_FAILURE(rc))
1183 return PDMDEV_SET_ERROR(pDevIns, rc,
1184 N_("Configuration error: Querying \"SpeakerEnabled\" as a bool failed"));
1185
1186 rc = CFGMR3QueryBoolDef(pCfg, "GCEnabled", &fGCEnabled, true);
1187 if (RT_FAILURE(rc))
1188 return PDMDEV_SET_ERROR(pDevIns, rc,
1189 N_("Configuration error: Querying \"GCEnabled\" as a bool failed"));
1190
1191 rc = CFGMR3QueryBoolDef(pCfg, "R0Enabled", &fR0Enabled, true);
1192 if (RT_FAILURE(rc))
1193 return PDMDEV_SET_ERROR(pDevIns, rc,
1194 N_("Configuration error: failed to read R0Enabled as boolean"));
1195
1196 pThis->pDevIns = pDevIns;
1197 pThis->IOPortBaseCfg = u16Base;
1198 pThis->fSpeakerCfg = fSpeaker;
1199 pThis->channels[0].irq = u8Irq;
1200 for (i = 0; i < RT_ELEMENTS(pThis->channels); i++)
1201 {
1202 pThis->channels[i].pPitR3 = pThis;
1203 pThis->channels[i].pPitR0 = PDMINS_2_DATA_R0PTR(pDevIns);
1204 pThis->channels[i].pPitRC = PDMINS_2_DATA_RCPTR(pDevIns);
1205 }
1206
1207 /*
1208 * Interfaces
1209 */
1210 /* IBase */
1211 pDevIns->IBase.pfnQueryInterface = pitQueryInterface;
1212 /* IHpetLegacyNotify */
1213 pThis->IHpetLegacyNotify.pfnModeChanged = pitNotifyHpetLegacyNotify_ModeChanged;
1214
1215 /*
1216 * We do our own locking. This must be done before creating timers.
1217 */
1218 rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "pit#%u", iInstance);
1219 AssertRCReturn(rc, rc);
1220
1221 rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
1222 AssertRCReturn(rc, rc);
1223
1224 /*
1225 * Create the timer, make it take our critsect.
1226 */
1227 rc = PDMDevHlpTMTimerCreate(pDevIns, TMCLOCK_VIRTUAL_SYNC, pitTimer, &pThis->channels[0],
1228 TMTIMER_FLAGS_NO_CRIT_SECT, "i8254 Programmable Interval Timer",
1229 &pThis->channels[0].pTimerR3);
1230 if (RT_FAILURE(rc))
1231 return rc;
1232 pThis->channels[0].pTimerRC = TMTimerRCPtr(pThis->channels[0].pTimerR3);
1233 pThis->channels[0].pTimerR0 = TMTimerR0Ptr(pThis->channels[0].pTimerR3);
1234 rc = TMR3TimerSetCritSect(pThis->channels[0].pTimerR3, &pThis->CritSect);
1235 AssertRCReturn(rc, rc);
1236
1237 /*
1238 * Register I/O ports.
1239 */
1240 rc = PDMDevHlpIOPortRegister(pDevIns, u16Base, 4, NULL, pitIOPortWrite, pitIOPortRead, NULL, NULL, "i8254 Programmable Interval Timer");
1241 if (RT_FAILURE(rc))
1242 return rc;
1243 if (fGCEnabled)
1244 {
1245 rc = PDMDevHlpIOPortRegisterRC(pDevIns, u16Base, 4, 0, "pitIOPortWrite", "pitIOPortRead", NULL, NULL, "i8254 Programmable Interval Timer");
1246 if (RT_FAILURE(rc))
1247 return rc;
1248 }
1249 if (fR0Enabled)
1250 {
1251 rc = PDMDevHlpIOPortRegisterR0(pDevIns, u16Base, 4, 0, "pitIOPortWrite", "pitIOPortRead", NULL, NULL, "i8254 Programmable Interval Timer");
1252 if (RT_FAILURE(rc))
1253 return rc;
1254 }
1255
1256 if (fSpeaker)
1257 {
1258 rc = PDMDevHlpIOPortRegister(pDevIns, 0x61, 1, NULL, pitIOPortSpeakerWrite, pitIOPortSpeakerRead, NULL, NULL, "PC Speaker");
1259 if (RT_FAILURE(rc))
1260 return rc;
1261 if (fGCEnabled)
1262 {
1263 rc = PDMDevHlpIOPortRegisterRC(pDevIns, 0x61, 1, 0, NULL, "pitIOPortSpeakerRead", NULL, NULL, "PC Speaker");
1264 if (RT_FAILURE(rc))
1265 return rc;
1266 }
1267 }
1268
1269 /*
1270 * Saved state.
1271 */
1272 rc = PDMDevHlpSSMRegister3(pDevIns, PIT_SAVED_STATE_VERSION, sizeof(*pThis), pitLiveExec, pitSaveExec, pitLoadExec);
1273 if (RT_FAILURE(rc))
1274 return rc;
1275
1276 /*
1277 * Initialize the device state.
1278 */
1279 pitReset(pDevIns);
1280
1281 /*
1282 * Register statistics and debug info.
1283 */
1284 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatPITIrq, STAMTYPE_COUNTER, "/TM/PIT/Irq", STAMUNIT_OCCURENCES, "The number of times a timer interrupt was triggered.");
1285 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatPITHandler, STAMTYPE_PROFILE, "/TM/PIT/Handler", STAMUNIT_TICKS_PER_CALL, "Profiling timer callback handler.");
1286
1287 PDMDevHlpDBGFInfoRegister(pDevIns, "pit", "Display PIT (i8254) status. (no arguments)", pitInfo);
1288
1289 return VINF_SUCCESS;
1290}
1291
1292
1293/**
1294 * The device registration structure.
1295 */
1296const PDMDEVREG g_DeviceI8254 =
1297{
1298 /* u32Version */
1299 PDM_DEVREG_VERSION,
1300 /* szName */
1301 "i8254",
1302 /* szRCMod */
1303 "VBoxDDGC.gc",
1304 /* szR0Mod */
1305 "VBoxDDR0.r0",
1306 /* pszDescription */
1307 "Intel 8254 Programmable Interval Timer (PIT) And Dummy Speaker Device",
1308 /* fFlags */
1309 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,
1310 /* fClass */
1311 PDM_DEVREG_CLASS_PIT,
1312 /* cMaxInstances */
1313 1,
1314 /* cbInstance */
1315 sizeof(PITSTATE),
1316 /* pfnConstruct */
1317 pitConstruct,
1318 /* pfnDestruct */
1319 NULL,
1320 /* pfnRelocate */
1321 pitRelocate,
1322 /* pfnMemSetup */
1323 NULL,
1324 /* pfnPowerOn */
1325 NULL,
1326 /* pfnReset */
1327 pitReset,
1328 /* pfnSuspend */
1329 NULL,
1330 /* pfnResume */
1331 NULL,
1332 /* pfnAttach */
1333 NULL,
1334 /* pfnDetach */
1335 NULL,
1336 /* pfnQueryInterface */
1337 NULL,
1338 /* pfnInitComplete */
1339 NULL,
1340 /* pfnPowerOff */
1341 NULL,
1342 /* pfnSoftReset */
1343 NULL,
1344 /* u32VersionEnd */
1345 PDM_DEVREG_VERSION
1346};
1347
1348#endif /* IN_RING3 */
1349#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
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