--- /dev/null
+#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
--- /dev/null
+#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;
+}
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) {