feat: block device support (mount device and partitions)
[lunaix-os.git] / lunaix-os / kernel / block.c
diff --git a/lunaix-os/kernel/block.c b/lunaix-os/kernel/block.c
new file mode 100644 (file)
index 0000000..0e28f66
--- /dev/null
@@ -0,0 +1,119 @@
+#include <hal/ahci/hba.h>
+#include <klibc/string.h>
+#include <lib/crc.h>
+#include <lunaix/block.h>
+#include <lunaix/mm/cake.h>
+#include <lunaix/mm/valloc.h>
+#include <lunaix/syslog.h>
+
+#define BLOCK_EREAD 1
+#define BLOCK_ESIG 2
+#define BLOCK_ECRC 3
+#define BLOCK_EFULL 3
+
+LOG_MODULE("BLOCK")
+
+#define MAX_DEV 32
+
+struct cake_pile* lbd_pile;
+struct block_dev** dev_registry;
+
+int free_slot = 0;
+
+int
+__block_mount_partitions(struct hba_device* hd_dev);
+
+int
+__block_register(struct block_dev* dev);
+
+void
+block_init()
+{
+    lbd_pile = cake_new_pile("block_dev", sizeof(struct block_dev), 1, 0);
+    dev_registry = vcalloc(sizeof(struct block_dev*), MAX_DEV);
+}
+
+int
+block_mount_disk(struct hba_device* hd_dev)
+{
+    int errno = 0;
+    struct block_dev* dev = cake_grab(lbd_pile);
+    strncpy(dev->name, hd_dev->model, PARTITION_NAME_SIZE);
+    dev->hd_dev = hd_dev;
+    dev->base_lba = 0;
+    dev->end_lba = hd_dev->max_lba;
+    if (!__block_register(dev)) {
+        errno = BLOCK_EFULL;
+        goto error;
+    }
+
+    if ((errno = __block_mount_partitions(hd_dev))) {
+        goto error;
+    }
+
+error:
+    kprintf(KERROR "Fail to mount hd: %s[%s] (%x)",
+            hd_dev->model,
+            hd_dev->serial_num,
+            -errno);
+}
+
+int
+__block_mount_partitions(struct hba_device* hd_dev)
+{
+    int errno = 0;
+    void* buffer = valloc_dma(hd_dev->block_size);
+    if (!hd_dev->ops.read_buffer(hd_dev, 1, buffer, hd_dev->block_size)) {
+        errno = BLOCK_EREAD;
+        goto done;
+    }
+
+    struct lpt_header* header = (struct lpt_header*)buffer;
+    if (header->signature != LPT_SIG) {
+        errno = BLOCK_ESIG;
+        goto done;
+    }
+
+    if (header->crc != crc32b(buffer, sizeof(*header))) {
+        errno = BLOCK_ECRC;
+        goto done;
+    }
+
+    uint64_t lba = header->pt_start_lba;
+    int j = 0;
+    int count_per_sector = hd_dev->block_size / sizeof(struct lpt_entry);
+    while (lba < header->pt_end_lba) {
+        if (!hd_dev->ops.read_buffer(hd_dev, lba, buffer, hd_dev->block_size)) {
+            errno = BLOCK_EREAD;
+            goto done;
+        }
+        struct lpt_entry* entry = (struct lpt_entry*)buffer;
+        for (int i = 0; i < count_per_sector; i++, j++) {
+            struct block_dev* dev = cake_grab(lbd_pile);
+            strncpy(dev->name, entry->part_name, PARTITION_NAME_SIZE);
+            dev->hd_dev = hd_dev;
+            dev->base_lba = entry->base_lba;
+            dev->end_lba = entry->end_lba;
+            __block_register(dev);
+
+            if (j >= header->table_len) {
+                goto done;
+            }
+        }
+    }
+
+done:
+    vfree_dma(buffer);
+    return -errno;
+}
+
+int
+__block_register(struct block_dev* dev)
+{
+    if (free_slot >= MAX_DEV) {
+        return 0;
+    }
+
+    dev_registry[free_slot++] = dev;
+    return 1;
+}
\ No newline at end of file