VirtualBox

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

Last change on this file since 9152 was 8155, checked in by vboxsync, 17 years ago

The Big Sun Rebranding Header Change

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