summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorAnton Kling <anton@kling.gg>2023-11-13 19:20:36 +0100
committerAnton Kling <anton@kling.gg>2023-11-13 19:23:58 +0100
commit1de1e8d02f392ed8bb061705d7717351bf3023bd (patch)
tree2f44b4e79d62225b27a8823bdfd9634ba5cb6a4e /kernel
parent1966047921e2958469f9faac1bd1f56d1fc5587a (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.c66
-rw-r--r--kernel/fs/ext2.c13
-rw-r--r--kernel/fs/vfs.h1
-rw-r--r--kernel/init/kernel.c5
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();