From b0ea42e4a95478197d4d7e73791dae012cda6a4a Mon Sep 17 00:00:00 2001 From: Miquel Sabaté Solà Date: Tue, 26 Nov 2024 22:44:45 +0100 Subject: Move initrd.c into lib MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Even if it's tightly coupled with the kernel (e.g. identifying specific tasks from the kernel), in the end it's a library and so it should be placed accordingly. Signed-off-by: Miquel Sabaté Solà --- lib/initrd.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 lib/initrd.c (limited to 'lib/initrd.c') diff --git a/lib/initrd.c b/lib/initrd.c new file mode 100644 index 0000000..62a4e20 --- /dev/null +++ b/lib/initrd.c @@ -0,0 +1,135 @@ +#include +#include +#include +#include + +#define BUFFER_SIZE 32 + +#define CPIO_HEADER_FILESIZE 54 +#define CPIO_HEADER_NAMESIZE 94 +#define CPIO_HEADER_SIZE 110 + +// Converts the given string 'str' of 'n' length to an integer by assuming it's +// in base 16. +__kernel uint64_t strtoul16(const char *str, size_t n) +{ + char c; + uint64_t ret = 0; + uint64_t aux = 0; + + for (uint64_t i = 1; n > 0; i *= 16, n--) { + c = str[n - 1]; + if (c >= 'A' && c <= 'F') { + aux = 10 + (uint64_t)(c - 'A'); + } else if (c >= 'a' && c <= 'f') { + aux = 10 + (uint64_t)(c - 'a'); + } else if (c < '0' || c > '9') { + die("Bad number\n"); + } else { + aux = (uint64_t)c - '0'; + } + + ret += aux * i; + } + return ret; +} + +// Returns the 'enum taks_id' that can be gathered by the given path. +__kernel int get_task_id_from_path(const char *const path) +{ + if (strcmp(path, "usr/bin/init") == 0) { + return TASK_INIT; + } else if (strcmp(path, "usr/bin/fizz") == 0) { + return TASK_FIZZ; + } else if (strcmp(path, "usr/bin/buzz") == 0) { + return TASK_BUZZ; + } else if (strcmp(path, "usr/bin/fizzbuzz") == 0) { + return TASK_FIZZBUZZ; + } + return TASK_UNKNOWN; +} + +// Fetch the 'entry_addr' for the ELF binary pointed by 'addr'. If everything +// goes right, the task identified by 'task_id' will finally be initialized with +// said address. +__kernel const void *get_task_entry_addr(const unsigned char *const addr) +{ + uint64_t offset; + + if (addr[0] != 0x7F || memcmp(&addr[1], "ELF", 3) != 0) { + die("Bad ELF format\n"); + } + if (addr[4] != 2) { + die("64-bit format is mandatory\n"); + } + if (addr[5] != 1) { + die("Little-endian only\n"); + } + + offset = (uint64_t)addr[0x18]; + return (const void *)(addr + offset); +} + +__kernel void extract_initrd(const unsigned char *const initrd_addr, uint64_t size, + struct task_struct g_tasks[4]) +{ + char buffer[BUFFER_SIZE]; + uint64_t name_size, file_size, padding, base = 0; + int task_id; + + // The `base` is the index from `initrd_addr` which points to the first byte + // of the header of the currently evaluated file inside of the CPIO archive. + while (base < size) { + // Only the "newc" format is supported, without checksums nor fancy + // stuff. + if (memcmp(&initrd_addr[base], "070701", 6) != 0) { + if (memcmp(&initrd_addr[base], "070702", 6) == 0 || + memcmp(&initrd_addr[base], "070707", 6) == 0) { + die("Incorrect cpio format: stick to 'newc'"); + } else { + die("No cpio magic number"); + } + } + + // We identify the task being extracted by looking at the file's path, + // so let's first get the size of it. + memcpy(buffer, &initrd_addr[base + CPIO_HEADER_NAMESIZE], 8); + buffer[8] = '\0'; + name_size = strtoul16(buffer, 8); + if (name_size >= BUFFER_SIZE) { + die("Path too large for initrd executable"); + } + + // Right after the header (hence current header + its size) there is the + // actual file's path, which is exactly `name_size` long. Fetch it now + // to identify the task at hand. + memcpy(buffer, &initrd_addr[base + CPIO_HEADER_SIZE], name_size); + buffer[name_size] = '\0'; + task_id = get_task_id_from_path(buffer); + + // Note that this is not necessarily a bad CPIO archive, it might just + // be the end "TRAILER!!!" delimiter. Either way, just quit at this + // point. + if (task_id == TASK_UNKNOWN) { + break; + } + + // Fetch the size of the executable, which is needed for `extract_elf`, + // as well as for advancing the `base` to the next file. + memcpy(buffer, &initrd_addr[base + CPIO_HEADER_FILESIZE], 8); + buffer[8] = '\0'; + file_size = strtoul16(buffer, 8); + + // Files are aligned in 4-byte boundaries after the header. That's why + // there might be some padding in between the header and the file. + padding = 4 - ((CPIO_HEADER_SIZE + name_size) & 3); + + // And extract the entry address for the current task from the ELF + // binary. + g_tasks[task_id].entry_addr = + get_task_entry_addr(&initrd_addr[base + name_size + CPIO_HEADER_SIZE + padding]); + + // Advance the base to the next file. + base += CPIO_HEADER_SIZE + name_size + padding + file_size; + } +} -- cgit v1.2.3