/base/Drivers/Disk/BusMasterDma.cs
C# | 364 lines | 244 code | 51 blank | 69 comment | 28 complexity | 986700e0722b596958d04a532506a5eb MD5 | raw file
- ////////////////////////////////////////////////////////////////////////////////
- //
- // 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)"
-
- //
- // 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.`
- //
-
- //#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();
- if ((status & (byte) BUSMASTER_STATUS_MASK_INTERRUPT) == 0) {
- 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();
- if ((status & (byte)BUSMASTER_STATUS_MASK_INTERRUPT) == 0) {
- Tracing.Log(Tracing.Debug,"BusMaster.Disarm: interrupt line not set {0}!\n",(UIntPtr) status);
- DebugStub.WriteLine("BusMaster.Disarm: interrupt line not set {0}!\n",__arglist(status));
- //DebugStub.Break();
- }
-
- if ((status & (byte)BUSMASTER_STATUS_MASK_ERROR) > 0) {
- Tracing.Log(Tracing.Debug,"BusMaster.Disarm: error!!!!\n",(UIntPtr) status);
- DebugStub.WriteLine("BusMaster.Disarm: error!!!!\n",__arglist(status));
- //DebugStub.Break();
- }
-
- 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()
- {
- for (int i = 0; i < PRD_MAX_ENTRIES; i++) {
- 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