]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
feat: multi-drive ATA support (4 drives) + fstab/mount command
authorTulio A M Mendes <[email protected]>
Fri, 13 Feb 2026 08:07:10 +0000 (05:07 -0300)
committerTulio A M Mendes <[email protected]>
Fri, 13 Feb 2026 08:07:10 +0000 (05:07 -0300)
Major ATA driver refactoring:

1. ATA PIO driver (ata_pio.h, hal/x86/ata_pio.c):
   - Support all 4 ATA drives: primary/master (hda), primary/slave (hdb),
     secondary/master (hdc), secondary/slave (hdd)
   - New API: ata_pio_init() probes both channels, ata_pio_read28/write28
     take a drive ID parameter, ata_pio_drive_present() query
   - ata_name_to_drive/ata_drive_to_name helpers for device name mapping
   - Per-channel I/O ports (0x1F0/0x3F6 primary, 0x170/0x376 secondary)
   - Master/slave selection (0xE0/0xF0 LBA mode bits)
   - Floating bus detection (0xFF = no controller)
   - ATAPI rejection (non-zero LBA1/LBA2 after IDENTIFY)

2. ATA DMA driver (ata_dma.h, hal/x86/ata_dma.c):
   - Per-channel DMA state: separate PRDT, bounce buffer, spinlock, IRQ
     handler for primary (BAR4+0) and secondary (BAR4+8)
   - KVA map extended: 4 pages (PRDT+buf x 2 channels) at 0xC0320000-3
   - PCI Bus Master probe factored out and cached (single PCI lookup)
   - DMA-aware IRQ handlers for both IRQ 14 and IRQ 15

3. Filesystem drivers updated to accept drive parameter:
   - fat_mount(int drive, uint32_t lba) — stores drive in g_fat struct
   - ext2_mount(int drive, uint32_t lba) — stores drive in g_ext2 struct
   - diskfs_create_root(int drive) — static g_diskfs_drive for all I/O
   - persistfs_create_root(int drive) — delegates to diskfs on same drive

4. Mount system redesigned — no more auto-mount of disk FS:
   - init.c: calls ata_pio_init() once, then parses /etc/fstab
   - Fstab format: /dev/hdX  /mountpoint  fstype  options
   - init_mount_fs() helper reusable by both fstab and kconsole
   - rootfs/etc/fstab created with default diskfs+persistfs on hda

5. kconsole mount command:
   - 'mount -t <type> /dev/<hd> <mountpoint>' for manual mounting
   - 'lsblk' lists all 4 ATA drives and their detection status
   - Help text updated with new commands

6. Weak stubs updated for new API signatures (src/drivers/ata_pio.c)
7. Mount table increased from 8 to 16 slots (previous commit)

Build: clean, cppcheck: clean, smoke: 19/19 pass

20 files changed:
Makefile
include/ata_dma.h
include/ata_pio.h
include/diskfs.h
include/ext2.h
include/fat.h
include/kernel/init.h
include/kernel_va_map.h
include/persistfs.h
rootfs/etc/fstab [new file with mode: 0644]
src/drivers/ata_pio.c
src/hal/x86/ata_dma.c
src/hal/x86/ata_pio.c
src/kernel/diskfs.c
src/kernel/ext2.c
src/kernel/fat.c
src/kernel/init.c
src/kernel/kconsole.c
src/kernel/persistfs.c
tests/smoke_test.exp

index 94f5b0e4195394cc220de4c1a7934f9a066e2db2..ba43e6347fad008e4cd37f326c60c0e9a094d96c 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -178,8 +178,9 @@ $(RM_ELF): user/rm.c user/linker.ld
 $(LDSO_ELF): user/ldso.c user/linker.ld
        @i686-elf-gcc -m32 -I include -ffreestanding -fno-pie -no-pie -nostdlib -Wl,-T,user/linker.ld -o $(LDSO_ELF) user/ldso.c
 
-INITRD_FILES := $(USER_ELF):bin/init.elf $(ECHO_ELF):bin/echo.elf $(SH_ELF):bin/sh $(CAT_ELF):bin/cat $(LS_ELF):bin/ls $(MKDIR_ELF):bin/mkdir $(RM_ELF):bin/rm $(LDSO_ELF):lib/ld.so
-INITRD_DEPS := $(MKINITRD) $(USER_ELF) $(ECHO_ELF) $(SH_ELF) $(CAT_ELF) $(LS_ELF) $(MKDIR_ELF) $(RM_ELF) $(LDSO_ELF)
+FSTAB := rootfs/etc/fstab
+INITRD_FILES := $(USER_ELF):bin/init.elf $(ECHO_ELF):bin/echo.elf $(SH_ELF):bin/sh $(CAT_ELF):bin/cat $(LS_ELF):bin/ls $(MKDIR_ELF):bin/mkdir $(RM_ELF):bin/rm $(LDSO_ELF):lib/ld.so $(FSTAB):etc/fstab
+INITRD_DEPS := $(MKINITRD) $(USER_ELF) $(ECHO_ELF) $(SH_ELF) $(CAT_ELF) $(LS_ELF) $(MKDIR_ELF) $(RM_ELF) $(LDSO_ELF) $(FSTAB)
 
 # Include doom.elf if it has been built
 ifneq ($(wildcard $(DOOM_ELF)),)
index fef3b4792ebf1f2a7cf86fac60f2d5aaa663cef9..1105292e8f2f6980388496edc2f2fb31eaf82abb 100644 (file)
@@ -3,27 +3,35 @@
 
 #include <stdint.h>
 
-/* Try to initialize ATA Bus Master DMA on the primary channel.
+/* ATA channel indices */
+#define ATA_CHANNEL_PRIMARY   0
+#define ATA_CHANNEL_SECONDARY 1
+#define ATA_NUM_CHANNELS      2
+
+/* Try to initialize ATA Bus Master DMA for the given channel.
  * Returns 0 on success, negative errno on failure (no PCI IDE, etc.).
  * If DMA init fails, PIO mode remains available. */
-int ata_dma_init(void);
+int ata_dma_init(int channel);
 
-/* Returns 1 if DMA is available and initialized. */
-int ata_dma_available(void);
+/* Returns 1 if DMA is available and initialized for the given channel. */
+int ata_dma_available(int channel);
 
 /* DMA read: read one sector (512 bytes) at LBA into buf.
- * buf must be a kernel virtual address (will be translated to physical).
+ * channel: 0=primary, 1=secondary.  slave: 0=master, 1=slave.
+ * buf must be a kernel virtual address.
  * Returns 0 on success, negative errno on failure. */
-int ata_dma_read28(uint32_t lba, uint8_t* buf512);
+int ata_dma_read28(int channel, int slave, uint32_t lba, uint8_t* buf512);
 
 /* DMA write: write one sector (512 bytes) from buf to LBA.
  * Returns 0 on success, negative errno on failure. */
-int ata_dma_write28(uint32_t lba, const uint8_t* buf512);
+int ata_dma_write28(int channel, int slave, uint32_t lba, const uint8_t* buf512);
 
 /* Zero-copy DMA: read/write using a caller-provided physical address.
  * phys_buf must be 32-bit aligned, below 4GB, and not cross a 64KB boundary.
  * Returns 0 on success, negative errno on failure. */
-int ata_dma_read_direct(uint32_t lba, uint32_t phys_buf, uint16_t byte_count);
-int ata_dma_write_direct(uint32_t lba, uint32_t phys_buf, uint16_t byte_count);
+int ata_dma_read_direct(int channel, int slave, uint32_t lba,
+                        uint32_t phys_buf, uint16_t byte_count);
+int ata_dma_write_direct(int channel, int slave, uint32_t lba,
+                         uint32_t phys_buf, uint16_t byte_count);
 
 #endif
index fa76bb2fab3c34879735efe24e184ee36fe07e4e..2d66717b33eba8c123ad7173baf0e8628c5e4d82 100644 (file)
@@ -4,9 +4,34 @@
 #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);
+/* ATA drive identifiers */
+#define ATA_DEV_PRIMARY_MASTER    0
+#define ATA_DEV_PRIMARY_SLAVE     1
+#define ATA_DEV_SECONDARY_MASTER  2
+#define ATA_DEV_SECONDARY_SLAVE   3
+#define ATA_MAX_DRIVES            4
+
+/* Initialize both ATA channels and probe all 4 drives.
+ * Returns 0 if at least one drive was found, negative errno otherwise. */
+int ata_pio_init(void);
+
+/* Returns 1 if the given drive was detected during init. */
+int ata_pio_drive_present(int drive);
+
+/* Read one 512-byte sector from the specified drive at the given LBA.
+ * Returns 0 on success, negative errno on failure. */
+int ata_pio_read28(int drive, uint32_t lba, uint8_t* buf512);
+
+/* Write one 512-byte sector to the specified drive at the given LBA.
+ * Returns 0 on success, negative errno on failure. */
+int ata_pio_write28(int drive, uint32_t lba, const uint8_t* buf512);
+
 uint32_t ata_pio_sector_size(void);
 
+/* Map device name ("hda".."hdd") to drive ID. Returns -1 if invalid. */
+int ata_name_to_drive(const char* name);
+
+/* Map drive ID to device name. Returns NULL if invalid. */
+const char* ata_drive_to_name(int drive);
+
 #endif
index 4e97031dee58115ec701d9d00844bd661c611120..b8ba45ed84c159ca7fe0b807ee210922f608dde1 100644 (file)
@@ -4,7 +4,7 @@
 #include "fs.h"
 #include <stdint.h>
 
-fs_node_t* diskfs_create_root(void);
+fs_node_t* diskfs_create_root(int drive);
 
 // Open (and optionally create) a diskfs file at the root (flat namespace).
 // rel_path must not contain '/'.
index e52b28a90f850080a2056b018eb0ded81bca6ec9..e12f823547fa4d71c56dad83785f75399f7d9575 100644 (file)
@@ -4,8 +4,8 @@
 #include "fs.h"
 #include <stdint.h>
 
-/* Mount an ext2 filesystem starting at the given LBA offset on disk.
- * Returns a VFS root node or NULL on failure. */
-fs_node_t* ext2_mount(uint32_t partition_lba);
+/* Mount an ext2 filesystem on the given ATA drive starting at the given
+ * LBA offset.  Returns a VFS root node or NULL on failure. */
+fs_node_t* ext2_mount(int drive, uint32_t partition_lba);
 
 #endif
index deb5686d58a79c4c0fbab5972088d82efeaf149c..dac0266ec75ce4c2f00c4d26a5e7fda3e0155bcc 100644 (file)
@@ -4,9 +4,9 @@
 #include "fs.h"
 #include <stdint.h>
 
-/* Mount a FAT12/16/32 filesystem starting at the given LBA offset on disk.
- * Auto-detects FAT type from BPB.
+/* Mount a FAT12/16/32 filesystem on the given ATA drive starting at
+ * the given LBA offset.  Auto-detects FAT type from BPB.
  * Returns a VFS root node or NULL on failure. */
-fs_node_t* fat_mount(uint32_t partition_lba);
+fs_node_t* fat_mount(int drive, uint32_t partition_lba);
 
 #endif
index b5fe3a32e4d56835283d423663eade28bae551fe..228fe701f4bd0ae0f74812f563057829a85655a5 100644 (file)
@@ -2,7 +2,16 @@
 #define KERNEL_INIT_H
 
 #include "kernel/boot_info.h"
+#include <stdint.h>
 
 int init_start(const struct boot_info* bi);
 
+/* Mount a filesystem on the given ATA drive at the given mountpoint.
+ * fstype: "diskfs", "fat", "ext2", "persistfs"
+ * drive: ATA_DEV_PRIMARY_MASTER .. ATA_DEV_SECONDARY_SLAVE
+ * lba: partition start LBA (0 for whole disk)
+ * mountpoint: e.g. "/disk", "/fat", "/ext2"
+ * Returns 0 on success, -1 on failure. */
+int init_mount_fs(const char* fstype, int drive, uint32_t lba, const char* mountpoint);
+
 #endif
index b1c462e3e50bfbbee9dcc92147e503e2a10ee07b..3360b99141c80125a86175a90a0418040a25ba86 100644 (file)
 #define KVA_ACPI_TMP_BASE   0xC0300000U
 #define KVA_ACPI_TMP_PAGES  16
 
-/* ATA DMA (hal/x86/ata_dma.c) */
-#define KVA_ATA_DMA_PRDT    0xC0320000U
-#define KVA_ATA_DMA_BUF     0xC0321000U
+/* ATA DMA (hal/x86/ata_dma.c) — two pages per channel (PRDT + bounce buf) */
+#define KVA_ATA_DMA_PRDT_PRI  0xC0320000U
+#define KVA_ATA_DMA_BUF_PRI   0xC0321000U
+#define KVA_ATA_DMA_PRDT_SEC  0xC0322000U
+#define KVA_ATA_DMA_BUF_SEC   0xC0323000U
 
 /* E1000 NIC (drivers/e1000.c) */
 #define KVA_E1000_MMIO      0xC0330000U
index 0414a194c888149405db8af732ee9f05b4746339..c187617a1eb58e8c460afceeffe5fb27c60bfe9d 100644 (file)
@@ -3,6 +3,6 @@
 
 #include "fs.h"
 
-fs_node_t* persistfs_create_root(void);
+fs_node_t* persistfs_create_root(int drive);
 
 #endif
diff --git a/rootfs/etc/fstab b/rootfs/etc/fstab
new file mode 100644 (file)
index 0000000..c7cd658
--- /dev/null
@@ -0,0 +1,4 @@
+# /etc/fstab — AdrOS filesystem table
+# <device>      <mountpoint>  <fstype>     <options>
+/dev/hda        /disk         diskfs       defaults
+/dev/hda        /persist      persistfs    defaults
index d9197d6ba96b6dd9a8335a495980c7d849ba9719..62c7fd3c9ec2d0afce78a33b38949f7b729739b4 100644 (file)
@@ -1,14 +1,28 @@
 #include "ata_pio.h"
 #include "errno.h"
+#include "utils.h"
 
 __attribute__((weak))
 uint32_t ata_pio_sector_size(void) { return 512; }
 
 __attribute__((weak))
-int ata_pio_init_primary_master(void) { return -ENOSYS; }
+int ata_pio_init(void) { return -ENOSYS; }
 
 __attribute__((weak))
-int ata_pio_read28(uint32_t lba, uint8_t* buf512) { (void)lba; (void)buf512; return -ENOSYS; }
+int ata_pio_drive_present(int drive) { (void)drive; return 0; }
 
 __attribute__((weak))
-int ata_pio_write28(uint32_t lba, const uint8_t* buf512) { (void)lba; (void)buf512; return -ENOSYS; }
+int ata_pio_read28(int drive, uint32_t lba, uint8_t* buf512) {
+    (void)drive; (void)lba; (void)buf512; return -ENOSYS;
+}
+
+__attribute__((weak))
+int ata_pio_write28(int drive, uint32_t lba, const uint8_t* buf512) {
+    (void)drive; (void)lba; (void)buf512; return -ENOSYS;
+}
+
+__attribute__((weak))
+int ata_name_to_drive(const char* name) { (void)name; return -1; }
+
+__attribute__((weak))
+const char* ata_drive_to_name(int drive) { (void)drive; return 0; }
index 896a625de40dcd803c95b06c0c4df0fa0b1195c7..e69048b2ce3567858071f465cdcf01dd7ad097ae 100644 (file)
 #include <stdint.h>
 #include <stddef.h>
 
-/* ATA I/O ports (primary channel) */
-#define ATA_IO_BASE     0x1F0
-#define ATA_CTRL_BASE   0x3F6
-
-#define ATA_REG_DATA       0x00
-#define ATA_REG_ERROR      0x01
+/* ATA register offsets */
 #define ATA_REG_SECCOUNT0  0x02
 #define ATA_REG_LBA0       0x03
 #define ATA_REG_LBA1       0x04
 #define ATA_SR_DRQ   0x08
 #define ATA_SR_ERR   0x01
 
-/* Bus Master IDE register offsets (from BAR4) */
-#define BM_CMD      0x00   /* Command register (byte) */
-#define BM_STATUS   0x02   /* Status register (byte) */
-#define BM_PRDT     0x04   /* PRDT address (dword) */
+/* Bus Master IDE register offsets (from channel BM base) */
+#define BM_CMD      0x00
+#define BM_STATUS   0x02
+#define BM_PRDT     0x04
 
-/* BM_CMD bits */
 #define BM_CMD_START  0x01
-#define BM_CMD_READ   0x08  /* 1 = device-to-memory (read), 0 = memory-to-device (write) */
+#define BM_CMD_READ   0x08
 
-/* BM_STATUS bits */
-#define BM_STATUS_ACTIVE  0x01  /* Bus master active */
-#define BM_STATUS_ERR     0x02  /* Error */
-#define BM_STATUS_IRQ     0x04  /* Interrupt (write 1 to clear) */
+#define BM_STATUS_ACTIVE  0x01
+#define BM_STATUS_ERR     0x02
+#define BM_STATUS_IRQ     0x04
 
 /* Physical Region Descriptor (PRD) entry */
 struct prd_entry {
-    uint32_t phys_addr;   /* Physical buffer address */
-    uint16_t byte_count;  /* Byte count (0 = 64KB) */
-    uint16_t flags;       /* Bit 15 = end of table */
+    uint32_t phys_addr;
+    uint16_t byte_count;
+    uint16_t flags;
 } __attribute__((packed));
 
-/* State */
-static int dma_available = 0;
-static uint16_t bm_base = 0;           /* Bus Master I/O base port */
-static struct prd_entry* prdt = NULL;   /* PRDT (kernel virtual) */
-static uint32_t prdt_phys = 0;         /* PRDT physical address */
-static uint8_t* dma_buf = NULL;        /* 512-byte DMA bounce buffer (kernel virtual) */
-static uint32_t dma_buf_phys = 0;      /* DMA bounce buffer physical address */
-
-static volatile int dma_active = 0;  /* Set during DMA polling to prevent IRQ handler race */
-static spinlock_t dma_lock = {0};
-
-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);
+/* Per-channel I/O bases */
+static const uint16_t ch_io[ATA_NUM_CHANNELS]   = { 0x1F0, 0x170 };
+static const uint16_t ch_ctrl[ATA_NUM_CHANNELS]  = { 0x3F6, 0x376 };
+static const uint8_t  ch_irq[ATA_NUM_CHANNELS]   = { 46, 47 };
+
+/* Per-channel KVA addresses for PRDT and bounce buffer */
+static const uint32_t ch_kva_prdt[ATA_NUM_CHANNELS] = {
+    KVA_ATA_DMA_PRDT_PRI, KVA_ATA_DMA_PRDT_SEC
+};
+static const uint32_t ch_kva_buf[ATA_NUM_CHANNELS] = {
+    KVA_ATA_DMA_BUF_PRI, KVA_ATA_DMA_BUF_SEC
+};
+
+/* Per-channel DMA state */
+struct dma_ch_state {
+    int available;
+    uint16_t bm_base;
+    struct prd_entry* prdt;
+    uint32_t prdt_phys;
+    uint8_t* dma_buf;
+    uint32_t dma_buf_phys;
+    volatile int dma_active;
+    spinlock_t lock;
+};
+
+static struct dma_ch_state dma_ch[ATA_NUM_CHANNELS];
+
+static inline void io_wait_400ns_ch(int channel) {
+    (void)inb(ch_ctrl[channel]);
+    (void)inb(ch_ctrl[channel]);
+    (void)inb(ch_ctrl[channel]);
+    (void)inb(ch_ctrl[channel]);
 }
 
-static int ata_wait_not_busy(void) {
+static int ata_wait_not_busy_ch(int channel) {
+    uint16_t io = ch_io[channel];
     for (int i = 0; i < 1000000; i++) {
-        uint8_t st = inb((uint16_t)(ATA_IO_BASE + ATA_REG_STATUS));
+        uint8_t st = inb((uint16_t)(io + ATA_REG_STATUS));
         if ((st & ATA_SR_BSY) == 0) return 0;
     }
     return -EIO;
 }
 
-/* IRQ 14 handler (vector 46): acknowledge device interrupt.
- * During active DMA polling, only clear BM status (polling loop handles ATA status).
- * During PIO operations, read ATA status to deassert INTRQ. */
-static void ata_irq14_handler(struct registers* regs) {
+/* IRQ handlers — clear BM IRQ bit during DMA, or just read ATA status */
+static void ata_dma_irq14(struct registers* regs) {
+    (void)regs;
+    struct dma_ch_state* s = &dma_ch[ATA_CHANNEL_PRIMARY];
+    if (s->dma_active && s->bm_base) {
+        uint8_t bm_stat = inb((uint16_t)(s->bm_base + BM_STATUS));
+        outb((uint16_t)(s->bm_base + BM_STATUS), bm_stat | BM_STATUS_IRQ);
+    } else {
+        (void)inb((uint16_t)(ch_io[0] + ATA_REG_STATUS));
+    }
+}
+
+static void ata_dma_irq15(struct registers* regs) {
     (void)regs;
-    if (dma_active) {
-        /* DMA polling loop is handling completion — just clear BM IRQ bit */
-        uint8_t bm_stat = inb((uint16_t)(bm_base + BM_STATUS));
-        outb((uint16_t)(bm_base + BM_STATUS), bm_stat | BM_STATUS_IRQ);
+    struct dma_ch_state* s = &dma_ch[ATA_CHANNEL_SECONDARY];
+    if (s->dma_active && s->bm_base) {
+        uint8_t bm_stat = inb((uint16_t)(s->bm_base + BM_STATUS));
+        outb((uint16_t)(s->bm_base + BM_STATUS), bm_stat | BM_STATUS_IRQ);
     } else {
-        /* PIO mode — read ATA status to deassert INTRQ */
-        (void)inb((uint16_t)(ATA_IO_BASE + ATA_REG_STATUS));
+        (void)inb((uint16_t)(ch_io[1] + ATA_REG_STATUS));
     }
 }
 
-int ata_dma_init(void) {
-    if (dma_available) return 0;  /* Already initialized */
+/* PCI Bus Master base — shared between both channels, offset 0x08 for secondary */
+static uint16_t pci_bm_base = 0;
+static int pci_bm_probed = 0;
+
+static int ata_dma_probe_pci(void) {
+    if (pci_bm_probed) return pci_bm_base ? 0 : -ENODEV;
+    pci_bm_probed = 1;
 
-    /* Find IDE controller: PCI class 0x01 (Mass Storage), subclass 0x01 (IDE) */
     const struct pci_device* ide = pci_find_class(0x01, 0x01);
-    if (!ide) {
-        kprintf("[ATA-DMA] No PCI IDE controller found.\n");
-        return -ENODEV;
-    }
+    if (!ide) return -ENODEV;
 
-    /* BAR4 contains the Bus Master IDE I/O base */
     uint32_t bar4 = ide->bar[4];
-    if ((bar4 & 1) == 0) {
-        kprintf("[ATA-DMA] BAR4 is not I/O space.\n");
-        return -ENODEV;
-    }
-    bm_base = (uint16_t)(bar4 & 0xFFFC);
+    if ((bar4 & 1) == 0) return -ENODEV;
 
-    if (bm_base == 0) {
-        kprintf("[ATA-DMA] BAR4 I/O base is zero.\n");
-        return -ENODEV;
-    }
+    pci_bm_base = (uint16_t)(bar4 & 0xFFFC);
+    if (pci_bm_base == 0) return -ENODEV;
 
-    /* Enable PCI Bus Mastering (bit 2 of command register) + I/O space (bit 0) */
+    /* Enable PCI Bus Mastering + I/O space */
     uint32_t cmd_reg = pci_config_read(ide->bus, ide->slot, ide->func, 0x04);
-    cmd_reg |= (1U << 0) | (1U << 2);  /* I/O Space Enable + Bus Master Enable */
+    cmd_reg |= (1U << 0) | (1U << 2);
     pci_config_write(ide->bus, ide->slot, ide->func, 0x04, cmd_reg);
 
-    /* Allocate PRDT: one page, physically contiguous, aligned.
-     * We only need 1 PRD entry (8 bytes) but allocate a full page. */
+    return 0;
+}
+
+int ata_dma_init(int channel) {
+    if (channel < 0 || channel >= ATA_NUM_CHANNELS) return -EINVAL;
+    struct dma_ch_state* s = &dma_ch[channel];
+    if (s->available) return 0;
+
+    if (ata_dma_probe_pci() < 0) return -ENODEV;
+
+    /* Primary at BAR4+0, secondary at BAR4+8 */
+    s->bm_base = pci_bm_base + (uint16_t)(channel * 8);
+
+    /* Allocate PRDT page */
     void* prdt_page = pmm_alloc_page();
-    if (!prdt_page) {
-        kprintf("[ATA-DMA] Failed to allocate PRDT page.\n");
-        return -ENOMEM;
-    }
-    prdt_phys = (uint32_t)(uintptr_t)prdt_page;
-    /* Map PRDT at a dedicated VA — see include/kernel_va_map.h */
-    uintptr_t prdt_virt = KVA_ATA_DMA_PRDT;
-    vmm_map_page((uint64_t)prdt_phys, (uint64_t)prdt_virt,
+    if (!prdt_page) return -ENOMEM;
+
+    s->prdt_phys = (uint32_t)(uintptr_t)prdt_page;
+    uintptr_t prdt_virt = ch_kva_prdt[channel];
+    vmm_map_page((uint64_t)s->prdt_phys, (uint64_t)prdt_virt,
                  VMM_FLAG_PRESENT | VMM_FLAG_RW);
-    prdt = (struct prd_entry*)prdt_virt;
-    memset((void*)prdt, 0, PAGE_SIZE);
+    s->prdt = (struct prd_entry*)prdt_virt;
+    memset((void*)s->prdt, 0, PAGE_SIZE);
 
-    /* Allocate DMA bounce buffer: one page for sector transfers */
+    /* Allocate bounce buffer page */
     void* buf_page = pmm_alloc_page();
     if (!buf_page) {
-        kprintf("[ATA-DMA] Failed to allocate DMA buffer page.\n");
         pmm_free_page(prdt_page);
         return -ENOMEM;
     }
-    dma_buf_phys = (uint32_t)(uintptr_t)buf_page;
-    uintptr_t buf_virt = KVA_ATA_DMA_BUF;
-    vmm_map_page((uint64_t)dma_buf_phys, (uint64_t)buf_virt,
+
+    s->dma_buf_phys = (uint32_t)(uintptr_t)buf_page;
+    uintptr_t buf_virt = ch_kva_buf[channel];
+    vmm_map_page((uint64_t)s->dma_buf_phys, (uint64_t)buf_virt,
                  VMM_FLAG_PRESENT | VMM_FLAG_RW);
-    dma_buf = (uint8_t*)buf_virt;
+    s->dma_buf = (uint8_t*)buf_virt;
 
-    /* Set up the single PRD entry: 512 bytes, end-of-table */
-    prdt[0].phys_addr = dma_buf_phys;
-    prdt[0].byte_count = 512;
-    prdt[0].flags = 0x8000;  /* EOT bit */
+    /* Set up single PRD entry: 512 bytes, EOT */
+    s->prdt[0].phys_addr = s->dma_buf_phys;
+    s->prdt[0].byte_count = 512;
+    s->prdt[0].flags = 0x8000;
 
-    /* Register IRQ 14 handler to acknowledge device interrupts */
-    register_interrupt_handler(46, ata_irq14_handler);
+    /* Register DMA-aware IRQ handler for this channel */
+    if (channel == 0) {
+        register_interrupt_handler(ch_irq[0], ata_dma_irq14);
+    } else {
+        register_interrupt_handler(ch_irq[1], ata_dma_irq15);
+    }
 
     /* Stop any in-progress DMA and clear status */
-    outb((uint16_t)(bm_base + BM_CMD), 0);
-    outb((uint16_t)(bm_base + BM_STATUS), BM_STATUS_IRQ | BM_STATUS_ERR);
-
-    dma_available = 1;
+    outb((uint16_t)(s->bm_base + BM_CMD), 0);
+    outb((uint16_t)(s->bm_base + BM_STATUS), BM_STATUS_IRQ | BM_STATUS_ERR);
 
-    kprintf("[ATA-DMA] Initialized, BM I/O base=0x%x\n", (unsigned)bm_base);
+    s->available = 1;
+    kprintf("[ATA-DMA] Ch%d initialized, BM I/O base=0x%x\n",
+            channel, (unsigned)s->bm_base);
 
     return 0;
 }
 
-int ata_dma_available(void) {
-    return dma_available;
+int ata_dma_available(int channel) {
+    if (channel < 0 || channel >= ATA_NUM_CHANNELS) return 0;
+    return dma_ch[channel].available;
 }
 
-/* Common DMA transfer setup and wait */
-static int ata_dma_transfer(uint32_t lba, int is_write) {
+/* Common per-channel DMA transfer */
+static int ata_dma_transfer(int channel, int slave, uint32_t lba, int is_write) {
     if (lba & 0xF0000000U) return -EINVAL;
+    struct dma_ch_state* s = &dma_ch[channel];
+    uint16_t io   = ch_io[channel];
+    uint16_t ctrl = ch_ctrl[channel];
 
-    /* Ensure nIEN is cleared so the device asserts INTRQ on completion.
-     * The Bus Master IRQ bit won't be set without INTRQ. */
-    outb((uint16_t)ATA_CTRL_BASE, 0x00);
+    /* Clear nIEN so device asserts INTRQ */
+    outb(ctrl, 0x00);
 
     /* Read ATA status to clear any pending interrupt */
-    (void)inb((uint16_t)(ATA_IO_BASE + ATA_REG_STATUS));
+    (void)inb((uint16_t)(io + ATA_REG_STATUS));
 
     /* Set PRDT address */
-    outl((uint16_t)(bm_base + BM_PRDT), prdt_phys);
+    outl((uint16_t)(s->bm_base + BM_PRDT), s->prdt_phys);
 
     /* Clear status bits */
-    outb((uint16_t)(bm_base + BM_STATUS), BM_STATUS_IRQ | BM_STATUS_ERR);
+    outb((uint16_t)(s->bm_base + BM_STATUS), BM_STATUS_IRQ | BM_STATUS_ERR);
 
     /* Wait for drive not busy */
-    if (ata_wait_not_busy() < 0) return -EIO;
+    if (ata_wait_not_busy_ch(channel) < 0) return -EIO;
 
     /* Select drive + LBA */
-    outb((uint16_t)(ATA_IO_BASE + ATA_REG_HDDEVSEL),
-         (uint8_t)(0xE0 | ((lba >> 24) & 0x0F)));
-    io_wait_400ns();
+    uint8_t sel = slave ? 0xF0 : 0xE0;
+    outb((uint16_t)(io + ATA_REG_HDDEVSEL), (uint8_t)(sel | ((lba >> 24) & 0x0F)));
+    io_wait_400ns_ch(channel);
 
     /* Sector count and LBA */
-    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)(io + ATA_REG_SECCOUNT0), 1);
+    outb((uint16_t)(io + ATA_REG_LBA0), (uint8_t)(lba & 0xFF));
+    outb((uint16_t)(io + ATA_REG_LBA1), (uint8_t)((lba >> 8) & 0xFF));
+    outb((uint16_t)(io + ATA_REG_LBA2), (uint8_t)((lba >> 16) & 0xFF));
 
-    /* Mark DMA as active so IRQ handler doesn't race on ATA status */
-    __atomic_store_n(&dma_active, 1, __ATOMIC_SEQ_CST);
+    __atomic_store_n(&s->dma_active, 1, __ATOMIC_SEQ_CST);
 
-    /* Step 3: Set direction bit in BM Command (without Start bit yet) */
+    /* Set direction bit (without Start) */
     uint8_t bm_dir = is_write ? 0x00 : BM_CMD_READ;
-    outb((uint16_t)(bm_base + BM_CMD), bm_dir);
+    outb((uint16_t)(s->bm_base + BM_CMD), bm_dir);
 
-    /* Step 4: Issue ATA DMA command to the device */
-    outb((uint16_t)(ATA_IO_BASE + ATA_REG_COMMAND),
+    /* Issue ATA DMA command */
+    outb((uint16_t)(io + ATA_REG_COMMAND),
          is_write ? ATA_CMD_WRITE_DMA : ATA_CMD_READ_DMA);
 
-    /* Step 5: Set Start bit to begin the DMA transfer */
-    outb((uint16_t)(bm_base + BM_CMD), bm_dir | BM_CMD_START);
+    /* Start DMA */
+    outb((uint16_t)(s->bm_base + BM_CMD), bm_dir | BM_CMD_START);
 
-    /* Poll for DMA completion:
-     * 1. Wait for ATA BSY to clear (device finished processing)
-     * 2. Check BM status for Active bit clear (DMA engine done)
-     * 3. Check for errors */
+    /* Poll for completion */
     int completed = 0;
     for (int i = 0; i < 2000000; i++) {
-        uint8_t ata_stat = inb((uint16_t)(ATA_IO_BASE + ATA_REG_STATUS));
-        uint8_t bm_stat = inb((uint16_t)(bm_base + BM_STATUS));
+        uint8_t ata_stat = inb((uint16_t)(io + ATA_REG_STATUS));
+        uint8_t bm_stat  = inb((uint16_t)(s->bm_base + BM_STATUS));
 
-        /* Check for Bus Master error */
         if (bm_stat & BM_STATUS_ERR) {
-            outb((uint16_t)(bm_base + BM_CMD), 0);
-            outb((uint16_t)(bm_base + BM_STATUS), BM_STATUS_IRQ | BM_STATUS_ERR);
-            kprintf("[ATA-DMA] Bus master error!\n");
+            outb((uint16_t)(s->bm_base + BM_CMD), 0);
+            outb((uint16_t)(s->bm_base + BM_STATUS), BM_STATUS_IRQ | BM_STATUS_ERR);
+            __atomic_store_n(&s->dma_active, 0, __ATOMIC_SEQ_CST);
             return -EIO;
         }
-
-        /* Check for ATA error */
         if (ata_stat & ATA_SR_ERR) {
-            outb((uint16_t)(bm_base + BM_CMD), 0);
-            outb((uint16_t)(bm_base + BM_STATUS), BM_STATUS_IRQ | BM_STATUS_ERR);
-            kprintf("[ATA-DMA] ATA error during DMA!\n");
+            outb((uint16_t)(s->bm_base + BM_CMD), 0);
+            outb((uint16_t)(s->bm_base + BM_STATUS), BM_STATUS_IRQ | BM_STATUS_ERR);
+            __atomic_store_n(&s->dma_active, 0, __ATOMIC_SEQ_CST);
             return -EIO;
         }
-
-        /* DMA complete when: BSY clear AND BM Active clear */
         if (!(ata_stat & ATA_SR_BSY) && !(bm_stat & BM_STATUS_ACTIVE)) {
             completed = 1;
             break;
         }
     }
 
-    /* Stop Bus Master */
-    outb((uint16_t)(bm_base + BM_CMD), 0);
-
-    /* Clear status bits */
-    outb((uint16_t)(bm_base + BM_STATUS), BM_STATUS_IRQ | BM_STATUS_ERR);
-
-    __atomic_store_n(&dma_active, 0, __ATOMIC_SEQ_CST);
-
-    if (!completed) {
-        kprintf("[ATA-DMA] Transfer timeout!\n");
-        return -EIO;
-    }
+    outb((uint16_t)(s->bm_base + BM_CMD), 0);
+    outb((uint16_t)(s->bm_base + BM_STATUS), BM_STATUS_IRQ | BM_STATUS_ERR);
+    __atomic_store_n(&s->dma_active, 0, __ATOMIC_SEQ_CST);
 
-    return 0;
+    return completed ? 0 : -EIO;
 }
 
-int ata_dma_read28(uint32_t lba, uint8_t* buf512) {
+int ata_dma_read28(int channel, int slave, uint32_t lba, uint8_t* buf512) {
     if (!buf512) return -EFAULT;
-    if (!dma_available) return -ENOSYS;
-
-    spin_lock(&dma_lock);
-
-    int ret = ata_dma_transfer(lba, 0);
-    if (ret == 0) {
-        /* Copy from DMA bounce buffer to caller's buffer */
-        memcpy(buf512, dma_buf, 512);
-    }
-
-    spin_unlock(&dma_lock);
+    if (channel < 0 || channel >= ATA_NUM_CHANNELS) return -EINVAL;
+    struct dma_ch_state* s = &dma_ch[channel];
+    if (!s->available) return -ENOSYS;
+
+    spin_lock(&s->lock);
+    int ret = ata_dma_transfer(channel, slave, lba, 0);
+    if (ret == 0) memcpy(buf512, s->dma_buf, 512);
+    spin_unlock(&s->lock);
     return ret;
 }
 
-int ata_dma_write28(uint32_t lba, const uint8_t* buf512) {
+int ata_dma_write28(int channel, int slave, uint32_t lba, const uint8_t* buf512) {
     if (!buf512) return -EFAULT;
-    if (!dma_available) return -ENOSYS;
-
-    spin_lock(&dma_lock);
-
-    /* Copy caller's data into DMA bounce buffer */
-    memcpy(dma_buf, buf512, 512);
-
-    int ret = ata_dma_transfer(lba, 1);
+    if (channel < 0 || channel >= ATA_NUM_CHANNELS) return -EINVAL;
+    struct dma_ch_state* s = &dma_ch[channel];
+    if (!s->available) return -ENOSYS;
 
+    spin_lock(&s->lock);
+    memcpy(s->dma_buf, buf512, 512);
+    int ret = ata_dma_transfer(channel, slave, lba, 1);
     if (ret == 0) {
-        /* Flush cache after write */
-        outb((uint16_t)(ATA_IO_BASE + ATA_REG_COMMAND), ATA_CMD_CACHE_FLUSH);
-        (void)ata_wait_not_busy();
+        outb((uint16_t)(ch_io[channel] + ATA_REG_COMMAND), ATA_CMD_CACHE_FLUSH);
+        (void)ata_wait_not_busy_ch(channel);
     }
-
-    spin_unlock(&dma_lock);
+    spin_unlock(&s->lock);
     return ret;
 }
 
-int ata_dma_read_direct(uint32_t lba, uint32_t phys_buf, uint16_t byte_count) {
-    if (!dma_available) return -ENOSYS;
+int ata_dma_read_direct(int channel, int slave, uint32_t lba,
+                        uint32_t phys_buf, uint16_t byte_count) {
+    if (channel < 0 || channel >= ATA_NUM_CHANNELS) return -EINVAL;
+    struct dma_ch_state* s = &dma_ch[channel];
+    if (!s->available) return -ENOSYS;
     if (phys_buf == 0 || (phys_buf & 1)) return -EINVAL;
     if (byte_count == 0) byte_count = 512;
 
-    spin_lock(&dma_lock);
+    spin_lock(&s->lock);
 
-    /* Save original PRDT and reprogram for zero-copy */
-    uint32_t saved_addr = prdt[0].phys_addr;
-    uint16_t saved_count = prdt[0].byte_count;
-    prdt[0].phys_addr = phys_buf;
-    prdt[0].byte_count = byte_count;
+    uint32_t saved_addr  = s->prdt[0].phys_addr;
+    uint16_t saved_count = s->prdt[0].byte_count;
+    s->prdt[0].phys_addr  = phys_buf;
+    s->prdt[0].byte_count = byte_count;
 
-    int ret = ata_dma_transfer(lba, 0);
+    int ret = ata_dma_transfer(channel, slave, lba, 0);
 
-    /* Restore original PRDT for bounce-buffer mode */
-    prdt[0].phys_addr = saved_addr;
-    prdt[0].byte_count = saved_count;
+    s->prdt[0].phys_addr  = saved_addr;
+    s->prdt[0].byte_count = saved_count;
 
-    spin_unlock(&dma_lock);
+    spin_unlock(&s->lock);
     return ret;
 }
 
-int ata_dma_write_direct(uint32_t lba, uint32_t phys_buf, uint16_t byte_count) {
-    if (!dma_available) return -ENOSYS;
+int ata_dma_write_direct(int channel, int slave, uint32_t lba,
+                         uint32_t phys_buf, uint16_t byte_count) {
+    if (channel < 0 || channel >= ATA_NUM_CHANNELS) return -EINVAL;
+    struct dma_ch_state* s = &dma_ch[channel];
+    if (!s->available) return -ENOSYS;
     if (phys_buf == 0 || (phys_buf & 1)) return -EINVAL;
     if (byte_count == 0) byte_count = 512;
 
-    spin_lock(&dma_lock);
+    spin_lock(&s->lock);
 
-    uint32_t saved_addr prdt[0].phys_addr;
-    uint16_t saved_count = prdt[0].byte_count;
-    prdt[0].phys_addr = phys_buf;
-    prdt[0].byte_count = byte_count;
+    uint32_t saved_addr  = s->prdt[0].phys_addr;
+    uint16_t saved_count = s->prdt[0].byte_count;
+    s->prdt[0].phys_addr  = phys_buf;
+    s->prdt[0].byte_count = byte_count;
 
-    int ret = ata_dma_transfer(lba, 1);
+    int ret = ata_dma_transfer(channel, slave, lba, 1);
 
     if (ret == 0) {
-        outb((uint16_t)(ATA_IO_BASE + ATA_REG_COMMAND), ATA_CMD_CACHE_FLUSH);
-        (void)ata_wait_not_busy();
+        outb((uint16_t)(ch_io[channel] + ATA_REG_COMMAND), ATA_CMD_CACHE_FLUSH);
+        (void)ata_wait_not_busy_ch(channel);
     }
 
-    prdt[0].phys_addr = saved_addr;
-    prdt[0].byte_count = saved_count;
+    s->prdt[0].phys_addr  = saved_addr;
+    s->prdt[0].byte_count = saved_count;
 
-    spin_unlock(&dma_lock);
+    spin_unlock(&s->lock);
     return ret;
 }
index ee79bd0a970840e6f73c766afedcf3b0a45f2cb6..93380ed0578298d7c0ca9cb06745f3bf41ed3bba 100644 (file)
@@ -5,18 +5,9 @@
 #include "io.h"
 #include "arch/x86/idt.h"
 #include "console.h"
+#include "utils.h"
 
-/* Basic IRQ 14 handler: read ATA status to deassert INTRQ.
- * Registered early so PIO IDENTIFY doesn't cause an IRQ storm. */
-static void ata_pio_irq14_handler(struct registers* regs) {
-    (void)regs;
-    (void)inb((uint16_t)(0x1F0 + 0x07));  /* Read ATA status */
-}
-
-// Primary ATA bus I/O ports
-#define ATA_IO_BASE 0x1F0
-#define ATA_CTRL_BASE 0x3F6
-
+/* ATA register offsets */
 #define ATA_REG_DATA       0x00
 #define ATA_REG_ERROR      0x01
 #define ATA_REG_SECCOUNT0  0x02
@@ -39,137 +30,248 @@ static void ata_pio_irq14_handler(struct registers* regs) {
 #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);
+/* Channel I/O port bases */
+static const uint16_t ch_io_base[ATA_NUM_CHANNELS]   = { 0x1F0, 0x170 };
+static const uint16_t ch_ctrl_base[ATA_NUM_CHANNELS]  = { 0x3F6, 0x376 };
+static const uint8_t  ch_irq_vec[ATA_NUM_CHANNELS]    = { 46, 47 };
+
+/* Drive presence flags */
+static int drive_present[ATA_MAX_DRIVES];
+static int ata_pio_inited = 0;
+
+static const char* drive_names[ATA_MAX_DRIVES] = { "hda", "hdb", "hdc", "hdd" };
+
+/* ---- Low-level helpers ---- */
+
+static inline void io_wait_400ns(uint16_t ctrl) {
+    (void)inb(ctrl);
+    (void)inb(ctrl);
+    (void)inb(ctrl);
+    (void)inb(ctrl);
 }
 
-static int ata_wait_not_busy(void) {
+static int ata_wait_not_busy(uint16_t io) {
     for (int i = 0; i < 1000000; i++) {
-        uint8_t st = inb((uint16_t)(ATA_IO_BASE + ATA_REG_STATUS));
+        uint8_t st = inb((uint16_t)(io + ATA_REG_STATUS));
         if ((st & ATA_SR_BSY) == 0) return 0;
     }
     return -EIO;
 }
 
-static int ata_wait_drq(void) {
+static int ata_wait_drq(uint16_t io) {
     for (int i = 0; i < 1000000; i++) {
-        uint8_t st = inb((uint16_t)(ATA_IO_BASE + ATA_REG_STATUS));
+        uint8_t st = inb((uint16_t)(io + ATA_REG_STATUS));
         if (st & ATA_SR_ERR) return -EIO;
-        if (st & ATA_SR_DF) 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;
+/* ---- IRQ handlers (deassert INTRQ by reading status) ---- */
+
+static void ata_irq14_handler(struct registers* regs) {
+    (void)regs;
+    (void)inb((uint16_t)(0x1F0 + ATA_REG_STATUS));
 }
 
-static int ata_pio_inited = 0;
+static void ata_irq15_handler(struct registers* regs) {
+    (void)regs;
+    (void)inb((uint16_t)(0x170 + ATA_REG_STATUS));
+}
 
-int ata_pio_init_primary_master(void) {
-    if (ata_pio_inited) return 0;
+/* ---- Drive probing ---- */
 
-    // Register IRQ 14 handler early to prevent INTRQ storm
-    register_interrupt_handler(46, ata_pio_irq14_handler);
+static int ata_probe_drive(int channel, int slave) {
+    uint16_t io   = ch_io_base[channel];
+    uint16_t ctrl = ch_ctrl_base[channel];
 
-    // Select drive: 0xA0 = master, CHS mode bits set, LBA bit cleared
-    outb((uint16_t)(ATA_IO_BASE + ATA_REG_HDDEVSEL), 0xA0);
-    io_wait_400ns();
+    /* Select drive */
+    uint8_t sel = slave ? 0xB0 : 0xA0;
+    outb((uint16_t)(io + ATA_REG_HDDEVSEL), sel);
+    io_wait_400ns(ctrl);
 
-    if (ata_wait_not_busy() < 0) return -EIO;
+    if (ata_wait_not_busy(io) < 0) return 0;
 
-    // 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);
+    /* Zero registers */
+    outb((uint16_t)(io + ATA_REG_SECCOUNT0), 0);
+    outb((uint16_t)(io + ATA_REG_LBA0), 0);
+    outb((uint16_t)(io + ATA_REG_LBA1), 0);
+    outb((uint16_t)(io + ATA_REG_LBA2), 0);
 
-    uint8_t st = inb((uint16_t)(ATA_IO_BASE + ATA_REG_STATUS));
-    if (st == 0) return -ENODEV;
+    /* IDENTIFY */
+    outb((uint16_t)(io + ATA_REG_COMMAND), ATA_CMD_IDENTIFY);
 
-    if (ata_wait_drq() < 0) return -EIO;
+    uint8_t st = inb((uint16_t)(io + ATA_REG_STATUS));
+    if (st == 0) return 0; /* No drive */
 
-    // Read 256 words (512 bytes) identify data and discard.
+    /* Wait for BSY to clear */
+    for (int i = 0; i < 1000000; i++) {
+        st = inb((uint16_t)(io + ATA_REG_STATUS));
+        if (!(st & ATA_SR_BSY)) break;
+    }
+    if (st & ATA_SR_BSY) return 0;
+
+    /* Non-zero LBA1/LBA2 means ATAPI — skip */
+    uint8_t lba1 = inb((uint16_t)(io + ATA_REG_LBA1));
+    uint8_t lba2 = inb((uint16_t)(io + ATA_REG_LBA2));
+    if (lba1 != 0 || lba2 != 0) return 0;
+
+    /* Wait for DRQ */
+    if (ata_wait_drq(io) < 0) return 0;
+
+    /* Read and discard 256 words of identify data */
     for (int i = 0; i < 256; i++) {
-        (void)inw((uint16_t)(ATA_IO_BASE + ATA_REG_DATA));
+        (void)inw((uint16_t)(io + ATA_REG_DATA));
     }
 
-    // Try to upgrade to DMA mode
-    if (ata_dma_init() == 0) {
-        kprintf("[ATA] Using DMA mode.\n");
-    } else {
-        kprintf("[ATA] Using PIO mode (DMA unavailable).\n");
+    return 1;
+}
+
+/* ---- Public API ---- */
+
+uint32_t ata_pio_sector_size(void) {
+    return 512;
+}
+
+int ata_pio_init(void) {
+    if (ata_pio_inited) return 0;
+
+    /* Register IRQ handlers for both channels */
+    register_interrupt_handler(ch_irq_vec[0], ata_irq14_handler);
+    register_interrupt_handler(ch_irq_vec[1], ata_irq15_handler);
+
+    int found = 0;
+
+    for (int ch = 0; ch < ATA_NUM_CHANNELS; ch++) {
+        /* Floating bus check — 0xFF means no controller */
+        uint8_t st = inb((uint16_t)(ch_io_base[ch] + ATA_REG_STATUS));
+        if (st == 0xFF) {
+            drive_present[ch * 2]     = 0;
+            drive_present[ch * 2 + 1] = 0;
+            continue;
+        }
+
+        for (int sl = 0; sl < 2; sl++) {
+            int id = ch * 2 + sl;
+            drive_present[id] = ata_probe_drive(ch, sl);
+            if (drive_present[id]) found++;
+        }
+
+        /* Try DMA for this channel */
+        if (ata_dma_init(ch) == 0) {
+            kprintf("[ATA] Channel %d: DMA mode.\n", ch);
+        } else {
+            kprintf("[ATA] Channel %d: PIO mode.\n", ch);
+        }
+    }
+
+    /* Log detected drives */
+    for (int i = 0; i < ATA_MAX_DRIVES; i++) {
+        if (drive_present[i]) {
+            kprintf("[ATA] /dev/%s detected.\n", drive_names[i]);
+        }
     }
 
     ata_pio_inited = 1;
-    return 0;
+    return found > 0 ? 0 : -ENODEV;
 }
 
-int ata_pio_read28(uint32_t lba, uint8_t* buf512) {
+int ata_pio_drive_present(int drive) {
+    if (drive < 0 || drive >= ATA_MAX_DRIVES) return 0;
+    return drive_present[drive];
+}
+
+int ata_pio_read28(int drive, uint32_t lba, uint8_t* buf512) {
     if (!buf512) return -EFAULT;
+    if (drive < 0 || drive >= ATA_MAX_DRIVES) return -EINVAL;
+    if (!drive_present[drive]) return -ENODEV;
     if (lba & 0xF0000000U) return -EINVAL;
 
-    // Use DMA if available
-    if (ata_dma_available()) {
-        return ata_dma_read28(lba, buf512);
+    int ch = drive / 2;
+    int sl = drive & 1;
+
+    /* Use DMA if available for this channel */
+    if (ata_dma_available(ch)) {
+        return ata_dma_read28(ch, sl, lba, buf512);
     }
 
-    if (ata_wait_not_busy() < 0) return -EIO;
+    uint16_t io   = ch_io_base[ch];
+    uint16_t ctrl = ch_ctrl_base[ch];
 
-    outb((uint16_t)(ATA_IO_BASE + ATA_REG_HDDEVSEL), (uint8_t)(0xE0 | ((lba >> 24) & 0x0F)));
-    io_wait_400ns();
+    if (ata_wait_not_busy(io) < 0) return -EIO;
 
-    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);
+    uint8_t sel = sl ? 0xF0 : 0xE0;
+    outb((uint16_t)(io + ATA_REG_HDDEVSEL), (uint8_t)(sel | ((lba >> 24) & 0x0F)));
+    io_wait_400ns(ctrl);
 
-    if (ata_wait_drq() < 0) return -EIO;
+    outb((uint16_t)(io + ATA_REG_SECCOUNT0), 1);
+    outb((uint16_t)(io + ATA_REG_LBA0), (uint8_t)(lba & 0xFF));
+    outb((uint16_t)(io + ATA_REG_LBA1), (uint8_t)((lba >> 8) & 0xFF));
+    outb((uint16_t)(io + ATA_REG_LBA2), (uint8_t)((lba >> 16) & 0xFF));
+    outb((uint16_t)(io + ATA_REG_COMMAND), ATA_CMD_READ_SECTORS);
+
+    if (ata_wait_drq(io) < 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));
+        w[i] = inw((uint16_t)(io + ATA_REG_DATA));
     }
 
-    io_wait_400ns();
+    io_wait_400ns(ctrl);
     return 0;
 }
 
-int ata_pio_write28(uint32_t lba, const uint8_t* buf512) {
+int ata_pio_write28(int drive, uint32_t lba, const uint8_t* buf512) {
     if (!buf512) return -EFAULT;
+    if (drive < 0 || drive >= ATA_MAX_DRIVES) return -EINVAL;
+    if (!drive_present[drive]) return -ENODEV;
     if (lba & 0xF0000000U) return -EINVAL;
 
-    // Use DMA if available
-    if (ata_dma_available()) {
-        return ata_dma_write28(lba, buf512);
+    int ch = drive / 2;
+    int sl = drive & 1;
+
+    if (ata_dma_available(ch)) {
+        return ata_dma_write28(ch, sl, lba, buf512);
     }
 
-    if (ata_wait_not_busy() < 0) return -EIO;
+    uint16_t io   = ch_io_base[ch];
+    uint16_t ctrl = ch_ctrl_base[ch];
+
+    if (ata_wait_not_busy(io) < 0) return -EIO;
 
-    outb((uint16_t)(ATA_IO_BASE + ATA_REG_HDDEVSEL), (uint8_t)(0xE0 | ((lba >> 24) & 0x0F)));
-    io_wait_400ns();
+    uint8_t sel = sl ? 0xF0 : 0xE0;
+    outb((uint16_t)(io + ATA_REG_HDDEVSEL), (uint8_t)(sel | ((lba >> 24) & 0x0F)));
+    io_wait_400ns(ctrl);
 
-    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);
+    outb((uint16_t)(io + ATA_REG_SECCOUNT0), 1);
+    outb((uint16_t)(io + ATA_REG_LBA0), (uint8_t)(lba & 0xFF));
+    outb((uint16_t)(io + ATA_REG_LBA1), (uint8_t)((lba >> 8) & 0xFF));
+    outb((uint16_t)(io + ATA_REG_LBA2), (uint8_t)((lba >> 16) & 0xFF));
+    outb((uint16_t)(io + ATA_REG_COMMAND), ATA_CMD_WRITE_SECTORS);
 
-    if (ata_wait_drq() < 0) return -EIO;
+    if (ata_wait_drq(io) < 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]);
+        outw((uint16_t)(io + 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();
+    outb((uint16_t)(io + ATA_REG_COMMAND), ATA_CMD_CACHE_FLUSH);
+    (void)ata_wait_not_busy(io);
+    io_wait_400ns(ctrl);
     return 0;
 }
+
+int ata_name_to_drive(const char* name) {
+    if (!name) return -1;
+    for (int i = 0; i < ATA_MAX_DRIVES; i++) {
+        if (strcmp(name, drive_names[i]) == 0) return i;
+    }
+    return -1;
+}
+
+const char* ata_drive_to_name(int drive) {
+    if (drive < 0 || drive >= ATA_MAX_DRIVES) return 0;
+    return drive_names[drive];
+}
index 4727d9d7480a88664978b7b783f7aee294f0eba5..15a506fbe221a4b4d7ba50e7f8e6b7da2899da80 100644 (file)
@@ -8,6 +8,8 @@
 #include <stddef.h>
 #include <stdint.h>
 
+static int g_diskfs_drive = 0;
+
 // Very small on-disk FS stored starting at LBA2.
 // - LBA0 reserved
 // - LBA1 used by legacy persist counter storage
@@ -100,8 +102,8 @@ static int diskfs_super_load(struct diskfs_super* sb) {
     if (!sb) return -EINVAL;
     uint8_t sec0[DISKFS_SECTOR];
     uint8_t sec1[DISKFS_SECTOR];
-    if (ata_pio_read28(DISKFS_LBA_SUPER, sec0) < 0) return -EIO;
-    if (ata_pio_read28(DISKFS_LBA_SUPER2, sec1) < 0) return -EIO;
+    if (ata_pio_read28(g_diskfs_drive, DISKFS_LBA_SUPER, sec0) < 0) return -EIO;
+    if (ata_pio_read28(g_diskfs_drive, DISKFS_LBA_SUPER2, sec1) < 0) return -EIO;
 
     if (sizeof(*sb) > (size_t)(DISKFS_SECTOR * 2U)) return -EIO;
     memcpy(sb, sec0, DISKFS_SECTOR);
@@ -197,8 +199,8 @@ static int diskfs_super_store(const struct diskfs_super* sb) {
         memcpy(sec1, ((const uint8_t*)sb) + DISKFS_SECTOR, sizeof(*sb) - DISKFS_SECTOR);
     }
 
-    if (ata_pio_write28(DISKFS_LBA_SUPER, sec0) < 0) return -EIO;
-    if (ata_pio_write28(DISKFS_LBA_SUPER2, sec1) < 0) return -EIO;
+    if (ata_pio_write28(g_diskfs_drive, DISKFS_LBA_SUPER, sec0) < 0) return -EIO;
+    if (ata_pio_write28(g_diskfs_drive, DISKFS_LBA_SUPER2, sec1) < 0) return -EIO;
     return 0;
 }
 
@@ -240,7 +242,7 @@ static int diskfs_alloc_inode_file(struct diskfs_super* sb, uint16_t parent, con
         uint8_t zero[DISKFS_SECTOR];
         memset(zero, 0, sizeof(zero));
         for (uint32_t s = 0; s < sb->inodes[i].cap_sectors; s++) {
-            (void)ata_pio_write28(sb->inodes[i].start_lba + s, zero);
+            (void)ata_pio_write28(g_diskfs_drive, sb->inodes[i].start_lba + s, zero);
         }
 
         *out_ino = i;
@@ -339,7 +341,7 @@ static uint32_t diskfs_read_impl(fs_node_t* node, uint32_t offset, uint32_t size
         if (lba_off >= de->cap_sectors) break;
 
         uint8_t sec[DISKFS_SECTOR];
-        if (ata_pio_read28(de->start_lba + lba_off, sec) < 0) break;
+        if (ata_pio_read28(g_diskfs_drive, de->start_lba + lba_off, sec) < 0) break;
         memcpy(buffer + total, sec + sec_off, chunk);
         total += chunk;
     }
@@ -381,11 +383,11 @@ static uint32_t diskfs_write_impl(fs_node_t* node, uint32_t offset, uint32_t siz
         for (uint32_t s = 0; s < new_cap; s++) {
             memset(sec, 0, sizeof(sec));
             if (s < de->cap_sectors) {
-                if (ata_pio_read28(de->start_lba + s, sec) < 0) {
+                if (ata_pio_read28(g_diskfs_drive, de->start_lba + s, sec) < 0) {
                     return 0;
                 }
             }
-            (void)ata_pio_write28(new_start + s, sec);
+            (void)ata_pio_write28(g_diskfs_drive, new_start + s, sec);
         }
 
         de->start_lba = new_start;
@@ -403,13 +405,13 @@ static uint32_t diskfs_write_impl(fs_node_t* node, uint32_t offset, uint32_t siz
 
         uint8_t sec[DISKFS_SECTOR];
         if (sec_off != 0 || chunk != DISKFS_SECTOR) {
-            if (ata_pio_read28(de->start_lba + lba_off, sec) < 0) break;
+            if (ata_pio_read28(g_diskfs_drive, de->start_lba + lba_off, sec) < 0) break;
         } else {
             memset(sec, 0, sizeof(sec));
         }
 
         memcpy(sec + sec_off, buffer + total, chunk);
-        if (ata_pio_write28(de->start_lba + lba_off, sec) < 0) break;
+        if (ata_pio_write28(g_diskfs_drive, de->start_lba + lba_off, sec) < 0) break;
 
         total += chunk;
     }
@@ -1052,9 +1054,10 @@ static void diskfs_set_dir_ops(fs_node_t* vfs) {
     vfs->link = &diskfs_vfs_link;
 }
 
-fs_node_t* diskfs_create_root(void) {
+fs_node_t* diskfs_create_root(int drive) {
     if (!g_ready) {
-        if (ata_pio_init_primary_master() == 0) {
+        g_diskfs_drive = drive;
+        if (ata_pio_drive_present(drive)) {
             g_ready = 1;
         } else {
             g_ready = 0;
index 1e7dc231ce5791498050ae83661aaa96b1fafe40..f9f942e7777efdb757b67fa2a9e6e936ed040fd4 100644 (file)
@@ -114,6 +114,7 @@ struct ext2_dir_entry {
 #define EXT2_SECTOR_SIZE 512
 
 struct ext2_state {
+    int      drive;
     uint32_t part_lba;        /* partition start LBA */
     uint32_t block_size;      /* bytes per block (1024, 2048, or 4096) */
     uint32_t sectors_per_block;
@@ -143,7 +144,7 @@ static int ext2_read_block(uint32_t block, void* buf) {
     uint32_t lba = g_ext2.part_lba + block * g_ext2.sectors_per_block;
     uint8_t* p = (uint8_t*)buf;
     for (uint32_t s = 0; s < g_ext2.sectors_per_block; s++) {
-        if (ata_pio_read28(lba + s, p + s * EXT2_SECTOR_SIZE) < 0)
+        if (ata_pio_read28(g_ext2.drive, lba + s, p + s * EXT2_SECTOR_SIZE) < 0)
             return -EIO;
     }
     return 0;
@@ -153,7 +154,7 @@ static int ext2_write_block(uint32_t block, const void* buf) {
     uint32_t lba = g_ext2.part_lba + block * g_ext2.sectors_per_block;
     const uint8_t* p = (const uint8_t*)buf;
     for (uint32_t s = 0; s < g_ext2.sectors_per_block; s++) {
-        if (ata_pio_write28(lba + s, p + s * EXT2_SECTOR_SIZE) < 0)
+        if (ata_pio_write28(g_ext2.drive, lba + s, p + s * EXT2_SECTOR_SIZE) < 0)
             return -EIO;
     }
     return 0;
@@ -168,7 +169,7 @@ static int ext2_read_superblock(struct ext2_superblock* sb) {
 
     uint8_t raw[1024];
     for (uint32_t i = 0; i < 1024 / EXT2_SECTOR_SIZE; i++) {
-        if (ata_pio_read28(sb_lba + i, sec) < 0) return -EIO;
+        if (ata_pio_read28(g_ext2.drive, sb_lba + i, sec) < 0) return -EIO;
         memcpy(raw + i * EXT2_SECTOR_SIZE, sec, EXT2_SECTOR_SIZE);
     }
     memcpy(sb, raw, sizeof(*sb));
@@ -183,7 +184,7 @@ static int ext2_write_superblock(const struct ext2_superblock* sb) {
     memcpy(raw, sb, sizeof(*sb));
 
     for (uint32_t i = 0; i < 1024 / EXT2_SECTOR_SIZE; i++) {
-        if (ata_pio_write28(sb_lba + i, raw + i * EXT2_SECTOR_SIZE) < 0)
+        if (ata_pio_write28(g_ext2.drive, sb_lba + i, raw + i * EXT2_SECTOR_SIZE) < 0)
             return -EIO;
     }
     return 0;
@@ -1322,8 +1323,9 @@ static int ext2_link_impl(struct fs_node* dir, const char* name, struct fs_node*
 
 /* ---- Mount ---- */
 
-fs_node_t* ext2_mount(uint32_t partition_lba) {
+fs_node_t* ext2_mount(int drive, uint32_t partition_lba) {
     memset(&g_ext2, 0, sizeof(g_ext2));
+    g_ext2.drive = drive;
     g_ext2.part_lba = partition_lba;
 
     struct ext2_superblock sb;
index 7ef0cb516f938308745d4d076d168b71a527ed0c..46f8af267a56d68a41610f8710b2cdd901565a48 100644 (file)
@@ -81,6 +81,7 @@ enum fat_type {
 /* ---- In-memory filesystem state ---- */
 
 struct fat_state {
+    int      drive;
     uint32_t part_lba;
     uint16_t bytes_per_sector;
     uint8_t  sectors_per_cluster;
@@ -113,11 +114,11 @@ static uint8_t g_sec_buf[FAT_SECTOR_SIZE];
 /* ---- Low-level sector I/O ---- */
 
 static int fat_read_sector(uint32_t lba, void* buf) {
-    return ata_pio_read28(lba, (uint8_t*)buf);
+    return ata_pio_read28(g_fat.drive, lba, (uint8_t*)buf);
 }
 
 static int fat_write_sector(uint32_t lba, const void* buf) {
-    return ata_pio_write28(lba, (const uint8_t*)buf);
+    return ata_pio_write28(g_fat.drive, lba, (const uint8_t*)buf);
 }
 
 /* ---- FAT table access ---- */
@@ -1091,7 +1092,10 @@ static int fat_truncate_impl(struct fs_node* node, uint32_t length) {
 
 /* ---- Mount ---- */
 
-fs_node_t* fat_mount(uint32_t partition_lba) {
+fs_node_t* fat_mount(int drive, uint32_t partition_lba) {
+    /* Store drive early so fat_read_sector can use it */
+    g_fat.drive = drive;
+
     uint8_t boot_sec[FAT_SECTOR_SIZE];
     if (fat_read_sector(partition_lba, boot_sec) < 0) {
         kprintf("[FAT] Failed to read BPB at LBA %u\n", partition_lba);
@@ -1110,6 +1114,7 @@ fs_node_t* fat_mount(uint32_t partition_lba) {
     }
 
     memset(&g_fat, 0, sizeof(g_fat));
+    g_fat.drive = drive;
     g_fat.part_lba = partition_lba;
     g_fat.bytes_per_sector = bpb->bytes_per_sector;
     g_fat.sectors_per_cluster = bpb->sectors_per_cluster;
index 0c60cf65515faccc5b8cd33642a3830e7c783a70..feeb6b896a46445a2d1b0a4fe41ab3b6b48e31f9 100644 (file)
 
 #include "ata_pio.h"
 #include "hal/mm.h"
+#include "heap.h"
+#include "utils.h"
 
 #include <stddef.h>
 
+/* ---- Mount helper: used by fstab parser and kconsole 'mount' command ---- */
+
+int init_mount_fs(const char* fstype, int drive, uint32_t lba, const char* mountpoint) {
+    fs_node_t* root = NULL;
+
+    if (strcmp(fstype, "diskfs") == 0) {
+        root = diskfs_create_root(drive);
+    } else if (strcmp(fstype, "fat") == 0) {
+        root = fat_mount(drive, lba);
+    } else if (strcmp(fstype, "ext2") == 0) {
+        root = ext2_mount(drive, lba);
+    } else if (strcmp(fstype, "persistfs") == 0) {
+        root = persistfs_create_root(drive);
+    } else {
+        kprintf("[MOUNT] Unknown filesystem type: %s\n", fstype);
+        return -1;
+    }
+
+    if (!root) {
+        kprintf("[MOUNT] Failed to mount %s on /dev/%s at %s\n",
+                fstype, ata_drive_to_name(drive) ? ata_drive_to_name(drive) : "?",
+                mountpoint);
+        return -1;
+    }
+
+    if (vfs_mount(mountpoint, root) < 0) {
+        kprintf("[MOUNT] Failed to register mount at %s\n", mountpoint);
+        return -1;
+    }
+
+    kprintf("[MOUNT] %s on /dev/%s -> %s\n",
+            fstype, ata_drive_to_name(drive) ? ata_drive_to_name(drive) : "?",
+            mountpoint);
+    return 0;
+}
+
+/* ---- /etc/fstab parser ---- */
+
+/* fstab format (one entry per line, '#' comments):
+ *   <device>  <mountpoint>  <fstype>  [options]
+ * Example:
+ *   /dev/hda  /disk    diskfs   defaults
+ *   /dev/hda  /persist persistfs defaults
+ *   /dev/hdb  /ext2    ext2     defaults
+ */
+static void init_parse_fstab(void) {
+    fs_node_t* fstab = vfs_lookup("/etc/fstab");
+    if (!fstab) return;
+
+    uint32_t len = fstab->length;
+    if (len == 0 || len > 4096) return;
+
+    uint8_t* buf = (uint8_t*)kmalloc(len + 1);
+    if (!buf) return;
+
+    uint32_t rd = vfs_read(fstab, 0, len, buf);
+    buf[rd] = '\0';
+
+    kprintf("[FSTAB] Parsing /etc/fstab (%u bytes)\n", rd);
+
+    /* Parse line by line */
+    char* p = (char*)buf;
+    while (*p) {
+        /* Skip leading whitespace */
+        while (*p == ' ' || *p == '\t') p++;
+        if (*p == '\0') break;
+        if (*p == '#' || *p == '\n') {
+            while (*p && *p != '\n') p++;
+            if (*p == '\n') p++;
+            continue;
+        }
+
+        /* Extract device field */
+        char* dev_start = p;
+        while (*p && *p != ' ' && *p != '\t' && *p != '\n') p++;
+        char dev_end_ch = *p; *p = '\0';
+        char device[32];
+        strncpy(device, dev_start, sizeof(device) - 1);
+        device[sizeof(device) - 1] = '\0';
+        *p = dev_end_ch;
+        if (*p == '\n' || *p == '\0') { if (*p == '\n') p++; continue; }
+
+        /* Skip whitespace */
+        while (*p == ' ' || *p == '\t') p++;
+
+        /* Extract mountpoint field */
+        char* mp_start = p;
+        while (*p && *p != ' ' && *p != '\t' && *p != '\n') p++;
+        char mp_end_ch = *p; *p = '\0';
+        char mountpoint[64];
+        strncpy(mountpoint, mp_start, sizeof(mountpoint) - 1);
+        mountpoint[sizeof(mountpoint) - 1] = '\0';
+        *p = mp_end_ch;
+        if (*p == '\n' || *p == '\0') { if (*p == '\n') p++; continue; }
+
+        /* Skip whitespace */
+        while (*p == ' ' || *p == '\t') p++;
+
+        /* Extract fstype field */
+        char* fs_start = p;
+        while (*p && *p != ' ' && *p != '\t' && *p != '\n') p++;
+        char fs_end_ch = *p; *p = '\0';
+        char fstype[16];
+        strncpy(fstype, fs_start, sizeof(fstype) - 1);
+        fstype[sizeof(fstype) - 1] = '\0';
+        *p = fs_end_ch;
+
+        /* Skip rest of line */
+        while (*p && *p != '\n') p++;
+        if (*p == '\n') p++;
+
+        /* Parse device: expect /dev/hdX */
+        int drive = -1;
+        if (strncmp(device, "/dev/", 5) == 0) {
+            drive = ata_name_to_drive(device + 5);
+        }
+        if (drive < 0) {
+            kprintf("[FSTAB] Unknown device: %s\n", device);
+            continue;
+        }
+        if (!ata_pio_drive_present(drive)) {
+            kprintf("[FSTAB] Device %s not present, skipping\n", device);
+            continue;
+        }
+
+        (void)init_mount_fs(fstype, drive, 0, mountpoint);
+    }
+
+    kfree(buf);
+}
+
 static int cmdline_has_token(const char* cmdline, const char* token) {
     if (!cmdline || !token) return 0;
 
@@ -92,53 +225,19 @@ int init_start(const struct boot_info* bi) {
     vbe_register_devfs();
     keyboard_register_devfs();
 
-    fs_node_t* persist = persistfs_create_root();
-    if (persist) {
-        (void)vfs_mount("/persist", persist);
-    }
-
     fs_node_t* proc = procfs_create_root();
     if (proc) {
         (void)vfs_mount("/proc", proc);
     }
 
-    /* Probe the ATA primary master disk and mount the appropriate
-     * filesystem.  ext2, FAT, and diskfs are mutually exclusive on
-     * the same device — we probe in order of specificity so that
-     * diskfs auto-format never overwrites a recognized filesystem.
-     *
-     * Probe order: ext2 (magic 0xEF53), FAT (BPB validation), diskfs.
-     * If neither ext2 nor FAT is detected, diskfs initializes and may
-     * auto-format the disk. */
-    if (ata_pio_init_primary_master() == 0) {
-        int disk_mounted = 0;
-
-        /* 1. Try ext2 — superblock at byte 1024 (LBA 2), magic at offset 56 */
-        {
-            fs_node_t* ext2fs = ext2_mount(0);
-            if (ext2fs) {
-                (void)vfs_mount("/ext2", ext2fs);
-                disk_mounted = 1;
-            }
-        }
-
-        /* 2. Try FAT — BPB at LBA 0 */
-        if (!disk_mounted) {
-            fs_node_t* fatfs = fat_mount(0);
-            if (fatfs) {
-                (void)vfs_mount("/fat", fatfs);
-                disk_mounted = 1;
-            }
-        }
+    /* Initialize ATA subsystem — probe all 4 drives
+     * (primary/secondary x master/slave). */
+    (void)ata_pio_init();
 
-        /* 3. Fallback to diskfs (may auto-format blank/unrecognized disks) */
-        if (!disk_mounted) {
-            fs_node_t* disk = diskfs_create_root();
-            if (disk) {
-                (void)vfs_mount("/disk", disk);
-            }
-        }
-    }
+    /* Disk-based filesystems (diskfs, FAT, ext2) are NOT auto-mounted.
+     * They are mounted either by /etc/fstab entries or manually via the
+     * kconsole 'mount' command.  Parse /etc/fstab if it exists. */
+    init_parse_fstab();
 
     if (!fs_root) {
         kprintf("[INIT] No root filesystem -- cannot start userspace.\n");
index d6a17bc7bf9a43447bb39b179a36c11f54cf16ae..16c3104612a33339f6be268ef52e4a54a31a51c0 100644 (file)
@@ -10,6 +10,8 @@
 #include "arch/arch_platform.h"
 #include "hal/system.h"
 #include "hal/cpu.h"
+#include "kernel/init.h"
+#include "ata_pio.h"
 
 #define KCMD_MAX 128
 
@@ -239,14 +241,16 @@ static int kc_readline(char* buf, int maxlen) {
 
 static void kconsole_help(void) {
     kc_puts("kconsole commands:\n");
-    kc_puts("  help        - Show this list\n");
-    kc_puts("  clear       - Clear screen\n");
-    kc_puts("  ls [path]   - List files in directory\n");
-    kc_puts("  cat <file>  - Read file content\n");
-    kc_puts("  mem         - Show memory stats\n");
-    kc_puts("  dmesg       - Show kernel log buffer\n");
-    kc_puts("  reboot      - Restart system\n");
-    kc_puts("  halt        - Halt the CPU\n");
+    kc_puts("  help                              - Show this list\n");
+    kc_puts("  clear                             - Clear screen\n");
+    kc_puts("  ls [path]                         - List files in directory\n");
+    kc_puts("  cat <file>                        - Read file content\n");
+    kc_puts("  mem                               - Show memory stats\n");
+    kc_puts("  dmesg                             - Show kernel log buffer\n");
+    kc_puts("  lsblk                             - List detected ATA drives\n");
+    kc_puts("  mount -t <type> /dev/<hd> <mnt>   - Mount filesystem\n");
+    kc_puts("  reboot                            - Restart system\n");
+    kc_puts("  halt                              - Halt the CPU\n");
 }
 
 static void kconsole_ls(const char* path) {
@@ -277,6 +281,80 @@ static void kconsole_ls(const char* path) {
     }
 }
 
+/* mount -t <fstype> /dev/<hdX> <mountpoint> */
+static void kconsole_mount(const char* args) {
+    const char* p = args;
+
+    /* Expect: -t <type> /dev/<hd> <mnt> */
+    while (*p == ' ') p++;
+    if (p[0] != '-' || p[1] != 't' || p[2] != ' ') {
+        kprintf("Usage: mount -t <type> /dev/<hd> <mountpoint>\n");
+        return;
+    }
+    p += 3;
+    while (*p == ' ') p++;
+
+    /* Extract fstype */
+    const char* fs_start = p;
+    while (*p && *p != ' ') p++;
+    if (*p == '\0') {
+        kprintf("Usage: mount -t <type> /dev/<hd> <mountpoint>\n");
+        return;
+    }
+    char fstype[16];
+    size_t fs_len = (size_t)(p - fs_start);
+    if (fs_len >= sizeof(fstype)) fs_len = sizeof(fstype) - 1;
+    memcpy(fstype, fs_start, fs_len);
+    fstype[fs_len] = '\0';
+
+    while (*p == ' ') p++;
+
+    /* Extract device */
+    const char* dev_start = p;
+    while (*p && *p != ' ') p++;
+    if (*p == '\0') {
+        kprintf("Usage: mount -t <type> /dev/<hd> <mountpoint>\n");
+        return;
+    }
+    char device[32];
+    size_t dev_len = (size_t)(p - dev_start);
+    if (dev_len >= sizeof(device)) dev_len = sizeof(device) - 1;
+    memcpy(device, dev_start, dev_len);
+    device[dev_len] = '\0';
+
+    while (*p == ' ') p++;
+
+    /* Extract mountpoint */
+    const char* mp_start = p;
+    while (*p && *p != ' ') p++;
+    char mountpoint[64];
+    size_t mp_len = (size_t)(p - mp_start);
+    if (mp_len >= sizeof(mountpoint)) mp_len = sizeof(mountpoint) - 1;
+    memcpy(mountpoint, mp_start, mp_len);
+    mountpoint[mp_len] = '\0';
+
+    if (mountpoint[0] != '/') {
+        kprintf("mount: mountpoint must be absolute path\n");
+        return;
+    }
+
+    /* Resolve device to drive ID */
+    int drive = -1;
+    if (strncmp(device, "/dev/", 5) == 0) {
+        drive = ata_name_to_drive(device + 5);
+    }
+    if (drive < 0) {
+        kprintf("mount: unknown device: %s\n", device);
+        return;
+    }
+    if (!ata_pio_drive_present(drive)) {
+        kprintf("mount: device %s not present\n", device);
+        return;
+    }
+
+    (void)init_mount_fs(fstype, drive, 0, mountpoint);
+}
+
 static void kconsole_exec(const char* cmd) {
     if (strcmp(cmd, "help") == 0) {
         kconsole_help();
@@ -338,6 +416,17 @@ static void kconsole_exec(const char* cmd) {
         hal_cpu_disable_interrupts();
         for (;;) hal_cpu_idle();
     }
+    else if (strcmp(cmd, "lsblk") == 0) {
+        for (int i = 0; i < ATA_MAX_DRIVES; i++) {
+            const char* name = ata_drive_to_name(i);
+            if (!name) continue;
+            kprintf("  /dev/%s  %s\n", name,
+                    ata_pio_drive_present(i) ? "present" : "not detected");
+        }
+    }
+    else if (strncmp(cmd, "mount ", 6) == 0) {
+        kconsole_mount(cmd + 6);
+    }
     else if (cmd[0] != '\0') {
         kprintf("unknown command: %s\n", cmd);
     }
index 86919cfb4d14abf9ef76880409c20c806fa40c99..bffa5d76940e6f8778c289a01471806795dd0e88 100644 (file)
@@ -71,9 +71,9 @@ static struct fs_node* persist_root_finddir(struct fs_node* node, const char* na
     return 0;
 }
 
-fs_node_t* persistfs_create_root(void) {
+fs_node_t* persistfs_create_root(int drive) {
     if (!g_ready) {
-        if (ata_pio_init_primary_master() == 0) {
+        if (ata_pio_drive_present(drive)) {
             g_ready = 1;
         } else {
             g_ready = 0;
@@ -81,11 +81,11 @@ fs_node_t* persistfs_create_root(void) {
 
         if (g_ready) {
             // Ensure diskfs is initialized even if /disk mount happens later.
-            (void)diskfs_create_root();
+            (void)diskfs_create_root(drive);
 
             // One-time migration from legacy LBA1 counter storage.
             uint8_t sec[512];
-            if (ata_pio_read28(PERSISTFS_LBA_COUNTER, sec) == 0) {
+            if (ata_pio_read28(drive, PERSISTFS_LBA_COUNTER, sec) == 0) {
                 fs_node_t* b = persistfs_backing_open(PERSIST_O_CREAT);
                 if (b) {
                     uint8_t cur4[4];
index 9c779f9d74cf2d8cdb163a1417bf09570088690f..1aef2c8b612ebd6c35334c4a9855be569f3725a1 100755 (executable)
@@ -44,8 +44,8 @@ after 1000
 set tests {
     {"Heap init"            "\\[HEAP\\] 8MB Buddy Allocator Ready."}
     {"PCI enumeration"      "\\[PCI\\] Enumerated"}
-    {"ATA DMA init"         "\\[ATA-DMA\\] Initialized"}
-    {"ATA DMA mode"         "\\[ATA\\] Using DMA mode."}
+    {"ATA DMA init"         "\\[ATA-DMA\\] Ch0 initialized"}
+    {"ATA DMA mode"         "\\[ATA\\] Channel 0: DMA mode."}
     {"SMP CPUs active"      "CPU\\(s\\) active."}
     {"User ring3 entry"     "\\[USER\\] enter ring3"}
     {"init.elf hello"       "\\[init\\] hello from init.elf"}