Files
claude-os/src/cpio.c
AI 3d5fb4c267 Add CPIO initial ramdisk with build infrastructure and parser (AI)
Build system changes:
- scripts/gen_initrd.sh packs all files from apps/ into a newc-format
  CPIO archive at build/isodir/boot/initrd.cpio.
- CMakeLists.txt adds 'initrd' target as ISO dependency. GRUB loads the
  archive as a Multiboot2 module via 'module2 /boot/initrd.cpio'.
- apps/README added as placeholder file for initial ramdisk content.

Kernel changes:
- kernel.c scans Multiboot2 tags for MULTIBOOT_TAG_TYPE_MODULE to find
  the initrd's physical address range, then passes it to cpio_init().
- cpio.c/h implements a parser for the SVR4/newc CPIO format:
  - cpio_init(): lists archive contents on startup
  - cpio_find(): look up a file by name (handles ./ prefix)
  - cpio_next(): iterate through all entries
  - cpio_count(): count files in archive
- The initrd lives in identity-mapped physical memory, so no additional
  mapping is needed to access it.

Verified in QEMU: GRUB loads the module at 0x0014A000, CPIO parser
finds the README file (38 bytes). All existing functionality (Ring 3
processes, syscalls) continues to work.
2026-02-23 12:16:24 +00:00

180 lines
4.7 KiB
C

/**
* @file cpio.c
* @brief CPIO newc archive parser implementation.
*
* Parses CPIO archives in the SVR4/newc format. The archive is expected
* to be loaded into memory by GRUB as a Multiboot2 module.
*/
#include "cpio.h"
#include <string.h>
/* Debug print helpers defined in kernel.c */
extern void offset_print(const char *str);
extern void print_hex(uint32_t val);
/** Pointer to the CPIO archive in memory. */
static const uint8_t *archive = NULL;
/** Size of the archive (0 if unknown). */
static uint32_t archive_len = 0;
/**
* Parse an N-character hexadecimal ASCII string to uint32_t.
*
* @param s Pointer to hex string.
* @param n Number of characters to parse.
* @return Parsed value.
*/
static uint32_t parse_hex(const char *s, int n) {
uint32_t val = 0;
for (int i = 0; i < n; i++) {
char c = s[i];
uint32_t digit;
if (c >= '0' && c <= '9') {
digit = (uint32_t)(c - '0');
} else if (c >= 'a' && c <= 'f') {
digit = (uint32_t)(c - 'a' + 10);
} else if (c >= 'A' && c <= 'F') {
digit = (uint32_t)(c - 'A' + 10);
} else {
break;
}
val = (val << 4) | digit;
}
return val;
}
/**
* Round up to 4-byte boundary.
*/
static inline uint32_t align4(uint32_t v) {
return (v + 3) & ~3u;
}
/**
* Parse a CPIO entry at the given offset.
*
* @param offset Byte offset into the archive.
* @param entry Output entry information.
* @return Offset of the next entry, or 0 on error/end.
*/
static uint32_t parse_entry(uint32_t offset, cpio_entry_t *entry) {
if (!archive) return 0;
const cpio_newc_header_t *hdr = (const cpio_newc_header_t *)(archive + offset);
/* Verify magic */
if (memcmp(hdr->magic, CPIO_MAGIC, 6) != 0) {
return 0;
}
uint32_t namesize = parse_hex(hdr->namesize, 8);
uint32_t filesize = parse_hex(hdr->filesize, 8);
uint32_t mode = parse_hex(hdr->mode, 8);
/* Filename starts right after the header */
const char *name = (const char *)(archive + offset + CPIO_HEADER_SIZE);
/* Data starts after header + name, aligned to 4 bytes */
uint32_t data_offset = align4(offset + CPIO_HEADER_SIZE + namesize);
const void *data = archive + data_offset;
/* Next entry starts after data, aligned to 4 bytes */
uint32_t next_offset = align4(data_offset + filesize);
entry->name = name;
entry->namesize = namesize;
entry->data = data;
entry->datasize = filesize;
entry->mode = mode;
return next_offset;
}
void cpio_init(const void *archive_start, uint32_t archive_size) {
archive = (const uint8_t *)archive_start;
archive_len = archive_size;
offset_print(" CPIO: archive at ");
print_hex((uint32_t)archive_start);
offset_print(" CPIO: size = ");
print_hex(archive_size);
/* Count and list entries */
uint32_t count = 0;
uint32_t off = 0;
cpio_entry_t entry;
while (1) {
uint32_t next = parse_entry(off, &entry);
if (next == 0) break;
if (strcmp(entry.name, CPIO_TRAILER) == 0) break;
offset_print(" CPIO: [");
offset_print(entry.name);
offset_print("] size=");
print_hex(entry.datasize);
count++;
off = next;
}
offset_print(" CPIO: ");
print_hex(count);
offset_print(" CPIO: files found\n");
}
int cpio_find(const char *name, cpio_entry_t *entry) {
if (!archive) return -1;
uint32_t off = 0;
while (1) {
uint32_t next = parse_entry(off, entry);
if (next == 0) return -1;
if (strcmp(entry->name, CPIO_TRAILER) == 0) return -1;
/* Match by name. CPIO entries often have "./" prefix, try both. */
if (strcmp(entry->name, name) == 0) return 0;
/* Try matching without "./" prefix */
if (entry->name[0] == '.' && entry->name[1] == '/' &&
strcmp(entry->name + 2, name) == 0) {
return 0;
}
/* Try matching with "./" prefix */
if (name[0] != '.' && entry->namesize > 2) {
/* Already handled above */
}
off = next;
}
}
int cpio_next(uint32_t *offset, cpio_entry_t *entry) {
if (!archive) return -1;
uint32_t next = parse_entry(*offset, entry);
if (next == 0) return -1;
if (strcmp(entry->name, CPIO_TRAILER) == 0) return -1;
*offset = next;
return 0;
}
uint32_t cpio_count(void) {
if (!archive) return 0;
uint32_t count = 0;
uint32_t off = 0;
cpio_entry_t entry;
while (1) {
uint32_t next = parse_entry(off, &entry);
if (next == 0) break;
if (strcmp(entry.name, CPIO_TRAILER) == 0) break;
count++;
off = next;
}
return count;
}