VirtualBox

source: vbox/trunk/src/VBox/Devices/PC/DevDMA.cpp

Last change on this file was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 46.8 KB
Line 
1/* $Id: DevDMA.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * DevDMA - DMA Controller Device.
4 */
5
6/*
7 * Copyright (C) 2006-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 * --------------------------------------------------------------------
27 *
28 * This code is loosely based on:
29 *
30 * QEMU DMA emulation
31 *
32 * Copyright (c) 2003 Vassili Karpov (malc)
33 *
34 * Permission is hereby granted, free of charge, to any person obtaining a copy
35 * of this software and associated documentation files (the "Software"), to deal
36 * in the Software without restriction, including without limitation the rights
37 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
38 * copies of the Software, and to permit persons to whom the Software is
39 * furnished to do so, subject to the following conditions:
40 *
41 * The above copyright notice and this permission notice shall be included in
42 * all copies or substantial portions of the Software.
43 *
44 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
45 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
46 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
47 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
48 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
49 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
50 * THE SOFTWARE.
51 */
52
53
54/*********************************************************************************************************************************
55* Header Files *
56*********************************************************************************************************************************/
57#define LOG_GROUP LOG_GROUP_DEV_DMA
58#include <VBox/vmm/pdmdev.h>
59#include <VBox/err.h>
60
61#include <VBox/AssertGuest.h>
62#include <VBox/log.h>
63#include <iprt/assert.h>
64#include <iprt/string.h>
65
66#include "VBoxDD.h"
67
68
69/** @page pg_dev_dma DMA Overview and notes
70 *
71 * Modern PCs typically emulate AT-compatible DMA. The IBM PC/AT used dual
72 * cascaded 8237A DMA controllers, augmented with a 74LS612 memory mapper.
73 * The 8237As are 8-bit parts, only capable of addressing up to 64KB; the
74 * 74LS612 extends addressing to 24 bits. That leads to well known and
75 * inconvenient DMA limitations:
76 * - DMA can only access physical memory under the 16MB line
77 * - DMA transfers must occur within a 64KB/128KB 'page'
78 *
79 * The 16-bit DMA controller added in the PC/AT shifts all 8237A addresses
80 * left by one, including the control registers addresses. The DMA register
81 * offsets (except for the page registers) are therefore "double spaced".
82 *
83 * Due to the address shifting, the DMA controller decodes more addresses
84 * than are usually documented, with aliasing. See the ICH8 datasheet.
85 *
86 * In the IBM PC and PC/XT, DMA channel 0 was used for memory refresh, thus
87 * preventing the use of memory-to-memory DMA transfers (which use channels
88 * 0 and 1). In the PC/AT, memory-to-memory DMA was theoretically possible.
89 * However, it would transfer a single byte at a time, while the CPU can
90 * transfer two (on a 286) or four (on a 386+) bytes at a time. On many
91 * compatibles, memory-to-memory DMA is not even implemented at all, and
92 * therefore has no practical use.
93 *
94 * Auto-init mode is handled implicitly; a device's transfer handler may
95 * return an end count lower than the start count.
96 *
97 * Naming convention: 'channel' refers to a system-wide DMA channel (0-7)
98 * while 'chidx' refers to a DMA channel index within a controller (0-3).
99 *
100 * References:
101 * - IBM Personal Computer AT Technical Reference, 1984
102 * - Intel 8237A-5 Datasheet, 1993
103 * - Frank van Gilluwe, The Undocumented PC, 1994
104 * - OPTi 82C206 Data Book, 1996 (or Chips & Tech 82C206)
105 * - Intel ICH8 Datasheet, 2007
106 */
107
108
109/* Saved state versions. */
110#define DMA_SAVESTATE_OLD 1 /* The original saved state. */
111#define DMA_SAVESTATE_CURRENT 2 /* The new and improved saved state. */
112
113/* State information for a single DMA channel. */
114typedef struct {
115 PPDMDEVINS pDevInsHandler; /**< The device instance the channel is associated with. */
116 RTR3PTR pvUser; /* User specific context. */
117 R3PTRTYPE(PFNDMATRANSFERHANDLER) pfnXferHandler; /* Transfer handler for channel. */
118 uint16_t u16BaseAddr; /* Base address for transfers. */
119 uint16_t u16BaseCount; /* Base count for transfers. */
120 uint16_t u16CurAddr; /* Current address. */
121 uint16_t u16CurCount; /* Current count. */
122 uint8_t u8Mode; /* Channel mode. */
123 uint8_t abPadding[7];
124} DMAChannel, DMACHANNEL;
125typedef DMACHANNEL *PDMACHANNEL;
126
127/* State information for a DMA controller (DMA8 or DMA16). */
128typedef struct {
129 DMAChannel ChState[4]; /* Per-channel state. */
130 uint8_t au8Page[8]; /* Page registers (A16-A23). */
131 uint8_t au8PageHi[8]; /* High page registers (A24-A31). */
132 uint8_t u8Command; /* Command register. */
133 uint8_t u8Status; /* Status register. */
134 uint8_t u8Mask; /* Mask register. */
135 uint8_t u8Temp; /* Temporary (mem/mem) register. */
136 uint8_t u8ModeCtr; /* Mode register counter for reads. */
137 bool fHiByte; /* Byte pointer (T/F -> high/low). */
138 uint8_t abPadding0[2];
139 uint32_t is16bit; /* True for 16-bit DMA. */
140 uint8_t abPadding1[4];
141 /** The base abd current address I/O port registration. */
142 IOMIOPORTHANDLE hIoPortBase;
143 /** The control register I/O port registration. */
144 IOMIOPORTHANDLE hIoPortCtl;
145 /** The page registers I/O port registration. */
146 IOMIOPORTHANDLE hIoPortPage;
147 /** The EISA style high page registers I/O port registration. */
148 IOMIOPORTHANDLE hIoPortHi;
149} DMAControl, DMACONTROLLER;
150/** Pointer to the shared DMA controller state. */
151typedef DMACONTROLLER *PDMACONTROLLER;
152
153/* Complete DMA state information. */
154typedef struct {
155 DMAControl DMAC[2]; /* Two DMA controllers. */
156 PPDMDEVINSR3 pDevIns; /* Device instance. */
157 R3PTRTYPE(PCPDMDMACHLP) pHlp; /* PDM DMA helpers. */
158 STAMPROFILE StatRun;
159} DMAState, DMASTATE;
160/** Pointer to the shared DMA state information. */
161typedef DMASTATE *PDMASTATE;
162
163/* DMA command register bits. */
164enum {
165 CMD_MEMTOMEM = 0x01, /* Enable mem-to-mem trasfers. */
166 CMD_ADRHOLD = 0x02, /* Address hold for mem-to-mem. */
167 CMD_DISABLE = 0x04, /* Disable controller. */
168 CMD_COMPRTIME = 0x08, /* Compressed timing. */
169 CMD_ROTPRIO = 0x10, /* Rotating priority. */
170 CMD_EXTWR = 0x20, /* Extended write. */
171 CMD_DREQHI = 0x40, /* DREQ is active high if set. */
172 CMD_DACKHI = 0x80, /* DACK is active high if set. */
173 CMD_UNSUPPORTED = CMD_MEMTOMEM | CMD_ADRHOLD | CMD_COMPRTIME
174 | CMD_EXTWR | CMD_DREQHI | CMD_DACKHI
175};
176
177/* DMA control register offsets for read accesses. */
178enum {
179 CTL_R_STAT, /* Read status registers. */
180 CTL_R_DMAREQ, /* Read DRQ register. */
181 CTL_R_CMD, /* Read command register. */
182 CTL_R_MODE, /* Read mode register. */
183 CTL_R_SETBPTR, /* Set byte pointer flip-flop. */
184 CTL_R_TEMP, /* Read temporary register. */
185 CTL_R_CLRMODE, /* Clear mode register counter. */
186 CTL_R_MASK /* Read all DRQ mask bits. */
187};
188
189/* DMA control register offsets for read accesses. */
190enum {
191 CTL_W_CMD, /* Write command register. */
192 CTL_W_DMAREQ, /* Write DRQ register. */
193 CTL_W_MASKONE, /* Write single DRQ mask bit. */
194 CTL_W_MODE, /* Write mode register. */
195 CTL_W_CLRBPTR, /* Clear byte pointer flip-flop. */
196 CTL_W_MASTRCLR, /* Master clear. */
197 CTL_W_CLRMASK, /* Clear all DRQ mask bits. */
198 CTL_W_MASK /* Write all DRQ mask bits. */
199};
200
201/* DMA transfer modes. */
202enum {
203 DMODE_DEMAND, /* Demand transfer mode. */
204 DMODE_SINGLE, /* Single transfer mode. */
205 DMODE_BLOCK, /* Block transfer mode. */
206 DMODE_CASCADE /* Cascade mode. */
207};
208
209/* DMA transfer types. */
210enum {
211 DTYPE_VERIFY, /* Verify transfer type. */
212 DTYPE_WRITE, /* Write transfer type. */
213 DTYPE_READ, /* Read transfer type. */
214 DTYPE_ILLEGAL /* Undefined. */
215};
216
217#ifndef VBOX_DEVICE_STRUCT_TESTCASE
218
219
220/* Convert DMA channel number (0-7) to controller number (0-1). */
221#define DMACH2C(c) (c < 4 ? 0 : 1)
222
223#ifdef LOG_ENABLED
224static int const g_aiDmaChannelMap[8] = {-1, 2, 3, 1, -1, -1, -1, 0};
225/* Map a DMA page register offset (0-7) to channel index (0-3). */
226# define DMAPG2CX(c) (g_aiDmaChannelMap[c])
227#endif
228
229#ifdef IN_RING3
230static int const g_aiDmaMapChannel[4] = {7, 3, 1, 2};
231/* Map a channel index (0-3) to DMA page register offset (0-7). */
232# define DMACX2PG(c) (g_aiDmaMapChannel[c])
233/* Map a channel number (0-7) to DMA page register offset (0-7). */
234# define DMACH2PG(c) (g_aiDmaMapChannel[c & 3])
235#endif
236
237/* Test the decrement bit of mode register. */
238#define IS_MODE_DEC(c) ((c) & 0x20)
239/* Test the auto-init bit of mode register. */
240#define IS_MODE_AI(c) ((c) & 0x10)
241/* Extract the transfer type bits of mode register. */
242#define GET_MODE_XTYP(c) (((c) & 0x0c) >> 2)
243
244
245/* Perform a master clear (reset) on a DMA controller. */
246static void dmaClear(DMAControl *dc)
247{
248 dc->u8Command = 0;
249 dc->u8Status = 0;
250 dc->u8Temp = 0;
251 dc->u8ModeCtr = 0;
252 dc->fHiByte = false;
253 dc->u8Mask = UINT8_MAX;
254}
255
256
257/** Read the byte pointer and flip it. */
258DECLINLINE(bool) dmaReadBytePtr(DMAControl *dc)
259{
260 bool fHighByte = !!dc->fHiByte;
261 dc->fHiByte ^= 1;
262 return fHighByte;
263}
264
265
266/* DMA address registers writes and reads. */
267
268/**
269 * @callback_method_impl{FNIOMIOPORTOUT, Ports 0-7 & 0xc0-0xcf}
270 */
271static DECLCALLBACK(VBOXSTRICTRC) dmaWriteAddr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
272{
273 PDMACONTROLLER dc = (PDMACONTROLLER)pvUser;
274 RT_NOREF(pDevIns);
275 if (cb == 1)
276 {
277 unsigned const reg = (offPort >> dc->is16bit) & 0x0f;
278 unsigned const chidx = reg >> 1;
279 unsigned const is_count = reg & 1;
280 PDMACHANNEL ch = &RT_SAFE_SUBSCRIPT(dc->ChState, chidx);
281 Assert(!(u32 & ~0xff)); /* Check for garbage in high bits. */
282
283 if (dmaReadBytePtr(dc))
284 {
285 /* Write the high byte. */
286 if (is_count)
287 ch->u16BaseCount = RT_MAKE_U16(ch->u16BaseCount, u32);
288 else
289 ch->u16BaseAddr = RT_MAKE_U16(ch->u16BaseAddr, u32);
290
291 ch->u16CurCount = 0;
292 ch->u16CurAddr = ch->u16BaseAddr;
293 }
294 else
295 {
296 /* Write the low byte. */
297 if (is_count)
298 ch->u16BaseCount = RT_MAKE_U16(u32, RT_HIBYTE(ch->u16BaseCount));
299 else
300 ch->u16BaseAddr = RT_MAKE_U16(u32, RT_HIBYTE(ch->u16BaseAddr));
301 }
302 Log2(("dmaWriteAddr/%u: offPort %#06x, chidx %d, data %#02x\n", dc->is16bit, offPort, chidx, u32));
303 }
304 else
305 {
306 /* Likely a guest bug. */
307 Log(("dmaWriteAddr/%u: Bad size write to count register %#x (size %d, data %#x)\n", dc->is16bit, offPort, cb, u32));
308 }
309 return VINF_SUCCESS;
310}
311
312
313/**
314 * @callback_method_impl{FNIOMIOPORTIN, Ports 0-7 & 0xc0-0xcf}
315 */
316static DECLCALLBACK(VBOXSTRICTRC) dmaReadAddr(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
317{
318 RT_NOREF(pDevIns);
319 if (cb == 1)
320 {
321 PDMACONTROLLER dc = (PDMACONTROLLER)pvUser;
322 unsigned const reg = (offPort >> dc->is16bit) & 0x0f;
323 unsigned const chidx = reg >> 1;
324 PDMACHANNEL ch = &RT_SAFE_SUBSCRIPT(dc->ChState, chidx);
325 int const dir = IS_MODE_DEC(ch->u8Mode) ? -1 : 1;
326 int val;
327 int bptr;
328
329 if (reg & 1)
330 val = ch->u16BaseCount - ch->u16CurCount;
331 else
332 val = ch->u16CurAddr + ch->u16CurCount * dir;
333
334 bptr = dmaReadBytePtr(dc);
335 *pu32 = RT_LOBYTE(val >> (bptr * 8));
336
337 Log(("dmaReadAddr/%u: Count read: offPort %#06x, reg %#04x, data %#x\n", dc->is16bit, offPort, reg, val));
338 return VINF_SUCCESS;
339 }
340 return VERR_IOM_IOPORT_UNUSED;
341}
342
343/* DMA control registers writes and reads. */
344
345/**
346 * @callback_method_impl{FNIOMIOPORTOUT, Ports 0x8-0xf & 0xd0-0xdf}
347 */
348static DECLCALLBACK(VBOXSTRICTRC) dmaWriteCtl(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
349{
350 PDMACONTROLLER dc = (PDMACONTROLLER)pvUser;
351 RT_NOREF(pDevIns);
352 if (cb == 1)
353 {
354 unsigned chidx = 0;
355 unsigned const reg = (offPort >> dc->is16bit) & 0x0f;
356 Assert((int)reg >= CTL_W_CMD && reg <= CTL_W_MASK);
357 Assert(!(u32 & ~0xff)); /* Check for garbage in high bits. */
358
359 switch (reg) {
360 case CTL_W_CMD:
361 /* Unsupported commands are entirely ignored. */
362 if (u32 & CMD_UNSUPPORTED)
363 {
364 Log(("dmaWriteCtl/%u: DMA command %#x is not supported, ignoring!\n", dc->is16bit, u32));
365 break;
366 }
367 dc->u8Command = u32;
368 break;
369 case CTL_W_DMAREQ:
370 chidx = u32 & 3;
371 if (u32 & 4)
372 dc->u8Status |= 1 << (chidx + 4);
373 else
374 dc->u8Status &= ~(1 << (chidx + 4));
375 dc->u8Status &= ~(1 << chidx); /* Clear TC for channel. */
376 break;
377 case CTL_W_MASKONE:
378 chidx = u32 & 3;
379 if (u32 & 4)
380 dc->u8Mask |= 1 << chidx;
381 else
382 dc->u8Mask &= ~(1 << chidx);
383 break;
384 case CTL_W_MODE:
385 chidx = u32 & 3;
386 dc->ChState[chidx].u8Mode = u32;
387 Log2(("dmaWriteCtl/%u: chidx %d, op %d, %sauto-init, %screment, opmode %d\n", dc->is16bit,
388 chidx, (u32 >> 2) & 3, IS_MODE_AI(u32) ? "" : "no ", IS_MODE_DEC(u32) ? "de" : "in", (u32 >> 6) & 3));
389 break;
390 case CTL_W_CLRBPTR:
391 dc->fHiByte = false;
392 break;
393 case CTL_W_MASTRCLR:
394 dmaClear(dc);
395 break;
396 case CTL_W_CLRMASK:
397 dc->u8Mask = 0;
398 break;
399 case CTL_W_MASK:
400 dc->u8Mask = u32;
401 break;
402 default:
403 ASSERT_GUEST_MSG_FAILED(("reg=%u\n", reg));
404 break;
405 }
406 Log(("dmaWriteCtl/%u: offPort %#06x, chidx %d, data %#02x\n", dc->is16bit, offPort, chidx, u32));
407 }
408 else
409 {
410 /* Likely a guest bug. */
411 Log(("dmaWriteCtl/%u: Bad size write to controller register %#x (size %d, data %#x)\n", dc->is16bit, offPort, cb, u32));
412 }
413 return VINF_SUCCESS;
414}
415
416
417/**
418 * @callback_method_impl{FNIOMIOPORTIN, Ports 0x8-0xf & 0xd0-0xdf}
419 */
420static DECLCALLBACK(VBOXSTRICTRC) dmaReadCtl(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
421{
422 RT_NOREF(pDevIns);
423 if (cb == 1)
424 {
425 PDMACONTROLLER dc = (PDMACONTROLLER)pvUser;
426 uint8_t val = 0;
427
428 unsigned const reg = (offPort >> dc->is16bit) & 0x0f;
429 Assert((int)reg >= CTL_R_STAT && reg <= CTL_R_MASK);
430
431 switch (reg)
432 {
433 case CTL_R_STAT:
434 val = dc->u8Status;
435 dc->u8Status &= 0xf0; /* A read clears all TCs. */
436 break;
437 case CTL_R_DMAREQ:
438 val = (dc->u8Status >> 4) | 0xf0;
439 break;
440 case CTL_R_CMD:
441 val = dc->u8Command;
442 break;
443 case CTL_R_MODE:
444 val = RT_SAFE_SUBSCRIPT(dc->ChState, dc->u8ModeCtr).u8Mode | 3;
445 dc->u8ModeCtr = (dc->u8ModeCtr + 1) & 3;
446 break;
447 case CTL_R_SETBPTR:
448 dc->fHiByte = true;
449 break;
450 case CTL_R_TEMP:
451 val = dc->u8Temp;
452 break;
453 case CTL_R_CLRMODE:
454 dc->u8ModeCtr = 0;
455 break;
456 case CTL_R_MASK:
457 val = dc->u8Mask;
458 break;
459 default:
460 Assert(0);
461 break;
462 }
463
464 Log(("dmaReadCtl/%u: Ctrl read: offPort %#06x, reg %#04x, data %#x\n", dc->is16bit, offPort, reg, val));
465 *pu32 = val;
466
467 return VINF_SUCCESS;
468 }
469 return VERR_IOM_IOPORT_UNUSED;
470}
471
472
473
474/**
475 * @callback_method_impl{FNIOMIOPORTIN,
476 * DMA page registers - Ports 0x80-0x87 & 0x88-0x8f}
477 *
478 * There are 16 R/W page registers for compatibility with the IBM PC/AT; only
479 * some of those registers are used for DMA. The page register accessible via
480 * port 80h may be read to insert small delays or used as a scratch register by
481 * a BIOS.
482 */
483static DECLCALLBACK(VBOXSTRICTRC) dmaReadPage(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
484{
485 RT_NOREF(pDevIns);
486 PDMACONTROLLER dc = (PDMACONTROLLER)pvUser;
487 int reg;
488
489 if (cb == 1)
490 {
491 reg = offPort & 7;
492 *pu32 = dc->au8Page[reg];
493 Log2(("dmaReadPage/%u: Read %#x (byte) from page register %#x (channel %d)\n", dc->is16bit, *pu32, offPort, DMAPG2CX(reg)));
494 return VINF_SUCCESS;
495 }
496
497 if (cb == 2)
498 {
499 reg = offPort & 7;
500 *pu32 = dc->au8Page[reg] | (dc->au8Page[(reg + 1) & 7] << 8);
501 Log2(("dmaReadPage/%u: Read %#x (word) from page register %#x (channel %d)\n", dc->is16bit, *pu32, offPort, DMAPG2CX(reg)));
502 return VINF_SUCCESS;
503 }
504
505 return VERR_IOM_IOPORT_UNUSED;
506}
507
508
509/**
510 * @callback_method_impl{FNIOMIOPORTOUT,
511 * DMA page registers - Ports 0x80-0x87 & 0x88-0x8f}
512 */
513static DECLCALLBACK(VBOXSTRICTRC) dmaWritePage(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
514{
515 RT_NOREF(pDevIns);
516 PDMACONTROLLER dc = (PDMACONTROLLER)pvUser;
517 unsigned reg;
518
519 if (cb == 1)
520 {
521 Assert(!(u32 & ~0xff)); /* Check for garbage in high bits. */
522 reg = offPort & 7;
523 dc->au8Page[reg] = u32;
524 dc->au8PageHi[reg] = 0; /* Corresponding high page cleared. */
525 Log2(("dmaWritePage/%u: Wrote %#x to page register %#x (channel %d)\n", dc->is16bit, u32, offPort, DMAPG2CX(reg)));
526 }
527 else if (cb == 2)
528 {
529 Assert(!(u32 & ~0xffff)); /* Check for garbage in high bits. */
530 reg = offPort & 7;
531 dc->au8Page[reg] = u32;
532 dc->au8PageHi[reg] = 0; /* Corresponding high page cleared. */
533 reg = (offPort + 1) & 7;
534 dc->au8Page[reg] = u32 >> 8;
535 dc->au8PageHi[reg] = 0; /* Corresponding high page cleared. */
536 }
537 else
538 {
539 /* Likely a guest bug. */
540 Log(("dmaWritePage/%u: Bad size write to page register %#x (size %d, data %#x)\n", dc->is16bit, offPort, cb, u32));
541 }
542 return VINF_SUCCESS;
543}
544
545
546/**
547 * @callback_method_impl{FNIOMIOPORTIN,
548 * EISA style high page registers for extending the DMA addresses to cover
549 * the entire 32-bit address space. Ports 0x480-0x487 & 0x488-0x48f}
550 */
551static DECLCALLBACK(VBOXSTRICTRC) dmaReadHiPage(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
552{
553 RT_NOREF(pDevIns);
554 if (cb == 1)
555 {
556 PDMACONTROLLER dc = (PDMACONTROLLER)pvUser;
557 unsigned const reg = offPort & 7;
558
559 *pu32 = dc->au8PageHi[reg];
560 Log2(("dmaReadHiPage/%u: Read %#x to from high page register %#x (channel %d)\n", dc->is16bit, *pu32, offPort, DMAPG2CX(reg)));
561 return VINF_SUCCESS;
562 }
563 return VERR_IOM_IOPORT_UNUSED;
564}
565
566
567/**
568 * @callback_method_impl{FNIOMIOPORTOUT, Ports 0x480-0x487 & 0x488-0x48f}
569 */
570static DECLCALLBACK(VBOXSTRICTRC) dmaWriteHiPage(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
571{
572 RT_NOREF(pDevIns);
573 PDMACONTROLLER dc = (PDMACONTROLLER)pvUser;
574 if (cb == 1)
575 {
576 unsigned const reg = offPort & 7;
577
578 Assert(!(u32 & ~0xff)); /* Check for garbage in high bits. */
579 dc->au8PageHi[reg] = u32;
580 Log2(("dmaWriteHiPage/%u: Wrote %#x to high page register %#x (channel %d)\n", dc->is16bit, u32, offPort, DMAPG2CX(reg)));
581 }
582 else
583 {
584 /* Likely a guest bug. */
585 Log(("dmaWriteHiPage/%u: Bad size write to high page register %#x (size %d, data %#x)\n", dc->is16bit, offPort, cb, u32));
586 }
587 return VINF_SUCCESS;
588}
589
590
591#ifdef IN_RING3
592
593/** Perform any pending transfers on a single DMA channel. */
594static void dmaR3RunChannel(DMAState *pThis, int ctlidx, int chidx)
595{
596 DMAControl *dc = &pThis->DMAC[ctlidx];
597 DMAChannel *ch = &dc->ChState[chidx];
598 uint32_t start_cnt, end_cnt;
599 int opmode;
600
601 opmode = (ch->u8Mode >> 6) & 3;
602
603 Log3(("DMA address %screment, mode %d\n", IS_MODE_DEC(ch->u8Mode) ? "de" : "in", ch->u8Mode >> 6));
604 AssertReturnVoid(ch->pfnXferHandler);
605
606 /* Addresses and counts are shifted for 16-bit channels. */
607 start_cnt = ch->u16CurCount << dc->is16bit;
608 /* NB: The device is responsible for examining the DMA mode and not
609 * transferring more than it should if auto-init is not in use.
610 */
611 end_cnt = ch->pfnXferHandler(ch->pDevInsHandler, ch->pvUser, (ctlidx * 4) + chidx,
612 start_cnt, (ch->u16BaseCount + 1) << dc->is16bit);
613 ch->u16CurCount = end_cnt >> dc->is16bit;
614 /* Set the TC (Terminal Count) bit if transfer was completed. */
615 if (ch->u16CurCount == ch->u16BaseCount + 1)
616 switch (opmode)
617 {
618 case DMODE_DEMAND:
619 case DMODE_SINGLE:
620 case DMODE_BLOCK:
621 dc->u8Status |= RT_BIT(chidx);
622 Log3(("TC set for DMA channel %d\n", (ctlidx * 4) + chidx));
623 break;
624 default:
625 break;
626 }
627 Log3(("DMA position %d, size %d\n", end_cnt, (ch->u16BaseCount + 1) << dc->is16bit));
628}
629
630/**
631 * @interface_method_impl{PDMDMAREG,pfnRun}
632 */
633static DECLCALLBACK(bool) dmaR3Run(PPDMDEVINS pDevIns)
634{
635 DMAState *pThis = PDMDEVINS_2_DATA(pDevIns, PDMASTATE);
636 DMAControl *dc;
637 int chidx, mask;
638
639 STAM_PROFILE_START(&pThis->StatRun, a);
640
641 /* We must first lock all the devices then the DMAC or we end up with a
642 lock order validation when the callback helpers (PDMDMACREG) are being
643 invoked from I/O port and MMIO callbacks in channel devices. While this
644 may sound a little brutish, it's actually in line with the bus locking
645 the original DMAC did back in the days. Besides, we've only got the FDC
646 and SB16 as potential customers here at present, so hardly a problem. */
647 for (unsigned idxCtl = 0; idxCtl < RT_ELEMENTS(pThis->DMAC); idxCtl++)
648 for (unsigned idxCh = 0; idxCh < RT_ELEMENTS(pThis->DMAC[idxCtl].ChState); idxCh++)
649 if (pThis->DMAC[idxCtl].ChState[idxCh].pDevInsHandler)
650 {
651 int const rc = PDMDevHlpCritSectEnter(pDevIns, pThis->DMAC[idxCtl].ChState[idxCh].pDevInsHandler->pCritSectRoR3,
652 VERR_IGNORED);
653 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pThis->DMAC[idxCtl].ChState[idxCh].pDevInsHandler->pCritSectRoR3, rc);
654 }
655 int const rc = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
656 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rc);
657
658 /* Run all controllers and channels. */
659 for (unsigned ctlidx = 0; ctlidx < RT_ELEMENTS(pThis->DMAC); ++ctlidx)
660 {
661 dc = &pThis->DMAC[ctlidx];
662
663 /* If controller is disabled, don't even bother. */
664 if (dc->u8Command & CMD_DISABLE)
665 continue;
666
667 for (chidx = 0; chidx < 4; ++chidx)
668 {
669 mask = 1 << chidx;
670 if (!(dc->u8Mask & mask) && (dc->u8Status & (mask << 4)))
671 dmaR3RunChannel(pThis, ctlidx, chidx);
672 }
673 }
674
675 /* Unlock everything (order is mostly irrelevant). */
676 for (unsigned idxCtl = 0; idxCtl < RT_ELEMENTS(pThis->DMAC); idxCtl++)
677 for (unsigned idxCh = 0; idxCh < RT_ELEMENTS(pThis->DMAC[idxCtl].ChState); idxCh++)
678 if (pThis->DMAC[idxCtl].ChState[idxCh].pDevInsHandler)
679 PDMDevHlpCritSectLeave(pDevIns, pThis->DMAC[idxCtl].ChState[idxCh].pDevInsHandler->pCritSectRoR3);
680 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
681
682 STAM_PROFILE_STOP(&pThis->StatRun, a);
683 return 0;
684}
685
686/**
687 * @interface_method_impl{PDMDMAREG,pfnRegister}
688 */
689static DECLCALLBACK(void) dmaR3Register(PPDMDEVINS pDevIns, unsigned uChannel, PPDMDEVINS pDevInsHandler,
690 PFNDMATRANSFERHANDLER pfnTransferHandler, void *pvUser)
691{
692 DMAState *pThis = PDMDEVINS_2_DATA(pDevIns, PDMASTATE);
693 DMAChannel *ch = &pThis->DMAC[DMACH2C(uChannel)].ChState[uChannel & 3];
694
695 LogFlow(("dmaR3Register: pThis=%p uChannel=%u pfnTransferHandler=%p pvUser=%p\n", pThis, uChannel, pfnTransferHandler, pvUser));
696
697 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
698 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
699
700 ch->pDevInsHandler = pDevInsHandler;
701 ch->pfnXferHandler = pfnTransferHandler;
702 ch->pvUser = pvUser;
703
704 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
705}
706
707/** Reverse the order of bytes in a memory buffer. */
708static void dmaReverseBuf8(void *buf, unsigned len)
709{
710 uint8_t *pBeg, *pEnd;
711 uint8_t temp;
712
713 pBeg = (uint8_t *)buf;
714 pEnd = pBeg + len - 1;
715 for (len = len / 2; len; --len)
716 {
717 temp = *pBeg;
718 *pBeg++ = *pEnd;
719 *pEnd-- = temp;
720 }
721}
722
723/** Reverse the order of words in a memory buffer. */
724static void dmaReverseBuf16(void *buf, unsigned len)
725{
726 uint16_t *pBeg, *pEnd;
727 uint16_t temp;
728
729 Assert(!(len & 1));
730 len /= 2; /* Convert to word count. */
731 pBeg = (uint16_t *)buf;
732 pEnd = pBeg + len - 1;
733 for (len = len / 2; len; --len)
734 {
735 temp = *pBeg;
736 *pBeg++ = *pEnd;
737 *pEnd-- = temp;
738 }
739}
740
741/**
742 * @interface_method_impl{PDMDMAREG,pfnReadMemory}
743 */
744static DECLCALLBACK(uint32_t) dmaR3ReadMemory(PPDMDEVINS pDevIns, unsigned uChannel,
745 void *pvBuffer, uint32_t off, uint32_t cbBlock)
746{
747 DMAState *pThis = PDMDEVINS_2_DATA(pDevIns, PDMASTATE);
748 DMAControl *dc = &pThis->DMAC[DMACH2C(uChannel)];
749 DMAChannel *ch = &dc->ChState[uChannel & 3];
750 uint32_t page, pagehi;
751 uint32_t addr;
752
753 LogFlow(("dmaR3ReadMemory: pThis=%p uChannel=%u pvBuffer=%p off=%u cbBlock=%u\n", pThis, uChannel, pvBuffer, off, cbBlock));
754
755 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
756 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
757
758 /* Build the address for this transfer. */
759 page = dc->au8Page[DMACH2PG(uChannel)] & ~dc->is16bit;
760 pagehi = dc->au8PageHi[DMACH2PG(uChannel)];
761 addr = (pagehi << 24) | (page << 16) | (ch->u16CurAddr << dc->is16bit);
762
763 if (IS_MODE_DEC(ch->u8Mode))
764 {
765 PDMDevHlpPhysRead(pThis->pDevIns, addr - off - cbBlock, pvBuffer, cbBlock);
766 if (dc->is16bit)
767 dmaReverseBuf16(pvBuffer, cbBlock);
768 else
769 dmaReverseBuf8(pvBuffer, cbBlock);
770 }
771 else
772 PDMDevHlpPhysRead(pThis->pDevIns, addr + off, pvBuffer, cbBlock);
773
774 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
775 return cbBlock;
776}
777
778/**
779 * @interface_method_impl{PDMDMAREG,pfnWriteMemory}
780 */
781static DECLCALLBACK(uint32_t) dmaR3WriteMemory(PPDMDEVINS pDevIns, unsigned uChannel,
782 const void *pvBuffer, uint32_t off, uint32_t cbBlock)
783{
784 DMAState *pThis = PDMDEVINS_2_DATA(pDevIns, PDMASTATE);
785 DMAControl *dc = &pThis->DMAC[DMACH2C(uChannel)];
786 DMAChannel *ch = &dc->ChState[uChannel & 3];
787 uint32_t page, pagehi;
788 uint32_t addr;
789
790 LogFlow(("dmaR3WriteMemory: pThis=%p uChannel=%u pvBuffer=%p off=%u cbBlock=%u\n", pThis, uChannel, pvBuffer, off, cbBlock));
791 if (GET_MODE_XTYP(ch->u8Mode) == DTYPE_VERIFY)
792 {
793 Log(("DMA verify transfer, ignoring write.\n"));
794 return cbBlock;
795 }
796
797 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
798 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
799
800 /* Build the address for this transfer. */
801 page = dc->au8Page[DMACH2PG(uChannel)] & ~dc->is16bit;
802 pagehi = dc->au8PageHi[DMACH2PG(uChannel)];
803 addr = (pagehi << 24) | (page << 16) | (ch->u16CurAddr << dc->is16bit);
804
805 if (IS_MODE_DEC(ch->u8Mode))
806 {
807 /// @todo This would need a temporary buffer.
808 Assert(0);
809#if 0
810 if (dc->is16bit)
811 dmaReverseBuf16(pvBuffer, cbBlock);
812 else
813 dmaReverseBuf8(pvBuffer, cbBlock);
814#endif
815 PDMDevHlpPhysWrite(pThis->pDevIns, addr - off - cbBlock, pvBuffer, cbBlock);
816 }
817 else
818 PDMDevHlpPhysWrite(pThis->pDevIns, addr + off, pvBuffer, cbBlock);
819
820 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
821 return cbBlock;
822}
823
824/**
825 * @interface_method_impl{PDMDMAREG,pfnSetDREQ}
826 */
827static DECLCALLBACK(void) dmaR3SetDREQ(PPDMDEVINS pDevIns, unsigned uChannel, unsigned uLevel)
828{
829 DMAState *pThis = PDMDEVINS_2_DATA(pDevIns, PDMASTATE);
830 DMAControl *dc = &pThis->DMAC[DMACH2C(uChannel)];
831 int chidx;
832
833 LogFlow(("dmaR3SetDREQ: pThis=%p uChannel=%u uLevel=%u\n", pThis, uChannel, uLevel));
834
835 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
836 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
837
838 chidx = uChannel & 3;
839 if (uLevel)
840 dc->u8Status |= 1 << (chidx + 4);
841 else
842 dc->u8Status &= ~(1 << (chidx + 4));
843
844 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
845}
846
847/**
848 * @interface_method_impl{PDMDMAREG,pfnGetChannelMode}
849 */
850static DECLCALLBACK(uint8_t) dmaR3GetChannelMode(PPDMDEVINS pDevIns, unsigned uChannel)
851{
852 PDMASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDMASTATE);
853
854 LogFlow(("dmaR3GetChannelMode: pThis=%p uChannel=%u\n", pThis, uChannel));
855
856 int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
857 PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
858
859 uint8_t u8Mode = pThis->DMAC[DMACH2C(uChannel)].ChState[uChannel & 3].u8Mode;
860
861 PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
862 return u8Mode;
863}
864
865
866static void dmaR3SaveController(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM, DMAControl *dc)
867{
868 /* Save controller state... */
869 pHlp->pfnSSMPutU8(pSSM, dc->u8Command);
870 pHlp->pfnSSMPutU8(pSSM, dc->u8Mask);
871 pHlp->pfnSSMPutU8(pSSM, dc->fHiByte);
872 pHlp->pfnSSMPutU32(pSSM, dc->is16bit);
873 pHlp->pfnSSMPutU8(pSSM, dc->u8Status);
874 pHlp->pfnSSMPutU8(pSSM, dc->u8Temp);
875 pHlp->pfnSSMPutU8(pSSM, dc->u8ModeCtr);
876 pHlp->pfnSSMPutMem(pSSM, &dc->au8Page, sizeof(dc->au8Page));
877 pHlp->pfnSSMPutMem(pSSM, &dc->au8PageHi, sizeof(dc->au8PageHi));
878
879 /* ...and all four of its channels. */
880 for (unsigned chidx = 0; chidx < RT_ELEMENTS(dc->ChState); ++chidx)
881 {
882 DMAChannel *ch = &dc->ChState[chidx];
883
884 pHlp->pfnSSMPutU16(pSSM, ch->u16CurAddr);
885 pHlp->pfnSSMPutU16(pSSM, ch->u16CurCount);
886 pHlp->pfnSSMPutU16(pSSM, ch->u16BaseAddr);
887 pHlp->pfnSSMPutU16(pSSM, ch->u16BaseCount);
888 pHlp->pfnSSMPutU8(pSSM, ch->u8Mode);
889 }
890}
891
892static int dmaR3LoadController(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM, DMAControl *dc, int version)
893{
894 uint8_t u8val;
895 uint32_t u32val;
896
897 pHlp->pfnSSMGetU8(pSSM, &dc->u8Command);
898 pHlp->pfnSSMGetU8(pSSM, &dc->u8Mask);
899 pHlp->pfnSSMGetU8(pSSM, &u8val);
900 dc->fHiByte = !!u8val;
901 pHlp->pfnSSMGetU32(pSSM, &dc->is16bit);
902 if (version > DMA_SAVESTATE_OLD)
903 {
904 pHlp->pfnSSMGetU8(pSSM, &dc->u8Status);
905 pHlp->pfnSSMGetU8(pSSM, &dc->u8Temp);
906 pHlp->pfnSSMGetU8(pSSM, &dc->u8ModeCtr);
907 pHlp->pfnSSMGetMem(pSSM, &dc->au8Page, sizeof(dc->au8Page));
908 pHlp->pfnSSMGetMem(pSSM, &dc->au8PageHi, sizeof(dc->au8PageHi));
909 }
910
911 for (unsigned chidx = 0; chidx < RT_ELEMENTS(dc->ChState); ++chidx)
912 {
913 DMAChannel *ch = &dc->ChState[chidx];
914
915 if (version == DMA_SAVESTATE_OLD)
916 {
917 /* Convert from 17-bit to 16-bit format. */
918 pHlp->pfnSSMGetU32(pSSM, &u32val);
919 ch->u16CurAddr = u32val >> dc->is16bit;
920 pHlp->pfnSSMGetU32(pSSM, &u32val);
921 ch->u16CurCount = u32val >> dc->is16bit;
922 }
923 else
924 {
925 pHlp->pfnSSMGetU16(pSSM, &ch->u16CurAddr);
926 pHlp->pfnSSMGetU16(pSSM, &ch->u16CurCount);
927 }
928 pHlp->pfnSSMGetU16(pSSM, &ch->u16BaseAddr);
929 pHlp->pfnSSMGetU16(pSSM, &ch->u16BaseCount);
930 pHlp->pfnSSMGetU8(pSSM, &ch->u8Mode);
931 /* Convert from old save state. */
932 if (version == DMA_SAVESTATE_OLD)
933 {
934 /* Remap page register contents. */
935 pHlp->pfnSSMGetU8(pSSM, &u8val);
936 dc->au8Page[DMACX2PG(chidx)] = u8val;
937 pHlp->pfnSSMGetU8(pSSM, &u8val);
938 dc->au8PageHi[DMACX2PG(chidx)] = u8val;
939 /* Throw away dack, eop. */
940 pHlp->pfnSSMGetU8(pSSM, &u8val);
941 pHlp->pfnSSMGetU8(pSSM, &u8val);
942 }
943 }
944 return 0;
945}
946
947/** @callback_method_impl{FNSSMDEVSAVEEXEC} */
948static DECLCALLBACK(int) dmaR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
949{
950 PDMASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDMASTATE);
951 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
952
953 dmaR3SaveController(pHlp, pSSM, &pThis->DMAC[0]);
954 dmaR3SaveController(pHlp, pSSM, &pThis->DMAC[1]);
955 return VINF_SUCCESS;
956}
957
958/** @callback_method_impl{FNSSMDEVLOADEXEC} */
959static DECLCALLBACK(int) dmaR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
960{
961 PDMASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDMASTATE);
962 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
963
964 AssertMsgReturn(uVersion <= DMA_SAVESTATE_CURRENT, ("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
965 Assert(uPass == SSM_PASS_FINAL); NOREF(uPass);
966
967 dmaR3LoadController(pHlp, pSSM, &pThis->DMAC[0], uVersion);
968 return dmaR3LoadController(pHlp, pSSM, &pThis->DMAC[1], uVersion);
969}
970
971/** @callback_method_impl{FNDBGFHANDLERDEV} */
972static DECLCALLBACK(void) dmaR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
973{
974 PDMASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDMASTATE);
975 NOREF(pszArgs);
976
977 /*
978 * Show info.
979 */
980 for (unsigned i = 0; i < RT_ELEMENTS(pThis->DMAC); i++)
981 {
982 PDMACONTROLLER pDmac = &pThis->DMAC[i];
983
984 pHlp->pfnPrintf(pHlp, "\nDMAC%d:\n", i);
985 pHlp->pfnPrintf(pHlp, " Status : %02X - DRQ 3210 TC 3210\n", pDmac->u8Status);
986 pHlp->pfnPrintf(pHlp, " %u%u%u%u %u%u%u%u\n",
987 !!(pDmac->u8Status & RT_BIT(7)), !!(pDmac->u8Status & RT_BIT(6)),
988 !!(pDmac->u8Status & RT_BIT(5)), !!(pDmac->u8Status & RT_BIT(4)),
989 !!(pDmac->u8Status & RT_BIT(3)), !!(pDmac->u8Status & RT_BIT(2)),
990 !!(pDmac->u8Status & RT_BIT(1)), !!(pDmac->u8Status & RT_BIT(0)));
991 pHlp->pfnPrintf(pHlp, " Mask : %02X - Chn 3210\n", pDmac->u8Mask);
992 pHlp->pfnPrintf(pHlp, " %u%u%u%u\n",
993 !!(pDmac->u8Mask & RT_BIT(3)), !!(pDmac->u8Mask & RT_BIT(2)),
994 !!(pDmac->u8Mask & RT_BIT(1)), !!(pDmac->u8Mask & RT_BIT(0)));
995 pHlp->pfnPrintf(pHlp, " Temp : %02x\n", pDmac->u8Temp);
996 pHlp->pfnPrintf(pHlp, " Command: %02X\n", pDmac->u8Command);
997 pHlp->pfnPrintf(pHlp, " DACK: active %s DREQ: active %s\n",
998 pDmac->u8Command & RT_BIT(7) ? "high" : "low ",
999 pDmac->u8Command & RT_BIT(6) ? "high" : "low ");
1000 pHlp->pfnPrintf(pHlp, " Extended write: %s Priority: %s\n",
1001 pDmac->u8Command & RT_BIT(5) ? "enabled " : "disabled",
1002 pDmac->u8Command & RT_BIT(4) ? "rotating" : "fixed ");
1003 pHlp->pfnPrintf(pHlp, " Timing: %s Controller: %s\n",
1004 pDmac->u8Command & RT_BIT(3) ? "normal " : "compressed",
1005 pDmac->u8Command & RT_BIT(2) ? "enabled " : "disabled");
1006 pHlp->pfnPrintf(pHlp, " Adress Hold: %s Mem-to-Mem Ch 0/1: %s\n",
1007 pDmac->u8Command & RT_BIT(1) ? "enabled " : "disabled",
1008 pDmac->u8Command & RT_BIT(0) ? "enabled " : "disabled");
1009
1010 for (unsigned ch = 0; ch < RT_ELEMENTS(pDmac->ChState); ch++)
1011 {
1012 PDMACHANNEL pChan = &pDmac->ChState[ch];
1013 const char *apszChanMode[] = { "demand ", "single ", "block ", "cascade" };
1014 const char *apszChanType[] = { "verify ", "write ", "read ", "illegal" };
1015
1016 pHlp->pfnPrintf(pHlp, "\n DMA Channel %d: Page:%02X\n",
1017 ch, pDmac->au8Page[DMACX2PG(ch)]);
1018 pHlp->pfnPrintf(pHlp, " Mode : %02X Auto-init: %s %screment\n",
1019 pChan->u8Mode, pChan->u8Mode & RT_BIT(4) ? "yes" : "no",
1020 pChan->u8Mode & RT_BIT(5) ? "De" : "In" );
1021 pHlp->pfnPrintf(pHlp, " Xfer Type: %s Mode: %s\n",
1022 apszChanType[((pChan->u8Mode >> 2) & 3)],
1023 apszChanMode[((pChan->u8Mode >> 6) & 3)]);
1024 pHlp->pfnPrintf(pHlp, " Base address:%04X count:%04X\n",
1025 pChan->u16BaseAddr, pChan->u16BaseCount);
1026 pHlp->pfnPrintf(pHlp, " Current address:%04X count:%04X\n",
1027 pChan->u16CurAddr, pChan->u16CurCount);
1028 }
1029 }
1030}
1031
1032/** @callback_method_impl{FNDBGFHANDLERDEV} */
1033static DECLCALLBACK(void) dmaR3InfoPageReg(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
1034{
1035 PDMASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDMASTATE);
1036 NOREF(pszArgs);
1037
1038 /*
1039 * Show page register contents.
1040 */
1041 for (unsigned i = 0; i < RT_ELEMENTS(pThis->DMAC); i++)
1042 {
1043 PDMACONTROLLER pDmac = &pThis->DMAC[i];
1044
1045 pHlp->pfnPrintf(pHlp, "DMA page registers at %02X:", i == 0 ? 0x80 : 0x88);
1046 for (unsigned pg = 0; pg < RT_ELEMENTS(pDmac->au8Page); pg++)
1047 pHlp->pfnPrintf(pHlp, " %02X", pDmac->au8Page[pg]);
1048
1049 pHlp->pfnPrintf(pHlp, "\n");
1050 }
1051}
1052
1053/**
1054 * @interface_method_impl{PDMDEVREG,pfnReset}
1055 */
1056static DECLCALLBACK(void) dmaR3Reset(PPDMDEVINS pDevIns)
1057{
1058 PDMASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDMASTATE);
1059
1060 LogFlow(("dmaR3Reset: pThis=%p\n", pThis));
1061
1062 /* NB: The page and address registers are unaffected by a reset
1063 * and in an undefined state after power-up.
1064 */
1065 dmaClear(&pThis->DMAC[0]);
1066 dmaClear(&pThis->DMAC[1]);
1067}
1068
1069/**
1070 * @interface_method_impl{PDMDEVREG,pfnConstruct}
1071 */
1072static DECLCALLBACK(int) dmaR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
1073{
1074 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1075 PDMASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDMASTATE);
1076 PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
1077 RT_NOREF(iInstance);
1078
1079 /*
1080 * Initialize data.
1081 */
1082 pThis->pDevIns = pDevIns;
1083
1084 DMAControl *pDC8 = &pThis->DMAC[0];
1085 DMAControl *pDC16 = &pThis->DMAC[1];
1086 pDC8->is16bit = false;
1087 pDC16->is16bit = true;
1088
1089 /*
1090 * Validate and read the configuration.
1091 */
1092 PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "HighPageEnable", "");
1093
1094 bool fHighPage = false;
1095 int rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "HighPageEnable", &fHighPage, false);
1096 AssertRCReturn(rc, rc);
1097
1098 /*
1099 * Register I/O callbacks.
1100 */
1101 /* Base and current address for each channel. */
1102 rc = PDMDevHlpIoPortCreateUAndMap(pDevIns, 0x00, 8, dmaWriteAddr, dmaReadAddr, pDC8, "DMA8 Address", NULL, &pDC8->hIoPortBase);
1103 AssertLogRelRCReturn(rc, rc);
1104 rc = PDMDevHlpIoPortCreateUAndMap(pDevIns, 0xc0, 16, dmaWriteAddr, dmaReadAddr, pDC16, "DMA16 Address", NULL, &pDC16->hIoPortBase);
1105 AssertLogRelRCReturn(rc, rc);
1106
1107 /* Control registers for both DMA controllers. */
1108 rc = PDMDevHlpIoPortCreateUAndMap(pDevIns, 0x08, 8, dmaWriteCtl, dmaReadCtl, pDC8, "DMA8 Control", NULL, &pDC8->hIoPortCtl);
1109 AssertLogRelRCReturn(rc, rc);
1110 rc = PDMDevHlpIoPortCreateUAndMap(pDevIns, 0xd0, 16, dmaWriteCtl, dmaReadCtl, pDC16, "DMA16 Control", NULL, &pDC16->hIoPortCtl);
1111 AssertLogRelRCReturn(rc, rc);
1112
1113 /* Page registers for each channel (plus a few unused ones). */
1114 rc = PDMDevHlpIoPortCreateUAndMap(pDevIns, 0x80, 8, dmaWritePage, dmaReadPage, pDC8, "DMA8 Page", NULL, &pDC8->hIoPortPage);
1115 AssertLogRelRCReturn(rc, rc);
1116 rc = PDMDevHlpIoPortCreateUAndMap(pDevIns, 0x88, 8, dmaWritePage, dmaReadPage, pDC16, "DMA16 Page", NULL, &pDC16->hIoPortPage);
1117 AssertLogRelRCReturn(rc, rc);
1118
1119 /* Optional EISA style high page registers (address bits 24-31). */
1120 if (fHighPage)
1121 {
1122 rc = PDMDevHlpIoPortCreateUAndMap(pDevIns, 0x480, 8, dmaWriteHiPage, dmaReadHiPage, pDC8, "DMA8 Page High", NULL, &pDC8->hIoPortHi);
1123 AssertLogRelRCReturn(rc, rc);
1124 rc = PDMDevHlpIoPortCreateUAndMap(pDevIns, 0x488, 8, dmaWriteHiPage, dmaReadHiPage, pDC16, "DMA16 Page High", NULL, &pDC16->hIoPortHi);
1125 AssertLogRelRCReturn(rc, rc);
1126 }
1127 else
1128 {
1129 pDC8->hIoPortHi = NIL_IOMIOPORTHANDLE;
1130 pDC16->hIoPortHi = NIL_IOMIOPORTHANDLE;
1131 }
1132
1133 /*
1134 * Reset controller state.
1135 */
1136 dmaR3Reset(pDevIns);
1137
1138 /*
1139 * Register ourselves with PDM as the DMA controller.
1140 */
1141 PDMDMACREG Reg;
1142 Reg.u32Version = PDM_DMACREG_VERSION;
1143 Reg.pfnRun = dmaR3Run;
1144 Reg.pfnRegister = dmaR3Register;
1145 Reg.pfnReadMemory = dmaR3ReadMemory;
1146 Reg.pfnWriteMemory = dmaR3WriteMemory;
1147 Reg.pfnSetDREQ = dmaR3SetDREQ;
1148 Reg.pfnGetChannelMode = dmaR3GetChannelMode;
1149
1150 rc = PDMDevHlpDMACRegister(pDevIns, &Reg, &pThis->pHlp);
1151 AssertRCReturn(rc, rc);
1152
1153 /*
1154 * Register the saved state.
1155 */
1156 rc = PDMDevHlpSSMRegister(pDevIns, DMA_SAVESTATE_CURRENT, sizeof(*pThis), dmaR3SaveExec, dmaR3LoadExec);
1157 AssertRCReturn(rc, rc);
1158
1159 /*
1160 * Statistics.
1161 */
1162 PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRun, STAMTYPE_PROFILE, "DmaRun", STAMUNIT_TICKS_PER_CALL, "Profiling dmaR3Run().");
1163
1164 /*
1165 * Register the info item.
1166 */
1167 PDMDevHlpDBGFInfoRegister(pDevIns, "dmac", "DMA controller info.", dmaR3Info);
1168 PDMDevHlpDBGFInfoRegister(pDevIns, "dmapage", "DMA page register info.", dmaR3InfoPageReg);
1169
1170 return VINF_SUCCESS;
1171}
1172
1173#else /* !IN_RING3 */
1174
1175/**
1176 * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
1177 */
1178static DECLCALLBACK(int) dmaRZConstruct(PPDMDEVINS pDevIns)
1179{
1180 PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
1181 PDMASTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDMASTATE);
1182 int rc;
1183
1184 for (unsigned i = 0; i < RT_ELEMENTS(pThis->DMAC); i++)
1185 {
1186 PDMACONTROLLER pCtl = &pThis->DMAC[i];
1187
1188 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pCtl->hIoPortBase, dmaWriteAddr, dmaReadAddr, pCtl);
1189 AssertLogRelRCReturn(rc, rc);
1190
1191 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pCtl->hIoPortCtl, dmaWriteCtl, dmaReadCtl, pCtl);
1192 AssertLogRelRCReturn(rc, rc);
1193
1194 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pCtl->hIoPortPage, dmaWritePage, dmaReadPage, pCtl);
1195 AssertLogRelRCReturn(rc, rc);
1196
1197 if (pCtl->hIoPortHi != NIL_IOMIOPORTHANDLE)
1198 {
1199 rc = PDMDevHlpIoPortSetUpContext(pDevIns, pCtl->hIoPortHi, dmaWriteHiPage, dmaReadHiPage, pCtl);
1200 AssertLogRelRCReturn(rc, rc);
1201 }
1202 }
1203
1204 return VINF_SUCCESS;
1205}
1206
1207#endif /* !IN_RING3 */
1208
1209/**
1210 * The device registration structure.
1211 */
1212const PDMDEVREG g_DeviceDMA =
1213{
1214 /* .u32Version = */ PDM_DEVREG_VERSION,
1215 /* .uReserved0 = */ 0,
1216 /* .szName = */ "8237A",
1217 /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
1218 /* .fClass = */ PDM_DEVREG_CLASS_DMA,
1219 /* .cMaxInstances = */ 1,
1220 /* .uSharedVersion = */ 42,
1221 /* .cbInstanceShared = */ sizeof(DMAState),
1222 /* .cbInstanceCC = */ 0,
1223 /* .cbInstanceRC = */ 0,
1224 /* .cMaxPciDevices = */ 0,
1225 /* .cMaxMsixVectors = */ 0,
1226 /* .pszDescription = */ "DMA Controller Device",
1227#if defined(IN_RING3)
1228 /* .pszRCMod = */ "VBoxDDRC.rc",
1229 /* .pszR0Mod = */ "VBoxDDR0.r0",
1230 /* .pfnConstruct = */ dmaR3Construct,
1231 /* .pfnDestruct = */ NULL,
1232 /* .pfnRelocate = */ NULL,
1233 /* .pfnMemSetup = */ NULL,
1234 /* .pfnPowerOn = */ NULL,
1235 /* .pfnReset = */ dmaR3Reset,
1236 /* .pfnSuspend = */ NULL,
1237 /* .pfnResume = */ NULL,
1238 /* .pfnAttach = */ NULL,
1239 /* .pfnDetach = */ NULL,
1240 /* .pfnQueryInterface = */ NULL,
1241 /* .pfnInitComplete = */ NULL,
1242 /* .pfnPowerOff = */ NULL,
1243 /* .pfnSoftReset = */ NULL,
1244 /* .pfnReserved0 = */ NULL,
1245 /* .pfnReserved1 = */ NULL,
1246 /* .pfnReserved2 = */ NULL,
1247 /* .pfnReserved3 = */ NULL,
1248 /* .pfnReserved4 = */ NULL,
1249 /* .pfnReserved5 = */ NULL,
1250 /* .pfnReserved6 = */ NULL,
1251 /* .pfnReserved7 = */ NULL,
1252#elif defined(IN_RING0)
1253 /* .pfnEarlyConstruct = */ NULL,
1254 /* .pfnConstruct = */ dmaRZConstruct,
1255 /* .pfnDestruct = */ NULL,
1256 /* .pfnFinalDestruct = */ NULL,
1257 /* .pfnRequest = */ NULL,
1258 /* .pfnReserved0 = */ NULL,
1259 /* .pfnReserved1 = */ NULL,
1260 /* .pfnReserved2 = */ NULL,
1261 /* .pfnReserved3 = */ NULL,
1262 /* .pfnReserved4 = */ NULL,
1263 /* .pfnReserved5 = */ NULL,
1264 /* .pfnReserved6 = */ NULL,
1265 /* .pfnReserved7 = */ NULL,
1266#elif defined(IN_RC)
1267 /* .pfnConstruct = */ dmaRZConstruct,
1268 /* .pfnReserved0 = */ NULL,
1269 /* .pfnReserved1 = */ NULL,
1270 /* .pfnReserved2 = */ NULL,
1271 /* .pfnReserved3 = */ NULL,
1272 /* .pfnReserved4 = */ NULL,
1273 /* .pfnReserved5 = */ NULL,
1274 /* .pfnReserved6 = */ NULL,
1275 /* .pfnReserved7 = */ NULL,
1276#else
1277# error "Not in IN_RING3, IN_RING0 or IN_RC!"
1278#endif
1279 /* .u32VersionEnd = */ PDM_DEVREG_VERSION
1280};
1281
1282#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
1283
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