Files
kernel/sys/init.c
T
2020-01-22 15:36:58 +02:00

203 lines
6.1 KiB
C

#include "sys/amd64/mm/pool.h"
#include "sys/amd64/mm/map.h"
#include "sys/amd64/hw/io.h"
#include "sys/amd64/syscall.h"
#include "sys/binfmt_elf.h"
#include "sys/amd64/cpu.h"
#include "sys/config.h"
#include "sys/vmalloc.h"
#include "sys/string.h"
#include "sys/assert.h"
#include "sys/thread.h"
#include "sys/debug.h"
#include "sys/sched.h"
#include "sys/errno.h"
#include "sys/fcntl.h"
#include "sys/panic.h"
#include "sys/heap.h"
#include "sys/dev.h"
#include "sys/mm.h"
extern uintptr_t argp_copy(struct thread *thr, const char *const argv[], size_t *arg_count);
static int user_init_start(const char *init_filename) {
const char *init_console = (const char *) kernel_config[CFG_CONSOLE];
struct ofile fd;
struct stat st;
struct thread *user_init;
char *file_buffer = NULL;
mm_space_t space;
ssize_t bread;
int res;
if ((res = vfs_stat(kernel_ioctx, init_filename, &st)) != 0) {
kerror("%s: %s\n", init_filename, kstrerror(res));
return -1;
}
if ((res = vfs_open(kernel_ioctx, &fd, init_filename, O_RDONLY | O_EXEC, 0)) != 0) {
panic("%s: %s\n", init_filename, kstrerror(res));
}
// TODO: use thread_create or something
user_init = kmalloc(sizeof(struct thread));
_assert(user_init);
space = amd64_mm_pool_alloc();
_assert(space);
mm_space_clone(space, mm_kernel, MM_CLONE_FLG_KERNEL);
if ((res = thread_init(user_init,
space,
0,
0,
0,
0,
0,
0,
THREAD_INIT_CTX,
NULL)) != 0) {
panic("Failed to initialize user init process: %s\n", kstrerror(res));
}
if ((res = elf_load(user_init, kernel_ioctx, &fd)) != 0) {
panic("Failed to load ELF binary: %s\n", kstrerror(res));
}
vfs_close(kernel_ioctx, &fd);
// Setup arguments for init task
const char *const argp[] = {
init_filename,
NULL
};
uintptr_t argp_page;
size_t argc;
_assert((argp_page = argp_copy(user_init, argp, &argc)) != MM_NADDR);
// Map arguments physical page into userspace
uintptr_t argp_virt = vmfind(user_init->space, 0x100000, 0xF0000000, 1);
_assert(argp_virt != MM_NADDR);
// Map it as non-writable user-accessable
_assert(amd64_map_single(user_init->space, argp_virt, argp_page, (1 << 2)) == 0);
// Fix up the pointers
uintptr_t *argp_fixup = (uintptr_t *) MM_VIRTUALIZE(argp_page);
for (size_t i = 0; i < argc; ++i) {
argp_fixup[i] += argp_virt;
}
struct cpu_context *ctx = (struct cpu_context *) user_init->data.rsp0;
ctx->rdi = argp_virt;
// Setup I/O context for init task
struct ofile *stdin, *stdout, *stderr;
stdin = kmalloc(sizeof(struct ofile));
_assert(stdin);
stdout = kmalloc(sizeof(struct ofile));
_assert(stdout);
stderr = stdout;
struct vnode *stdout_device;
if ((res = dev_find(DEV_CLASS_CHAR, init_console, &stdout_device)) != 0) {
panic("%s: %s\n", init_console, kstrerror(res));
}
if ((res = vfs_open_vnode(&user_init->ioctx, stdin, stdout_device, O_RDONLY)) != 0) {
panic("Failed to open stdin for init task: %s\n", kstrerror(res));
}
if ((res = vfs_open_vnode(&user_init->ioctx, stdout, stdout_device, O_WRONLY)) != 0) {
panic("Failed to open stdout/stderr for init task: %s\n", kstrerror(res));
}
++stdout->refcount;
user_init->fds[0] = stdin;
user_init->fds[1] = stdout;
user_init->fds[2] = stderr;
user_init->ioctx.uid = 0;
user_init->ioctx.gid = 0;
user_init->ioctx.cwd_vnode = NULL;
sched_add(user_init);
return 0;
}
void init_func(void *arg) {
const char *root_dev_name = (const char *) kernel_config[CFG_ROOT];
const char *root_fallback = "ram0";
const char *init_filename =
(const char *) kernel_config[!strncmp(root_dev_name, "ram", 3) ? CFG_RDINIT : CFG_INIT];
const char *init_fallback = "/init";
struct vnode *root_dev;
int res;
// XXX XXX XXX: ext2 is broken, lol
// Reads for large files fuck up
//ext2_class_init();
// Be [init] now
thread_set_name(get_cpu()->thread, "init");
// Create an alias for root device
if ((res = dev_find(DEV_CLASS_BLOCK, root_dev_name, &root_dev)) != 0) {
kerror("Failed to find root device: %s\n", root_dev_name);
root_dev = NULL;
if (strcmp(root_dev_name, root_fallback)) {
// Try fallback root option
if ((res = dev_find(DEV_CLASS_BLOCK, root_fallback, &root_dev)) != 0) {
kerror("Failed to find root device: %s\n", root_fallback);
root_dev = NULL;
}
}
}
if (!root_dev) {
panic("No root filesystem\n");
}
dev_add_link("root", root_dev);
if (!strncmp(root_dev_name, "ram", 3)) {
// Check if device is ram-based, if so, try mounting using "ustar"
if ((res = vfs_mount(kernel_ioctx, "/", root_dev->dev, "ustar", NULL)) != 0) {
panic("Failed to mount root device using tarfs: %s\n", kstrerror(res));
}
} else {
// Try "auto" option
if ((res = vfs_mount(kernel_ioctx, "/", root_dev->dev, NULL, NULL)) != 0) {
panic("Failed to mount root device: %s\n", kstrerror(res));
}
}
// Start user init binary
int init_success = 0;
if (user_init_start(init_filename) != 0) {
// User init failed, try /init
if (strcmp(init_filename, init_fallback)) {
if (user_init_start(init_fallback) == 0) {
init_success = 1;
kinfo("Using fallback \"%s\"\n", init_fallback);
}
}
} else {
init_success = 1;
kinfo("Using \"%s\" as init\n", init_filename);
}
if (!init_success) {
panic("No working init found\n");
}
outb(0x20, 0x20);
outb(0xA0, 0x20);
struct thread *this = get_cpu()->thread;
this->exit_code = 0;
this->flags |= THREAD_STOPPED;
amd64_syscall_yield_stopped();
}