Implement serial logging. Finish modularizing power management.
[monolithium.git] / kernel / src / start.c
1 /*
2  * start.c
3  *
4  * Copyright (C) 2016 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <boot/multiboot.h>
21 #include <common.h>
22 #include <log.h>
23 #include <cpu.h>
24 #include <memory.h>
25 #include <heap.h>
26 #include <segments.h>
27 #include <interrupt.h>
28 #include <timer.h>
29 #include <clock.h>
30 #include <process.h>
31 #include <thread.h>
32 #include <exception.h>
33 #include <video.h>
34 #include <device.h>
35 #include <power.h>
36 #include <module.h>
37 #include <exec/elf.h>
38
39 #define DO_TASK(x, ...) x(__VA_ARGS__); advance_progress()
40
41 #define MAX_CMDLINE_LENGTH 256
42
43 static char cmdline[MAX_CMDLINE_LENGTH] = "";
44 static uintptr_t lowest_physical = 0;
45 static multiboot_tag_mmap_t *mmap = NULL;
46 static multiboot_tag_sections_t *kernel_sections = NULL;
47 static size_t tasks_completed = 0, num_tasks = 12;
48 static const char *manager_path = "";
49
50 bool_t video_initialized = FALSE;
51 const elf32_symbol_t *kernel_symtab = NULL;
52 dword_t kernel_symcount = 0;
53 const char *kernel_strtab = NULL;
54
55 void *get_kernel_symbol(const char *name)
56 {
57     int i;
58
59     for (i = 0; i < kernel_symcount; i++)
60     {
61         if (strcmp(name, &kernel_strtab[kernel_symtab[i].st_name]) == 0)
62         {
63             return (void*)kernel_symtab[i].st_value;
64         }
65     }
66
67     return NULL;
68 }
69
70 const char *lookup_kernel_symbol_name(uintptr_t address)
71 {
72     int i;
73
74     for (i = 0; i < kernel_symcount; i++)
75     {
76         if (address >= kernel_symtab[i].st_value && address < (kernel_symtab[i].st_value + kernel_symtab[i].st_size))
77         {
78             return &kernel_strtab[kernel_symtab[i].st_name];
79         }
80     }
81
82     return NULL;
83 }
84
85
86 static dword_t __attribute__((__noreturn__)) system_idle_thread(void *param)
87 {
88     UNUSED_PARAMETER(param);
89
90     while (TRUE)
91     {
92         syscall_yield_quantum();
93         halt();
94     }
95 }
96
97 static void pre_initialization(void)
98 {
99     cpu_init();
100     segments_init();
101     interrupt_init();
102     syscalls_init();
103     irq_init();
104     exceptions_init();
105     enable_ints();
106 }
107
108 static void scan_multiboot_info(multiboot_tag_t *mboot)
109 {
110     multiboot_tag_t *tag;
111     for (tag = mboot; tag->type != MULTIBOOT_INFO_END; tag = (multiboot_tag_t*)(((uintptr_t)tag + tag->size + 7) & ~7))
112     {
113         switch (tag->type)
114         {
115         case MULTIBOOT_INFO_CMDLINE:
116             strncpy(cmdline, ((multiboot_tag_cmdline_t*)tag)->string, sizeof(cmdline));
117             break;
118
119         case MULTIBOOT_INFO_MODULES:
120             {
121                 multiboot_tag_module_t *module = (multiboot_tag_module_t*)tag;
122                 if (module->mod_end > lowest_physical) lowest_physical = module->mod_end;
123                 num_tasks++;
124             }
125             break;
126
127         case MULTIBOOT_INFO_MEMORY_MAP:
128             mmap = (multiboot_tag_mmap_t*)tag;
129             break;
130
131         case MULTIBOOT_INFO_SECTIONS:
132             {
133                 kernel_sections = (multiboot_tag_sections_t*)tag;
134                 elf32_section_t *sections = (elf32_section_t*)(kernel_sections + 1);
135
136                 int i;
137                 for (i = 0; i < kernel_sections->num; i++)
138                 {
139                     uintptr_t section_end = sections[i].sh_addr + sections[i].sh_size;
140                     if (section_end < 0x80000000 && section_end > lowest_physical) lowest_physical = section_end;
141                 }
142             }
143             break;
144         }
145     }
146 }
147
148 static bool_t load_kernel_symbols()
149 {
150     int i;
151     const elf32_section_t *sections = (elf32_section_t*)(kernel_sections + 1);
152
153     for (i = 0; i < kernel_sections->num; i++)
154     {
155         if (sections[i].sh_type == ELF_SECTION_TYPE_SYMTAB && !kernel_symtab)
156         {
157             if (map_memory((void*)sections[i].sh_addr, (void**)&kernel_symtab, sections[i].sh_size, MEMORY_BLOCK_ACCESSIBLE) != ERR_SUCCESS)
158             {
159                 continue;
160             }
161
162             kernel_symcount = sections[i].sh_size / sizeof(elf32_symbol_t);
163         }
164         else if (sections[i].sh_type == ELF_SECTION_TYPE_STRTAB && i != kernel_sections->shndx && !kernel_strtab)
165         {
166             if (map_memory((void*)sections[i].sh_addr, (void**)&kernel_strtab, sections[i].sh_size, MEMORY_BLOCK_ACCESSIBLE) != ERR_SUCCESS)
167             {
168                 continue;
169             }
170         }
171     }
172
173     if (!kernel_symtab || !kernel_strtab)
174     {
175         if (kernel_symtab) unmap_memory((void*)kernel_symtab);
176         if (kernel_strtab) unmap_memory((void*)kernel_strtab);
177         kernel_symcount = 0;
178         return FALSE;
179     }
180
181     return TRUE;
182 }
183
184 static void setup_memory_management(void)
185 {
186     if (!mmap) KERNEL_CRASH("The bootloader failed to supply a memory map");
187     memory_init(mmap, PAGE_ALIGN_UP(lowest_physical));
188     heap_init();
189
190     map_memory_internal((void*)TEXT_VIDEO_MEMORY,
191                         (void*)TEXT_VIDEO_MEMORY,
192                         TEXT_HEIGHT * TEXT_WIDTH * sizeof(word_t),
193                         PAGE_PRESENT | PAGE_WRITABLE);
194 }
195
196 static void parse_command_line(void)
197 {
198     static char cmdline_copy[MAX_CMDLINE_LENGTH];
199     strcpy(cmdline_copy, cmdline);
200
201     char *token;
202     for (token = strtok(cmdline_copy, " "); token != NULL; token = strtok(NULL, " "))
203     {
204         char *separator = strchr(token, ':');
205         if (separator == NULL) continue;
206
207         char *value = separator + 1;
208         *separator = '\0';
209
210         if (strcmp(token, "manager") == 0)
211         {
212             manager_path = value;
213         }
214         else if (strcmp(token, "debug") == 0)
215         {
216             char *ptr = strchr(value, ',');
217
218             if (ptr)
219             {
220                 *ptr = '\0';
221                 log_level_t min_level = *(ptr + 1) - '0';
222                 if (min_level >= LOG_DEBUG || min_level <= LOG_CRITICAL) debug_min_level = min_level;
223             }
224
225             debug_channel = value;
226         }
227     }
228 }
229
230 static handle_t start_system_manager(void)
231 {
232     char *root_disk = strdup(manager_path);
233     char *slash = strchr(root_disk, '/');
234     if (slash == NULL) KERNEL_CRASH("Invalid or missing system manager path");
235     *slash = 0;
236
237     log_write(LOG_NORMAL, "Mounting root disk: %s\n", root_disk);
238     dword_t ret = syscall_mount(root_disk, root_disk, NULL, MOUNT_FLAG_READONLY);
239     if (ret != ERR_SUCCESS) KERNEL_CRASH("Cannot mount root disk");
240     free(root_disk);
241
242     handle_t manager_process;
243     handle_t main_thread;
244
245     process_params_t parameters;
246     parameters.command_line = cmdline;
247     parameters.standard_input = INVALID_HANDLE;
248     parameters.standard_output = INVALID_HANDLE;
249     parameters.standard_error = INVALID_HANDLE;
250
251     log_write(LOG_NORMAL, "Starting the system manager: %s\n", manager_path);
252     ret = syscall_create_process(manager_path, 0, &parameters, &manager_process, &main_thread);
253     if (ret != ERR_SUCCESS) KERNEL_CRASH("Cannot start system manager");
254
255     syscall_close_object(main_thread);
256     return manager_process;
257 }
258
259 static void draw_progress_bar(void)
260 {
261     static const char text[] = "The Monolithium kernel is loading, please wait...";
262
263     word_t *video_mem = (word_t*)(video_initialized ? VIDEO_MEMORY : TEXT_VIDEO_MEMORY);
264     memset(video_mem, 0x00, TEXT_HEIGHT * TEXT_WIDTH * sizeof(word_t));
265
266     int i;
267     size_t length = sizeof(text) - 1;
268     size_t text_start = 10 * TEXT_WIDTH + (TEXT_WIDTH - length) / 2;
269     for (i = 0; i < length; i++) video_mem[text_start + i] = text[i] | 0x0700;
270
271     video_mem[11 * TEXT_WIDTH + 14] = 0x07DA;
272     for (i = 15; i < TEXT_WIDTH - 15; i++) video_mem[11 * TEXT_WIDTH + i] = 0x07C4;
273     video_mem[12 * TEXT_WIDTH - 15] = 0x07BF;
274
275     video_mem[12 * TEXT_WIDTH + 14] = video_mem[13 * TEXT_WIDTH - 15] = 0x07B3;
276
277     video_mem[13 * TEXT_WIDTH + 14] = 0x07C0;
278     for (i = 15; i < TEXT_WIDTH - 15; i++) video_mem[13 * TEXT_WIDTH + i] = 0x07C4;
279     video_mem[14 * TEXT_WIDTH - 15] = 0x07D9;
280 }
281
282 static void advance_progress(void)
283 {
284     word_t *video_mem = (word_t*)(video_initialized ? VIDEO_MEMORY : TEXT_VIDEO_MEMORY);
285     size_t old_status = tasks_completed * 50 / num_tasks;
286     size_t new_status = (tasks_completed + 1) * 50 / num_tasks;
287
288     int i;
289     for (i = old_status; i < new_status; i++) video_mem[12 * TEXT_WIDTH + i + 15] = 0x7020;
290     tasks_completed++;
291 }
292
293 void kernel_main(uintptr_t mboot_tags, size_t mboot_size)
294 {
295     log_write(LOG_NORMAL, "Monolithium 0.1\n");
296
297     pre_initialization();
298     log_write(LOG_NORMAL, "Pre-initialization complete\n");
299
300     draw_progress_bar();
301
302     extern int _end;
303     lowest_physical = PAGE_ALIGN_UP((uintptr_t)&_end) - 0x7FF00000;
304     if ((mboot_tags + mboot_size) > lowest_physical) lowest_physical = mboot_tags + mboot_size;
305
306     multiboot_tag_t *mboot = (multiboot_tag_t*)(PAGE_ALIGN_UP((uintptr_t)&_end) + PAGE_OFFSET(mboot_tags));
307     map_memory_internal((void*)mboot_tags, mboot, mboot_size, PAGE_PRESENT | PAGE_WRITABLE);
308     DO_TASK(scan_multiboot_info, mboot);
309     DO_TASK(parse_command_line);
310     DO_TASK(setup_memory_management);
311     if (kernel_sections) load_kernel_symbols();
312     DO_TASK(device_init);
313     DO_TASK(timer_init);
314     DO_TASK(clock_init);
315     DO_TASK(process_init);
316     DO_TASK(thread_init);
317
318     thread_t *idle;
319     ASSERT(create_system_thread(system_idle_thread, 0, THREAD_PRIORITY_IDLE, 0, NULL, &idle) == ERR_SUCCESS);
320
321     DO_TASK(user_init);
322     DO_TASK(pci_init);
323     DO_TASK(video_init);
324     unmap_memory_internal((void*)TEXT_VIDEO_MEMORY, TEXT_HEIGHT * TEXT_WIDTH * sizeof(word_t));
325     video_initialized = TRUE;
326
327     multiboot_tag_t *tag;
328     for (tag = mboot; tag->type != MULTIBOOT_INFO_END; tag = (multiboot_tag_t*)(((uintptr_t)tag + tag->size + 7) & ~7))
329     {
330         if (tag->type == MULTIBOOT_INFO_MODULES)
331         {
332             multiboot_tag_module_t *module = (multiboot_tag_module_t*)tag;
333             module_t *mod = (module_t*)malloc(sizeof(module_t));
334             mod->physical_address = module->mod_start;
335             mod->size = module->mod_end - module->mod_start;
336
337             dword_t ret = module_load_from_physical(mod, module->string);
338             if (ret == ERR_SUCCESS)
339             {
340                 log_write(LOG_NORMAL, "Loaded module %s at 0x%08X\n", mod->name, mod->base_address);
341             }
342             else
343             {
344                 if (mod->name) log_write(LOG_ERROR, "Cannot load module %s: %s\n", mod->name, get_error_string(ret));
345                 else log_write(LOG_ERROR, "Cannot load module from physical address 0x%08X: %s\n", module->mod_start, get_error_string(ret));
346                 free(mod);
347             }
348
349             advance_progress();
350             // TODO: Reclaim pages
351         }
352     }
353
354     handle_t manager_process = DO_TASK(start_system_manager);
355     syscall_wait_process(manager_process, NO_TIMEOUT);
356
357     KERNEL_CRASH("The system manager has stopped working");
358     ASSERT(FALSE);
359 }