128 lines
3.5 KiB
C
128 lines
3.5 KiB
C
|
#include <sys.h>
|
||
|
|
||
|
#include "ata.h"
|
||
|
#include "pci.h"
|
||
|
#include "../panic.h"
|
||
|
|
||
|
#define REG_DATA 0
|
||
|
#define REG_ERR 1
|
||
|
#define REG_FEATURES 1
|
||
|
#define REG_SECTOR_CNT 2
|
||
|
#define REG_SECTOR_NUM 3
|
||
|
#define REG_LBA_LO 3
|
||
|
#define REG_CYLINDER_LO 4
|
||
|
#define REG_LBA_MID 4
|
||
|
#define REG_CYLINDER_HI 5
|
||
|
#define REG_LBA_HI 5
|
||
|
#define REG_DRIVE_HEAD 6
|
||
|
#define REG_STATUS 7
|
||
|
#define REG_COMMAND 7
|
||
|
|
||
|
#define STAT_ERR 0x01
|
||
|
#define STAT_IDX 0x02
|
||
|
#define STAT_CORR 0x04
|
||
|
#define STAT_DRQ 0x08
|
||
|
#define STAT_SRV 0x10
|
||
|
#define STAT_DF 0x20
|
||
|
#define STAT_RDY 0x40
|
||
|
#define STAT_BSY 0x80
|
||
|
|
||
|
static bool ata_init_impl(uint16_t base, uint16_t ctrl, struct AtaDevice *dev) {
|
||
|
uint8_t status = inb(base + REG_STATUS);
|
||
|
outb(base + REG_DRIVE_HEAD, 0xA0);
|
||
|
outb(base + REG_SECTOR_CNT, 0x00);
|
||
|
outb(base + REG_LBA_LO, 0x00);
|
||
|
outb(base + REG_LBA_MID, 0x00);
|
||
|
outb(base + REG_LBA_HI, 0x00);
|
||
|
outb(base + REG_COMMAND, 0xEC);
|
||
|
status = STAT_BSY;
|
||
|
while(status & STAT_BSY) {
|
||
|
status = inb(base + REG_STATUS);
|
||
|
}
|
||
|
if(inb(base + REG_LBA_MID) != 0 || inb(base + REG_LBA_HI) != 0) {
|
||
|
panic("Device is not an ATA device");
|
||
|
}
|
||
|
while(!(status & (STAT_DRQ | STAT_ERR))) {
|
||
|
status = inb(base + REG_STATUS);
|
||
|
}
|
||
|
if(status & STAT_ERR) {
|
||
|
uint8_t err = inb(base + REG_ERR);
|
||
|
panic("Error initializing ATA device: %X", err);
|
||
|
}
|
||
|
uint16_t data[256];
|
||
|
for(int i = 0; i < 256; i++) {
|
||
|
data[i] = inw(base + REG_DATA);
|
||
|
}
|
||
|
uint32_t sector_count = data[60] | (data[61] << 16);
|
||
|
if(sector_count == 0) {
|
||
|
panic("ATA device does not support LBA28");
|
||
|
}
|
||
|
dev->base = base;
|
||
|
dev->ctrl = ctrl;
|
||
|
dev->sector_count = sector_count;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool ata_init(struct AtaDevice *atadev, struct PciDevice pcidev) {
|
||
|
uint8_t prog_if = pci_rcfg_b(pcidev, PCI_PROG_IF_B);
|
||
|
|
||
|
if((prog_if & 0x7F) != 0) {
|
||
|
panic("Could not initialize ATA device: unsupported prog_if %X", prog_if);
|
||
|
}
|
||
|
|
||
|
atadev->pcidev = pcidev;
|
||
|
|
||
|
if(!ata_init_impl(0x1F0, 0x3F6, atadev)) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool ata_read(struct AtaDevice atadev, uint32_t lba, uint8_t sector_count, uint16_t buf[sector_count*256]) {
|
||
|
outb(atadev.base + REG_DRIVE_HEAD, 0xE0 | ((lba >> 24) & 0x0F));
|
||
|
outb(atadev.base + REG_FEATURES, 0x00);
|
||
|
outb(atadev.base + REG_SECTOR_CNT, sector_count);
|
||
|
outb(atadev.base + REG_LBA_LO, lba & 0xFF);
|
||
|
outb(atadev.base + REG_LBA_MID, (lba >> 8) & 0xFF);
|
||
|
outb(atadev.base + REG_LBA_HI, (lba >> 16) & 0xFF);
|
||
|
outb(atadev.base + REG_COMMAND, 0x20);
|
||
|
for(int i = 0; i < sector_count; i++) {
|
||
|
while(true) {
|
||
|
uint8_t status = inb(atadev.ctrl);
|
||
|
if(status & STAT_DRQ) break;
|
||
|
if(status & STAT_DF) panic("Drive failure");
|
||
|
if(status & STAT_ERR) panic("Read disk error");
|
||
|
}
|
||
|
for(int j = 0; j < 256; j++) {
|
||
|
buf[i*256 + j] = inw(atadev.base + REG_DATA);
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool ata_write(struct AtaDevice atadev, uint32_t lba, uint8_t sector_count, uint16_t buf[sector_count*256]) {
|
||
|
outb(atadev.base + REG_DRIVE_HEAD, 0xE0 | ((lba >> 24) & 0x0F));
|
||
|
outb(atadev.base + REG_FEATURES, 0x00);
|
||
|
outb(atadev.base + REG_SECTOR_CNT, sector_count);
|
||
|
outb(atadev.base + REG_LBA_LO, lba & 0xFF);
|
||
|
outb(atadev.base + REG_LBA_MID, (lba >> 8) & 0xFF);
|
||
|
outb(atadev.base + REG_LBA_HI, (lba >> 16) & 0xFF);
|
||
|
outb(atadev.base + REG_COMMAND, 0x30);
|
||
|
for(int i = 0; i < sector_count; i++) {
|
||
|
while(true) {
|
||
|
uint8_t status = inb(atadev.ctrl);
|
||
|
if(status & STAT_DRQ) break;
|
||
|
if(status & STAT_DF) panic("Drive failure");
|
||
|
if(status & STAT_ERR) panic("Write disk error");
|
||
|
}
|
||
|
for(int j = 0; j < 256; j++) {
|
||
|
outw(atadev.base + REG_DATA, buf[i*256 + j]);
|
||
|
}
|
||
|
}
|
||
|
io_wait();
|
||
|
outb(atadev.base + REG_COMMAND, 0xE7);
|
||
|
while(inb(atadev.ctrl & STAT_BSY)) {}
|
||
|
return true;
|
||
|
}
|