VirtualBox

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

Last change on this file since 59381 was 57358, checked in by vboxsync, 9 years ago

*: scm cleanup run.

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