summaryrefslogtreecommitdiff
path: root/na/fs/initramfs.cc
diff options
context:
space:
mode:
authorRichard Qin <richard.qin.x@gmail.com>2026-07-10 11:24:56 +0800
committerRichard Qin <richard.qin.x@gmail.com>2026-07-10 11:24:56 +0800
commit1767d390fbf3d0c65389de46caf0b6d43ef3b6ad (patch)
tree0f5e01cd7c7a05abb6b1c7472d7f00f49caf02cc /na/fs/initramfs.cc
parent63dc025705f6533b91fb1a7d26e6ed864efd9900 (diff)
downloadlume-1767d390fbf3d0c65389de46caf0b6d43ef3b6ad.tar.gz
lume-1767d390fbf3d0c65389de46caf0b6d43ef3b6ad.tar.xz
feat: establish bootable new architecture checkpoint
Replace the compressed boot path with a relocatable higher-half RISC-V entry, expand memory and SMP support, and add the driver, TTY, VFS, exec, signal, syscall, initramfs, and self-test infrastructure needed for a bootable kernel checkpoint.
Diffstat (limited to 'na/fs/initramfs.cc')
-rw-r--r--na/fs/initramfs.cc285
1 files changed, 285 insertions, 0 deletions
diff --git a/na/fs/initramfs.cc b/na/fs/initramfs.cc
new file mode 100644
index 0000000..ba10a3e
--- /dev/null
+++ b/na/fs/initramfs.cc
@@ -0,0 +1,285 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2026 Richard Qin */
+
+/*
+ * initramfs.cc — Unpack embedded CPIO archive into ramfs
+ *
+ * The build system embeds a newc-format CPIO archive into the kernel
+ * ELF as the section .init.ramfs. The linker script exports
+ * __initramfs_start / __initramfs_end symbols.
+ *
+ * At boot, populate_rootfs() walks the CPIO entries and creates
+ * directories and files in the already-mounted ramfs root.
+ *
+ * Reference: Linux init/initramfs.c
+ *
+ * CPIO "newc" header format (110 bytes, ASCII hex fields):
+ * magic[6] "070701"
+ * ino[8] mode[8] uid[8] gid[8] nlink[8] mtime[8]
+ * filesize[8] devmajor[8] devminor[8] rdevmajor[8] rdevminor[8]
+ * namesize[8] check[8]
+ * filename (namesize bytes, NUL terminated, padded to 4-byte boundary)
+ * file data (filesize bytes, padded to 4-byte boundary)
+ */
+
+#include <lume/klog.h>
+#include <lume/fs.h>
+#include <lume/dcache.h>
+#include <lume/file.h>
+#include <lume/errno.h>
+#include <lume/fs_types.h>
+
+/* Linker-provided symbols for the embedded CPIO archive */
+extern "C" const char __initramfs_start[];
+extern "C" const char __initramfs_end[];
+
+/* ramfs inode creation (from fs/ramfs/inode.cc) */
+extern inode* ramfs_get_inode(super_block* sb, const inode* dir,
+ uint32 mode, uint32 rdev);
+
+/* Global root dentry (from fs/vfs_init.cc) */
+extern dentry* g_root_dentry;
+
+/* ================================================================== */
+/* CPIO newc header parsing */
+/* ================================================================== */
+
+struct cpio_header {
+ char magic[6];
+ char ino[8];
+ char mode[8];
+ char uid[8];
+ char gid[8];
+ char nlink[8];
+ char mtime[8];
+ char filesize[8];
+ char devmajor[8];
+ char devminor[8];
+ char rdevmajor[8];
+ char rdevminor[8];
+ char namesize[8];
+ char check[8];
+};
+
+static constexpr int kCpioHeaderSize = 110;
+
+static uint32 parse_hex8(const char* s)
+{
+ uint32 val = 0;
+ for (int i = 0; i < 8; i++) {
+ char c = s[i];
+ uint32 nibble;
+ if (c >= '0' && c <= '9') nibble = c - '0';
+ else if (c >= 'a' && c <= 'f') nibble = c - 'a' + 10;
+ else if (c >= 'A' && c <= 'F') nibble = c - 'A' + 10;
+ else return val;
+ val = (val << 4) | nibble;
+ }
+ return val;
+}
+
+static inline uint32 align4(uint32 v)
+{
+ return (v + 3) & ~3U;
+}
+
+/* ================================================================== */
+/* Path resolution helpers */
+/* ================================================================== */
+
+static dentry* cpio_mkdir_p(dentry* root, const char* path,
+ const char** out_name)
+{
+ dentry* cur = root;
+
+ while (*path == '/') path++;
+
+ while (true) {
+ const char* slash = path;
+ while (*slash && *slash != '/') slash++;
+
+ if (*slash == '\0') {
+ *out_name = path;
+ return cur;
+ }
+
+ uint32 len = static_cast<uint32>(slash - path);
+
+ qstr name;
+ name.name = path;
+ name.len = len;
+ name.hash = full_name_hash(path, len);
+
+ dentry* child = d_lookup(cur, &name);
+ if (!child) {
+ child = d_alloc(cur, &name);
+ if (!child) return nullptr;
+
+ inode* ip = ramfs_get_inode(cur->d_inode->i_sb, cur->d_inode,
+ S_IFDIR | 0755, 0);
+ if (!ip) return nullptr;
+
+ d_instantiate(child, ip);
+ dget(child);
+ inc_nlink(cur->d_inode);
+ }
+
+ cur = child;
+ path = slash + 1;
+ while (*path == '/') path++;
+ }
+}
+
+/* ================================================================== */
+/* populate_rootfs — the main entry point */
+/* ================================================================== */
+
+void populate_rootfs()
+{
+ const char* p = __initramfs_start;
+ const char* end = __initramfs_end;
+ uint64 archive_size = static_cast<uint64>(end - p);
+
+ if (archive_size == 0) {
+ printk("[initramfs] no embedded archive found\n");
+ return;
+ }
+
+ printk("[initramfs] unpacking %llu bytes...\n", archive_size);
+
+ int file_count = 0;
+ int dir_count = 0;
+
+ while (p + kCpioHeaderSize <= end) {
+ auto* hdr = reinterpret_cast<const cpio_header*>(p);
+
+ /* Verify magic */
+ if (hdr->magic[0] != '0' || hdr->magic[1] != '7' ||
+ hdr->magic[2] != '0' || hdr->magic[3] != '7' ||
+ hdr->magic[4] != '0' || hdr->magic[5] != '1') {
+ printk("[initramfs] bad cpio magic at offset %llu\n",
+ static_cast<uint64>(p - __initramfs_start));
+ break;
+ }
+
+ uint32 mode = parse_hex8(hdr->mode);
+ uint32 filesize = parse_hex8(hdr->filesize);
+ uint32 namesize = parse_hex8(hdr->namesize);
+
+ const char* name = p + kCpioHeaderSize;
+ uint32 name_padded = align4(kCpioHeaderSize + namesize);
+ const char* data = p + name_padded;
+ uint32 data_padded = align4(filesize);
+
+ const char* next = p + name_padded + data_padded;
+
+ /* Check for TRAILER!!! end-of-archive marker */
+ if (namesize == 11 && name[0] == 'T' && name[1] == 'R' &&
+ name[2] == 'A' && name[3] == 'I' && name[4] == 'L' &&
+ name[5] == 'E' && name[6] == 'R' && name[7] == '!' &&
+ name[8] == '!' && name[9] == '!') {
+ break;
+ }
+
+ /* Skip "." entry */
+ if (namesize == 2 && name[0] == '.' && name[1] == '\0') {
+ p = next;
+ continue;
+ }
+
+ /* Resolve parent directory, creating intermediate dirs */
+ const char* basename = nullptr;
+ dentry* parent = cpio_mkdir_p(g_root_dentry, name, &basename);
+ if (!parent || !basename || basename[0] == '\0') {
+ p = next;
+ continue;
+ }
+
+ /* Build qstr for the final component */
+ uint32 blen = 0;
+ while (basename[blen] && basename[blen] != '/') blen++;
+
+ qstr bname;
+ bname.name = basename;
+ bname.len = blen;
+ bname.hash = full_name_hash(basename, blen);
+
+ if (S_ISDIR(mode)) {
+ dentry* existing = d_lookup(parent, &bname);
+ if (!existing) {
+ dentry* de = d_alloc(parent, &bname);
+ if (de) {
+ inode* ip = ramfs_get_inode(parent->d_inode->i_sb,
+ parent->d_inode,
+ S_IFDIR | (mode & 0777), 0);
+ if (ip) {
+ d_instantiate(de, ip);
+ dget(de);
+ inc_nlink(parent->d_inode);
+ dir_count++;
+ }
+ }
+ }
+ } else if (S_ISREG(mode)) {
+ dentry* de = d_alloc(parent, &bname);
+ if (de) {
+ inode* ip = ramfs_get_inode(parent->d_inode->i_sb,
+ parent->d_inode,
+ S_IFREG | (mode & 0777), 0);
+ if (ip) {
+ d_instantiate(de, ip);
+ dget(de);
+
+ /* Write file content via ramfs write path */
+ if (filesize > 0 && ip->i_fop && ip->i_fop->write) {
+ file tmp_fp = {};
+ tmp_fp.f_inode = ip;
+ tmp_fp.f_path.de = de;
+ tmp_fp.f_pos = 0;
+ tmp_fp.f_flags = O_WRONLY;
+ tmp_fp.f_count.store(1);
+
+ int64 pos = 0;
+ ip->i_fop->write(&tmp_fp, data,
+ filesize, &pos);
+ }
+
+ file_count++;
+ printk("[initramfs] %s (%u bytes, mode %03o)\n",
+ name, filesize, mode & 0777);
+ }
+ }
+ } else if (S_ISLNK(mode)) {
+ /* Symlink: file data is the target path (not NUL-terminated).
+ * Linux: init/initramfs.c do_symlink() */
+ dentry* de = d_alloc(parent, &bname);
+ if (de) {
+ inode* ip = ramfs_get_inode(parent->d_inode->i_sb,
+ parent->d_inode,
+ S_IFLNK | 0777, 0);
+ if (ip) {
+ /* Store symlink target in inode.
+ * Allocate a kernel buffer and copy the target. */
+ char* target = new char[filesize + 1];
+ if (target) {
+ for (uint32 i = 0; i < filesize; i++)
+ target[i] = data[i];
+ target[filesize] = '\0';
+ ip->i_link = target;
+ }
+
+ d_instantiate(de, ip);
+ dget(de);
+ file_count++;
+ printk("[initramfs] %s -> %s (symlink)\n",
+ name, target ? target : "?");
+ }
+ }
+ }
+
+ p = next;
+ }
+
+ printk("[initramfs] done: %d files, %d dirs\n",
+ file_count, dir_count);
+}