new file mode 100644
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_LKL_SETUP_H
+#define _ASM_LKL_SETUP_H
+
+#define COMMAND_LINE_SIZE 4096
+
+#endif
@@ -17,8 +17,14 @@ struct lkl_jmp_buf {
* These operations must be provided by a host library or by the application
* itself.
*
+ * @virtio_devices - string containg the list of virtio devices in virtio mmio
+ * command line format. This string is appended to the kernel command line and
+ * is provided here for convenience to be implemented by the host library.
+ *
* @print - optional operation that receives console messages
*
+ * @panic - called during a kernel panic
+ *
* @sem_alloc - allocate a host semaphore an initialize it to count
* @sem_free - free a host semaphore
* @sem_up - perform an up operation on the semaphore
@@ -78,7 +84,10 @@ struct lkl_jmp_buf {
* @jmp_buf_longjmp - perform a jump back to the saved jump buffer
*/
struct lkl_host_operations {
+ const char *virtio_devices;
+
void (*print)(const char *str, int len);
+ void (*panic)(void);
struct lkl_sem *(*sem_alloc)(int count);
void (*sem_free)(struct lkl_sem *sem);
@@ -121,6 +130,23 @@ struct lkl_host_operations {
void (*jmp_buf_longjmp)(struct lkl_jmp_buf *jmpb, int val);
};
+/**
+ * lkl_start_kernel - registers the host operations and starts the kernel
+ *
+ * The function returns only after the kernel is shutdown with lkl_sys_halt.
+ *
+ * @lkl_ops - pointer to host operations
+ * @cmd_line - format for command line string that is going to be used to
+ * generate the Linux kernel command line
+ */
+int lkl_start_kernel(struct lkl_host_operations *lkl_ops, const char *cmd_line,
+ ...);
+
+/**
+ * lkl_is_running - returns 1 if the kernel is currently running
+ */
+int lkl_is_running(void);
+
int lkl_printf(const char *fmt, ...);
void lkl_bug(const char *fmt, ...);
new file mode 100644
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/binfmts.h>
+#include <linux/init.h>
+#include <linux/init_task.h>
+#include <linux/personality.h>
+#include <linux/reboot.h>
+#include <linux/fs.h>
+#include <linux/start_kernel.h>
+#include <linux/syscalls.h>
+#include <linux/tick.h>
+#include <asm/host_ops.h>
+#include <asm/irq.h>
+#include <asm/unistd.h>
+#include <asm/syscalls.h>
+#include <asm/cpu.h>
+
+struct lkl_host_operations *lkl_ops;
+static char cmd_line[COMMAND_LINE_SIZE];
+static void *init_sem;
+static int is_running;
+void (*pm_power_off)(void) = NULL;
+static unsigned long mem_size = 64 * 1024 * 1024;
+
+static long lkl_panic_blink(int state)
+{
+ lkl_ops->panic();
+ return 0;
+}
+
+static int __init setup_mem_size(char *str)
+{
+ mem_size = memparse(str, NULL);
+ return 0;
+}
+early_param("mem", setup_mem_size);
+
+void __init setup_arch(char **cl)
+{
+ *cl = cmd_line;
+ panic_blink = lkl_panic_blink;
+ parse_early_param();
+ bootmem_init(mem_size);
+}
+
+static void __init lkl_run_kernel(void *arg)
+{
+ threads_init();
+ lkl_cpu_get();
+ start_kernel();
+}
+
+int __init lkl_start_kernel(struct lkl_host_operations *ops, const char *fmt,
+ ...)
+{
+ va_list ap;
+ int ret;
+
+ lkl_ops = ops;
+
+ va_start(ap, fmt);
+ ret = vsnprintf(boot_command_line, COMMAND_LINE_SIZE, fmt, ap);
+ va_end(ap);
+
+ if (ops->virtio_devices)
+ strscpy(boot_command_line + ret, ops->virtio_devices,
+ COMMAND_LINE_SIZE - ret);
+
+ memcpy(cmd_line, boot_command_line, COMMAND_LINE_SIZE);
+
+ init_sem = lkl_ops->sem_alloc(0);
+ if (!init_sem)
+ return -ENOMEM;
+
+ ret = lkl_cpu_init();
+ if (ret)
+ goto out_free_init_sem;
+
+ ret = lkl_ops->thread_create(lkl_run_kernel, NULL);
+ if (!ret) {
+ ret = -ENOMEM;
+ goto out_free_init_sem;
+ }
+
+ lkl_ops->sem_down(init_sem);
+ lkl_ops->sem_free(init_sem);
+ current_thread_info()->tid = lkl_ops->thread_self();
+ lkl_cpu_change_owner(current_thread_info()->tid);
+
+ lkl_cpu_put();
+ is_running = 1;
+
+ return 0;
+
+out_free_init_sem:
+ lkl_ops->sem_free(init_sem);
+
+ return ret;
+}
+
+int lkl_is_running(void)
+{
+ return is_running;
+}
+
+void machine_halt(void)
+{
+ lkl_cpu_shutdown();
+}
+
+void machine_power_off(void)
+{
+ machine_halt();
+}
+
+void machine_restart(char *unused)
+{
+ machine_halt();
+}
+
+long lkl_sys_halt(void)
+{
+ long err;
+ long params[6] = {
+ LINUX_REBOOT_MAGIC1,
+ LINUX_REBOOT_MAGIC2,
+ LINUX_REBOOT_CMD_RESTART,
+ };
+
+ err = lkl_syscall(__NR_reboot, params);
+ if (err < 0)
+ return err;
+
+ is_running = false;
+
+ lkl_cpu_wait_shutdown();
+
+ syscalls_cleanup();
+ threads_cleanup();
+ /* Shutdown the clockevents source. */
+ tick_suspend_local();
+ free_mem();
+ lkl_ops->thread_join(current_thread_info()->tid);
+
+ return 0;
+}
+
+static int lkl_run_init(struct linux_binprm *bprm);
+
+static struct linux_binfmt lkl_run_init_binfmt = {
+ .module = THIS_MODULE,
+ .load_binary = lkl_run_init,
+};
+
+static int lkl_run_init(struct linux_binprm *bprm)
+{
+ int ret;
+
+ if (strcmp("/init", bprm->filename) != 0)
+ return -EINVAL;
+
+ ret = flush_old_exec(bprm);
+ if (ret)
+ return ret;
+ set_personality(PER_LINUX);
+ setup_new_exec(bprm);
+ install_exec_creds(bprm);
+
+ set_binfmt(&lkl_run_init_binfmt);
+
+ init_pid_ns.child_reaper = NULL;
+
+ syscalls_init();
+
+ lkl_ops->sem_up(init_sem);
+ lkl_ops->thread_exit();
+
+ return 0;
+}
+
+/* skip mounting the "real" rootfs. ramfs is good enough. */
+static int __init fs_setup(void)
+{
+ int fd;
+
+ fd = sys_open("/init", O_CREAT, 0700);
+ WARN_ON(fd < 0);
+ sys_close(fd);
+
+ register_binfmt(&lkl_run_init_binfmt);
+
+ return 0;
+}
+late_initcall(fs_setup);