2008-03-05 09:52:00 -05:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Microsoft Research Singularity
|
|
|
|
//
|
|
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
//
|
|
|
|
// File: BusMasterDma.cs
|
|
|
|
//
|
|
|
|
// See "Programming Interface for Bus Master IDE Controller"
|
|
|
|
// or www.t13.org 1510D Draft "ATA/ATAPI Host Adapters Standard(ATA - Adapter)"
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
//
|
|
|
|
// 1. Software prepares a PRD Table in host memory.
|
|
|
|
// 2. Software provides the starting address of the PRD Table by loading the PRD Table
|
|
|
|
// Pointer Register.
|
|
|
|
// Setting of the Read/Write Control bit specifies the direction of the data transfer.
|
|
|
|
// Clearing the Interrupt and Error bits in the ATA Bus Master Status register
|
|
|
|
// to zero readies the adapter for a data transfer.
|
|
|
|
// 3. Software issues the appropriate DMA transfer command to the device.
|
|
|
|
// 4. Software initiates the bus master function by writing a one to the Start bit
|
|
|
|
// in the ATA Bus Master Command Register for the appropriate channel.
|
|
|
|
// 5. The adapter transfers data to/from host memory responding to DMA requests from the ATA device.`
|
|
|
|
//
|
2008-03-05 09:52:00 -05:00
|
|
|
|
|
|
|
//#define DEBUG_BUS_MASTER_DMA
|
|
|
|
|
|
|
|
using System;
|
|
|
|
using System.Text;
|
|
|
|
using System.Threading;
|
|
|
|
using System.Runtime.CompilerServices; //StructAlign attribute
|
|
|
|
using System.Runtime.InteropServices; //structLayout attribute
|
|
|
|
using Microsoft.SingSharp;
|
|
|
|
using Microsoft.Singularity;
|
|
|
|
using Microsoft.Singularity.Io;
|
|
|
|
using Microsoft.Singularity.Drivers.IDE;
|
|
|
|
using Microsoft.Singularity.V1.Services;
|
|
|
|
|
|
|
|
namespace Microsoft.Singularity.Drivers.IDE
|
|
|
|
{
|
|
|
|
[CLSCompliant(false)]
|
|
|
|
public class BusMasterDma
|
|
|
|
{
|
|
|
|
enum BmState
|
|
|
|
{
|
|
|
|
BmIdle = 0,
|
|
|
|
BmPrepared ,
|
|
|
|
BmArmed,
|
|
|
|
BmDisarmed,
|
|
|
|
}
|
|
|
|
|
|
|
|
public const int PRD_BYTES_PER_ENTRY = 8;
|
|
|
|
public const int PRD_ALIGNMENT = 4096;
|
|
|
|
public const int PRD_MAX_ENTRIES = 4096 / PRD_BYTES_PER_ENTRY;
|
|
|
|
|
|
|
|
// BusMaster Status Bits
|
|
|
|
private const byte BUSMASTER_STATUS_MASK_DEVICE1_DMA_OK = 0x40;
|
|
|
|
private const byte BUSMASTER_STATUS_MASK_DEVICE0_DMA_OK = 0x20;
|
|
|
|
private const byte BUSMASTER_STATUS_MASK_INTERRUPT = 0x04;
|
|
|
|
private const byte BUSMASTER_STATUS_MASK_ERROR = 0x02;
|
|
|
|
private const byte BUSMASTER_STATUS_MASK_ACTIVE = 0x01;
|
|
|
|
|
|
|
|
//BusMaster Control bits
|
|
|
|
private const byte BUSMASTER_CONTROL_MASK_WRITE = 0x08;
|
|
|
|
private const byte BUSMASTER_CONTROL_MASK_START = 0x01;
|
|
|
|
private const byte BUSMASTER_CONTROL_MASK_STOP = 0x00;
|
|
|
|
|
|
|
|
private IoPort commandPort;
|
|
|
|
private IoPort statusPort;
|
|
|
|
private IoPort prdPort;
|
|
|
|
private IoMemory prdRegion;
|
|
|
|
|
|
|
|
private IdeConfig ideConfigHandle;
|
|
|
|
private bool runningVPC;
|
|
|
|
|
|
|
|
private BmState busMasterState; // the state we think the BusMaster it is in
|
|
|
|
|
|
|
|
const byte PrdTableTerminate = 0x80;
|
|
|
|
|
|
|
|
public BusMasterDma(IdeConfig! ideConfig)
|
|
|
|
{
|
|
|
|
ideConfigHandle = ideConfig;
|
|
|
|
|
|
|
|
// Set up BusMaster ports for this controller
|
|
|
|
// <command, status, PRD table>
|
|
|
|
commandPort = ideConfig.DmaBase.PortAtOffset(0, 1, Access.ReadWrite);
|
|
|
|
statusPort = ideConfig.DmaBase.PortAtOffset(2, 1, Access.ReadWrite);
|
|
|
|
prdPort = ideConfig.DmaBase.PortAtOffset(4, 4, Access.ReadWrite);
|
|
|
|
// PRD needs to be 4-byte aligned and within one 4k page.
|
|
|
|
prdRegion =
|
|
|
|
IoMemory.AllocatePhysical(PRD_MAX_ENTRIES * PRD_BYTES_PER_ENTRY,
|
|
|
|
PRD_ALIGNMENT);
|
|
|
|
} // BusMasterInitialize
|
|
|
|
|
|
|
|
public void Uninitialize ()
|
|
|
|
{
|
|
|
|
commandPort = null; //???
|
|
|
|
}
|
|
|
|
|
|
|
|
public void Setup ()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
public void BmPrepareController (IdeRequest! ideRequest)
|
|
|
|
{
|
|
|
|
// Perform steps 1 and 2 above: set up PRD, clear
|
|
|
|
// error snd interrupt
|
|
|
|
|
|
|
|
// Init. Scatter Gather List Register
|
|
|
|
uint thePrd = FillPrdTable(ideRequest);
|
|
|
|
prdPort.Write32(thePrd);
|
|
|
|
|
|
|
|
// Clear Errors
|
|
|
|
byte status = (byte) (BUSMASTER_STATUS_MASK_INTERRUPT | BUSMASTER_STATUS_MASK_ERROR);
|
|
|
|
statusPort.Write8(status);
|
|
|
|
|
|
|
|
return;
|
|
|
|
} // BmPrepareController
|
|
|
|
|
|
|
|
public void SetVirtualized()
|
|
|
|
{
|
|
|
|
runningVPC = true;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
public int Arm (IdeRequest! ideRequest)
|
|
|
|
{
|
|
|
|
byte value = BUSMASTER_CONTROL_MASK_START;
|
|
|
|
|
|
|
|
if (ideRequest.Command == IdeCmdType.Read) {
|
|
|
|
value |= BUSMASTER_CONTROL_MASK_WRITE;
|
|
|
|
}
|
|
|
|
commandPort.Write8(value); // enable BM
|
|
|
|
return 0;
|
|
|
|
} // BmArm
|
|
|
|
|
|
|
|
public byte GetStatus()
|
|
|
|
{
|
|
|
|
byte status = statusPort.Read8();
|
|
|
|
return status;
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool InterruptHigh()
|
|
|
|
{
|
|
|
|
//probe the status register to see if we have been interrupted
|
|
|
|
byte status = statusPort.Read8();
|
2008-11-17 18:29:00 -05:00
|
|
|
if ((status & (byte) BUSMASTER_STATUS_MASK_INTERRUPT) == 0) {
|
2008-03-05 09:52:00 -05:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
|
|
|
|
}
|
|
|
|
public void Disarm ()
|
|
|
|
{
|
|
|
|
//According to the "Programming Interface for Bus Master IDE Controller"
|
|
|
|
// 1) reset the Start/Stop bit in command register
|
|
|
|
// 2) read controller status
|
|
|
|
// 3) read drive status
|
|
|
|
// to determine if the xfer completed successfully.
|
|
|
|
|
|
|
|
commandPort.Write8(BUSMASTER_CONTROL_MASK_STOP); // disable BM
|
|
|
|
//Tracing.Log(Tracing.Debug," stop: fullstatus ={0:x}\n",(UIntPtr)ideConfigHandle.IdeController.ReadFullStatus());
|
|
|
|
byte status = GetStatus();
|
2008-11-17 18:29:00 -05:00
|
|
|
if ((status & (byte)BUSMASTER_STATUS_MASK_INTERRUPT) == 0) {
|
2008-03-05 09:52:00 -05:00
|
|
|
Tracing.Log(Tracing.Debug,"BusMaster.Disarm: interrupt line not set {0}!\n",(UIntPtr) status);
|
2008-11-17 18:29:00 -05:00
|
|
|
DebugStub.WriteLine("BusMaster.Disarm: interrupt line not set {0}!\n",__arglist(status));
|
|
|
|
//DebugStub.Break();
|
2008-03-05 09:52:00 -05:00
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
if ((status & (byte)BUSMASTER_STATUS_MASK_ERROR) > 0) {
|
2008-03-05 09:52:00 -05:00
|
|
|
Tracing.Log(Tracing.Debug,"BusMaster.Disarm: error!!!!\n",(UIntPtr) status);
|
2008-11-17 18:29:00 -05:00
|
|
|
DebugStub.WriteLine("BusMaster.Disarm: error!!!!\n",__arglist(status));
|
|
|
|
//DebugStub.Break();
|
2008-03-05 09:52:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
status = (byte) (BUSMASTER_STATUS_MASK_INTERRUPT | BUSMASTER_STATUS_MASK_ERROR);
|
|
|
|
statusPort.Write8( status); // clear interrupt BM
|
|
|
|
|
|
|
|
//Tracing.Log(Tracing.Debug,"cntlr status ={0:x}\n",(UIntPtr)ideConfigHandle.IdeController.ReadFullStatus());
|
|
|
|
//Tracing.Log(Tracing.Debug,"bm status : {0:x}",(UIntPtr) statusPort.Read8());
|
|
|
|
} // BmDisarm
|
|
|
|
|
|
|
|
[ System.Diagnostics.Conditional("DEBUG_BUS_MASTER_DMA") ]
|
|
|
|
public void DumpPrd()
|
|
|
|
{
|
2008-11-17 18:29:00 -05:00
|
|
|
for (int i = 0; i < PRD_MAX_ENTRIES; i++) {
|
2008-03-05 09:52:00 -05:00
|
|
|
uint address;
|
|
|
|
uint length;
|
|
|
|
bool eot;
|
|
|
|
|
|
|
|
ReadPrdEntry(i, out address, out length, out eot);
|
|
|
|
Tracing.Log(Tracing.Debug,
|
|
|
|
"prd[{0}]: {1:x8} {2:x4}\n",
|
|
|
|
(UIntPtr) i,
|
|
|
|
(UIntPtr) address,
|
|
|
|
(UIntPtr) length);
|
|
|
|
if (eot) {
|
|
|
|
Tracing.Log(Tracing.Debug, "prd[{0}]: EOT\n", (UIntPtr) i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private uint FillPrdTable(IdeRequest! ideRequest)
|
|
|
|
{
|
|
|
|
// given a memory address and a length generate
|
|
|
|
// a Physical Region Descriptor Table (PDRT) to be used
|
|
|
|
// in a IDE busmaster DMA transfer
|
|
|
|
|
|
|
|
//a PRDT table is an array of PRD entries, each 8 bytes in
|
|
|
|
//length. There is no count associated with this structure
|
|
|
|
// Bit 7 of the last byte of the last entry signifies the
|
|
|
|
//end of the table
|
|
|
|
|
|
|
|
// PRD (Physical Region Descriptor)
|
|
|
|
// the first 4 bytes of a PRD specify the memory address
|
|
|
|
// Bytes 5 and 6 (16 bits) specify the length.
|
|
|
|
// At most a PRD can specify a transfer of 64KB.
|
|
|
|
// The memory specified by a PRD cannot cross a 64KB boundary
|
|
|
|
// Any transfer that would cross such a boundary needs to be
|
|
|
|
// split into to separate PRDs
|
|
|
|
|
|
|
|
uint addr = (uint)((UIntPtr)ideRequest.BufferAddress + ideRequest.BufferOffset);
|
|
|
|
uint len = (uint)ideRequest.Length;
|
|
|
|
|
|
|
|
// Write a bad entry at the end
|
|
|
|
WritePrdEntry(PRD_MAX_ENTRIES - 1, 0, 0xbad1, true);
|
|
|
|
|
|
|
|
uint baseAddr = addr;
|
|
|
|
uint bytesToMap = len;
|
|
|
|
int i = 0;
|
|
|
|
while (0 != bytesToMap) {
|
|
|
|
uint did = WritePrdChunk(i, baseAddr, bytesToMap);
|
|
|
|
baseAddr += did;
|
|
|
|
bytesToMap -= did;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// DEBUG CHECK
|
|
|
|
uint computedLen = 0 ;
|
|
|
|
bool eotFound = false;
|
|
|
|
for (i = 0; i < PRD_MAX_ENTRIES; i++) {
|
|
|
|
uint dummy;
|
|
|
|
uint length;
|
|
|
|
bool eot;
|
|
|
|
ReadPrdEntry(i, out dummy, out length, out eot);
|
|
|
|
computedLen = computedLen + length;
|
|
|
|
if (eot) {
|
|
|
|
eotFound = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (computedLen != len || !eotFound) {
|
|
|
|
throw new ApplicationException("PRD length mismatch");
|
|
|
|
}
|
|
|
|
DumpPrd();
|
|
|
|
|
|
|
|
return (uint) prdRegion.PhysicalAddress.Value;
|
|
|
|
}
|
|
|
|
|
|
|
|
private UIntPtr Get64KLimit(UIntPtr a)
|
|
|
|
{
|
|
|
|
const uint count = 0x10000;
|
|
|
|
return (a.ToUInt32() + count) & ~(count - 1u);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Write a descriptor for up to 64K of user data. Write stops on
|
|
|
|
// 64K boundaries or discontinuity in physical memory.
|
|
|
|
//
|
|
|
|
// Returns the number of bytes written into PRD.
|
|
|
|
private uint WritePrdChunk(int prdIndex,
|
|
|
|
UIntPtr baseVA,
|
|
|
|
uint baseVALength)
|
|
|
|
{
|
|
|
|
UIntPtr basePA;
|
|
|
|
UIntPtr bytesOnPage;
|
|
|
|
|
|
|
|
if (!DeviceService.GetDmaPhysicalAddress(baseVA,
|
|
|
|
out basePA,
|
|
|
|
out bytesOnPage)) {
|
|
|
|
throw new ApplicationException(
|
|
|
|
"Bad physical address translation"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
UIntPtr basePALimit = Get64KLimit(basePA);
|
|
|
|
if (basePALimit > basePA + baseVALength) {
|
|
|
|
basePALimit = basePA + baseVALength;
|
|
|
|
}
|
|
|
|
|
|
|
|
UIntPtr expectedPA = basePA + bytesOnPage;
|
|
|
|
baseVA += bytesOnPage;
|
|
|
|
while (expectedPA < basePALimit) {
|
|
|
|
UIntPtr currentPA;
|
|
|
|
if (!DeviceService.GetDmaPhysicalAddress(baseVA,
|
|
|
|
out currentPA,
|
|
|
|
out bytesOnPage)) {
|
|
|
|
throw new ApplicationException(
|
|
|
|
"Bad physical address translation"
|
|
|
|
);
|
|
|
|
}
|
|
|
|
if (currentPA != expectedPA) {
|
|
|
|
// Physical page discontinuity
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
expectedPA = currentPA + bytesOnPage;
|
|
|
|
baseVA = baseVA + bytesOnPage;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (expectedPA < basePALimit) {
|
|
|
|
basePALimit = expectedPA;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint done = (basePALimit - basePA).ToUInt32();
|
|
|
|
|
|
|
|
WritePrdEntry(prdIndex,
|
|
|
|
basePA.ToUInt32(),
|
|
|
|
(ushort) (done & 0xffff),
|
|
|
|
done == baseVALength);
|
|
|
|
|
|
|
|
return done;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void
|
|
|
|
WritePrdEntry(int index,
|
|
|
|
uint addr,
|
|
|
|
uint length,
|
|
|
|
bool endOfTable)
|
|
|
|
requires length != 0 && length <= 0x10000;
|
|
|
|
{
|
|
|
|
// PRD structure in octets:
|
|
|
|
// - 0:3 baseAddress
|
|
|
|
// - 4:5 length
|
|
|
|
// - 6:6 reserved
|
|
|
|
// - 7:7 end of table
|
|
|
|
int baseOffset = index * PRD_BYTES_PER_ENTRY;
|
|
|
|
this.prdRegion.Write32(baseOffset,
|
|
|
|
ByteOrder.HostToLittleEndian(addr));
|
|
|
|
ushort slen = (ushort)(length & 0xffffu); // 0 == 64K
|
|
|
|
this.prdRegion.Write16(baseOffset + 4,
|
|
|
|
ByteOrder.HostToLittleEndian(slen));
|
|
|
|
this.prdRegion.Write8(baseOffset + 7,
|
|
|
|
endOfTable ? PrdTableTerminate : (byte)0);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void
|
|
|
|
ReadPrdEntry(int index,
|
|
|
|
out uint addr,
|
|
|
|
out uint length,
|
|
|
|
out bool endOfTable)
|
|
|
|
{
|
|
|
|
int baseOffset = index * PRD_BYTES_PER_ENTRY;
|
|
|
|
addr = ByteOrder.LittleEndianToHost(
|
|
|
|
this.prdRegion.Read32(baseOffset)
|
|
|
|
);
|
|
|
|
ushort slen = ByteOrder.LittleEndianToHost(
|
|
|
|
this.prdRegion.Read16(baseOffset + 4)
|
|
|
|
);
|
|
|
|
length = (slen == 0) ? 0x10000u : (uint)slen;
|
|
|
|
byte end = this.prdRegion.Read8(baseOffset + 7);
|
|
|
|
endOfTable = (end == PrdTableTerminate);
|
|
|
|
}
|
|
|
|
} // BusMaster Class
|
|
|
|
}//namespace
|