/* * Copyright (c) 2011-2018, 2021-2022 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * kernel.cpp * The main kernel initialization routine. Configures hardware and starts an * initial process from the init ramdisk, allowing a full operating system. */ #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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include "com.h" #include "disk/ahci/ahci.h" #include "disk/ata/ata.h" #include "fs/full.h" #include "fs/kram.h" #include "fs/null.h" #include "fs/random.h" #include "fs/zero.h" #include "gpu/bga/bga.h" #include "initrd.h" #include "kb/default-kblayout.h" #include "kb/kblayout.h" #include "kb/ps2.h" #include "logterminal.h" #include "mouse/ps2.h" #include "multiboot.h" #include "net/em/em.h" #include "net/fs.h" #include "net/lo/lo.h" #include "net/ping.h" #include "net/tcp.h" #include "net/udp.h" #include "poll.h" #include "pty.h" #include "uart.h" #include "vga.h" #if defined(__i386__) || defined(__x86_64__) #include "x86-family/cmos.h" #include "x86-family/float.h" #include "x86-family/gdt.h" #include "x86-family/ps2.h" #include "x86-family/vbox.h" #endif struct kernel_allocation_site* first_kernel_allocation_site = NULL; // Keep the stack size aligned with $CPU/boot.s const size_t STACK_SIZE = 64*1024; extern "C" { __attribute__((aligned(16))) size_t stack[STACK_SIZE / sizeof(size_t)]; } namespace Sortix { // Forward declarations. static void BootThread(void* user); static void InitThread(void* user); static void SystemIdleThread(void* user); static int argc; static char** argv; static multiboot_info_t* bootinfo; static bool enable_em = true; static bool enable_network_drivers = true; static char* cmdline_tokenize(char** saved) { char* data = *saved; if ( !data ) return *saved = NULL; while ( data[0] && isspace((unsigned char) data[0]) ) data++; if ( !data[0] ) return *saved = NULL; size_t input = 0; size_t output = 0; bool singly = false; bool doubly = false; bool escaped = false; for ( ; data[input]; input++ ) { char c = data[input]; if ( !escaped && !singly && !doubly && isspace((unsigned char) c) ) break; if ( !escaped && !doubly && c == '\'' ) { singly = !singly; continue; } if ( !escaped && !singly && c == '"' ) { doubly = !doubly; continue; } if ( !singly && !escaped && c == '\\' ) { escaped = true; continue; } escaped = false; data[output++] = c; } if ( data[input] ) *saved = data + input + 1; else *saved = NULL; data[output] = '\0'; return data; } static void compact_arguments(int* argc, char*** argv) { for ( int i = 0; i < *argc; i++ ) { while ( i < *argc && !(*argv)[i] ) { for ( int n = i; n < *argc; n++ ) (*argv)[n] = (*argv)[n+1]; (*argc)--; } } } extern "C" void KernelInit(unsigned long magic, multiboot_info_t* bootinfo_p) { (void) magic; bootinfo = bootinfo_p; // // Stage 1. Initialization of Early Environment. // // TODO: Call global constructors using the _init function. // Detect available physical memory. Memory::Init(bootinfo); // Initialize randomness from the random seed if provided. Random::Init(bootinfo); // Initialize the kernel log. Log::Init(bootinfo); // Display the logo. Log::PrintF("\e[37;41m\e[2J"); Log::Center(BRAND_LOGO); #if defined(__x86_64__) // TODO: Remove this hack when qemu 1.4.x and 1.5.0 are obsolete. // Verify that we are not running under a buggy qemu where the instruction // movl (%eax), %esi is misinterpreted (amongst others). In this case it // will try to access the memory at [bx + si]. We'll make sure that eax // points to a variable on the stack that has another value than at bx + si, // and if the values compare equal using the buggy instruction, we panic. uint32_t intended_variable; // rax will point to here. uint32_t is_buggy_qemu; asm ("movq $0x1000, %%rbx\n" /* access 32-bit value at 0x1000 */ "movl (%%rbx), %%esi\n" "subl $1, %%esi\n" /* change the 32-bit value */ "movl %%esi, (%%rax)\n" /* store the new value in intended_variable */ "movq $0x0, %%rsi\n" /* make rsi zero, so bx + si points to 0x1000 */ "movl (%%eax), %%esi\n" /* do the perhaps-buggy memory access */ "movl (%%rax), %%ebx\n" /* do a working memory access */ "movl %%ebx, %0\n" /* load the desired value into is_buggy_qemu */ "subl %%esi, %0\n" /* subtract the possibly incorrect value. */ : "=r"(is_buggy_qemu) : "a"(&intended_variable) : "rsi", "rbx"); if ( is_buggy_qemu ) Panic("You are running a buggy version of qemu. The 1.4.x and 1.5.0 " "releases are known to execute some instructions incorrectly on " "x86_64 without KVM. You have three options: 1) Enable KVM 2) " "Use a 32-bit OS 3) Use another version of qemu."); #endif char* cmdline = NULL; if ( bootinfo->flags & MULTIBOOT_INFO_CMDLINE && bootinfo->cmdline ) { addr_t physical_from = Page::AlignDown(bootinfo->cmdline); size_t offset = bootinfo->cmdline - physical_from; size_t desired = 16 * Page::Size(); size_t mapped = offset + desired; addralloc_t alloc; if ( !AllocateKernelAddress(&alloc, mapped) ) Panic("Failed to allocate virtual space for command line"); for ( size_t i = 0; i < mapped; i += Page::Size() ) { if ( !Memory::Map(physical_from + i, alloc.from + i, PROT_KREAD) ) Panic("Failed to memory map command line"); } char* bootloader_cmdline = (char*) (alloc.from + offset); size_t cmdline_length = strnlen(bootloader_cmdline, desired); if ( desired <= cmdline_length ) Panic("Kernel command line is too long"); if ( !(cmdline = strdup(bootloader_cmdline)) ) Panic("Failed to strdup command line"); for ( size_t i = 0; i < mapped; i += Page::Size() ) Memory::Unmap(alloc.from + i); Memory::Flush(); FreeKernelAddress(&alloc); } int argmax = 1; argv = new char*[argmax + 1]; if ( !argv ) Panic("Failed to allocate kernel command line"); char* arg_saved = cmdline; char* arg; while ( (arg = cmdline_tokenize(&arg_saved)) ) { if ( argc == argmax ) { argmax = argmax ? 2 * argmax : 8; char** new_argv = new char*[argmax + 1]; if ( !new_argv ) Panic("Failed to allocate kernel command line"); for ( int i = 0; i < argc; i++ ) new_argv[i] = argv[i]; argv = new_argv; } argv[argc++] = arg; } argv[argc] = NULL; bool no_random_seed = false; for ( int i = 0; i < argc; i++ ) { const char* arg = argv[i]; if ( arg[0] != '-' || !arg[1] ) continue; argv[i] = NULL; if ( !strcmp(arg, "--") ) break; if ( arg[1] != '-' ) { char c; while ( (c = *++arg) ) switch ( c ) { default: Log::PrintF("\r\e[J"); Log::PrintF("kernel: fatal: unknown option -- '%c'\n", c); HaltKernel(); } } else if ( !strcmp(arg, "--disable-em") ) enable_em = false; else if ( !strcmp(arg, "--enable-em") ) enable_em = true; else if ( !strcmp(arg, "--disable-network-drivers") ) enable_network_drivers = false; else if ( !strcmp(arg, "--enable-network-drivers") ) enable_network_drivers = true; else if ( !strcmp(arg, "--no-random-seed") ) no_random_seed = true; else { Log::PrintF("\r\e[J"); Log::PrintF("kernel: fatal: unrecognized option '%s'\n", arg); HaltKernel(); } } compact_arguments(&argc, &argv); if ( argc == 0 ) { argv[argc++] = (char*) "/sbin/init"; argv[argc] = NULL; } // Initialize the interrupt handler table and enable interrupts. Interrupt::Init(); // Initialize the clocks. Time::Init(); // Initialize the real-time clock. CMOS::Init(); // Check a random seed was provided, or try to fallback and warn. int random_status = Random::GetFallbackStatus(); if ( random_status ) { if ( no_random_seed ) { // There's not much we can if this is an initial boot. } else if ( random_status == 1 ) { Log::PrintF("kernel: warning: No random seed file was loaded\n"); Log::PrintF("kernel: warning: With GRUB, try: " "module /boot/random.seed --random-seed\n"); } else { Log::PrintF("kernel: warning: The random seed file is too small\n"); } } // // Stage 2. Transition to Multithreaded Environment // // Initialize Unix Signals. Signal::Init(); // Now that the base system has been loaded, it's time to go threaded. First // we create an object that represents this process. Ref ptable(new ProcessTable()); if ( !ptable ) Panic("Could not allocate the process table"); Process* system = new Process; if ( !system ) Panic("Could not allocate the system process"); if ( (system->pid = (system->ptable = ptable)->Allocate(system)) < 0 ) Panic("Could not allocate the system process a pid"); ptable.Reset(); system->addrspace = Memory::GetAddressSpace(); system->group = system; system->groupprev = NULL; system->groupnext = NULL; system->groupfirst = system; system->session = system; system->sessionprev = NULL; system->sessionnext = NULL; system->sessionfirst = system; if ( !(system->program_image_path = String::Clone("")) ) Panic("Unable to clone string for system process name"); // We construct this thread manually for bootstrap reasons. We wish to // create a kernel thread that is the current thread and isn't put into the // scheduler's set of runnable threads, but rather run whenever there is // _nothing_ else to run on this CPU. Thread* idlethread = new Thread(); idlethread->name = "idle"; idlethread->process = system; idlethread->kernelstackpos = (addr_t) stack; idlethread->kernelstacksize = STACK_SIZE; idlethread->kernelstackmalloced = false; system->firstthread = idlethread; system->threads_not_exiting_count = 1; Scheduler::SetIdleThread(idlethread); // Let's create a regular kernel thread that can decide what happens next. // Note that we don't do the work here: if all other threads are not running // and this thread isn't runnable, then there is nothing to run. Therefore // we must become the system idle thread. RunKernelThread(BootThread, NULL, "boot"); // The time driver will run the scheduler on the next timer interrupt. Time::Start(); // Become the system idle thread. SystemIdleThread(NULL); } static void SystemIdleThread(void* /*user*/) { // Alright, we are now the system idle thread. If there is nothing to do, // then we are run. Note that we must never do any real work here as the // idle thread must always be runnable. while ( true ) { #if defined(__i386__) || defined(__x86_64__) asm volatile ("hlt"); #else #warning "Implement a power efficient kernel idle thread" #endif } } kthread_mutex_t driver_threads_lock = KTHREAD_MUTEX_INITIALIZER; kthread_cond_t driver_threads_cond = KTHREAD_COND_INITIALIZER; size_t driver_threads_done = 0; size_t driver_threads_total = 0; // Parallelize the initialization of slow drivers. static void RunDriverThread(void (*entry)(void*), void* user, const char* name) { kthread_mutex_lock(&driver_threads_lock); driver_threads_total++; kthread_mutex_unlock(&driver_threads_lock); if ( !RunKernelThread(entry, user, name) ) PanicF("RunKernelThread: %s", name); } static void FinishedDriverThread() { ScopedLock lock(&driver_threads_lock); driver_threads_done++; kthread_cond_broadcast(&driver_threads_cond); } struct PS2ThreadCtx { PS2Keyboard* keyboard; PS2Mouse* mouse; }; static void PS2Thread(void* user) { struct PS2ThreadCtx* ctx = (struct PS2ThreadCtx*) user; PS2::Init(ctx->keyboard, ctx->mouse); FinishedDriverThread(); } struct DevCtx { Ref dev; }; static void AHCIThread(void* user) { AHCI::Init("/dev", ((struct DevCtx*) user)->dev); FinishedDriverThread(); } static void ATAThread(void* user) { ATA::Init("/dev", ((struct DevCtx*) user)->dev); FinishedDriverThread(); } static void BGAThread(void* /*user*/) { BGA::Init(); FinishedDriverThread(); } static void EMThread(void* user) { EM::Init("/dev", ((struct DevCtx*) user)->dev); FinishedDriverThread(); } static void BootThread(void* /*user*/) { // // Stage 3. Spawning Kernel Worker Threads. // // Hello, threaded world! You can now regard the kernel as a multi-threaded // process with super-root access to the system. Before we boot the full // system we need to start some worker threads. // Spawn worker threads to asyncronously draw the console thread. TextBuffer* textbuf = Log::device_textbufhandle->Acquire(); if ( textbuf ) { textbuf->SpawnThreads(); Log::device_textbufhandle->Release(textbuf); } // Let's create the interrupt worker thread that executes additional work // requested by interrupt handlers, where such work isn't safe. Interrupt::interrupt_worker_thread = RunKernelThread(Interrupt::WorkerThread, NULL, "interrupt"); if ( !Interrupt::interrupt_worker_thread ) Panic("Could not create interrupt worker"); // Initialize the worker thread data structures. Worker::Init(); // Create a general purpose worker thread. Thread* worker_thread = RunKernelThread(Worker::Thread, NULL, "worker"); if ( !worker_thread ) Panic("Unable to create general purpose worker thread"); // // Stage 4. Initialize the Filesystem // // Bring up the filesystem cache. FileCache::Init(); Ref dtable(new DescriptorTable()); if ( !dtable ) Panic("Unable to allocate descriptor table"); Ref mtable(new MountTable()); if ( !mtable ) Panic("Unable to allocate mount table."); CurrentProcess()->BootstrapTables(dtable, mtable); // Let's begin preparing the filesystem. // TODO: Setup the right device id for the KRAMFS dir? Ref iroot(new KRAMFS::Dir((dev_t) 0, (ino_t) 0, 0, 0, 0755)); if ( !iroot ) Panic("Unable to allocate root inode."); ioctx_t ctx; SetupKernelIOCtx(&ctx); Ref vroot(new Vnode(iroot, Ref(NULL), 0, 0)); if ( !vroot ) Panic("Unable to allocate root vnode."); Ref droot(new Descriptor(vroot, O_SEARCH)); if ( !droot ) Panic("Unable to allocate root descriptor."); CurrentProcess()->BootstrapDirectories(droot); // Initialize the root directory. if ( iroot->link_raw(&ctx, ".", iroot) != 0 ) Panic("Unable to link /. to /"); if ( iroot->link_raw(&ctx, "..", iroot) != 0 ) Panic("Unable to link /.. to /"); // Extract the initrds. if ( bootinfo->mods_count == 0 ) Panic("No initrd was loaded"); ExtractModules(bootinfo, droot); // // Stage 5. Loading and Initializing Core Drivers. // // Get a descriptor for the /dev directory so we can populate it. if ( droot->mkdir(&ctx, "dev", 0775) != 0 && errno != EEXIST ) Panic("Unable to create RAM filesystem /dev directory."); Ref slashdev = droot->open(&ctx, "dev", O_READ | O_DIRECTORY); if ( !slashdev ) Panic("Unable to create descriptor for RAM filesystem /dev directory."); struct DevCtx dev_ctx = { slashdev }; // Initialize the keyboard. PS2Keyboard* keyboard = new PS2Keyboard(); if ( !keyboard ) Panic("Could not allocate PS2 Keyboard driver"); KeyboardLayoutExecutor* kblayout = new KeyboardLayoutExecutor; if ( !kblayout ) Panic("Could not allocate keyboard layout executor"); if ( !kblayout->Upload(default_kblayout, sizeof(default_kblayout)) ) Panic("Could not load the default keyboard layout into the executor"); // Initialize the mouse. PS2Mouse* mouse = new PS2Mouse(); if ( !mouse ) Panic("Could not allocate PS2 Mouse driver"); // Initialize the PS/2 controller. struct PS2ThreadCtx ps2_context = { keyboard, mouse }; RunDriverThread(PS2Thread, &ps2_context, "ps2"); // Register /dev/tty as the current-terminal factory. Ref tty(new DevTTY(slashdev->dev, 0666, 0, 0)); if ( !tty ) Panic("Could not allocate a kernel terminal factory"); if ( LinkInodeInDir(&ctx, slashdev, "tty", tty) != 0 ) Panic("Unable to link /dev/tty to kernel terminal factory."); // Register the psuedo terminal filesystem as /dev/pts. pts = Ref(new PTS(0755, 0, 0)); if ( !pts ) Panic("Could not allocate a psuedo terminal filesystem"); if ( slashdev->mkdir(&ctx, "pts", 0755) < 0 ) Panic("Could not mkdir /dev/pts"); Ref ptsdir = slashdev->open(&ctx, "pts", O_DIRECTORY | O_READ); if ( !ptsdir ) Panic("Could not open /dev/pts"); if ( !mtable->AddMount(ptsdir->ino, ptsdir->dev, pts, true) ) Panic("Could not mount pseudo terminal filesystem on /dev/pts"); if ( slashdev->symlink(&ctx, "pts/ptmx", "ptmx") < 0 ) Panic("Could not symlink /dev/ptmx -> pts/ptmx"); // Register the kernel terminal as /dev/tty1. Ref tty1(new LogTerminal(slashdev->dev, 0666, 0, 0, keyboard, kblayout, "tty1")); if ( !tty1 ) Panic("Could not allocate a kernel terminal"); if ( LinkInodeInDir(&ctx, slashdev, "tty1", tty1) != 0 ) Panic("Unable to link /dev/tty1 to kernel terminal."); // Register the mouse as /dev/mouse. Ref mousedev(new PS2MouseDevice(slashdev->dev, 0666, 0, 0, mouse)); if ( !mousedev ) Panic("Could not allocate a mouse device"); if ( LinkInodeInDir(&ctx, slashdev, "mouse", mousedev) != 0 ) Panic("Unable to link /dev/mouse to mouse."); // Register the null device as /dev/null. Ref null_device(new Null(slashdev->dev, (ino_t) 0, (uid_t) 0, (gid_t) 0, (mode_t) 0666)); if ( !null_device ) Panic("Could not allocate a null device"); if ( LinkInodeInDir(&ctx, slashdev, "null", null_device) != 0 ) Panic("Unable to link /dev/null to the null device."); // Register the zero device as /dev/zero. Ref zero_device(new Zero(slashdev->dev, (ino_t) 0, (uid_t) 0, (gid_t) 0, (mode_t) 0666)); if ( !zero_device ) Panic("Could not allocate a zero device"); if ( LinkInodeInDir(&ctx, slashdev, "zero", zero_device) != 0 ) Panic("Unable to link /dev/zero to the zero device."); // Register the full device as /dev/full. Ref full_device(new Full(slashdev->dev, (ino_t) 0, (uid_t) 0, (gid_t) 0, (mode_t) 0666)); if ( !full_device ) Panic("Could not allocate a full device"); if ( LinkInodeInDir(&ctx, slashdev, "full", full_device) != 0 ) Panic("Unable to link /dev/full to the full device."); // Register the random device as /dev/random. Ref random_device(new DevRandom(slashdev->dev, (ino_t) 0, (uid_t) 0, (gid_t) 0, (mode_t) 0666)); if ( !random_device ) Panic("Could not allocate a random device"); if ( LinkInodeInDir(&ctx, slashdev, "random", random_device) != 0 ) Panic("Unable to link /dev/random to the random device."); if ( LinkInodeInDir(&ctx, slashdev, "urandom", random_device) != 0 ) Panic("Unable to link /dev/urandom to the random device."); // Initialize the COM ports. COM::Init("/dev", slashdev); // Initialize the VGA driver. VGA::Init("/dev", slashdev); // Search for PCI devices and load their drivers. PCI::Init(); #if defined(__i386__) || defined(__x86_64__) // Initialize the VirtualBox Guest Additions. VBox::Init(); #endif // Initialize AHCI devices. RunDriverThread(AHCIThread, &dev_ctx, "ahci"); // Initialize ATA devices. RunDriverThread(ATAThread, &dev_ctx, "ata"); // Initialize the BGA driver. RunDriverThread(BGAThread, NULL, "bga"); // Initialize the filesystem network. NetFS::Init(); // Initialize the ping protocol. Ping::Init(); // Initialize the TCP. TCP::Init(); // Initialize the UDP. UDP::Init(); // Initialize the loopback driver. Loopback::Init("/dev", slashdev); // Initialize the network drivers. if ( enable_network_drivers ) { // Initialize the EM driver. if ( enable_em ) RunDriverThread(EMThread, &dev_ctx, "em"); } // Await the drivers initialized in the background threads. kthread_mutex_lock(&driver_threads_lock); while ( driver_threads_done != driver_threads_total ) kthread_cond_wait(&driver_threads_cond, &driver_threads_lock); kthread_mutex_unlock(&driver_threads_lock); // // Stage 6. Executing Hosted Environment ("User-Space") // // Finally, let's transfer control to a new kernel process that will // eventually run user-space code known as the operating system. addr_t initaddrspace = Memory::Fork(); if ( !initaddrspace ) { Panic("Could not create init's address space"); } Process* init = new Process; if ( !init ) Panic("Could not allocate init process"); if ( (init->pid = (init->ptable = CurrentProcess()->ptable)->Allocate(init)) < 0 ) Panic("Could not allocate init a pid"); kthread_mutex_lock(&process_family_lock); Process* kernel_process = CurrentProcess(); init->parent = kernel_process; init->nextsibling = kernel_process->firstchild; init->prevsibling = NULL; if ( kernel_process->firstchild ) kernel_process->firstchild->prevsibling = init; kernel_process->firstchild = init; init->group = init; init->groupprev = NULL; init->groupnext = NULL; init->groupfirst = init; init->session = init; init->sessionprev = NULL; init->sessionnext = NULL; init->sessionfirst = init; kthread_mutex_unlock(&process_family_lock); // TODO: Why don't we fork from pid=0 and this is done for us? // TODO: Fork dtable and mtable, don't share them! init->BootstrapTables(dtable, mtable); dtable.Reset(); mtable.Reset(); init->BootstrapDirectories(droot); init->addrspace = initaddrspace; Scheduler::SetInitProcess(init); Thread* initthread = RunKernelThread(init, InitThread, NULL, "main"); if ( !initthread ) Panic("Could not create init thread"); // Wait until init is done and then shut down the computer. int status; pid_t pid = CurrentProcess()->Wait(init->pid, &status, 0); if ( pid != init->pid ) PanicF("Waiting for init to exit returned %ji (errno=%i)", (intmax_t) pid, errno); status = WEXITSTATUS(status); switch ( status ) { case 0: CPU::ShutDown(); case 1: CPU::Reboot(); case 2: Log::Print("kernel: fatal: Halting system due to init fatality\n"); Log::Sync(); HaltKernel(); default: Log::PrintF("kernel: fatal: init exited with unexpected exit code %i\n", status); Log::Sync(); HaltKernel(); } } static void InitThread(void* /*user*/) { // We are the init process's first thread. Let's load the init program from // the init ramdisk and transfer execution to it. We will then become a // regular user-space program with root permissions. Process* process = CurrentProcess(); ioctx_t ctx; SetupKernelIOCtx(&ctx); Ref root = CurrentProcess()->GetRoot(); Ref dtable = process->GetDTable(); Ref tty1 = root->open(&ctx, "/dev/tty1", O_READ | O_WRITE); if ( !tty1 ) PanicF("/dev/tty1: %m"); if ( tty1->ioctl(&ctx, TIOCSCTTY, 0) < 0 ) PanicF("/dev/tty1: ioctl: TIOCSCTTY: %m"); tty1.Reset(); Ref tty_stdin = root->open(&ctx, "/dev/tty", O_READ); if ( !tty_stdin || dtable->Allocate(tty_stdin, 0) != 0 ) Panic("Could not prepare stdin for initialization process"); Ref tty_stdout = root->open(&ctx, "/dev/tty", O_WRITE); if ( !tty_stdout || dtable->Allocate(tty_stdout, 0) != 1 ) Panic("Could not prepare stdout for initialization process"); Ref tty_stderr = root->open(&ctx, "/dev/tty", O_WRITE); if ( !tty_stderr || dtable->Allocate(tty_stderr, 0) != 2 ) Panic("Could not prepare stderr for initialization process"); dtable.Reset(); const char* initpath = argv[0]; Ref init = root->open(&ctx, initpath, O_EXEC | O_READ); if ( !init ) PanicF("Could not open %s in early kernel RAM filesystem:\n%s", initpath, strerror(errno)); struct stat st; if ( init->stat(&ctx, &st) ) PanicF("Could not stat '%s' in initrd.", initpath); assert(0 <= st.st_size); if ( (uintmax_t) SIZE_MAX < (uintmax_t) st.st_size ) PanicF("%s is bigger than SIZE_MAX.", initpath); size_t programsize = st.st_size; uint8_t* program = new uint8_t[programsize]; if ( !program ) PanicF("Unable to allocate 0x%zx bytes needed for %s.", programsize, initpath); size_t sofar = 0; while ( sofar < programsize ) { ssize_t numbytes = init->read(&ctx, program+sofar, programsize-sofar); if ( !numbytes ) PanicF("Premature EOF when reading %s.", initpath); if ( numbytes < 0 ) PanicF("IO error when reading %s.", initpath); sofar += numbytes; } init.Reset(); Log::PrintF("\r\e[m\e[J"); int envc = 1; const char* envp[] = { "TERM=sortix", NULL }; struct thread_registers regs; assert((((uintptr_t) ®s) & (alignof(regs)-1)) == 0); if ( process->Execute(initpath, program, programsize, argc, argv, envc, envp, ®s) ) PanicF("Unable to execute %s.", initpath); delete[] program; delete[] argv; // Now become the init process and the operation system shall run. LoadRegisters(®s); } } // namespace Sortix