PageRenderTime 57ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/base/Drivers/Disk/BusMasterDma.cs

#
C# | 364 lines | 244 code | 51 blank | 69 comment | 28 complexity | 986700e0722b596958d04a532506a5eb MD5 | raw file
  1. ////////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Microsoft Research Singularity
  4. //
  5. // Copyright (c) Microsoft Corporation. All rights reserved.
  6. //
  7. // File: BusMasterDma.cs
  8. //
  9. // See "Programming Interface for Bus Master IDE Controller"
  10. // or www.t13.org 1510D Draft "ATA/ATAPI Host Adapters Standard(ATA - Adapter)"
  11. //
  12. // 1. Software prepares a PRD Table in host memory.
  13. // 2. Software provides the starting address of the PRD Table by loading the PRD Table
  14. // Pointer Register.
  15. // Setting of the Read/Write Control bit specifies the direction of the data transfer.
  16. // Clearing the Interrupt and Error bits in the ATA Bus Master Status register
  17. // to zero readies the adapter for a data transfer.
  18. // 3. Software issues the appropriate DMA transfer command to the device.
  19. // 4. Software initiates the bus master function by writing a one to the Start bit
  20. // in the ATA Bus Master Command Register for the appropriate channel.
  21. // 5. The adapter transfers data to/from host memory responding to DMA requests from the ATA device.`
  22. //
  23. //#define DEBUG_BUS_MASTER_DMA
  24. using System;
  25. using System.Text;
  26. using System.Threading;
  27. using System.Runtime.CompilerServices; //StructAlign attribute
  28. using System.Runtime.InteropServices; //structLayout attribute
  29. using Microsoft.SingSharp;
  30. using Microsoft.Singularity;
  31. using Microsoft.Singularity.Io;
  32. using Microsoft.Singularity.Drivers.IDE;
  33. using Microsoft.Singularity.V1.Services;
  34. namespace Microsoft.Singularity.Drivers.IDE
  35. {
  36. [CLSCompliant(false)]
  37. public class BusMasterDma
  38. {
  39. enum BmState
  40. {
  41. BmIdle = 0,
  42. BmPrepared ,
  43. BmArmed,
  44. BmDisarmed,
  45. }
  46. public const int PRD_BYTES_PER_ENTRY = 8;
  47. public const int PRD_ALIGNMENT = 4096;
  48. public const int PRD_MAX_ENTRIES = 4096 / PRD_BYTES_PER_ENTRY;
  49. // BusMaster Status Bits
  50. private const byte BUSMASTER_STATUS_MASK_DEVICE1_DMA_OK = 0x40;
  51. private const byte BUSMASTER_STATUS_MASK_DEVICE0_DMA_OK = 0x20;
  52. private const byte BUSMASTER_STATUS_MASK_INTERRUPT = 0x04;
  53. private const byte BUSMASTER_STATUS_MASK_ERROR = 0x02;
  54. private const byte BUSMASTER_STATUS_MASK_ACTIVE = 0x01;
  55. //BusMaster Control bits
  56. private const byte BUSMASTER_CONTROL_MASK_WRITE = 0x08;
  57. private const byte BUSMASTER_CONTROL_MASK_START = 0x01;
  58. private const byte BUSMASTER_CONTROL_MASK_STOP = 0x00;
  59. private IoPort commandPort;
  60. private IoPort statusPort;
  61. private IoPort prdPort;
  62. private IoMemory prdRegion;
  63. private IdeConfig ideConfigHandle;
  64. private bool runningVPC;
  65. private BmState busMasterState; // the state we think the BusMaster it is in
  66. const byte PrdTableTerminate = 0x80;
  67. public BusMasterDma(IdeConfig! ideConfig)
  68. {
  69. ideConfigHandle = ideConfig;
  70. // Set up BusMaster ports for this controller
  71. // <command, status, PRD table>
  72. commandPort = ideConfig.DmaBase.PortAtOffset(0, 1, Access.ReadWrite);
  73. statusPort = ideConfig.DmaBase.PortAtOffset(2, 1, Access.ReadWrite);
  74. prdPort = ideConfig.DmaBase.PortAtOffset(4, 4, Access.ReadWrite);
  75. // PRD needs to be 4-byte aligned and within one 4k page.
  76. prdRegion =
  77. IoMemory.AllocatePhysical(PRD_MAX_ENTRIES * PRD_BYTES_PER_ENTRY,
  78. PRD_ALIGNMENT);
  79. } // BusMasterInitialize
  80. public void Uninitialize ()
  81. {
  82. commandPort = null; //???
  83. }
  84. public void Setup ()
  85. {
  86. }
  87. public void BmPrepareController (IdeRequest! ideRequest)
  88. {
  89. // Perform steps 1 and 2 above: set up PRD, clear
  90. // error snd interrupt
  91. // Init. Scatter Gather List Register
  92. uint thePrd = FillPrdTable(ideRequest);
  93. prdPort.Write32(thePrd);
  94. // Clear Errors
  95. byte status = (byte) (BUSMASTER_STATUS_MASK_INTERRUPT | BUSMASTER_STATUS_MASK_ERROR);
  96. statusPort.Write8(status);
  97. return;
  98. } // BmPrepareController
  99. public void SetVirtualized()
  100. {
  101. runningVPC = true;
  102. return;
  103. }
  104. public int Arm (IdeRequest! ideRequest)
  105. {
  106. byte value = BUSMASTER_CONTROL_MASK_START;
  107. if (ideRequest.Command == IdeCmdType.Read) {
  108. value |= BUSMASTER_CONTROL_MASK_WRITE;
  109. }
  110. commandPort.Write8(value); // enable BM
  111. return 0;
  112. } // BmArm
  113. public byte GetStatus()
  114. {
  115. byte status = statusPort.Read8();
  116. return status;
  117. }
  118. public bool InterruptHigh()
  119. {
  120. //probe the status register to see if we have been interrupted
  121. byte status = statusPort.Read8();
  122. if ((status & (byte) BUSMASTER_STATUS_MASK_INTERRUPT) == 0) {
  123. return false;
  124. }
  125. return true;
  126. }
  127. public void Disarm ()
  128. {
  129. //According to the "Programming Interface for Bus Master IDE Controller"
  130. // 1) reset the Start/Stop bit in command register
  131. // 2) read controller status
  132. // 3) read drive status
  133. // to determine if the xfer completed successfully.
  134. commandPort.Write8(BUSMASTER_CONTROL_MASK_STOP); // disable BM
  135. //Tracing.Log(Tracing.Debug," stop: fullstatus ={0:x}\n",(UIntPtr)ideConfigHandle.IdeController.ReadFullStatus());
  136. byte status = GetStatus();
  137. if ((status & (byte)BUSMASTER_STATUS_MASK_INTERRUPT) == 0) {
  138. Tracing.Log(Tracing.Debug,"BusMaster.Disarm: interrupt line not set {0}!\n",(UIntPtr) status);
  139. DebugStub.WriteLine("BusMaster.Disarm: interrupt line not set {0}!\n",__arglist(status));
  140. //DebugStub.Break();
  141. }
  142. if ((status & (byte)BUSMASTER_STATUS_MASK_ERROR) > 0) {
  143. Tracing.Log(Tracing.Debug,"BusMaster.Disarm: error!!!!\n",(UIntPtr) status);
  144. DebugStub.WriteLine("BusMaster.Disarm: error!!!!\n",__arglist(status));
  145. //DebugStub.Break();
  146. }
  147. status = (byte) (BUSMASTER_STATUS_MASK_INTERRUPT | BUSMASTER_STATUS_MASK_ERROR);
  148. statusPort.Write8( status); // clear interrupt BM
  149. //Tracing.Log(Tracing.Debug,"cntlr status ={0:x}\n",(UIntPtr)ideConfigHandle.IdeController.ReadFullStatus());
  150. //Tracing.Log(Tracing.Debug,"bm status : {0:x}",(UIntPtr) statusPort.Read8());
  151. } // BmDisarm
  152. [ System.Diagnostics.Conditional("DEBUG_BUS_MASTER_DMA") ]
  153. public void DumpPrd()
  154. {
  155. for (int i = 0; i < PRD_MAX_ENTRIES; i++) {
  156. uint address;
  157. uint length;
  158. bool eot;
  159. ReadPrdEntry(i, out address, out length, out eot);
  160. Tracing.Log(Tracing.Debug,
  161. "prd[{0}]: {1:x8} {2:x4}\n",
  162. (UIntPtr) i,
  163. (UIntPtr) address,
  164. (UIntPtr) length);
  165. if (eot) {
  166. Tracing.Log(Tracing.Debug, "prd[{0}]: EOT\n", (UIntPtr) i);
  167. break;
  168. }
  169. }
  170. }
  171. private uint FillPrdTable(IdeRequest! ideRequest)
  172. {
  173. // given a memory address and a length generate
  174. // a Physical Region Descriptor Table (PDRT) to be used
  175. // in a IDE busmaster DMA transfer
  176. //a PRDT table is an array of PRD entries, each 8 bytes in
  177. //length. There is no count associated with this structure
  178. // Bit 7 of the last byte of the last entry signifies the
  179. //end of the table
  180. // PRD (Physical Region Descriptor)
  181. // the first 4 bytes of a PRD specify the memory address
  182. // Bytes 5 and 6 (16 bits) specify the length.
  183. // At most a PRD can specify a transfer of 64KB.
  184. // The memory specified by a PRD cannot cross a 64KB boundary
  185. // Any transfer that would cross such a boundary needs to be
  186. // split into to separate PRDs
  187. uint addr = (uint)((UIntPtr)ideRequest.BufferAddress + ideRequest.BufferOffset);
  188. uint len = (uint)ideRequest.Length;
  189. // Write a bad entry at the end
  190. WritePrdEntry(PRD_MAX_ENTRIES - 1, 0, 0xbad1, true);
  191. uint baseAddr = addr;
  192. uint bytesToMap = len;
  193. int i = 0;
  194. while (0 != bytesToMap) {
  195. uint did = WritePrdChunk(i, baseAddr, bytesToMap);
  196. baseAddr += did;
  197. bytesToMap -= did;
  198. i++;
  199. }
  200. // DEBUG CHECK
  201. uint computedLen = 0 ;
  202. bool eotFound = false;
  203. for (i = 0; i < PRD_MAX_ENTRIES; i++) {
  204. uint dummy;
  205. uint length;
  206. bool eot;
  207. ReadPrdEntry(i, out dummy, out length, out eot);
  208. computedLen = computedLen + length;
  209. if (eot) {
  210. eotFound = true;
  211. break;
  212. }
  213. }
  214. if (computedLen != len || !eotFound) {
  215. throw new ApplicationException("PRD length mismatch");
  216. }
  217. DumpPrd();
  218. return (uint) prdRegion.PhysicalAddress.Value;
  219. }
  220. private UIntPtr Get64KLimit(UIntPtr a)
  221. {
  222. const uint count = 0x10000;
  223. return (a.ToUInt32() + count) & ~(count - 1u);
  224. }
  225. // Write a descriptor for up to 64K of user data. Write stops on
  226. // 64K boundaries or discontinuity in physical memory.
  227. //
  228. // Returns the number of bytes written into PRD.
  229. private uint WritePrdChunk(int prdIndex,
  230. UIntPtr baseVA,
  231. uint baseVALength)
  232. {
  233. UIntPtr basePA;
  234. UIntPtr bytesOnPage;
  235. if (!DeviceService.GetDmaPhysicalAddress(baseVA,
  236. out basePA,
  237. out bytesOnPage)) {
  238. throw new ApplicationException(
  239. "Bad physical address translation"
  240. );
  241. }
  242. UIntPtr basePALimit = Get64KLimit(basePA);
  243. if (basePALimit > basePA + baseVALength) {
  244. basePALimit = basePA + baseVALength;
  245. }
  246. UIntPtr expectedPA = basePA + bytesOnPage;
  247. baseVA += bytesOnPage;
  248. while (expectedPA < basePALimit) {
  249. UIntPtr currentPA;
  250. if (!DeviceService.GetDmaPhysicalAddress(baseVA,
  251. out currentPA,
  252. out bytesOnPage)) {
  253. throw new ApplicationException(
  254. "Bad physical address translation"
  255. );
  256. }
  257. if (currentPA != expectedPA) {
  258. // Physical page discontinuity
  259. break;
  260. }
  261. expectedPA = currentPA + bytesOnPage;
  262. baseVA = baseVA + bytesOnPage;
  263. }
  264. if (expectedPA < basePALimit) {
  265. basePALimit = expectedPA;
  266. }
  267. uint done = (basePALimit - basePA).ToUInt32();
  268. WritePrdEntry(prdIndex,
  269. basePA.ToUInt32(),
  270. (ushort) (done & 0xffff),
  271. done == baseVALength);
  272. return done;
  273. }
  274. private void
  275. WritePrdEntry(int index,
  276. uint addr,
  277. uint length,
  278. bool endOfTable)
  279. requires length != 0 && length <= 0x10000;
  280. {
  281. // PRD structure in octets:
  282. // - 0:3 baseAddress
  283. // - 4:5 length
  284. // - 6:6 reserved
  285. // - 7:7 end of table
  286. int baseOffset = index * PRD_BYTES_PER_ENTRY;
  287. this.prdRegion.Write32(baseOffset,
  288. ByteOrder.HostToLittleEndian(addr));
  289. ushort slen = (ushort)(length & 0xffffu); // 0 == 64K
  290. this.prdRegion.Write16(baseOffset + 4,
  291. ByteOrder.HostToLittleEndian(slen));
  292. this.prdRegion.Write8(baseOffset + 7,
  293. endOfTable ? PrdTableTerminate : (byte)0);
  294. }
  295. private void
  296. ReadPrdEntry(int index,
  297. out uint addr,
  298. out uint length,
  299. out bool endOfTable)
  300. {
  301. int baseOffset = index * PRD_BYTES_PER_ENTRY;
  302. addr = ByteOrder.LittleEndianToHost(
  303. this.prdRegion.Read32(baseOffset)
  304. );
  305. ushort slen = ByteOrder.LittleEndianToHost(
  306. this.prdRegion.Read16(baseOffset + 4)
  307. );
  308. length = (slen == 0) ? 0x10000u : (uint)slen;
  309. byte end = this.prdRegion.Read8(baseOffset + 7);
  310. endOfTable = (end == PrdTableTerminate);
  311. }
  312. } // BusMaster Class
  313. }//namespace