#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include struct ParsedCommandLine { bool force_pic = false; bool disable_acpi = false; bool disable_serial = false; bool disable_smp = false; bool disable_usb = false; uint8_t ps2_override = 0; BAN::StringView console = "tty0"_sv; BAN::StringView root; }; static bool should_disable_serial(BAN::StringView full_command_line) { const char* start = full_command_line.data(); const char* current = start; while (true) { if (!*current || *current == ' ' || *current == '\t') { if (current - start == 8 && memcmp(start, "noserial", 8) == 0) return true; if (!*current) break; start = current + 1; } current++; } return false; } extern bool g_disable_debug; static ParsedCommandLine cmdline; static void parse_command_line() { auto full_command_line = Kernel::g_boot_info.command_line.sv(); auto arguments = MUST(full_command_line.split(' ')); for (auto argument : arguments) { if (argument == "noapic") cmdline.force_pic = true; else if (argument == "noserial") cmdline.disable_serial = true; else if (argument == "nosmp") cmdline.disable_smp = true; else if (argument == "nousb") cmdline.disable_usb = true; else if (argument == "noacpi") cmdline.disable_acpi = true; else if (argument == "readonly") Kernel::g_disable_disk_write = true; else if (argument == "nodebug") g_disable_debug = true; else if (argument.starts_with("ps2=")) { if (argument == "ps2=auto"_sv) cmdline.ps2_override = 0xFF; else { if (argument.size() != 5 || !isdigit(argument[4])) dprintln("Invalid ps2= command line argument format '{}'", argument); cmdline.ps2_override = argument[4] - '0'; } } else if (argument.size() > 5 && argument.substring(0, 5) == "root=") cmdline.root = argument.substring(5); else if (argument.size() > 8 && argument.substring(0, 8) == "console=") cmdline.console = argument.substring(8); } } static void init2(void*); extern "C" void kernel_main(uint32_t boot_magic, uint32_t boot_info) { using namespace Kernel; if (!validate_boot_magic(boot_magic)) { Serial::initialize(); dprintln("Unrecognized boot magic {8H}", boot_magic); return; } if (!should_disable_serial(get_early_boot_command_line(boot_magic, boot_info))) { Serial::initialize(); dprintln("Serial output initialized"); } kmalloc_initialize(); dprintln("kmalloc initialized"); parse_boot_info(boot_magic, boot_info); dprintln("boot info parsed"); Processor::create(PROCESSOR_NONE); Processor::initialize(); dprintln("BSP initialized"); PageTable::initialize_pre_heap(); PageTable::kernel().initial_load(); dprintln("PageTable stage1 initialized"); Heap::initialize(); dprintln("Heap initialzed"); PageTable::initialize_post_heap(); dprintln("PageTable stage2 initialized"); parse_command_line(); dprintln("command line parsed, root='{}', console='{}'", cmdline.root, cmdline.console); MUST(ACPI::ACPI::initialize()); dprintln("ACPI initialized"); InterruptController::initialize(cmdline.force_pic); dprintln("Interrupt controller initialized"); SystemTimer::initialize(cmdline.force_pic); dprintln("Timers initialized"); DevFileSystem::initialize(); dprintln("devfs initialized"); if (auto ret = TerminalDriver::initialize_from_boot_info(); ret.is_error()) dprintln("failed to initialize terminal driver: {}", ret.error()); else dprintln("terminal driver initialized"); if (!cmdline.disable_smp) InterruptController::get().initialize_multiprocessor(); ProcFileSystem::initialize(); dprintln("procfs initialized"); MUST(SharedMemoryObjectManager::initialize()); dprintln("Shared memory object system initialized"); if (Serial::has_devices()) { Serial::initialize_devices(); dprintln("Serial devices initialized"); } if (g_terminal_driver) { auto vtty = MUST(VirtualTTY::create(g_terminal_driver)); dprintln("Virtual TTY initialized"); } Random::initialize(); dprintln("RNG initialized"); if (InterruptController::get().is_using_apic()) { SystemTimer::get().dont_invoke_scheduler(); static_cast(InterruptController::get()).initialize_timer(); } Processor::wait_until_processors_ready(); MUST(Processor::scheduler().initialize()); auto* init_thread = MUST(Thread::create_kernel(init2, nullptr)); MUST(Processor::scheduler().add_thread(init_thread)); Processor::yield(); ASSERT_NOT_REACHED(); } static void init2(void*) { using namespace Kernel; using namespace Kernel::Input; dprintln("Scheduler started"); auto console = MUST(DevFileSystem::get().root_inode()->find_inode(cmdline.console)); ASSERT(console->is_tty()); static_cast(console.ptr())->set_as_current(); // This only initializes PCIManager by enumerating available devices and choosing PCIe/legacy // ACPI might require PCI access during its namespace initialization PCI::PCIManager::initialize(); dprintln("PCI initialized"); if (!cmdline.disable_usb) { MUST(USBManager::initialize()); dprintln("USBManager initialized"); } if (!cmdline.disable_acpi) { if (auto ret = ACPI::ACPI::get().enter_acpi_mode(InterruptController::get().is_using_apic()); ret.is_error()) dprintln("Failed to enter ACPI mode: {}", ret.error()); if (auto ret = ACPI::ACPI::get().initialize_acpi_devices(); ret.is_error()) dwarnln("Could not initialize ACPI devices: {}", ret.error()); } DevFileSystem::get().initialize_device_updater(); #if 0 dprintln("sleeping for 5 seconds"); SystemTimer::get().sleep(5000); #endif // Initialize empty keymap MUST(LibInput::KeyboardLayout::initialize()); if (auto res = PS2Controller::initialize(cmdline.ps2_override); res.is_error()) dprintln("{}", res.error()); MUST(NetworkManager::initialize()); // NOTE: PCI devices are the last ones to be initialized // so other devices can reserve predefined interrupts PCI::PCIManager::get().initialize_devices(cmdline.disable_usb); dprintln("PCI devices initialized"); VirtualFileSystem::initialize(cmdline.root); dprintln("VFS initialized"); // FIXME: release memory used by modules. If modules are used // they are already loaded in here TTY::initialize_devices(); auto console_path = MUST(BAN::String::formatted("/dev/{}", cmdline.console)); auto console_path_sv = console_path.sv(); MUST(Process::create_userspace({ 0, 0, 0, 0 }, "/usr/bin/init"_sv, BAN::Span(&console_path_sv, 1))); } extern "C" void ap_main() { using namespace Kernel; Processor::initialize(); PageTable::kernel().initial_load(); InterruptController::get().enable(); Processor::wait_until_processors_ready(); MUST(Processor::scheduler().initialize()); asm volatile("sti; 1: hlt; jmp 1b"); ASSERT_NOT_REACHED(); } // comment from banan_os