Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "Devices/PATADiskDevice.h"
28#include "KSyms.h"
29#include "Process.h"
30#include "RTC.h"
31#include "Scheduler.h"
32#include <AK/Types.h>
33#include <Kernel/ACPI/ACPIDynamicParser.h>
34#include <Kernel/ACPI/ACPIStaticParser.h>
35#include <Kernel/ACPI/DMIDecoder.h>
36#include <Kernel/ACPI/MultiProcessorParser.h>
37#include <Kernel/Arch/i386/CPU.h>
38#include <Kernel/CMOS.h>
39#include <Kernel/Devices/BXVGADevice.h>
40#include <Kernel/Devices/DebugLogDevice.h>
41#include <Kernel/Devices/DiskPartition.h>
42#include <Kernel/Devices/EBRPartitionTable.h>
43#include <Kernel/Devices/FloppyDiskDevice.h>
44#include <Kernel/Devices/FullDevice.h>
45#include <Kernel/Devices/GPTPartitionTable.h>
46#include <Kernel/Devices/KeyboardDevice.h>
47#include <Kernel/Devices/MBRPartitionTable.h>
48#include <Kernel/Devices/MBVGADevice.h>
49#include <Kernel/Devices/NullDevice.h>
50#include <Kernel/Devices/PATAChannel.h>
51#include <Kernel/Devices/PIT.h>
52#include <Kernel/Devices/PS2MouseDevice.h>
53#include <Kernel/Devices/RandomDevice.h>
54#include <Kernel/Devices/SB16.h>
55#include <Kernel/Devices/SerialDevice.h>
56#include <Kernel/Devices/VMWareBackdoor.h>
57#include <Kernel/Devices/ZeroDevice.h>
58#include <Kernel/FileSystem/Ext2FileSystem.h>
59#include <Kernel/FileSystem/VirtualFileSystem.h>
60#include <Kernel/Heap/SlabAllocator.h>
61#include <Kernel/Heap/kmalloc.h>
62#include <Kernel/Interrupts/APIC.h>
63#include <Kernel/Interrupts/InterruptManagement.h>
64#include <Kernel/Interrupts/PIC.h>
65#include <Kernel/KParams.h>
66#include <Kernel/Multiboot.h>
67#include <Kernel/Net/LoopbackAdapter.h>
68#include <Kernel/Net/NetworkTask.h>
69#include <Kernel/PCI/Access.h>
70#include <Kernel/PCI/Initializer.h>
71#include <Kernel/Random.h>
72#include <Kernel/TTY/PTYMultiplexer.h>
73#include <Kernel/TTY/VirtualConsole.h>
74#include <Kernel/VM/MemoryManager.h>
75
76// Defined in the linker script
77typedef void (*ctor_func_t)();
78extern ctor_func_t start_ctors;
79extern ctor_func_t end_ctors;
80
81extern u32 __stack_chk_guard;
82u32 __stack_chk_guard;
83
84namespace Kernel {
85
86[[noreturn]] static void init_stage2();
87static void setup_serial_debug();
88static void setup_acpi();
89static void setup_vmmouse();
90static void setup_pci();
91static void setup_interrupts();
92
93VirtualConsole* tty0;
94
95extern "C" [[noreturn]] void init()
96{
97 setup_serial_debug();
98
99 cpu_setup();
100
101 kmalloc_init();
102 slab_alloc_init();
103
104 new KParams(String(reinterpret_cast<const char*>(low_physical_to_virtual(multiboot_info_ptr->cmdline))));
105
106 MemoryManager::initialize();
107
108 bool text_debug = KParams::the().has("text_debug");
109 gdt_init();
110 idt_init();
111
112 setup_acpi();
113 setup_interrupts();
114
115 new VFS;
116 new DebugLogDevice;
117
118 new Console;
119
120 kprintf("Starting SerenityOS...\n");
121
122 __stack_chk_guard = get_good_random<u32>();
123
124 PIT::initialize();
125 RTC::initialize();
126
127 // call global constructors after gtd and itd init
128 for (ctor_func_t* ctor = &start_ctors; ctor < &end_ctors; ctor++)
129 (*ctor)();
130
131 new KeyboardDevice;
132 new PS2MouseDevice;
133 setup_vmmouse();
134
135 new SB16;
136 new NullDevice;
137 if (!get_serial_debug())
138 new SerialDevice(SERIAL_COM1_ADDR, 64);
139 new SerialDevice(SERIAL_COM2_ADDR, 65);
140 new SerialDevice(SERIAL_COM3_ADDR, 66);
141 new SerialDevice(SERIAL_COM4_ADDR, 67);
142
143 VirtualConsole::initialize();
144 tty0 = new VirtualConsole(0, VirtualConsole::AdoptCurrentVGABuffer);
145 new VirtualConsole(1);
146 VirtualConsole::switch_to(0);
147
148 // Sample test to see if the ACPI parser is working...
149 kprintf("ACPI: HPET table @ P 0x%x\n", ACPIParser::the().find_table("HPET").get());
150
151 setup_pci();
152
153 PIT::initialize();
154
155 if (text_debug) {
156 dbgprintf("Text mode enabled\n");
157 } else {
158 if (multiboot_info_ptr->framebuffer_type == 1 || multiboot_info_ptr->framebuffer_type == 2) {
159 new MBVGADevice(
160 PhysicalAddress((u32)(multiboot_info_ptr->framebuffer_addr)),
161 multiboot_info_ptr->framebuffer_pitch,
162 multiboot_info_ptr->framebuffer_width,
163 multiboot_info_ptr->framebuffer_height);
164 } else {
165 new BXVGADevice;
166 }
167 }
168
169 LoopbackAdapter::the();
170
171 Process::initialize();
172 Thread::initialize();
173
174 Thread* init_stage2_thread = nullptr;
175 Process::create_kernel_process(init_stage2_thread, "init_stage2", init_stage2);
176
177 Thread* syncd_thread = nullptr;
178 Process::create_kernel_process(syncd_thread, "syncd", [] {
179 for (;;) {
180 VFS::the().sync();
181 Thread::current->sleep(1 * TICKS_PER_SECOND);
182 }
183 });
184
185 Process::create_kernel_process(g_finalizer, "Finalizer", [] {
186 Thread::current->set_priority(THREAD_PRIORITY_LOW);
187 for (;;) {
188 {
189 InterruptDisabler disabler;
190 if (!g_finalizer_has_work)
191 Thread::current->wait_on(*g_finalizer_wait_queue);
192 ASSERT(g_finalizer_has_work);
193 g_finalizer_has_work = false;
194 }
195 Thread::finalize_dying_threads();
196 }
197 });
198
199 Scheduler::pick_next();
200
201 sti();
202
203 Scheduler::idle_loop();
204 ASSERT_NOT_REACHED();
205}
206
207void init_stage2()
208{
209 Syscall::initialize();
210
211 new ZeroDevice;
212 new FullDevice;
213 new RandomDevice;
214 new PTYMultiplexer;
215
216 bool dmi_unreliable = KParams::the().has("dmi_unreliable");
217 if (dmi_unreliable) {
218 DMIDecoder::initialize_untrusted();
219 } else {
220 DMIDecoder::initialize();
221 }
222
223 bool text_debug = KParams::the().has("text_debug");
224 bool force_pio = KParams::the().has("force_pio");
225
226 auto root = KParams::the().get("root");
227 if (root.is_empty()) {
228 root = "/dev/hda";
229 }
230
231 if (!root.starts_with("/dev/hda")) {
232 kprintf("init_stage2: root filesystem must be on the first IDE hard drive (/dev/hda)\n");
233 hang();
234 }
235
236 auto pata0 = PATAChannel::create(PATAChannel::ChannelType::Primary, force_pio);
237 NonnullRefPtr<BlockDevice> root_dev = *pata0->master_device();
238
239 root = root.substring(strlen("/dev/hda"), root.length() - strlen("/dev/hda"));
240
241 if (root.length()) {
242 bool ok;
243 unsigned partition_number = root.to_uint(ok);
244
245 if (!ok) {
246 kprintf("init_stage2: couldn't parse partition number from root kernel parameter\n");
247 hang();
248 }
249
250 MBRPartitionTable mbr(root_dev);
251
252 if (!mbr.initialize()) {
253 kprintf("init_stage2: couldn't read MBR from disk\n");
254 hang();
255 }
256
257 if (mbr.is_protective_mbr()) {
258 dbgprintf("GPT Partitioned Storage Detected!\n");
259 GPTPartitionTable gpt(root_dev);
260 if (!gpt.initialize()) {
261 kprintf("init_stage2: couldn't read GPT from disk\n");
262 hang();
263 }
264 auto partition = gpt.partition(partition_number);
265 if (!partition) {
266 kprintf("init_stage2: couldn't get partition %d\n", partition_number);
267 hang();
268 }
269 root_dev = *partition;
270 } else {
271 dbgprintf("MBR Partitioned Storage Detected!\n");
272 if (mbr.contains_ebr()) {
273 EBRPartitionTable ebr(root_dev);
274 if (!ebr.initialize()) {
275 kprintf("init_stage2: couldn't read EBR from disk\n");
276 hang();
277 }
278 auto partition = ebr.partition(partition_number);
279 if (!partition) {
280 kprintf("init_stage2: couldn't get partition %d\n", partition_number);
281 hang();
282 }
283 root_dev = *partition;
284 } else {
285 if (partition_number < 1 || partition_number > 4) {
286 kprintf("init_stage2: invalid partition number %d; expected 1 to 4\n", partition_number);
287 hang();
288 }
289 auto partition = mbr.partition(partition_number);
290 if (!partition) {
291 kprintf("init_stage2: couldn't get partition %d\n", partition_number);
292 hang();
293 }
294 root_dev = *partition;
295 }
296 }
297 }
298 auto e2fs = Ext2FS::create(root_dev);
299 if (!e2fs->initialize()) {
300 kprintf("init_stage2: couldn't open root filesystem\n");
301 hang();
302 }
303
304 if (!VFS::the().mount_root(e2fs)) {
305 kprintf("VFS::mount_root failed\n");
306 hang();
307 }
308
309 Process::current->set_root_directory(VFS::the().root_custody());
310
311 dbgprintf("Load ksyms\n");
312 load_ksyms();
313 dbgprintf("Loaded ksyms\n");
314
315 // Now, detect whether or not there are actually any floppy disks attached to the system
316 u8 detect = CMOS::read(0x10);
317 RefPtr<FloppyDiskDevice> fd0;
318 RefPtr<FloppyDiskDevice> fd1;
319 if ((detect >> 4) & 0x4) {
320 fd0 = FloppyDiskDevice::create(FloppyDiskDevice::DriveType::Master);
321 kprintf("fd0 is 1.44MB floppy drive\n");
322 } else {
323 kprintf("fd0 type unsupported! Type == 0x%x\n", detect >> 4);
324 }
325
326 if (detect & 0x0f) {
327 fd1 = FloppyDiskDevice::create(FloppyDiskDevice::DriveType::Slave);
328 kprintf("fd1 is 1.44MB floppy drive");
329 } else {
330 kprintf("fd1 type unsupported! Type == 0x%x\n", detect & 0x0f);
331 }
332
333 int error;
334
335 // SystemServer will start WindowServer, which will be doing graphics.
336 // From this point on we don't want to touch the VGA text terminal or
337 // accept keyboard input.
338 if (text_debug) {
339 tty0->set_graphical(false);
340 Thread* thread = nullptr;
341 Process::create_user_process(thread, "/bin/Shell", (uid_t)0, (gid_t)0, (pid_t)0, error, {}, {}, tty0);
342 if (error != 0) {
343 kprintf("init_stage2: error spawning Shell: %d\n", error);
344 hang();
345 }
346 thread->set_priority(THREAD_PRIORITY_HIGH);
347 } else {
348 tty0->set_graphical(true);
349 Thread* thread = nullptr;
350 Process::create_user_process(thread, "/bin/SystemServer", (uid_t)0, (gid_t)0, (pid_t)0, error, {}, {}, tty0);
351 if (error != 0) {
352 kprintf("init_stage2: error spawning SystemServer: %d\n", error);
353 hang();
354 }
355 thread->set_priority(THREAD_PRIORITY_HIGH);
356 }
357 {
358 Thread* thread = nullptr;
359 Process::create_kernel_process(thread, "NetworkTask", NetworkTask_main);
360 }
361
362 Process::current->sys$exit(0);
363 ASSERT_NOT_REACHED();
364}
365
366void setup_serial_debug()
367{
368 // this is only used one time, directly below here. we can't use this part
369 // of libc at this point in the boot process, or we'd just pull strstr in
370 // from <string.h>.
371 auto bad_prefix_check = [](const char* str, const char* search) -> bool {
372 while (*search)
373 if (*search++ != *str++)
374 return false;
375
376 return true;
377 };
378
379 // serial_debug will output all the kprintf and dbgprintf data to COM1 at
380 // 8-N-1 57600 baud. this is particularly useful for debugging the boot
381 // process on live hardware.
382 //
383 // note: it must be the first option in the boot cmdline.
384 u32 cmdline = low_physical_to_virtual(multiboot_info_ptr->cmdline);
385 if (cmdline && bad_prefix_check(reinterpret_cast<const char*>(cmdline), "serial_debug"))
386 set_serial_debug(true);
387}
388
389extern "C" {
390multiboot_info_t* multiboot_info_ptr;
391}
392
393// Define some Itanium C++ ABI methods to stop the linker from complaining
394// If we actually call these something has gone horribly wrong
395void* __dso_handle __attribute__((visibility("hidden")));
396
397extern "C" int __cxa_atexit(void (*)(void*), void*, void*)
398{
399 ASSERT_NOT_REACHED();
400 return 0;
401}
402
403void setup_acpi()
404{
405 if (!KParams::the().has("acpi")) {
406 ACPIDynamicParser::initialize_without_rsdp();
407 return;
408 }
409
410 auto acpi = KParams::the().get("acpi");
411 if (acpi == "off") {
412 ACPIParser::initialize_limited();
413 return;
414 }
415 if (acpi == "on") {
416 ACPIDynamicParser::initialize_without_rsdp();
417 return;
418 }
419 if (acpi == "limited") {
420 ACPIStaticParser::initialize_without_rsdp();
421 return;
422 }
423 kprintf("acpi boot argmuent has an invalid value.\n");
424 hang();
425}
426
427void setup_vmmouse()
428{
429 VMWareBackdoor::initialize();
430 if (!KParams::the().has("vmmouse")) {
431 VMWareBackdoor::the().enable_absolute_vmmouse();
432 return;
433 }
434 auto vmmouse = KParams::the().get("vmmouse");
435 if (vmmouse == "off")
436 return;
437 if (vmmouse == "on") {
438 VMWareBackdoor::the().enable_absolute_vmmouse();
439 return;
440 }
441 kprintf("vmmouse boot argmuent has an invalid value.\n");
442 hang();
443}
444
445void setup_pci()
446{
447 if (!KParams::the().has("pci_mmio")) {
448 PCI::Initializer::the().test_and_initialize(false);
449 PCI::Initializer::the().dismiss();
450 return;
451 }
452 auto pci_mmio = KParams::the().get("pci_mmio");
453 if (pci_mmio == "on") {
454 PCI::Initializer::the().test_and_initialize(false);
455 } else if (pci_mmio == "off") {
456 PCI::Initializer::the().test_and_initialize(true);
457 } else {
458 kprintf("pci_mmio boot argmuent has an invalid value.\n");
459 hang();
460 }
461 PCI::Initializer::the().dismiss();
462}
463
464static void setup_interrupt_management()
465{
466 auto madt = ACPIParser::the().find_table("APIC");
467 if (madt.is_null()) {
468 InterruptManagement::initialize();
469 return;
470 }
471 AdvancedInterruptManagement::initialize(madt);
472}
473
474void setup_interrupts()
475{
476 setup_interrupt_management();
477
478 if (!KParams::the().has("smp")) {
479 InterruptManagement::the().switch_to_pic_mode();
480 return;
481 }
482 auto smp = KParams::the().get("smp");
483 if (smp == "off") {
484 InterruptManagement::the().switch_to_pic_mode();
485 return;
486 }
487 if (smp == "on") {
488 ASSERT_NOT_REACHED(); // FIXME: The IOAPIC mode is not stable yet so we can't use it now.
489 InterruptManagement::the().switch_to_ioapic_mode();
490 APIC::init();
491 APIC::enable_bsp();
492 return;
493 }
494
495 kprintf("smp boot argmuent has an invalid value.\n");
496 hang();
497}
498}