diff --git a/source/Cosmos.Core/MemoryGroup/AHCI.cs b/source/Cosmos.Core/MemoryGroup/AHCI.cs new file mode 100644 index 000000000..d0638ee51 --- /dev/null +++ b/source/Cosmos.Core/MemoryGroup/AHCI.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Cosmos.Core.MemoryGroup +{ + public class AHCI + { + public MemoryBlock DataBlock; + + public AHCI(uint aSectorSize) + { + DataBlock = new Core.MemoryBlock(0x0046C000, aSectorSize * 1024); + DataBlock.Fill(0); + } + } +} diff --git a/source/Cosmos.HAL2/BlockDevice/AHCI.cs b/source/Cosmos.HAL2/BlockDevice/AHCI.cs new file mode 100644 index 000000000..60eec7057 --- /dev/null +++ b/source/Cosmos.HAL2/BlockDevice/AHCI.cs @@ -0,0 +1,384 @@ +using System; +using System.Collections.Generic; +using Cosmos.HAL; +using Cosmos.Debug.Kernel; +using Cosmos.HAL.BlockDevice; +using Cosmos.HAL.BlockDevice.Ports; +using Cosmos.HAL.BlockDevice.Registers; +using Cosmos.Core; +using Cosmos.Core.Memory.Old; + +namespace Cosmos.HAL.BlockDevice +{ + public class AHCI + { + internal static Debugger mAHCIDebugger = new Debugger("HAL", "AHCI"); + internal static PCIDevice xDevice = HAL.PCI.GetDeviceClass(HAL.ClassID.MassStorageController, + HAL.SubclassID.SATAController, + HAL.ProgramIF.SATA_AHCI); + + private static List mPorts = new List(); + private static GenericRegisters mGeneric; + private static ulong mABAR; + + // Capabilities + #region Capabilities + private static bool Supports64bitAddressing; + private static bool SupportsNativeCommandQueuing; + private static bool SupportsSNotificationRegister; + private static bool SupportsMechanicalPresenceSwitch; + private static bool SupportsStaggeredSpinup; + private static bool SupportsAggressiveLinkPowerManagement; + private static bool SupportsActivityLED; + private static bool SupportsCommandListOverride; + private static uint InterfaceSpeedSupport; + private static bool SupportsAHCIModeOnly; + private static bool SupportsPortMutliplier; + private static bool FISBasedSwitchingSupported; + private static bool PIOMultipleDRQBlock; + private static bool SlumberStateCapable; + private static bool PartialStateCapable; + private static uint NumOfCommandSlots; + private static bool CommandCompletionCoalsecingSupported; + private static bool EnclosureManagementSupported; + private static bool SupportsExternalSATA; + private static uint NumOfPorts; + #endregion + + // Constants + public const ulong RegularSectorSize = 512UL; + + // Informations + public string SerialNo; + public string Version + { + get => ((byte)mGeneric.AHCIVersion >> 24) + (byte)(mGeneric.AHCIVersion >> 16) + "." + (byte)(mGeneric.AHCIVersion >> 8) + ((byte)(mGeneric.AHCIVersion) > 0 ? "." + (byte)mGeneric.AHCIVersion : ""); + } + + internal static void InitDriver() + { + + if (xDevice != null) + { + AHCI Driver = new AHCI(xDevice); + } + } + + internal PCIDevice GetDevice() => xDevice; + + public AHCI(PCIDevice aAHCIDevice) + { + aAHCIDevice.EnableBusMaster(true); + aAHCIDevice.EnableMemory(true); + + mABAR = aAHCIDevice.BaseAddressBar[5].BaseAddress; + mGeneric = new GenericRegisters(aAHCIDevice.BaseAddressBar[5].BaseAddress); + mGeneric.GlobalHostControl |= (1U << 31); // Enable AHCI + + GetCapabilities(); + mPorts.Capacity = (int)NumOfPorts; + GetPorts(); + + foreach(StoragePort xPort in mPorts) + { + if(xPort.mPortType == PortType.SATA) + { + mAHCIDebugger.Send($"{xPort.mPortName} Port 0:{xPort.mPortNumber}"); + var xMBRData = new byte[512]; + xPort.ReadBlock(0UL, 1U, xMBRData); + var xMBR = new MBR(xMBRData); + + if (xMBR.EBRLocation != 0) + { + // EBR Detected! + mAHCIDebugger.Send("EBR Detected within MBR code"); + var xEBRData = new byte[512]; + xPort.ReadBlock(xMBR.EBRLocation, 1U, xEBRData); + var xEBR = new EBR(xEBRData); + for (int i = 0; i < xEBR.Partitions.Count; i++) + { + //var xPart = xEBR.Partitions[i]; + //var xPartDevice = new Partition(xSATA, xPart.StartSector, xPart.SectorCount); + //Devices.Add(xPartDevice); + } + } + + mAHCIDebugger.Send($"Number of MBR partitions found on port 0:{xPort.mPortNumber} "); + mAHCIDebugger.SendNumber(xMBR.Partitions.Count); + for (int i = 0; i < xMBR.Partitions.Count; i++) + { + var xPart = xMBR.Partitions[i]; + if (xPart == null) + { + Console.WriteLine("Null partition found at idx: " + i); + } + else + { + var xPartDevice = new Partition(xPort, xPart.StartSector, xPart.SectorCount); + BlockDevice.Devices.Add(xPartDevice); + Console.WriteLine("Found partition at idx: " + i); + } + } + } + else if (xPort.mPortType == PortType.SATAPI) + { + mAHCIDebugger.Send($"{xPort.mPortName} Port 0:{xPort.mPortNumber}"); + + // Just to test Read Sector! + + //byte[] xMBRData = new byte[512]; + //xPort.ReadBlock(0UL, 1U, xMBRData); + //MBR xMBR = new MBR(xMBRData); + + //mAHCIDebugger.Send($"Number of corrupted MBR partitions found on port 0:{xPort.mPortNumber} "); + //mAHCIDebugger.SendNumber(xMBR.Partitions.Count); + //for (int i = 0; i < xMBR.Partitions.Count; i++) + //{ + // var xPart = xMBR.Partitions[i]; + // if (xPart == null) + // { + // Console.WriteLine("Null partition found at idx: " + i); + // } + // else + // { + // var xPartDevice = new Partition(xPort, xPart.StartSector, xPart.SectorCount); + // BlockDevice.Devices.Add(xPartDevice); + // Console.WriteLine("Found corrupted partition at idx: " + i); + // } + //} + } + } + } + + public static void HBAReset() + { + mGeneric.GlobalHostControl = 1; + uint HR = 0; + do + { + Wait(1); + HR = mGeneric.GlobalHostControl & 1; + } while (HR != 0); + } + + public static void Wait(int microsecondsTimeout) + { + byte xVoid; + for (int i = 0; i < microsecondsTimeout; i++) + { + // Random IOPort + xVoid = Core.Global.BaseIOGroups.TextScreen.Data1.Byte; + xVoid = Core.Global.BaseIOGroups.TextScreen.Data1.Byte; + xVoid = Core.Global.BaseIOGroups.TextScreen.Data1.Byte; + xVoid = Core.Global.BaseIOGroups.TextScreen.Data1.Byte; + xVoid = Core.Global.BaseIOGroups.TextScreen.Data1.Byte; + xVoid = Core.Global.BaseIOGroups.TextScreen.Data1.Byte; + xVoid = Core.Global.BaseIOGroups.TextScreen.Data1.Byte; + xVoid = Core.Global.BaseIOGroups.TextScreen.Data1.Byte; + xVoid = Core.Global.BaseIOGroups.TextScreen.Data1.Byte; + xVoid = Core.Global.BaseIOGroups.TextScreen.Data1.Byte; + } + } + + private void GetCapabilities() + { + NumOfPorts = mGeneric.Capabilities & 0x1F; + SupportsExternalSATA = (mGeneric.Capabilities >> 5 & 1) == 1; + EnclosureManagementSupported = (mGeneric.Capabilities >> 6 & 1) == 1; + CommandCompletionCoalsecingSupported = (mGeneric.Capabilities >> 7 & 1) == 1; + NumOfCommandSlots = mGeneric.Capabilities >> 8 & 0x1F; + PartialStateCapable = (mGeneric.Capabilities >> 13 & 1) == 1; + SlumberStateCapable = (mGeneric.Capabilities >> 14 & 1) == 1; + PIOMultipleDRQBlock = (mGeneric.Capabilities >> 15 & 1) == 1; + FISBasedSwitchingSupported = (mGeneric.Capabilities >> 16 & 1) == 1; + SupportsPortMutliplier = (mGeneric.Capabilities >> 17 & 1) == 1; + SupportsAHCIModeOnly = (mGeneric.Capabilities >> 18 & 1) == 1; + InterfaceSpeedSupport = mGeneric.Capabilities >> 20 & 0x0F; + SupportsCommandListOverride = (mGeneric.Capabilities >> 24 & 1) == 1; + SupportsActivityLED = (mGeneric.Capabilities >> 25 & 1) == 1; + SupportsAggressiveLinkPowerManagement = (mGeneric.Capabilities >> 26 & 1) == 1; + SupportsStaggeredSpinup = (mGeneric.Capabilities >> 27 & 1) == 1; + SupportsMechanicalPresenceSwitch = (mGeneric.Capabilities >> 28 & 1) == 1; + SupportsSNotificationRegister = (mGeneric.Capabilities >> 29 & 1) == 1; + SupportsNativeCommandQueuing = (mGeneric.Capabilities >> 30 & 1) == 1; + Supports64bitAddressing = (mGeneric.Capabilities >> 31 & 1) == 1; + } + + private void GetPorts() + { + // Search for disks + var xImplementedPort = mGeneric.ImplementedPorts; + var xPort = 0; + for(; xPort < 32; xPort++) + { + if ((xImplementedPort & 1) != 0) + { + PortRegisters xPortReg = new PortRegisters((uint)mABAR + 0x100, (uint)xPort); + PortType PortType = CheckPortType(xPortReg); + xPortReg.mPortType = PortType; + var xPortString = "0:" + ((xPort.ToString().Length <= 1) ? xPort.ToString().PadLeft(1, '0') : xPort.ToString()); + if (PortType == PortType.SATA) // If Port type was SATA. + { + mAHCIDebugger.Send("Initializing Port " + xPortString + " with type SATA"); + PortRebase(xPortReg, (uint)xPort); + var xSATAPort = new SATA(xPortReg); + mPorts.Add(xSATAPort); + } + else if (PortType == PortType.SATAPI) // If Port type was SATAPI. + { + mAHCIDebugger.Send("Initializing Port " + xPortString + " with type Serial ATAPI"); + //PortRebase(xPortReg, (uint)xPort); + //var xSATAPIPort = new SATAPI(xPortReg); + //mPorts.Add(xSATAPIPort); + } + else if (PortType == PortType.SEMB) // If Port type was SEMB. + { + mAHCIDebugger.Send("SEMB Drive at port " + xPortString + " found, which is not supported yet!"); + } + else if (PortType == PortType.PM) // If Port type was Port Mulitplier. + { + mAHCIDebugger.Send("Port Multiplier Drive at port " + xPortString + " found, which is not supported yet!"); + } + else if (PortType != PortType.Nothing) + throw new Exception("SATA Error"); + } + xImplementedPort >>= 1; + } + } + + private PortType CheckPortType(PortRegisters aPort) + { + var xIPM = (InterfacePowerManagementStatus)((aPort.SSTS >> 8) & 0x0F); + var xSPD = (CurrentInterfaceSpeedStatus)((aPort.SSTS >> 4) & 0x0F); + var xDET = (DeviceDetectionStatus)(aPort.SSTS & 0x0F); + var xSignature = aPort.SIG; + + // Check if the port is active! + if (xIPM != InterfacePowerManagementStatus.Active) + return PortType.Nothing; + if (xDET != DeviceDetectionStatus.DeviceDetectedWithPhy) + return PortType.Nothing; + + xSignature >>= 16; + + switch ((AHCISignature)xSignature) + { + case AHCISignature.SATA: return PortType.SATA; + case AHCISignature.SATAPI: return PortType.SATAPI; + case AHCISignature.SEMB: return PortType.SEMB; + case AHCISignature.PortMultiplier: return PortType.PM; + case AHCISignature.Nothing: return PortType.Nothing; + default: throw new Exception("SATA Error: Unknown drive found at port: " + aPort.mPortNumber);; + } + } + + private void PortRebase(PortRegisters aPort, uint aPortNumber) + { + mAHCIDebugger.Send("Stop"); + if (!StopCMD(aPort)) SATA.PortReset(aPort); + + aPort.CLB = (uint)Base.AHCI + (0x400 * aPortNumber); + aPort.FB = (uint)Base.AHCI + 0x8000 + (0x100 * aPortNumber); + + aPort.SERR = 1; + aPort.IS = 0; + aPort.IE = 0; + + new MemoryBlock(aPort.CLB, 1024).Fill(0); + new MemoryBlock(aPort.FB, 256).Fill(0); + + GetCommandHeader(aPort); // Rebase Command header + + if (!StartCMD(aPort)) SATA.PortReset(aPort); + + aPort.IS = 0; + aPort.IE = 0xFFFFFFFF; + + mAHCIDebugger.Send("Finished!"); + } + + private static HBACommandHeader[] GetCommandHeader(PortRegisters aPort) + { + HBACommandHeader[] xCMDHeader = new HBACommandHeader[32]; + for (uint i = 0; i < xCMDHeader.Length; i++) + { + xCMDHeader[i] = new HBACommandHeader(aPort.CLB, i) + { + PRDTL = 8, + + CTBA = (uint)(Base.AHCI + 0xA000) + (0x2000 * aPort.mPortNumber) + (0x100 * i), + + CTBAU = 0 + }; + new MemoryBlock(xCMDHeader[i].CTBA, 0x100).Fill(0); + } + return xCMDHeader; + } + + private bool StartCMD(PortRegisters aPort) + { + int xSpin; + for (xSpin = 0; xSpin < 101; xSpin++) + { + if ((aPort.CMD & (uint)CommandAndStatus.CMDListRunning) == 0) break; + Wait(5000); + } + if (xSpin == 101) return false; + + aPort.CMD |= (1 << 4); + aPort.CMD |= (1 << 0); + + return true; + } + + // Thanks to Microsoft for the detailed info about stopping command process! + private bool StopCMD(PortRegisters aPort) + { + int xSpin; + aPort.CMD &= ~(1U << 0); //Bit 0 + + for (xSpin = 0; xSpin < 101; xSpin++) + { + if ((aPort.CMD & (uint)CommandAndStatus.CMDListRunning) == 0) break; + Wait(5000); + } + if (xSpin == 101) return false; + + for (xSpin = 0; xSpin < 101; xSpin++) + { + if ((aPort.CI == 0)) break; + Wait(50); + } + if (xSpin == 101) return false; + + aPort.CMD &= ~(1U << 4); //Bit 4 + + if (SupportsCommandListOverride) + { + if ((aPort.TFD & (uint)ATADeviceStatus.Busy) != 0) + { + aPort.CMD |= (1U << 3); + } + } + + for (xSpin = 0; xSpin < 101; xSpin++) + { + if ((aPort.CMD & (uint)CommandAndStatus.CMDListRunning) == 0 && + (aPort.CMD & (uint)CommandAndStatus.FISRecieveRunning) == 0 && + (aPort.CMD & (uint)CommandAndStatus.StartProccess) == 0 && + (aPort.CMD & (uint)CommandAndStatus.FISRecieveEnable) == 0) break; + Wait(5000); + } + if (xSpin == 101) + { + if (SupportsCommandListOverride) + aPort.CMD |= (1U << 3); + else + aPort.CMD &= ~(1U << 3); + return false; + } + + return true; + } + } +} diff --git a/source/Cosmos.HAL2/BlockDevice/IDE.cs b/source/Cosmos.HAL2/BlockDevice/IDE.cs new file mode 100644 index 000000000..87c874314 --- /dev/null +++ b/source/Cosmos.HAL2/BlockDevice/IDE.cs @@ -0,0 +1,80 @@ +using System; +using Cosmos.HAL.BlockDevice; + +namespace Cosmos.HAL.BlockDevice +{ + public class IDE + { + private static PCIDevice xDevice = HAL.PCI.GetDeviceClass(HAL.ClassID.MassStorageController, + HAL.SubclassID.IDEInterface); + + internal static void InitDriver() + { + if (xDevice != null) + { + Console.WriteLine("ATA Primary Master"); + Initialize(Ata.ControllerIdEnum.Primary, Ata.BusPositionEnum.Master); + //Console.WriteLine("ATA Primary Slave"); + //Initialize(Ata.ControllerIdEnum.Primary, Ata.BusPositionEnum.Slave); + Console.WriteLine("ATA Secondary Master"); + Initialize(Ata.ControllerIdEnum.Secondary, Ata.BusPositionEnum.Master); + //Console.WriteLine("ATA Secondary Slave"); + //Initialize(Ata.ControllerIdEnum.Secondary, Ata.BusPositionEnum.Slave); + } + } + + private static void Initialize(Ata.ControllerIdEnum aControllerID, Ata.BusPositionEnum aBusPosition) + { + var xIO = aControllerID == Ata.ControllerIdEnum.Primary ? Core.Global.BaseIOGroups.ATA1 : Core.Global.BaseIOGroups.ATA2; + var xATA = new AtaPio(xIO, aControllerID, aBusPosition); + if (xATA.DriveType == AtaPio.SpecLevel.Null) + return; + else if (xATA.DriveType == AtaPio.SpecLevel.ATA) + { + BlockDevice.Devices.Add(xATA); + Ata.AtaDebugger.Send("ATA device with speclevel ATA found."); + } + else if (xATA.DriveType == AtaPio.SpecLevel.ATAPI) + { + Ata.AtaDebugger.Send("ATA device with speclevel ATAPI found, which is not supported yet!"); + return; + } + var xMbrData = new byte[512]; + xATA.ReadBlock(0UL, 1U, xMbrData); + var xMBR = new MBR(xMbrData); + + if (xMBR.EBRLocation != 0) + { + //EBR Detected + var xEbrData = new byte[512]; + xATA.ReadBlock(xMBR.EBRLocation, 1U, xEbrData); + var xEBR = new EBR(xEbrData); + + for (int i = 0; i < xEBR.Partitions.Count; i++) + { + //var xPart = xEBR.Partitions[i]; + //var xPartDevice = new BlockDevice.Partition(xATA, xPart.StartSector, xPart.SectorCount); + //BlockDevice.BlockDevice.Devices.Add(xPartDevice); + } + } + + // TODO Change this to foreach when foreach is supported + Ata.AtaDebugger.Send("Number of MBR partitions found:"); + Ata.AtaDebugger.SendNumber(xMBR.Partitions.Count); + for(int i = 0; i < xMBR.Partitions.Count; i++) + { + var xPart = xMBR.Partitions[i]; + if (xPart == null) + { + Console.WriteLine("Null partition found at idx: " + i); + } + else + { + var xPartDevice = new Partition(xATA, xPart.StartSector, xPart.SectorCount); + BlockDevice.Devices.Add(xPartDevice); + Console.WriteLine("Found partition at idx: " + i); + } + } + } + } +} diff --git a/source/Cosmos.HAL2/BlockDevice/Ports/Sata.cs b/source/Cosmos.HAL2/BlockDevice/Ports/Sata.cs new file mode 100644 index 000000000..927a82b4f --- /dev/null +++ b/source/Cosmos.HAL2/BlockDevice/Ports/Sata.cs @@ -0,0 +1,368 @@ +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, 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, byte[] aData) + { + Mem.DataBlock.Write8(aData); + SendSATA48Command(ATACommands.WriteDmaExt, (uint)(aBlockNo), (uint)aBlockCount); + SendSATACommand(ATACommands.CacheFlush); + } + } +} diff --git a/source/Cosmos.HAL2/BlockDevice/Ports/Satapi.cs b/source/Cosmos.HAL2/BlockDevice/Ports/Satapi.cs new file mode 100644 index 000000000..40e975b12 --- /dev/null +++ b/source/Cosmos.HAL2/BlockDevice/Ports/Satapi.cs @@ -0,0 +1,148 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Cosmos.Core.Memory.Old; +using Cosmos.HAL.BlockDevice.Registers; +using Cosmos.Core; +using Cosmos.Debug.Kernel; + +namespace Cosmos.HAL.BlockDevice.Ports +{ + public class SATAPI : StoragePort + { + internal static Debugger mSATAPIDebugger = new Debugger("HAL", "SATAPI"); + + public PortRegisters mPortReg; + + public override PortType mPortType => PortType.SATAPI; + public override string mPortName => "SATAPI"; + public override uint mPortNumber => mPortReg.mPortNumber; + + public SATAPI(PortRegisters aSATAPIPort) + { + + // Check if it is really a SATAPI Port! + if (aSATAPIPort.mPortType != PortType.SATAPI || (aSATAPIPort.CMD & (1U << 24)) == 0) + { + throw new Exception($"SATAPI Error: 0:{mPortNumber} is not SATAPI port!"); + } + mSATAPIDebugger.Send("SATAPI Constructor"); + + mPortReg = aSATAPIPort; + + mBlockSize = 2048; + } + + public void SendSATAPICommand(ATACommands aCommand, uint aStart, uint aCount) + { + mPortReg.IS = unchecked((uint)-1); + + int xSlot = FindCMDSlot(mPortReg); + if (xSlot == -1) return; + + HBACommandHeader xCMDHeader = new HBACommandHeader(mPortReg.CLB, (uint)xSlot); + xCMDHeader.CFL = 5; + xCMDHeader.ATAPI = 1; + xCMDHeader.PRDTL = (ushort)(((aCount - 1) >> 4) + 1); + xCMDHeader.Write = 0; + + xCMDHeader.CTBA = Heap.MemAlloc(128 + ((uint)xCMDHeader.PRDTL) * 16); + + HBACommandTable xCMDTable = new HBACommandTable(xCMDHeader.CTBA, xCMDHeader.PRDTL); + + uint DataBaseAddress = 0x0046C000; + for (int i = 0; i < xCMDHeader.PRDTL - 1; i++) + { + xCMDTable.PRDTEntry[i].DBA = DataBaseAddress; + xCMDTable.PRDTEntry[i].DBC = 8 * 1024 - 1; // 8K bytes (this value should always be set to 1 less than the actual value) + xCMDTable.PRDTEntry[i].InterruptOnCompletion = 1; + DataBaseAddress += 8 * 1024; // 4K words + aCount -= 16; // 16 sectors + } + + // 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)ATACommands.Packet, + Device = 1 << 4 + }; + + byte[] xATAPICMD = new byte[12]; + xATAPICMD[0] = (byte)aCommand; + xATAPICMD[2] = (byte)((aStart >> 0x18) & 0xFF); + xATAPICMD[3] = (byte)((aStart >> 0x10) & 0xFF); + xATAPICMD[4] = (byte)((aStart >> 0x08) & 0xFF); + xATAPICMD[5] = (byte)((aStart >> 0x00) & 0xFF); + xATAPICMD[9] = (byte)(aCount); + for (uint i = 0; i < xATAPICMD.Length; i++) + new Core.MemoryBlock(xCMDTable.ACMD, 12).Bytes[i] = xATAPICMD[i]; + + int xSpin = 0; + do xSpin++; while ((mPortReg.TFD & 0x88) != 0 && xSpin < 1000000); + + if (xSpin == 1000000) + { + mSATAPIDebugger.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("\n[Success]: "); + //Console.Write("Command has been sent successfully!"); + //Console.ResetColor(); + + return; + } + + public int FindCMDSlot(PortRegisters aPort) + { + // If not set in SACT and CI, the slot is free + var xSlots = (aPort.SACT | aPort.CI); + + for (int i = 1; i < 32; i++) + { + if ((xSlots & 1) == 0) + return i; + xSlots >>= 1; + } + mSATAPIDebugger.Send("SATA Error: Cannot find a free command slot!"); + return -1; + } + + public override void ReadBlock(ulong aBlockNo, ulong aBlockCount, byte[] aData) + { + SendSATAPICommand(ATACommands.Read, (uint)aBlockNo, (uint)aBlockCount); + byte[] xByte = new byte[512]; + new MemoryBlock(0x0046C000, 512).Read8(xByte); + for (int i = 0; i < 512; i++) + { + Console.Write(xByte[i]); + } + } + + public override void WriteBlock(ulong aBlockNo, ulong aBlockCount, byte[] aData) + { + // To be implemented! + } + } +} diff --git a/source/Cosmos.HAL2/BlockDevice/Ports/StoragePort.cs b/source/Cosmos.HAL2/BlockDevice/Ports/StoragePort.cs new file mode 100644 index 000000000..5bb31c5c2 --- /dev/null +++ b/source/Cosmos.HAL2/BlockDevice/Ports/StoragePort.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Cosmos.HAL.BlockDevice.Registers; + +namespace Cosmos.HAL.BlockDevice.Ports +{ + public abstract class StoragePort : BlockDevice + { + public abstract PortType mPortType { get; } + public abstract string mPortName { get; } + public abstract uint mPortNumber { get; } + } +} diff --git a/source/Cosmos.HAL2/BlockDevice/Registers/AHCIRegs.cs b/source/Cosmos.HAL2/BlockDevice/Registers/AHCIRegs.cs new file mode 100644 index 000000000..81320b8de --- /dev/null +++ b/source/Cosmos.HAL2/BlockDevice/Registers/AHCIRegs.cs @@ -0,0 +1,793 @@ +using System; +using System.Collections.Generic; +using System.Text; +using Cosmos.Core; + +namespace Cosmos.HAL.BlockDevice.Registers +{ + public enum Base : uint + { + AHCI = 0x00400000 + } + // Registers + public class GenericRegisters + { + private MemoryBlock xBlock; + private uint xAddress; + public GenericRegisters(uint aAddress) + { + xAddress = aAddress; + xBlock = new MemoryBlock(aAddress, 0x100); + } + + public uint Capabilities + { + get { return xBlock[0x00]; } + set { xBlock[0x00] = value; } + } + + public uint GlobalHostControl + { + get { return xBlock[0x04]; } + set { xBlock[0x04] = value; } + } + public uint InterruptStatus + { + get { return xBlock[0x08]; } + set { xBlock[0x08] = value; } + } + + public uint ImplementedPorts + { + get { return xBlock[0x0C]; } + set { xBlock[0x0C] = value; } + } + + public uint AHCIVersion + { + get { return xBlock[0x10]; } + set { xBlock[0x10] = value; } + } + + public uint CCC_Control + { + get { return xBlock[0x14]; } + set { xBlock[0x14] = value; } + } + + public uint CCC_Ports + { + get { return xBlock[0x18]; } + set { xBlock[0x18] = value; } + } + + public uint EM_Location + { + get { return xBlock[0x1C]; } + set { xBlock[0x1C] = value; } + } + + public uint EM_Control + { + get { return xBlock[0x20]; } + set { xBlock[0x20] = value; } + } + + public uint ExtendedCapabilities + { + get { return xBlock[0x24]; } + set { xBlock[0x24] = value; } + } + + public uint BIOSHandOffStatus + { + get { return xBlock[0x28]; } + set { xBlock[0x28] = value; } + } + } + + public class PortRegisters + { + private MemoryBlock xBlock; + private uint xAddress; + public uint mPortNumber; + public PortType mPortType = PortType.Nothing; + public bool Active; + public PortRegisters(uint aAddress, uint aPortNumber) + { + xAddress = aAddress; + mPortNumber = aPortNumber; + xBlock = new MemoryBlock(aAddress + (0x80 * mPortNumber), 0x80); + Active = false; + } + + public uint CLB + { + get { return xBlock[0x00]; } + set { xBlock[0x00] = value; } + } + + public uint CLBU + { + get { return xBlock[0x04]; } + set { xBlock[0x04] = value; } + } + + public uint FB + { + get { return xBlock[0x08]; } + set { xBlock[0x08] = value; } + } + + public uint FBU + { + get { return xBlock[0x0C]; } + set { xBlock[0x0C] = value; } + } + + public uint IS + { + get { return xBlock[0x10]; } + set { xBlock[0x10] = value; } + } + + public uint IE + { + get { return xBlock[0x14]; } + set { xBlock[0x14] = value; } + } + + public uint CMD + { + get { return xBlock[0x18]; } + set { xBlock[0x18] = value; } + } + + public uint Reserved + { + get { return xBlock[0x1C]; } + } + + public uint TFD + { + get { return xBlock[0x20]; } + set { xBlock[0x20] = value; } + } + + public uint SIG + { + get { return xBlock[0x24]; } + set { xBlock[0x24] = value; } + } + + public uint SSTS + { + get { return xBlock[0x28]; } + set { xBlock[0x28] = value; } + } + + public uint SCTL + { + get { return xBlock[0x2C]; } + set { xBlock[0x2C] = value; } + } + + public uint SERR + { + get { return xBlock[0x30]; } + set { xBlock[0x30] = value; } + } + + public uint SACT + { + get { return xBlock[0x34]; } + set { xBlock[0x34] = value; } + } + + public uint CI + { + get { return xBlock[0x38]; } + set { xBlock[0x38] = value; } + } + + public uint SNTF + { + get { return xBlock[0x3C]; } + set { xBlock[0x3C] = value; } + } + + public uint FBS + { + get { return xBlock[0x40]; } + set { xBlock[0x40] = value; } + } + + public uint DEVSLP + { + get { return xBlock[0x44]; } + set { xBlock[0x44] = value; } + } + } + + // Command List + public class HBACommandHeader + { + private MemoryBlock xBlock; + private uint xAddress;// + private uint xSlot; + public HBACommandHeader(uint aAddress, uint aSlot) + { + xAddress = aAddress; + xSlot = aSlot; + xBlock = new MemoryBlock(aAddress + (32 * aSlot), 0x20); + xBlock.Fill(0); + } + + public byte CFL + { + get { return (byte)(xBlock.Bytes[0x00] & 0x1F); } + set { xBlock.Bytes[0x00] = value; } + } + public byte ATAPI + { + get { return (byte)((xBlock.Bytes[0x00] >> 5) & 1); } + set { xBlock.Bytes[0x00] |= (byte)(value << 5); } + } + public byte Write + { + get { return (byte)((xBlock.Bytes[0x00] >> 6) & 1); } + set { xBlock.Bytes[0x00] |= (byte)(value << 6); } + } + public byte Prefetchable + { + get { return (byte)((xBlock.Bytes[0x00] >> 7) & 1); } + set { xBlock.Bytes[0x00] |= (byte)(value << 7); } + } + public byte Reset + { + get { return (byte)((xBlock.Bytes[0x01]) & 1); } + set { xBlock.Bytes[0x01] |= (byte)(value); } + } + public byte BIST + { + get { return (byte)((xBlock.Bytes[0x01] >> 1) & 1); } + set { xBlock.Bytes[0x01] |= (byte)(value << 1); } + } + public byte ClearBusy + { + get { return (byte)((xBlock.Bytes[0x01] >> 2) & 1); } + set { xBlock.Bytes[0x01] |= (byte)(value << 2); } + } + public byte Reserved + { + get { return (byte)((xBlock.Bytes[0x01] >> 3) & 1); } + } + public byte PMP + { + get { return (byte)((xBlock.Bytes[0x01] >> 4) & 0x0F); } + set { xBlock.Bytes[0x01] = (byte)(value << 4); } + } + public ushort PRDTL + { + get { return xBlock.Words[0x02]; } + set { xBlock.Words[0x02] = value; } + } + + public uint PRDBC + { + get { return xBlock[0x04]; } + set { xBlock[0x04] = value; } + } + + public uint CTBA + { + get { return xBlock[0x08]; } + set { xBlock[0x08] = value; } + } + + public uint CTBAU + { + get { return xBlock[0x0C]; } + set { xBlock[0x0C] = value; } + } + + public uint Reserved1 + { + get { return xBlock[0x10]; } + } + + public uint Reserved2 + { + get { return xBlock[0x14]; } + } + + public uint Reserved3 + { + get { return xBlock[0x18]; } + } + + public uint Reserved4 + { + get { return xBlock[0x1C]; } + } + } + + public class HBACommandTable + { + private MemoryBlock xBlock; + private uint xAddress; + public HBACommandTable(uint aAddress, uint aPRDTCount) + { + xAddress = aAddress; + xBlock = new MemoryBlock(aAddress, 0x80); + xBlock.Fill(0); + PRDTEntry = new HBAPRDTEntry[aPRDTCount]; + for(uint i = 0; i < aPRDTCount; i++) + { + PRDTEntry[i] = new HBAPRDTEntry(aAddress + 0x80, i); + } + } + + public uint CFIS + { + get { return xAddress; } + } + + public uint ACMD + { + get { return xAddress + 0x40; } + } + + public uint Reserved + { + get { return xBlock[0x50]; } + } + + public HBAPRDTEntry[] PRDTEntry; + } + + public class HBAPRDTEntry + { + private MemoryBlock xBlock; + private uint xAddress; + private uint xEntry; + public HBAPRDTEntry(uint aAddress, uint aEntry) + { + xAddress = aAddress; + xEntry = aEntry; + xBlock = new MemoryBlock(aAddress + (0x10 * xEntry), 0x10); + xBlock.Fill(0); + } + + public uint DBA + { + get { return xBlock[0x00]; } + set { xBlock[0x00] = value; } + } + + public uint DBAU + { + get { return xBlock[0x04]; } + set { xBlock[0x04] = value; } + } + + public uint Reserved + { + get { return xBlock[0x08]; } + } + + public uint DBC + { + get { return xBlock[0x0C] & 0x3FFFFF; } + set { xBlock[0x0C] = value; } + } + public uint Reserved1 + { + get { return xBlock[0x0E] << 6; } + } + public byte InterruptOnCompletion + { + get { return (byte)(xBlock.Bytes[0x0F] >> 7); } + set { xBlock.Bytes[0x0F] = (byte)(value << 7); } + } + } + + // FISes + public class FISRegisterH2D + { + private MemoryBlock xBlock; + private uint xAddress; + public FISRegisterH2D(uint aAddress) + { + xAddress = aAddress; + xBlock = new MemoryBlock(aAddress, 20); + xBlock.Fill(0); + } + + public byte FISType + { + get { return (byte)(xBlock.Bytes[0x00]); } + set { xBlock.Bytes[0x00] = value; } + } + + public byte IsCommand + { + get { return (byte)((xBlock.Bytes[0x01] >> 7)); } + set { xBlock.Bytes[0x01] = (byte)(value << 7); } + } + public byte Command + { + get { return xBlock.Bytes[0x02]; } + set { xBlock.Bytes[0x02] = value; } + } + public byte FeatureLow + { + get { return xBlock.Bytes[0x03]; } + set { xBlock.Bytes[0x03] = value; } + } + + public byte LBA0 + { + get { return xBlock.Bytes[0x04]; } + set { xBlock.Bytes[0x04] = value; } + } + public byte LBA1 + { + get { return xBlock.Bytes[0x05]; } + set { xBlock.Bytes[0x05] = value; } + } + public byte LBA2 + { + get { return xBlock.Bytes[0x06]; } + set { xBlock.Bytes[0x06] = value; } + } + public byte Device + { + get { return xBlock.Bytes[0x07]; } + set { xBlock.Bytes[0x07] = value; } + } + + public byte LBA3 + { + get { return xBlock.Bytes[0x08]; } + set { xBlock.Bytes[0x08] = value; } + } + public byte LBA4 + { + get { return xBlock.Bytes[0x09]; } + set { xBlock.Bytes[0x09] = value; } + } + public byte LBA5 + { + get { return xBlock.Bytes[0x0A]; } + set { xBlock.Bytes[0x0A] = value; } + } + public byte FeatureHigh + { + get { return xBlock.Bytes[0x0B]; } + set { xBlock.Bytes[0x0B] = value; } + } + + public byte CountL + { + get { return xBlock.Bytes[0x0C]; } + set { xBlock.Bytes[0x0C] = value; } + } + public byte CountH + { + get { return xBlock.Bytes[0x0D]; } + set { xBlock.Bytes[0x0D] = value; } + } + public byte ICC + { + get { return xBlock.Bytes[0x0E]; } + set { xBlock.Bytes[0x0E] = value; } + } + public byte Control + { + get { return xBlock.Bytes[0x0F]; } + set { xBlock.Bytes[0x0F] = value; } + } + + public byte Reserved1 + { + get { return xBlock.Bytes[0x10]; } + } + public byte Reserved2 + { + get { return xBlock.Bytes[0x11]; } + } + public byte Reserved3 + { + get { return xBlock.Bytes[0x12]; } + } + public byte Reserved4 + { + get { return xBlock.Bytes[0x13]; } + } + } + + public class FISRegisterD2H + { + private MemoryBlock xBlock; + private uint xAddress; + public FISRegisterD2H(uint aAddress) + { + xAddress = aAddress; + xBlock = new MemoryBlock(aAddress, 20); + } + + public FISType FISType + { + get { return (FISType)xBlock.Bytes[0x00]; } + set { xBlock.Bytes[0x00] = (byte)value; } + } + public byte PortMultiplier + { + get { return (byte)(xBlock.Bytes[0x00] << 8); } + set { xBlock.Bytes[0x00] = (byte)value; } + } + public byte Reserved + { + get { return (byte)(xBlock.Bytes[0x00] << 12); } + } + public byte InterruptBit + { + get { return (byte)(xBlock.Bytes[0x00] << 14); } + set { xBlock.Bytes[0x00] = (byte)(value << 14); } + } + public byte Reserved1 + { + get { return (byte)(xBlock.Bytes[0x00] << 15); } + } + + public byte Status + { + get { return (byte)(xBlock.Bytes[0x00] << 16); } + set { xBlock.Bytes[0x00] = (byte)(value << 16); } + } + public byte Error + { + get { return (byte)(xBlock.Bytes[0x00] << 24); } + set { xBlock.Bytes[0x00] = (byte)(value << 24); } + } + + public byte LBA0 + { + get { return xBlock.Bytes[0x04]; } + set { xBlock.Bytes[0x04] = value; } + } + public byte LBA1 + { + get { return (byte)(xBlock.Bytes[0x04] << 8); } + set { xBlock.Bytes[0x04] = (byte)(value << 8); } + } + public byte LBA2 + { + get { return (byte)(xBlock.Bytes[0x04] << 16); } + set { xBlock.Bytes[0x04] = (byte)(value << 16); } + } + public byte Device + { + get { return (byte)(xBlock.Bytes[0x04] << 24); } + set { xBlock.Bytes[0x04] = (byte)(value << 24); } + } + + public byte LBA3 + { + get { return xBlock.Bytes[0x08]; } + set { xBlock.Bytes[0x08] = value; } + } + public byte LBA4 + { + get { return (byte)(xBlock.Bytes[0x08] << 8); } + set { xBlock.Bytes[0x08] = (byte)(value << 8); } + } + public byte LBA5 + { + get { return (byte)(xBlock.Bytes[0x08] << 16); } + set { xBlock.Bytes[0x08] = (byte)(value << 16); } + } + public byte Reserved2 + { + get { return (byte)(xBlock.Bytes[0x08] << 24); } + } + + public byte CountL + { + get { return xBlock.Bytes[0x0C]; } + set { xBlock.Bytes[0x0C] = value; } + } + public byte CountH + { + get { return (byte)(xBlock.Bytes[0x0C] << 8); } + set { xBlock.Bytes[0x0C] = (byte)(value << 8); } + } + public byte Reserved3 + { + get { return (byte)(xBlock.Bytes[0x0C] << 16); } + } + public byte Reserved4 + { + get { return (byte)(xBlock.Bytes[0x0C] << 24); } + } + + public byte Reserved5 + { + get { return xBlock.Bytes[0x10]; } + } + public byte Reserved6 + { + get { return xBlock.Bytes[0x11]; } + } + + public byte Reserved7 + { + get { return xBlock.Bytes[0x12]; } + } + public byte Reserved8 + { + get { return xBlock.Bytes[0x13]; } + } + } + + // Enums + public enum PortType + { + Nothing = 0x00, + SATA = 0x01, + SATAPI = 0x02, + SEMB = 0x03, + PM = 0x04 + } + + public enum FISType + { + FIS_Type_RegisterH2D = 0x27, // Register FIS: Host to Device + FIS_Type_RegisterD2H = 0x34, // Register FIS: Device to Host + FIS_Type_DMA_Activate = 0x39, // DMA Activate + FIS_Type_DMA_Setup = 0x41, // DMA Setup: Device to Host + FIS_Type_Data = 0x46, // Data FIS: Bidirectional + FIS_Type_BIST = 0x58, // BIST + FIS_Type_PIO_Setup = 0x5F, // PIO Setup: Device to Host + FIS_Type_DeviceBits = 0xA1 // Device bits + } + + public enum FISSize : byte + { + //FISRegisterH2D = Marshal.SizeOf(FISRegisterH2D); + FISRegisterH2D = 40 / sizeof(uint) + } + + public enum AHCISignature : uint // Drive Signature to identify what drive is plugged to Port X:X + { + SATA = 0x0000, + PortMultiplier = 0x9669, + SATAPI = 0xEB14, + SEMB = 0xC33C, + Nothing = 0xFFFF + } + + public enum InterfacePowerManagementStatus : uint // SATA Status: Interface Power Management Status + { + NotPresent = 0x00, + Active = 0x01, + Partial = 0x02, + Slumber = 0x06, + DeviceSleep = 0x08 + } + + public enum CurrentInterfaceSpeedStatus : uint // SATA Status: Current Interface Speed + { + NotPresent = 0x00, + Gen1Rate = 0x01, + Gen2Rate = 0x02, + Gen3Rate = 0x03 + } + + public enum DeviceDetectionStatus : uint // SATA Status: Device Detection Status + { + NotDetected = 0x00, + DeviceDetectedNoPhy = 0x01, + DeviceDetectedWithPhy = 0x03, + PhyOffline = 0x04 + } + + public enum ATADeviceStatus : uint + { + Busy = 0x80, + DRQ = 0x08 + } + + public enum CommandAndStatus : uint + { + ICC_Reserved0 = 0x0000000F, + ICC_DevSleep = 0x00000008, + ICC_Slumber = 0x00000006, + ICC_Partial = 0x00000002, + ICC_Active = 0x00000001, + ICC_Idle = 0x00000000, + ASP = (01 << 27), + ALPE = (01 << 26), + EnableATAPILED = (01 << 25), + ATAPIDevice = (01 << 24), + APSTE = (01 << 23), + FISSwitchPort = (01 << 22), + ExternalSATAPort = (01 << 21), + ColdPresenceDetect = (01 << 20), + MPSP = (01 << 19), + HotPlugCapPort = (01 << 18), + PortMultAttach = (01 << 17), + ColdPresenceState = (01 << 16), + CMDListRunning = (01 << 15), + FISRecieveRunning = (01 << 14), + MPSS = (01 << 13), + CurrentCMDSlot = (01 << 12), + Reserved0 = (01 << 07), + FISRecieveEnable = (01 << 04), + CMDListOverride = (01 << 03), + PowerOnDevice = (01 << 02), + SpinUpDevice = (01 << 01), + StartProccess = (01 << 00), + Null = 0xFFFF + } + + public enum InterruptStatus : int + { + ColdPortDetectStatus = (01 << 31), + TaskFileErrorStatus = (01 << 30), + HostBusFatalErrorStatus = (01 << 29), + HostBusDataErrorStatus = (01 << 28), + InterfaceFatalErrorStatus = (01 << 27), + InterfaceNFatalErrorStatus = (01 << 26), + OverflowStatus = (01 << 24), + IncorrectPMStatus = (01 << 23), + PhyRdyChangeStatus = (01 << 22), + DevMechanicalPresenceStatus = (01 << 07), + PortConnectChangeStatus = (01 << 06), + DescriptorProcessed = (01 << 05), + UnknownFISInterrupt = (01 << 04), + SetDeviceBitsInterrupt = (01 << 03), + DMASetupFISInterrupt = (01 << 02), + PIOSetupFISInterrupt = (01 << 01), + D2HRegFISInterrupt = (01 << 00), + Null = 0xFFFF + } + + public enum InterruptEnable : uint + { + OverflowEnable = (01 << 24), + IncorrectPMEnable = (01 << 23), + PhyRdyChangeInterruptEnable = (01 << 22), + DevMechanicalPresenceEnable = (01 << 07), + PortChangeInterruptEnable = (01 << 06), + DescProcessedInterruptEnable = (01 << 05), + UnknownFISInterruptEnable = (01 << 04), + SetDeviceBitsInterruptEnable = (01 << 03), + DMASetupFISInterruptEnable = (01 << 02), + PIOSetupFISInterruptEnable = (01 << 01), + D2HRegFISInterruptEnable = (01 << 00), + Null = 0xFFFF + } + + public enum ATACommands : byte + { + ReadDma = 0xC8, + ReadDmaExt = 0x25, + WriteDma = 0xCA, + WriteDmaExt = 0x35, + CacheFlush = 0xE7, + CacheFlushExt = 0xEA, + Packet = 0xA0, + IdentifyPacket = 0xA1, + IdentifyDMA = 0xEE, + Identify = 0xEC, + Read = 0xA8, + Eject = 0x1B + } + + public enum Bases : uint + { + SATA = 0x00400000 + } +} diff --git a/source/Cosmos.HAL2/Drivers/PCI/Video/VMWareSVGAII.cs b/source/Cosmos.HAL2/Drivers/PCI/Video/VMWareSVGAII.cs index 2c90cdf02..00a75ac88 100644 --- a/source/Cosmos.HAL2/Drivers/PCI/Video/VMWareSVGAII.cs +++ b/source/Cosmos.HAL2/Drivers/PCI/Video/VMWareSVGAII.cs @@ -145,7 +145,7 @@ namespace Cosmos.HAL.Drivers.PCI.Video public VMWareSVGAII() { - device = (HAL.PCI.GetDevice(0x15AD, 0x0405)); + device = (HAL.PCI.GetDevice(HAL.VendorID.VMWare, HAL.DeviceID.SVGAIIAdapter)); device.EnableMemory(true); uint basePort = device.BaseAddressBar[0].BaseAddress; IndexPort = new IOPort((ushort)(basePort + (uint)IOPortOffset.Index)); diff --git a/source/Cosmos.HAL2/Global.cs b/source/Cosmos.HAL2/Global.cs index d09b92700..6ab841147 100644 --- a/source/Cosmos.HAL2/Global.cs +++ b/source/Cosmos.HAL2/Global.cs @@ -1,128 +1,61 @@ -using System; - +using System; using Cosmos.Core; using Cosmos.Debug.Kernel; using Cosmos.HAL.BlockDevice; namespace Cosmos.HAL { - public static class Global - { - public static readonly Debugger mDebugger = new Debugger("HAL", "Global"); - - //static public PIT PIT = new PIT(); - // Must be static init, other static inits rely on it not being null - - public static TextScreenBase TextScreen = new TextScreen(); - public static PCI Pci; - - static public void Init(TextScreenBase textScreen) + public static class Global { - if (textScreen != null) - { - TextScreen = textScreen; - } + public static readonly Debugger mDebugger = new Debugger("HAL", "Global"); - mDebugger.Send("Before Core.Global.Init"); - Core.Global.Init(); + //static public PIT PIT = new PIT(); + // Must be static init, other static inits rely on it not being null - //TODO Redo this - Global init should be other. - // Move PCI detection to hardware? Or leave it in core? Is Core PC specific, or deeper? - // If we let hardware do it, we need to protect it from being used by System. - // Probably belongs in hardware, and core is more specific stuff like CPU, memory, etc. - //Core.PCI.OnPCIDeviceFound = PCIDeviceFound; + public static TextScreenBase TextScreen = new TextScreen(); + public static PCI Pci; - //TODO: Since this is FCL, its "common". Otherwise it should be - // system level and not accessible from Core. Need to think about this - // for the future. - - Console.WriteLine("Finding PCI Devices"); - mDebugger.Send("PCI Devices"); - PCI.Setup(); - - Console.WriteLine("Starting ACPI"); - mDebugger.Send("ACPI Init"); - ACPI.Start(); - - mDebugger.Send("Done initializing Cosmos.HAL.Global"); - - mDebugger.Send("ATA Primary Master"); - InitAta(Ata.ControllerIdEnum.Primary, Ata.BusPositionEnum.Master); - - //TODO Need to change code to detect if ATA controllers are present or not. How to do this? via PCI enum? - // They do show up in PCI space as well as the fixed space. - // Or is it always here, and was our compiler stack corruption issue? - mDebugger.Send("ATA Secondary Master"); - InitAta(Ata.ControllerIdEnum.Secondary, Ata.BusPositionEnum.Master); - //InitAta(BlockDevice.Ata.ControllerIdEnum.Secondary, BlockDevice.Ata.BusPositionEnum.Slave); - - } - - public static void EnableInterrupts() - { - CPU.EnableInterrupts(); - } - - private static void InitAta(Ata.ControllerIdEnum aControllerID, - Ata.BusPositionEnum aBusPosition) - { - var xIO = aControllerID == Ata.ControllerIdEnum.Primary - ? Core.Global.BaseIOGroups.ATA1 - : Core.Global.BaseIOGroups.ATA2; - var xATA = new AtaPio(xIO, aControllerID, aBusPosition); - if (xATA.DriveType == AtaPio.SpecLevel.Null) - { - return; - } - if (xATA.DriveType == AtaPio.SpecLevel.ATA) - { - BlockDevice.BlockDevice.Devices.Add(xATA); - Ata.AtaDebugger.Send("ATA device with speclevel ATA found."); - } - else - { - //Ata.AtaDebugger.Send("ATA device with spec level " + (int)xATA.DriveType + - // " found, which is not supported!"); - return; - } - var xMbrData = new byte[512]; - xATA.ReadBlock(0UL, 1U, xMbrData); - var xMBR = new MBR(xMbrData); - - if (xMBR.EBRLocation != 0) - { - //EBR Detected - var xEbrData = new byte[512]; - xATA.ReadBlock(xMBR.EBRLocation, 1U, xEbrData); - var xEBR = new EBR(xEbrData); - - for (int i = 0; i < xEBR.Partitions.Count; i++) + static public void Init(TextScreenBase textScreen) { - //var xPart = xEBR.Partitions[i]; - //var xPartDevice = new BlockDevice.Partition(xATA, xPart.StartSector, xPart.SectorCount); - //BlockDevice.BlockDevice.Devices.Add(xPartDevice); - } - } + if (textScreen != null) + { + TextScreen = textScreen; + } + + mDebugger.Send("Before Core.Global.Init"); + Core.Global.Init(); + + //TODO Redo this - Global init should be other. + // Move PCI detection to hardware? Or leave it in core? Is Core PC specific, or deeper? + // If we let hardware do it, we need to protect it from being used by System. + // Probably belongs in hardware, and core is more specific stuff like CPU, memory, etc. + //Core.PCI.OnPCIDeviceFound = PCIDeviceFound; + + //TODO: Since this is FCL, its "common". Otherwise it should be + // system level and not accessible from Core. Need to think about this + // for the future. + + Console.WriteLine("Finding PCI Devices"); + mDebugger.Send("PCI Devices"); + PCI.Setup(); + + Console.WriteLine("Starting ACPI"); + mDebugger.Send("ACPI Init"); + ACPI.Start(); + + IDE.InitDriver(); + AHCI.InitDriver(); + //EHCI.InitDriver(); + + mDebugger.Send("Done initializing Cosmos.HAL.Global"); - // TODO Change this to foreach when foreach is supported - Ata.AtaDebugger.Send("Number of MBR partitions found:"); - Ata.AtaDebugger.SendNumber(xMBR.Partitions.Count); - for (int i = 0; i < xMBR.Partitions.Count; i++) - { - var xPart = xMBR.Partitions[i]; - if (xPart == null) - { - Console.WriteLine("Null partition found at idx: " + i); } - else + + public static void EnableInterrupts() { - var xPartDevice = new Partition(xATA, xPart.StartSector, xPart.SectorCount); - BlockDevice.BlockDevice.Devices.Add(xPartDevice); - Console.WriteLine("Found partition at idx" + i); + CPU.EnableInterrupts(); } - } + + public static bool InterruptsEnabled => CPU.mInterruptsEnabled; } - - public static bool InterruptsEnabled => CPU.mInterruptsEnabled; - } } diff --git a/source/Cosmos.HAL2/PCI.cs b/source/Cosmos.HAL2/PCI.cs index 200b3a09a..88bcd0e75 100644 --- a/source/Cosmos.HAL2/PCI.cs +++ b/source/Cosmos.HAL2/PCI.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -6,6 +6,70 @@ using Cosmos.Debug.Kernel; namespace Cosmos.HAL { + public enum ClassID + { + PCIDevice_2_0 = 0x00, + MassStorageController = 0x01, + NetworkController = 0x02, + DisplayController = 0x03, + MultimediaDevice = 0x04, + MemoryController = 0x05, + BridgeDevice = 0x06, + SimpleCommController = 0x07, + BaseSystemPreiph = 0x08, + InputDevice = 0x09, + DockingStations = 0x0A, + Proccesors = 0x0B, + SerialBusController = 0x0C, + WirelessController = 0x0D, + InteligentController = 0x0E, + SateliteCommController = 0x0F, + EncryptionController = 0x10, + SignalProcessingController = 0x11, + ProcessingAccelerators = 0x12, + NonEssentialInstsrumentation = 0x13, + Coprocessor = 0x40, + Unclassified = 0xFF + } + + public enum SubclassID + { + // MassStorageController: + SCSIStorageController = 0x00, + IDEInterface = 0x01, + FloppyDiskController = 0x02, + IPIBusController = 0x03, + RAIDController = 0x04, + ATAController = 0x05, + SATAController = 0x06, + SASController = 0x07, + NVMController = 0x08, + UnknownMassStorage = 0x09, + } + + public enum ProgramIF + { + // MassStorageController: + SATA_VendorSpecific = 0x00, + SATA_AHCI = 0x01, + SATA_SerialStorageBus = 0x02, + SAS_SerialStorageBus = 0x01, + NVM_NVMHCI = 0x01, + NVM_NVMExpress = 0x02 + } + + public enum VendorID + { + Intel = 0x8086, + AMD = 0x0438, + VMWare = 0x15AD + } + + public enum DeviceID + { + SVGAIIAdapter = 0x0405 + } + public class PCI { private static List Devices; @@ -61,12 +125,13 @@ namespace Cosmos.HAL CheckBus(xPCIDevice.SecondaryBusNumber); } - public static PCIDevice GetDevice(ushort VendorID, ushort DeviceID) + public static PCIDevice GetDevice(VendorID aVendorID, DeviceID aDeviceID) { for (int i = 0; i < Devices.Count; i++) { var xDevice = Devices[i]; - if (xDevice.VendorID == VendorID && xDevice.DeviceID == DeviceID) + if ((VendorID)xDevice.VendorID == aVendorID && + (DeviceID)xDevice.DeviceID == aDeviceID) { return Devices[i]; } @@ -74,12 +139,28 @@ namespace Cosmos.HAL return null; } - public static PCIDevice GetDeviceClass(ushort Class, ushort SubClass) + public static PCIDevice GetDeviceClass(ClassID Class, SubclassID SubClass) { for (int i = 0; i < Devices.Count; i++) { var xDevice = Devices[i]; - if (xDevice.ClassCode == Class && xDevice.Subclass == SubClass) + if ((ClassID)xDevice.ClassCode == Class && + (SubclassID)xDevice.Subclass == SubClass) + { + return Devices[i]; + } + } + return null; + } + + public static PCIDevice GetDeviceClass(ClassID aClass, SubclassID aSubClass, ProgramIF aProgIF) + { + for (int i = 0; i < Devices.Count; i++) + { + var xDevice = Devices[i]; + if ((ClassID)xDevice.ClassCode == aClass && + (SubclassID)xDevice.Subclass == aSubClass && + (ProgramIF)xDevice.ProgIF == aProgIF) { return Devices[i]; } diff --git a/source/Cosmos.HAL2/PciDevice.cs b/source/Cosmos.HAL2/PciDevice.cs index 572a6c928..6419beceb 100644 --- a/source/Cosmos.HAL2/PciDevice.cs +++ b/source/Cosmos.HAL2/PciDevice.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -198,6 +198,20 @@ namespace Cosmos.HAL WriteRegister16(0x04, command); } + public void EnableBusMaster(bool enable) + { + UInt16 command = ReadRegister16(0x04); + + UInt16 flags = (1 << 2); + + if (enable) + command |= flags; + else + command &= (ushort)~flags; + + WriteRegister16(0x04, command); + } + public class DeviceClass { public static string GetString(PCIDevice device) @@ -345,4 +359,4 @@ namespace Cosmos.HAL get { return isIO; } } } -} \ No newline at end of file +}