run: iso
@rm -f serial.log qemu.log
- @test -f disk.img || dd if=/dev/zero of=disk.img bs=1M count=4 2>/dev/null
+ @test -f disk.img || { dd if=/dev/zero of=disk.img bs=1M count=4 2>/dev/null && printf '\x31\x53\x46\x44\x03\x00\x00\x00\x04\x00\x00\x00' | dd of=disk.img bs=1 seek=1024 conv=notrunc 2>/dev/null; }
@qemu-system-i386 -boot d -cdrom adros-$(ARCH).iso -m 128M -display none \
-drive file=disk.img,if=ide,format=raw \
-nic user,model=e1000 \
test-gdb: $(KERNEL_NAME) iso
@echo "[TEST-GDB] Starting QEMU with GDB stub..."
@rm -f serial.log
- @test -f disk.img || dd if=/dev/zero of=disk.img bs=1M count=4 2>/dev/null
+ @test -f disk.img || { dd if=/dev/zero of=disk.img bs=1M count=4 2>/dev/null && printf '\x31\x53\x46\x44\x03\x00\x00\x00\x04\x00\x00\x00' | dd of=disk.img bs=1 seek=1024 conv=notrunc 2>/dev/null; }
@qemu-system-i386 -smp 4 -boot d -cdrom adros-$(ARCH).iso -m 128M -display none \
-drive file=disk.img,if=ide,format=raw \
-serial file:serial.log -monitor none -no-reboot -no-shutdown \
// -ENODEV if not diskfs, or other negative errno on I/O error.
int diskfs_probe(int drive);
+// Explicit formatting: write a fresh diskfs superblock to the drive.
+// Returns 0 on success, negative errno on error.
+int diskfs_mkfs(int drive);
+
// Open (and optionally create) a diskfs file at the root (flat namespace).
// rel_path must not contain '/'.
// flags: supports O_CREAT (0x40) and O_TRUNC (0x200) semantics (minimal).
* 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);
+ * flags: mount flags (MS_RDONLY, etc.) — stored in VFS mount table
+ * Returns 0 on success, negative errno on failure. */
+int init_mount_fs(const char* fstype, int drive, uint32_t lba, const char* mountpoint, unsigned long flags);
#endif
# /etc/fstab — AdrOS filesystem table
# <device> <mountpoint> <fstype> <options>
+# Note: diskfs requires a pre-formatted disk (use 'mkfs diskfs /dev/hdX').
/dev/hda /disk diskfs defaults
/dev/hda /persist persistfs defaults
fs_node_t* diskfs_create_root(int drive) {
if (!g_ready) {
g_diskfs_drive = drive;
- if (ata_pio_drive_present(drive)) {
- g_ready = 1;
- } else {
- g_ready = 0;
- }
+ if (!ata_pio_drive_present(drive))
+ return NULL;
memset(&g_root, 0, sizeof(g_root));
strcpy(g_root.vfs.name, "disk");
g_root.vfs.i_ops = &diskfs_dir_iops;
g_root.ino = 0;
- if (g_ready) {
- struct diskfs_super sb;
- int rc = diskfs_super_load(&sb);
- if (rc == -ENODEV) {
- /* No diskfs superblock found — format a fresh one.
- * This preserves the auto-format behavior for boot-time
- * mounting of blank disks (e.g. battery test root_hda.img).
- * Autodetect probing will check diskfs_probe() first and
- * skip diskfs if it returns -ENODEV, so this path is only
- * reached when the caller explicitly requests diskfs. */
- (void)diskfs_format(&sb);
- }
+ struct diskfs_super sb;
+ int rc = diskfs_super_load(&sb);
+ if (rc < 0) {
+ /* No valid diskfs superblock — do NOT auto-format.
+ * Use 'mkfs diskfs /dev/hdX' from kconsole to format. */
+ return NULL;
}
+
+ g_ready = 1;
}
- return g_ready ? &g_root.vfs : NULL;
+ return &g_root.vfs;
}
int diskfs_probe(int drive) {
if (magic != DISKFS_MAGIC) return -ENODEV;
return 0;
}
+
+int diskfs_mkfs(int drive) {
+ /* Explicit formatting: write a fresh diskfs superblock to the drive.
+ * This is the ONLY way to create a new diskfs filesystem.
+ * Returns 0 on success, negative errno on error. */
+ if (!ata_pio_drive_present(drive)) return -ENODEV;
+
+ /* If diskfs is already initialized on this drive, reject */
+ if (g_ready && g_diskfs_drive == drive) return -EBUSY;
+
+ /* Temporarily set the drive so diskfs_format can write */
+ int saved_drive = g_diskfs_drive;
+ g_diskfs_drive = drive;
+
+ struct diskfs_super sb;
+ int rc = diskfs_format(&sb);
+
+ g_diskfs_drive = saved_drive;
+ return rc;
+}
/* ---- 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) {
+int init_mount_fs(const char* fstype, int drive, uint32_t lba, const char* mountpoint, unsigned long flags) {
fs_node_t* root = NULL;
if (strcmp(fstype, "diskfs") == 0) {
*dp = '\0';
}
- int rc = vfs_mount_full(mountpoint, root, fstype, devname, 0);
+ int rc = vfs_mount_full(mountpoint, root, fstype, devname, flags);
if (rc < 0) {
kprintf("[MOUNT] Failed to register mount at %s (err=%d)\n", mountpoint, rc);
return rc;
if (drive >= 0 && ata_pio_drive_present(drive)) {
/* Auto-detect: try ext2, fat first (non-destructive), then diskfs.
* diskfs_probe() is non-destructive — it only checks the magic
- * without formatting. diskfs_create_root() will auto-format
- * blank disks, so it is only called when probe confirms diskfs. */
+ * without formatting. diskfs_create_root() no longer auto-formats;
+ * use 'mkfs diskfs /dev/hdX' from kconsole to create a new fs. */
static const char* fstypes[] = { "ext2", "fat", "diskfs", NULL };
int mounted = 0;
for (int i = 0; fstypes[i]; i++) {
extern int diskfs_probe(int drive);
if (diskfs_probe(drive) != 0) continue;
}
- if (init_mount_fs(fstypes[i], drive, 0, "/disk") == 0) {
+ if (init_mount_fs(fstypes[i], drive, 0, "/disk", 0) == 0) {
kprintf("[INIT] root=%s mounted as %s on /disk\n",
root_dev, fstypes[i]);
mounted = 1;
extern int diskfs_probe(int drive);
if (diskfs_probe(0) != 0) continue;
}
- if (init_mount_fs(fstypes[i], 0, 0, "/disk") == 0) {
+ if (init_mount_fs(fstypes[i], 0, 0, "/disk", 0) == 0) {
kprintf("[INIT] /dev/hda auto-mounted as %s on /disk\n", fstypes[i]);
break;
}
}
/* Also mount persistfs on /persist (was previously in /etc/fstab) */
- if (init_mount_fs("persistfs", 0, 0, "/persist") == 0) {
+ if (init_mount_fs("persistfs", 0, 0, "/persist", 0) == 0) {
kprintf("[INIT] /dev/hda auto-mounted as persistfs on /persist\n");
}
}
#include "hal/cpu.h"
#include "kernel/init.h"
#include "ata_pio.h"
+#include "errno.h"
#define KCMD_MAX 128
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(" mkfs diskfs /dev/<hd> - Format diskfs filesystem\n");
kc_puts(" reboot - Restart system\n");
kc_puts(" halt - Halt the CPU\n");
}
return;
}
- (void)init_mount_fs(fstype, drive, 0, mountpoint);
+ (void)init_mount_fs(fstype, drive, 0, mountpoint, 0);
+}
+
+/* mkfs diskfs /dev/<hdX> */
+static void kconsole_mkfs(const char* args) {
+ const char* p = args;
+ while (*p == ' ') p++;
+
+ /* Extract fstype */
+ const char* fs_start = p;
+ while (*p && *p != ' ') p++;
+ 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';
+
+ if (strcmp(fstype, "diskfs") != 0) {
+ kprintf("mkfs: unsupported filesystem type: %s (only 'diskfs')\n", fstype);
+ return;
+ }
+
+ while (*p == ' ') p++;
+
+ /* Extract device */
+ const char* dev_start = p;
+ while (*p && *p != ' ') p++;
+ 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';
+
+ int drive = -1;
+ if (strncmp(device, "/dev/", 5) == 0) {
+ drive = ata_name_to_drive(device + 5);
+ }
+ if (drive < 0) {
+ kprintf("mkfs: unknown device: %s\n", device);
+ return;
+ }
+ if (!ata_pio_drive_present(drive)) {
+ kprintf("mkfs: device %s not present\n", device);
+ return;
+ }
+
+ extern int diskfs_mkfs(int drive);
+ int rc = diskfs_mkfs(drive);
+ if (rc == 0) {
+ kprintf("mkfs: diskfs filesystem created on %s\n", device);
+ } else if (rc == -EBUSY) {
+ kprintf("mkfs: %s already has an active diskfs mount\n", device);
+ } else {
+ kprintf("mkfs: failed to format %s (err=%d)\n", device, rc);
+ }
}
static void kconsole_exec(const char* cmd) {
else if (strncmp(cmd, "mount ", 6) == 0) {
kconsole_mount(cmd + 6);
}
+ else if (strncmp(cmd, "mkfs ", 5) == 0) {
+ kconsole_mkfs(cmd + 5);
+ }
else if (cmd[0] != '\0') {
kprintf("unknown command: %s\n", cmd);
}
#include "persistfs.h"
#include "ata_pio.h"
+#include "console.h"
#include "diskfs.h"
#include "errno.h"
#include "heap.h"
}
if (g_ready) {
- // Ensure diskfs is initialized even if /disk mount happens later.
- (void)diskfs_create_root(drive);
-
- // One-time migration from legacy LBA1 counter storage.
- uint8_t sec[512];
- 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];
- uint32_t rd = vfs_read(b, 0, 4, cur4);
- if (rd == 0) {
- (void)vfs_write(b, 0, 4, sec);
+ /* Only initialize diskfs backing if the drive actually contains
+ * a diskfs filesystem. Without this check, diskfs_create_root()
+ * would auto-format a non-diskfs disk (FAT/ext2), corrupting
+ * data. Now that diskfs_create_root() no longer auto-formats,
+ * the call would simply fail — but we skip it entirely when the
+ * probe fails to avoid unnecessary I/O and confusing log msgs. */
+ extern int diskfs_probe(int drive);
+ if (diskfs_probe(drive) == 0) {
+ (void)diskfs_create_root(drive);
+
+ // One-time migration from legacy LBA1 counter storage.
+ uint8_t sec[512];
+ 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];
+ uint32_t rd = vfs_read(b, 0, 4, cur4);
+ if (rd == 0) {
+ (void)vfs_write(b, 0, 4, sec);
+ }
+ persistfs_backing_close(b);
}
- persistfs_backing_close(b);
}
+ } else {
+ kprintf("[PERSISTFS] No diskfs backing on drive %d — skipping\n", drive);
}
}
int drive = ata_name_to_drive(devname);
if (drive < 0) { sc_ret(regs) = (uint32_t)-ENODEV; return; }
- extern int init_mount_fs(const char* fstype, int drive, uint32_t lba, const char* mountpoint);
+ extern int init_mount_fs(const char* fstype, int drive, uint32_t lba, const char* mountpoint, unsigned long flags);
(void)vfs_mkdirp(kmp); /* auto-create mountpoint (recursive) */
- int rc = init_mount_fs(ktype, drive, 0, kmp);
+ int rc = init_mount_fs(ktype, drive, 0, kmp, mount_flags);
sc_ret(regs) = (uint32_t)(rc < 0 ? rc : 0);
return;
}
set disk "disk.img"
set serial_log "serial.log"
-# Ensure disk image exists
+# Ensure disk image exists with diskfs superblock pre-formatted
+# (diskfs no longer auto-formats on mount)
if {![file exists $disk]} {
exec dd if=/dev/zero of=$disk bs=1M count=4 2>/dev/null
+ set fd [open $disk r+]
+ fconfigure $fd -translation binary
+ seek $fd 1024
+ puts -nonewline $fd [binary format iiii 0x44465331 3 4 0]
+ seek $fd [expr {1024 + 12}]
+ puts -nonewline $fd [binary format ii 1 0]
+ close $fd
}
# Remove old serial log
}
}
+# Create a disk image with a minimal diskfs superblock pre-formatted.
+# diskfs no longer auto-formats on mount, so test disks need this.
+proc create_diskfs_disk {path mb} {
+ if {![file exists $path]} {
+ exec dd if=/dev/zero of=$path bs=1M count=$mb 2>/dev/null
+ # Write diskfs superblock at LBA 2 (byte offset 1024):
+ # magic=0x44465331 version=3 next_free_lba=4
+ # root inode: type=1 (DIR) parent=0
+ # Using printf + dd to stamp the 12-byte header + root inode
+ set fd [open $path r+]
+ fconfigure $fd -translation binary
+ seek $fd 1024
+ # magic (4 bytes LE) + version (4 bytes LE) + next_free_lba (4 bytes LE)
+ puts -nonewline $fd [binary format iiii 0x44465331 3 4 0]
+ # Root inode at offset 1024+12: type=1(DIR), parent=0
+ # struct diskfs_inode: type(4) parent(4) name(24) start_lba(4) nsectors(4) nlink(4) cap_sectors(4)
+ seek $fd [expr {1024 + 12}]
+ puts -nonewline $fd [binary format ii 1 0]
+ close $fd
+ }
+}
+
proc run_qemu {iso smp serial_log timeout_sec drive_args} {
file delete -force $serial_log
set cmd [list qemu-system-i386 \
puts " AdrOS Test Battery"
puts "========================================="
-create_disk "disk.img" 4
+create_diskfs_disk "disk.img" 4
set pid [run_qemu $iso $smp $serial_log $timeout_sec \
{{-drive file=disk.img,if=ide,format=raw}}]
report_section "Multi-disk ATA (3 drives)" [lindex $res 0] [lindex $res 1]
# ================================================================
-# TEST 3: VFS mount root=/dev/hda (diskfs auto-format)
+# TEST 3: VFS mount root=/dev/hda (diskfs pre-formatted)
# ================================================================
-create_disk "root_hda.img" 4
+create_diskfs_disk "root_hda.img" 4
set pid [run_qemu $iso $smp $serial_log $timeout_sec \
{{-drive file=root_hda.img,if=ide,index=0,format=raw}}]
# TEST 6: SMP=1 boot (single-CPU regression)
# ================================================================
-create_disk "smp1_disk.img" 4
+create_diskfs_disk "smp1_disk.img" 4
set pid [run_qemu $iso 1 $serial_log $timeout_sec \
{{-drive file=smp1_disk.img,if=ide,format=raw}}]
# TEST 7: SMP=2 boot (dual-CPU)
# ================================================================
-create_disk "smp2_disk.img" 4
+create_diskfs_disk "smp2_disk.img" 4
set pid [run_qemu $iso 2 $serial_log $timeout_sec \
{{-drive file=smp2_disk.img,if=ide,format=raw}}]
#include <sys/mount.h>
#include <errno.h>
-static void show_mounts(void) {
+static int show_mounts(void) {
int fd = open("/proc/mounts", O_RDONLY);
if (fd >= 0) {
char buf[2048];
while ((n = read(fd, buf, sizeof(buf))) > 0)
write(STDOUT_FILENO, buf, (size_t)n);
close(fd);
+ return 0;
} else {
fprintf(stderr, "mount: /proc/mounts not available\n");
+ return 1;
}
}
int main(int argc, char** argv) {
if (argc < 2) {
- show_mounts();
- return 0;
+ return show_mounts();
}
const char* fstype = NULL;
o += 7;
} else if (strncmp(o, "rw", 2) == 0 && (o[2] == ',' || o[2] == '\0')) {
o += 2;
+ } else if (strncmp(o, "nosuid", 6) == 0 && (o[6] == ',' || o[6] == '\0')) {
+ mountflags |= MS_NOSUID;
+ o += 6;
+ } else if (strncmp(o, "nodev", 5) == 0 && (o[5] == ',' || o[5] == '\0')) {
+ mountflags |= MS_NODEV;
+ o += 5;
+ } else if (strncmp(o, "noexec", 6) == 0 && (o[6] == ',' || o[6] == '\0')) {
+ mountflags |= MS_NOEXEC;
+ o += 6;
} else {
- /* Warn on unknown option */
+ /* Fail on unknown option */
const char* start = o;
while (*o && *o != ',') o++;
fprintf(stderr, "mount: unknown option: %.*s\n", (int)(o - start), start);
+ return 1;
}
if (*o == ',') o++;
}