VirtualBox

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

Last change on this file since 93115 was 93115, checked in by vboxsync, 2 years ago

scm --update-copyright-year

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

© 2023 Oracle
ContactPrivacy policyTerms of Use