singrdk/base/Drivers/Disk/IdeDisk.sg

898 lines
37 KiB
Plaintext
Raw Normal View History

2008-03-05 09:52:00 -05:00
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: IdeDisk.cs
//
// Base Classes and Interfaces for ATA Disk devices
//
// References used:
// "AT Attachment Interface with Extension (ATA-2)", Revision 4c, DRAFT
// 09/03/97, ISO Working Group T13, http://www.t13.org/
//
// #define DEBUG_LBA48
#define DEBUG_SHARED_IRQ
using System;
using System.Diagnostics;
using Microsoft.SingSharp;
using Microsoft.Contracts;
2008-11-17 18:29:00 -05:00
using System.Threading;
2008-03-05 09:52:00 -05:00
using Microsoft.Singularity;
using Microsoft.Singularity.Channels;
using Microsoft.Singularity.Directory;
using Microsoft.Singularity.Io;
using System.Runtime.CompilerServices; //StructAlign attribute
using System.Runtime.InteropServices; //structLayout attribute
using System.GCs;
using Microsoft.Singularity.V1.Services;
using Microsoft.Singularity.Extending;
2008-11-17 18:29:00 -05:00
using UserAllocation = Microsoft.Singularity.V1.Services.SharedHeapService.Allocation;
2008-03-05 09:52:00 -05:00
namespace Microsoft.Singularity.Drivers.IDE
{
public enum IdeCmdType
{
Read = 0,
Write = 1,
}
[CLSCompliant(false)]
public class IdeRequest
{
public const int SECTOR_SIZE = 512;
2008-11-17 18:29:00 -05:00
// [grp] make fields private and add properties??
// [grp] performance difference should optimize away
2008-03-05 09:52:00 -05:00
public ulong SectorNumber;
2008-11-17 18:29:00 -05:00
//\ public byte* opt(ExHeap[]) Buffer;
2008-03-05 09:52:00 -05:00
public UIntPtr BufferOffset;
2008-11-17 18:29:00 -05:00
/// <summary>Note: This is a pointer into a byte array in the ExHeap.</summary>
2008-03-05 09:52:00 -05:00
public byte * BufferAddress;
public UIntPtr BufferLength;
public UIntPtr Length;
public IdeCmdType Command;
public IdeRequest()
{
}
public void Set(ulong sector,
byte* opt(ExHeap[])! buffer,
UIntPtr bufferOffset,
UIntPtr len,
IdeCmdType command)
{
this.SectorNumber = sector;
2008-11-17 18:29:00 -05:00
//\ this.Buffer = buffer;
2008-03-05 09:52:00 -05:00
this.Length = len;
this.Command = command;
this.BufferOffset = bufferOffset;
this.BufferLength = (UIntPtr)buffer.Length;
unsafe { this.BufferAddress = (byte*) &buffer[0]; }
}
}
2008-11-17 18:29:00 -05:00
#region IdeDisk
2008-03-05 09:52:00 -05:00
public class IdeDisk
{
public const int MAX_LBA28_SECTOR_COUNT = 256;
public const int MAX_LBA48_SECTOR_COUNT = 65536;
public const byte IDE_MODE_SET_ULTRA_DMA = 0x40; // page 176
public const byte IDE_FEATURE_SET_TRANSFER_MODE = 0x03; // Set the transfer mode.
private string deviceName;
private string registeredName; //with Directory
private string strDevSel;
private byte devsel;
private bool atapi;
private IdentifyDeviceInformation ident;
private bool present = false;
private IdeRequest ideRequest;
private IdeConfig ideConfigHandle;
private string ideInstanceName;
private IdeDiskConfig ideDiskConfig;
private IdeController controller;
2008-11-17 18:29:00 -05:00
private int maxSectorCount; // [grp] camel case privates?
2008-03-05 09:52:00 -05:00
private byte readCommand;
private byte writeCommand;
private int diskId;
private static int diskIdGenerator = 0;
2008-11-17 18:29:00 -05:00
private static int connectionCount = 0;
2008-03-05 09:52:00 -05:00
[CLSCompliant(false)]
private enum DiskIoEvent : ushort
{
2008-11-17 18:29:00 -05:00
Start = 0, // +1 if this is a write // [grp] Seems excessively economical - make explicit?
2008-03-05 09:52:00 -05:00
End = 2, // +1 if this is a write
NewDiskName = 4,
NewDiskId = 5,
}
private class DiskConnectionState
{
public const int NoSystemId = 0xffff; // Whole disk
private int systemId;
private ulong startSector;
private ulong sectorCount;
public DiskConnectionState(int sysId, ulong start, ulong count)
{
this.systemId = sysId;
this.startSector = start;
this.sectorCount = count;
}
public DiskConnectionState(ulong start, ulong count)
2008-11-17 18:29:00 -05:00
: this(NoSystemId, start, count)
2008-03-05 09:52:00 -05:00
{
}
public DiskAttributes DiskAttributes
{
get { return 0; }
}
public int SystemId
{
get { return this.systemId; }
}
public ulong StartSector
{
get { return this.startSector; }
}
public ulong SectorCount
{
get { return this.sectorCount; }
}
}
2008-11-17 18:29:00 -05:00
// TODO: need to set up connection to VolumeManager
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
// TODO: do we still need ideDiskConfig.DeviceName field,
2008-03-05 09:52:00 -05:00
// now that this driver no longer needs to know its public name?
public void Finalize()
{
Tracing.Log(Tracing.Debug," Ide Disk: Finalize");
}
public void Run([Claims] ExtensionContract.Exp! extension,
[Claims] ServiceProviderContract.Exp:Start! service,
[Claims] VolumeManagerContract.Imp:Start! volman)
{
2008-11-17 18:29:00 -05:00
//\ Tracing.Log(Tracing.Debug," Ide Disk: initialize\n");
2008-03-05 09:52:00 -05:00
this.ideRequest = new IdeRequest();
if (!this.present) {
delete extension;
delete service;
delete volman;
return;
}
this.registeredName = this.ideDiskConfig.DeviceName;
Tracing.Log(Tracing.Debug,"found VolMgr, awaiting Success");
DebugStub.Print("Waiting for VolMgr to ack.\n");
switch receive {
case volman.Success():
Tracing.Log(Tracing.Debug,"Should be in ready state");
break;
case volman.ContractNotSupported():
Tracing.Log(Tracing.Debug,"volman is not a volume manager");
delete extension;
delete service;
delete volman;
return;
case volman.ChannelClosed() :
Tracing.Log(Tracing.Debug,"volman has closed channel");
delete extension;
delete service;
delete volman;
return;
}
DebugStub.Print("Connected to VolMgr.\n");
SetUdmaMode();
// copy disk name to SharedHeap
Tracing.Log(Tracing.Debug,"Registering disk {0}", this.registeredName);
assert volman.InState(VolumeManagerContract.Ready.Value);
2008-11-17 18:29:00 -05:00
if (!this.atapi) {
// Assume CD - ROM (ie., no MBR, partitions) if Atapi
2008-03-05 09:52:00 -05:00
volman.SendRegisterDisk(Bitter.FromString2(this.registeredName));
2008-11-17 18:29:00 -05:00
}
2008-03-05 09:52:00 -05:00
Tracing.Log(Tracing.Audit, "Starting diskMsgPump.");
extension.SendSuccess();
// create a set of all client endpoints connected to the device
EMap<DiskDeviceContract.Exp:Ready,DiskConnectionState!> clients =
2008-11-17 18:29:00 -05:00
new EMap<DiskDeviceContract.Exp:Ready,DiskConnectionState!>();
2008-03-05 09:52:00 -05:00
// These sets will only ever contain one thing, but they are here to
// save memory, since this avoids boxing these endpoints
// once per iteration.
ESet<ServiceProviderContract.Exp:Start> serviceproviders =
2008-11-17 18:29:00 -05:00
new ESet<ServiceProviderContract.Exp:Start>();
2008-03-05 09:52:00 -05:00
serviceproviders.Add(service);
ESet<VolumeManagerContract.Imp:RegisterAck> volumemanagers1 =
2008-11-17 18:29:00 -05:00
new ESet<VolumeManagerContract.Imp:RegisterAck>();
2008-03-05 09:52:00 -05:00
volumemanagers1.Add(volman);
ESet<VolumeManagerContract.Imp:Accept> volumemanagers2 =
2008-11-17 18:29:00 -05:00
new ESet<VolumeManagerContract.Imp:Accept>();
2008-03-05 09:52:00 -05:00
ESet<ExtensionContract.Exp:Ready> extensions =
2008-11-17 18:29:00 -05:00
new ESet<ExtensionContract.Exp:Ready>();
2008-03-05 09:52:00 -05:00
extensions.Add(extension);
try {
for (bool run = true; run;) {
switch receive {
// Listen for new connections
case sp.Connect(candidate) in serviceproviders:
DiskDeviceContract.Exp newClient = candidate as DiskDeviceContract.Exp;
if (newClient != null) {
newClient.SendSuccess();
// switching to use lba48 numbers
DiskConnectionState! conn = new DiskConnectionState(0, this.ident.MaxLBASectors);
clients.Add(newClient, conn);
sp.SendAckConnect();
}
else {
sp.SendNackConnect(candidate);
}
serviceproviders.Add(sp);
break;
case sp.ChannelClosed() in serviceproviders:
delete sp;
break;
2008-11-17 18:29:00 -05:00
// Listen for client departure
2008-03-05 09:52:00 -05:00
case ep.ChannelClosed() in clients~>connState:
Tracing.Log(Tracing.Debug, "Client channel closes.");
delete ep;
2008-11-17 18:29:00 -05:00
//\ IdeDisk.DumpGCStats();
2008-03-05 09:52:00 -05:00
break;
2008-11-17 18:29:00 -05:00
// Listen for extension parent
2008-03-05 09:52:00 -05:00
case ext.Shutdown() in extensions:
ext.SendAckShutdown();
delete ext;
run = false;
break;
case ext.ChannelClosed() in extensions:
delete ext;
break;
2008-11-17 18:29:00 -05:00
//Listen for messages on Volume Manager
2008-03-05 09:52:00 -05:00
case vm.Connect(candidate, systemId, start, count) in volumemanagers2:
DiskDeviceContract.Exp newClient = candidate as DiskDeviceContract.Exp;
if (newClient != null) {
newClient.SendSuccess();
DiskConnectionState! conn = new DiskConnectionState(systemId, start, count);
clients.Add(newClient,conn);
vm.SendAckConnect();
}
else {
vm.SendNakConnect(candidate);
}
volumemanagers2.Add(vm);
break;
case vm.ChannelClosed() in volumemanagers2:
delete vm;
break;
case vm.AckRegisterDisk() in volumemanagers1:
Tracing.Log(Tracing.Debug,"volman has accepted the disk");
// transition vm into Accept state
assert vm.InState(VolumeManagerContract.Accept.Value);
volumemanagers2.Add(vm);
break;
case vm.NakRegisterDisk() in volumemanagers1:
Tracing.Log(Tracing.Debug,"volman did not want to register our disk");
delete vm;
break;
case vm.ChannelClosed() in volumemanagers1:
Tracing.Log(Tracing.Debug,"volman has closed channel");
delete vm;
break;
2008-11-17 18:29:00 -05:00
// Listen for client requests
2008-03-05 09:52:00 -05:00
case ep.GetDeviceName()in clients~>connState:
char[] in ExHeap blee = Bitter.FromString(GetDeviceName());
ep.SendAckGetDeviceName(blee);
clients.Add(ep,connState);
break;
case ep.GetDiskAttributes() in clients~>connState:
ep.SendAckGetDiskAttributes(connState.DiskAttributes);
clients.Add(ep, connState);
break;
case ep.GetStartSector() in clients~>connState:
ep.SendAckGetStartSector(connState.StartSector);
clients.Add(ep, connState);
break;
case ep.GetSectorCount() in clients~>connState:
ep.SendAckGetSectorCount(connState.SectorCount);
clients.Add(ep, connState);
break;
case ep.GetSystemId() in clients~>connState:
int systemId = connState.SystemId;
if (systemId != DiskConnectionState.NoSystemId) {
ep.SendSystemId((byte)systemId);
}
else {
ep.SendNoSystemId();
}
clients.Add(ep, connState);
break;
case ep.Write(data, offset, length, sectorId) in clients~>connState:
if (data == null) {
Tracing.Log(Tracing.Debug,"data buffer is null!!!\n");
ep.SendNakWrite();
}
2008-11-17 18:29:00 -05:00
else if (this.atapi) { // assume CD - ROM
2008-03-05 09:52:00 -05:00
Tracing.Log(Tracing.Debug,"can't write to atapi cdrom\n");
delete data;
ep.SendNakWrite();
}
else {
//byte* addr;
//unsafe { addr = (byte*) &data[0]; }
Tracing.Log(Tracing.Debug,"write: sector={0} len={1}",sectorId,length);
ReadWrite(true, data, offset, length, sectorId, connState);
ep.SendAckWrite(data);
}
clients.Add(ep, connState);
Tracing.Log(Tracing.Debug,"write: ends.");
break;
case ep.NoOp()in clients~>connState:
//DebugStub.Break();
ep.SendAckNoOp();
clients.Add(ep, connState);
break;
case ep.Read(data, offset, length, sectorId) in clients~>connState:
if (data == null) {
Tracing.Log(Tracing.Debug,"data buffer is null!!!\n");
ep.SendNakRead();
}
else {
//byte* addr;
//unsafe { addr = (byte*) &data[0]; }
Tracing.Log(Tracing.Debug,"read: sector={0} len={1}", sectorId, length);
ReadWrite(false, data, offset, length, sectorId, connState);
ep.SendAckRead(data);
}
clients.Add(ep, connState);
Tracing.Log(Tracing.Debug,"read: ends.");
break;
case ep.ReadPerf(numMB, chunkSize) in clients~>connState:
2008-11-17 18:29:00 -05:00
{
2008-03-05 09:52:00 -05:00
long cycles;
long ticks;
ReadPerf(numMB, chunkSize, out cycles, out ticks, connState);
ep.SendAckReadPerf(cycles, ticks);
clients.Add(ep, connState);
break;
2008-11-17 18:29:00 -05:00
}
2008-03-05 09:52:00 -05:00
// If all else fails....
case clients.Empty() && serviceproviders.Empty() && volumemanagers1.Empty() && volumemanagers2.Empty() && extensions.Empty():
Tracing.Log(Tracing.Debug, "Disk driver has no more connections");
run = false;
break;
2008-11-17 18:29:00 -05:00
}
}
2008-03-05 09:52:00 -05:00
}
finally {
Tracing.Log(Tracing.Debug, "diskMsgPump exiting.");
clients.Dispose();
serviceproviders.Dispose();
extensions.Dispose();
volumemanagers1.Dispose();
volumemanagers2.Dispose();
}
}
[NotDelayed]
public IdeDisk(IdeDiskConfig! config, string! instanceName)
{
DebugStub.Print("IdeDisk: {0}\n", __arglist(instanceName));
this.ideDiskConfig = config;
this.ideInstanceName = instanceName;
this.ideConfigHandle = config.ControllerConfig;
this.controller = config.ControllerConfig.IdeController;
this.devsel = config.DiskNumber;
this.atapi = config.Atapi;
if (this.devsel == 0) {
this.strDevSel = "master";
}
else {
this.strDevSel = "slave";
this.devsel = ATA6.DriveSelect;
}
try {
this.ident = ATA6.GetDeviceIdentification(this.controller, this.devsel, this.atapi);
}
catch {
Tracing.Log(Tracing.Debug," GetDeviceIdentification failed!\n");
this.present = false;
return;
}
if (!this.ident.LBASupported) {
Tracing.Log(Tracing.Debug,"IdeDisk: Device ({0}) missing or does not support LBA.\n",
2008-11-17 18:29:00 -05:00
this.controller.ControllerName);
DebugStub.WriteLine("IdeDisk: Device ({0}) missing or does not support LBA.\n",
__arglist(this.controller.ControllerName));
2008-03-05 09:52:00 -05:00
this.present = false;
this.deviceName = String.Format("IdeDisk: Device ({0}) {1} missing or not supported.\n",
2008-11-17 18:29:00 -05:00
this.strDevSel, this.controller.ControllerName);
2008-03-05 09:52:00 -05:00
return;
}
if (this.ident.LBA48Enabled) {
this.maxSectorCount = MAX_LBA48_SECTOR_COUNT;
this.readCommand = (byte) ATA6.IdeCommand.ReadDmaExt;
this.writeCommand = (byte) ATA6.IdeCommand.WriteDmaExt;
}
else {
this.maxSectorCount = MAX_LBA28_SECTOR_COUNT;
this.readCommand = (byte) ATA6.IdeCommand.ReadDmaRetry;
this.writeCommand = (byte) ATA6.IdeCommand.WriteDmaRetry;
}
this.present = true;
if (this.ident.ModelNumber == "Virtual HD" ||
this.ident.ModelNumber == "Virtual CD") {
this.ideDiskConfig.ControllerConfig.BusMasterDma.SetVirtualized();
}
this.deviceName = String.Format("IdeDisk {0} ser {1} {2} MB",
2008-11-17 18:29:00 -05:00
this.ident.ModelNumber,
this.ident.SerialNumber,
(this.ident.MaxLBASectors>>11));
2008-03-05 09:52:00 -05:00
this.diskId = ++IdeDisk.diskIdGenerator;
announceDrive();
Monitoring.Log(Monitoring.Provider.DiskIo,
(ushort)DiskIoEvent.NewDiskName, instanceName);
Monitoring.Log(Monitoring.Provider.DiskIo,
(ushort)DiskIoEvent.NewDiskId, 0,
(uint)this.diskId, 0, 0, 0, 0);
}
private void dumpStatus(int start, int now)
{
byte status;
status = this.controller.ReadStatusPort();
2008-11-17 18:29:00 -05:00
// We timed out and the interrupt occurred. This implies that we did
2008-03-05 09:52:00 -05:00
// not get scheduled in a timely way. Note this and keep on going
2008-11-17 18:29:00 -05:00
if (start + 1 == now) {
2008-03-05 09:52:00 -05:00
DebugStub.WriteLine("ideDisk: timeout; status={0:x},expected INT={1}, actual={2}",
2008-11-17 18:29:00 -05:00
__arglist(status, start+1, now));
DebugStub.WriteLine("ideDisk: timeout; status={0:x},expected INT={1}, actual={2}",
__arglist(status, start+1, now));
2008-03-05 09:52:00 -05:00
return;
}
byte control, sectorCount;
ATA6.DebugQueryStatus(this.controller);
byte bmStatus = this.ideConfigHandle.BusMasterDma.GetStatus();
Tracing.Log(Tracing.Debug,"BusMaster status={0:x2}\n",(UIntPtr) bmStatus);
sectorCount = this.controller.ReadSectorCountPort();
Tracing.Log(Tracing.Debug," status={0:x} count={1:x}",(UIntPtr) status,(UIntPtr) sectorCount);
DebugStub.Break();
}
private void ShowOffsetAndCount()
{
byte lbaLow2 = 0;
byte lbaMid2 = 0;
byte lbaHigh2 = 0;
this.controller.WriteControlPort(0);
byte lbaLow = this.controller.ReadLBALowPort();
byte lbaMid = this.controller.ReadLBAMidPort();
byte lbaHigh = this.controller.ReadLBAHighPort();
if (this.ident.LBA48Enabled) {
// Write High Order Bit to Control Register
// so we can probe the "previous" elements of the LBA queue
this.controller.WriteControlPort(ATA6.DeviceControlHOB);
lbaLow2 = this.controller.ReadLBALowPort();
this.controller.WriteControlPort(ATA6.DeviceControlHOB);
lbaMid2 = this.controller.ReadLBAMidPort();
this.controller.WriteControlPort(ATA6.DeviceControlHOB);
lbaHigh2 = this.controller.ReadLBAHighPort();
DebugStub.WriteLine("LBA={0:x2},{1:x2},{2:x2}, {3:x2},{4:x2},{5:x2}",
2008-11-17 18:29:00 -05:00
__arglist(lbaHigh2,lbaMid2,lbaLow2,lbaHigh,lbaMid,lbaLow));
2008-03-05 09:52:00 -05:00
}
}
private void IssueOffsetAndCount(ulong SectorOffset,short sectorCount)
{
uint sectors = (uint) sectorCount;
if (sectorCount > this.maxSectorCount) {
throw new Exception("IDE Disk: too many sectors");
}
if (sectorCount == this.maxSectorCount) {
sectorCount = 0; // read all sectors
}
if (this.ident.LBA48Enabled) {
// 48 bit addresses are formed by pushing 2 sets of values into the
// LBA registers: See ATA-6 spec and beyond
this.controller.WriteSectorCountPort ((byte)((sectors >> 8) & 0xff));
this.controller.WriteLBALowPort ((byte)((SectorOffset >> 24) & 0xff));
this.controller.WriteLBAMidPort ((byte)((SectorOffset >> 32) & 0xff));
this.controller.WriteLBAHighPort((byte)((SectorOffset >> 40) & 0xff));
this.controller.WriteSectorCountPort ((byte)((sectors >> 0) & 0xff));
this.controller.WriteLBALowPort ((byte)((SectorOffset >> 0) & 0xff));
this.controller.WriteLBAMidPort ((byte)((SectorOffset >> 8) & 0xff));
this.controller.WriteLBAHighPort((byte)((SectorOffset >> 16) & 0xff));
this.controller.WriteDeviceHeadPort((byte)(this.devsel | 0x40 )); //use LBA
}
else {
this.controller.WriteSectorCountPort ((byte)((sectors >> 0) & 0xff));
this.controller.WriteLBALowPort ((byte)((SectorOffset >> 0) & 0xff));
this.controller.WriteLBAMidPort ((byte)((SectorOffset >> 8) & 0xff));
this.controller.WriteLBAHighPort((byte)((SectorOffset >> 16) & 0xff));
2008-11-17 18:29:00 -05:00
this.controller.WriteDeviceHeadPort((byte)(this.devsel | 0x40 | (byte)(SectorOffset >> 24))); // [grp] Doesn't (SectorOffset >> 24) collide with devsel (0|1) if Non-Zero?
2008-03-05 09:52:00 -05:00
}
#if DEBUG_LBA48
DebugStub.WriteLine("IDE offset={0}, count ={1}",__arglist(SectorOffset,sectors));
ShowOffsetAndCount();
#endif
//Tracing.Log(Tracing.Debug," {0:x} sectorId={1}",(UIntPtr) this.controller.ReadFullStatus(),(UIntPtr)SectorOffset);
}
private int ReadPerf(int numMB,
int chunkSize,
out long cycles,
out long ticks,
DiskConnectionState! conn)
{
long endCycles;
long endClock;
long startCycles;
long startClock;
int sectors = numMB * 1024 * (1024 / IdeRequest.SECTOR_SIZE);
int sectorsPerChunk = chunkSize / IdeRequest.SECTOR_SIZE;
byte* opt(ExHeap[]) perfBuffer = new [ExHeap] byte[chunkSize];
2008-11-17 18:29:00 -05:00
try {
2008-03-05 09:52:00 -05:00
startCycles = ProcessService.GetCycleCount();
startClock = ProcessService.GetUpTime().Ticks;
int i = 0;
2008-11-17 18:29:00 -05:00
while (i < sectors) {
2008-03-05 09:52:00 -05:00
ReadWrite(false, perfBuffer, 0, (UIntPtr) chunkSize, (ulong)i, conn);
i += sectorsPerChunk;
}
endCycles = ProcessService.GetCycleCount();
endClock = ProcessService.GetUpTime().Ticks;
}
2008-11-17 18:29:00 -05:00
finally {
2008-03-05 09:52:00 -05:00
delete perfBuffer;
}
cycles = endCycles - startCycles;
ticks = endClock - startClock;
return 0;
}
private static ushort evt2ushort(DiskIoEvent evt, bool offset)
{
return (ushort)(((ushort)evt) + (offset? 1 : 0));
}
private int ReadWrite( bool doWrite,
byte[]! in ExHeap buffer,
UIntPtr bufferOffset,
UIntPtr length,
ulong sector,
DiskConnectionState! conn)
{
ushort temp;
ulong sectorId;
IdeCmdType ideCmdType;
byte controllerCommand;
uint dataStart;
uint bufferStart;
//if (true) return 0 ; //use for testing overhead
// checks
unsafe {
bufferStart = (uint)&buffer[0];
dataStart = bufferStart + (uint)bufferOffset;
if ((dataStart & 0x03) != 0) {
DebugStub.Break();
throw new Exception("IDE ReadWrite: Impermissible buffer (not 4-byte aligned)");
}
}
if ((int)length % IdeRequest.SECTOR_SIZE != 0) {
DebugStub.Break();
throw new Exception("IDE ReadWrite: Impermissible buffer (not in sector-size chunks)");
}
ulong numSectors = (ulong)length / IdeRequest.SECTOR_SIZE;
if (sector > conn.SectorCount) {
throw new Exception("IDE ReadWrite: Sector start out of Bounds.");
}
if ((sector + numSectors) > conn.SectorCount) {
throw new Exception("IDE ReadWrite: Sector operation out of Bounds.");
}
sectorId = sector + conn.StartSector;
if (sectorId >= this.ident.MaxLBASectors) {
throw new Exception("IDE ReadWrite: Sector out of Bounds.");
}
if ((length + bufferOffset) > (UIntPtr)buffer.Length) {
throw new Exception("IDE ReadWrite: length + offset exceeds array bounds");
}
if (length > (UIntPtr) (IdeRequest.SECTOR_SIZE * this.maxSectorCount)) {
throw new Exception("IDE ReadWrite: Length exceeds MAX_SECTORS");
}
2008-11-17 18:29:00 -05:00
// [grp] Reduce branch count w/ Interrupts Off (and total branch count as well).
2008-03-05 09:52:00 -05:00
if (doWrite) {
ideCmdType = IdeCmdType.Write;
controllerCommand = this.writeCommand;
}
else {
ideCmdType = IdeCmdType.Read;
controllerCommand = this.readCommand;
}
//Tracing.Log(Tracing.Debug,"pre cmds {0:x}",
// (UIntPtr) this.controller.ReadFullStatus());
lock (this) {
if (!this.present) {
DebugStub.Break();
throw new Exception("IDE ReadWrite: Device Not Present");
}
//
// perform operation
//
this.ideRequest.Set((ulong)sectorId,buffer,bufferOffset, length, ideCmdType);
int startCount;
2008-11-17 18:29:00 -05:00
bool iflag = PrivilegedGate.DisableInterrupts();
2008-03-05 09:52:00 -05:00
try {
unsafe {
2008-11-17 18:29:00 -05:00
Monitoring.Log(Monitoring.Provider.DiskIo,
IdeDisk.evt2ushort(DiskIoEvent.Start, doWrite), 0,
(uint)sectorId, (uint)length, (uint)this.diskId,
(uint)this.ideRequest.BufferAddress, 0);
2008-03-05 09:52:00 -05:00
}
startCount = DeviceService.GetIrqCount((byte)this.ideConfigHandle.Interrupt.Irq);
//Tracing.Log(Tracing.Debug,"pre cmd int={0}",(UIntPtr) startCount);
this.controller.SetCommandPending(true);
this.controller.SelectDevice(this.devsel, this.atapi); //set master or slave
this.controller.PollDRDY(true); //Wait for Device to indicate ready.
this.ideConfigHandle.BusMasterDma.BmPrepareController(this.ideRequest); //setup PRD
if (!this.atapi) {
this.IssueOffsetAndCount(sectorId,(short) (length >> 9)); //setup LBA
this.controller.WriteCommandPort(controllerCommand);
}
else {
this.controller.WriteCommandPort((byte) ATA6.IdeCommand.AtapiPacket);
this.controller.WriteFeaturesPort((byte) ATA6.Atapi.DMA);
this.controller.WriteLBAHighPort ((byte)(length >> 8));
this.controller.WriteLBAMidPort ((byte)(length >> 0));
// set-up the CDB (Command Descriptor Block)
byte []cdb; cdb = new byte[10];
cdb[0] = (byte)ATA6.Atapi.Read;
cdb[1] = 0; // LUN
cdb[2] = (byte)(sectorId >> 24);
cdb[3] = (byte)(sectorId >> 16);
cdb[4] = (byte)(sectorId >> 8);
cdb[5] = (byte)(sectorId >> 0);
cdb[6] = 0; // Reserved
cdb[7] = (byte)(length >> (11 + 8));
cdb[8] = (byte)(length >> (11 + 0));
cdb[9] = 0; // Control
for (int i = 0; i < 10; i += 2) {
ushort x = cdb[i+1]; x <<= 8; x |= cdb[i];
this.controller.WriteDataPort (x);
}
this.controller.WriteDataPort (0); // controller needs 12-byte cdb
}
// DMA protocol Check_Status State
// After dma command has been issued the host must read the status register.
// Before doing so it must wait 400ns.
this.controller.Delay400ns(); // mandatory delay for controller commands
//Tracing.Log(Tracing.Debug,"post cmds status={0:x}",
// (UIntPtr) this.controller.ReadFullStatus());
this.controller.ReadFullStatus();
this.ideConfigHandle.BusMasterDma.Arm(this.ideRequest);
2008-11-17 18:29:00 -05:00
//\ ATA6.DebugQueryStatus(this.controller);
2008-03-05 09:52:00 -05:00
int currentCount = DeviceService.GetIrqCount((byte)this.ideConfigHandle.Interrupt.Irq);
Debug.Assert(currentCount == startCount);
}
finally {
Tracing.Log(Tracing.Debug, "enabling interrupts for {0}",
this.controller.ControllerNameInternal);
2008-11-17 18:29:00 -05:00
PrivilegedGate.RestoreInterrupts(iflag);
2008-03-05 09:52:00 -05:00
}
bool success = false;
success = this.ideConfigHandle.CommandEndEvent.WaitOne(TimeSpan.FromMilliseconds(1000));
int c = DeviceService.GetIrqCount((byte)this.ideConfigHandle.Interrupt.Irq);
Tracing.Log(Tracing.Debug,"post wait int={0}, expected={1}",(UIntPtr) c, (uint) (startCount +1) );
#if DEBUG_SHARED_IRQ
Tracing.Log(Tracing.Debug,"read completes on {0}, numInterrupts={1}\n",
this.controller.ControllerNameInternal,
(UIntPtr) (c - startCount)
);
#endif //DEBUG_SHARED_IRQ
unsafe {
Monitoring.Log(Monitoring.Provider.DiskIo,
IdeDisk.evt2ushort(DiskIoEvent.End, doWrite), 0,
(uint)sectorId, (uint)length, (uint)this.diskId,
(uint)this.ideRequest.BufferAddress, 0);
}
if (!success) {
dumpStatus(startCount, c);
}
if ((c - startCount) != 1) {
this.controller.CatchBug = true;
this.controller.CatchCount = (c-startCount);
Tracing.Log(Tracing.Debug," ^^^^^^ TRYING To Catch COUNT={0} ^^^^",
(UIntPtr) this.controller.CatchCount);
2008-11-17 18:29:00 -05:00
//\ DebugStub.Break();
2008-03-05 09:52:00 -05:00
}
return 0;
2008-11-17 18:29:00 -05:00
}
2008-03-05 09:52:00 -05:00
}
private static void DumpGCStats()
{
int count;
long millis;
long bytes;
GC.PerformanceCounters(out count, out millis, out bytes);
2008-11-17 18:29:00 -05:00
DebugStub.WriteLine("Disk GC stats - count {0} millis {1} bytes {2}", __arglist(count, millis, bytes));
2008-03-05 09:52:00 -05:00
}
private ulong GetSectorCount()
{
if (!this.present) {
throw new Exception("IDE Disk: Device Not Present");
}
return this.ident.MaxLBASectors;
}
private String GetDeviceName()
{
if (!this.present) {
throw new Exception("IDE Disk: Device Not Present");
}
return this.deviceName;
}
private void SetUdmaMode()
{
2008-11-17 18:29:00 -05:00
if (atapi) {
return;
}
if (!ident.UltraDmaSupported) {
return;
}
2008-03-05 09:52:00 -05:00
this.controller.IssueSet(IdeSet.IDE_SET_MAX_UDMA, ref this.ident);
this.controller.IssueSet(IdeSet.IDE_SET_READ_AHEAD, ref this.ident);
// let's see what we get back once we have set it...
try {
this.ident = ATA6.GetDeviceIdentification(this.controller, this.devsel, this.atapi);
}
catch {
Tracing.Log(Tracing.Debug, " GetDeviceIdentification failed!\n");
this.present = false;
return;
}
}
2008-11-17 18:29:00 -05:00
private void announceDrive() // [grp] was Parameterized, Why? (ref IdentifyDeviceInformation ident)
2008-03-05 09:52:00 -05:00
{
Tracing.Log(Tracing.Debug,"IdeDisk: id {0}, present={1}\n",
this.deviceName,
(UIntPtr) (this.present ? 1: 0));
2008-11-17 18:29:00 -05:00
DebugStub.WriteLine(" IdeDisk: id={0}", __arglist(this.deviceName));
DebugStub.WriteLine(" IdeDisk: {0}" ,__arglist(this.controller.ControllerName));
DebugStub.WriteLine(" IdeDisk: LBA48={0}, UDMA={1},MaxSectors={2}",
__arglist(this.ident.LBA48Enabled,
this.ident.UltraDmaSupported ? this.ident.HighestUltraDmaMode : (byte)0,
this.ident.MaxLBASectors
));
2008-03-05 09:52:00 -05:00
#if false
for (ushort i = 8; i > 3; i--) {
2008-11-17 18:29:00 -05:00
if ((this.ident.MajorVersion & (1 << i)) > 0) {
2008-03-05 09:52:00 -05:00
DebugStub.Print("Device supports ATA {0}\n",__arglist(i));
break;
}
}
#endif
}
}
2008-11-17 18:29:00 -05:00
#endregion
2008-03-05 09:52:00 -05:00
}