mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-19 04:18:43 +00:00
454 lines
20 KiB
C#
454 lines
20 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Cosmos.Kernel;
|
|
|
|
namespace Cosmos.Hardware2.Storage.ATA
|
|
{
|
|
public class ATA : BlockDevice
|
|
{
|
|
//private static readonly ushort[] mControllerAddresses1 = new ushort[] {
|
|
// 0x1F0,
|
|
// 0x170
|
|
//};
|
|
//private static readonly ushort[] mControllerAddresses2 = new ushort[] {
|
|
//0x3F0,
|
|
// 0x370
|
|
//};
|
|
private static ushort GetControllerAddress1(int aIndex)
|
|
{
|
|
if (aIndex == 0)
|
|
{
|
|
return 0x1F0;
|
|
}
|
|
else
|
|
{
|
|
return 0x170;
|
|
}
|
|
}
|
|
|
|
private static ushort GetControllerAddress2(int aIndex)
|
|
{
|
|
if (aIndex == 0)
|
|
{
|
|
return 0x3F0;
|
|
}
|
|
else
|
|
{
|
|
return 0x370;
|
|
}
|
|
}
|
|
|
|
private static int GetControllerAddressCount()
|
|
{
|
|
return 2;
|
|
}
|
|
private static string[] mControllerNumbers = new string[] { "First", "Second", "Third", "Fourth" };
|
|
private static string[] mControllerChannels = new string[] { "Primary", "Secondary" };
|
|
private static string[] mDriveNames = new string[] { "Master", "Slave" };
|
|
private const byte ATA_DELAY_DEFAULT = 5;
|
|
private const byte ATA_DATA = 0;
|
|
private const byte ATA_ERROR = 1;
|
|
private const byte ATA_SECTORCOUNT = 2;
|
|
private const byte ATA_SECTORNUMBER = 3;
|
|
private const byte ATA_CYLINDERLOW = 4;
|
|
private const byte ATA_CYLINDERHIGH = 5;
|
|
private const byte ATA_DRIVEHEAD = 6;
|
|
private const byte ATA_STATUS = 7;
|
|
private const byte ATA_COMMAND = 8;
|
|
|
|
private const byte IDE_STATUSREG_ERR = 0x00000001;
|
|
private const byte IDE_STATUSREG_IDX = 0x00000002;
|
|
private const byte IDE_STATUSREG_CORR = 0x00000004;
|
|
private const byte IDE_STATUSREG_DRQ = 0x00000008;
|
|
private const byte IDE_STATUSREG_DSC = 0x00000010;
|
|
private const byte IDE_STATUSREG_DWF = 0x00000020;
|
|
private const byte IDE_STATUSREG_DRDY = 0x00000040;
|
|
private const byte IDE_STATUSREG_BSY = 0x00000080;
|
|
private const byte IDE_ERRORREG_ABRT = 0x00000004;
|
|
|
|
private const uint Timeout = 60;
|
|
|
|
private enum ATA_Commands : byte
|
|
{
|
|
Nop = 0x00,
|
|
CfaRequestExtErrCode = 0x03,
|
|
DeviceReset = 0x08,
|
|
Recalibrate = 0x10,
|
|
ReadSectors = 0x20,
|
|
ReadSectorsExt = 0x24,
|
|
ReadDmaExt = 0x25,
|
|
ReadDmaQueuedExt = 0x26,
|
|
ReadMultipleExt = 0x29,
|
|
WriteSectors = 0x30,
|
|
WriteSectorsExt = 0x34,
|
|
WriteDmaExt = 0x35,
|
|
WriteDmaQueuedExt = 0x36,
|
|
CfaWriteSectorsWoErase = 0x38,
|
|
WriteMultipleExt = 0x39,
|
|
WriteVerify = 0x3C,
|
|
ReadVerifySectors = 0x40,
|
|
ReadVerifySectorsExt = 0x42,
|
|
FormatTrack = 0x50,
|
|
Seek = 0x70,
|
|
CfaTranslateSector = 0x87,
|
|
ExecuteDeviceDiagnostic = 0x90,
|
|
InitializeDriveParameters = 0x91,
|
|
InitializeDeviceParameters = 0x91,
|
|
StandbyImmediate2 = 0x94,
|
|
IdleImmediate2 = 0x95,
|
|
Standby2 = 0x96,
|
|
Idle2 = 0x97,
|
|
CheckPowerMode2 = 0x98,
|
|
Sleep2 = 0x99,
|
|
Packet = 0xA0,
|
|
IdentifyDevicePacket = 0xA1,
|
|
IdentifyPacketDevice = 0xA1,
|
|
Smart = 0xB0,
|
|
CfaEraseSectors = 0xC0,
|
|
ReadMultiple = 0xC4,
|
|
WriteMultiple = 0xC5,
|
|
SetMultipleMode = 0xC6,
|
|
ReadDmaQueued = 0xC7,
|
|
ReadDma = 0xC8,
|
|
WriteDma = 0xCA,
|
|
WriteDmaQueued = 0xCC,
|
|
CfaWriteMultipleWoErase = 0xCD,
|
|
StandbyImmediate1 = 0xE0,
|
|
IdleImmediate1 = 0xE1,
|
|
Standby1 = 0xE2,
|
|
Idle1 = 0xE3,
|
|
ReadBuffer = 0xE4,
|
|
CheckPowerMode1 = 0xE5,
|
|
Sleep1 = 0xE6,
|
|
FlushCache = 0xE7,
|
|
WriteBuffer = 0xE8,
|
|
FlushCacheExt = 0xEA,
|
|
IdentifyDevice = 0xEC,
|
|
SetFeatures = 0xEF,
|
|
}
|
|
private enum DeviceControlBits : byte
|
|
{
|
|
/// <summary>
|
|
/// High Order Byte (48-bit LBA)
|
|
/// </summary>
|
|
HighOrderByte = 0x80,
|
|
/// <summary>
|
|
/// Soft reset
|
|
/// </summary>
|
|
CB_DC_SRST = 0x04,// soft reset
|
|
/// <summary>
|
|
/// Disable interrupts
|
|
/// </summary>
|
|
CB_DC_NIEN = 0x02 // disable interrupts
|
|
|
|
}
|
|
|
|
private readonly string mName;
|
|
private readonly byte mControllerIndex;
|
|
private readonly ushort mController;
|
|
private readonly ushort mController2;
|
|
private readonly ushort mController_Data;
|
|
private readonly ushort mController_Error;
|
|
private readonly ushort mController_FeatureReg;
|
|
private readonly ushort mController_SectorCount;
|
|
private readonly ushort mController_SectorNumber;
|
|
private readonly ushort mController_CylinderLow;
|
|
private readonly ushort mController_CylinderHigh;
|
|
private readonly ushort mController_DeviceHead;
|
|
private readonly ushort mController_PrimaryStatus;
|
|
private readonly ushort mController_Command;
|
|
private readonly ushort mController_AlternateStatus;
|
|
private readonly ushort mController_DeviceControl;
|
|
private readonly ushort mController_DeviceAddress;
|
|
private readonly bool mIsPrimary;
|
|
/*
|
|
#define CB_DATA 0 // data reg in/out pio_base_addr1+0
|
|
#define CB_ERR 1 // error in pio_base_addr1+1
|
|
#define CB_FR 1 // feature reg out pio_base_addr1+1
|
|
#define CB_SC 2 // sector count in/out pio_base_addr1+2
|
|
#define CB_SN 3 // sector number in/out pio_base_addr1+3
|
|
#define CB_CL 4 // cylinder low in/out pio_base_addr1+4
|
|
#define CB_CH 5 // cylinder high in/out pio_base_addr1+5
|
|
#define CB_DH 6 // device head in/out pio_base_addr1+6
|
|
#define CB_STAT 7 // primary status in pio_base_addr1+7
|
|
#define CB_CMD 7 // command out pio_base_addr1+7
|
|
#define CB_ASTAT 8 // alternate status in pio_base_addr2+6
|
|
#define CB_DC 8 // device control out pio_base_addr2+6
|
|
#define CB_DA 9 // device address in pio_base_addr2+7*/
|
|
private readonly byte mDrive;
|
|
private ATA(string aName, byte aController, byte aDrive)
|
|
{
|
|
mName = aName;
|
|
mControllerIndex = aController;
|
|
mController = GetControllerAddress1(aController);
|
|
mController2 = GetControllerAddress2(aController);
|
|
mType = DeviceType.Storage;
|
|
mDrive = aDrive;
|
|
mController_Data = mController;
|
|
mIsPrimary = aController == 0;
|
|
mController_Error = (ushort)(mController + 1);
|
|
mController_FeatureReg = (ushort)(mController + 1);
|
|
mController_SectorCount = (ushort)(mController + 2);
|
|
mController_SectorNumber = (ushort)(mController + 3);
|
|
mController_CylinderLow = (ushort)(mController + 4);
|
|
mController_CylinderHigh = (ushort)(mController + 5);
|
|
mController_DeviceHead = (ushort)(mController + 6);
|
|
mController_PrimaryStatus = (ushort)(mController + 7);
|
|
mController_Command = (ushort)(mController + 7);
|
|
mController_AlternateStatus = (ushort)(mController2 + 6);
|
|
mController_DeviceControl = (ushort)(mController2 + 6);
|
|
mController_DeviceAddress = (ushort)(mController2 + 7);
|
|
IOWriteByte(mController_DeviceControl, 0);
|
|
mBlockCount = GetBlockCount();
|
|
}
|
|
|
|
private uint GetBlockCount()
|
|
{
|
|
IOWriteByte(mController_DeviceHead, (byte)((mDrive << 4) + (1 << 6)));
|
|
//uint xTimeout = Timeout;
|
|
while ((IOReadByte(mController_Command) & IDE_STATUSREG_DRDY) == 0)
|
|
{
|
|
; ; ;
|
|
//xTimeout--;
|
|
}
|
|
if ((IOReadByte(mController_Command) & IDE_STATUSREG_DRDY) == 0)
|
|
{
|
|
throw new Exception("[ATA#1] GetBlockCount failed!");
|
|
}
|
|
IOWriteByte(mController_Command, 0xF8);
|
|
//xTimeout = Timeout;
|
|
while ((IOReadByte(mController_Command) & IDE_STATUSREG_BSY) != 0)
|
|
{
|
|
//CPU.Halt();
|
|
//xTimeout--;
|
|
; ; ;
|
|
}
|
|
if ((IOReadByte(mController_Command) & IDE_STATUSREG_BSY) != 0)
|
|
{
|
|
throw new Exception("[ATA#2] GetBlockCount failed!");
|
|
}
|
|
uint xResult = IOReadByte(mController_SectorNumber);
|
|
xResult += (uint)IOReadByte(mController_CylinderLow) << 8;
|
|
xResult += (uint)IOReadByte(mController_CylinderHigh) << 16;
|
|
xResult += (uint)(IOReadByte(mController_DeviceHead) & 0xF) << 8;
|
|
return xResult;
|
|
}
|
|
|
|
public static void HandleInterruptSecondary()
|
|
{
|
|
mSecondaryInterruptCount++;
|
|
IOReadByte((ushort)(GetControllerAddress1(0) + 7));
|
|
}
|
|
|
|
private static uint mSecondaryInterruptCount;
|
|
|
|
public static void HandleInterruptPrimary()
|
|
{
|
|
mPrimaryInterruptCount++;
|
|
IOReadByte((ushort)(GetControllerAddress1(1) + 7));
|
|
}
|
|
|
|
private static uint mPrimaryInterruptCount;
|
|
|
|
public static void Initialize()
|
|
{
|
|
for (byte xControllerBaseAIdx = 0; xControllerBaseAIdx < GetControllerAddressCount(); xControllerBaseAIdx++)
|
|
{
|
|
var xOldValue = IOReadByte((ushort)(GetControllerAddress1(xControllerBaseAIdx) + ATA_STATUS));
|
|
IOWriteByte((ushort)(GetControllerAddress1(xControllerBaseAIdx) + ATA_STATUS), (byte)(xOldValue | 0x4));
|
|
IOWriteByte((ushort)(GetControllerAddress1(xControllerBaseAIdx) + ATA_STATUS), xOldValue);
|
|
|
|
for (byte xDrive = 0; xDrive < 2; xDrive++)
|
|
{
|
|
IOWriteByte((ushort)(GetControllerAddress1(xControllerBaseAIdx) + ATA_DRIVEHEAD),
|
|
(byte)((xControllerBaseAIdx << 4) | 0xA0 | (xDrive << 4)));
|
|
Console.Write(" Drive " + xDrive);
|
|
// we should wait 400ns
|
|
IOReadByte((ushort)(GetControllerAddress1(xControllerBaseAIdx) + ATA_STATUS));
|
|
IOReadByte((ushort)(GetControllerAddress1(xControllerBaseAIdx) + ATA_STATUS));
|
|
IOReadByte((ushort)(GetControllerAddress1(xControllerBaseAIdx) + ATA_STATUS));
|
|
IOReadByte((ushort)(GetControllerAddress1(xControllerBaseAIdx) + ATA_STATUS));
|
|
// end wait 400ns
|
|
if (IOReadByte((ushort)(GetControllerAddress1(xControllerBaseAIdx) + ATA_STATUS)) == 0x50)
|
|
{
|
|
ATA xATA = xATA = new ATA(String.Concat(mControllerNumbers[xControllerBaseAIdx], " ", mDriveNames[xDrive]), xControllerBaseAIdx, xDrive);
|
|
Device.Add(xATA);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public override uint BlockSize
|
|
{
|
|
get
|
|
{
|
|
return 512;
|
|
}
|
|
}
|
|
|
|
private readonly ulong mBlockCount;
|
|
|
|
public override ulong BlockCount
|
|
{
|
|
get
|
|
{
|
|
return mBlockCount;
|
|
}
|
|
}
|
|
/*
|
|
#define CB_DATA 0 // data reg in/out pio_base_addr1+0
|
|
#define CB_ERR 1 // error in pio_base_addr1+1
|
|
#define CB_FR 1 // feature reg out pio_base_addr1+1
|
|
#define CB_SC 2 // sector count in/out pio_base_addr1+2
|
|
#define CB_SN 3 // sector number in/out pio_base_addr1+3
|
|
#define CB_CL 4 // cylinder low in/out pio_base_addr1+4
|
|
#define CB_CH 5 // cylinder high in/out pio_base_addr1+5
|
|
#define CB_DH 6 // device head in/out pio_base_addr1+6
|
|
#define CB_STAT 7 // primary status in pio_base_addr1+7
|
|
#define CB_CMD 7 // command out pio_base_addr1+7
|
|
#define CB_ASTAT 8 // alternate status in pio_base_addr2+6
|
|
#define CB_DC 8 // device control out pio_base_addr2+6
|
|
#define CB_DA 9 // device address in pio_base_addr2+7*/
|
|
public override void ReadBlock(ulong aBlock, byte[] aBuffer)
|
|
{
|
|
// 1) Read the status register of the primary or the secondary IDE controller.
|
|
// 2) The BSY and DRQ bits must be zero if the controller is ready.
|
|
uint xSleepCount = Timeout;
|
|
while (((IOReadByte(mController_Command) & (IDE_STATUSREG_BSY | IDE_STATUSREG_DRQ)) != 0) && xSleepCount > 0)
|
|
{
|
|
//CPU.Halt();
|
|
// xSleepCount--;
|
|
; ; ;
|
|
}
|
|
if (((IOReadByte(mController_Command) & (IDE_STATUSREG_BSY | IDE_STATUSREG_DRQ)) != 0) && xSleepCount > 0)
|
|
{
|
|
throw new Exception("[ATA#2] Read failed");
|
|
}
|
|
//3) Set the DEV bit to 0 for Drive0 and to 1 for Drive1 on the selected IDE controller using
|
|
// the Device/Head register and wait for approximately 400 nanoseconds using some NOP perhaps.
|
|
IOWriteByte(mController_DeviceHead, (byte)(mDrive << 4));
|
|
//4) Read the status register again.
|
|
//5) The BSY and DRQ bits must be 0 again for you to know that the IDE controller and the selected IDE drive are ready.
|
|
xSleepCount = Timeout;
|
|
while (((IOReadByte(mController_Command) & (IDE_STATUSREG_BSY | IDE_STATUSREG_DRQ)) != 0) && xSleepCount > 0)
|
|
{
|
|
//CPU.Halt();
|
|
//xSleepCount--;
|
|
; ; ;
|
|
}
|
|
if (((IOReadByte(mController_Command) & (IDE_STATUSREG_BSY | IDE_STATUSREG_DRQ)) != 0) && xSleepCount > 0)
|
|
{
|
|
throw new Exception("[ATA#5] Read failed");
|
|
}
|
|
// 6) Write the LBA28 address to the designated IDE registers.
|
|
IOWriteByte(mController_SectorNumber, (byte)aBlock);
|
|
IOWriteByte(mController_CylinderLow, (byte)(aBlock >> 8));
|
|
IOWriteByte(mController_CylinderHigh, (byte)(aBlock >> 16));
|
|
IOWriteByte(mController_DeviceHead, (byte)(0xE0 | (mDrive << 4) | ((byte)((aBlock >> 24) & 0x0F))));
|
|
// 7) Set the Sector count using the Sector Count register.
|
|
IOWriteByte(mController_SectorCount, 1);
|
|
//8) Issue the Read Sector(s) command.
|
|
IOWriteByte(mController_Command, 0x20);
|
|
// 9) Read the Error register. If the ABRT bit is set then the Read Sector(s) command
|
|
// is not supported for that IDE drive. If the ABRT bit is not set, continue to the next step.
|
|
if ((IOReadByte(mController_Error) & IDE_ERRORREG_ABRT) == IDE_ERRORREG_ABRT)
|
|
{
|
|
throw new Exception("[ATA#9] Read failed");
|
|
}
|
|
// 10) If you want to receive interrupts after reading each sector, clear the nIEN bit in the
|
|
// Device Control register. If you do not clear this bit then interrupts will not be generated
|
|
// after the reading of each sector which might cause an infinite loop if you are waiting for them.
|
|
// The Primary IDE Controller will generate IRQ14 and the secondary IDE controller generates IRQ 15.
|
|
IOWriteByte(mController_DeviceControl, 0); // receive interrupts...
|
|
// 11) Read the Alternate Status Register (you may even ignore the value that is read)
|
|
IOReadByte(mController_AlternateStatus);
|
|
// 12) Read the Status register for the selected IDE Controller.
|
|
IOReadByte(mController_Command);
|
|
//13) Whenever a sector of data is ready to be read from the Data Register, the BSY bit
|
|
// in the status register will be set to 0 and DRQ to 1 so you might want to wait until
|
|
// those bits are set to the mentioned values before attempting to read from the drive.
|
|
xSleepCount = Timeout;
|
|
while (((IOReadByte(mController_Command) & (IDE_STATUSREG_BSY | IDE_STATUSREG_DRQ)) != IDE_STATUSREG_DRQ) && xSleepCount != 0)
|
|
{
|
|
//xSleepCount--;
|
|
//CPU.Halt();
|
|
; ; ;
|
|
}
|
|
if ((IOReadByte(mController_Command) & (IDE_STATUSREG_BSY | IDE_STATUSREG_DRQ)) != IDE_STATUSREG_DRQ)
|
|
{
|
|
throw new Exception("[ATA#13] Read failed");
|
|
}
|
|
//14) Read one sector from the IDE Controller 16-bits at a time using the IN or the INSW instructions.
|
|
for (uint i = 0; i < 256; i++)
|
|
{
|
|
ushort xValue = IOReadWord(mController);
|
|
aBuffer[i * 2] = (byte)xValue;
|
|
aBuffer[(i * 2) + 1] = (byte)(xValue >> 8);
|
|
}
|
|
// 15) See if you have to read one more sector. If yes, repeat from step 11 again.
|
|
//16) If you don't need to read any more sectors, read the Alternate Status Register and ignore the byte that you read.
|
|
IOReadByte(mController_AlternateStatus);
|
|
//17) Read the status register. When the status register is read, the IDE Controller will negate
|
|
// the INTRQ and you will not have pending IRQs waiting to be detected. This is a MUST to read
|
|
// the status register when you are done reading from IDE ports.
|
|
IOReadByte(mController_Command);
|
|
}
|
|
|
|
public override void WriteBlock(ulong aBlock, byte[] aContents)
|
|
{
|
|
uint xSleepCount = Timeout;
|
|
while (((IOReadByte(mController_Command) & 0x80) == 0x80) && xSleepCount > 0)
|
|
{
|
|
//CPU.Halt();
|
|
//xSleepCount--;
|
|
; ; ;
|
|
}
|
|
if (xSleepCount == 0)
|
|
{
|
|
throw new Exception("[ATA|WriteBlockNew] Failed 1");
|
|
}
|
|
IOWriteByte(mController_FeatureReg, 0);
|
|
IOWriteByte(mController_SectorCount, 1);
|
|
IOWriteByte(mController_SectorNumber, (byte)aBlock);
|
|
IOWriteByte(mController_CylinderLow, (byte)(aBlock >> 8));
|
|
IOWriteByte(mController_CylinderHigh, (byte)(aBlock >> 16));
|
|
IOWriteByte(mController_DeviceHead, (byte)(0xE0 | (mDrive << 4) | (byte)(aBlock >> 24)));
|
|
IOWriteByte(mController_DeviceControl, 0); // receive interrupts...
|
|
IOWriteByte(mController_Command, 0x30);
|
|
xSleepCount = Timeout;
|
|
while (((IOReadByte(mController_Command) & 0x80) == 0x80) && xSleepCount > 0)
|
|
{
|
|
//CPU.Halt();
|
|
//xSleepCount--;
|
|
; ; ;
|
|
}
|
|
if (xSleepCount == 0)
|
|
{
|
|
throw new Exception("[ATA|WriteBlockNew] Failed 2");
|
|
}
|
|
xSleepCount = Timeout;
|
|
while (((IOReadByte(mController_AlternateStatus) & 0x8) == 0x8) && xSleepCount > 0)
|
|
{
|
|
//CPU.Halt();
|
|
//xSleepCount--;
|
|
; ; ;
|
|
}
|
|
if (xSleepCount == 0)
|
|
{
|
|
throw new Exception("[ATA|WriteBlockNew] Failed 3");
|
|
}
|
|
for (int i = 0; i < 256; i++)
|
|
{
|
|
IOWriteWord(mController_Data, (ushort)((aContents[i * 2]) | (aContents[i * 2 + 1] << 8)));
|
|
}
|
|
}
|
|
|
|
public override string Name
|
|
{
|
|
get
|
|
{
|
|
return mName;
|
|
}
|
|
}
|
|
}
|
|
}
|