mirror of https://gitlab.com/nakst/essence
608 lines
21 KiB
C++
608 lines
21 KiB
C++
// This file is part of the Essence operating system.
|
|
// It is released under the terms of the MIT license -- see LICENSE.md.
|
|
// Written by: nakst.
|
|
|
|
// TODO Asynchronous timeout.
|
|
// TODO Inserting/removing ATAPI devices.
|
|
|
|
#include <module.h>
|
|
#include <arch/x86_pc.h>
|
|
|
|
#define ATA_BUSES 2
|
|
#define ATA_DRIVES (ATA_BUSES * 2)
|
|
#define ATA_SECTOR_SIZE (512)
|
|
#define ATA_TIMEOUT (10000)
|
|
#define ATAPI_SECTOR_SIZE (2048)
|
|
|
|
#define ATA_REGISTER(_bus, _reg) (_reg != -1 ? ((_bus ? IO_ATA_1 : IO_ATA_2) + _reg) : (_bus ? IO_ATA_3 : IO_ATA_4))
|
|
#define ATA_IRQ(_bus) (_bus ? 15 : 14)
|
|
#define ATA_DATA 0
|
|
#define ATA_FEATURES 1
|
|
#define ATA_SECTOR_COUNT 2
|
|
#define ATA_LBA1 3
|
|
#define ATA_LBA2 4
|
|
#define ATA_LBA3 5
|
|
#define ATA_DRIVE_SELECT 6
|
|
#define ATA_STATUS 7
|
|
#define ATA_COMMAND 7
|
|
#define ATA_DCR -1
|
|
#define ATA_IDENTIFY 0xEC
|
|
#define ATA_IDENTIFY_PACKET 0xA1
|
|
#define ATA_READ_PIO 0x20
|
|
#define ATA_READ_PIO_48 0x24
|
|
#define ATA_READ_DMA 0xC8
|
|
#define ATA_READ_DMA_48 0x25
|
|
#define ATA_WRITE_PIO 0x30
|
|
#define ATA_WRITE_PIO_48 0x34
|
|
#define ATA_PACKET 0xA0
|
|
#define ATA_WRITE_DMA 0xCA
|
|
#define ATA_WRITE_DMA_48 0x35
|
|
|
|
#define DMA_REGISTER(_bus, _reg) 4, ((_bus ? (_reg + 8) : _reg))
|
|
#define DMA_COMMAND 0
|
|
#define DMA_STATUS 2
|
|
#define DMA_PRDT 4
|
|
|
|
struct PRD {
|
|
volatile uint32_t base;
|
|
volatile uint16_t size;
|
|
volatile uint16_t end;
|
|
};
|
|
|
|
struct ATAOperation {
|
|
void *buffer;
|
|
uintptr_t offsetIntoSector, readIndex;
|
|
size_t countBytes, sectorsNeededToLoad;
|
|
uint8_t operation, readingData, bus, slave;
|
|
bool pio;
|
|
};
|
|
|
|
struct ATAController : KDevice {
|
|
void Initialise();
|
|
bool Access(uintptr_t drive, uint64_t sector, size_t count, int operation, uint8_t *buffer); // Returns true on success.
|
|
bool AccessStart(int bus, int slave, uint64_t sector, uintptr_t offsetIntoSector, size_t sectorsNeededToLoad, size_t countBytes, int operation, uint8_t *buffer, bool atapi);
|
|
bool AccessEnd(int bus, int slave);
|
|
|
|
void SetDrive(int bus, int slave, int extra = 0);
|
|
void Unblock();
|
|
|
|
KPCIDevice *pci;
|
|
|
|
uint64_t sectorCount[ATA_DRIVES];
|
|
bool isATAPI[ATA_DRIVES];
|
|
|
|
KSemaphore semaphore;
|
|
|
|
PRD *prdts[ATA_BUSES];
|
|
void *buffers[ATA_BUSES];
|
|
KEvent irqs[ATA_BUSES];
|
|
|
|
uint16_t identifyData[ATA_SECTOR_SIZE / 2];
|
|
|
|
volatile ATAOperation op;
|
|
|
|
KMutex blockedPacketsMutex;
|
|
};
|
|
|
|
static ATAController *ataController;
|
|
|
|
struct ATADrive : KBlockDevice {
|
|
uintptr_t index;
|
|
};
|
|
|
|
void ATAController::SetDrive(int bus, int slave, int extra) {
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_DRIVE_SELECT), extra | 0xA0 | (slave << 4));
|
|
for (int i = 0; i < 4; i++) ProcessorIn8(ATA_REGISTER(bus, ATA_STATUS));
|
|
}
|
|
|
|
bool ATAController::AccessStart(int bus, int slave, uint64_t sector, uintptr_t offsetIntoSector, size_t sectorsNeededToLoad, size_t countBytes,
|
|
int operation, uint8_t *_buffer, bool atapi) {
|
|
uint16_t *buffer = (uint16_t *) _buffer;
|
|
|
|
bool s48 = false;
|
|
|
|
// Start a timeout.
|
|
KTimeout timeout(1000);
|
|
|
|
while ((ProcessorIn8(ATA_REGISTER(bus, ATA_STATUS)) & 0x80) && !timeout.Hit());
|
|
|
|
if (timeout.Hit()) return false;
|
|
|
|
if (atapi) {
|
|
SetDrive(bus, slave);
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_FEATURES), 1); // Using DMA.
|
|
uint32_t maxByteCount = sectorsNeededToLoad * ATAPI_SECTOR_SIZE;
|
|
if (maxByteCount > 65535) KernelPanic("ATAController::AccessStart - Access too large for ATAPI drive (max 64KB).\n");
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_LBA2), maxByteCount & 0xFF);
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_LBA3), (maxByteCount >> 8) & 0xFF);
|
|
} else if (sector >= 0x10000000) {
|
|
s48 = true;
|
|
|
|
SetDrive(bus, slave, 0x40);
|
|
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_SECTOR_COUNT), 0);
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_SECTOR_COUNT), sectorsNeededToLoad);
|
|
|
|
// Set the sector to access.
|
|
// The drive will keep track of the previous and current values of these registers,
|
|
// allowing it to construct a 48-bit sector number.
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_LBA3), sector >> 40);
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_LBA2), sector >> 32);
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_LBA1), sector >> 24);
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_LBA3), sector >> 16);
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_LBA2), sector >> 8);
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_LBA1), sector >> 0);
|
|
} else {
|
|
SetDrive(bus, slave, 0x40 | (sector >> 24));
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_SECTOR_COUNT), sectorsNeededToLoad);
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_LBA3), sector >> 16);
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_LBA2), sector >> 8);
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_LBA1), sector >> 0);
|
|
}
|
|
|
|
KEvent *event = irqs + bus;
|
|
event->autoReset = false;
|
|
KEventReset(event);
|
|
|
|
// Save the operation information.
|
|
op.buffer = buffer;
|
|
op.offsetIntoSector = offsetIntoSector;
|
|
op.countBytes = countBytes;
|
|
op.operation = operation;
|
|
op.readingData = false;
|
|
op.readIndex = 0;
|
|
op.sectorsNeededToLoad = sectorsNeededToLoad;
|
|
op.pio = false;
|
|
op.bus = bus;
|
|
op.slave = slave;
|
|
|
|
{
|
|
// Make sure the previous request has completed.
|
|
ProcessorIn8(ATA_REGISTER(bus, ATA_STATUS));
|
|
pci->ReadBAR8(DMA_REGISTER(bus, DMA_STATUS));
|
|
|
|
// Prepare the PRDT and buffer
|
|
prdts[bus]->size = sectorsNeededToLoad * (atapi ? ATAPI_SECTOR_SIZE : ATA_SECTOR_SIZE);
|
|
if (operation == K_ACCESS_WRITE) EsMemoryCopy((uint8_t *) buffers[bus] + offsetIntoSector, buffer, countBytes);
|
|
|
|
// Set the mode.
|
|
pci->WriteBAR8(DMA_REGISTER(bus, DMA_COMMAND), operation == K_ACCESS_WRITE ? 0 : 8);
|
|
pci->WriteBAR8(DMA_REGISTER(bus, DMA_STATUS), 6);
|
|
|
|
// Wait for the RDY bit to set.
|
|
while (!(ProcessorIn8(ATA_REGISTER(bus, ATA_STATUS)) & (1 << 6)) && !timeout.Hit());
|
|
if (timeout.Hit()) return false;
|
|
|
|
// Issue the command.
|
|
if (atapi) ProcessorOut8(ATA_REGISTER(bus, ATA_COMMAND), ATA_PACKET);
|
|
else if (s48) ProcessorOut8(ATA_REGISTER(bus, ATA_COMMAND), operation == K_ACCESS_READ ? ATA_READ_DMA_48 : ATA_WRITE_DMA_48);
|
|
else ProcessorOut8(ATA_REGISTER(bus, ATA_COMMAND), operation == K_ACCESS_READ ? ATA_READ_DMA : ATA_WRITE_DMA );
|
|
|
|
// Wait for the DRQ bit to set.
|
|
while (!(ProcessorIn8(ATA_REGISTER(bus, ATA_DCR)) & (1 << 3)) && !timeout.Hit());
|
|
|
|
if (timeout.Hit()) return false;
|
|
|
|
if (atapi) {
|
|
uint8_t packet[12] = {};
|
|
packet[0] = 0xA8; // Read sectors.
|
|
packet[2] = (sector >> 0x18) & 0xFF;
|
|
packet[3] = (sector >> 0x10) & 0xFF;
|
|
packet[4] = (sector >> 0x08) & 0xFF;
|
|
packet[5] = (sector >> 0x00) & 0xFF;
|
|
packet[9] = sectorsNeededToLoad;
|
|
|
|
// Wait for the BSY bit to clear.
|
|
while ((ProcessorIn8(ATA_REGISTER(bus, ATA_STATUS)) & (1 << 7)) && !timeout.Hit());
|
|
if (timeout.Hit()) return false;
|
|
|
|
// Send the ATAPI command.
|
|
ProcessorOut16(ATA_REGISTER(bus, ATA_DATA), ((uint16_t *) packet)[0]);
|
|
ProcessorOut16(ATA_REGISTER(bus, ATA_DATA), ((uint16_t *) packet)[1]);
|
|
ProcessorOut16(ATA_REGISTER(bus, ATA_DATA), ((uint16_t *) packet)[2]);
|
|
ProcessorOut16(ATA_REGISTER(bus, ATA_DATA), ((uint16_t *) packet)[3]);
|
|
ProcessorOut16(ATA_REGISTER(bus, ATA_DATA), ((uint16_t *) packet)[4]);
|
|
ProcessorOut16(ATA_REGISTER(bus, ATA_DATA), ((uint16_t *) packet)[5]);
|
|
}
|
|
|
|
pci->WriteBAR8(DMA_REGISTER(bus, DMA_COMMAND), operation == K_ACCESS_WRITE ? 1 : 9);
|
|
if (pci->ReadBAR8(DMA_REGISTER(bus, DMA_STATUS)) & 2) return false;
|
|
if (ProcessorIn8(ATA_REGISTER(bus, ATA_DCR)) & 33) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ATAController::AccessEnd(int bus, int slave) {
|
|
(void) slave;
|
|
KEvent *event = irqs + bus;
|
|
|
|
{
|
|
// Wait for the command to complete.
|
|
KEventWait(event, ATA_TIMEOUT);
|
|
|
|
// Copy the data that we read.
|
|
ATAOperation *op = (ATAOperation *) &ataController->op;
|
|
if (op->buffer && op->operation == K_ACCESS_READ) {
|
|
// EsPrint("copying %d to %x\n", op->countBytes, op->buffer);
|
|
EsMemoryCopy((void *) op->buffer, (uint8_t *) ataController->buffers[op->bus] + op->offsetIntoSector, op->countBytes);
|
|
// EsPrint("done\n");
|
|
}
|
|
|
|
// Check for error.
|
|
if (ProcessorIn8(ATA_REGISTER(bus, ATA_STATUS)) & 33) return false;
|
|
if (pci->ReadBAR8(DMA_REGISTER(bus, DMA_STATUS)) & 3) return false;
|
|
|
|
// Check if the command has completed.
|
|
if (!KEventPoll(event)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
void ATAController::Unblock() {
|
|
KMutexAcquire(&blockedPacketsMutex);
|
|
// EsPrint("unblock!\n");
|
|
KSemaphoreReturn(&semaphore, 1);
|
|
KMutexRelease(&blockedPacketsMutex);
|
|
}
|
|
|
|
bool ATAController::Access(uintptr_t drive, uint64_t offset, size_t countBytes, int operation, uint8_t *_buffer) {
|
|
bool atapi = isATAPI[drive];
|
|
uint64_t sectorSize = atapi ? ATAPI_SECTOR_SIZE : ATA_SECTOR_SIZE;
|
|
uint64_t sector = offset / sectorSize;
|
|
uint64_t offsetIntoSector = offset % sectorSize;
|
|
uint64_t sectorsNeededToLoad = (countBytes + offsetIntoSector + sectorSize - 1) / sectorSize;
|
|
uintptr_t bus = drive >> 1;
|
|
uintptr_t slave = drive & 1;
|
|
|
|
if (drive >= ATA_DRIVES) KernelPanic("ATAController::Access - Drive %d exceedes the maximum number of ATA driver (%d).\n", drive, ATA_DRIVES);
|
|
if (atapi && operation == K_ACCESS_WRITE) KernelPanic("ATAController::Access - Drive %d is an ATAPI drive. ATAPI write operations are currently not supported.\n", drive);
|
|
if (!sectorCount[drive] && !atapi) KernelPanic("ATAController::Access - Drive %d is invalid.\n", drive);
|
|
if ((sector > sectorCount[drive] || (sector + sectorsNeededToLoad) > sectorCount[drive]) && !atapi) KernelPanic("ATAController::Access - Attempt to access sector %d when drive only has %d sectors.\n", sector, sectorCount[drive]);
|
|
if (sectorsNeededToLoad > 64) KernelPanic("ATAController::Access - Attempt to read more than 64 consecutive sectors in 1 function call.\n");
|
|
|
|
// Lock the driver.
|
|
{
|
|
while (true) {
|
|
KEventWait(&semaphore.available, ES_WAIT_NO_TIMEOUT);
|
|
KMutexAcquire(&blockedPacketsMutex);
|
|
|
|
if (semaphore.units) {
|
|
KSemaphoreTake(&semaphore, 1);
|
|
break;
|
|
}
|
|
|
|
KMutexRelease(&blockedPacketsMutex);
|
|
}
|
|
|
|
KMutexRelease(&blockedPacketsMutex);
|
|
}
|
|
|
|
// EsPrint("locked (%d/%x)!\n", countBytes, _buffer);
|
|
|
|
op.bus = bus;
|
|
op.slave = slave;
|
|
|
|
if (!AccessStart(bus, slave, sector, offsetIntoSector, sectorsNeededToLoad, countBytes, operation, _buffer, atapi)) {
|
|
KSemaphoreReturn(&semaphore, 1);
|
|
return false;
|
|
}
|
|
|
|
bool result = AccessEnd(bus, slave);
|
|
Unblock();
|
|
return result;
|
|
}
|
|
|
|
bool ATAIRQHandler(uintptr_t interruptIndex, void *) {
|
|
int bus = interruptIndex - ATA_IRQ(0);
|
|
|
|
// Acknowledge the interrupt.
|
|
ProcessorIn8(ATA_REGISTER(bus, ATA_STATUS));
|
|
ataController->pci->ReadBAR8(DMA_REGISTER(bus, DMA_STATUS));
|
|
|
|
// *Don't* queue an asynchronous task.
|
|
// First of all, we don't need to (it's slower),
|
|
// and secondly, we need to Sync() nodes we're closing during process handle table termination,
|
|
// which takes place in the asynchronous task thread. (Meaning we'd get deadlock).
|
|
// TODO Is there a better way to do this, preventing similar bugs in the future?
|
|
|
|
ATAOperation *op = (ATAOperation *) &ataController->op;
|
|
KEvent *event = ataController->irqs + op->bus;
|
|
|
|
if (op->pio) {
|
|
KEventSet(event);
|
|
} else if (!(ataController->pci->ReadBAR8(DMA_REGISTER(op->bus, DMA_STATUS)) & 4)) {
|
|
// The interrupt bit was not set, so the IRQ must have been generated by a different device.
|
|
} else {
|
|
if (!event->state) {
|
|
// Stop the transfer.
|
|
ataController->pci->WriteBAR8(DMA_REGISTER(op->bus, DMA_COMMAND), 0);
|
|
KEventSet(event);
|
|
} else {
|
|
KernelLog(LOG_ERROR, "IDE", "too many IRQs", "ATAIRQHandler - Received more interrupts than expected.\n");
|
|
}
|
|
}
|
|
|
|
KSwitchThreadAfterIRQ();
|
|
|
|
return true;
|
|
}
|
|
|
|
inline uint32_t ByteSwap32(uint32_t x) {
|
|
return ((x & 0xFF000000) >> 24)
|
|
| ((x & 0x000000FF) << 24)
|
|
| ((x & 0x00FF0000) >> 8)
|
|
| ((x & 0x0000FF00) << 8);
|
|
}
|
|
|
|
void ATAController::Initialise() {
|
|
KSemaphoreReturn(&semaphore, 1);
|
|
|
|
KernelLog(LOG_INFO, "IDE", "found controller", "ATAController::Initialise - Found an ATA controller.\n");
|
|
|
|
for (uintptr_t bus = 0; bus < ATA_BUSES; bus++) {
|
|
// If the status is 0xFF, then the bus does not exist.
|
|
if (ProcessorIn8(ATA_REGISTER(bus, ATA_STATUS)) == 0xFF) {
|
|
continue;
|
|
}
|
|
|
|
// Check that the LBA registers are RW.
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_LBA1), 0xAB);
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_LBA2), 0xCD);
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_LBA3), 0xEF);
|
|
|
|
// Otherwise, the bus doesn't exist.
|
|
if (ProcessorIn8(ATA_REGISTER(bus, ATA_LBA1) != 0xAB)) continue;
|
|
if (ProcessorIn8(ATA_REGISTER(bus, ATA_LBA2) != 0xCD)) continue;
|
|
if (ProcessorIn8(ATA_REGISTER(bus, ATA_LBA3) != 0xEF)) continue;
|
|
|
|
// Clear the device command register.
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_DCR), 0);
|
|
|
|
int dmaDrivesOnBus = 0;
|
|
int drivesOnBus = 0;
|
|
uint8_t status;
|
|
|
|
size_t drivesPerBus = 2;
|
|
|
|
for (uintptr_t slave = 0; slave < drivesPerBus; slave++) {
|
|
// Issue the IDENTIFY command to the drive.
|
|
SetDrive(bus, slave);
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_LBA2), 0);
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_LBA3), 0);
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_COMMAND), ATA_IDENTIFY);
|
|
|
|
// Start a timeout.
|
|
KTimeout timeout(100);
|
|
|
|
// Check for error.
|
|
bool atapi = false;
|
|
if (ProcessorIn8(ATA_REGISTER(bus, ATA_STATUS)) & 32) continue;
|
|
if (ProcessorIn8(ATA_REGISTER(bus, ATA_STATUS)) & 1) {
|
|
uint8_t a = ProcessorIn8(ATA_REGISTER(bus, ATA_LBA2));
|
|
uint8_t b = ProcessorIn8(ATA_REGISTER(bus, ATA_LBA3));
|
|
if (a == 0x14 && b == 0xEB) {
|
|
atapi = true;
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_COMMAND), ATA_IDENTIFY_PACKET);
|
|
} else {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Wait for the drive to be ready for the data transfer.
|
|
while ((ProcessorIn8(ATA_REGISTER(bus, ATA_STATUS)) & 0x80) && !timeout.Hit());
|
|
if (timeout.Hit()) continue;
|
|
while ((!(status = ProcessorIn8(ATA_REGISTER(bus, ATA_STATUS)) & 9)) && !timeout.Hit());
|
|
if (timeout.Hit()) continue;
|
|
if (status & 33) continue;
|
|
|
|
// Transfer the data.
|
|
for (uintptr_t i = 0; i < 256; i++) {
|
|
identifyData[i] = ProcessorIn16(ATA_REGISTER(bus, ATA_DATA));
|
|
}
|
|
|
|
// Check if the device supports LBA/DMA.
|
|
if (!(identifyData[49] & 0x200)) continue;
|
|
if (!(identifyData[49] & 0x100)) continue;
|
|
dmaDrivesOnBus |= 1;
|
|
drivesOnBus |= 1;
|
|
|
|
// Work out the number of sectors in the drive.
|
|
uint32_t lba28Sectors = ((uint32_t) identifyData[60] << 0) + ((uint32_t) identifyData[61] << 16);
|
|
uint64_t lba48Sectors = ((uint64_t) identifyData[100] << 0) + ((uint64_t) identifyData[101] << 16) +
|
|
((uint64_t) identifyData[102] << 32) + ((uint64_t) identifyData[103] << 48);
|
|
bool supportsLBA48 = lba48Sectors && (identifyData[83] & (1 << 10));
|
|
uint64_t sectors = supportsLBA48 ? lba48Sectors : lba28Sectors;
|
|
sectorCount[slave + bus * 2] = sectors;
|
|
|
|
if (atapi) {
|
|
isATAPI[slave + bus * 2] = true;
|
|
KernelLog(LOG_INFO, "IDE", "found drive", "ATAController::Initialise - Found ATAPI drive: %d/%d%z.\n",
|
|
bus, slave, supportsLBA48 ? "; supports LBA48" : "");
|
|
} else {
|
|
KernelLog(LOG_INFO, "IDE", "found drive", "ATAController::Initialise - Found ATA drive: %d/%d with %x sectors%z.\n",
|
|
bus, slave, sectors, supportsLBA48 ? "; supports LBA48" : "");
|
|
}
|
|
}
|
|
|
|
if (dmaDrivesOnBus) {
|
|
uint8_t *dataVirtual;
|
|
uintptr_t dataPhysical;
|
|
|
|
if (!MMPhysicalAllocateAndMap(131072, 131072, 32, true, ES_FLAGS_DEFAULT, &dataVirtual, &dataPhysical)) {
|
|
KernelLog(LOG_ERROR, "IDE", "allocation failure", "ATAController::Initialise - Could not allocate memory for DMA on bus %d.\n", bus);
|
|
sectorCount[bus * 2 + 0] = sectorCount[bus * 2 + 1] = 0;
|
|
drivesOnBus = 0;
|
|
continue;
|
|
}
|
|
|
|
PRD *prdt = (PRD *) dataVirtual;
|
|
prdt->end = 0x8000;
|
|
prdt->base = dataPhysical + 65536;
|
|
prdts[bus] = prdt;
|
|
|
|
void *buffer = (void *) (dataVirtual + 65536);
|
|
buffers[bus] = buffer;
|
|
|
|
pci->WriteBAR32(DMA_REGISTER(bus, DMA_PRDT), dataPhysical);
|
|
}
|
|
|
|
if (drivesOnBus) {
|
|
if (!KRegisterIRQ(ATA_IRQ(bus), ATAIRQHandler, nullptr, "IDE")) {
|
|
KernelLog(LOG_ERROR, "IDE", "IRQ registration failure", "ATAController::Initialise - Could not register IRQ for bus %d.\n", bus);
|
|
|
|
// Disable the drives on this bus.
|
|
sectorCount[bus * 2 + 0] = 0;
|
|
sectorCount[bus * 2 + 1] = 0;
|
|
isATAPI[bus * 2 + 0] = false;
|
|
isATAPI[bus * 2 + 1] = false;
|
|
}
|
|
}
|
|
|
|
for (uintptr_t slave = 0; slave < 2; slave++) {
|
|
if (sectorCount[slave + bus * 2]) {
|
|
bool success = Access(bus * 2 + slave, 0, ATA_SECTOR_SIZE, K_ACCESS_READ, (uint8_t *) identifyData);
|
|
|
|
if (!success) {
|
|
KernelLog(LOG_ERROR, "IDE", "test read failure", "ATAController::Initialise - Could not perform test read on drive.\n");
|
|
continue;
|
|
}
|
|
|
|
success = Access(bus * 2 + slave, 0, ATA_SECTOR_SIZE, K_ACCESS_WRITE, (uint8_t *) identifyData);
|
|
|
|
if (!success) {
|
|
KernelLog(LOG_ERROR, "IDE", "test write failure", "ATAController::Initialise - Could not perform test write to drive.\n");
|
|
}
|
|
} else if (isATAPI[slave + bus * 2]) {
|
|
KEvent *event = irqs + bus;
|
|
event->autoReset = false;
|
|
KEventReset(event);
|
|
|
|
op.bus = bus;
|
|
op.slave = slave;
|
|
op.pio = true;
|
|
|
|
uint16_t capacity[4];
|
|
|
|
SetDrive(bus, slave);
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_LBA2), 0);
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_LBA3), 8);
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_FEATURES), 0);
|
|
ProcessorOut8(ATA_REGISTER(bus, ATA_COMMAND), ATA_PACKET);
|
|
|
|
KTimeout timeout(100);
|
|
|
|
if (ProcessorIn8(ATA_REGISTER(bus, ATA_STATUS)) & 33) {
|
|
goto readCapacityFailure;
|
|
}
|
|
|
|
while ((ProcessorIn8(ATA_REGISTER(bus, ATA_STATUS)) & 0x80) && !timeout.Hit());
|
|
if (timeout.Hit()) goto readCapacityFailure;
|
|
while ((!(status = ProcessorIn8(ATA_REGISTER(bus, ATA_STATUS)) & 9)) && !timeout.Hit());
|
|
if (timeout.Hit()) goto readCapacityFailure;
|
|
if (status & 33) goto readCapacityFailure;
|
|
|
|
ProcessorOut16(ATA_REGISTER(bus, ATA_DATA), 0x0025);
|
|
ProcessorOut16(ATA_REGISTER(bus, ATA_DATA), 0x0000);
|
|
ProcessorOut16(ATA_REGISTER(bus, ATA_DATA), 0x0000);
|
|
ProcessorOut16(ATA_REGISTER(bus, ATA_DATA), 0x0000);
|
|
ProcessorOut16(ATA_REGISTER(bus, ATA_DATA), 0x0000);
|
|
ProcessorOut16(ATA_REGISTER(bus, ATA_DATA), 0x0000);
|
|
|
|
KEventWait(event, ATA_TIMEOUT);
|
|
KEventReset(event);
|
|
|
|
while ((ProcessorIn8(ATA_REGISTER(bus, ATA_STATUS)) & 0x80) && !timeout.Hit());
|
|
if (timeout.Hit()) goto readCapacityFailure;
|
|
while ((!(status = ProcessorIn8(ATA_REGISTER(bus, ATA_STATUS)) & 9)) && !timeout.Hit());
|
|
if (timeout.Hit()) goto readCapacityFailure;
|
|
if (status & 33) goto readCapacityFailure;
|
|
|
|
capacity[0] = ProcessorIn16(ATA_REGISTER(bus, ATA_DATA));
|
|
capacity[1] = ProcessorIn16(ATA_REGISTER(bus, ATA_DATA));
|
|
capacity[2] = ProcessorIn16(ATA_REGISTER(bus, ATA_DATA));
|
|
capacity[3] = ProcessorIn16(ATA_REGISTER(bus, ATA_DATA));
|
|
|
|
KEventWait(event, ATA_TIMEOUT);
|
|
KEventReset(event);
|
|
|
|
{
|
|
uint32_t blockCount = ByteSwap32(capacity[0] | ((uint32_t) capacity[1] << 16)) + 1;
|
|
uint32_t blockLength = ByteSwap32(capacity[2] | ((uint32_t) capacity[3] << 16));
|
|
|
|
if (blockLength != ATAPI_SECTOR_SIZE) {
|
|
KernelLog(LOG_ERROR, "IDE", "unsupported ATAPI block length",
|
|
"ATAController::Initialise - ATAPI drive reported block length of %d bytes, which is unsupported.\n", blockLength);
|
|
continue;
|
|
}
|
|
|
|
KernelLog(LOG_INFO, "IDE", "ATAPI capacity", "ATAController::Initialise - ATAPI drive has %d blocks (%D).\n",
|
|
blockCount, blockCount * ATAPI_SECTOR_SIZE);
|
|
|
|
sectorCount[slave + bus * 2] = blockCount;
|
|
continue;
|
|
}
|
|
|
|
readCapacityFailure:;
|
|
KernelLog(LOG_ERROR, "IDE", "read capacity failure", "ATAController::Initialise - Could not read capacity of ATAPI drive %d/%d.\n", bus, slave);
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (uintptr_t i = 0; i < ATA_DRIVES; i++) {
|
|
if (sectorCount[i]) {
|
|
// Register the drive.
|
|
ATADrive *device = (ATADrive *) KDeviceCreate("IDE drive", this, sizeof(ATADrive));
|
|
|
|
if (!device) {
|
|
KernelLog(LOG_ERROR, "IDE", "allocation failure", "Could not create device for drive %d.\n", i);
|
|
break;
|
|
}
|
|
|
|
device->index = i;
|
|
device->information.sectorSize = isATAPI[i] ? ATAPI_SECTOR_SIZE : ATA_SECTOR_SIZE;
|
|
device->information.sectorCount = sectorCount[i];
|
|
device->maxAccessSectorCount = isATAPI[i] ? 31 : 64;
|
|
device->information.readOnly = isATAPI[i];
|
|
device->information.driveType = isATAPI[i] ? ES_DRIVE_TYPE_CDROM : ES_DRIVE_TYPE_HDD;
|
|
|
|
device->access = [] (KBlockDeviceAccessRequest request) {
|
|
request.dispatchGroup->Start();
|
|
bool success = ataController->Access(((ATADrive *) request.device)->index,
|
|
request.offset, request.count, request.operation, (uint8_t *) KDMABufferGetVirtualAddress(request.buffer));
|
|
request.dispatchGroup->End(success);
|
|
};
|
|
|
|
FSRegisterBlockDevice(device);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void DeviceAttach(KDevice *_parent) {
|
|
KPCIDevice *parent = (KPCIDevice *) _parent;
|
|
|
|
if (ataController) {
|
|
KernelLog(LOG_ERROR, "IDE", "multiple controllers", "EntryIDE - Attempt to register multiple IDE controllers; ignored.\n");
|
|
return;
|
|
}
|
|
|
|
ATAController *device = (ATAController *) KDeviceCreate("IDE controller", parent, sizeof(ATAController));
|
|
if (!device) return;
|
|
ataController = device;
|
|
device->pci = parent;
|
|
|
|
// Enable busmastering DMA and interrupts.
|
|
parent->EnableFeatures(K_PCI_FEATURE_INTERRUPTS | K_PCI_FEATURE_BUSMASTERING_DMA | K_PCI_FEATURE_BAR_4);
|
|
|
|
// Initialise the controller.
|
|
device->Initialise();
|
|
}
|
|
|
|
KDriver driverIDE = {
|
|
.attach = DeviceAttach,
|
|
};
|