]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
storage: add ATA PIO + persistfs mounted at /persist
authorTulio A M Mendes <[email protected]>
Sun, 8 Feb 2026 10:51:54 +0000 (07:51 -0300)
committerTulio A M Mendes <[email protected]>
Sun, 8 Feb 2026 10:51:54 +0000 (07:51 -0300)
Makefile
include/ata_pio.h [new file with mode: 0644]
include/errno.h
include/persistfs.h [new file with mode: 0644]
src/drivers/ata_pio.c [new file with mode: 0644]
src/kernel/init.c
src/kernel/persistfs.c [new file with mode: 0644]
user/init.c

index 0238493bf09ec1744ed826337754a047ece05e29..02b67abddbd6cf1602d85f5fd9ef60dd919208a8 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -129,7 +129,9 @@ $(INITRD_IMG): $(MKINITRD) $(USER_ELF) $(ECHO_ELF)
 
 run: iso
        @rm -f serial.log qemu.log
+       @test -f disk.img || dd if=/dev/zero of=disk.img bs=1M count=4 2>/dev/null
        @qemu-system-i386 -boot d -cdrom adros-$(ARCH).iso -m 128M -display none \
+               -drive file=disk.img,if=ide,format=raw \
                -serial file:serial.log -monitor none -no-reboot -no-shutdown \
                $(QEMU_DFLAGS)
 
diff --git a/include/ata_pio.h b/include/ata_pio.h
new file mode 100644 (file)
index 0000000..fa76bb2
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef ATA_PIO_H
+#define ATA_PIO_H
+
+#include <stdint.h>
+#include <stddef.h>
+
+int ata_pio_init_primary_master(void);
+int ata_pio_read28(uint32_t lba, uint8_t* buf512);
+int ata_pio_write28(uint32_t lba, const uint8_t* buf512);
+uint32_t ata_pio_sector_size(void);
+
+#endif
index fb82c75083e3881b66dc913b54704f18e9ec4283..44ba57621e80ff67b9f17b5936d66f1873f8f4fc 100644 (file)
@@ -10,6 +10,7 @@
 #define EFAULT 14
 #define EEXIST 17
 #define ENOMEM 12
+#define ENODEV 19
 #define ENOTDIR 20
 #define EINVAL 22
 #define ENOSPC 28
diff --git a/include/persistfs.h b/include/persistfs.h
new file mode 100644 (file)
index 0000000..0414a19
--- /dev/null
@@ -0,0 +1,8 @@
+#ifndef PERSISTFS_H
+#define PERSISTFS_H
+
+#include "fs.h"
+
+fs_node_t* persistfs_create_root(void);
+
+#endif
diff --git a/src/drivers/ata_pio.c b/src/drivers/ata_pio.c
new file mode 100644 (file)
index 0000000..6c33b93
--- /dev/null
@@ -0,0 +1,151 @@
+#include "ata_pio.h"
+
+#include "errno.h"
+#include "io.h"
+
+#if defined(__i386__)
+
+// Primary ATA bus I/O ports
+#define ATA_IO_BASE 0x1F0
+#define ATA_CTRL_BASE 0x3F6
+
+#define ATA_REG_DATA       0x00
+#define ATA_REG_ERROR      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_CMD_READ_SECTORS  0x20
+#define ATA_CMD_WRITE_SECTORS 0x30
+#define ATA_CMD_CACHE_FLUSH   0xE7
+#define ATA_CMD_IDENTIFY      0xEC
+
+#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_ERR  0x01
+
+static inline void io_wait_400ns(void) {
+    (void)inb((uint16_t)ATA_CTRL_BASE);
+    (void)inb((uint16_t)ATA_CTRL_BASE);
+    (void)inb((uint16_t)ATA_CTRL_BASE);
+    (void)inb((uint16_t)ATA_CTRL_BASE);
+}
+
+static int ata_wait_not_busy(void) {
+    for (int i = 0; i < 1000000; i++) {
+        uint8_t st = inb((uint16_t)(ATA_IO_BASE + ATA_REG_STATUS));
+        if ((st & ATA_SR_BSY) == 0) return 0;
+    }
+    return -EIO;
+}
+
+static int ata_wait_drq(void) {
+    for (int i = 0; i < 1000000; i++) {
+        uint8_t st = inb((uint16_t)(ATA_IO_BASE + ATA_REG_STATUS));
+        if (st & ATA_SR_ERR) return -EIO;
+        if (st & ATA_SR_DF) return -EIO;
+        if ((st & ATA_SR_BSY) == 0 && (st & ATA_SR_DRQ)) return 0;
+    }
+    return -EIO;
+}
+
+uint32_t ata_pio_sector_size(void) {
+    return 512;
+}
+
+int ata_pio_init_primary_master(void) {
+    // Select drive: 0xA0 = master, CHS mode bits set, LBA bit cleared
+    outb((uint16_t)(ATA_IO_BASE + ATA_REG_HDDEVSEL), 0xA0);
+    io_wait_400ns();
+
+    if (ata_wait_not_busy() < 0) return -EIO;
+
+    // IDENTIFY
+    outb((uint16_t)(ATA_IO_BASE + ATA_REG_SECCOUNT0), 0);
+    outb((uint16_t)(ATA_IO_BASE + ATA_REG_LBA0), 0);
+    outb((uint16_t)(ATA_IO_BASE + ATA_REG_LBA1), 0);
+    outb((uint16_t)(ATA_IO_BASE + ATA_REG_LBA2), 0);
+    outb((uint16_t)(ATA_IO_BASE + ATA_REG_COMMAND), ATA_CMD_IDENTIFY);
+
+    uint8_t st = inb((uint16_t)(ATA_IO_BASE + ATA_REG_STATUS));
+    if (st == 0) return -ENODEV;
+
+    if (ata_wait_drq() < 0) return -EIO;
+
+    // Read 256 words (512 bytes) identify data and discard.
+    for (int i = 0; i < 256; i++) {
+        (void)inw((uint16_t)(ATA_IO_BASE + ATA_REG_DATA));
+    }
+
+    return 0;
+}
+
+int ata_pio_read28(uint32_t lba, uint8_t* buf512) {
+    if (!buf512) return -EFAULT;
+    if (lba & 0xF0000000U) return -EINVAL;
+
+    if (ata_wait_not_busy() < 0) return -EIO;
+
+    outb((uint16_t)(ATA_IO_BASE + ATA_REG_HDDEVSEL), (uint8_t)(0xE0 | ((lba >> 24) & 0x0F)));
+    io_wait_400ns();
+
+    outb((uint16_t)(ATA_IO_BASE + ATA_REG_SECCOUNT0), 1);
+    outb((uint16_t)(ATA_IO_BASE + ATA_REG_LBA0), (uint8_t)(lba & 0xFF));
+    outb((uint16_t)(ATA_IO_BASE + ATA_REG_LBA1), (uint8_t)((lba >> 8) & 0xFF));
+    outb((uint16_t)(ATA_IO_BASE + ATA_REG_LBA2), (uint8_t)((lba >> 16) & 0xFF));
+    outb((uint16_t)(ATA_IO_BASE + ATA_REG_COMMAND), ATA_CMD_READ_SECTORS);
+
+    if (ata_wait_drq() < 0) return -EIO;
+
+    uint16_t* w = (uint16_t*)buf512;
+    for (int i = 0; i < 256; i++) {
+        w[i] = inw((uint16_t)(ATA_IO_BASE + ATA_REG_DATA));
+    }
+
+    io_wait_400ns();
+    return 0;
+}
+
+int ata_pio_write28(uint32_t lba, const uint8_t* buf512) {
+    if (!buf512) return -EFAULT;
+    if (lba & 0xF0000000U) return -EINVAL;
+
+    if (ata_wait_not_busy() < 0) return -EIO;
+
+    outb((uint16_t)(ATA_IO_BASE + ATA_REG_HDDEVSEL), (uint8_t)(0xE0 | ((lba >> 24) & 0x0F)));
+    io_wait_400ns();
+
+    outb((uint16_t)(ATA_IO_BASE + ATA_REG_SECCOUNT0), 1);
+    outb((uint16_t)(ATA_IO_BASE + ATA_REG_LBA0), (uint8_t)(lba & 0xFF));
+    outb((uint16_t)(ATA_IO_BASE + ATA_REG_LBA1), (uint8_t)((lba >> 8) & 0xFF));
+    outb((uint16_t)(ATA_IO_BASE + ATA_REG_LBA2), (uint8_t)((lba >> 16) & 0xFF));
+    outb((uint16_t)(ATA_IO_BASE + ATA_REG_COMMAND), ATA_CMD_WRITE_SECTORS);
+
+    if (ata_wait_drq() < 0) return -EIO;
+
+    const uint16_t* w = (const uint16_t*)buf512;
+    for (int i = 0; i < 256; i++) {
+        outw((uint16_t)(ATA_IO_BASE + ATA_REG_DATA), w[i]);
+    }
+
+    outb((uint16_t)(ATA_IO_BASE + ATA_REG_COMMAND), ATA_CMD_CACHE_FLUSH);
+    (void)ata_wait_not_busy();
+    io_wait_400ns();
+    return 0;
+}
+
+#else
+
+uint32_t ata_pio_sector_size(void) { return 512; }
+int ata_pio_init_primary_master(void) { return -ENOSYS; }
+int ata_pio_read28(uint32_t lba, uint8_t* buf512) { (void)lba; (void)buf512; return -ENOSYS; }
+int ata_pio_write28(uint32_t lba, const uint8_t* buf512) { (void)lba; (void)buf512; return -ENOSYS; }
+
+#endif
index afb5749f23500bf56ab1144e154ee53cc60a2077..c6a7dc37de5b14e770fd728f0c0636d6e5219364 100644 (file)
@@ -8,6 +8,7 @@
 #include "tmpfs.h"
 #include "devfs.h"
 #include "tty.h"
+#include "persistfs.h"
 #include "uart_console.h"
 
 #include "hal/mm.h"
@@ -69,6 +70,11 @@ int init_start(const struct boot_info* bi) {
         (void)vfs_mount("/dev", dev);
     }
 
+    fs_node_t* persist = persistfs_create_root();
+    if (persist) {
+        (void)vfs_mount("/persist", persist);
+    }
+
     int user_ret = arch_platform_start_userspace(bi);
 
     if (bi && cmdline_has_token(bi->cmdline, "ring3")) {
diff --git a/src/kernel/persistfs.c b/src/kernel/persistfs.c
new file mode 100644 (file)
index 0000000..76d7f50
--- /dev/null
@@ -0,0 +1,89 @@
+#include "persistfs.h"
+
+#include "ata_pio.h"
+#include "errno.h"
+#include "heap.h"
+#include "utils.h"
+
+// Minimal on-disk persistent storage:
+// - LBA0 reserved
+// - LBA1 holds one 512-byte file called "counter" (first 4 bytes are the counter value)
+
+#define PERSISTFS_LBA_COUNTER 1U
+
+static fs_node_t g_root;
+static fs_node_t g_counter;
+static uint32_t g_ready = 0;
+
+static uint32_t persist_counter_read(fs_node_t* node, uint32_t offset, uint32_t size, uint8_t* buffer) {
+    (void)node;
+    if (!buffer) return 0;
+    if (!g_ready) return 0;
+
+    uint8_t sec[512];
+    if (ata_pio_read28(PERSISTFS_LBA_COUNTER, sec) < 0) return 0;
+
+    if (offset >= 512U) return 0;
+    if (offset + size > 512U) size = 512U - offset;
+
+    memcpy(buffer, sec + offset, size);
+    return size;
+}
+
+static uint32_t persist_counter_write(fs_node_t* node, uint32_t offset, uint32_t size, const uint8_t* buffer) {
+    (void)node;
+    if (!buffer) return 0;
+    if (!g_ready) return 0;
+
+    if (offset >= 512U) return 0;
+    if (offset + size > 512U) size = 512U - offset;
+
+    uint8_t sec[512];
+    if (ata_pio_read28(PERSISTFS_LBA_COUNTER, sec) < 0) return 0;
+
+    memcpy(sec + offset, buffer, size);
+
+    if (ata_pio_write28(PERSISTFS_LBA_COUNTER, sec) < 0) return 0;
+    return size;
+}
+
+static struct fs_node* persist_root_finddir(struct fs_node* node, const char* name) {
+    (void)node;
+    if (!name || name[0] == 0) return 0;
+    if (strcmp(name, "counter") == 0) return &g_counter;
+    return 0;
+}
+
+fs_node_t* persistfs_create_root(void) {
+    if (!g_ready) {
+        if (ata_pio_init_primary_master() == 0) {
+            g_ready = 1;
+        } else {
+            g_ready = 0;
+        }
+
+        memset(&g_root, 0, sizeof(g_root));
+        strcpy(g_root.name, "persist");
+        g_root.flags = FS_DIRECTORY;
+        g_root.inode = 1;
+        g_root.length = 0;
+        g_root.read = 0;
+        g_root.write = 0;
+        g_root.open = 0;
+        g_root.close = 0;
+        g_root.finddir = &persist_root_finddir;
+
+        memset(&g_counter, 0, sizeof(g_counter));
+        strcpy(g_counter.name, "counter");
+        g_counter.flags = FS_FILE;
+        g_counter.inode = 2;
+        g_counter.length = 512;
+        g_counter.read = &persist_counter_read;
+        g_counter.write = &persist_counter_write;
+        g_counter.open = 0;
+        g_counter.close = 0;
+        g_counter.finddir = 0;
+    }
+
+    return g_ready ? &g_root : 0;
+}
index 5f798cc501110d39e42068642394790b3825dbd9..d128756e5dee5954c8ef68a31ac1e2b5b9b14e64 100644 (file)
@@ -1187,6 +1187,46 @@ void _start(void) {
         sys_write(1, "[init] /dev/null OK\n", (uint32_t)(sizeof("[init] /dev/null OK\n") - 1));
     }
 
+    // B1: persistent storage smoke. Value should increment across reboots (disk.img).
+    {
+        int fd = sys_open("/persist/counter", 0);
+        if (fd < 0) {
+            sys_write(1, "[init] /persist/counter open failed\n",
+                      (uint32_t)(sizeof("[init] /persist/counter open failed\n") - 1));
+            sys_exit(1);
+        }
+
+        (void)sys_lseek(fd, 0, SEEK_SET);
+        uint8_t b[4] = {0, 0, 0, 0};
+        int rd = sys_read(fd, b, 4);
+        if (rd != 4) {
+            sys_write(1, "[init] /persist/counter read failed\n",
+                      (uint32_t)(sizeof("[init] /persist/counter read failed\n") - 1));
+            sys_exit(1);
+        }
+
+        uint32_t v = (uint32_t)b[0] | ((uint32_t)b[1] << 8) | ((uint32_t)b[2] << 16) | ((uint32_t)b[3] << 24);
+        v++;
+        b[0] = (uint8_t)(v & 0xFF);
+        b[1] = (uint8_t)((v >> 8) & 0xFF);
+        b[2] = (uint8_t)((v >> 16) & 0xFF);
+        b[3] = (uint8_t)((v >> 24) & 0xFF);
+
+        (void)sys_lseek(fd, 0, SEEK_SET);
+        int wr = sys_write(fd, b, 4);
+        if (wr != 4) {
+            sys_write(1, "[init] /persist/counter write failed\n",
+                      (uint32_t)(sizeof("[init] /persist/counter write failed\n") - 1));
+            sys_exit(1);
+        }
+
+        (void)sys_close(fd);
+
+        sys_write(1, "[init] /persist/counter=", (uint32_t)(sizeof("[init] /persist/counter=") - 1));
+        write_int_dec((int)v);
+        sys_write(1, "\n", 1);
+    }
+
     {
         int fd = sys_open("/dev/tty", 0);
         if (fd < 0) {