diff options
author | Anton Kling <anton@kling.gg> | 2023-11-13 19:20:36 +0100 |
---|---|---|
committer | Anton Kling <anton@kling.gg> | 2023-11-13 19:23:58 +0100 |
commit | 1de1e8d02f392ed8bb061705d7717351bf3023bd (patch) | |
tree | 2f44b4e79d62225b27a8823bdfd9634ba5cb6a4e /kernel | |
parent | 1966047921e2958469f9faac1bd1f56d1fc5587a (diff) |
Kernel: Add support for AHCI and make use of it with the current filesystem.
It will now also create a corresponding /dev/sd* device for each
detected SATA drive. The filesystem still writes using the ATA driver.
This should be fixed soon.
Diffstat (limited to 'kernel')
-rw-r--r-- | kernel/drivers/ahci.c | 66 | ||||
-rw-r--r-- | kernel/fs/ext2.c | 13 | ||||
-rw-r--r-- | kernel/fs/vfs.h | 1 | ||||
-rw-r--r-- | kernel/init/kernel.c | 5 |
4 files changed, 67 insertions, 18 deletions
diff --git a/kernel/drivers/ahci.c b/kernel/drivers/ahci.c index d660f55..60b9363 100644 --- a/kernel/drivers/ahci.c +++ b/kernel/drivers/ahci.c @@ -13,6 +13,10 @@ // https://wiki.osdev.org/ATA_Command_Matrix #define ATA_CMD_READ_DMA_EX 0x25 +volatile struct HBA_MEM *hba; + +const u16 num_prdt = 8; + typedef enum { FIS_TYPE_REG_H2D = 0x27, // Register FIS - host to device FIS_TYPE_REG_D2H = 0x34, // Register FIS - device to host @@ -246,8 +250,8 @@ void ahci_set_base(volatile struct HBA_PORT *port, u32 virt_clb_address, struct HBA_CMD_HEADER *cmdheader = (struct HBA_CMD_HEADER *)(virt_clb_address); for (u8 i = 0; i < 32; i++) { - cmdheader[i].prdtl = 8; // 8 prdt entries per command table - // 256 bytes per command table, 64+16+48+16*8 + cmdheader[i].prdtl = num_prdt; // 8 prdt entries per command table + // 256 bytes per command table, 64+16+48+16*8 cmdheader[i].ctba = command_table_array + i * 256; cmdheader[i].ctbau = 0; } @@ -294,8 +298,12 @@ u8 get_free_command_slot(volatile struct HBA_PORT *port, u8 *err) { return 0; } -u8 ahci_read(volatile struct HBA_PORT *port, u32 startl, u32 starth, u32 count, - u16 *outbuffer) { +u8 ahci_raw_read(volatile struct HBA_PORT *port, u32 startl, u32 starth, + u32 count, u16 *outbuffer) { + // TODO: The number of PRDT tables are hardcoded at a seemingly + // very low number. It can be up to 65,535. Should it maybe be + // changed? + assert(count <= num_prdt); port->is = -1; // Clear pending interrupts u8 err; u32 command_slot = get_free_command_slot(port, &err); @@ -356,10 +364,10 @@ u8 ahci_read(volatile struct HBA_PORT *port, u32 startl, u32 starth, u32 count, // The below loop waits until the port is no longer busy before issuing a new // command u32 spin = 0; - while ((port->tfd & (ATA_DEV_BUSY | ATA_DEV_DRQ)) && spin < 1000000) { + while ((port->tfd & (ATA_DEV_BUSY | ATA_DEV_DRQ)) && spin < 10000) { spin++; } - if (spin == 1000000) { + if (spin == 10000) { kprintf("Port is hung\n"); return 0; } @@ -388,13 +396,43 @@ u8 ahci_read(volatile struct HBA_PORT *port, u32 startl, u32 starth, u32 count, return 1; } -void ahci_test(volatile struct HBA_PORT *port) { - char buffer[1024]; - memset(buffer, 1, 1024); - assert(ahci_read(port, 0, 0, 2, (u16 *)buffer)); - for (u32 i = 0; i < 1024; i++) { - kprintf("%x", buffer[i]); +int ahci_read(u8 *buffer, u64 offset, u64 len, vfs_fd_t *fd) { + vfs_inode_t *inode = fd->inode; + int port = inode->inode_num; + assert(port == 0); + u32 lba = offset / 512; + offset %= 512; + int rc = len; + + u32 sector_count = len / 512; + if (len % 512 != 0) + sector_count++; + u8 tmp_buffer[512 * num_prdt]; + for (; sector_count >= num_prdt; lba++) { + ahci_raw_read(&hba->ports[port], lba, 0, num_prdt, (u16 *)tmp_buffer); + memcpy(buffer, tmp_buffer + offset, 512 * num_prdt); + offset = 0; + buffer += num_prdt * 512; + len -= num_prdt * 512; + sector_count -= num_prdt; + } + + if (sector_count > 0) { + ahci_raw_read(&hba->ports[port], lba, 0, sector_count, (u16 *)tmp_buffer); + memcpy(buffer, tmp_buffer + offset, len); } + return rc; +} + +void add_devfs_drive_file(u8 port) { + static u8 num_drives_added = 0; + char *path = "/sda"; + path[strlen(path)] += num_drives_added; + num_drives_added++; + vfs_inode_t *inode = + devfs_add_file(path, ahci_read, NULL /*write*/, NULL /*get_vm_object*/, + 1 /*has_data*/, 0 /*can_write*/, FS_TYPE_BLOCK_DEVICE); + inode->inode_num = port; } void ahci_init(void) { @@ -409,7 +447,7 @@ void ahci_init(void) { pci_get_bar(&device, 5, &bar); u8 *HBA_base = mmu_map_frames((void *)bar.address, bar.size); - volatile struct HBA_MEM *hba = (volatile struct HBA_MEM *)(HBA_base); + hba = (volatile struct HBA_MEM *)(HBA_base); for (u8 i = 0; i < 32; i++) { if (!((hba->pi >> i) & 1)) continue; @@ -417,7 +455,7 @@ void ahci_init(void) { switch (type) { case AHCI_DEV_SATA: ahci_sata_setup(&hba->ports[i]); - ahci_test(&hba->ports[i]); + add_devfs_drive_file(i); kprintf("SATA drive found at port %d\n", i); break; case AHCI_DEV_SATAPI: diff --git a/kernel/fs/ext2.c b/kernel/fs/ext2.c index 8092b23..17d9e11 100644 --- a/kernel/fs/ext2.c +++ b/kernel/fs/ext2.c @@ -14,6 +14,8 @@ u32 block_byte_size; u32 inode_size; u32 inodes_per_block; +vfs_fd_t *mount_fd = NULL; + #define BLOCK_SIZE (block_byte_size) void ext2_close(vfs_fd_t *fd) { @@ -55,7 +57,7 @@ void cached_read_block(u32 block, void *address, size_t size, size_t offset) { struct BLOCK_CACHE *c = &cache[free_found]; c->block_num = block; - read_lba(block * block_byte_size / 512, c->block, 1024, 0); + raw_vfs_pread(mount_fd, c->block, 1024, block * block_byte_size); cached_read_block(block, address, size, offset); } @@ -740,6 +742,11 @@ int ext2_create_file(const char *path, int mode) { } vfs_inode_t *ext2_mount(void) { + int fd = vfs_open("/dev/sda", O_RDWR, 0); + // TODO: Can this be done better? Maybe create a seperate function in + // the VFS? + mount_fd = get_current_task()->file_descriptors[fd]; + get_current_task()->file_descriptors[fd] = NULL; parse_superblock(); return vfs_create_inode(0 /*inode_num*/, 0 /*type*/, 0 /*has_data*/, 0 /*can_write*/, 0 /*is_open*/, @@ -751,7 +758,9 @@ vfs_inode_t *ext2_mount(void) { void parse_superblock(void) { superblock = ksbrk(2 * SECTOR_SIZE); - read_lba(EXT2_SUPERBLOCK_SECTOR, (void *)superblock, 2 * SECTOR_SIZE, 0); + raw_vfs_pread(mount_fd, superblock, 2 * SECTOR_SIZE, + EXT2_SUPERBLOCK_SECTOR * 512); + block_byte_size = 1024 << superblock->block_size; if (0xEF53 != superblock->ext2_signature) { diff --git a/kernel/fs/vfs.h b/kernel/fs/vfs.h index 0ce5cd1..949d175 100644 --- a/kernel/fs/vfs.h +++ b/kernel/fs/vfs.h @@ -78,6 +78,7 @@ void vfs_mount(char *path, vfs_inode_t *local_root); int vfs_pwrite(int fd, void *buf, u64 count, u64 offset); int raw_vfs_pwrite(vfs_fd_t *vfs_fd, void *buf, u64 count, u64 offset); +int raw_vfs_pread(vfs_fd_t *vfs_fd, void *buf, u64 count, u64 offset); int vfs_pread(int fd, void *buf, u64 count, u64 offset); vfs_vm_object_t *vfs_get_vm_object(int fd, u64 length, u64 offset); int vfs_dup2(int org_fd, int new_fd); diff --git a/kernel/init/kernel.c b/kernel/init/kernel.c index e9c8ea1..24da680 100644 --- a/kernel/init/kernel.c +++ b/kernel/init/kernel.c @@ -23,7 +23,6 @@ #include <sched/scheduler.h> #include <stdbool.h> #include <stddef.h> -#include <typedefs.h> #include <stdio.h> #include <string.h> #include <typedefs.h> @@ -82,8 +81,10 @@ void kernel_main(u32 kernel_end, unsigned long magic, unsigned long addr, install_keyboard(); klog("PS2 Keyboard driver installed", LOG_SUCCESS); - vfs_mount("/", ext2_mount()); vfs_mount("/dev", devfs_mount()); + ahci_init(); + vfs_mount("/", ext2_mount()); + add_stdout(); add_serial(); add_random_devices(); |