Skip to content

Instantly share code, notes, and snippets.

@skejeton
Created April 2, 2020 18:04
Show Gist options
  • Save skejeton/c5b485b193926879d9ec86f66f5072b1 to your computer and use it in GitHub Desktop.
Save skejeton/c5b485b193926879d9ec86f66f5072b1 to your computer and use it in GitHub Desktop.
#include <inc/drivers/ide/ide.h>
Byte atapiPacket[12];
UInt8 IDERead(IDEDevice *dev, UInt8 offset)
{
unsigned char result;
if (offset > 0x07 && offset < 0x0C)
IDEWrite(dev, ATA_REG_CONTROL, 0x80 | dev->channel->nIEN);
if (offset < 0x08)
result = InB(dev->channel->base + offset - 0x00);
else if (offset < 0x0C)
result = InB(dev->channel->base + offset - 0x06);
else if (offset < 0x0E)
result = InB((dev->channel->base+0x206) + offset - 0x0A);
if (offset > 0x07 && offset < 0x0C)
IDEWrite(dev, ATA_REG_CONTROL, dev->channel->nIEN);
return result;
}
void IDEWrite(IDEDevice *dev, UInt8 offset, UInt8 data)
{
if (offset > 0x07 && offset < 0x0C)
IDEWrite(dev, ATA_REG_CONTROL, 0x80 | dev->channel->nIEN);
if (offset < 0x08)
OutB(dev->channel->base + offset - 0x00, data);
else if (offset < 0x0C)
OutB(dev->channel->base + offset - 0x06, data);
else if (offset < 0x0E)
OutB((dev->channel->base+0x206) + offset - 0x0A, data);
if (offset > 0x07 && offset < 0x0C)
IDEWrite(dev, ATA_REG_CONTROL, dev->channel->nIEN);
}
UInt8 IDEPoll(IDEDevice *dev, bool acheck)
{
for (int i = 0; i < 4; i++)
IDERead(dev->channel->base, ATA_REG_ALTSTATUS);
while (IDERead(dev->channel->base, ATA_REG_STATUS) & ATA_BIT_BSY);
if (acheck)
{
UInt8 state = IDERead(dev->channel->base, ATA_REG_STATUS);
TVMPrintA(ToHex(state));
if (state & ATA_BIT_ERR)
return ATAERR_ERR;
if (state & ATA_BIT_DEVF)
return ATAERR_DEVF;
if ((state & ATA_BIT_DRQ) == 0)
return ATAERR_DRQ;
}
return ATAERR_NOERR;
}
void IDESleep(int ms)
{
PITSleep(ms);
}
UInt16 IDEPrintError(IDEDevice *dev, UInt16 err)
{
if (err == 0) return 0;
TVMPrintA("=========================================");
TVMPrintA("IDE Error: ");
if (err == ATAERR_DEVF)
TVMPrintA("Device fault");
else if (err == ATAERR_ERR)
{
TVMPrintA("ERR");
UInt8 errType = IDERead(dev->channel, ATA_REG_ERROR);
if (errType & ATA_IERR_AMNF) TVMPrintA("No address mark");
if (errType & ATA_IERR_TK0NF) TVMPrintA("Track 0 not found");
if (errType & ATA_IERR_ABRT) TVMPrintA("Command aborted");
if (errType & ATA_IERR_MCR) TVMPrintA("Media change request");
if (errType & ATA_IERR_IDNF) TVMPrintA("Id mark not found");
if (errType & ATA_IERR_MC) TVMPrintA("Media changed");
if (errType & ATA_IERR_UNC) TVMPrintA("Uncorrectable data");
if (errType & ATA_IERR_BBK) TVMPrintA("Bad block");
}
if (err == ATAERR_DRQ)
TVMPrintA("Reads nothing");
TVMPrintA("On");
TVMPrintA(dev->channel->isPrimary ? "Primary" : "Secondary");
TVMPrintA(dev->isMaster ? "Master" : "Slave");
TVMPrintA(ToHex(IDERead(dev, ATA_REG_STATUS)));
TVMPrintA("=========================================");
return err;
}
IDEDevice* IDECreateATADeviceInstance(PCIDeviceDescriptor *dev, bool primary, bool master)
{
IDEDevice *res = NEW(IDEDevice, 1);
IDEChannel *ch = NEW(IDEChannel, 1);
UInt8 status;
res->channel = ch;
res->channel->isPrimary = primary;
if (primary)
{
res->channel->base = ((UInt32)dev->bars[0]->address) + 0x1F0 * (!((UInt32)dev->bars[0]->address));
res->channel->busMaster = (UInt32)dev->bars[4]->address;
}
else
{
res->channel->base = ((UInt32)dev->bars[2]->address) + 0x170 * (!((UInt32)dev->bars[2]->address));
res->channel->busMaster = ((UInt32)dev->bars[4]->address) + 8;
}
res->exists = true;
res->isMaster = master;
// Disable irqs
IDEWrite(res, ATA_REG_CONTROL, 2);
UInt8 error;
// Identify device
IDEWrite(res, ATA_REG_HDDEVSEL, 0xA0 | ((!master) << 4));
// Wait
IDESleep(10);
IDEWrite(res, ATA_REG_COMMAND, 0xEC); // Identify command
// Wait
IDESleep(10);
if (IDERead(res, ATA_REG_STATUS) == 0) return NULL;
while(1)
{
status = IDERead(res, ATA_REG_STATUS);
if ((status & ATA_BIT_ERR))
{
error = 1;
break;
} // If Err, Device is not ATA.
if (!(status & ATA_BIT_BSY) && (status & ATA_BIT_DRQ))
{
break;
} // Everything is right.
}
res->type = ATA_TYPE_ATA;
// Error happened, must be atapi device then
if (error != 0)
{
// Check if device is ATAPI
UInt8 sig1 = IDERead(res, ATA_REG_LBA1);
UInt8 sig2 = IDERead(res, ATA_REG_LBA2);
res->type = ATA_TYPE_ATAPI;
if (sig1 == 0x14 && sig2 == 0xeb)
IDEWrite(res, ATA_REG_COMMAND, 0xA1); // Identify Packet command
IDESleep(10);
}
res->channel->nIEN = true;
// TODO: Read buffer
////////////////////
return res;
}
UInt8 IDEATAPIRead(IDEDevice *dev, UInt32 lba, Byte *buffer)
{
TVMPrintA("============= Entering read =============");
UInt8 error;
IDEWrite(dev, ATA_REG_CONTROL, dev->channel->nIEN = ataLock = 0);
atapiPacket[ 0] = 0xA8;
atapiPacket[ 1] = 0x0;
atapiPacket[ 2] = (lba >> 24) & 0xFF;
atapiPacket[ 3] = (lba >> 16) & 0xFF;
atapiPacket[ 4] = (lba >> 8) & 0xFF;
atapiPacket[ 5] = (lba >> 0) & 0xFF;
atapiPacket[ 6] = 0x0;
atapiPacket[ 7] = 0x0;
atapiPacket[ 8] = 0x0;
atapiPacket[ 9] = 1; // Number of sectors
atapiPacket[10] = 0x0;
atapiPacket[11] = 0x0;
// IDEWrite(dev->channel->base, ATA_REG_CONTROL, dev->channel->nIEN = ataLock = 0);
// IDEWrite(dev, ATA_REG_HDDEVSEL, dev->isMaster << 4);
IDEWrite(dev, ATA_REG_HDDEVSEL, (!dev->isMaster) << 4);
// Delay
for(int i = 0; i < 4; i++)
IDERead(dev, ATA_REG_ALTSTATUS);
// PIO mode
IDEWrite(dev, ATA_REG_FEATURES, 0);
// Size of buffer
IDEWrite(dev, ATA_REG_LBA1, ATAPI_SECTOR_SIZE & 0xFF);
IDEWrite(dev, ATA_REG_LBA2, (ATAPI_SECTOR_SIZE >> 8));
// Send packet
IDEWrite(dev, ATA_REG_COMMAND, 0xA0); // Packet command
if (error = IDEPoll(dev, true)) return error;
asm volatile("rep outsw" : : "c"(6), "d"(dev->channel->base), "S"(atapiPacket)); // Send Packet Data
// Wait for irq
while (!ataLock) {
asm("hlt");
}
ataLock = false;
TVMPrintA("got past irq");
if (error = IDEPoll(dev, true)) return error;
}
#pragma once
#include <inc/types.h>
#include <inc/port.h>
#include <inc/io.h>
#include <inc/pci/pci.h>
#include <inc/irq_handlers.h>
// Based on https://wiki.osdev.org/PCI_IDE_Controller
#define ATA_BIT_BSY 0x80 // Busy
#define ATA_BIT_DRDY 0x40 // Drive ready
#define ATA_BIT_DEVF 0x20 // Drive write fault
#define ATA_BIT_DSC 0x10 // Drive seek complete
#define ATA_BIT_DRQ 0x08 // Data request ready
#define ATA_BIT_CORR 0x04 // Corrected data
#define ATA_BIT_IDX 0x02 // Index
#define ATA_BIT_ERR 0x01 // Error
#define ATA_REG_DATA 0x00
#define ATA_REG_ERROR 0x01
#define ATA_REG_FEATURES 0x01
#define ATA_REG_SECCOUNT0 0x02
#define ATA_REG_LBA0 0x03
#define ATA_REG_LBA1 0x04
#define ATA_REG_LBA2 0x05
#define ATA_REG_HDDEVSEL 0x06
#define ATA_REG_COMMAND 0x07
#define ATA_REG_STATUS 0x07
#define ATA_REG_CONTROL 0x0C
#define ATA_REG_ALTSTATUS 0x0C
#define ATAERR_NOERR 0
#define ATAERR_ERR 1
#define ATAERR_DEVF 2
#define ATAERR_DRQ 3
#define ATA_IERR_BBK 0x80 // Bad block
#define ATA_IERR_UNC 0x40 // Uncorrectable data
#define ATA_IERR_MC 0x20 // Media changed
#define ATA_IERR_IDNF 0x10 // ID mark not found
#define ATA_IERR_MCR 0x08 // Media change request
#define ATA_IERR_ABRT 0x04 // Command aborted
#define ATA_IERR_TK0NF 0x02 // Track 0 not found
#define ATA_IERR_AMNF 0x01 // No address mark
#define ATA_TYPE_ATAPI 0
#define ATA_TYPE_ATA 1
#define ATAPI_SECTOR_SIZE 2048
typedef struct
{
bool isPrimary;
UInt16 base;
UInt16 busMaster;
UInt8 nIEN;
} IDEChannel;
typedef struct
{
UInt8 type;
IDEChannel *channel;
bool exists;
bool isMaster;
UInt16 signature;
UInt16 features;
UInt32 commands;
UInt32 capacity;
Byte model[41];
} IDEDevice;
UInt8 IDERead(IDEDevice *dev, UInt8 offset);
void IDEWrite(IDEDevice *dev, UInt8 offset, UInt8 data);
UInt8 IDEPoll(IDEDevice *dev, bool acheck);
UInt16 IDEPrintError(IDEDevice *dev, UInt16 err);
IDEDevice* IDECreateATADeviceInstance(PCIDeviceDescriptor *dev, bool primary, bool master);
UInt8 IDEATAPIRead(IDEDevice *dev, UInt32 lba, Byte *buffer);
bool ataLock = false;
void IRQ14Handler(void)
{
ataLock = true;
TVMPrint("PRIMARY", 0x70, 0);
OutB(0xA0, 0x20);
OutB(0x20, 0x20); //EOI
}
void IRQ15Handler(void)
{
ataLock = true;
TVMPrint("SECONDARY", 0x70, 0);
OutB(0xA0, 0x20);
OutB(0x20, 0x20); //EOI
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment