summaryrefslogtreecommitdiff
path: root/src/user
diff options
context:
space:
mode:
Diffstat (limited to 'src/user')
-rw-r--r--src/user/bootstrap/linker.ld37
-rw-r--r--src/user/bootstrap/main.c36
-rw-r--r--src/user/bootstrap/tar.c159
-rw-r--r--src/user/bootstrap/tar.h5
4 files changed, 237 insertions, 0 deletions
diff --git a/src/user/bootstrap/linker.ld b/src/user/bootstrap/linker.ld
new file mode 100644
index 0000000..917388c
--- /dev/null
+++ b/src/user/bootstrap/linker.ld
@@ -0,0 +1,37 @@
+ENTRY(main)
+OUTPUT_FORMAT("binary")
+
+SECTIONS
+{
+ /* Not an ELF, but we need this to link with elfreloc.c.
+ * Not that we need to link with it, it's just easier that way */
+ _DYNAMIC = 0;
+ _image_base = 0;
+
+ . = 2M;
+ .text BLOCK(4K) : ALIGN(4K)
+ {
+ *(.text.startup)
+ *(.text)
+ }
+ .rodata BLOCK(4K) : ALIGN(4K)
+ {
+ *(.rodata)
+ }
+ .data BLOCK(4K) : ALIGN(4K)
+ {
+ *(.data)
+ }
+
+ . = ALIGN(32); /* seems to be necessary? */
+ _initrd = .; /* is just appended onto the end of the binary */
+ . += 2M;
+
+ _bss_start = .;
+ .bss BLOCK(4K) : ALIGN(4K)
+ {
+ *(COMMON)
+ *(.bss)
+ }
+ _bss_end = .;
+}
diff --git a/src/user/bootstrap/main.c b/src/user/bootstrap/main.c
new file mode 100644
index 0000000..5f36aa1
--- /dev/null
+++ b/src/user/bootstrap/main.c
@@ -0,0 +1,36 @@
+#include <shared/flags.h>
+#include <shared/mem.h>
+#include <shared/syscalls.h>
+#include <user/lib/elfload.h>
+#include <user/lib/fs/misc.h>
+
+#include "tar.h"
+
+extern char _bss_start;
+extern char _bss_end;
+extern char _initrd;
+
+__attribute__((section(".text.startup")))
+int main(void) {
+ _syscall_memflag(&_bss_start, &_bss_end - &_bss_start, MEMFLAG_PRESENT);
+
+ /* move everything provided by the kernel to /kdev */
+ MOUNT("/kdev/", fs_passthru(NULL));
+ if (!fork2_n_mount("/")) {
+ const char *l[] = {"/kdev/", NULL};
+ fs_whitelist(l);
+ }
+ if (!fork2_n_mount("/")) fs_dir_inject("/kdev/"); // TODO should be part of fs_whitelist
+
+ MOUNT("/init/", tar_driver(&_initrd));
+
+ void *init = tar_find("init.elf", 8, &_initrd, ~0) + 512;
+ if (init) {
+ _klogf("execing init.elf");
+ elf_exec(init);
+ _klogf("elf_exec failed");
+ } else {
+ _klogf("couldn't find init.elf");
+ }
+ _syscall_exit(1);
+}
diff --git a/src/user/bootstrap/tar.c b/src/user/bootstrap/tar.c
new file mode 100644
index 0000000..40da437
--- /dev/null
+++ b/src/user/bootstrap/tar.c
@@ -0,0 +1,159 @@
+#include <shared/flags.h>
+#include <shared/mem.h>
+#include <shared/syscalls.h>
+#include <stdint.h>
+
+#include "tar.h"
+
+#define BUF_SIZE 64
+
+static void *tar_open(const char *path, int len, void *base, size_t base_len);
+static void tar_read(struct fs_wait_response *res, void *base, size_t base_len);
+static int tar_size(void *sector);
+static int oct_parse(char *str, size_t len);
+
+
+static const char *root_fakemeta = ""; /* see comment in tar_open */
+
+
+void tar_driver(void *base) {
+ static char buf[BUF_SIZE];
+ struct fs_wait_response res;
+ void *ptr;
+ while (!_syscall_fs_wait(buf, BUF_SIZE, &res)) {
+ switch (res.op) {
+ case VFSOP_OPEN:
+ ptr = tar_open(buf, res.len, base, ~0);
+ _syscall_fs_respond(ptr, ptr ? 0 : -1, 0);
+ break;
+
+ case VFSOP_READ:
+ tar_read(&res, base, ~0);
+ break;
+
+ default:
+ _syscall_fs_respond(NULL, -1, 0); // unsupported
+ break;
+ }
+ }
+ _syscall_exit(0);
+}
+
+static void *tar_open(const char *path, int len, void *base, size_t base_len) {
+ if (len <= 0) return NULL;
+ path += 1; // skip the leading slash
+ len -= 1;
+
+ /* TAR archives don't (seem to) contain an entry for the root dir, so i'm
+ * returning a fake one. this isn't a full entry because i'm currently too
+ * lazy to create a full one - thus, it has to be special cased in tar_read */
+ if (len == 0)
+ return (void*)root_fakemeta;
+
+ return tar_find(path, len, base, base_len);
+}
+
+static void tar_read(struct fs_wait_response *res, void *base, size_t base_len) {
+ void *meta = (void*)res->id;
+ char type = *(char*)(meta + 156);
+ size_t meta_len;
+ int size;
+
+ static char buf[BUF_SIZE]; // TODO reuse a single buffer
+ size_t buf_pos = 0;
+
+ if (meta == root_fakemeta) type = '5'; /* see comment in tar_open() */
+
+ switch (type) {
+ case '\0':
+ case '0': /* normal files */
+ size = tar_size(meta);
+ if (res->offset < 0 || res->offset > size) {
+ // TODO support negative offsets
+ _syscall_fs_respond(NULL, -1, 0);
+ } else {
+ _syscall_fs_respond(meta + 512 + res->offset, size - res->offset, 0);
+ }
+ break;
+
+ case '5': /* directory */
+ meta_len = strlen(meta);
+ size_t to_skip = res->offset;
+
+ /* find files in dir */
+ for (size_t off = 0; off < base_len;) {
+ if (0 != memcmp(base + off + 257, "ustar", 5))
+ break; // not a metadata sector
+ // TODO more meaningful variable names and clean code up
+
+ /* check if prefix matches */
+ if (0 == memcmp(base + off, meta, meta_len) &&
+ *(char*)(base + off + meta_len) != '\0') {
+ char *suffix = base + off + meta_len;
+ size_t suffix_len = strlen(suffix);
+
+ /* check if the path contains any non-trailing slashes */
+ char *next = suffix;
+ while (*next && *next != '/') next++;
+ if (*next == '/') next++;
+ if (*next == '\0') {
+ if (to_skip > suffix_len) {
+ to_skip -= suffix_len;
+ } else {
+ suffix += to_skip;
+ suffix_len -= to_skip;
+ to_skip = 0;
+
+ /* it doesn't - so let's add it to the result */
+ memcpy(buf + buf_pos, suffix, suffix_len);
+ buf[buf_pos + suffix_len] = '\0';
+ buf_pos += suffix_len + 1;
+ // TODO no buffer overrun check
+ }
+ }
+ }
+
+ size = tar_size(base + off);
+ off += 512; // skip this metadata sector
+ off += (size + 511) & ~511; // skip the data sectors
+ }
+
+ _syscall_fs_respond(buf, buf_pos, 0);
+ break;
+
+ default:
+ _syscall_fs_respond(NULL, -1, 0);
+ break;
+ }
+}
+
+static int tar_size(void *sector) {
+ return oct_parse(sector + 124, 11);
+}
+
+void *tar_find(const char *path, size_t path_len, void *base, size_t base_len) {
+ int size;
+ if (path_len > 100) return NULL; // illegal path
+
+ for (size_t off = 0; off < base_len;) {
+ if (0 != memcmp(base + off + 257, "ustar", 5))
+ break; // not a metadata sector
+ if (0 == memcmp(base + off, path, path_len) &&
+ *(char*)(base + off + path_len) == '\0')
+ return base + off; // file found, quit
+
+ size = tar_size(base + off);
+ off += 512; // skip this metadata sector
+ off += (size + 511) & ~511; // skip the data sectors
+ }
+ return NULL;
+}
+
+static int oct_parse(char *str, size_t len) {
+ int res = 0;
+ for (size_t i = 0; i < len; i++) {
+ res *= 8;
+ res += str[i] - '0'; // no format checking
+ }
+ return res;
+}
diff --git a/src/user/bootstrap/tar.h b/src/user/bootstrap/tar.h
new file mode 100644
index 0000000..43aa9ed
--- /dev/null
+++ b/src/user/bootstrap/tar.h
@@ -0,0 +1,5 @@
+#pragma once
+#include <shared/types.h>
+
+_Noreturn void tar_driver(void *base);
+void *tar_find(const char *path, size_t path_len, void *base, size_t base_len);