| 1 | // SPDX-License-Identifier: GPL-3.0-or-later |
| 2 | |
| 3 | #include "mos/filesystem/sysfs/sysfs.hpp" |
| 4 | #include "mos/filesystem/sysfs/sysfs_autoinit.hpp" |
| 5 | #include "mos/mm/mm.hpp" |
| 6 | #include "mos/mm/paging/paging.hpp" |
| 7 | #include "mos/mm/physical/pmm.hpp" |
| 8 | #include "mos/syslog/debug.hpp" |
| 9 | #include "mos/syslog/syslog.hpp" |
| 10 | #include "mos/tasks/elf.hpp" |
| 11 | |
| 12 | #include <mos/allocator.hpp> |
| 13 | #include <mos/device/console.hpp> |
| 14 | #include <mos/filesystem/vfs.hpp> |
| 15 | #include <mos/interrupt/ipi.hpp> |
| 16 | #include <mos/ipc/ipc.hpp> |
| 17 | #include <mos/lib/cmdline.hpp> |
| 18 | #include <mos/list.hpp> |
| 19 | #include <mos/misc/cmdline.hpp> |
| 20 | #include <mos/misc/setup.hpp> |
| 21 | #include <mos/platform/platform.hpp> |
| 22 | #include <mos/shared_ptr.hpp> |
| 23 | #include <mos/syslog/printk.hpp> |
| 24 | #include <mos/tasks/kthread.hpp> |
| 25 | #include <mos/tasks/schedule.hpp> |
| 26 | #include <mos/type_utils.hpp> |
| 27 | #include <mos_stdio.hpp> |
| 28 | #include <mos_stdlib.hpp> |
| 29 | #include <mos_string.hpp> |
| 30 | |
| 31 | MMContext mos_kernel_mm; |
| 32 | |
| 33 | mos::vector<mos::string> init_args; |
| 34 | |
| 35 | static bool init_sysfs_argv(sysfs_file_t *file) |
| 36 | { |
| 37 | for (u32 i = 0; i < init_args.size(); i++) |
| 38 | sysfs_printf(file, fmt: "%s " , init_args[i].c_str()); |
| 39 | sysfs_printf(file, fmt: "\n" ); |
| 40 | return true; |
| 41 | } |
| 42 | |
| 43 | SYSFS_ITEM_RO_STRING(kernel_sysfs_version, MOS_KERNEL_VERSION) |
| 44 | SYSFS_ITEM_RO_STRING(kernel_sysfs_revision, MOS_KERNEL_REVISION) |
| 45 | SYSFS_ITEM_RO_STRING(kernel_sysfs_build_date, __DATE__) |
| 46 | SYSFS_ITEM_RO_STRING(kernel_sysfs_build_time, __TIME__) |
| 47 | SYSFS_ITEM_RO_STRING(kernel_sysfs_compiler, __VERSION__) |
| 48 | SYSFS_ITEM_RO_STRING(kernel_sysfs_arch, MOS_ARCH) |
| 49 | SYSFS_ITEM_RO_STRING(init_sysfs_path, init_args[0].c_str()) |
| 50 | SYSFS_ITEM_RO_PRINTF(initrd_sysfs_info, "pfn: " PFN_FMT "\nnpages: %zu\n" , platform_info->initrd_pfn, platform_info->initrd_npages) |
| 51 | |
| 52 | static sysfs_item_t kernel_sysfs_items[] = { |
| 53 | SYSFS_RO_ITEM("arch" , kernel_sysfs_arch), // |
| 54 | SYSFS_RO_ITEM("build_date" , kernel_sysfs_build_date), // |
| 55 | SYSFS_RO_ITEM("build_time" , kernel_sysfs_build_time), // |
| 56 | SYSFS_RO_ITEM("compiler" , kernel_sysfs_compiler), // |
| 57 | SYSFS_RO_ITEM("init_argv" , init_sysfs_argv), // |
| 58 | SYSFS_RO_ITEM("init_path" , init_sysfs_path), // |
| 59 | SYSFS_RO_ITEM("initrd" , initrd_sysfs_info), // |
| 60 | SYSFS_RO_ITEM("revision" , kernel_sysfs_revision), // |
| 61 | SYSFS_RO_ITEM("version" , kernel_sysfs_version), // |
| 62 | }; |
| 63 | |
| 64 | SYSFS_AUTOREGISTER(kernel, kernel_sysfs_items); |
| 65 | |
| 66 | MOS_SETUP("init" , setup_init_path) |
| 67 | { |
| 68 | if (arg.empty()) |
| 69 | { |
| 70 | pr_warn("init path not specified" ); |
| 71 | return false; |
| 72 | } |
| 73 | |
| 74 | if (init_args.empty()) |
| 75 | init_args.push_back(value: arg.data()); |
| 76 | else |
| 77 | init_args[0] = arg; |
| 78 | |
| 79 | return true; |
| 80 | } |
| 81 | |
| 82 | MOS_SETUP("init_args" , setup_init_args) |
| 83 | { |
| 84 | char *var_arg = strdup(src: arg.data()); |
| 85 | string_unquote(str: var_arg); |
| 86 | for (const auto &cmd : cmdline_parse_vector(inbuf: var_arg, length: strlen(str: var_arg))) |
| 87 | init_args.push_back(value: cmd); |
| 88 | kfree(ptr: var_arg); |
| 89 | return true; |
| 90 | } |
| 91 | |
| 92 | static void setup_sane_environment() |
| 93 | { |
| 94 | platform_startup_early(); |
| 95 | pmm_init(); |
| 96 | |
| 97 | pr_dinfo(vmm, "initializing paging..." ); |
| 98 | mos_kernel_mm.pgd = pgd_create(pml_create_table(MOS_PMLTOP)); |
| 99 | platform_startup_setup_kernel_mm(); |
| 100 | |
| 101 | pr_dinfo(vmm, "mapping kernel space..." ); |
| 102 | mm_map_kernel_pages( // |
| 103 | mmctx: platform_info->kernel_mm, // |
| 104 | vaddr: (ptr_t) __MOS_KERNEL_CODE_START, // |
| 105 | MOS_KERNEL_PFN((ptr_t) __MOS_KERNEL_CODE_START), // |
| 106 | ALIGN_UP_TO_PAGE((ptr_t) __MOS_KERNEL_CODE_END - (ptr_t) __MOS_KERNEL_CODE_START) / MOS_PAGE_SIZE, // |
| 107 | flags: VM_READ | VM_EXEC | VM_GLOBAL // |
| 108 | ); |
| 109 | |
| 110 | mm_map_kernel_pages( // |
| 111 | mmctx: platform_info->kernel_mm, // |
| 112 | vaddr: (ptr_t) __MOS_KERNEL_RODATA_START, // |
| 113 | MOS_KERNEL_PFN((ptr_t) __MOS_KERNEL_RODATA_START), // |
| 114 | ALIGN_UP_TO_PAGE((ptr_t) __MOS_KERNEL_RODATA_END - (ptr_t) __MOS_KERNEL_RODATA_START) / MOS_PAGE_SIZE, // |
| 115 | flags: VM_READ | VM_GLOBAL // |
| 116 | ); |
| 117 | |
| 118 | mm_map_kernel_pages( // |
| 119 | mmctx: platform_info->kernel_mm, // |
| 120 | vaddr: (ptr_t) __MOS_KERNEL_RW_START, // |
| 121 | MOS_KERNEL_PFN((ptr_t) __MOS_KERNEL_RW_START), // |
| 122 | ALIGN_UP_TO_PAGE((ptr_t) __MOS_KERNEL_RW_END - (ptr_t) __MOS_KERNEL_RW_START) / MOS_PAGE_SIZE, // |
| 123 | flags: VM_READ | VM_WRITE | VM_GLOBAL // |
| 124 | ); |
| 125 | |
| 126 | platform_switch_mm(new_mm: platform_info->kernel_mm); |
| 127 | slab_init(); // now mos::create<T>, kmalloc<T>, etc. are available |
| 128 | } |
| 129 | |
| 130 | void mos_start_kernel(void) |
| 131 | { |
| 132 | setup_sane_environment(); |
| 133 | mInfo << "Welcome to MOS!" ; |
| 134 | mInfo << fmt("MOS {}-{} on ({}, {}), compiler {}" , MOS_KERNEL_VERSION, MOS_ARCH, MOS_KERNEL_REVISION, __DATE__, __VERSION__); |
| 135 | |
| 136 | if (platform_info->n_cmdlines) |
| 137 | { |
| 138 | mInfo << "MOS Kernel cmdline" ; |
| 139 | for (u32 i = 0; i < platform_info->n_cmdlines; i++) |
| 140 | { |
| 141 | const cmdline_option_t *opt = &platform_info->cmdlines[i]; |
| 142 | if (opt->arg) |
| 143 | pr_info2(" %-2d: %-10s = %s" , i, opt->name, opt->arg); |
| 144 | else |
| 145 | pr_info2(" %-2d: %s" , i, opt->name); |
| 146 | } |
| 147 | } |
| 148 | |
| 149 | // power management |
| 150 | startup_invoke_autoinit(target: INIT_TARGET_POWER); |
| 151 | |
| 152 | // register builtin filesystems |
| 153 | startup_invoke_autoinit(target: INIT_TARGET_PRE_VFS); |
| 154 | startup_invoke_autoinit(target: INIT_TARGET_VFS); |
| 155 | startup_invoke_autoinit(target: INIT_TARGET_SYSFS); |
| 156 | |
| 157 | platform_startup_late(); |
| 158 | |
| 159 | init_args.push_back(MOS_DEFAULT_INIT_PATH); |
| 160 | startup_invoke_cmdline_hooks(); |
| 161 | { |
| 162 | const auto ret = vfs_mount(device: "none" , path: "/" , fs: "tmpfs" , NULL); |
| 163 | if (ret.isErr()) |
| 164 | mos_panic("failed to mount rootfs, vfs_mount returns %ld" , ret.getErr()); |
| 165 | } |
| 166 | { |
| 167 | const auto ret = vfs_mkdir(path: "/initrd" ); |
| 168 | if (ret.isErr()) |
| 169 | mos_panic("failed to create /initrd, vfs_mkdir returns %ld" , ret.getErr()); |
| 170 | } |
| 171 | { |
| 172 | const auto ret = vfs_mount(device: "none" , path: "/initrd/" , fs: "cpiofs" , NULL); |
| 173 | if (ret.isErr()) |
| 174 | mos_panic("failed to mount initrd, vfs_mount returns %ld" , ret.getErr()); |
| 175 | } |
| 176 | ipc_init(); |
| 177 | scheduler_init(); |
| 178 | |
| 179 | const auto init_con = platform_info->boot_console; |
| 180 | if (unlikely(!init_con)) |
| 181 | mos_panic("failed to get console" ); |
| 182 | |
| 183 | const stdio_t init_io = { .in = init_con, .out = init_con, .err = init_con }; |
| 184 | const mos::vector<mos::string> init_envp = { |
| 185 | "PATH=/initrd/programs:/initrd/bin:/bin" , |
| 186 | "HOME=/" , |
| 187 | "TERM=linux" , |
| 188 | }; |
| 189 | |
| 190 | mInfo << "running '" << init_args[0] << "' as init process." ; |
| 191 | mInfo << " with arguments:" ; |
| 192 | for (u32 i = 0; i < init_args.size(); i++) |
| 193 | mInfo << fmt(" argv[{}] = {}" , i, init_args[i].c_str()); |
| 194 | mInfo << " with environment:" ; |
| 195 | for (u32 i = 0; i < init_envp.size(); i++) |
| 196 | mInfo << " " << init_envp[i].c_str(); |
| 197 | |
| 198 | const auto init = elf_create_process(path: init_args[0], NULL, argv: init_args, envp: init_envp, ios: &init_io); |
| 199 | if (unlikely(!init)) |
| 200 | mos_panic("failed to create init process" ); |
| 201 | |
| 202 | const auto m = mm_map_user_pages(mmctx: init->mm, MOS_INITRD_BASE, pfn: platform_info->initrd_pfn, npages: platform_info->initrd_npages, flags: VM_USER_RO, type: VMAP_TYPE_SHARED, content: VMAP_FILE, exact: true); |
| 203 | pmm_ref(pfn_start: platform_info->initrd_pfn, npages: platform_info->initrd_npages); |
| 204 | pmm_ref(pfn_start: platform_info->initrd_pfn, npages: platform_info->initrd_npages); |
| 205 | |
| 206 | MOS_ASSERT_X(m, "failed to map initrd into init process" ); |
| 207 | |
| 208 | kthread_init(); // must be called after creating the first init process |
| 209 | startup_invoke_autoinit(target: INIT_TARGET_KTHREAD); |
| 210 | |
| 211 | unblock_scheduler(); |
| 212 | |
| 213 | mInfo << "\n" ; |
| 214 | enter_scheduler(); |
| 215 | MOS_UNREACHABLE(); |
| 216 | } |
| 217 | |