DevDMA.cpp revision b33c5732f8c9a08686891ff1eb6e0d906247b9b3
/* $Id$ */
/** @file
* DevDMA - DMA Controller Device.
*/
/*
* Copyright (C) 2006-2007 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
* --------------------------------------------------------------------
*
* This code is loosely based on:
*
* QEMU DMA emulation
*
* Copyright (c) 2003 Vassili Karpov (malc)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#define LOG_GROUP LOG_GROUP_DEV_DMA
#include <stdio.h>
#include <stdlib.h>
#include "VBoxDD.h"
/* DMA Overview and notes
*
* cascaded 8237A DMA controllers, augmented with a 74LS612 memory mapper.
* The 8237As are 8-bit parts, only capable of addressing up to 64KB; the
* 74LS612 extends addressing to 24 bits. That leads to well known and
* inconvenient DMA limitations:
* - DMA can only access physical memory under the 16MB line
* - DMA transfers must occur within a 64KB/128KB 'page'
*
* left by one, including the control registers addresses. The DMA register
* offsets (except for the page registers) are therefore "double spaced".
*
* Due to the address shifting, the DMA controller decodes more addresses
* than are usually documented, with aliasing. See the ICH8 datasheet.
*
* preventing the use of memory-to-memory DMA transfers (which use channels
* However, it would transfer a single byte at a time, while the CPU can
* transfer two (on a 286) or four (on a 386+) bytes at a time. On many
* compatibles, memory-to-memory DMA is not even implemented at all, and
* therefore has no practical use.
*
* Auto-init mode is handled implicitly; a device's transfer handler may
* return an end count lower than the start count.
*
* Naming convention: 'channel' refers to a system-wide DMA channel (0-7)
* while 'chidx' refers to a DMA channel index within a controller (0-3).
*
* References:
* - IBM Personal Computer AT Technical Reference, 1984
* - Intel 8237A-5 Datasheet, 1993
* - Frank van Gilluwe, The Undocumented PC, 1994
* - OPTi 82C206 Data Book, 1996 (or Chips & Tech 82C206)
* - Intel ICH8 Datasheet, 2007
*/
/* Saved state versions. */
/* State information for a single DMA channel. */
typedef struct {
void *pvUser; /* User specific context. */
} DMAChannel;
/* State information for a DMA controller (DMA8 or DMA16). */
typedef struct {
} DMAControl;
/* Complete DMA state information. */
typedef struct {
} DMAState;
/* DMA command register bits. */
enum {
};
/* DMA control register offsets for read accesses. */
enum {
CTL_R_STAT, /* Read status registers. */
CTL_R_DMAREQ, /* Read DRQ register. */
CTL_R_CMD, /* Read command register. */
CTL_R_MODE, /* Read mode register. */
CTL_R_SETBPTR, /* Set byte pointer flip-flop. */
CTL_R_TEMP, /* Read temporary register. */
CTL_R_CLRMODE, /* Clear mode register counter. */
CTL_R_MASK /* Read all DRQ mask bits. */
};
/* DMA control register offsets for read accesses. */
enum {
CTL_W_CMD, /* Write command register. */
CTL_W_DMAREQ, /* Write DRQ register. */
CTL_W_MASKONE, /* Write single DRQ mask bit. */
CTL_W_MODE, /* Write mode register. */
CTL_W_CLRBPTR, /* Clear byte pointer flip-flop. */
CTL_W_MASTRCLR, /* Master clear. */
CTL_W_CLRMASK, /* Clear all DRQ mask bits. */
CTL_W_MASK /* Write all DRQ mask bits. */
};
/* Convert DMA channel number (0-7) to controller number (0-1). */
/* Map a DMA page register offset (0-7) to channel index (0-3). */
#define DMAPG2CX(c) (dmaChannelMap[c])
/* Map a channel index (0-3) to DMA page register offset (0-7). */
#define DMACX2PG(c) (dmaMapChannel[c])
/* Map a channel number (0-7) to DMA page register offset (0-7). */
/* Test the decrement bit of mode register. */
#define IS_MODE_DEC(c) ((c) & 0x20)
/* Test the auto-init bit of mode register. */
#define IS_MODE_AI(c) ((c) & 0x10)
/* Perform a master clear (reset) on a DMA controller. */
{
}
/* Read the byte pointer and flip it. */
{
bool bHighByte;
return bHighByte;
}
/* DMA address registers writes and reads. */
{
if (cb == 1)
{
DMAChannel *ch;
if (dmaReadBytePtr(dc))
{
/* Write the high byte. */
if (is_count)
else
ch->u16CurCount = 0;
}
else
{
/* Write the low byte. */
if (is_count)
else
}
Log2(("dmaWriteAddr: port %#06x, chidx %d, data %#02x\n",
}
else
{
/* Likely a guest bug. */
Log(("Bad size write to count register %#x (size %d, data %#x)\n",
}
return VINF_SUCCESS;
}
{
if (cb == 1)
{
DMAChannel *ch;
int bptr;
if (reg & 1)
else
return VINF_SUCCESS;
}
else
return VERR_IOM_IOPORT_UNUSED;
}
/* DMA control registers writes and reads. */
{
if (cb == 1)
{
int chidx = 0;
int reg;
switch (reg) {
case CTL_W_CMD:
/* Unsupported commands are entirely ignored. */
if (u32 & CMD_UNSUPPORTED)
{
break;
}
break;
case CTL_W_DMAREQ:
if (u32 & 4)
else
break;
case CTL_W_MASKONE:
if (u32 & 4)
else
break;
case CTL_W_MODE:
{
Log2(("chidx %d, op %d, %sauto-init, %screment, opmode %d\n",
break;
}
case CTL_W_CLRBPTR:
break;
case CTL_W_MASTRCLR:
break;
case CTL_W_CLRMASK:
break;
case CTL_W_MASK:
break;
default:
Assert(0);
break;
}
Log(("dmaWriteCtl: port %#06x, chidx %d, data %#02x\n",
}
else
{
/* Likely a guest bug. */
Log(("Bad size write to controller register %#x (size %d, data %#x)\n",
}
return VINF_SUCCESS;
}
{
if (cb == 1)
{
int reg;
switch (reg) {
case CTL_R_STAT:
break;
case CTL_R_DMAREQ:
break;
case CTL_R_CMD:
break;
case CTL_R_MODE:
case CTL_R_SETBPTR:
break;
case CTL_R_TEMP:
break;
case CTL_R_CLRMODE:
break;
case CTL_R_MASK:
break;
default:
Assert(0);
val = 0;
break;
}
return VINF_SUCCESS;
}
else
return VERR_IOM_IOPORT_UNUSED;
}
/* DMA page registers. There are 16 R/W page registers for compatibility with
* accessible via port 80h may be read to insert small delays or used as a scratch
* register by a BIOS.
*/
{
if (cb == 1)
{
int reg;
Log2(("Read %#x to from page register %#x (channel %d)\n",
return VINF_SUCCESS;
}
else
return VERR_IOM_IOPORT_UNUSED;
}
{
if (cb == 1)
{
int reg;
Log2(("Wrote %#x to page register %#x (channel %d)\n",
}
else
{
/* Likely a guest bug. */
Log(("Bad size write to page register %#x (size %d, data %#x)\n",
}
return VINF_SUCCESS;
}
/* EISA style high page registers, for extending the DMA addresses to cover
* the entire 32-bit address space.
*/
{
if (cb == 1)
{
int reg;
Log2(("Read %#x to from high page register %#x (channel %d)\n",
return VINF_SUCCESS;
}
else
return VERR_IOM_IOPORT_UNUSED;
}
{
if (cb == 1)
{
int reg;
Log2(("Wrote %#x to high page register %#x (channel %d)\n",
}
else
{
/* Likely a guest bug. */
Log(("Bad size write to high page register %#x (size %d, data %#x)\n",
}
return VINF_SUCCESS;
}
/* Perform any pending transfers on a single DMA channel. */
{
int opmode;
Log3(("DMA address %screment, mode %d\n",
/* Addresses and counts are shifted for 16-bit channels. */
}
{
DMAControl *dc;
/* Run all controllers and channels. */
{
/* If controller is disabled, don't even bother. */
continue;
{
}
}
return 0;
}
{
LogFlow(("dmaRegister: s=%p channel=%u XferHandler=%p pvUser=%p\n",
}
/* Reverse the order of bytes in a memory buffer. */
{
{
}
}
/* Reverse the order of words in a memory buffer. */
{
{
}
}
{
LogFlow(("dmaReadMemory: s=%p channel=%u buf=%p pos=%u len=%u\n",
/* Build the address for this transfer. */
{
else
}
else
return len;
}
{
LogFlow(("dmaWriteMemory: s=%p channel=%u buf=%p pos=%u len=%u\n",
/* Build the address for this transfer. */
{
//@todo: This would need a temporary buffer.
Assert(0);
#if 0
else
#endif
}
else
return len;
}
{
int chidx;
if (level)
else
}
{
}
{
LogFlow(("dmaReset: s=%p\n", s));
/* NB: The page and address registers are unaffected by a reset
* and in an undefined state after power-up.
*/
}
/* Register DMA I/O port handlers. */
{
/* Base and current address for each channel. */
/* Control registers for both DMA controllers. */
/* Page registers for each channel (plus a few unused ones). */
/* Optional EISA style high page registers (address bits 24-31). */
if (bHighPage)
{
}
}
{
int chidx;
/* Save controller state... */
/* ...and all four of its channels. */
{
}
}
{
int chidx;
if (version > DMA_SAVESTATE_OLD)
{
}
{
if (version == DMA_SAVESTATE_OLD)
{
/* Convert from 17-bit to 16-bit format. */
}
else
{
}
/* Convert from old save state. */
if (version == DMA_SAVESTATE_OLD)
{
/* Remap page register contents. */
/* Throw away dack, eop. */
}
}
return 0;
}
{
return VINF_SUCCESS;
}
{
AssertMsgReturn(uVersion <= DMA_SAVESTATE_CURRENT, ("%d\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
}
/**
* @interface_method_impl{PDMDEVREG,pfnConstruct}
*/
{
bool bHighPage = false;
int rc;
/*
* Validate configuration.
*/
#if 0
if (RT_FAILURE (rc))
return rc;
#endif
if (RT_FAILURE (rc))
return rc;
if (RT_FAILURE(rc))
return rc;
return VINF_SUCCESS;
}
/**
* The device registration structure.
*/
const PDMDEVREG g_DeviceDMA =
{
/* u32Version */
/* szName */
"8237A",
/* szRCMod */
"",
/* szR0Mod */
"",
/* pszDescription */
"DMA Controller Device",
/* fFlags */
/* fClass */
/* cMaxInstances */
1,
/* cbInstance */
sizeof(DMAState),
/* pfnConstruct */
/* pfnDestruct */
NULL,
/* pfnRelocate */
NULL,
/* pfnIOCtl */
NULL,
/* pfnPowerOn */
NULL,
/* pfnReset */
/* pfnSuspend */
NULL,
/* pfnResume */
NULL,
/* pfnAttach */
NULL,
/* pfnDetach */
NULL,
/* pfnQueryInterface. */
NULL,
/* pfnInitComplete */
NULL,
/* pfnPowerOff */
NULL,
/* pfnSoftReset */
NULL,
/* u32VersionEnd */
};