diff options
author | Marvin Borner | 2021-01-08 22:49:54 +0100 |
---|---|---|
committer | Marvin Borner | 2021-01-08 22:49:54 +0100 |
commit | 45a9df836accd39cf2dbfbb2453496b0e4d93fa5 (patch) | |
tree | f2732f45d4b080644c1ab007be251c5b1eaf060e /kernel | |
parent | 3ad1fce1671c25c5db85977588fd1ceee436e1ba (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')
-rw-r--r-- | kernel/drivers/ide.c | 164 | ||||
-rw-r--r-- | kernel/features/fs.c | 93 | ||||
-rw-r--r-- | kernel/inc/fs.h | 10 | ||||
-rw-r--r-- | kernel/inc/ide.h | 84 | ||||
-rw-r--r-- | kernel/main.c | 2 |
5 files changed, 258 insertions, 95 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(); } diff --git a/kernel/features/fs.c b/kernel/features/fs.c index a4a1ef6..6278d50 100644 --- a/kernel/features/fs.c +++ b/kernel/features/fs.c @@ -9,8 +9,6 @@ #include <random.h> #include <str.h> -#include <cpu.h> // TODO: Remove later - /** * VFS */ @@ -26,6 +24,17 @@ char *vfs_normalize_path(const char *path) return fixed; } +u32 vfs_path_mounted(const char *path) +{ + struct node *iterator = mount_points->head; + while (iterator) { + if (!strcmp(((struct mount_info *)iterator->data)->path, path)) + return 1; + iterator = iterator->next; + } + return 0; +} + struct mount_info *vfs_recursive_find(char *path) { struct node *iterator = mount_points->head; @@ -166,7 +175,9 @@ struct device *device_get(u32 id) u32 devfs_read(const char *path, void *buf, u32 offset, u32 count, struct device *dev) { - return printf("%s - off: %d, cnt: %d, buf: %x, dev %x\n", path, offset, count, buf, dev); + assert(dev && dev->read); + printf("%s - off: %d, cnt: %d, buf: %x, dev %x\n", path, offset, count, buf, dev); + return dev->read(buf, offset, count, dev); } void device_install(void) @@ -185,16 +196,6 @@ void device_install(void) device_add(dev); vfs_mount(dev, "/dev/"); - vfs = malloc(sizeof(*vfs)); - vfs->type = VFS_EXT2; - vfs->read = ext2_read; - vfs->stat = ext2_stat; - dev = malloc(sizeof(*dev)); - dev->name = "/dev/hda"; // TODO: Use actual disk device - dev->vfs = vfs; - device_add(dev); - vfs_mount(dev, "/"); - /* vfs_list_mounts(); */ } @@ -202,29 +203,32 @@ void device_install(void) * EXT2 */ -void *buffer_read(u32 block) +void *buffer_read(u32 block, struct device *dev) { - return ide_read(malloc(BLOCK_SIZE), block); + void *buf = malloc(BLOCK_SIZE); + dev->read(buf, block << 1, 2, dev); + return buf; } -struct ext2_superblock *get_superblock(void) +struct ext2_superblock *get_superblock(struct device *dev) { - struct ext2_superblock *sb = buffer_read(EXT2_SUPER); + struct ext2_superblock *sb = buffer_read(EXT2_SUPER, dev); + if (sb->magic != EXT2_MAGIC) return NULL; return sb; } -struct ext2_bgd *get_bgd(void) +struct ext2_bgd *get_bgd(struct device *dev) { - return buffer_read(EXT2_SUPER + 1); + return buffer_read(EXT2_SUPER + 1, dev); } -struct ext2_inode *get_inode(u32 i) +struct ext2_inode *get_inode(u32 i, struct device *dev) { - struct ext2_superblock *s = get_superblock(); + struct ext2_superblock *s = get_superblock(dev); assert(s); - struct ext2_bgd *b = get_bgd(); + struct ext2_bgd *b = get_bgd(dev); assert(b); u32 block_group = (i - 1) / s->inodes_per_group; @@ -232,16 +236,16 @@ struct ext2_inode *get_inode(u32 i) u32 block = (index * EXT2_INODE_SIZE) / BLOCK_SIZE; b += block_group; - u32 *data = buffer_read(b->inode_table + block); + u32 *data = buffer_read(b->inode_table + block, dev); struct ext2_inode *in = (struct ext2_inode *)((u32)data + (index % (BLOCK_SIZE / EXT2_INODE_SIZE)) * EXT2_INODE_SIZE); return in; } -u32 read_indirect(u32 indirect, u32 block_num) +u32 read_indirect(u32 indirect, u32 block_num, struct device *dev) { - char *data = buffer_read(indirect); + char *data = buffer_read(indirect, dev); return *(u32 *)((u32)data + block_num * sizeof(u32)); } @@ -249,7 +253,6 @@ u32 read_inode(struct ext2_inode *in, void *buf, u32 offset, u32 count, struct d { // TODO: Support all read parameters (void)offset; - (void)dev; assert(in); if (!in) @@ -279,18 +282,20 @@ u32 read_inode(struct ext2_inode *in, void *buf, u32 offset, u32 count, struct d for (u32 i = 0; i < num_blocks; i++) { if (i < 12) { blocknum = in->block[i]; - data = buffer_read(blocknum); + data = buffer_read(blocknum, dev); memcpy((u32 *)((u32)buf + i * BLOCK_SIZE), data, BLOCK_SIZE); } else if (i < BLOCK_COUNT + 12) { indirect = in->block[12]; - blocknum = read_indirect(indirect, i - 12); - data = buffer_read(blocknum); + blocknum = read_indirect(indirect, i - 12, dev); + data = buffer_read(blocknum, dev); memcpy((u32 *)((u32)buf + i * BLOCK_SIZE), data, BLOCK_SIZE); } else { indirect = in->block[13]; - blocknum = read_indirect(indirect, (i - (BLOCK_COUNT + 12)) / BLOCK_COUNT); - blocknum = read_indirect(blocknum, (i - (BLOCK_COUNT + 12)) % BLOCK_COUNT); - data = buffer_read(blocknum); + blocknum = read_indirect(indirect, (i - (BLOCK_COUNT + 12)) / BLOCK_COUNT, + dev); + blocknum = read_indirect(blocknum, (i - (BLOCK_COUNT + 12)) % BLOCK_COUNT, + dev); + data = buffer_read(blocknum, dev); memcpy((u32 *)((u32)buf + i * BLOCK_SIZE), data, BLOCK_SIZE); } /* printf("Loaded %d of %d\n", i + 1, num_blocks); */ @@ -299,18 +304,18 @@ u32 read_inode(struct ext2_inode *in, void *buf, u32 offset, u32 count, struct d return count; } -u32 find_inode(const char *name, u32 dir_inode) +u32 find_inode(const char *name, u32 dir_inode, struct device *dev) { if (!dir_inode) return (unsigned)-1; - struct ext2_inode *i = get_inode(dir_inode); + struct ext2_inode *i = get_inode(dir_inode, dev); char *buf = malloc(BLOCK_SIZE * i->blocks / 2); memset(buf, 0, BLOCK_SIZE * i->blocks / 2); for (u32 q = 0; q < i->blocks / 2; q++) { - char *data = buffer_read(i->block[q]); + char *data = buffer_read(i->block[q], dev); memcpy((u32 *)((u32)buf + q * BLOCK_SIZE), data, BLOCK_SIZE); } @@ -332,7 +337,7 @@ u32 find_inode(const char *name, u32 dir_inode) return (unsigned)-1; } -struct ext2_inode *find_inode_by_path(const char *path) +struct ext2_inode *find_inode_by_path(const char *path, struct device *dev) { if (path[0] != '/') return 0; @@ -352,7 +357,7 @@ struct ext2_inode *find_inode_by_path(const char *path) break; path_cp[i] = '\0'; - current_inode = find_inode(path_cp, current_inode); + current_inode = find_inode(path_cp, current_inode, dev); path_cp[i] = '/'; if (current_inode == 0) { @@ -363,17 +368,17 @@ struct ext2_inode *find_inode_by_path(const char *path) path_cp += i + 1; } - u32 inode = find_inode(path_cp, current_inode); + u32 inode = find_inode(path_cp, current_inode, dev); free(init); if ((signed)inode <= 0) return 0; - return get_inode(inode); + return get_inode(inode, dev); } u32 ext2_read(const char *path, void *buf, u32 offset, u32 count, struct device *dev) { - struct ext2_inode *in = find_inode_by_path(path); + struct ext2_inode *in = find_inode_by_path(path, dev); if (in) return read_inode(in, buf, offset, count, dev); else @@ -383,11 +388,11 @@ u32 ext2_read(const char *path, void *buf, u32 offset, u32 count, struct device u32 ext2_stat(const char *path, struct stat *buf, struct device *dev) { if (!buf) - return 0; + return 1; - struct ext2_inode *in = find_inode_by_path(path); + struct ext2_inode *in = find_inode_by_path(path, dev); if (!in) - return 0; + return 1; u32 num_blocks = in->blocks / (BLOCK_SIZE / SECTOR_SIZE); u32 sz = BLOCK_SIZE * num_blocks; @@ -395,5 +400,5 @@ u32 ext2_stat(const char *path, struct stat *buf, struct device *dev) buf->dev_id = dev->id; buf->size = sz; // Actually in->size but ext2.. - return 1; + return 0; } diff --git a/kernel/inc/fs.h b/kernel/inc/fs.h index 06154e7..75cb49f 100644 --- a/kernel/inc/fs.h +++ b/kernel/inc/fs.h @@ -10,17 +10,22 @@ * Device */ +enum dev_type { DEV_BLOCK, DEV_CHAR }; + struct device { u32 id; const char *name; - int type; // TODO: Block, char device + enum dev_type type; struct vfs *vfs; + void *data; u32 (*read)(void *buf, u32 offset, u32 count, struct device *dev); u32 (*write)(void *buf, u32 offset, u32 count, struct device *dev); }; void device_install(void); +void device_add(struct device *dev); + /** * VFS */ @@ -42,6 +47,9 @@ struct mount_info { void vfs_install(void); +u32 vfs_path_mounted(const char *path); +u32 vfs_mount(struct device *dev, const char *path); + u32 vfs_read(const char *path, void *buf, u32 offset, u32 count); u32 vfs_write(const char *path, void *buf, u32 offset, u32 count); u32 vfs_stat(const char *path, struct stat *buf); diff --git a/kernel/inc/ide.h b/kernel/inc/ide.h index c145760..6e43ece 100644 --- a/kernel/inc/ide.h +++ b/kernel/inc/ide.h @@ -9,32 +9,66 @@ #define BLOCK_COUNT 256 // BLOCK_SIZE / sizeof(u32) #define SECTOR_SIZE 512 -#define IDE_BUSY (1 << 7) -#define IDE_READY (1 << 6) -#define IDE_DRIVE_FAULT (1 << 5) -#define IDE_ERROR (1 << 0) +#define ATA_PRIMARY_IO 0x1f0 +#define ATA_SECONDARY_IO 0x170 -#define IDE_IO 0x1F0 -#define IDE_DATA 0x0 -#define IDE_FEATURES 0x1 -#define IDE_SECTOR_COUNT 0x2 -#define IDE_LOW 0x3 -#define IDE_MID 0x4 -#define IDE_HIGH 0x5 -#define IDE_SELECT 0x6 -#define IDE_CMD 0x7 -#define IDE_ALTERNATE 0x3F6 +// From spec +#define ATA_PRIMARY 0x00 +#define ATA_SECONDARY 0x01 +#define ATA_READ 0x00 +#define ATA_WRITE 0x013 +#define ATA_MASTER 0x00 +#define ATA_SLAVE 0x01 +#define ATA_SR_BSY 0x80 +#define ATA_SR_DRDY 0x40 +#define ATA_SR_DF 0x20 +#define ATA_SR_DSC 0x10 +#define ATA_SR_DRQ 0x08 +#define ATA_SR_CORR 0x04 +#define ATA_SR_IDX 0x02 +#define ATA_SR_ERR 0x01 +#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_SECCOUNT1 0x08 +#define ATA_REG_LBA3 0x09 +#define ATA_REG_LBA4 0x0a +#define ATA_REG_LBA5 0x0b +#define ATA_REG_CONTROL 0x0c +#define ATA_REG_ALTSTATUS 0x0c +#define ATA_REG_DEVADDRESS 0x0d +#define ATA_CMD_READ_PIO 0x20 +#define ATA_CMD_READ_PIO_EXT 0x24 +#define ATA_CMD_READ_DMA 0xc8 +#define ATA_CMD_READ_DMA_EXT 0x25 +#define ATA_CMD_WRITE_PIO 0x30 +#define ATA_CMD_WRITE_PIO_EXT 0x34 +#define ATA_CMD_WRITE_DMA 0xca +#define ATA_CMD_WRITE_DMA_EXT 0x35 +#define ATA_CMD_CACHE_FLUSH 0xe7 +#define ATA_CMD_CACHE_FLUSH_EXT 0xea +#define ATA_CMD_PACKET 0xa0 +#define ATA_CMD_IDENTIFY_PACKET 0xa1 +#define ATA_CMD_IDENTIFY 0xec +#define ATA_IDENT_DEVICETYPE 0 +#define ATA_IDENT_CYLINDERS 2 +#define ATA_IDENT_HEADS 6 +#define ATA_IDENT_SECTORS 12 +#define ATA_IDENT_SERIAL 20 +#define ATA_IDENT_MODEL 54 +#define ATA_IDENT_CAPABILITIES 98 +#define ATA_IDENT_FIELDVALID 106 +#define ATA_IDENT_MAX_LBA 120 +#define ATA_IDENT_COMMANDSETS 164 +#define ATA_IDENT_MAX_LBA_EXT 200 -#define LBA_LOW(c) ((u8)(c & 0xFF)) -#define LBA_MID(c) ((u8)(c >> 8) & 0xFF) -#define LBA_HIGH(c) ((u8)(c >> 16) & 0xFF) -#define LBA_LAST(c) ((u8)(c >> 24) & 0xF) - -#define IDE_CMD_READ (BLOCK_SIZE / SECTOR_SIZE == 1) ? 0x20 : 0xC4 -#define IDE_CMD_WRITE (BLOCK_SIZE / SECTOR_SIZE == 1) ? 0x30 : 0xC5 -#define IDE_CMD_READ_MUL 0xC4 -#define IDE_CMD_WRITE_MUL 0xC5 - -void *ide_read(void *b, u32 block); +void ata_install(void); #endif diff --git a/kernel/main.c b/kernel/main.c index b0e1a15..563cae7 100644 --- a/kernel/main.c +++ b/kernel/main.c @@ -3,6 +3,7 @@ #include <boot.h> #include <cpu.h> #include <fs.h> +#include <ide.h> #include <interrupts.h> #include <keyboard.h> #include <load.h> @@ -29,6 +30,7 @@ void kernel_main(struct vid_info *vid_info) // Install drivers vfs_install(); device_install(); + ata_install(); pci_install(); interrupts_install(); timer_install(); |