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