mirror of
https://github.com/danbulant/Cosmos
synced 2026-05-19 12:30:32 +00:00
368 lines
13 KiB
C#
368 lines
13 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Cosmos.Core;
|
|
using Cosmos.Core.Memory.Old;
|
|
using Cosmos.HAL.BlockDevice.Registers;
|
|
using Cosmos.Debug.Kernel;
|
|
|
|
namespace Cosmos.HAL.BlockDevice.Ports
|
|
{
|
|
public class SATA : StoragePort
|
|
{
|
|
internal static Debugger mSATADebugger = new Debugger("HAL", "SATA");
|
|
|
|
public override PortType mPortType => PortType.SATA;
|
|
public override string mPortName => "SATA";
|
|
public override uint mPortNumber => mPortReg.mPortNumber;
|
|
|
|
public PortRegisters mPortReg;
|
|
public Core.MemoryGroup.AHCI Mem;
|
|
|
|
// Constants
|
|
public const ulong RegularSectorSize = 512UL;
|
|
|
|
// Properties
|
|
private string mSerialNo;
|
|
private string mFirmwareRev;
|
|
private string mModelNo;
|
|
|
|
public string SerialNo { get => mSerialNo; }
|
|
public string FirmwareRev { get => mFirmwareRev; }
|
|
public string ModelNo { get => mModelNo; }
|
|
|
|
public SATA(PortRegisters aSATAPort)
|
|
{
|
|
// Check if it is really a SATA Port!
|
|
if (aSATAPort.mPortType != PortType.SATA || (aSATAPort.CMD & (1U << 24)) != 0)
|
|
{
|
|
throw new Exception($" 0:{aSATAPort.mPortNumber} is not a SATA port!\n");
|
|
}
|
|
|
|
Mem = new Core.MemoryGroup.AHCI((uint)RegularSectorSize);
|
|
|
|
mPortReg = aSATAPort;
|
|
|
|
// Setting Offset arg to Global offset
|
|
mBlockSize = RegularSectorSize;
|
|
|
|
// TODO: Use SendSATACommand(ATACommands.Identify) and copy the useful isIdentify if's from SendSATA28Command
|
|
// But make sure that isIdentify returns the exact value (true if the command is identify
|
|
// or false if not identify).
|
|
SendSATA28Command((ATACommands)0x00, 0, 0);
|
|
UInt16[] xBuffer = new UInt16[256];
|
|
Mem.DataBlock.Read16(xBuffer);
|
|
|
|
mSerialNo = GetString(xBuffer, 10, 20);
|
|
mFirmwareRev = GetString(xBuffer, 23, 8);
|
|
mModelNo = GetString(xBuffer, 27, 40);
|
|
|
|
mBlockCount = ((UInt32)xBuffer[61] << 16 | xBuffer[60]) - 1;
|
|
|
|
}
|
|
|
|
public void SendSATACommand(ATACommands aCommand)
|
|
{
|
|
mPortReg.IS = 0xFFFF;
|
|
|
|
int xSlot = FindCMDSlot();
|
|
if (xSlot == -1) return;
|
|
|
|
HBACommandHeader xCMDHeader = new HBACommandHeader(mPortReg.CLB, (uint)xSlot);
|
|
xCMDHeader.CFL = 5;
|
|
xCMDHeader.PRDTL = 1;
|
|
xCMDHeader.Write = 0;
|
|
|
|
xCMDHeader.CTBA = (uint)((uint)(Base.AHCI + 0xA000) + (0x2000 * mPortNumber) + (0x100 * xSlot));
|
|
|
|
HBACommandTable xCMDTable = new HBACommandTable(xCMDHeader.CTBA, xCMDHeader.PRDTL);
|
|
|
|
uint DataBaseAddress = Mem.DataBlock.Base;
|
|
xCMDTable.PRDTEntry[0].DBA = DataBaseAddress;
|
|
xCMDTable.PRDTEntry[0].DBC = 511;
|
|
xCMDTable.PRDTEntry[0].InterruptOnCompletion = 1;
|
|
|
|
FISRegisterH2D xCMDFIS = new FISRegisterH2D(xCMDTable.CFIS)
|
|
{
|
|
FISType = (byte)FISType.FIS_Type_RegisterH2D,
|
|
IsCommand = 1,
|
|
Command = (byte)aCommand,
|
|
Device = 0
|
|
};
|
|
|
|
int xSpin = 0;
|
|
|
|
while (((mPortReg.TFD & 0x88) != 0) && xSpin < 1000000) xSpin++;
|
|
|
|
if (xSpin == 1000000)
|
|
{
|
|
mSATADebugger.Send($"Port {mPortNumber} timed out!");
|
|
return;
|
|
};
|
|
|
|
mPortReg.CI = 1U;
|
|
|
|
while (true)
|
|
{
|
|
if ((mPortReg.CI & (1 << xSlot)) == 0) break;
|
|
if ((mPortReg.IS & (1 << 30)) != 0)
|
|
{
|
|
throw new Exception("SATA Fatal error: Command aborted");
|
|
//mSATADebugger.Send("[Fatal]: Fatal error occurred while sending command!");
|
|
//PortReset(mPortReg);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//Console.ForegroundColor = ConsoleColor.Green;
|
|
//Console.Write("[Success]: ");
|
|
//Console.Write("Command has been sent successfully!\n");
|
|
//Console.ResetColor();
|
|
|
|
return;
|
|
}
|
|
|
|
public void SendSATA28Command(ATACommands aCommand, uint aStart, uint aCount)
|
|
{
|
|
bool isIdentify = false;
|
|
if (aStart == 0 && aCount == 0) isIdentify = true;
|
|
|
|
mPortReg.IS = 0xFFFF;
|
|
|
|
int xSlot = FindCMDSlot();
|
|
if (xSlot == -1) return;
|
|
|
|
HBACommandHeader xCMDHeader = new HBACommandHeader(mPortReg.CLB, (uint)xSlot);
|
|
xCMDHeader.CFL = 5;
|
|
xCMDHeader.PRDTL = 1;
|
|
xCMDHeader.Write = 0;
|
|
|
|
xCMDHeader.CTBA = (uint)((uint)(Base.AHCI + 0xA000) + (0x2000 * mPortNumber) + (0x100 * xSlot));
|
|
|
|
HBACommandTable xCMDTable = new HBACommandTable(xCMDHeader.CTBA, xCMDHeader.PRDTL);
|
|
|
|
uint DataBaseAddress = Mem.DataBlock.Base;
|
|
|
|
// Last entry
|
|
if (isIdentify)
|
|
{
|
|
xCMDTable.PRDTEntry[xCMDHeader.PRDTL - 1].DBA = DataBaseAddress;
|
|
xCMDTable.PRDTEntry[xCMDHeader.PRDTL - 1].DBC = 511;
|
|
xCMDTable.PRDTEntry[xCMDHeader.PRDTL - 1].InterruptOnCompletion = 1;
|
|
}
|
|
else
|
|
{
|
|
xCMDTable.PRDTEntry[xCMDHeader.PRDTL - 1].DBA = DataBaseAddress;
|
|
xCMDTable.PRDTEntry[xCMDHeader.PRDTL - 1].DBC = aCount * 512 - 1; // 8K bytes (this value should always be set to 1 less than the actual value)
|
|
xCMDTable.PRDTEntry[xCMDHeader.PRDTL - 1].InterruptOnCompletion = 1;
|
|
}
|
|
|
|
if (isIdentify)
|
|
{
|
|
FISRegisterH2D xCMDFIS = new FISRegisterH2D(xCMDTable.CFIS)
|
|
{
|
|
FISType = (byte)FISType.FIS_Type_RegisterH2D,
|
|
IsCommand = 1,
|
|
Command = (byte)ATACommands.Identify,
|
|
Device = 0
|
|
};
|
|
}
|
|
else
|
|
{
|
|
FISRegisterH2D xCMDFIS = new FISRegisterH2D(xCMDTable.CFIS)
|
|
{
|
|
FISType = (byte)FISType.FIS_Type_RegisterH2D,
|
|
IsCommand = 1,
|
|
Command = (byte)aCommand,
|
|
|
|
LBA0 = (byte)((aStart) & 0xFF),
|
|
LBA1 = (byte)((aStart >> 8) & 0xFF),
|
|
LBA2 = (byte)((aStart >> 16) & 0xFF),
|
|
Device = (byte)(0x40 | ((aStart >> 24) & 0x0F)),
|
|
|
|
CountL = (byte)(aCount & 0xFF)
|
|
};
|
|
}
|
|
|
|
int xSpin = 0;
|
|
|
|
while (((mPortReg.TFD & 0x88) != 0) && xSpin < 1000000) xSpin++;
|
|
|
|
if (xSpin == 1000000)
|
|
{
|
|
mSATADebugger.Send($"Port {mPortNumber} timed out!");
|
|
return;
|
|
};
|
|
|
|
mPortReg.CI = 1U;
|
|
|
|
while (true)
|
|
{
|
|
if ((mPortReg.CI & (1 << xSlot)) == 0) break;
|
|
if ((mPortReg.IS & (1 << 30)) != 0)
|
|
{
|
|
throw new Exception("SATA Fatal error: Command aborted");
|
|
//mSATADebugger.Send("[Fatal]: Fatal error occurred while sending command!");
|
|
//PortReset(mPortReg);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//Console.ForegroundColor = ConsoleColor.Green;
|
|
//Console.Write("[Success]: ");
|
|
//Console.Write("Command has been sent successfully!\n");
|
|
//Console.ResetColor();
|
|
|
|
return;
|
|
}
|
|
|
|
public void SendSATA48Command(ATACommands aCommand, ulong aStart, uint aCount)
|
|
{
|
|
mPortReg.IS = 0xFFFF;
|
|
|
|
int xSlot = FindCMDSlot();
|
|
if (xSlot == -1) return;
|
|
|
|
HBACommandHeader xCMDHeader = new HBACommandHeader(mPortReg.CLB, (uint)xSlot);
|
|
xCMDHeader.CFL = 5;
|
|
xCMDHeader.PRDTL = 1;
|
|
xCMDHeader.Write = 0;
|
|
|
|
xCMDHeader.CTBA = (uint)((uint)(Base.AHCI + 0xA000) + (0x2000 * mPortNumber) + (0x100 * xSlot));
|
|
|
|
HBACommandTable xCMDTable = new HBACommandTable(xCMDHeader.CTBA, xCMDHeader.PRDTL);
|
|
|
|
uint DataBaseAddress = Mem.DataBlock.Base;
|
|
|
|
// Last entry
|
|
xCMDTable.PRDTEntry[xCMDHeader.PRDTL - 1].DBA = DataBaseAddress;
|
|
xCMDTable.PRDTEntry[xCMDHeader.PRDTL - 1].DBC = (aCount * 512) - 1; // 8K bytes (this value should always be set to 1 less than the actual value)
|
|
xCMDTable.PRDTEntry[xCMDHeader.PRDTL - 1].InterruptOnCompletion = 1;
|
|
|
|
FISRegisterH2D xCMDFIS = new FISRegisterH2D(xCMDTable.CFIS)
|
|
{
|
|
FISType = (byte)FISType.FIS_Type_RegisterH2D,
|
|
IsCommand = 1,
|
|
Command = (byte)aCommand,
|
|
|
|
LBA0 = (byte)((aStart >> 00) & 0xFF),
|
|
LBA1 = (byte)((aStart >> 08) & 0xFF),
|
|
LBA2 = (byte)((aStart >> 16) & 0xFF),
|
|
LBA3 = (byte)((aStart >> 24) & 0xFF),
|
|
LBA4 = (byte)((aStart >> 32) & 0xFF),
|
|
LBA5 = (byte)((aStart >> 40) & 0xFF),
|
|
|
|
Device = 1 << 6,
|
|
|
|
CountL = (byte)(aCount & 0xFF),
|
|
CountH = (byte)((aCount >> 8) & 0xFF)
|
|
};
|
|
|
|
int xSpin = 0;
|
|
|
|
while (((mPortReg.TFD & 0x88) != 0) && xSpin < 1000000) xSpin++;
|
|
|
|
if (xSpin == 1000000)
|
|
{
|
|
mSATADebugger.Send($"Port {mPortNumber} timed out!");
|
|
return;
|
|
};
|
|
|
|
mPortReg.CI = 1U;
|
|
|
|
while (true)
|
|
{
|
|
if ((mPortReg.CI & (1 << xSlot)) == 0) break;
|
|
if ((mPortReg.IS & (1 << 30)) != 0)
|
|
{
|
|
throw new Exception("SATA Fatal error: Command aborted");
|
|
//mSATADebugger.Send("[Fatal]: Fatal error occurred while sending command!");
|
|
//PortReset(mPortReg);
|
|
return;
|
|
}
|
|
}
|
|
|
|
//Console.ForegroundColor = ConsoleColor.Green;
|
|
//Console.Write("[Success]: ");
|
|
//Console.Write("Command has been sent successfully!\n");
|
|
//Console.ResetColor();
|
|
return;
|
|
}
|
|
|
|
public static void PortReset(PortRegisters aPort)
|
|
{
|
|
// TODO: Make a connection between AHCI Methods and SATA
|
|
|
|
// Semi-StopCMD()
|
|
aPort.CMD &= ~(1U << 0);
|
|
int i;
|
|
for(i = 0; i <= 50; i++)
|
|
{
|
|
if ((aPort.CMD & (1 << 0)) == 0) break;
|
|
AHCI.Wait(10000);
|
|
}
|
|
if (i == 101) AHCI.HBAReset();
|
|
|
|
aPort.SCTL = 1;
|
|
AHCI.Wait(1000);
|
|
aPort.SCTL &= ~(1U << 0);
|
|
|
|
while ((aPort.SSTS & 0x0F) != 3) ;
|
|
|
|
aPort.SERR = 1;
|
|
|
|
while ((aPort.SCTL & 0x0F) != 0) ;
|
|
}
|
|
|
|
private void HBAReset() => AHCI.HBAReset();
|
|
|
|
private int FindCMDSlot()
|
|
{
|
|
// If not set in SACT and CI, the slot is free
|
|
var xSlots = (mPortReg.SACT | mPortReg.CI);
|
|
|
|
for (int i = 0; i < 32; i++)
|
|
{
|
|
if ((xSlots & 1) == 0)
|
|
return i;
|
|
xSlots >>= 1;
|
|
}
|
|
|
|
//Console.ForegroundColor = ConsoleColor.Red;
|
|
//Console.Write("[Error]: ");
|
|
//Console.Write("Cannot find a free command slot!\n");
|
|
//Console.ResetColor();
|
|
return -1;
|
|
}
|
|
|
|
protected string GetString(UInt16[] aBuffer, int aIndexStart, int aStringLength)
|
|
{
|
|
// Would be nice to convert to byte[] and use
|
|
// new string(ASCIIEncoding.ASCII.GetChars(xBytes));
|
|
// But it requires some code Cosmos doesnt support yet
|
|
var xChars = new char[aStringLength];
|
|
for (int i = 0; i < aStringLength / 2; i++)
|
|
{
|
|
UInt16 xChar = aBuffer[aIndexStart + i];
|
|
xChars[i * 2] = (char)(xChar >> 8);
|
|
xChars[i * 2 + 1] = (char)xChar;
|
|
}
|
|
return new string(xChars);
|
|
}
|
|
|
|
// BlockDevice Methods
|
|
public override void ReadBlock(ulong aBlockNo, ulong aBlockCount, ref byte[] aData)
|
|
{
|
|
//CheckDataSize(aData, aBlockCount);
|
|
//CheckBlockNo(aBlockNo, aBlockCount);
|
|
SendSATA48Command(ATACommands.ReadDmaExt, (uint)aBlockNo, (uint)aBlockCount);
|
|
Mem.DataBlock.Read8(aData);
|
|
}
|
|
|
|
public override void WriteBlock(ulong aBlockNo, ulong aBlockCount, ref byte[] aData)
|
|
{
|
|
Mem.DataBlock.Write8(aData);
|
|
SendSATA48Command(ATACommands.WriteDmaExt, (uint)(aBlockNo), (uint)aBlockCount);
|
|
SendSATACommand(ATACommands.CacheFlush);
|
|
}
|
|
}
|
|
}
|