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.
This commit is contained in:
179
src/cpio.c
Normal file
179
src/cpio.c
Normal file
@@ -0,0 +1,179 @@
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
Reference in New Issue
Block a user