aboutsummaryrefslogtreecommitdiff
path: root/kernel/drivers
diff options
context:
space:
mode:
authorMarvin Borner2021-01-08 22:49:54 +0100
committerMarvin Borner2021-01-08 22:49:54 +0100
commit45a9df836accd39cf2dbfbb2453496b0e4d93fa5 (patch)
treef2732f45d4b080644c1ab007be251c5b1eaf060e /kernel/drivers
parent3ad1fce1671c25c5db85977588fd1ceee436e1ba (diff)
Major IDE/ATA driver rewrite
This adds non-hardcoded multi-disk support. I just need to remove the boot/load.c loader and fix the max bootloader ext2 loading size. After that's done I'll try running it on real hardware.
Diffstat (limited to 'kernel/drivers')
-rw-r--r--kernel/drivers/ide.c164
1 files changed, 139 insertions, 25 deletions
diff --git a/kernel/drivers/ide.c b/kernel/drivers/ide.c
index 227dfe5..6f73b13 100644
--- a/kernel/drivers/ide.c
+++ b/kernel/drivers/ide.c
@@ -1,46 +1,160 @@
// MIT License, Copyright (c) 2020 Marvin Borner
+#include <assert.h>
#include <cpu.h>
#include <def.h>
+#include <fs.h>
#include <ide.h>
+#include <mem.h>
#include <print.h>
-int ide_stat()
+static u8 *ide_buf = NULL;
+
+struct ata_data {
+ u8 drive;
+};
+
+void ide_select_drive(u8 bus, u8 drive)
{
- inb(IDE_IO + IDE_CMD);
- inb(IDE_IO + IDE_CMD);
- inb(IDE_IO + IDE_CMD);
- inb(IDE_IO + IDE_CMD);
- return inb(IDE_IO + IDE_CMD);
+ if (bus == ATA_PRIMARY) {
+ if (drive == ATA_MASTER)
+ outb(ATA_PRIMARY_IO + ATA_REG_HDDEVSEL, 0xa0);
+ else
+ outb(ATA_PRIMARY_IO + ATA_REG_HDDEVSEL, 0xb0);
+ } else {
+ if (drive == ATA_MASTER)
+ outb(ATA_SECONDARY_IO + ATA_REG_HDDEVSEL, 0xa0);
+ else
+ outb(ATA_SECONDARY_IO + ATA_REG_HDDEVSEL, 0xb0);
+ }
}
-void ide_wait()
+u8 ide_find(u8 bus, u8 drive)
{
- int stat = 0;
- do
- stat = ide_stat();
- while ((stat & IDE_BUSY) != 0);
+ u16 io = bus == ATA_PRIMARY ? ATA_PRIMARY_IO : ATA_SECONDARY_IO;
+ ide_select_drive(bus, drive);
+
+ // Reset
+ outb(io + ATA_REG_SECCOUNT0, 0);
+ outb(io + ATA_REG_LBA0, 0);
+ outb(io + ATA_REG_LBA1, 0);
+ outb(io + ATA_REG_LBA2, 0);
+
+ // Identify
+ outb(io + ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
+ u8 status = inb(io + ATA_REG_STATUS);
+ if (!status)
+ return 0;
+
+ while ((inb(io + ATA_REG_STATUS) & ATA_SR_BSY) != 0)
+ ;
+
+ do {
+ status = inb(io + ATA_REG_STATUS);
+ if (status & ATA_SR_ERR)
+ return 0;
+ } while ((status & ATA_SR_DRQ) == 0);
+
+ for (int i = 0; i < 256; i++)
+ *(u16 *)(ide_buf + i * 2) = inw(io + ATA_REG_DATA);
+
+ return 1;
}
-// TODO: Fix strange ide_read bugs
-void *ide_read(void *b, u32 block)
+void ide_delay(u16 io) // 400ns
{
- u8 sector_count = BLOCK_SIZE / SECTOR_SIZE; // 2
- u32 sector = block * sector_count;
+ for (int i = 0; i < 4; i++)
+ inb(io + ATA_REG_ALTSTATUS);
+}
- outb(IDE_IO + IDE_SECTOR_COUNT, (u8)sector_count); // Number of sectors
+void ide_poll(u16 io)
+{
+ for (int i = 0; i < 4; i++)
+ inb(io + ATA_REG_ALTSTATUS);
- outb(IDE_IO + IDE_LOW, LBA_LOW(sector));
- outb(IDE_IO + IDE_MID, LBA_MID(sector));
- outb(IDE_IO + IDE_HIGH, LBA_HIGH(sector));
+ u8 status;
+ do {
+ status = inb(io + ATA_REG_STATUS);
+ } while (status & ATA_SR_BSY);
- // Slave/Master << 4 and last 4 bits
- outb(IDE_IO + IDE_SELECT, 0xE0 | (1 << 4) | LBA_LAST(sector));
+ do {
+ status = inb(io + ATA_REG_STATUS);
+ assert(!(status & ATA_SR_ERR))
+ } while (!(status & ATA_SR_DRQ));
+}
- outb(IDE_IO + IDE_CMD, IDE_CMD_READ);
+u8 ata_read_one(u8 *buf, u32 lba, struct device *dev)
+{
+ u8 drive = ((struct ata_data *)dev->data)->drive;
+ u16 io = (drive & ATA_PRIMARY << 1) == ATA_PRIMARY ? ATA_PRIMARY_IO : ATA_SECONDARY_IO;
+ drive = (drive & ATA_SLAVE) == ATA_SLAVE ? ATA_SLAVE : ATA_MASTER;
+ u8 cmd = drive == ATA_MASTER ? 0xe0 : 0xf0;
+ outb(io + ATA_REG_HDDEVSEL, (cmd | (u8)((lba >> 24 & 0x0f))));
+ outb(io + 1, 0x00);
+ outb(io + ATA_REG_SECCOUNT0, 1);
+ outb(io + ATA_REG_LBA0, (u8)lba);
+ outb(io + ATA_REG_LBA1, (u8)(lba >> 8));
+ outb(io + ATA_REG_LBA2, (u8)(lba >> 16));
+ outb(io + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
+ ide_poll(io);
- ide_wait();
- insl(IDE_IO, b, BLOCK_SIZE / 4);
+ for (int i = 0; i < 256; i++) {
+ u16 data = inw(io + ATA_REG_DATA);
+ *(u16 *)(buf + i * 2) = data;
+ }
+ ide_delay(io);
+ return 1;
+}
- return b;
+u32 ata_read(void *buf, u32 lba, u32 numsects, struct device *dev)
+{
+ u8 *b = buf; // I love bytes, yk
+ for (u32 i = 0; i < numsects; i++) {
+ ata_read_one(b, lba + i, dev);
+ b += 512;
+ }
+ return numsects;
+}
+
+int ata_pm = 0, ata_ps = 0, ata_sm = 0, ata_ss = 0;
+void ata_probe(void)
+{
+ for (int i = 0; i < 4; i++) {
+ int bus = i < 2 ? ATA_PRIMARY : ATA_SECONDARY;
+ int drive = i % 2 ? ATA_MASTER : ATA_SLAVE;
+
+ if (!ide_find(bus, drive))
+ continue;
+
+ struct device *dev = malloc(sizeof(*dev));
+ struct ata_data *data = malloc(sizeof(*data));
+ data->drive = (bus << 1) | drive;
+
+ char *str = malloc(40);
+ for (int j = 0; j < 40; j += 2) {
+ str[j] = ide_buf[ATA_IDENT_MODEL + j + 1];
+ str[j + 1] = ide_buf[ATA_IDENT_MODEL + j];
+ }
+
+ dev->name = str;
+ dev->type = DEV_BLOCK;
+ dev->read = ata_read;
+ device_add(dev);
+ if (vfs_path_mounted("/"))
+ continue;
+
+ // TODO: Check if ext2 first
+ struct vfs *vfs = malloc(sizeof(*vfs));
+ vfs->type = VFS_EXT2;
+ vfs->read = ext2_read;
+ vfs->stat = ext2_stat;
+ dev->vfs = vfs;
+ vfs_mount(dev, "/");
+ }
+}
+
+void ata_install(void)
+{
+ ide_buf = malloc(512);
+ ata_probe();
}