From: Tulio A M Mendes Date: Fri, 13 Feb 2026 08:07:10 +0000 (-0300) Subject: feat: multi-drive ATA support (4 drives) + fstab/mount command X-Git-Url: https://projects.tadryanom.me/docs/static/git-logo.png?a=commitdiff_plain;h=f5cffcafbf3e927b3c2801f7e318b7aa1cdc45fc;p=AdrOS.git feat: multi-drive ATA support (4 drives) + fstab/mount command 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 /dev/ ' 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 --- diff --git a/Makefile b/Makefile index 94f5b0e..ba43e63 100644 --- 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)),) diff --git a/include/ata_dma.h b/include/ata_dma.h index fef3b47..1105292 100644 --- a/include/ata_dma.h +++ b/include/ata_dma.h @@ -3,27 +3,35 @@ #include -/* 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 diff --git a/include/ata_pio.h b/include/ata_pio.h index fa76bb2..2d66717 100644 --- a/include/ata_pio.h +++ b/include/ata_pio.h @@ -4,9 +4,34 @@ #include #include -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 diff --git a/include/diskfs.h b/include/diskfs.h index 4e97031..b8ba45e 100644 --- a/include/diskfs.h +++ b/include/diskfs.h @@ -4,7 +4,7 @@ #include "fs.h" #include -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 '/'. diff --git a/include/ext2.h b/include/ext2.h index e52b28a..e12f823 100644 --- a/include/ext2.h +++ b/include/ext2.h @@ -4,8 +4,8 @@ #include "fs.h" #include -/* 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 diff --git a/include/fat.h b/include/fat.h index deb5686..dac0266 100644 --- a/include/fat.h +++ b/include/fat.h @@ -4,9 +4,9 @@ #include "fs.h" #include -/* 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 diff --git a/include/kernel/init.h b/include/kernel/init.h index b5fe3a3..228fe70 100644 --- a/include/kernel/init.h +++ b/include/kernel/init.h @@ -2,7 +2,16 @@ #define KERNEL_INIT_H #include "kernel/boot_info.h" +#include 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 diff --git a/include/kernel_va_map.h b/include/kernel_va_map.h index b1c462e..3360b99 100644 --- a/include/kernel_va_map.h +++ b/include/kernel_va_map.h @@ -36,9 +36,11 @@ #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 diff --git a/include/persistfs.h b/include/persistfs.h index 0414a19..c187617 100644 --- a/include/persistfs.h +++ b/include/persistfs.h @@ -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 index 0000000..c7cd658 --- /dev/null +++ b/rootfs/etc/fstab @@ -0,0 +1,4 @@ +# /etc/fstab — AdrOS filesystem table +# +/dev/hda /disk diskfs defaults +/dev/hda /persist persistfs defaults diff --git a/src/drivers/ata_pio.c b/src/drivers/ata_pio.c index d9197d6..62c7fd3 100644 --- a/src/drivers/ata_pio.c +++ b/src/drivers/ata_pio.c @@ -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; } diff --git a/src/hal/x86/ata_dma.c b/src/hal/x86/ata_dma.c index 896a625..e69048b 100644 --- a/src/hal/x86/ata_dma.c +++ b/src/hal/x86/ata_dma.c @@ -13,12 +13,7 @@ #include #include -/* 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 @@ -37,322 +32,334 @@ #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; } diff --git a/src/hal/x86/ata_pio.c b/src/hal/x86/ata_pio.c index ee79bd0..93380ed 100644 --- a/src/hal/x86/ata_pio.c +++ b/src/hal/x86/ata_pio.c @@ -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]; +} diff --git a/src/kernel/diskfs.c b/src/kernel/diskfs.c index 4727d9d..15a506f 100644 --- a/src/kernel/diskfs.c +++ b/src/kernel/diskfs.c @@ -8,6 +8,8 @@ #include #include +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; diff --git a/src/kernel/ext2.c b/src/kernel/ext2.c index 1e7dc23..f9f942e 100644 --- a/src/kernel/ext2.c +++ b/src/kernel/ext2.c @@ -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; diff --git a/src/kernel/fat.c b/src/kernel/fat.c index 7ef0cb5..46f8af2 100644 --- a/src/kernel/fat.c +++ b/src/kernel/fat.c @@ -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; diff --git a/src/kernel/init.c b/src/kernel/init.c index 0c60cf6..feeb6b8 100644 --- a/src/kernel/init.c +++ b/src/kernel/init.c @@ -24,9 +24,142 @@ #include "ata_pio.h" #include "hal/mm.h" +#include "heap.h" +#include "utils.h" #include +/* ---- 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): + * [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"); diff --git a/src/kernel/kconsole.c b/src/kernel/kconsole.c index d6a17bc..16c3104 100644 --- a/src/kernel/kconsole.c +++ b/src/kernel/kconsole.c @@ -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 - 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 - 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 /dev/ - 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 /dev/ */ +static void kconsole_mount(const char* args) { + const char* p = args; + + /* Expect: -t /dev/ */ + while (*p == ' ') p++; + if (p[0] != '-' || p[1] != 't' || p[2] != ' ') { + kprintf("Usage: mount -t /dev/ \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 /dev/ \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 /dev/ \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); } diff --git a/src/kernel/persistfs.c b/src/kernel/persistfs.c index 86919cf..bffa5d7 100644 --- a/src/kernel/persistfs.c +++ b/src/kernel/persistfs.c @@ -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]; diff --git a/tests/smoke_test.exp b/tests/smoke_test.exp index 9c779f9..1aef2c8 100755 --- a/tests/smoke_test.exp +++ b/tests/smoke_test.exp @@ -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"}