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 | |
29 | static MMContext mos_kernel_mm; |
30 | |
31 | static 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 | |
37 | static 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 | |
45 | SYSFS_ITEM_RO_STRING(kernel_sysfs_version, MOS_KERNEL_VERSION) |
46 | SYSFS_ITEM_RO_STRING(kernel_sysfs_revision, MOS_KERNEL_REVISION) |
47 | SYSFS_ITEM_RO_STRING(kernel_sysfs_build_date, __DATE__) |
48 | SYSFS_ITEM_RO_STRING(kernel_sysfs_build_time, __TIME__) |
49 | SYSFS_ITEM_RO_STRING(kernel_sysfs_compiler, __VERSION__) |
50 | SYSFS_ITEM_RO_STRING(kernel_sysfs_arch, MOS_ARCH) |
51 | SYSFS_ITEM_RO_STRING(init_sysfs_path, init_args.argv[0]) |
52 | SYSFS_ITEM_RO_PRINTF(initrd_sysfs_info, "pfn: " PFN_FMT "\nnpages: %zu\n" , platform_info->initrd_pfn, platform_info->initrd_npages) |
53 | |
54 | static 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 | |
66 | SYSFS_AUTOREGISTER(kernel, kernel_sysfs_items); |
67 | |
68 | MOS_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 | |
83 | MOS_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 | |
92 | void 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 | |