Viewing: init.c
📄 init.c (Read Only) ⬅ To go back
#include "kernel/init.h"

#include "arch/arch_platform.h"
#include "hal/driver.h"

#include "fs.h"
#include "initrd.h"
#include "overlayfs.h"
#include "tmpfs.h"
#include "devfs.h"
#include "tty.h"
#include "pty.h"
#include "persistfs.h"
#include "diskfs.h"
#include "procfs.h"
#include "fat.h"
#include "ext2.h"
#include "pci.h"
#include "e1000.h"
#include "net.h"
#include "socket.h"
#include "vbe.h"
#include "keyboard.h"
#include "console.h"
#include "errno.h"
#include "utils.h"

#include "ata_pio.h"
#include "hal/mm.h"
#include "heap.h"
#include "kernel/cmdline.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);
}

int init_start(const struct boot_info* bi) {
    /* Parse kernel command line (Linux-like triaging) */
    cmdline_parse(bi ? bi->cmdline : NULL);

    /* Apply console= parameter: serial, vga, or both (default) */
    const char* con = cmdline_get("console");
    if (con) {
        if (strcmp(con, "serial") == 0 || strcmp(con, "ttyS0") == 0) {
            console_enable_uart(1);
            console_enable_vga(0);
            kprintf("[CONSOLE] output: serial only\n");
        } else if (strcmp(con, "vga") == 0 || strcmp(con, "tty0") == 0) {
            console_enable_uart(0);
            console_enable_vga(1);
            kprintf("[CONSOLE] output: VGA only\n");
        } else {
            kprintf("[CONSOLE] unknown console=%s, using both\n", con);
        }
    }

    if (bi && bi->initrd_start) {
        uintptr_t initrd_virt = 0;
        if (hal_mm_map_physical_range((uintptr_t)bi->initrd_start, (uintptr_t)bi->initrd_end,
                                      HAL_MM_MAP_RW, &initrd_virt) == 0) {
            uint32_t initrd_sz = (uint32_t)(bi->initrd_end - bi->initrd_start);
            fs_root = initrd_init((uint32_t)initrd_virt, initrd_sz);
        } else {
            kprintf("[INITRD] Failed to map initrd physical range.\n");
        }
    }

    if (fs_root) {
        fs_node_t* upper = tmpfs_create_root();
        if (upper) {
            fs_node_t* ovl = overlayfs_create_root(fs_root, upper);
            if (ovl) {
                (void)vfs_mount("/", ovl);
            }
        }
    }
    {
        int rc;
        rc = vfs_mkdir("/dev");
        if (rc < 0 && rc != -EEXIST) kprintf("[INIT] mkdir /dev failed: %d\n", rc);
        rc = vfs_mkdir("/proc");
        if (rc < 0 && rc != -EEXIST) kprintf("[INIT] mkdir /proc failed: %d\n", rc);
        rc = vfs_mkdir("/disk");
        if (rc < 0 && rc != -EEXIST) kprintf("[INIT] mkdir /disk failed: %d\n", rc);
        rc = vfs_mkdir("/persist");
        if (rc < 0 && rc != -EEXIST) kprintf("[INIT] mkdir /persist failed: %d\n", rc);
    }

    fs_node_t* tmp = tmpfs_create_root();
    if (tmp) {
        static const uint8_t hello[] = "hello from tmpfs\n";
        (void)tmpfs_add_file(tmp, "hello.txt", hello, (uint32_t)(sizeof(hello) - 1));
        (void)vfs_mount("/tmp", tmp);
    }

    /* Register hardware drivers with HAL and init in priority order */
    pci_driver_register();      /* priority 10: bus */
    e1000_driver_register();    /* priority 20: NIC (probes PCI) */
    extern void virtio_blk_driver_register(void);
    virtio_blk_driver_register(); /* priority 25: virtio-blk */
    hal_drivers_init_all();

    net_init();
    net_ping_test();
    ksocket_init();
    vbe_init(bi);

    tty_init();
    pty_init();

    fs_node_t* dev = devfs_create_root();
    if (dev) {
        (void)vfs_mount("/dev", dev);
    }

    vbe_register_devfs();
    keyboard_register_devfs();

    fs_node_t* proc = procfs_create_root();
    if (proc) {
        (void)vfs_mount("/proc", proc);
    }

    /* Initialize ATA subsystem — probe all 4 drives
     * (primary/secondary x master/slave). */
    (void)ata_pio_init();

    /* Register detected ATA drives as /dev/hdX block device nodes */
    extern void ata_register_devfs(void);
    ata_register_devfs();

    /* If root= is specified on the kernel command line, mount that device
     * as the disk root filesystem.  The filesystem type is auto-detected
     * by trying each supported type in order.
     * Example:  root=/dev/hda  or  root=/dev/hdb */
    const char* root_dev = cmdline_get("root");
    if (root_dev) {
        int drive = -1;
        if (strncmp(root_dev, "/dev/", 5) == 0)
            drive = ata_name_to_drive(root_dev + 5);
        if (drive >= 0 && ata_pio_drive_present(drive)) {
            /* Try auto-detect: diskfs, fat, ext2 */
            static const char* fstypes[] = { "diskfs", "fat", "ext2", NULL };
            int mounted = 0;
            for (int i = 0; fstypes[i]; i++) {
                if (init_mount_fs(fstypes[i], drive, 0, "/disk") == 0) {
                    kprintf("[INIT] root=%s mounted as %s on /disk\n",
                            root_dev, fstypes[i]);
                    mounted = 1;
                    break;
                }
            }
            if (!mounted)
                kprintf("[INIT] root=%s: no supported filesystem found\n", root_dev);
        } else {
            kprintf("[INIT] root=%s: device not found\n", root_dev);
        }
    }

    /* Disk-based filesystems can also be mounted via /etc/fstab entries
     * or manually via the kconsole 'mount' command. */
    init_parse_fstab();

    if (!fs_root) {
        kprintf("[INIT] No root filesystem -- cannot start userspace.\n");
        return -1;
    }

    int user_ret = arch_platform_start_userspace(bi);

    if (cmdline_has("ring3")) {
        arch_platform_usermode_test_start();
    }

    return user_ret;
}