Serenity Operating System
at hosted 358 lines 12 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 <AK/Types.h> 28#include <Kernel/ACPI/DMIDecoder.h> 29#include <Kernel/ACPI/DynamicParser.h> 30#include <Kernel/ACPI/Initialize.h> 31#include <Kernel/ACPI/MultiProcessorParser.h> 32#include <Kernel/Arch/i386/CPU.h> 33#include <Kernel/CMOS.h> 34#include <Kernel/CommandLine.h> 35#include <Kernel/Devices/BXVGADevice.h> 36#include <Kernel/Devices/DiskPartition.h> 37#include <Kernel/Devices/EBRPartitionTable.h> 38#include <Kernel/Devices/FullDevice.h> 39#include <Kernel/Devices/GPTPartitionTable.h> 40#include <Kernel/Devices/KeyboardDevice.h> 41#include <Kernel/Devices/MBRPartitionTable.h> 42#include <Kernel/Devices/MBVGADevice.h> 43#include <Kernel/Devices/NullDevice.h> 44#include <Kernel/Devices/PATAChannel.h> 45#include <Kernel/Devices/PATADiskDevice.h> 46#include <Kernel/Devices/PS2MouseDevice.h> 47#include <Kernel/Devices/RandomDevice.h> 48#include <Kernel/Devices/SB16.h> 49#include <Kernel/Devices/SerialDevice.h> 50#include <Kernel/Devices/VMWareBackdoor.h> 51#include <Kernel/Devices/ZeroDevice.h> 52#include <Kernel/FileSystem/Ext2FileSystem.h> 53#include <Kernel/FileSystem/VirtualFileSystem.h> 54#include <Kernel/Heap/SlabAllocator.h> 55#include <Kernel/Heap/kmalloc.h> 56#include <Kernel/Interrupts/APIC.h> 57#include <Kernel/Interrupts/InterruptManagement.h> 58#include <Kernel/Interrupts/PIC.h> 59#include <Kernel/KSyms.h> 60#include <Kernel/Multiboot.h> 61#include <Kernel/Net/LoopbackAdapter.h> 62#include <Kernel/Net/NetworkTask.h> 63#include <Kernel/PCI/Access.h> 64#include <Kernel/PCI/Initializer.h> 65#include <Kernel/Process.h> 66#include <Kernel/RTC.h> 67#include <Kernel/Random.h> 68#include <Kernel/Scheduler.h> 69#include <Kernel/TTY/PTYMultiplexer.h> 70#include <Kernel/TTY/VirtualConsole.h> 71#include <Kernel/Tasks/FinalizerTask.h> 72#include <Kernel/Tasks/SyncTask.h> 73#include <Kernel/Time/TimeManagement.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(); 88 89VirtualConsole* tty0; 90 91// SerenityOS Kernel C++ entry point :^) 92// 93// This is where C++ execution begins, after boot.S transfers control here. 94// 95// The purpose of init() is to start multi-tasking. It does the bare minimum 96// amount of work needed to start the scheduler. 97// 98// Once multi-tasking is ready, we spawn a new thread that starts in the 99// init_stage2() function. Initialization continues there. 100 101extern "C" [[noreturn]] void init() 102{ 103 setup_serial_debug(); 104 105 cpu_setup(); 106 107 kmalloc_init(); 108 slab_alloc_init(); 109 110 CommandLine::initialize(reinterpret_cast<const char*>(low_physical_to_virtual(multiboot_info_ptr->cmdline))); 111 112 MemoryManager::initialize(); 113 114 gdt_init(); 115 idt_init(); 116 117 // Invoke all static global constructors in the kernel. 118 // Note that we want to do this as early as possible. 119 for (ctor_func_t* ctor = &start_ctors; ctor < &end_ctors; ctor++) 120 (*ctor)(); 121 122 InterruptManagement::initialize(); 123 ACPI::initialize(); 124 125 new VFS; 126 new KeyboardDevice; 127 new PS2MouseDevice; 128 new Console; 129 130 klog() << "Starting SerenityOS..."; 131 132 __stack_chk_guard = get_good_random<u32>(); 133 134 TimeManagement::initialize(); 135 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 Process::initialize(); 149 Thread::initialize(); 150 151 Thread* init_stage2_thread = nullptr; 152 Process::create_kernel_process(init_stage2_thread, "init_stage2", init_stage2); 153 154 Scheduler::pick_next(); 155 156 sti(); 157 158 Scheduler::idle_loop(); 159 ASSERT_NOT_REACHED(); 160} 161 162void init_stage2() 163{ 164 SyncTask::spawn(); 165 FinalizerTask::spawn(); 166 167 PCI::initialize(); 168 169 if (kernel_command_line().contains("text_debug")) { 170 dbg() << "Text mode enabled"; 171 } else { 172 if (multiboot_info_ptr->framebuffer_type == 1 || multiboot_info_ptr->framebuffer_type == 2) { 173 new MBVGADevice( 174 PhysicalAddress((u32)(multiboot_info_ptr->framebuffer_addr)), 175 multiboot_info_ptr->framebuffer_pitch, 176 multiboot_info_ptr->framebuffer_width, 177 multiboot_info_ptr->framebuffer_height); 178 } else { 179 new BXVGADevice; 180 } 181 } 182 183 LoopbackAdapter::the(); 184 185 Syscall::initialize(); 186 187 new ZeroDevice; 188 new FullDevice; 189 new RandomDevice; 190 new PTYMultiplexer; 191 new SB16; 192 VMWareBackdoor::initialize(); 193 194 bool dmi_unreliable = kernel_command_line().contains("dmi_unreliable"); 195 if (dmi_unreliable) { 196 DMIDecoder::initialize_untrusted(); 197 } else { 198 DMIDecoder::initialize(); 199 } 200 201 bool text_debug = kernel_command_line().contains("text_debug"); 202 bool force_pio = kernel_command_line().contains("force_pio"); 203 204 auto root = kernel_command_line().get("root"); 205 if (root.is_empty()) { 206 root = "/dev/hda"; 207 } 208 209 if (!root.starts_with("/dev/hda")) { 210 klog() << "init_stage2: root filesystem must be on the first IDE hard drive (/dev/hda)"; 211 hang(); 212 } 213 214 auto pata0 = PATAChannel::create(PATAChannel::ChannelType::Primary, force_pio); 215 NonnullRefPtr<BlockDevice> root_dev = *pata0->master_device(); 216 217 root = root.substring(strlen("/dev/hda"), root.length() - strlen("/dev/hda")); 218 219 if (root.length()) { 220 bool ok; 221 unsigned partition_number = root.to_uint(ok); 222 223 if (!ok) { 224 klog() << "init_stage2: couldn't parse partition number from root kernel parameter"; 225 hang(); 226 } 227 228 MBRPartitionTable mbr(root_dev); 229 230 if (!mbr.initialize()) { 231 klog() << "init_stage2: couldn't read MBR from disk"; 232 hang(); 233 } 234 235 if (mbr.is_protective_mbr()) { 236 dbg() << "GPT Partitioned Storage Detected!"; 237 GPTPartitionTable gpt(root_dev); 238 if (!gpt.initialize()) { 239 klog() << "init_stage2: couldn't read GPT from disk"; 240 hang(); 241 } 242 auto partition = gpt.partition(partition_number); 243 if (!partition) { 244 klog() << "init_stage2: couldn't get partition " << partition_number; 245 hang(); 246 } 247 root_dev = *partition; 248 } else { 249 dbg() << "MBR Partitioned Storage Detected!"; 250 if (mbr.contains_ebr()) { 251 EBRPartitionTable ebr(root_dev); 252 if (!ebr.initialize()) { 253 klog() << "init_stage2: couldn't read EBR from disk"; 254 hang(); 255 } 256 auto partition = ebr.partition(partition_number); 257 if (!partition) { 258 klog() << "init_stage2: couldn't get partition " << partition_number; 259 hang(); 260 } 261 root_dev = *partition; 262 } else { 263 if (partition_number < 1 || partition_number > 4) { 264 klog() << "init_stage2: invalid partition number " << partition_number << "; expected 1 to 4"; 265 hang(); 266 } 267 auto partition = mbr.partition(partition_number); 268 if (!partition) { 269 klog() << "init_stage2: couldn't get partition " << partition_number; 270 hang(); 271 } 272 root_dev = *partition; 273 } 274 } 275 } 276 auto e2fs = Ext2FS::create(*FileDescription::create(root_dev)); 277 if (!e2fs->initialize()) { 278 klog() << "init_stage2: couldn't open root filesystem"; 279 hang(); 280 } 281 282 if (!VFS::the().mount_root(e2fs)) { 283 klog() << "VFS::mount_root failed"; 284 hang(); 285 } 286 287 Process::current->set_root_directory(VFS::the().root_custody()); 288 289 load_kernel_symbol_table(); 290 291 int error; 292 293 // SystemServer will start WindowServer, which will be doing graphics. 294 // From this point on we don't want to touch the VGA text terminal or 295 // accept keyboard input. 296 if (text_debug) { 297 tty0->set_graphical(false); 298 Thread* thread = nullptr; 299 Process::create_user_process(thread, "/bin/Shell", (uid_t)0, (gid_t)0, (pid_t)0, error, {}, {}, tty0); 300 if (error != 0) { 301 klog() << "init_stage2: error spawning Shell: " << error; 302 hang(); 303 } 304 thread->set_priority(THREAD_PRIORITY_HIGH); 305 } else { 306 tty0->set_graphical(true); 307 Thread* thread = nullptr; 308 Process::create_user_process(thread, "/bin/SystemServer", (uid_t)0, (gid_t)0, (pid_t)0, error, {}, {}, tty0); 309 if (error != 0) { 310 klog() << "init_stage2: error spawning SystemServer: " << error; 311 hang(); 312 } 313 thread->set_priority(THREAD_PRIORITY_HIGH); 314 } 315 316 NetworkTask::spawn(); 317 318 Process::current->sys$exit(0); 319 ASSERT_NOT_REACHED(); 320} 321 322void setup_serial_debug() 323{ 324 // this is only used one time, directly below here. we can't use this part 325 // of libc at this point in the boot process, or we'd just pull strstr in 326 // from <string.h>. 327 auto bad_prefix_check = [](const char* str, const char* search) -> bool { 328 while (*search) 329 if (*search++ != *str++) 330 return false; 331 332 return true; 333 }; 334 335 // serial_debug will output all the klog() and dbg() data to COM1 at 336 // 8-N-1 57600 baud. this is particularly useful for debugging the boot 337 // process on live hardware. 338 // 339 // note: it must be the first option in the boot cmdline. 340 u32 cmdline = low_physical_to_virtual(multiboot_info_ptr->cmdline); 341 if (cmdline && bad_prefix_check(reinterpret_cast<const char*>(cmdline), "serial_debug")) 342 set_serial_debug(true); 343} 344 345extern "C" { 346multiboot_info_t* multiboot_info_ptr; 347} 348 349// Define some Itanium C++ ABI methods to stop the linker from complaining 350// If we actually call these something has gone horribly wrong 351void* __dso_handle __attribute__((visibility("hidden"))); 352 353extern "C" int __cxa_atexit(void (*)(void*), void*, void*) 354{ 355 ASSERT_NOT_REACHED(); 356 return 0; 357} 358}