X-Git-Url: https://git.ucc.asn.au/?a=blobdiff_plain;ds=sidebyside;f=Modules%2FATA%2Fmain.c;fp=Modules%2FATA%2Fmain.c;h=13e974aa25cf9ef0bc0e3c1c58a384e9ee04ef89;hb=b289025913a0e4573181155aeeaab37ed92cf525;hp=0000000000000000000000000000000000000000;hpb=6249939b47bd8520ef4c1b8fdb8eeb11cc052d57;p=tpg%2Facess2.git diff --git a/Modules/ATA/main.c b/Modules/ATA/main.c new file mode 100644 index 00000000..13e974aa --- /dev/null +++ b/Modules/ATA/main.c @@ -0,0 +1,1062 @@ +/* + * Acess2 IDE Harddisk Driver + * - main.c + */ +#define DEBUG 0 +#include +#include +#include +#include +#include +#include +#include + +// --- Flags --- +#define START_BEFORE_CMD 0 + +// === CONSTANTS === +#define MAX_ATA_DISKS 4 +#define SECTOR_SIZE 512 +#define MAX_DMA_SECTORS (0x1000 / SECTOR_SIZE) + +#define IDE_PRI_BASE 0x1F0 +#define IDE_SEC_BASE 0x170 + +#define IDE_PRDT_LAST 0x8000 +/** + \enum HddControls + \brief Commands to be sent to HDD_CMD +*/ +enum HddControls { + HDD_PIO_R28 = 0x20, + HDD_PIO_R48 = 0x24, + HDD_DMA_R48 = 0x25, + HDD_PIO_W28 = 0x30, + HDD_PIO_W48 = 0x34, + HDD_DMA_W48 = 0x35, + HDD_DMA_R28 = 0xC8, + HDD_DMA_W28 = 0xCA, +}; + +// === STRUCTURES === +typedef struct { + Uint32 PBufAddr; + Uint16 Bytes; + Uint16 Flags; +} tPRDT_Ent; +typedef struct { + Uint16 Flags; // 1 + Uint16 Usused1[9]; // 10 + char SerialNum[20]; // 20 + Uint16 Usused2[3]; // 23 + char FirmwareVer[8]; // 27 + char ModelNumber[40]; // 47 + Uint16 SectPerInt; // 48 - and with 0xFF to get true value; + Uint16 Unused3; // 49 + Uint16 Capabilities[2]; // 51 + Uint16 Unused4[2]; // 53 + Uint16 ValidExtData; // 54 + Uint16 Unused5[5]; // 59 + Uint16 SizeOfRWMultiple; // 60 + Uint32 Sectors28; // 62 + Uint16 Unused6[100-62]; + Uint64 Sectors48; + Uint16 Unused7[256-104]; +} tIdentify; +typedef struct { + Uint8 BootCode[0x1BE]; + struct { + Uint8 Boot; + Uint8 Unused1; // Also CHS Start + Uint16 StartHi; // Also CHS Start + Uint8 SystemID; + Uint8 Unused2; // Also CHS Length + Uint16 LengthHi; // Also CHS Length + Uint32 LBAStart; + Uint32 LBALength; + } __attribute__ ((packed)) Parts[4]; + Uint16 BootFlag; // = 0xAA 55 +} __attribute__ ((packed)) tMBR; + +typedef struct { + Uint64 Start; + Uint64 Length; + char Name[4]; + tVFS_Node Node; +} tATA_Partition; +typedef struct { + Uint64 Sectors; + char Name[2]; + tVFS_Node Node; + int NumPartitions; + tATA_Partition *Partitions; +} tATA_Disk; + +// === PROTOTYPES === + int ATA_Install(); + int ATA_SetupIO(); +void ATA_SetupPartitions(); +void ATA_SetupVFS(); + int ATA_ScanDisk(int Disk); +void ATA_ParseGPT(int Disk); +void ATA_ParseMBR(int Disk); +void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length); +Uint16 ATA_GetBasePort(int Disk); +// Filesystem Interface +char *ATA_ReadDir(tVFS_Node *Node, int Pos); +tVFS_Node *ATA_FindDir(tVFS_Node *Node, char *Name); +Uint64 ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); +Uint64 ATA_WriteFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer); + int ATA_IOCtl(tVFS_Node *Node, int Id, void *Data); +// Read/Write Interface/Quantiser +Uint ATA_ReadRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk); +Uint ATA_WriteRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk); +// Read/Write DMA + int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer); + int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer); +// IRQs +void ATA_IRQHandlerPri(int unused); +void ATA_IRQHandlerSec(int unused); +// Controller IO +Uint8 ATA_int_BusMasterReadByte(int Ofs); +void ATA_int_BusMasterWriteByte(int Ofs, Uint8 Value); +void ATA_int_BusMasterWriteDWord(int Ofs, Uint32 Value); + +// === GLOBALS === +MODULE_DEFINE(0, 0x0032, i386ATA, ATA_Install, NULL, NULL); +tDevFS_Driver gATA_DriverInfo = { + NULL, "ata", + { + .NumACLs = 1, + .Size = -1, + .Flags = VFS_FFLAG_DIRECTORY, + .ACLs = &gVFS_ACL_EveryoneRX, + .ReadDir = ATA_ReadDir, + .FindDir = ATA_FindDir + } +}; +tATA_Disk gATA_Disks[MAX_ATA_DISKS]; + int giATA_NumNodes; +tVFS_Node **gATA_Nodes; +Uint16 gATA_BusMasterBase = 0; +Uint8 *gATA_BusMasterBasePtr = 0; + int gATA_IRQPri = 14; + int gATA_IRQSec = 15; + int giaATA_ControllerLock[2] = {0}; //!< Spinlocks for each controller +Uint8 gATA_Buffers[2][4096] __attribute__ ((section(".padata"))); + int gaATA_IRQs[2] = {0}; +tPRDT_Ent gATA_PRDTs[2] = { + {0, 512, IDE_PRDT_LAST}, + {0, 512, IDE_PRDT_LAST} +}; + +// === CODE === +/** + * \fn int ATA_Install() + */ +int ATA_Install() +{ + int ret; + + ret = ATA_SetupIO(); + if(ret != 1) return ret; + + ATA_SetupPartitions(); + + ATA_SetupVFS(); + + if( DevFS_AddDevice( &gATA_DriverInfo ) == 0 ) + return MODULE_INIT_FAILURE; + + return MODULE_INIT_SUCCESS; +} + +/** + * \fn int ATA_SetupIO() + * \brief Sets up the ATA controller's DMA mode + */ +int ATA_SetupIO() +{ + int ent; + tPAddr addr; + + ENTER(""); + + // Get IDE Controller's PCI Entry + ent = PCI_GetDeviceByClass(0x0101, 0xFFFF, -1); + LOG("ent = %i", ent); + gATA_BusMasterBase = PCI_GetBAR4( ent ); + if( gATA_BusMasterBase == 0 ) { + Warning("It seems that there is no Bus Master Controller on this machine. Get one"); + LEAVE('i', MODULE_INIT_FAILURE); + return MODULE_INIT_FAILURE; + } + if( !(gATA_BusMasterBase & 1) ) + { + if( gATA_BusMasterBase < 0x100000 ) + gATA_BusMasterBasePtr = (void*)(0xC0000000|gATA_BusMasterBase); + else + gATA_BusMasterBasePtr = (void*)( MM_MapHWPage( gATA_BusMasterBase, 1 ) + (gATA_BusMasterBase&0xFFF) ); + LOG("gATA_BusMasterBasePtr = %p", gATA_BusMasterBasePtr); + } + else { + // Bit 0 is left set as a flag to other functions + LOG("gATA_BusMasterBase = 0x%x", gATA_BusMasterBase & ~1); + } + + IRQ_AddHandler( gATA_IRQPri, ATA_IRQHandlerPri ); + IRQ_AddHandler( gATA_IRQSec, ATA_IRQHandlerSec ); + + gATA_PRDTs[0].PBufAddr = MM_GetPhysAddr( (Uint)&gATA_Buffers[0] ); + gATA_PRDTs[1].PBufAddr = MM_GetPhysAddr( (Uint)&gATA_Buffers[1] ); + + LOG("gATA_PRDTs = {PBufAddr: 0x%x, PBufAddr: 0x%x}", gATA_PRDTs[0].PBufAddr, gATA_PRDTs[1].PBufAddr); + + addr = MM_GetPhysAddr( (Uint)&gATA_PRDTs[0] ); + LOG("addr = 0x%x", addr); + ATA_int_BusMasterWriteDWord(4, addr); + addr = MM_GetPhysAddr( (Uint)&gATA_PRDTs[1] ); + LOG("addr = 0x%x", addr); + ATA_int_BusMasterWriteDWord(12, addr); + + outb(IDE_PRI_BASE+1, 1); + outb(IDE_SEC_BASE+1, 1); + + LEAVE('i', MODULE_INIT_SUCCESS); + return MODULE_INIT_SUCCESS; +} + +/** + * \fn void ATA_SetupPartitions() + */ +void ATA_SetupPartitions() +{ + int i; + for( i = 0; i < MAX_ATA_DISKS; i ++ ) + { + if( !ATA_ScanDisk(i) ) { + gATA_Disks[i].Name[0] = '\0'; // Mark as unused + continue; + } + } +} + +/** + * \fn void ATA_SetupVFS() + * \brief Sets up the ATA drivers VFS information and registers with DevFS + */ +void ATA_SetupVFS() +{ + int i, j, k; + + // Count number of nodes needed + giATA_NumNodes = 0; + for( i = 0; i < MAX_ATA_DISKS; i++ ) + { + if(gATA_Disks[i].Name[0] == '\0') continue; // Ignore + giATA_NumNodes ++; + giATA_NumNodes += gATA_Disks[i].NumPartitions; + } + + // Allocate Node space + gATA_Nodes = malloc( giATA_NumNodes * sizeof(void*) ); + + // Set nodes + k = 0; + for( i = 0; i < MAX_ATA_DISKS; i++ ) + { + if(gATA_Disks[i].Name[0] == '\0') continue; // Ignore + gATA_Nodes[ k++ ] = &gATA_Disks[i].Node; + for( j = 0; j < gATA_Disks[i].NumPartitions; j ++ ) + gATA_Nodes[ k++ ] = &gATA_Disks[i].Partitions[j].Node; + } + + gATA_DriverInfo.RootNode.Size = giATA_NumNodes; +} + +/** + * \fn int ATA_ScanDisk(int Disk) + */ +int ATA_ScanDisk(int Disk) +{ + Uint16 buf[256]; + tIdentify *identify = (void*)buf; + tMBR *mbr = (void*)buf; + Uint16 base; + Uint8 val; + int i; + tVFS_Node *node; + + ENTER("iDisk", Disk); + + base = ATA_GetBasePort( Disk ); + + LOG("base = 0x%x", base); + + // Send Disk Selector + if(Disk == 1 || Disk == 3) + outb(base+6, 0xB0); + else + outb(base+6, 0xA0); + + // Send IDENTIFY + outb(base+7, 0xEC); + val = inb(base+7); // Read status + if(val == 0) { + LEAVE('i', 0); + return 0; // Disk does not exist + } + + // Poll until BSY clears and DRQ sets or ERR is set + while( ((val & 0x80) || !(val & 0x08)) && !(val & 1)) val = inb(base+7); + + if(val & 1) { + LEAVE('i', 0); + return 0; // Error occured, so return false + } + + // Read Data + for(i=0;i<256;i++) buf[i] = inw(base); + + // Populate Disk Structure + if(identify->Sectors48 != 0) + gATA_Disks[ Disk ].Sectors = identify->Sectors48; + else + gATA_Disks[ Disk ].Sectors = identify->Sectors28; + + + LOG("gATA_Disks[ Disk ].Sectors = 0x%x", gATA_Disks[ Disk ].Sectors); + + if( gATA_Disks[ Disk ].Sectors / (2048*1024) ) + Log("Disk %i: 0x%llx Sectors (%i GiB)", Disk, + gATA_Disks[ Disk ].Sectors, gATA_Disks[ Disk ].Sectors / (2048*1024)); + else if( gATA_Disks[ Disk ].Sectors / 2048 ) + Log("Disk %i: 0x%llx Sectors (%i MiB)", Disk, + gATA_Disks[ Disk ].Sectors, gATA_Disks[ Disk ].Sectors / 2048); + else + Log("Disk %i: 0x%llx Sectors (%i KiB)", Disk, + gATA_Disks[ Disk ].Sectors, gATA_Disks[ Disk ].Sectors / 2); + + // Create Name + gATA_Disks[ Disk ].Name[0] = 'A'+Disk; + gATA_Disks[ Disk ].Name[1] = '\0'; + + // Get pointer to vfs node and populate it + node = &gATA_Disks[ Disk ].Node; + node->Size = gATA_Disks[Disk].Sectors * SECTOR_SIZE; + node->NumACLs = 0; // Means Superuser only can access it + node->Inode = (Disk << 8) | 0xFF; + node->ImplPtr = gATA_Disks[ Disk ].Name; + + node->ATime = node->MTime + = node->CTime = now(); + + node->Read = ATA_ReadFS; + node->Write = ATA_WriteFS; + node->IOCtl = ATA_IOCtl; + + + // --- Scan Partitions --- + LOG("Reading MBR"); + // Read Boot Sector + ATA_ReadDMA( Disk, 0, 1, mbr ); + + // Check for a GPT table + if(mbr->Parts[0].SystemID == 0xEE) + ATA_ParseGPT(Disk); + else // No? Just parse the MBR + ATA_ParseMBR(Disk); + + LEAVE('i', 0); + return 1; +} + +/** + * \fn void ATA_ParseGPT(int Disk) + * \brief Parses the GUID Partition Table + */ +void ATA_ParseGPT(int Disk) +{ + ///\todo Support GPT Disks + Warning("GPT Disks are currently unsupported"); +} + +/** + * \fn void ATA_ParseMBR(int Disk) + */ +void ATA_ParseMBR(int Disk) +{ + int i, j = 0, k = 4; + tMBR mbr; + Uint64 extendedLBA; + + ENTER("iDisk", Disk); + + // Read Boot Sector + ATA_ReadDMA( Disk, 0, 1, &mbr ); + + // Count Partitions + gATA_Disks[Disk].NumPartitions = 0; + extendedLBA = 0; + for( i = 0; i < 4; i ++ ) + { + if( mbr.Parts[i].SystemID == 0 ) continue; + if( + mbr.Parts[i].Boot == 0x0 || mbr.Parts[i].Boot == 0x80 // LBA 28 + || mbr.Parts[i].Boot == 0x1 || mbr.Parts[i].Boot == 0x81 // LBA 48 + ) + { + if( mbr.Parts[i].SystemID == 0xF || mbr.Parts[i].SystemID == 5 ) { + LOG("Extended Partition"); + if(extendedLBA != 0) { + Warning("Disk %i has multiple extended partitions, ignoring rest", Disk); + continue; + } + extendedLBA = mbr.Parts[i].LBAStart; + continue; + } + LOG("Primary Partition"); + + gATA_Disks[Disk].NumPartitions ++; + continue; + } + // Invalid Partition, so don't count it + } + while(extendedLBA != 0) + { + if( ATA_ReadDMA( Disk, extendedLBA, 1, &mbr ) != 0 ) + break; // Stop on Errors + + extendedLBA = 0; + + if( mbr.Parts[0].SystemID == 0 ) continue; + if( mbr.Parts[0].Boot == 0x0 || mbr.Parts[0].Boot == 0x80 // LBA 28 + || mbr.Parts[0].Boot == 0x1 || mbr.Parts[0].Boot == 0x81 // LBA 48 + ) + { + if(mbr.Parts[0].SystemID == 0xF || mbr.Parts[0].SystemID == 0x7) + extendedLBA = mbr.Parts[0].LBAStart; + else + gATA_Disks[Disk].NumPartitions ++; + } + + if( mbr.Parts[1].SystemID == 0 ) continue; + if( mbr.Parts[1].Boot == 0x0 || mbr.Parts[1].Boot == 0x80 // LBA 28 + || mbr.Parts[1].Boot == 0x1 || mbr.Parts[1].Boot == 0x81 // LBA 48 + ) + { + if(mbr.Parts[1].SystemID == 0xF || mbr.Parts[1].SystemID == 0x7) { + if(extendedLBA == 0) { + Warning("Disk %i has twp forward link in the extended partition", + Disk); + break; + } + extendedLBA = mbr.Parts[1].LBAStart; + } + else { + if(extendedLBA != 0) { + Warning("Disk %i lacks a forward link in the extended partition", + Disk); + break; + } + gATA_Disks[Disk].NumPartitions ++; + } + } + } + LOG("gATA_Disks[Disk].NumPartitions = %i", gATA_Disks[Disk].NumPartitions); + + // Create patition array + gATA_Disks[Disk].Partitions = malloc( gATA_Disks[Disk].NumPartitions * sizeof(tATA_Partition) ); + + // --- Fill Partition Info --- + extendedLBA = 0; + for( i = 0; i < 4; i ++ ) + { + Log("mbr.Parts[%i].SystemID = 0x%02x", i, mbr.Parts[i].SystemID); + if( mbr.Parts[i].SystemID == 0 ) continue; + if( mbr.Parts[i].Boot == 0x0 || mbr.Parts[i].Boot == 0x80 ) // LBA 28 + { + if( mbr.Parts[1].SystemID == 0xF || mbr.Parts[1].SystemID == 5 ) { + if(extendedLBA != 0) { + Warning("Disk %i has multiple extended partitions, ignoring rest", Disk); + continue; + } + extendedLBA = mbr.Parts[1].LBAStart; + continue; + } + // Create Partition + ATA_int_MakePartition( &gATA_Disks[Disk].Partitions[j], Disk, i, + mbr.Parts[i].LBAStart, mbr.Parts[i].LBALength + ); + j ++; + continue; + } + if( mbr.Parts[i].Boot == 0x1 || mbr.Parts[i].Boot == 0x81 ) // LBA 48 + { + if( mbr.Parts[i].SystemID == 0xF || mbr.Parts[i].SystemID == 5 ) { + if(extendedLBA != 0) { + Warning("Disk %i has multiple extended partitions, ignoring rest", Disk); + continue; + } + extendedLBA = (mbr.Parts[i].StartHi << 16) | mbr.Parts[i].LBAStart; + continue; + } + ATA_int_MakePartition( &gATA_Disks[Disk].Partitions[j], Disk, i, + (mbr.Parts[i].StartHi << 16) | mbr.Parts[i].LBAStart, + (mbr.Parts[i].LengthHi << 16) | mbr.Parts[i].LBALength + ); + j ++; + } + // Invalid Partition, so don't count it + } + // Scan extended partition + while(extendedLBA != 0) + { + if( ATA_ReadDMA( Disk, extendedLBA, 1, &mbr ) != 0 ) + break; // Stop on Errors + + extendedLBA = 0; + + // Check first entry (should be partition) + if( mbr.Parts[0].SystemID != 0) + { + if( mbr.Parts[0].Boot == 0x0 || mbr.Parts[0].Boot == 0x80 ) // LBA 28 + { + // Forward Link to next Extended partition entry + if(mbr.Parts[0].SystemID == 0xF || mbr.Parts[0].SystemID == 0x7) + extendedLBA = mbr.Parts[0].LBAStart; + else { + ATA_int_MakePartition( &gATA_Disks[Disk].Partitions[j], Disk, k, + mbr.Parts[0].LBAStart, mbr.Parts[0].LBALength + ); + j ++; k ++; + } + } + else if( mbr.Parts[0].Boot == 0x1 || mbr.Parts[0].Boot == 0x81 ) // LBA 48 + { + if(mbr.Parts[0].SystemID == 0xF || mbr.Parts[0].SystemID == 0x7) + extendedLBA = (mbr.Parts[0].StartHi << 16) | mbr.Parts[0].LBAStart; + else { + ATA_int_MakePartition( &gATA_Disks[Disk].Partitions[j], Disk, k, + (mbr.Parts[0].StartHi << 16) | mbr.Parts[0].LBAStart, + (mbr.Parts[0].LengthHi << 16) | mbr.Parts[0].LBALength + ); + j ++; k ++; + } + } + } + + // Check second entry (should be forward link) + if( mbr.Parts[1].SystemID != 0) + { + if(mbr.Parts[1].Boot == 0x0 || mbr.Parts[1].Boot == 0x80 ) // LBA 28 + { + if(mbr.Parts[1].SystemID == 0xF || mbr.Parts[1].SystemID == 0x7) { + if(extendedLBA == 0) { + Warning("Disk %i has twp forward link in the extended partition", + Disk); + break; + } + extendedLBA = mbr.Parts[1].LBAStart; + } + else + { + if(extendedLBA != 0) { + Warning("Disk %i lacks a forward link in the extended partition", + Disk); + break; + } + ATA_int_MakePartition( &gATA_Disks[Disk].Partitions[j], Disk, k, + mbr.Parts[1].LBAStart, mbr.Parts[1].LBALength + ); + j ++; k ++; + } + + } + else if( mbr.Parts[1].Boot == 0x1 || mbr.Parts[1].Boot == 0x81 ) // LBA 48 + { + if(mbr.Parts[1].SystemID == 0xF || mbr.Parts[1].SystemID == 0x7) { + if(extendedLBA == 0) { + Warning("Disk %i has twp forward link in the extended partition", + Disk); + break; + } + extendedLBA = (mbr.Parts[1].StartHi << 16) | mbr.Parts[1].LBAStart; + } + else + { + if(extendedLBA != 0) { + Warning("Disk %i lacks a forward link in the extended partition", + Disk); + break; + } + ATA_int_MakePartition( &gATA_Disks[Disk].Partitions[j], Disk, k, + (mbr.Parts[1].StartHi << 16) | mbr.Parts[1].LBAStart, + (mbr.Parts[1].LengthHi << 16) | mbr.Parts[1].LBALength + ); + j ++; k ++; + } + } + } + } + + LEAVE('-'); +} + +/** + * \fn void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length) + * \brief Fills a parition's information structure + */ +void ATA_int_MakePartition(tATA_Partition *Part, int Disk, int Num, Uint64 Start, Uint64 Length) +{ + ENTER("pPart iDisk iNum XStart XLength", Part, Disk, Num, Start, Length); + Part->Start = Start; + Part->Length = Length; + Part->Name[0] = 'A'+Disk; + if(Num >= 10) { + Part->Name[1] = '1'+Num/10; + Part->Name[2] = '1'+Num%10; + Part->Name[3] = '\0'; + } else { + Part->Name[1] = '1'+Num; + Part->Name[2] = '\0'; + } + Part->Node.NumACLs = 0; // Only root can read/write raw block devices + Part->Node.Inode = (Disk << 8) | Num; + Part->Node.ImplPtr = Part->Name; + + Part->Node.Read = ATA_ReadFS; + Part->Node.Write = ATA_WriteFS; + Part->Node.IOCtl = ATA_IOCtl; + LOG("Made '%s' (&Node=%p)", Part->Name, &Part->Node); + LEAVE('-'); +} + +/** + * \fn Uint16 ATA_GetPortBase(int Disk) + * \brief Returns the base port for a given disk + */ +Uint16 ATA_GetBasePort(int Disk) +{ + switch(Disk) + { + case 0: case 1: return IDE_PRI_BASE; + case 2: case 3: return IDE_SEC_BASE; + } + return 0; +} + +/** + * \fn char *ATA_ReadDir(tVFS_Node *Node, int Pos) + */ +char *ATA_ReadDir(tVFS_Node *Node, int Pos) +{ + if(Pos >= giATA_NumNodes || Pos < 0) return NULL; + return strdup( gATA_Nodes[Pos]->ImplPtr ); +} + +/** + * \fn tVFS_Node *ATA_FindDir(tVFS_Node *Node, char *Name) + */ +tVFS_Node *ATA_FindDir(tVFS_Node *Node, char *Name) +{ + int part; + // Check first character + if(Name[0] < 'A' || Name[0] > 'A'+MAX_ATA_DISKS) + return NULL; + // Raw Disk + if(Name[1] == '\0') { + if( gATA_Disks[Name[0]-'A'].Sectors == 0 ) + return NULL; + return &gATA_Disks[Name[0]-'A'].Node; + } + + // Partitions + if(Name[1] < '0' || '9' < Name[1]) return NULL; + if(Name[2] == '\0') { // <= 9 + part = Name[1] - '0'; + part --; + return &gATA_Disks[Name[0]-'A'].Partitions[part].Node; + } + // > 9 + if('0' > Name[2] || '9' < Name[2]) return NULL; + if(Name[3] != '\0') return NULL; + + part = (Name[1] - '0') * 10; + part += Name[2] - '0'; + part --; + return &gATA_Disks[Name[0]-'A'].Partitions[part].Node; + +} + +/** + * \fn Uint64 ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + */ +Uint64 ATA_ReadFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + int disk = Node->Inode >> 8; + int part = Node->Inode & 0xFF; + + // Raw Disk Access + if(part == 0xFF) + { + if( Offset >= gATA_Disks[disk].Sectors * SECTOR_SIZE ) + return 0; + if( Offset + Length > gATA_Disks[disk].Sectors*SECTOR_SIZE ) + Length = gATA_Disks[disk].Sectors*SECTOR_SIZE - Offset; + } + // Partition + else + { + if( Offset >= gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE ) + return 0; + if( Offset + Length > gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE ) + Length = gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE - Offset; + Offset += gATA_Disks[disk].Partitions[part].Start * SECTOR_SIZE; + } + + //Log("ATA_ReadFS: (Node=%p, Offset=0x%llx, Length=0x%llx, Buffer=%p)", Node, Offset, Length, Buffer); + return DrvUtil_ReadBlock(Offset, Length, Buffer, ATA_ReadRaw, SECTOR_SIZE, disk); +} + +/** + * \fn Uint64 ATA_WriteFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) + */ +Uint64 ATA_WriteFS(tVFS_Node *Node, Uint64 Offset, Uint64 Length, void *Buffer) +{ + int disk = Node->Inode >> 8; + int part = Node->Inode & 0xFF; + + // Raw Disk Access + if(part == 0xFF) + { + if( Offset >= gATA_Disks[disk].Sectors * SECTOR_SIZE ) + return 0; + if( Offset + Length > gATA_Disks[disk].Sectors*SECTOR_SIZE ) + Length = gATA_Disks[disk].Sectors*SECTOR_SIZE - Offset; + } + // Partition + else + { + if( Offset >= gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE ) + return 0; + if( Offset + Length > gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE ) + Length = gATA_Disks[disk].Partitions[part].Length * SECTOR_SIZE - Offset; + Offset += gATA_Disks[disk].Partitions[part].Start * SECTOR_SIZE; + } + + Log("ATA_WriteFS: (Node=%p, Offset=0x%llx, Length=0x%llx, Buffer=%p)", Node, Offset, Length, Buffer); + Debug_HexDump("ATA_WriteFS", Buffer, Length); + return DrvUtil_WriteBlock(Offset, Length, Buffer, ATA_ReadRaw, ATA_WriteRaw, SECTOR_SIZE, disk); +} + +/** + * \fn int ATA_IOCtl(tVFS_Node *Node, int Id, void *Data) + * \brief IO Control Funtion + */ +int ATA_IOCtl(tVFS_Node *Node, int Id, void *Data) +{ + switch(Id) + { + case DRV_IOCTL_TYPE: return DRV_TYPE_DISK; + } + return 0; +} + +// --- Disk Access --- +/** + * \fn Uint ATA_ReadRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk) + */ +Uint ATA_ReadRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk) +{ + int ret; + Uint offset; + Uint done = 0; + + // Pass straight on to ATA_ReadDMAPage if we can + if(Count <= MAX_DMA_SECTORS) + { + ret = ATA_ReadDMA(Disk, Address, Count, Buffer); + if(ret == 0) return 0; + return Count; + } + + // Else we will have to break up the transfer + offset = 0; + while(Count > MAX_DMA_SECTORS) + { + ret = ATA_ReadDMA(Disk, Address+offset, MAX_DMA_SECTORS, Buffer+offset); + // Check for errors + if(ret != 1) return done; + // Change Position + done += MAX_DMA_SECTORS; + Count -= MAX_DMA_SECTORS; + offset += MAX_DMA_SECTORS*SECTOR_SIZE; + } + + ret = ATA_ReadDMA(Disk, Address+offset, Count, Buffer+offset); + if(ret != 1) return 0; + return done+Count; +} + +/** + * \fn Uint ATA_WriteRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk) + */ +Uint ATA_WriteRaw(Uint64 Address, Uint Count, void *Buffer, Uint Disk) +{ + int ret; + Uint offset; + Uint done = 0; + + // Pass straight on to ATA_WriteDMA if we can + if(Count <= MAX_DMA_SECTORS) + { + ret = ATA_WriteDMA(Disk, Address, Count, Buffer); + if(ret == 0) return 0; + return Count; + } + + // Else we will have to break up the transfer + offset = 0; + while(Count > MAX_DMA_SECTORS) + { + ret = ATA_WriteDMA(Disk, Address+offset, MAX_DMA_SECTORS, Buffer+offset); + // Check for errors + if(ret != 1) return done; + // Change Position + done += MAX_DMA_SECTORS; + Count -= MAX_DMA_SECTORS; + offset += MAX_DMA_SECTORS*SECTOR_SIZE; + } + + ret = ATA_WriteDMA(Disk, Address+offset, Count, Buffer+offset); + if(ret != 1) return 0; + return done+Count; +} + +/** + * \fn int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer) + */ +int ATA_ReadDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer) +{ + int cont = (Disk>>1)&1; // Controller ID + int disk = Disk & 1; + Uint16 base; + + ENTER("iDisk XAddress iCount pBuffer", Disk, Address, Count, Buffer); + + // Check if the count is small enough + if(Count > MAX_DMA_SECTORS) { + Warning("Passed too many sectors for a bulk DMA read (%i > %i)", + Count, MAX_DMA_SECTORS); + LEAVE('i'); + return 0; + } + + // Get exclusive access to the disk controller + LOCK( &giaATA_ControllerLock[ cont ] ); + + // Set Size + gATA_PRDTs[ cont ].Bytes = Count * SECTOR_SIZE; + + // Get Port Base + base = ATA_GetBasePort(Disk); + + // Reset IRQ Flag + gaATA_IRQs[cont] = 0; + + // Set up transfer + outb(base+0x01, 0x00); + if( Address > 0x0FFFFFFF ) // Use LBA48 + { + outb(base+0x6, 0x40 | (disk << 4)); + outb(base+0x2, 0 >> 8); // Upper Sector Count + outb(base+0x3, Address >> 24); // Low 2 Addr + outb(base+0x3, Address >> 28); // Mid 2 Addr + outb(base+0x3, Address >> 32); // High 2 Addr + } + else + { + outb(base+0x06, 0xE0 | (disk << 4) | ((Address >> 24) & 0x0F)); //Disk,Magic,High addr + } + + outb(base+0x02, (Uint8) Count); // Sector Count + outb(base+0x03, (Uint8) Address); // Low Addr + outb(base+0x04, (Uint8) (Address >> 8)); // Middle Addr + outb(base+0x05, (Uint8) (Address >> 16)); // High Addr + + LOG("Starting Transfer"); + #if START_BEFORE_CMD + // Start transfer + ATA_int_BusMasterWriteByte( cont << 3, 9 ); // Read and start + if( Address > 0x0FFFFFFF ) + outb(base+0x07, HDD_DMA_R48); // Read Command (LBA48) + else + outb(base+0x07, HDD_DMA_R28); // Read Command (LBA28) + #else + if( Address > 0x0FFFFFFF ) + outb(base+0x07, HDD_DMA_R48); // Read Command (LBA48) + else + outb(base+0x07, HDD_DMA_R28); // Read Command (LBA28) + // Start transfer + ATA_int_BusMasterWriteByte( cont << 3, 9 ); // Read and start + #endif + + // Wait for transfer to complete + //ATA_int_BusMasterWriteByte( (cont << 3) + 2, 0x4 ); + while( gaATA_IRQs[cont] == 0 ) { + //Uint8 val = ATA_int_BusMasterReadByte( (cont << 3) + 2, 0x4 ); + //LOG("val = 0x%02x", val); + Threads_Yield(); + } + + // Complete Transfer + ATA_int_BusMasterWriteByte( cont << 3, 0 ); // Write and stop + + LOG("Transfer Completed & Acknowledged"); + + // Copy to destination buffer + memcpy( Buffer, gATA_Buffers[cont], Count*SECTOR_SIZE ); + + // Release controller lock + RELEASE( &giaATA_ControllerLock[ cont ] ); + + LEAVE('i', 1); + return 1; +} + +/** + * \fn int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer) + */ +int ATA_WriteDMA(Uint8 Disk, Uint64 Address, Uint Count, void *Buffer) +{ + int cont = (Disk>>1)&1; // Controller ID + int disk = Disk & 1; + Uint16 base; + + // Check if the count is small enough + if(Count > MAX_DMA_SECTORS) return 0; + + // Get exclusive access to the disk controller + LOCK( &giaATA_ControllerLock[ cont ] ); + + // Set Size + gATA_PRDTs[ cont ].Bytes = Count * SECTOR_SIZE; + + // Get Port Base + base = ATA_GetBasePort(Disk); + + // Set up transfer + outb(base+0x01, 0x00); + if( Address > 0x0FFFFFFF ) // Use LBA48 + { + outb(base+0x6, 0x40 | (disk << 4)); + outb(base+0x2, 0 >> 8); // Upper Sector Count + outb(base+0x3, Address >> 24); // Low 2 Addr + outb(base+0x3, Address >> 28); // Mid 2 Addr + outb(base+0x3, Address >> 32); // High 2 Addr + } + else + { + outb(base+0x06, 0xE0 | (disk << 4) | ((Address >> 24) & 0x0F)); //Disk,Magic,High addr + } + + outb(base+0x02, (Uint8) Count); // Sector Count + outb(base+0x03, (Uint8) Address); // Low Addr + outb(base+0x04, (Uint8) (Address >> 8)); // Middle Addr + outb(base+0x05, (Uint8) (Address >> 16)); // High Addr + if( Address > 0x0FFFFFFF ) + outb(base+0x07, HDD_DMA_W48); // Write Command (LBA48) + else + outb(base+0x07, HDD_DMA_W28); // Write Command (LBA28) + + // Reset IRQ Flag + gaATA_IRQs[cont] = 0; + + // Copy to output buffer + memcpy( gATA_Buffers[cont], Buffer, Count*SECTOR_SIZE ); + + // Start transfer + ATA_int_BusMasterWriteByte( cont << 3, 1 ); // Write and start + + // Wait for transfer to complete + while( gaATA_IRQs[cont] == 0 ) Threads_Yield(); + + // Complete Transfer + ATA_int_BusMasterWriteByte( cont << 3, 0 ); // Write and stop + + // Release controller lock + RELEASE( &giaATA_ControllerLock[ cont ] ); + + return 1; +} + +/** + * \fn void ATA_IRQHandlerPri(int unused) + */ +void ATA_IRQHandlerPri(int unused) +{ + Uint8 val; + + // IRQ bit set for Primary Controller + val = ATA_int_BusMasterReadByte( 0x2 ); + LOG("IRQ val = 0x%x", val); + if(val & 4) { + LOG("IRQ hit (val = 0x%x)", val); + ATA_int_BusMasterWriteByte( 0x2, 4 ); + gaATA_IRQs[0] = 1; + return ; + } +} + +/** + * \fn void ATA_IRQHandlerSec(int unused) + */ +void ATA_IRQHandlerSec(int unused) +{ + Uint8 val; + // IRQ bit set for Secondary Controller + val = ATA_int_BusMasterReadByte( 0xA ); + if(val & 4) { + LOG("IRQ hit (val = 0x%x)", val); + ATA_int_BusMasterWriteByte( 0xA, 4 ); + gaATA_IRQs[1] = 1; + return ; + } +} + +/** + * \fn Uint8 ATA_int_BusMasterReadByte(int Ofs) + */ +Uint8 ATA_int_BusMasterReadByte(int Ofs) +{ + if( gATA_BusMasterBase & 1 ) + return inb( (gATA_BusMasterBase & ~1) + Ofs ); + else + return *(Uint8*)(gATA_BusMasterBasePtr + Ofs); +} + +/** + * \fn void ATA_int_BusMasterWriteByte(int Ofs, Uint8 Value) + * \brief Writes a byte to a Bus Master Register + */ +void ATA_int_BusMasterWriteByte(int Ofs, Uint8 Value) +{ + if( gATA_BusMasterBase & 1 ) + outb( (gATA_BusMasterBase & ~1) + Ofs, Value ); + else + *(Uint8*)(gATA_BusMasterBasePtr + Ofs) = Value; +} + +/** + * \fn void ATA_int_BusMasterWriteDWord(int Ofs, Uint32 Value) + * \brief Writes a dword to a Bus Master Register + */ +void ATA_int_BusMasterWriteDWord(int Ofs, Uint32 Value) +{ + + if( gATA_BusMasterBase & 1 ) + outd( (gATA_BusMasterBase & ~1) + Ofs, Value ); + else + *(Uint32*)(gATA_BusMasterBasePtr + Ofs) = Value; +}