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