[
  {
    "path": ".cargo/config.toml",
    "content": "[build]\ntarget = \"x86_64-unknown-none\"\nrustflags = [\"-Cforce-frame-pointers=yes\"]\n\n[unstable]\nbuild-std = [\"core\", \"compiler_builtins\", \"alloc\"]\nbuild-std-features = [\"compiler-builtins-mem\"]\n\n[alias]\nqemu = \"run --release --config .cargo/release.toml\"\nqemu-debug = \"run --config .cargo/debug.toml\"\n"
  },
  {
    "path": ".cargo/debug.toml",
    "content": "[target.x86_64-unknown-none]\nrunner = [\"bash\", \".cargo/runner_debug.sh\"]\n"
  },
  {
    "path": ".cargo/linker.ld",
    "content": "\nENTRY(start)\nOUTPUT_ARCH(i386:x86-64)\nOUTPUT_FORMAT(elf64-x86-64)\n\nKERNEL_BASE = 0xffffffff80000000;\n\nSECTIONS {\n    . = KERNEL_BASE + SIZEOF_HEADERS;\n\n    .hash                   : { *(.hash) }\n    .gnu.hash               : { *(.gnu.hash) }\n    .dynsym                 : { *(.dynsym) }\n    .dynstr                 : { *(.dynstr) }\n    .rela                   : { *(.rela*) }\n    .rodata                 : { *(.rodata .rodata.*) }\n    .note.gnu.build-id      : { *(.note.gnu.build-id) }\n    .eh_frame_hdr           : {\n        PROVIDE(__eh_frame_hdr = .);\n        KEEP(*(.eh_frame_hdr))\n        PROVIDE(__eh_frame_hdr_end = .);\n    }\n    .eh_frame               : {\n        PROVIDE(__eh_frame = .);\n        KEEP(*(.eh_frame))\n        PROVIDE(__eh_frame_end = .);\n    }\n    .gcc_except_table       : { KEEP(*(.gcc_except_table .gcc_except_table.*)) }\n\n    . += CONSTANT(MAXPAGESIZE);\n\n    .plt                    : { *(.plt .plt.*) }\n    .text                   : { *(.text .text.*) }\n\n    . += CONSTANT(MAXPAGESIZE);\n\n    .tdata                  : { *(.tdata .tdata.*) }\n    .tbss                   : { *(.tbss .tbss.*) }\n\n    .data.rel.ro            : { *(.data.rel.ro .data.rel.ro.*) }\n    .dynamic                : { *(.dynamic) }\n\n    . = DATA_SEGMENT_RELRO_END(0, .);\n\n    .got                    : { *(.got .got.*) }\n    .got.plt                : { *(.got.plt .got.plt.*) }\n    .data                   : { *(.data .data.*) }\n    .bss                    : { *(.bss .bss.*) *(COMMON) }\n\n    . = DATA_SEGMENT_END(.);\n\n    .comment              0 : { *(.comment) }\n    .debug                0 : { *(.debug) }\n    .debug_abbrev         0 : { *(.debug_abbrev) }\n    .debug_aranges        0 : { *(.debug_aranges) }\n    .debug_frame          0 : { *(.debug_frame) }\n    .debug_funcnames      0 : { *(.debug_funcnames) }\n    .debug_info           0 : { *(.debug_info .gnu.linkonce.wi.*) }\n    .debug_line           0 : { *(.debug_line) }\n    .debug_loc            0 : { *(.debug_loc) }\n    .debug_macinfo        0 : { *(.debug_macinfo) }\n    .debug_pubnames       0 : { *(.debug_pubnames) }\n    .debug_pubtypes       0 : { *(.debug_pubtypes) }\n    .debug_ranges         0 : { *(.debug_ranges) }\n    .debug_sfnames        0 : { *(.debug_sfnames) }\n    .debug_srcinfo        0 : { *(.debug_srcinfo) }\n    .debug_str            0 : { *(.debug_str) }\n    .debug_typenames      0 : { *(.debug_typenames) }\n    .debug_varnames       0 : { *(.debug_varnames) }\n    .debug_weaknames      0 : { *(.debug_weaknames) }\n    .line                 0 : { *(.line) }\n    .shstrtab             0 : { *(.shstrtab) }\n    .strtab               0 : { *(.strtab) }\n    .symtab               0 : { *(.symtab) }\n}"
  },
  {
    "path": ".cargo/release.toml",
    "content": "[target.x86_64-unknown-none]\nrunner = [\"bash\", \".cargo/runner_release.sh\"]\n"
  },
  {
    "path": ".cargo/runner_debug.sh",
    "content": "#! /bin/bash\n#\n# This script will be executed by `cargo run`.\n\nset -xe\n\nLIMINE_GIT_URL=\"https://github.com/limine-bootloader/limine.git\"\n\n# Cargo passes the path to the built executable as the first argument.\nKERNEL=$1\n\n\n# Clone the `limine` repository if we don't have it yet.\nif [ ! -d target/limine ]; then\n    git clone $LIMINE_GIT_URL --depth=1 --branch v3.0-branch-binary target/limine\nfi\n\n# Make sure we have an up-to-date version of the bootloader.\ncd target/limine\ngit fetch\nmake\ncd -\n\n# Copy the needed files into an ISO image.\nmkdir -p target/iso_root\ncp $KERNEL conf/limine.cfg target/limine/limine{.sys,-cd.bin,-cd-efi.bin} target/iso_root\n\n\nxorriso -as mkisofs                                             \\\n    -b limine-cd.bin                                            \\\n    -no-emul-boot -boot-load-size 4 -boot-info-table            \\\n    --efi-boot limine-cd-efi.bin                                \\\n    -efi-boot-part --efi-boot-image --protective-msdos-label    \\\n    target/iso_root -o $KERNEL.iso\n\n# For the image to be bootable on BIOS systems, we must run `limine-deploy` on it.\ntarget/limine/limine-deploy $KERNEL.iso\n\n# Run the created image with QEMU.\n# -machine q35 -cpu EPYC\necho \"Running in debug mode.\" >&2\nqemu-system-x86_64 \\\n    -machine q35 -cpu EPYC -M smm=off \\\n    -D target/log.txt -d int,guest_errors -no-reboot -no-shutdown \\\n    -s -S \\\n    -serial stdio \\\n    -serial file:target/fb_log.txt \\\n    -m 4G \\\n    -cdrom $KERNEL.iso >&2\n"
  },
  {
    "path": ".cargo/runner_release.sh",
    "content": "#! /bin/bash\n#\n# This script will be executed by `cargo run`.\n\nset -xe\n\nLIMINE_GIT_URL=\"https://github.com/limine-bootloader/limine.git\"\n\n# Cargo passes the path to the built executable as the first argument.\nKERNEL=$1\n\n\n# Clone the `limine` repository if we don't have it yet.\nif [ ! -d target/limine ]; then\n    git clone $LIMINE_GIT_URL --depth=1 --branch v3.0-branch-binary target/limine\nfi\n\n# Make sure we have an up-to-date version of the bootloader.\ncd target/limine\ngit fetch\nmake\ncd -\n\n# Copy the needed files into an ISO image.\nmkdir -p target/iso_root\ncp $KERNEL conf/limine.cfg target/limine/limine{.sys,-cd.bin,-cd-efi.bin} target/iso_root\n\n\nxorriso -as mkisofs                                             \\\n    -b limine-cd.bin                                            \\\n    -no-emul-boot -boot-load-size 4 -boot-info-table            \\\n    --efi-boot limine-cd-efi.bin                                \\\n    -efi-boot-part --efi-boot-image --protective-msdos-label    \\\n    target/iso_root -o $KERNEL.iso\n\n# For the image to be bootable on BIOS systems, we must run `limine-deploy` on it.\ntarget/limine/limine-deploy $KERNEL.iso\n\n# Run the created image with QEMU.\n# -machine q35 -cpu EPYC\necho \"Running in release mode.\" >&2\nqemu-system-x86_64 \\\n    -M smm=off \\\n    -machine q35 -cpu EPYC \\\n    -D target/log.txt -d int,guest_errors -no-reboot -no-shutdown \\\n    -s \\\n    -serial stdio \\\n    -serial file:target/fb_log.txt \\\n    -m 4G \\\n    -cdrom $KERNEL.iso >&2\n"
  },
  {
    "path": ".cargo/x86_64-kados.json",
    "content": "{\r\n    \"llvm-target\": \"x86_64-unknown-none-elf\",\r\n    \"target-endian\": \"little\",\r\n    \"target-pointer-width\": \"64\",\r\n    \"target-c-int-width\": \"32\",\r\n    \"data-layout\": \"e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128\",\r\n    \"arch\": \"x86_64\",\r\n    \"os\": \"none\",\r\n    \"env\": \"\",\r\n    \"vendor\": \"unknown\",\r\n    \"linker\": \"rust-lld\",\r\n    \"linker-flavor\": \"ld.lld\",\r\n    \"pre-link-args\": {\r\n        \"ld.lld\": [\r\n            \"--script=.cargo/linker.ld\"\r\n        ]\r\n    },\r\n    \"features\": \"-mmx,-sse,+soft-float\",\r\n    \"executables\": true,\r\n    \"relocation-model\": \"pic\",\r\n    \"code-model\": \"kernel\",\r\n    \"disable-redzone\": true,\r\n    \"frame-pointer\": \"always\",\r\n    \"no-default-libraries\": true,\r\n    \"position-independent-executables\": false,\r\n    \"tls-model\": \"global-dynamic\"\r\n}"
  },
  {
    "path": ".gitignore",
    "content": "/target\n/initramfs\n/initramfs_old\n/extern\n*.objdump\n*.map\nstrace_vi.txt\nkados.map\n/extern_old"
  },
  {
    "path": ".vscode/launch.json",
    "content": "{\n    \"version\": \"0.2.0\",\n    \"configurations\": [\n        {\n            \"type\": \"lldb\",\n            \"request\": \"custom\",\n            \"name\": \"Attach to gdbserver\",\n            \"initCommands\": [\n                \"platform select remote-gdb-server\"\n            ],\n            \"targetCreateCommands\": [\n                \"target create ${workspaceFolder}/target/iso_root/k4dos\"\n            ],\n            \"processCreateCommands\": [\n                \"gdb-remote 127.0.0.1:1234\"\n            ]\n        },\n    ]\n}"
  },
  {
    "path": ".vscode/settings.json",
    "content": "{\n    \"lldb.displayFormat\": \"auto\",\n    \"lldb.dereferencePointers\": true,\n    \"lldb.consoleMode\": \"commands\",\n    \"lldb.showDisassembly\": \"auto\",\n    \"rust-analyzer.check.command\": \"clippy\",\n    \"editor.formatOnSave\": true,\n    \"rust-analyzer.cargo.target\": \"x86_64-unknown-none\",\n}"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"k4dos\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n[[bin]]\nname = \"k4dos\"\ntest = false\nbench = false\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\nlimine = \"0.3.1\"\nvolatile = \"0.2.6\" # DO NOT CHANGE\nspin = \"0.9.8\"\nbit_field = \"0.10.2\"\nx86 = \"0.52.0\"\nx86_64 = \"0.15.2\"\nlog = \"0.4\"\npic8259 = \"0.11\"\nbitflags = \"2.6.0\"\nuart_16550 = \"0.3.2\"\ncrossbeam-utils = { version = \"0.8.20\", default-features = false }\natomic_refcell = \"0.1.13\"\nbuddy_system_allocator = { version = \"0.11.0\", features = [] }\npc-keyboard = \"0.8.0\"\nxmas-elf = \"0.8\"\nrustc-demangle = \"0.1.24\"\narrayvec = { version = \"0.7.6\", default-features = false }\nx2apic = \"0.4.3\"\nelfloader = \"0.16.0\"\nbitvec = { version = \"1.0.1\", default-features = false }\nembedded-graphics = \"0.8.1\"\nsmoltcp = { version = \"0.12.0\", default-features = false, features = [\n    \"alloc\",\n    \"proto-ipv4\",\n    \"socket\",\n    \"socket-raw\",\n    \"socket-udp\",\n    \"socket-tcp\",\n    \"proto-dhcpv4\",\n    \"medium-ethernet\",\n] }\nlazy_static = { version = \"1.5.0\", features = [\"spin_no_std\"] }\nembedded-profiling = { version = \"0.3.0\", features = [\"proc-macros\"] }\n\n[profile.release]\ndebug = 1\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2022 Connor Statham\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# k4dos\n\nK4DOS is a hobby operating system written in Rust. It current supports x86_64 architecture and aims for Linux ABI compatibility (running unmodified Linux programs).\n\n## k4dos running NetHack\n\n![image](https://user-images.githubusercontent.com/58794204/227795203-976c46ac-1c75-4125-89d8-4377397deff0.png)\n\n## k4dos running FreeDOOM\n\n![image](https://user-images.githubusercontent.com/58794204/227990608-a0dec362-8624-4869-84ea-9db5dd170a14.png)\n\n## k4dos running [RustyRays](https://github.com/clstatham/rustyrays/tree/k4dos_port), my own ray tracer\n\n![image](https://user-images.githubusercontent.com/58794204/228302199-c4a6e4ea-590d-4453-8987-1374773342b3.png)\n\n## Building\n\nI haven't set up a proper build system for this to work on other people's machines yet. Contact me or open a PR if you'd like to help with that.\nFor now, this repo exists to present the source code to others who might want to learn from it.\n\n## License\n\nMIT licensed (see LICENSE file)\n"
  },
  {
    "path": "build.rs",
    "content": "use std::{env, error::Error};\n\nfn main() -> Result<(), Box<dyn Error + Send + Sync>> {\n    // Get the name of the package.\n    let kernel_name = env::var(\"CARGO_PKG_NAME\")?;\n\n    // let status = Command::new(\"./deps.sh\")\n    //     .args([\"makeimg\"])\n    //     .status()\n    //     .unwrap();\n    // assert!(status.success());\n\n    println!(\"cargo:rustc-link-arg-bin={kernel_name}=--script=.cargo/linker.ld\");\n\n    // Add linker args.\n    println!(\"cargo:rustc-link-arg-bin={kernel_name}=--gc-sections\");\n\n    // Have cargo rerun this script if the linker script or CARGO_PKG_ENV changes.\n    println!(\"cargo:rerun-if-changed=.cargo/linker.ld\");\n    println!(\"cargo:rerun-if-env-changed=CARGO_PKG_NAME\");\n    println!(\"cargo:rerun-if-changed=build.rs\");\n\n    Ok(())\n}\n"
  },
  {
    "path": "conf/grub.cfg",
    "content": "set timeout=0\nset default=0\nGRUB_TERMINAL=console\n\nmenuentry \"K4DOS\" {\n    multiboot2 /boot/k4dos\n    boot\n}"
  },
  {
    "path": "conf/limine.cfg",
    "content": "TIMEOUT=0\nVERBOSE=yes\n\n: k4dos\nPROTOCOL=limine\nKASLR=no\nRESOLUTION=640x400x32\nKERNEL_PATH=boot:///k4dos"
  },
  {
    "path": "deps.sh",
    "content": "#!/bin/bash\n\nset -e\n\nif [[ -z \"$@\" ]]; then\n    echo \"Usage: deps.sh [COMMANDS]...\"\n    echo \"Available commands:\"\n    echo \"download       Downloads Busybox\"\n    echo \"menuconfig     Configures Busybox with menuconfig\"\n    echo \"allnoconfig    Configures Busybox with allnoconfig\"\n    echo \"defconfig      Configures Busybox with its default config (currently doesn't build!)\"\n    echo \"clean          Cleans the output directory\"\n    echo \"build          Builds Busybox\"\n    echo \"makeimg        Creates the initramfs image for K4DOS\"\n    echo \"kash           Builds Kash, the K4DOS shell\"\n    exit\nfi\n\nSTARTDIR=$(pwd)\n\nmkdir -p extern\n\ncd extern\nif [[ $@ =~ \"download\" ]];\nthen\n    echo\n    echo \"!!!IMPORTANT!!!\"\n    echo \"If you just want a prebuilt image, go to the following link to download one and put it in a new folder in this directory called initramfs\"\n    echo \"The prebuilt image currently contains preconfigured and prebuilt distributions of Busybox, FreeDOOM, NetHack, and RustyRays.\"\n    echo \"Busybox, FreeDOOM, Doom-Generic, and NetHack are subject to their own copyright and license terms.\"\n    echo \"https://drive.google.com/file/d/1Yl8Ei1toRCmoHbNVxOxamXrGS_s4xvf6/view\"\n    echo \"Press ENTER to continue, or Ctrl-C to cancel.\"\n    read\n\n    echo \"Downloading Busybox.\"\n    git clone git://busybox.net/busybox.git\n    echo \"Installing musl with pacman and creating symlinks. (will sudo)\"\n    sudo pacman -S musl\n    sudo ln -s /usr/bin/ar /usr/bin/musl-ar\n    sudo ln -s /usr/bin/strip /usr/bin/musl-strip\n    echo \"Now run 'deps.sh menuconfig' and load kados.config, located in the same directory as this script\"\nfi\n\n\nBUSYBOXDIR=$STARTDIR/extern/busybox\ncd busybox\nif [[ $@ =~ \"menuconfig\" ]];\nthen\n    make menuconfig\nfi\nif [[ $@ =~ \"defconfig\" ]];\nthen\n    make defconfig\nfi\n\nif [[ $@ =~ \"allnoconfig\" ]];\nthen\n    make allnoconfig\nfi\nif [[ $@ =~ \"clean\" ]];\nthen\n    make clean\nfi\nif [[ $@ =~ \"build\" ]];\nthen\n    time make -j6\n    make install\nfi\nif [[ $@ =~ \"kash\" ]];\nthen\n    cd $STARTDIR/userland/kash\n    cargo build\n    cd -\nfi\nif [[ $@ =~ \"makeimg\" ]];\nthen\n    cd $STARTDIR\n    mkdir -p initramfs/busybox_fs\n    cd $STARTDIR\n    cd initramfs/busybox_fs\n    rm -f ../initramfs_busybox\n    cp -r $BUSYBOXDIR/_install/* ./\n    cp -r $BUSYBOXDIR/busybox_unstripped ./bin/busybox\n    mkdir -p dev\n    find . | cpio -ov --format=newc > ../initramfs\nfi\n\n\ncd $STARTDIR\necho \"Done.\""
  },
  {
    "path": "kados.config",
    "content": "#\n# Automatically generated make config: don't edit\n# Busybox version: 1.37.0.git\n# Sun Mar 12 20:07:49 2023\n#\nCONFIG_HAVE_DOT_CONFIG=y\n\n#\n# Settings\n#\n# CONFIG_DESKTOP is not set\n# CONFIG_EXTRA_COMPAT is not set\n# CONFIG_FEDORA_COMPAT is not set\n# CONFIG_INCLUDE_SUSv2 is not set\n# CONFIG_LONG_OPTS is not set\n# CONFIG_SHOW_USAGE is not set\n# CONFIG_FEATURE_VERBOSE_USAGE is not set\n# CONFIG_FEATURE_COMPRESS_USAGE is not set\nCONFIG_LFS=y\n# CONFIG_PAM is not set\n# CONFIG_FEATURE_DEVPTS is not set\n# CONFIG_FEATURE_UTMP is not set\n# CONFIG_FEATURE_WTMP is not set\n# CONFIG_FEATURE_PIDFILE is not set\nCONFIG_PID_FILE_PATH=\"\"\n# CONFIG_BUSYBOX is not set\n# CONFIG_FEATURE_SHOW_SCRIPT is not set\n# CONFIG_FEATURE_INSTALLER is not set\nCONFIG_INSTALL_NO_USR=y\n# CONFIG_FEATURE_SUID is not set\n# CONFIG_FEATURE_SUID_CONFIG is not set\n# CONFIG_FEATURE_SUID_CONFIG_QUIET is not set\n# CONFIG_FEATURE_PREFER_APPLETS is not set\nCONFIG_BUSYBOX_EXEC_PATH=\"/proc/self/exe\"\n# CONFIG_SELINUX is not set\n# CONFIG_FEATURE_CLEAN_UP is not set\n# CONFIG_FEATURE_SYSLOG_INFO is not set\n# CONFIG_FEATURE_SYSLOG is not set\n\n#\n# Build Options\n#\nCONFIG_STATIC=y\n# CONFIG_PIE is not set\n# CONFIG_NOMMU is not set\n# CONFIG_BUILD_LIBBUSYBOX is not set\n# CONFIG_FEATURE_LIBBUSYBOX_STATIC is not set\n# CONFIG_FEATURE_INDIVIDUAL is not set\n# CONFIG_FEATURE_SHARED_BUSYBOX is not set\nCONFIG_CROSS_COMPILER_PREFIX=\"/usr/bin/musl-\"\nCONFIG_SYSROOT=\"\"\nCONFIG_EXTRA_CFLAGS=\"\"\nCONFIG_EXTRA_LDFLAGS=\"\"\nCONFIG_EXTRA_LDLIBS=\"\"\n# CONFIG_USE_PORTABLE_CODE is not set\n# CONFIG_STACK_OPTIMIZATION_386 is not set\nCONFIG_STATIC_LIBGCC=y\n\n#\n# Installation Options (\"make install\" behavior)\n#\nCONFIG_INSTALL_APPLET_SYMLINKS=y\n# CONFIG_INSTALL_APPLET_HARDLINKS is not set\n# CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set\n# CONFIG_INSTALL_APPLET_DONT is not set\n# CONFIG_INSTALL_SH_APPLET_SYMLINK is not set\n# CONFIG_INSTALL_SH_APPLET_HARDLINK is not set\n# CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set\nCONFIG_PREFIX=\"./_install\"\n\n#\n# Debugging Options\n#\nCONFIG_DEBUG=y\nCONFIG_DEBUG_PESSIMIZE=y\n# CONFIG_DEBUG_SANITIZE is not set\n# CONFIG_UNIT_TEST is not set\n# CONFIG_WERROR is not set\n# CONFIG_WARN_SIMPLE_MSG is not set\nCONFIG_NO_DEBUG_LIB=y\n# CONFIG_DMALLOC is not set\n# CONFIG_EFENCE is not set\n\n#\n# Library Tuning\n#\n# CONFIG_FEATURE_USE_BSS_TAIL is not set\n# CONFIG_FLOAT_DURATION is not set\n# CONFIG_FEATURE_RTMINMAX is not set\n# CONFIG_FEATURE_RTMINMAX_USE_LIBC_DEFINITIONS is not set\nCONFIG_FEATURE_BUFFERS_USE_MALLOC=y\n# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set\n# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set\nCONFIG_PASSWORD_MINLEN=6\nCONFIG_MD5_SMALL=1\nCONFIG_SHA1_SMALL=3\n# CONFIG_SHA1_HWACCEL is not set\n# CONFIG_SHA256_HWACCEL is not set\nCONFIG_SHA3_SMALL=1\n# CONFIG_FEATURE_NON_POSIX_CP is not set\n# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set\n# CONFIG_FEATURE_USE_SENDFILE is not set\nCONFIG_FEATURE_COPYBUF_KB=4\n# CONFIG_MONOTONIC_SYSCALL is not set\n# CONFIG_IOCTL_HEX2STR_ERROR is not set\n# CONFIG_FEATURE_EDITING is not set\nCONFIG_FEATURE_EDITING_MAX_LEN=0\n# CONFIG_FEATURE_EDITING_VI is not set\nCONFIG_FEATURE_EDITING_HISTORY=0\n# CONFIG_FEATURE_EDITING_SAVEHISTORY is not set\n# CONFIG_FEATURE_EDITING_SAVE_ON_EXIT is not set\n# CONFIG_FEATURE_REVERSE_SEARCH is not set\n# CONFIG_FEATURE_TAB_COMPLETION is not set\n# CONFIG_FEATURE_USERNAME_COMPLETION is not set\n# CONFIG_FEATURE_EDITING_FANCY_PROMPT is not set\n# CONFIG_FEATURE_EDITING_WINCH is not set\n# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set\n# CONFIG_LOCALE_SUPPORT is not set\n# CONFIG_UNICODE_SUPPORT is not set\n# CONFIG_UNICODE_USING_LOCALE is not set\n# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set\nCONFIG_SUBST_WCHAR=0\nCONFIG_LAST_SUPPORTED_WCHAR=0\n# CONFIG_UNICODE_COMBINING_WCHARS is not set\n# CONFIG_UNICODE_WIDE_WCHARS is not set\n# CONFIG_UNICODE_BIDI_SUPPORT is not set\n# CONFIG_UNICODE_NEUTRAL_TABLE is not set\n# CONFIG_UNICODE_PRESERVE_BROKEN is not set\n# CONFIG_LOOP_CONFIGURE is not set\n# CONFIG_NO_LOOP_CONFIGURE is not set\nCONFIG_TRY_LOOP_CONFIGURE=y\n\n#\n# Applets\n#\n\n#\n# Archival Utilities\n#\n# CONFIG_FEATURE_SEAMLESS_XZ is not set\n# CONFIG_FEATURE_SEAMLESS_LZMA is not set\n# CONFIG_FEATURE_SEAMLESS_BZ2 is not set\n# CONFIG_FEATURE_SEAMLESS_GZ is not set\n# CONFIG_FEATURE_SEAMLESS_Z is not set\n# CONFIG_AR is not set\n# CONFIG_FEATURE_AR_LONG_FILENAMES is not set\n# CONFIG_FEATURE_AR_CREATE is not set\n# CONFIG_UNCOMPRESS is not set\n# CONFIG_GUNZIP is not set\n# CONFIG_ZCAT is not set\n# CONFIG_FEATURE_GUNZIP_LONG_OPTIONS is not set\n# CONFIG_BUNZIP2 is not set\n# CONFIG_BZCAT is not set\n# CONFIG_UNLZMA is not set\n# CONFIG_LZCAT is not set\n# CONFIG_LZMA is not set\n# CONFIG_UNXZ is not set\n# CONFIG_XZCAT is not set\n# CONFIG_XZ is not set\n# CONFIG_BZIP2 is not set\nCONFIG_BZIP2_SMALL=0\n# CONFIG_FEATURE_BZIP2_DECOMPRESS is not set\n# CONFIG_CPIO is not set\n# CONFIG_FEATURE_CPIO_O is not set\n# CONFIG_FEATURE_CPIO_P is not set\n# CONFIG_FEATURE_CPIO_IGNORE_DEVNO is not set\n# CONFIG_FEATURE_CPIO_RENUMBER_INODES is not set\n# CONFIG_DPKG is not set\n# CONFIG_DPKG_DEB is not set\n# CONFIG_GZIP is not set\n# CONFIG_FEATURE_GZIP_LONG_OPTIONS is not set\nCONFIG_GZIP_FAST=0\n# CONFIG_FEATURE_GZIP_LEVELS is not set\n# CONFIG_FEATURE_GZIP_DECOMPRESS is not set\n# CONFIG_LZOP is not set\n# CONFIG_UNLZOP is not set\n# CONFIG_LZOPCAT is not set\n# CONFIG_LZOP_COMPR_HIGH is not set\n# CONFIG_RPM is not set\n# CONFIG_RPM2CPIO is not set\n# CONFIG_TAR is not set\n# CONFIG_FEATURE_TAR_LONG_OPTIONS is not set\n# CONFIG_FEATURE_TAR_CREATE is not set\n# CONFIG_FEATURE_TAR_AUTODETECT is not set\n# CONFIG_FEATURE_TAR_FROM is not set\n# CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY is not set\n# CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY is not set\n# CONFIG_FEATURE_TAR_GNU_EXTENSIONS is not set\n# CONFIG_FEATURE_TAR_TO_COMMAND is not set\n# CONFIG_FEATURE_TAR_UNAME_GNAME is not set\n# CONFIG_FEATURE_TAR_NOPRESERVE_TIME is not set\n# CONFIG_FEATURE_TAR_SELINUX is not set\n# CONFIG_UNZIP is not set\n# CONFIG_FEATURE_UNZIP_CDF is not set\n# CONFIG_FEATURE_UNZIP_BZIP2 is not set\n# CONFIG_FEATURE_UNZIP_LZMA is not set\n# CONFIG_FEATURE_UNZIP_XZ is not set\n# CONFIG_FEATURE_LZMA_FAST is not set\n\n#\n# Coreutils\n#\n# CONFIG_FEATURE_VERBOSE is not set\n\n#\n# Common options for date and touch\n#\n# CONFIG_FEATURE_TIMEZONE is not set\n\n#\n# Common options for cp and mv\n#\nCONFIG_FEATURE_PRESERVE_HARDLINKS=y\n\n#\n# Common options for df, du, ls\n#\nCONFIG_FEATURE_HUMAN_READABLE=y\n# CONFIG_BASENAME is not set\nCONFIG_CAT=y\nCONFIG_FEATURE_CATN=y\nCONFIG_FEATURE_CATV=y\n# CONFIG_CHGRP is not set\n# CONFIG_CHMOD is not set\n# CONFIG_CHOWN is not set\n# CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set\n# CONFIG_CHROOT is not set\n# CONFIG_CKSUM is not set\n# CONFIG_CRC32 is not set\n# CONFIG_COMM is not set\nCONFIG_CP=y\n# CONFIG_FEATURE_CP_LONG_OPTIONS is not set\n# CONFIG_FEATURE_CP_REFLINK is not set\n# CONFIG_CUT is not set\n# CONFIG_FEATURE_CUT_REGEX is not set\n# CONFIG_DATE is not set\n# CONFIG_FEATURE_DATE_ISOFMT is not set\n# CONFIG_FEATURE_DATE_NANO is not set\n# CONFIG_FEATURE_DATE_COMPAT is not set\n# CONFIG_DD is not set\n# CONFIG_FEATURE_DD_SIGNAL_HANDLING is not set\n# CONFIG_FEATURE_DD_THIRD_STATUS_LINE is not set\n# CONFIG_FEATURE_DD_IBS_OBS is not set\n# CONFIG_FEATURE_DD_STATUS is not set\n# CONFIG_DF is not set\n# CONFIG_FEATURE_DF_FANCY is not set\n# CONFIG_FEATURE_SKIP_ROOTFS is not set\n# CONFIG_DIRNAME is not set\n# CONFIG_DOS2UNIX is not set\n# CONFIG_UNIX2DOS is not set\n# CONFIG_DU is not set\n# CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K is not set\nCONFIG_ECHO=y\nCONFIG_FEATURE_FANCY_ECHO=y\n# CONFIG_ENV is not set\n# CONFIG_EXPAND is not set\n# CONFIG_UNEXPAND is not set\n# CONFIG_EXPR is not set\n# CONFIG_EXPR_MATH_SUPPORT_64 is not set\n# CONFIG_FACTOR is not set\n# CONFIG_FALSE is not set\n# CONFIG_FOLD is not set\n# CONFIG_HEAD is not set\n# CONFIG_FEATURE_FANCY_HEAD is not set\n# CONFIG_HOSTID is not set\n# CONFIG_ID is not set\n# CONFIG_GROUPS is not set\n# CONFIG_INSTALL is not set\n# CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set\n# CONFIG_LINK is not set\nCONFIG_LN=y\n# CONFIG_LOGNAME is not set\nCONFIG_LS=y\nCONFIG_FEATURE_LS_FILETYPES=y\nCONFIG_FEATURE_LS_FOLLOWLINKS=y\nCONFIG_FEATURE_LS_RECURSIVE=y\nCONFIG_FEATURE_LS_WIDTH=y\nCONFIG_FEATURE_LS_SORTFILES=y\n# CONFIG_FEATURE_LS_TIMESTAMPS is not set\n# CONFIG_FEATURE_LS_USERNAME is not set\n# CONFIG_FEATURE_LS_COLOR is not set\n# CONFIG_FEATURE_LS_COLOR_IS_DEFAULT is not set\n# CONFIG_MD5SUM is not set\n# CONFIG_SHA1SUM is not set\n# CONFIG_SHA256SUM is not set\n# CONFIG_SHA512SUM is not set\n# CONFIG_SHA3SUM is not set\n# CONFIG_FEATURE_MD5_SHA1_SUM_CHECK is not set\nCONFIG_MKDIR=y\n# CONFIG_MKFIFO is not set\n# CONFIG_MKNOD is not set\n# CONFIG_MKTEMP is not set\nCONFIG_MV=y\n# CONFIG_NICE is not set\n# CONFIG_NL is not set\n# CONFIG_NOHUP is not set\n# CONFIG_NPROC is not set\n# CONFIG_OD is not set\n# CONFIG_PASTE is not set\n# CONFIG_PRINTENV is not set\n# CONFIG_PRINTF is not set\nCONFIG_PWD=y\n# CONFIG_READLINK is not set\n# CONFIG_FEATURE_READLINK_FOLLOW is not set\n# CONFIG_REALPATH is not set\nCONFIG_RM=y\nCONFIG_RMDIR=y\n# CONFIG_SEQ is not set\n# CONFIG_SHRED is not set\n# CONFIG_SHUF is not set\n# CONFIG_SLEEP is not set\n# CONFIG_FEATURE_FANCY_SLEEP is not set\n# CONFIG_SORT is not set\n# CONFIG_FEATURE_SORT_BIG is not set\n# CONFIG_FEATURE_SORT_OPTIMIZE_MEMORY is not set\n# CONFIG_SPLIT is not set\n# CONFIG_FEATURE_SPLIT_FANCY is not set\nCONFIG_STAT=y\n# CONFIG_FEATURE_STAT_FORMAT is not set\n# CONFIG_FEATURE_STAT_FILESYSTEM is not set\n# CONFIG_STTY is not set\n# CONFIG_SUM is not set\n# CONFIG_SYNC is not set\n# CONFIG_FEATURE_SYNC_FANCY is not set\n# CONFIG_FSYNC is not set\n# CONFIG_TAC is not set\n# CONFIG_TAIL is not set\n# CONFIG_FEATURE_FANCY_TAIL is not set\n# CONFIG_TEE is not set\n# CONFIG_FEATURE_TEE_USE_BLOCK_IO is not set\n# CONFIG_TEST is not set\n# CONFIG_TEST1 is not set\n# CONFIG_TEST2 is not set\n# CONFIG_FEATURE_TEST_64 is not set\n# CONFIG_TIMEOUT is not set\n# CONFIG_TOUCH is not set\n# CONFIG_FEATURE_TOUCH_SUSV3 is not set\n# CONFIG_TR is not set\n# CONFIG_FEATURE_TR_CLASSES is not set\n# CONFIG_FEATURE_TR_EQUIV is not set\n# CONFIG_TRUE is not set\n# CONFIG_TRUNCATE is not set\n# CONFIG_TSORT is not set\n# CONFIG_TTY is not set\n# CONFIG_UNAME is not set\nCONFIG_UNAME_OSNAME=\"\"\n# CONFIG_BB_ARCH is not set\n# CONFIG_UNIQ is not set\n# CONFIG_UNLINK is not set\n# CONFIG_USLEEP is not set\n# CONFIG_UUDECODE is not set\n# CONFIG_BASE32 is not set\n# CONFIG_BASE64 is not set\n# CONFIG_UUENCODE is not set\n# CONFIG_WC is not set\n# CONFIG_FEATURE_WC_LARGE is not set\n# CONFIG_WHO is not set\n# CONFIG_W is not set\n# CONFIG_USERS is not set\n# CONFIG_WHOAMI is not set\n# CONFIG_YES is not set\n\n#\n# Console Utilities\n#\n# CONFIG_CHVT is not set\n# CONFIG_CLEAR is not set\n# CONFIG_DEALLOCVT is not set\n# CONFIG_DUMPKMAP is not set\n# CONFIG_FGCONSOLE is not set\n# CONFIG_KBD_MODE is not set\n# CONFIG_LOADFONT is not set\n# CONFIG_SETFONT is not set\n# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set\nCONFIG_DEFAULT_SETFONT_DIR=\"\"\n# CONFIG_FEATURE_LOADFONT_PSF2 is not set\n# CONFIG_FEATURE_LOADFONT_RAW is not set\n# CONFIG_LOADKMAP is not set\n# CONFIG_OPENVT is not set\n# CONFIG_RESET is not set\n# CONFIG_RESIZE is not set\n# CONFIG_FEATURE_RESIZE_PRINT is not set\n# CONFIG_SETCONSOLE is not set\n# CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set\n# CONFIG_SETKEYCODES is not set\n# CONFIG_SETLOGCONS is not set\n# CONFIG_SHOWKEY is not set\n\n#\n# Debian Utilities\n#\n# CONFIG_PIPE_PROGRESS is not set\n# CONFIG_RUN_PARTS is not set\n# CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set\n# CONFIG_FEATURE_RUN_PARTS_FANCY is not set\n# CONFIG_START_STOP_DAEMON is not set\n# CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set\n# CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set\n# CONFIG_WHICH is not set\n\n#\n# klibc-utils\n#\n# CONFIG_MINIPS is not set\n# CONFIG_NUKE is not set\n# CONFIG_RESUME is not set\n# CONFIG_RUN_INIT is not set\n\n#\n# Editors\n#\n# CONFIG_AWK is not set\n# CONFIG_FEATURE_AWK_LIBM is not set\n# CONFIG_FEATURE_AWK_GNU_EXTENSIONS is not set\n# CONFIG_CMP is not set\n# CONFIG_DIFF is not set\n# CONFIG_FEATURE_DIFF_LONG_OPTIONS is not set\n# CONFIG_FEATURE_DIFF_DIR is not set\n# CONFIG_ED is not set\n# CONFIG_PATCH is not set\n# CONFIG_SED is not set\n# CONFIG_VI is not set\nCONFIG_FEATURE_VI_MAX_LEN=0\n# CONFIG_FEATURE_VI_8BIT is not set\n# CONFIG_FEATURE_VI_COLON is not set\n# CONFIG_FEATURE_VI_COLON_EXPAND is not set\n# CONFIG_FEATURE_VI_YANKMARK is not set\n# CONFIG_FEATURE_VI_SEARCH is not set\n# CONFIG_FEATURE_VI_REGEX_SEARCH is not set\n# CONFIG_FEATURE_VI_USE_SIGNALS is not set\n# CONFIG_FEATURE_VI_DOT_CMD is not set\n# CONFIG_FEATURE_VI_READONLY is not set\n# CONFIG_FEATURE_VI_SETOPTS is not set\n# CONFIG_FEATURE_VI_SET is not set\n# CONFIG_FEATURE_VI_WIN_RESIZE is not set\n# CONFIG_FEATURE_VI_ASK_TERMINAL is not set\n# CONFIG_FEATURE_VI_UNDO is not set\n# CONFIG_FEATURE_VI_UNDO_QUEUE is not set\nCONFIG_FEATURE_VI_UNDO_QUEUE_MAX=0\n# CONFIG_FEATURE_VI_VERBOSE_STATUS is not set\n# CONFIG_FEATURE_ALLOW_EXEC is not set\n\n#\n# Finding Utilities\n#\n# CONFIG_FIND is not set\n# CONFIG_FEATURE_FIND_PRINT0 is not set\n# CONFIG_FEATURE_FIND_MTIME is not set\n# CONFIG_FEATURE_FIND_ATIME is not set\n# CONFIG_FEATURE_FIND_CTIME is not set\n# CONFIG_FEATURE_FIND_MMIN is not set\n# CONFIG_FEATURE_FIND_AMIN is not set\n# CONFIG_FEATURE_FIND_CMIN is not set\n# CONFIG_FEATURE_FIND_PERM is not set\n# CONFIG_FEATURE_FIND_TYPE is not set\n# CONFIG_FEATURE_FIND_EXECUTABLE is not set\n# CONFIG_FEATURE_FIND_XDEV is not set\n# CONFIG_FEATURE_FIND_MAXDEPTH is not set\n# CONFIG_FEATURE_FIND_NEWER is not set\n# CONFIG_FEATURE_FIND_INUM is not set\n# CONFIG_FEATURE_FIND_SAMEFILE is not set\n# CONFIG_FEATURE_FIND_EXEC is not set\n# CONFIG_FEATURE_FIND_EXEC_PLUS is not set\n# CONFIG_FEATURE_FIND_USER is not set\n# CONFIG_FEATURE_FIND_GROUP is not set\n# CONFIG_FEATURE_FIND_NOT is not set\n# CONFIG_FEATURE_FIND_DEPTH is not set\n# CONFIG_FEATURE_FIND_PAREN is not set\n# CONFIG_FEATURE_FIND_SIZE is not set\n# CONFIG_FEATURE_FIND_PRUNE is not set\n# CONFIG_FEATURE_FIND_QUIT is not set\n# CONFIG_FEATURE_FIND_DELETE is not set\n# CONFIG_FEATURE_FIND_EMPTY is not set\n# CONFIG_FEATURE_FIND_PATH is not set\n# CONFIG_FEATURE_FIND_REGEX is not set\n# CONFIG_FEATURE_FIND_CONTEXT is not set\n# CONFIG_FEATURE_FIND_LINKS is not set\n# CONFIG_GREP is not set\n# CONFIG_EGREP is not set\n# CONFIG_FGREP is not set\n# CONFIG_FEATURE_GREP_CONTEXT is not set\n# CONFIG_XARGS is not set\n# CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION is not set\n# CONFIG_FEATURE_XARGS_SUPPORT_QUOTES is not set\n# CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT is not set\n# CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM is not set\n# CONFIG_FEATURE_XARGS_SUPPORT_REPL_STR is not set\n# CONFIG_FEATURE_XARGS_SUPPORT_PARALLEL is not set\n# CONFIG_FEATURE_XARGS_SUPPORT_ARGS_FILE is not set\n\n#\n# Init Utilities\n#\n# CONFIG_BOOTCHARTD is not set\n# CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set\n# CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set\n# CONFIG_HALT is not set\n# CONFIG_POWEROFF is not set\n# CONFIG_REBOOT is not set\n# CONFIG_FEATURE_WAIT_FOR_INIT is not set\n# CONFIG_FEATURE_CALL_TELINIT is not set\nCONFIG_TELINIT_PATH=\"\"\n# CONFIG_INIT is not set\n# CONFIG_LINUXRC is not set\n# CONFIG_FEATURE_USE_INITTAB is not set\n# CONFIG_FEATURE_KILL_REMOVED is not set\nCONFIG_FEATURE_KILL_DELAY=0\n# CONFIG_FEATURE_INIT_SCTTY is not set\n# CONFIG_FEATURE_INIT_SYSLOG is not set\n# CONFIG_FEATURE_INIT_QUIET is not set\n# CONFIG_FEATURE_INIT_COREDUMPS is not set\nCONFIG_INIT_TERMINAL_TYPE=\"\"\n# CONFIG_FEATURE_INIT_MODIFY_CMDLINE is not set\n\n#\n# Login/Password Management Utilities\n#\n# CONFIG_FEATURE_SHADOWPASSWDS is not set\n# CONFIG_USE_BB_PWD_GRP is not set\n# CONFIG_USE_BB_SHADOW is not set\n# CONFIG_USE_BB_CRYPT is not set\n# CONFIG_USE_BB_CRYPT_SHA is not set\n# CONFIG_ADD_SHELL is not set\n# CONFIG_REMOVE_SHELL is not set\n# CONFIG_ADDGROUP is not set\n# CONFIG_FEATURE_ADDUSER_TO_GROUP is not set\n# CONFIG_ADDUSER is not set\n# CONFIG_FEATURE_CHECK_NAMES is not set\nCONFIG_LAST_ID=0\nCONFIG_FIRST_SYSTEM_ID=0\nCONFIG_LAST_SYSTEM_ID=0\n# CONFIG_CHPASSWD is not set\nCONFIG_FEATURE_DEFAULT_PASSWD_ALGO=\"\"\n# CONFIG_CRYPTPW is not set\n# CONFIG_MKPASSWD is not set\n# CONFIG_DELUSER is not set\n# CONFIG_DELGROUP is not set\n# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set\n# CONFIG_GETTY is not set\n# CONFIG_LOGIN is not set\n# CONFIG_LOGIN_SESSION_AS_CHILD is not set\n# CONFIG_LOGIN_SCRIPTS is not set\n# CONFIG_FEATURE_NOLOGIN is not set\n# CONFIG_FEATURE_SECURETTY is not set\n# CONFIG_PASSWD is not set\n# CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set\n# CONFIG_SU is not set\n# CONFIG_FEATURE_SU_SYSLOG is not set\n# CONFIG_FEATURE_SU_CHECKS_SHELLS is not set\n# CONFIG_FEATURE_SU_BLANK_PW_NEEDS_SECURE_TTY is not set\n# CONFIG_SULOGIN is not set\n# CONFIG_VLOCK is not set\n\n#\n# Linux Ext2 FS Progs\n#\n# CONFIG_CHATTR is not set\n# CONFIG_FSCK is not set\n# CONFIG_LSATTR is not set\n# CONFIG_TUNE2FS is not set\n\n#\n# Linux Module Utilities\n#\n# CONFIG_MODPROBE_SMALL is not set\n# CONFIG_DEPMOD is not set\n# CONFIG_INSMOD is not set\n# CONFIG_LSMOD is not set\n# CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set\n# CONFIG_MODINFO is not set\n# CONFIG_MODPROBE is not set\n# CONFIG_FEATURE_MODPROBE_BLACKLIST is not set\n# CONFIG_RMMOD is not set\n\n#\n# Options common to multiple modutils\n#\n# CONFIG_FEATURE_CMDLINE_MODULE_OPTIONS is not set\n# CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set\n# CONFIG_FEATURE_2_4_MODULES is not set\n# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set\n# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set\n# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set\n# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set\n# CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set\n# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set\n# CONFIG_FEATURE_INSMOD_TRY_MMAP is not set\n# CONFIG_FEATURE_MODUTILS_ALIAS is not set\n# CONFIG_FEATURE_MODUTILS_SYMBOLS is not set\nCONFIG_DEFAULT_MODULES_DIR=\"\"\nCONFIG_DEFAULT_DEPMOD_FILE=\"\"\n\n#\n# Linux System Utilities\n#\n# CONFIG_ACPID is not set\n# CONFIG_FEATURE_ACPID_COMPAT is not set\n# CONFIG_BLKDISCARD is not set\n# CONFIG_BLKID is not set\n# CONFIG_FEATURE_BLKID_TYPE is not set\n# CONFIG_BLOCKDEV is not set\n# CONFIG_CAL is not set\n# CONFIG_CHRT is not set\n# CONFIG_DMESG is not set\n# CONFIG_FEATURE_DMESG_PRETTY is not set\n# CONFIG_EJECT is not set\n# CONFIG_FEATURE_EJECT_SCSI is not set\n# CONFIG_FALLOCATE is not set\n# CONFIG_FATATTR is not set\n# CONFIG_FBSET is not set\n# CONFIG_FEATURE_FBSET_FANCY is not set\n# CONFIG_FEATURE_FBSET_READMODE is not set\n# CONFIG_FDFORMAT is not set\n# CONFIG_FDISK is not set\n# CONFIG_FDISK_SUPPORT_LARGE_DISKS is not set\n# CONFIG_FEATURE_FDISK_WRITABLE is not set\n# CONFIG_FEATURE_AIX_LABEL is not set\n# CONFIG_FEATURE_SGI_LABEL is not set\n# CONFIG_FEATURE_SUN_LABEL is not set\n# CONFIG_FEATURE_OSF_LABEL is not set\n# CONFIG_FEATURE_GPT_LABEL is not set\n# CONFIG_FEATURE_FDISK_ADVANCED is not set\n# CONFIG_FINDFS is not set\n# CONFIG_FLOCK is not set\n# CONFIG_FDFLUSH is not set\n# CONFIG_FREERAMDISK is not set\n# CONFIG_FSCK_MINIX is not set\n# CONFIG_FSFREEZE is not set\n# CONFIG_FSTRIM is not set\n# CONFIG_GETOPT is not set\n# CONFIG_FEATURE_GETOPT_LONG is not set\n# CONFIG_HEXDUMP is not set\n# CONFIG_HD is not set\n# CONFIG_XXD is not set\n# CONFIG_HWCLOCK is not set\n# CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set\n# CONFIG_IONICE is not set\n# CONFIG_IPCRM is not set\n# CONFIG_IPCS is not set\n# CONFIG_LAST is not set\n# CONFIG_FEATURE_LAST_FANCY is not set\n# CONFIG_LOSETUP is not set\n# CONFIG_LSPCI is not set\n# CONFIG_LSUSB is not set\n# CONFIG_MDEV is not set\n# CONFIG_FEATURE_MDEV_CONF is not set\n# CONFIG_FEATURE_MDEV_RENAME is not set\n# CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set\n# CONFIG_FEATURE_MDEV_EXEC is not set\n# CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set\n# CONFIG_FEATURE_MDEV_DAEMON is not set\n# CONFIG_MESG is not set\n# CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP is not set\n# CONFIG_MKE2FS is not set\n# CONFIG_MKFS_EXT2 is not set\n# CONFIG_MKFS_MINIX is not set\n# CONFIG_FEATURE_MINIX2 is not set\n# CONFIG_MKFS_REISER is not set\n# CONFIG_MKDOSFS is not set\n# CONFIG_MKFS_VFAT is not set\n# CONFIG_MKSWAP is not set\n# CONFIG_FEATURE_MKSWAP_UUID is not set\n# CONFIG_MORE is not set\n# CONFIG_MOUNT is not set\n# CONFIG_FEATURE_MOUNT_FAKE is not set\n# CONFIG_FEATURE_MOUNT_VERBOSE is not set\n# CONFIG_FEATURE_MOUNT_HELPERS is not set\n# CONFIG_FEATURE_MOUNT_LABEL is not set\n# CONFIG_FEATURE_MOUNT_NFS is not set\n# CONFIG_FEATURE_MOUNT_CIFS is not set\n# CONFIG_FEATURE_MOUNT_FLAGS is not set\n# CONFIG_FEATURE_MOUNT_FSTAB is not set\n# CONFIG_FEATURE_MOUNT_OTHERTAB is not set\n# CONFIG_MOUNTPOINT is not set\nCONFIG_NOLOGIN=y\n# CONFIG_NOLOGIN_DEPENDENCIES is not set\n# CONFIG_NSENTER is not set\n# CONFIG_PIVOT_ROOT is not set\n# CONFIG_RDATE is not set\n# CONFIG_RDEV is not set\n# CONFIG_READPROFILE is not set\n# CONFIG_RENICE is not set\n# CONFIG_REV is not set\n# CONFIG_RTCWAKE is not set\n# CONFIG_SCRIPT is not set\n# CONFIG_SCRIPTREPLAY is not set\n# CONFIG_SETARCH is not set\n# CONFIG_LINUX32 is not set\n# CONFIG_LINUX64 is not set\n# CONFIG_SETPRIV is not set\n# CONFIG_FEATURE_SETPRIV_DUMP is not set\n# CONFIG_FEATURE_SETPRIV_CAPABILITIES is not set\n# CONFIG_FEATURE_SETPRIV_CAPABILITY_NAMES is not set\n# CONFIG_SETSID is not set\n# CONFIG_SWAPON is not set\n# CONFIG_FEATURE_SWAPON_DISCARD is not set\n# CONFIG_FEATURE_SWAPON_PRI is not set\n# CONFIG_SWAPOFF is not set\n# CONFIG_FEATURE_SWAPONOFF_LABEL is not set\n# CONFIG_SWITCH_ROOT is not set\n# CONFIG_TASKSET is not set\n# CONFIG_FEATURE_TASKSET_FANCY is not set\n# CONFIG_FEATURE_TASKSET_CPULIST is not set\n# CONFIG_UEVENT is not set\n# CONFIG_UMOUNT is not set\n# CONFIG_FEATURE_UMOUNT_ALL is not set\n# CONFIG_UNSHARE is not set\n# CONFIG_WALL is not set\n# CONFIG_FEATURE_MOUNT_LOOP is not set\n# CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set\n# CONFIG_FEATURE_MTAB_SUPPORT is not set\n# CONFIG_VOLUMEID is not set\n# CONFIG_FEATURE_VOLUMEID_BCACHE is not set\n# CONFIG_FEATURE_VOLUMEID_BTRFS is not set\n# CONFIG_FEATURE_VOLUMEID_CRAMFS is not set\n# CONFIG_FEATURE_VOLUMEID_EROFS is not set\n# CONFIG_FEATURE_VOLUMEID_EXFAT is not set\n# CONFIG_FEATURE_VOLUMEID_EXT is not set\n# CONFIG_FEATURE_VOLUMEID_F2FS is not set\n# CONFIG_FEATURE_VOLUMEID_FAT is not set\n# CONFIG_FEATURE_VOLUMEID_HFS is not set\n# CONFIG_FEATURE_VOLUMEID_ISO9660 is not set\n# CONFIG_FEATURE_VOLUMEID_JFS is not set\n# CONFIG_FEATURE_VOLUMEID_LFS is not set\n# CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set\n# CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set\n# CONFIG_FEATURE_VOLUMEID_LUKS is not set\n# CONFIG_FEATURE_VOLUMEID_MINIX is not set\n# CONFIG_FEATURE_VOLUMEID_NILFS is not set\n# CONFIG_FEATURE_VOLUMEID_NTFS is not set\n# CONFIG_FEATURE_VOLUMEID_OCFS2 is not set\n# CONFIG_FEATURE_VOLUMEID_REISERFS is not set\n# CONFIG_FEATURE_VOLUMEID_ROMFS is not set\n# CONFIG_FEATURE_VOLUMEID_SQUASHFS is not set\n# CONFIG_FEATURE_VOLUMEID_SYSV is not set\n# CONFIG_FEATURE_VOLUMEID_UBIFS is not set\n# CONFIG_FEATURE_VOLUMEID_UDF is not set\n# CONFIG_FEATURE_VOLUMEID_XFS is not set\n\n#\n# Miscellaneous Utilities\n#\n# CONFIG_ADJTIMEX is not set\n# CONFIG_ASCII is not set\n# CONFIG_BBCONFIG is not set\n# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set\n# CONFIG_BC is not set\n# CONFIG_DC is not set\n# CONFIG_FEATURE_DC_BIG is not set\n# CONFIG_FEATURE_DC_LIBM is not set\n# CONFIG_FEATURE_BC_INTERACTIVE is not set\n# CONFIG_FEATURE_BC_LONG_OPTIONS is not set\n# CONFIG_BEEP is not set\nCONFIG_FEATURE_BEEP_FREQ=0\nCONFIG_FEATURE_BEEP_LENGTH_MS=0\n# CONFIG_CHAT is not set\n# CONFIG_FEATURE_CHAT_NOFAIL is not set\n# CONFIG_FEATURE_CHAT_TTY_HIFI is not set\n# CONFIG_FEATURE_CHAT_IMPLICIT_CR is not set\n# CONFIG_FEATURE_CHAT_SWALLOW_OPTS is not set\n# CONFIG_FEATURE_CHAT_SEND_ESCAPES is not set\n# CONFIG_FEATURE_CHAT_VAR_ABORT_LEN is not set\n# CONFIG_FEATURE_CHAT_CLR_ABORT is not set\n# CONFIG_CONSPY is not set\n# CONFIG_CROND is not set\n# CONFIG_FEATURE_CROND_D is not set\n# CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set\n# CONFIG_FEATURE_CROND_SPECIAL_TIMES is not set\nCONFIG_FEATURE_CROND_DIR=\"\"\n# CONFIG_CRONTAB is not set\n# CONFIG_DEVFSD is not set\n# CONFIG_DEVFSD_MODLOAD is not set\n# CONFIG_DEVFSD_FG_NP is not set\n# CONFIG_DEVFSD_VERBOSE is not set\n# CONFIG_FEATURE_DEVFS is not set\n# CONFIG_DEVMEM is not set\n# CONFIG_FBSPLASH is not set\n# CONFIG_FLASH_ERASEALL is not set\n# CONFIG_FLASH_LOCK is not set\n# CONFIG_FLASH_UNLOCK is not set\n# CONFIG_FLASHCP is not set\n# CONFIG_HDPARM is not set\n# CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set\n# CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set\n# CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set\n# CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET is not set\n# CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF is not set\n# CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA is not set\n# CONFIG_HEXEDIT is not set\n# CONFIG_I2CGET is not set\n# CONFIG_I2CSET is not set\n# CONFIG_I2CDUMP is not set\n# CONFIG_I2CDETECT is not set\n# CONFIG_I2CTRANSFER is not set\n# CONFIG_INOTIFYD is not set\n# CONFIG_LESS is not set\nCONFIG_FEATURE_LESS_MAXLINES=0\n# CONFIG_FEATURE_LESS_BRACKETS is not set\n# CONFIG_FEATURE_LESS_FLAGS is not set\n# CONFIG_FEATURE_LESS_TRUNCATE is not set\n# CONFIG_FEATURE_LESS_MARKS is not set\n# CONFIG_FEATURE_LESS_REGEXP is not set\n# CONFIG_FEATURE_LESS_WINCH is not set\n# CONFIG_FEATURE_LESS_ASK_TERMINAL is not set\n# CONFIG_FEATURE_LESS_DASHCMD is not set\n# CONFIG_FEATURE_LESS_LINENUMS is not set\n# CONFIG_FEATURE_LESS_RAW is not set\n# CONFIG_FEATURE_LESS_ENV is not set\n# CONFIG_LSSCSI is not set\n# CONFIG_MAKEDEVS is not set\n# CONFIG_FEATURE_MAKEDEVS_LEAF is not set\n# CONFIG_FEATURE_MAKEDEVS_TABLE is not set\n# CONFIG_MAN is not set\n# CONFIG_MICROCOM is not set\nCONFIG_MIM=y\n# CONFIG_MT is not set\n# CONFIG_NANDWRITE is not set\n# CONFIG_NANDDUMP is not set\n# CONFIG_PARTPROBE is not set\n# CONFIG_RAIDAUTORUN is not set\nCONFIG_READAHEAD=y\n# CONFIG_RFKILL is not set\n# CONFIG_RUNLEVEL is not set\n# CONFIG_RX is not set\n# CONFIG_SEEDRNG is not set\n# CONFIG_SETFATTR is not set\n# CONFIG_SETSERIAL is not set\n# CONFIG_STRINGS is not set\n# CONFIG_TIME is not set\n# CONFIG_TREE is not set\n# CONFIG_TS is not set\n# CONFIG_TTYSIZE is not set\n# CONFIG_UBIATTACH is not set\n# CONFIG_UBIDETACH is not set\n# CONFIG_UBIMKVOL is not set\n# CONFIG_UBIRMVOL is not set\n# CONFIG_UBIRSVOL is not set\n# CONFIG_UBIUPDATEVOL is not set\n# CONFIG_UBIRENAME is not set\n# CONFIG_VOLNAME is not set\n# CONFIG_WATCHDOG is not set\n# CONFIG_FEATURE_WATCHDOG_OPEN_TWICE is not set\n\n#\n# Networking Utilities\n#\n# CONFIG_FEATURE_IPV6 is not set\n# CONFIG_FEATURE_UNIX_LOCAL is not set\n# CONFIG_FEATURE_PREFER_IPV4_ADDRESS is not set\n# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set\n# CONFIG_FEATURE_ETC_NETWORKS is not set\n# CONFIG_FEATURE_ETC_SERVICES is not set\n# CONFIG_FEATURE_HWIB is not set\n# CONFIG_FEATURE_TLS_SHA1 is not set\n# CONFIG_ARP is not set\n# CONFIG_ARPING is not set\n# CONFIG_BRCTL is not set\n# CONFIG_FEATURE_BRCTL_FANCY is not set\n# CONFIG_FEATURE_BRCTL_SHOW is not set\n# CONFIG_DNSD is not set\n# CONFIG_ETHER_WAKE is not set\n# CONFIG_FTPD is not set\n# CONFIG_FEATURE_FTPD_WRITE is not set\n# CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST is not set\n# CONFIG_FEATURE_FTPD_AUTHENTICATION is not set\n# CONFIG_FTPGET is not set\n# CONFIG_FTPPUT is not set\n# CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS is not set\n# CONFIG_HOSTNAME is not set\n# CONFIG_DNSDOMAINNAME is not set\n# CONFIG_HTTPD is not set\nCONFIG_FEATURE_HTTPD_PORT_DEFAULT=0\n# CONFIG_FEATURE_HTTPD_RANGES is not set\n# CONFIG_FEATURE_HTTPD_SETUID is not set\n# CONFIG_FEATURE_HTTPD_BASIC_AUTH is not set\n# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set\n# CONFIG_FEATURE_HTTPD_CGI is not set\n# CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set\n# CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set\n# CONFIG_FEATURE_HTTPD_ENCODE_URL_STR is not set\n# CONFIG_FEATURE_HTTPD_ERROR_PAGES is not set\n# CONFIG_FEATURE_HTTPD_PROXY is not set\n# CONFIG_FEATURE_HTTPD_GZIP is not set\n# CONFIG_FEATURE_HTTPD_ETAG is not set\n# CONFIG_FEATURE_HTTPD_LAST_MODIFIED is not set\n# CONFIG_FEATURE_HTTPD_DATE is not set\n# CONFIG_FEATURE_HTTPD_ACL_IP is not set\n# CONFIG_IFCONFIG is not set\n# CONFIG_FEATURE_IFCONFIG_STATUS is not set\n# CONFIG_FEATURE_IFCONFIG_SLIP is not set\n# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set\n# CONFIG_FEATURE_IFCONFIG_HW is not set\n# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set\n# CONFIG_IFENSLAVE is not set\n# CONFIG_IFPLUGD is not set\n# CONFIG_IFUP is not set\n# CONFIG_IFDOWN is not set\nCONFIG_IFUPDOWN_IFSTATE_PATH=\"\"\n# CONFIG_FEATURE_IFUPDOWN_IP is not set\n# CONFIG_FEATURE_IFUPDOWN_IPV4 is not set\n# CONFIG_FEATURE_IFUPDOWN_IPV6 is not set\n# CONFIG_FEATURE_IFUPDOWN_MAPPING is not set\n# CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set\n# CONFIG_INETD is not set\n# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set\n# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set\n# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set\n# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set\n# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set\n# CONFIG_FEATURE_INETD_RPC is not set\n# CONFIG_IP is not set\n# CONFIG_IPADDR is not set\n# CONFIG_IPLINK is not set\n# CONFIG_IPROUTE is not set\n# CONFIG_IPTUNNEL is not set\n# CONFIG_IPRULE is not set\n# CONFIG_IPNEIGH is not set\n# CONFIG_FEATURE_IP_ADDRESS is not set\n# CONFIG_FEATURE_IP_LINK is not set\n# CONFIG_FEATURE_IP_ROUTE is not set\nCONFIG_FEATURE_IP_ROUTE_DIR=\"\"\n# CONFIG_FEATURE_IP_TUNNEL is not set\n# CONFIG_FEATURE_IP_RULE is not set\n# CONFIG_FEATURE_IP_NEIGH is not set\n# CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set\n# CONFIG_IPCALC is not set\n# CONFIG_FEATURE_IPCALC_LONG_OPTIONS is not set\n# CONFIG_FEATURE_IPCALC_FANCY is not set\n# CONFIG_FAKEIDENTD is not set\n# CONFIG_NAMEIF is not set\n# CONFIG_FEATURE_NAMEIF_EXTENDED is not set\n# CONFIG_NBDCLIENT is not set\n# CONFIG_NC is not set\n# CONFIG_NETCAT is not set\n# CONFIG_NC_SERVER is not set\n# CONFIG_NC_EXTRA is not set\n# CONFIG_NC_110_COMPAT is not set\n# CONFIG_NETSTAT is not set\n# CONFIG_FEATURE_NETSTAT_WIDE is not set\n# CONFIG_FEATURE_NETSTAT_PRG is not set\n# CONFIG_NSLOOKUP is not set\n# CONFIG_FEATURE_NSLOOKUP_BIG is not set\n# CONFIG_FEATURE_NSLOOKUP_LONG_OPTIONS is not set\n# CONFIG_NTPD is not set\n# CONFIG_FEATURE_NTPD_SERVER is not set\n# CONFIG_FEATURE_NTPD_CONF is not set\n# CONFIG_FEATURE_NTP_AUTH is not set\n# CONFIG_PING is not set\n# CONFIG_PING6 is not set\n# CONFIG_FEATURE_FANCY_PING is not set\n# CONFIG_PSCAN is not set\n# CONFIG_ROUTE is not set\n# CONFIG_SLATTACH is not set\n# CONFIG_SSL_CLIENT is not set\n# CONFIG_TC is not set\n# CONFIG_FEATURE_TC_INGRESS is not set\n# CONFIG_TCPSVD is not set\n# CONFIG_UDPSVD is not set\n# CONFIG_TELNET is not set\n# CONFIG_FEATURE_TELNET_TTYPE is not set\n# CONFIG_FEATURE_TELNET_AUTOLOGIN is not set\n# CONFIG_FEATURE_TELNET_WIDTH is not set\n# CONFIG_TELNETD is not set\n# CONFIG_FEATURE_TELNETD_STANDALONE is not set\nCONFIG_FEATURE_TELNETD_PORT_DEFAULT=0\n# CONFIG_FEATURE_TELNETD_INETD_WAIT is not set\n# CONFIG_TFTP is not set\n# CONFIG_FEATURE_TFTP_PROGRESS_BAR is not set\n# CONFIG_FEATURE_TFTP_HPA_COMPAT is not set\n# CONFIG_TFTPD is not set\n# CONFIG_FEATURE_TFTP_GET is not set\n# CONFIG_FEATURE_TFTP_PUT is not set\n# CONFIG_FEATURE_TFTP_BLOCKSIZE is not set\n# CONFIG_TFTP_DEBUG is not set\n# CONFIG_TLS is not set\n# CONFIG_TRACEROUTE is not set\n# CONFIG_TRACEROUTE6 is not set\n# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set\n# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set\n# CONFIG_TUNCTL is not set\n# CONFIG_FEATURE_TUNCTL_UG is not set\n# CONFIG_VCONFIG is not set\n# CONFIG_WGET is not set\n# CONFIG_FEATURE_WGET_LONG_OPTIONS is not set\n# CONFIG_FEATURE_WGET_STATUSBAR is not set\n# CONFIG_FEATURE_WGET_FTP is not set\n# CONFIG_FEATURE_WGET_AUTHENTICATION is not set\n# CONFIG_FEATURE_WGET_TIMEOUT is not set\n# CONFIG_FEATURE_WGET_HTTPS is not set\n# CONFIG_FEATURE_WGET_OPENSSL is not set\n# CONFIG_WHOIS is not set\n# CONFIG_ZCIP is not set\n# CONFIG_UDHCPD is not set\n# CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set\n# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set\nCONFIG_DHCPD_LEASES_FILE=\"\"\n# CONFIG_DUMPLEASES is not set\n# CONFIG_DHCPRELAY is not set\n# CONFIG_UDHCPC is not set\n# CONFIG_FEATURE_UDHCPC_ARPING is not set\n# CONFIG_FEATURE_UDHCPC_SANITIZEOPT is not set\nCONFIG_UDHCPC_DEFAULT_SCRIPT=\"\"\nCONFIG_UDHCPC6_DEFAULT_SCRIPT=\"\"\n# CONFIG_UDHCPC6 is not set\n# CONFIG_FEATURE_UDHCPC6_RFC3646 is not set\n# CONFIG_FEATURE_UDHCPC6_RFC4704 is not set\n# CONFIG_FEATURE_UDHCPC6_RFC4833 is not set\n# CONFIG_FEATURE_UDHCPC6_RFC5970 is not set\nCONFIG_UDHCPC_DEFAULT_INTERFACE=\"\"\n# CONFIG_FEATURE_UDHCP_PORT is not set\nCONFIG_UDHCP_DEBUG=0\nCONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0\n# CONFIG_FEATURE_UDHCP_RFC3397 is not set\n# CONFIG_FEATURE_UDHCP_8021Q is not set\nCONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS=\"\"\n\n#\n# Print Utilities\n#\n# CONFIG_LPD is not set\n# CONFIG_LPR is not set\n# CONFIG_LPQ is not set\n\n#\n# Mail Utilities\n#\nCONFIG_FEATURE_MIME_CHARSET=\"\"\n# CONFIG_MAKEMIME is not set\n# CONFIG_POPMAILDIR is not set\n# CONFIG_FEATURE_POPMAILDIR_DELIVERY is not set\n# CONFIG_REFORMIME is not set\n# CONFIG_FEATURE_REFORMIME_COMPAT is not set\n# CONFIG_SENDMAIL is not set\n\n#\n# Process Utilities\n#\n# CONFIG_FEATURE_FAST_TOP is not set\n# CONFIG_FEATURE_SHOW_THREADS is not set\n# CONFIG_FREE is not set\n# CONFIG_FUSER is not set\n# CONFIG_IOSTAT is not set\n# CONFIG_KILL is not set\n# CONFIG_KILLALL is not set\n# CONFIG_KILLALL5 is not set\n# CONFIG_LSOF is not set\n# CONFIG_MPSTAT is not set\n# CONFIG_NMETER is not set\n# CONFIG_PGREP is not set\n# CONFIG_PKILL is not set\n# CONFIG_PIDOF is not set\n# CONFIG_FEATURE_PIDOF_SINGLE is not set\n# CONFIG_FEATURE_PIDOF_OMIT is not set\n# CONFIG_PMAP is not set\n# CONFIG_POWERTOP is not set\n# CONFIG_FEATURE_POWERTOP_INTERACTIVE is not set\n# CONFIG_PS is not set\n# CONFIG_FEATURE_PS_WIDE is not set\n# CONFIG_FEATURE_PS_LONG is not set\n# CONFIG_FEATURE_PS_TIME is not set\n# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set\n# CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set\n# CONFIG_PSTREE is not set\n# CONFIG_PWDX is not set\n# CONFIG_SMEMCAP is not set\n# CONFIG_BB_SYSCTL is not set\n# CONFIG_TOP is not set\n# CONFIG_FEATURE_TOP_INTERACTIVE is not set\n# CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE is not set\n# CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS is not set\n# CONFIG_FEATURE_TOP_SMP_CPU is not set\n# CONFIG_FEATURE_TOP_DECIMALS is not set\n# CONFIG_FEATURE_TOP_SMP_PROCESS is not set\n# CONFIG_FEATURE_TOPMEM is not set\n# CONFIG_UPTIME is not set\n# CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set\n# CONFIG_WATCH is not set\n\n#\n# Runit Utilities\n#\n# CONFIG_CHPST is not set\n# CONFIG_SETUIDGID is not set\n# CONFIG_ENVUIDGID is not set\n# CONFIG_ENVDIR is not set\n# CONFIG_SOFTLIMIT is not set\n# CONFIG_RUNSV is not set\n# CONFIG_RUNSVDIR is not set\n# CONFIG_FEATURE_RUNSVDIR_LOG is not set\n# CONFIG_SV is not set\nCONFIG_SV_DEFAULT_SERVICE_DIR=\"\"\n# CONFIG_SVC is not set\n# CONFIG_SVOK is not set\n# CONFIG_SVLOGD is not set\n# CONFIG_CHCON is not set\n# CONFIG_GETENFORCE is not set\n# CONFIG_GETSEBOOL is not set\n# CONFIG_LOAD_POLICY is not set\n# CONFIG_MATCHPATHCON is not set\n# CONFIG_RUNCON is not set\n# CONFIG_SELINUXENABLED is not set\n# CONFIG_SESTATUS is not set\n# CONFIG_SETENFORCE is not set\n# CONFIG_SETFILES is not set\n# CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set\n# CONFIG_RESTORECON is not set\n# CONFIG_SETSEBOOL is not set\n\n#\n# Shells\n#\nCONFIG_SH_IS_ASH=y\n# CONFIG_SH_IS_HUSH is not set\n# CONFIG_SH_IS_NONE is not set\n# CONFIG_BASH_IS_ASH is not set\n# CONFIG_BASH_IS_HUSH is not set\nCONFIG_BASH_IS_NONE=y\nCONFIG_SHELL_ASH=y\nCONFIG_ASH=y\n# CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set\nCONFIG_ASH_INTERNAL_GLOB=y\n# CONFIG_ASH_BASH_COMPAT is not set\n# CONFIG_ASH_BASH_SOURCE_CURDIR is not set\n# CONFIG_ASH_BASH_NOT_FOUND_HOOK is not set\n# CONFIG_ASH_JOB_CONTROL is not set\n# CONFIG_ASH_ALIAS is not set\n# CONFIG_ASH_RANDOM_SUPPORT is not set\n# CONFIG_ASH_EXPAND_PRMT is not set\n# CONFIG_ASH_IDLE_TIMEOUT is not set\n# CONFIG_ASH_MAIL is not set\nCONFIG_ASH_ECHO=y\n# CONFIG_ASH_PRINTF is not set\n# CONFIG_ASH_TEST is not set\n# CONFIG_ASH_SLEEP is not set\n# CONFIG_ASH_HELP is not set\n# CONFIG_ASH_GETOPTS is not set\n# CONFIG_ASH_CMDCMD is not set\n# CONFIG_CTTYHACK is not set\n# CONFIG_HUSH is not set\n# CONFIG_SHELL_HUSH is not set\n# CONFIG_HUSH_BASH_COMPAT is not set\n# CONFIG_HUSH_BRACE_EXPANSION is not set\n# CONFIG_HUSH_BASH_SOURCE_CURDIR is not set\n# CONFIG_HUSH_LINENO_VAR is not set\n# CONFIG_HUSH_INTERACTIVE is not set\n# CONFIG_HUSH_SAVEHISTORY is not set\n# CONFIG_HUSH_JOB is not set\n# CONFIG_HUSH_TICK is not set\n# CONFIG_HUSH_IF is not set\n# CONFIG_HUSH_LOOPS is not set\n# CONFIG_HUSH_CASE is not set\n# CONFIG_HUSH_FUNCTIONS is not set\n# CONFIG_HUSH_LOCAL is not set\n# CONFIG_HUSH_RANDOM_SUPPORT is not set\n# CONFIG_HUSH_MODE_X is not set\n# CONFIG_HUSH_ECHO is not set\n# CONFIG_HUSH_PRINTF is not set\n# CONFIG_HUSH_TEST is not set\n# CONFIG_HUSH_HELP is not set\n# CONFIG_HUSH_EXPORT is not set\n# CONFIG_HUSH_EXPORT_N is not set\n# CONFIG_HUSH_READONLY is not set\n# CONFIG_HUSH_KILL is not set\n# CONFIG_HUSH_WAIT is not set\n# CONFIG_HUSH_COMMAND is not set\n# CONFIG_HUSH_TRAP is not set\n# CONFIG_HUSH_TYPE is not set\n# CONFIG_HUSH_TIMES is not set\n# CONFIG_HUSH_READ is not set\n# CONFIG_HUSH_SET is not set\n# CONFIG_HUSH_UNSET is not set\n# CONFIG_HUSH_ULIMIT is not set\n# CONFIG_HUSH_UMASK is not set\n# CONFIG_HUSH_GETOPTS is not set\n# CONFIG_HUSH_MEMLEAK is not set\n\n#\n# Options common to all shells\n#\n# CONFIG_FEATURE_SH_MATH is not set\n# CONFIG_FEATURE_SH_MATH_64 is not set\n# CONFIG_FEATURE_SH_MATH_BASE is not set\n# CONFIG_FEATURE_SH_EXTRA_QUIET is not set\n# CONFIG_FEATURE_SH_STANDALONE is not set\nCONFIG_FEATURE_SH_NOFORK=y\n# CONFIG_FEATURE_SH_READ_FRAC is not set\n# CONFIG_FEATURE_SH_HISTFILESIZE is not set\nCONFIG_FEATURE_SH_EMBEDDED_SCRIPTS=y\n\n#\n# System Logging Utilities\n#\n# CONFIG_KLOGD is not set\n# CONFIG_FEATURE_KLOGD_KLOGCTL is not set\n# CONFIG_LOGGER is not set\n# CONFIG_LOGREAD is not set\n# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set\n# CONFIG_SYSLOGD is not set\n# CONFIG_FEATURE_ROTATE_LOGFILE is not set\n# CONFIG_FEATURE_REMOTE_LOG is not set\n# CONFIG_FEATURE_SYSLOGD_DUP is not set\n# CONFIG_FEATURE_SYSLOGD_CFG is not set\n# CONFIG_FEATURE_SYSLOGD_PRECISE_TIMESTAMPS is not set\nCONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0\n# CONFIG_FEATURE_IPC_SYSLOG is not set\nCONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0\n# CONFIG_FEATURE_KMSG_SYSLOG is not set\n"
  },
  {
    "path": "rust-toolchain.toml",
    "content": "[toolchain]\nchannel = \"nightly\"\ntargets = [\"x86_64-unknown-none\"]\ncomponents = [\"rust-src\"]\n"
  },
  {
    "path": "src/arch/mod.rs",
    "content": "pub mod x86_64;\n\npub use self::x86_64::*;\n"
  },
  {
    "path": "src/arch/x86_64/cpu_local.rs",
    "content": "use x86::msr::{rdmsr, IA32_GS_BASE};\nuse x86_64::structures::{gdt::GlobalDescriptorTable, tss::TaskStateSegment};\n\npub struct CpuLocalData {\n    pub kernel_sp: usize,\n    pub gdt: GlobalDescriptorTable,\n}\n\n#[repr(C, packed)]\npub struct Kpcr {\n    pub tss: TaskStateSegment,\n    pub cpu_local: &'static mut CpuLocalData,\n    pub user_rsp0_tmp: usize,\n}\n\npub fn get_kpcr() -> &'static mut Kpcr {\n    unsafe { &mut *(rdmsr(IA32_GS_BASE) as *mut _) }\n}\n\npub fn get_tss() -> &'static mut TaskStateSegment {\n    unsafe { &mut *(rdmsr(IA32_GS_BASE) as *mut _) }\n}\n"
  },
  {
    "path": "src/arch/x86_64/gdt.rs",
    "content": "use core::alloc::Layout;\n\nuse alloc::alloc::alloc_zeroed;\n\nuse lazy_static::lazy_static;\nuse x86::msr::{wrmsr, IA32_GS_BASE};\nuse x86_64::{\n    instructions::tables::load_tss,\n    registers::segmentation::{Segment, CS, DS, ES, FS, GS, SS},\n    structures::{\n        gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector},\n        tss::TaskStateSegment,\n    },\n};\n\nuse crate::mem::consts::KERNEL_STACK_SIZE;\n\nuse super::cpu_local::{get_kpcr, get_tss, CpuLocalData, Kpcr};\n\npub const KERNEL_CS_IDX: u16 = 1;\npub const KERNEL_DS_IDX: u16 = 2;\npub const TSS_IDX: u16 = 3;\npub const USER_DS_IDX: u16 = 5;\npub const USER_CS_IDX: u16 = 6;\n\nstatic mut STACK: [u8; KERNEL_STACK_SIZE] = [0; KERNEL_STACK_SIZE];\n\nlazy_static! {\n    static ref BOOT_GDT: (GlobalDescriptorTable, [SegmentSelector; 2]) = {\n        let mut gdt = GlobalDescriptorTable::new();\n        let kernel_code_sel = gdt.append(Descriptor::kernel_code_segment());\n        let kernel_data_sel = gdt.append(Descriptor::kernel_data_segment());\n        (gdt, [kernel_code_sel, kernel_data_sel])\n    };\n}\n\npub fn init_boot() {\n    unsafe {\n        BOOT_GDT.0.load();\n        CS::set_reg(BOOT_GDT.1[0]);\n        DS::set_reg(BOOT_GDT.1[1]);\n        ES::set_reg(BOOT_GDT.1[1]);\n        FS::set_reg(BOOT_GDT.1[1]);\n\n        GS::set_reg(BOOT_GDT.1[1]);\n\n        SS::set_reg(BOOT_GDT.1[1]);\n    }\n}\n\npub fn init() {\n    unsafe {\n        let kpcr_layout = Layout::new::<Kpcr>();\n        let kpcr_ptr = alloc_zeroed(kpcr_layout) as *mut Kpcr;\n        wrmsr(IA32_GS_BASE, kpcr_ptr as u64);\n\n        let tls_layout = Layout::new::<CpuLocalData>();\n        let tls_ptr = alloc_zeroed(tls_layout) as *mut CpuLocalData;\n        get_kpcr().cpu_local = &mut *tls_ptr;\n    }\n\n    let tss = get_tss();\n    *tss = TaskStateSegment::new();\n\n    tss.privilege_stack_table[0] = x86_64::VirtAddr::new(\n        unsafe {\n            #[allow(static_mut_refs)]\n            STACK.as_mut_ptr()\n        } as u64\n            + KERNEL_STACK_SIZE as u64,\n    );\n\n    let gdt = &mut get_kpcr().cpu_local.gdt;\n    *gdt = GlobalDescriptorTable::new();\n    // kernel code\n    let kernel_cs_sel = gdt.append(Descriptor::kernel_code_segment());\n    // kernel data\n    let kernel_ds_sel = gdt.append(Descriptor::kernel_data_segment());\n    // TSS\n    let tss_sel = gdt.append(Descriptor::tss_segment(tss));\n    // user data (syscall)\n    let _user_ds_sel = gdt.append(Descriptor::user_data_segment());\n    // user code\n    let _user_cs_sel = gdt.append(Descriptor::user_code_segment());\n\n    gdt.load();\n\n    unsafe {\n        CS::set_reg(kernel_cs_sel);\n        DS::set_reg(kernel_ds_sel);\n        ES::set_reg(kernel_ds_sel);\n        SS::set_reg(kernel_ds_sel);\n        // FS::set_reg(kernel_ds_sel);\n        // GS::set_reg(kernel_ds_sel);\n\n        load_tss(tss_sel);\n    }\n}\n"
  },
  {
    "path": "src/arch/x86_64/idt.rs",
    "content": "use lazy_static::lazy_static;\n\nuse pc_keyboard::{DecodedKey, HandleControl, Keyboard, ScancodeSet1, layouts::Us104Key};\nuse pic8259::ChainedPics;\nuse spin::Mutex;\n\nuse x86::{\n    io::outb,\n    msr::{IA32_FS_BASE, rdmsr},\n};\nuse x86_64::{\n    instructions::port::Port,\n    registers::control::Cr3,\n    structures::idt::{InterruptDescriptorTable, PageFaultErrorCode},\n};\n\nuse crate::{\n    backtrace,\n    fs::devfs::{input::KBD_DEVICE, tty::TTY},\n    mem::addr::VirtAddr,\n    task::get_scheduler,\n    util::IrqMutex,\n};\n\npub const PIC_1_OFFSET: u8 = 32;\npub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8;\n\npub static PICS: Mutex<ChainedPics> =\n    Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) });\n\npub const TIMER_IRQ: u8 = PIC_1_OFFSET;\npub const KEYBOARD_IRQ: u8 = PIC_1_OFFSET + 1;\npub const COM2_IRQ: u8 = PIC_1_OFFSET + 3;\n\nlazy_static! {\n    pub static ref IDT: InterruptDescriptorTable = {\n        let mut idt = InterruptDescriptorTable::new();\n        #[allow(clippy::fn_to_numeric_cast)]\n        unsafe {\n            idt.divide_error.set_handler_addr(x86_64::VirtAddr::new(divide_error_handler as u64));\n            idt.debug.set_handler_addr(x86_64::VirtAddr::new(debug_handler as u64));\n            idt.non_maskable_interrupt.set_handler_addr(x86_64::VirtAddr::new(nmi_handler as u64));\n            idt.breakpoint.set_handler_addr(x86_64::VirtAddr::new(breakpoint_handler as u64));\n            idt.overflow.set_handler_addr(x86_64::VirtAddr::new(overflow_handler as u64));\n            idt.bound_range_exceeded\n            .set_handler_addr(x86_64::VirtAddr::new(bound_range_exceeded_handler as u64));\n            idt.invalid_opcode.set_handler_addr(x86_64::VirtAddr::new(invalid_opcode_handler as u64));\n            idt.device_not_available\n            .set_handler_addr(x86_64::VirtAddr::new(device_not_available_handler as u64));\n            idt.double_fault\n            .set_handler_addr(x86_64::VirtAddr::new(double_fault_handler as u64))\n                .set_stack_index(0);\n\n            // reserved: 0x09 coprocessor segment overrun exception\n            idt.invalid_tss.set_handler_addr(x86_64::VirtAddr::new(invalid_tss_handler as u64));\n            idt.segment_not_present\n            .set_handler_addr(x86_64::VirtAddr::new(segment_not_present_handler as u64));\n            idt.stack_segment_fault\n            .set_handler_addr(x86_64::VirtAddr::new(stack_segment_fault_handler as u64));\n            idt.general_protection_fault\n            .set_handler_addr(x86_64::VirtAddr::new(general_protection_fault_handler as u64));\n            idt.page_fault.set_handler_addr(x86_64::VirtAddr::new(page_fault_handler as u64));\n            // reserved: 0x0F\n            idt.x87_floating_point\n            .set_handler_addr(x86_64::VirtAddr::new(x87_floating_point_handler as u64));\n            idt.alignment_check.set_handler_addr(x86_64::VirtAddr::new(alignment_check_handler as u64));\n            idt.machine_check.set_handler_addr(x86_64::VirtAddr::new(machine_check_handler as u64));\n            idt.simd_floating_point\n            .set_handler_addr(x86_64::VirtAddr::new(simd_floating_point_handler as u64));\n            idt.virtualization.set_handler_addr(x86_64::VirtAddr::new(virtualization_handler as u64));\n            // reserved: 0x15 - 0x1C\n            idt.vmm_communication_exception\n            .set_handler_addr(x86_64::VirtAddr::new(vmm_communication_exception_handler as u64));\n            idt.security_exception\n            .set_handler_addr(x86_64::VirtAddr::new(security_exception_handler as u64));\n\n            idt[TIMER_IRQ].set_handler_addr(x86_64::VirtAddr::new(timer_handler as u64));\n            idt[KEYBOARD_IRQ].set_handler_addr(x86_64::VirtAddr::new(keyboard_handler as u64));\n            idt[COM2_IRQ].set_handler_addr(x86_64::VirtAddr::new(com2_handler as u64));\n        }\n\n\n        idt\n    };\n}\n\n#[derive(Copy, Clone, Debug, Default)]\n#[repr(C, packed)]\npub struct InterruptFrame {\n    pub r15: usize,\n    pub r14: usize,\n    pub r13: usize,\n    pub r12: usize,\n    pub rbp: usize,\n    pub rbx: usize,\n\n    pub r11: usize,\n    pub r10: usize,\n    pub r9: usize,\n    pub r8: usize,\n    pub rsi: usize,\n    pub rdi: usize,\n    pub rdx: usize,\n    pub rcx: usize,\n    pub rax: usize,\n\n    pub rip: usize,\n    pub cs: usize,\n    pub rflags: usize,\n    pub rsp: usize,\n    pub ss: usize,\n}\n\nimpl InterruptFrame {\n    #[inline(always)]\n    pub fn is_user_mode(&self) -> bool {\n        self.cs & 0x3 != 0\n    }\n}\n\n#[derive(Copy, Clone, Debug, Default)]\n#[repr(C, packed)]\npub struct InterruptErrorFrame {\n    pub code: usize,\n\n    pub frame: InterruptFrame,\n}\n\npub fn notify_eoi(index: u8) {\n    unsafe { PICS.lock().notify_end_of_interrupt(index) }\n}\n\nmacro_rules! interrupt_handler {\n    ($name:ident, $num:literal, $push_error:expr) => {\n        #[unsafe(naked)]\n        unsafe extern \"C\" fn $name() {\n            {\n                core::arch::naked_asm!(concat!(\n                    $push_error,\n                    \"\n                    test qword ptr [rsp + 16], 0x3\n                    jz 2f\n                    swapgs\n                2:\n                    xchg [rsp], rax\n                    \", crate::push_regs!(),\"\n                    push rax\n\n                    mov rdi, {num}\n                    mov rsi, rsp\n\n                    call x64_handle_interrupt\n\n                    add rsp, 8\n                    \", crate::pop_regs!(),\"\n\n                    test qword ptr [rsp + 8], 0x3\n                    jz 3f\n                    swapgs\n                3:\n                    iretq\n                    \"\n                ),\n                num = const($num))\n            }\n        }\n    };\n}\n\nmacro_rules! has_error {\n    () => {\n        \"\"\n    };\n}\nmacro_rules! no_error {\n    () => {\n        \"push 0\"\n    };\n}\ninterrupt_handler!(divide_error_handler, 0x0, no_error!());\ninterrupt_handler!(debug_handler, 0x1, no_error!());\ninterrupt_handler!(nmi_handler, 0x2, no_error!());\ninterrupt_handler!(breakpoint_handler, 0x3, no_error!());\ninterrupt_handler!(overflow_handler, 0x4, no_error!());\ninterrupt_handler!(bound_range_exceeded_handler, 0x5, no_error!());\ninterrupt_handler!(invalid_opcode_handler, 0x6, no_error!());\ninterrupt_handler!(device_not_available_handler, 0x7, no_error!());\n\ninterrupt_handler!(double_fault_handler, 0x8, has_error!());\ninterrupt_handler!(invalid_tss_handler, 0xA, has_error!());\ninterrupt_handler!(segment_not_present_handler, 0xB, has_error!());\ninterrupt_handler!(stack_segment_fault_handler, 0xC, has_error!());\ninterrupt_handler!(general_protection_fault_handler, 0xD, has_error!());\ninterrupt_handler!(page_fault_handler, 0xE, has_error!());\n\ninterrupt_handler!(x87_floating_point_handler, 0x10, no_error!());\n\ninterrupt_handler!(alignment_check_handler, 0x11, has_error!());\n\ninterrupt_handler!(machine_check_handler, 0x12, no_error!());\ninterrupt_handler!(simd_floating_point_handler, 0x13, no_error!());\ninterrupt_handler!(virtualization_handler, 0x14, no_error!());\n\ninterrupt_handler!(vmm_communication_exception_handler, 0x1D, has_error!());\ninterrupt_handler!(security_exception_handler, 0x1E, has_error!());\n\ninterrupt_handler!(timer_handler, 32, no_error!());\ninterrupt_handler!(keyboard_handler, 33, no_error!());\ninterrupt_handler!(com2_handler, 35, no_error!());\n\nuse x86::irq::*;\n\n#[unsafe(no_mangle)]\nextern \"C\" fn x64_handle_interrupt(vector: u8, stack_frame: *mut InterruptErrorFrame) {\n    let stack_frame = unsafe { &mut *stack_frame };\n    let error_code = stack_frame.code;\n\n    match vector {\n        TIMER_IRQ => {\n            super::time::pit_irq();\n            let sched = get_scheduler();\n            notify_eoi(TIMER_IRQ);\n            sched.preempt();\n        }\n        KEYBOARD_IRQ => {\n            do_keyboard_input();\n            notify_eoi(KEYBOARD_IRQ);\n        }\n        COM2_IRQ => {\n            notify_eoi(COM2_IRQ);\n        }\n        DIVIDE_ERROR_VECTOR => {\n            log::error!(\"\\nEXCEPTION: DIVIDE ERROR\\n{:#x?}\", stack_frame);\n            panic!(\"Divide error\");\n        }\n        DEBUG_VECTOR => {\n            log::error!(\"\\nEXCEPTION: DEBUG EXCEPTION\\n{:#x?}\", stack_frame);\n        }\n        NONMASKABLE_INTERRUPT_VECTOR => {\n            log::error!(\"\\nEXCEPTION: NON-MASKABLE INTERRUPT\\n{:#x?}\", stack_frame);\n            panic!(\"Non-maskable interrupt\");\n        }\n        OVERFLOW_VECTOR => {\n            log::error!(\"\\nEXCEPTION: OVERFLOW\\n{:#x?}\", stack_frame);\n            panic!(\"Overflow\");\n        }\n        BOUND_RANGE_EXCEEDED_VECTOR => {\n            log::error!(\"\\nEXCEPTION: BOUND RANGE EXCEEDED\\n{:#x?}\", stack_frame);\n            panic!(\"Bound range exceeded\");\n        }\n        INVALID_OPCODE_VECTOR => {\n            log::error!(\"\\nEXCEPTION: INVALID OPCODE\\n{:#x?}\", stack_frame);\n            panic!(\"Invalid opcode\");\n        }\n        DEVICE_NOT_AVAILABLE_VECTOR => {\n            log::error!(\"\\nEXCEPTION: DEVICE NOT AVAILABLE\\n{:#x?}\", stack_frame);\n            panic!(\"Device not available\");\n        }\n        DOUBLE_FAULT_VECTOR => {\n            log::error!(\n                \"\\nEXCEPTION: DOUBLE FAULT\\n{:#x?}\\nError code: {:#b}\\ncr3: {:#x}\",\n                stack_frame,\n                error_code,\n                Cr3::read_raw().0.start_address().as_u64()\n            );\n            panic!(\"Double fault\");\n        }\n        INVALID_TSS_VECTOR => {\n            log::error!(\n                \"\\nEXCEPTION: INVALID TSS\\n{:#x?}\\nError code: {:#b}\",\n                stack_frame,\n                error_code\n            );\n            panic!(\"Invalid TSS\");\n        }\n        SEGMENT_NOT_PRESENT_VECTOR => {\n            log::error!(\n                \"\\nEXCEPTION: SEGMENT NOT PRESENT\\n{:#x?}\\nError code: {:#b}\",\n                stack_frame,\n                error_code\n            );\n            panic!(\"Segment not present\");\n        }\n        STACK_SEGEMENT_FAULT_VECTOR => {\n            log::error!(\n                \"\\nEXCEPTION: STACK SEGMENT FAULT\\n{:#x?}\\nError code: {:#b}\",\n                stack_frame,\n                error_code\n            );\n            panic!(\"Stack segment fault\");\n        }\n        GENERAL_PROTECTION_FAULT_VECTOR => {\n            log::error!(\n                \"\\nEXCEPTION: GENERAL PROTECTION FAULT\\n{:#x?}\\nError code: {:#b}\",\n                stack_frame,\n                error_code\n            );\n            unsafe {\n                let fsbase = rdmsr(IA32_FS_BASE);\n                log::debug!(\"FSBASE: {:#x}\", fsbase);\n            }\n            if stack_frame.frame.is_user_mode() {\n                backtrace::unwind_user_stack_from(stack_frame.frame.rbp, stack_frame.frame.rip);\n                get_scheduler().exit_current(1);\n            }\n            panic!(\"General Protection Fault\");\n        }\n        PAGE_FAULT_VECTOR => {\n            let accessed_address = x86_64::registers::control::Cr2::read_raw();\n            let cr3 = x86_64::registers::control::Cr3::read_raw().0;\n            let error_code = PageFaultErrorCode::from_bits_truncate(error_code as u64);\n            let current = get_scheduler().current_task_opt();\n            if let Some(current) = current {\n                if let Err(e) = current.handle_page_fault(\n                    unsafe { VirtAddr::new_unchecked(accessed_address as usize) },\n                    *stack_frame,\n                    error_code,\n                ) {\n                    log::error!(\n                        \"\\nEXCEPTION: USER PAGE FAULT while accessing {:#x}\\n\\\n                        error code: {:?}\\ncr3: {:#x}\\n{:#x?}\",\n                        accessed_address,\n                        error_code,\n                        cr3.start_address().as_u64(),\n                        stack_frame,\n                    );\n                    let rip = stack_frame.frame.rip;\n                    log::error!(\"Exception IP {:#x}\", rip);\n                    log::error!(\"Faulted access address {:#x}\", accessed_address);\n                    log::error!(\"Error: {:?}\", e);\n                    panic!(\"User page fault\");\n                }\n            } else {\n                log::error!(\n                    \"\\nEXCEPTION: KERNEL PAGE FAULT while accessing {:#x}\\n\\\n                    error code: {:?}\\ncr3: {:#x}\\n{:#x?}\",\n                    accessed_address,\n                    error_code,\n                    cr3.start_address().as_u64(),\n                    stack_frame,\n                );\n                let rip = stack_frame.frame.rip;\n                log::error!(\"Exception IP {:#x}\", rip);\n                log::error!(\"Faulted access address {:#x}\", accessed_address,);\n                panic!(\"Kernel page fault\");\n            }\n        }\n        X87_FPU_VECTOR => {\n            log::error!(\"\\nEXCEPTION: x87 FLOATING POINT\\n{:#x?}\", stack_frame);\n            panic!(\"x87 floating point exception\");\n        }\n        ALIGNMENT_CHECK_VECTOR => {\n            log::error!(\n                \"\\nEXCEPTION: ALIGNMENT CHECK\\n{:#x?}\\nError code: {:#b}\",\n                stack_frame,\n                error_code\n            );\n            panic!(\"Alignment check\");\n        }\n        MACHINE_CHECK_VECTOR => {\n            log::error!(\"\\nEXCEPTION: MACHINE CHECK\\n{:#x?}\", stack_frame);\n            panic!()\n        }\n        SIMD_FLOATING_POINT_VECTOR => {\n            log::error!(\"\\nEXCEPTION: SIMD FLOATING POINT\\n{:#x?}\", stack_frame);\n            panic!(\"SIMD floating point exception\");\n        }\n        VIRTUALIZATION_VECTOR => {\n            log::error!(\"\\nEXCEPTION: VIRTUALIZATION\\n{:#x?}\", stack_frame);\n            panic!(\"Virtualization exception\");\n        }\n        0x1D => {\n            log::error!(\n                \"\\nEXCEPTION: VMM COMMUNICATION EXCEPTION\\n{:#x?}\\nError code: {:#b}\",\n                stack_frame,\n                error_code\n            );\n            panic!(\"VMM communication exception\");\n        }\n        0x1E => {\n            log::error!(\n                \"\\nEXCEPTION: SECURITY EXCEPTION\\n{:#x?}\\nError code: {:#b}\",\n                stack_frame,\n                error_code\n            );\n            panic!(\"Security exception\");\n        }\n        _ => log::warn!(\"Unhandled interrupt: {}\", vector),\n    }\n}\n\npub const PIC_1_DATA_PORT: u8 = 0x21;\npub const PIC_2_DATA_PORT: u8 = 0xa1;\n\npub fn mask_irq(irq: u8) {\n    let irq = irq - PIC_1_OFFSET;\n    let port = if irq < 8 {\n        PIC_1_DATA_PORT\n    } else {\n        PIC_2_DATA_PORT\n    };\n    let irq = if irq < 8 { irq } else { irq - 8 };\n    let val = unsafe { x86::io::inb(port as u16) } | (1 << irq);\n    unsafe { outb(port as u16, val) };\n}\n\npub fn unmask_irq(irq: u8) {\n    let irq = irq - PIC_1_OFFSET;\n    let port = if irq < 8 {\n        PIC_1_DATA_PORT\n    } else {\n        PIC_2_DATA_PORT\n    };\n    let irq = if irq < 8 { irq } else { irq - 8 };\n    let val = unsafe { x86::io::inb(port as u16) } & !(1 << irq);\n    unsafe { outb(port as u16, val) };\n}\n\npub fn init() {\n    IDT.load();\n    unsafe {\n        PICS.lock().initialize();\n    }\n\n    unmask_irq(TIMER_IRQ);\n    unmask_irq(KEYBOARD_IRQ);\n    unmask_irq(COM2_IRQ);\n}\n\nfn do_keyboard_input() {\n    lazy_static! {\n        static ref KEYBOARD: IrqMutex<Keyboard<Us104Key, ScancodeSet1>> = IrqMutex::new(\n            Keyboard::new(ScancodeSet1::new(), Us104Key, HandleControl::Ignore)\n        );\n    }\n\n    let mut port = Port::new(0x60);\n    let scancode: u8 = unsafe { port.read() };\n\n    let mut keyboard = KEYBOARD.lock();\n    if let Ok(Some(key_evt)) = keyboard.add_byte(scancode) {\n        if let Some(kbd) = KBD_DEVICE.get() {\n            kbd.handle_kbd_irq(&key_evt);\n        }\n        if let Some(key) = keyboard.process_keyevent(key_evt) {\n            match key {\n                DecodedKey::Unicode(c) => {\n                    TTY.get().unwrap().input_char(c as u8);\n                }\n                DecodedKey::RawKey(_code) => {}\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/arch/x86_64/mod.rs",
    "content": "use alloc::sync::Arc;\nuse limine::request::{\n    BootTimeRequest, FramebufferRequest, HhdmRequest, KernelFileRequest, MemoryMapRequest,\n    StackSizeRequest,\n};\n\nuse spin::Once;\nuse x86::{\n    controlregs::{self, Cr0, Cr4},\n    cpuid::{CpuId, FeatureInfo},\n};\nuse x86_64::instructions::interrupts;\n\nuse crate::{\n    backtrace,\n    fs::{\n        self,\n        initramfs::get_root,\n        opened_file::{OpenFlags, OpenedFile},\n        path::Path,\n    },\n    god_mode::GOD_MODE_FIFO,\n    graphics,\n    mem::{\n        self,\n        allocator::{KERNEL_FRAME_ALLOCATOR, KERNEL_PAGE_ALLOCATOR},\n        consts::KERNEL_STACK_SIZE,\n    },\n    serial::serial1_recv,\n    task::{current_task, get_scheduler, Task},\n};\n\npub mod cpu_local;\npub mod gdt;\npub mod idt;\npub mod syscall;\npub mod task;\npub mod time;\n\nstatic HHDM: HhdmRequest = HhdmRequest::new();\nstatic _STACK: StackSizeRequest = StackSizeRequest::new().with_size(KERNEL_STACK_SIZE as u64);\nstatic BOOT_TIME: BootTimeRequest = BootTimeRequest::new();\nstatic FB_REQUEST: FramebufferRequest = FramebufferRequest::new();\nstatic MEM_MAP: MemoryMapRequest = MemoryMapRequest::new();\nstatic KERNEL_FILE: KernelFileRequest = KernelFileRequest::new();\n\npub static CPUID_FEATURE_INFO: Once<FeatureInfo> = Once::new();\n\npub fn get_cpuid_feature_info() -> &'static FeatureInfo {\n    CPUID_FEATURE_INFO.call_once(|| {\n        CpuId::new()\n            .get_feature_info()\n            .expect(\"Error getting CPUID feature info\")\n    })\n}\n\npub fn arch_main() {\n    interrupts::disable();\n\n    let kernel_file = KERNEL_FILE\n        .get_response()\n        .expect(\"Error getting kernel binary from Limine\");\n    let kernel_file = kernel_file.file();\n    let kernel_file_base = kernel_file.addr();\n    let kernel_file_len = kernel_file.size() as usize;\n    let kernel_file_data =\n        unsafe { core::slice::from_raw_parts(kernel_file_base, kernel_file_len) };\n    backtrace::KERNEL_ELF.call_once(|| {\n        xmas_elf::ElfFile::new(kernel_file_data).expect(\"Error parsing kernel ELF file data\")\n    });\n\n    crate::PHYSICAL_OFFSET.call_once(|| {\n        HHDM.get_response()\n            .expect(\"Error getting HHDM response from Limine\")\n            .offset() as usize\n    });\n\n    let memmap = MEM_MAP\n        .get_response()\n        .expect(\"Error getting memory map response from Limine\")\n        .entries();\n\n    crate::logging::init();\n    log::info!(\"Logger initialized.\");\n\n    log::info!(\"Setting up time structures.\");\n    let boot_time = BOOT_TIME\n        .get_response()\n        .expect(\"Error getting boot time response from Limine\")\n        .boot_time();\n    time::init(boot_time.as_secs() as i64);\n\n    log::info!(\"Initializing FPU mechanisms.\");\n    let features = get_cpuid_feature_info();\n    assert!(features.has_fxsave_fxstor(), \"FXSAVE/FXRSTOR not available\");\n    assert!(features.has_mmx(), \"MMX not available\");\n    assert!(features.has_fpu(), \"FPU not available\");\n    assert!(features.has_sse(), \"SSE not available\");\n    unsafe {\n        // enable FXSAVE and FXRSTOR\n        controlregs::cr4_write(controlregs::cr4() | Cr4::CR4_ENABLE_SSE | Cr4::CR4_UNMASKED_SSE);\n        log::trace!(\"CR4_ENABLE_SSE and CR4_UNMASKED_SSE set.\");\n\n        // controlregs::xcr0_write(\n        //     controlregs::xcr0()\n        //         | Xcr0::XCR0_SSE_STATE\n        //         | Xcr0::XCR0_FPU_MMX_STATE\n        //         | Xcr0::XCR0_AVX_STATE,\n        // );\n        // log::trace!(\"XCR0_SSE_STATE, XCR0_FPU_MMX_STATE, and XCR0_AVX_STATE set.\");\n        controlregs::cr0_write(controlregs::cr0() & !Cr0::CR0_EMULATE_COPROCESSOR);\n        log::trace!(\"CR0_EMULATE_COPROCESSOR cleared.\");\n        controlregs::cr0_write(controlregs::cr0() | Cr0::CR0_MONITOR_COPROCESSOR);\n        log::trace!(\"CR0_MONITOR_COPROCESSOR set.\");\n    }\n\n    log::info!(\"Initializing boot GDT.\");\n    gdt::init_boot();\n\n    // let fb_tag = boot_info.framebuffer_tag().expect(\"No multiboot2 framebuffer tag found\");\n    let fb_resp = FB_REQUEST\n        .get_response()\n        .expect(\"Error getting framebuffer response from Limine\");\n    log::info!(\"Initializing kernel frame and page allocators.\");\n    mem::allocator::init(memmap).expect(\"Error initializing kernel frame and page allocators\");\n\n    log::info!(\"Remapping kernel to new page table.\");\n    let kernel_addr_space = mem::remap_kernel().expect(\"Error remapping kernel\");\n\n    log::info!(\"Setting up kernel heap.\");\n    let _heap_mp = kernel_addr_space\n        .lock()\n        .with_mapper(|mut mapper| mem::init_heap(&mut mapper).expect(\"Error setting up heap\"));\n\n    log::info!(\"Converting kernel frame and page allocators to use heap.\");\n    {\n        KERNEL_FRAME_ALLOCATOR\n            .get()\n            .expect(\"Error getting kernel frame allocator\")\n            .lock()\n            .convert_to_heap_allocated();\n        KERNEL_PAGE_ALLOCATOR\n            .get()\n            .expect(\"Error getting kernel page allocator\")\n            .lock()\n            .convert_to_heap_allocated();\n    }\n\n    log::info!(\"Initializing VGA graphics.\");\n\n    graphics::init(fb_resp).expect(\"Error initializing VGA graphics\");\n\n    log::info!(\"Setting up syscalls.\");\n    unsafe {\n        syscall::init();\n    }\n\n    log::info!(\"Loading GDT.\");\n    gdt::init();\n\n    log::info!(\"Loading IDT.\");\n    idt::init();\n\n    log::info!(\"Initializing filesystems.\");\n    fs::initramfs::init().expect(\"Error initializing initramfs\");\n\n    log::info!(\"Initializing task scheduler.\");\n    crate::task::init();\n\n    log::info!(\"Starting init process.\");\n\n    let sched = get_scheduler();\n\n    fs::devfs::init();\n\n    log::info!(\"Welcome to K4DOS!\");\n\n    {\n        let task = Task::new_kernel(sched, poll_serial1, true);\n        sched.push_runnable(task, true);\n    }\n\n    // god_mode::init();\n\n    loop {\n        interrupts::enable_and_hlt();\n    }\n}\n\npub fn startup_init() {\n    let exe = \"/bin/sh\";\n    let file = get_root()\n        .unwrap()\n        .lookup(Path::new(exe), true)\n        .unwrap()\n        .as_file()\n        .unwrap()\n        .clone();\n\n    let current = current_task();\n    let mut files = current.opened_files.lock();\n\n    let console = get_root()\n        .unwrap()\n        .lookup_path(Path::new(\"/dev/tty\"), true)\n        .unwrap();\n\n    // stdin\n    files\n        .open_with_fd(\n            0,\n            Arc::new(OpenedFile::new(console.clone(), OpenFlags::O_RDONLY, 0)),\n            OpenFlags::O_RDONLY | OpenFlags::O_CLOEXEC,\n        )\n        .unwrap();\n    // stdout\n    files\n        .open_with_fd(\n            1,\n            Arc::new(OpenedFile::new(console.clone(), OpenFlags::O_WRONLY, 0)),\n            OpenFlags::O_WRONLY | OpenFlags::O_CLOEXEC,\n        )\n        .unwrap();\n    // stderr\n    files\n        .open_with_fd(\n            2,\n            Arc::new(OpenedFile::new(console, OpenFlags::O_WRONLY, 0)),\n            OpenFlags::O_WRONLY | OpenFlags::O_CLOEXEC,\n        )\n        .unwrap();\n    drop(files);\n    current\n        .exec(file, &[exe.as_bytes()], &[b\"FOO=bar\"])\n        .unwrap();\n}\n\nfn poll_serial1() {\n    loop {\n        let c = serial1_recv();\n        if let Some(c) = c {\n            // TTY.get().unwrap().input_char(c);\n            loop {\n                if let Ok(mut lock) = GOD_MODE_FIFO.get().unwrap().try_lock() {\n                    lock.push_back(c);\n                    drop(lock);\n                    break;\n                }\n                // sched.preempt();\n                interrupts::enable_and_hlt();\n            }\n        }\n        interrupts::enable_and_hlt();\n    }\n}\n\npub fn idle() {\n    loop {\n        interrupts::enable_and_hlt();\n    }\n}\n"
  },
  {
    "path": "src/arch/x86_64/syscall.rs",
    "content": "use core::mem::offset_of;\n\nuse x86::msr::{rdmsr, wrmsr};\n\nuse crate::mem::kernel_addr_space_scope;\nuse crate::userland::syscall::{\n    QUIET_SYSCALLS, SyscallHandler, errno_to_isize, syscall_name_by_number,\n};\n\nuse super::gdt::{KERNEL_CS_IDX, USER_DS_IDX};\nuse super::idt::InterruptFrame;\n\n#[macro_export]\nmacro_rules! push_regs {\n    () => {\n        \"   \n        // push scratch regs\n        push rcx\n        push rdx\n        push rdi\n        push rsi\n        push r8\n        push r9\n        push r10\n        push r11\n\n        // push preserved regs\n        push rbx\n        push rbp\n        push r12\n        push r13\n        push r14\n        push r15\n        \"\n    };\n}\n\n#[macro_export]\nmacro_rules! pop_regs {\n    () => {\n        \"\n        // pop preserved regs\n        pop r15\n        pop r14\n        pop r13\n        pop r12\n        pop rbp\n        pop rbx\n\n        // pop scratch regs\n        pop r11\n        pop r10\n        pop r9\n        pop r8\n        pop rsi\n        pop rdi\n        pop rdx\n        pop rcx\n\n        pop rax\n        \"\n    };\n}\n\n#[unsafe(naked)]\npub unsafe extern \"C\" fn syscall_entry() {\n    use x86_64::structures::tss::TaskStateSegment;\n    {\n        core::arch::naked_asm!(\n            concat!(\n                \"\n        cli\n        swapgs\n        mov gs:[{off} + {sp}], rsp\n        mov rsp, gs:[{off} + {ksp}]\n        push qword ptr {ss_sel}\n        push qword ptr gs:[{off} + {sp}]\n        push r11\n        push qword ptr {cs_sel}\n        push rcx\n\n        push rax\n        \",\n                push_regs!(),\n                \"\n        mov rdi, rsp\n        cld\n        call x64_handle_syscall\n        cli\n        \",\n                pop_regs!(),\n                \"\n        test dword ptr [rsp + 4], 0xFFFF8000\n        jnz 2f\n\n        pop rcx\n        add rsp, 8\n        pop r11\n        pop qword ptr gs:[{off} + {sp}]\n        mov rsp, gs:[{off} + {sp}]\n        // pop rsp\n        cli\n        swapgs\n        sysretq\n    2:\n        xor rcx, rcx\n        xor r11, r11\n        cli\n        swapgs\n        iretq\n        \"\n            ),\n            off = const(0),\n            sp = const(offset_of!(crate::arch::cpu_local::Kpcr, user_rsp0_tmp)),\n            ksp = const(offset_of!(TaskStateSegment, privilege_stack_table)),\n            ss_sel = const((crate::arch::gdt::USER_DS_IDX << 3) | 3),\n            cs_sel = const((crate::arch::gdt::USER_CS_IDX << 3) | 3),\n        )\n    }\n}\n\n#[unsafe(no_mangle)]\nunsafe extern \"C\" fn x64_handle_syscall(ctx: *mut InterruptFrame) -> isize {\n    let context = unsafe { core::ptr::read(ctx) };\n    handle_syscall(\n        context.rdi,\n        context.rsi,\n        context.rdx,\n        context.r10,\n        context.r8,\n        context.r9,\n        context.rax,\n        ctx,\n    )\n}\n\n#[allow(clippy::too_many_arguments)]\nfn handle_syscall(\n    a1: usize,\n    a2: usize,\n    a3: usize,\n    a4: usize,\n    a5: usize,\n    a6: usize,\n    n: usize,\n    frame: *mut InterruptFrame,\n) -> isize {\n    let mut handler = SyscallHandler {\n        frame: unsafe { &mut *frame },\n    };\n\n    let guard = kernel_addr_space_scope().unwrap();\n    let res = handler.dispatch(a1, a2, a3, a4, a5, a6, n);\n    drop(guard);\n\n    if let Err(ref err) = res {\n        if !QUIET_SYSCALLS.contains(&n) {\n            log::error!(\n                \"Syscall handler for `{}` returned Err: {}\",\n                syscall_name_by_number(n),\n                err\n            );\n        }\n    }\n    let retval = errno_to_isize(&res);\n    handler.frame.rax = retval as usize;\n    retval\n}\n\n/// # Safety\n/// This writes several MSR registers.\npub unsafe fn init() {\n    let kernel_cs_offset = (KERNEL_CS_IDX as u64) << 3;\n    let user_ds_offset = (USER_DS_IDX as u64) << 3;\n    let mut star = 0u64;\n    star |= (user_ds_offset - 8) << 48;\n    star |= kernel_cs_offset << 32;\n    unsafe {\n        wrmsr(x86::msr::IA32_STAR, star);\n        wrmsr(x86::msr::IA32_LSTAR, syscall_entry as *const u8 as u64);\n        wrmsr(x86::msr::IA32_FMASK, 0x200);\n\n        wrmsr(x86::msr::IA32_CSTAR, 0);\n\n        wrmsr(x86::msr::IA32_EFER, rdmsr(x86::msr::IA32_EFER) | 1);\n    }\n}\n"
  },
  {
    "path": "src/arch/x86_64/task.rs",
    "content": "use core::{alloc::Layout, ptr::Unique, slice::SlicePattern};\n\nuse alloc::{alloc::alloc_zeroed, boxed::Box, vec::Vec};\nuse x86::{\n    cpuid::CpuId,\n    msr::{IA32_FS_BASE, IA32_GS_BASE, rdmsr, wrmsr},\n    tlb,\n};\nuse x86_64::instructions::interrupts;\n\nuse crate::{\n    fs::FileRef,\n    mem::{\n        addr::VirtAddr,\n        addr_space::AddressSpace,\n        allocator::alloc_kernel_frames,\n        consts::{KERNEL_STACK_SIZE, PAGE_SIZE, USER_STACK_BOTTOM, USER_STACK_TOP},\n    },\n    task::{\n        signal::Signal,\n        vmem::{MMapFlags, MMapKind, MMapProt, Vmem},\n    },\n    userland::elf::{self, AuxvType, SymTabEntry},\n    util::{KResult, stack::Stack},\n};\n\nuse super::{\n    cpu_local::get_tss,\n    gdt::{KERNEL_CS_IDX, KERNEL_DS_IDX, USER_DS_IDX},\n    idt::{InterruptErrorFrame, InterruptFrame},\n};\n\nfn fxsave(fpu: &mut Box<[u8]>) {\n    unsafe {\n        core::arch::asm!(\"fxsave [{}]\", in(reg) (**fpu).as_ptr(), in(\"rax\") u64::MAX, in(\"rdx\") u64::MAX)\n    }\n}\n\nfn fxrstor(fpu: &mut Box<[u8]>) {\n    unsafe {\n        core::arch::asm!(\"fxrstor [{}]\", in(reg) (**fpu).as_ptr(), in(\"rax\") u64::MAX, in(\"rdx\") u64::MAX)\n    }\n}\n\npub fn arch_context_switch(prev: &mut ArchTask, next: &mut ArchTask) {\n    unsafe {\n        // prev.fsbase = VirtAddr::new(rdmsr(IA32_FS_BASE) as usize);\n        // prev.gsbase = VirtAddr::new(rdmsr(IA32_GS_BASE) as usize);\n        wrmsr(IA32_FS_BASE, next.fsbase.value() as u64);\n        // swapgs();\n\n        wrmsr(IA32_GS_BASE, next.gsbase.value() as u64);\n        get_tss().privilege_stack_table[0] = x86_64::VirtAddr::new(\n            (next.kernel_stack.as_ptr() as usize + next.kernel_stack.len()) as u64,\n        );\n        // swapgs();\n\n        if let Some(fpu) = prev.fpu_storage.as_mut() {\n            fxsave(fpu);\n        }\n\n        if let Some(fpu) = next.fpu_storage.as_mut() {\n            fxrstor(fpu)\n        }\n\n        next.address_space.switch();\n        // interrupts::disable(); // why doesn't this work instead of the FIXME in fork()?\n        context_switch(&mut prev.context, next.context.as_ref())\n    }\n}\n\n#[unsafe(naked)]\nunsafe extern \"C\" fn iretq_init() -> ! {\n    {\n        core::arch::naked_asm!(\n            \"\n    cli\n    \n    add rsp, 8\n    \",\n            crate::pop_regs!(),\n            \"\n    \n    iretq\n    \",\n        )\n    }\n}\n\n#[unsafe(naked)]\nunsafe extern \"C\" fn fork_init() -> ! {\n    {\n        core::arch::naked_asm!(concat!(\n            \"\n        cli\n        \n        add rsp, 8\n        \",\n            crate::pop_regs!(),\n            \"\n\n        swapgs\n        iretq\n    \"\n        ),)\n    }\n}\n\n#[unsafe(naked)]\nunsafe extern \"C\" fn context_switch(_prev: &mut Unique<Context>, _next: &Context) {\n    {\n        core::arch::naked_asm!(\n            \"\n        pushfq\n        push rbp\n        push rbx\n        push r12\n        push r13\n        push r14\n        push r15\n\n        mov [rdi], rsp\n        mov rsp, rsi\n\n        pop r15\n        pop r14\n        pop r13\n        pop r12\n        pop rbx\n        pop rbp\n        popfq\n\n        ret\n\n    \",\n        )\n    }\n}\n\n#[derive(Clone, Debug, Default)]\n#[repr(C)]\npub struct Context {\n    r15: usize,\n    r14: usize,\n    r13: usize,\n    r12: usize,\n\n    rbx: usize,\n    rbp: usize,\n\n    rflags: usize,\n    rip: usize,\n}\n\n#[unsafe(naked)]\nunsafe extern \"C\" fn exec_entry(rcx: usize, rsp: usize, r11: usize) -> ! {\n    {\n        core::arch::naked_asm!(\n            \"\n            cli\n            swapgs\n\n            mov r11, rdx\n            mov rcx, rdi\n            mov rsp, rsi\n\n            mov r15, {user_ds}\n            mov ds, r15d\n            mov es, r15d\n            mov fs, r15d\n            mov gs, r15d\n\n            xor rax, rax\n            xor rbx, rbx\n            xor rdx, rdx\n            xor rsi, rsi\n            xor rbp, rbp\n            xor r8, r8\n            xor r9, r9\n            xor r10, r10\n            xor r12, r12\n            xor r13, r13\n            xor r14, r14\n            xor r15, r15\n\n            sysretq\n            \",\n            user_ds = const(((USER_DS_IDX as u64) << 3) | 3),\n        )\n    }\n}\n\n#[repr(C)]\npub struct ArchTask {\n    context: Unique<Context>,\n    kernel_stack: Box<[u8]>,\n    user: bool,\n    pub(crate) address_space: AddressSpace,\n    fsbase: VirtAddr,\n    gsbase: VirtAddr,\n    fpu_storage: Option<Box<[u8]>>,\n    pub symtab: Option<Vec<SymTabEntry>>,\n}\n\nimpl ArchTask {\n    pub fn new_idle() -> ArchTask {\n        ArchTask {\n            context: Unique::dangling(),\n            address_space: AddressSpace::current(),\n            kernel_stack: alloc::vec![0u8; KERNEL_STACK_SIZE].into_boxed_slice(),\n            user: false,\n            fsbase: VirtAddr::null(),\n            gsbase: VirtAddr::null(),\n            fpu_storage: None,\n            symtab: None,\n        }\n    }\n\n    pub fn new_kernel(entry_point: VirtAddr, enable_interrupts: bool) -> ArchTask {\n        let switch_stack = Self::alloc_switch_stack().unwrap();\n        let task_stack = unsafe {\n            alloc_zeroed(Layout::from_size_align_unchecked(\n                KERNEL_STACK_SIZE,\n                PAGE_SIZE,\n            ))\n            .add(KERNEL_STACK_SIZE)\n        };\n\n        let address_space = AddressSpace::current();\n\n        let mut stack_ptr = switch_stack.value();\n        let mut stack = Stack::new(&mut stack_ptr);\n\n        let kframe = unsafe { stack.offset::<InterruptErrorFrame>() };\n        *kframe = InterruptErrorFrame::default();\n        kframe.frame.ss = (KERNEL_DS_IDX as usize) << 3;\n        kframe.frame.cs = (KERNEL_CS_IDX as usize) << 3;\n        kframe.frame.rip = entry_point.value();\n        kframe.frame.rsp = task_stack as usize;\n        kframe.frame.rflags = if enable_interrupts { 0x200 } else { 0 };\n\n        let context = unsafe { stack.offset::<Context>() };\n        *context = Context::default();\n        context.rip = iretq_init as usize;\n        Self {\n            context: unsafe { Unique::new_unchecked(context) },\n            address_space,\n            kernel_stack: alloc::vec![0u8; KERNEL_STACK_SIZE].into_boxed_slice(),\n            user: false,\n            fsbase: VirtAddr::null(),\n            gsbase: unsafe { VirtAddr::new(rdmsr(IA32_GS_BASE) as usize) },\n            fpu_storage: None,\n            symtab: None,\n        }\n    }\n\n    pub fn exec(\n        &mut self,\n        vmem: &mut Vmem,\n        file: FileRef,\n        argv: &[&[u8]],\n        envp: &[&[u8]],\n    ) -> KResult<()> {\n        interrupts::disable();\n        let userland_entry = elf::load_elf(file)?;\n\n        self.kernel_stack = alloc::vec![0u8; KERNEL_STACK_SIZE].into_boxed_slice();\n        self.fsbase = userland_entry.fsbase.unwrap_or(VirtAddr::null());\n        self.gsbase = unsafe { VirtAddr::new_unchecked(rdmsr(IA32_GS_BASE) as usize) };\n\n        self.user = true;\n        self.address_space = userland_entry.addr_space;\n\n        *vmem = userland_entry.vmem;\n\n        self.address_space.switch();\n\n        // userland_entry\n        self.address_space.with_mapper(|mut mapper| {\n            vmem.map_area(\n                USER_STACK_BOTTOM,\n                USER_STACK_TOP,\n                MMapFlags::empty(),\n                MMapProt::PROT_READ | MMapProt::PROT_WRITE | MMapProt::PROT_EXEC,\n                MMapKind::Anonymous,\n                &mut mapper,\n            )\n        })?;\n\n        let stack_addr = USER_STACK_TOP - core::mem::size_of::<usize>();\n        let mut stack_addr = stack_addr.value();\n        let mut stack = Stack::new(&mut stack_addr);\n\n        fn push_strs(strs: &[&[u8]], stack: &mut Stack) -> Vec<usize> {\n            let mut tops = Vec::new();\n            for slice in strs.iter() {\n                unsafe {\n                    stack.push(0u8);\n                    stack.push_bytes(slice);\n                }\n                tops.push(stack.top());\n            }\n            tops\n        }\n\n        let envp_tops = push_strs(envp, &mut stack);\n        let argv_tops = push_strs(argv, &mut stack);\n\n        stack.align_down(16);\n\n        let size = envp.len() + 1 + argv.len() + 1 + 1;\n        if size % 2 == 1 {\n            unsafe {\n                stack.push(0u64);\n            }\n        }\n\n        unsafe {\n            stack.push(0usize);\n            stack.push(AuxvType::AtNull);\n            stack.push(userland_entry.hdr);\n\n            stack.push(0u64);\n            for envp_top in envp_tops.iter() {\n                stack.push(*envp_top);\n            }\n            stack.push(0u64);\n            for argv_top in argv_tops.iter() {\n                stack.push(*argv_top);\n            }\n            stack.push(argv_tops.len());\n        }\n\n        core::mem::drop(argv_tops);\n        core::mem::drop(envp_tops);\n        assert_eq!(stack.top() % 16, 0);\n\n        self.fpu_storage = Some(Self::alloc_fpu_storage());\n        self.context = Unique::dangling();\n        self.symtab = userland_entry.symtab;\n        unsafe {\n            exec_entry(userland_entry.entry_point.value(), stack.top(), 0x200);\n        }\n    }\n\n    pub fn fork(&mut self) -> KResult<Self> {\n        assert!(self.user, \"Cannot fork a kernel task\");\n\n        let address_space = self.address_space.fork(true)?;\n        unsafe { tlb::flush_all() };\n\n        let switch_stack = Self::alloc_switch_stack()?.as_raw_ptr_mut::<u8>();\n        let mut old_rsp = self.kernel_stack.as_ptr() as usize + self.kernel_stack.len();\n        let mut old_stack = Stack::new(&mut old_rsp);\n\n        let mut new_rsp = switch_stack as usize;\n        let mut new_stack = Stack::new(&mut new_rsp);\n\n        unsafe {\n            let new_frame = new_stack.offset::<InterruptErrorFrame>();\n            let old_frame = old_stack.offset::<InterruptErrorFrame>();\n            *new_frame = *old_frame;\n\n            new_frame.frame.rax = 0x0; // fork return value\n\n            // fixme: having interrupts enabled between the context switch and fork_init being called will clobber the stack\n            new_frame.frame.rflags = old_frame.frame.rflags & !0x200;\n        }\n        let context = unsafe { new_stack.offset::<Context>() };\n        *context = Context::default();\n        context.rip = fork_init as usize;\n        let mut fpu_storage = Self::alloc_fpu_storage();\n        fpu_storage.copy_from_slice(self.fpu_storage.as_ref().unwrap().as_slice());\n        Ok(Self {\n            context: unsafe { Unique::new_unchecked(context) },\n            address_space,\n            user: true,\n            kernel_stack: alloc::vec![0u8; KERNEL_STACK_SIZE].into_boxed_slice(),\n            fsbase: self.fsbase,\n            gsbase: self.gsbase,\n            fpu_storage: Some(fpu_storage),\n            symtab: self.symtab.clone(),\n        })\n    }\n\n    pub fn clone_process(\n        &self,\n        entry_point: VirtAddr,\n        user_stack: VirtAddr,\n        args: VirtAddr,\n        r8: usize,\n        r9: usize,\n        syscall_frame: &InterruptFrame,\n    ) -> KResult<Self> {\n        assert!(self.user, \"Cannot clone a kernel task\");\n\n        let address_space = AddressSpace::current().fork(true)?;\n        let switch_stack = Self::alloc_switch_stack()?.as_raw_ptr_mut::<u8>();\n\n        let mut new_rsp = switch_stack as usize;\n        let mut new_stack = Stack::new(&mut new_rsp);\n\n        let new_frame = unsafe { new_stack.offset::<InterruptErrorFrame>() };\n        *new_frame = InterruptErrorFrame::default();\n\n        new_frame.frame.cs = syscall_frame.cs;\n        new_frame.frame.ss = syscall_frame.ss;\n\n        new_frame.frame.r8 = r8;\n        new_frame.frame.r9 = r9;\n        new_frame.frame.rdi = args.value();\n\n        new_frame.frame.rip = entry_point.value();\n        new_frame.frame.rsp = user_stack.value();\n        new_frame.frame.rflags = 0x200;\n\n        let context = unsafe { new_stack.offset::<Context>() };\n        *context = Context::default();\n        context.rip = fork_init as usize;\n\n        let mut fpu_storage = Self::alloc_fpu_storage();\n        fpu_storage.copy_from_slice(self.fpu_storage.as_ref().unwrap().as_slice());\n\n        Ok(Self {\n            context: unsafe { Unique::new_unchecked(context) },\n            address_space,\n            user: true,\n            fpu_storage: Some(fpu_storage),\n            gsbase: self.gsbase,\n            fsbase: self.fsbase,\n            kernel_stack: alloc::vec![0u8; KERNEL_STACK_SIZE].into_boxed_slice(),\n            symtab: self.symtab.clone(),\n        })\n    }\n\n    fn alloc_fpu_storage() -> Box<[u8]> {\n        unsafe {\n            let xsave_size = CpuId::new().get_extended_state_info().unwrap().xsave_size();\n            let layout = Layout::from_size_align(xsave_size as usize, 16).unwrap();\n            let ptr = alloc_zeroed(layout);\n            Box::from_raw(core::ptr::slice_from_raw_parts_mut(\n                ptr,\n                xsave_size as usize,\n            ))\n        }\n    }\n\n    fn alloc_switch_stack() -> KResult<VirtAddr> {\n        Ok(alloc_kernel_frames(1)?.start_address().as_hhdm_virt() + PAGE_SIZE)\n    }\n\n    pub fn set_fsbase(&mut self, addr: VirtAddr) {\n        self.fsbase = addr;\n        unsafe {\n            wrmsr(IA32_FS_BASE, self.fsbase.value() as u64);\n        }\n    }\n\n    pub fn setup_signal_stack(\n        frame: &mut InterruptFrame,\n        signal: Signal,\n        handler: VirtAddr,\n        _syscall_result: isize,\n    ) -> KResult<()> {\n        const TRAMPOLINE: &[u8] = &[\n            0xb8, 0x0f, 0x00, 0x00, 0x00, // mov eax, 15\n            0x0f, 0x05, // syscall\n            0x90, // nop (for alignment)\n        ];\n\n        if frame.cs & 0x3 == 0 {\n            return Ok(());\n        }\n        let mut rsp = frame.rsp;\n        let mut stack = Stack::new(&mut rsp);\n        // red zone\n        stack.skip_by(128);\n        unsafe {\n            stack.push_bytes(TRAMPOLINE);\n            stack.push(stack.top());\n        }\n\n        frame.rip = handler.value();\n        frame.rsp = rsp;\n        frame.rdi = signal as usize;\n\n        Ok(())\n    }\n\n    pub fn setup_sigreturn_stack(\n        &self,\n        current_frame: &mut InterruptFrame,\n        signaled_frame: &InterruptFrame,\n    ) {\n        *current_frame = *signaled_frame;\n    }\n}\n"
  },
  {
    "path": "src/arch/x86_64/time.rs",
    "content": "use core::sync::atomic::{AtomicUsize, Ordering};\n\nuse x86::io::{inb, outb};\n\nuse crate::{userland::syscall::syscall_impl::time::TimeSpec, util::IrqMutex};\n\nconst PIT_FREQUENCY_HZ: usize = 1000;\npub const PIT_DIVIDEND: usize = 1193182;\n\nstatic UPTIME_RAW: AtomicUsize = AtomicUsize::new(0);\nstatic UPTIME_SEC: AtomicUsize = AtomicUsize::new(0);\n\npub static EPOCH: AtomicUsize = AtomicUsize::new(usize::MAX);\npub static RT_CLOCK: IrqMutex<TimeSpec> = IrqMutex::new(TimeSpec::zero());\n\npub fn get_uptime_ns() -> usize {\n    let ts = get_rt_clock();\n    (ts.tv_sec * 1000000000 + ts.tv_nsec) as usize\n}\n\npub fn get_uptime_ms() -> usize {\n    get_uptime_ns() / 1000000\n}\n\npub fn get_rt_clock() -> TimeSpec {\n    *RT_CLOCK.lock()\n}\n\npub fn get_pit_count() -> u16 {\n    unsafe {\n        outb(0x43, 0);\n\n        let lower = inb(0x40) as u16;\n        let higher = inb(0x40) as u16;\n\n        (higher << 8) | lower\n    }\n}\n\npub fn set_reload_value(new_count: u16) {\n    unsafe {\n        outb(0x43, 0x34);\n        outb(0x40, new_count as u8);\n        outb(0x40, (new_count >> 8) as u8);\n    }\n}\n\npub fn set_pit_frequency(frequency: usize) {\n    let mut new_divisor = PIT_DIVIDEND / frequency;\n\n    if PIT_DIVIDEND % frequency > frequency / 2 {\n        new_divisor += 1;\n    }\n\n    set_reload_value(new_divisor as u16);\n}\n\npub fn pit_irq() {\n    {\n        let interval = TimeSpec {\n            tv_sec: 0,\n            tv_nsec: (1000000000 / PIT_FREQUENCY_HZ) as isize,\n        };\n\n        let mut clk = RT_CLOCK.lock();\n\n        if clk.tv_nsec + interval.tv_nsec > 999999999 {\n            let diff = (clk.tv_nsec + interval.tv_nsec) - 1000000000;\n\n            clk.tv_nsec = diff;\n            clk.tv_sec += 1;\n        } else {\n            clk.tv_nsec += interval.tv_nsec;\n        }\n    }\n\n    let value = UPTIME_RAW.fetch_add(1, Ordering::Relaxed);\n    if value % PIT_FREQUENCY_HZ == 0 {\n        UPTIME_SEC.fetch_add(1, Ordering::Relaxed);\n    }\n}\n\npub fn init(boot_time: i64) {\n    EPOCH.store(boot_time as usize, Ordering::SeqCst);\n    RT_CLOCK.lock().tv_sec = boot_time as isize;\n    set_pit_frequency(PIT_FREQUENCY_HZ);\n}\n"
  },
  {
    "path": "src/backtrace.rs",
    "content": "use core::{alloc::Layout, mem::size_of, panic::PanicInfo};\n\nuse alloc::vec::Vec;\nuse spin::Once;\nuse x86_64::instructions::interrupts;\nuse xmas_elf::{\n    ElfFile,\n    sections::{SectionData, ShType},\n    symbol_table::Entry,\n};\n\nuse crate::{\n    fb_println,\n    graphics::FRAMEBUFFER,\n    kerror,\n    mem::{addr::VirtAddr, addr_space::AddressSpace, consts::PAGE_SIZE},\n    task::get_scheduler,\n    userland::elf::SymTabEntry,\n    util::{KResult, SavedInterruptStatus},\n};\n\npub static KERNEL_ELF: Once<ElfFile<'static>> = Once::new();\n\nfn print_symbol(rip: usize, symtab: &Option<Vec<SymTabEntry>>, depth: usize) {\n    if let Some(symbol_table) = symtab {\n        let mut name = None;\n        for data in symbol_table {\n            let st_value = data.value as usize;\n            let st_size = data.size as usize;\n\n            if rip >= st_value && rip < (st_value + st_size) {\n                name = Some(data.name.clone());\n            }\n        }\n\n        if let Some(name) = name {\n            serial0_println!(\"{:>2}: 0x{:016x} - {}\", depth, rip, name);\n        } else {\n            serial0_println!(\n                \"{:>2}: 0x{:016x} - <unknown> (symbol not found)\",\n                depth,\n                rip\n            );\n        }\n    } else {\n        serial0_println!(\"{:>2}: 0x{:016x} - <unknown> (no symbol table)\", depth, rip);\n    }\n}\n\npub fn unwind_user_stack_from(mut rbp: usize, mut rip: usize) {\n    let _guard = SavedInterruptStatus::save();\n    interrupts::disable();\n    let mut addr_space = AddressSpace::current();\n\n    if rbp == 0 {\n        serial0_println!(\"<empty backtrace>\");\n        return;\n    }\n\n    let current = get_scheduler().current_task_opt();\n    let symtab = if let Some(current) = current {\n        let s = current.arch_mut().symtab.clone();\n        if s.is_none() {\n            serial0_println!(\n                \"Warning: Couldn't find symbol table for pid {}\",\n                current.pid().as_usize()\n            );\n        }\n        s\n    } else {\n        serial0_println!(\"Warning: Couldn't lock current scheduler task\");\n        None\n    };\n\n    serial0_println!(\"---BEGIN BACKTRACE---\");\n    print_symbol(rip, &symtab, 0);\n    for depth in 1..17 {\n        if let Some(rip_rbp) = rbp.checked_add(size_of::<usize>()) {\n            let rip_rbp = unsafe { VirtAddr::new_unchecked(rip_rbp) };\n            let translated = addr_space.with_mapper(|mapper| mapper.translate(rip_rbp));\n            if rip_rbp.value() < PAGE_SIZE || translated.is_none() {\n                serial0_println!(\"{:>2}: <guard page>\", depth);\n                break;\n            }\n\n            rip = unsafe { rip_rbp.read::<usize>().unwrap_or(0) };\n            if rip == 0 || rbp == 0 {\n                break;\n            }\n\n            unsafe {\n                rbp = *(rbp as *const usize);\n            }\n\n            print_symbol(rip, &symtab, depth);\n        } else {\n            break;\n        }\n    }\n    serial0_println!(\"---END BACKTRACE---\");\n}\n\npub fn unwind_stack() -> KResult<()> {\n    let _guard = SavedInterruptStatus::save();\n    interrupts::disable();\n    let mut addr_space = AddressSpace::current();\n\n    let kernel_elf = KERNEL_ELF\n        .get()\n        .ok_or(kerror!(\"KERNEL_ELF not initialized\"))?;\n    let mut symbol_table = None;\n\n    for section in kernel_elf.section_iter() {\n        if section.get_type() == Ok(ShType::SymTab) {\n            let section_data = section\n                .get_data(kernel_elf)\n                .map_err(|_| kerror!(\"Failed to get kernel section data\"))?;\n\n            if let SectionData::SymbolTable64(symtab) = section_data {\n                symbol_table = Some(symtab);\n            }\n        }\n    }\n\n    let symbol_table = symbol_table.ok_or(kerror!(\"No symbol table available\"))?;\n    let mut rbp: usize;\n    unsafe {\n        core::arch::asm!(\"mov {}, rbp\", out(reg) rbp);\n    }\n\n    if rbp == 0 {\n        serial0_println!(\"<empty backtrace>\");\n        return Ok(());\n    }\n\n    serial0_println!(\"---BEGIN BACKTRACE---\");\n    for depth in 0..16 {\n        if let Some(rip_rbp) = rbp.checked_add(size_of::<usize>()) {\n            let rip_rbp = unsafe { VirtAddr::new_unchecked(rip_rbp) };\n            let translated = addr_space.with_mapper(|mapper| mapper.translate(rip_rbp));\n            if translated.is_none() {\n                serial0_println!(\"{:>2}: <guard page>\", depth);\n                break;\n            }\n\n            let rip = unsafe { rip_rbp.read::<usize>().unwrap_or(0) };\n            if rip == 0 || rbp == 0 {\n                break;\n            }\n\n            unsafe {\n                rbp = *(rbp as *const usize);\n            }\n\n            let mut name = None;\n            for data in symbol_table {\n                let st_value = data.value() as usize;\n                let st_size = data.size() as usize;\n\n                if rip >= st_value && rip < (st_value + st_size) {\n                    let mangled_name = data.get_name(kernel_elf).unwrap_or(\"<unknown>\");\n                    name = Some(rustc_demangle::demangle(mangled_name));\n                }\n            }\n\n            if let Some(name) = name {\n                serial0_println!(\"{:>2}: 0x{:016x} - {}\", depth, rip, name);\n            } else {\n                serial0_println!(\"{:>2}: 0x{:016x} - <unknown>\", depth, rip);\n            }\n        } else {\n            break;\n        }\n    }\n    serial0_println!(\"---END BACKTRACE---\");\n\n    Ok(())\n}\n\n#[panic_handler]\nfn rust_panic(info: &PanicInfo) -> ! {\n    interrupts::disable();\n    let panic_msg = info.message();\n\n    serial0_println!(\"Panicked at '{}'\", panic_msg);\n    if FRAMEBUFFER.get().is_some() {\n        fb_println!(\"Panicked at '{}'\", panic_msg);\n    }\n\n    if let Some(panic_location) = info.location() {\n        serial0_println!(\"{}\", panic_location);\n        if FRAMEBUFFER.get().is_some() {\n            fb_println!(\"{}\", panic_location);\n        }\n    }\n\n    match unwind_stack() {\n        Ok(()) => {}\n        Err(e) => crate::serial::_print0(format_args!(\"Error unwinding stack: {:?}\\n\", e.msg())),\n    }\n\n    crate::hcf();\n}\n\n#[allow(non_snake_case)]\n#[unsafe(no_mangle)]\nextern \"C\" fn _Unwind_Resume(unwind_context_ptr: usize) -> ! {\n    serial0_println!(\"{:#x}\", unwind_context_ptr);\n    crate::hcf();\n}\n\n#[lang = \"eh_personality\"]\n#[unsafe(no_mangle)]\nextern \"C\" fn rust_eh_personality() -> ! {\n    serial0_println!(\"Poisoned function `rust_eh_personality` was called.\");\n    crate::hcf()\n}\n\n#[alloc_error_handler]\nfn handle_alloc_error(layout: Layout) -> ! {\n    panic!(\"Alloc Error for layout {:?}\", layout)\n}\n"
  },
  {
    "path": "src/fs/devfs/fb.rs",
    "content": "use alloc::sync::Arc;\nuse spin::Once;\n\nuse crate::{\n    fs::{initramfs::get_root, opened_file::OpenFlags, File, FsNode, INode},\n    graphics::fb,\n    kerror,\n    mem::addr::VirtAddr,\n    userland::buffer::{UserBuffer, UserBufferMut, UserBufferReader, UserBufferWriter},\n    util::KResult,\n};\n\npub static DEV_FB0: Once<Arc<FbDevice>> = Once::new();\n\npub fn init() {\n    let fb0 = Arc::new(FbDevice);\n    get_root()\n        .unwrap()\n        .root_dir()\n        .lookup(\"dev\")\n        .unwrap()\n        .as_dir()\n        .unwrap()\n        .insert(INode::File(fb0.clone()));\n    DEV_FB0.call_once(|| fb0);\n}\n\npub struct FbDevice;\n\nimpl FsNode for FbDevice {\n    fn get_name(&self) -> alloc::string::String {\n        \"fb0\".into()\n    }\n}\n\nimpl File for FbDevice {\n    fn read(&self, offset: usize, buf: UserBufferMut, _options: &OpenFlags) -> KResult<usize> {\n        assert_eq!(offset % 4, 0);\n        let buf_len = buf.len();\n        assert_eq!(buf_len % 4, 0);\n        let mut writer = UserBufferWriter::from_buf(buf);\n        let mut fb = fb();\n        let mem = fb.frame_mut();\n        let start = offset / 4;\n        let len = buf_len / 4;\n        let end = (start + len).min(mem.len());\n        let mem = mem[start..end].iter().flat_map(|pixel| pixel.to_le_bytes());\n        for byte in mem {\n            writer.write(byte)?;\n        }\n        Ok((end - start) * 4)\n    }\n\n    fn write(&self, offset: usize, buf: UserBuffer<'_>, _options: &OpenFlags) -> KResult<usize> {\n        assert_eq!(offset % 4, 0);\n        let buf_len = buf.len();\n        assert_eq!(buf_len % 4, 0);\n        let mut reader = UserBufferReader::from_buf(buf);\n        let mut fb = fb();\n        let mem = fb.frame_mut();\n        let mut i = 0;\n        while (offset / 4 + i) < mem.len() && i < buf_len / 4 {\n            let pixel = reader.read::<u32>()?;\n            // let pixel = u32::from_le_bytes([byte0, byte1, byte2, byte3]);\n            mem[offset / 4 + i] = pixel;\n            i += 1;\n        }\n        fb.present();\n        Ok(i * 4)\n    }\n\n    fn ioctl(&self, cmd: usize, arg: usize) -> KResult<isize> {\n        const FBIOGET_VSCREENINFO: usize = 0x4600;\n        // const FBIOGET_FSCREENINFO: usize = 0x4602;\n        match cmd {\n            FBIOGET_VSCREENINFO => {\n                let fb = fb();\n                let info = FbVarScreenInfo {\n                    xres: fb.width() as u32,\n                    yres: fb.height() as u32,\n                    xres_virtual: fb.width() as u32,\n                    yres_virtual: fb.height() as u32,\n                    bpp: fb.bpp() as u32,\n                    ..FbVarScreenInfo::default()\n                };\n                unsafe { VirtAddr::new(arg).write_user(info) }?;\n            }\n            // FBIOGET_FSCREENINFO => {\n\n            // }\n            _ => return Err(kerror!(EINVAL, \"ioctl(): unknown cmd\")),\n        }\n        Ok(0)\n    }\n}\n\n#[repr(C)]\n#[derive(Clone, Copy, Default)]\nstruct FbBitField {\n    offset: u32,\n    length: u32,\n    msb_right: u32,\n}\n\n#[repr(C)]\n#[derive(Clone, Copy, Default)]\nstruct FbVarScreenInfo {\n    xres: u32,\n    yres: u32,\n    xres_virtual: u32,\n    yres_virtual: u32,\n    xoffset: u32,\n    yoffset: u32,\n    bpp: u32,\n    grayscale: u32,\n    red: FbBitField,\n    green: FbBitField,\n    blue: FbBitField,\n    transp: FbBitField,\n    nonstd: u32,\n    activate: u32,\n    height_mm: u32,\n    width_mm: u32,\n    accel_flags: u32,\n    pixclock: u32,\n    left_margin: u32,\n    right_margin: u32,\n    upper_margin: u32,\n    lower_margin: u32,\n    hsync_len: u32,\n    vsync_len: u32,\n    sync: u32,\n    vmode: u32,\n    rotate: u32,\n    colorspace: u32,\n    reserved: [u32; 4],\n}\n"
  },
  {
    "path": "src/fs/devfs/input.rs",
    "content": "//! TODO: make this more like an actual linux keyboard device file\n\nuse alloc::sync::Arc;\nuse pc_keyboard::{KeyEvent, KeyState};\nuse spin::{mutex::SpinMutex, Once};\n\nuse crate::{\n    fs::{initramfs::get_root, opened_file::OpenFlags, File, FsNode, INode},\n    userland::buffer::{UserBufferMut, UserBufferWriter},\n    util::KResult,\n};\n\npub static KBD_DEVICE: Once<Arc<KbdDevice>> = Once::new();\n\npub fn init() {\n    let kbd = Arc::new(KbdDevice::new());\n    get_root()\n        .unwrap()\n        .root_dir()\n        .lookup(\"dev\")\n        .unwrap()\n        .as_dir()\n        .unwrap()\n        .insert(INode::File(kbd.clone()));\n    KBD_DEVICE.call_once(|| kbd);\n}\n\npub struct KbdDevice {\n    keys_pressed: Arc<SpinMutex<[u8; 256]>>,\n}\n\nimpl KbdDevice {\n    pub fn new() -> Self {\n        Self {\n            keys_pressed: Arc::new(SpinMutex::new([0u8; 256])),\n        }\n    }\n\n    pub fn handle_kbd_irq(&self, key_evt: &KeyEvent) {\n        match key_evt.state {\n            KeyState::Down => self.keys_pressed.lock()[key_evt.code as u8 as usize] = 1,\n            KeyState::Up => self.keys_pressed.lock()[key_evt.code as u8 as usize] = 0,\n            _ => {}\n        }\n    }\n}\n\nimpl Default for KbdDevice {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl FsNode for KbdDevice {\n    fn get_name(&self) -> alloc::string::String {\n        \"kbd\".into()\n    }\n}\n\nimpl File for KbdDevice {\n    fn read(&self, _offset: usize, buf: UserBufferMut, _options: &OpenFlags) -> KResult<usize> {\n        let mut writer = UserBufferWriter::from_buf(buf);\n        let keys = *self.keys_pressed.lock();\n        writer.write(keys)\n    }\n}\n"
  },
  {
    "path": "src/fs/devfs/mod.rs",
    "content": "pub mod fb;\npub mod input;\npub mod null;\npub mod socket;\npub mod tty;\npub mod urandom;\n\npub fn init() {\n    self::tty::init();\n    self::null::init();\n    self::urandom::init();\n    self::fb::init();\n    self::input::init();\n    self::socket::init();\n}\n"
  },
  {
    "path": "src/fs/devfs/null.rs",
    "content": "use alloc::{borrow::ToOwned, sync::Arc};\nuse spin::Once;\n\nuse crate::{\n    fs::{\n        initramfs::get_root, opened_file::OpenFlags, File, FileMode, FsNode, INode, Stat, S_IFCHR,\n    },\n    userland::buffer::UserBufferWriter,\n};\n\nstatic DEV_NULL: Once<Arc<NullDevice>> = Once::new();\n\npub fn init() {\n    let null = Arc::new(NullDevice);\n    get_root()\n        .unwrap()\n        .root_dir()\n        .lookup(\"dev\")\n        .unwrap()\n        .as_dir()\n        .unwrap()\n        .insert(INode::File(null.clone()));\n    DEV_NULL.call_once(|| null);\n}\n\npub struct NullDevice;\n\nimpl FsNode for NullDevice {\n    fn get_name(&self) -> alloc::string::String {\n        \"null\".to_owned()\n    }\n}\n\nimpl File for NullDevice {\n    fn write(\n        &self,\n        _offset: usize,\n        buf: crate::userland::buffer::UserBuffer<'_>,\n        _options: &OpenFlags,\n    ) -> crate::util::KResult<usize> {\n        Ok(buf.len())\n    }\n\n    fn read(\n        &self,\n        _offset: usize,\n        buf: crate::userland::buffer::UserBufferMut,\n        _options: &OpenFlags,\n    ) -> crate::util::KResult<usize> {\n        let mut writer = UserBufferWriter::from_buf(buf);\n        writer.write_bytes(&[0x05])?; // EOF\n        Ok(writer.written_len())\n    }\n\n    fn stat(&self) -> crate::util::KResult<Stat> {\n        Ok(Stat {\n            inode_no: 4,\n            mode: FileMode(S_IFCHR),\n            ..Stat::zeroed()\n        })\n    }\n}\n"
  },
  {
    "path": "src/fs/devfs/socket.rs",
    "content": "use core::sync::atomic::{AtomicUsize, Ordering};\n\nuse crate::{\n    fs::{File, FsNode},\n    kerror,\n    mem::addr::VirtAddr,\n    util::{KError, KResult},\n};\n\npub fn init() {}\n\n#[repr(C)]\n#[non_exhaustive]\npub enum Domain {\n    Unix = 0,\n    Inet = 2,\n}\n\nimpl TryFrom<usize> for Domain {\n    type Error = KError<'static>;\n    fn try_from(value: usize) -> Result<Self, Self::Error> {\n        match value {\n            0 => Ok(Self::Unix),\n            2 => Ok(Self::Inet),\n            _ => Err(kerror!(EINVAL, \"invalid socket domain\")),\n        }\n    }\n}\n\n#[repr(C)]\n#[non_exhaustive]\npub enum SocketType {\n    Stream = 1,\n    Datagram = 2,\n    Raw = 3,\n    SeqPacket = 5,\n    Packet = 10,\n}\n\nimpl TryFrom<usize> for SocketType {\n    type Error = KError<'static>;\n    fn try_from(value: usize) -> Result<Self, Self::Error> {\n        match value {\n            1 => Ok(Self::Stream),\n            2 => Ok(Self::Datagram),\n            3 => Ok(Self::Raw),\n            5 => Ok(Self::SeqPacket),\n            10 => Ok(Self::Packet),\n            _ => Err(kerror!(EINVAL, \"invalid socket type\")),\n        }\n    }\n}\n\n#[repr(C)]\n#[non_exhaustive]\npub enum Protocol {\n    Ipv4 = 4,\n    Tcp = 6,\n    Udp = 17,\n}\n\nimpl TryFrom<usize> for Protocol {\n    type Error = KError<'static>;\n    fn try_from(value: usize) -> Result<Self, Self::Error> {\n        match value {\n            4 => Ok(Self::Ipv4),\n            6 => Ok(Self::Tcp),\n            17 => Ok(Self::Udp),\n            _ => Err(kerror!(EINVAL, \"invalid socket protocol\")),\n        }\n    }\n}\n\npub struct Socket {\n    pub id: usize,\n    // pub handle: SocketHandle,\n    pub domain: Domain,\n    pub typ: SocketType,\n    pub protocol: Protocol,\n}\n\nstatic NEXT_ID: AtomicUsize = AtomicUsize::new(0);\n\nimpl Socket {\n    pub fn alloc_id() -> usize {\n        NEXT_ID.fetch_add(1, Ordering::SeqCst)\n    }\n\n    // pub fn new() -> Self {\n    //     Self {\n    //         id: Self::alloc_id(),\n    //         handle: (),\n    //         domain: (),\n    //         typ: (),\n    //         protocol: (),\n    //     }\n    // }\n}\n\nimpl FsNode for Socket {\n    fn get_name(&self) -> alloc::string::String {\n        alloc::format!(\"socket{}\", self.id)\n    }\n}\n\n#[derive(Clone, Copy)]\n#[repr(C, packed)]\npub struct SockAddrInet {\n    family: u16,\n    port: [u8; 2],\n    addr: [u8; 4],\n    zero: [u8; 8],\n}\n\npub fn read_sockaddr(addr: VirtAddr, len: usize) -> KResult<SockAddrInet> {\n    let family = unsafe { addr.read_volatile::<u16>()? };\n    let sockaddr = match Domain::try_from(family as usize)? {\n        Domain::Inet => {\n            if len < core::mem::size_of::<SockAddrInet>() {\n                return Err(kerror!(EINVAL, \"read_sockaddr(): buffer overflow\"));\n            }\n\n            unsafe { addr.read_volatile::<SockAddrInet>()? }\n        }\n        Domain::Unix => {\n            todo!()\n        }\n    };\n    Ok(sockaddr)\n}\n\npub fn write_sockaddr(\n    sockaddr: SockAddrInet,\n    dst: Option<VirtAddr>,\n    socklen: Option<VirtAddr>,\n) -> KResult<()> {\n    if let Some(dst) = dst {\n        unsafe { dst.write_volatile(sockaddr) }?;\n    }\n    if let Some(socklen) = socklen {\n        unsafe { socklen.write_volatile(core::mem::size_of::<SockAddrInet>() as u32) }?;\n    }\n    Ok(())\n}\n\nimpl File for Socket {\n    fn ioctl(&self, cmd: usize, _arg: usize) -> KResult<isize> {\n        const FIONBIO: usize = 0x5421;\n        match cmd {\n            FIONBIO => {\n                // todo: set/clear non block flag\n            }\n            _ => return Err(kerror!(EINVAL, \"ioctl(): unknown cmd for socket\")),\n        }\n        Ok(0)\n    }\n}\n"
  },
  {
    "path": "src/fs/devfs/tty.rs",
    "content": "use core::fmt::Debug;\n\nuse alloc::{\n    borrow::ToOwned,\n    string::String,\n    sync::{Arc, Weak},\n    vec::Vec,\n};\n\nuse bitflags::bitflags;\nuse spin::Once;\n\nuse crate::{\n    fb_print,\n    graphics::{self, render_text_buf},\n    kerror,\n    mem::addr::VirtAddr,\n    task::{current_task, get_scheduler, group::TaskGroup, signal::SIGINT, wait_queue::WaitQueue},\n    userland::buffer::{UserBuffer, UserBufferMut, UserBufferReader, UserBufferWriter},\n    util::{\n        ctypes::c_int, errno::Errno, error::KResult, lock::IrqMutex, ringbuffer::RingBuffer, KError,\n    },\n    vga_text,\n};\n\nuse crate::fs::{\n    initramfs::{dir::InitRamFsDir, get_root},\n    opened_file::OpenFlags,\n    path::Path,\n    File, FileMode, FileRef, FsNode, INode, PollStatus, Stat, POLL_WAIT_QUEUE, S_IFCHR,\n};\n\npub static TTY: Once<Arc<Tty>> = Once::new();\n\npub fn init() {\n    let tty = Arc::new(Tty::new(\"tty\"));\n    TTY.call_once(|| tty.clone());\n    get_root()\n        .unwrap()\n        .lookup(Path::new(\"dev\"), true)\n        .unwrap()\n        .as_dir()\n        .unwrap()\n        .insert(INode::File(tty));\n}\n\nbitflags! {\n    #[repr(C)]\n    #[derive(Clone, Copy, Debug)]\n    pub struct LFlag: u32 {\n        const ICANON = 0o0000002;\n        const ECHO   = 0o0000010;\n    }\n}\n\nimpl Default for LFlag {\n    fn default() -> Self {\n        Self::all()\n    }\n}\n\nbitflags! {\n    #[repr(C)]\n    #[derive(Clone, Copy, Debug)]\n    pub struct IFlag: u32 {\n        const IGNBRK\t= 0o0000001;\n        const BRKINT\t= 0o0000002;\n        const IGNPAR\t= 0o0000004;\n        const PARMRK\t= 0o0000010;\n        const INPCK\t    = 0o0000020;\n        const ISTRIP\t= 0o0000040;\n        const INLCR\t    = 0o0000100;\n        const IGNCR\t    = 0o0000200;\n        const ICRNL\t    = 0o0000400;\n        const IUCLC\t    = 0o0001000;\n        const IXON\t    = 0o0002000;\n        const IXANY\t    = 0o0004000;\n        const IXOFF\t    = 0o0010000;\n        const IMAXBEL\t= 0o0020000;\n        const IUTF8\t    = 0o0040000;\n    }\n}\n\nimpl Default for IFlag {\n    fn default() -> Self {\n        Self::ICRNL\n    }\n}\n\n#[repr(C)]\n#[derive(Clone, Copy)]\npub struct Termios {\n    iflag: IFlag,\n    oflag: u32,\n    cflag: u32,\n    lflag: LFlag,\n    cc: [u8; 0],\n    reserved: [u32; 3],\n    ispeed: u32,\n    ospeed: u32,\n}\n\nimpl Termios {\n    pub fn is_cooked(&self) -> bool {\n        self.lflag.contains(LFlag::ICANON)\n    }\n}\n\nimpl Default for Termios {\n    fn default() -> Self {\n        Termios {\n            iflag: IFlag::ICRNL,\n            lflag: LFlag::ICANON | LFlag::ECHO,\n\n            oflag: 0,\n            cflag: 0,\n            cc: [0; 0],\n            reserved: [0; 3],\n            ispeed: 0,\n            ospeed: 0,\n        }\n    }\n}\n\nimpl Debug for Termios {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        f.debug_struct(\"Termios\")\n            .field(\"iflag\", &self.iflag)\n            .field(\"lflag\", &self.lflag)\n            .finish()\n    }\n}\n\npub enum LineControl {\n    Backspace,\n    Echo(u8),\n}\n\npub struct LineDiscipline {\n    wait_queue: WaitQueue,\n    current_line: IrqMutex<Vec<u8>>,\n    buf: IrqMutex<RingBuffer<u8, 4096>>,\n    termios: IrqMutex<Termios>,\n    foreground_group: IrqMutex<Weak<IrqMutex<TaskGroup>>>,\n}\n\nimpl LineDiscipline {\n    pub fn new() -> Self {\n        Self {\n            wait_queue: WaitQueue::new(),\n            current_line: IrqMutex::new(Vec::new()),\n            buf: IrqMutex::new(RingBuffer::new()),\n            termios: IrqMutex::new(Termios::default()),\n            foreground_group: IrqMutex::new(Weak::new()),\n        }\n    }\n\n    pub fn is_readable(&self) -> bool {\n        self.buf.lock().is_readable()\n    }\n\n    pub fn is_writable(&self) -> bool {\n        self.buf.lock().is_writable()\n    }\n\n    pub fn foreground_group(&self) -> Option<Arc<IrqMutex<TaskGroup>>> {\n        self.foreground_group.lock().upgrade()\n    }\n\n    pub fn set_foreground_group(&self, pg: Weak<IrqMutex<TaskGroup>>) {\n        *self.foreground_group.lock() = pg;\n    }\n\n    fn _is_current_foreground(&self) -> bool {\n        let pg = &*self.foreground_group.lock();\n        current_task().belongs_to_group(pg) || pg.upgrade().is_none()\n    }\n\n    pub fn write<F>(&self, buf: UserBuffer<'_>, callback: F) -> KResult<usize>\n    where\n        F: Fn(LineControl),\n    {\n        let termios = self.termios.lock();\n        let mut current_line = self.current_line.lock();\n        let mut ringbuf = self.buf.lock();\n        let mut written_len = 0;\n        let mut reader = UserBufferReader::from_buf(buf);\n        while reader.remaining_len() > 0 {\n            let mut tmp = [0; 1];\n            let copied_len = reader.read_bytes(&mut tmp)?;\n            for ch in &tmp.as_slice()[..copied_len] {\n                match ch {\n                    0x03 if termios.is_cooked() => {\n                        if let Some(pg) = self.foreground_group() {\n                            pg.lock().signal(SIGINT);\n                        }\n                    }\n                    0x08 if termios.is_cooked() => {\n                        if !current_line.is_empty() {\n                            current_line.pop();\n                            callback(LineControl::Backspace);\n                        }\n                    }\n                    b'\\r' if termios.iflag.contains(IFlag::ICRNL) => {\n                        current_line.push(b'\\n');\n                        ringbuf.push_slice(&current_line);\n                        current_line.clear();\n                        if termios.lflag.contains(LFlag::ECHO) {\n                            callback(LineControl::Echo(b'\\r'));\n                            callback(LineControl::Echo(b'\\n'));\n                        }\n                    }\n                    b'\\n' => {\n                        current_line.push(b'\\n');\n                        ringbuf.push_slice(&current_line);\n                        current_line.clear();\n                        if termios.lflag.contains(LFlag::ECHO) {\n                            callback(LineControl::Echo(b'\\n'));\n                        }\n                    }\n                    ch if termios.is_cooked() => {\n                        if 0x20 <= *ch && *ch < 0x7f {\n                            current_line.push(*ch);\n                            if termios.lflag.contains(LFlag::ECHO) {\n                                callback(LineControl::Echo(*ch));\n                            }\n                        }\n                    }\n                    _ => {\n                        ringbuf.push(*ch).ok();\n                    }\n                }\n\n                written_len += 1;\n            }\n        }\n\n        get_scheduler().wake_all(&self.wait_queue);\n\n        Ok(written_len)\n    }\n\n    pub fn read(&self, dst: UserBufferMut<'_>, options: &OpenFlags) -> KResult<usize> {\n        let mut writer = UserBufferWriter::from_buf(dst);\n        let timeout = if options.contains(OpenFlags::O_NONBLOCK) {\n            Some(0)\n        } else {\n            None\n        };\n        self.wait_queue.sleep_signalable_until(timeout, || {\n            // todo: figure out how to get this working\n            // if !self.is_current_foreground() {\n            //     return Ok(None)\n            // }\n\n            let mut buf_lock = self.buf.lock();\n            while writer.remaining_len() > 0 {\n                if let Some(slice) = buf_lock.pop_slice(writer.remaining_len()) {\n                    writer.write_bytes(slice)?;\n                } else {\n                    break;\n                }\n            }\n\n            if writer.written_len() > 0 {\n                Ok(Some(writer.written_len()))\n            } else {\n                Ok(None)\n            }\n        })\n    }\n}\n\nimpl Default for LineDiscipline {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\npub struct Tty {\n    name: String,\n    discipline: LineDiscipline,\n}\n\nimpl Tty {\n    pub fn new(name: &str) -> Self {\n        Self {\n            name: name.to_owned(),\n            discipline: LineDiscipline::new(),\n        }\n    }\n\n    pub fn set_cooked_mode(&self, cooked: bool) {\n        if cooked {\n            self.discipline.termios.lock().lflag |= LFlag::ICANON | LFlag::ECHO;\n        } else {\n            self.discipline.termios.lock().lflag &= !(LFlag::ICANON | LFlag::ECHO);\n        }\n    }\n\n    pub fn input_char(&self, ch: u8) {\n        self.discipline\n            .write(UserBuffer::from_slice(&[ch]), |ctrl| match ctrl {\n                LineControl::Backspace => {\n                    // serial1_print!(\"\\x08 \\x08\");\n                    graphics::backspace();\n                }\n                LineControl::Echo(ch) => {\n                    self.write(0, UserBuffer::from_slice(&[ch]), &OpenFlags::empty())\n                        .ok();\n                }\n            })\n            .ok();\n    }\n\n    pub fn set_foreground_group(&self, pg: Weak<IrqMutex<TaskGroup>>) {\n        self.discipline.set_foreground_group(pg)\n    }\n}\n\nimpl FsNode for Tty {\n    fn get_name(&self) -> String {\n        self.name.clone()\n    }\n}\nconst TCGETS: usize = 0x5401;\nconst TCSETS: usize = 0x5402;\nconst TCSETSW: usize = 0x5403;\n\nconst TIOCGPGRP: usize = 0x540f;\nconst TIOCSPGRP: usize = 0x5410;\nconst TIOCGWINSZ: usize = 0x5413;\n\n#[repr(C)]\n#[derive(Copy, Clone)]\nstruct WinSize {\n    ws_row: u16,\n    ws_col: u16,\n    ws_xpixel: u16,\n    ws_ypixel: u16,\n}\n\nimpl File for Tty {\n    fn ioctl(&self, cmd: usize, arg: usize) -> KResult<isize> {\n        match cmd {\n            TCGETS => {\n                let termios = *self.discipline.termios.lock();\n                let arg = VirtAddr::new(arg);\n                unsafe { arg.write_user(termios) }?;\n            }\n            TCSETS | TCSETSW => {\n                let arg = VirtAddr::new(arg);\n                let termios = unsafe { arg.read_user::<Termios>()? };\n                *self.discipline.termios.lock() = termios;\n            }\n            TIOCGPGRP => {\n                let group = self\n                    .discipline\n                    .foreground_group()\n                    .unwrap_or(current_task().group.borrow().upgrade().unwrap());\n                let id = group.lock().pgid();\n                let arg = VirtAddr::new(arg);\n                unsafe { arg.write_user(id) }?;\n            }\n            TIOCSPGRP => {\n                let arg = VirtAddr::new(arg);\n                let pgid = unsafe { arg.read_user::<c_int>()? };\n                let pg = get_scheduler().find_or_create_group(pgid);\n                self.discipline.set_foreground_group(Arc::downgrade(&pg));\n            }\n            TIOCGWINSZ => {\n                let winsize = WinSize {\n                    ws_row: vga_text::BUFFER_HEIGHT as u16,\n                    ws_col: vga_text::BUFFER_WIDTH as u16,\n                    ws_xpixel: 0,\n                    ws_ypixel: 0,\n                };\n                let arg = VirtAddr::new(arg);\n                unsafe { arg.write_user(winsize) }?;\n            }\n            _ => return Err(kerror!(ENOTTY, \"ioctl(): command not found\")),\n        }\n\n        Ok(0)\n    }\n\n    fn stat(&self) -> KResult<Stat> {\n        Ok(Stat {\n            inode_no: 3,\n            mode: FileMode::new(S_IFCHR | 0o666),\n            ..Stat::zeroed()\n        })\n    }\n\n    fn read(&self, _offset: usize, buf: UserBufferMut, options: &OpenFlags) -> KResult<usize> {\n        let read_len = self.discipline.read(buf, options);\n        match read_len {\n            Ok(read_len) => {\n                if read_len > 0 {\n                    get_scheduler().wake_all(&POLL_WAIT_QUEUE);\n                }\n                Ok(read_len)\n            }\n            Err(KError { errno, .. })\n                if options.contains(OpenFlags::O_NONBLOCK) && errno == Some(Errno::EINTR) =>\n            {\n                Ok(0)\n            }\n            Err(e) => Err(e),\n        }\n    }\n\n    fn write(&self, _offset: usize, buf: UserBuffer<'_>, _options: &OpenFlags) -> KResult<usize> {\n        // let mut tmp = [0; 1];\n        // let mut total_len = 0;\n        let reader = UserBufferReader::from_buf(buf);\n        let total_len = parse(reader)?;\n        if total_len > 0 {\n            render_text_buf();\n            get_scheduler().wake_all(&POLL_WAIT_QUEUE);\n        }\n        Ok(total_len)\n    }\n\n    fn poll(&self) -> KResult<PollStatus> {\n        let mut status = PollStatus::empty();\n        // if self.discipline.is_readable() {\n        status |= PollStatus::POLLIN;\n        // }\n        // if self.discipline.is_writable() {\n        status |= PollStatus::POLLOUT;\n        // }\n        Ok(status)\n    }\n}\n\nfn parse(mut reader: UserBufferReader) -> KResult<usize> {\n    let mut bytes = alloc::vec![0u8; reader.remaining_len()];\n    reader.read_bytes(&mut bytes)?;\n\n    let mut escape_codes = bytes.split(|b| *b == 0x1b);\n    if bytes[0] != 0x1b {\n        // print until the first escape code\n        if let Some(next) = escape_codes.next() {\n            fb_print!(\"{}\", core::str::from_utf8(next).unwrap());\n        } else {\n            return Ok(0);\n        }\n    }\n    for chunk in escape_codes {\n        if chunk.is_empty() {\n            continue;\n        }\n        if chunk[0] != b'[' {\n            continue;\n        }\n        let chunk = &chunk[1..];\n        // iterate through the chunk until we find one of the ANSI \"functions\"\n        // const ANSI_FUNCTIONS: &[u8] = b\"ABCDEFGHJKSTsufm\";\n        const ANSI_FUNCTIONS: &[u8] = b\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\";\n        let res = chunk\n            .iter()\n            .enumerate()\n            .find(|(_i, byte)| ANSI_FUNCTIONS.contains(*byte));\n        let (f_idx, function) = if let Some(res) = res {\n            res\n        } else {\n            unreachable!()\n        };\n        // get its arguments, if any\n        let arguments = chunk[..f_idx]\n            .split(|byte| *byte == b';')\n            .collect::<Vec<&[u8]>>();\n\n        let parse_usize = |arg: &[u8]| core::str::from_utf8(arg).unwrap().parse::<usize>();\n\n        let (x, y) = graphics::cursor_xy();\n        match *function {\n            b'A' => {\n                let n = parse_usize(arguments[0]).unwrap_or(1);\n                graphics::set_cursor_y(y.saturating_sub(n));\n            }\n            b'B' => {\n                let n = parse_usize(arguments[0]).unwrap_or(1);\n                graphics::set_cursor_y(y.saturating_add(n));\n            }\n            b'C' => {\n                let n = parse_usize(arguments[0]).unwrap_or(1);\n                graphics::set_cursor_x(x.saturating_add(n));\n            }\n            b'D' => {\n                let n = parse_usize(arguments[0]).unwrap_or(1);\n                graphics::set_cursor_x(x.saturating_sub(n));\n            }\n            b'E' => {\n                let n = parse_usize(arguments[0]).unwrap_or(1);\n                graphics::set_cursor_x(0);\n                graphics::set_cursor_y(y.saturating_add(n));\n            }\n            b'F' => {\n                let n = parse_usize(arguments[0]).unwrap_or(1);\n                graphics::set_cursor_x(0);\n                graphics::set_cursor_y(y.saturating_sub(n));\n            }\n            b'G' | b'f' => {\n                let n = parse_usize(arguments[0]).unwrap();\n                graphics::set_cursor_x(n);\n            }\n            b'H' => {\n                if arguments[0].is_empty() {\n                    graphics::set_cursor_xy((0, 0));\n                } else {\n                    let n = parse_usize(arguments[0]).unwrap_or(0);\n                    let m = parse_usize(arguments[1]).unwrap_or(0);\n                    graphics::set_cursor_xy((n, m));\n                }\n            }\n            b'J' => {\n                if arguments.is_empty() {\n                    graphics::clear_until_end();\n                } else {\n                    let n = parse_usize(arguments[0]).unwrap_or(0);\n                    match n {\n                        0 => graphics::clear_until_end(),\n                        1 => graphics::clear_until_beginning(),\n                        2 => graphics::clear_screen(),\n                        3 => todo!(\"erase saved lines\"),\n                        _ => unimplemented!(),\n                    }\n                }\n            }\n            b'K' => {\n                if arguments.is_empty() {\n                    graphics::clear_until_eol();\n                } else {\n                    let n = parse_usize(arguments[0]).unwrap_or(0);\n                    match n {\n                        0 => graphics::clear_until_eol(),\n                        1 => graphics::clear_from_bol(),\n                        2 => graphics::clear_line(),\n                        _ => unimplemented!(),\n                    }\n                }\n            }\n            b'S' => {\n                todo!(\"scroll up by N lines\")\n            }\n            b'T' => {\n                todo!(\"scroll down by N ines\")\n            }\n            b's' => {\n                todo!(\"save cursor position\")\n            }\n            b'u' => {\n                todo!(\"restore cursor postion\")\n            }\n            b'm' => {\n                // let arg0 = parse_usize(arguments[0]).unwrap() as u8;\n                // match arg0 {\n                //     0 => graphics::set_color_code(ColorCode::new(Color::White, Color::Black)),\n                //     1 => {} // bold\n                //     3 => {} // italic\n                //     4 => {} // underline\n                //     30..=37 => {\n                //         let color = graphics::get_color_code();\n                //         graphics::set_color_code(ColorCode::new(\n                //             unsafe { core::mem::transmute(arg0 - 30) },\n                //             color.background(),\n                //         ));\n                //     }\n                //     40..=47 => {\n                //         let color = graphics::get_color_code();\n                //         graphics::set_color_code(ColorCode::new(color.foreground(), unsafe {\n                //             core::mem::transmute(arg0 - 40)\n                //         }));\n                //     }\n                //     90..=97 => {\n                //         todo!(\"bright foreground color\")\n                //     }\n                //     100..=107 => {\n                //         todo!(\"bright background color\")\n                //     }\n                //     _ => todo!(\n                //         \"Unknown ANSI function: {}\",\n                //         core::str::from_utf8(chunk).unwrap()\n                //     ),\n                // }\n            }\n            _function if chunk[0] == b'?' => {\n                let n = parse_usize(&arguments[0][1..]).unwrap();\n                match n {\n                    /*\n                    Save cursor as in DECSC, xterm.  After\n                    saving the cursor, switch to the Alternate Screen Buffer,\n                    clearing it first.\n                     */\n                    1049 => {}\n                    n => unimplemented!(\"Unknown ANSI extension function: {}\", n),\n                }\n            }\n            _ => {\n                unimplemented!(\n                    \"Unknown ANSI function: {}\",\n                    core::str::from_utf8(chunk).unwrap()\n                )\n            }\n        }\n\n        fb_print!(\"{}\", core::str::from_utf8(&chunk[f_idx + 1..]).unwrap());\n    }\n\n    Ok(reader.read_len())\n}\n\npub struct PtyMaster {\n    wait_queue: WaitQueue,\n    buf: IrqMutex<Vec<u8>>,\n    discipline: LineDiscipline,\n}\n\nimpl PtyMaster {\n    pub fn new() -> KResult<(Arc<PtyMaster>, Arc<PtySlave>)> {\n        let master = Arc::new(PtyMaster {\n            wait_queue: WaitQueue::new(),\n            buf: IrqMutex::new(Vec::new()),\n            discipline: LineDiscipline::new(),\n        });\n        let slave = Arc::new(PtySlave::new(master.clone()));\n        Ok((master, slave))\n    }\n}\n\nimpl FsNode for PtyMaster {\n    fn get_name(&self) -> String {\n        \"tty0\".to_owned()\n    }\n}\n\nimpl File for PtyMaster {\n    fn read(&self, _offset: usize, buf: UserBufferMut<'_>, options: &OpenFlags) -> KResult<usize> {\n        let mut writer = UserBufferWriter::from_buf(buf);\n        let timeout = if options.contains(OpenFlags::O_NONBLOCK) {\n            Some(0)\n        } else {\n            None\n        };\n        let read_len = self.wait_queue.sleep_signalable_until(timeout, || {\n            let mut buf_lock = self.buf.lock();\n            if buf_lock.is_empty() {\n                return Ok(None);\n            }\n\n            let copy_len = core::cmp::min(buf_lock.len(), writer.remaining_len());\n            writer.write_bytes(&buf_lock[..copy_len])?;\n            buf_lock.drain(..copy_len);\n            Ok(Some(copy_len))\n        })?;\n\n        if read_len > 0 {\n            get_scheduler().wake_all(&POLL_WAIT_QUEUE);\n        }\n\n        Ok(read_len)\n    }\n\n    fn write(&self, _offset: usize, buf: UserBuffer<'_>, _options: &OpenFlags) -> KResult<usize> {\n        let written_len = self.discipline.write(buf, |ctrl| {\n            let mut master_buf = self.buf.lock();\n            match ctrl {\n                LineControl::Backspace => {\n                    master_buf.extend_from_slice(b\"\\x08 \\x08\");\n                }\n                LineControl::Echo(ch) => {\n                    master_buf.push(ch);\n                }\n            }\n        })?;\n\n        if written_len > 0 {\n            get_scheduler().wake_all(&POLL_WAIT_QUEUE);\n        }\n\n        Ok(written_len)\n    }\n\n    fn ioctl(&self, cmd: usize, _arg: usize) -> KResult<isize> {\n        log::warn!(\"ioctl(): unknown cmd for PtyMaster ({:#x})\", cmd);\n        Ok(0)\n    }\n\n    fn stat(&self) -> KResult<Stat> {\n        Ok(Stat {\n            inode_no: 5,\n            mode: FileMode::new(S_IFCHR | 0o666),\n            ..Stat::zeroed()\n        })\n    }\n\n    fn poll(&self) -> KResult<PollStatus> {\n        let mut status = PollStatus::empty();\n        if !self.buf.lock().is_empty() {\n            status |= PollStatus::POLLIN;\n        }\n        if self.discipline.is_writable() {\n            status |= PollStatus::POLLOUT;\n        }\n\n        Ok(status)\n    }\n}\n\npub struct PtySlave {\n    master: Arc<PtyMaster>,\n}\n\nimpl PtySlave {\n    pub fn new(master: Arc<PtyMaster>) -> Self {\n        Self { master }\n    }\n}\n\nimpl FsNode for PtySlave {\n    fn get_name(&self) -> String {\n        \"ttyS0\".to_owned()\n    }\n}\n\nimpl File for PtySlave {\n    fn read(&self, _offset: usize, buf: UserBufferMut, options: &OpenFlags) -> KResult<usize> {\n        let read_len = self.master.discipline.read(buf, options)?;\n        if read_len > 0 {\n            get_scheduler().wake_all(&POLL_WAIT_QUEUE);\n        }\n        Ok(read_len)\n    }\n\n    fn write(&self, _offset: usize, buf: UserBuffer<'_>, _options: &OpenFlags) -> KResult<usize> {\n        let mut written_len = 0;\n        let mut master_buf = self.master.buf.lock();\n        let mut reader = UserBufferReader::from_buf(buf);\n\n        while reader.remaining_len() > 0 {\n            let mut tmp = [0; 1];\n            let copied_len = reader.read_bytes(&mut tmp)?;\n            for ch in &tmp[..copied_len] {\n                match *ch {\n                    b'\\n' => {\n                        master_buf.push(b'\\r');\n                        master_buf.push(b'\\n');\n                    }\n                    _ => {\n                        master_buf.push(*ch);\n                    }\n                }\n            }\n            written_len += copied_len;\n        }\n\n        if written_len > 0 {\n            get_scheduler().wake_all(&POLL_WAIT_QUEUE);\n        }\n\n        Ok(written_len)\n    }\n\n    fn stat(&self) -> KResult<Stat> {\n        Ok(Stat {\n            inode_no: 6,\n            mode: FileMode::new(S_IFCHR | 0o666),\n            ..Stat::zeroed()\n        })\n    }\n\n    fn ioctl(&self, cmd: usize, _arg: usize) -> KResult<isize> {\n        const TIOCSPTLCK: usize = 0x40045431;\n        match cmd {\n            TIOCSPTLCK => Ok(0),\n            _ => {\n                log::warn!(\"ioctl(): unknown cmd for PtySlave ({:#x})\", cmd);\n                Ok(0)\n            }\n        }\n    }\n\n    fn poll(&self) -> KResult<PollStatus> {\n        let mut status = PollStatus::empty();\n\n        if self.master.discipline.is_readable() {\n            status |= PollStatus::POLLIN;\n        }\n\n        status |= PollStatus::POLLOUT;\n\n        Ok(status)\n    }\n}\n\npub struct Ptmx {\n    pts_dir: Arc<InitRamFsDir>,\n}\n\nimpl Ptmx {\n    pub fn new(pts_dir: Arc<InitRamFsDir>) -> Self {\n        Self { pts_dir }\n    }\n}\n\nimpl FsNode for Ptmx {\n    fn get_name(&self) -> String {\n        todo!()\n    }\n}\n\nimpl File for Ptmx {\n    fn open(&self, _options: &OpenFlags) -> KResult<Option<FileRef>> {\n        let (master, slave) = PtyMaster::new()?;\n        self.pts_dir.add_file(slave);\n        Ok(Some(master as FileRef))\n    }\n\n    fn stat(&self) -> KResult<Stat> {\n        Ok(Stat {\n            inode_no: 4,\n            mode: FileMode::new(S_IFCHR | 0o666),\n            ..Stat::zeroed()\n        })\n    }\n\n    fn read(&self, _offset: usize, _buf: UserBufferMut, _options: &OpenFlags) -> KResult<usize> {\n        unreachable!()\n    }\n\n    fn write(&self, _offset: usize, _buf: UserBuffer<'_>, _options: &OpenFlags) -> KResult<usize> {\n        unreachable!()\n    }\n\n    fn poll(&self) -> KResult<PollStatus> {\n        Ok(PollStatus::empty())\n    }\n}\n"
  },
  {
    "path": "src/fs/devfs/urandom.rs",
    "content": "use alloc::sync::Arc;\nuse x86::random::rdrand_slice;\n\nuse crate::{\n    fs::{initramfs::get_root, path::Path, File, FsNode, INode},\n    userland::buffer::UserBufferWriter,\n};\n\npub fn init() {\n    get_root()\n        .unwrap()\n        .lookup(Path::new(\"dev\"), true)\n        .unwrap()\n        .as_dir()\n        .unwrap()\n        .insert(INode::File(Arc::new(URandom)));\n}\n\npub struct URandom;\n\nimpl FsNode for URandom {\n    fn get_name(&self) -> alloc::string::String {\n        \"urandom\".into()\n    }\n}\n\nimpl File for URandom {\n    fn read(\n        &self,\n        _offset: usize,\n        buf: crate::userland::buffer::UserBufferMut,\n        _options: &crate::fs::opened_file::OpenFlags,\n    ) -> crate::util::KResult<usize> {\n        let mut bytes = alloc::vec![0u8; buf.len()];\n        unsafe {\n            rdrand_slice(&mut bytes);\n        }\n        let mut writer = UserBufferWriter::from_buf(buf);\n        writer.write_bytes(&bytes)?;\n        Ok(writer.written_len())\n    }\n}\n"
  },
  {
    "path": "src/fs/initramfs/dir.rs",
    "content": "use alloc::{\n    string::String,\n    sync::{Arc, Weak},\n    vec::Vec,\n};\n\nuse crate::{\n    fs::{\n        alloc_inode_no, DirEntry, Directory, FileMode, FileRef, FileType, FsNode, INode, Stat,\n        S_IFDIR,\n    },\n    kerror,\n    util::{lock::IrqMutex, KResult},\n};\n\npub struct DirInner {\n    pub children: Vec<INode>,\n    pub stat: Stat,\n    pub name: String,\n}\n\npub struct InitRamFsDir {\n    pub(super) parent: Weak<InitRamFsDir>,\n    pub(super) inner: IrqMutex<DirInner>,\n}\n\nimpl InitRamFsDir {\n    pub fn new(name: String, inode_no: usize) -> InitRamFsDir {\n        InitRamFsDir {\n            parent: Weak::new(),\n            inner: IrqMutex::new(DirInner {\n                name,\n                children: Vec::new(),\n                stat: Stat {\n                    inode_no,\n                    mode: FileMode::new(S_IFDIR | 0o755),\n                    ..Stat::zeroed()\n                },\n            }),\n        }\n    }\n\n    pub fn add_dir(&self, name: String) -> Arc<InitRamFsDir> {\n        let dir = Arc::new(InitRamFsDir::new(name, alloc_inode_no()));\n        self.inner.lock().children.push(INode::Dir(dir.clone()));\n        dir\n    }\n\n    pub fn add_file(&self, file: FileRef) {\n        self.inner.lock().children.push(INode::File(file.clone()));\n    }\n\n    pub fn parent_dir(&self) -> Option<Arc<InitRamFsDir>> {\n        self.parent.upgrade()\n    }\n}\n\nimpl Directory for InitRamFsDir {\n    fn insert(&self, inode: INode) {\n        self.inner.lock().children.push(inode);\n    }\n\n    fn lookup(&self, name: &str) -> KResult<INode> {\n        let inode = self\n            .inner\n            .lock()\n            .children\n            .iter()\n            .find(|child| child.get_name() == *name)\n            .cloned()\n            .ok_or(kerror!(ENOENT, \"lookup(): not found\"))?;\n        Ok(inode)\n    }\n\n    fn stat(&self) -> KResult<Stat> {\n        Ok(self.inner.lock().stat)\n    }\n\n    fn readdir(&self, index: usize) -> KResult<Option<crate::fs::DirEntry>> {\n        let entry = self\n            .inner\n            .lock()\n            .children\n            .get(index)\n            .map(|entry| match entry {\n                INode::Pipe(_) => unreachable!(\"Pipes should be in PipeFs\"),\n                INode::Dir(dir) => DirEntry {\n                    inode_no: dir.stat().unwrap().inode_no,\n                    file_type: FileType::Directory,\n                    name: dir.get_name(),\n                },\n                INode::File(file) => DirEntry {\n                    inode_no: file.stat().unwrap().inode_no,\n                    file_type: FileType::Directory,\n                    name: file.get_name(),\n                },\n                INode::Symlink(link) => DirEntry {\n                    inode_no: link.stat().unwrap().inode_no,\n                    file_type: FileType::Link,\n                    name: link.get_name(),\n                },\n            });\n\n        Ok(entry)\n    }\n\n    fn unlink(&self, name: &str) -> KResult<()> {\n        self.inner\n            .lock()\n            .children\n            .retain(|child| child.get_name() != name);\n        Ok(())\n    }\n}\n\nimpl FsNode for InitRamFsDir {\n    fn get_name(&self) -> String {\n        self.inner.lock().name.clone()\n    }\n}\n"
  },
  {
    "path": "src/fs/initramfs/file.rs",
    "content": "use alloc::{string::String, vec, vec::Vec};\n\nuse crate::{\n    fs::{opened_file::OpenFlags, File, FileMode, FsNode, Stat, S_IFREG},\n    userland::buffer::{UserBuffer, UserBufferMut, UserBufferReader, UserBufferWriter},\n    util::{lock::IrqMutex, KResult},\n};\n\npub struct InitRamFsFile {\n    pub name: IrqMutex<String>,\n    pub(super) data: IrqMutex<Vec<u8>>,\n    pub(super) stat: IrqMutex<Stat>,\n}\n\nimpl InitRamFsFile {\n    pub fn new(name: String, inode_no: usize) -> InitRamFsFile {\n        InitRamFsFile {\n            name: IrqMutex::new(name),\n            data: IrqMutex::new(Vec::new()),\n            stat: IrqMutex::new(Stat {\n                inode_no,\n                mode: FileMode::new(S_IFREG | 0o644),\n                ..Stat::zeroed()\n            }),\n        }\n    }\n}\n\nimpl FsNode for InitRamFsFile {\n    fn get_name(&self) -> String {\n        self.name.lock().clone()\n    }\n}\n\nimpl File for InitRamFsFile {\n    fn read(&self, offset: usize, buf: UserBufferMut<'_>, _options: &OpenFlags) -> KResult<usize> {\n        let lock = self.data.lock();\n        if offset > lock.len() {\n            return Ok(0);\n        }\n        let mut writer = UserBufferWriter::from_buf(buf);\n        writer.write_bytes(&lock[offset..])\n    }\n\n    fn write(&self, offset: usize, buf: UserBuffer<'_>, options: &OpenFlags) -> KResult<usize> {\n        let mut reader = UserBufferReader::from_buf(buf);\n        let mut data = self.data.lock();\n        let data_len = data.len();\n        if data.is_empty() {\n            data.extend_from_slice(&vec![0u8; offset + reader.remaining_len()]);\n        } else if offset + reader.remaining_len() < isize::MAX as usize\n            && (offset + reader.remaining_len() > data_len || options.contains(OpenFlags::O_APPEND))\n        {\n            data.push(0u8);\n        }\n\n        reader.read_bytes(&mut data[offset..])\n    }\n\n    fn stat(&self) -> KResult<Stat> {\n        Ok(*self.stat.lock())\n    }\n}\n"
  },
  {
    "path": "src/fs/initramfs/mod.rs",
    "content": "use core::iter::Peekable;\n\nuse alloc::{\n    string::{String, ToString},\n    sync::{Arc, Weak},\n    vec::Vec,\n};\nuse spin::Once;\n\nuse crate::{\n    fs::{\n        DirRef, FileMode, FileSize, FsNode, INode, Stat,\n        initramfs::{\n            dir::{DirInner, InitRamFsDir},\n            file::InitRamFsFile,\n            symlink::InitRamFsSymlink,\n        },\n        path::{Components, Path, PathBuf},\n    },\n    kbail, kerror,\n    util::{IrqMutex, KResult, align_up},\n};\n\nuse self::root::RootFs;\n\npub mod dir;\npub mod file;\npub mod root;\npub mod symlink;\n\npub struct ByteParser<'a> {\n    buffer: &'a [u8],\n    current: usize,\n}\n\nimpl<'a> ByteParser<'a> {\n    pub fn new(buffer: &'a [u8]) -> ByteParser<'a> {\n        ByteParser { buffer, current: 0 }\n    }\n\n    pub fn remaining(&self) -> &[u8] {\n        &self.buffer[self.current..]\n    }\n    pub fn remaining_len(&self) -> usize {\n        self.buffer.len() - self.current\n    }\n\n    pub fn skip(&mut self, len: usize) -> KResult<()> {\n        if self.current + len > self.buffer.len() {\n            kbail!(\"skip(): out of bounds\");\n        }\n\n        self.current += len;\n        Ok(())\n    }\n\n    pub fn skip_until_alignment(&mut self, align: usize) -> KResult<()> {\n        let next = align_up(self.current, align);\n        if next > self.buffer.len() {\n            kbail!(\"skip_until_alignment(): out of bounds\");\n        }\n\n        self.current = next;\n        Ok(())\n    }\n\n    pub fn consume_bytes(&mut self, len: usize) -> KResult<&'a [u8]> {\n        if self.current + len > self.buffer.len() {\n            kbail!(\"consume_bytes(): out of bounds\");\n        }\n\n        self.current += len;\n        Ok(&self.buffer[self.current - len..self.current])\n    }\n}\n\nfn parse_str_field(bytes: &[u8]) -> KResult<&str> {\n    core::str::from_utf8(bytes).map_err(|_e| kerror!(\"parse_str_field(): UTF-8 parsing error\"))\n}\n\nfn parse_hex_field(bytes: &[u8]) -> KResult<usize> {\n    usize::from_str_radix(parse_str_field(bytes)?, 16)\n        .map_err(|_e| kerror!(\"parse_hex_field(): int parsing error\"))\n}\n\npub static INITRAM_FS: Once<Arc<InitRamFs>> = Once::new();\n\npub fn init() -> KResult<()> {\n    INITRAM_FS.call_once(|| {\n        let image = include_bytes!(concat!(\n            env!(\"CARGO_MANIFEST_DIR\"),\n            \"/initramfs/initramfs.img\"\n        ));\n        if image.is_empty() {\n            panic!(\"initramfs not embedded\");\n        }\n\n        log::info!(\"Parsing initramfs...\");\n        Arc::new(InitRamFs::parse(image.as_slice()).expect(\"error parsing initramfs\"))\n    });\n    Ok(())\n}\n\npub fn get_root() -> Option<&'static RootFs> {\n    Some(&INITRAM_FS.get()?.root)\n}\n\npub struct InitRamFs {\n    root: RootFs,\n}\n\nimpl InitRamFs {\n    pub fn parse(fs_image: &[u8]) -> KResult<InitRamFs> {\n        let mut image = ByteParser::new(fs_image);\n        let mut n_files = 0;\n        let mut loaded_size = 0;\n        let root = Arc::new(InitRamFsDir::new(String::new(), 2));\n        loop {\n            if image.remaining_len() == 0 {\n                break;\n            }\n            let magic = image.consume_bytes(6).and_then(parse_hex_field)?;\n            if magic != 0x070701 {\n                log::error!(\n                    \"initramfs: invalid magic (expected {:#x}, got {:#x})\",\n                    0x070701,\n                    magic\n                );\n                kbail!(\"parse(): invalid magic\");\n            }\n\n            let ino = parse_hex_field(image.consume_bytes(8)?)?;\n            let mode = FileMode::new(parse_hex_field(image.consume_bytes(8)?)? as u32);\n            let _uid = parse_hex_field(image.consume_bytes(8)?)?;\n            let _gid = parse_hex_field(image.consume_bytes(8)?)?;\n            let _nlink = parse_hex_field(image.consume_bytes(8)?)?;\n            let _mtime = parse_hex_field(image.consume_bytes(8)?)?;\n            let filesize = parse_hex_field(image.consume_bytes(8)?)?;\n            let _dev_major = parse_hex_field(image.consume_bytes(8)?)?;\n            let _dev_minor = parse_hex_field(image.consume_bytes(8)?)?;\n\n            image.skip(16)?;\n\n            let path_len = parse_hex_field(image.consume_bytes(8)?)?;\n            if path_len == 0 {\n                kbail!(\"parse(): path_len is 0\");\n            }\n\n            image.skip(8)?;\n\n            let mut path = parse_str_field(image.consume_bytes(path_len - 1)?)?;\n\n            if path.starts_with(\"./\") {\n                path = &path[1..];\n            }\n            if path == \"TRAILER!!!\" {\n                break;\n            }\n\n            if path.is_empty() {\n                kbail!(\"parse(): empty path\");\n            }\n            image.skip(1)?;\n            image.skip_until_alignment(4)?;\n\n            let components = Path::new(path).components().peekable();\n\n            fn walk(\n                mut components_peekable: Peekable<Components>,\n                dir: DirRef,\n            ) -> Option<(DirRef, String)> {\n                let next = components_peekable.next();\n                next?;\n                if components_peekable.peek().is_none() {\n                    return Some((dir, next.unwrap().to_string()));\n                }\n\n                let dir_clone = dir.clone();\n                if let Ok(child) = dir.lookup(next.unwrap()) {\n                    if let INode::Dir(next_dir) = child {\n                        return walk(components_peekable, next_dir.clone());\n                    } else {\n                        return Some((dir_clone, child.get_name()));\n                    }\n                }\n                None\n            }\n\n            if path == \".\" {\n                image.skip_until_alignment(4)?;\n                continue;\n            }\n\n            let walk_result = walk(components, root.clone());\n            let (parent_dir, filename) = if let Some((parent, name)) = walk_result {\n                (parent, name)\n            } else {\n                image.consume_bytes(filesize)?;\n                image.skip_until_alignment(4)?;\n                continue;\n            };\n\n            let data = image.consume_bytes(filesize)?;\n            if mode.is_symbolic_link() {\n                let inode = INode::Symlink(Arc::new(InitRamFsSymlink {\n                    name: filename.clone(),\n                    dst: PathBuf::from(core::str::from_utf8(data).unwrap()),\n                    stat: Stat {\n                        inode_no: ino,\n                        mode,\n                        ..Stat::zeroed()\n                    },\n                }));\n                parent_dir.insert(inode);\n            } else if mode.is_directory() {\n                let inode = INode::Dir(Arc::new(InitRamFsDir {\n                    parent: Weak::new(),\n                    inner: IrqMutex::new(DirInner {\n                        children: Vec::new(),\n                        stat: Stat {\n                            inode_no: ino,\n                            mode,\n                            ..Stat::zeroed()\n                        },\n                        name: filename.clone(),\n                    }),\n                }));\n                parent_dir.insert(inode);\n            } else if mode.is_regular_file() {\n                let file = InitRamFsFile {\n                    name: IrqMutex::new(filename.clone()),\n                    data: IrqMutex::new(data.to_vec()),\n                    stat: IrqMutex::new(Stat {\n                        inode_no: ino,\n                        mode,\n                        size: FileSize(filesize as isize),\n                        ..Stat::zeroed()\n                    }),\n                };\n                parent_dir.insert(INode::File(Arc::new(file)));\n            }\n\n            image.skip_until_alignment(4)?;\n            n_files += 1;\n            loaded_size += data.len();\n        }\n\n        log::info!(\n            \"initramfs: found {} files taking up {} bytes\",\n            n_files,\n            loaded_size\n        );\n\n        Ok(InitRamFs {\n            root: RootFs::new(root),\n        })\n    }\n}\n"
  },
  {
    "path": "src/fs/initramfs/root.rs",
    "content": "use alloc::{borrow::ToOwned, boxed::Box, string::String, sync::Arc};\n\nuse crate::{\n    fs::{\n        path::{Path, PathComponent},\n        pipe::PIPE_FS,\n        DirRef, INode,\n    },\n    kbail,\n    util::KResult,\n};\n\nuse super::dir::InitRamFsDir;\n\nconst MAX_SYMLINK_FOLLOW_DEPTH: usize = 20;\n\n#[derive(Clone)]\npub struct RootFs {\n    root_path: PathComponent,\n    cwd_path: PathComponent,\n}\n\nimpl RootFs {\n    pub fn new(root: Arc<InitRamFsDir>) -> RootFs {\n        let root_path = PathComponent {\n            parent_dir: None,\n            name: Arc::new(String::new()),\n            inode: INode::Dir(root),\n        };\n        RootFs {\n            root_path: root_path.clone(),\n            cwd_path: root_path,\n        }\n    }\n\n    pub fn cwd_path(&self) -> &PathComponent {\n        &self.cwd_path\n    }\n\n    pub fn root_dir(&self) -> DirRef {\n        self.root_path.inode.as_dir().unwrap().clone()\n    }\n\n    pub fn cwd_dir(&self) -> DirRef {\n        self.cwd_path.inode.as_dir().unwrap().clone()\n    }\n\n    pub fn chdir(&mut self, path: &Path) -> KResult<()> {\n        self.cwd_path = self.lookup_path(path, true)?;\n        Ok(())\n    }\n\n    pub fn lookup(&self, path: &Path, follow_symlinks: bool) -> KResult<INode> {\n        if path.is_pipe() {\n            return PIPE_FS.lookup(path).map(INode::Pipe);\n        }\n        self.lookup_path(path, follow_symlinks)\n            .map(|cmp| cmp.inode.clone())\n    }\n\n    pub fn lookup_path(&self, path: &Path, follow_symlinks: bool) -> KResult<PathComponent> {\n        if path.is_empty() {\n            kbail!(ENOENT, \"lookup_path(): empty path\");\n        }\n\n        let lookup_from = if path.is_absolute() {\n            self.root_path.clone()\n        } else {\n            self.cwd_path.clone()\n        };\n\n        self.do_lookup_path(\n            &lookup_from,\n            path,\n            follow_symlinks,\n            MAX_SYMLINK_FOLLOW_DEPTH,\n        )\n    }\n\n    fn do_lookup_path(\n        &self,\n        lookup_from: &PathComponent,\n        path: &Path,\n        follow_symlinks: bool,\n        symlink_follow_limit: usize,\n    ) -> KResult<PathComponent> {\n        let mut parent = lookup_from.clone();\n        let mut components = path.components().peekable();\n        while let Some(name) = components.next() {\n            let path_comp = match name {\n                \".\" => continue,\n                \"..\" => parent\n                    .parent_dir\n                    .as_deref()\n                    .unwrap_or(&self.root_path)\n                    .clone(),\n                _ => {\n                    let inode = parent.inode.as_dir()?.lookup(name)?;\n                    PathComponent {\n                        parent_dir: Some(Box::new(parent.clone())),\n                        name: Arc::new(name.to_owned()),\n                        inode,\n                    }\n                }\n            };\n\n            if components.peek().is_some() {\n                parent = match &path_comp.inode {\n                    INode::Dir(_) => path_comp,\n                    INode::Pipe(_) => {\n                        unreachable!(\"Pipes should be contained in PipeFs, not RootFs\")\n                    }\n                    INode::Symlink(link) if follow_symlinks => {\n                        if symlink_follow_limit == 0 {\n                            kbail!(ELOOP, \"lookup_path(): maximum symlink depth reached\");\n                        }\n                        let dst = link.link_location()?;\n                        let follow_from = if dst.is_absolute() {\n                            &self.root_path\n                        } else {\n                            &parent\n                        };\n\n                        let dst_path = self.do_lookup_path(\n                            follow_from,\n                            &dst,\n                            follow_symlinks,\n                            symlink_follow_limit - 1,\n                        )?;\n\n                        match dst_path.inode {\n                            INode::Dir(_) => dst_path,\n                            _ => {\n                                kbail!(ENOTDIR, \"lookup_path(): not a directory\");\n                            }\n                        }\n                    }\n                    INode::Symlink(_) => {\n                        kbail!(ENOTDIR, \"lookup_path(): not a directory\");\n                    }\n                    INode::File(_) => {\n                        kbail!(ENOTDIR, \"lookup_path(): not a directory\");\n                    }\n                }\n            } else {\n                match &path_comp.inode {\n                    INode::Symlink(link) if follow_symlinks => {\n                        if symlink_follow_limit == 0 {\n                            kbail!(ELOOP, \"lookup_path(): maximum symlink depth reached\");\n                        }\n                        let dst = link.link_location()?;\n                        let follow_from = if dst.is_absolute() {\n                            &self.root_path\n                        } else {\n                            &parent\n                        };\n\n                        return self.do_lookup_path(\n                            follow_from,\n                            &dst,\n                            follow_symlinks,\n                            symlink_follow_limit - 1,\n                        );\n                    }\n                    _ => return Ok(path_comp),\n                }\n            }\n        }\n\n        Ok(parent)\n    }\n}\n"
  },
  {
    "path": "src/fs/initramfs/symlink.rs",
    "content": "use alloc::string::String;\n\nuse crate::{\n    fs::{path::PathBuf, FsNode, Stat, Symlink},\n    util::KResult,\n};\n\npub struct InitRamFsSymlink {\n    pub(crate) name: String,\n    pub(crate) dst: PathBuf,\n    pub(crate) stat: Stat,\n}\n\nimpl Symlink for InitRamFsSymlink {\n    fn link_location(&self) -> KResult<PathBuf> {\n        Ok(self.dst.clone())\n    }\n\n    fn stat(&self) -> KResult<Stat> {\n        Ok(self.stat)\n    }\n}\n\nimpl FsNode for InitRamFsSymlink {\n    fn get_name(&self) -> String {\n        self.name.clone()\n    }\n}\n"
  },
  {
    "path": "src/fs/mod.rs",
    "content": "use core::sync::atomic::{AtomicUsize, Ordering};\n\nuse alloc::{string::String, sync::Arc};\nuse bitflags::bitflags;\n\nuse crate::{\n    kerror,\n    task::wait_queue::WaitQueue,\n    userland::buffer::{UserBuffer, UserBufferMut},\n    util::{ctypes::c_short, KResult},\n};\n\nuse self::{opened_file::OpenFlags, path::PathBuf, pipe::Pipe};\n\npub mod devfs;\npub mod initramfs;\npub mod opened_file;\npub mod path;\npub mod pipe;\n\npub type FileRef = Arc<dyn File + Send + Sync>;\npub type DirRef = Arc<dyn Directory + Send + Sync>;\npub type SymlinkRef = Arc<dyn Symlink + Send + Sync>;\n\npub static POLL_WAIT_QUEUE: WaitQueue = WaitQueue::new();\n\npub fn alloc_inode_no() -> usize {\n    // Inode #1 is reserved for the root dir.\n    static NEXT_INODE_NO: AtomicUsize = AtomicUsize::new(2);\n\n    NEXT_INODE_NO.fetch_add(1, Ordering::AcqRel)\n}\n\nbitflags! {\n    #[derive(Debug)]\n    pub struct PollStatus: c_short {\n        const POLLIN     = 0x001;\n        const POLLPRI    = 0x002;\n        const POLLOUT    = 0x004;\n        const POLLERR    = 0x008;\n        const POLLHUP    = 0x010;\n        const POLLNVAL   = 0x020;\n        const POLLRDNORM = 0x040;\n        const POLLRDBAND = 0x080;\n        const POLLWRNORM = 0x100;\n        const POLLWRBAND = 0x200;\n    }\n}\n\n#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[repr(u8)]\n#[non_exhaustive]\npub enum FileType {\n    Directory = 4,\n    Regular = 8,\n    Link = 10,\n}\n\n/// for readdir(3)\npub struct DirEntry {\n    pub inode_no: usize,\n    pub file_type: FileType,\n    pub name: String,\n}\n\n/// The device file's ID.\n#[derive(Debug, Copy, Clone)]\n#[repr(transparent)]\npub struct DevId(usize);\n\n/// The number of hard links.\n#[derive(Debug, Copy, Clone)]\n#[repr(transparent)]\npub struct NLink(usize);\n\n/// The file size in bytes.\n#[derive(Debug, Copy, Clone)]\n#[repr(transparent)]\npub struct FileSize(pub isize);\n\n/// The user ID.\n#[derive(Debug, Copy, Clone)]\n#[repr(transparent)]\npub struct UId(u32);\n\n/// The Group ID.\n#[derive(Debug, Copy, Clone)]\n#[repr(transparent)]\npub struct GId(u32);\n\n/// The size in bytes of a block file file system I/O operations.\n#[derive(Debug, Copy, Clone)]\n#[repr(transparent)]\npub struct BlockSize(isize);\n\n/// The number of blocks.\n#[derive(Debug, Copy, Clone)]\n#[repr(transparent)]\npub struct BlockCount(isize);\n\n#[derive(Debug, Copy, Clone)]\n#[repr(transparent)]\npub struct Time(isize);\n\n#[derive(Debug, Copy, Clone)]\n#[repr(C, packed)]\npub struct Stat {\n    pub dev: DevId,\n    pub inode_no: usize,\n    pub nlink: NLink,\n    pub mode: FileMode,\n    pub uid: UId,\n    pub gid: GId,\n    pub pad0: u32,\n    pub rdev: DevId,\n    pub size: FileSize,\n    pub blksize: BlockSize,\n    pub blocks: BlockCount,\n    pub atime: Time,\n    pub mtime: Time,\n    pub ctime: Time,\n}\n\nimpl Stat {\n    pub fn zeroed() -> Stat {\n        Stat {\n            dev: DevId(0),\n            inode_no: 0,\n            mode: FileMode(0),\n            nlink: NLink(0),\n            uid: UId(0),\n            gid: GId(0),\n            pad0: 0,\n            rdev: DevId(0),\n            size: FileSize(0),\n            blksize: BlockSize(0),\n            blocks: BlockCount(0),\n            atime: Time(0),\n            mtime: Time(0),\n            ctime: Time(0),\n        }\n    }\n}\n\npub const S_IFMT: u32 = 0o170000;\npub const S_IFCHR: u32 = 0o020000;\npub const S_IFDIR: u32 = 0o040000;\npub const S_IFREG: u32 = 0o100000;\npub const S_IFLNK: u32 = 0o120000;\n\npub const O_ACCMODE: u32 = 0o3;\n\n// FIXME: OpenFlags also define these values.\n#[allow(unused)]\npub const O_RDONLY: u32 = 0o0;\npub const O_WRONLY: u32 = 0o1;\npub const O_RDWR: u32 = 0o2;\n\n#[derive(Debug, Clone, Copy)]\n#[repr(transparent)]\npub struct FileMode(u32);\n\nimpl FileMode {\n    pub fn new(val: u32) -> FileMode {\n        FileMode(val)\n    }\n\n    pub fn access_mode(self) -> u32 {\n        self.0 & O_ACCMODE\n    }\n\n    pub fn is_directory(self) -> bool {\n        (self.0 & S_IFMT) == S_IFDIR\n    }\n\n    pub fn is_regular_file(self) -> bool {\n        (self.0 & S_IFMT) == S_IFREG\n    }\n\n    pub fn is_symbolic_link(self) -> bool {\n        (self.0 & S_IFMT) == S_IFLNK\n    }\n}\n\npub trait FsNode {\n    fn get_name(&self) -> String;\n}\n\npub trait File: FsNode {\n    /// `open(2)`.\n    fn open(&self, _options: &OpenFlags) -> KResult<Option<FileRef>> {\n        Ok(None)\n    }\n\n    /// `stat(2)`.\n    fn stat(&self) -> KResult<Stat> {\n        Err(kerror!(EBADF, \"stat(): not implemented\"))\n    }\n\n    /// `readlink(2)`.\n    fn readlink(&self) -> KResult<PathBuf> {\n        // \"EINVAL - The named file is not a symbolic link.\" -- readlink(2)\n        Err(kerror!(EINVAL, \"readlink(): not a symbolic link\"))\n    }\n\n    /// `poll(2)` and `select(2)`.\n    fn poll(&self) -> KResult<PollStatus> {\n        Err(kerror!(EBADF, \"poll(): not implemented\"))\n    }\n\n    /// `ioctl(2)`.\n    fn ioctl(&self, _cmd: usize, _arg: usize) -> KResult<isize> {\n        Err(kerror!(EBADF, \"ioctl(): not implemented\"))\n    }\n\n    /// `read(2)`.\n    fn read(&self, _offset: usize, _buf: UserBufferMut, _options: &OpenFlags) -> KResult<usize> {\n        Err(kerror!(EBADF, \"read(): not implemented\"))\n    }\n\n    /// `write(2)`.\n    fn write(&self, _offset: usize, _buf: UserBuffer<'_>, _options: &OpenFlags) -> KResult<usize> {\n        Err(kerror!(EBADF, \"write(): not implemented\"))\n    }\n}\n\npub trait Symlink: FsNode {\n    fn link_location(&self) -> KResult<PathBuf>;\n    fn stat(&self) -> KResult<Stat>;\n    fn fsync(&self) -> KResult<()> {\n        Ok(())\n    }\n}\n\npub trait Directory: FsNode {\n    fn insert(&self, inode: INode);\n\n    /// Looks for an existing file.\n    fn lookup(&self, name: &str) -> KResult<INode>;\n    /// `stat(2)`.\n    fn stat(&self) -> KResult<Stat>;\n    /// `fsync(2)`.\n    fn fsync(&self) -> KResult<()> {\n        Ok(())\n    }\n    /// `readlink(2)`.\n    fn readlink(&self) -> KResult<PathBuf> {\n        // \"EINVAL - The named file is not a symbolic link.\" -- readlink(2)\n        Err(kerror!(EINVAL, \"readlink(): not a symbolic link\"))\n    }\n\n    fn readdir(&self, index: usize) -> KResult<Option<DirEntry>>;\n\n    fn unlink(&self, name: &str) -> KResult<()>;\n}\n\n#[derive(Clone)]\npub enum INode {\n    File(FileRef),\n    Dir(DirRef),\n    Symlink(SymlinkRef),\n    Pipe(Arc<Pipe>),\n}\n\nimpl INode {\n    pub fn is_file(&self) -> bool {\n        matches!(self, INode::File(_))\n    }\n\n    pub fn is_dir(&self) -> bool {\n        matches!(self, INode::Dir(_))\n    }\n\n    pub fn is_symlink(&self) -> bool {\n        matches!(self, INode::Symlink(_))\n    }\n\n    pub fn is_pipe(&self) -> bool {\n        matches!(self, INode::Pipe(_))\n    }\n\n    pub fn as_file(&self) -> KResult<&FileRef> {\n        match self {\n            INode::File(file) => Ok(file),\n            _ => Err(kerror!(EINVAL, \"as_file(): not a file\")),\n        }\n    }\n\n    pub fn stat(&self) -> KResult<Stat> {\n        match self {\n            INode::Dir(d) => d.stat(),\n            INode::File(d) => d.stat(),\n            INode::Symlink(d) => d.stat(),\n            INode::Pipe(p) => p.stat(),\n        }\n    }\n\n    pub fn as_dir(&self) -> KResult<&DirRef> {\n        match self {\n            INode::Dir(d) => Ok(d),\n            _ => Err(kerror!(ENOTDIR, \"as_dir(): not a directory\")),\n        }\n    }\n\n    pub fn as_symlink(&self) -> KResult<&SymlinkRef> {\n        match self {\n            INode::Symlink(s) => Ok(s),\n            _ => Err(kerror!(EINVAL, \"as_symlink(): not a symbolic link\")),\n        }\n    }\n\n    pub fn as_pipe(&self) -> KResult<&Arc<Pipe>> {\n        match self {\n            INode::Pipe(p) => Ok(p),\n            _ => Err(kerror!(EINVAL, \"as_pipe(): not a pipe\")),\n        }\n    }\n}\n\nimpl FsNode for INode {\n    fn get_name(&self) -> String {\n        match self {\n            INode::Dir(d) => d.get_name(),\n            INode::File(f) => f.get_name(),\n            INode::Symlink(l) => l.get_name(),\n            INode::Pipe(p) => p.get_name(),\n        }\n    }\n}\n"
  },
  {
    "path": "src/fs/opened_file.rs",
    "content": "use core::{borrow::BorrowMut, ops::Deref};\n\nuse alloc::{sync::Arc, vec::Vec};\nuse atomic_refcell::AtomicRefCell;\nuse bitflags::bitflags;\nuse crossbeam_utils::atomic::AtomicCell;\n\nuse crate::{\n    kerror,\n    userland::buffer::{UserBuffer, UserBufferMut},\n    util::{ctypes::c_int, error::KResult},\n};\n\nuse super::{\n    devfs::socket::Socket,\n    path::PathComponent,\n    pipe::{Pipe, PIPE_FS},\n    DirEntry, DirRef, FileRef, FsNode, INode, PollStatus,\n};\n\nconst FD_MAX: c_int = 1024;\n\nbitflags! {\n    #[derive(Clone, Copy, Debug)]\n    pub struct OpenFlags: i32 {\n        const O_RDONLY    = 0o0;\n        const O_WRONLY    = 0o1;\n        const O_RDWR      = 0o2;\n        const O_CREAT     = 0o0100;\n        const O_EXCL      = 0o0200;\n        const O_NOCTTY    = 0o0400;\n        const O_TRUNC     = 0o01000;\n        const O_APPEND    = 0o02000;\n        const O_NONBLOCK  = 0o04000;\n        const O_DSYNC     = 0o010000;\n        const O_SYNC      = 0o04010000;\n        const O_RSYNC     = 0o04010000;\n        const O_DIRECTORY = 0o0200000;\n        const O_NOFOLLOW  = 0o0400000;\n        const O_CLOEXEC   = 0o02000000;\n        const O_ASYNC     = 0o020000;\n        const O_DIRECT    = 0o040000;\n        const O_LARGEFILE = 0o0100000;\n        const O_NOATIME   = 0o01000000;\n        const O_PATH      = 0o010000000;\n        const O_TMPFILE   = 0o020200000;\n    }\n}\n\npub type FileDesc = c_int;\n\n#[repr(C)]\npub struct OpenedFile {\n    path: PathComponent,\n    pos: AtomicCell<usize>,\n    options: AtomicRefCell<OpenFlags>,\n}\n\nimpl OpenedFile {\n    pub fn new(path: PathComponent, options: OpenFlags, pos: usize) -> OpenedFile {\n        OpenedFile {\n            path,\n            pos: AtomicCell::new(pos),\n            options: AtomicRefCell::new(options),\n        }\n    }\n\n    pub fn as_file(&self) -> KResult<&FileRef> {\n        self.path.inode.as_file()\n    }\n\n    pub fn as_dir(&self) -> KResult<&DirRef> {\n        self.path.inode.as_dir()\n    }\n\n    pub fn pos(&self) -> usize {\n        self.pos.load()\n    }\n\n    pub fn options(&self) -> OpenFlags {\n        *self.options.borrow()\n    }\n\n    pub fn path(&self) -> &PathComponent {\n        &self.path\n    }\n\n    pub fn inode(&self) -> &INode {\n        &self.path.inode\n    }\n\n    pub fn read(&self, buf: UserBufferMut) -> KResult<usize> {\n        let options = self.options();\n        let pos = self.pos();\n        let read_len = self.as_file()?.read(pos, buf, &options)?;\n        self.pos.fetch_add(read_len);\n        Ok(read_len)\n    }\n\n    pub fn write(&self, buf: UserBuffer) -> KResult<usize> {\n        let options = self.options();\n        let pos = self.pos();\n        let written_len = self.as_file()?.write(pos, buf, &options)?;\n        self.pos.fetch_add(written_len);\n        Ok(written_len)\n    }\n\n    pub fn set_close_on_exec(&self, close_on_exec: bool) {\n        self.options()\n            .borrow_mut()\n            .set(OpenFlags::O_CLOEXEC, close_on_exec);\n    }\n\n    pub fn set_flags(&self, flags: OpenFlags) -> KResult<()> {\n        *self.options.borrow_mut() = flags;\n\n        Ok(())\n    }\n\n    pub fn get_flags(&self) -> OpenFlags {\n        *self.options.borrow()\n    }\n\n    pub fn poll(&self) -> KResult<PollStatus> {\n        self.as_file()?.poll()\n    }\n\n    pub fn ioctl(&self, cmd: usize, arg: usize) -> KResult<isize> {\n        self.as_file()?.ioctl(cmd, arg)\n    }\n\n    pub fn readdir(&self) -> KResult<Option<DirEntry>> {\n        let pos = self.pos();\n\n        let entry = self.as_dir()?.readdir(pos)?;\n        self.pos.fetch_add(1);\n        Ok(entry)\n    }\n\n    pub fn lseek(&self, offset: usize, whence: LseekWhence) -> KResult<usize> {\n        // We perform this check before we know we need something from the inode\n        // because this should only be called on files anyway.\n        let file = self.inode().as_file()?;\n        match whence {\n            LseekWhence::Set => self.pos.store(offset),\n            LseekWhence::Cur => _ = self.pos.fetch_add(offset),\n            LseekWhence::End => self.pos.store(file.stat()?.size.0 as usize - offset),\n        };\n        Ok(self.pos())\n    }\n}\n\n#[repr(usize)]\npub enum LseekWhence {\n    Set = 0,\n    Cur = 1,\n    End = 2,\n}\n\nimpl From<usize> for LseekWhence {\n    fn from(value: usize) -> Self {\n        match value {\n            0 => LseekWhence::Set,\n            1 => LseekWhence::Cur,\n            2 => LseekWhence::End,\n            _ => panic!(\"Invalid LseekWhence\"),\n        }\n    }\n}\n\n#[derive(Clone)]\npub struct LocalOpenedFile {\n    opened_file: Arc<OpenedFile>,\n}\n\nimpl Deref for LocalOpenedFile {\n    type Target = OpenedFile;\n\n    fn deref(&self) -> &Self::Target {\n        &self.opened_file\n    }\n}\n\n#[derive(Clone)]\npub struct OpenedFileTable {\n    files: Vec<Option<LocalOpenedFile>>,\n    prev_fd: i32,\n}\n\nimpl Default for OpenedFileTable {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl OpenedFileTable {\n    pub fn new() -> OpenedFileTable {\n        OpenedFileTable {\n            files: Vec::new(),\n            prev_fd: 1,\n        }\n    }\n\n    pub fn get(&self, fd: FileDesc) -> KResult<LocalOpenedFile> {\n        match self.files.get(fd as usize) {\n            Some(Some(file)) => Ok(file.clone()),\n            _ => Err(kerror!(EBADF, \"get(): file not opened\")),\n        }\n    }\n\n    pub fn open(&mut self, path: PathComponent, options: OpenFlags) -> KResult<FileDesc> {\n        let fd = self.alloc_fd(None)?;\n        self.open_with_fd(fd, OpenedFile::new(path, options, 0).into(), options)?;\n        Ok(fd)\n    }\n\n    pub fn open_with_fd(\n        &mut self,\n        fd: FileDesc,\n        mut opened_file: Arc<OpenedFile>,\n        options: OpenFlags,\n    ) -> KResult<()> {\n        if let INode::File(file) = &opened_file.path.inode {\n            if let Some(new_inode) = file.open(&options)? {\n                opened_file = Arc::new(OpenedFile::new(\n                    PathComponent {\n                        parent_dir: opened_file.path.parent_dir.clone(),\n                        name: opened_file.path.name.clone(),\n                        inode: INode::File(new_inode),\n                    },\n                    options,\n                    0,\n                ));\n            }\n        }\n\n        match self.files.get_mut(fd as usize) {\n            Some(Some(_)) => return Err(kerror!(EBADF, \"open_with_fd(): file already opened\")),\n            Some(entry @ None) => {\n                *entry = Some(LocalOpenedFile { opened_file });\n            }\n            None if fd >= FD_MAX => {\n                return Err(kerror!(\n                    ENFILE,\n                    \"open_with_fd(): maximum file descriptor reached\"\n                ))\n            }\n            None => {\n                self.files.resize(fd as usize + 1, None);\n                self.files[fd as usize] = Some(LocalOpenedFile { opened_file })\n            }\n        }\n\n        Ok(())\n    }\n\n    fn alloc_fd(&mut self, gte: Option<i32>) -> KResult<FileDesc> {\n        let (mut i, gte) = match gte {\n            Some(gte) => (gte, gte),\n            None => ((self.prev_fd + 1) % FD_MAX, 0),\n        };\n\n        while i != self.prev_fd && i >= gte {\n            if matches!(self.files.get(i as usize), Some(None) | None) {\n                self.prev_fd = i;\n                return Ok(i);\n            }\n\n            i = (i + 1) % FD_MAX;\n        }\n\n        Err(kerror!(ENFILE, \"alloc_fd(): cannot alloc file descriptor\"))\n    }\n\n    pub fn close_all(&mut self) {\n        self.files.clear()\n    }\n\n    pub fn close_cloexec_files(&mut self) {\n        for opened_file in &mut self.files {\n            if matches!(\n                opened_file,\n                Some(LocalOpenedFile {\n                    // close_on_exec: true,\n                    // opened_file\n                    ..\n                })\n            ) {\n                let cloexec = opened_file\n                    .as_ref()\n                    .unwrap()\n                    .opened_file\n                    .options()\n                    .contains(OpenFlags::O_CLOEXEC);\n                if cloexec {\n                    *opened_file = None;\n                }\n            }\n        }\n    }\n\n    pub fn close(&mut self, fd: FileDesc) -> KResult<()> {\n        match self.files.get_mut(fd as usize) {\n            Some(opened_file) => *opened_file = None,\n            _ => return Err(kerror!(EBADF, \"close(): file not opened\")),\n        }\n        Ok(())\n    }\n\n    pub fn dup(&mut self, fd: FileDesc, gte: Option<i32>, options: OpenFlags) -> KResult<FileDesc> {\n        let file = self.get(fd)?;\n        let new_fd = self.alloc_fd(gte)?;\n        self.open_with_fd(new_fd, file.opened_file, options)?;\n\n        Ok(new_fd)\n    }\n\n    pub fn dup2(&mut self, old_fd: FileDesc, new_fd: FileDesc) -> KResult<FileDesc> {\n        let old_file = self.get(old_fd)?;\n        let options = old_file.options();\n        self.open_with_fd(new_fd, old_file.opened_file, options)?;\n        Ok(new_fd)\n    }\n\n    pub fn open_socket(&mut self, domain: usize, typ: usize, protocol: usize) -> KResult<FileDesc> {\n        let fd = self.alloc_fd(None)?;\n        let socket = Arc::new(Socket {\n            domain: domain.try_into()?,\n            typ: typ.try_into()?,\n            protocol: protocol.try_into()?,\n            id: Socket::alloc_id(),\n        });\n        self.open_with_fd(\n            fd,\n            OpenedFile::new(\n                PathComponent {\n                    parent_dir: None,\n                    name: Arc::new(socket.get_name()),\n                    inode: INode::File(socket.clone()),\n                },\n                OpenFlags::empty(),\n                0,\n            )\n            .into(),\n            OpenFlags::empty(),\n        )?;\n        Ok(fd)\n    }\n\n    pub fn open_pipe(&mut self, options: OpenFlags) -> KResult<Arc<Pipe>> {\n        let write_fd = self.alloc_fd(None)?;\n        let read_fd = self.alloc_fd(Some(write_fd + 1))?;\n        let pipe = Arc::new(Pipe::new(read_fd, write_fd));\n        PIPE_FS.insert(pipe.clone());\n\n        self.files.resize(read_fd as usize + 1, None);\n        self.open_with_fd(\n            write_fd,\n            OpenedFile::new(\n                PathComponent {\n                    parent_dir: None,\n                    name: Arc::new(pipe.get_name()),\n                    inode: INode::Pipe(pipe.clone()),\n                },\n                options,\n                0,\n            )\n            .into(),\n            options,\n        )?;\n        self.open_with_fd(\n            read_fd,\n            OpenedFile::new(\n                PathComponent {\n                    parent_dir: None,\n                    name: Arc::new(pipe.get_name()),\n                    inode: INode::Pipe(pipe.clone()),\n                },\n                options,\n                0,\n            )\n            .into(),\n            options,\n        )?;\n\n        Ok(pipe)\n    }\n}\n"
  },
  {
    "path": "src/fs/path.rs",
    "content": "use alloc::{borrow::ToOwned, boxed::Box, string::String, sync::Arc};\n\nuse super::INode;\n\n#[derive(Debug, Eq, PartialEq, Hash)]\npub struct Path {\n    path: str,\n}\n\nimpl Path {\n    pub fn new(path: &str) -> &Path {\n        let path = if path == \"/\" {\n            path\n        } else {\n            path.trim_end_matches('/')\n        };\n        unsafe { &*(path as *const str as *const Path) }\n    }\n\n    pub fn as_str(&self) -> &str {\n        &self.path\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.path.is_empty()\n    }\n\n    pub fn is_absolute(&self) -> bool {\n        self.path.starts_with('/')\n            && !self\n                .components()\n                .any(|comp| matches!(comp, \"..\" | \".\" | \"\"))\n    }\n\n    pub fn is_pipe(&self) -> bool {\n        self.path.starts_with(\"pipe:\")\n    }\n\n    pub fn pipe_name(&self) -> Option<&str> {\n        if self.is_pipe() {\n            Some(&self.path[5..])\n        } else {\n            None\n        }\n    }\n\n    pub fn components(&self) -> Components<'_> {\n        let path = if self.path.starts_with('/') {\n            &self.path[1..]\n        } else {\n            &self.path\n        };\n\n        Components { path }\n    }\n\n    pub fn parent_and_basename(&self) -> Option<(&Path, &str)> {\n        if &self.path == \"/\" {\n            return None;\n        }\n\n        if let Some(slash_idx) = self.path.rfind('/') {\n            let parent_dir = if slash_idx == 0 {\n                Path::new(\"/\")\n            } else {\n                Path::new(&self.path[..slash_idx])\n            };\n\n            let basename = &self.path[(slash_idx + 1)..];\n            Some((parent_dir, basename))\n        } else {\n            Some((Path::new(\".\"), &self.path))\n        }\n    }\n}\n\nimpl AsRef<Path> for Path {\n    fn as_ref(&self) -> &Path {\n        self\n    }\n}\n\nimpl AsRef<Path> for str {\n    fn as_ref(&self) -> &Path {\n        Path::new(self)\n    }\n}\n\nimpl core::fmt::Display for Path {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"{}\", &self.path)\n    }\n}\n\npub struct Components<'a> {\n    path: &'a str,\n}\n\nimpl<'a> Iterator for Components<'a> {\n    type Item = &'a str;\n    fn next(&mut self) -> Option<Self::Item> {\n        if self.path.is_empty() {\n            return None;\n        }\n\n        let (path_str, next_start) = match self.path.find('/') {\n            Some(slash_idx) => (&self.path[..slash_idx], slash_idx + 1),\n            None => (self.path, self.path.len()),\n        };\n\n        self.path = &self.path[next_start..];\n        Some(path_str)\n    }\n}\n\n#[derive(Debug, Clone, Eq, PartialEq, Hash)]\npub struct PathBuf {\n    path: String,\n}\n\nimpl PathBuf {\n    pub fn new() -> PathBuf {\n        PathBuf {\n            path: String::new(),\n        }\n    }\n\n    pub fn as_path(&self) -> &Path {\n        Path::new(&self.path)\n    }\n\n    pub fn pop(&mut self) {\n        if let Some((index, _)) = self.path.char_indices().rfind(|(_, ch)| *ch == '/') {\n            self.path.truncate(index);\n        }\n    }\n\n    pub fn push<P: AsRef<Path>>(&mut self, path: P) {\n        let path = path.as_ref();\n        let path_str = if path.as_str() == \"/\" {\n            \"/\"\n        } else {\n            path.as_str().trim_end_matches('/')\n        };\n\n        if path.is_absolute() {\n            self.path = path_str.to_owned()\n        } else {\n            if self.path != \"/\" {\n                self.path.push('/');\n            }\n            self.path.push_str(path_str)\n        }\n    }\n}\n\nimpl Default for PathBuf {\n    fn default() -> Self {\n        PathBuf::new()\n    }\n}\n\nimpl core::ops::Deref for PathBuf {\n    type Target = Path;\n    fn deref(&self) -> &Self::Target {\n        self.as_path()\n    }\n}\n\nimpl AsRef<Path> for PathBuf {\n    fn as_ref(&self) -> &Path {\n        self.as_path()\n    }\n}\n\nimpl From<&Path> for PathBuf {\n    fn from(value: &Path) -> Self {\n        PathBuf {\n            path: value.path.to_owned(),\n        }\n    }\n}\n\nimpl From<String> for PathBuf {\n    fn from(value: String) -> Self {\n        // TODO: check if this is a valid path\n        PathBuf { path: value }\n    }\n}\n\nimpl From<&str> for PathBuf {\n    fn from(value: &str) -> Self {\n        // TODO: check if this is a valid path\n        PathBuf {\n            path: value.to_owned(),\n        }\n    }\n}\n\n#[derive(Clone)]\npub struct PathComponent {\n    pub parent_dir: Option<Box<PathComponent>>,\n    pub name: Arc<String>,\n    pub inode: INode,\n}\n\nimpl PathComponent {\n    pub fn resolve_abs_path(&self) -> PathBuf {\n        let path = if self.parent_dir.is_some() {\n            let mut path = self.name.as_ref().to_owned();\n            let mut parent_dir = self.parent_dir.clone();\n            while let Some(ref path_comp) = parent_dir {\n                path = path_comp.name.as_ref().to_owned() + \"/\" + &path;\n                parent_dir = path_comp.parent_dir.clone();\n            }\n\n            debug_assert!(path.starts_with('/'));\n            path\n        } else {\n            \"/\".to_owned()\n        };\n\n        PathBuf::from(path)\n    }\n}\n"
  },
  {
    "path": "src/fs/pipe.rs",
    "content": "use alloc::{format, sync::Arc, vec::Vec};\n\nuse crate::{\n    kerror,\n    task::wait_queue::WaitQueue,\n    userland::buffer::{UserBuffer, UserBufferMut, UserBufferReader, UserBufferWriter},\n    util::{ringbuffer::RingBuffer, IrqMutex, KResult},\n};\n\nuse super::{opened_file::FileDesc, path::Path, File, FsNode};\n\npub static PIPE_FS: PipeFs = PipeFs::new();\n\npub struct Pipe {\n    wait_queue: WaitQueue,\n    ringbuffer: Arc<IrqMutex<RingBuffer<u8, 65536>>>,\n    read_fd: FileDesc,\n    write_fd: FileDesc,\n}\n\nimpl Pipe {\n    pub fn new(read_fd: FileDesc, write_fd: FileDesc) -> Self {\n        Self {\n            wait_queue: WaitQueue::new(),\n            ringbuffer: Arc::new(IrqMutex::new(RingBuffer::new())),\n            read_fd,\n            write_fd,\n        }\n    }\n\n    pub fn read_pipe(&self, buf: UserBufferMut<'_>) -> KResult<usize> {\n        let mut writer = UserBufferWriter::from_buf(buf);\n        let mut ringbuffer = self.wait_queue.sleep_signalable_until(None, || {\n            let ringbuffer = self.ringbuffer.try_lock();\n            if let Ok(ringbuffer) = ringbuffer {\n                if ringbuffer.is_readable() {\n                    Ok(Some(ringbuffer))\n                } else {\n                    Ok(None)\n                }\n            } else {\n                Ok(None)\n            }\n        })?;\n        while let Some(byte) = ringbuffer.pop() {\n            writer.write(byte)?;\n        }\n        Ok(writer.written_len())\n    }\n\n    pub fn write_pipe(&self, buf: UserBuffer<'_>) -> KResult<usize> {\n        let mut reader = UserBufferReader::from_buf(buf);\n        let mut ringbuffer = self.wait_queue.sleep_signalable_until(None, || {\n            let ringbuffer = self.ringbuffer.try_lock();\n            if let Ok(ringbuffer) = ringbuffer {\n                if ringbuffer.is_writable() {\n                    Ok(Some(ringbuffer))\n                } else {\n                    Ok(None)\n                }\n            } else {\n                Ok(None)\n            }\n        })?;\n        while let Ok(byte) = reader.read::<u8>() {\n            ringbuffer.push(byte).ok();\n        }\n        Ok(reader.read_len())\n    }\n\n    pub fn read_fd(&self) -> FileDesc {\n        self.read_fd\n    }\n\n    pub fn write_fd(&self) -> FileDesc {\n        self.write_fd\n    }\n}\n\nimpl FsNode for Pipe {\n    fn get_name(&self) -> alloc::string::String {\n        format!(\"pipe_{}_{}\", self.write_fd, self.read_fd)\n    }\n}\n\nimpl File for Pipe {\n    fn read(\n        &self,\n        _offset: usize,\n        buf: UserBufferMut,\n        _options: &super::opened_file::OpenFlags,\n    ) -> KResult<usize> {\n        self.read_pipe(buf)\n    }\n\n    fn write(\n        &self,\n        _offset: usize,\n        buf: UserBuffer<'_>,\n        _options: &super::opened_file::OpenFlags,\n    ) -> KResult<usize> {\n        self.write_pipe(buf)\n    }\n}\n\npub struct PipeFs {\n    pipes: IrqMutex<Vec<Arc<Pipe>>>,\n}\n\nimpl PipeFs {\n    #[allow(clippy::new_without_default)]\n    pub const fn new() -> Self {\n        Self {\n            pipes: IrqMutex::new(Vec::new()),\n        }\n    }\n\n    pub fn insert(&self, pipe: Arc<Pipe>) {\n        self.pipes.lock().push(pipe);\n    }\n\n    pub fn lookup(&self, path: &Path) -> KResult<Arc<Pipe>> {\n        self.pipes\n            .lock()\n            .iter()\n            .find(|pipe| pipe.get_name() == path.pipe_name().unwrap())\n            .cloned()\n            .ok_or(kerror!(ENOENT, \"pipe does not exist\"))\n    }\n}\n"
  },
  {
    "path": "src/god_mode.rs",
    "content": "use alloc::{borrow::ToOwned, collections::VecDeque, string::String, sync::Arc};\nuse spin::Once;\nuse x86_64::instructions::interrupts;\n\nuse crate::{\n    mem::{\n        addr::PhysAddr,\n        allocator::{GLOBAL_ALLOC, KERNEL_FRAME_ALLOCATOR},\n        consts::{KERNEL_HEAP_SIZE, PAGE_SIZE},\n    },\n    task::{get_scheduler, Task, TaskId},\n    util::{align_down, BlockingMutex},\n};\n\npub static GOD_MODE_TASK: Once<Arc<Task>> = Once::new();\n\npub static GOD_MODE_FIFO: Once<BlockingMutex<VecDeque<u8>>> = Once::new();\n\npub fn init() {\n    GOD_MODE_FIFO.call_once(|| BlockingMutex::new(VecDeque::new()));\n    let sched = get_scheduler();\n    GOD_MODE_TASK.call_once(|| Task::new_kernel(sched, god_mode_repl, true));\n    sched.push_runnable(GOD_MODE_TASK.get().unwrap().clone(), false);\n}\n\nfn read_cmd() -> String {\n    let mut cmd = String::new();\n    loop {\n        let mut lock = GOD_MODE_FIFO.get().unwrap().try_lock();\n        while let Ok(Some(ch)) = lock.as_mut().map(|lock| lock.pop_front()) {\n            if ch == b'\\n' || ch == b'\\r' {\n                drop(lock);\n                return cmd;\n            }\n            let st = core::str::from_utf8(&[ch]).unwrap().to_owned();\n            cmd.push_str(&st);\n            serial1_print!(\"{}\", st);\n        }\n        drop(lock);\n        interrupts::enable_and_hlt();\n    }\n}\n\npub fn god_mode_repl() {\n    loop {\n        serial1_print!(\"\\ngodmode > \");\n        let cmd = read_cmd();\n        let mut args = cmd.split_whitespace();\n        let cmd = if let Some(cmd) = args.next() {\n            cmd\n        } else {\n            continue;\n        };\n\n        serial1_println!();\n        log::warn!(\"God said: {}\", cmd);\n        match cmd {\n            \"f\" | \"frames\" => {\n                serial1_println!(\"Dumping free physical memory.\");\n                let fa = KERNEL_FRAME_ALLOCATOR.get().unwrap().lock();\n                let mut total_space_pages = 0;\n                for area in fa.free_regions() {\n                    total_space_pages += area.size_in_pages();\n                    serial1_println!(\"Free chunk at {:?}\", area);\n                }\n                serial1_println!(\"Total free pages: {}\", total_space_pages);\n                serial1_println!(\"Total free bytes: {}\", total_space_pages * PAGE_SIZE);\n            }\n            \"vm\" | \"vmem\" => {\n                let pid = if let Some(Ok(pid)) = args.next().map(|arg| arg.parse()) {\n                    TaskId::new(pid)\n                } else {\n                    serial1_println!(\"Invalid argument. Specify a PID to inspect vmem of.\");\n                    continue;\n                };\n                let sched = get_scheduler();\n                let task = if let Some(task) = sched.find_task(pid) {\n                    task\n                } else {\n                    serial1_println!(\"Invalid argument. PID not found.\");\n                    continue;\n                };\n                serial1_println!(\n                    \"Dumping virtual memory of pid {}. Check serial0 (stdio).\",\n                    pid.as_usize()\n                );\n                task.vmem().lock().log();\n            }\n            \"x\" | \"examine\" => {\n                let start = if let Some(Ok(start)) =\n                    args.next().map(|arg| usize::from_str_radix(arg, 16))\n                {\n                    align_down(start, PAGE_SIZE)\n                } else {\n                    serial1_println!(\"Invalid argument. Specify the address to dump the frame of.\");\n                    continue;\n                };\n\n                let ptr = PhysAddr::new(start).as_hhdm_virt().as_raw_ptr::<u64>();\n\n                let max_i = PAGE_SIZE / core::mem::size_of::<u64>();\n                serial1_println!(\"Dumping frame at {:#x}.\", start);\n                for i in 0..max_i / 4 {\n                    let i = i * 4;\n                    serial1_print!(\"{:#016x} >> \", start + i * core::mem::size_of::<u64>());\n                    for j in 0..4 {\n                        let offset = i + j;\n                        serial1_print!(\"{:016x} \", unsafe { ptr.add(offset).read_volatile() });\n                    }\n                    serial1_println!();\n                }\n            }\n            \"h\" | \"heap\" => {\n                let lock = GLOBAL_ALLOC.try_lock();\n                if let Some(lock) = lock {\n                    serial1_println!(\"Kernel heap size:           {:#08x}\", KERNEL_HEAP_SIZE);\n                    serial1_println!(\n                        \"Kernel heap usage (actual): {:#08x} ({:.4}%)\",\n                        lock.stats_alloc_actual(),\n                        lock.stats_alloc_actual() as f64 / KERNEL_HEAP_SIZE as f64 * 100.0\n                    );\n                    serial1_println!(\n                        \"Kernel heap usage (user):   {:#08x} ({:.4}%)\",\n                        lock.stats_alloc_user(),\n                        lock.stats_alloc_user() as f64 / KERNEL_HEAP_SIZE as f64 * 100.0\n                    );\n                } else {\n                    serial1_println!(\"Error locking global allocator.\");\n                }\n            }\n            _ => {}\n        }\n    }\n}\n"
  },
  {
    "path": "src/graphics/mod.rs",
    "content": "use core::ops::Add;\n\nuse alloc::{boxed::Box, vec::Vec};\nuse embedded_graphics::{\n    mono_font::{ascii::FONT_8X13, MonoFont, MonoTextStyle},\n    pixelcolor::Rgb888,\n    prelude::*,\n    text::{Alignment, Text},\n};\nuse limine::response::FramebufferResponse;\nuse spin::Once;\n\nuse crate::{\n    mem::addr::VirtAddr,\n    util::{IrqMutex, IrqMutexGuard, KResult},\n    vga_text::{BUFFER_HEIGHT, BUFFER_WIDTH},\n};\n\nconst FONT: MonoFont = FONT_8X13;\n\npub struct FrameBuffer {\n    back_buffer: Box<[u32]>,\n    start_addr: VirtAddr,\n    width: usize,\n    height: usize,\n    bpp: usize,\n    text_buf: [[Option<u8>; BUFFER_WIDTH]; BUFFER_HEIGHT],\n    text_cursor_x: usize,\n    text_cursor_y: usize,\n    text_fgcolor: Rgb888,\n}\n\nimpl FrameBuffer {\n    pub fn width(&self) -> usize {\n        self.width\n    }\n\n    pub fn height(&self) -> usize {\n        self.height\n    }\n\n    pub fn bpp(&self) -> usize {\n        self.bpp\n    }\n\n    pub fn render_text_buf(&mut self) {\n        let mut out = Vec::new();\n        for line in 0..BUFFER_HEIGHT {\n            'inner: for col in 0..BUFFER_WIDTH {\n                if let Some(ch) = self.text_buf[line][col] {\n                    out.push(ch);\n                } else {\n                    out.push(b'\\n');\n                    break 'inner;\n                }\n            }\n        }\n\n        let mono_font = MonoTextStyle::new(&FONT, self.text_fgcolor);\n\n        Text::with_alignment(\n            core::str::from_utf8(&out).unwrap(),\n            self.bounding_box().top_left\n                + Point::new(\n                    FONT.character_size.width as i32,\n                    FONT.character_size.height as i32,\n                ),\n            mono_font,\n            Alignment::Left,\n        )\n        .draw(self)\n        .unwrap();\n    }\n\n    pub fn clear_pixels(&mut self) {\n        self.clear(Rgb888::new(0, 0, 0)).unwrap();\n    }\n\n    pub fn frame_mut(&mut self) -> &mut [u32] {\n        &mut self.back_buffer\n    }\n\n    pub fn present(&mut self) {\n        unsafe {\n            self.start_addr\n                .as_raw_ptr_mut::<u32>()\n                .copy_from_nonoverlapping(self.back_buffer.as_ptr(), self.width * self.height);\n        }\n    }\n\n    pub fn write_byte(&mut self, byte: u8) {\n        match byte {\n            0x8 => self.backspace(),\n            b'\\n' => self.new_line(),\n            b'\\r' => self.text_cursor_x = 0,\n            byte => {\n                if self.text_cursor_x >= BUFFER_WIDTH - 1 {\n                    self.new_line();\n                }\n\n                let row = self.text_cursor_y;\n                let col = self.text_cursor_x;\n\n                self.text_buf[row][col] = Some(byte);\n                self.move_right();\n            }\n        }\n        self.cursor_color_hook();\n    }\n\n    fn cursor_color_hook(&mut self) {}\n\n    pub fn backspace(&mut self) {\n        let row = self.text_cursor_y;\n        let col = self.text_cursor_x.saturating_sub(1);\n        self.text_buf[row][col] = None;\n        self.text_cursor_x = col;\n        self.cursor_color_hook();\n    }\n\n    pub fn write_string(&mut self, s: &str) {\n        for byte in s.bytes() {\n            self.write_byte(byte)\n        }\n    }\n\n    fn new_line(&mut self) {\n        if self.text_cursor_y >= BUFFER_HEIGHT - 1 {\n            for row in 1..BUFFER_HEIGHT {\n                for col in 0..BUFFER_WIDTH {\n                    let character = self.text_buf[row][col];\n                    self.text_buf[row - 1][col] = character;\n                }\n            }\n            self.text_cursor_y = BUFFER_HEIGHT - 1;\n            self.clear_row(self.text_cursor_y);\n            self.text_cursor_x = 0;\n        } else {\n            self.text_cursor_y += 1;\n            self.text_cursor_x = 0;\n        }\n        self.cursor_color_hook();\n    }\n\n    fn clear_row(&mut self, row: usize) {\n        for col in 0..BUFFER_WIDTH {\n            self.text_buf[row][col] = None;\n        }\n        self.cursor_color_hook();\n    }\n    fn clear_until_end(&mut self) {\n        for col in self.text_cursor_x..BUFFER_WIDTH {\n            self.text_buf[self.text_cursor_y][col] = None;\n        }\n        for row in self.text_cursor_y + 1..BUFFER_HEIGHT {\n            self.clear_row(row);\n        }\n        self.cursor_color_hook();\n    }\n    fn clear_until_beginning(&mut self) {\n        for col in 0..self.text_cursor_x {\n            self.text_buf[self.text_cursor_y][col] = None;\n        }\n        for row in 0..self.text_cursor_y - 1 {\n            self.clear_row(row);\n        }\n        self.cursor_color_hook();\n    }\n    fn clear_until_eol(&mut self) {\n        for col in self.text_cursor_x..BUFFER_WIDTH {\n            self.text_buf[self.text_cursor_y][col] = None;\n        }\n        self.cursor_color_hook();\n    }\n    fn clear_from_bol(&mut self) {\n        for col in 0..self.text_cursor_x {\n            self.text_buf[self.text_cursor_y][col] = None;\n        }\n        self.cursor_color_hook();\n    }\n    fn clear_line(&mut self) {\n        self.clear_row(self.text_cursor_y);\n    }\n    fn clear_all(&mut self) {\n        for row in 0..BUFFER_HEIGHT {\n            self.clear_row(row)\n        }\n        self.cursor_color_hook();\n    }\n    fn move_up(&mut self) {\n        let new_y = self.text_cursor_y.saturating_sub(1);\n        // let mut new_x = self.text_cursor_x;\n        // while new_x > 0 && self.text_buf[new_y][new_x] == b' ' {\n        // new_x -= 1;\n        // }\n        self.text_cursor_y = new_y;\n        // self.text_cursor_x = new_x;\n        self.cursor_color_hook();\n    }\n    fn move_down(&mut self) {\n        let new_y = self.text_cursor_y.add(1).min(BUFFER_HEIGHT - 1);\n        // let mut new_x = self.text_cursor_x;\n        // while new_x > 0 && self.text_buf[new_y][new_x] == b' ' {\n        //     new_x -= 1;\n        // }\n        self.text_cursor_y = new_y;\n        // self.text_cursor_x = new_x;\n        self.cursor_color_hook();\n    }\n    fn move_left(&mut self) {\n        self.text_cursor_x = self.text_cursor_x.saturating_sub(1);\n        self.cursor_color_hook();\n    }\n    fn move_right(&mut self) {\n        self.text_cursor_x = self.text_cursor_x.add(1).min(BUFFER_WIDTH - 1);\n        self.cursor_color_hook();\n    }\n}\n\nimpl core::fmt::Write for FrameBuffer {\n    fn write_str(&mut self, s: &str) -> core::fmt::Result {\n        self.write_string(s);\n        Ok(())\n    }\n}\n\nimpl DrawTarget for FrameBuffer {\n    type Color = Rgb888;\n    type Error = core::convert::Infallible;\n\n    fn draw_iter<I>(&mut self, pixels: I) -> Result<(), Self::Error>\n    where\n        I: IntoIterator<Item = embedded_graphics::Pixel<Self::Color>>,\n    {\n        for Pixel(coord, color) in pixels.into_iter() {\n            let (x, y) = coord.into();\n            if (0..self.width as i32).contains(&x) && (0..self.height as i32).contains(&y) {\n                let index: usize = x as usize + y as usize * self.width;\n                self.back_buffer[index] = color.into_storage();\n            }\n        }\n\n        Ok(())\n    }\n\n    fn clear(&mut self, color: Self::Color) -> Result<(), Self::Error> {\n        self.back_buffer.fill(color.into_storage());\n        Ok(())\n    }\n}\n\nimpl OriginDimensions for FrameBuffer {\n    fn size(&self) -> embedded_graphics::prelude::Size {\n        Size::new(self.width as u32, self.height as u32)\n    }\n}\n\npub static FRAMEBUFFER: Once<IrqMutex<FrameBuffer>> = Once::new();\n\npub fn fb<'a>() -> IrqMutexGuard<'a, FrameBuffer> {\n    FRAMEBUFFER.get().unwrap().lock()\n}\n\npub fn clear_screen() {\n    fb().clear_all()\n}\n\npub fn backspace() {\n    fb().backspace()\n}\n\npub fn set_cursor_x(x: usize) {\n    fb().text_cursor_x = x.min(BUFFER_WIDTH - 1);\n}\n\npub fn set_cursor_y(y: usize) {\n    fb().text_cursor_y = y.min(BUFFER_HEIGHT - 1);\n}\n\npub fn set_cursor_xy(xy: (usize, usize)) {\n    set_cursor_x(xy.0.min(BUFFER_WIDTH - 1));\n    set_cursor_y(xy.1.min(BUFFER_HEIGHT - 1));\n}\n\npub fn cursor_xy() -> (usize, usize) {\n    let fb = fb();\n    (fb.text_cursor_x, fb.text_cursor_y)\n}\n\npub fn write_byte(byte: u8) {\n    fb().write_byte(byte);\n}\n\npub fn clear_until_end() {\n    fb().clear_until_end();\n}\n\npub fn clear_until_beginning() {\n    fb().clear_until_beginning();\n}\n\npub fn clear_from_bol() {\n    fb().clear_from_bol();\n}\n\npub fn clear_until_eol() {\n    fb().clear_until_eol();\n}\n\npub fn clear_line() {\n    fb().clear_line();\n}\n\npub fn move_up() {\n    fb().move_up();\n}\n\npub fn move_down() {\n    fb().move_down();\n}\n\npub fn move_left() {\n    fb().move_left();\n}\n\npub fn move_right() {\n    fb().move_right();\n}\n\npub fn render_text_buf() {\n    fb().clear_pixels();\n    fb().render_text_buf();\n    fb().present();\n}\n\n#[macro_export]\nmacro_rules! fb_print {\n    ($($arg:tt)*) => ({\n        $crate::graphics::_fb_print(format_args!($($arg)*));\n        $crate::serial::_print2(format_args!($($arg)*));\n    });\n}\n\n#[macro_export]\nmacro_rules! fb_println {\n    () => ($crate::fb_print!(\"\\n\"));\n    ($($arg:tt)*) => ($crate::fb_print!(\"{}\\n\", format_args!($($arg)*)));\n}\n\n#[doc(hidden)]\npub fn _fb_print(args: core::fmt::Arguments) {\n    use core::fmt::Write;\n    x86_64::instructions::interrupts::without_interrupts(|| {\n        FRAMEBUFFER.get().unwrap().lock().write_fmt(args).unwrap();\n    });\n}\n\npub fn init(fb_tag: &FramebufferResponse) -> KResult<()> {\n    let fb_tag = fb_tag.framebuffers().next().unwrap();\n    let fb_base = fb_tag.addr() as usize;\n    log::debug!(\"FB addr: {:#x}\", fb_base);\n    let framebuf = FrameBuffer {\n        back_buffer: alloc::vec![0u32; fb_tag.width() as usize * fb_tag.height() as usize]\n            .into_boxed_slice(),\n        start_addr: VirtAddr::new(fb_base),\n        width: fb_tag.width() as usize,\n        height: fb_tag.height() as usize,\n        bpp: fb_tag.bpp() as usize,\n        text_buf: [[None; BUFFER_WIDTH]; BUFFER_HEIGHT],\n        text_cursor_x: 0,\n        text_cursor_y: 0,\n        text_fgcolor: Rgb888::WHITE,\n    };\n\n    FRAMEBUFFER.call_once(|| IrqMutex::new(framebuf));\n\n    fb().clear_pixels();\n    clear_screen();\n    Ok(())\n}\n"
  },
  {
    "path": "src/logging.rs",
    "content": "use log::Level;\nuse log::Log;\n\nuse crate::serial0_print;\nuse crate::serial0_println;\nuse crate::task::SCHEDULER;\n\nstruct KaDOSLogger;\n\nimpl Log for KaDOSLogger {\n    fn enabled(&self, _metadata: &log::Metadata) -> bool {\n        true\n    }\n\n    fn log(&self, record: &log::Record) {\n        match record.level() {\n            Level::Debug => serial0_print!(\"\\x1b[1;36m\"), // cyan\n            Level::Error => serial0_print!(\"\\x1b[1;31m\"), // red\n            Level::Info => serial0_print!(\"\\x1b[1;32m\"),  // green\n            Level::Warn => serial0_print!(\"\\x1b[1;33m\"),  // yellow\n            Level::Trace => serial0_print!(\"\\x1b[1;37m\"), // white\n        }\n        if let Some(sched) = SCHEDULER.get() {\n            if let Some(current) = sched.current_task_opt() {\n                serial0_print!(\n                    \"[{}]\\t[{}] - {}\",\n                    record.level(),\n                    current.pid().as_usize(),\n                    record.args()\n                );\n            } else {\n                serial0_print!(\"[{}]\\t{}\", record.level(), record.args());\n            }\n        } else {\n            serial0_print!(\"[{}]\\t{}\", record.level(), record.args());\n        }\n        serial0_println!(\"\\x1b[0m\"); // reset color\n    }\n\n    fn flush(&self) {}\n}\n\nstruct KaDOSProfiler;\n\nimpl embedded_profiling::EmbeddedProfiler for KaDOSProfiler {\n    fn read_clock(&self) -> embedded_profiling::EPInstant {\n        let uptime = crate::arch::x86_64::time::get_uptime_ns();\n        embedded_profiling::EPInstant::from_ticks(uptime as u32)\n    }\n\n    fn log_snapshot(&self, snapshot: &embedded_profiling::EPSnapshot) {\n        crate::serial0_println!(\"{}\", snapshot);\n    }\n}\n\npub fn init() {\n    log::set_logger(&KaDOSLogger).expect(\"error setting logger\");\n    let level = if let Some(level) = option_env!(\"RUST_LOG\")\n        .or(option_env!(\"KADOS_LOG\"))\n        .or(option_env!(\"K4DOS_LOG\"))\n    {\n        match level {\n            \"error\" => log::LevelFilter::Error,\n            \"warn\" => log::LevelFilter::Warn,\n            \"info\" => log::LevelFilter::Info,\n            \"debug\" => log::LevelFilter::Debug,\n            \"trace\" => log::LevelFilter::Trace,\n            _ => log::LevelFilter::Info,\n        }\n    } else {\n        log::LevelFilter::Info\n    };\n    log::set_max_level(level);\n\n    unsafe { embedded_profiling::set_profiler(&KaDOSProfiler).expect(\"error setting profiler\") };\n}\n"
  },
  {
    "path": "src/main.rs",
    "content": "#![no_std]\n#![no_main]\n#![feature(\n    lang_items,\n    abi_x86_interrupt,\n    ptr_internals,\n    slice_pattern,\n    map_try_insert,\n    iter_advance_by,\n    alloc_error_handler\n)]\n#![allow(internal_features)]\n#![allow(clippy::missing_safety_doc, clippy::uninlined_format_args)]\n#![deny(unsafe_op_in_unsafe_fn)]\n// #![warn(clippy::unwrap_used)]\n\nextern crate alloc;\n\n#[macro_use]\npub mod vga_text;\n#[macro_use]\npub mod serial;\npub mod arch;\npub mod backtrace;\npub mod fs;\npub mod logging;\npub mod mem;\npub mod task;\npub mod userland;\npub mod util;\n#[macro_use]\npub mod graphics;\npub mod god_mode;\n\nuse mem::addr::VirtAddr;\n\nuse spin::Once;\nuse x86_64::instructions::hlt;\n\npub static PHYSICAL_OFFSET: Once<usize> = Once::new();\n\n#[inline]\npub fn phys_offset() -> VirtAddr {\n    unsafe { VirtAddr::new_unchecked(*PHYSICAL_OFFSET.get().unwrap()) }\n}\n\n#[unsafe(no_mangle)]\npub extern \"C\" fn start() -> ! {\n    arch::arch_main();\n\n    hcf();\n}\n\npub fn hcf() -> ! {\n    loop {\n        hlt();\n        core::hint::spin_loop();\n    }\n}\n"
  },
  {
    "path": "src/mem/addr.rs",
    "content": "use core::fmt::{self};\nuse core::mem::align_of;\nuse core::ops::*;\nuse core::ptr::NonNull;\n\nuse crate::kbail;\nuse crate::task::current_task;\nuse crate::util::{align_down, align_up, KResult};\n\nuse super::consts::PAGE_SIZE;\n\n#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\n#[repr(transparent)]\npub struct PhysAddr {\n    addr: usize,\n}\n\n#[inline]\npub const fn canonicalize_physaddr(addr: usize) -> usize {\n    addr & 0x000F_FFFF_FFFF_FFFF\n}\n\n#[inline]\npub const fn is_canonical_physaddr(addr: usize) -> bool {\n    canonicalize_physaddr(addr) == addr\n}\n\n#[inline]\npub const fn canonicalize_virtaddr(addr: usize) -> usize {\n    ((addr << 16) as isize >> 16) as usize\n}\n\n#[inline]\npub const fn is_canonical_virtaddr(addr: usize) -> bool {\n    canonicalize_virtaddr(addr) == addr\n}\n\nimpl PhysAddr {\n    #[inline]\n    pub const unsafe fn new_unchecked(addr: usize) -> Self {\n        Self { addr }\n    }\n\n    #[inline]\n    pub const fn new(addr: usize) -> Self {\n        assert!(\n            is_canonical_physaddr(addr),\n            \"PhysAddr::new(): non-canonical address\"\n        );\n\n        unsafe { Self::new_unchecked(addr) }\n    }\n\n    #[inline]\n    pub const fn null() -> Self {\n        unsafe { Self::new_unchecked(0) }\n    }\n\n    #[inline]\n    pub const fn is_null(&self) -> bool {\n        self.addr == 0\n    }\n\n    #[inline]\n    pub const fn is_canonical(&self) -> bool {\n        is_canonical_physaddr(self.addr)\n    }\n\n    #[inline]\n    pub const fn value(&self) -> usize {\n        self.addr\n    }\n\n    #[inline]\n    pub fn as_hhdm_virt(&self) -> VirtAddr {\n        VirtAddr::new(crate::phys_offset().value() + self.value())\n    }\n\n    #[inline]\n    pub fn is_aligned(&self, align: usize) -> bool {\n        self.addr % align == 0\n    }\n\n    pub const fn const_add(&self, offset: usize) -> Self {\n        Self::new(self.addr + offset)\n    }\n\n    pub const fn const_sub(&self, offset: usize) -> Self {\n        Self::new(self.addr - offset)\n    }\n}\n\nimpl fmt::Debug for PhysAddr {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        f.debug_tuple(\"PhysAddr\")\n            .field(&format_args!(\"{:#x}\", self.addr))\n            .finish()\n    }\n}\n\nimpl fmt::Binary for PhysAddr {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        fmt::Binary::fmt(&self.addr, f)\n    }\n}\n\nimpl fmt::LowerHex for PhysAddr {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        fmt::LowerHex::fmt(&self.addr, f)\n    }\n}\n\nimpl fmt::Octal for PhysAddr {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        fmt::Octal::fmt(&self.addr, f)\n    }\n}\n\nimpl fmt::UpperHex for PhysAddr {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        fmt::UpperHex::fmt(&self.addr, f)\n    }\n}\n\nimpl fmt::Pointer for PhysAddr {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        fmt::Pointer::fmt(&(self.addr as *const ()), f)\n    }\n}\n\nimpl Add<usize> for PhysAddr {\n    type Output = Self;\n    fn add(self, rhs: usize) -> Self::Output {\n        PhysAddr::new(self.addr + rhs)\n    }\n}\n\nimpl AddAssign<usize> for PhysAddr {\n    fn add_assign(&mut self, rhs: usize) {\n        *self = *self + rhs;\n    }\n}\n\nimpl Sub<usize> for PhysAddr {\n    type Output = Self;\n    fn sub(self, rhs: usize) -> Self::Output {\n        PhysAddr::new(self.addr.checked_sub(rhs).unwrap())\n    }\n}\n\nimpl SubAssign<usize> for PhysAddr {\n    fn sub_assign(&mut self, rhs: usize) {\n        *self = *self - rhs;\n    }\n}\n\nimpl Sub<PhysAddr> for PhysAddr {\n    type Output = usize;\n    fn sub(self, rhs: PhysAddr) -> Self::Output {\n        self.value().checked_sub(rhs.value()).unwrap()\n    }\n}\n\n#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\n#[repr(transparent)]\npub struct VirtAddr {\n    addr: usize,\n}\n\nimpl VirtAddr {\n    #[inline]\n    pub const unsafe fn new_unchecked(addr: usize) -> Self {\n        Self { addr }\n    }\n\n    #[inline]\n    pub const fn new(addr: usize) -> Self {\n        // assert!(is_canonical_virtaddr(addr));\n        unsafe { Self::new_unchecked(canonicalize_virtaddr(addr)) }\n    }\n\n    #[inline]\n    pub const fn null() -> Self {\n        Self { addr: 0 }\n    }\n\n    #[inline]\n    pub const fn is_null(&self) -> bool {\n        self.addr == 0\n    }\n\n    #[inline]\n    pub const fn is_canonical(&self) -> bool {\n        is_canonical_virtaddr(self.addr)\n    }\n\n    #[inline]\n    pub const fn value(&self) -> usize {\n        self.addr\n    }\n\n    #[inline]\n    pub const fn const_add(&self, offset: usize) -> Self {\n        Self::new(self.addr + offset)\n    }\n\n    #[inline]\n    pub const fn const_sub(&self, offset: usize) -> Self {\n        Self::new(self.addr - offset)\n    }\n\n    #[inline]\n    pub const fn as_raw_ptr<T>(&self) -> *const T {\n        self.value() as *const T\n    }\n\n    #[inline]\n    pub const fn as_raw_ptr_mut<T>(&self) -> *mut T {\n        self.value() as *mut T\n    }\n\n    #[inline]\n    pub unsafe fn deref<T>(&self) -> KResult<&T> {\n        self.align_ok::<T>()?;\n        Ok(unsafe { &*self.as_raw_ptr() })\n    }\n\n    #[inline]\n    pub unsafe fn deref_mut<T>(&mut self) -> KResult<&mut T> {\n        self.align_ok::<T>()?;\n        Ok(unsafe { &mut *self.as_raw_ptr_mut() })\n    }\n\n    #[inline]\n    pub const fn as_ptr<T>(self) -> UnsafePtr<T> {\n        UnsafePtr::new(self)\n    }\n\n    #[inline]\n    pub fn as_hhdm_phys(&self) -> PhysAddr {\n        PhysAddr::new(self.value() - crate::phys_offset().value())\n    }\n\n    pub const fn align_ok<T: Sized>(&self) -> KResult<()> {\n        if self.addr == 0 {\n            kbail!(EFAULT, \"align_ok(): null VirtAddr\");\n        }\n        if self.addr % align_of::<T>() != 0 {\n            kbail!(EFAULT, \"align_ok(): unaligned VirtAddr\");\n        }\n        if !is_canonical_virtaddr(self.addr) {\n            kbail!(EFAULT, \"align_ok(): non-canonical VirtAddr\");\n        }\n\n        Ok(())\n    }\n\n    pub fn user_ok(&self) -> KResult<()> {\n        if self.addr == 0 {\n            kbail!(EFAULT, \"user_ok(): null VirtAddr\");\n        }\n        if !is_canonical_virtaddr(self.addr) {\n            kbail!(EFAULT, \"user_ok(): non-canonical VirtAddr\");\n        }\n        if self >= &crate::mem::consts::MAX_LOW_VADDR {\n            kbail!(EFAULT, \"user_ok(): VirtAddr in kernel memory\");\n        }\n\n        Ok(())\n    }\n\n    pub unsafe fn read<T: Sized + Copy>(&self) -> KResult<T> {\n        self.align_ok::<T>()?;\n        Ok(unsafe { core::ptr::read(self.as_raw_ptr::<T>().cast()) })\n    }\n\n    pub unsafe fn read_volatile<T: Sized + Copy>(&self) -> KResult<T> {\n        self.align_ok::<T>()?;\n        Ok(unsafe { core::ptr::read_volatile(self.as_raw_ptr::<T>().cast()) })\n    }\n\n    pub unsafe fn read_user<T: Sized + Copy>(&self) -> KResult<T> {\n        self.align_ok::<T>()?;\n        self.user_ok()?;\n        let _guard = current_task().arch_mut().address_space.temporarily_switch();\n        Ok(unsafe { core::ptr::read_volatile(self.as_raw_ptr::<T>().cast()) })\n    }\n\n    pub unsafe fn read_bytes(&self, buf: &mut [u8]) -> KResult<usize> {\n        self.align_ok::<u8>()?;\n        unsafe { core::ptr::copy(self.as_raw_ptr(), buf.as_mut_ptr(), buf.len()) };\n        Ok(buf.len())\n    }\n\n    pub unsafe fn read_bytes_user(&self, buf: &mut [u8]) -> KResult<usize> {\n        self.align_ok::<u8>()?;\n        (*self + buf.len()).user_ok()?;\n        let _guard = current_task().arch_mut().address_space.temporarily_switch();\n        unsafe { core::ptr::copy(self.as_raw_ptr(), buf.as_mut_ptr(), buf.len()) };\n        Ok(buf.len())\n    }\n\n    pub unsafe fn write<T: Sized + Copy>(&self, t: T) -> KResult<()> {\n        self.align_ok::<T>()?;\n        unsafe { core::ptr::write(self.as_raw_ptr_mut(), t) };\n        Ok(())\n    }\n\n    pub unsafe fn write_volatile<T: Sized + Copy>(&self, t: T) -> KResult<()> {\n        self.align_ok::<T>()?;\n        unsafe { core::ptr::write_volatile(self.as_raw_ptr_mut(), t) };\n        Ok(())\n    }\n\n    pub unsafe fn write_user<T: Sized + Copy>(&self, t: T) -> KResult<()> {\n        self.align_ok::<T>()?;\n        (*self + size_of::<T>()).user_ok()?;\n        let _guard = current_task().arch_mut().address_space.temporarily_switch();\n        unsafe { core::ptr::write_volatile(self.as_raw_ptr_mut(), t) };\n        Ok(())\n    }\n\n    pub unsafe fn write_bytes(&self, bytes: &[u8]) -> KResult<usize> {\n        if self.is_null() {\n            kbail!(EFAULT, \"write_bytes(): null VirtAddr\");\n        }\n        unsafe {\n            core::slice::from_raw_parts_mut(self.as_raw_ptr_mut(), bytes.len())\n                .copy_from_slice(bytes)\n        };\n        Ok(bytes.len())\n    }\n\n    pub unsafe fn write_bytes_user(&self, bytes: &[u8]) -> KResult<usize> {\n        if self.is_null() {\n            kbail!(EFAULT, \"write_bytes_user(): null VirtAddr\");\n        }\n        self.user_ok()?;\n        (*self + bytes.len()).user_ok()?;\n        let _guard = current_task().arch_mut().address_space.temporarily_switch();\n        unsafe {\n            core::slice::from_raw_parts_mut(self.as_raw_ptr_mut(), bytes.len())\n                .copy_from_slice(bytes)\n        };\n        Ok(bytes.len())\n    }\n\n    pub unsafe fn fill(&self, value: u8, len: usize) -> KResult<usize> {\n        if self.is_null() {\n            kbail!(EFAULT, \"fill(): null VirtAddr\");\n        }\n        unsafe { (self.value() as *mut u8).write_bytes(value, len) };\n        Ok(len)\n    }\n\n    #[inline]\n    pub const fn align_down(&self, align: usize) -> Self {\n        VirtAddr::new(align_down(self.addr, align))\n    }\n\n    #[inline]\n    pub const fn align_up(&self, align: usize) -> Self {\n        VirtAddr::new(align_up(self.addr, align))\n    }\n\n    pub fn p4_index(&self) -> usize {\n        ((self.addr / PAGE_SIZE) >> 27) & 0x1FF\n    }\n    pub fn p3_index(&self) -> usize {\n        ((self.addr / PAGE_SIZE) >> 18) & 0x1FF\n    }\n    pub fn p2_index(&self) -> usize {\n        ((self.addr / PAGE_SIZE) >> 9) & 0x1FF\n    }\n    pub fn p1_index(&self) -> usize {\n        (self.addr / PAGE_SIZE) & 0x1FF\n    }\n}\n\nimpl fmt::Debug for VirtAddr {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        f.debug_tuple(\"VirtAddr\")\n            .field(&format_args!(\"{:#x}\", self.addr))\n            .finish()\n    }\n}\n\nimpl fmt::Binary for VirtAddr {\n    #[inline]\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        fmt::Binary::fmt(&self.addr, f)\n    }\n}\n\nimpl fmt::LowerHex for VirtAddr {\n    #[inline]\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        fmt::LowerHex::fmt(&self.addr, f)\n    }\n}\n\nimpl fmt::Octal for VirtAddr {\n    #[inline]\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        fmt::Octal::fmt(&self.addr, f)\n    }\n}\n\nimpl fmt::UpperHex for VirtAddr {\n    #[inline]\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        fmt::UpperHex::fmt(&self.addr, f)\n    }\n}\n\nimpl fmt::Pointer for VirtAddr {\n    #[inline]\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        fmt::Pointer::fmt(&(self.addr as *const ()), f)\n    }\n}\n\nimpl Add<usize> for VirtAddr {\n    type Output = Self;\n    #[inline]\n    fn add(self, rhs: usize) -> Self::Output {\n        VirtAddr::new(self.addr + rhs)\n    }\n}\n\nimpl AddAssign<usize> for VirtAddr {\n    #[inline]\n    fn add_assign(&mut self, rhs: usize) {\n        self.addr += rhs;\n    }\n}\n\nimpl Sub<usize> for VirtAddr {\n    type Output = Self;\n    #[inline]\n    fn sub(self, rhs: usize) -> Self::Output {\n        VirtAddr::new(self.addr.checked_sub(rhs).unwrap())\n    }\n}\n\nimpl SubAssign<usize> for VirtAddr {\n    #[inline]\n    fn sub_assign(&mut self, rhs: usize) {\n        self.addr -= rhs;\n    }\n}\n\nimpl Sub<VirtAddr> for VirtAddr {\n    type Output = usize;\n    #[inline]\n    fn sub(self, rhs: VirtAddr) -> Self::Output {\n        self.value().checked_sub(rhs.value()).unwrap()\n    }\n}\n\n/// Like a `NonNull<T>`, but backed by a `VirtAddr`.\n#[derive(Debug, Clone, Copy)]\n#[repr(transparent)]\npub struct UnsafePtr<T> {\n    addr: VirtAddr,\n    _phantom: core::marker::PhantomData<*mut T>,\n}\n\nimpl<T> UnsafePtr<T> {\n    pub const fn new(addr: VirtAddr) -> Self {\n        assert!(!addr.is_null(), \"UnsafePtr::new(): null VirtAddr\");\n        assert!(\n            addr.value() % align_of::<T>() == 0,\n            \"UnsafePtr::new(): unaligned VirtAddr\"\n        );\n        assert!(\n            is_canonical_virtaddr(addr.value()),\n            \"UnsafePtr::new(): non-canonical VirtAddr\"\n        );\n\n        Self {\n            addr,\n            _phantom: core::marker::PhantomData,\n        }\n    }\n\n    pub fn from_ref(data: &T) -> Self {\n        Self::new(VirtAddr::new(data as *const T as usize))\n    }\n\n    pub fn from_mut(data: &mut T) -> Self {\n        Self::new(VirtAddr::new(data as *mut T as usize))\n    }\n\n    pub fn from_raw_ptr(ptr: *mut T) -> Self {\n        Self::new(VirtAddr::new(ptr as usize))\n    }\n\n    pub fn as_raw_ptr(&self) -> *const T {\n        self.addr.as_raw_ptr()\n    }\n\n    pub fn as_raw_ptr_mut(&self) -> *mut T {\n        self.addr.as_raw_ptr_mut()\n    }\n\n    pub unsafe fn deref(&self) -> &T {\n        unsafe { &*self.as_raw_ptr() }\n    }\n\n    pub unsafe fn deref_mut(&mut self) -> &mut T {\n        unsafe { &mut *self.as_raw_ptr_mut() }\n    }\n\n    pub unsafe fn as_ref(&self) -> Ref<'_, T> {\n        Ref::new(unsafe { self.deref() })\n    }\n\n    pub unsafe fn as_mut(&mut self) -> Mut<'_, T> {\n        Mut::new(unsafe { self.deref_mut() })\n    }\n\n    pub fn cast<U>(self) -> UnsafePtr<U> {\n        UnsafePtr::new(self.addr)\n    }\n\n    pub fn into_non_null(self) -> NonNull<T> {\n        assert!(\n            !self.addr.is_null(),\n            \"UnsafePtr::as_non_null(): null VirtAddr\"\n        );\n        NonNull::new(self.as_raw_ptr_mut()).unwrap()\n    }\n\n    pub fn read(&self) -> KResult<T>\n    where\n        T: Copy,\n    {\n        unsafe { self.addr.read() }\n    }\n\n    pub fn read_volatile(&self) -> KResult<T>\n    where\n        T: Copy,\n    {\n        unsafe { self.addr.read_volatile() }\n    }\n}\n\nimpl<T> fmt::Pointer for UnsafePtr<T> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        fmt::Pointer::fmt(&self.as_raw_ptr(), f)\n    }\n}\n\n/// Like a `NonNull<[T]>`, but backed by a `VirtAddr`.\n#[derive(Debug, Clone, Copy)]\npub struct UnsafeSlice<T> {\n    addr: VirtAddr,\n    len: usize,\n    _phantom: core::marker::PhantomData<*mut [T]>,\n}\n\nimpl<T> UnsafeSlice<T> {\n    pub fn new(addr: VirtAddr, len: usize) -> Self {\n        assert!(!addr.is_null(), \"UnsafeSlice::new(): null VirtAddr\");\n        assert!(\n            addr.value() % align_of::<T>() == 0,\n            \"UnsafeSlice::new(): unaligned VirtAddr\"\n        );\n        assert!(\n            is_canonical_virtaddr(addr.value()),\n            \"UnsafeSlice::new(): non-canonical VirtAddr\"\n        );\n\n        Self {\n            addr,\n            len,\n            _phantom: core::marker::PhantomData,\n        }\n    }\n\n    pub fn len(&self) -> usize {\n        self.len\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.len == 0\n    }\n\n    pub fn as_ptr(&self) -> UnsafePtr<T> {\n        UnsafePtr::new(self.addr)\n    }\n\n    pub unsafe fn as_slice(&self) -> &[T] {\n        unsafe { core::slice::from_raw_parts(self.addr.as_raw_ptr(), self.len) }\n    }\n\n    pub unsafe fn as_mut_slice(&mut self) -> &mut [T] {\n        unsafe { core::slice::from_raw_parts_mut(self.addr.as_raw_ptr_mut(), self.len) }\n    }\n\n    pub fn into_raw_parts(self) -> (VirtAddr, usize) {\n        (self.addr, self.len)\n    }\n}\n\n/// Like a `&T`, but backed by a `VirtAddr`.\n#[derive(Debug, Clone, Copy)]\npub struct Ref<'a, T> {\n    addr: VirtAddr,\n    _phantom: core::marker::PhantomData<&'a T>,\n}\n\nimpl<'a, T> Ref<'a, T> {\n    pub fn new(data: &'a T) -> Self {\n        Self {\n            addr: VirtAddr::new(data as *const T as usize),\n            _phantom: core::marker::PhantomData,\n        }\n    }\n\n    pub fn addr(&self) -> VirtAddr {\n        self.addr\n    }\n\n    #[allow(clippy::should_implement_trait)]\n    pub fn deref(&self) -> &'a T {\n        unsafe { &*self.as_raw_ptr() }\n    }\n\n    pub fn as_ptr(&self) -> UnsafePtr<T> {\n        self.addr.as_ptr()\n    }\n\n    pub fn as_raw_ptr(&self) -> *const T {\n        self.addr.as_raw_ptr()\n    }\n\n    pub fn as_raw_ptr_mut(&self) -> *mut T {\n        self.addr.as_raw_ptr_mut()\n    }\n}\n\nimpl<T> Deref for Ref<'_, T> {\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        <Ref<T>>::deref(self)\n    }\n}\n\nimpl<T> fmt::Pointer for Ref<'_, T> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        fmt::Pointer::fmt(&self.as_raw_ptr(), f)\n    }\n}\n\n/// Like a `&mut T`, but backed by a `VirtAddr`.\n#[derive(Debug)]\npub struct Mut<'a, T> {\n    addr: VirtAddr,\n    _phantom: core::marker::PhantomData<&'a mut T>,\n}\n\nimpl<'a, T> Mut<'a, T> {\n    pub fn new(data: &'a mut T) -> Self {\n        Self {\n            addr: VirtAddr::new(data as *mut T as usize),\n            _phantom: core::marker::PhantomData,\n        }\n    }\n\n    pub fn addr(&self) -> VirtAddr {\n        self.addr\n    }\n\n    pub fn as_ptr(&self) -> UnsafePtr<T> {\n        self.addr.as_ptr()\n    }\n\n    #[allow(clippy::should_implement_trait)]\n    pub fn deref(&self) -> &'a T {\n        unsafe { &*self.as_raw_ptr() }\n    }\n\n    #[allow(clippy::should_implement_trait)]\n    pub fn deref_mut(&mut self) -> &'a mut T {\n        unsafe { &mut *self.as_raw_ptr_mut() }\n    }\n\n    pub fn as_raw_ptr(&self) -> *const T {\n        self.addr.as_raw_ptr()\n    }\n\n    pub fn as_raw_ptr_mut(&self) -> *mut T {\n        self.addr.as_raw_ptr_mut()\n    }\n}\n\nimpl<T> fmt::Pointer for Mut<'_, T> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        fmt::Pointer::fmt(&self.as_raw_ptr(), f)\n    }\n}\n\nimpl<T> Deref for Mut<'_, T> {\n    type Target = T;\n\n    fn deref(&self) -> &Self::Target {\n        <Mut<T>>::deref(self)\n    }\n}\n\nimpl<T> DerefMut for Mut<'_, T> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        <Mut<T>>::deref_mut(self)\n    }\n}\n\n/// Like a `&[T]`, but backed by a `VirtAddr`.\n#[derive(Debug, Clone, Copy)]\npub struct Slice<'a, T> {\n    addr: VirtAddr,\n    len: usize,\n    _phantom: core::marker::PhantomData<&'a [T]>,\n}\n\nimpl<'a, T> Slice<'a, T> {\n    pub fn new(data: &'a [T]) -> Self {\n        Self {\n            addr: VirtAddr::new(data.as_ptr() as usize),\n            len: data.len(),\n            _phantom: core::marker::PhantomData,\n        }\n    }\n\n    pub unsafe fn from_raw_parts(addr: VirtAddr, len: usize) -> Self {\n        addr.align_ok::<T>().unwrap();\n        Self {\n            addr,\n            len,\n            _phantom: core::marker::PhantomData,\n        }\n    }\n\n    pub fn addr(&self) -> VirtAddr {\n        self.addr\n    }\n\n    pub fn len(&self) -> usize {\n        self.len\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.len == 0\n    }\n\n    pub fn as_ptr(&self) -> UnsafePtr<T> {\n        self.addr.as_ptr()\n    }\n\n    pub fn as_slice(&self) -> &'a [T] {\n        unsafe { core::slice::from_raw_parts(self.addr.as_raw_ptr(), self.len) }\n    }\n\n    pub fn as_raw_ptr(&self) -> *const T {\n        self.addr.as_raw_ptr()\n    }\n\n    pub fn as_raw_ptr_mut(&self) -> *mut T {\n        self.addr.as_raw_ptr_mut()\n    }\n}\n\nimpl<T> fmt::Pointer for Slice<'_, T> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        fmt::Pointer::fmt(&self.as_raw_ptr(), f)\n    }\n}\n\nimpl<T> Deref for Slice<'_, T> {\n    type Target = [T];\n\n    fn deref(&self) -> &Self::Target {\n        self.as_slice()\n    }\n}\n\nimpl<T> Index<usize> for Slice<'_, T> {\n    type Output = T;\n\n    fn index(&self, index: usize) -> &Self::Output {\n        &self.as_slice()[index]\n    }\n}\n\n/// Like a `&mut [T]`, but backed by a `VirtAddr`.\n#[derive(Debug)]\npub struct SliceMut<'a, T> {\n    addr: VirtAddr,\n    len: usize,\n    _phantom: core::marker::PhantomData<&'a mut [T]>,\n}\n\nimpl<'a, T> SliceMut<'a, T> {\n    pub fn new(data: &'a mut [T]) -> Self {\n        Self {\n            addr: VirtAddr::new(data.as_mut_ptr() as usize),\n            len: data.len(),\n            _phantom: core::marker::PhantomData,\n        }\n    }\n\n    pub unsafe fn from_raw_parts(addr: VirtAddr, len: usize) -> Self {\n        addr.align_ok::<T>().unwrap();\n        Self {\n            addr,\n            len,\n            _phantom: core::marker::PhantomData,\n        }\n    }\n\n    pub fn addr(&self) -> VirtAddr {\n        self.addr\n    }\n\n    pub fn len(&self) -> usize {\n        self.len\n    }\n\n    pub fn is_empty(&self) -> bool {\n        self.len == 0\n    }\n\n    pub fn as_ptr(&self) -> UnsafePtr<T> {\n        self.addr.as_ptr()\n    }\n\n    pub fn as_slice(&self) -> &'a [T] {\n        unsafe { core::slice::from_raw_parts(self.addr.as_raw_ptr(), self.len) }\n    }\n\n    pub fn as_mut_slice(&mut self) -> &'a mut [T] {\n        unsafe { core::slice::from_raw_parts_mut(self.addr.as_raw_ptr_mut(), self.len) }\n    }\n\n    pub fn as_raw_ptr(&self) -> *const T {\n        self.addr.as_raw_ptr()\n    }\n\n    pub fn as_raw_ptr_mut(&self) -> *mut T {\n        self.addr.as_raw_ptr_mut()\n    }\n}\n\nimpl<T> fmt::Pointer for SliceMut<'_, T> {\n    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {\n        fmt::Pointer::fmt(&self.as_raw_ptr(), f)\n    }\n}\n\nimpl<T> Deref for SliceMut<'_, T> {\n    type Target = [T];\n\n    fn deref(&self) -> &Self::Target {\n        self.as_slice()\n    }\n}\n\nimpl<T> DerefMut for SliceMut<'_, T> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        self.as_mut_slice()\n    }\n}\n\nimpl<T> Index<usize> for SliceMut<'_, T> {\n    type Output = T;\n\n    fn index(&self, index: usize) -> &Self::Output {\n        &self.as_slice()[index]\n    }\n}\n\nimpl<T> IndexMut<usize> for SliceMut<'_, T> {\n    fn index_mut(&mut self, index: usize) -> &mut Self::Output {\n        &mut self.as_mut_slice()[index]\n    }\n}\n"
  },
  {
    "path": "src/mem/addr_space.rs",
    "content": "use x86_64::{\n    registers::control::{Cr3, Cr3Flags},\n    structures::paging::{PageTableFlags, PhysFrame},\n};\n\nuse crate::{util::KResult, vga_text};\n\nuse super::{\n    addr::{PhysAddr, VirtAddr},\n    allocator::alloc_kernel_frames,\n    consts::PAGE_TABLE_ENTRIES,\n    paging::{\n        mapper::Mapper,\n        table::{active_table, PageTable},\n        units::{AllocatedFrames, Frame, FrameRange, MemoryUnit, Page},\n    },\n};\n\npub struct AddressSpace {\n    cr3: AllocatedFrames,\n}\n\nimpl AddressSpace {\n    pub fn new() -> KResult<Self> {\n        let cr3 = unsafe {\n            let frame = alloc_kernel_frames(1)?;\n            let phys_addr = frame.start_address();\n            let mut virt_addr = phys_addr.as_hhdm_virt();\n\n            let page_table: &mut PageTable = virt_addr.deref_mut()?;\n            let active_table = active_table();\n\n            // zero out lower half of virtual address space\n            for i in 0..256 {\n                page_table[i].set_unused();\n            }\n\n            // copy kernel mappings\n            for i in 256..512 {\n                page_table[i] = active_table[i];\n            }\n\n            frame\n        };\n\n        let mut this = Self { cr3 };\n        this.with_mapper(|mut mapper| {\n            mapper.map_to_single(\n                Page::containing_address(unsafe {\n                    VirtAddr::new_unchecked(vga_text::VGA_BUFFER_START_PADDR)\n                }),\n                Frame::containing_address(unsafe {\n                    PhysAddr::new_unchecked(vga_text::VGA_BUFFER_START_PADDR)\n                }),\n                PageTableFlags::PRESENT\n                    | PageTableFlags::WRITABLE\n                    | PageTableFlags::NO_EXECUTE\n                    | PageTableFlags::USER_ACCESSIBLE,\n            )\n        })?;\n\n        Ok(this)\n    }\n\n    pub fn current() -> Self {\n        let cr3 = Cr3::read().0.start_address().as_u64() as usize;\n        let cr3 = PhysAddr::new(cr3);\n        let cr3 = unsafe {\n            AllocatedFrames::assume_allocated(FrameRange::new(\n                Frame::containing_address(cr3),\n                Frame::containing_address(cr3),\n            ))\n        };\n        Self { cr3 }\n    }\n\n    pub fn cr3(&self) -> PhysAddr {\n        self.cr3.start_address()\n    }\n\n    pub fn switch(&self) {\n        unsafe {\n            Cr3::write(\n                PhysFrame::containing_address(x86_64::PhysAddr::new(\n                    self.cr3.start_address().value() as u64,\n                )),\n                Cr3Flags::empty(),\n            );\n        }\n    }\n\n    pub fn temporarily_switch(&self) -> TmpAddrSpaceGuard {\n        let previous = Self::current();\n        self.switch();\n        TmpAddrSpaceGuard { previous }\n    }\n\n    pub fn with_mapper<R>(&mut self, f: impl FnOnce(Mapper) -> R) -> R {\n        let mut addr = self.cr3.start_address().as_hhdm_virt();\n        let table = unsafe { addr.deref_mut().unwrap() };\n        let mapper = Mapper::new(table);\n        let _guard = self.temporarily_switch();\n        f(mapper)\n        // _guard is dropped here\n    }\n\n    pub fn map_two<R>(\n        first: &mut Self,\n        second: &mut Self,\n        f: impl FnOnce(Mapper, Mapper) -> R,\n    ) -> R {\n        let mut addr = first.cr3.start_address().as_hhdm_virt();\n        let table = unsafe { addr.deref_mut().unwrap() };\n        let mapper = Mapper::new(table);\n\n        let mut addr = second.cr3.start_address().as_hhdm_virt();\n        let table = unsafe { addr.deref_mut().unwrap() };\n        let other_mapper = Mapper::new(table);\n\n        f(mapper, other_mapper)\n    }\n\n    pub fn is_active(&self) -> bool {\n        self.cr3.start_address().value() == Cr3::read().0.start_address().as_u64() as usize\n    }\n\n    pub fn fork(&mut self, set_cow: bool) -> KResult<AddressSpace> {\n        assert!(self.is_active(), \"Can only fork the active address space\");\n        let mut new = AddressSpace::new()?;\n\n        let mut insert_flags = PageTableFlags::PRESENT | PageTableFlags::USER_ACCESSIBLE;\n\n        if !set_cow {\n            insert_flags |= PageTableFlags::WRITABLE;\n        }\n\n        Self::map_two(self, &mut new, |my_mapper, new_mapper| -> KResult<()> {\n            let my_p4 = my_mapper.into_inner();\n            let new_p4 = new_mapper.into_inner();\n\n            for p4_idx in 0..256 {\n                let my_entry = &my_p4[p4_idx];\n                if my_entry.is_unused() {\n                    continue;\n                }\n                let my_p3 = my_p4.next_table(p4_idx).unwrap();\n                let new_p3 = new_p4.next_table_create(p4_idx, insert_flags)?;\n                for p3_idx in 0..PAGE_TABLE_ENTRIES {\n                    let my_entry = &my_p3[p3_idx];\n                    if my_entry.is_unused() {\n                        continue;\n                    }\n                    let my_p2 = my_p3.next_table(p3_idx).unwrap();\n                    let new_p2 = new_p3.next_table_create(p3_idx, insert_flags)?;\n\n                    for p2_idx in 0..PAGE_TABLE_ENTRIES {\n                        let my_entry = &my_p2[p2_idx];\n                        if my_entry.is_unused() {\n                            continue;\n                        }\n                        let my_p1 = my_p2.next_table(p2_idx).unwrap();\n                        let new_p1 = new_p2.next_table_create(p2_idx, insert_flags)?;\n\n                        for p1_idx in 0..PAGE_TABLE_ENTRIES {\n                            let my_entry = &my_p1[p1_idx];\n                            if my_entry.is_unused() {\n                                continue;\n                            }\n                            let mut flags = my_entry.flags();\n\n                            if set_cow {\n                                flags.remove(PageTableFlags::WRITABLE);\n                            }\n\n                            new_p1[p1_idx].set_frame(my_entry.frame().unwrap(), flags);\n                        }\n                    }\n                }\n            }\n\n            Ok(())\n        })?;\n\n        Ok(new)\n    }\n}\n\n#[must_use = \"TmpAddrSpaceGuard restores previous address space on drop\"]\npub struct TmpAddrSpaceGuard {\n    previous: AddressSpace,\n}\n\nimpl Drop for TmpAddrSpaceGuard {\n    fn drop(&mut self) {\n        self.previous.switch();\n    }\n}\n"
  },
  {
    "path": "src/mem/allocator.rs",
    "content": "use core::ops::{Index, IndexMut};\n\nuse alloc::vec::Vec;\nuse arrayvec::ArrayVec;\nuse buddy_system_allocator::LockedHeap;\nuse limine::memory_map::{Entry, EntryType};\nuse spin::Once;\n\nuse super::addr::{PhysAddr, VirtAddr};\nuse super::consts::{MAX_LOW_VADDR, MIN_HIGH_VADDR, PAGE_SIZE};\nuse super::paging::units::{\n    Allocated, AllocatedFrames, AllocatedPages, Frame, FrameRange, MemoryRange, MemoryUnit, Page,\n    PageIndex, PageRange,\n};\n\nuse crate::kerror;\nuse crate::util::{align_down, IrqMutex, KResult};\n\npub static KERNEL_FRAME_ALLOCATOR: Once<IrqMutex<FrameAllocator>> = Once::new();\npub static KERNEL_PAGE_ALLOCATOR: Once<IrqMutex<PageAllocator>> = Once::new();\n\n#[global_allocator]\npub static GLOBAL_ALLOC: LockedHeap<32> = LockedHeap::new();\n\npub fn alloc_kernel_frames(count: usize) -> KResult<AllocatedFrames> {\n    KERNEL_FRAME_ALLOCATOR\n        .get()\n        .ok_or(kerror!(\"KERNEL_FRAME_ALLOCATOR not initialized\"))?\n        .try_lock()?\n        .allocate(count)\n}\n\npub fn alloc_kernel_frames_at(start: Frame, count: usize) -> KResult<AllocatedFrames> {\n    KERNEL_FRAME_ALLOCATOR\n        .get()\n        .ok_or(kerror!(\"KERNEL_FRAME_ALLOCATOR not initialized\"))?\n        .try_lock()?\n        .allocate_at(start, count)\n}\n\npub fn alloc_kernel_pages(count: usize) -> KResult<AllocatedPages> {\n    KERNEL_PAGE_ALLOCATOR\n        .get()\n        .ok_or(kerror!(\"KERNEL_PAGE_ALLOCATOR not initialized\"))?\n        .try_lock()?\n        .allocate(count)\n}\n\npub fn alloc_kernel_pages_at(start: Page, count: usize) -> KResult<AllocatedPages> {\n    KERNEL_PAGE_ALLOCATOR\n        .get()\n        .ok_or(kerror!(\"KERNEL_PAGE_ALLOCATOR not initialized\"))?\n        .try_lock()?\n        .allocate_at(start, count)\n}\n\npub fn free_kernel_frames(frames: &mut AllocatedFrames, merge: bool) -> KResult<()> {\n    KERNEL_FRAME_ALLOCATOR\n        .get()\n        .ok_or(kerror!(\"KERNEL_FRAME_ALLOCATOR not initialized\"))?\n        .try_lock()?\n        .free(frames, merge);\n    Ok(())\n}\n\npub fn free_kernel_pages(pages: &mut AllocatedPages, merge: bool) -> KResult<()> {\n    KERNEL_PAGE_ALLOCATOR\n        .get()\n        .ok_or(kerror!(\"KERNEL_PAGE_ALLOCATOR not initialized\"))?\n        .try_lock()?\n        .free(pages, merge);\n    Ok(())\n}\n\npub fn init(memmap: &[&Entry]) -> KResult<()> {\n    let mut frame_alloc = FrameAllocator::new_static();\n    for entry in memmap\n        .iter()\n        .filter(|entry| entry.entry_type == EntryType::USABLE)\n    {\n        if (entry.length as usize) < PAGE_SIZE {\n            continue;\n        }\n        let start = entry.base as usize;\n        let end = start + entry.length as usize;\n        let frames = FrameRange::new(\n            Frame::containing_address(PhysAddr::new(start)),\n            Frame::containing_address(PhysAddr::new(end)),\n        );\n        unsafe { frame_alloc.insert_free_region(frames) };\n    }\n    KERNEL_FRAME_ALLOCATOR.call_once(|| IrqMutex::new(frame_alloc));\n\n    let mut page_alloc = PageAllocator::new_static();\n    let pages = PageRange::new(\n        Page::at_index(PageIndex(1)),\n        Page::containing_address(MAX_LOW_VADDR - 1),\n    );\n    unsafe { page_alloc.insert_free_region(pages) };\n    let pages = PageRange::new(\n        Page::containing_address(MIN_HIGH_VADDR),\n        Page::containing_address(VirtAddr::new(align_down(usize::MAX, PAGE_SIZE))),\n    );\n    unsafe { page_alloc.insert_free_region(pages) };\n    KERNEL_PAGE_ALLOCATOR.call_once(|| IrqMutex::new(page_alloc));\n\n    Ok(())\n}\n\n#[derive(Clone)]\npub enum StaticListOrVec<T: Clone, const STATIC_CAP: usize> {\n    StaticList(ArrayVec<T, STATIC_CAP>),\n    Vec(Vec<T>),\n}\n\nimpl<T: Clone, const STATIC_CAP: usize> StaticListOrVec<T, STATIC_CAP> {\n    pub const fn new_static() -> StaticListOrVec<T, STATIC_CAP> {\n        StaticListOrVec::StaticList(ArrayVec::<T, STATIC_CAP>::new_const())\n    }\n    pub const fn new_vec() -> StaticListOrVec<T, STATIC_CAP> {\n        StaticListOrVec::Vec(Vec::new())\n    }\n    pub fn push(&mut self, item: T) {\n        match self {\n            StaticListOrVec::StaticList(a) => a.push(item),\n            StaticListOrVec::Vec(v) => v.push(item),\n        }\n    }\n    pub fn remove(&mut self, index: usize) -> T {\n        match self {\n            StaticListOrVec::StaticList(a) => a.remove(index),\n            StaticListOrVec::Vec(v) => v.remove(index),\n        }\n    }\n    pub fn convert_to_vec(&mut self) {\n        match self {\n            StaticListOrVec::Vec(_v) => {}\n            StaticListOrVec::StaticList(a) => {\n                *self = StaticListOrVec::Vec(a.into_iter().map(|t| t.clone()).collect());\n            }\n        }\n    }\n    pub fn iter(&self) -> impl Iterator<Item = &T> {\n        match self {\n            StaticListOrVec::StaticList(a) => a.iter(),\n            StaticListOrVec::Vec(v) => v.iter(),\n        }\n    }\n    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {\n        match self {\n            StaticListOrVec::StaticList(a) => a.iter_mut(),\n            StaticListOrVec::Vec(v) => v.iter_mut(),\n        }\n    }\n    pub fn len(&self) -> usize {\n        match self {\n            StaticListOrVec::StaticList(a) => a.len(),\n            StaticListOrVec::Vec(v) => v.len(),\n        }\n    }\n    pub fn is_empty(&self) -> bool {\n        match self {\n            Self::StaticList(a) => a.is_empty(),\n            Self::Vec(v) => v.is_empty(),\n        }\n    }\n    pub fn swap(&mut self, a: usize, b: usize) {\n        match self {\n            StaticListOrVec::StaticList(l) => l.swap(a, b),\n            StaticListOrVec::Vec(v) => v.swap(a, b),\n        }\n    }\n    pub fn get(&self, index: usize) -> Option<&T> {\n        match self {\n            StaticListOrVec::StaticList(a) => a.get(index),\n            StaticListOrVec::Vec(v) => v.get(index),\n        }\n    }\n}\n\nimpl<T: Clone, const CAP: usize> Index<usize> for StaticListOrVec<T, CAP> {\n    type Output = T;\n    fn index(&self, index: usize) -> &Self::Output {\n        match self {\n            StaticListOrVec::StaticList(a) => &a[index],\n            StaticListOrVec::Vec(v) => &v[index],\n        }\n    }\n}\n\nimpl<T: Clone, const CAP: usize> IndexMut<usize> for StaticListOrVec<T, CAP> {\n    fn index_mut(&mut self, index: usize) -> &mut Self::Output {\n        match self {\n            StaticListOrVec::StaticList(a) => &mut a[index],\n            StaticListOrVec::Vec(v) => &mut v[index],\n        }\n    }\n}\n\n#[derive(Clone)]\npub struct Allocator<T: MemoryUnit> {\n    free_regions: StaticListOrVec<MemoryRange<T>, 32>,\n}\n\nimpl<T: MemoryUnit> Allocator<T> {\n    pub fn new_static() -> Self {\n        Self {\n            free_regions: StaticListOrVec::new_static(),\n        }\n    }\n\n    pub fn new_vec() -> Self {\n        Self {\n            free_regions: StaticListOrVec::new_vec(),\n        }\n    }\n\n    pub fn convert_to_heap_allocated(&mut self) {\n        self.free_regions.convert_to_vec();\n    }\n\n    pub fn free_regions(&self) -> impl Iterator<Item = &MemoryRange<T>> {\n        self.free_regions.iter()\n    }\n\n    pub unsafe fn insert_free_region(&mut self, range: MemoryRange<T>) {\n        self.free_regions.push(range);\n    }\n\n    pub fn allocate(&mut self, count: usize) -> KResult<Allocated<T>> {\n        if count == 0 {\n            return Err(kerror!(\"Cannot allocate 0 units\"));\n        }\n\n        let mut best_fit = None;\n        for region in self.free_regions.iter() {\n            if region.size_in_pages() >= count {\n                best_fit = Some(*region);\n                break;\n            }\n        }\n        let best_fit = best_fit.ok_or(kerror!(\"Out of memory\"))?;\n        let new_region = MemoryRange::new(best_fit.start, best_fit.start + count);\n        if best_fit.size_in_pages() == count {\n            self.free_regions.remove(0);\n        } else {\n            self.free_regions[0] = MemoryRange::new(best_fit.start + count, best_fit.end);\n        }\n        Ok(unsafe { Allocated::assume_allocated(new_region) })\n    }\n\n    pub fn allocate_at(&mut self, start: T, count: usize) -> KResult<Allocated<T>> {\n        if count == 0 {\n            return Err(kerror!(\"Cannot allocate 0 units\"));\n        }\n\n        let mut best_fit = None;\n        for region in self.free_regions.iter() {\n            if region.start() <= start && region.end() >= start + count {\n                best_fit = Some(*region);\n                break;\n            }\n        }\n        let best_fit = best_fit.ok_or(kerror!(\"Out of memory\"))?;\n        let new_region = MemoryRange::new(start, start + count);\n        if best_fit.start() == start {\n            if best_fit.size_in_pages() == count {\n                self.free_regions.remove(0);\n            } else {\n                self.free_regions[0] = MemoryRange::new(start + count, best_fit.end);\n            }\n        } else if best_fit.end() == start + count {\n            self.free_regions[0] = MemoryRange::new(best_fit.start, start);\n        } else {\n            self.free_regions[0] = MemoryRange::new(best_fit.start, start);\n            self.free_regions\n                .push(MemoryRange::new(start + count, best_fit.end));\n        }\n        Ok(unsafe { Allocated::assume_allocated(new_region) })\n    }\n\n    pub fn free(&mut self, allocated: &mut Allocated<T>, merge: bool) {\n        let mut new_region = allocated.range;\n        if merge {\n            for region in self.free_regions.iter_mut() {\n                if region.start() == new_region.end() {\n                    new_region = MemoryRange::new(new_region.start(), region.end());\n                    *region = new_region;\n                    return;\n                } else if region.end() == new_region.start() {\n                    new_region = MemoryRange::new(region.start(), new_region.end());\n                    *region = new_region;\n                    return;\n                }\n            }\n        }\n        self.free_regions.push(new_region);\n    }\n}\n\npub type FrameAllocator = Allocator<Frame>;\npub type PageAllocator = Allocator<Page>;\n"
  },
  {
    "path": "src/mem/consts.rs",
    "content": "use super::addr::VirtAddr;\n\npub const PAGE_SHIFT: usize = 12;\npub const PAGE_SIZE: usize = 0x1000;\npub const PAGE_TABLE_ENTRIES: usize = 512;\npub const L4_SHIFT: usize = 39;\npub const L3_SHIFT: usize = 30;\npub const L2_SHIFT: usize = 21;\npub const L1_SHIFT: usize = 12;\n\npub const KERNEL_STACK_SIZE: usize = PAGE_SIZE * 32;\npub const USER_STACK_SIZE: usize = PAGE_SIZE * 64;\n\n/// The maximum canonical virtual address in low (user) address space.\n/// All user virtual addresses are less than this value.\npub const MAX_LOW_VADDR: VirtAddr = unsafe { VirtAddr::new_unchecked(0x0000_7000_0000_0000) };\n\n/// The minimum canonical virtual address in high (kernel) address space.\n/// All kernel virtual addresses are greater than or equal to this value.\npub const MIN_HIGH_VADDR: VirtAddr = unsafe { VirtAddr::new_unchecked(0xffff_8000_0000_0000) };\n\npub const USER_VALLOC_BASE: VirtAddr = unsafe { VirtAddr::new_unchecked(0x0000_000a_0000_0000) };\npub const USER_VALLOC_END: VirtAddr = unsafe { VirtAddr::new_unchecked(0x0000_0fff_0000_0000) };\npub const USER_STACK_TOP: VirtAddr = unsafe { VirtAddr::new_unchecked(0x0000_0fff_ffff_e000) };\npub const USER_STACK_BOTTOM: VirtAddr = USER_STACK_TOP.const_sub(USER_STACK_SIZE);\n\npub const KERNEL_HEAP_START: VirtAddr = unsafe { VirtAddr::new_unchecked(0xFFFF_FE80_0000_0000) };\npub const KERNEL_HEAP_SIZE: usize = 1024 * 1024 * 1024; // 1024 MiB\n"
  },
  {
    "path": "src/mem/mod.rs",
    "content": "use alloc::sync::Arc;\nuse paging::units::MemoryUnit;\nuse spin::Once;\nuse x86_64::structures::paging::PageTableFlags;\n\nuse crate::{\n    kerror,\n    task::{get_scheduler, Task},\n    util::{IrqMutex, KResult},\n};\n\nuse self::{\n    addr_space::AddressSpace,\n    allocator::{alloc_kernel_pages_at, GLOBAL_ALLOC},\n    consts::{KERNEL_HEAP_SIZE, KERNEL_HEAP_START, PAGE_SIZE},\n    paging::{\n        mapper::Mapper,\n        units::{MappedPages, Page},\n    },\n};\n\npub mod addr;\npub mod addr_space;\npub mod allocator;\npub mod consts;\npub mod paging;\n\npub static KERNEL_ADDR_SPACE: Once<IrqMutex<AddressSpace>> = Once::new();\n\npub fn remap_kernel() -> KResult<&'static IrqMutex<AddressSpace>> {\n    let active = AddressSpace::current();\n    log::info!(\"Active page table at {:?}\", active.cr3());\n    let new_space = AddressSpace::new()?;\n\n    // and that's all we gotta do, because Offset Page Tables RULE!\n\n    new_space.switch();\n    log::info!(\"Switched to new page table at {:?}\", new_space.cr3());\n    Ok(KERNEL_ADDR_SPACE.call_once(|| IrqMutex::new(new_space)))\n}\n\npub fn init_heap(kernel_mapper: &mut Mapper) -> KResult<MappedPages> {\n    let heap_ap = alloc_kernel_pages_at(\n        Page::containing_address(KERNEL_HEAP_START),\n        KERNEL_HEAP_SIZE / PAGE_SIZE,\n    )?;\n    let heap_mp = kernel_mapper.map(\n        heap_ap,\n        PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::NO_EXECUTE,\n    )?;\n    unsafe {\n        GLOBAL_ALLOC.lock().add_to_heap(\n            heap_mp.pages().start_address().value(),\n            heap_mp.pages().end_address().value(),\n        );\n    }\n    Ok(heap_mp)\n}\n\npub fn kernel_addr_space_scope() -> KResult<KernelAddrSpaceGuard> {\n    KernelAddrSpaceGuard::new()\n}\n\npub fn with_kernel_addr_space<R, F: FnOnce() -> R>(f: F) -> R {\n    let _guard = kernel_addr_space_scope().expect(\"Failed to switch to kernel address space\");\n    f()\n}\n\n#[must_use = \"KernelAddrSpaceGuard restores previous address space on drop\"]\npub struct KernelAddrSpaceGuard {\n    _private: (),\n    current_task: Option<Arc<Task>>,\n}\n\nimpl KernelAddrSpaceGuard {\n    pub fn new() -> KResult<Self> {\n        let _kernel_addr_space = KERNEL_ADDR_SPACE\n            .get()\n            .ok_or(kerror!(\"KERNEL_ADDR_SPACE not initialized\"))?\n            .try_lock()?;\n\n        _kernel_addr_space.switch();\n\n        Ok(Self {\n            _private: (),\n            current_task: get_scheduler().current_task_opt(),\n        })\n    }\n}\n\nimpl Drop for KernelAddrSpaceGuard {\n    fn drop(&mut self) {\n        if let Some(task) = self.current_task.as_ref() {\n            task.arch_mut().address_space.switch();\n        }\n    }\n}\n"
  },
  {
    "path": "src/mem/paging/mapper.rs",
    "content": "use x86::tlb;\nuse x86_64::structures::paging::PageTableFlags;\n\nuse crate::{\n    mem::{\n        addr::{PhysAddr, VirtAddr},\n        allocator::alloc_kernel_frames,\n    },\n    util::KResult,\n};\n\nuse super::{\n    table::PageTable,\n    units::{AllocatedFrames, AllocatedPages, Frame, MappedPages, MemoryUnit, Page},\n};\n\n#[derive(Debug)]\n#[must_use = \"Changes to page tables must be flushed or ignored.\"]\npub struct PageFlush(Page);\n\nimpl PageFlush {\n    pub fn new(page: Page) -> Self {\n        PageFlush(page)\n    }\n\n    pub fn ignore(self) {}\n\n    pub fn flush(self) {\n        unsafe { tlb::flush(self.0.start_address().value()) }\n    }\n}\n\n#[derive(Debug)]\npub struct Mapper<'a> {\n    p4: &'a mut PageTable,\n}\n\nimpl<'a> Mapper<'a> {\n    pub fn new(p4: &'a mut PageTable) -> Self {\n        Self { p4 }\n    }\n\n    pub fn into_inner(self) -> &'a mut PageTable {\n        self.p4\n    }\n\n    pub fn translate(&self, addr: VirtAddr) -> Option<(PhysAddr, PageTableFlags)> {\n        let p3 = self.p4.next_table(addr.p4_index())?;\n        let p2 = p3.next_table(addr.p3_index())?;\n        let p1 = p2.next_table(addr.p2_index())?;\n        let entry = p1[addr.p1_index()];\n\n        Some((entry.addr(), entry.flags()))\n    }\n\n    pub fn map_to_single(\n        &mut self,\n        page: Page,\n        frame: Frame,\n        flags: PageTableFlags,\n    ) -> KResult<()> {\n        let mut insert_flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE;\n        if flags.contains(PageTableFlags::USER_ACCESSIBLE) {\n            insert_flags |= PageTableFlags::USER_ACCESSIBLE;\n        }\n        let addr = page.start_address();\n\n        let p3 = self.p4.next_table_create(addr.p4_index(), insert_flags)?;\n        let p2 = p3.next_table_create(addr.p3_index(), insert_flags)?;\n        let p1 = p2.next_table_create(addr.p2_index(), insert_flags)?;\n        let entry = &mut p1[addr.p1_index()];\n        if !entry.is_unused() {\n            unsafe { self.unmap_single(page) };\n            // log::error!(\n            //     \"Attempt to remap {:?} to {:?} with {:?}\",\n            //     page,\n            //     frame,\n            //     flags\n            // );\n            // log::error!(\n            //     \"-- {:?} already mapped to {:?}, with {:?}\",\n            //     page,\n            //     entry.frame(),\n            //     entry.flags()\n            // );\n            // kbail!(\"Page already mapped to different frame\");\n        }\n        entry.set_frame(frame, flags);\n        unsafe { tlb::flush(addr.value()) }\n        Ok(())\n    }\n\n    pub fn map_to(\n        &mut self,\n        pages: AllocatedPages,\n        frames: AllocatedFrames,\n        flags: PageTableFlags,\n    ) -> KResult<MappedPages> {\n        assert_eq!(\n            pages.size_in_pages(),\n            frames.size_in_pages(),\n            \"Number of pages must equal number of frames\"\n        );\n        for (page, frame) in pages.iter().zip(frames.iter()) {\n            self.map_to_single(page, frame, flags)?;\n        }\n\n        Ok(unsafe { MappedPages::assume_mapped(pages, frames, flags) })\n    }\n\n    pub fn map(&mut self, pages: AllocatedPages, flags: PageTableFlags) -> KResult<MappedPages> {\n        let frames = alloc_kernel_frames(pages.size_in_pages())?;\n        self.map_to(pages, frames, flags)\n    }\n\n    pub fn set_flags(&mut self, mp: &mut MappedPages, flags: PageTableFlags) {\n        for page in mp.pages().iter() {\n            let addr = page.start_address();\n            // these unwraps should be safe since we know the pages are already mapped\n            let p3 = self.p4.next_table_mut(addr.p4_index()).unwrap();\n            let p2 = p3.next_table_mut(addr.p3_index()).unwrap();\n            let p1 = p2.next_table_mut(addr.p2_index()).unwrap();\n            p1[addr.p1_index()].set_flags(flags);\n            unsafe { tlb::flush(addr.value()) };\n        }\n        mp.flags = flags;\n    }\n\n    pub unsafe fn unmap(&mut self, mp: MappedPages) -> (AllocatedPages, AllocatedFrames) {\n        for page in mp.pages().iter() {\n            let addr = page.start_address();\n            // these unwraps should be safe since we know the pages are already mapped\n            let p3 = self.p4.next_table_mut(addr.p4_index()).unwrap();\n            let p2 = p3.next_table_mut(addr.p3_index()).unwrap();\n            let p1 = p2.next_table_mut(addr.p2_index()).unwrap();\n            p1[addr.p1_index()].set_unused();\n            unsafe { tlb::flush(addr.value()) };\n        }\n        let MappedPages { pages, frames, .. } = mp;\n        (pages, frames)\n    }\n\n    pub unsafe fn unmap_single(&mut self, page: Page) -> Option<Frame> {\n        let addr = page.start_address();\n        // these unwraps should be safe since we know the pages are already mapped\n        let p3 = self.p4.next_table_mut(addr.p4_index())?;\n        let p2 = p3.next_table_mut(addr.p3_index())?;\n        let p1 = p2.next_table_mut(addr.p2_index())?;\n        let old_frame = p1[addr.p1_index()].frame();\n        p1[addr.p1_index()].set_unused();\n        unsafe { tlb::flush(addr.value()) };\n        old_frame\n    }\n\n    pub unsafe fn set_flags_single(&mut self, page: Page, flags: PageTableFlags) {\n        let addr = page.start_address();\n        // these unwraps should be safe since we know the pages are already mapped\n        let p3 = self.p4.next_table_mut(addr.p4_index()).unwrap();\n        let p2 = p3.next_table_mut(addr.p3_index()).unwrap();\n        let p1 = p2.next_table_mut(addr.p2_index()).unwrap();\n        p1[addr.p1_index()].set_flags(flags);\n        unsafe { tlb::flush(addr.value()) };\n    }\n}\n"
  },
  {
    "path": "src/mem/paging/mod.rs",
    "content": "pub mod mapper;\npub mod table;\npub mod units;\n"
  },
  {
    "path": "src/mem/paging/table.rs",
    "content": "use core::{\n    fmt::Debug,\n    ops::{Index, IndexMut},\n};\n\nuse x86_64::{registers::control::Cr3, structures::paging::PageTableFlags};\n\nuse crate::{\n    kbail,\n    mem::{\n        addr::{PhysAddr, VirtAddr},\n        allocator::alloc_kernel_frames,\n        consts::{PAGE_SIZE, PAGE_TABLE_ENTRIES},\n    },\n    util::KResult,\n};\n\nuse super::units::{Frame, MemoryUnit};\n\nfn frame_to_table(frame: Frame) -> *mut PageTable {\n    let virt = crate::phys_offset() + frame.start_address().value();\n    virt.as_raw_ptr_mut()\n}\n\n#[derive(Clone, Copy)]\n#[repr(transparent)]\npub struct PageTableEntry {\n    data: usize,\n}\n\nimpl PageTableEntry {\n    const ADDRESS_MASK: usize = 0x000f_ffff_ffff_f000;\n    const FLAGS_MASK: usize = 0x01ff;\n\n    #[allow(clippy::new_without_default)]\n    pub const fn new() -> Self {\n        PageTableEntry { data: 0 }\n    }\n\n    pub const fn is_unused(&self) -> bool {\n        self.data == 0\n    }\n\n    pub fn set_unused(&mut self) {\n        self.data = 0\n    }\n\n    pub const fn flags(&self) -> PageTableFlags {\n        PageTableFlags::from_bits_truncate(self.data as u64)\n    }\n\n    pub fn addr(&self) -> PhysAddr {\n        PhysAddr::new(self.data & Self::ADDRESS_MASK)\n    }\n\n    pub fn frame(&self) -> Option<Frame> {\n        if !self.flags().contains(PageTableFlags::PRESENT)\n            || self.flags().contains(PageTableFlags::HUGE_PAGE)\n        {\n            None\n        } else {\n            Some(Frame::containing_address(self.addr()))\n        }\n    }\n\n    pub fn set_addr(&mut self, addr: PhysAddr, flags: PageTableFlags) {\n        assert!(addr.is_aligned(PAGE_SIZE));\n\n        self.data = addr.value() | flags.bits() as usize;\n    }\n\n    pub fn set_frame(&mut self, frame: Frame, flags: PageTableFlags) {\n        self.set_addr(frame.start_address(), flags)\n    }\n\n    pub fn set_flags(&mut self, flags: PageTableFlags) {\n        self.data &= !Self::FLAGS_MASK;\n        self.data |= flags.bits() as usize;\n    }\n}\n\nimpl Debug for PageTableEntry {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        f.debug_struct(\"PageTableEntry\")\n            .field(\"addr\", &self.addr())\n            .field(\"flags\", &self.flags())\n            .finish()\n    }\n}\n\n#[repr(C, align(4096))]\n#[derive(Clone)]\npub struct PageTable {\n    pub(super) entries: [PageTableEntry; PAGE_TABLE_ENTRIES],\n}\n\nimpl PageTable {\n    #[allow(clippy::new_without_default)]\n    #[inline]\n    pub const fn new() -> Self {\n        Self {\n            entries: [PageTableEntry::new(); PAGE_TABLE_ENTRIES],\n        }\n    }\n\n    #[inline]\n    pub fn zero(&mut self) {\n        for entry in self.entries.iter_mut() {\n            entry.set_unused();\n        }\n    }\n\n    pub fn next_table<'b>(&self, index: usize) -> Option<&'b PageTable> {\n        let ptr = frame_to_table(self[index].frame()?);\n        Some(unsafe { &*ptr })\n    }\n\n    pub fn next_table_mut<'b>(&self, index: usize) -> Option<&'b mut PageTable> {\n        let ptr = frame_to_table(self[index].frame()?);\n        Some(unsafe { &mut *ptr })\n    }\n\n    pub fn next_table_create<'b>(\n        &mut self,\n        index: usize,\n        insert_flags: PageTableFlags,\n    ) -> KResult<&'b mut PageTable> {\n        let entry = &mut self[index];\n        let created;\n        if entry.is_unused() {\n            match alloc_kernel_frames(1) {\n                Ok(frame) => {\n                    entry.set_frame(frame.start(), insert_flags);\n                    created = true;\n                }\n                Err(_e) => {\n                    kbail!(\"Could not allocate frame for new page table\")\n                }\n            }\n        } else {\n            entry.set_flags(entry.flags() | insert_flags);\n            created = false;\n        }\n\n        let page_table = match self.next_table_mut(index) {\n            Some(pt) => pt,\n            None => {\n                kbail!(\"Could not get mutable reference to new page table, likely due to huge page\")\n            }\n        };\n\n        if created {\n            page_table.zero();\n        }\n\n        Ok(page_table)\n    }\n}\n\nimpl Index<usize> for PageTable {\n    type Output = PageTableEntry;\n    fn index(&self, index: usize) -> &Self::Output {\n        &self.entries[index]\n    }\n}\n\nimpl IndexMut<usize> for PageTable {\n    fn index_mut(&mut self, index: usize) -> &mut Self::Output {\n        &mut self.entries[index]\n    }\n}\n\nimpl Debug for PageTable {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        writeln!(\n            f,\n            \"PageTable[{:?}]\",\n            VirtAddr::new(self as *const _ as usize).as_hhdm_phys()\n        )?;\n        for (i, entry) in self.entries.iter().enumerate() {\n            if !entry.is_unused() {\n                writeln!(f, \"{:>3}: {:>16?} | {:?}\", i, entry.addr(), entry.flags())?;\n            }\n        }\n        Ok(())\n    }\n}\n\npub fn active_table() -> &'static mut PageTable {\n    let cr3 = PhysAddr::new(Cr3::read().0.start_address().as_u64() as usize);\n    unsafe { &mut *cr3.as_hhdm_virt().as_raw_ptr_mut() }\n}\n"
  },
  {
    "path": "src/mem/paging/units.rs",
    "content": "use core::{\n    fmt::Debug,\n    ops::{Add, AddAssign, Sub, SubAssign},\n};\n\nuse x86_64::structures::paging::PageTableFlags;\n\nuse crate::mem::{\n    addr::{PhysAddr, VirtAddr},\n    consts::PAGE_SIZE,\n};\n\nuse super::mapper::Mapper;\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\n#[repr(transparent)]\npub struct PageIndex(pub usize);\n\nimpl PageIndex {\n    #[inline]\n    pub fn new(index: usize) -> Self {\n        Self(index)\n    }\n\n    #[inline]\n    pub fn containing_physaddr(paddr: PhysAddr) -> Self {\n        Self::new(paddr.value() / PAGE_SIZE)\n    }\n\n    #[inline]\n    pub fn containing_virtaddr(vaddr: VirtAddr) -> Self {\n        Self::new(vaddr.value() / PAGE_SIZE)\n    }\n\n    #[inline]\n    pub fn as_physaddr(self) -> PhysAddr {\n        PhysAddr::new(self.0 * PAGE_SIZE)\n    }\n\n    #[inline]\n    pub fn as_virtaddr(self) -> VirtAddr {\n        VirtAddr::new(self.0 * PAGE_SIZE)\n    }\n}\n\nimpl Add<usize> for PageIndex {\n    type Output = Self;\n    fn add(self, rhs: usize) -> Self::Output {\n        Self(self.0 + rhs)\n    }\n}\n\nimpl Sub<usize> for PageIndex {\n    type Output = Self;\n    fn sub(self, rhs: usize) -> Self::Output {\n        Self(self.0 - rhs)\n    }\n}\n\nimpl AddAssign<usize> for PageIndex {\n    fn add_assign(&mut self, rhs: usize) {\n        self.0 += rhs\n    }\n}\n\nimpl SubAssign<usize> for PageIndex {\n    fn sub_assign(&mut self, rhs: usize) {\n        self.0 -= rhs\n    }\n}\n\npub trait MemoryUnit:\n    Copy\n    + Debug\n    + PartialEq\n    + Eq\n    + PartialOrd\n    + Ord\n    + Add<usize, Output = Self>\n    + Sub<usize, Output = Self>\n{\n    type Address: Copy\n        + Debug\n        + PartialEq\n        + Eq\n        + PartialOrd\n        + Ord\n        + Add<usize, Output = Self::Address>\n        + Sub<usize, Output = Self::Address>\n        + core::fmt::Pointer\n        + core::fmt::LowerHex;\n\n    fn containing_address(addr: Self::Address) -> Self;\n    fn at_index(index: PageIndex) -> Self;\n    fn start_address(self) -> Self::Address;\n    fn inclusive_end_address(self) -> Self::Address;\n    fn index(self) -> PageIndex;\n}\n\n#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\n#[repr(transparent)]\npub struct Frame {\n    index: PageIndex,\n}\n\nimpl MemoryUnit for Frame {\n    type Address = PhysAddr;\n\n    #[inline]\n    fn containing_address(addr: PhysAddr) -> Self {\n        Self {\n            index: PageIndex::containing_physaddr(addr),\n        }\n    }\n\n    #[inline]\n    fn at_index(index: PageIndex) -> Self {\n        Self { index }\n    }\n\n    #[inline]\n    fn start_address(self) -> Self::Address {\n        self.index.as_physaddr()\n    }\n\n    #[inline]\n    fn inclusive_end_address(self) -> Self::Address {\n        self.start_address() + PAGE_SIZE - 1\n    }\n\n    #[inline]\n    fn index(self) -> PageIndex {\n        self.index\n    }\n}\n\nimpl Debug for Frame {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"Frame<{:x}>\", self.start_address())\n    }\n}\n\nimpl Add<usize> for Frame {\n    type Output = Self;\n    fn add(self, rhs: usize) -> Self::Output {\n        Self::at_index(self.index + rhs)\n    }\n}\n\nimpl Sub<usize> for Frame {\n    type Output = Self;\n    fn sub(self, rhs: usize) -> Self::Output {\n        Self::at_index(self.index - rhs)\n    }\n}\n\nimpl AddAssign<usize> for Frame {\n    fn add_assign(&mut self, rhs: usize) {\n        self.index += rhs\n    }\n}\n\nimpl SubAssign<usize> for Frame {\n    fn sub_assign(&mut self, rhs: usize) {\n        self.index -= rhs\n    }\n}\n\n#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub struct Page {\n    index: PageIndex,\n}\n\nimpl MemoryUnit for Page {\n    type Address = VirtAddr;\n\n    #[inline]\n    fn containing_address(addr: VirtAddr) -> Self {\n        Self {\n            index: PageIndex::containing_virtaddr(addr),\n        }\n    }\n\n    #[inline]\n    fn at_index(index: PageIndex) -> Self {\n        Self { index }\n    }\n\n    #[inline]\n    fn start_address(self) -> Self::Address {\n        self.index.as_virtaddr()\n    }\n\n    #[inline]\n    fn inclusive_end_address(self) -> Self::Address {\n        self.index.as_virtaddr() + PAGE_SIZE - 1\n    }\n\n    #[inline]\n    fn index(self) -> PageIndex {\n        self.index\n    }\n}\n\nimpl Debug for Page {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(f, \"Page<{:x}>\", self.start_address())\n    }\n}\n\nimpl Add<usize> for Page {\n    type Output = Self;\n    fn add(self, rhs: usize) -> Self::Output {\n        Self::at_index(self.index + rhs)\n    }\n}\n\nimpl Sub<usize> for Page {\n    type Output = Self;\n    fn sub(self, rhs: usize) -> Self::Output {\n        Self::at_index(self.index - rhs)\n    }\n}\n\nimpl AddAssign<usize> for Page {\n    fn add_assign(&mut self, rhs: usize) {\n        self.index += rhs\n    }\n}\n\nimpl SubAssign<usize> for Page {\n    fn sub_assign(&mut self, rhs: usize) {\n        self.index -= rhs\n    }\n}\n\n#[derive(Debug)]\npub struct PageMergeError;\n\n#[derive(Debug, Clone, Copy)]\npub struct MemoryRange<T: MemoryUnit> {\n    pub start: T,\n    pub end: T,\n}\n\nimpl<T: MemoryUnit> MemoryRange<T> {\n    #[inline]\n    pub fn new(start: T, end: T) -> Self {\n        Self { start, end }\n    }\n\n    #[inline]\n    pub fn empty() -> Self {\n        Self {\n            start: T::at_index(PageIndex(0)),\n            end: T::at_index(PageIndex(0)),\n        }\n    }\n\n    #[inline]\n    pub fn start(self) -> T {\n        self.start\n    }\n\n    #[inline]\n    pub fn end(self) -> T {\n        self.end\n    }\n\n    #[inline]\n    pub fn size_in_pages(self) -> usize {\n        if self.is_empty() {\n            return 0;\n        }\n        self.end.index().0 - self.start.index().0\n    }\n\n    #[inline]\n    pub fn size_in_bytes(self) -> usize {\n        self.size_in_pages() * PAGE_SIZE\n    }\n\n    #[inline]\n    pub fn is_empty(self) -> bool {\n        self.start >= self.end\n    }\n\n    #[inline]\n    pub fn start_address(self) -> T::Address {\n        self.start.start_address()\n    }\n\n    #[inline]\n    pub fn end_address(self) -> T::Address {\n        self.end.start_address()\n    }\n\n    #[inline]\n    pub fn inclusive_end_address(self) -> T::Address {\n        self.end_address() - 1\n    }\n\n    pub fn merge_with(&mut self, other: Self) -> Result<(), PageMergeError> {\n        if other.is_empty() {\n            return Ok(());\n        }\n        if other.start != self.end && other.end != self.start {\n            return Err(PageMergeError);\n        }\n        if other.start < self.start {\n            self.start = other.start;\n        }\n        if other.end > self.end {\n            self.end = other.end;\n        }\n\n        Ok(())\n    }\n\n    pub fn overlaps(self, other: Self) -> bool {\n        if self.is_empty() || other.is_empty() {\n            return false;\n        }\n        self.start <= other.end && other.start <= self.end\n    }\n\n    pub fn consumes(self, other: Self) -> bool {\n        if self.is_empty() || other.is_empty() {\n            return false;\n        }\n        self.start <= other.start && self.end >= other.end\n    }\n\n    pub fn contains(self, unit: T) -> bool {\n        if self.is_empty() {\n            return false;\n        }\n        self.start <= unit && self.end >= unit\n    }\n\n    pub fn iter(self) -> MemoryRangeIter<T> {\n        MemoryRangeIter {\n            current: self.start,\n            limit: self.end,\n        }\n    }\n}\n\npub type FrameRange = MemoryRange<Frame>;\npub type PageRange = MemoryRange<Page>;\n\npub struct MemoryRangeIter<T: MemoryUnit> {\n    current: T,\n    limit: T,\n}\n\nimpl<T: MemoryUnit> Iterator for MemoryRangeIter<T> {\n    type Item = T;\n    fn next(&mut self) -> Option<Self::Item> {\n        if self.current >= self.limit {\n            None\n        } else {\n            let current = self.current;\n            self.current = self.current + 1;\n            Some(current)\n        }\n    }\n}\n\npub struct Allocated<T: MemoryUnit> {\n    pub range: MemoryRange<T>,\n}\n\nimpl<T: MemoryUnit> Allocated<T> {\n    pub unsafe fn assume_allocated(range: MemoryRange<T>) -> Self {\n        Self { range }\n    }\n}\n\nimpl<T: MemoryUnit> Debug for Allocated<T> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        write!(\n            f,\n            \"Allocated[{:x} .. {:x}]\",\n            self.range.start_address(),\n            self.range.inclusive_end_address()\n        )\n    }\n}\n\npub type AllocatedFrames = Allocated<Frame>;\npub type AllocatedPages = Allocated<Page>;\n\nimpl<T: MemoryUnit> core::ops::Deref for Allocated<T> {\n    type Target = MemoryRange<T>;\n    fn deref(&self) -> &Self::Target {\n        &self.range\n    }\n}\n\nimpl<T: MemoryUnit> core::ops::DerefMut for Allocated<T> {\n    fn deref_mut(&mut self) -> &mut Self::Target {\n        &mut self.range\n    }\n}\n\npub struct MappedPages {\n    pub(super) pages: AllocatedPages,\n    pub(super) frames: AllocatedFrames,\n    pub(super) flags: PageTableFlags,\n}\n\nimpl MappedPages {\n    pub unsafe fn assume_mapped(\n        pages: AllocatedPages,\n        frames: AllocatedFrames,\n        flags: PageTableFlags,\n    ) -> Self {\n        Self {\n            pages,\n            frames,\n            flags,\n        }\n    }\n\n    pub unsafe fn unmap(self, table: &mut Mapper) -> (AllocatedPages, AllocatedFrames) {\n        unsafe { table.unmap(self) }\n    }\n\n    pub fn pages(&self) -> &AllocatedPages {\n        &self.pages\n    }\n\n    pub fn frames(&self) -> &AllocatedFrames {\n        &self.frames\n    }\n\n    pub fn flags(&self) -> PageTableFlags {\n        self.flags\n    }\n}\n\nimpl Debug for MappedPages {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        f.debug_struct(\"MappedPages\")\n            .field(\"pages\", &self.pages)\n            .field(\"frames\", &self.frames)\n            .finish()\n    }\n}\n"
  },
  {
    "path": "src/serial.rs",
    "content": "use lazy_static::lazy_static;\n\nuse uart_16550::SerialPort;\nuse x86::io::inb;\n\nuse crate::util::lock::IrqMutex;\n\npub const SERIAL0_IOPORT: u16 = 0x3f8;\npub const SERIAL1_IOPORT: u16 = 0x2f8;\npub const SERIAL2_IOPORT: u16 = 0x3e8;\n\nlazy_static! {\n    pub static ref SERIAL0: IrqMutex<SerialPort> = {\n        let mut serial_port = unsafe { SerialPort::new(SERIAL0_IOPORT) };\n        serial_port.init();\n\n        IrqMutex::new(serial_port)\n    };\n    pub static ref SERIAL1: IrqMutex<SerialPort> = {\n        let mut serial_port = unsafe { SerialPort::new(SERIAL1_IOPORT) };\n        serial_port.init();\n        IrqMutex::new(serial_port)\n    };\n    pub static ref SERIAL2: IrqMutex<SerialPort> = {\n        let mut serial_port = unsafe { SerialPort::new(SERIAL2_IOPORT) };\n        serial_port.init();\n        IrqMutex::new(serial_port)\n    };\n}\n\n#[doc(hidden)]\n#[allow(unreachable_code)]\npub fn _print0(args: ::core::fmt::Arguments) {\n    use core::fmt::Write;\n    // #[cfg(debug_assertions)]\n    x86_64::instructions::interrupts::without_interrupts(|| {\n        SERIAL0\n            .lock()\n            .write_fmt(args)\n            .expect(\"Printing to serial failed\");\n    });\n}\n\n/// Prints to the host through the logging serial interface.\n#[macro_export]\nmacro_rules! serial0_print {\n    ($($arg:tt)*) => {\n        $crate::serial::_print0(format_args!($($arg)*))\n    };\n}\n\n/// Prints to the host through the logging serial interface, appending a newline.\n#[macro_export]\nmacro_rules! serial0_println {\n    () => ($crate::serial0_print!(\"\\n\"));\n    ($fmt:expr) => ($crate::serial0_print!(concat!($fmt, \"\\n\")));\n    ($fmt:expr, $($arg:tt)*) => ($crate::serial0_print!(\n        concat!($fmt, \"\\n\"), $($arg)*));\n}\n\n#[doc(hidden)]\n#[allow(unreachable_code)]\npub fn _print1(args: ::core::fmt::Arguments) {\n    use core::fmt::Write;\n    // #[cfg(debug_assertions)]\n    x86_64::instructions::interrupts::without_interrupts(|| {\n        SERIAL1\n            .lock()\n            .write_fmt(args)\n            .expect(\"Printing to serial failed\");\n    });\n}\n\n#[inline]\npub fn serial1_recv() -> Option<u8> {\n    // #[cfg(debug_assertions)]\n    unsafe {\n        let line_sts = inb(SERIAL1_IOPORT + 5);\n        if line_sts & 0x1 != 0 {\n            return Some(inb(SERIAL1_IOPORT));\n        }\n        None\n    }\n    // #[cfg(not(debug_assertions))]\n    // None\n\n    // })\n}\n\n/// Prints to the host through the user serial interface.\n#[macro_export]\nmacro_rules! serial1_print {\n    ($($arg:tt)*) => {\n        $crate::serial::_print1(format_args!($($arg)*))\n    };\n}\n\n/// Prints to the host through the user serial interface, appending a newline.\n#[macro_export]\nmacro_rules! serial1_println {\n    () => ($crate::serial1_print!(\"\\r\\n\"));\n    ($fmt:expr) => ($crate::serial1_print!(concat!($fmt, \"\\r\\n\")));\n    ($fmt:expr, $($arg:tt)*) => ($crate::serial1_print!(\n        concat!($fmt, \"\\r\\n\"), $($arg)*));\n}\n\n#[doc(hidden)]\n#[allow(unreachable_code)]\npub fn _print2(args: ::core::fmt::Arguments) {\n    use core::fmt::Write;\n    // #[cfg(debug_assertions)]\n    x86_64::instructions::interrupts::without_interrupts(|| {\n        SERIAL2\n            .lock()\n            .write_fmt(args)\n            .expect(\"Printing to serial failed\");\n    });\n}\n\n/// Prints to the host through the TTY logging serial interface.\n#[macro_export]\nmacro_rules! serial2_print {\n    ($($arg:tt)*) => {\n        $crate::serial::_print2(format_args!($($arg)*))\n    };\n}\n\n/// Prints to the host through the TTY logging serial interface, appending a newline.\n#[macro_export]\nmacro_rules! serial2_println {\n    () => ($crate::serial2_print!(\"\\n\"));\n    ($fmt:expr) => ($crate::serial2_print!(concat!($fmt, \"\\n\")));\n    ($fmt:expr, $($arg:tt)*) => ($crate::serial2_print!(\n        concat!($fmt, \"\\n\"), $($arg)*));\n}\n"
  },
  {
    "path": "src/task/group.rs",
    "content": "use alloc::{\n    sync::{Arc, Weak},\n    vec::Vec,\n};\n\nuse crate::util::IrqMutex;\n\nuse super::{get_scheduler, signal::Signal, Task};\n\npub type PgId = i32;\n\npub struct TaskGroup {\n    pgid: PgId,\n    tasks: Vec<Weak<Task>>,\n}\n\nimpl TaskGroup {\n    pub(super) fn new(pgid: PgId) -> Arc<IrqMutex<TaskGroup>> {\n        Arc::new(IrqMutex::new(TaskGroup {\n            pgid,\n            tasks: Vec::new(),\n        }))\n    }\n\n    pub fn pgid(&self) -> PgId {\n        self.pgid\n    }\n\n    pub fn set_pgid(&mut self, pgid: PgId) {\n        self.pgid = pgid\n    }\n\n    pub fn add(&mut self, task: Weak<Task>) {\n        self.tasks.push(task)\n    }\n\n    pub fn remove(&mut self, task: &Weak<Task>) {\n        self.tasks.retain(|p| !Weak::ptr_eq(p, task));\n        if self.tasks.is_empty() {\n            get_scheduler().task_groups.lock().remove(&self.pgid);\n        }\n    }\n\n    pub fn gc_dropped_processes(&mut self) {\n        self.tasks.retain(|task| task.upgrade().is_some());\n        if self.tasks.is_empty() {\n            get_scheduler().task_groups.lock().remove(&self.pgid);\n        }\n    }\n\n    pub fn signal(&mut self, signal: Signal) {\n        for task in self.tasks.iter() {\n            get_scheduler().send_signal_to(task.upgrade().unwrap(), signal);\n        }\n    }\n}\n"
  },
  {
    "path": "src/task/mod.rs",
    "content": "use core::{\n    cell::UnsafeCell,\n    sync::atomic::{AtomicUsize, Ordering},\n};\n\nuse alloc::{\n    sync::{Arc, Weak},\n    vec::Vec,\n};\nuse atomic_refcell::AtomicRefCell;\nuse crossbeam_utils::atomic::AtomicCell;\nuse spin::Once;\nuse x86_64::structures::idt::PageFaultErrorCode;\n\nuse crate::{\n    arch::{\n        idt::{InterruptErrorFrame, InterruptFrame},\n        task::ArchTask,\n    },\n    fs::{\n        initramfs::{get_root, root::RootFs},\n        opened_file::{FileDesc, LocalOpenedFile, OpenedFileTable},\n        FileRef,\n    },\n    mem::addr::VirtAddr,\n    util::{ctypes::c_int, IrqMutex, KResult},\n};\n\nuse self::{\n    group::{PgId, TaskGroup},\n    scheduler::Scheduler,\n    signal::{SigSet, SignalDelivery, SignalMask},\n    vmem::Vmem,\n    wait_queue::WaitQueue,\n};\n\npub mod group;\npub mod scheduler;\npub mod signal;\npub mod vmem;\npub mod wait_queue;\n\npub static SCHEDULER: Once<Arc<Scheduler>> = Once::new();\npub static JOIN_WAIT_QUEUE: WaitQueue = WaitQueue::new();\npub fn init() {\n    SCHEDULER.call_once(Scheduler::new);\n}\n\npub fn get_scheduler() -> &'static Arc<Scheduler> {\n    SCHEDULER.get().unwrap()\n}\n\npub fn current_task() -> Arc<Task> {\n    get_scheduler().current_task()\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]\npub struct TaskId(usize);\n\nimpl TaskId {\n    pub const fn new(pid: usize) -> Self {\n        Self(pid)\n    }\n\n    fn allocate() -> Self {\n        static NEXT_PID: AtomicUsize = AtomicUsize::new(1);\n        Self::new(NEXT_PID.fetch_add(1, Ordering::AcqRel))\n    }\n\n    pub fn as_usize(&self) -> usize {\n        self.0\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq)]\npub enum TaskState {\n    Runnable,\n    Waiting,\n    ExitedWith(c_int),\n}\n\npub struct Task {\n    sref: Weak<Task>,\n\n    arch: UnsafeCell<ArchTask>,\n    state: AtomicCell<TaskState>,\n\n    pid: TaskId,\n\n    pub(crate) start_time: Once<usize>,\n\n    pub(crate) root_fs: Arc<IrqMutex<RootFs>>,\n    pub(crate) opened_files: Arc<IrqMutex<OpenedFileTable>>,\n\n    parent: IrqMutex<Weak<Task>>,\n    pub(crate) children: Arc<IrqMutex<Vec<Arc<Task>>>>,\n    pub(crate) group: AtomicRefCell<Weak<IrqMutex<TaskGroup>>>,\n\n    vmem: Arc<IrqMutex<Vmem>>,\n\n    pub(crate) signals: Arc<IrqMutex<SignalDelivery>>,\n    signaled_frame: AtomicCell<Option<InterruptFrame>>,\n    sigset: Arc<IrqMutex<SigSet>>,\n}\n\nunsafe impl Sync for Task {}\n\nimpl Task {\n    pub fn new_idle(sched: &mut Scheduler) -> Arc<Task> {\n        let pid = TaskId::new(0);\n        let group = sched.find_or_create_group(0);\n        let t = Arc::new_cyclic(|sref| Self {\n            sref: sref.clone(),\n            arch: UnsafeCell::new(ArchTask::new_idle()),\n            state: AtomicCell::new(TaskState::Runnable),\n            pid,\n            start_time: Once::new(),\n            parent: IrqMutex::new(Weak::new()),\n            children: Arc::new(IrqMutex::new(Vec::new())),\n            root_fs: Arc::new(IrqMutex::new(get_root().unwrap().clone())),\n            opened_files: Arc::new(IrqMutex::new(OpenedFileTable::new())),\n            vmem: Arc::new(IrqMutex::new(Vmem::new())),\n            signaled_frame: AtomicCell::new(None),\n            signals: Arc::new(IrqMutex::new(SignalDelivery::new())),\n            sigset: Arc::new(IrqMutex::new(SigSet::ZERO)),\n            group: AtomicRefCell::new(Arc::downgrade(&group)),\n        });\n        group.lock().add(Arc::downgrade(&t));\n        t\n    }\n\n    pub fn new_kernel(sched: &Scheduler, entry_point: fn(), enable_interrupts: bool) -> Arc<Task> {\n        let pid = TaskId::allocate();\n        let group = sched.find_or_create_group(0);\n        let t = Arc::new_cyclic(|sref| Self {\n            sref: sref.clone(),\n            arch: UnsafeCell::new(ArchTask::new_kernel(\n                VirtAddr::new(entry_point as usize),\n                enable_interrupts,\n            )),\n            group: AtomicRefCell::new(Arc::downgrade(&group)),\n            state: AtomicCell::new(TaskState::Runnable),\n            pid,\n            start_time: Once::new(),\n            parent: IrqMutex::new(Weak::new()),\n            children: Arc::new(IrqMutex::new(Vec::new())),\n            root_fs: Arc::new(IrqMutex::new(get_root().unwrap().clone())),\n            opened_files: Arc::new(IrqMutex::new(OpenedFileTable::new())),\n            vmem: Arc::new(IrqMutex::new(Vmem::new())),\n            signaled_frame: AtomicCell::new(None),\n            signals: Arc::new(IrqMutex::new(SignalDelivery::new())),\n            sigset: Arc::new(IrqMutex::new(SigSet::ZERO)),\n        });\n        group.lock().add(Arc::downgrade(&t));\n        t\n    }\n\n    pub fn exec(&self, file: FileRef, argv: &[&[u8]], envp: &[&[u8]]) -> KResult<()> {\n        {\n            self.opened_files.lock().close_cloexec_files();\n            self.arch_mut().address_space.with_mapper(|mut mapper| {\n                self.vmem.lock().clear(&mut mapper);\n            });\n            *self.signals.lock() = SignalDelivery::new();\n            *self.sigset.lock() = SigSet::ZERO;\n            self.signaled_frame.store(None);\n        }\n        let lock = &mut self.vmem.lock();\n        unsafe { self.vmem.force_unlock() };\n        self.arch_mut().exec(lock, file, argv, envp)\n    }\n\n    pub fn make_child(&self, arch: UnsafeCell<ArchTask>) -> Arc<Task> {\n        let pid = TaskId::allocate();\n\n        let group = self.group.borrow().upgrade().unwrap();\n        let new = Arc::new_cyclic(|sref| Self {\n            sref: sref.clone(),\n            arch,\n            root_fs: Arc::new(IrqMutex::new(self.root_fs.lock().clone())),\n            opened_files: Arc::new(IrqMutex::new(self.opened_files.lock().clone())), // todo: deeper clone\n            children: Arc::new(IrqMutex::new(Vec::new())),\n            parent: IrqMutex::new(Weak::new()),\n            group: AtomicRefCell::new(Arc::downgrade(&group)),\n            state: AtomicCell::new(TaskState::Runnable),\n            pid,\n            start_time: Once::new(),\n            vmem: Arc::new(IrqMutex::new(Vmem::new())),\n            signals: Arc::new(IrqMutex::new(SignalDelivery::new())),\n            signaled_frame: AtomicCell::new(None),\n            sigset: Arc::new(IrqMutex::new(SigSet::ZERO)),\n        });\n        self.add_child(new.clone());\n        new.signals.lock().clone_from(&self.signals.lock());\n        new.vmem.lock().fork_from(&self.vmem.lock());\n        group.lock().add(Arc::downgrade(&new));\n        get_scheduler().push_runnable(new.clone(), false);\n        new\n    }\n\n    pub fn fork(&self) -> Arc<Task> {\n        let arch = UnsafeCell::new(self.arch_mut().fork().unwrap());\n        self.make_child(arch)\n    }\n\n    pub fn clone_process(\n        &self,\n        entry_point: VirtAddr,\n        user_stack: VirtAddr,\n        args: VirtAddr,\n        r8: usize,\n        r9: usize,\n        syscall_frame: &InterruptFrame,\n    ) -> Arc<Task> {\n        let arch = UnsafeCell::new(\n            self.arch_mut()\n                .clone_process(entry_point, user_stack, args, r8, r9, syscall_frame)\n                .unwrap(),\n        );\n        let pid = TaskId::allocate();\n\n        let group = self.group.borrow().upgrade().unwrap();\n        let t = Arc::new_cyclic(|sref| Self {\n            sref: sref.clone(),\n            arch,\n            opened_files: Arc::new(IrqMutex::new(self.opened_files.lock().clone())), // todo: deeper clone\n            state: AtomicCell::new(TaskState::Runnable),\n            pid,\n            start_time: Once::new(),\n            root_fs: Arc::new(IrqMutex::new(self.root_fs.lock().clone())), // todo: actually fork the root fs\n            children: Arc::new(IrqMutex::new(Vec::new())),\n            parent: IrqMutex::new(Weak::new()),\n            group: AtomicRefCell::new(Arc::downgrade(&group)),\n            signals: Arc::new(IrqMutex::new(self.signals.lock().clone())),\n            signaled_frame: AtomicCell::new(None),\n            sigset: Arc::new(IrqMutex::new(SigSet::ZERO)),\n            vmem: self.vmem.clone(), // important: we don't fork_from here\n        });\n        self.add_child(t.clone());\n        group.lock().add(Arc::downgrade(&t));\n        get_scheduler().push_runnable(t.clone(), false);\n        t\n    }\n\n    fn add_child(&self, child: Arc<Task>) {\n        let mut children = self.children.lock();\n        child.set_parent(self.sref.clone());\n        children.push(child);\n    }\n\n    #[allow(clippy::mut_from_ref)] // FIXME\n    pub fn arch_mut(&self) -> &mut ArchTask {\n        unsafe { &mut *self.arch.get() }\n    }\n\n    pub fn pid(&self) -> TaskId {\n        self.pid\n    }\n\n    pub fn ppid(&self) -> TaskId {\n        if let Some(parent) = self.parent.lock().upgrade() {\n            parent.pid\n        } else {\n            TaskId::new(0)\n        }\n    }\n\n    pub fn pgid(&self) -> Option<PgId> {\n        Some(self.group.borrow().upgrade()?.lock().pgid())\n    }\n\n    pub fn get_state(&self) -> TaskState {\n        self.state.load()\n    }\n\n    pub fn set_state(&self, state: TaskState) {\n        if !matches!(self.get_state(), TaskState::ExitedWith(_)) {\n            self.state.store(state)\n        } else {\n            unreachable!();\n        }\n    }\n\n    fn set_parent(&self, parent: Weak<Task>) {\n        *self.parent.lock() = parent;\n    }\n\n    pub fn belongs_to_group(&self, pg: &Weak<IrqMutex<TaskGroup>>) -> bool {\n        Weak::ptr_eq(&self.group.borrow(), pg)\n    }\n\n    pub fn get_opened_file_by_fd(&self, fd: FileDesc) -> KResult<LocalOpenedFile> {\n        Ok(self.opened_files.lock().get(fd)?.clone())\n    }\n\n    pub fn vmem(&self) -> Arc<IrqMutex<Vmem>> {\n        self.vmem.clone()\n    }\n\n    pub fn handle_page_fault(\n        &self,\n        faulted_addr: VirtAddr,\n        stack_frame: InterruptErrorFrame,\n        reason: PageFaultErrorCode,\n    ) -> KResult<()> {\n        let addr_space = &mut self.arch_mut().address_space;\n        self.vmem\n            .try_lock()?\n            .handle_page_fault(addr_space, faulted_addr, stack_frame, reason)\n    }\n\n    pub fn set_signal_mask(\n        &self,\n        how: SignalMask,\n        set: VirtAddr,\n        oldset: &mut VirtAddr,\n        _length: usize,\n    ) -> KResult<()> {\n        let mut sigset = self.sigset.lock();\n        if !oldset.is_null() {\n            let slice = sigset.as_raw_slice();\n            assert_eq!(slice.len(), 8);\n            unsafe { oldset.write_bytes_user(slice) }?;\n        }\n\n        if !set.is_null() {\n            let new_set = unsafe { set.read_user::<[u8; 8]>()? };\n            let new_set = SigSet::new(new_set);\n            match how {\n                SignalMask::Block => *sigset |= new_set,\n                SignalMask::Unblock => *sigset &= !new_set,\n                SignalMask::Set => *sigset = new_set,\n            }\n        }\n\n        Ok(())\n    }\n\n    pub fn has_pending_signals(&self) -> bool {\n        self.signals.lock().is_pending()\n    }\n}\n"
  },
  {
    "path": "src/task/scheduler.rs",
    "content": "use alloc::{\n    collections::{BTreeMap, VecDeque},\n    sync::Arc,\n    vec::Vec,\n};\nuse spin::RwLock;\nuse x86_64::instructions::interrupts::enable_and_hlt;\n\nuse crate::{\n    arch::{\n        self,\n        idt::InterruptFrame,\n        startup_init,\n        task::{arch_context_switch, ArchTask},\n        time,\n    },\n    fs::POLL_WAIT_QUEUE,\n    kerror,\n    mem::addr::VirtAddr,\n    task::JOIN_WAIT_QUEUE,\n    util::{ctypes::c_int, IrqMutex, KResult},\n};\n\nuse super::{\n    get_scheduler,\n    group::{PgId, TaskGroup},\n    signal::{SigAction, Signal, SIGCHLD},\n    wait_queue::WaitQueue,\n    Task, TaskId, TaskState,\n};\n\npub struct Scheduler {\n    tasks: Arc<IrqMutex<BTreeMap<TaskId, Arc<Task>>>>,\n\n    run_queue: Arc<IrqMutex<VecDeque<Arc<Task>>>>,\n    waiting_queue: Arc<IrqMutex<VecDeque<Arc<Task>>>>,\n    #[allow(clippy::type_complexity)]\n    deadline_waiting_queue: Arc<IrqMutex<VecDeque<(Arc<Task>, usize)>>>,\n\n    idle_thread: Option<Arc<Task>>,\n    preempt_task: Option<Arc<Task>>,\n\n    current_task: Arc<RwLock<Option<Arc<Task>>>>,\n    exited_tasks: Arc<IrqMutex<Vec<Arc<Task>>>>,\n\n    pub(super) task_groups: IrqMutex<BTreeMap<PgId, Arc<IrqMutex<TaskGroup>>>>,\n}\n\nimpl Scheduler {\n    pub fn new() -> Arc<Self> {\n        let mut s = Self {\n            tasks: Arc::new(IrqMutex::new(BTreeMap::new())),\n            run_queue: Arc::new(IrqMutex::new(VecDeque::new())),\n            waiting_queue: Arc::new(IrqMutex::new(VecDeque::new())),\n            deadline_waiting_queue: Arc::new(IrqMutex::new(VecDeque::new())),\n            idle_thread: None,\n            preempt_task: None,\n            current_task: Arc::new(RwLock::new(None)),\n            task_groups: IrqMutex::new(BTreeMap::new()),\n            exited_tasks: Arc::new(IrqMutex::new(Vec::new())),\n        };\n        let idle_thread = Task::new_idle(&mut s);\n        let init_task = Task::new_kernel(&s, startup_init, false);\n        s.push_runnable(init_task, false);\n        let preempt_task = Task::new_kernel(&s, preempt, false);\n        let reaper_task = Task::new_kernel(&s, reap, true);\n        s.idle_thread = Some(idle_thread);\n        s.preempt_task = Some(preempt_task);\n        s.push_runnable(reaper_task, false);\n        Arc::new(s)\n    }\n\n    pub fn push_runnable(&self, task: Arc<Task>, priority: bool) {\n        task.state.store(TaskState::Runnable);\n        let mut queue = self.run_queue.lock();\n        self.tasks.lock().try_insert(task.pid, task.clone()).ok();\n        self.waiting_queue.lock().retain(|t| t.pid != task.pid);\n        let already_in_queue = queue.iter().any(|t| t.pid == task.pid);\n        if !already_in_queue {\n            if priority {\n                queue.push_front(task);\n            } else {\n                queue.push_back(task);\n            }\n        }\n    }\n\n    pub fn push_waiting(&self, task: Arc<Task>) {\n        task.state.store(TaskState::Waiting);\n        let mut queue = self.waiting_queue.lock();\n        self.tasks.lock().try_insert(task.pid, task.clone()).ok();\n        self.run_queue.lock().retain(|t| t.pid != task.pid);\n        self.deadline_waiting_queue\n            .lock()\n            .retain(|(t, _)| t.pid != task.pid);\n        let already_in_queue = queue.iter().any(|t| t.pid == task.pid);\n        if !already_in_queue {\n            queue.push_back(task);\n        }\n    }\n\n    pub fn push_deadline_waiting(&self, task: Arc<Task>, duration: usize) {\n        task.state.store(TaskState::Waiting);\n        let mut queue = self.deadline_waiting_queue.lock();\n        self.tasks.lock().try_insert(task.pid, task.clone()).ok();\n        self.run_queue.lock().retain(|t| t.pid != task.pid);\n        self.waiting_queue.lock().retain(|t| t.pid != task.pid);\n        let already_in_queue = queue.iter().any(|(t, _)| t.pid == task.pid);\n        if !already_in_queue {\n            let deadline = arch::time::get_uptime_ms() + duration;\n            queue.push_back((task, deadline));\n        }\n    }\n\n    fn check_deadline(&self) {\n        let time = arch::time::get_uptime_ms();\n        let mut queue = self.deadline_waiting_queue.lock();\n        for _ in 0..queue.len() {\n            if let Some((task, deadline)) = queue.pop_front() {\n                if deadline <= time {\n                    // time's up!\n                    drop(queue);\n                    self.push_runnable(task, true);\n                    queue = self.deadline_waiting_queue.lock();\n                } else {\n                    queue.push_back((task, deadline));\n                }\n            }\n        }\n    }\n\n    pub fn current_task_opt(&self) -> Option<Arc<Task>> {\n        let current = self.current_task.try_read()?;\n        let clone = current.as_ref().cloned();\n        drop(current);\n        clone\n    }\n\n    pub fn current_task(&self) -> Arc<Task> {\n        let current = self.current_task.read();\n        let clone = current.as_ref().unwrap().clone();\n        drop(current);\n        clone\n    }\n\n    pub fn find_task(&self, pid: TaskId) -> Option<Arc<Task>> {\n        self.tasks.lock().get(&pid).cloned()\n    }\n\n    pub fn find_group(&self, pgid: PgId) -> Option<Arc<IrqMutex<TaskGroup>>> {\n        self.task_groups.lock().get(&pgid).cloned()\n    }\n\n    pub fn find_or_create_group(&self, pgid: PgId) -> Arc<IrqMutex<TaskGroup>> {\n        self.find_group(pgid).unwrap_or_else(|| {\n            let g = TaskGroup::new(pgid);\n            self.task_groups.lock().insert(pgid, g.clone());\n            g\n        })\n    }\n\n    pub fn exit_current(&self, status: c_int) {\n        let current = self.current_task();\n        if current.pid.as_usize() == 1 {\n            panic!(\"init (pid=1) tried to exit with status {}\", status);\n        }\n\n        current.set_state(TaskState::ExitedWith(status));\n        if let Some(parent) = current.parent.lock().upgrade() {\n            let mut parent_signals = parent.signals.lock();\n            if parent_signals.get_action(SIGCHLD) == SigAction::Ignore {\n                parent.children.lock().retain(|p| p.pid != current.pid);\n            } else {\n                log::debug!(\"Sending SIGCHLD to {}\", parent.pid.as_usize());\n                parent_signals.signal(SIGCHLD);\n            }\n        }\n\n        current.opened_files.lock().close_all();\n        self.run_queue.lock().retain(|t| t.pid != current.pid);\n        self.waiting_queue.lock().retain(|t| t.pid != current.pid);\n        self.deadline_waiting_queue\n            .lock()\n            .retain(|t| t.0.pid != current.pid);\n        self.tasks.lock().remove(&current.pid);\n        self.exited_tasks.lock().push(current);\n        self.wake_all(&JOIN_WAIT_QUEUE);\n        self.preempt();\n    }\n\n    pub fn wake_all(&self, queue: &WaitQueue) {\n        let mut q = queue.queue.lock();\n        while let Some(proc) = q.pop_front() {\n            self.resume_task(proc)\n        }\n    }\n\n    pub fn send_signal_to(&self, task: Arc<Task>, signal: Signal) {\n        task.signals.lock().signal(signal);\n        self.resume_task(task);\n    }\n\n    pub fn resume_task(&self, task: Arc<Task>) {\n        self.push_runnable(task, false);\n    }\n\n    pub fn try_delivering_signal(\n        &self,\n        frame: &mut InterruptFrame,\n        syscall_result: isize,\n    ) -> KResult<()> {\n        let current = self.current_task();\n        if let Some((signal, sigaction)) = current.signals.lock().pop_pending() {\n            let mut set = current.sigset.lock();\n            if !set.get(signal as usize).as_deref().unwrap_or(&true) {\n                match sigaction {\n                    SigAction::Ignore => {}\n                    SigAction::Terminate => {\n                        log::trace!(\n                            \"terminating pid {} by signal {:?}\",\n                            current.pid.as_usize(),\n                            signal\n                        );\n                        self.exit_current(1);\n                    }\n                    SigAction::Handler { handler } => {\n                        log::trace!(\n                            \"delivering signal {:?} to pid {} (handler addr {:#x})\",\n                            signal,\n                            current.pid.as_usize(),\n                            handler as usize\n                        );\n                        current.signaled_frame.store(Some(*frame));\n                        set.set(signal as usize, true);\n                        ArchTask::setup_signal_stack(\n                            frame,\n                            signal,\n                            VirtAddr::new(handler as usize),\n                            syscall_result,\n                        )?;\n                    }\n                }\n            }\n        }\n\n        Ok(())\n    }\n\n    pub fn restore_signaled_user_stack(&self, current_frame: &mut InterruptFrame) {\n        let current = self.current_task();\n        if let Some(signaled_frame) = current.signaled_frame.swap(None) {\n            *current_frame = signaled_frame;\n        } else {\n            log::warn!(\"User called sigreturn(2) while it is not signaled\");\n        }\n    }\n\n    pub fn reap_dead(&self) {\n        let mut exited = self.exited_tasks.lock();\n\n        for task in exited.iter() {\n            self.tasks.lock().remove(&task.pid);\n            self.run_queue.lock().retain(|t| t.pid != task.pid);\n            self.waiting_queue.lock().retain(|t| t.pid != task.pid);\n            JOIN_WAIT_QUEUE.queue.lock().retain(|t| t.pid != task.pid);\n            POLL_WAIT_QUEUE.queue.lock().retain(|t| t.pid != task.pid);\n            if let Some(group) = task.group.borrow_mut().upgrade() {\n                group.lock().gc_dropped_processes();\n            }\n            // assert_eq!(Arc::strong_count(task), 1, \"PID {} has dangling references\", task.pid.as_usize());\n        }\n        exited.clear();\n    }\n\n    pub fn preempt(&self) {\n        let current = self.current_task.read();\n        if let Some(current_task) = current.as_ref().cloned() {\n            // log::debug!(\"Switching from PID {:?} to preempt task\", current_task.pid);\n            drop(current);\n            arch_context_switch(\n                current_task.arch_mut(),\n                self.preempt_task.as_ref().unwrap().arch_mut(),\n            );\n        } else {\n            // log::debug!(\"Switching from idle thread to preempt task\");\n            drop(current);\n            arch_context_switch(\n                self.idle_thread.as_ref().unwrap().arch_mut(),\n                self.preempt_task.as_ref().unwrap().arch_mut(),\n            );\n        }\n    }\n\n    pub fn sleep(&self, duration: Option<usize>) -> KResult<()> {\n        let task = self.current_task();\n\n        if let Some(duration) = duration {\n            self.push_deadline_waiting(task.clone(), duration);\n        } else {\n            self.push_waiting(task.clone());\n        }\n        self.preempt();\n\n        if task.has_pending_signals() {\n            Err(kerror!(EINTR, \"sleep(): pending signals\"))\n        } else {\n            Ok(())\n        }\n    }\n}\n\npub fn switch() {\n    let sched = get_scheduler();\n\n    sched.check_deadline();\n    let mut queue = sched.run_queue.lock();\n    let current = sched.current_task.try_write();\n    if current.is_none() {\n        log::warn!(\"Couldn't lock current task for writing.\");\n        return;\n    }\n    let mut current = current.unwrap();\n    let mut task = None;\n    loop {\n        let t = queue.pop_front();\n        if let Some(t) = t {\n            if t.get_state() == TaskState::Runnable {\n                task = Some(t);\n                break;\n            } else {\n                // we'll take the opportunity to purge the run queue of sleeping/dead tasks\n            }\n        } else {\n            break;\n        }\n    }\n\n    if let Some(task) = task {\n        if let Some(current_task) = current.as_ref() {\n            if current_task.pid != task.pid {\n                queue.push_back(current_task.clone());\n            }\n        }\n\n        *current = Some(task.clone());\n        drop(queue);\n        drop(current);\n        task.start_time.call_once(time::get_uptime_ms);\n        // log::debug!(\"Switching from preempt task to PID {}\", task.pid.as_usize());\n        arch_context_switch(\n            sched.preempt_task.as_ref().unwrap().arch_mut(),\n            task.arch_mut(),\n        );\n    } else {\n        if let Some(current_task) = current.as_ref().cloned() {\n            let state = { current_task.get_state() };\n            if state == TaskState::Runnable {\n                drop(current);\n                drop(queue);\n                // log::debug!(\n                //     \"Switching from preempt task to PID {}\",\n                //     current_task.pid.as_usize()\n                // );\n                arch_context_switch(\n                    sched.preempt_task.as_ref().unwrap().arch_mut(),\n                    current_task.arch_mut(),\n                );\n                return;\n            }\n        }\n\n        *current = None;\n        drop(current);\n        drop(queue);\n\n        // log::debug!(\"Switching from preempt task to idle thread\");\n        arch_context_switch(\n            sched.preempt_task.as_ref().unwrap().arch_mut(),\n            sched.idle_thread.as_ref().unwrap().arch_mut(),\n        );\n    }\n}\n\nfn reap() {\n    let scheduler = get_scheduler();\n    loop {\n        scheduler.reap_dead();\n        enable_and_hlt();\n    }\n}\n\nfn preempt() {\n    loop {\n        switch();\n    }\n}\n"
  },
  {
    "path": "src/task/signal.rs",
    "content": "use bitvec::prelude::*;\n\nuse crate::{\n    kbail,\n    util::{ctypes::c_int, error::KResult},\n};\n\npub type Signal = c_int;\n#[allow(unused)]\npub const SIGHUP: Signal = 1;\n#[allow(unused)]\npub const SIGINT: Signal = 2;\n#[allow(unused)]\npub const SIGQUIT: Signal = 3;\n#[allow(unused)]\npub const SIGILL: Signal = 4;\n#[allow(unused)]\npub const SIGTRAP: Signal = 5;\n#[allow(unused)]\npub const SIGABRT: Signal = 6;\n#[allow(unused)]\npub const SIGBUS: Signal = 7;\n#[allow(unused)]\npub const SIGFPE: Signal = 8;\n#[allow(unused)]\npub const SIGKILL: Signal = 9;\n#[allow(unused)]\npub const SIGUSR1: Signal = 10;\n#[allow(unused)]\npub const SIGSEGV: Signal = 11;\n#[allow(unused)]\npub const SIGUSR2: Signal = 12;\n#[allow(unused)]\npub const SIGPIPE: Signal = 13;\n#[allow(unused)]\npub const SIGALRM: Signal = 14;\n#[allow(unused)]\npub const SIGTERM: Signal = 15;\n#[allow(unused)]\npub const SIGSTKFLT: Signal = 16;\n#[allow(unused)]\npub const SIGCHLD: Signal = 17;\n#[allow(unused)]\npub const SIGCONT: Signal = 18;\n#[allow(unused)]\npub const SIGSTOP: Signal = 19;\n#[allow(unused)]\npub const SIGTSTP: Signal = 20;\n#[allow(unused)]\npub const SIGTTIN: Signal = 21;\n#[allow(unused)]\npub const SIGTTOU: Signal = 22;\n#[allow(unused)]\npub const SIGURG: Signal = 23;\n#[allow(unused)]\npub const SIGXCPU: Signal = 24;\n#[allow(unused)]\npub const SIGXFSZ: Signal = 25;\n#[allow(unused)]\npub const SIGVTALRM: Signal = 26;\n#[allow(unused)]\npub const SIGPROF: Signal = 27;\n#[allow(unused)]\npub const SIGWINCH: Signal = 28;\n#[allow(unused)]\npub const SIGIO: Signal = 29;\n#[allow(unused)]\npub const SIGPWR: Signal = 30;\n#[allow(unused)]\npub const SIGSYS: Signal = 31;\n\nconst SIGMAX: c_int = 32;\n\npub const SIG_DFL: usize = 0;\npub const SIG_IGN: usize = 1;\npub const SIG_ERR: usize = usize::MAX;\n\n#[derive(Clone, Copy, PartialEq, Eq)]\npub enum SigAction {\n    Ignore,\n    Terminate,\n    Handler { handler: fn() },\n}\n\npub const DEFAULT_ACTIONS: [SigAction; SIGMAX as usize] = [\n    /* (unused) */ SigAction::Ignore,\n    /* SIGHUP */ SigAction::Terminate,\n    /* SIGINT */ SigAction::Terminate,\n    /* SIGQUIT */ SigAction::Terminate,\n    /* SIGILL */ SigAction::Terminate,\n    /* SIGTRAP */ SigAction::Ignore,\n    /* SIGABRT */ SigAction::Terminate,\n    /* SIGBUS */ SigAction::Terminate,\n    /* SIGFPE */ SigAction::Terminate,\n    /* SIGKILL */ SigAction::Terminate,\n    /* SIGUSR1 */ SigAction::Ignore,\n    /* SIGSEGV */ SigAction::Terminate,\n    /* SIGUSR2 */ SigAction::Ignore,\n    /* SIGPIPE */ SigAction::Ignore,\n    /* SIGALRM */ SigAction::Ignore,\n    /* SIGTERM */ SigAction::Terminate,\n    /* SIGSTKFLT */ SigAction::Ignore,\n    /* SIGCHLD */ SigAction::Ignore,\n    /* SIGCONT */ SigAction::Terminate,\n    /* SIGSTOP */ SigAction::Ignore,\n    /* SIGTSTP */ SigAction::Ignore,\n    /* SIGTTIN */ SigAction::Ignore,\n    /* SIGTTOU */ SigAction::Ignore,\n    /* SIGURG */ SigAction::Ignore,\n    /* SIGXCPU */ SigAction::Ignore,\n    /* SIGXFSZ */ SigAction::Ignore,\n    /* SIGVTALRM */ SigAction::Ignore,\n    /* SIGPROF */ SigAction::Ignore,\n    /* SIGWINCH */ SigAction::Ignore,\n    /* SIGIO */ SigAction::Ignore,\n    /* SIGPWR */ SigAction::Ignore,\n    /* SIGSYS */ SigAction::Ignore,\n];\n\n#[derive(Clone)]\npub struct SignalDelivery {\n    pending: u32,\n    actions: [SigAction; SIGMAX as usize],\n}\n\nimpl Default for SignalDelivery {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl SignalDelivery {\n    pub fn new() -> SignalDelivery {\n        SignalDelivery {\n            pending: 0,\n            actions: DEFAULT_ACTIONS,\n        }\n    }\n\n    pub fn get_action(&self, signal: Signal) -> SigAction {\n        self.actions[signal as usize]\n    }\n\n    pub fn set_action(&mut self, signal: Signal, action: SigAction) -> KResult<()> {\n        if signal > SIGMAX {\n            kbail!(EINVAL, \"set_action(): signal out of range\");\n        }\n\n        self.actions[signal as usize] = action;\n        Ok(())\n    }\n\n    pub fn is_pending(&self) -> bool {\n        self.pending != 0\n    }\n\n    pub fn signal(&mut self, signal: Signal) {\n        self.pending |= 1 << signal\n    }\n\n    pub fn pop_pending(&mut self) -> Option<(Signal, SigAction)> {\n        if self.pending == 0 {\n            return None;\n        }\n\n        let signal = self.pending.trailing_zeros();\n        self.pending &= !(1 << signal);\n        Some((signal as Signal, self.actions[signal as usize]))\n    }\n}\n\npub type SigSet = BitArray<[u8; 8], LocalBits>;\n\n#[derive(Clone, Copy, Debug)]\npub enum SignalMask {\n    Block,\n    Unblock,\n    Set,\n}\n"
  },
  {
    "path": "src/task/vmem.rs",
    "content": "use core::sync::atomic::AtomicUsize;\n\nuse alloc::vec::Vec;\nuse x86::controlregs::cr3;\nuse x86_64::structures::{idt::PageFaultErrorCode, paging::PageTableFlags};\n\nuse crate::{\n    arch::idt::InterruptErrorFrame,\n    backtrace,\n    fs::{\n        opened_file::{FileDesc, OpenFlags},\n        FileRef,\n    },\n    kbail, kerror,\n    mem::{\n        addr::VirtAddr,\n        addr_space::AddressSpace,\n        allocator::{alloc_kernel_frames, free_kernel_frames, PageAllocator},\n        consts::{PAGE_SIZE, USER_STACK_TOP, USER_VALLOC_BASE},\n        paging::{\n            mapper::Mapper,\n            units::{AllocatedFrames, Frame, FrameRange, MemoryUnit, Page, PageRange},\n        },\n    },\n    task::{current_task, get_scheduler, signal::SIGSEGV},\n    userland::buffer::UserBufferMut,\n    util::{align_up, KResult},\n};\n\nbitflags::bitflags! {\n    #[derive(Debug, Clone, Copy, PartialEq, Eq)]\n    pub struct MMapProt: u64 {\n        const PROT_READ = 0x1;\n        const PROT_WRITE = 0x2;\n        const PROT_EXEC = 0x4;\n        const PROT_NONE = 0x0;\n    }\n}\n\nbitflags::bitflags! {\n    #[derive(Debug, Clone, Copy, PartialEq, Eq)]\n    pub struct MMapFlags: u64 {\n        const MAP_PRIVATE   = 0x02;\n        const MAP_FIXED     = 0x10;\n        const MAP_ANONYMOUS = 0x20;\n    }\n}\n\nimpl From<MMapProt> for PageTableFlags {\n    fn from(e: MMapProt) -> Self {\n        let mut res = PageTableFlags::empty();\n\n        res.insert(PageTableFlags::PRESENT);\n        res.insert(PageTableFlags::USER_ACCESSIBLE);\n\n        if !e.contains(MMapProt::PROT_EXEC) {\n            res.insert(PageTableFlags::NO_EXECUTE);\n        }\n\n        if e.contains(MMapProt::PROT_WRITE) {\n            res.insert(PageTableFlags::WRITABLE);\n        }\n\n        res\n    }\n}\n\n#[derive(Clone)]\npub enum MMapKind {\n    Anonymous,\n    File {\n        file: FileRef,\n        offset: usize,\n        size: usize,\n    },\n}\n\n#[derive(Clone)]\npub struct VmemArea {\n    start_addr: VirtAddr,\n    end_addr: VirtAddr,\n    flags: MMapFlags,\n    pub(crate) prot: MMapProt,\n    kind: MMapKind,\n}\n\nimpl VmemArea {\n    pub const fn new(\n        start_addr: VirtAddr,\n        end_addr: VirtAddr,\n        flags: MMapFlags,\n        prot: MMapProt,\n        kind: MMapKind,\n    ) -> Self {\n        Self {\n            start_addr,\n            end_addr,\n            flags,\n            prot,\n            kind,\n        }\n    }\n\n    pub const fn null() -> Self {\n        Self {\n            start_addr: VirtAddr::null(),\n            end_addr: VirtAddr::null(),\n            flags: MMapFlags::empty(),\n            prot: MMapProt::PROT_NONE,\n            kind: MMapKind::Anonymous,\n        }\n    }\n\n    pub fn contains_addr(&self, addr: VirtAddr) -> bool {\n        (self.start_addr..self.end_addr).contains(&addr)\n    }\n\n    pub fn overlaps_range(&self, start: VirtAddr, end: VirtAddr) -> bool {\n        self.contains_addr(start) || self.contains_addr(end)\n    }\n\n    pub fn start_address(&self) -> VirtAddr {\n        self.start_addr\n    }\n\n    pub fn end_address(&self) -> VirtAddr {\n        self.end_addr\n    }\n\n    pub fn size_in_bytes(&self) -> usize {\n        self.end_addr.value() - self.start_addr.value()\n    }\n\n    pub fn merge_with(&mut self, other: Self) -> KResult<()> {\n        if other.size_in_bytes() == 0 {\n            return Ok(());\n        }\n        if other.start_addr != self.end_addr && other.end_addr != self.start_addr {\n            kbail!(\"Cannot merge non-contiguous areas\");\n        }\n        if other.start_addr < self.start_addr {\n            self.start_addr = other.start_addr;\n        }\n        if other.end_addr > self.end_addr {\n            self.end_addr = other.end_addr;\n        }\n\n        Ok(())\n    }\n}\n\npub struct Vmem {\n    areas: Vec<VmemArea>,\n    next_id: AtomicUsize,\n    page_allocator: PageAllocator,\n}\n\nimpl Vmem {\n    pub fn new() -> Self {\n        let mut page_allocator = PageAllocator::new_vec();\n        unsafe {\n            page_allocator.insert_free_region(PageRange::new(\n                Page::containing_address(VirtAddr::new(PAGE_SIZE)),\n                Page::containing_address(USER_STACK_TOP),\n            ))\n        }\n        Self {\n            areas: Vec::new(),\n            next_id: AtomicUsize::new(0),\n            page_allocator,\n        }\n    }\n\n    pub fn area_containing_mut(\n        &mut self,\n        start_addr: VirtAddr,\n        end_addr: VirtAddr,\n    ) -> Option<&mut VmemArea> {\n        self.areas\n            .iter_mut()\n            .find(|area| area.contains_addr(start_addr) && area.contains_addr(end_addr))\n    }\n\n    pub fn area_containing(&self, start_addr: VirtAddr, end_addr: VirtAddr) -> Option<&VmemArea> {\n        self.areas\n            .iter()\n            .find(|area| area.contains_addr(start_addr) && area.contains_addr(end_addr))\n    }\n\n    pub fn add_area(\n        &mut self,\n        start_addr: VirtAddr,\n        end_addr: VirtAddr,\n        flags: MMapFlags,\n        prot: MMapProt,\n        kind: MMapKind,\n    ) -> KResult<()> {\n        if self.area_containing_mut(start_addr, end_addr).is_some() {\n            self.log();\n            panic!(\"Cannot add vmem area that already exists\");\n        }\n        self.areas.push(VmemArea {\n            start_addr,\n            end_addr,\n            flags,\n            prot,\n            kind,\n        });\n        self.areas.retain(|a| a.start_addr != a.end_addr);\n        self.areas.sort_by_key(|area| area.start_address().value());\n        self.merge_contiguous_chunks();\n        Ok(())\n    }\n\n    pub fn merge_contiguous_chunks(&mut self) {\n        let mut i = 0;\n        while i < self.areas.len() - 1 {\n            let mut j = i + 1;\n            while j < self.areas.len() {\n                if self.areas[i]\n                    .overlaps_range(self.areas[j].start_address(), self.areas[j].end_address())\n                {\n                    let old = self.areas.remove(j);\n                    self.areas[i].merge_with(old).expect(\"Error merging pages\");\n                } else {\n                    j += 1;\n                }\n            }\n            i += 1;\n        }\n    }\n\n    fn zero_memory(&self, start_addr: VirtAddr, end_addr: VirtAddr) -> KResult<()> {\n        unsafe { start_addr.fill(0, end_addr.value() - start_addr.value()) }?;\n        Ok(())\n    }\n\n    pub fn mprotect(\n        &mut self,\n        start_addr: VirtAddr,\n        size: usize,\n        protection: MMapProt,\n    ) -> KResult<()> {\n        let area = self\n            .area_containing_mut(start_addr, start_addr + size - 1)\n            .ok_or(kerror!(ENOMEM, \"mprotect(): no areas containing address\"))?;\n        area.prot = protection;\n        Ok(())\n    }\n\n    pub fn mremap(\n        &mut self,\n        old_addr: VirtAddr,\n        old_size: usize,\n        new_size: usize,\n        active_mapper: &mut Mapper,\n    ) -> KResult<VirtAddr> {\n        if new_size == 0 {\n            return Err(kerror!(EINVAL, \"mremap(): new_size is zero\"));\n        }\n\n        // let new_size_aligned = align_up(new_size, PAGE_SIZE);\n        let conflicting_area = self\n            .area_containing(old_addr + new_size, old_addr + new_size)\n            .cloned();\n        let old_area = self.area_containing_mut(old_addr, old_addr + old_size);\n\n        if let Some(old_area) = old_area {\n            if let Some(ref conflicting_area) = conflicting_area {\n                if conflicting_area.start_addr == old_area.start_addr {\n                    if old_area.end_address() < old_addr + new_size {\n                        old_area.end_addr = old_addr + new_size;\n                    }\n                    return Ok(old_addr);\n                }\n            } else {\n                old_area.end_addr = old_area.start_addr + new_size;\n                return Ok(old_area.start_addr);\n            }\n        } else {\n            kbail!(EFAULT, \"mremap(): address not owned by task\");\n        }\n        self.log();\n        let old_area = self.area_containing(old_addr, old_addr).unwrap().clone();\n        let VmemArea {\n            start_addr,\n            end_addr,\n            flags,\n            prot,\n            kind: _,\n        } = old_area;\n\n        let new_addr = self.mmap(\n            VirtAddr::null(),\n            new_size,\n            prot,\n            flags,\n            -1,\n            0,\n            active_mapper,\n        )?;\n\n        let old_pages = PageRange::new(\n            Page::containing_address(start_addr),\n            Page::containing_address(end_addr),\n        );\n        let new_pages = PageRange::new(\n            Page::containing_address(new_addr),\n            Page::containing_address(new_addr + old_area.size_in_bytes()),\n        );\n        for (old_page, new_page) in old_pages.iter().zip(new_pages.iter()) {\n            let frame = active_mapper.translate(old_page.start_address());\n            if let Some((frame, flags)) = frame {\n                active_mapper\n                    .map_to_single(new_page, Frame::containing_address(frame), flags)\n                    .unwrap();\n            }\n            // else, do nothing?\n        }\n        self.munmap(active_mapper, start_addr, end_addr)?;\n        Ok(new_addr)\n    }\n\n    #[allow(clippy::too_many_arguments)]\n    pub fn mmap(\n        &mut self,\n        start_addr: VirtAddr,\n        size: usize,\n        protection: MMapProt,\n        flags: MMapFlags,\n        _fd: FileDesc,\n        _offset: usize,\n        active_mapper: &mut Mapper,\n    ) -> KResult<VirtAddr> {\n        if size == 0 {\n            kbail!(EINVAL, \"mmap(): size is zero\");\n        }\n        if flags.contains(MMapFlags::MAP_FIXED) {\n            if start_addr.align_down(PAGE_SIZE) != start_addr {\n                kbail!(EINVAL, \"mmap(): start_addr not page-aligned\");\n            }\n            let end_addr = start_addr + size;\n            if end_addr.align_up(PAGE_SIZE) != end_addr {\n                kbail!(EINVAL, \"mmap(): end_addr not page-aligned\");\n            }\n            if self.area_containing(start_addr, end_addr).is_some() {\n                kbail!(ENOMEM, \"mmap(): address already in use\");\n            }\n\n            self.map_area(\n                start_addr,\n                end_addr,\n                flags,\n                protection,\n                MMapKind::Anonymous,\n                active_mapper,\n            )?;\n            return Ok(start_addr);\n        }\n\n        let size_aligned = align_up(size, PAGE_SIZE);\n        if start_addr == VirtAddr::null() {\n            let start = self.find_free_space_above(USER_VALLOC_BASE, size_aligned);\n            if let Some((start, prev_idx)) = start {\n                if let Some(prev_idx) = prev_idx {\n                    let prev = &mut self.areas[prev_idx];\n                    if prev.end_addr == start && prev.prot == protection {\n                        assert_eq!(prev.flags, flags);\n                        assert!(matches!(prev.kind, MMapKind::Anonymous));\n                        prev.end_addr = start + size_aligned;\n                        return Ok(start);\n                    } else {\n                        log::warn!(\n                            \"Couldn't merge area [{:?} .. {:?}] with [{:?} .. {:?}]\",\n                            prev.start_addr,\n                            prev.end_addr,\n                            start,\n                            start + size_aligned\n                        );\n                    }\n                }\n\n                self.add_area(\n                    start,\n                    start + size_aligned,\n                    flags,\n                    protection,\n                    MMapKind::Anonymous,\n                )?;\n                return Ok(start);\n            }\n\n            self.log();\n            kbail!(ENOMEM, \"mmap(): no free space big enough\");\n        }\n\n        kbail!(ENOSYS, \"mmap(): not yet implemented for start_addr != null\");\n    }\n\n    // pub fn brk(&mut self, active_mapper: &mut Mapper, new_brk: VirtAddr) -> KResult<VirtAddr> {\n    //     let current_brk = self\n    //         .areas\n    //         .iter()\n    //         .find(|area| matches!(area.kind, MMapKind::Anonymous))\n    //         .map(|area| area.end_addr)\n    //         .unwrap_or(USER_VALLOC_BASE);\n    //     Ok(current_brk)\n    // }\n\n    fn find_free_space_above(\n        &mut self,\n        minimum_start: VirtAddr,\n        size: usize,\n    ) -> Option<(VirtAddr, Option<usize>)> {\n        if self.areas.is_empty() {\n            return Some((minimum_start, None));\n        }\n\n        assert!(self.areas.is_sorted_by_key(|a| a.start_addr));\n        for i in 0..self.areas.len() - 1 {\n            if self.areas[i + 1].start_addr >= minimum_start + size\n                && self.areas[i + 1].start_addr.value() - self.areas[i].end_addr.value() >= size\n            {\n                if self.areas[i].end_addr < minimum_start {\n                    return Some((minimum_start, Some(i)));\n                } else {\n                    return Some((self.areas[i].end_addr, Some(i)));\n                }\n            }\n        }\n\n        None\n    }\n\n    pub fn map_area(\n        &mut self,\n        start_addr: VirtAddr,\n        end_addr: VirtAddr,\n        flags: MMapFlags,\n        prot: MMapProt,\n        kind: MMapKind,\n        active_mapper: &mut Mapper,\n    ) -> KResult<()> {\n        let count = PageRange::new(\n            Page::containing_address(start_addr),\n            Page::containing_address(end_addr),\n        )\n        .size_in_pages();\n        let ap = self\n            .page_allocator\n            .allocate_at(Page::containing_address(start_addr), count)?;\n        let _mp = active_mapper.map(ap, prot.into())?;\n        self.add_area(\n            start_addr.align_down(PAGE_SIZE),\n            end_addr.align_up(PAGE_SIZE),\n            flags,\n            prot,\n            kind,\n        )?;\n        Ok(())\n    }\n\n    unsafe fn do_unmap(\n        &mut self,\n        start_addr: VirtAddr,\n        end_addr: VirtAddr,\n        active_mapper: &mut Mapper,\n    ) -> Option<()> {\n        let range = PageRange::new(\n            Page::containing_address(start_addr),\n            Page::containing_address(end_addr),\n        );\n        unsafe { self.page_allocator.insert_free_region(range) }\n        for page in range.iter() {\n            unsafe {\n                if let Some(frame) = active_mapper.unmap_single(page) {\n                    free_kernel_frames(\n                        &mut AllocatedFrames::assume_allocated(FrameRange::new(frame, frame)),\n                        false,\n                    )\n                    .ok();\n                } else {\n                    // log::warn!(\"Tried to free memory that wasn't mapped: {:?}\", page);\n                }\n            };\n        }\n\n        // KERNEL_FRAME_ALLOCATOR\n        //     .get()\n        //     .unwrap()\n        //     .lock()\n        //     .merge_contiguous_chunks();\n        Some(())\n    }\n\n    pub fn munmap(\n        &mut self,\n        active_mapper: &mut Mapper,\n        start_addr: VirtAddr,\n        end_addr: VirtAddr,\n    ) -> KResult<()> {\n        let area_idx = self\n            .areas\n            .iter_mut()\n            .enumerate()\n            .find(|(_idx, area)| area.contains_addr(start_addr))\n            .map(|(idx, _area)| idx)\n            .ok_or(kerror!(EINVAL, \"munmap(): address range not owned by task\"))?;\n        let area_clone = self.areas[area_idx].clone();\n        if start_addr <= area_clone.start_addr && end_addr >= area_clone.end_addr {\n            // remove the whole area and continue recursively unmapping until the whole range is unmapped\n            unsafe {\n                self.do_unmap(area_clone.start_addr, area_clone.end_addr, active_mapper);\n            }\n            self.areas.remove(area_idx);\n            self.munmap(active_mapper, area_clone.end_addr, end_addr)?;\n        } else if start_addr >= area_clone.start_addr && end_addr < area_clone.end_addr {\n            // split the area in two\n            unsafe {\n                self.do_unmap(start_addr, end_addr, active_mapper);\n            }\n            self.areas.remove(area_idx);\n            assert!(!matches!(area_clone.kind, MMapKind::File { .. })); // todo: handle this\n            self.add_area(\n                area_clone.start_addr,\n                start_addr,\n                area_clone.flags,\n                area_clone.prot,\n                area_clone.kind.clone(),\n            )?;\n            self.add_area(\n                end_addr,\n                area_clone.end_addr,\n                area_clone.flags,\n                area_clone.prot,\n                area_clone.kind,\n            )?;\n        } else if start_addr <= area_clone.start_addr && end_addr < area_clone.end_addr {\n            // replace the end of the area (start was unmapped)\n            assert!(!matches!(area_clone.kind, MMapKind::File { .. })); // todo: handle this\n            unsafe {\n                self.do_unmap(area_clone.start_addr, end_addr, active_mapper);\n            }\n            self.areas[area_idx].start_addr = end_addr;\n        } else if start_addr > area_clone.start_addr && end_addr >= area_clone.end_addr {\n            // replace the start of the area (end was unmapped)\n            unsafe {\n                self.do_unmap(start_addr, area_clone.end_addr, active_mapper);\n            }\n            self.areas[area_idx].end_addr = end_addr;\n        } else {\n            unreachable!()\n        }\n        Ok(())\n    }\n\n    pub fn clear(&mut self, active_mapper: &mut Mapper) {\n        for id in 0..self.next_id.load(core::sync::atomic::Ordering::Acquire) {\n            if let Some(area) = self.areas.get(id) {\n                unsafe {\n                    self.do_unmap(area.start_addr, area.end_addr, active_mapper);\n                }\n            }\n        }\n        self.areas.clear();\n    }\n\n    pub fn log(&self) {\n        log::debug!(\"BEGIN VIRTUAL MEMORY STATE DUMP\");\n        for area in self.areas.iter() {\n            log::debug!(\n                \"{:>16x?} .. {:>16x?}   | {:?}  {:?}\",\n                area.start_addr,\n                area.end_addr,\n                area.flags,\n                area.prot\n            );\n        }\n        log::debug!(\"END VIRTUAL MEMORY STATE DUMP\");\n    }\n\n    pub fn fork_from(&mut self, parent: &Vmem) {\n        self.areas = parent.areas.clone();\n        // self.mp = parent.mp.clone();\n        self.page_allocator = parent.page_allocator.clone();\n        self.next_id.store(\n            parent.next_id.load(core::sync::atomic::Ordering::Acquire),\n            core::sync::atomic::Ordering::Release,\n        );\n    }\n\n    pub fn handle_page_fault(\n        &mut self,\n        process_addr_space: &mut AddressSpace,\n        faulted_addr: VirtAddr,\n        stack_frame: InterruptErrorFrame,\n        reason: PageFaultErrorCode,\n    ) -> KResult<()> {\n        let dump_and_exit = || {\n            let current = current_task();\n            log::error!(\"PID: {}\", current.pid().as_usize());\n            log::error!(\"Instruction pointer: {:#x}\", { stack_frame.frame.rip });\n            log::error!(\n                \"Process page table: {:#x}\",\n                process_addr_space.cr3().value()\n            );\n            log::error!(\"Current page table: {:#x}\", unsafe { cr3() });\n            log::error!(\"Faulted address: {:?}\", faulted_addr);\n            log::error!(\"Reason: {:?}\", reason);\n            log::debug!(\"{:#x?}\", stack_frame);\n            self.log();\n            backtrace::unwind_user_stack_from(stack_frame.frame.rbp, stack_frame.frame.rip);\n            get_scheduler().send_signal_to(current, SIGSEGV);\n            get_scheduler().exit_current(1)\n        };\n\n        // log::debug!(\"User page fault at {:#x}\", { stack_frame.frame.rip });\n        // log::debug!(\"PID: {}\", current_task().pid().as_usize());\n        // log::debug!(\"Faulted address: {:?}\", faulted_addr);\n        // log::debug!(\"Reason: {:?}\", reason);\n        // self.log();\n        // backtrace::unwind_user_stack_from(stack_frame.frame.rbp, stack_frame.frame.rip);\n        if faulted_addr.align_down(PAGE_SIZE) == VirtAddr::null() {\n            log::error!(\"User segmentation fault: null pointer access\");\n            dump_and_exit()\n        }\n\n        let mut faulted_area = None;\n        for area in self.areas.iter() {\n            if area.contains_addr(faulted_addr) {\n                faulted_area = Some(area);\n                break;\n            }\n        }\n\n        if let Some(area) = faulted_area {\n            if !reason.contains(PageFaultErrorCode::PROTECTION_VIOLATION) {\n                // allocate and map pages\n                let page = Page::containing_address(faulted_addr);\n                let ap = self.page_allocator.allocate_at(page, 1)?;\n                process_addr_space.with_mapper(|mut mapper| mapper.map(ap, area.prot.into()))?;\n                match &area.kind {\n                    MMapKind::Anonymous => {\n                        self.zero_memory(page.start_address(), page.start_address() + PAGE_SIZE)?;\n                    }\n                    MMapKind::File { file, offset, size } => {\n                        let size = size.min(&PAGE_SIZE);\n                        let mut buf = alloc::vec![0; *size];\n                        let user_buf = UserBufferMut::from_slice(&mut buf);\n                        let read = file.read(*offset, user_buf, &OpenFlags::empty())?;\n                        if read == 0 {\n                            self.zero_memory(\n                                page.start_address(),\n                                page.start_address() + PAGE_SIZE,\n                            )?;\n                        } else {\n                            unsafe {\n                                page.start_address().write_bytes_user(&buf)?;\n                            }\n                        }\n                    }\n                }\n                return Ok(());\n            } else if reason.contains(PageFaultErrorCode::CAUSED_BY_WRITE) {\n                if !area.prot.contains(MMapProt::PROT_WRITE) {\n                    log::error!(\"User segmentation fault: illegal write\");\n                    dump_and_exit()\n                }\n                // COW\n                let new_frame = alloc_kernel_frames(1)?;\n                let new_page = unsafe {\n                    core::slice::from_raw_parts_mut(\n                        new_frame\n                            .start_address()\n                            .as_hhdm_virt()\n                            .as_raw_ptr_mut::<u8>(),\n                        PAGE_SIZE,\n                    )\n                };\n                let old_page = unsafe {\n                    core::slice::from_raw_parts(\n                        faulted_addr.align_down(PAGE_SIZE).as_raw_ptr::<u8>(),\n                        PAGE_SIZE,\n                    )\n                };\n                new_page.copy_from_slice(old_page);\n                process_addr_space.with_mapper(|mut mapper| -> KResult<()> {\n                    unsafe {\n                        mapper.unmap_single(Page::containing_address(faulted_addr));\n                    }\n                    mapper.map_to_single(\n                        Page::containing_address(faulted_addr),\n                        new_frame.start(),\n                        area.prot.into(),\n                    )?;\n                    Ok(())\n                })?;\n\n                return Ok(());\n            }\n            unreachable!(\n                \"handle_page_fault(): faulted area found, but was already readable and writable\"\n            )\n        } else {\n            log::error!(\"User segmentation fault: illegal access\");\n            dump_and_exit()\n        }\n\n        log::warn!(\"Unrecoverable page fault at {:#x}\", {\n            stack_frame.frame.rip\n        });\n        kbail!(EFAULT, \"handle_page_fault(): couldn't handle page fault\");\n    }\n}\n\nimpl Default for Vmem {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n"
  },
  {
    "path": "src/task/wait_queue.rs",
    "content": "use alloc::{collections::VecDeque, sync::Arc};\n\nuse crate::{\n    arch, kbail,\n    util::{IrqMutex, KResult},\n};\n\nuse super::{current_task, get_scheduler, Task, TaskState};\n\npub struct WaitQueue {\n    pub(super) queue: IrqMutex<VecDeque<Arc<Task>>>,\n}\n\nimpl Default for WaitQueue {\n    fn default() -> Self {\n        Self::new()\n    }\n}\n\nimpl WaitQueue {\n    pub const fn new() -> WaitQueue {\n        WaitQueue {\n            queue: IrqMutex::new(VecDeque::new()),\n        }\n    }\n\n    pub fn sleep_signalable_until<F, R>(\n        &self,\n        timeout: Option<usize>,\n        mut sleep_if_none: F,\n    ) -> KResult<R>\n    where\n        F: FnMut() -> KResult<Option<R>>,\n    {\n        let start_time = arch::time::get_uptime_ms();\n        loop {\n            let current = current_task();\n            let scheduler = get_scheduler();\n            current.set_state(TaskState::Waiting);\n            {\n                let mut q_lock = self.queue.lock();\n                if !q_lock.iter().any(|t| t.pid == current.pid) {\n                    q_lock.push_back(current.clone());\n                }\n            }\n\n            if current.has_pending_signals() {\n                scheduler.resume_task(current.clone());\n                self.queue.lock().retain(|t| t.pid != current.pid);\n                kbail!(\n                    EINTR,\n                    \"sleep_signalable_until(): interrupted by pending signals\"\n                );\n            }\n\n            let ret_value = match sleep_if_none() {\n                Ok(Some(ret_val)) => Some(Ok(ret_val)),\n                Ok(None) => None,\n                Err(err) => Some(Err(err)),\n            };\n\n            let current = current_task();\n            if let Some(ret_val) = ret_value {\n                scheduler.resume_task(current.clone());\n                self.queue.lock().retain(|t| t.pid != current.pid);\n                return ret_val;\n            }\n\n            scheduler.sleep(timeout)?;\n\n            if let Some(timeout) = timeout {\n                if arch::time::get_uptime_ms() >= start_time + timeout {\n                    kbail!(EINTR, \"sleep_signalable_until(): timeout reached\");\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/userland/buffer.rs",
    "content": "use core::{mem::size_of, ops::Add};\n\nuse alloc::string::{String, ToString};\n\nuse crate::{\n    kbail, kerror,\n    mem::{addr::VirtAddr, addr_space::TmpAddrSpaceGuard},\n    task::current_task,\n    util::{align_up, error::KResult},\n};\n\n#[allow(dead_code)]\nenum Inner<'a> {\n    Slice(&'a [u8]),\n    User { base: VirtAddr, len: usize },\n}\n\npub struct UserBuffer<'a> {\n    inner: Inner<'a>,\n}\n\nimpl<'a> UserBuffer<'a> {\n    pub fn from_vaddr(vaddr: VirtAddr, len: usize) -> UserBuffer<'static> {\n        UserBuffer {\n            inner: Inner::User { base: vaddr, len },\n        }\n    }\n\n    pub fn from_slice(slc: &'a [u8]) -> UserBuffer<'a> {\n        UserBuffer {\n            inner: Inner::Slice(slc),\n        }\n    }\n\n    #[allow(clippy::len_without_is_empty)]\n    pub fn len(&self) -> usize {\n        match &self.inner {\n            Inner::Slice(slice) => slice.len(),\n            Inner::User { len, .. } => *len,\n        }\n    }\n}\n\n#[allow(dead_code)]\nenum InnerMut<'a> {\n    Slice(&'a mut [u8]),\n    User { base: VirtAddr, len: usize },\n}\n\npub struct UserBufferMut<'a> {\n    inner: InnerMut<'a>,\n}\n\nimpl<'a> UserBufferMut<'a> {\n    pub fn from_slice(slice: &'a mut [u8]) -> UserBufferMut<'a> {\n        UserBufferMut {\n            inner: InnerMut::Slice(slice),\n        }\n    }\n\n    pub fn from_vaddr(vaddr: VirtAddr, len: usize) -> UserBufferMut<'static> {\n        UserBufferMut {\n            inner: InnerMut::User { base: vaddr, len },\n        }\n    }\n\n    #[allow(clippy::len_without_is_empty)]\n    pub fn len(&self) -> usize {\n        match &self.inner {\n            InnerMut::Slice(slice) => slice.len(),\n            InnerMut::User { len, .. } => *len,\n        }\n    }\n}\n\npub unsafe fn user_strncpy_rust(dst: *mut u8, src: *const u8, max_len: usize) -> usize {\n    let mut read_len = 0usize;\n    loop {\n        let byte = unsafe { src.add(read_len).read_volatile() };\n        if byte == b'\\0' || read_len > max_len {\n            break;\n        }\n        unsafe { dst.add(read_len).write_volatile(byte) };\n        read_len += 1;\n    }\n    read_len\n}\n\npub struct CStr {\n    string: String,\n}\n\nimpl CStr {\n    pub fn new(vaddr: VirtAddr, max_len: usize, is_user: bool) -> KResult<CStr> {\n        vaddr.align_ok::<u8>()?;\n        if is_user {\n            (vaddr + max_len).user_ok()?;\n        }\n        let mut tmp = alloc::vec![0; max_len];\n        let guard = current_task().arch_mut().address_space.temporarily_switch();\n        let read_len = unsafe { user_strncpy_rust(tmp.as_mut_ptr(), vaddr.as_raw_ptr(), max_len) };\n        drop(guard);\n        let string = core::str::from_utf8(&tmp[..read_len])\n            .map_err(|_| kerror!(EINVAL, \"UserCStr: UTF-8 parsing error\"))?\n            .to_string();\n        Ok(CStr { string })\n    }\n\n    pub fn as_bytes(&self) -> &[u8] {\n        self.string.as_bytes()\n    }\n\n    pub fn as_str(&self) -> &str {\n        &self.string\n    }\n}\n\npub struct UserBufferReader<'a> {\n    buf: UserBuffer<'a>,\n    pos: usize,\n    #[allow(dead_code)]\n    guard: TmpAddrSpaceGuard,\n}\n\nimpl<'a> UserBufferReader<'a> {\n    pub fn from_vaddr(buf: VirtAddr, len: usize) -> UserBufferReader<'a> {\n        let guard = current_task().arch_mut().address_space.temporarily_switch();\n        UserBufferReader {\n            buf: UserBuffer::from_vaddr(buf, len),\n            pos: 0,\n            guard,\n        }\n    }\n\n    pub fn from_buf(buf: UserBuffer<'a>) -> UserBufferReader<'a> {\n        let guard = current_task().arch_mut().address_space.temporarily_switch();\n        UserBufferReader { buf, pos: 0, guard }\n    }\n\n    pub fn read_len(&self) -> usize {\n        self.pos\n    }\n\n    pub fn skip(&mut self, len: usize) -> KResult<()> {\n        self.check_remaining_len(len)?;\n        self.pos += len;\n        Ok(())\n    }\n\n    pub fn read_bytes(&mut self, dst: &mut [u8]) -> KResult<usize> {\n        let read_len = core::cmp::min(dst.len(), self.remaining_len());\n        if read_len == 0 {\n            return Ok(0);\n        }\n\n        match &self.buf.inner {\n            Inner::Slice(src) => {\n                dst[..read_len].copy_from_slice(&src[self.pos..(self.pos + read_len)])\n            }\n            Inner::User { base, .. } => unsafe {\n                base.add(self.pos).read_bytes(&mut dst[..read_len])?;\n            },\n        }\n\n        self.pos += read_len;\n\n        Ok(read_len)\n    }\n\n    pub fn read<T: Copy + Sized>(&mut self) -> KResult<T> {\n        self.check_remaining_len(size_of::<T>())?;\n\n        let val = match &self.buf.inner {\n            Inner::Slice(src) => {\n                // this could cause a page fault if the inner slice of the buffer isn't mapped to the current page table!\n                unsafe { *(src.as_ptr().add(self.pos) as *const T) }\n            }\n            Inner::User { base, .. } => unsafe { base.add(self.pos).read()? },\n        };\n\n        self.pos += size_of::<T>();\n\n        Ok(val)\n    }\n\n    fn check_remaining_len(&self, len: usize) -> KResult<()> {\n        if len <= self.remaining_len() {\n            Ok(())\n        } else {\n            kbail!(EINVAL, \"check_remaining_len(): len out of bounds\");\n        }\n    }\n\n    pub fn remaining_len(&self) -> usize {\n        self.buf.len() - self.pos\n    }\n}\n\npub struct UserBufferWriter<'a> {\n    buf: UserBufferMut<'a>,\n    pos: usize,\n    #[allow(dead_code)]\n    guard: TmpAddrSpaceGuard,\n}\n\nimpl<'a> UserBufferWriter<'a> {\n    pub fn from_vaddr(buf: VirtAddr, len: usize) -> UserBufferWriter<'a> {\n        let guard = current_task().arch_mut().address_space.temporarily_switch();\n        UserBufferWriter {\n            buf: UserBufferMut::from_vaddr(buf, len),\n            pos: 0,\n            guard,\n        }\n    }\n\n    pub fn from_buf(buf: UserBufferMut<'a>) -> UserBufferWriter<'a> {\n        let guard = current_task().arch_mut().address_space.temporarily_switch();\n        UserBufferWriter { buf, pos: 0, guard }\n    }\n\n    pub fn written_len(&self) -> usize {\n        self.pos\n    }\n\n    pub fn write<T: Copy + Sized>(&mut self, value: T) -> KResult<usize> {\n        let bytes =\n            unsafe { core::slice::from_raw_parts(&value as *const T as *const u8, size_of::<T>()) };\n        self.write_bytes(bytes)\n    }\n\n    pub fn write_bytes(&mut self, src: &[u8]) -> KResult<usize> {\n        let copy_len = core::cmp::min(self.remaining_len(), src.len());\n        if copy_len == 0 {\n            return Ok(0);\n        }\n        self.check_remaining_len(copy_len)?;\n\n        match &mut self.buf.inner {\n            InnerMut::Slice(dst) => {\n                dst[self.pos..(self.pos + copy_len)].copy_from_slice(&src[..copy_len]);\n            }\n            InnerMut::User { base, .. } => {\n                unsafe { base.add(self.pos).write_bytes(&src[..copy_len]) }?;\n            }\n        }\n\n        self.pos += copy_len;\n        Ok(copy_len)\n    }\n\n    pub fn skip_until_alignment(&mut self, alignment: usize) -> KResult<()> {\n        let new_pos = align_up(self.pos, alignment);\n        self.check_remaining_len(new_pos - self.pos)?;\n        self.pos = new_pos;\n        Ok(())\n    }\n\n    pub fn fill(&mut self, value: u8, len: usize) -> KResult<()> {\n        self.check_remaining_len(len)?;\n\n        match &mut self.buf.inner {\n            InnerMut::Slice(dst) => {\n                dst[self.pos..(self.pos + len)].fill(value);\n            }\n            InnerMut::User { base, .. } => {\n                unsafe { base.add(self.pos).fill(value, len) }?;\n            }\n        }\n\n        self.pos += len;\n        Ok(())\n    }\n\n    pub fn write_bytes_or_zeros(&mut self, buf: &[u8], max_len: usize) -> KResult<()> {\n        let zero_start = core::cmp::min(buf.len(), max_len);\n        self.check_remaining_len(zero_start)?;\n        self.write_bytes(&buf[..zero_start])?;\n        self.fill(0, max_len - zero_start)?;\n        Ok(())\n    }\n\n    fn check_remaining_len(&self, len: usize) -> KResult<()> {\n        if len <= self.remaining_len() {\n            Ok(())\n        } else {\n            kbail!(EINVAL, \"check_remaining_len(): len out of bounds\");\n        }\n    }\n\n    pub fn remaining_len(&self) -> usize {\n        self.buf.len() - self.pos\n    }\n}\n"
  },
  {
    "path": "src/userland/elf.rs",
    "content": "use alloc::{borrow::ToOwned, string::String, vec::Vec};\nuse elfloader::{ElfBinary, ElfLoader, Entry};\nuse x86::random::rdrand_slice;\n\nuse xmas_elf::{\n    program::Type,\n    sections::{SectionData, ShType},\n};\n\nuse crate::{\n    fs::{initramfs::get_root, opened_file::OpenFlags, path::Path, FileRef},\n    kerror,\n    mem::{\n        addr::VirtAddr, addr_space::AddressSpace, allocator::alloc_kernel_frames, consts::PAGE_SIZE,\n    },\n    task::vmem::{MMapFlags, MMapKind, MMapProt, Vmem},\n    userland::buffer::UserBufferMut,\n    util::{align_up, KResult},\n};\n\npub fn gen_stack_canary() -> [u8; 16] {\n    let mut random_bytes = [0u8; 16];\n    unsafe { rdrand_slice(&mut random_bytes) };\n    random_bytes\n}\n\n#[repr(u64)]\n#[derive(Debug, Copy, Clone)]\npub enum AuxvType {\n    AtNull = 0,\n    AtPhdr = 3,\n    AtPhEnt = 4,\n    AtPhNum = 5,\n    AtEntry = 9,\n}\n\n#[derive(Clone)]\npub struct SymTabEntry {\n    pub name: String,\n    pub value: u64,\n    pub size: u64,\n}\n\npub struct UserlandEntry {\n    pub entry_point: VirtAddr,\n    pub vmem: Vmem,\n    pub fsbase: Option<VirtAddr>,\n    pub addr_space: AddressSpace,\n    pub hdr: [(AuxvType, usize); 4],\n    pub symtab: Option<Vec<SymTabEntry>>,\n}\n\npub fn load_elf(file: FileRef) -> KResult<UserlandEntry> {\n    let len = file.stat()?.size.0 as usize;\n    let current = AddressSpace::current();\n    // let mut buf = alloc::vec![0u8; len];\n    let mut addr_space = AddressSpace::new()?;\n    addr_space.switch();\n    let frames = alloc_kernel_frames(align_up(len, PAGE_SIZE) / PAGE_SIZE)?;\n\n    // let mp = addr_space.mapper().map(pages, PageTableFlags::PRESENT | PageTableFlags::WRITABLE)?;\n    let buf = unsafe {\n        core::slice::from_raw_parts_mut(\n            frames.start_address().as_hhdm_virt().as_raw_ptr_mut(),\n            frames.size_in_bytes(),\n        )\n    };\n    let ubuf = UserBufferMut::from_slice(buf);\n    file.read(0, ubuf, &OpenFlags::empty())?;\n    current.switch();\n    let elf = ElfBinary::new(buf).map_err(|_e| kerror!(\"load_elf(): elf loader error\"))?;\n\n    let mut start_of_image = usize::MAX;\n    let mut end_of_image = 0;\n    for hdr in elf.program_headers() {\n        if hdr.get_type().unwrap() == xmas_elf::program::Type::Load {\n            end_of_image = end_of_image.max((hdr.virtual_addr() + hdr.mem_size()) as usize);\n            start_of_image = end_of_image.min(hdr.virtual_addr() as usize);\n        }\n    }\n    let mut symbol_table = None;\n    for section in elf.file.section_iter() {\n        if section.get_type() == Ok(ShType::SymTab) {\n            let section_data = section.get_data(&elf.file);\n            if let Ok(ref _section_data @ SectionData::SymbolTable64(symtab)) = section_data {\n                symbol_table = Some(\n                    symtab\n                        .iter()\n                        .map(|e| SymTabEntry {\n                            name: e.get_name(&elf.file).unwrap().to_owned(),\n                            size: e.size(),\n                            value: e.value(),\n                        })\n                        .collect::<Vec<_>>(),\n                );\n            }\n        }\n    }\n    if symbol_table.is_none() {\n        log::warn!(\"Couldn't get symbol table for ELF.\");\n    }\n    log::debug!(\n        \"ELF loaded into memory at {:#x} .. {:#x}\",\n        start_of_image,\n        end_of_image\n    );\n    if elf.is_pie() {\n        log::warn!(\"It's a PIE\");\n    }\n\n    let mut vmem = Vmem::new();\n\n    let load_offset =\n        if elf.file.header.pt2.type_().as_type() == xmas_elf::header::Type::SharedObject {\n            0x40000000\n        } else {\n            0\n        };\n\n    let entry_point = VirtAddr::new(elf.entry_point() as usize + load_offset);\n\n    log::debug!(\"Entry point: {:?}\", entry_point);\n\n    addr_space.switch();\n    let mut loader = KadosElfLoader {\n        vmem: &mut vmem,\n        addr_space: &mut addr_space,\n        base_addr: usize::MAX,\n        load_offset,\n        file: file.clone(),\n        entry_point,\n    };\n\n    elf.load(&mut loader).unwrap();\n\n    let p2 = elf.file.header.pt2;\n    log::debug!(\"Base address at {:?}\", VirtAddr::new(loader.base_addr));\n    let hdr = [\n        (AuxvType::AtPhdr, p2.ph_offset() as usize + loader.base_addr),\n        (AuxvType::AtPhEnt, p2.ph_entry_size() as usize),\n        (AuxvType::AtPhNum, p2.ph_count() as usize),\n        (AuxvType::AtEntry, p2.entry_point() as usize),\n    ];\n\n    if let Some(ref symtab) = symbol_table {\n        for sym in symtab.iter() {\n            if sym.name == \"__stack_chk_fail\" {\n                // unsafe {\n                //     *(sym.value as *mut u8) = 0xc3; // \"ret\" instruction\n                // }\n                log::warn!(\"SSP is ON for this binary!\");\n                break;\n            }\n        }\n    }\n    current.switch();\n\n    log::debug!(\"ELF load complete.\");\n    Ok(UserlandEntry {\n        entry_point: loader.entry_point,\n        vmem,\n        fsbase: None,\n        addr_space,\n        hdr,\n        symtab: symbol_table,\n    })\n}\n\nstruct KadosElfLoader<'a> {\n    vmem: &'a mut Vmem,\n    addr_space: &'a mut AddressSpace,\n    base_addr: usize,\n    load_offset: usize,\n    file: FileRef,\n    entry_point: VirtAddr,\n}\n\nimpl ElfLoader for KadosElfLoader<'_> {\n    fn allocate(\n        &mut self,\n        load_headers: elfloader::LoadableHeaders,\n    ) -> Result<(), elfloader::ElfLoaderErr> {\n        for header in load_headers {\n            if header.get_type().unwrap() == Type::Load {\n                let start = VirtAddr::new(header.virtual_addr() as usize + self.load_offset)\n                    .align_down(PAGE_SIZE);\n                let mem_end = VirtAddr::new(\n                    header.virtual_addr() as usize + header.mem_size() as usize + self.load_offset,\n                )\n                .align_up(PAGE_SIZE);\n                if start.value() < self.base_addr {\n                    self.base_addr = start.value();\n                }\n                let flags = MMapFlags::empty();\n                let mut prot = MMapProt::PROT_WRITE;\n                if header.flags().is_execute() {\n                    prot.insert(MMapProt::PROT_EXEC);\n                }\n                let kind = MMapKind::File {\n                    file: self.file.clone(),\n                    offset: header.offset() as usize,\n                    size: header.file_size() as usize,\n                };\n                log::debug!(\"Mapping region {:?} .. {:?}\", start, mem_end);\n                self.addr_space.with_mapper(|mut mapper| {\n                    self.vmem\n                        .map_area(start, mem_end, flags, prot, kind, &mut mapper)\n                        .unwrap();\n                });\n            } else if header.get_type().unwrap() == Type::Interp {\n                let ld = get_root()\n                    .unwrap()\n                    .lookup(Path::new(\"/usr/lib/ld.so\"), true)\n                    .unwrap()\n                    .as_file()\n                    .unwrap()\n                    .clone();\n                let res = load_elf(ld).unwrap();\n                self.entry_point = res.entry_point;\n            }\n        }\n\n        Ok(())\n    }\n\n    fn load(\n        &mut self,\n        flags: elfloader::Flags,\n        base: elfloader::VAddr,\n        region: &[u8],\n    ) -> Result<(), elfloader::ElfLoaderErr> {\n        let region_start = VirtAddr::new(base as usize + self.load_offset);\n        let region_end = region_start + region.len();\n        let area = self\n            .vmem\n            .area_containing_mut(region_start, region_end)\n            .unwrap();\n        let mut prot = MMapProt::empty();\n        if flags.is_read() {\n            prot |= MMapProt::PROT_READ;\n        }\n        if flags.is_write() {\n            prot |= MMapProt::PROT_WRITE;\n        }\n        if flags.is_execute() {\n            prot |= MMapProt::PROT_EXEC;\n        }\n        // this should be safe since the pages should already be mapped and writable in allocate()\n        unsafe { region_start.write_bytes(region).unwrap() };\n        // set the correct protections now\n        area.prot = prot;\n        Ok(())\n    }\n\n    fn tls(\n        &mut self,\n        tdata_start: elfloader::VAddr,\n        _tdata_length: u64,\n        _total_size: u64,\n        _align: u64,\n    ) -> Result<(), elfloader::ElfLoaderErr> {\n        log::warn!(\n            \"TLS section found at {:?}\",\n            VirtAddr::new(tdata_start as usize)\n        );\n        Ok(())\n    }\n\n    fn relocate(\n        &mut self,\n        entry: elfloader::RelocationEntry,\n    ) -> Result<(), elfloader::ElfLoaderErr> {\n        use elfloader::arch::x86_64::RelocationTypes;\n        match entry.rtype {\n            elfloader::RelocationType::x86_64(rtype) => match rtype {\n                RelocationTypes::R_AMD64_RELATIVE => {\n                    let reloc_value = entry.addend.unwrap() as usize + self.load_offset;\n                    log::trace!(\n                        \"Applying relocation R_AMD64_RELATIVE at location {:#x} -> {:#x}\",\n                        entry.offset,\n                        reloc_value\n                    );\n                    unsafe {\n                        *((entry.offset + self.load_offset as u64) as *mut usize) = reloc_value;\n                    }\n                }\n                rtype => {\n                    log::error!(\"Unsupported relocation type: {:?}\", rtype);\n                    return Err(elfloader::ElfLoaderErr::UnsupportedRelocationEntry);\n                }\n            },\n            _ => return Err(elfloader::ElfLoaderErr::UnsupportedArchitecture),\n        }\n        Ok(())\n    }\n}\n"
  },
  {
    "path": "src/userland/mod.rs",
    "content": "pub mod buffer;\npub mod elf;\npub mod syscall;\n"
  },
  {
    "path": "src/userland/syscall/mod.rs",
    "content": "use alloc::borrow::ToOwned;\n\nuse crate::{\n    arch::idt::InterruptFrame,\n    fs::{\n        opened_file::{FileDesc, OpenFlags},\n        path::{Path, PathBuf},\n        FileMode,\n    },\n    kerror,\n    mem::addr::VirtAddr,\n    task::{\n        current_task, get_scheduler,\n        group::PgId,\n        vmem::{MMapFlags, MMapProt},\n        TaskId,\n    },\n    userland::syscall::syscall_impl::task::WaitOptions,\n    util::{\n        ctypes::{c_int, c_nfds},\n        KError, KResult,\n    },\n};\n\nuse super::buffer::CStr;\n\npub mod syscall_impl;\n\npub fn errno_to_isize(res: &Result<isize, KError<'_>>) -> isize {\n    match res {\n        Ok(retval) => *retval,\n        Err(err) => {\n            let errno = err.errno().unwrap() as i32;\n            -errno as isize\n        }\n    }\n}\n\npub const QUIET_SYSCALLS: &[usize] = &[\n    SYS_UNLINK,\n    SYS_CLOCK_GETTIME,\n    SYS_NANOSLEEP,\n    SYS_LSEEK,\n    SYS_WRITEV,\n    SYS_READV,\n    SYS_GETRANDOM,\n    SYS_WRITE,\n    SYS_READ,\n    SYS_OPEN,\n    SYS_STAT,\n];\n\npub struct SyscallHandler<'a> {\n    pub frame: &'a mut InterruptFrame,\n}\n\nimpl SyscallHandler<'_> {\n    #[allow(clippy::too_many_arguments)]\n    pub fn dispatch(\n        &mut self,\n        a1: usize,\n        a2: usize,\n        a3: usize,\n        a4: usize,\n        a5: usize,\n        a6: usize,\n        n: usize,\n    ) -> Result<isize, KError<'_>> {\n        let rip = self.frame.rip;\n        let quiet = QUIET_SYSCALLS.contains(&n);\n\n        if !quiet {\n            let current = current_task();\n            let symtab = &current.arch_mut().symtab;\n            if let Some(symtab) = symtab {\n                let mut symbol = None;\n                for sym in symtab.iter() {\n                    if rip as u64 >= sym.value && rip as u64 <= (sym.value + sym.size) {\n                        symbol = Some(sym.name.to_owned());\n                        break;\n                    }\n                }\n                if let Some(symbol) = symbol {\n                    log::trace!(\n                        \"[{}] SYSCALL #{} {}({:#x}, {:#x}, {:#x}, {:#x}, {:#x}, {:#x})\",\n                        symbol,\n                        n,\n                        syscall_name_by_number(n),\n                        a1,\n                        a2,\n                        a3,\n                        a4,\n                        a5,\n                        a6\n                    );\n                } else {\n                    log::trace!(\n                        \"[{:#x}] SYSCALL #{} {}({:#x}, {:#x}, {:#x}, {:#x}, {:#x}, {:#x})\",\n                        rip,\n                        n,\n                        syscall_name_by_number(n),\n                        a1,\n                        a2,\n                        a3,\n                        a4,\n                        a5,\n                        a6\n                    );\n                }\n            } else {\n                log::trace!(\n                    \"[{:#x}] SYSCALL #{} {}({:#x}, {:#x}, {:#x}, {:#x}, {:#x}, {:#x})\",\n                    rip,\n                    n,\n                    syscall_name_by_number(n),\n                    a1,\n                    a2,\n                    a3,\n                    a4,\n                    a5,\n                    a6\n                );\n            }\n        }\n\n        let res = match n {\n            SYS_ARCH_PRCTL => self.sys_arch_prctl(a1 as i32, VirtAddr::new(a2)),\n            SYS_SET_TID_ADDRESS => self.sys_set_tid_address(VirtAddr::new(a1)),\n            SYS_WRITE => self.sys_write(a1 as FileDesc, VirtAddr::new(a2), a3),\n            SYS_WRITEV => self.sys_writev(a1 as FileDesc, VirtAddr::new(a2), a3),\n            SYS_READV => self.sys_readv(a1 as FileDesc, VirtAddr::new(a2), a3),\n            SYS_READ => self.sys_read(a1 as FileDesc, VirtAddr::new(a2), a3),\n            SYS_IOCTL => self.sys_ioctl(a1 as FileDesc, a2, a3),\n            SYS_RT_SIGPROCMASK => {\n                self.sys_rt_sigprocmask(a1, VirtAddr::new(a2), VirtAddr::new(a3), a4)\n            }\n            SYS_FORK => self.sys_fork(),\n            SYS_WAIT4 => self.sys_wait4(\n                TaskId::new(a1),\n                VirtAddr::new(a2),\n                crate::bitflags_from_user!(WaitOptions, a3 as i32),\n                VirtAddr::new(a4),\n            ),\n            SYS_EXECVE => self.sys_execve(&resolve_path(a1)?, VirtAddr::new(a2), VirtAddr::new(a3)),\n            SYS_GETTID => self.sys_getpid(), // todo\n            SYS_GETPID => self.sys_getpid(),\n            SYS_GETPPID => self.sys_getppid(),\n            SYS_GETPGID => self.sys_getpgid(TaskId::new(a1)),\n            SYS_SETPGID => self.sys_setpgid(TaskId::new(a1), a2 as PgId),\n            SYS_EXIT => self.sys_exit(a1 as c_int),\n            SYS_EXIT_GROUP => self.sys_exit(a1 as c_int), // todo\n            SYS_MMAP => self.sys_mmap(\n                VirtAddr::new(a1),\n                a2,\n                crate::bitflags_from_user!(MMapProt, a3 as u64),\n                crate::bitflags_from_user!(MMapFlags, a4 as u64),\n                a5 as FileDesc,\n                a6,\n            ),\n            SYS_MPROTECT => self.sys_mprotect(\n                VirtAddr::new(a1),\n                a2,\n                crate::bitflags_from_user!(MMapProt, a3 as u64),\n            ),\n            SYS_MUNMAP => self.sys_munmap(VirtAddr::new(a1), a2),\n            // SYS_BRK => self.sys_brk(VirtAddr::new(a1)),\n            SYS_MREMAP => self.sys_mremap(VirtAddr::new(a1), a2, a3),\n            SYS_RT_SIGACTION => {\n                self.sys_rt_sigaction(a1 as c_int, VirtAddr::new(a2), VirtAddr::new(a3))\n            }\n            SYS_GETUID => Ok(0),    // TODO:\n            SYS_GETEUID => Ok(0),   // TODO:\n            SYS_SETUID => Ok(0),    // TODO:\n            SYS_SETGID => Ok(0),    // TODO:\n            SYS_SETGROUPS => Ok(0), // TODO:\n            SYS_STAT => self.sys_stat(&resolve_path(a1)?, VirtAddr::new(a2)),\n            SYS_LSTAT => self.sys_lstat(&resolve_path(a1)?, VirtAddr::new(a2)),\n            SYS_FSTAT => self.sys_fstat(a1 as FileDesc, VirtAddr::new(a2)),\n            SYS_OPEN => self.sys_open(\n                &resolve_path(a1)?,\n                crate::bitflags_from_user!(OpenFlags, a2 as i32),\n                FileMode::new(a3 as u32),\n            ),\n            SYS_GETCWD => self.sys_getcwd(VirtAddr::new(a1), a2),\n            SYS_GETDENTS64 => self.sys_getdents64(a1 as FileDesc, VirtAddr::new(a2), a3),\n            SYS_FCNTL => self.sys_fcntl(a1 as FileDesc, a2 as c_int, a3),\n            SYS_UNAME => self.sys_uname(VirtAddr::new(a1)),\n            SYS_CLOSE => self.sys_close(a1 as FileDesc),\n            SYS_POLL => self.sys_poll(VirtAddr::new(a1), a2 as c_nfds, a3 as c_int),\n            SYS_CHDIR => self.sys_chdir(&resolve_path(a1)?),\n            SYS_RT_SIGRETURN => self.sys_rt_sigreturn(),\n            SYS_PIPE => self.sys_pipe(VirtAddr::new(a1)),\n            SYS_CLONE => self.sys_clone(\n                a1,\n                VirtAddr::new(a2),\n                a3,\n                VirtAddr::new(a4),\n                a5,\n                VirtAddr::new(a6),\n            ),\n            SYS_KILL => self.sys_kill(TaskId::new(a1), a2 as c_int),\n            SYS_TKILL => self.sys_kill(TaskId::new(a1), a2 as c_int), // todo\n            SYS_UNLINK => self.sys_unlink(&resolve_path(a1)?),\n            SYS_LSEEK => self.sys_lseek(a1 as FileDesc, a2, a3.into()),\n            SYS_DUP2 => self.sys_dup2(a1 as FileDesc, a2 as FileDesc),\n            SYS_CLOCK_GETTIME => self.sys_clock_gettime(a1, VirtAddr::new(a2)),\n            SYS_NANOSLEEP => self.sys_nanosleep(VirtAddr::new(a1), VirtAddr::new(a2)),\n            SYS_MKDIR => self.sys_mkdir(&resolve_path(a1)?, FileMode::new(a2 as u32)),\n            SYS_GETRANDOM => self.sys_getrandom(VirtAddr::new(a1), a2),\n            SYS_SOCKET => self.sys_socket(a1, a2, a3),\n            SYS_SETSOCKOPT => self.sys_setsockopt(\n                a1 as FileDesc,\n                a2 as c_int,\n                a3 as c_int,\n                VirtAddr::new(a4),\n                a5,\n            ),\n            SYS_MADVISE => Ok(0), // todo\n            _ => Err(kerror!(\n                ENOSYS,\n                \"SyscallHandler::dispatch(): syscall not implemented\"\n            )),\n        };\n\n        if let Err(err) = get_scheduler().try_delivering_signal(self.frame, errno_to_isize(&res)) {\n            if !quiet {\n                log::error!(\"Failed to send signal: {:?}\", err);\n            }\n        }\n\n        res\n    }\n}\n\n#[macro_export]\nmacro_rules! bitflags_from_user {\n    ($st:tt, $input:expr) => {{\n        let bits = $input;\n        match $st::from_bits(bits) {\n            Some(flags) => Ok(flags)?,\n            None => {\n                log::warn!(\n                    concat!(\"unsupported bitflags for \", stringify!($st), \": {:#x}\"),\n                    bits\n                );\n\n                Err($crate::kerror!(\n                    ENOSYS,\n                    \"bitflags_from_user(): unsupported bitflags\"\n                ))?\n            }\n        }\n    }};\n}\n\n#[inline]\nfn resolve_path(uaddr: usize) -> KResult<PathBuf> {\n    Ok(Path::new(CStr::new(VirtAddr::new(uaddr), 512, false)?.as_str()).into())\n}\n\npub fn syscall_name_by_number(n: usize) -> &'static str {\n    match n {\n        0 => \"read\",\n        1 => \"write\",\n        2 => \"open\",\n        3 => \"close\",\n        4 => \"stat\",\n        5 => \"fstat\",\n        6 => \"lstat\",\n        7 => \"poll\",\n        8 => \"lseek\",\n        9 => \"mmap\",\n        10 => \"mprotect\",\n        11 => \"munmap\",\n        12 => \"brk\",\n        13 => \"rt_sigaction\",\n        14 => \"rt_sigprocmask\",\n        15 => \"rt_sigreturn\",\n        16 => \"ioctl\",\n        17 => \"pread64\",\n        18 => \"pwrite64\",\n        19 => \"readv\",\n        20 => \"writev\",\n        21 => \"access\",\n        22 => \"pipe\",\n        23 => \"select\",\n        24 => \"sched_yield\",\n        25 => \"mremap\",\n        26 => \"msync\",\n        27 => \"mincore\",\n        28 => \"madvise\",\n        29 => \"shmget\",\n        30 => \"shmat\",\n        31 => \"shmctl\",\n        32 => \"dup\",\n        33 => \"dup2\",\n        34 => \"pause\",\n        35 => \"nanosleep\",\n        36 => \"getitimer\",\n        37 => \"alarm\",\n        38 => \"setitimer\",\n        39 => \"getpid\",\n        40 => \"sendfile\",\n        41 => \"socket\",\n        42 => \"connect\",\n        43 => \"accept\",\n        44 => \"sendto\",\n        45 => \"recvfrom\",\n        46 => \"sendmsg\",\n        47 => \"recvmsg\",\n        48 => \"shutdown\",\n        49 => \"bind\",\n        50 => \"listen\",\n        51 => \"getsockname\",\n        52 => \"getpeername\",\n        53 => \"socketpair\",\n        54 => \"setsockopt\",\n        55 => \"getsockopt\",\n        56 => \"clone\",\n        57 => \"fork\",\n        58 => \"vfork\",\n        59 => \"execve\",\n        60 => \"exit\",\n        61 => \"wait4\",\n        62 => \"kill\",\n        63 => \"uname\",\n        64 => \"semget\",\n        65 => \"semop\",\n        66 => \"semctl\",\n        67 => \"shmdt\",\n        68 => \"msgget\",\n        69 => \"msgsnd\",\n        70 => \"msgrcv\",\n        71 => \"msgctl\",\n        72 => \"fcntl\",\n        73 => \"flock\",\n        74 => \"fsync\",\n        75 => \"fdatasync\",\n        76 => \"truncate\",\n        77 => \"ftruncate\",\n        78 => \"getdents\",\n        79 => \"getcwd\",\n        80 => \"chdir\",\n        81 => \"fchdir\",\n        82 => \"rename\",\n        83 => \"mkdir\",\n        84 => \"rmdir\",\n        85 => \"creat\",\n        86 => \"link\",\n        87 => \"unlink\",\n        88 => \"symlink\",\n        89 => \"readlink\",\n        90 => \"chmod\",\n        91 => \"fchmod\",\n        92 => \"chown\",\n        93 => \"fchown\",\n        94 => \"lchown\",\n        95 => \"umask\",\n        96 => \"gettimeofday\",\n        97 => \"getrlimit\",\n        98 => \"getrusage\",\n        99 => \"sysinfo\",\n        100 => \"times\",\n        101 => \"ptrace\",\n        102 => \"getuid\",\n        103 => \"syslog\",\n        104 => \"getgid\",\n        105 => \"setuid\",\n        106 => \"setgid\",\n        107 => \"geteuid\",\n        108 => \"getegid\",\n        109 => \"setpgid\",\n        110 => \"getppid\",\n        111 => \"getpgrp\",\n        112 => \"setsid\",\n        113 => \"setreuid\",\n        114 => \"setregid\",\n        115 => \"getgroups\",\n        116 => \"setgroups\",\n        117 => \"setresuid\",\n        118 => \"getresuid\",\n        119 => \"setresgid\",\n        120 => \"getresgid\",\n        121 => \"getpgid\",\n        122 => \"setfsuid\",\n        123 => \"setfsgid\",\n        124 => \"getsid\",\n        125 => \"capget\",\n        126 => \"capset\",\n        127 => \"rt_sigpending\",\n        128 => \"rt_sigtimedwait\",\n        129 => \"rt_sigqueueinfo\",\n        130 => \"rt_sigsuspend\",\n        131 => \"sigaltstack\",\n        132 => \"utime\",\n        133 => \"mknod\",\n        134 => \"uselib\",\n        135 => \"personality\",\n        136 => \"ustat\",\n        137 => \"statfs\",\n        138 => \"fstatfs\",\n        139 => \"sysfs\",\n        140 => \"getpriority\",\n        141 => \"setpriority\",\n        142 => \"sched_setparam\",\n        143 => \"sched_getparam\",\n        144 => \"sched_setscheduler\",\n        145 => \"sched_getscheduler\",\n        146 => \"sched_get_priority_max\",\n        147 => \"sched_get_priority_min\",\n        148 => \"sched_rr_get_interval\",\n        149 => \"mlock\",\n        150 => \"munlock\",\n        151 => \"mlockall\",\n        152 => \"munlockall\",\n        153 => \"vhangup\",\n        154 => \"modify_ldt\",\n        155 => \"pivot_root\",\n        156 => \"_sysctl\",\n        157 => \"prctl\",\n        158 => \"arch_prctl\",\n        159 => \"adjtimex\",\n        160 => \"setrlimit\",\n        161 => \"chroot\",\n        162 => \"sync\",\n        163 => \"acct\",\n        164 => \"settimeofday\",\n        165 => \"mount\",\n        166 => \"umount2\",\n        167 => \"swapon\",\n        168 => \"swapoff\",\n        169 => \"reboot\",\n        170 => \"sethostname\",\n        171 => \"setdomainname\",\n        172 => \"iopl\",\n        173 => \"ioperm\",\n        174 => \"create_module\",\n        175 => \"init_module\",\n        176 => \"delete_module\",\n        177 => \"get_kernel_syms\",\n        178 => \"query_module\",\n        179 => \"quotactl\",\n        180 => \"nfsservctl\",\n        181 => \"getpmsg\",\n        182 => \"putpmsg\",\n        183 => \"afs_syscall\",\n        184 => \"tuxcall\",\n        185 => \"security\",\n        186 => \"gettid\",\n        187 => \"readahead\",\n        188 => \"setxattr\",\n        189 => \"lsetxattr\",\n        190 => \"fsetxattr\",\n        191 => \"getxattr\",\n        192 => \"lgetxattr\",\n        193 => \"fgetxattr\",\n        194 => \"listxattr\",\n        195 => \"llistxattr\",\n        196 => \"flistxattr\",\n        197 => \"removexattr\",\n        198 => \"lremovexattr\",\n        199 => \"fremovexattr\",\n        200 => \"tkill\",\n        201 => \"time\",\n        202 => \"futex\",\n        203 => \"sched_setaffinity\",\n        204 => \"sched_getaffinity\",\n        205 => \"set_thread_area\",\n        206 => \"io_setup\",\n        207 => \"io_destroy\",\n        208 => \"io_getevents\",\n        209 => \"io_submit\",\n        210 => \"io_cancel\",\n        211 => \"get_thread_area\",\n        212 => \"lookup_dcookie\",\n        213 => \"epoll_create\",\n        214 => \"epoll_ctl_old\",\n        215 => \"epoll_wait_old\",\n        216 => \"remap_file_pages\",\n        217 => \"getdents64\",\n        218 => \"set_tid_address\",\n        219 => \"restart_syscall\",\n        220 => \"semtimedop\",\n        221 => \"fadvise64\",\n        222 => \"timer_create\",\n        223 => \"timer_settime\",\n        224 => \"timer_gettime\",\n        225 => \"timer_getoverrun\",\n        226 => \"timer_delete\",\n        227 => \"clock_settime\",\n        228 => \"clock_gettime\",\n        229 => \"clock_getres\",\n        230 => \"clock_nanosleep\",\n        231 => \"exit_group\",\n        232 => \"epoll_wait\",\n        233 => \"epoll_ctl\",\n        234 => \"tgkill\",\n        235 => \"utimes\",\n        236 => \"vserver\",\n        237 => \"mbind\",\n        238 => \"set_mempolicy\",\n        239 => \"get_mempolicy\",\n        240 => \"mq_open\",\n        241 => \"mq_unlink\",\n        242 => \"mq_timedsend\",\n        243 => \"mq_timedreceive\",\n        244 => \"mq_notify\",\n        245 => \"mq_getsetattr\",\n        246 => \"kexec_load\",\n        247 => \"waitid\",\n        248 => \"add_key\",\n        249 => \"request_key\",\n        250 => \"keyctl\",\n        251 => \"ioprio_set\",\n        252 => \"ioprio_get\",\n        253 => \"inotify_init\",\n        254 => \"inotify_add_watch\",\n        255 => \"inotify_rm_watch\",\n        256 => \"migrate_pages\",\n        257 => \"openat\",\n        258 => \"mkdirat\",\n        259 => \"mknodat\",\n        260 => \"fchownat\",\n        261 => \"futimesat\",\n        262 => \"newfstatat\",\n        263 => \"unlinkat\",\n        264 => \"renameat\",\n        265 => \"linkat\",\n        266 => \"symlinkat\",\n        267 => \"readlinkat\",\n        268 => \"fchmodat\",\n        269 => \"faccessat\",\n        270 => \"pselect6\",\n        271 => \"ppoll\",\n        272 => \"unshare\",\n        273 => \"set_robust_list\",\n        274 => \"get_robust_list\",\n        275 => \"splice\",\n        276 => \"tee\",\n        277 => \"sync_file_range\",\n        278 => \"vmsplice\",\n        279 => \"move_pages\",\n        280 => \"utimensat\",\n        281 => \"epoll_pwait\",\n        282 => \"signalfd\",\n        283 => \"timerfd_create\",\n        284 => \"eventfd\",\n        285 => \"fallocate\",\n        286 => \"timerfd_settime\",\n        287 => \"timerfd_gettime\",\n        288 => \"accept4\",\n        289 => \"signalfd4\",\n        290 => \"eventfd2\",\n        291 => \"epoll_create1\",\n        292 => \"dup3\",\n        293 => \"pipe2\",\n        294 => \"inotify_init1\",\n        295 => \"preadv\",\n        296 => \"pwritev\",\n        297 => \"rt_tgsigqueueinfo\",\n        298 => \"perf_event_open\",\n        299 => \"recvmmsg\",\n        300 => \"fanotify_init\",\n        301 => \"fanotify_mark\",\n        302 => \"prlimit64\",\n        303 => \"name_to_handle_at\",\n        304 => \"open_by_handle_at\",\n        305 => \"clock_adjtime\",\n        306 => \"syncfs\",\n        307 => \"sendmmsg\",\n        308 => \"setns\",\n        309 => \"getcpu\",\n        310 => \"process_vm_readv\",\n        311 => \"process_vm_writev\",\n        312 => \"kcmp\",\n        313 => \"finit_module\",\n        314 => \"sched_setattr\",\n        315 => \"sched_getattr\",\n        316 => \"renameat2\",\n        317 => \"seccomp\",\n        318 => \"getrandom\",\n        319 => \"memfd_create\",\n        320 => \"kexec_file_load\",\n        321 => \"bpf\",\n        322 => \"execveat\",\n        323 => \"userfaultfd\",\n        324 => \"membarrier\",\n        325 => \"mlock2\",\n        326 => \"copy_file_range\",\n        327 => \"preadv2\",\n        328 => \"pwritev2\",\n        329 => \"pkey_mprotect\",\n        330 => \"pkey_alloc\",\n        331 => \"pkey_free\",\n        332 => \"statx\",\n        333 => \"io_pgetevents\",\n        334 => \"rseq\",\n\n        51729 => \"kados_debug\",\n        _ => \"(unknown)\",\n    }\n}\n\npub const SYS_READ: usize = 0;\npub const SYS_WRITE: usize = 1;\npub const SYS_OPEN: usize = 2;\npub const SYS_CLOSE: usize = 3;\npub const SYS_STAT: usize = 4;\npub const SYS_FSTAT: usize = 5;\npub const SYS_LSTAT: usize = 6;\npub const SYS_POLL: usize = 7;\npub const SYS_LSEEK: usize = 8;\npub const SYS_MMAP: usize = 9;\npub const SYS_MPROTECT: usize = 10;\npub const SYS_MUNMAP: usize = 11;\npub const SYS_BRK: usize = 12;\npub const SYS_RT_SIGACTION: usize = 13;\npub const SYS_RT_SIGPROCMASK: usize = 14;\npub const SYS_RT_SIGRETURN: usize = 15;\npub const SYS_IOCTL: usize = 16;\npub const SYS_READV: usize = 19;\npub const SYS_WRITEV: usize = 20;\npub const SYS_PIPE: usize = 22;\npub const SYS_SELECT: usize = 23;\npub const SYS_MREMAP: usize = 25;\npub const SYS_MADVISE: usize = 28;\npub const SYS_DUP2: usize = 33;\npub const SYS_NANOSLEEP: usize = 35;\npub const SYS_GETPID: usize = 39;\npub const SYS_SOCKET: usize = 41;\npub const SYS_CONNECT: usize = 42;\npub const SYS_ACCEPT: usize = 43;\npub const SYS_SENDTO: usize = 44;\npub const SYS_RECVFROM: usize = 45;\npub const SYS_SHUTDOWN: usize = 48;\npub const SYS_BIND: usize = 49;\npub const SYS_LISTEN: usize = 50;\npub const SYS_GETSOCKNAME: usize = 51;\npub const SYS_GETPEERNAME: usize = 52;\npub const SYS_SETSOCKOPT: usize = 54;\npub const SYS_GETSOCKOPT: usize = 55;\npub const SYS_CLONE: usize = 56;\npub const SYS_FORK: usize = 57;\npub const SYS_EXECVE: usize = 59;\npub const SYS_EXIT: usize = 60;\npub const SYS_WAIT4: usize = 61;\npub const SYS_KILL: usize = 62;\npub const SYS_UNAME: usize = 63;\npub const SYS_FCNTL: usize = 72;\npub const SYS_FSYNC: usize = 74;\npub const SYS_GETCWD: usize = 79;\npub const SYS_CHDIR: usize = 80;\npub const SYS_MKDIR: usize = 83;\npub const SYS_LINK: usize = 86;\npub const SYS_UNLINK: usize = 87;\npub const SYS_READLINK: usize = 89;\npub const SYS_CHMOD: usize = 90;\npub const SYS_CHOWN: usize = 92;\npub const SYS_GETUID: usize = 102;\npub const SYS_SYSLOG: usize = 103;\npub const SYS_SETUID: usize = 105;\npub const SYS_SETGID: usize = 106;\npub const SYS_GETEUID: usize = 107;\npub const SYS_SETPGID: usize = 109;\npub const SYS_GETPPID: usize = 110;\npub const SYS_GETPGRP: usize = 111;\npub const SYS_GETPGID: usize = 121;\npub const SYS_SETGROUPS: usize = 116;\npub const SYS_ARCH_PRCTL: usize = 158;\npub const SYS_REBOOT: usize = 169;\npub const SYS_GETTID: usize = 186;\npub const SYS_TKILL: usize = 200;\npub const SYS_GETDENTS64: usize = 217;\npub const SYS_SET_TID_ADDRESS: usize = 218;\npub const SYS_CLOCK_GETTIME: usize = 228;\npub const SYS_EXIT_GROUP: usize = 231;\npub const SYS_UTIMES: usize = 235;\npub const SYS_LINKAT: usize = 265;\npub const SYS_GETRANDOM: usize = 318;\n"
  },
  {
    "path": "src/userland/syscall/syscall_impl/fs.rs",
    "content": "use core::{mem::size_of, ops::Add};\n\nuse alloc::{borrow::ToOwned, string::String, sync::Arc};\nuse x86::random::rdrand_slice;\n\nuse crate::{\n    bitflags_from_user,\n    fs::{\n        alloc_inode_no,\n        initramfs::{dir::InitRamFsDir, file::InitRamFsFile},\n        opened_file::{FileDesc, LseekWhence, OpenFlags},\n        path::Path,\n        FileMode, INode, PollStatus, O_RDWR, O_WRONLY, POLL_WAIT_QUEUE, S_IFDIR, S_IFREG,\n    },\n    kbail, kerror,\n    mem::addr::VirtAddr,\n    task::current_task,\n    userland::{\n        buffer::{UserBuffer, UserBufferMut, UserBufferReader, UserBufferWriter},\n        syscall::SyscallHandler,\n    },\n    util::{\n        align_up,\n        ctypes::{c_int, c_nfds, c_short},\n        errno::Errno,\n        KResult,\n    },\n};\n\npub const F_DUPFD: c_int = 0;\npub const F_GETFD: c_int = 1;\npub const F_SETFD: c_int = 2;\npub const F_GETFL: c_int = 3;\npub const F_SETFL: c_int = 4;\npub const F_SETLK: c_int = 6;\n\n// Linux-specific commands.\npub const F_LINUX_SPECIFIC_BASE: c_int = 1024;\npub const F_DUPFD_CLOEXEC: c_int = F_LINUX_SPECIFIC_BASE + 6;\n\nimpl SyscallHandler<'_> {\n    pub fn sys_fcntl(&mut self, fd: FileDesc, cmd: c_int, arg: usize) -> KResult<isize> {\n        let current = current_task();\n        let mut files = current.opened_files.lock();\n        match cmd {\n            F_GETFL => {\n                let flags = files.get(fd)?.get_flags();\n                Ok(flags.bits() as isize)\n            }\n            F_SETFD => {\n                files.get(fd)?.set_close_on_exec(arg == 1);\n                Ok(0)\n            }\n            F_SETFL => {\n                files\n                    .get(fd)?\n                    .set_flags(OpenFlags::from_bits_truncate(arg as i32))?;\n                Ok(0)\n            }\n            F_GETFD => Ok(0),\n            F_DUPFD_CLOEXEC => {\n                let fd = files.dup(fd, Some(arg as i32), OpenFlags::O_CLOEXEC)?;\n                Ok(fd as isize)\n            }\n            F_SETLK => Ok(0),\n            _ => Err(kerror!(ENOSYS, \"sys_fctnl(): unknown command\")),\n        }\n    }\n\n    pub fn sys_getcwd(&mut self, buf: VirtAddr, len: usize) -> KResult<isize> {\n        let cwd = current_task().root_fs.lock().cwd_path().resolve_abs_path();\n\n        if len < cwd.as_str().len() {\n            return Err(kerror!(ERANGE, \"sys_getcwd(): buffer too small\"));\n        }\n\n        let mut cwd = String::from(cwd.as_str());\n        cwd.push('\\0');\n        let buf_val = buf.value();\n        let ubuf = UserBufferMut::from_vaddr(buf, len);\n        let mut writer = UserBufferWriter::from_buf(ubuf);\n        writer.write_bytes(cwd.as_bytes()).unwrap(); // this currently never returns Err; may change\n        Ok(buf_val as isize)\n    }\n\n    pub fn sys_getdents64(\n        &mut self,\n        fd: FileDesc,\n        dir_ptr: VirtAddr,\n        len: usize,\n    ) -> KResult<isize> {\n        let current = current_task();\n        let opened_files = current.opened_files.lock();\n        let dir = opened_files.get(fd)?;\n        let mut writer = UserBufferWriter::from_vaddr(dir_ptr, len);\n        while let Some(entry) = dir.readdir()? {\n            let alignment = size_of::<u64>();\n            let record_len = align_up(\n                size_of::<u64>() * 2 + size_of::<u16>() + 1 + entry.name.len() + 1,\n                alignment,\n            );\n            if writer.written_len() + record_len > len {\n                break;\n            }\n\n            writer.write(entry.inode_no as u64)?;\n            writer.write(dir.pos() as u64)?;\n            writer.write(record_len as u16)?;\n            writer.write(entry.file_type as u8)?;\n            writer.write_bytes(entry.name.as_bytes())?;\n            writer.write(0u8)?;\n            writer.skip_until_alignment(alignment)?;\n        }\n\n        Ok(writer.written_len() as isize)\n    }\n\n    pub fn sys_chdir(&mut self, path: &Path) -> KResult<isize> {\n        current_task().root_fs.lock().chdir(path)?;\n        Ok(0)\n    }\n\n    pub fn sys_ioctl(&mut self, fd: FileDesc, cmd: usize, arg: usize) -> KResult<isize> {\n        let opened_file = current_task().get_opened_file_by_fd(fd)?;\n        opened_file.ioctl(cmd, arg)\n    }\n\n    pub fn sys_getrandom(&mut self, buf: VirtAddr, bufflen: usize) -> KResult<isize> {\n        let mut v = alloc::vec![0u8; bufflen];\n        unsafe {\n            rdrand_slice(&mut v);\n        }\n        unsafe { buf.write_bytes_user(&v) }?;\n        Ok(bufflen as isize)\n    }\n}\n\nfn create(path: &Path, _flags: OpenFlags, mode: FileMode) -> KResult<INode> {\n    // if flags.contains(OpenFlags::O_DIRECTORY) {\n    //     return Err(errno!(Errno::EINVAL, \"create(): invalid flags\"));\n    // }\n\n    let (parent_dir, name) = path\n        .parent_and_basename()\n        .ok_or(kerror!(EINVAL, \"create(): invalid path\"))?;\n\n    let current = current_task();\n    let root = current.root_fs.lock();\n    let inode = if mode.is_regular_file() {\n        INode::File(Arc::new(InitRamFsFile::new(\n            name.to_owned(),\n            alloc_inode_no(),\n        )))\n    } else if mode.is_directory() {\n        INode::Dir(Arc::new(InitRamFsDir::new(\n            name.to_owned(),\n            alloc_inode_no(),\n        )))\n    } else {\n        return Err(kerror!(EINVAL, \"create(): invalid flags\"));\n    };\n    root.lookup(parent_dir, true)?\n        .as_dir()?\n        .insert(inode.clone());\n    Ok(inode)\n}\n\nimpl SyscallHandler<'_> {\n    pub fn sys_open(&mut self, path: &Path, flags: OpenFlags, mode: FileMode) -> KResult<isize> {\n        let current = current_task();\n        // log::trace!(\"Attempting to open {}\", path);\n        if flags.contains(OpenFlags::O_CREAT) {\n            match create(path, flags, FileMode::new(S_IFREG | mode.access_mode())) {\n                Ok(_) => {}\n                Err(err) if err.errno() == Some(Errno::EINVAL) => {}\n                Err(err)\n                    if flags.contains(OpenFlags::O_EXCL) && err.errno() == Some(Errno::EEXIST) => {}\n                Err(err) => return Err(err),\n            }\n        }\n\n        let root = current.root_fs.lock();\n        let mut opened_files = current.opened_files.lock();\n        let path_comp = root.lookup_path(path, true)?;\n        if flags.contains(OpenFlags::O_DIRECTORY) && !path_comp.inode.is_dir() {\n            kbail!(ENOTDIR, \"sys_open(): not a directory\");\n        }\n        let access_mode = mode.access_mode();\n        if path_comp.inode.is_dir() && (access_mode == O_WRONLY || access_mode == O_RDWR) {\n            kbail!(EISDIR, \"sys_open(): is a directory\");\n        }\n\n        let fd = opened_files.open(path_comp, flags)?;\n        log::trace!(\"Opened {} as {}.\", path, fd);\n\n        Ok(fd as isize)\n    }\n\n    pub fn sys_close(&mut self, fd: FileDesc) -> KResult<isize> {\n        let current = current_task();\n        current.opened_files.lock().close(fd)?;\n        log::trace!(\"Closed {}\", fd);\n        Ok(0)\n    }\n\n    pub fn sys_mkdir(&mut self, path: &Path, mode: FileMode) -> KResult<isize> {\n        create(\n            path,\n            OpenFlags::empty(),\n            FileMode::new(S_IFDIR | mode.access_mode()),\n        )?;\n        Ok(0)\n    }\n\n    pub fn sys_pipe(&mut self, fds: VirtAddr) -> KResult<isize> {\n        if fds == VirtAddr::null() {\n            kbail!(EINVAL, \"sys_pipe(): fds was NULL\");\n        }\n\n        let current = current_task();\n        let pipe = current.opened_files.lock().open_pipe(OpenFlags::empty())?;\n\n        let write_fd = pipe.write_fd();\n        let read_fd = pipe.read_fd();\n\n        let mut writer = UserBufferWriter::from_vaddr(fds, size_of::<FileDesc>() * 2);\n        writer.write(write_fd)?;\n        writer.write(read_fd)?;\n\n        Ok(0)\n    }\n\n    pub fn sys_unlink(&mut self, path: &Path) -> KResult<isize> {\n        if path.is_empty() {\n            kbail!(EINVAL, \"sys_unlink(): path was empty\");\n        }\n        let current = current_task();\n        let root = current.root_fs.lock();\n        // log::debug!(\"Attempting to unlink {}\", path);\n        let path_component = root.lookup_path(path, true)?;\n        path_component\n            .parent_dir\n            .as_ref()\n            .unwrap()\n            .inode\n            .as_dir()\n            .unwrap()\n            .unlink(&path_component.name)?;\n        Ok(0)\n    }\n}\n\nimpl SyscallHandler<'_> {\n    pub fn sys_poll(&mut self, fds: VirtAddr, nfds: c_nfds, timeout: c_int) -> KResult<isize> {\n        let timeout = if timeout >= 0 {\n            Some(timeout as usize)\n        } else {\n            None\n        };\n\n        POLL_WAIT_QUEUE.sleep_signalable_until(timeout, || {\n            let mut ready_fds = 0;\n            let fds_len = (nfds as usize) * (size_of::<FileDesc>() + 2 * size_of::<c_short>());\n            let mut reader = UserBufferReader::from_vaddr(fds, fds_len);\n            for _ in 0..nfds {\n                let fd = reader.read::<FileDesc>()?;\n                let events = bitflags_from_user!(PollStatus, reader.read::<c_short>()?);\n\n                if fd < 0 {\n                    kbail!(EINVAL, \"sys_poll(): invalid fd\");\n                } else if events.is_empty() {\n                    kbail!(EINVAL, \"sys_poll(): invalid events\");\n                } else {\n                    let current = current_task();\n                    let opened_files = current.opened_files.lock();\n                    let status = opened_files.get(fd)?.poll()?;\n\n                    let revents = events & status;\n                    if !revents.is_empty() {\n                        ready_fds += 1;\n                    }\n\n                    unsafe {\n                        fds.add(reader.read_len())\n                            .write::<c_short>(revents.bits())?;\n                    }\n\n                    reader.skip(size_of::<c_short>())?;\n                };\n            }\n\n            if ready_fds > 0 {\n                Ok(Some(ready_fds))\n            } else {\n                Ok(None)\n            }\n        })\n    }\n\n    pub fn sys_read(&mut self, fd: FileDesc, vaddr: VirtAddr, len: usize) -> KResult<isize> {\n        let opened_file = current_task().get_opened_file_by_fd(fd)?;\n        let ubuf = UserBufferMut::from_vaddr(vaddr, len);\n        let read_len = opened_file.read(ubuf)?;\n        // log::debug!(\"read {}\", read_len);\n        Ok(read_len as isize)\n    }\n\n    pub fn sys_stat(&mut self, path: &Path, buf: VirtAddr) -> KResult<isize> {\n        // log::debug!(\"sys_stat-ing path {}\", path);\n        let stat = current_task().root_fs.lock().lookup(path, true)?.stat()?;\n        unsafe {\n            buf.write_user(stat)?;\n        }\n        Ok(0)\n    }\n\n    pub fn sys_lstat(&mut self, path: &Path, buf: VirtAddr) -> KResult<isize> {\n        let stat = current_task().root_fs.lock().lookup(path, false)?.stat()?;\n        unsafe { buf.write_user(stat) }?;\n        Ok(0)\n    }\n\n    pub fn sys_fstat(&mut self, fd: FileDesc, buf: VirtAddr) -> KResult<isize> {\n        let file = current_task().get_opened_file_by_fd(fd)?;\n        let stat = file.path().inode.stat()?;\n        unsafe { buf.write_user(stat) }?;\n        Ok(0)\n    }\n\n    pub fn sys_write(&mut self, fd: FileDesc, addr: VirtAddr, len: usize) -> KResult<isize> {\n        let user_buf = UserBuffer::from_vaddr(addr, len);\n        let file = current_task().get_opened_file_by_fd(fd)?;\n        let written_len = file.write(user_buf)?;\n        Ok(written_len as isize)\n    }\n}\n\npub const IOV_MAX: usize = 1024;\n#[repr(C)]\n#[derive(Clone, Copy)]\npub struct IoVec {\n    base: VirtAddr,\n    len: usize,\n}\n\nimpl SyscallHandler<'_> {\n    pub fn sys_readv(\n        &mut self,\n        fd: FileDesc,\n        iov_base: VirtAddr,\n        iov_count: usize,\n    ) -> KResult<isize> {\n        let iov_count = iov_count.min(IOV_MAX);\n\n        let file = current_task().get_opened_file_by_fd(fd)?;\n        let mut total: usize = 0;\n        for i in 0..iov_count {\n            let mut iov: IoVec =\n                unsafe { iov_base.add(i * size_of::<IoVec>()).read_user::<IoVec>() }?;\n\n            match total.checked_add(iov.len) {\n                Some(len) if len > isize::MAX as usize => {\n                    iov.len = isize::MAX as usize - total;\n                }\n                None => {\n                    iov.len = isize::MAX as usize - total;\n                }\n                _ => {}\n            }\n\n            if iov.len == 0 {\n                continue;\n            }\n\n            total += file.read(UserBufferMut::from_vaddr(iov.base, iov.len))?;\n        }\n\n        Ok(total as isize)\n    }\n\n    pub fn sys_writev(\n        &mut self,\n        fd: FileDesc,\n        iov_base: VirtAddr,\n        iov_count: usize,\n    ) -> KResult<isize> {\n        let iov_count = iov_count.min(IOV_MAX);\n\n        let file = current_task().get_opened_file_by_fd(fd)?;\n        let mut total: usize = 0;\n        for i in 0..iov_count {\n            let mut iov: IoVec =\n                unsafe { iov_base.add(i * size_of::<IoVec>()).read_user::<IoVec>() }?;\n\n            match total.checked_add(iov.len) {\n                Some(len) if len > isize::MAX as usize => {\n                    iov.len = isize::MAX as usize - total;\n                }\n                None => {\n                    iov.len = isize::MAX as usize - total;\n                }\n                _ => {}\n            }\n\n            if iov.len == 0 {\n                continue;\n            }\n\n            total += file.write(UserBuffer::from_vaddr(iov.base, iov.len))?;\n        }\n\n        Ok(total as isize)\n    }\n\n    pub fn sys_lseek(\n        &mut self,\n        fd: FileDesc,\n        offset: usize,\n        whence: LseekWhence,\n    ) -> KResult<isize> {\n        let file = current_task().get_opened_file_by_fd(fd)?;\n        file.lseek(offset, whence).map(|off| off as isize)\n    }\n\n    pub fn sys_dup2(&mut self, old_fd: FileDesc, new_fd: FileDesc) -> KResult<isize> {\n        let current = current_task();\n        let _old = current.get_opened_file_by_fd(old_fd)?;\n        // now that we know it's valid, check if they're the same (as per the man page)\n        if new_fd == old_fd {\n            return Ok(new_fd as isize);\n        }\n\n        if let Ok(_existing) = current.get_opened_file_by_fd(new_fd) {\n            let mut files = current.opened_files.lock();\n            files.close(new_fd)?;\n        }\n\n        let mut files = current.opened_files.lock();\n        let newfd_dup = files.dup2(old_fd, new_fd)?;\n        assert_eq!(new_fd, newfd_dup);\n        Ok(new_fd as isize)\n    }\n\n    pub fn sys_socket(&mut self, domain: usize, typ: usize, protocol: usize) -> KResult<isize> {\n        let current = current_task();\n        let mut files = current.opened_files.lock();\n        let fd = files.open_socket(domain, typ, protocol)?;\n        Ok(fd as isize)\n    }\n\n    pub fn sys_setsockopt(\n        &mut self,\n        fd: FileDesc,\n        level: c_int,\n        _option_name: c_int,\n        _option_value: VirtAddr,\n        _option_len: usize,\n    ) -> KResult<isize> {\n        let current = current_task();\n        let _socket = current.get_opened_file_by_fd(fd)?;\n        match level {\n            SOL_SOCKET => {\n                // todo\n            }\n            _ => kbail!(EINVAL, \"sys_setsockopt(): unknown level\"),\n        }\n        Ok(0)\n    }\n}\n\nconst SOL_SOCKET: c_int = 1;\n"
  },
  {
    "path": "src/userland/syscall/syscall_impl/mem.rs",
    "content": "use crate::{\n    fs::opened_file::FileDesc,\n    mem::addr::VirtAddr,\n    task::{\n        current_task,\n        vmem::{MMapFlags, MMapProt},\n    },\n    userland::syscall::SyscallHandler,\n    util::KResult,\n};\n\nimpl SyscallHandler<'_> {\n    pub fn sys_mmap(\n        &mut self,\n        addr: VirtAddr,\n        size: usize,\n        prot: MMapProt,\n        flags: MMapFlags,\n        fd: FileDesc,\n        offset: usize,\n    ) -> KResult<isize> {\n        if fd as isize != -1 {\n            todo!(\"mmap file\");\n        }\n\n        let current = current_task();\n        let vmem = current.vmem();\n        current\n            .arch_mut()\n            .address_space\n            .with_mapper(|mut mapper| {\n                vmem.lock()\n                    .mmap(addr, size, prot, flags, fd, offset, &mut mapper)\n            })\n            .map(|addr| addr.value() as isize)\n    }\n\n    pub fn sys_mprotect(&mut self, addr: VirtAddr, size: usize, prot: MMapProt) -> KResult<isize> {\n        current_task().vmem().lock().mprotect(addr, size, prot)?;\n        Ok(0)\n    }\n\n    // pub fn sys_brk(&mut self, addr: VirtAddr) -> KResult<isize> {\n    //     let current = current_task();\n    //     let new_addr = current\n    //         .arch_mut()\n    //         .address_space\n    //         .with_mapper(|mut mapper| current.vmem().lock().brk(&mut mapper, addr))?;\n    //     Ok(new_addr.value() as isize)\n    // }\n\n    pub fn sys_munmap(&mut self, addr: VirtAddr, size: usize) -> KResult<isize> {\n        let current = current_task();\n        current.arch_mut().address_space.with_mapper(|mut mapper| {\n            current.vmem().lock().munmap(&mut mapper, addr, addr + size)\n        })?;\n        Ok(0)\n    }\n\n    pub fn sys_mremap(&mut self, addr: VirtAddr, old_size: usize, size: usize) -> KResult<isize> {\n        let current = current_task();\n        let new_addr = current.arch_mut().address_space.with_mapper(|mut mapper| {\n            current\n                .vmem()\n                .lock()\n                .mremap(addr, old_size, size, &mut mapper)\n        })?;\n        Ok(new_addr.value() as isize)\n    }\n}\n"
  },
  {
    "path": "src/userland/syscall/syscall_impl/mod.rs",
    "content": "pub mod fs;\npub mod mem;\npub mod signal;\npub mod sys;\npub mod task;\npub mod time;\n"
  },
  {
    "path": "src/userland/syscall/syscall_impl/signal.rs",
    "content": "use crate::{\n    kbail, kerror,\n    mem::addr::VirtAddr,\n    task::{\n        current_task, get_scheduler,\n        signal::{SigAction, SignalMask, DEFAULT_ACTIONS, SIG_DFL, SIG_ERR, SIG_IGN},\n        TaskId,\n    },\n    userland::syscall::SyscallHandler,\n    util::{ctypes::c_int, error::KResult},\n};\n\nimpl SyscallHandler<'_> {\n    pub fn sys_rt_sigprocmask(\n        &mut self,\n        how: usize,\n        set: VirtAddr,\n        mut oldset: VirtAddr,\n        length: usize,\n    ) -> KResult<isize> {\n        if length != 8 {\n            log::warn!(\"sys_rt_sigprocmask: length != 8\");\n        }\n\n        let how = match how {\n            0 => SignalMask::Block,\n            1 => SignalMask::Unblock,\n            2 => SignalMask::Set,\n            _ => kbail!(EINVAL, \"sys_rt_sigprocmask(): invalid how\"),\n        };\n\n        current_task().set_signal_mask(how, set, &mut oldset, length)?;\n\n        Ok(0)\n    }\n\n    pub fn sys_rt_sigaction(\n        &mut self,\n        signum: c_int,\n        act: VirtAddr,\n        oldact: VirtAddr,\n    ) -> KResult<isize> {\n        if oldact != VirtAddr::null() {\n            let action = current_task().signals.lock().get_action(signum);\n            let action = match action {\n                SigAction::Ignore => SIG_IGN,\n                SigAction::Terminate => SIG_ERR, // todo?\n                SigAction::Handler { handler } => handler as usize,\n            };\n            unsafe { oldact.write_user(action) }?;\n        }\n        if act != VirtAddr::null() {\n            let handler = unsafe { act.read_user::<usize>() }?;\n            let new_action = match handler {\n                SIG_IGN => SigAction::Ignore,\n                SIG_DFL => match DEFAULT_ACTIONS.get(signum as usize) {\n                    Some(def) => *def,\n                    None => {\n                        kbail!(EINVAL, \"sys_rt_sigaction(): invalid signal number\");\n                    }\n                },\n                _ => SigAction::Handler {\n                    handler: unsafe { core::mem::transmute::<usize, fn()>(handler) },\n                },\n            };\n\n            current_task()\n                .signals\n                .lock()\n                .set_action(signum, new_action)?;\n        }\n\n        Ok(0)\n    }\n\n    pub fn sys_rt_sigreturn(&mut self) -> KResult<isize> {\n        get_scheduler().restore_signaled_user_stack(self.frame);\n        kbail!(EINTR, \"sys_rt_sigreturn(): should not return\")\n    }\n\n    pub fn sys_kill(&mut self, pid: TaskId, signum: c_int) -> KResult<isize> {\n        let sched = get_scheduler();\n\n        sched.send_signal_to(\n            sched\n                .find_task(pid)\n                .ok_or(kerror!(ESRCH, \"sys_kill(): pid not found\"))?,\n            signum,\n        );\n        Ok(0)\n    }\n}\n"
  },
  {
    "path": "src/userland/syscall/syscall_impl/sys.rs",
    "content": "use crate::{\n    mem::addr::VirtAddr,\n    userland::{buffer::UserBufferWriter, syscall::SyscallHandler},\n    util::KResult,\n};\n\nconst UTS_FIELD_LEN: usize = 65;\n\nimpl SyscallHandler<'_> {\n    pub fn sys_uname(&mut self, buf: VirtAddr) -> KResult<isize> {\n        let mut writer = UserBufferWriter::from_vaddr(buf, UTS_FIELD_LEN * 6);\n        writer.write_bytes_or_zeros(b\"Linux\", UTS_FIELD_LEN)?;\n        writer.write_bytes_or_zeros(b\"\", UTS_FIELD_LEN)?;\n        writer.write_bytes_or_zeros(b\"4.0.0\", UTS_FIELD_LEN)?;\n        writer.write_bytes_or_zeros(b\"K4DOS\", UTS_FIELD_LEN)?;\n        writer.write_bytes_or_zeros(b\"\", UTS_FIELD_LEN)?;\n        writer.write_bytes_or_zeros(b\"\", UTS_FIELD_LEN)?;\n        Ok(0)\n    }\n}\n"
  },
  {
    "path": "src/userland/syscall/syscall_impl/task.rs",
    "content": "use core::{mem::size_of, ops::Add};\n\nuse alloc::{sync::Arc, vec::Vec};\nuse bitflags::bitflags;\n\nuse crate::{\n    arch::time,\n    fs::path::Path,\n    kbail, kerror,\n    mem::addr::VirtAddr,\n    task::{current_task, get_scheduler, group::PgId, Task, TaskId, TaskState, JOIN_WAIT_QUEUE},\n    userland::{buffer::CStr, syscall::SyscallHandler},\n    util::{ctypes::c_int, errno::Errno, KResult},\n};\n\nuse super::time::TimeSpec;\n\nconst ARG_MAX: usize = 512;\nconst ARG_LEN_MAX: usize = 4096;\nconst ENV_MAX: usize = 512;\nconst ENV_LEN_MAX: usize = 4096;\n\nimpl SyscallHandler<'_> {\n    pub fn sys_arch_prctl(&mut self, code: i32, uaddr: VirtAddr) -> KResult<isize> {\n        arch_prctl(current_task(), code, uaddr)?;\n        Ok(0)\n    }\n\n    pub fn sys_fork(&mut self) -> KResult<isize> {\n        let current = current_task();\n        let _guard = current.arch_mut().address_space.temporarily_switch();\n        let child = current.fork();\n        Ok(child.pid().as_usize() as isize)\n    }\n\n    pub fn sys_clone(\n        &mut self,\n        _clone_flags: usize,\n        user_stack: VirtAddr,\n        r8: usize,\n        args: VirtAddr,\n        r9: usize,\n        entry_point: VirtAddr,\n    ) -> KResult<isize> {\n        let child = current_task().clone_process(entry_point, user_stack, args, r8, r9, self.frame);\n        Ok(child.pid().as_usize() as isize)\n    }\n\n    pub fn sys_execve(\n        &mut self,\n        path: &Path,\n        argv_addr: VirtAddr,\n        envp_addr: VirtAddr,\n    ) -> KResult<isize> {\n        let current = current_task();\n        let _guard = current.arch_mut().address_space.temporarily_switch();\n        log::debug!(\"Statting path {}\", path);\n        let exefile = current\n            .root_fs\n            .lock()\n            .lookup(path, true)?\n            .as_file()?\n            .clone();\n\n        let mut argv = Vec::new();\n        for i in 0..ARG_MAX {\n            let ptr = argv_addr.add(i * size_of::<usize>());\n            let str_ptr = unsafe { ptr.read_user::<usize>() }?;\n            if str_ptr != 0 {\n                argv.push(CStr::new(VirtAddr::new(str_ptr), ARG_LEN_MAX, false)?);\n            } else {\n                break;\n            }\n        }\n\n        let mut envp = Vec::new();\n        for i in 0..ENV_MAX {\n            let ptr = envp_addr.add(i * size_of::<usize>());\n            let str_ptr = unsafe { ptr.read_user::<usize>() }?;\n            if str_ptr != 0 {\n                envp.push(CStr::new(VirtAddr::new(str_ptr), ENV_LEN_MAX, false)?);\n            } else {\n                break;\n            }\n        }\n        let argv: Vec<&[u8]> = argv.as_slice().iter().map(|s| s.as_bytes()).collect();\n        let envp: Vec<&[u8]> = envp.as_slice().iter().map(|s| s.as_bytes()).collect();\n        current.exec(exefile, &argv, &envp)?;\n        Ok(0)\n    }\n\n    pub fn sys_exit(&mut self, status: c_int) -> KResult<isize> {\n        get_scheduler().exit_current(status);\n        Ok(0)\n    }\n\n    pub fn sys_set_tid_address(&mut self, _addr: VirtAddr) -> KResult<isize> {\n        // todo: use addr\n        Ok(current_task().pid().as_usize() as isize)\n    }\n\n    pub fn sys_getpid(&mut self) -> KResult<isize> {\n        Ok(current_task().pid().as_usize() as isize)\n    }\n\n    pub fn sys_getppid(&mut self) -> KResult<isize> {\n        Ok(current_task().ppid().as_usize() as isize)\n    }\n\n    pub fn sys_getpgid(&mut self, pid: TaskId) -> KResult<isize> {\n        if pid.as_usize() == 0 {\n            Ok(current_task().pgid().unwrap() as isize)\n        } else {\n            Ok(get_scheduler()\n                .find_task(pid)\n                .ok_or(kerror!(ESRCH, \"sys_getpgid(): task not found\"))?\n                .pgid()\n                .unwrap() as isize)\n        }\n    }\n\n    pub fn sys_setpgid(&mut self, pid: TaskId, pgid: PgId) -> KResult<isize> {\n        if pid.as_usize() == 0 {\n            current_task()\n                .group\n                .borrow_mut()\n                .upgrade()\n                .unwrap()\n                .lock()\n                .set_pgid(pgid);\n        } else {\n            get_scheduler()\n                .find_task(pid)\n                .ok_or(kerror!(ESRCH, \"sys_setpgid(): task not found\"))?\n                .group\n                .borrow_mut()\n                .upgrade()\n                .unwrap()\n                .lock()\n                .set_pgid(pgid);\n        }\n        Ok(0)\n    }\n}\n\nbitflags! {\n    pub struct WaitOptions: c_int {\n        const WNOHANG   = 1;\n        const WUNTRACED = 2;\n    }\n}\n\nimpl SyscallHandler<'_> {\n    pub fn sys_wait4(\n        &mut self,\n        pid: TaskId,\n        status: VirtAddr,\n        options: WaitOptions,\n        _rusage: VirtAddr, // could be null\n    ) -> KResult<isize> {\n        let (got_pid, status_val) = JOIN_WAIT_QUEUE.sleep_signalable_until(None, || {\n            let current = current_task();\n            let children = current.children.lock();\n            if children.is_empty() {\n                kbail!(ECHILD, \"sys_wait4(): all subprocesses have exited\");\n            }\n            for child in children.iter() {\n                if pid.as_usize() as isize > 0 && pid != child.pid() {\n                    continue;\n                }\n\n                if pid.as_usize() == 0 {\n                    todo!()\n                }\n\n                if let TaskState::ExitedWith(status_val) = child.get_state() {\n                    return Ok(Some((child.pid(), status_val)));\n                }\n            }\n\n            if options.contains(WaitOptions::WNOHANG) {\n                return Ok(Some((TaskId::new(0), 0)));\n            }\n\n            Ok(None)\n        })?;\n\n        log::debug!(\"wait4: status = {status_val}\");\n        current_task()\n            .children\n            .lock()\n            .retain(|p| p.pid() != got_pid);\n\n        if status.value() != 0 {\n            unsafe { status.write_user::<c_int>(status_val) }?;\n        }\n\n        Ok(got_pid.as_usize() as isize)\n    }\n\n    pub fn sys_nanosleep(&mut self, req: VirtAddr, _rem: VirtAddr) -> KResult<isize> {\n        let req = unsafe { req.read_user::<TimeSpec>() }?;\n        assert_eq!(req.tv_nsec % 1000000, 0);\n        let duration = req.tv_sec * 1000 + req.tv_nsec / 1000000;\n        #[allow(clippy::redundant_guards)]\n        match get_scheduler().sleep(Some(duration as usize)) {\n            Ok(_) => {}\n            Err(e) if e.errno == Some(Errno::EINTR) => {\n                todo!()\n                // return Err(KError::Errno { errno: Errno::EINTR, msg })\n            }\n            Err(_) => {\n                todo!()\n            }\n        }\n        Ok(0)\n    }\n\n    pub fn sys_clock_gettime(&mut self, clk_id: usize, tp: VirtAddr) -> KResult<isize> {\n        match clk_id {\n            0 => {\n                let ts = time::get_rt_clock();\n                unsafe { tp.write_user(ts) }?;\n            }\n            1 => {\n                let ts = time::get_rt_clock();\n                unsafe { tp.write_user(ts) }?;\n            }\n            2 => {\n                let current = current_task();\n                let delta_ns = time::get_uptime_ns() - current.start_time.get().unwrap() * 1000000;\n                let delta_sec = delta_ns / 1000000000;\n                let ts = TimeSpec {\n                    tv_sec: delta_sec as isize,\n                    tv_nsec: (delta_ns % 1000000000) as isize,\n                };\n                unsafe { tp.write_user(ts) }?;\n            }\n            3 => {\n                let current = current_task();\n                let delta_ns = time::get_uptime_ns() - current.start_time.get().unwrap() * 1000000;\n                let delta_sec = delta_ns / 1000000000;\n                let ts = TimeSpec {\n                    tv_sec: delta_sec as isize,\n                    tv_nsec: (delta_ns % 1000000000) as isize,\n                };\n                unsafe { tp.write_user(ts) }?;\n            }\n            _ => unreachable!(),\n        }\n\n        Ok(0)\n    }\n}\n\nfn arch_prctl(current_task: Arc<Task>, code: i32, addr: VirtAddr) -> KResult<()> {\n    const ARCH_SET_FS: i32 = 0x1002;\n\n    match code {\n        ARCH_SET_FS => {\n            current_task.arch_mut().set_fsbase(addr);\n        }\n        _ => kbail!(EINVAL, \"arch_prctl(): unknown code\"),\n    }\n\n    Ok(())\n}\n"
  },
  {
    "path": "src/userland/syscall/syscall_impl/time.rs",
    "content": "pub const ITIMER_REAL: usize = 0;\npub const ITIMER_VIRTUAL: usize = 1;\npub const ITIMER_PROF: usize = 2;\n\n#[derive(Default, PartialEq)]\n#[repr(C)]\npub struct TimeVal {\n    pub tv_sec: i64,\n    pub tv_usec: i64,\n}\n\n#[derive(Default, PartialEq)]\n#[repr(C)]\npub struct ITimerVal {\n    pub it_interval: TimeVal,\n    pub it_value: TimeVal,\n}\n\n#[derive(Clone, Copy, Default, PartialEq)]\n#[repr(C)]\npub struct TimeSpec {\n    pub tv_sec: isize,\n    pub tv_nsec: isize,\n}\n\nimpl TimeSpec {\n    pub const fn zero() -> Self {\n        TimeSpec {\n            tv_sec: 0,\n            tv_nsec: 0,\n        }\n    }\n}\n"
  },
  {
    "path": "src/util/ctypes.rs",
    "content": "#[allow(non_camel_case_types)]\npub type c_int16 = i16;\n#[allow(non_camel_case_types)]\npub type c_int32 = i32;\n#[allow(non_camel_case_types)]\npub type c_int64 = i64;\n#[allow(non_camel_case_types)]\npub type c_uint32 = u32;\n#[allow(non_camel_case_types)]\npub type c_uint64 = u64;\n\n#[allow(non_camel_case_types)]\npub type c_int = c_int32;\n#[allow(non_camel_case_types)]\npub type c_uint = c_uint32;\n#[allow(non_camel_case_types)]\npub type c_short = c_int16;\n#[allow(non_camel_case_types)]\npub type c_long = c_int64;\n#[allow(non_camel_case_types)]\npub type c_ulong = c_uint64;\n\n#[allow(non_camel_case_types)]\npub type c_time = c_int64;\n#[allow(non_camel_case_types)]\npub type c_suseconds = c_int64;\n#[allow(non_camel_case_types)]\npub type c_clockid = c_int;\n#[allow(non_camel_case_types)]\npub type c_nfds = c_ulong;\n#[allow(non_camel_case_types)]\npub type c_size = c_ulong;\n#[allow(non_camel_case_types)]\npub type c_off = c_uint64;\n"
  },
  {
    "path": "src/util/errno.rs",
    "content": "#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[repr(i32)]\n#[allow(unused)]\n#[allow(clippy::upper_case_acronyms)]\npub enum Errno {\n    /// Operation not permitted\n    EPERM = 1,\n    /// No such file or directory\n    ENOENT = 2,\n    /// No such process\n    ESRCH = 3,\n    /// Interrupted system call\n    EINTR = 4,\n    /// I/O error\n    EIO = 5,\n    /// No such device or address\n    ENXIO = 6,\n    /// Argument list too long\n    E2BIG = 7,\n    /// Exec format error\n    ENOEXEC = 8,\n    /// Bad file number\n    EBADF = 9,\n    /// No child processes\n    ECHILD = 10,\n    /// Resource temporarily unavailable\n    EAGAIN = 11,\n    /// Cannot allocate memory\n    ENOMEM = 12,\n    /// Permission denied\n    EACCES = 13,\n    /// Bad address\n    EFAULT = 14,\n    /// Block device required\n    ENOTBLK = 15,\n    /// Device or resource busy\n    EBUSY = 16,\n    /// File exists\n    EEXIST = 17,\n    /// Invalid cross-device link\n    EXDEV = 18,\n    /// No such device\n    ENODEV = 19,\n    /// Not a directory\n    ENOTDIR = 20,\n    /// Is a directory\n    EISDIR = 21,\n    /// Invalid argument\n    EINVAL = 22,\n    /// Too many open files in system\n    ENFILE = 23,\n    /// Too many open files\n    EMFILE = 24,\n    /// Inappropriate ioctl for device\n    ENOTTY = 25,\n    /// Text file busy\n    ETXTBSY = 26,\n    /// File too large\n    EFBIG = 27,\n    /// No space left on device\n    ENOSPC = 28,\n    /// Illegal seek\n    ESPIPE = 29,\n    /// Read-only file system\n    EROFS = 30,\n    /// Too many links\n    EMLINK = 31,\n    /// Broken pipe\n    EPIPE = 32,\n    /// Math argument out of domain of func\n    EDOM = 33,\n    /// Math result not representable\n    ERANGE = 34,\n    /// Resource deadlock would occur\n    EDEADLK = 35,\n    /// File name too long\n    ENAMETOOLONG = 36,\n    /// No locks available\n    ENOLCK = 37,\n    /// Function not implemented\n    ENOSYS = 38,\n    /// Directory not empty\n    ENOTEMPTY = 39,\n    /// Too many levels of symbolic links\n    ELOOP = 40,\n    /// No message of desired type\n    ENOMSG = 42,\n    /// Identifier removed\n    EIDRM = 43,\n    /// Channel number out of range\n    ECHRNG = 44,\n    /// Level 2 not synchronized\n    EL2NSYNC = 45,\n    /// Level 3 halted\n    EL3HLT = 46,\n    /// Level 3 reset\n    EL3RST = 47,\n    /// Link number out of range\n    ELNRNG = 48,\n    /// Protocol driver not attached\n    EUNATCH = 49,\n    /// No CSI structure available\n    ENOCSI = 50,\n    /// Level 2 halted\n    EL2HLT = 51,\n    /// Invalid exchange\n    EBADE = 52,\n    /// Invalid request descriptor\n    EBADR = 53,\n    /// Exchange full\n    EXFULL = 54,\n    /// No anode\n    ENOANO = 55,\n    /// Invalid request code\n    EBADRQC = 56,\n    /// Invalid slot\n    EBADSLT = 57,\n    /// Bad font file format\n    EBFONT = 59,\n    /// Device not a stream\n    ENOSTR = 60,\n    /// No data available\n    ENODATA = 61,\n    /// Timer expired\n    ETIME = 62,\n    /// Out of streams resources\n    ENOSR = 63,\n    /// Machine is not on the network\n    ENONET = 64,\n    /// Package not installed\n    ENOPKG = 65,\n    /// Object is remote\n    EREMOTE = 66,\n    /// Link has been severed\n    ENOLINK = 67,\n    /// Advertise error\n    EADV = 68,\n    /// Srmount error\n    ESRMNT = 69,\n    /// Communication error on send\n    ECOMM = 70,\n    /// Protocol error\n    EPROTO = 71,\n    /// Multihop attempted\n    EMULTIHOP = 72,\n    /// RFS specific error\n    EDOTDOT = 73,\n    /// Not a data message\n    EBADMSG = 74,\n    /// Value too large for defined data type\n    EOVERFLOW = 75,\n    /// Name not unique on network\n    ENOTUNIQ = 76,\n    /// File descriptor in bad state\n    EBADFD = 77,\n    /// Remote address changed\n    EREMCHG = 78,\n    /// Can not access a needed shared library\n    ELIBACC = 79,\n    /// Accessing a corrupted shared library\n    ELIBBAD = 80,\n    /// .lib section in a.out corrupted\n    ELIBSCN = 81,\n    /// Attempting to link in too many shared libraries\n    ELIBMAX = 82,\n    /// Cannot exec a shared library directly\n    ELIBEXEC = 83,\n    /// Illegal byte sequence\n    EILSEQ = 84,\n    /// Interrupted system call should be restarted\n    ERESTART = 85,\n    /// Streams pipe error\n    ESTRPIPE = 86,\n    /// Too many users\n    EUSERS = 87,\n    /// Socket operation on non-socket\n    ENOTSOCK = 88,\n    /// Destination address required\n    EDESTADDRREQ = 89,\n    /// Message too long\n    EMSGSIZE = 90,\n    /// Protocol wrong type for socket\n    EPROTOTYPE = 91,\n    /// Protocol not available\n    ENOPROTOOPT = 92,\n    /// Protocol not supported\n    EPROTONOSUPPORT = 93,\n    /// Socket type not supported\n    ESOCKTNOSUPPORT = 94,\n    /// Operation not supported on transport endpoint\n    EOPNOTSUPP = 95,\n    /// Protocol family not supported\n    EPFNOSUPPORT = 96,\n    /// Address family not supported by protocol\n    EAFNOSUPPORT = 97,\n    /// Address already in use\n    EADDRINUSE = 98,\n    /// Cannot assign requested address\n    EADDRNOTAVAIL = 99,\n    /// Network is down\n    ENETDOWN = 100,\n    /// Network is unreachable\n    ENETUNREACH = 101,\n    /// Network dropped connection because of reset\n    ENETRESET = 102,\n    /// Software caused connection abort\n    ECONNABORTED = 103,\n    /// Connection reset by peer\n    ECONNRESET = 104,\n    /// No buffer space available\n    ENOBUFS = 105,\n    /// Transport endpoint is already connected\n    EISCONN = 106,\n    /// Transport endpoint is not connected\n    ENOTCONN = 107,\n    /// Cannot send after transport endpoint shutdown\n    ESHUTDOWN = 108,\n    /// Too many references: cannot splice\n    ETOOMANYREFS = 109,\n    /// Connection timed out\n    ETIMEDOUT = 110,\n    /// Connection refused\n    ECONNREFUSED = 111,\n    /// Host is down\n    EHOSTDOWN = 112,\n    /// No route to host\n    EHOSTUNREACH = 113,\n    /// Operation already in progress\n    EALREADY = 114,\n    /// Operation now in progress\n    EINPROGRESS = 115,\n    /// Stale file handle\n    ESTALE = 116,\n    /// Structure needs cleaning\n    EUCLEAN = 117,\n    /// Not a XENIX named type file\n    ENOTNAM = 118,\n    /// No XENIX semaphores available\n    ENAVAIL = 119,\n    /// Is a named type file\n    EISNAM = 120,\n    /// Remote I/O error\n    EREMOTEIO = 121,\n    /// Quota exceeded\n    EDQUOT = 122,\n    /// No medium found\n    ENOMEDIUM = 123,\n    /// Wrong medium type\n    EMEDIUMTYPE = 124,\n    /// Operation canceled\n    ECANCELED = 125,\n    /// Required key not available\n    ENOKEY = 126,\n    /// Key has expired\n    EKEYEXPIRED = 127,\n    /// Key has been revoked\n    EKEYREVOKED = 128,\n    /// Key was rejected by service\n    EKEYREJECTED = 129,\n    /// Owner died\n    EOWNERDEAD = 130,\n    /// State not recoverable\n    ENOTRECOVERABLE = 131,\n    /// Operation not possible due to RF-kill\n    ERFKILL = 132,\n    /// Memory page has hardware error\n    EHWPOISON = 133,\n}\n"
  },
  {
    "path": "src/util/error.rs",
    "content": "use core::fmt::{Debug, Display};\n\nuse super::errno::Errno;\n\npub type KResult<T> = Result<T, KError<'static>>;\n\n#[derive(Clone, Default, Debug)]\npub struct KError<'a> {\n    pub(crate) msg: Option<core::fmt::Arguments<'a>>,\n    pub(crate) errno: Option<Errno>,\n}\n\nimpl<'a> KError<'a> {\n    pub fn msg(&self) -> Option<core::fmt::Arguments<'a>> {\n        self.msg\n    }\n\n    pub fn errno(&self) -> Option<Errno> {\n        self.errno\n    }\n}\n\nimpl Display for KError<'_> {\n    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {\n        match (self.errno, self.msg) {\n            (Some(errno), Some(msg)) => write!(f, \"{:?}: {}\", errno, msg),\n            (Some(errno), None) => write!(f, \"{:?}\", errno),\n            (None, Some(msg)) => write!(f, \"{}\", msg),\n            (None, None) => write!(f, \"Unknown error\"),\n        }\n    }\n}\n\n#[macro_export]\nmacro_rules! kerror {\n    ($e:ident) => {\n        $crate::util::error::KError {\n            errno: Some($crate::util::errno::Errno::$e),\n            msg: None,\n        }\n    };\n    ($e:ident, $($tts:tt)*) => {\n        $crate::util::error::KError {\n            errno: Some($crate::util::errno::Errno::$e),\n            msg: Some(format_args!($($tts)*)),\n        }\n    };\n    ($($tts:tt)*) => {\n        $crate::util::error::KError {\n            errno: None,\n            msg: Some(format_args!($($tts)*)),\n        }\n    };\n}\n\n#[macro_export]\nmacro_rules! kbail {\n    ($e:ident) => {\n        return Err($crate::kerror!($e))\n    };\n    ($e:ident, $($tts:tt)*) => {\n        return Err($crate::kerror!($e, $($tts)*))\n    };\n    ($($tts:tt)*) => {\n        return Err($crate::kerror!($($tts)*))\n    };\n}\n"
  },
  {
    "path": "src/util/lock.rs",
    "content": "use core::mem::ManuallyDrop;\nuse core::ops::{Deref, DerefMut};\n\nuse spin::mutex::{SpinMutex, SpinMutexGuard};\nuse x86::current::rflags::{self, RFlags};\nuse x86_64::instructions::interrupts;\n\nuse crate::backtrace;\nuse crate::task::wait_queue::WaitQueue;\n\nuse super::error::KResult;\n\npub struct SavedInterruptStatus {\n    rflags: RFlags,\n}\n\nimpl SavedInterruptStatus {\n    pub fn save() -> SavedInterruptStatus {\n        SavedInterruptStatus {\n            rflags: rflags::read(),\n        }\n    }\n}\n\nimpl Drop for SavedInterruptStatus {\n    fn drop(&mut self) {\n        rflags::set(rflags::read() | (self.rflags & rflags::RFlags::FLAGS_IF));\n    }\n}\n\npub struct BlockingMutex<T: ?Sized> {\n    queue: WaitQueue,\n    inner: IrqMutex<T>,\n}\n\nimpl<T> BlockingMutex<T> {\n    pub const fn new(value: T) -> BlockingMutex<T> {\n        BlockingMutex {\n            queue: WaitQueue::new(),\n            inner: IrqMutex::new(value),\n        }\n    }\n}\n\nimpl<T: ?Sized> BlockingMutex<T> {\n    pub fn get_mut(&mut self) -> &mut T {\n        self.inner.get_mut()\n    }\n\n    pub fn try_lock(&self) -> KResult<BlockingMutexGuard<'_, T>> {\n        if self.inner.is_locked() {\n            Err(kerror!(\"Cannot relock BlockingMutex\")) // todo: more verbose error message\n        } else {\n            Ok(BlockingMutexGuard {\n                inner: ManuallyDrop::new(self.inner.lock()),\n            })\n        }\n    }\n\n    pub fn lock(&self) -> KResult<BlockingMutexGuard<'_, T>> {\n        let guard = self.queue.sleep_signalable_until(None, || {\n            if let Ok(guard) = self.inner.try_lock() {\n                Ok(Some(BlockingMutexGuard {\n                    inner: ManuallyDrop::new(guard),\n                }))\n            } else {\n                Ok(None)\n            }\n        })?;\n        Ok(guard)\n    }\n\n    pub fn is_locked(&self) -> bool {\n        self.inner.is_locked()\n    }\n\n    /// # Safety\n    /// See `spin::SpinMutex::force_unlock()`\n    pub unsafe fn force_unlock(&self) {\n        unsafe { self.inner.force_unlock() };\n    }\n}\n\nunsafe impl<T: ?Sized + Send> Sync for BlockingMutex<T> {}\nunsafe impl<T: ?Sized + Send> Send for BlockingMutex<T> {}\n\npub struct BlockingMutexGuard<'a, T: ?Sized> {\n    inner: ManuallyDrop<IrqMutexGuard<'a, T>>,\n}\n\nimpl<T: ?Sized> Drop for BlockingMutexGuard<'_, T> {\n    fn drop(&mut self) {\n        unsafe {\n            ManuallyDrop::drop(&mut self.inner);\n        }\n    }\n}\n\nimpl<T: ?Sized> Deref for BlockingMutexGuard<'_, T> {\n    type Target = T;\n    fn deref(&self) -> &T {\n        &self.inner\n    }\n}\n\nimpl<T: ?Sized> DerefMut for BlockingMutexGuard<'_, T> {\n    fn deref_mut(&mut self) -> &mut T {\n        &mut self.inner\n    }\n}\n\npub struct IrqMutex<T: ?Sized> {\n    inner: SpinMutex<T>,\n}\n\nimpl<T> IrqMutex<T> {\n    pub const fn new(value: T) -> IrqMutex<T> {\n        IrqMutex {\n            inner: SpinMutex::new(value),\n        }\n    }\n}\n\nimpl<T: ?Sized> IrqMutex<T> {\n    pub fn get_mut(&mut self) -> &mut T {\n        self.inner.get_mut()\n    }\n\n    pub fn try_lock(&self) -> KResult<IrqMutexGuard<'_, T>> {\n        if self.inner.is_locked() {\n            Err(kerror!(\"Cannot relock IrqMutex\")) // todo: more verbose error message\n        } else {\n            Ok(self.lock())\n        }\n    }\n\n    pub fn lock(&self) -> IrqMutexGuard<'_, T> {\n        if self.inner.is_locked() {\n            serial0_println!(\n                \"WARNING: Tried to relock IrqMutex of {}\",\n                core::any::type_name::<T>()\n            );\n            backtrace::unwind_stack().ok();\n        }\n\n        let saved_intr_status = SavedInterruptStatus::save();\n        interrupts::disable();\n\n        let guard = self.inner.lock();\n\n        IrqMutexGuard {\n            inner: ManuallyDrop::new(guard),\n            saved_intr_status: ManuallyDrop::new(saved_intr_status),\n        }\n    }\n\n    pub fn is_locked(&self) -> bool {\n        self.inner.is_locked()\n    }\n\n    /// # Safety\n    /// See `spin::SpinMutex::force_unlock()`\n    pub unsafe fn force_unlock(&self) {\n        unsafe { self.inner.force_unlock() };\n    }\n}\n\nunsafe impl<T: ?Sized + Send> Sync for IrqMutex<T> {}\nunsafe impl<T: ?Sized + Send> Send for IrqMutex<T> {}\n\npub struct IrqMutexGuard<'a, T: ?Sized> {\n    inner: ManuallyDrop<SpinMutexGuard<'a, T>>,\n    saved_intr_status: ManuallyDrop<SavedInterruptStatus>,\n}\n\nimpl<T: ?Sized> Drop for IrqMutexGuard<'_, T> {\n    fn drop(&mut self) {\n        unsafe {\n            ManuallyDrop::drop(&mut self.inner);\n        }\n\n        unsafe {\n            ManuallyDrop::drop(&mut self.saved_intr_status);\n        }\n    }\n}\n\nimpl<T: ?Sized> Deref for IrqMutexGuard<'_, T> {\n    type Target = T;\n    fn deref(&self) -> &T {\n        &self.inner\n    }\n}\n\nimpl<T: ?Sized> DerefMut for IrqMutexGuard<'_, T> {\n    fn deref_mut(&mut self) -> &mut T {\n        &mut self.inner\n    }\n}\n"
  },
  {
    "path": "src/util/mod.rs",
    "content": "#[macro_use]\npub mod error;\npub mod ctypes;\npub mod errno;\npub mod lock;\npub mod ringbuffer;\npub mod stack;\n\npub use self::error::*;\npub use self::lock::*;\n\n#[inline]\npub const fn align_down(val: usize, align: usize) -> usize {\n    val / align * align\n}\n#[inline]\npub const fn align_up(val: usize, align: usize) -> usize {\n    val.div_ceil(align) * align\n}\n"
  },
  {
    "path": "src/util/ringbuffer.rs",
    "content": "//! The ringbuffer from Kerla.\n\nuse core::{cmp::min, mem::MaybeUninit, ops::Range, slice};\n\npub struct RingBuffer<T, const CAP: usize> {\n    buf: [MaybeUninit<T>; CAP],\n    rp: usize,\n    wp: usize,\n    full: bool,\n}\n\nimpl<T, const CAP: usize> Default for RingBuffer<T, CAP> {\n    fn default() -> Self {\n        RingBuffer {\n            buf: unsafe { MaybeUninit::uninit().assume_init() },\n            rp: 0,\n            wp: 0,\n            full: false,\n        }\n    }\n}\n\nimpl<T, const CAP: usize> RingBuffer<T, CAP> {\n    pub fn new() -> RingBuffer<T, CAP> {\n        Self::default()\n    }\n\n    pub fn is_writable(&self) -> bool {\n        !self.full\n    }\n\n    pub fn is_readable(&self) -> bool {\n        self.full || self.rp != self.wp\n    }\n\n    pub fn push(&mut self, data: T) -> Result<(), T>\n    where\n        T: Copy,\n    {\n        if self.push_slice(&[data]) == 0 {\n            Err(data)\n        } else {\n            Ok(())\n        }\n    }\n\n    pub fn pop(&mut self) -> Option<T>\n    where\n        T: Copy,\n    {\n        self.pop_slice(1).map(|slice| slice[0])\n    }\n\n    pub fn push_slice(&mut self, data: &[T]) -> usize\n    where\n        T: Copy,\n    {\n        if !self.is_writable() || data.is_empty() {\n            return 0;\n        }\n\n        let written_len = if self.wp >= self.rp {\n            let free1 = self.wp..CAP;\n            let free2 = 0..self.rp;\n            let src1 = &data[..min(data.len(), free1.len())];\n            let src2 = &data[src1.len()..min(data.len(), src1.len() + free2.len())];\n            let dst1 = free1.start..(free1.start + src1.len());\n            let dst2 = free2.start..(free2.start + src2.len());\n            self.slice_mut(dst1).copy_from_slice(src1);\n            self.slice_mut(dst2).copy_from_slice(src2);\n            src1.len() + src2.len()\n        } else {\n            let free = self.wp..self.rp;\n            let src = &data[..min(data.len(), free.len())];\n            let dst = free.start..(free.start + src.len());\n            self.slice_mut(dst).copy_from_slice(src);\n            src.len()\n        };\n\n        self.wp = (self.wp + written_len) % CAP;\n        self.full = self.wp == self.rp;\n        written_len\n    }\n\n    pub fn pop_slice(&mut self, len: usize) -> Option<&[T]> {\n        if !self.is_readable() {\n            return None;\n        }\n\n        let range = if self.rp < self.wp {\n            self.rp..min(self.rp + len, self.wp)\n        } else {\n            self.wp..min(self.wp + len, CAP)\n        };\n\n        self.rp = (self.rp + range.len()) % CAP;\n        self.full = false;\n        Some(self.slice(range))\n    }\n\n    fn slice(&self, range: Range<usize>) -> &[T] {\n        debug_assert!(range.end <= CAP);\n        unsafe {\n            let ptr = self.buf.as_ptr() as *const T;\n            slice::from_raw_parts(ptr.add(range.start), range.end - range.start)\n        }\n    }\n\n    fn slice_mut(&mut self, range: Range<usize>) -> &mut [T] {\n        debug_assert!(range.end <= CAP);\n        unsafe {\n            let ptr = self.buf.as_mut_ptr() as *mut T;\n            slice::from_raw_parts_mut(ptr.add(range.start), range.end - range.start)\n        }\n    }\n}\n"
  },
  {
    "path": "src/util/stack.rs",
    "content": "use core::mem::size_of;\n\nuse super::align_down;\n\npub struct Stack<'a> {\n    ptr: &'a mut usize,\n}\n\nimpl<'a> Stack<'a> {\n    pub fn new(ptr: &'a mut usize) -> Self {\n        Self { ptr }\n    }\n\n    pub fn skip_by(&mut self, by: usize) {\n        *self.ptr -= by;\n    }\n\n    pub unsafe fn offset<'b, T: Sized>(&mut self) -> &'b mut T {\n        self.skip_by(size_of::<T>());\n        unsafe { &mut *(*self.ptr as *mut T) }\n    }\n\n    pub fn top(&self) -> usize {\n        *self.ptr\n    }\n\n    pub unsafe fn push_bytes(&mut self, bytes: &[u8]) {\n        self.skip_by(bytes.len());\n\n        unsafe { (*self.ptr as *mut u8).copy_from(bytes.as_ptr(), bytes.len()) };\n    }\n\n    pub unsafe fn push<T: Sized>(&mut self, value: T) {\n        self.skip_by(size_of::<T>());\n        unsafe { (*self.ptr as *mut T).write(value) };\n    }\n\n    pub fn pop_by(&mut self, by: usize) {\n        *self.ptr += by;\n    }\n\n    pub unsafe fn pop_bytes(&mut self, len: usize) -> &[u8] {\n        let x = unsafe { core::slice::from_raw_parts(*self.ptr as *const u8, len) };\n        self.pop_by(len);\n        x\n    }\n\n    pub unsafe fn pop<'b, T: Sized>(&mut self) -> &'b mut T {\n        let x = unsafe { &mut *(*self.ptr as *mut T) };\n        self.pop_by(size_of::<T>());\n        x\n    }\n\n    pub fn align_down(&mut self, align: usize) {\n        *self.ptr = align_down(*self.ptr, align)\n    }\n}\n"
  },
  {
    "path": "src/vga_text.rs",
    "content": "use core::ops::Add;\n\nuse lazy_static::lazy_static;\n\n#[allow(dead_code)]\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[repr(u8)]\npub enum Color {\n    Black = 0,\n    Blue = 1,\n    Green = 2,\n    Cyan = 3,\n    Red = 4,\n    Magenta = 5,\n    Brown = 6,\n    LightGray = 7,\n    DarkGray = 8,\n    LightBlue = 9,\n    LightGreen = 10,\n    LightCyan = 11,\n    LightRed = 12,\n    Pink = 13,\n    Yellow = 14,\n    White = 15,\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[repr(transparent)]\npub struct ColorCode(u8);\n\nimpl ColorCode {\n    pub fn new(foreground: Color, background: Color) -> ColorCode {\n        ColorCode(((background as u8) << 4) | (foreground as u8))\n    }\n\n    pub fn background(self) -> Color {\n        unsafe { core::mem::transmute((self.0 >> 4) & 0xf) }\n    }\n\n    pub fn foreground(self) -> Color {\n        unsafe { core::mem::transmute(self.0 & 0xf) }\n    }\n}\n\n#[derive(Debug, Clone, Copy, PartialEq, Eq)]\n#[repr(C)]\nstruct ScreenChar {\n    ascii_character: u8,\n    color_code: ColorCode,\n}\n\npub const VGA_BUFFER_START_PADDR: usize = 0xb8000;\npub const BUFFER_HEIGHT: usize = 25;\npub const BUFFER_WIDTH: usize = 80;\n\n#[repr(transparent)]\nstruct Buffer {\n    chars: [[volatile::Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],\n}\n\npub struct Writer {\n    x: usize,\n    y: usize,\n    color_code: ColorCode,\n    buffer: &'static mut Buffer,\n}\n\nimpl Writer {\n    pub fn write_byte(&mut self, byte: u8) {\n        match byte {\n            0x8 => self.backspace(),\n            b'\\n' => self.new_line(),\n            b'\\r' => self.x = 0,\n            byte => {\n                if self.x >= BUFFER_WIDTH - 1 {\n                    self.new_line();\n                }\n\n                let row = self.y;\n                let col = self.x;\n\n                let color_code = self.color_code;\n                self.buffer.chars[row][col].write(ScreenChar {\n                    ascii_character: byte,\n                    color_code,\n                });\n                self.move_right();\n            }\n        }\n        self.cursor_color_hook();\n    }\n\n    fn cursor_color_hook(&mut self) {\n        // let cursor = self.buffer.chars[self.y][self.x].read();\n        // for y in 0..BUFFER_HEIGHT {\n        //     for x in 0..BUFFER_WIDTH {\n        //         let chr = self.buffer.chars[y][x].read();\n        //         if y == self.y && x == self.x {\n        //             self.buffer.chars[y][x].write(ScreenChar { ascii_character: cursor.ascii_character, color_code: ColorCode::new(Color::White, Color::Cyan) });\n        //         } else {\n        //             self.buffer.chars[y][x].write(ScreenChar { ascii_character: chr.ascii_character, color_code: self.color_code });\n        //         }\n        //     }\n        // }\n    }\n\n    pub fn backspace(&mut self) {\n        let row = self.y;\n        let col = self.x.saturating_sub(1);\n        let color_code = self.color_code;\n        self.buffer.chars[row][col].write(ScreenChar {\n            ascii_character: b' ',\n            color_code,\n        });\n        self.x = col;\n        self.cursor_color_hook();\n    }\n\n    pub fn write_string(&mut self, s: &str) {\n        for byte in s.bytes() {\n            // match byte {\n            //     0x20..=0x7e | b'\\n' | b'\\r' | 0x8 => self.write_byte(byte),\n            //     // _ => self.write_byte(0xfe),\n            //     _ => {},\n            // }\n            self.write_byte(byte)\n        }\n    }\n\n    fn new_line(&mut self) {\n        if self.y >= BUFFER_HEIGHT - 1 {\n            for row in 1..BUFFER_HEIGHT {\n                for col in 0..BUFFER_WIDTH {\n                    let character = self.buffer.chars[row][col].read();\n                    self.buffer.chars[row - 1][col].write(character);\n                }\n            }\n            self.y = BUFFER_HEIGHT - 1;\n            self.clear_row(self.y);\n            self.x = 0;\n        } else {\n            self.y += 1;\n            self.x = 0;\n        }\n        self.cursor_color_hook();\n    }\n\n    fn clear_row(&mut self, row: usize) {\n        let blank = ScreenChar {\n            ascii_character: b' ',\n            color_code: self.color_code,\n        };\n        for col in 0..BUFFER_WIDTH {\n            self.buffer.chars[row][col].write(blank);\n        }\n        self.cursor_color_hook();\n    }\n    fn clear_until_end(&mut self) {\n        let blank = ScreenChar {\n            ascii_character: b' ',\n            color_code: self.color_code,\n        };\n        for col in self.x..BUFFER_WIDTH {\n            self.buffer.chars[self.y][col].write(blank);\n        }\n        for row in self.y + 1..BUFFER_HEIGHT {\n            self.clear_row(row);\n        }\n        self.cursor_color_hook();\n    }\n    fn clear_until_beginning(&mut self) {\n        let blank = ScreenChar {\n            ascii_character: b' ',\n            color_code: self.color_code,\n        };\n        for col in 0..self.x {\n            self.buffer.chars[self.y][col].write(blank);\n        }\n        for row in 0..self.y - 1 {\n            self.clear_row(row);\n        }\n        self.cursor_color_hook();\n    }\n    fn clear_until_eol(&mut self) {\n        let blank = ScreenChar {\n            ascii_character: b' ',\n            color_code: self.color_code,\n        };\n        for col in self.x..BUFFER_WIDTH {\n            self.buffer.chars[self.y][col].write(blank);\n        }\n        self.cursor_color_hook();\n    }\n    fn clear_from_bol(&mut self) {\n        let blank = ScreenChar {\n            ascii_character: b' ',\n            color_code: self.color_code,\n        };\n        for col in 0..self.x {\n            self.buffer.chars[self.y][col].write(blank);\n        }\n        self.cursor_color_hook();\n    }\n    fn clear_line(&mut self) {\n        self.clear_row(self.y);\n    }\n    fn clear_all(&mut self) {\n        for row in 0..BUFFER_HEIGHT {\n            self.clear_row(row)\n        }\n        self.cursor_color_hook();\n    }\n    fn move_up(&mut self) {\n        let new_y = self.y.saturating_sub(1);\n        let mut new_x = self.x;\n        while new_x > 0 && self.buffer.chars[new_y][new_x].read().ascii_character == b' ' {\n            new_x -= 1;\n        }\n        self.y = new_y;\n        self.x = new_x;\n        self.cursor_color_hook();\n    }\n    fn move_down(&mut self) {\n        let new_y = self.y.add(1).min(BUFFER_HEIGHT - 1);\n        let mut new_x = self.x;\n        while new_x > 0 && self.buffer.chars[new_y][new_x].read().ascii_character == b' ' {\n            new_x -= 1;\n        }\n        self.y = new_y;\n        self.x = new_x;\n        self.cursor_color_hook();\n    }\n    fn move_left(&mut self) {\n        self.x = self.x.saturating_sub(1);\n        self.cursor_color_hook();\n    }\n    fn move_right(&mut self) {\n        self.x = self.x.add(1).min(BUFFER_WIDTH - 1);\n        self.cursor_color_hook();\n    }\n}\n\nimpl core::fmt::Write for Writer {\n    fn write_str(&mut self, s: &str) -> core::fmt::Result {\n        self.write_string(s);\n        Ok(())\n    }\n}\n\nlazy_static! {\n    pub static ref WRITER: spin::Mutex<Writer> = spin::Mutex::new(Writer {\n        y: 0,\n        x: 0,\n        color_code: ColorCode::new(Color::White, Color::Black),\n        buffer: unsafe { &mut *(VGA_BUFFER_START_PADDR as *mut Buffer) },\n    });\n}\n\npub fn clear_screen() {\n    WRITER.lock().clear_all()\n}\n\npub fn backspace() {\n    WRITER.lock().backspace()\n}\n\npub fn set_color_code(color_code: ColorCode) {\n    WRITER.lock().color_code = color_code;\n}\n\npub fn get_color_code() -> ColorCode {\n    WRITER.lock().color_code\n}\n\npub fn set_cursor_x(x: usize) {\n    WRITER.lock().x = x.min(BUFFER_WIDTH - 1);\n}\n\npub fn set_cursor_y(y: usize) {\n    WRITER.lock().y = y.min(BUFFER_HEIGHT - 1);\n}\n\npub fn set_cursor_xy(xy: (usize, usize)) {\n    set_cursor_x(xy.0.min(BUFFER_WIDTH - 1));\n    set_cursor_y(xy.1.min(BUFFER_HEIGHT - 1));\n}\n\npub fn cursor_xy() -> (usize, usize) {\n    let writer = WRITER.lock();\n    (writer.x, writer.y)\n}\n\npub fn write_byte(byte: u8) {\n    WRITER.lock().write_byte(byte);\n}\n\npub fn clear_until_end() {\n    WRITER.lock().clear_until_end();\n}\n\npub fn clear_until_beginning() {\n    WRITER.lock().clear_until_beginning();\n}\n\npub fn clear_from_bol() {\n    WRITER.lock().clear_from_bol();\n}\n\npub fn clear_until_eol() {\n    WRITER.lock().clear_until_eol();\n}\n\npub fn clear_line() {\n    WRITER.lock().clear_line();\n}\n\npub fn move_up() {\n    WRITER.lock().move_up();\n}\n\npub fn move_down() {\n    WRITER.lock().move_down();\n}\n\npub fn move_left() {\n    WRITER.lock().move_left();\n}\n\npub fn move_right() {\n    WRITER.lock().move_right();\n}\n\n#[macro_export]\nmacro_rules! vga_print {\n    ($($arg:tt)*) => ($crate::vga_text::_vga_print(format_args!($($arg)*)));\n}\n\n#[macro_export]\nmacro_rules! vga_println {\n    () => ($crate::vga_print!(\"\\n\"));\n    ($($arg:tt)*) => ($crate::vga_print!(\"{}\\n\", format_args!($($arg)*)));\n}\n\n#[doc(hidden)]\npub fn _vga_print(args: core::fmt::Arguments) {\n    use core::fmt::Write;\n    x86_64::instructions::interrupts::without_interrupts(|| {\n        WRITER.lock().write_fmt(args).unwrap();\n    });\n}\n"
  },
  {
    "path": "userland/.gitignore",
    "content": "/target\n"
  },
  {
    "path": "userland/.vscode/settings.json",
    "content": "{\n    \"rust-analyzer.cargo.target\": \"x86_64-unknown-linux-musl\",\n}"
  },
  {
    "path": "userland/Cargo.toml",
    "content": "[workspace]\nmembers = [\"kash\", \"kados_syscall\"]\nresolver = \"3\"\n"
  },
  {
    "path": "userland/kados_syscall/.cargo/config.toml",
    "content": "[build]\ntarget = \"x86_64-unknown-linux-musl\"\n\n[target.'cfg(target_os = \"linux\")']\nrustflags = [\"-C\", \"linker=rust-lld\", \"-C\", \"relocation-model=static\"]"
  },
  {
    "path": "userland/kados_syscall/Cargo.toml",
    "content": "[package]\nname = \"kados_syscall\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[dependencies]\n"
  },
  {
    "path": "userland/kados_syscall/src/consts.rs",
    "content": "#[derive(Debug, Copy, Clone, Eq, PartialEq)]\n#[repr(i32)]\n#[allow(unused)]\n#[allow(clippy::upper_case_acronyms)]\npub enum Errno {\n    EPERM = 1,\n    ENOENT = 2,\n    ESRCH = 3,\n    EINTR = 4,\n    EIO = 5,\n    ENXIO = 6,\n    E2BIG = 7,\n    ENOEXEC = 8,\n    EBADF = 9,\n    ECHILD = 10,\n    EAGAIN = 11,\n    ENOMEM = 12,\n    EACCES = 13,\n    EFAULT = 14,\n    ENOTBLK = 15,\n    EBUSY = 16,\n    EEXIST = 17,\n    EXDEV = 18,\n    ENODEV = 19,\n    ENOTDIR = 20,\n    EISDIR = 21,\n    EINVAL = 22,\n    ENFILE = 23,\n    EMFILE = 24,\n    ENOTTY = 25,\n    ETXTBSY = 26,\n    EFBIG = 27,\n    ENOSPC = 28,\n    ESPIPE = 29,\n    EROFS = 30,\n    EMLINK = 31,\n    EPIPE = 32,\n    EDOM = 33,\n    ERANGE = 34,\n\n    ENOSYS = 38,\n    ELOOP = 40,\n\n    EADDRINUSE = 98,\n    EADDRNOTAVAIL = 99,\n    ENETDOWN = 100,\n    ENETUNREACH = 101,\n    ENETRESET = 102,\n    ECONNABORTED = 103,\n    ECONNRESET = 104,\n    ENOBUFS = 105,\n    EISCONN = 106,\n    ENOTCONN = 107,\n\n    /// Temporary errno to use until I get everything POSIX-compatible.\n    ETMP = 255,\n}\n\npub type SyscallResult = Result<usize, Errno>;\n\n#[inline]\npub fn syscall_result(sys_res: isize) -> SyscallResult {\n    if sys_res >= 0 {\n        Ok(sys_res as usize)\n    } else {\n        Err(unsafe { core::mem::transmute((-sys_res) as i32)})\n    }\n}\n\npub fn syscall_name_by_number(n: usize) -> &'static str {\n    match n {\n        0 => \"read\",\n        1 => \"write\",\n        2 => \"open\",\n        3 => \"close\",\n        4 => \"stat\",\n        5 => \"fstat\",\n        6 => \"lstat\",\n        7 => \"poll\",\n        8 => \"lseek\",\n        9 => \"mmap\",\n        10 => \"mprotect\",\n        11 => \"munmap\",\n        12 => \"brk\",\n        13 => \"rt_sigaction\",\n        14 => \"rt_sigprocmask\",\n        15 => \"rt_sigreturn\",\n        16 => \"ioctl\",\n        17 => \"pread64\",\n        18 => \"pwrite64\",\n        19 => \"readv\",\n        20 => \"writev\",\n        21 => \"access\",\n        22 => \"pipe\",\n        23 => \"select\",\n        24 => \"sched_yield\",\n        25 => \"mremap\",\n        26 => \"msync\",\n        27 => \"mincore\",\n        28 => \"madvise\",\n        29 => \"shmget\",\n        30 => \"shmat\",\n        31 => \"shmctl\",\n        32 => \"dup\",\n        33 => \"dup2\",\n        34 => \"pause\",\n        35 => \"nanosleep\",\n        36 => \"getitimer\",\n        37 => \"alarm\",\n        38 => \"setitimer\",\n        39 => \"getpid\",\n        40 => \"sendfile\",\n        41 => \"socket\",\n        42 => \"connect\",\n        43 => \"accept\",\n        44 => \"sendto\",\n        45 => \"recvfrom\",\n        46 => \"sendmsg\",\n        47 => \"recvmsg\",\n        48 => \"shutdown\",\n        49 => \"bind\",\n        50 => \"listen\",\n        51 => \"getsockname\",\n        52 => \"getpeername\",\n        53 => \"socketpair\",\n        54 => \"setsockopt\",\n        55 => \"getsockopt\",\n        56 => \"clone\",\n        57 => \"fork\",\n        58 => \"vfork\",\n        59 => \"execve\",\n        60 => \"exit\",\n        61 => \"wait4\",\n        62 => \"kill\",\n        63 => \"uname\",\n        64 => \"semget\",\n        65 => \"semop\",\n        66 => \"semctl\",\n        67 => \"shmdt\",\n        68 => \"msgget\",\n        69 => \"msgsnd\",\n        70 => \"msgrcv\",\n        71 => \"msgctl\",\n        72 => \"fcntl\",\n        73 => \"flock\",\n        74 => \"fsync\",\n        75 => \"fdatasync\",\n        76 => \"truncate\",\n        77 => \"ftruncate\",\n        78 => \"getdents\",\n        79 => \"getcwd\",\n        80 => \"chdir\",\n        81 => \"fchdir\",\n        82 => \"rename\",\n        83 => \"mkdir\",\n        84 => \"rmdir\",\n        85 => \"creat\",\n        86 => \"link\",\n        87 => \"unlink\",\n        88 => \"symlink\",\n        89 => \"readlink\",\n        90 => \"chmod\",\n        91 => \"fchmod\",\n        92 => \"chown\",\n        93 => \"fchown\",\n        94 => \"lchown\",\n        95 => \"umask\",\n        96 => \"gettimeofday\",\n        97 => \"getrlimit\",\n        98 => \"getrusage\",\n        99 => \"sysinfo\",\n        100 => \"times\",\n        101 => \"ptrace\",\n        102 => \"getuid\",\n        103 => \"syslog\",\n        104 => \"getgid\",\n        105 => \"setuid\",\n        106 => \"setgid\",\n        107 => \"geteuid\",\n        108 => \"getegid\",\n        109 => \"setpgid\",\n        110 => \"getppid\",\n        111 => \"getpgrp\",\n        112 => \"setsid\",\n        113 => \"setreuid\",\n        114 => \"setregid\",\n        115 => \"getgroups\",\n        116 => \"setgroups\",\n        117 => \"setresuid\",\n        118 => \"getresuid\",\n        119 => \"setresgid\",\n        120 => \"getresgid\",\n        121 => \"getpgid\",\n        122 => \"setfsuid\",\n        123 => \"setfsgid\",\n        124 => \"getsid\",\n        125 => \"capget\",\n        126 => \"capset\",\n        127 => \"rt_sigpending\",\n        128 => \"rt_sigtimedwait\",\n        129 => \"rt_sigqueueinfo\",\n        130 => \"rt_sigsuspend\",\n        131 => \"sigaltstack\",\n        132 => \"utime\",\n        133 => \"mknod\",\n        134 => \"uselib\",\n        135 => \"personality\",\n        136 => \"ustat\",\n        137 => \"statfs\",\n        138 => \"fstatfs\",\n        139 => \"sysfs\",\n        140 => \"getpriority\",\n        141 => \"setpriority\",\n        142 => \"sched_setparam\",\n        143 => \"sched_getparam\",\n        144 => \"sched_setscheduler\",\n        145 => \"sched_getscheduler\",\n        146 => \"sched_get_priority_max\",\n        147 => \"sched_get_priority_min\",\n        148 => \"sched_rr_get_interval\",\n        149 => \"mlock\",\n        150 => \"munlock\",\n        151 => \"mlockall\",\n        152 => \"munlockall\",\n        153 => \"vhangup\",\n        154 => \"modify_ldt\",\n        155 => \"pivot_root\",\n        156 => \"_sysctl\",\n        157 => \"prctl\",\n        158 => \"arch_prctl\",\n        159 => \"adjtimex\",\n        160 => \"setrlimit\",\n        161 => \"chroot\",\n        162 => \"sync\",\n        163 => \"acct\",\n        164 => \"settimeofday\",\n        165 => \"mount\",\n        166 => \"umount2\",\n        167 => \"swapon\",\n        168 => \"swapoff\",\n        169 => \"reboot\",\n        170 => \"sethostname\",\n        171 => \"setdomainname\",\n        172 => \"iopl\",\n        173 => \"ioperm\",\n        174 => \"create_module\",\n        175 => \"init_module\",\n        176 => \"delete_module\",\n        177 => \"get_kernel_syms\",\n        178 => \"query_module\",\n        179 => \"quotactl\",\n        180 => \"nfsservctl\",\n        181 => \"getpmsg\",\n        182 => \"putpmsg\",\n        183 => \"afs_syscall\",\n        184 => \"tuxcall\",\n        185 => \"security\",\n        186 => \"gettid\",\n        187 => \"readahead\",\n        188 => \"setxattr\",\n        189 => \"lsetxattr\",\n        190 => \"fsetxattr\",\n        191 => \"getxattr\",\n        192 => \"lgetxattr\",\n        193 => \"fgetxattr\",\n        194 => \"listxattr\",\n        195 => \"llistxattr\",\n        196 => \"flistxattr\",\n        197 => \"removexattr\",\n        198 => \"lremovexattr\",\n        199 => \"fremovexattr\",\n        200 => \"tkill\",\n        201 => \"time\",\n        202 => \"futex\",\n        203 => \"sched_setaffinity\",\n        204 => \"sched_getaffinity\",\n        205 => \"set_thread_area\",\n        206 => \"io_setup\",\n        207 => \"io_destroy\",\n        208 => \"io_getevents\",\n        209 => \"io_submit\",\n        210 => \"io_cancel\",\n        211 => \"get_thread_area\",\n        212 => \"lookup_dcookie\",\n        213 => \"epoll_create\",\n        214 => \"epoll_ctl_old\",\n        215 => \"epoll_wait_old\",\n        216 => \"remap_file_pages\",\n        217 => \"getdents64\",\n        218 => \"set_tid_address\",\n        219 => \"restart_syscall\",\n        220 => \"semtimedop\",\n        221 => \"fadvise64\",\n        222 => \"timer_create\",\n        223 => \"timer_settime\",\n        224 => \"timer_gettime\",\n        225 => \"timer_getoverrun\",\n        226 => \"timer_delete\",\n        227 => \"clock_settime\",\n        228 => \"clock_gettime\",\n        229 => \"clock_getres\",\n        230 => \"clock_nanosleep\",\n        231 => \"exit_group\",\n        232 => \"epoll_wait\",\n        233 => \"epoll_ctl\",\n        234 => \"tgkill\",\n        235 => \"utimes\",\n        236 => \"vserver\",\n        237 => \"mbind\",\n        238 => \"set_mempolicy\",\n        239 => \"get_mempolicy\",\n        240 => \"mq_open\",\n        241 => \"mq_unlink\",\n        242 => \"mq_timedsend\",\n        243 => \"mq_timedreceive\",\n        244 => \"mq_notify\",\n        245 => \"mq_getsetattr\",\n        246 => \"kexec_load\",\n        247 => \"waitid\",\n        248 => \"add_key\",\n        249 => \"request_key\",\n        250 => \"keyctl\",\n        251 => \"ioprio_set\",\n        252 => \"ioprio_get\",\n        253 => \"inotify_init\",\n        254 => \"inotify_add_watch\",\n        255 => \"inotify_rm_watch\",\n        256 => \"migrate_pages\",\n        257 => \"openat\",\n        258 => \"mkdirat\",\n        259 => \"mknodat\",\n        260 => \"fchownat\",\n        261 => \"futimesat\",\n        262 => \"newfstatat\",\n        263 => \"unlinkat\",\n        264 => \"renameat\",\n        265 => \"linkat\",\n        266 => \"symlinkat\",\n        267 => \"readlinkat\",\n        268 => \"fchmodat\",\n        269 => \"faccessat\",\n        270 => \"pselect6\",\n        271 => \"ppoll\",\n        272 => \"unshare\",\n        273 => \"set_robust_list\",\n        274 => \"get_robust_list\",\n        275 => \"splice\",\n        276 => \"tee\",\n        277 => \"sync_file_range\",\n        278 => \"vmsplice\",\n        279 => \"move_pages\",\n        280 => \"utimensat\",\n        281 => \"epoll_pwait\",\n        282 => \"signalfd\",\n        283 => \"timerfd_create\",\n        284 => \"eventfd\",\n        285 => \"fallocate\",\n        286 => \"timerfd_settime\",\n        287 => \"timerfd_gettime\",\n        288 => \"accept4\",\n        289 => \"signalfd4\",\n        290 => \"eventfd2\",\n        291 => \"epoll_create1\",\n        292 => \"dup3\",\n        293 => \"pipe2\",\n        294 => \"inotify_init1\",\n        295 => \"preadv\",\n        296 => \"pwritev\",\n        297 => \"rt_tgsigqueueinfo\",\n        298 => \"perf_event_open\",\n        299 => \"recvmmsg\",\n        300 => \"fanotify_init\",\n        301 => \"fanotify_mark\",\n        302 => \"prlimit64\",\n        303 => \"name_to_handle_at\",\n        304 => \"open_by_handle_at\",\n        305 => \"clock_adjtime\",\n        306 => \"syncfs\",\n        307 => \"sendmmsg\",\n        308 => \"setns\",\n        309 => \"getcpu\",\n        310 => \"process_vm_readv\",\n        311 => \"process_vm_writev\",\n        312 => \"kcmp\",\n        313 => \"finit_module\",\n        314 => \"sched_setattr\",\n        315 => \"sched_getattr\",\n        316 => \"renameat2\",\n        317 => \"seccomp\",\n        318 => \"getrandom\",\n        319 => \"memfd_create\",\n        320 => \"kexec_file_load\",\n        321 => \"bpf\",\n        322 => \"execveat\",\n        323 => \"userfaultfd\",\n        324 => \"membarrier\",\n        325 => \"mlock2\",\n        326 => \"copy_file_range\",\n        327 => \"preadv2\",\n        328 => \"pwritev2\",\n        329 => \"pkey_mprotect\",\n        330 => \"pkey_alloc\",\n        331 => \"pkey_free\",\n        332 => \"statx\",\n        333 => \"io_pgetevents\",\n        334 => \"rseq\",\n\n        51729 => \"kados_debug\",\n        _ => \"(unknown)\",\n    }\n}\n\npub const SYS_READ: usize = 0;\npub const SYS_WRITE: usize = 1;\npub const SYS_OPEN: usize = 2;\npub const SYS_CLOSE: usize = 3;\npub const SYS_STAT: usize = 4;\npub const SYS_FSTAT: usize = 5;\npub const SYS_LSTAT: usize = 6;\npub const SYS_POLL: usize = 7;\npub const SYS_MMAP: usize = 9;\npub const SYS_MPROTECT: usize = 10;\npub const SYS_MUNMAP: usize = 11;\npub const SYS_BRK: usize = 12;\npub const SYS_RT_SIGACTION: usize = 13;\npub const SYS_RT_SIGPROCMASK: usize = 14;\npub const SYS_RT_SIGRETURN: usize = 15;\npub const SYS_IOCTL: usize = 16;\npub const SYS_WRITEV: usize = 20;\npub const SYS_PIPE: usize = 22;\npub const SYS_SELECT: usize = 23;\npub const SYS_DUP2: usize = 33;\npub const SYS_GETPID: usize = 39;\npub const SYS_SOCKET: usize = 41;\npub const SYS_CONNECT: usize = 42;\npub const SYS_ACCEPT: usize = 43;\npub const SYS_SENDTO: usize = 44;\npub const SYS_RECVFROM: usize = 45;\npub const SYS_SHUTDOWN: usize = 48;\npub const SYS_BIND: usize = 49;\npub const SYS_LISTEN: usize = 50;\npub const SYS_GETSOCKNAME: usize = 51;\npub const SYS_GETPEERNAME: usize = 52;\npub const SYS_GETSOCKOPT: usize = 55;\npub const SYS_CLONE: usize = 56;\npub const SYS_FORK: usize = 57;\npub const SYS_EXECVE: usize = 59;\npub const SYS_EXIT: usize = 60;\npub const SYS_WAIT4: usize = 61;\npub const SYS_KILL: usize = 62;\npub const SYS_UNAME: usize = 63;\npub const SYS_FCNTL: usize = 72;\npub const SYS_FSYNC: usize = 74;\npub const SYS_GETCWD: usize = 79;\npub const SYS_CHDIR: usize = 80;\npub const SYS_MKDIR: usize = 83;\npub const SYS_LINK: usize = 86;\npub const SYS_READLINK: usize = 89;\npub const SYS_CHMOD: usize = 90;\npub const SYS_CHOWN: usize = 92;\npub const SYS_GETUID: usize = 102;\npub const SYS_SYSLOG: usize = 103;\npub const SYS_SETUID: usize = 105;\npub const SYS_SETGID: usize = 106;\npub const SYS_GETEUID: usize = 107;\npub const SYS_SETPGID: usize = 109;\npub const SYS_GETPPID: usize = 110;\npub const SYS_GETPGRP: usize = 111;\npub const SYS_GETPGID: usize = 121;\npub const SYS_SETGROUPS: usize = 116;\npub const SYS_ARCH_PRCTL: usize = 158;\npub const SYS_REBOOT: usize = 169;\npub const SYS_GETTID: usize = 186;\npub const SYS_GETDENTS64: usize = 217;\npub const SYS_SET_TID_ADDRESS: usize = 218;\npub const SYS_CLOCK_GETTIME: usize = 228;\npub const SYS_EXIT_GROUP: usize = 231;\npub const SYS_UTIMES: usize = 235;\npub const SYS_LINKAT: usize = 265;\npub const SYS_GETRANDOM: usize = 318;\n\n"
  },
  {
    "path": "userland/kados_syscall/src/lib.rs",
    "content": "use core::arch::asm;\n\nuse consts::*;\n\npub mod consts;\n\n#[no_mangle]\n#[inline(always)]\n#[allow(clippy::missing_safety_doc)]\npub extern \"C\" fn syscall0(n: usize) -> isize {\n    let ret: isize;\n    unsafe {\n        asm!(\"syscall\", in(\"rax\") n, lateout(\"rax\") ret);\n    }\n    ret\n}\n\n#[no_mangle]\n#[inline(always)]\n#[allow(clippy::missing_safety_doc)]\npub extern \"C\" fn syscall1(n: usize, a1: usize) -> isize {\n    let ret: isize;\n    unsafe {\n        asm!(\"syscall\", in(\"rax\") n, in(\"rdi\") a1, lateout(\"rax\") ret);\n    }\n    ret\n}\n\n#[no_mangle]\n#[inline(always)]\n#[allow(clippy::missing_safety_doc)]\npub extern \"C\" fn syscall2(n: usize, a1: usize, a2: usize) -> isize {\n    let ret: isize;\n    unsafe {\n        asm!(\"syscall\", in(\"rax\") n, in(\"rdi\") a1, in(\"rsi\") a2, lateout(\"rax\") ret);\n    }\n    ret\n}\n\n#[no_mangle]\n#[inline(always)]\n#[allow(clippy::missing_safety_doc)]\npub extern \"C\" fn syscall3(n: usize, a1: usize, a2: usize, a3: usize) -> isize {\n    let ret: isize;\n    unsafe {\n        asm!(\"syscall\", in(\"rax\") n, in(\"rdi\") a1, in(\"rsi\") a2, in(\"rdx\") a3, lateout(\"rax\") ret);\n    }\n    ret\n}\n\n#[no_mangle]\n#[inline(always)]\n#[allow(clippy::missing_safety_doc)]\npub extern \"C\" fn syscall4(n: usize, a1: usize, a2: usize, a3: usize, a4: usize) -> isize {\n    let ret: isize;\n    unsafe {\n        asm!(\"syscall\", in(\"rax\") n, in(\"rdi\") a1, in(\"rsi\") a2, in(\"rdx\") a3, in(\"r10\") a4, lateout(\"rax\") ret);\n    }\n    ret\n}\n\n#[no_mangle]\n#[inline(always)]\n#[allow(clippy::missing_safety_doc)]\npub extern \"C\" fn syscall5(\n    n: usize,\n    a1: usize,\n    a2: usize,\n    a3: usize,\n    a4: usize,\n    a5: usize,\n) -> isize {\n    let ret: isize;\n    unsafe {\n        asm!(\"syscall\", in(\"rax\") n, in(\"rdi\") a1, in(\"rsi\") a2, in(\"rdx\") a3, in(\"r10\") a4, in(\"r8\") a5, lateout(\"rax\") ret);\n    }\n    ret\n}\n\n#[no_mangle]\n#[inline(always)]\n#[allow(clippy::missing_safety_doc)]\npub extern \"C\" fn syscall6(\n    n: usize,\n    a1: usize,\n    a2: usize,\n    a3: usize,\n    a4: usize,\n    a5: usize,\n    a6: usize,\n) -> isize {\n    let ret: isize;\n    unsafe {\n        asm!(\"syscall\", in(\"rax\") n, in(\"rdi\") a1, in(\"rsi\") a2, in(\"rdx\") a3, in(\"r10\") a4, in(\"r8\") a5, in(\"r9\") a6, lateout(\"rax\") ret);\n    }\n    ret\n}\n\npub fn sys_arch_prctl(code: i32, address: usize) -> SyscallResult {\n    syscall_result(syscall2(SYS_ARCH_PRCTL, code as usize, address))\n}\n\npub fn sys_set_tid_address(address: usize) -> SyscallResult {\n    syscall_result(syscall1(SYS_SET_TID_ADDRESS, address))\n}\n\npub fn sys_write(fd: i32, address: usize, len: usize) -> SyscallResult {\n    syscall_result(syscall3(SYS_WRITE, fd as usize, address, len))\n}\n\npub fn sys_writev(fd: i32, iov_base: usize, iov_count: usize) -> SyscallResult {\n    syscall_result(syscall3(SYS_WRITEV, fd as usize, iov_base, iov_count))\n}\n\npub fn sys_read(fd: i32, address: usize, len: usize) -> SyscallResult {\n    syscall_result(syscall3(SYS_READ, fd as usize, address, len))\n}\n\npub fn sys_fork() -> SyscallResult {\n    syscall_result(syscall0(SYS_FORK))\n}\n\npub fn sys_wait4(pid: i32, status_addr: usize, options: i32, rusage_addr: usize) -> SyscallResult {\n    syscall_result(syscall4(\n        SYS_WAIT4,\n        pid as usize,\n        status_addr,\n        options as usize,\n        rusage_addr,\n    ))\n}\n\npub fn sys_execve(path_addr: usize, argv_addr: usize, envp_addr: usize) -> SyscallResult {\n    syscall_result(syscall3(SYS_EXECVE, path_addr, argv_addr, envp_addr))\n}\n\npub fn sys_getcwd(buf_addr: usize, buf_len: usize) -> SyscallResult {\n    syscall_result(syscall2(SYS_GETCWD, buf_addr, buf_len))\n}\n"
  },
  {
    "path": "userland/rust-toolchain.toml",
    "content": "[toolchain]\nchannel = \"nightly\"\ntargets = [\"x86_64-unknown-linux-musl\"]\ncomponents = [\"rust-src\"]\n"
  }
]