Support up to 4 ATA drives (primary and secondary bus)

This commit is contained in:
2025-10-19 15:46:56 +02:00
parent 4452f1b196
commit c3621a33dc
4 changed files with 155 additions and 66 deletions

View File

@ -1,2 +1,3 @@
print 'Mounting filesystems...\n'
setlogcmds yes
$fs mount -mp system -fs LittleFS -dev atasd-ch0-M -fmt no

View File

@ -10,16 +10,15 @@
#include "hshtb.h"
#include "vfs/vfs.h"
#define ATA_REG_DATA 0x1f0
#define ATA_REG_ERROR 0x1f1
#define ATA_REG_SECCOUNT0 0x1f2
#define ATA_REG_LBA0 0x1f3
#define ATA_REG_LBA1 0x1f4
#define ATA_REG_LBA2 0x1f5
#define ATA_REG_DRIVE 0x1f6
#define ATA_REG_STATUS 0x1f7
#define ATA_REG_COMMAND 0x1f7
#define ATA_REG_CTRL 0x3f6
#define ATA_REG_DATA 0x00
#define ATA_REG_ERROR 0x01
#define ATA_REG_SECCOUNT0 0x02
#define ATA_REG_LBA0 0x03
#define ATA_REG_LBA1 0x04
#define ATA_REG_LBA2 0x05
#define ATA_REG_DRIVE 0x06
#define ATA_REG_STATUS 0x07
#define ATA_REG_COMMAND 0x07
#define ATA_BSY 0x80
#define ATA_DRDY 0x40
@ -35,11 +34,11 @@
#define ATA_MASTER 0x00
#define ATA_SLAVE 0x01
int ata_wait(uint32_t timeout, bool require_drq, int checkerr) {
int ata_wait(uint16_t iobase, uint32_t timeout, bool require_drq, int checkerr) {
uint32_t i = 0;
uint8_t st;
for(;;) {
st = io_in8(ATA_REG_STATUS);
st = io_in8(iobase + ATA_REG_STATUS);
if (!(st & ATA_BSY))
break;
if (++i >= timeout)
@ -50,7 +49,7 @@ int ata_wait(uint32_t timeout, bool require_drq, int checkerr) {
while (!(st & ATA_DRQ)) {
if (st & ATA_ERR)
return -1;
st = io_in8(ATA_REG_STATUS);
st = io_in8(iobase + ATA_REG_STATUS);
if (++i >= timeout)
return -1;
}
@ -60,38 +59,38 @@ int ata_wait(uint32_t timeout, bool require_drq, int checkerr) {
return 0;
}
void ata_delay400ns(void) {
io_in8(ATA_REG_CTRL);
io_in8(ATA_REG_CTRL);
io_in8(ATA_REG_CTRL);
io_in8(ATA_REG_CTRL);
void ata_delay400ns(uint16_t ctrlbase) {
io_in8(ctrlbase);
io_in8(ctrlbase);
io_in8(ctrlbase);
io_in8(ctrlbase);
}
// caller must synchronize
uint64_t ata_probesize_bytes(int devno) {
uint64_t ata_probesize_bytes(uint16_t iobase, uint16_t ctrlbase, int devno) {
uint16_t identifydata[0x100];
io_out8(ATA_REG_DRIVE, 0xA0 | (devno << 4));
ata_delay400ns();
io_out8(iobase + ATA_REG_DRIVE, 0xA0 | (devno << 4));
ata_delay400ns(ctrlbase);
io_out8(ATA_REG_SECCOUNT0, 0);
io_out8(ATA_REG_LBA0, 0);
io_out8(ATA_REG_LBA1, 0);
io_out8(ATA_REG_LBA2, 0);
io_out8(iobase + ATA_REG_SECCOUNT0, 0);
io_out8(iobase + ATA_REG_LBA0, 0);
io_out8(iobase + ATA_REG_LBA1, 0);
io_out8(iobase + ATA_REG_LBA2, 0);
io_out8(ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
io_out8(iobase + ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
if (ata_wait(100000, false, 0) < 0)
if (ata_wait(iobase, 100000, false, 0) < 0)
return 0;
uint8_t st = io_in8(ATA_REG_STATUS);
uint8_t st = io_in8(iobase + ATA_REG_STATUS);
if (st == 0)
return 0;
if (ata_wait(100000, true, 1) < 0)
if (ata_wait(iobase, 100000, true, 1) < 0)
return 0;
io_ins16(ATA_REG_DATA, identifydata, 256);
io_ins16(iobase + ATA_REG_DATA, identifydata, 256);
bool lba48 = (identifydata[83] & (1<<10)) != 0;
if (lba48) {
@ -107,46 +106,126 @@ uint64_t ata_probesize_bytes(int devno) {
}
}
bool ata_probe1(int drive) {
io_out8(ATA_REG_DRIVE, 0xA0 | (drive << 4));
for (int i = 0; i < 1000; i++) {
uint8_t status = io_in8(ATA_REG_STATUS);
if (status != 0 && status != 0xFF)
return true; // got something
}
return false;
}
/* bool ata_probe1(uint16_t iobase, uint16_t ctrlbase, int drive) { */
/* /1* io_out8(iobase + ATA_REG_DRIVE, 0xA0 | (drive << 4)); *1/ */
/* /1* for (int i = 0; i < 1000; i++) { *1/ */
/* /1* uint8_t status = io_in8(iobase + ATA_REG_STATUS); *1/ */
/* /1* if (status != 0 && status != 0xFF) *1/ */
/* /1* return true; // got something *1/ */
/* /1* } *1/ */
/* /1* return false; *1/ */
/* uint8_t cl, ch; */
/* io_out8(iobase + ATA_REG_DRIVE, 0xA0 | (drive << 4)); */
/* ata_delay400ns(ctrlbase); */
/* io_out8(iobase + ATA_REG_SECCOUNT0, 0x55); */
/* io_out8(iobase + ATA_REG_LBA0, 0xAA); */
/* io_out8(iobase + ATA_REG_SECCOUNT0, 0xAA); */
/* io_out8(iobase + ATA_REG_LBA0, 0x55); */
/* cl = io_in8(iobase + ATA_REG_SECCOUNT0); */
/* ch = io_in8(iobase + ATA_REG_LBA0); */
/* if (cl != 0x55 || ch != 0xAA) */
/* return false; */
/* io_out8(iobase + ATA_REG_COMMAND, ATA_CMD_IDENTIFY); */
/* ata_delay400ns(ctrlbase); */
/* uint8_t st = io_in8(iobase + ATA_REG_STATUS); */
/* if (st == 0) */
/* return false; */
/* while ((st & (ATA_BSY | ATA_DRQ | ATA_ERR)) == ATA_BSY) */
/* st = io_in8(iobase + ATA_REG_STATUS); */
/* if (st & ATA_ERR) */
/* return false; */
/* if (!(st & ATA_DRQ)) */
/* return false; */
/* return true; */
/* } */
void ata_probe(void) {
if (ata_probe1(ATA_MASTER)) {
uint64_t probesize = ata_probesize_bytes(ATA_MASTER);
uint64_t probesize;
probesize = ata_probesize_bytes(0x1F0, 0x3F6, ATA_MASTER);
if (probesize > 0) {
char hs[20];
LOG("ata", "found ATA Master, size = %s\n", human_size(probesize, hs, sizeof(hs)));
LOG("ata", "found ATA primary bus master, size = %s\n", human_size(probesize, hs, sizeof(hs)));
AtaSdInitExtra extra = {
.devno = ATA_MASTER,
.capacity = probesize,
.iobase = 0x1F0,
.ctrlbase = 0x3F6,
};
storedev_create(STOREDEV_ATASD, (void *)&extra);
}
probesize = ata_probesize_bytes(0x1F0, 0x3F6, ATA_SLAVE);
if (probesize > 0) {
char hs[20];
LOG("ata", "found ATA primary bus slave, size = %s\n", human_size(probesize, hs, sizeof(hs)));
AtaSdInitExtra extra = {
.devno = ATA_SLAVE,
.capacity = probesize,
.iobase = 0x1F0,
.ctrlbase = 0x3F6,
};
storedev_create(STOREDEV_ATASD, (void *)&extra);
}
probesize = ata_probesize_bytes(0x170, 0x376, ATA_MASTER);
if (probesize > 0) {
char hs[20];
LOG("ata", "found ATA secondary bus master, size = %s\n", human_size(probesize, hs, sizeof(hs)));
AtaSdInitExtra extra = {
.devno = ATA_MASTER,
.capacity = probesize,
.iobase = 0x170,
.ctrlbase = 0x376,
};
storedev_create(STOREDEV_ATASD, (void *)&extra);
}
probesize = ata_probesize_bytes(0x170, 0x376, ATA_SLAVE);
if (probesize > 0) {
char hs[20];
LOG("ata", "found ATA secondary bus slave, size = %s\n", human_size(probesize, hs, sizeof(hs)));
AtaSdInitExtra extra = {
.devno = ATA_SLAVE,
.capacity = probesize,
.iobase = 0x170,
.ctrlbase = 0x376,
};
storedev_create(STOREDEV_ATASD, (void *)&extra);
}
}
void ata_setup(int devno, uint16_t sectors, uint64_t lba) {
io_out8(ATA_REG_CTRL, 0x02);
void ata_setup(uint16_t iobase, uint16_t ctrlbase, int devno, uint16_t sectors, uint64_t lba) {
io_out8(ctrlbase, 0x02);
io_out8(ATA_REG_DRIVE, 0x40 | (devno << 4));
ata_delay400ns();
io_out8(iobase + ATA_REG_DRIVE, 0x40 | (devno << 4));
ata_delay400ns(ctrlbase);
io_out8(ATA_REG_SECCOUNT0, (sectors >> 8) & 0xFF);
io_out8(ATA_REG_LBA0, (lba >> 24) & 0xFF);
io_out8(ATA_REG_LBA1, (lba >> 32) & 0xFF);
io_out8(ATA_REG_LBA2, (lba >> 40) & 0xFF);
io_out8(iobase + ATA_REG_SECCOUNT0, (sectors >> 8) & 0xFF);
io_out8(iobase + ATA_REG_LBA0, (lba >> 24) & 0xFF);
io_out8(iobase + ATA_REG_LBA1, (lba >> 32) & 0xFF);
io_out8(iobase + ATA_REG_LBA2, (lba >> 40) & 0xFF);
io_out8(ATA_REG_SECCOUNT0, sectors & 0xFF);
io_out8(ATA_REG_LBA0, (uint8_t)(lba & 0xFF));
io_out8(ATA_REG_LBA1, (uint8_t)((lba >> 8) & 0xFF));
io_out8(ATA_REG_LBA2, (uint8_t)((lba >> 16) & 0xFF));
io_out8(iobase + ATA_REG_SECCOUNT0, sectors & 0xFF);
io_out8(iobase + ATA_REG_LBA0, (uint8_t)(lba & 0xFF));
io_out8(iobase + ATA_REG_LBA1, (uint8_t)((lba >> 8) & 0xFF));
io_out8(iobase + ATA_REG_LBA2, (uint8_t)((lba >> 16) & 0xFF));
}
@ -154,6 +233,8 @@ int32_t atasd_init(struct StoreDev *sd, void *extra) {
AtaSdInitExtra *e = (AtaSdInitExtra *)extra;
sd->sd.atasd.devno = e->devno;
sd->sd.atasd.capacity = e->capacity;
sd->sd.atasd.iobase = e->iobase;
sd->sd.atasd.ctrlbase = e->ctrlbase;
return E_OK;
}
@ -167,12 +248,12 @@ int32_t atasd_read(struct StoreDev *sd, uint8_t *const buffer, ptrdiff_t sector,
size_t sectors = size / STOREDEV_ATASD_SECTORSIZE;
if (size % STOREDEV_ATASD_SECTORSIZE) sectors++;
ata_setup(ata->devno, sectors, lba);
io_out8(ATA_REG_COMMAND, ATA_CMD_READ_PIO_EXT);
ata_setup(ata->iobase, ata->ctrlbase, ata->devno, sectors, lba);
io_out8(ata->iobase + ATA_REG_COMMAND, ATA_CMD_READ_PIO_EXT);
for (size_t s = 0; s < sectors; s++) {
if (ata_wait(100000, true, 1) < 0) { ret = E_BADIO; goto done; }
io_ins16(ATA_REG_DATA, (uint16_t *)(buffer + s * STOREDEV_ATASD_SECTORSIZE), STOREDEV_ATASD_SECTORSIZE/2);
if (ata_wait(ata->iobase, 100000, true, 1) < 0) { ret = E_BADIO; goto done; }
io_ins16(ata->iobase + ATA_REG_DATA, (uint16_t *)(buffer + s * STOREDEV_ATASD_SECTORSIZE), STOREDEV_ATASD_SECTORSIZE/2);
}
ret = E_OK;
@ -192,16 +273,16 @@ int32_t atasd_write(struct StoreDev *sd, const uint8_t *const buffer, ptrdiff_t
size_t sectors = size / STOREDEV_ATASD_SECTORSIZE;
if (size % STOREDEV_ATASD_SECTORSIZE) sectors++;
ata_setup(ata->devno, sectors, lba);
io_out8(ATA_REG_COMMAND, ATA_CMD_WRITE_PIO_EXT);
ata_setup(ata->iobase, ata->ctrlbase, ata->devno, sectors, lba);
io_out8(ata->iobase + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO_EXT);
for (size_t s = 0; s < sectors; s++) {
if (ata_wait(100000, true, 1) < 0) { ret = E_BADIO; goto done; }
io_outs16(ATA_REG_DATA, (uint16_t *)(buffer + s * STOREDEV_ATASD_SECTORSIZE), STOREDEV_ATASD_SECTORSIZE/2);
if (ata_wait(ata->iobase, 100000, true, 1) < 0) { ret = E_BADIO; goto done; }
io_outs16(ata->iobase + ATA_REG_DATA, (uint16_t *)(buffer + s * STOREDEV_ATASD_SECTORSIZE), STOREDEV_ATASD_SECTORSIZE/2);
}
io_out8(ATA_REG_COMMAND, ATA_CMD_CACHE_FLUSH_EXT);
if (ata_wait(100000, false, 1) < 0) { ret = E_BADIO; goto done; }
io_out8(ata->iobase + ATA_REG_COMMAND, ATA_CMD_CACHE_FLUSH_EXT);
if (ata_wait(ata->iobase, 100000, false, 1) < 0) { ret = E_BADIO; goto done; }
ret = E_OK;

View File

@ -12,11 +12,15 @@ struct StoreDev;
typedef struct {
int devno;
size_t capacity;
uint16_t iobase;
uint16_t ctrlbase;
} AtaSdInitExtra;
typedef struct {
int devno;
size_t capacity;
uint16_t iobase;
uint16_t ctrlbase;
} AtaSd;
int32_t atasd_init(struct StoreDev *sd, void *extra);

View File

@ -32,7 +32,10 @@ void storedev_register_dev_entry(StoreDev *sd, int32_t sdtype) {
if (sdtype == STOREDEV_RAMSD) {
ksprintf(key, "ramsd-%zu", ramsd_counter++);
} else if (sdtype == STOREDEV_ATASD) {
ksprintf(key, "atasd-ch0-%c", sd->sd.atasd.devno == 0x00 ? 'M' : 'S');
ksprintf(key, "atasd-ch%d-%c",
sd->sd.atasd.iobase == 0x1F0 ? 0 : 1,
sd->sd.atasd.devno == 0x00 ? 'M' : 'S'
);
}
spinlock_acquire(&DEVTABLE.spinlock);