$(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)),)
#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
#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
#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 '/'.
#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
#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
#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
#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
#include "fs.h"
-fs_node_t* persistfs_create_root(void);
+fs_node_t* persistfs_create_root(int drive);
#endif
--- /dev/null
+# /etc/fstab — AdrOS filesystem table
+# <device> <mountpoint> <fstype> <options>
+/dev/hda /disk diskfs defaults
+/dev/hda /persist persistfs defaults
#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; }
#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;
}
#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
#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];
+}
#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
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);
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;
}
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;
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;
}
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;
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;
}
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;
#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;
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;
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;
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));
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;
/* ---- 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;
/* ---- In-memory filesystem state ---- */
struct fat_state {
+ int drive;
uint32_t part_lba;
uint16_t bytes_per_sector;
uint8_t sectors_per_cluster;
/* ---- 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 ---- */
/* ---- 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);
}
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;
#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;
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");
#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
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) {
}
}
+/* 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();
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);
}
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;
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];
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"}