aboutsummaryrefslogtreecommitdiff
path: root/lib/initrd.c
diff options
context:
space:
mode:
authorMiquel Sabaté Solà <mikisabate@gmail.com>2024-11-26 22:44:45 +0100
committerMiquel Sabaté Solà <mikisabate@gmail.com>2024-11-26 22:44:45 +0100
commitb0ea42e4a95478197d4d7e73791dae012cda6a4a (patch)
tree974ff04c0100467bf6ca420bc7d15e015231598d /lib/initrd.c
parent053f93edecf6aa02ec29366621adf2de6522cdf3 (diff)
downloadfbos-b0ea42e4a95478197d4d7e73791dae012cda6a4a.tar.gz
fbos-b0ea42e4a95478197d4d7e73791dae012cda6a4a.zip
Move initrd.c into lib
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à <mikisabate@gmail.com>
Diffstat (limited to 'lib/initrd.c')
-rw-r--r--lib/initrd.c135
1 files changed, 135 insertions, 0 deletions
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 <fbos/init.h>
+#include <fbos/sched.h>
+#include <fbos/printk.h>
+#include <fbos/string.h>
+
+#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;
+ }
+}