Serenity Operating System
at portability 498 lines 16 kB view raw
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}