[
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2019 Stephen Marz\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": "# osblog\nThe Adventures of OS\n\n# RISC-V OS in Rust\n- risc_v/src - contains RISC-V OS in Rust\n- risc_v/src/asm - contains assembly portions\n- risc_v/userspace - contains C++ userspace programs\n"
  },
  {
    "path": "assembly/.gitignore",
    "content": "*\n!*.S\n!*.c\n!.gitignore\n!Makefile\n"
  },
  {
    "path": "assembly/intrin.S",
    "content": ".intel_syntax noprefix\n.section .text\n.global calc_asm\ncalc_asm:\n\t# rdi rsi rdx rcx\n\tmovupd\txmm0, [rsi + 0]\n\tmovupd\txmm1, [rsi + 16]\n\tmovupd  xmm2, [rsi + 32]\n\tmovupd\txmm3, [rsi + 48]\n\n\tmovupd\txmm4, [rdx]\n\n\tmulps\txmm0, xmm4\n\tmulps\txmm1, xmm4\n\tmulps\txmm2, xmm4\n\tmulps\txmm3, xmm4\n\n\thaddps\txmm0, xmm0\n\thaddps\txmm0, xmm0\n\thaddps\txmm1, xmm1\n\thaddps\txmm1, xmm1\n\thaddps\txmm2, xmm2\n\thaddps\txmm2, xmm2\n\thaddps\txmm3, xmm3\n\thaddps\txmm3, xmm3\n\n\tmovss\t[rdi + 0], xmm0\n\tmovss\t[rdi + 4], xmm1\n\tmovss\t[rdi + 8], xmm2\n\tmovss\t[rdi + 12], xmm3\n\n\n\tret\n"
  },
  {
    "path": "assembly/intrin.c",
    "content": "#include <stdio.h>\n#include <pmmintrin.h>\n\nvoid calc_intrin(float result[], float matrix[], float vector[]);\nvoid calc_asm(float result[], float matrix[], float vector[]);\n\nint main() {\n\tint row, col;\n\tfloat vec[] = {1.0, 10.0, 100.0, 1000.0};\n\tfloat mat[] = {2.0, 0.0, 0.0, 0.0,\n\t\t       0.0, 2.2, 0.0, 0.0,\n\t\t       0.0, 0.0, 22.2, 0.0,\n\t\t       0.0, 0.0, 0.0, 22.22};\n\n\tfloat result[4];\n\n\tcalc_intrin(result, mat, vec);\n\n\tprintf(\"%5.3f %5.3f %5.3f %5.3f\\n\", result[0], result[1], result[2], result[3]);\n\n\tcalc_asm(result, mat, vec);\n\n\tprintf(\"%5.3f %5.3f %5.3f %5.3f\\n\", result[0], result[1], result[2], result[3]);\n\t\n\t\n\treturn 0;\n}\n\nvoid calc_intrin(float result[], float matrix[], float vector[])\n{\n\tint row;\n\t__m128 vec = _mm_loadu_ps(vector);\n\tfor (row = 0;row < 4;row++) {\n\t\t__m128 rowvec = _mm_loadu_ps(&matrix[row * 4]);\n\t\t__m128 rowvec2 = _mm_mul_ps(vec, rowvec);\n\t\t__m128 rowvec3 = _mm_hadd_ps(rowvec2, rowvec2);\n\t\t__m128 rowvec4 = _mm_hadd_ps(rowvec3, rowvec3);\n\n\t\t_mm_store_ss(&result[row], rowvec4);\n\t}\n}\n\n"
  },
  {
    "path": "risc_v/.cargo/config",
    "content": "[build]\ntarget = \"riscv64gc-unknown-none-elf\"\nrustflags = ['-Clink-arg=-Tsrc/lds/virt.lds']\n\n[target.riscv64gc-unknown-none-elf]\nrunner = \"qemu-system-riscv64 -machine virt -cpu rv64 -d guest_errors,unimp -smp 4 -m 128M -drive if=none,format=raw,file=hdd.dsk,id=foo -device virtio-blk-device,scsi=off,drive=foo -serial mon:stdio -bios none -device virtio-rng-device -device virtio-gpu-device -device virtio-net-device -device virtio-tablet-device -device virtio-keyboard-device -kernel \"\n"
  },
  {
    "path": "risc_v/.gitignore",
    "content": "os.elf\ntarget/*\nCargo.lock\nhdd.dsk\n"
  },
  {
    "path": "risc_v/BUILD.md",
    "content": "# PREREQS\nYou will need to install the riscv64gc target using rustup as well as cargo-binutils using cargo.\n\n* rustup target add riscv64gc-unknown-none-elf\n* cargo install cargo-binutils\n\n# BUILDING\nEdit .cargo/config to match your host's configuration. The runner will execute when you type `cargo run`.\n\nType `cargo build` to start the build process.\nType `cargo run` to run using the runner provided in .cargo/config\n\n# RELEASE BUILDS\n\nRelease builds turn on the optimizer and make it run much quicker. To run release builds, add `--release` as a parameter to cargo:\n\n* cargo build --release\n* cargo run --release\n\n# HARD DRIVE FILE\n\nTo run this as I have it configured, you'll need a hard drive file called hdd.dsk in this directory. You can create an empty\none by typing the following.\n\n* fallocate -l 32M hdd.dsk\n\n"
  },
  {
    "path": "risc_v/Cargo.toml",
    "content": "[package]\nname = \"sos\"\nversion = \"0.1.0\"\nauthors = [\"Stephen Marz <stephen.marz@utk.edu>\"]\nedition = \"2018\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[profile.dev]\nopt-level = 0\nlto = false\n\n[profile.release]\nopt-level = 3\nlto = true\ncodegen-units = 1\n\n[dependencies]\n"
  },
  {
    "path": "risc_v/chapters/ch0/.build.config",
    "content": "USE_SUDO=\"sudo\"\nJOBS=\"10\"\nLINUX_VER=\"git\"\nBUILD_ROOT=\"${PWD}\"\nTOOLCHAIN_ROOT=\"\"\nBUILD_BINUTILS=\"${BUILD_ROOT}/build-binutils\"\nBUILD_GCC_S1=\"${BUILD_ROOT}/build-gcc-s1\"\nBUILD_GCC_S2=\"${BUILD_ROOT}/build-gcc-s2\"\nBUILD_GLIBC_S1=\"${BUILD_ROOT}/build-glibc-s1\"\nBUILD_GLIBC_S2=\"${BUILD_ROOT}/build-glibc-s2\"\nBUILD_QEMU=\"${BUILD_ROOT}/build-qemu\"\nARCH=\"riscv\"\nBITS=\"64\"\nTAG=\"_1\"\nABI=\"lp64\"\nISA=\"rv64g\"\nTARGET=\"${ARCH}${BITS}-unknown-linux-gnu\"\nLIB_HEADER=\"linux.h\"\nBUILD_LINUX_ARCH=$ARCH\nBUILD_LINUX=\"${BUILD_ROOT}/linux-${LINUX_VER}\"\nBUILD_LINUX_HEADERS=\"${BUILD_ROOT}/build-${TARGET}-linux-headers\"\nPREFIX=\"${TOOLCHAIN_ROOT}/opt/${ARCH}${BITS}${TAG}\"\nSYSROOT=\"${PREFIX}/sysroot\"\nPATH=\"${PREFIX}/bin:${PATH}\"\n"
  },
  {
    "path": "risc_v/chapters/ch0/.cargo/config",
    "content": "[build]\ntarget = \"riscv64gc-unknown-none-elf\"\n\n[target.riscv64gc-unknown-none-elf]\nlinker = \"riscv64-unknown-linux-gnu-gcc\"\n"
  },
  {
    "path": "risc_v/chapters/ch0/Cargo.toml",
    "content": "[package]\nname = \"sos\"\nversion = \"0.1.0\"\nauthors = [\"Stephen Marz <stephen.marz@utk.edu>\"]\nedition = \"2018\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[lib]\ncrate-type = [\"staticlib\"]\n\n[dependencies]\n"
  },
  {
    "path": "risc_v/chapters/ch0/Makefile",
    "content": "#####\n## BUILD\n#####\nCC=riscv64-unknown-linux-gnu-g++\nCFLAGS=-Wall -Wextra -pedantic -Wextra -O0 -g -std=c++17\nCFLAGS+=-static -ffreestanding -nostdlib -fno-rtti -fno-exceptions\nCFLAGS+=-march=rv64gc -mabi=lp64\nINCLUDES=\nLINKER_SCRIPT=-Tsrc/lds/virt.lds\nTYPE=debug\nRUST_TARGET=./target/riscv64gc-unknown-none-elf/$(TYPE)\nLIBS=-L$(RUST_TARGET)\nSOURCES_ASM=$(wildcard src/asm/*.S)\nLIB=-lsos -lgcc\nOUT=os.elf\n\n#####\n## QEMU\n#####\nQEMU=qemu-system-riscv64\nMACH=virt\nCPU=rv64\nCPUS=4\nMEM=128M\nDRIVE=hdd.dsk\n\nall:\n\tcargo build\n\t$(CC) $(CFLAGS) $(LINKER_SCRIPT) $(INCLUDES) -o $(OUT) $(SOURCES_ASM) $(LIBS) $(LIB)\n\t\nrun: all\n\t$(QEMU) -machine $(MACH) -cpu $(CPU) -smp $(CPUS) -m $(MEM)  -nographic -serial mon:stdio -bios none -kernel $(OUT) -drive if=none,format=raw,file=$(DRIVE),id=foo -device virtio-blk-device,scsi=off,drive=foo\n\n\n.PHONY: clean\nclean:\n\tcargo clean\n\trm -f $(OUT)\n"
  },
  {
    "path": "risc_v/chapters/ch0/do.sh",
    "content": "#!/bin/bash\n# For building cross compilers\n# Use this at your own risk!\n# I make no warranties or guarantees with this script!\n# Stephen Marz\n# 15 Jan 2018\n\n. ./.build.config\nif [ $# -eq 0 ]; then\n\techo \"Must provide a number\"\n\techo \"0 - Binutils\"\n\techo \"1 - GCC Stage 1\"\n\techo \"2 - Linux Headers\"\n\techo \"3 - GLIBC Headers\"\n\techo \"4 - GLIBC\"\n\techo \"5 - GCC Stage 2\"\n\techo \"6 - QEMU\"\n\techo \"7 - Libs and Links\"\n\techo \"Add 90 if you just want to build that one stage\"\n\techo \"99 - Clean\"\n\texit 99\nelse\n\tARG=$1\nfi\n#Build BINUTILS\nif [ $ARG -le 0 -o $ARG -eq 90 ]; then\n\techo \"+-+-+-+ BINUTILS +-+-+-+\"\n\tmkdir -p ${BUILD_BINUTILS}\n\tcd ${BUILD_BINUTILS}\n\t${BUILD_ROOT}/binutils-gdb/configure --target=${TARGET} --prefix=${PREFIX} --with-sysroot=${SYSROOT} --disable-multilib --disable-werror --disable-nls --with-expat=yes --enable-gdb > ${BUILD_ROOT}/binutils.log 2>&1\n\tif [ $? -ne 0 ]; then\n\t\techo \"Error configuring BINUTILS\"\n\t\techo \"~~~~~~~~~~~~~~~~~~~~~~~~~~\"\n\t\tcat ${BUILD_ROOT}/binutils.log\n\t\texit 1\n\tfi\n\tcd ${BUILD_ROOT}\n\tmake -C ${BUILD_BINUTILS} -j${JOBS} >> ${BUILD_ROOT}/binutils.log 2>&1\n\tif [ $? -ne 0 ]; then\n\t\techo \"Error building BINUTILS\"\n\t\techo \"~~~~~~~~~~~~~~~~~~~~~~~\"\n\t\tcat ${BUILD_ROOT}/binutils.log\n\t\texit 1\n\tfi\n\t${USE_SUDO} make -C ${BUILD_BINUTILS} install >> ${BUILD_ROOT}/binutils.log 2>&1\n\tif [ $? -ne 0 ]; then\n\t\techo \"Error installing BINUTILS\"\n\t\techo \"~~~~~~~~~~~~~~~~~~~~~~~~~\"\n\t\tcat ${BUILD_ROOT}/binutils.log\n\t\texit 1\n\tfi\nfi\n\n#Build GCC Stage 1\nif [ $ARG -le 1 -o $ARG -eq 91 ]; then\n\techo \"+-+-+-+ GCC STAGE 1 +-+-+-+\"\n\tsed -i \"s|\\\"/lib/ld-linux-${ARCH}|\\\"${SYSROOT}/lib/ld-linux-${ARCH}|\" ${BUILD_ROOT}/gcc/gcc/config/${ARCH}/${LIB_HEADER}\n\tmkdir -p ${BUILD_GCC_S1}\n\tcd ${BUILD_GCC_S1}\n\t${BUILD_ROOT}/gcc/configure --target=${TARGET} --prefix=${PREFIX} --with-sysroot=${SYSROOT} --with-newlib --without-headers --disable-shared --disable-threads --with-system-zlib --enable-tls --enable-languages=c --disable-libatomic --disable-libmudflap --disable-libssp --disable-libquadmath --disable-libgomp --disable-nls --disable-bootstrap --enable-checking=yes --disable-multilib --with-abi=${ABI} --with-arch=${ISA} > ${BUILD_ROOT}/gccs1.log 2>&1\n\tif [ $? -ne 0 ]; then\n\t\techo \"Error configuring GCC stage 1\"\n\t\techo \"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\"\n\t\tcat ${BUILD_ROOT}/gccs1.log\n\t\texit 2\n\tfi\n\tcd ${BUILD_ROOT}\n\tmake -j${JOBS} -C ${BUILD_GCC_S1} >> ${BUILD_ROOT}/gccs1.log 2>&1\n\tif [ $? -ne 0 ]; then\n\t\techo \"Error building GCC stage 1\"\n\t\techo \"~~~~~~~~~~~~~~~~~~~~~~~~~~\"\n\t\tcat ${BUILD_ROOT}/gccs1.log\n\t\texit 2\n\tfi\n\t${USE_SUDO} make -C ${BUILD_GCC_S1} install >> ${BUILD_ROOT}/gccs1.log 2>&1\n\tif [ $? -ne 0 ]; then\n\t\techo \"Error installing GCC stage 1\"\n\t\techo \"~~~~~~~~~~~~~~~~~~~~~~~~~~~~\"\n\t\tcat ${BUILD_ROOT}/gccs1.log\n\t\texit 2\n\tfi\nfi\n\n#Build Linux Headers\nif [ $ARG -le 2 -o $ARG -eq 92 ]; then\n\techo \"+-+-+-+ LINUX HEADERS +-+-+-+\"\n\tif [ ! -x ${BUILD_ROOT}/linux-${LINUX_VER} ]; then\n\t\ttar xf ${BUILD_ROOT}/linux-${LINUX_VER}.tar.xz -C ${BUILD_ROOT} > ${BUILD_ROOT}/linhdr.log 2>&1\n\tfi\n\tif [ $? -ne 0 ]; then\n\t\techo \"Error unpacking Linux Headers\"\n\t\techo \"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\"\n\t\tcat ${BUILD_ROOT}/linhdr.log\n\t\texit 3\n\tfi\n\tmake ARCH=${BUILD_LINUX_ARCH} INSTALL_HDR_PATH=${BUILD_LINUX_HEADERS} -C ${BUILD_LINUX} defconfig >> ${BUILD_ROOT}/linhdr.log 2>&1\n\tif [ $? -ne 0 ]; then\n\t\techo \"Error configuring Linux Headers\"\n\t\techo \"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\"\n\t\tcat ${BUILD_ROOT}/linhdr.log\n\t\texit 3\n\tfi\n\tmake ARCH=${BUILD_LINUX_ARCH} INSTALL_HDR_PATH=${BUILD_LINUX_HEADERS} -C ${BUILD_LINUX} headers_install >> ${BUILD_ROOT}/linhdr.log 2>&1\n\tif [ $? -ne 0 ]; then\n\t\techo \"Error installing Linux Headers\"\n\t\techo \"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\"\n\t\tcat ${BUILD_ROOT}/linhdr.log\n\t\texit 3\n\tfi\nfi\nif [ $ARG -le 3 -o $ARG -eq 93 ]; then\n\t#Build GLIBC Headers\n\techo \"+-+-+-+ GLIBC HEADERS +-+-+-+\"\n\tmkdir -p ${BUILD_GLIBC_S1}\n\tcd ${BUILD_GLIBC_S1}\n\t${BUILD_ROOT}/glibc/configure --host=${TARGET} --prefix=${SYSROOT}/usr --enable-shared --with-headers=${BUILD_LINUX_HEADERS}/include --disable-multilib --enable-kernel=3.0.0 > ${BUILD_ROOT}/glibchdr.log 2>&1\n\tif [ $? -ne 0 ]; then\n\t\techo \"Error configuring GLIBC headers\"\n\t\techo \"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\"\n\t\tcat ${BUILD_ROOT}/glibchdr.log\n\t\texit 4\n\tfi\n\tcd ${BUILD_ROOT}\n\t${USE_SUDO} make -C ${BUILD_GLIBC_S1} install-headers >> ${BUILD_ROOT}/glibchdr.log 2>&1\n\tif [ $? -ne 0 ]; then\n\t\techo \"Error installing GLIBC headers\"\n\t\techo \"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\"\n\t\tcat ${BUILD_ROOT}/glibchdr.log\n\t\texit 4\n\tfi\n\t${USE_SUDO} cp -a ${BUILD_LINUX_HEADERS}/include/* ${SYSROOT}/usr/include/ >> ${BUILD_ROOT}/glibchdr.log 2>&1\n\tif [ $? -ne 0 ]; then\n\t\techo \"Error copying include files\"\n\t\techo \"~~~~~~~~~~~~~~~~~~~~~~~~~~~\"\n\t\tcat ${BUILD_ROOT}/glibchdr.log\n\t\texit 4\n\tfi\nfi\nif [ $ARG -le 4 -o $ARG -eq 94 ]; then\n\t#Build GLIBC\n\techo \"+-+-+-+ GLIBC +-+-+-+\"\n\tmkdir -p ${BUILD_GLIBC_S2}\n\tcd ${BUILD_GLIBC_S2}\n\t${BUILD_ROOT}/glibc/configure --host=${TARGET} --prefix=/usr --disable-werror --enable-tls --disable-nls --enable-shared --enable-obsolete-rpc --with-headers=${SYSROOT}/usr/include --disable-multilib --enable-kernel=3.0.0 > ${BUILD_ROOT}/glibc.log 2>&1\n\tif [ $? -ne 0 ]; then\n\t\techo \"Error configuring GLIBC\"\n\t\techo \"~~~~~~~~~~~~~~~~~~~~~~~\"\n\t\tcat ${BUILD_ROOT}/glibc.log\n\t\texit 5\n\tfi\n\tcd ${BUILD_ROOT}\n\tmake -C ${BUILD_GLIBC_S2} -j${JOBS} >> ${BUILD_ROOT}/glibc.log 2>&1\n\tif [ $? -ne 0 ]; then\n\t\techo \"Error building GLIBC\"\n\t\techo \"~~~~~~~~~~~~~~~~~~~~\"\n\t\tcat ${BUILD_ROOT}/glibc.log\n\t\texit 5\n\tfi\n\t${USE_SUDO} make -C ${BUILD_GLIBC_S2} install install_root=${SYSROOT} >> ${BUILD_ROOT}/glibc.log 2>&1\n\tif [ $? -ne 0 ]; then\n\t\techo \"Error installing GLIBC\"\n\t\techo \"~~~~~~~~~~~~~~~~~~~~~~\"\n\t\tcat ${BUILD_ROOT}/glibc.log\n\t\texit 5\n\tfi\n\t${USE_SUDO} ln -s ${SYSROOT}/lib64 ${SYSROOT}/lib\nfi\n\nif [ $ARG -le 5 -o $ARG -eq 95 ]; then\n\t#Build GCC Stage 2\n\techo \"+-+-+-+ GCC STAGE 2 +-+-+-+\"\n\tmkdir -p ${BUILD_GCC_S2}\n\tcd ${BUILD_GCC_S2}\n\t${BUILD_ROOT}/gcc/configure --target=${TARGET} --prefix=${PREFIX} --with-sysroot=${SYSROOT} --with-system-zlib --enable-shared --enable-tls --enable-languages=c,c++ --disable-libmudflap --disable-libssp --disable-libquadmath --disable-nls --disable-bootstrap --disable-multilib --enable-checking=yes --with-abi=${ABI} > ${BUILD_ROOT}/gccs2.log 2>&1\n\tif [ $? -ne 0 ]; then\n\t\techo \"Error configuring GCC stage 2\"\n\t\techo \"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\"\n\t\tcat ${BUILD_ROOT}/gccs2.log\n\t\texit 6\n\tfi\n\tcd ${BUILD_ROOT}\n\tmake -C ${BUILD_GCC_S2} -j${JOBS} >> ${BUILD_ROOT}/gccs2.log 2>&1\n\tif [ $? -ne 0 ]; then\n\t\techo \"Error building GCC stage 2\"\n\t\techo \"~~~~~~~~~~~~~~~~~~~~~~~~~~\"\n\t\tcat ${BUILD_ROOT}/gccs2.log\n\t\texit 6\n\tfi\n\t${USE_SUDO} make -C ${BUILD_GCC_S2} install >> ${BUILD_ROOT}/gccs2.log 2>&1\n\tif [ $? -ne 0 ]; then\n\t\techo \"Error installing GCC stage 2\"\n\t\techo \"~~~~~~~~~~~~~~~~~~~~~~~~~~~~\"\n\t\tcat ${BUILD_ROOT}/gccs2.log\n\t\texit 6\n\tfi\n\t${USE_SUDO} cp -a ${PREFIX}/${TARGET}/lib* ${SYSROOT}\n\tif [ $? -ne 0 ]; then\n\t\techo \"Error copying libraries\"\n\t\techo \"~~~~~~~~~~~~~~~~~~~~~~~\"\n\t\texit 6\n\tfi\nfi\n\nif [ $ARG -le 6 -o $ARG -eq 96 ]; then\n\t#Build QEMU\n\techo \"+-+-+-+ QEMU +-+-+-+\"\n\tmkdir -p ${BUILD_QEMU}\n\tcd ${BUILD_QEMU}\n\t${BUILD_ROOT}/qemu/configure --prefix=${PREFIX} --interp-prefix=${SYSROOT} --target-list=riscv32-linux-user,riscv32-softmmu,${ARCH}${BITS}-linux-user,${ARCH}${BITS}-softmmu --enable-jemalloc --disable-werror > ${BUILD_ROOT}/qemu.log 2>&1\n\tif [ $? -ne 0 ]; then\n\t\techo \"Error configuring QEMU\"\n\t\techo \"~~~~~~~~~~~~~~~~~~~~~~\"\n\t\tcat ${BUILD_ROOT}/qemu.log\n\t\texit 7\n\tfi\n\tcd ${BUILD_ROOT}\n\tmake -C ${BUILD_QEMU} -j${JOBS} >> ${BUILD_ROOT}/qemu.log 2>&1\n\tif [ $? -ne 0 ]; then\n\t\techo \"Error building QEMU\"\n\t\techo \"~~~~~~~~~~~~~~~~~~~\"\n\t\tcat ${BUILD_ROOT}/qemu.log\n\t\texit 7\n\tfi\n\t${USE_SUDO} make -C ${BUILD_QEMU} install >> ${BUILD_ROOT}/qemu.log 2>&1\n\tif [ $? -ne 0 ]; then\n\t\techo \"Error installing QEMU\"\n\t\techo \"~~~~~~~~~~~~~~~~~~~~~\"\n\t\tcat ${BUILD_ROOT}/qemu.log\n\t\texit 7\n\tfi\nfi\n\nif [ $ARG -le 7 -o $ARG -eq 97 ]; then\n\t#Make Symlinks\n\techo \"+-+-+-+ SYMLINKS +-+-+-+\"\n\t${USE_SUDO} ln -s ${PREFIX}/bin/${TARGET}-gcc ${PREFIX}/bin/${ARCH}${BITS}-gcc\n\t${USE_SUDO} ln -s ${PREFIX}/bin/${TARGET}-g++ ${PREFIX}/bin/${ARCH}${BITS}-g++\n\t${USE_SUDO} ln -s ${PREFIX}/bin/${TARGET}-objdump ${PREFIX}/bin/${ARCH}${BITS}-objdump\n\t${USE_SUDO} ln -s ${PREFIX}/bin/${TARGET}-gdb ${PREFIX}/bin/${ARCH}${BITS}-gdb\n\n#Copy Libraries\necho \"+-+-+-+ COPY LIBRARIES +-+-+-+\"\n${USE_SUDO} cp -a ${SYSROOT}/lib/* ${SYSROOT}/usr/lib${BITS}/${ABI}/\nfi\n\nif [ $ARG -eq 99 ]; then\n\techo \"+-+-+-+ CLEANING +-+-+-+\"\n\t${USE_SUDO} rm -fr ${BUILD_BINUTILS}\n\t${USE_SUDO} rm -fr ${BUILD_GCC_S1}\n\t${USE_SUDO} rm -fr ${BUILD_LINUX_HEADERS}\n\t${USE_SUDO} rm -fr ${BUILD_GLIBC_S1}\n\t${USE_SUDO} rm -fr ${BUILD_GLIBC_S2}\n\t${USE_SUDO} rm -fr ${BUILD_GCC_S2}\n\t${USE_SUDO} rm -fr ${BUILD_QEMU}\n\trm -fr *.log\nfi\necho \"+-+-+-+ !! DONE !! +-+-+-+\"\n"
  },
  {
    "path": "risc_v/chapters/ch0/virt.lds",
    "content": "/*\n virt.lds\n Linker script for outputting to RISC-V QEMU \"virt\" machine.\n Stephen Marz\n 6 October 2019\n*/\n\n/*\n  riscv is the name of the architecture that the linker understands\n  for any RISC-V target (64-bit or 32-bit).\n\n  We will further refine this by using -mabi=lp64 and -march=rv64gc\n*/\nOUTPUT_ARCH( \"riscv\" )\n\n/*\nWe're setting our entry point to a symbol\ncalled _start which is inside of boot.S. This\nessentially stores the address of _start as the\n\"entry point\", or where CPU instructions should start\nexecuting.\n\nIn the rest of this script, we are going to place _start\nright at the beginning of 0x8000_0000 because this is where\nthe virtual machine and many RISC-V boards will start executing.\n*/\nENTRY( _start )\n\n/*\nThe MEMORY section will explain that we have \"ram\" that contains\na section that is 'w' (writeable), 'x' (executable), and 'a' (allocatable).\nWe use '!' to invert 'r' (read-only) and 'i' (initialized). We don't want\nour memory to be read-only, and we're stating that it is NOT initialized\nat the beginning.\n\nThe ORIGIN is the memory address 0x8000_0000. If we look at the virt\nspec or the specification for the RISC-V HiFive Unleashed, this is the\nstarting memory address for our code.\n\nSide note: There might be other boot ROMs at different addresses, but\ntheir job is to get to this point.\n\nFinally LENGTH = 128M tells the linker that we have 128 megabyte of RAM.\nThe linker will double check this to make sure everything can fit.\n\nThe HiFive Unleashed has a lot more RAM than this, but for the virtual \nmachine, I went with 128M since I think that's enough RAM for now.\n\nWe can provide other pieces of memory, such as QSPI, or ROM, but we're\ntelling the linker script here that we have one pool of RAM.\n*/\nMEMORY\n{\n  ram   (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M\n}\n\n/*\nPHDRS is short for \"program headers\", which we specify three here:\ntext - CPU instructions (executable sections)\ndata - Global, initialized variables\nbss  - Global, uninitialized variables (all will be set to 0 by boot.S)\n\nThe command PT_LOAD tells the linker that these sections will be loaded\nfrom the file into memory.\n\nWe can actually stuff all of these into a single program header, but by\nsplitting it up into three, we can actually use the other PT_* commands\nsuch as PT_DYNAMIC, PT_INTERP, PT_NULL to tell the linker where to find\nadditional information.\n\nHowever, for our purposes, every section will be loaded from the program\nheaders.\n*/\nPHDRS\n{\n  text PT_LOAD;\n  data PT_LOAD;\n  bss PT_LOAD;\n}\n\n/*\nWe are now going to organize the memory based on which\nsection it is in. In assembly, we can change the section\nwith the \".section\" directive. However, in C++ and Rust,\nCPU instructions go into text, global constants go into\nrodata, global initialized variables go into data, and\nglobal uninitialized variables go into bss.\n*/\nSECTIONS\n{\n  /*\n    The first part of our RAM layout will be the text section.\n\tSince our CPU instructions are here, and our memory starts at\n\t0x8000_0000, we need our entry point to line up here.\n  */\n  .text : {\n\t  /* \n\t    PROVIDE allows me to access a symbol called _text_start so\n\t\tI know where the text section starts in the operating system.\n\t\tThis should not move, but it is here for convenience.\n\t\tThe period '.' tells the linker to set _text_start to the\n\t\tCURRENT location ('.' = current memory location). This current\n\t\tmemory location moves as we add things.\n\t  */\n\n    PROVIDE(_text_start = .);\n\t/*\n\t  We are going to layout all text sections here, starting with \n\t  .text.init. The asterisk in front of the parentheses means to match\n\t  the .text.init section of ANY object file. Otherwise, we can specify\n\t  which object file should contain the .text.init section, for example,\n\t  boot.o(.text.init) would specifically put the .text.init section of\n\t  our bootloader here.\n\n\t  Because we might want to change the name of our files, we'll leave it\n\t  with a *.\n\n\t  Inside the parentheses is the name of the section. I created my own\n\t  called .text.init to make 100% sure that the _start is put right at the\n\t  beginning. The linker will lay this out in the order it receives it:\n\n\t  .text.init first\n\t  all .text sections next\n\t  any .text.* sections last\n\n\t  .text.* means to match anything after .text. If we didn't already specify\n\t  .text.init, this would've matched here. The assembler and linker can place\n\t  things in \"special\" text sections, so we match any we might come across here.\n\t*/\n    *(.text.init) *(.text .text.*)\n\n\t/*\n\t  Again, with PROVIDE, we're providing a readable symbol called _text_end, which is\n\t  set to the memory address AFTER .text.init, .text, and .text.*'s have been added.\n\t*/\n    PROVIDE(_text_end = .);\n\t/*\n\t  The portion after the right brace is in an odd format. However, this is telling the\n\t  linker what memory portion to put it in. We labeled our RAM, ram, with the constraints\n\t  that it is writeable, allocatable, and executable. The linker will make sure with this\n\t  that we can do all of those things.\n\n\t  >ram - This just tells the linker script to put this entire section (.text) into the\n\t         ram region of memory. To my knowledge, the '>' does not mean \"greater than\". Instead,\n\t\t\t it is a symbol to let the linker know we want to put this in ram.\n\n\t  AT>ram - This sets the LMA (load memory address) region to the same thing. LMA is the final\n\t           translation of a VMA (virtual memory address). With this linker script, we're loading\n\t\t\t   everything into its physical location. We'll let the kernel copy and sort out the \n\t\t\t   virtual memory. That's why >ram and AT>ram are continually the same thing.\n\n\t  :text  - This tells the linker script to put this into the :text program header. We've only\n\t           defined three: text, data, and bss. In this case, we're telling the linker script\n\t\t\t   to go into the text section.\n\t*/\n  } >ram AT>ram :text\n   /*\n     The global pointer allows the linker to position global variables and constants into\n\t independent positions relative to the gp (global pointer) register. The globals start\n\t after the text sections and are only relevant to the rodata, data, and bss sections.\n   */\n   PROVIDE(_global_pointer = .);\n   /*\n     Most compilers create a rodata (read only data) section for global constants. However,\n\t we're going to place ours in the text section. We can actually put this in :data, but\n\t since the .text section is read-only, we can place it there.\n\n\t NOTE: This doesn't actually do anything, yet. The actual \"protection\" cannot be done\n\t at link time. Instead, when we program the memory management unit (MMU), we will be\n\t able to choose which bits (R=read, W=write, X=execute) we want each memory segment\n\t to be able to do.\n   */\n  .rodata : {\n    PROVIDE(_rodata_start = .);\n    *(.rodata .rodata.*)\n    PROVIDE(_rodata_end = .);\n\t/*\n\t   Again, we're placing the rodata section in the memory segment \"ram\" and we're putting\n\t   it in the :text program header. We don't have one for rodata anyway.\n\t*/\n  } >ram AT>ram :text\n\n  .data : {\n\t/*\n\t   . = ALIGN(4096) tells the linker to align the current memory location (which is\n\t   0x8000_0000 + text section + rodata section) to 4096 bytes. This is because our paging\n\t   system's resolution is 4,096 bytes or 4 KiB.\n\t*/\n    . = ALIGN(4096);\n    PROVIDE(_data_start = .);\n\t/*\n\t   sdata and data are essentially the same thing. However, compilers usually use the\n\t   sdata sections for shorter, quicker loading sections. So, usually critical data\n\t   is loaded there. However, we're loading all of this in one fell swoop.\n\t   So, we're looking to put all of the following sections under the umbrella .data:\n\t   .sdata\n\t   .sdata.[anything]\n\t   .data\n\t   .data.[anything]\n\n\t   ...in that order.\n\t*/\n    *(.sdata .sdata.*) *(.data .data.*)\n    PROVIDE(_data_end = .);\n  } >ram AT>ram :data\n\n  .bss : {\n    PROVIDE(_bss_start = .);\n    *(.sbss .sbss.*) *(.bss .bss.*)\n    PROVIDE(_bss_end = .);\n  } >ram AT>ram :bss\n\n  /*\n     The following will be helpful when we allocate the kernel stack (_stack) and\n\t determine where the heap begnis and ends (_heap_start and _heap_start + _heap_size)/\n\t When we do memory allocation, we can use these symbols.\n\n\t We use the symbols instead of hard-coding an address because this is a floating target.\n\t As we add code, the heap moves farther down the memory and gets shorter.\n\n\t _memory_start will be set to 0x8000_0000 here. We use ORIGIN(ram) so that it will take\n\t whatever we set the origin of ram to. Otherwise, we'd have to change it more than once\n\t if we ever stray away from 0x8000_0000 as our entry point.\n  */\n  PROVIDE(_memory_start = ORIGIN(ram));\n  /*\n     Our kernel stack starts at the end of the bss segment (_bss_end). However, we're allocating\n\t 0x80000 bytes (524 KiB) to our kernel stack. This should be PLENTY of space. The reason\n\t we add the memory is because the stack grows from higher memory to lower memory (bottom to top).\n\t Therefore we set the stack at the very bottom of its allocated slot.\n\t When we go to allocate from the stack, we'll subtract the number of bytes we need.\n  */\n  PROVIDE(_stack = _bss_end + 0x80000);\n  PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));\n\n  /* \n     Finally, our heap starts right after the kernel stack. This heap will be used mainly\n\t to dole out memory for user-space applications. However, in some circumstances, it will\n\t be used for kernel memory as well.\n\n\t We don't align here because we let the kernel determine how it wants to do this.\n  */\n  PROVIDE(_heap_start = _stack);\n  PROVIDE(_heap_size = _memory_end - _stack);\n}\n"
  },
  {
    "path": "risc_v/chapters/ch1/.cargo/config",
    "content": "[build]\ntarget = \"riscv64gc-unknown-none-elf\"\n\n[target.riscv64gc-unknown-none-elf]\nlinker = \"riscv64-unknown-linux-gnu-gcc\"\n"
  },
  {
    "path": "risc_v/chapters/ch1/.gitignore",
    "content": "os.elf\ntarget/*\nCargo.lock\nhdd.dsk\n"
  },
  {
    "path": "risc_v/chapters/ch1/Cargo.toml",
    "content": "[package]\nname = \"sos\"\nversion = \"0.1.0\"\nauthors = [\"Stephen Marz <stephen.marz@utk.edu>\"]\nedition = \"2018\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[lib]\ncrate-type = [\"staticlib\"]\n\n[dependencies]\n"
  },
  {
    "path": "risc_v/chapters/ch1/Makefile",
    "content": "#####\n## BUILD\n#####\nCC=riscv64-unknown-linux-gnu-g++\nCFLAGS=-Wall -Wextra -pedantic -Wextra -O0 -g -std=c++17\nCFLAGS+=-static -ffreestanding -nostdlib -fno-rtti -fno-exceptions\nCFLAGS+=-march=rv64gc -mabi=lp64\nINCLUDES=\nLINKER_SCRIPT=-Tsrc/lds/virt.lds\nTYPE=debug\nRUST_TARGET=./target/riscv64gc-unknown-none-elf/$(TYPE)\nLIBS=-L$(RUST_TARGET)\nSOURCES_ASM=$(wildcard src/asm/*.S)\nLIB=-lsos -lgcc\nOUT=os.elf\n\n#####\n## QEMU\n#####\nQEMU=qemu-system-riscv64\nMACH=virt\nCPU=rv64\nCPUS=4\nMEM=128M\nDRIVE=hdd.dsk\n\nall:\n\tcargo build\n\t$(CC) $(CFLAGS) $(LINKER_SCRIPT) $(INCLUDES) -o $(OUT) $(SOURCES_ASM) $(LIBS) $(LIB)\n\t\nrun: all\n\t$(QEMU) -machine $(MACH) -cpu $(CPU) -smp $(CPUS) -m $(MEM)  -nographic -serial mon:stdio -bios none -kernel $(OUT) -drive if=none,format=raw,file=$(DRIVE),id=foo -device virtio-blk-device,scsi=off,drive=foo\n\n\n.PHONY: clean\nclean:\n\tcargo clean\n\trm -f $(OUT)\n"
  },
  {
    "path": "risc_v/chapters/ch1/make_hdd.sh",
    "content": "#!/bin/sh\n\ndd if=/dev/zero of=hdd.dsk bs=1M count=32\n"
  },
  {
    "path": "risc_v/chapters/ch1/src/asm/boot.S",
    "content": "# boot.S\n# bootloader for SoS\n# Stephen Marz\n# 8 February 2019\n\n# Disable generation of compressed instructions.\n.option norvc\n\n# Define a .data section.\n.section .data\n\n# Define a .text.init section.\n.section .text.init\n\n# Execution starts here.\n.global _start\n_start:\n\t# Any hardware threads (hart) that are not bootstrapping\n\t# need to wait for an IPI\n\tcsrr\tt0, mhartid\n\tbnez\tt0, 3f\n\t# SATP should be zero, but let's make sure\n\tcsrw\tsatp, zero\n\t\n\t# Disable linker instruction relaxation for the `la` instruction below.\n\t# This disallows the assembler from assuming that `gp` is already initialized.\n\t# This causes the value stored in `gp` to be calculated from `pc`.\n.option push\n.option norelax\n\tla\t\tgp, _global_pointer\n.option pop\n\t# Set all bytes in the BSS section to zero.\n\tla \t\ta0, _bss_start\n\tla\t\ta1, _bss_end\n\tbgeu\ta0, a1, 2f\n1:\n\tsd\t\tzero, (a0)\n\taddi\ta0, a0, 8\n\tbltu\ta0, a1, 1b\n2:\n\t# Control registers, set the stack, mstatus, mepc,\n\t# and mtvec to return to the main function.\n\t# li\t\tt5, 0xffff;\n\t# csrw\tmedeleg, t5\n\t# csrw\tmideleg, t5\n\tla\t\tsp, _stack\n\t# Setting `mstatus` register:\n\t# 0b11 << 11: Machine's previous protection mode is 3 (MPP=3).\n\t# 1 << 7    : Machine's previous interrupt-enable bit is 1 (MPIE=1).\n\t# 1 << 3    : Machine's interrupt-enable bit is 1 (MIE=1).\n\tli\t\tt0, (0b11 << 11) | (1 << 7) | (1 << 3)\n\tcsrw\tmstatus, t0\n\t# Machine's exception program counter (MEPC) is set to `kmain`.\n\tla\t\tt1, kmain\n\tcsrw\tmepc, t1\n\t# Machine's trap vector base address is set to `asm_trap_vector`.\n\tla\t\tt2, asm_trap_vector\n\tcsrw\tmtvec, t2\n\t# Setting Machine's interrupt-enable bits (`mie` register):\n\t# 1 << 3 : Machine's M-mode software interrupt-enable bit is 1 (MSIE=1).\n\t# 1 << 7 : Machine's timer interrupt-enable bit is 1 (MTIE=1).\n\t# 1 << 11: Machine's external interrupt-enable bit is 1 (MEIE=1).\n\tli\t\tt3, (1 << 3) | (1 << 7) | (1 << 11)\n\tcsrw\tmie, t3\n\t# Set the return address to infinitely wait for interrupts.\n\tla\t\tra, 4f\n\t# We use mret here so that the mstatus register is properly updated.\n\tmret\n3:\n\n\t# Parked harts go here. We need to set these\n\t# to only awaken if it receives a software interrupt,\n\t# which we're going to call the SIPI (Software Intra-Processor Interrupt).\n\t# We only use these to run user-space programs, although this may\n\t# change.\n4:\n\twfi\n\tj\t\t4b\n\n"
  },
  {
    "path": "risc_v/chapters/ch1/src/asm/trap.S",
    "content": "# trap.S\n# In the future our trap vector will go here.\n\n.global asm_trap_vector\n# This will be our trap vector when we start\n# handling interrupts.\nasm_trap_vector:\n\tmret\n\n"
  },
  {
    "path": "risc_v/chapters/ch1/src/lds/virt.lds",
    "content": "OUTPUT_ARCH( \"riscv\" )\n\nENTRY( _start )\n\nMEMORY\n{\n  ram   (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M\n}\n\nPHDRS\n{\n  text PT_LOAD;\n  data PT_LOAD;\n  bss PT_LOAD;\n}\n\nSECTIONS\n{\n  .text : {\n    PROVIDE(_text_start = .);\n    *(.text.init) *(.text .text.*)\n    PROVIDE(_text_end = .);\n  } >ram AT>ram :text\n   PROVIDE(_global_pointer = .);\n  .rodata : {\n    PROVIDE(_rodata_start = .);\n    *(.rodata .rodata.*)\n    PROVIDE(_rodata_end = .);\n  } >ram AT>ram :text\n\n  .data : {\n    . = ALIGN(4096);\n    PROVIDE(_data_start = .);\n    *(.sdata .sdata.*) *(.data .data.*)\n    PROVIDE(_data_end = .);\n  } >ram AT>ram :data\n\n  .bss :{\n    PROVIDE(_bss_start = .);\n    *(.sbss .sbss.*) *(.bss .bss.*)\n    PROVIDE(_bss_end = .);\n  } >ram AT>ram :bss\n\n  PROVIDE(_memory_start = ORIGIN(ram));\n  PROVIDE(_stack = _bss_end + 0x80000);\n  PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));\n  PROVIDE(_heap_start = _stack);\n  PROVIDE(_heap_size = _memory_end - _stack);\n}\n"
  },
  {
    "path": "risc_v/chapters/ch1/src/lib.rs",
    "content": "// Steve Operating System\n// Stephen Marz\n// 21 Sep 2019\n#![no_std]\n#![feature(panic_info_message,asm)]\n\n// ///////////////////////////////////\n// / RUST MACROS\n// ///////////////////////////////////\n#[macro_export]\nmacro_rules! print\n{\n\t($($args:tt)+) => ({\n\n\t});\n}\n#[macro_export]\nmacro_rules! println\n{\n\t() => ({\n\t\tprint!(\"\\r\\n\")\n\t});\n\t($fmt:expr) => ({\n\t\tprint!(concat!($fmt, \"\\r\\n\"))\n\t});\n\t($fmt:expr, $($args:tt)+) => ({\n\t\tprint!(concat!($fmt, \"\\r\\n\"), $($args)+)\n\t});\n}\n\n// ///////////////////////////////////\n// / LANGUAGE STRUCTURES / FUNCTIONS\n// ///////////////////////////////////\n#[no_mangle]\nextern \"C\" fn eh_personality() {}\n#[panic_handler]\nfn panic(info: &core::panic::PanicInfo) -> ! {\n\tprint!(\"Aborting: \");\n\tif let Some(p) = info.location() {\n\t\tprintln!(\n\t\t         \"line {}, file {}: {}\",\n\t\t         p.line(),\n\t\t         p.file(),\n\t\t         info.message().unwrap()\n\t\t);\n\t}\n\telse {\n\t\tprintln!(\"no information available.\");\n\t}\n\tabort();\n}\n#[no_mangle]\nextern \"C\"\nfn abort() -> ! {\n\tloop {\n\t\tunsafe {\n\t\t\tasm!(\"wfi\"::::\"volatile\");\n\t\t}\n\t}\n}\n\n// ///////////////////////////////////\n// / CONSTANTS\n// ///////////////////////////////////\n\n// ///////////////////////////////////\n// / ENTRY POINT\n// ///////////////////////////////////\n#[no_mangle]\nextern \"C\"\nfn kmain() {\n\t// Main should initialize all sub-systems and get\n\t// ready to start scheduling. The last thing this\n\t// should do is start the timer.\n}\n\n// ///////////////////////////////////\n// / RUST MODULES\n// ///////////////////////////////////\n\n\n"
  },
  {
    "path": "risc_v/chapters/ch2/.cargo/config",
    "content": "[build]\ntarget = \"riscv64gc-unknown-none-elf\"\n\n[target.riscv64gc-unknown-none-elf]\nlinker = \"riscv64-unknown-linux-gnu-gcc\"\n"
  },
  {
    "path": "risc_v/chapters/ch2/.gitignore",
    "content": "os.elf\ntarget/*\nCargo.lock\nhdd.dsk\n"
  },
  {
    "path": "risc_v/chapters/ch2/Cargo.toml",
    "content": "[package]\nname = \"sos\"\nversion = \"0.1.0\"\nauthors = [\"Stephen Marz <stephen.marz@utk.edu>\"]\nedition = \"2018\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[lib]\ncrate-type = [\"staticlib\"]\n\n[dependencies]\n"
  },
  {
    "path": "risc_v/chapters/ch2/Makefile",
    "content": "#####\n## BUILD\n#####\nCC=riscv64-unknown-linux-gnu-g++\nCFLAGS=-Wall -Wextra -pedantic -Wextra -O0 -g -std=c++17\nCFLAGS+=-static -ffreestanding -nostdlib -fno-rtti -fno-exceptions\nCFLAGS+=-march=rv64gc -mabi=lp64\nINCLUDES=\nLINKER_SCRIPT=-Tsrc/lds/virt.lds\nTYPE=debug\nRUST_TARGET=./target/riscv64gc-unknown-none-elf/$(TYPE)\nLIBS=-L$(RUST_TARGET)\nSOURCES_ASM=$(wildcard src/asm/*.S)\nLIB=-lsos -lgcc\nOUT=os.elf\n\n#####\n## QEMU\n#####\nQEMU=qemu-system-riscv64\nMACH=virt\nCPU=rv64\nCPUS=4\nMEM=128M\nDRIVE=hdd.dsk\n\nall:\n\tcargo build\n\t$(CC) $(CFLAGS) $(LINKER_SCRIPT) $(INCLUDES) -o $(OUT) $(SOURCES_ASM) $(LIBS) $(LIB)\n\t\nrun: all\n\t$(QEMU) -machine $(MACH) -cpu $(CPU) -smp $(CPUS) -m $(MEM)  -nographic -serial mon:stdio -bios none -kernel $(OUT) -drive if=none,format=raw,file=$(DRIVE),id=foo -device virtio-blk-device,scsi=off,drive=foo\n\n\n.PHONY: clean\nclean:\n\tcargo clean\n\trm -f $(OUT)\n"
  },
  {
    "path": "risc_v/chapters/ch2/make_hdd.sh",
    "content": "#!/bin/sh\n\ndd if=/dev/zero of=hdd.dsk bs=1M count=32\n"
  },
  {
    "path": "risc_v/chapters/ch2/src/asm/boot.S",
    "content": "# boot.S\n# bootloader for SoS\n# Stephen Marz\n# 8 February 2019\n.option norvc\n.section .data\n.section .text.init\n.global _start\n_start:\n\t# Any hardware threads (hart) that are not bootstrapping\n\t# need to wait for an IPI\n\tcsrr\tt0, mhartid\n\tbnez\tt0, 3f\n\t# SATP should be zero, but let's make sure\n\tcsrw\tsatp, zero\n.option push\n.option norelax\n\tla\t\tgp, _global_pointer\n.option pop\n\t# The BSS section is expected to be zero\n\tla \t\ta0, _bss_start\n\tla\t\ta1, _bss_end\n\tbgeu\ta0, a1, 2f\n1:\n\tsd\t\tzero, (a0)\n\taddi\ta0, a0, 8\n\tbltu\ta0, a1, 1b\n2:\n\t# Control registers, set the stack, mstatus, mepc,\n\t# and mtvec to return to the main function.\n\t# li\t\tt5, 0xffff;\n\t# csrw\tmedeleg, t5\n\t# csrw\tmideleg, t5\n\tla\t\tsp, _stack\n\t# We use mret here so that the mstatus register\n\t# is properly updated.\n\tli\t\tt0, (0b11 << 11) | (1 << 7) | (1 << 3)\n\tcsrw\tmstatus, t0\n\tla\t\tt1, kmain\n\tcsrw\tmepc, t1\n\tla\t\tt2, asm_trap_vector\n\tcsrw\tmtvec, t2\n\tli\t\tt3, (1 << 3) | (1 << 7) | (1 << 11)\n\tcsrw\tmie, t3\n\tla\t\tra, 4f\n\tmret\n3:\n\n\t# Parked harts go here. We need to set these\n\t# to only awaken if it receives a software interrupt,\n\t# which we're going to call the SIPI (Software Intra-Processor Interrupt).\n\t# We only use these to run user-space programs, although this may\n\t# change.\n4:\n\twfi\n\tj\t\t4b\n\n"
  },
  {
    "path": "risc_v/chapters/ch2/src/asm/trap.S",
    "content": "# trap.S\n# In the future our trap vector will go here.\n\n.global asm_trap_vector\n# This will be our trap vector when we start\n# handling interrupts.\nasm_trap_vector:\n\tmret\n\n"
  },
  {
    "path": "risc_v/chapters/ch2/src/lds/virt.lds",
    "content": "/*\n virt.lds\n Linker script for outputting to RISC-V QEMU \"virt\" machine.\n Stephen Marz\n 6 October 2019\n*/\n\n/*\n  riscv is the name of the architecture that the linker understands\n  for any RISC-V target (64-bit or 32-bit).\n\n  We will further refine this by using -mabi=lp64 and -march=rv64gc\n*/\nOUTPUT_ARCH( \"riscv\" )\n\n/*\nWe're setting our entry point to a symbol\ncalled _start which is inside of boot.S. This\nessentially stores the address of _start as the\n\"entry point\", or where CPU instructions should start\nexecuting.\n\nIn the rest of this script, we are going to place _start\nright at the beginning of 0x8000_0000 because this is where\nthe virtual machine and many RISC-V boards will start executing.\n*/\nENTRY( _start )\n\n/*\nThe MEMORY section will explain that we have \"ram\" that contains\na section that is 'w' (writeable), 'x' (executable), and 'a' (allocatable).\nWe use '!' to invert 'r' (read-only) and 'i' (initialized). We don't want\nour memory to be read-only, and we're stating that it is NOT initialized\nat the beginning.\n\nThe ORIGIN is the memory address 0x8000_0000. If we look at the virt\nspec or the specification for the RISC-V HiFive Unleashed, this is the\nstarting memory address for our code.\n\nSide note: There might be other boot ROMs at different addresses, but\ntheir job is to get to this point.\n\nFinally LENGTH = 128M tells the linker that we have 128 megabyte of RAM.\nThe linker will double check this to make sure everything can fit.\n\nThe HiFive Unleashed has a lot more RAM than this, but for the virtual \nmachine, I went with 128M since I think that's enough RAM for now.\n\nWe can provide other pieces of memory, such as QSPI, or ROM, but we're\ntelling the linker script here that we have one pool of RAM.\n*/\nMEMORY\n{\n  ram   (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M\n}\n\n/*\nPHDRS is short for \"program headers\", which we specify three here:\ntext - CPU instructions (executable sections)\ndata - Global, initialized variables\nbss  - Global, uninitialized variables (all will be set to 0 by boot.S)\n\nThe command PT_LOAD tells the linker that these sections will be loaded\nfrom the file into memory.\n\nWe can actually stuff all of these into a single program header, but by\nsplitting it up into three, we can actually use the other PT_* commands\nsuch as PT_DYNAMIC, PT_INTERP, PT_NULL to tell the linker where to find\nadditional information.\n\nHowever, for our purposes, every section will be loaded from the program\nheaders.\n*/\nPHDRS\n{\n  text PT_LOAD;\n  data PT_LOAD;\n  bss PT_LOAD;\n}\n\n/*\nWe are now going to organize the memory based on which\nsection it is in. In assembly, we can change the section\nwith the \".section\" directive. However, in C++ and Rust,\nCPU instructions go into text, global constants go into\nrodata, global initialized variables go into data, and\nglobal uninitialized variables go into bss.\n*/\nSECTIONS\n{\n  /*\n    The first part of our RAM layout will be the text section.\n\tSince our CPU instructions are here, and our memory starts at\n\t0x8000_0000, we need our entry point to line up here.\n  */\n  .text : {\n\t  /* \n\t    PROVIDE allows me to access a symbol called _text_start so\n\t\tI know where the text section starts in the operating system.\n\t\tThis should not move, but it is here for convenience.\n\t\tThe period '.' tells the linker to set _text_start to the\n\t\tCURRENT location ('.' = current memory location). This current\n\t\tmemory location moves as we add things.\n\t  */\n\n    PROVIDE(_text_start = .);\n\t/*\n\t  We are going to layout all text sections here, starting with \n\t  .text.init. The asterisk in front of the parentheses means to match\n\t  the .text.init section of ANY object file. Otherwise, we can specify\n\t  which object file should contain the .text.init section, for example,\n\t  boot.o(.text.init) would specifically put the .text.init section of\n\t  our bootloader here.\n\n\t  Because we might want to change the name of our files, we'll leave it\n\t  with a *.\n\n\t  Inside the parentheses is the name of the section. I created my own\n\t  called .text.init to make 100% sure that the _start is put right at the\n\t  beginning. The linker will lay this out in the order it receives it:\n\n\t  .text.init first\n\t  all .text sections next\n\t  any .text.* sections last\n\n\t  .text.* means to match anything after .text. If we didn't already specify\n\t  .text.init, this would've matched here. The assembler and linker can place\n\t  things in \"special\" text sections, so we match any we might come across here.\n\t*/\n    *(.text.init) *(.text .text.*)\n\n\t/*\n\t  Again, with PROVIDE, we're providing a readable symbol called _text_end, which is\n\t  set to the memory address AFTER .text.init, .text, and .text.*'s have been added.\n\t*/\n    PROVIDE(_text_end = .);\n\t/*\n\t  The portion after the right brace is in an odd format. However, this is telling the\n\t  linker what memory portion to put it in. We labeled our RAM, ram, with the constraints\n\t  that it is writeable, allocatable, and executable. The linker will make sure with this\n\t  that we can do all of those things.\n\n\t  >ram - This just tells the linker script to put this entire section (.text) into the\n\t         ram region of memory. To my knowledge, the '>' does not mean \"greater than\". Instead,\n\t\t\t it is a symbol to let the linker know we want to put this in ram.\n\n\t  AT>ram - This sets the LMA (load memory address) region to the same thing. LMA is the final\n\t           translation of a VMA (virtual memory address). With this linker script, we're loading\n\t\t\t   everything into its physical location. We'll let the kernel copy and sort out the \n\t\t\t   virtual memory. That's why >ram and AT>ram are continually the same thing.\n\n\t  :text  - This tells the linker script to put this into the :text program header. We've only\n\t           defined three: text, data, and bss. In this case, we're telling the linker script\n\t\t\t   to go into the text section.\n\t*/\n  } >ram AT>ram :text\n   /*\n     The global pointer allows the linker to position global variables and constants into\n\t independent positions relative to the gp (global pointer) register. The globals start\n\t after the text sections and are only relevant to the rodata, data, and bss sections.\n   */\n   PROVIDE(_global_pointer = .);\n   /*\n     Most compilers create a rodata (read only data) section for global constants. However,\n\t we're going to place ours in the text section. We can actually put this in :data, but\n\t since the .text section is read-only, we can place it there.\n\n\t NOTE: This doesn't actually do anything, yet. The actual \"protection\" cannot be done\n\t at link time. Instead, when we program the memory management unit (MMU), we will be\n\t able to choose which bits (R=read, W=write, X=execute) we want each memory segment\n\t to be able to do.\n   */\n  .rodata : {\n    PROVIDE(_rodata_start = .);\n    *(.rodata .rodata.*)\n    PROVIDE(_rodata_end = .);\n\t/*\n\t   Again, we're placing the rodata section in the memory segment \"ram\" and we're putting\n\t   it in the :text program header. We don't have one for rodata anyway.\n\t*/\n  } >ram AT>ram :text\n\n  .data : {\n\t/*\n\t   . = ALIGN(4096) tells the linker to align the current memory location (which is\n\t   0x8000_0000 + text section + rodata section) to 4096 bytes. This is because our paging\n\t   system's resolution is 4,096 bytes or 4 KiB.\n\t*/\n    . = ALIGN(4096);\n    PROVIDE(_data_start = .);\n\t/*\n\t   sdata and data are essentially the same thing. However, compilers usually use the\n\t   sdata sections for shorter, quicker loading sections. So, usually critical data\n\t   is loaded there. However, we're loading all of this in one fell swoop.\n\t   So, we're looking to put all of the following sections under the umbrella .data:\n\t   .sdata\n\t   .sdata.[anything]\n\t   .data\n\t   .data.[anything]\n\n\t   ...in that order.\n\t*/\n    *(.sdata .sdata.*) *(.data .data.*)\n    PROVIDE(_data_end = .);\n  } >ram AT>ram :data\n\n  .bss : {\n    PROVIDE(_bss_start = .);\n    *(.sbss .sbss.*) *(.bss .bss.*)\n    PROVIDE(_bss_end = .);\n  } >ram AT>ram :bss\n\n  /*\n     The following will be helpful when we allocate the kernel stack (_stack) and\n\t determine where the heap begnis and ends (_heap_start and _heap_start + _heap_size)/\n\t When we do memory allocation, we can use these symbols.\n\n\t We use the symbols instead of hard-coding an address because this is a floating target.\n\t As we add code, the heap moves farther down the memory and gets shorter.\n\n\t _memory_start will be set to 0x8000_0000 here. We use ORIGIN(ram) so that it will take\n\t whatever we set the origin of ram to. Otherwise, we'd have to change it more than once\n\t if we ever stray away from 0x8000_0000 as our entry point.\n  */\n  PROVIDE(_memory_start = ORIGIN(ram));\n  /*\n     Our kernel stack starts at the end of the bss segment (_bss_end). However, we're allocating\n\t 0x80000 bytes (524 KiB) to our kernel stack. This should be PLENTY of space. The reason\n\t we add the memory is because the stack grows from higher memory to lower memory (bottom to top).\n\t Therefore we set the stack at the very bottom of its allocated slot.\n\t When we go to allocate from the stack, we'll subtract the number of bytes we need.\n  */\n  PROVIDE(_stack = _bss_end + 0x80000);\n  PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));\n\n  /* \n     Finally, our heap starts right after the kernel stack. This heap will be used mainly\n\t to dole out memory for user-space applications. However, in some circumstances, it will\n\t be used for kernel memory as well.\n\n\t We don't align here because we let the kernel determine how it wants to do this.\n  */\n  PROVIDE(_heap_start = _stack);\n  PROVIDE(_heap_size = _memory_end - _stack);\n}\n"
  },
  {
    "path": "risc_v/chapters/ch2/src/lib.rs",
    "content": "// Steve Operating System\n// Stephen Marz\n// 21 Sep 2019\n#![no_std]\n#![feature(panic_info_message,asm)]\n\n// ///////////////////////////////////\n// / RUST MACROS\n// ///////////////////////////////////\n#[macro_export]\nmacro_rules! print\n{\n\t($($args:tt)+) => ({\n\t\t\tuse core::fmt::Write;\n\t\t\tlet _ = write!(crate::uart::Uart::new(0x1000_0000), $($args)+);\n\t\t\t});\n}\n#[macro_export]\nmacro_rules! println\n{\n\t() => ({\n\t\t   print!(\"\\r\\n\")\n\t\t   });\n\t($fmt:expr) => ({\n\t\t\tprint!(concat!($fmt, \"\\r\\n\"))\n\t\t\t});\n\t($fmt:expr, $($args:tt)+) => ({\n\t\t\tprint!(concat!($fmt, \"\\r\\n\"), $($args)+)\n\t\t\t});\n}\n\n// ///////////////////////////////////\n// / LANGUAGE STRUCTURES / FUNCTIONS\n// ///////////////////////////////////\n#[no_mangle]\nextern \"C\" fn eh_personality() {}\n\n#[panic_handler]\nfn panic(info: &core::panic::PanicInfo) -> ! {\n\tprint!(\"Aborting: \");\n\tif let Some(p) = info.location() {\n\t\tprintln!(\n\t\t\t\t\"line {}, file {}: {}\",\n\t\t\t\tp.line(),\n\t\t\t\tp.file(),\n\t\t\t\tinfo.message().unwrap()\n\t\t\t\t);\n\t}\n\telse {\n\t\tprintln!(\"no information available.\");\n\t}\n\tabort();\n}\n#[no_mangle]\nextern \"C\"\nfn abort() -> ! {\n\tloop {\n\t\tunsafe {\n\t\t\tasm!(\"wfi\"::::\"volatile\");\n\t\t}\n\t}\n}\n\n// ///////////////////////////////////\n// / CONSTANTS\n// ///////////////////////////////////\n\n// ///////////////////////////////////\n// / ENTRY POINT\n// ///////////////////////////////////\n#[no_mangle]\nextern \"C\"\nfn kmain() {\n\t// Main should initialize all sub-systems and get\n\t// ready to start scheduling. The last thing this\n\t// should do is start the timer.\n\n\t// Let's try using our newly minted UART by initializing it first.\n\t// The UART is sitting at MMIO address 0x1000_0000, so for testing\n\t// now, lets connect to it and see if we can initialize it and write\n\t// to it.\n\tlet mut my_uart = uart::Uart::new(0x1000_0000);\n\n\tmy_uart.init();\n\n\t// Now test println! macro!\n\tprintln!(\"This is my operating system!\");\n\tprintln!(\"I'm so awesome. If you start typing something, I'll show you what you typed!\");\n\n\t// Now see if we can read stuff:\n\t// Usually we can use #[test] modules in Rust, but it would convolute the\n\t// task at hand. So, we'll just add testing snippets.\n\tloop {\n\t\tif let Some(c) = my_uart.get() {\n\t\t\tmatch c {\n\t\t\t\t8 => {\n\t\t\t\t\t// This is a backspace, so we essentially have\n\t\t\t\t\t// to write a space and backup again:\n\t\t\t\t\tprint!(\"{}{}{}\", 8 as char, ' ', 8 as char);\n\t\t\t\t},\n\t\t\t\t  10 | 13 => {\n\t\t\t\t\t  // Newline or carriage-return\n\t\t\t\t\t  println!();\n\t\t\t\t  },\n\t\t\t\t  0x1b => {\n\t\t\t\t\t  // Those familiar with ANSI escape sequences\n\t\t\t\t\t  // knows that this is one of them. The next\n\t\t\t\t\t  // thing we should get is the left bracket [\n\t\t\t\t\t  // These are multi-byte sequences, so we can take\n\t\t\t\t\t  // a chance and get from UART ourselves.\n\t\t\t\t\t  // Later, we'll button this up.\n\t\t\t\t\t  if let Some(next_byte) = my_uart.get() {\n\t\t\t\t\t\t  if next_byte == 91 {\n\t\t\t\t\t\t\t  // This is a right bracket! We're on our way!\n\t\t\t\t\t\t\t  if let Some(b) = my_uart.get() {\n\t\t\t\t\t\t\t\t  match b as char {\n\t\t\t\t\t\t\t\t\t  'A' => {\n\t\t\t\t\t\t\t\t\t\t  println!(\"That's the up arrow!\");\n\t\t\t\t\t\t\t\t\t  },\n\t\t\t\t\t\t\t\t\t  'B' => {\n\t\t\t\t\t\t\t\t\t\t  println!(\"That's the down arrow!\");\n\t\t\t\t\t\t\t\t\t  },\n\t\t\t\t\t\t\t\t\t  'C' => {\n\t\t\t\t\t\t\t\t\t\t  println!(\"That's the right arrow!\");\n\t\t\t\t\t\t\t\t\t  },\n\t\t\t\t\t\t\t\t\t  'D' => {\n\t\t\t\t\t\t\t\t\t\t  println!(\"That's the left arrow!\");\n\t\t\t\t\t\t\t\t\t  },\n\t\t\t\t\t\t\t\t\t  _ => {\n\t\t\t\t\t\t\t\t\t\t  println!(\"That's something else.....\");\n\t\t\t\t\t\t\t\t\t  }\n\t\t\t\t\t\t\t\t  }\n\t\t\t\t\t\t\t  }\n\t\t\t\t\t\t  }\n\t\t\t\t\t  }\n\t\t\t\t  },\n\t\t\t\t  _ => {\n\t\t\t\t\t  print!(\"{}\", c as char);\n\t\t\t\t  }\n\t\t\t}\n\t\t}\n\t}\n}\n\n// ///////////////////////////////////\n// / RUST MODULES\n// ///////////////////////////////////\n\npub mod uart;\n"
  },
  {
    "path": "risc_v/chapters/ch2/src/uart.rs",
    "content": "// uart.rs\n// UART routines and driver\n\nuse core::convert::TryInto;\nuse core::fmt::Write;\nuse core::fmt::Error;\n\npub struct Uart {\n\tbase_address: usize,\n}\n\nimpl Write for Uart {\n\tfn write_str(&mut self, out: &str) -> Result<(), Error> {\n\t\tfor c in out.bytes() {\n\t\t\tself.put(c);\n\t\t}\n\t\tOk(())\n\t}\n}\n\nimpl Uart {\n\tpub fn new(base_address: usize) -> Self {\n\t\tUart {\n\t\t\tbase_address\n\t\t}\n\t}\n\n\tpub fn init(&mut self) {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\t// First, set the word length, which\n\t\t\t// are bits 0 and 1 of the line control register (LCR)\n\t\t\t// which is at base_address + 3\n\t\t\t// We can easily write the value 3 here or 0b11, but I'm\n\t\t\t// extending it so that it is clear we're setting two individual\n\t\t\t// fields\n\t\t\t//                         Word 0     Word 1\n\t\t\t//                         ~~~~~~     ~~~~~~\n\t\t\tptr.add(3).write_volatile((1 << 0) | (1 << 1));\n\n\t\t\t// Now, enable the FIFO, which is bit index 0 of the FIFO\n\t\t\t// control register (FCR at offset 2).\n\t\t\t// Again, we can just write 1 here, but when we use left shift,\n\t\t\t// it's easier to see that we're trying to write bit index #0.\n\t\t\tptr.add(2).write_volatile(1 << 0);\n\n\t\t\t// Enable receiver buffer interrupts, which is at bit index\n\t\t\t// 0 of the interrupt enable register (IER at offset 1).\n\t\t\tptr.add(1).write_volatile(1 << 0);\n\n\t\t\t// If we cared about the divisor, the code below would set the divisor\n\t\t\t// from a global clock rate of 22.729 MHz (22,729,000 cycles per second)\n\t\t\t// to a signaling rate of 2400 (BAUD). We usually have much faster signalling\n\t\t\t// rates nowadays, but this demonstrates what the divisor actually does.\n\t\t\t// The formula given in the NS16500A specification for calculating the divisor\n\t\t\t// is:\n\t\t\t// divisor = ceil( (clock_hz) / (baud_sps x 16) )\n\t\t\t// So, we substitute our values and get:\n\t\t\t// divisor = ceil( 22_729_000 / (2400 x 16) )\n\t\t\t// divisor = ceil( 22_729_000 / 38_400 )\n\t\t\t// divisor = ceil( 591.901 ) = 592\n\n\t\t\t// The divisor register is two bytes (16 bits), so we need to split the value\n\t\t\t// 592 into two bytes. Typically, we would calculate this based on measuring\n\t\t\t// the clock rate, but again, for our purposes [qemu], this doesn't really do\n\t\t\t// anything.\n\t\t\tlet divisor: u16 = 592;\n\t\t\tlet divisor_least: u8 = (divisor & 0xff).try_into().unwrap();\n\t\t\tlet divisor_most:  u8 = (divisor >> 8).try_into().unwrap();\n\n\t\t\t// Notice that the divisor register DLL (divisor latch least) and DLM (divisor latch most)\n\t\t\t// have the same base address as the receiver/transmitter and the interrupt enable register.\n\t\t\t// To change what the base address points to, we open the \"divisor latch\" by writing 1 into\n\t\t\t// the Divisor Latch Access Bit (DLAB), which is bit index 7 of the Line Control Register (LCR)\n\t\t\t// which is at base_address + 3.\n\t\t\tlet lcr = ptr.add(3).read_volatile();\n\t\t\tptr.add(3).write_volatile(lcr | 1 << 7);\n\n\t\t\t// Now, base addresses 0 and 1 point to DLL and DLM, respectively.\n\t\t\t// Put the lower 8 bits of the divisor into DLL\n\t\t\tptr.add(0).write_volatile(divisor_least);\n\t\t\tptr.add(1).write_volatile(divisor_most);\n\n\t\t\t// Now that we've written the divisor, we never have to touch this again. In hardware, this\n\t\t\t// will divide the global clock (22.729 MHz) into one suitable for 2,400 signals per second.\n\t\t\t// So, to once again get access to the RBR/THR/IER registers, we need to close the DLAB bit\n\t\t\t// by clearing it to 0. Here, we just restore the original value of lcr.\n\t\t\tptr.add(3).write_volatile(lcr);\n\t\t}\n\t}\n\n\tpub fn put(&mut self, c: u8) {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\tptr.add(0).write_volatile(c);\n\t\t}\n\t}\n\n\tpub fn get(&mut self) -> Option<u8> {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\tif ptr.add(5).read_volatile() & 1 == 0 {\n\t\t\t\t// The DR bit is 0, meaning no data\n\t\t\t\tNone\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// The DR bit is 1, meaning data!\n\t\t\t\tSome(ptr.add(0).read_volatile())\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "risc_v/chapters/ch3/.cargo/config",
    "content": "[build]\ntarget = \"riscv64gc-unknown-none-elf\"\n\n[target.riscv64gc-unknown-none-elf]\nlinker = \"riscv64-unknown-linux-gnu-gcc\"\n"
  },
  {
    "path": "risc_v/chapters/ch3/.gitignore",
    "content": "os.elf\ntarget/*\nCargo.lock\nhdd.dsk\n"
  },
  {
    "path": "risc_v/chapters/ch3/Cargo.toml",
    "content": "[package]\nname = \"sos\"\nversion = \"0.1.0\"\nauthors = [\"Stephen Marz <stephen.marz@utk.edu>\"]\nedition = \"2018\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[lib]\ncrate-type = [\"staticlib\"]\n\n[dependencies]\n"
  },
  {
    "path": "risc_v/chapters/ch3/Makefile",
    "content": "#####\n## BUILD\n#####\nCC=riscv64-unknown-linux-gnu-gcc\nCFLAGS=-Wall -Wextra -pedantic -Wextra -O0 -g\nCFLAGS+=-static -ffreestanding -nostdlib -fno-rtti -fno-exceptions\nCFLAGS+=-march=rv64gc -mabi=lp64\nINCLUDES=\nLINKER_SCRIPT=-Tsrc/lds/virt.lds\nTYPE=debug\nRUST_TARGET=./target/riscv64gc-unknown-none-elf/$(TYPE)\nLIBS=-L$(RUST_TARGET)\nSOURCES_ASM=$(wildcard src/asm/*.S)\nLIB=-lsos -lgcc\nOUT=os.elf\n\n#####\n## QEMU\n#####\nQEMU=qemu-system-riscv64\nMACH=virt\nCPU=rv64\nCPUS=4\nMEM=128M\nDISK=hdd.dsk\n# DRIVE= -drive if=none,format=raw,file=$(DISK),id=foo -device virtio-blk-device,scsi=off,drive=foo\nDRIVE=\n\n\nall:\n\tcargo build\n\t$(CC) $(CFLAGS) $(LINKER_SCRIPT) $(INCLUDES) -o $(OUT) $(SOURCES_ASM) $(LIBS) $(LIB)\n\t\nrun: all\n\t$(QEMU) -machine $(MACH) -cpu $(CPU) -smp $(CPUS) -m $(MEM) $(DRIVE) -nographic -serial mon:stdio -bios none -kernel $(OUT)\n\n\n.PHONY: clean\nclean:\n\tcargo clean\n\trm -f $(OUT)\n"
  },
  {
    "path": "risc_v/chapters/ch3/make_hdd.sh",
    "content": "#!/bin/sh\n\ndd if=/dev/zero of=hdd.dsk bs=1M count=32\n"
  },
  {
    "path": "risc_v/chapters/ch3/src/asm/boot.S",
    "content": "# boot.S\n# bootloader for SoS\n# Stephen Marz\n# 8 February 2019\n\n# Disable generation of compressed instructions.\n.option norvc\n\n# Define a .data section.\n.section .data\n\n# Define a .text.init section.\n.section .text.init\n\n# Execution starts here.\n.global _start\n_start:\n\t# Any hardware threads (hart) that are not bootstrapping\n\t# need to wait for an IPI\n\tcsrr\tt0, mhartid\n\tbnez\tt0, 3f\n\t# SATP should be zero, but let's make sure\n\tcsrw\tsatp, zero\n\t\n\t# Disable linker instruction relaxation for the `la` instruction below.\n\t# This disallows the assembler from assuming that `gp` is already initialized.\n\t# This causes the value stored in `gp` to be calculated from `pc`.\n.option push\n.option norelax\n\tla\t\tgp, _global_pointer\n.option pop\n\t# Set all bytes in the BSS section to zero.\n\tla \t\ta0, _bss_start\n\tla\t\ta1, _bss_end\n\tbgeu\ta0, a1, 2f\n1:\n\tsd\t\tzero, (a0)\n\taddi\ta0, a0, 8\n\tbltu\ta0, a1, 1b\n2:\n\t# Control registers, set the stack, mstatus, mepc,\n\t# and mtvec to return to the main function.\n\t# li\t\tt5, 0xffff;\n\t# csrw\tmedeleg, t5\n\t# csrw\tmideleg, t5\n\t# The stack grows from bottom to top, so we put the stack pointer\n\t# to the very end of the stack range.\n\tla\t\tsp, _stack_end\n\t# Setting `mstatus` register:\n\t# 0b11 << 11: Machine's previous protection mode is 3 (MPP=3).\n\tli\t\tt0, 0b11 << 11\n\tcsrw\tmstatus, t0\n\t# Machine's exception program counter (MEPC) is set to `kinit`.\n\tla\t\tt1, kinit\n\tcsrw\tmepc, t1\n\t# Machine's trap vector base address is set to `asm_trap_vector`.\n\tla\t\tt2, asm_trap_vector\n\tcsrw\tmtvec, t2\n\t# Set the return address to get us into supervisor mode\n\tla\t\tra, 2f\n\t# We use mret here so that the mstatus register is properly updated.\n\tmret\n2:\n\t# We set the return address (ra above) to this label. When kinit() is finished\n\t# in Rust, it will return here.\n\n\t# Setting `sstatus` (supervisor status) register:\n\t# 1 << 8    : Supervisor's previous protection mode is 1 (SPP=1 [Supervisor]).\n\t# 1 << 5    : Supervisor's previous interrupt-enable bit is 1 (SPIE=1 [Enabled]).\n\t# 1 << 1    : Supervisor's interrupt-enable bit will be set to 1 after sret.\n\t# We set the \"previous\" bits because the sret will write the current bits\n\t# with the previous bits.\n\tli\t\tt0, (1 << 8) | (1 << 5)\n\tcsrw\tsstatus, t0\n\tla\t\tt1, kmain\n\tcsrw\tsepc, t1\n\t# Setting `mideleg` (machine interrupt delegate) register:\n\t# 1 << 1   : Software interrupt delegated to supervisor mode\n\t# 1 << 5   : Timer interrupt delegated to supervisor mode\n\t# 1 << 9   : External interrupt delegated to supervisor mode\n\t# By default all traps (interrupts or exceptions) automatically\n\t# cause an elevation to the machine privilege mode (mode 3).\n\t# When we delegate, we're telling the CPU to only elevate to\n\t# the supervisor privilege mode (mode 1)\n\tli\t\tt2, (1 << 1) | (1 << 5) | (1 << 9)\n\tcsrw\tmideleg, t2\n\t# Setting `sie` (supervisor interrupt enable) register:\n\t# This register takes the same bits as mideleg\n\t# 1 << 1    : Supervisor software interrupt enable (SSIE=1 [Enabled])\n\t# 1 << 5    : Supervisor timer interrupt enable (STIE=1 [Enabled])\n\t# 1 << 9    : Supervisor external interrupt enable (SEIE=1 [Enabled])\n\tcsrw\tsie, t2\n\t# Setting `stvec` (supervisor trap vector) register:\n\t# Essentially this is a function pointer, but the last two bits can be 00 or 01\n\t# 00        : All exceptions set pc to BASE\n\t# 01        : Asynchronous interrupts set pc to BASE + 4 x scause\n\tla\t\tt3, asm_trap_vector\n\tcsrw\tstvec, t3\n\t# kinit() is required to return back the SATP value (including MODE) via a0\n\tcsrw\tsatp, a0\n\t# Force the CPU to take our SATP register.\n\t# To be efficient, if the address space identifier (ASID) portion of SATP is already\n\t# in cache, it will just grab whatever's in cache. However, that means if we've updated\n\t# it in memory, it will be the old table. So, sfence.vma will ensure that the MMU always\n\t# grabs a fresh copy of the SATP register and associated tables.\n\tsfence.vma\n\t# sret will put us in supervisor mode and re-enable interrupts\n\tsret\n3:\n\n\t# Parked harts go here. We need to set these\n\t# to only awaken if it receives a software interrupt,\n\t# which we're going to call the SIPI (Software Intra-Processor Interrupt).\n\t# We call the SIPI by writing the software interrupt into the Core Local Interruptor (CLINT)\n\t# Which is calculated by: base_address + hart * 4\n\t# where base address is 0x0200_0000 (MMIO CLINT base address)\n\t# We only use additional harts to run user-space programs, although this may\n\t# change.\n4:\n\twfi\n\tj\t\t4b\n\n\n"
  },
  {
    "path": "risc_v/chapters/ch3/src/asm/mem.S",
    "content": "// mem.S\n// Importation of linker symbols\n\n.section .rodata\n.global HEAP_START\nHEAP_START: .dword _heap_start\n\n.global HEAP_SIZE\nHEAP_SIZE: .dword _heap_size\n\n.global TEXT_START\nTEXT_START: .dword _text_start\n\n.global TEXT_END\nTEXT_END: .dword _text_end\n\n.global DATA_START\nDATA_START: .dword _data_start\n\n.global DATA_END\nDATA_END: .dword _data_end\n\n.global RODATA_START\nRODATA_START: .dword _rodata_start\n\n.global RODATA_END\nRODATA_END: .dword _rodata_end\n\n.global BSS_START\nBSS_START: .dword _bss_start\n\n.global BSS_END\nBSS_END: .dword _bss_end\n\n.global KERNEL_STACK_START\nKERNEL_STACK_START: .dword _stack_start\n\n.global KERNEL_STACK_END\nKERNEL_STACK_END: .dword _stack_end\n\n.section .data\n.global KERNEL_TABLE\nKERNEL_TABLE: .dword 0\n\n"
  },
  {
    "path": "risc_v/chapters/ch3/src/asm/trap.S",
    "content": "# trap.S\n# In the future our trap vector will go here.\n\n.global asm_trap_vector\n# This will be our trap vector when we start\n# handling interrupts.\nasm_trap_vector:\n\tcsrr\ta0, mtval\n\twfi\n\tj asm_trap_vector\n\n"
  },
  {
    "path": "risc_v/chapters/ch3/src/kmem.rs",
    "content": "// kmem.rs\n// Sub-page level: malloc-like allocation system\n// Stephen Marz\n// 7 October 2019\n\nuse crate::page::{align_val, zalloc, Table, PAGE_SIZE};\nuse core::{mem::size_of, ptr::null_mut};\n\n#[repr(usize)]\nenum AllocListFlags {\n\tTaken = 1 << 63,\n}\nimpl AllocListFlags {\n\tpub fn val(self) -> usize {\n\t\tself as usize\n\t}\n}\n\nstruct AllocList {\n\tpub flags_size: usize,\n}\nimpl AllocList {\n\tpub fn is_taken(&self) -> bool {\n\t\tself.flags_size & AllocListFlags::Taken.val() != 0\n\t}\n\n\tpub fn is_free(&self) -> bool {\n\t\t!self.is_taken()\n\t}\n\n\tpub fn set_taken(&mut self) {\n\t\tself.flags_size |= AllocListFlags::Taken.val();\n\t}\n\n\tpub fn set_free(&mut self) {\n\t\tself.flags_size &= !AllocListFlags::Taken.val();\n\t}\n\n\tpub fn set_size(&mut self, sz: usize) {\n\t\tlet k = self.is_taken();\n\t\tself.flags_size = sz & !AllocListFlags::Taken.val();\n\t\tif k {\n\t\t\tself.flags_size |= AllocListFlags::Taken.val();\n\t\t}\n\t}\n\n\tpub fn get_size(&self) -> usize {\n\t\tself.flags_size & !AllocListFlags::Taken.val()\n\t}\n}\n\n// This is the head of the allocation. We start here when\n// we search for a free memory location.\nstatic mut KMEM_HEAD: *mut AllocList = null_mut();\n// In the future, we will have on-demand pages\n// so, we need to keep track of our memory footprint to\n// see if we actually need to allocate more.\nstatic mut KMEM_ALLOC: usize = 0;\nstatic mut KMEM_PAGE_TABLE: *mut Table = null_mut();\n\n\n// These functions are safe helpers around an unsafe\n// operation.\npub fn get_head() -> *mut u8 {\n\tunsafe { KMEM_HEAD as *mut u8 }\n}\n\npub fn get_page_table() -> *mut Table {\n\tunsafe { KMEM_PAGE_TABLE as *mut Table }\n}\n\npub fn get_num_allocations() -> usize {\n\tunsafe { KMEM_ALLOC }\n}\n\n/// Initialize kernel's memory\n/// This is not to be used to allocate memory\n/// for user processes. If that's the case, use\n/// alloc/dealloc from the page crate.\npub fn init() {\n\tunsafe {\n\t\t// Allocate 64 kernel pages (64 * 4096 = 262 KiB)\n\t\tlet k_alloc = zalloc(64);\n\t\tassert!(!k_alloc.is_null());\n\t\tKMEM_ALLOC = 64;\n\t\tKMEM_HEAD = k_alloc as *mut AllocList;\n\t\t(*KMEM_HEAD).set_free();\n\t\t(*KMEM_HEAD).set_size(KMEM_ALLOC * PAGE_SIZE);\n\t\tKMEM_PAGE_TABLE = zalloc(1) as *mut Table;\n\t}\n}\n\n/// Allocate sub-page level allocation based on bytes and zero the memory\npub fn kzmalloc(sz: usize) -> *mut u8 {\n\tlet size = align_val(sz, 3);\n\tlet ret = kmalloc(size);\n\n\tif !ret.is_null() {\n\t\tfor i in 0..size {\n\t\t\tunsafe {\n\t\t\t\t(*ret.add(i)) = 0;\n\t\t\t}\n\t\t}\n\t}\n\tret\n}\n\n/// Allocate sub-page level allocation based on bytes\npub fn kmalloc(sz: usize) -> *mut u8 {\n\tunsafe {\n\t\tlet size = align_val(sz, 3) + size_of::<AllocList>();\n\t\tlet mut head = KMEM_HEAD;\n\t\t// .add() uses pointer arithmetic, so we type-cast into a u8\n\t\t// so that we multiply by an absolute size (KMEM_ALLOC *\n\t\t// PAGE_SIZE).\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\n\t\twhile head < tail {\n\t\t\tif (*head).is_free() && size <= (*head).get_size() {\n\t\t\t\tlet chunk_size = (*head).get_size();\n\t\t\t\tlet rem = chunk_size - size;\n\t\t\t\t(*head).set_taken();\n\t\t\t\tif rem > size_of::<AllocList>() {\n\t\t\t\t\tlet next = (head as *mut u8).add(size)\n\t\t\t\t\t           as *mut AllocList;\n\t\t\t\t\t// There is space remaining here.\n\t\t\t\t\t(*next).set_free();\n\t\t\t\t\t(*next).set_size(rem);\n\t\t\t\t\t(*head).set_size(size);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// If we get here, take the entire chunk\n\t\t\t\t\t(*head).set_size(chunk_size);\n\t\t\t\t}\n\t\t\t\treturn head.add(1) as *mut u8;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// If we get here, what we saw wasn't a free\n\t\t\t\t// chunk, move on to the next.\n\t\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t\t       as *mut AllocList;\n\t\t\t}\n\t\t}\n\t}\n\t// If we get here, we didn't find any free chunks--i.e. there isn't\n\t// enough memory for this. TODO: Add on-demand page allocation.\n\tnull_mut()\n}\n\n/// Free a sub-page level allocation\npub fn kfree(ptr: *mut u8) {\n\tunsafe {\n\t\tif !ptr.is_null() {\n\t\t\tlet p = (ptr as *mut AllocList).offset(-1);\n\t\t\tif (*p).is_taken() {\n\t\t\t\t(*p).set_free();\n\t\t\t}\n\t\t\t// After we free, see if we can combine adjacent free\n\t\t\t// spots to see if we can reduce fragmentation.\n\t\t\tcoalesce();\n\t\t}\n\t}\n}\n\n/// Merge smaller chunks into a bigger chunk\npub fn coalesce() {\n\tunsafe {\n\t\tlet mut head = KMEM_HEAD;\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\n\t\twhile head < tail {\n\t\t\tlet next = (head as *mut u8).add((*head).get_size())\n\t\t\t           as *mut AllocList;\n\t\t\tif (*head).get_size() == 0 {\n\t\t\t\t// If this happens, then we have a bad heap\n\t\t\t\t// (double free or something). However, that\n\t\t\t\t// will cause an infinite loop since the next\n\t\t\t\t// pointer will never move beyond the current\n\t\t\t\t// location.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if next >= tail {\n\t\t\t\t// We calculated the next by using the size\n\t\t\t\t// given as get_size(), however this could push\n\t\t\t\t// us past the tail. In that case, the size is\n\t\t\t\t// wrong, hence we break and stop doing what we\n\t\t\t\t// need to do.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (*head).is_free() && (*next).is_free() {\n\t\t\t\t// This means we have adjacent blocks needing to\n\t\t\t\t// be freed. So, we combine them into one\n\t\t\t\t// allocation.\n\t\t\t\t(*head).set_size(\n\t\t\t\t                 (*head).get_size()\n\t\t\t\t                 + (*next).get_size(),\n\t\t\t\t);\n\t\t\t}\n\t\t\t// If we get here, we might've moved. Recalculate new\n\t\t\t// head.\n\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t       as *mut AllocList;\n\t\t}\n\t}\n}\n\n/// For debugging purposes, print the kmem table\npub fn print_table() {\n\tunsafe {\n\t\tlet mut head = KMEM_HEAD;\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\t\twhile head < tail {\n\t\t\tprintln!(\n\t\t\t         \"{:p}: Length = {:<10} Taken = {}\",\n\t\t\t         head,\n\t\t\t         (*head).get_size(),\n\t\t\t         (*head).is_taken()\n\t\t\t);\n\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t       as *mut AllocList;\n\t\t}\n\t}\n}\n\n// ///////////////////////////////////\n// / GLOBAL ALLOCATOR\n// ///////////////////////////////////\n\n// The global allocator allows us to use the data structures\n// in the core library, such as a linked list or B-tree.\n// We want to use these sparingly since we have a coarse-grained\n// allocator.\nuse core::alloc::{GlobalAlloc, Layout};\n\n// The global allocator is a static constant to a global allocator\n// structure. We don't need any members because we're using this\n// structure just to implement alloc and dealloc.\nstruct OsGlobalAlloc;\n\nunsafe impl GlobalAlloc for OsGlobalAlloc {\n\tunsafe fn alloc(&self, layout: Layout) -> *mut u8 {\n\t\t// We align to the next page size so that when\n\t\t// we divide by PAGE_SIZE, we get exactly the number\n\t\t// of pages necessary.\n\t\tkzmalloc(layout.size())\n\t}\n\n\tunsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {\n\t\t// We ignore layout since our allocator uses ptr_start -> last\n\t\t// to determine the span of an allocation.\n\t\tkfree(ptr);\n\t}\n}\n\n#[global_allocator]\n/// Technically, we don't need the {} at the end, but it\n/// reveals that we're creating a new structure and not just\n/// copying a value.\nstatic GA: OsGlobalAlloc = OsGlobalAlloc {};\n\n#[alloc_error_handler]\n/// If for some reason alloc() in the global allocator gets null_mut(),\n/// then we come here. This is a divergent function, so we call panic to\n/// let the tester know what's going on.\npub fn alloc_error(l: Layout) -> ! {\n\tpanic!(\n\t       \"Allocator failed to allocate {} bytes with {}-byte alignment.\",\n\t       l.size(),\n\t       l.align()\n\t);\n}\n"
  },
  {
    "path": "risc_v/chapters/ch3/src/lds/virt.lds",
    "content": "/*\n virt.lds\n Linker script for outputting to RISC-V QEMU \"virt\" machine.\n Stephen Marz\n 6 October 2019\n*/\n\n/*\n  riscv is the name of the architecture that the linker understands\n  for any RISC-V target (64-bit or 32-bit).\n\n  We will further refine this by using -mabi=lp64 and -march=rv64gc\n*/\nOUTPUT_ARCH( \"riscv\" )\n\n/*\nWe're setting our entry point to a symbol\ncalled _start which is inside of boot.S. This\nessentially stores the address of _start as the\n\"entry point\", or where CPU instructions should start\nexecuting.\n\nIn the rest of this script, we are going to place _start\nright at the beginning of 0x8000_0000 because this is where\nthe virtual machine and many RISC-V boards will start executing.\n*/\nENTRY( _start )\n\n/*\nThe MEMORY section will explain that we have \"ram\" that contains\na section that is 'w' (writeable), 'x' (executable), and 'a' (allocatable).\nWe use '!' to invert 'r' (read-only) and 'i' (initialized). We don't want\nour memory to be read-only, and we're stating that it is NOT initialized\nat the beginning.\n\nThe ORIGIN is the memory address 0x8000_0000. If we look at the virt\nspec or the specification for the RISC-V HiFive Unleashed, this is the\nstarting memory address for our code.\n\nSide note: There might be other boot ROMs at different addresses, but\ntheir job is to get to this point.\n\nFinally LENGTH = 128M tells the linker that we have 128 megabyte of RAM.\nThe linker will double check this to make sure everything can fit.\n\nThe HiFive Unleashed has a lot more RAM than this, but for the virtual \nmachine, I went with 128M since I think that's enough RAM for now.\n\nWe can provide other pieces of memory, such as QSPI, or ROM, but we're\ntelling the linker script here that we have one pool of RAM.\n*/\nMEMORY\n{\n  ram   (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M\n}\n\n/*\nPHDRS is short for \"program headers\", which we specify three here:\ntext - CPU instructions (executable sections)\ndata - Global, initialized variables\nbss  - Global, uninitialized variables (all will be set to 0 by boot.S)\n\nThe command PT_LOAD tells the linker that these sections will be loaded\nfrom the file into memory.\n\nWe can actually stuff all of these into a single program header, but by\nsplitting it up into three, we can actually use the other PT_* commands\nsuch as PT_DYNAMIC, PT_INTERP, PT_NULL to tell the linker where to find\nadditional information.\n\nHowever, for our purposes, every section will be loaded from the program\nheaders.\n*/\nPHDRS\n{\n  text PT_LOAD;\n  data PT_LOAD;\n  bss PT_LOAD;\n}\n\n/*\nWe are now going to organize the memory based on which\nsection it is in. In assembly, we can change the section\nwith the \".section\" directive. However, in C++ and Rust,\nCPU instructions go into text, global constants go into\nrodata, global initialized variables go into data, and\nglobal uninitialized variables go into bss.\n*/\nSECTIONS\n{\n  /*\n    The first part of our RAM layout will be the text section.\n\tSince our CPU instructions are here, and our memory starts at\n\t0x8000_0000, we need our entry point to line up here.\n  */\n  .text : {\n\t  /* \n\t    PROVIDE allows me to access a symbol called _text_start so\n\t\tI know where the text section starts in the operating system.\n\t\tThis should not move, but it is here for convenience.\n\t\tThe period '.' tells the linker to set _text_start to the\n\t\tCURRENT location ('.' = current memory location). This current\n\t\tmemory location moves as we add things.\n\t  */\n\n    PROVIDE(_text_start = .);\n\t/*\n\t  We are going to layout all text sections here, starting with \n\t  .text.init. The asterisk in front of the parentheses means to match\n\t  the .text.init section of ANY object file. Otherwise, we can specify\n\t  which object file should contain the .text.init section, for example,\n\t  boot.o(.text.init) would specifically put the .text.init section of\n\t  our bootloader here.\n\n\t  Because we might want to change the name of our files, we'll leave it\n\t  with a *.\n\n\t  Inside the parentheses is the name of the section. I created my own\n\t  called .text.init to make 100% sure that the _start is put right at the\n\t  beginning. The linker will lay this out in the order it receives it:\n\n\t  .text.init first\n\t  all .text sections next\n\t  any .text.* sections last\n\n\t  .text.* means to match anything after .text. If we didn't already specify\n\t  .text.init, this would've matched here. The assembler and linker can place\n\t  things in \"special\" text sections, so we match any we might come across here.\n\t*/\n    *(.text.init) *(.text .text.*)\n\n\t/*\n\t  Again, with PROVIDE, we're providing a readable symbol called _text_end, which is\n\t  set to the memory address AFTER .text.init, .text, and .text.*'s have been added.\n\t*/\n    PROVIDE(_text_end = .);\n\t/*\n\t  The portion after the right brace is in an odd format. However, this is telling the\n\t  linker what memory portion to put it in. We labeled our RAM, ram, with the constraints\n\t  that it is writeable, allocatable, and executable. The linker will make sure with this\n\t  that we can do all of those things.\n\n\t  >ram - This just tells the linker script to put this entire section (.text) into the\n\t         ram region of memory. To my knowledge, the '>' does not mean \"greater than\". Instead,\n\t\t\t it is a symbol to let the linker know we want to put this in ram.\n\n\t  AT>ram - This sets the LMA (load memory address) region to the same thing. LMA is the final\n\t           translation of a VMA (virtual memory address). With this linker script, we're loading\n\t\t\t   everything into its physical location. We'll let the kernel copy and sort out the \n\t\t\t   virtual memory. That's why >ram and AT>ram are continually the same thing.\n\n\t  :text  - This tells the linker script to put this into the :text program header. We've only\n\t           defined three: text, data, and bss. In this case, we're telling the linker script\n\t\t\t   to go into the text section.\n\t*/\n  } >ram AT>ram :text\n   /*\n     The global pointer allows the linker to position global variables and constants into\n\t independent positions relative to the gp (global pointer) register. The globals start\n\t after the text sections and are only relevant to the rodata, data, and bss sections.\n   */\n   PROVIDE(_global_pointer = .);\n   /*\n     Most compilers create a rodata (read only data) section for global constants. However,\n\t we're going to place ours in the text section. We can actually put this in :data, but\n\t since the .text section is read-only, we can place it there.\n\n\t NOTE: This doesn't actually do anything, yet. The actual \"protection\" cannot be done\n\t at link time. Instead, when we program the memory management unit (MMU), we will be\n\t able to choose which bits (R=read, W=write, X=execute) we want each memory segment\n\t to be able to do.\n   */\n  .rodata : {\n    PROVIDE(_rodata_start = .);\n    *(.rodata .rodata.*)\n    PROVIDE(_rodata_end = .);\n\t/*\n\t   Again, we're placing the rodata section in the memory segment \"ram\" and we're putting\n\t   it in the :text program header. We don't have one for rodata anyway.\n\t*/\n  } >ram AT>ram :text\n\n  .data : {\n\t/*\n\t   . = ALIGN(4096) tells the linker to align the current memory location (which is\n\t   0x8000_0000 + text section + rodata section) to 4096 bytes. This is because our paging\n\t   system's resolution is 4,096 bytes or 4 KiB.\n\t*/\n    . = ALIGN(4096);\n    PROVIDE(_data_start = .);\n\t/*\n\t   sdata and data are essentially the same thing. However, compilers usually use the\n\t   sdata sections for shorter, quicker loading sections. So, usually critical data\n\t   is loaded there. However, we're loading all of this in one fell swoop.\n\t   So, we're looking to put all of the following sections under the umbrella .data:\n\t   .sdata\n\t   .sdata.[anything]\n\t   .data\n\t   .data.[anything]\n\n\t   ...in that order.\n\t*/\n    *(.sdata .sdata.*) *(.data .data.*)\n    PROVIDE(_data_end = .);\n  } >ram AT>ram :data\n\n  .bss : {\n    PROVIDE(_bss_start = .);\n    *(.sbss .sbss.*) *(.bss .bss.*)\n    PROVIDE(_bss_end = .);\n  } >ram AT>ram :bss\n\n  /*\n     The following will be helpful when we allocate the kernel stack (_stack) and\n\t determine where the heap begnis and ends (_heap_start and _heap_start + _heap_size)/\n\t When we do memory allocation, we can use these symbols.\n\n\t We use the symbols instead of hard-coding an address because this is a floating target.\n\t As we add code, the heap moves farther down the memory and gets shorter.\n\n\t _memory_start will be set to 0x8000_0000 here. We use ORIGIN(ram) so that it will take\n\t whatever we set the origin of ram to. Otherwise, we'd have to change it more than once\n\t if we ever stray away from 0x8000_0000 as our entry point.\n  */\n  PROVIDE(_memory_start = ORIGIN(ram));\n  /*\n     Our kernel stack starts at the end of the bss segment (_bss_end). However, we're allocating\n\t 0x80000 bytes (524 KiB) to our kernel stack. This should be PLENTY of space. The reason\n\t we add the memory is because the stack grows from higher memory to lower memory (bottom to top).\n\t Therefore we set the stack at the very bottom of its allocated slot.\n\t When we go to allocate from the stack, we'll subtract the number of bytes we need.\n  */\n  PROVIDE(_stack_start = _bss_end);\n  PROVIDE(_stack_end = _stack_start + 0x8000);\n  PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));\n\n  /* \n     Finally, our heap starts right after the kernel stack. This heap will be used mainly\n\t to dole out memory for user-space applications. However, in some circumstances, it will\n\t be used for kernel memory as well.\n\n\t We don't align here because we let the kernel determine how it wants to do this.\n  */\n  PROVIDE(_heap_start = _stack_end);\n  PROVIDE(_heap_size = _memory_end - _heap_start);\n}\n"
  },
  {
    "path": "risc_v/chapters/ch3/src/lib.rs",
    "content": "// Steve Operating System\n// Stephen Marz\n// 21 Sep 2019\n#![no_std]\n#![feature(panic_info_message,\n           asm,\n           allocator_api,\n           alloc_error_handler,\n           alloc_prelude,\n           const_raw_ptr_to_usize_cast)]\n\n#[macro_use]\nextern crate alloc;\n// This is experimental and requires alloc_prelude as a feature\nuse alloc::prelude::v1::*;\n\n// ///////////////////////////////////\n// / RUST MACROS\n// ///////////////////////////////////\n#[macro_export]\nmacro_rules! print\n{\n\t($($args:tt)+) => ({\n\t\t\tuse core::fmt::Write;\n\t\t\tlet _ = write!(crate::uart::Uart::new(0x1000_0000), $($args)+);\n\t\t\t});\n}\n#[macro_export]\nmacro_rules! println\n{\n\t() => ({\n\t\t   print!(\"\\r\\n\")\n\t\t   });\n\t($fmt:expr) => ({\n\t\t\tprint!(concat!($fmt, \"\\r\\n\"))\n\t\t\t});\n\t($fmt:expr, $($args:tt)+) => ({\n\t\t\tprint!(concat!($fmt, \"\\r\\n\"), $($args)+)\n\t\t\t});\n}\n\n// ///////////////////////////////////\n// / LANGUAGE STRUCTURES / FUNCTIONS\n// ///////////////////////////////////\n#[no_mangle]\nextern \"C\" fn eh_personality() {}\n\n#[panic_handler]\nfn panic(info: &core::panic::PanicInfo) -> ! {\n\tprint!(\"Aborting: \");\n\tif let Some(p) = info.location() {\n\t\tprintln!(\n\t\t         \"line {}, file {}: {}\",\n\t\t         p.line(),\n\t\t         p.file(),\n\t\t         info.message().unwrap()\n\t\t);\n\t}\n\telse {\n\t\tprintln!(\"no information available.\");\n\t}\n\tabort();\n}\n#[no_mangle]\nextern \"C\" fn abort() -> ! {\n\tloop {\n\t\tunsafe {\n\t\t\tasm!(\"wfi\"::::\"volatile\");\n\t\t}\n\t}\n}\n\n// ///////////////////////////////////\n// / CONSTANTS\n// ///////////////////////////////////\n// const STR_Y: &str = \"\\x1b[38;2;79;221;13m✓\\x1b[m\";\n// const STR_N: &str = \"\\x1b[38;2;221;41;13m✘\\x1b[m\";\n\n// The following symbols come from asm/mem.S. We can use\n// the symbols directly, but the address of the symbols\n// themselves are their values, which can cause issues.\n// Instead, I created doubleword values in mem.S in the .rodata and .data\n// sections.\nextern \"C\" {\n\tstatic TEXT_START: usize;\n\tstatic TEXT_END: usize;\n\tstatic DATA_START: usize;\n\tstatic DATA_END: usize;\n\tstatic RODATA_START: usize;\n\tstatic RODATA_END: usize;\n\tstatic BSS_START: usize;\n\tstatic BSS_END: usize;\n\tstatic KERNEL_STACK_START: usize;\n\tstatic KERNEL_STACK_END: usize;\n\tstatic HEAP_START: usize;\n\tstatic HEAP_SIZE: usize;\n\tstatic mut KERNEL_TABLE: usize;\n}\n/// Identity map range\n/// Takes a contiguous allocation of memory and maps it using PAGE_SIZE\n/// This assumes that start <= end\npub fn id_map_range(root: &mut page::Table,\n                    start: usize,\n                    end: usize,\n                    bits: i64)\n{\n\tlet mut memaddr = start & !(page::PAGE_SIZE - 1);\n\tlet num_kb_pages = (page::align_val(end, 12)\n\t                 - memaddr)\n\t                / page::PAGE_SIZE;\n\n\t// I named this num_kb_pages for future expansion when\n\t// I decide to allow for GiB (2^30) and 2MiB (2^21) page\n\t// sizes. However, the overlapping memory regions are causing\n\t// nightmares.\n\tfor _ in 0..num_kb_pages {\n\t\tpage::map(root, memaddr, memaddr, bits, 0);\n\t\tmemaddr += 1 << 12;\n\t}\n}\n// ///////////////////////////////////\n// / ENTRY POINT\n// ///////////////////////////////////\n#[no_mangle]\nextern \"C\" fn kinit() -> usize {\n\t// We created kinit, which runs in super-duper mode\n\t// 3 called \"machine mode\".\n\t// The job of kinit() is to get us into supervisor mode\n\t// as soon as possible.\n\t// Interrupts are disabled for the duration of kinit()\n\tuart::Uart::new(0x1000_0000).init();\n\tpage::init();\n\tkmem::init();\n\n\t// Map heap allocations\n\tlet root_ptr = kmem::get_page_table();\n\tlet root_u = root_ptr as usize;\n\tlet mut root = unsafe { root_ptr.as_mut().unwrap() };\n\tlet kheap_head = kmem::get_head() as usize;\n\tlet total_pages = kmem::get_num_allocations();\n\tprintln!();\n\tprintln!();\n\tunsafe {\n\t\tprintln!(\"TEXT:   0x{:x} -> 0x{:x}\", TEXT_START, TEXT_END);\n\t\tprintln!(\"RODATA: 0x{:x} -> 0x{:x}\", RODATA_START, RODATA_END);\n\t\tprintln!(\"DATA:   0x{:x} -> 0x{:x}\", DATA_START, DATA_END);\n\t\tprintln!(\"BSS:    0x{:x} -> 0x{:x}\", BSS_START, BSS_END);\n\t\tprintln!(\"STACK:  0x{:x} -> 0x{:x}\", KERNEL_STACK_START, KERNEL_STACK_END);\n\t\tprintln!(\"HEAP:   0x{:x} -> 0x{:x}\", kheap_head, kheap_head + total_pages * 4096);\n\t}\n\tid_map_range(\n\t             &mut root,\n\t             kheap_head,\n\t             kheap_head + total_pages * 4096,\n\t             page::EntryBits::ReadWrite.val(),\n\t);\n\tunsafe {\n\t\t// Map heap descriptors\n\t\tlet num_pages = HEAP_SIZE / page::PAGE_SIZE;\n\t\tid_map_range(&mut root,\n\t\t\t\t\t HEAP_START,\n\t\t\t\t\t HEAP_START + num_pages,\n\t\t\t\t\t page::EntryBits::ReadWrite.val()\n\t\t);\n\t\t// Map executable section\n\t\tid_map_range(\n\t\t             &mut root,\n\t\t             TEXT_START,\n\t\t             TEXT_END,\n\t\t             page::EntryBits::ReadExecute.val(),\n\t\t);\n\t\t// Map rodata section\n\t\t// We put the ROdata section into the text section, so they can\n\t\t// potentially overlap however, we only care that it's read\n\t\t// only.\n\t\tid_map_range(\n\t\t             &mut root,\n\t\t             RODATA_START,\n\t\t             RODATA_END,\n\t\t             page::EntryBits::ReadExecute.val(),\n\t\t);\n\t\t// Map data section\n\t\tid_map_range(\n\t\t             &mut root,\n\t\t             DATA_START,\n\t\t             DATA_END,\n\t\t             page::EntryBits::ReadWrite.val(),\n\t\t);\n\t\t// Map bss section\n\t\tid_map_range(\n\t\t             &mut root,\n\t\t             BSS_START,\n\t\t             BSS_END,\n\t\t             page::EntryBits::ReadWrite.val(),\n\t\t);\n\t\t// Map kernel stack\n\t\tid_map_range(\n\t\t             &mut root,\n\t\t             KERNEL_STACK_START,\n\t\t             KERNEL_STACK_END,\n\t\t             page::EntryBits::ReadWrite.val(),\n\t\t);\n\t}\n\n\t// UART\n\tpage::map(\n\t          &mut root,\n\t          0x1000_0000,\n\t          0x1000_0000,\n\t          page::EntryBits::ReadWrite.val(),\n\t\t\t  0\n\t);\n\n\t// CLINT\n\t//  -> MSIP\n\tpage::map(\n\t          &mut root,\n\t          0x0200_0000,\n\t          0x0200_0000,\n\t          page::EntryBits::ReadWrite.val(),\n\t\t\t  0\n\t);\n\t//  -> MTIMECMP\n\tpage::map(\n\t          &mut root,\n\t          0x0200_b000,\n\t          0x0200_b000,\n\t          page::EntryBits::ReadWrite.val(),\n\t\t\t  0\n\t);\n\t//  -> MTIME\n\tpage::map(\n\t          &mut root,\n\t          0x0200_c000,\n\t          0x0200_c000,\n\t          page::EntryBits::ReadWrite.val(),\n\t\t\t  0\n\t);\n\t// PLIC\n\tid_map_range(\n\t             &mut root,\n\t             0x0c00_0000,\n\t             0x0c00_2000,\n\t             page::EntryBits::ReadWrite.val(),\n\t);\n\tid_map_range(\n\t             &mut root,\n\t             0x0c20_0000,\n\t             0x0c20_8000,\n\t             page::EntryBits::ReadWrite.val(),\n\t);\n\tpage::print_page_allocations();\n\t// The following shows how we're going to walk to translate a virtual\n\t// address into a physical address. We will use this whenever a user\n\t// space application requires services. Since the user space application\n\t// only knows virtual addresses, we have to translate silently behind\n\t// the scenes.\n\tlet p = 0x8005_7000 as usize;\n\tlet m = page::virt_to_phys(&root, p).unwrap_or(0);\n\tprintln!(\"Walk 0x{:x} = 0x{:x}\", p, m);\n\t// When we return from here, we'll go back to boot.S and switch into\n\t// supervisor mode We will return the SATP register to be written when\n\t// we return. root_u is the root page table's address. When stored into\n\t// the SATP register, this is divided by 4 KiB (right shift by 12 bits).\n\t// We enable the MMU by setting mode 8. Bits 63, 62, 61, 60 determine\n\t// the mode. \n\t// 0 = Bare (no translation)\n\t// 8 = Sv39\n\t// 9 = Sv48\n\tunsafe {\n\t\t// We have to store the kernel's table. The tables will be moved back\n\t\t// and forth between the kernel's table and user applicatons' tables.\n\t\tKERNEL_TABLE = root_u;\n\t}\n\t// table / 4096    Sv39 mode\n\t(root_u >> 12)  | (8 << 60)\n}\n\n#[no_mangle]\nextern \"C\" fn kmain() {\n\t// kmain() starts in supervisor mode. So, we should have the trap\n\t// vector setup and the MMU turned on when we get here.\n\n\t// We initialized my_uart in machine mode under kinit for debugging\n\t// prints, but this just grabs a pointer to it.\n\tlet mut my_uart = uart::Uart::new(0x1000_0000);\n\t// Create a new scope so that we can test the global allocator and\n\t// deallocator\n\t{\n\t\t// We have the global allocator, so let's see if that works!\n\t\tlet k = Box::<u32>::new(100);\n\t\tprintln!(\"Boxed value = {}\", *k);\n\t\tkmem::print_table();\n\t\t// The following comes from the Rust documentation:\n\t\t// some bytes, in a vector\n\t\tlet sparkle_heart = vec![240, 159, 146, 150];\n\t\t// We know these bytes are valid, so we'll use `unwrap()`.\n\t\tlet sparkle_heart = String::from_utf8(sparkle_heart).unwrap();\n\t\tprintln!(\"String = {}\", sparkle_heart);\n\t}\n\t// If we get here, the Box, vec, and String should all be freed since\n\t// they go out of scope. This calls their \"Drop\" trait.\n\t// Now see if we can read stuff:\n\t// Usually we can use #[test] modules in Rust, but it would convolute\n\t// the task at hand, and it requires us to create the testing harness\n\t// since the embedded testing system is part of the \"std\" library.\n\tloop {\n\t\tif let Some(c) = my_uart.get() {\n\t\t\tmatch c {\n\t\t\t\t8 => {\n\t\t\t\t\t// This is a backspace, so we\n\t\t\t\t\t// essentially have to write a space and\n\t\t\t\t\t// backup again:\n\t\t\t\t\tprint!(\"{} {}\", 8 as char, 8 as char);\n\t\t\t\t},\n\t\t\t\t10 | 13 => {\n\t\t\t\t\t// Newline or carriage-return\n\t\t\t\t\tprintln!();\n\t\t\t\t},\n\t\t\t\t_ => {\n\t\t\t\t\tprint!(\"{}\", c as char);\n\t\t\t\t},\n\t\t\t}\n\t\t}\n\t}\n}\n\n// ///////////////////////////////////\n// / RUST MODULES\n// ///////////////////////////////////\n\npub mod kmem;\npub mod page;\npub mod uart;\n"
  },
  {
    "path": "risc_v/chapters/ch3/src/page.rs",
    "content": "// page.rs\n// Memory routines\n// Stephen Marz\n// 6 October 2019\n\nuse core::{mem::size_of, ptr::null_mut};\n\n// ////////////////////////////////\n// // Allocation routines\n// ////////////////////////////////\nextern \"C\" {\n\tstatic HEAP_START: usize;\n\tstatic HEAP_SIZE: usize;\n}\n\n// We will use ALLOC_START to mark the start of the actual\n// memory we can dish out.\nstatic mut ALLOC_START: usize = 0;\nconst PAGE_ORDER: usize = 12;\npub const PAGE_SIZE: usize = 1 << 12;\n\n/// Align (set to a multiple of some power of two)\n/// This takes an order which is the exponent to 2^order\n/// Therefore, all alignments must be made as a power of two.\n/// This function always rounds up.\npub const fn align_val(val: usize, order: usize) -> usize {\n\tlet o = (1usize << order) - 1;\n\t(val + o) & !o\n}\n\n#[repr(u8)]\npub enum PageBits {\n\tEmpty = 0,\n\tTaken = 1 << 0,\n\tLast = 1 << 1,\n}\n\nimpl PageBits {\n\t// We convert PageBits to a u8 a lot, so this is\n\t// for convenience.\n\tpub fn val(self) -> u8 {\n\t\tself as u8\n\t}\n}\n\n// Each page is described by the Page structure. Linux does this\n// as well, where each 4096-byte chunk of memory has a structure\n// associated with it. However, there structure is much larger.\npub struct Page {\n\tflags: u8,\n}\n\nimpl Page {\n\t// If this page has been marked as the final allocation,\n\t// this function returns true. Otherwise, it returns false.\n\tpub fn is_last(&self) -> bool {\n\t\tif self.flags & PageBits::Last.val() != 0 {\n\t\t\ttrue\n\t\t}\n\t\telse {\n\t\t\tfalse\n\t\t}\n\t}\n\n\t// If the page is marked as being taken (allocated), then\n\t// this function returns true. Otherwise, it returns false.\n\tpub fn is_taken(&self) -> bool {\n\t\tif self.flags & PageBits::Taken.val() != 0 {\n\t\t\ttrue\n\t\t}\n\t\telse {\n\t\t\tfalse\n\t\t}\n\t}\n\n\t// This is the opposite of is_taken().\n\tpub fn is_free(&self) -> bool {\n\t\t!self.is_taken()\n\t}\n\n\t// Clear the Page structure and all associated allocations.\n\tpub fn clear(&mut self) {\n\t\tself.flags = PageBits::Empty.val();\n\t}\n\n\t// Set a certain flag. We ran into trouble here since PageBits\n\t// is an enumeration and we haven't implemented the BitOr Trait\n\t// on it.\n\tpub fn set_flag(&mut self, flag: PageBits) {\n\t\tself.flags |= flag.val();\n\t}\n\n\tpub fn clear_flag(&mut self, flag: PageBits) {\n\t\tself.flags &= !(flag.val());\n\t}\n}\n\n/// Initialize the allocation system. There are several ways that we can\n/// implement the page allocator:\n/// 1. Free list (singly linked list where it starts at the first free\n/// allocation) 2. Bookkeeping list (structure contains a taken and length)\n/// 3. Allocate one Page structure per 4096 bytes (this is what I chose)\n/// 4. Others\npub fn init() {\n\tunsafe {\n\t\tlet num_pages = HEAP_SIZE / PAGE_SIZE;\n\t\tlet ptr = HEAP_START as *mut Page;\n\t\t// Clear all pages to make sure that they aren't accidentally\n\t\t// taken\n\t\tfor i in 0..num_pages {\n\t\t\t(*ptr.add(i)).clear();\n\t\t}\n\t\t// Determine where the actual useful memory starts. This will be\n\t\t// after all Page structures. We also must align the ALLOC_START\n\t\t// to a page-boundary (PAGE_SIZE = 4096). ALLOC_START =\n\t\t// (HEAP_START + num_pages * size_of::<Page>() + PAGE_SIZE - 1)\n\t\t// & !(PAGE_SIZE - 1);\n\t\tALLOC_START = align_val(\n\t\t                        HEAP_START\n\t\t                        + num_pages * size_of::<Page,>(),\n\t\t                        PAGE_ORDER,\n\t\t);\n\t}\n}\n\n/// Allocate a page or multiple pages\n/// pages: the number of PAGE_SIZE pages to allocate\npub fn alloc(pages: usize) -> *mut u8 {\n\t// We have to find a contiguous allocation of pages\n\tassert!(pages > 0);\n\tunsafe {\n\t\t// We create a Page structure for each page on the heap. We\n\t\t// actually might have more since HEAP_SIZE moves and so does\n\t\t// the size of our structure, but we'll only waste a few bytes.\n\t\tlet num_pages = HEAP_SIZE / PAGE_SIZE;\n\t\tlet ptr = HEAP_START as *mut Page;\n\t\tfor i in 0..num_pages - pages {\n\t\t\tlet mut found = false;\n\t\t\t// Check to see if this Page is free. If so, we have our\n\t\t\t// first candidate memory address.\n\t\t\tif (*ptr.add(i)).is_free() {\n\t\t\t\t// It was FREE! Yay!\n\t\t\t\tfound = true;\n\t\t\t\tfor j in i..i + pages {\n\t\t\t\t\t// Now check to see if we have a\n\t\t\t\t\t// contiguous allocation for all of the\n\t\t\t\t\t// request pages. If not, we should\n\t\t\t\t\t// check somewhere else.\n\t\t\t\t\tif (*ptr.add(j)).is_taken() {\n\t\t\t\t\t\tfound = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// We've checked to see if there are enough contiguous\n\t\t\t// pages to form what we need. If we couldn't, found\n\t\t\t// will be false, otherwise it will be true, which means\n\t\t\t// we've found valid memory we can allocate.\n\t\t\tif found {\n\t\t\t\tfor k in i..i + pages - 1 {\n\t\t\t\t\t(*ptr.add(k)).set_flag(PageBits::Taken);\n\t\t\t\t}\n\t\t\t\t// The marker for the last page is\n\t\t\t\t// PageBits::Last This lets us know when we've\n\t\t\t\t// hit the end of this particular allocation.\n\t\t\t\t(*ptr.add(i+pages-1)).set_flag(PageBits::Taken);\n\t\t\t\t(*ptr.add(i+pages-1)).set_flag(PageBits::Last);\n\t\t\t\t// The Page structures themselves aren't the\n\t\t\t\t// useful memory. Instead, there is 1 Page\n\t\t\t\t// structure per 4096 bytes starting at\n\t\t\t\t// ALLOC_START.\n\t\t\t\treturn (ALLOC_START + PAGE_SIZE * i)\n\t\t\t\t       as *mut u8;\n\t\t\t}\n\t\t}\n\t}\n\n\t// If we get here, that means that no contiguous allocation was\n\t// found.\n\tnull_mut()\n}\n\n/// Allocate and zero a page or multiple pages\n/// pages: the number of pages to allocate\n/// Each page is PAGE_SIZE which is calculated as 1 << PAGE_ORDER\n/// On RISC-V, this typically will be 4,096 bytes.\npub fn zalloc(pages: usize) -> *mut u8 {\n\t// Allocate and zero a page.\n\t// First, let's get the allocation\n\tlet ret = alloc(pages);\n\tif !ret.is_null() {\n\t\tlet size = (PAGE_SIZE * pages) / 8;\n\t\tlet big_ptr = ret as *mut u64;\n\t\tfor i in 0..size {\n\t\t\t// We use big_ptr so that we can force an\n\t\t\t// sd (store doubleword) instruction rather than\n\t\t\t// the sb. This means 8x fewer stores than before.\n\t\t\t// Typically we have to be concerned about remaining\n\t\t\t// bytes, but fortunately 4096 % 8 = 0, so we\n\t\t\t// won't have any remaining bytes.\n\t\t\tunsafe {\n\t\t\t\t(*big_ptr.add(i)) = 0;\n\t\t\t}\n\t\t}\n\t}\n\tret\n}\n\n/// Deallocate a page by its pointer\n/// The way we've structured this, it will automatically coalesce\n/// contiguous pages.\npub fn dealloc(ptr: *mut u8) {\n\t// Make sure we don't try to free a null pointer.\n\tassert!(!ptr.is_null());\n\tunsafe {\n\t\tlet addr =\n\t\t\tHEAP_START + (ptr as usize - ALLOC_START) / PAGE_SIZE;\n\t\t// Make sure that the address makes sense. The address we\n\t\t// calculate here is the page structure, not the HEAP address!\n\t\tassert!(addr >= HEAP_START && addr < HEAP_START + HEAP_SIZE);\n\t\tlet mut p = addr as *mut Page;\n\t\t// Keep clearing pages until we hit the last page.\n\t\twhile (*p).is_taken() && !(*p).is_last() {\n\t\t\t(*p).clear();\n\t\t\tp = p.add(1);\n\t\t}\n\t\t// If the following assertion fails, it is most likely\n\t\t// caused by a double-free.\n\t\tassert!(\n\t\t        (*p).is_last() == true,\n\t\t        \"Possible double-free detected! (Not taken found \\\n\t\t         before last)\"\n\t\t);\n\t\t// If we get here, we've taken care of all previous pages and\n\t\t// we are on the last page.\n\t\t(*p).clear();\n\t}\n}\n\n/// Print all page allocations\n/// This is mainly used for debugging.\npub fn print_page_allocations() {\n\tunsafe {\n\t\tlet num_pages = HEAP_SIZE / PAGE_SIZE;\n\t\tlet mut beg = HEAP_START as *const Page;\n\t\tlet end = beg.add(num_pages);\n\t\tlet alloc_beg = ALLOC_START;\n\t\tlet alloc_end = ALLOC_START + num_pages * PAGE_SIZE;\n\t\tprintln!();\n\t\tprintln!(\n\t\t         \"PAGE ALLOCATION TABLE\\nMETA: {:p} -> {:p}\\nPHYS: \\\n\t\t          0x{:x} -> 0x{:x}\",\n\t\t         beg, end, alloc_beg, alloc_end\n\t\t);\n\t\tprintln!(\"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\");\n\t\tlet mut num = 0;\n\t\twhile beg < end {\n\t\t\tif (*beg).is_taken() {\n\t\t\t\tlet start = beg as usize;\n\t\t\t\tlet memaddr = ALLOC_START\n\t\t\t\t              + (start - HEAP_START)\n\t\t\t\t                * PAGE_SIZE;\n\t\t\t\tprint!(\"0x{:x} => \", memaddr);\n\t\t\t\tloop {\n\t\t\t\t\tnum += 1;\n\t\t\t\t\tif (*beg).is_last() {\n\t\t\t\t\t\tlet end = beg as usize;\n\t\t\t\t\t\tlet memaddr = ALLOC_START\n\t\t\t\t\t\t              + (end\n\t\t\t\t\t\t                 - HEAP_START)\n\t\t\t\t\t\t                * PAGE_SIZE\n\t\t\t\t\t\t              + PAGE_SIZE - 1;\n\t\t\t\t\t\tprint!(\n\t\t\t\t\t\t       \"0x{:x}: {:>3} page(s)\",\n\t\t\t\t\t\t       memaddr,\n\t\t\t\t\t\t       (end - start + 1)\n\t\t\t\t\t\t);\n\t\t\t\t\t\tprintln!(\".\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tbeg = beg.add(1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbeg = beg.add(1);\n\t\t}\n\t\tprintln!(\"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\");\n\t\tprintln!(\n\t\t         \"Allocated: {:>5} pages ({:>9} bytes).\",\n\t\t         num,\n\t\t         num * PAGE_SIZE\n\t\t);\n\t\tprintln!(\n\t\t         \"Free     : {:>5} pages ({:>9} bytes).\",\n\t\t         num_pages - num,\n\t\t         (num_pages - num) * PAGE_SIZE\n\t\t);\n\t\tprintln!();\n\t}\n}\n\n// ////////////////////////////////\n// // MMU Routines\n// ////////////////////////////////\n\n// Represent (repr) our entry bits as\n// unsigned 64-bit integers.\n#[repr(i64)]\n#[derive(Copy, Clone)]\npub enum EntryBits {\n\tNone = 0,\n\tValid = 1 << 0,\n\tRead = 1 << 1,\n\tWrite = 1 << 2,\n\tExecute = 1 << 3,\n\tUser = 1 << 4,\n\tGlobal = 1 << 5,\n\tAccess = 1 << 6,\n\tDirty = 1 << 7,\n\n\t// Convenience combinations\n\tReadWrite = 1 << 1 | 1 << 2,\n\tReadExecute = 1 << 1 | 1 << 3,\n\tReadWriteExecute = 1 << 1 | 1 << 2 | 1 << 3,\n\n\t// User Convenience Combinations\n\tUserReadWrite = 1 << 1 | 1 << 2 | 1 << 4,\n\tUserReadExecute = 1 << 1 | 1 << 3 | 1 << 4,\n\tUserReadWriteExecute = 1 << 1 | 1 << 2 | 1 << 3 | 1 << 4,\n}\n\n// Helper functions to convert the enumeration\n// into an i64, which is what our page table\n// entries will be.\nimpl EntryBits {\n\tpub fn val(self) -> i64 {\n\t\tself as i64\n\t}\n}\n\n// A single entry. We're using an i64 so that\n// this will sign-extend rather than zero-extend\n// since RISC-V requires that the reserved sections\n// take on the most significant bit.\npub struct Entry {\n\tpub entry: i64,\n}\n\n// The Entry structure describes one of the 512 entries per table, which is\n// described in the RISC-V privileged spec Figure 4.18.\nimpl Entry {\n\tpub fn is_valid(&self) -> bool {\n\t\tself.get_entry() & EntryBits::Valid.val() != 0\n\t}\n\n\t// The first bit (bit index #0) is the V bit for\n\t// valid.\n\tpub fn is_invalid(&self) -> bool {\n\t\t!self.is_valid()\n\t}\n\n\t// A leaf has one or more RWX bits set\n\tpub fn is_leaf(&self) -> bool {\n\t\tself.get_entry() & 0xe != 0\n\t}\n\n\tpub fn is_branch(&self) -> bool {\n\t\t!self.is_leaf()\n\t}\n\n\tpub fn set_entry(&mut self, entry: i64) {\n\t\tself.entry = entry;\n\t}\n\n\tpub fn get_entry(&self) -> i64 {\n\t\tself.entry\n\t}\n}\n\n// Table represents a single table, which contains 512 (2^9), 64-bit entries.\npub struct Table {\n\tpub entries: [Entry; 512],\n}\n\nimpl Table {\n\tpub fn len() -> usize {\n\t\t512\n\t}\n}\n\n/// Map a virtual address to a physical address using 4096-byte page\n/// size.\n/// root: a mutable reference to the root Table\n/// vaddr: The virtual address to map\n/// paddr: The physical address to map\n/// bits: An OR'd bitset containing the bits the leaf should have.\n///       The bits should contain only the following:\n///          Read, Write, Execute, User, and/or Global\n///       The bits MUST include one or more of the following:\n///          Read, Write, Execute\n///       The valid bit automatically gets added.\npub fn map(root: &mut Table, vaddr: usize, paddr: usize, bits: i64, level: usize) {\n\t// Make sure that Read, Write, or Execute have been provided\n\t// otherwise, we'll leak memory and always create a page fault.\n\tassert!(bits & 0xe != 0);\n\t// Extract out each VPN from the virtual address\n\t// On the virtual address, each VPN is exactly 9 bits,\n\t// which is why we use the mask 0x1ff = 0b1_1111_1111 (9 bits)\n\tlet vpn = [\n\t           // VPN[0] = vaddr[20:12]\n\t           (vaddr >> 12) & 0x1ff,\n\t           // VPN[1] = vaddr[29:21]\n\t           (vaddr >> 21) & 0x1ff,\n\t           // VPN[2] = vaddr[38:30]\n\t           (vaddr >> 30) & 0x1ff,\n\t];\n\n\t// Just like the virtual address, extract the physical address\n\t// numbers (PPN). However, PPN[2] is different in that it stores\n\t// 26 bits instead of 9. Therefore, we use,\n\t// 0x3ff_ffff = 0b11_1111_1111_1111_1111_1111_1111 (26 bits).\n\tlet ppn = [\n\t           // PPN[0] = paddr[20:12]\n\t           (paddr >> 12) & 0x1ff,\n\t           // PPN[1] = paddr[29:21]\n\t           (paddr >> 21) & 0x1ff,\n\t           // PPN[2] = paddr[55:30]\n\t           (paddr >> 30) & 0x3ff_ffff,\n\t];\n\t// We will use this as a floating reference so that we can set\n\t// individual entries as we walk the table.\n\tlet mut v = &mut root.entries[vpn[2]];\n\t// Now, we're going to traverse the page table and set the bits\n\t// properly. We expect the root to be valid, however we're required to\n\t// create anything beyond the root.\n\t// In Rust, we create a range iterator using the .. operator.\n\t// The .rev() will reverse the iteration since we need to start with\n\t// VPN[2] The .. operator is inclusive on start but exclusive on end.\n\t// So, (0..2) will iterate 0 and 1.\n\tfor i in (level..2).rev() {\n\t\tif !v.is_valid() {\n\t\t\t// Allocate a page\n\t\t\tlet page = zalloc(1);\n\t\t\t// The page is already aligned by 4,096, so store it\n\t\t\t// directly The page is stored in the entry shifted\n\t\t\t// right by 2 places.\n\t\t\tv.set_entry(\n\t\t\t            (page as i64 >> 2)\n\t\t\t            | EntryBits::Valid.val(),\n\t\t\t);\n\t\t}\n\t\tlet entry = ((v.get_entry() & !0x3ff) << 2) as *mut Entry;\n\t\tv = unsafe { entry.add(vpn[i]).as_mut().unwrap() };\n\t}\n\t// When we get here, we should be at VPN[0] and v should be pointing to\n\t// our entry.\n\t// The entry structure is Figure 4.18 in the RISC-V Privileged\n\t// Specification\n\tlet entry = (ppn[2] << 28) as i64 |   // PPN[2] = [53:28]\n\t            (ppn[1] << 19) as i64 |   // PPN[1] = [27:19]\n\t\t\t\t(ppn[0] << 10) as i64 |   // PPN[0] = [18:10]\n\t\t\t\tbits |                    // Specified bits, such as User, Read, Write, etc\n\t\t\t\tEntryBits::Valid.val();   // Valid bit\n\t\t\t // Set the entry. V should be set to the correct pointer by the loop\n\t\t\t // above.\n\tv.set_entry(entry);\n}\n\n/// Unmaps and frees all memory associated with a table.\n/// root: The root table to start freeing.\n/// NOTE: This does NOT free root directly. This must be\n/// freed manually.\n/// The reason we don't free the root is because it is\n/// usually embedded into the Process structure.\npub fn unmap(root: &mut Table) {\n\t// Start with level 2\n\tfor lv2 in 0..Table::len() {\n\t\tlet ref entry_lv2 = root.entries[lv2];\n\t\tif entry_lv2.is_valid() && entry_lv2.is_branch() {\n\t\t\t// This is a valid entry, so drill down and free.\n\t\t\tlet memaddr_lv1 = (entry_lv2.get_entry() & !0x3ff) << 2;\n\t\t\tlet table_lv1 = unsafe {\n\t\t\t\t// Make table_lv1 a mutable reference instead of a pointer.\n\t\t\t\t(memaddr_lv1 as *mut Table).as_mut().unwrap()\n\t\t\t};\n\t\t\tfor lv1 in 0..Table::len() {\n\t\t\t\tlet ref entry_lv1 = table_lv1.entries[lv1];\n\t\t\t\tif entry_lv1.is_valid() && entry_lv1.is_branch()\n\t\t\t\t{\n\t\t\t\t\tlet memaddr_lv0 = (entry_lv1.get_entry()\n\t\t\t\t\t                   & !0x3ff) << 2;\n\t\t\t\t\t// The next level is level 0, which\n\t\t\t\t\t// cannot have branches, therefore,\n\t\t\t\t\t// we free here.\n\t\t\t\t\tdealloc(memaddr_lv0 as *mut u8);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdealloc(memaddr_lv1 as *mut u8);\n\t\t}\n\t}\n}\n\n/// Walk the page table to convert a virtual address to a\n/// physical address.\n/// If a page fault would occur, this returns None\n/// Otherwise, it returns Some with the physical address.\npub fn virt_to_phys(root: &Table, vaddr: usize) -> Option<usize> {\n\t// Walk the page table pointed to by root\n\tlet vpn = [\n\t           // VPN[0] = vaddr[20:12]\n\t           (vaddr >> 12) & 0x1ff,\n\t           // VPN[1] = vaddr[29:21]\n\t           (vaddr >> 21) & 0x1ff,\n\t           // VPN[2] = vaddr[38:30]\n\t           (vaddr >> 30) & 0x1ff,\n\t];\n\n\tlet mut v = &root.entries[vpn[2]];\n\tfor i in (0..=2).rev() {\n\t\tif v.is_invalid() {\n\t\t\t// This is an invalid entry, page fault.\n\t\t\tbreak;\n\t\t}\n\t\telse if v.is_leaf() {\n\t\t\t// According to RISC-V, a leaf can be at any level.\n\n\t\t\t// The offset mask masks off the PPN. Each PPN is 9\n\t\t\t// bits and they start at bit #12. So, our formula\n\t\t\t// 12 + i * 9\n\t\t\tlet off_mask = (1 << (12 + i * 9)) - 1;\n\t\t\tlet vaddr_pgoff = vaddr & off_mask;\n\t\t\tlet addr = ((v.get_entry() << 2) as usize) & !off_mask;\n\t\t\treturn Some(addr | vaddr_pgoff);\n\t\t}\n\t\t// Set v to the next entry which is pointed to by this\n\t\t// entry. However, the address was shifted right by 2 places\n\t\t// when stored in the page table entry, so we shift it left\n\t\t// to get it back into place.\n\t\tlet entry = ((v.get_entry() & !0x3ff) << 2) as *const Entry;\n\t\t// We do i - 1 here, however we should get None or Some() above\n\t\t// before we do 0 - 1 = -1.\n\t\tv = unsafe { entry.add(vpn[i - 1]).as_ref().unwrap() };\n\t}\n\n\t// If we get here, we've exhausted all valid tables and haven't\n\t// found a leaf.\n\tNone\n}\n"
  },
  {
    "path": "risc_v/chapters/ch3/src/uart.rs",
    "content": "// uart.rs\n// UART routines and driver\n\nuse core::convert::TryInto;\nuse core::fmt::Write;\nuse core::fmt::Error;\n\npub struct Uart {\n\tbase_address: usize,\n}\n\nimpl Write for Uart {\n\tfn write_str(&mut self, out: &str) -> Result<(), Error> {\n\t\tfor c in out.bytes() {\n\t\t\tself.put(c);\n\t\t}\n\t\tOk(())\n\t}\n}\n\nimpl Uart {\n\tpub fn new(base_address: usize) -> Self {\n\t\tUart {\n\t\t\tbase_address\n\t\t}\n\t}\n\n\tpub fn init(&mut self) {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\t// First, set the word length, which\n\t\t\t// are bits 0 and 1 of the line control register (LCR)\n\t\t\t// which is at base_address + 3\n\t\t\t// We can easily write the value 3 here or 0b11, but I'm\n\t\t\t// extending it so that it is clear we're setting two individual\n\t\t\t// fields\n\t\t\t//             Word 0     Word 1\n\t\t\t//             ~~~~~~     ~~~~~~\n            let lcr: u8 = (1 << 0) | (1 << 1);\n\t\t\tptr.add(3).write_volatile(lcr);\n\n\t\t\t// Now, enable the FIFO, which is bit index 0 of the FIFO\n\t\t\t// control register (FCR at offset 2).\n\t\t\t// Again, we can just write 1 here, but when we use left shift,\n\t\t\t// it's easier to see that we're trying to write bit index #0.\n\t\t\tptr.add(2).write_volatile(1 << 0);\n\n\t\t\t// Enable receiver buffer interrupts, which is at bit index\n\t\t\t// 0 of the interrupt enable register (IER at offset 1).\n\t\t\tptr.add(1).write_volatile(1 << 0);\n\n\t\t\t// If we cared about the divisor, the code below would set the divisor\n\t\t\t// from a global clock rate of 22.729 MHz (22,729,000 cycles per second)\n\t\t\t// to a signaling rate of 2400 (BAUD). We usually have much faster signalling\n\t\t\t// rates nowadays, but this demonstrates what the divisor actually does.\n\t\t\t// The formula given in the NS16500A specification for calculating the divisor\n\t\t\t// is:\n\t\t\t// divisor = ceil( (clock_hz) / (baud_sps x 16) )\n\t\t\t// So, we substitute our values and get:\n\t\t\t// divisor = ceil( 22_729_000 / (2400 x 16) )\n\t\t\t// divisor = ceil( 22_729_000 / 38_400 )\n\t\t\t// divisor = ceil( 591.901 ) = 592\n\n\t\t\t// The divisor register is two bytes (16 bits), so we need to split the value\n\t\t\t// 592 into two bytes. Typically, we would calculate this based on measuring\n\t\t\t// the clock rate, but again, for our purposes [qemu], this doesn't really do\n\t\t\t// anything.\n\t\t\tlet divisor: u16 = 592;\n\t\t\tlet divisor_least: u8 = (divisor & 0xff).try_into().unwrap();\n\t\t\tlet divisor_most:  u8 = (divisor >> 8).try_into().unwrap();\n\n\t\t\t// Notice that the divisor register DLL (divisor latch least) and DLM (divisor latch most)\n\t\t\t// have the same base address as the receiver/transmitter and the interrupt enable register.\n\t\t\t// To change what the base address points to, we open the \"divisor latch\" by writing 1 into\n\t\t\t// the Divisor Latch Access Bit (DLAB), which is bit index 7 of the Line Control Register (LCR)\n\t\t\t// which is at base_address + 3.\n\t\t\tptr.add(3).write_volatile(lcr | 1 << 7);\n\n\t\t\t// Now, base addresses 0 and 1 point to DLL and DLM, respectively.\n\t\t\t// Put the lower 8 bits of the divisor into DLL\n\t\t\tptr.add(0).write_volatile(divisor_least);\n\t\t\tptr.add(1).write_volatile(divisor_most);\n\n\t\t\t// Now that we've written the divisor, we never have to touch this again. In hardware, this\n\t\t\t// will divide the global clock (22.729 MHz) into one suitable for 2,400 signals per second.\n\t\t\t// So, to once again get access to the RBR/THR/IER registers, we need to close the DLAB bit\n\t\t\t// by clearing it to 0.\n\t\t\tptr.add(3).write_volatile(lcr);\n\t\t}\n\t}\n\n\tpub fn put(&mut self, c: u8) {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\tptr.add(0).write_volatile(c);\n\t\t}\n\t}\n\n\tpub fn get(&mut self) -> Option<u8> {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\tif ptr.add(5).read_volatile() & 1 == 0 {\n\t\t\t\t// The DR bit is 0, meaning no data\n\t\t\t\tNone\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// The DR bit is 1, meaning data!\n\t\t\t\tSome(ptr.add(0).read_volatile())\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "risc_v/chapters/ch4/.cargo/config",
    "content": "[build]\ntarget = \"riscv64gc-unknown-none-elf\"\n\n[target.riscv64gc-unknown-none-elf]\nlinker = \"riscv64-unknown-linux-gnu-gcc\"\n"
  },
  {
    "path": "risc_v/chapters/ch4/.gitignore",
    "content": "os.elf\ntarget/*\nCargo.lock\nhdd.dsk\n"
  },
  {
    "path": "risc_v/chapters/ch4/Cargo.toml",
    "content": "[package]\nname = \"sos\"\nversion = \"0.1.0\"\nauthors = [\"Stephen Marz <stephen.marz@utk.edu>\"]\nedition = \"2018\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[lib]\ncrate-type = [\"staticlib\"]\n\n[dependencies]\n"
  },
  {
    "path": "risc_v/chapters/ch4/Makefile",
    "content": "#####\n## BUILD\n#####\nCC=riscv64-unknown-linux-gnu-gcc\nCFLAGS=-Wall -Wextra -pedantic -Wextra -O0 -g\nCFLAGS+=-static -ffreestanding -nostdlib -fno-rtti -fno-exceptions\nCFLAGS+=-march=rv64gc -mabi=lp64\nINCLUDES=\nLINKER_SCRIPT=-Tsrc/lds/virt.lds\nTYPE=debug\nRUST_TARGET=./target/riscv64gc-unknown-none-elf/$(TYPE)\nLIBS=-L$(RUST_TARGET)\nSOURCES_ASM=$(wildcard src/asm/*.S)\nLIB=-lsos -lgcc\nOUT=os.elf\n\n#####\n## QEMU\n#####\nQEMU=qemu-system-riscv64\nMACH=virt\nCPU=rv64\nCPUS=4\nMEM=128M\nDISK=hdd.dsk\n# DRIVE= -drive if=none,format=raw,file=$(DISK),id=foo -device virtio-blk-device,scsi=off,drive=foo\nDRIVE=\n\n\nall:\n\tcargo build\n\t$(CC) $(CFLAGS) $(LINKER_SCRIPT) $(INCLUDES) -o $(OUT) $(SOURCES_ASM) $(LIBS) $(LIB)\n\t\nrun: all\n\t$(QEMU) -machine $(MACH) -cpu $(CPU) -smp $(CPUS) -m $(MEM) $(DRIVE) -nographic -serial mon:stdio -bios none -kernel $(OUT)\n\n\n.PHONY: clean\nclean:\n\tcargo clean\n\trm -f $(OUT)\n"
  },
  {
    "path": "risc_v/chapters/ch4/make_hdd.sh",
    "content": "#!/bin/sh\n\ndd if=/dev/zero of=hdd.dsk bs=1M count=32\n"
  },
  {
    "path": "risc_v/chapters/ch4/src/asm/boot.S",
    "content": "# boot.S\n# bootloader for SoS\n# Stephen Marz\n# 8 February 2019\n\n# Disable generation of compressed instructions.\n.option norvc\n\n# Define a .text.init section. The .text.init is put at the\n# starting address so that the entry _start is put at the RISC-V\n# address 0x8000_0000.\n.section .text.init\n\n# Execution starts here.\n.global _start\n_start:\n\n\t# Disable linker instruction relaxation for the `la` instruction below.\n\t# This disallows the assembler from assuming that `gp` is already initialized.\n\t# This causes the value stored in `gp` to be calculated from `pc`.\n\t# The job of the global pointer is to give the linker the ability to address\n\t# memory relative to GP instead of as an absolute address.\n.option push\n.option norelax\n\tla\t\tgp, _global_pointer\n.option pop\n\t# SATP should be zero, but let's make sure. Each HART has its own\n\t# SATP register.\n\tcsrw\tsatp, zero\n\t# Any hardware threads (hart) that are not bootstrapping\n\t# need to wait for an IPI\n\tcsrr\tt0, mhartid\n\tbnez\tt0, 3f\n\n\t# Set all bytes in the BSS section to zero.\n\tla \t\ta0, _bss_start\n\tla\t\ta1, _bss_end\n\tbgeu\ta0, a1, 2f\n1:\n\tsd\t\tzero, (a0)\n\taddi\ta0, a0, 8\n\tbltu\ta0, a1, 1b\n2:\n\t# The stack grows from bottom to top, so we put the stack pointer\n\t# to the very end of the stack range.\n\tla\t\tsp, _stack_end\n\t# Setting `mstatus` register:\n\t# 0b01 << 11: Machine's previous protection mode is 2 (MPP=2).\n\tli\t\tt0, 0b11 << 11\n\tcsrw\tmstatus, t0\n\t# Do not allow interrupts while running kinit\n\tcsrw\tmie, zero\n\t# Machine's exception program counter (MEPC) is set to `kinit`.\n\tla\t\tt1, kinit\n\tcsrw\tmepc, t1\n\t# Set the return address to get us into supervisor mode\n\tla\t\tra, 2f\n\t# We use mret here so that the mstatus register is properly updated.\n\tmret\n2:\n\t# We set the return address (ra above) to this label. When kinit() is finished\n\t# in Rust, it will return here.\n\n\t# Setting `mstatus` (supervisor status) register:\n\t# 0b01 << 11 : Previous protection mode is 1 (MPP=01 [Supervisor]).\n\t# 1 << 7     : Previous machine interrupt-enable bit is 1 (MPIE=1 [Enabled])\n\t# 1 << 5     : Previous interrupt-enable bit is 1 (SPIE=1 [Enabled]).\n\t# We set the \"previous\" bits because the mret will write the current bits\n\t# with the previous bits.\n\tli\t\tt0, (0b01 << 11) | (1 << 7) | (1 << 5)\n\tcsrw\tmstatus, t0\n\t# Machine's trap vector base address is set to `m_trap_vector`, for\n\t# \"machine\" trap vector.\n\tla\t\tt2, m_trap_vector\n\tcsrw\tmtvec, t2\n\t# Setting `stvec` (supervisor trap vector) register:\n\t# Essentially this is a function pointer, but the last two bits can be 00 or 01\n\t# 00        : All exceptions set pc to BASE\n\t# 01        : Asynchronous interrupts set pc to BASE + 4 x scause\n\t# la\t\tt3, s_trap_vector\n\t# csrw\tstvec, t3\n\t# Jump to kmain. We put the MPP = 01 for supervisor mode, so after\n\t# mret, we will jump to kmain in supervisor mode.\n\tla\t\tt1, kmain\n\tcsrw\tmepc, t1\n\t# Setting `sie` (supervisor interrupt enable) register:\n\t# This register takes the same bits as mideleg\n\t# 1 << 1    : Supervisor software interrupt enable (SSIE=1 [Enabled])\n\t# 1 << 5    : Supervisor timer interrupt enable (STIE=1 [Enabled])\n\t# 1 << 9    : Supervisor external interrupt enable (SEIE=1 [Enabled])\n\t# 0xaaa = MEIP/SEIP and MTIP/STIP and MSIP/SSIP\n\tli\t\tt2, 0x888\n\tcsrw\tmie, t2\n\tmret\n3:\n\n\t# Parked harts go here. We need to set these\n\t# to only awaken if it receives a software interrupt,\n\t# which we're going to call the SIPI (Software Intra-Processor Interrupt).\n\t# We call the SIPI by writing the software interrupt into the Core Local Interruptor (CLINT)\n\t# Which is calculated by: base_address + hart * 4\n\t# where base address is 0x0200_0000 (MMIO CLINT base address)\n\t# We only use additional harts to run user-space programs, although this may\n\t# change.\n\n\t# We divide up the stack so the harts aren't clobbering one another.\n\tla\t\tsp, _stack_end\n\tli\t\tt0, 0x10000\n\tcsrr\ta0, mhartid\n\tmul\t\tt0, t0, a0\n\tsub\t\tsp, sp, t0\n\n\t# The parked harts will be put into machine mode with interrupts enabled.\n\tli\t\tt0, 0b11 << 11 | (1 << 7)\n\tcsrw\tmstatus, t0\n\t# Allow for MSIP (Software interrupt). We will write the MSIP from hart #0 to\n\t# awaken these parked harts.\n\tli\t\tt3, (1 << 3)\n\tcsrw\tmie, t3\n\t# Machine's exception program counter (MEPC) is set to the Rust initialization\n\t# code and waiting loop.\n\tla\t\tt1, kinit_hart\n\tcsrw\tmepc, t1\n\t# Machine's trap vector base address is set to `m_trap_vector`, for\n\t# \"machine\" trap vector. The Rust initialization routines will give each\n\t# hart its own trap frame. We can use the same trap function and distinguish\n\t# between each hart by looking at the trap frame.\n\tla\t\tt2, m_trap_vector\n\tcsrw\tmtvec, t2\n\t# Whenever our hart is done initializing, we want it to return to the waiting\n\t# loop, which is just below mret.\n\tla\t\tra, 4f\n\t# We use mret here so that the mstatus register is properly updated.\n\tmret\n\n4:\n\t# wfi = wait for interrupt. This is a hint to the harts to shut everything needed\n\t# down. However, the RISC-V specification allows for wfi to do nothing. Anyway,\n\t# with QEMU, this will save some CPU!\n\twfi\n\tj\t\t4b\n\n\n"
  },
  {
    "path": "risc_v/chapters/ch4/src/asm/mem.S",
    "content": "// mem.S\n// Importation of linker symbols\n\n.section .rodata\n.global HEAP_START\nHEAP_START: .dword _heap_start\n\n.global HEAP_SIZE\nHEAP_SIZE: .dword _heap_size\n\n.global TEXT_START\nTEXT_START: .dword _text_start\n\n.global TEXT_END\nTEXT_END: .dword _text_end\n\n.global DATA_START\nDATA_START: .dword _data_start\n\n.global DATA_END\nDATA_END: .dword _data_end\n\n.global RODATA_START\nRODATA_START: .dword _rodata_start\n\n.global RODATA_END\nRODATA_END: .dword _rodata_end\n\n.global BSS_START\nBSS_START: .dword _bss_start\n\n.global BSS_END\nBSS_END: .dword _bss_end\n\n.global KERNEL_STACK_START\nKERNEL_STACK_START: .dword _stack_start\n\n.global KERNEL_STACK_END\nKERNEL_STACK_END: .dword _stack_end\n\n\n"
  },
  {
    "path": "risc_v/chapters/ch4/src/asm/trap.S",
    "content": "# trap.S\n# Trap handler and global context\n# Steve Operating System\n# Stephen Marz\n# 24 February 2019\n.option norvc\n.altmacro\n.set NUM_GP_REGS, 32  # Number of registers per context\n.set NUM_FP_REGS, 32\n.set REG_SIZE, 8   # Register size (in bytes)\n.set MAX_CPUS, 8   # Maximum number of CPUs\n\n# Use macros for saving and restoring multiple registers\n.macro save_gp i, basereg=t6\n\tsd\tx\\i, ((\\i)*REG_SIZE)(\\basereg)\n.endm\n.macro load_gp i, basereg=t6\n\tld\tx\\i, ((\\i)*REG_SIZE)(\\basereg)\n.endm\n.macro save_fp i, basereg=t6\n\tfsd\tf\\i, ((NUM_GP_REGS+(\\i))*REG_SIZE)(\\basereg)\n.endm\n.macro load_fp i, basereg=t6\n\tfld\tf\\i, ((NUM_GP_REGS+(\\i))*REG_SIZE)(\\basereg)\n.endm\n\n\n.section .text\n.global m_trap_vector\n# This must be aligned by 4 since the last two bits\n# of the mtvec register do not contribute to the address\n# of this vector.\n.align 4\nm_trap_vector:\n\t# All registers are volatile here, we need to save them\n\t# before we do anything.\n\tcsrrw\tt6, mscratch, t6\n\t# csrrw will atomically swap t6 into mscratch and the old\n\t# value of mscratch into t6. This is nice because we just\n\t# switched values and didn't destroy anything -- all atomically!\n\t# in cpu.rs we have a structure of:\n\t#  32 gp regs\t\t0\n\t#  32 fp regs\t\t256\n\t#  SATP register\t512\n\t#  Trap stack       520\n\t#  CPU HARTID\t\t528\n\t# We use t6 as the temporary register because it is the very\n\t# bottom register (x31)\n\t.set \ti, 1\n\t.rept\t30\n\t\tsave_gp\t%i\n\t\t.set\ti, i+1\n\t.endr\n\n\t# Save the actual t6 register, which we swapped into\n\t# mscratch\n\tmv\t\tt5, t6\n\tcsrr\tt6, mscratch\n\tsave_gp 31, t5\n\n\t# Restore the kernel trap frame into mscratch\n\tcsrw\tmscratch, t5\n\n\t# Get ready to go into Rust (trap.rs)\n\t# We don't want to write into the user's stack or whomever\n\t# messed with us here.\n\tcsrr\ta0, mepc\n\tcsrr\ta1, mtval\n\tcsrr\ta2, mcause\n\tcsrr\ta3, mhartid\n\tcsrr\ta4, mstatus\n\tmv\t\ta5, t5\n\tld\t\tsp, 520(a5)\n\tcall\tm_trap\n\n\t# When we get here, we've returned from m_trap, restore registers\n\t# and return.\n\t# m_trap will return the return address via a0.\n\n\tcsrw\tmepc, a0\n\n\t# Now load the trap frame back into t6\n\tcsrr\tt6, mscratch\n\n\t# Restore all GP registers\n\t.set\ti, 1\n\t.rept\t31\n\t\tload_gp %i\n\t\t.set\ti, i+1\n\t.endr\n\n\t# Since we ran this loop 31 times starting with i = 1,\n\t# the last one loaded t6 back to its original value.\n\n\tmret\n\n\n.global make_syscall\nmake_syscall:\n\tecall\n\tret\n"
  },
  {
    "path": "risc_v/chapters/ch4/src/cpu.rs",
    "content": "// cpu.rs\n// CPU and CPU-related routines\n// Also contains the kernel's trap frame\n// Stephen Marz\n// 14 October 2019\n\nuse core::ptr::null_mut;\n\n#[repr(usize)]\npub enum SatpMode {\n\tOff = 0,\n\tSv39 = 8,\n\tSv48 = 9,\n}\n\n#[repr(C)]\n#[derive(Clone, Copy)]\npub struct TrapFrame {\n\tpub regs:       [usize; 32], // 0 - 255\n\tpub fregs:      [usize; 32], // 256 - 511\n\tpub satp:       usize,       // 512 - 519\n\tpub trap_stack: *mut u8,     // 520\n\tpub hartid:     usize,       // 528\n}\n\nimpl TrapFrame {\n\tpub const fn zero() -> Self {\n\t\tTrapFrame { regs:       [0; 32],\n\t\t            fregs:      [0; 32],\n\t\t            satp:       0,\n\t\t            trap_stack: null_mut(),\n\t\t             hartid:     0, }\n\t}\n}\n\npub static mut KERNEL_TRAP_FRAME: [TrapFrame; 8] =\n\t[TrapFrame::zero(); 8];\n\npub const fn build_satp(mode: SatpMode, asid: usize, addr: usize) -> usize {\n\t(mode as usize) << 60\n\t| (asid & 0xffff) << 44\n\t| (addr >> 12) & 0xff_ffff_ffff\n}\n\npub fn mhartid_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, mhartid\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn mstatus_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tmstatus, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn mstatus_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, mstatus\":\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn stvec_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tstvec, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn stvec_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, stvec\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn mscratch_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tmscratch, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn mscratch_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, mscratch\" : \"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn mscratch_swap(to: usize) -> usize {\n\tunsafe {\n\t\tlet from;\n\t\tasm!(\"csrrw\t$0, mscratch, $1\" : \"=r\"(from) : \"r\"(to));\n\t\tfrom\n\t}\n}\n\npub fn sscratch_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tsscratch, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn sscratch_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, sscratch\" : \"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn sscratch_swap(to: usize) -> usize {\n\tunsafe {\n\t\tlet from;\n\t\tasm!(\"csrrw\t$0, sscratch, $1\" : \"=r\"(from) : \"r\"(to));\n\t\tfrom\n\t}\n}\n\npub fn sepc_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw sepc, $0\" :: \"r\"(val));\n\t}\n}\n\npub fn sepc_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, sepc\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn satp_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw satp, $0\" :: \"r\"(val));\n\t}\n}\n\npub fn satp_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, satp\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn satp_fence(vaddr: usize, asid: usize) {\n\tunsafe {\n\t\tasm!(\"sfence.vma $0, $1\" :: \"r\"(vaddr), \"r\"(asid));\n\t}\n}\n\npub fn satp_fence_asid(asid: usize) {\n\tunsafe {\n\t\tasm!(\"sfence.vma zero, $0\" :: \"r\"(asid));\n\t}\n}\n"
  },
  {
    "path": "risc_v/chapters/ch4/src/kmem.rs",
    "content": "// kmem.rs\n// Sub-page level: malloc-like allocation system\n// Stephen Marz\n// 7 October 2019\n\nuse crate::page::{align_val, zalloc, Table, PAGE_SIZE};\nuse core::{mem::size_of, ptr::null_mut};\n\n#[repr(usize)]\nenum AllocListFlags {\n\tTaken = 1 << 63,\n}\nimpl AllocListFlags {\n\tpub fn val(self) -> usize {\n\t\tself as usize\n\t}\n}\n\nstruct AllocList {\n\tpub flags_size: usize,\n}\nimpl AllocList {\n\tpub fn is_taken(&self) -> bool {\n\t\tself.flags_size & AllocListFlags::Taken.val() != 0\n\t}\n\n\tpub fn is_free(&self) -> bool {\n\t\t!self.is_taken()\n\t}\n\n\tpub fn set_taken(&mut self) {\n\t\tself.flags_size |= AllocListFlags::Taken.val();\n\t}\n\n\tpub fn set_free(&mut self) {\n\t\tself.flags_size &= !AllocListFlags::Taken.val();\n\t}\n\n\tpub fn set_size(&mut self, sz: usize) {\n\t\tlet k = self.is_taken();\n\t\tself.flags_size = sz & !AllocListFlags::Taken.val();\n\t\tif k {\n\t\t\tself.flags_size |= AllocListFlags::Taken.val();\n\t\t}\n\t}\n\n\tpub fn get_size(&self) -> usize {\n\t\tself.flags_size & !AllocListFlags::Taken.val()\n\t}\n}\n\n// This is the head of the allocation. We start here when\n// we search for a free memory location.\nstatic mut KMEM_HEAD: *mut AllocList = null_mut();\n// In the future, we will have on-demand pages\n// so, we need to keep track of our memory footprint to\n// see if we actually need to allocate more.\nstatic mut KMEM_ALLOC: usize = 0;\nstatic mut KMEM_PAGE_TABLE: *mut Table = null_mut();\n\n// These functions are safe helpers around an unsafe\n// operation.\npub fn get_head() -> *mut u8 {\n\tunsafe { KMEM_HEAD as *mut u8 }\n}\n\npub fn get_page_table() -> *mut Table {\n\tunsafe { KMEM_PAGE_TABLE as *mut Table }\n}\n\npub fn get_num_allocations() -> usize {\n\tunsafe { KMEM_ALLOC }\n}\n\n/// Initialize kernel's memory\n/// This is not to be used to allocate memory\n/// for user processes. If that's the case, use\n/// alloc/dealloc from the page crate.\npub fn init() {\n\tunsafe {\n\t\t// Allocate kernel pages (KMEM_ALLOC)\n\t\tKMEM_ALLOC = 512;\n\t\tlet k_alloc = zalloc(KMEM_ALLOC);\n\t\tassert!(!k_alloc.is_null());\n\t\tKMEM_HEAD = k_alloc as *mut AllocList;\n\t\t(*KMEM_HEAD).set_free();\n\t\t(*KMEM_HEAD).set_size(KMEM_ALLOC * PAGE_SIZE);\n\t\tKMEM_PAGE_TABLE = zalloc(1) as *mut Table;\n\t}\n}\n\n/// Allocate sub-page level allocation based on bytes and zero the memory\npub fn kzmalloc(sz: usize) -> *mut u8 {\n\tlet size = align_val(sz, 3);\n\tlet ret = kmalloc(size);\n\n\tif !ret.is_null() {\n\t\tfor i in 0..size {\n\t\t\tunsafe {\n\t\t\t\t(*ret.add(i)) = 0;\n\t\t\t}\n\t\t}\n\t}\n\tret\n}\n\n/// Allocate sub-page level allocation based on bytes\npub fn kmalloc(sz: usize) -> *mut u8 {\n\tunsafe {\n\t\tlet size = align_val(sz, 3) + size_of::<AllocList>();\n\t\tlet mut head = KMEM_HEAD;\n\t\t// .add() uses pointer arithmetic, so we type-cast into a u8\n\t\t// so that we multiply by an absolute size (KMEM_ALLOC *\n\t\t// PAGE_SIZE).\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\n\t\twhile head < tail {\n\t\t\tif (*head).is_free() && size <= (*head).get_size() {\n\t\t\t\tlet chunk_size = (*head).get_size();\n\t\t\t\tlet rem = chunk_size - size;\n\t\t\t\t(*head).set_taken();\n\t\t\t\tif rem > size_of::<AllocList>() {\n\t\t\t\t\tlet next = (head as *mut u8).add(size)\n\t\t\t\t\t           as *mut AllocList;\n\t\t\t\t\t// There is space remaining here.\n\t\t\t\t\t(*next).set_free();\n\t\t\t\t\t(*next).set_size(rem);\n\t\t\t\t\t(*head).set_size(size);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// If we get here, take the entire chunk\n\t\t\t\t\t(*head).set_size(chunk_size);\n\t\t\t\t}\n\t\t\t\treturn head.add(1) as *mut u8;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// If we get here, what we saw wasn't a free\n\t\t\t\t// chunk, move on to the next.\n\t\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t\t       as *mut AllocList;\n\t\t\t}\n\t\t}\n\t}\n\t// If we get here, we didn't find any free chunks--i.e. there isn't\n\t// enough memory for this. TODO: Add on-demand page allocation.\n\tnull_mut()\n}\n\n/// Free a sub-page level allocation\npub fn kfree(ptr: *mut u8) {\n\tunsafe {\n\t\tif !ptr.is_null() {\n\t\t\tlet p = (ptr as *mut AllocList).offset(-1);\n\t\t\tif (*p).is_taken() {\n\t\t\t\t(*p).set_free();\n\t\t\t}\n\t\t\t// After we free, see if we can combine adjacent free\n\t\t\t// spots to see if we can reduce fragmentation.\n\t\t\tcoalesce();\n\t\t}\n\t}\n}\n\n/// Merge smaller chunks into a bigger chunk\npub fn coalesce() {\n\tunsafe {\n\t\tlet mut head = KMEM_HEAD;\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\n\t\twhile head < tail {\n\t\t\tlet next = (head as *mut u8).add((*head).get_size())\n\t\t\t           as *mut AllocList;\n\t\t\tif (*head).get_size() == 0 {\n\t\t\t\t// If this happens, then we have a bad heap\n\t\t\t\t// (double free or something). However, that\n\t\t\t\t// will cause an infinite loop since the next\n\t\t\t\t// pointer will never move beyond the current\n\t\t\t\t// location.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if next >= tail {\n\t\t\t\t// We calculated the next by using the size\n\t\t\t\t// given as get_size(), however this could push\n\t\t\t\t// us past the tail. In that case, the size is\n\t\t\t\t// wrong, hence we break and stop doing what we\n\t\t\t\t// need to do.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (*head).is_free() && (*next).is_free() {\n\t\t\t\t// This means we have adjacent blocks needing to\n\t\t\t\t// be freed. So, we combine them into one\n\t\t\t\t// allocation.\n\t\t\t\t(*head).set_size(\n\t\t\t\t                 (*head).get_size()\n\t\t\t\t                 + (*next).get_size(),\n\t\t\t\t);\n\t\t\t}\n\t\t\t// If we get here, we might've moved. Recalculate new\n\t\t\t// head.\n\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t       as *mut AllocList;\n\t\t}\n\t}\n}\n\n/// For debugging purposes, print the kmem table\npub fn print_table() {\n\tunsafe {\n\t\tlet mut head = KMEM_HEAD;\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\t\twhile head < tail {\n\t\t\tprintln!(\n\t\t\t         \"{:p}: Length = {:<10} Taken = {}\",\n\t\t\t         head,\n\t\t\t         (*head).get_size(),\n\t\t\t         (*head).is_taken()\n\t\t\t);\n\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t       as *mut AllocList;\n\t\t}\n\t}\n}\n\n// ///////////////////////////////////\n// / GLOBAL ALLOCATOR\n// ///////////////////////////////////\n\n// The global allocator allows us to use the data structures\n// in the core library, such as a linked list or B-tree.\n// We want to use these sparingly since we have a coarse-grained\n// allocator.\nuse core::alloc::{GlobalAlloc, Layout};\n\n// The global allocator is a static constant to a global allocator\n// structure. We don't need any members because we're using this\n// structure just to implement alloc and dealloc.\nstruct OsGlobalAlloc;\n\nunsafe impl GlobalAlloc for OsGlobalAlloc {\n\tunsafe fn alloc(&self, layout: Layout) -> *mut u8 {\n\t\t// We align to the next page size so that when\n\t\t// we divide by PAGE_SIZE, we get exactly the number\n\t\t// of pages necessary.\n\t\tkzmalloc(layout.size())\n\t}\n\n\tunsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {\n\t\t// We ignore layout since our allocator uses ptr_start -> last\n\t\t// to determine the span of an allocation.\n\t\tkfree(ptr);\n\t}\n}\n\n#[global_allocator]\n/// Technically, we don't need the {} at the end, but it\n/// reveals that we're creating a new structure and not just\n/// copying a value.\nstatic GA: OsGlobalAlloc = OsGlobalAlloc {};\n\n#[alloc_error_handler]\n/// If for some reason alloc() in the global allocator gets null_mut(),\n/// then we come here. This is a divergent function, so we call panic to\n/// let the tester know what's going on.\npub fn alloc_error(l: Layout) -> ! {\n\tpanic!(\n\t       \"Allocator failed to allocate {} bytes with {}-byte alignment.\",\n\t       l.size(),\n\t       l.align()\n\t);\n}\n"
  },
  {
    "path": "risc_v/chapters/ch4/src/lds/virt.lds",
    "content": "/*\n virt.lds\n Linker script for outputting to RISC-V QEMU \"virt\" machine.\n Stephen Marz\n 6 October 2019\n*/\n\n/*\n  riscv is the name of the architecture that the linker understands\n  for any RISC-V target (64-bit or 32-bit).\n\n  We will further refine this by using -mabi=lp64 and -march=rv64gc\n*/\nOUTPUT_ARCH( \"riscv\" )\n\n/*\nWe're setting our entry point to a symbol\ncalled _start which is inside of boot.S. This\nessentially stores the address of _start as the\n\"entry point\", or where CPU instructions should start\nexecuting.\n\nIn the rest of this script, we are going to place _start\nright at the beginning of 0x8000_0000 because this is where\nthe virtual machine and many RISC-V boards will start executing.\n*/\nENTRY( _start )\n\n/*\nThe MEMORY section will explain that we have \"ram\" that contains\na section that is 'w' (writeable), 'x' (executable), and 'a' (allocatable).\nWe use '!' to invert 'r' (read-only) and 'i' (initialized). We don't want\nour memory to be read-only, and we're stating that it is NOT initialized\nat the beginning.\n\nThe ORIGIN is the memory address 0x8000_0000. If we look at the virt\nspec or the specification for the RISC-V HiFive Unleashed, this is the\nstarting memory address for our code.\n\nSide note: There might be other boot ROMs at different addresses, but\ntheir job is to get to this point.\n\nFinally LENGTH = 128M tells the linker that we have 128 megabyte of RAM.\nThe linker will double check this to make sure everything can fit.\n\nThe HiFive Unleashed has a lot more RAM than this, but for the virtual \nmachine, I went with 128M since I think that's enough RAM for now.\n\nWe can provide other pieces of memory, such as QSPI, or ROM, but we're\ntelling the linker script here that we have one pool of RAM.\n*/\nMEMORY\n{\n  ram   (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M\n}\n\n/*\nPHDRS is short for \"program headers\", which we specify three here:\ntext - CPU instructions (executable sections)\ndata - Global, initialized variables\nbss  - Global, uninitialized variables (all will be set to 0 by boot.S)\n\nThe command PT_LOAD tells the linker that these sections will be loaded\nfrom the file into memory.\n\nWe can actually stuff all of these into a single program header, but by\nsplitting it up into three, we can actually use the other PT_* commands\nsuch as PT_DYNAMIC, PT_INTERP, PT_NULL to tell the linker where to find\nadditional information.\n\nHowever, for our purposes, every section will be loaded from the program\nheaders.\n*/\nPHDRS\n{\n  text PT_LOAD;\n  data PT_LOAD;\n  bss PT_LOAD;\n}\n\n/*\nWe are now going to organize the memory based on which\nsection it is in. In assembly, we can change the section\nwith the \".section\" directive. However, in C++ and Rust,\nCPU instructions go into text, global constants go into\nrodata, global initialized variables go into data, and\nglobal uninitialized variables go into bss.\n*/\nSECTIONS\n{\n  /*\n    The first part of our RAM layout will be the text section.\n\tSince our CPU instructions are here, and our memory starts at\n\t0x8000_0000, we need our entry point to line up here.\n  */\n  .text : {\n\t  /* \n\t    PROVIDE allows me to access a symbol called _text_start so\n\t\tI know where the text section starts in the operating system.\n\t\tThis should not move, but it is here for convenience.\n\t\tThe period '.' tells the linker to set _text_start to the\n\t\tCURRENT location ('.' = current memory location). This current\n\t\tmemory location moves as we add things.\n\t  */\n\n    PROVIDE(_text_start = .);\n\t/*\n\t  We are going to layout all text sections here, starting with \n\t  .text.init. The asterisk in front of the parentheses means to match\n\t  the .text.init section of ANY object file. Otherwise, we can specify\n\t  which object file should contain the .text.init section, for example,\n\t  boot.o(.text.init) would specifically put the .text.init section of\n\t  our bootloader here.\n\n\t  Because we might want to change the name of our files, we'll leave it\n\t  with a *.\n\n\t  Inside the parentheses is the name of the section. I created my own\n\t  called .text.init to make 100% sure that the _start is put right at the\n\t  beginning. The linker will lay this out in the order it receives it:\n\n\t  .text.init first\n\t  all .text sections next\n\t  any .text.* sections last\n\n\t  .text.* means to match anything after .text. If we didn't already specify\n\t  .text.init, this would've matched here. The assembler and linker can place\n\t  things in \"special\" text sections, so we match any we might come across here.\n\t*/\n    *(.text.init) *(.text .text.*)\n\n\t/*\n\t  Again, with PROVIDE, we're providing a readable symbol called _text_end, which is\n\t  set to the memory address AFTER .text.init, .text, and .text.*'s have been added.\n\t*/\n    PROVIDE(_text_end = .);\n\t/*\n\t  The portion after the right brace is in an odd format. However, this is telling the\n\t  linker what memory portion to put it in. We labeled our RAM, ram, with the constraints\n\t  that it is writeable, allocatable, and executable. The linker will make sure with this\n\t  that we can do all of those things.\n\n\t  >ram - This just tells the linker script to put this entire section (.text) into the\n\t         ram region of memory. To my knowledge, the '>' does not mean \"greater than\". Instead,\n\t\t\t it is a symbol to let the linker know we want to put this in ram.\n\n\t  AT>ram - This sets the LMA (load memory address) region to the same thing. LMA is the final\n\t           translation of a VMA (virtual memory address). With this linker script, we're loading\n\t\t\t   everything into its physical location. We'll let the kernel copy and sort out the \n\t\t\t   virtual memory. That's why >ram and AT>ram are continually the same thing.\n\n\t  :text  - This tells the linker script to put this into the :text program header. We've only\n\t           defined three: text, data, and bss. In this case, we're telling the linker script\n\t\t\t   to go into the text section.\n\t*/\n  } >ram AT>ram :text\n   /*\n     The global pointer allows the linker to position global variables and constants into\n\t independent positions relative to the gp (global pointer) register. The globals start\n\t after the text sections and are only relevant to the rodata, data, and bss sections.\n   */\n   PROVIDE(_global_pointer = .);\n   /*\n     Most compilers create a rodata (read only data) section for global constants. However,\n\t we're going to place ours in the text section. We can actually put this in :data, but\n\t since the .text section is read-only, we can place it there.\n\n\t NOTE: This doesn't actually do anything, yet. The actual \"protection\" cannot be done\n\t at link time. Instead, when we program the memory management unit (MMU), we will be\n\t able to choose which bits (R=read, W=write, X=execute) we want each memory segment\n\t to be able to do.\n   */\n  .rodata : {\n    PROVIDE(_rodata_start = .);\n    *(.rodata .rodata.*)\n    PROVIDE(_rodata_end = .);\n\t/*\n\t   Again, we're placing the rodata section in the memory segment \"ram\" and we're putting\n\t   it in the :text program header. We don't have one for rodata anyway.\n\t*/\n  } >ram AT>ram :text\n\n  .data : {\n\t/*\n\t   . = ALIGN(4096) tells the linker to align the current memory location (which is\n\t   0x8000_0000 + text section + rodata section) to 4096 bytes. This is because our paging\n\t   system's resolution is 4,096 bytes or 4 KiB.\n\t*/\n    . = ALIGN(4096);\n    PROVIDE(_data_start = .);\n\t/*\n\t   sdata and data are essentially the same thing. However, compilers usually use the\n\t   sdata sections for shorter, quicker loading sections. So, usually critical data\n\t   is loaded there. However, we're loading all of this in one fell swoop.\n\t   So, we're looking to put all of the following sections under the umbrella .data:\n\t   .sdata\n\t   .sdata.[anything]\n\t   .data\n\t   .data.[anything]\n\n\t   ...in that order.\n\t*/\n    *(.sdata .sdata.*) *(.data .data.*)\n    PROVIDE(_data_end = .);\n  } >ram AT>ram :data\n\n  .bss : {\n    PROVIDE(_bss_start = .);\n    *(.sbss .sbss.*) *(.bss .bss.*)\n    PROVIDE(_bss_end = .);\n  } >ram AT>ram :bss\n\n  /*\n     The following will be helpful when we allocate the kernel stack (_stack) and\n\t determine where the heap begnis and ends (_heap_start and _heap_start + _heap_size)/\n\t When we do memory allocation, we can use these symbols.\n\n\t We use the symbols instead of hard-coding an address because this is a floating target.\n\t As we add code, the heap moves farther down the memory and gets shorter.\n\n\t _memory_start will be set to 0x8000_0000 here. We use ORIGIN(ram) so that it will take\n\t whatever we set the origin of ram to. Otherwise, we'd have to change it more than once\n\t if we ever stray away from 0x8000_0000 as our entry point.\n  */\n  PROVIDE(_memory_start = ORIGIN(ram));\n  /*\n     Our kernel stack starts at the end of the bss segment (_bss_end). However, we're allocating\n\t 0x80000 bytes (524 KiB) to our kernel stack. This should be PLENTY of space. The reason\n\t we add the memory is because the stack grows from higher memory to lower memory (bottom to top).\n\t Therefore we set the stack at the very bottom of its allocated slot.\n\t When we go to allocate from the stack, we'll subtract the number of bytes we need.\n  */\n  PROVIDE(_stack_start = _bss_end);\n  PROVIDE(_stack_end = _stack_start + 0x8000);\n  PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));\n\n  /* \n     Finally, our heap starts right after the kernel stack. This heap will be used mainly\n\t to dole out memory for user-space applications. However, in some circumstances, it will\n\t be used for kernel memory as well.\n\n\t We don't align here because we let the kernel determine how it wants to do this.\n  */\n  PROVIDE(_heap_start = _stack_end);\n  PROVIDE(_heap_size = _memory_end - _heap_start);\n}\n"
  },
  {
    "path": "risc_v/chapters/ch4/src/lib.rs",
    "content": "// Steve Operating System\n// Stephen Marz\n// 21 Sep 2019\n#![no_std]\n#![feature(panic_info_message,\n           asm,\n           allocator_api,\n           alloc_error_handler,\n           alloc_prelude,\n           const_raw_ptr_to_usize_cast)]\n\n#[macro_use]\nextern crate alloc;\n// This is experimental and requires alloc_prelude as a feature\nuse alloc::prelude::v1::*;\n\n// ///////////////////////////////////\n// / RUST MACROS\n// ///////////////////////////////////\n#[macro_export]\nmacro_rules! print\n{\n\t($($args:tt)+) => ({\n\t\t\tuse core::fmt::Write;\n\t\t\tlet _ = write!(crate::uart::Uart::new(0x1000_0000), $($args)+);\n\t\t\t});\n}\n#[macro_export]\nmacro_rules! println\n{\n\t() => ({\n\t\t   print!(\"\\r\\n\")\n\t\t   });\n\t($fmt:expr) => ({\n\t\t\tprint!(concat!($fmt, \"\\r\\n\"))\n\t\t\t});\n\t($fmt:expr, $($args:tt)+) => ({\n\t\t\tprint!(concat!($fmt, \"\\r\\n\"), $($args)+)\n\t\t\t});\n}\n\n// ///////////////////////////////////\n// / LANGUAGE STRUCTURES / FUNCTIONS\n// ///////////////////////////////////\n#[no_mangle]\nextern \"C\" fn eh_personality() {}\n\n#[panic_handler]\nfn panic(info: &core::panic::PanicInfo) -> ! {\n\tprint!(\"Aborting: \");\n\tif let Some(p) = info.location() {\n\t\tprintln!(\n\t\t         \"line {}, file {}: {}\",\n\t\t         p.line(),\n\t\t         p.file(),\n\t\t         info.message().unwrap()\n\t\t);\n\t}\n\telse {\n\t\tprintln!(\"no information available.\");\n\t}\n\tabort();\n}\n#[no_mangle]\nextern \"C\" fn abort() -> ! {\n\tloop {\n\t\tunsafe {\n\t\t\tasm!(\"wfi\"::::\"volatile\");\n\t\t}\n\t}\n}\n\n// ///////////////////////////////////\n// / CONSTANTS\n// ///////////////////////////////////\n// const STR_Y: &str = \"\\x1b[38;2;79;221;13m✓\\x1b[m\";\n// const STR_N: &str = \"\\x1b[38;2;221;41;13m✘\\x1b[m\";\n\n// The following symbols come from asm/mem.S. We can use\n// the symbols directly, but the address of the symbols\n// themselves are their values, which can cause issues.\n// Instead, I created doubleword values in mem.S in the .rodata and .data\n// sections.\nextern \"C\" {\n\tstatic TEXT_START: usize;\n\tstatic TEXT_END: usize;\n\tstatic DATA_START: usize;\n\tstatic DATA_END: usize;\n\tstatic RODATA_START: usize;\n\tstatic RODATA_END: usize;\n\tstatic BSS_START: usize;\n\tstatic BSS_END: usize;\n\tstatic KERNEL_STACK_START: usize;\n\tstatic KERNEL_STACK_END: usize;\n\tstatic HEAP_START: usize;\n\tstatic HEAP_SIZE: usize;\n}\n/// Identity map range\n/// Takes a contiguous allocation of memory and maps it using PAGE_SIZE\n/// This assumes that start <= end\npub fn id_map_range(root: &mut page::Table,\n                    start: usize,\n                    end: usize,\n                    bits: i64)\n{\n\tlet mut memaddr = start & !(page::PAGE_SIZE - 1);\n\tlet num_kb_pages =\n\t\t(page::align_val(end, 12) - memaddr) / page::PAGE_SIZE;\n\n\t// I named this num_kb_pages for future expansion when\n\t// I decide to allow for GiB (2^30) and 2MiB (2^21) page\n\t// sizes. However, the overlapping memory regions are causing\n\t// nightmares.\n\tfor _ in 0..num_kb_pages {\n\t\tpage::map(root, memaddr, memaddr, bits, 0);\n\t\tmemaddr += 1 << 12;\n\t}\n}\n// ///////////////////////////////////\n// / ENTRY POINT\n// ///////////////////////////////////\n#[no_mangle]\nextern \"C\" fn kinit() {\n\t// We created kinit, which runs in super-duper mode\n\t// 3 called \"machine mode\".\n\t// The job of kinit() is to get us into supervisor mode\n\t// as soon as possible.\n\t// Interrupts are disabled for the duration of kinit()\n\tuart::Uart::new(0x1000_0000).init();\n\tpage::init();\n\tkmem::init();\n\n\t// Map heap allocations\n\tlet root_ptr = kmem::get_page_table();\n\tlet root_u = root_ptr as usize;\n\tlet mut root = unsafe { root_ptr.as_mut().unwrap() };\n\tlet kheap_head = kmem::get_head() as usize;\n\tlet total_pages = kmem::get_num_allocations();\n\tprintln!();\n\tprintln!();\n\tunsafe {\n\t\tprintln!(\"TEXT:   0x{:x} -> 0x{:x}\", TEXT_START, TEXT_END);\n\t\tprintln!(\"RODATA: 0x{:x} -> 0x{:x}\", RODATA_START, RODATA_END);\n\t\tprintln!(\"DATA:   0x{:x} -> 0x{:x}\", DATA_START, DATA_END);\n\t\tprintln!(\"BSS:    0x{:x} -> 0x{:x}\", BSS_START, BSS_END);\n\t\tprintln!(\n\t\t         \"STACK:  0x{:x} -> 0x{:x}\",\n\t\t         KERNEL_STACK_START, KERNEL_STACK_END\n\t\t);\n\t\tprintln!(\n\t\t         \"HEAP:   0x{:x} -> 0x{:x}\",\n\t\t         kheap_head,\n\t\t         kheap_head + total_pages * page::PAGE_SIZE\n\t\t);\n\t}\n\tid_map_range(\n\t             &mut root,\n\t             kheap_head,\n\t             kheap_head + total_pages * page::PAGE_SIZE,\n\t             page::EntryBits::ReadWrite.val(),\n\t);\n\t// Using statics is inherently unsafe.\n\tunsafe {\n\t\t// Map heap descriptors\n\t\tlet num_pages = HEAP_SIZE / page::PAGE_SIZE;\n\t\tid_map_range(\n\t\t             &mut root,\n\t\t             HEAP_START,\n\t\t             HEAP_START + num_pages,\n\t\t             page::EntryBits::ReadWrite.val(),\n\t\t);\n\t\t// Map executable section\n\t\tid_map_range(\n\t\t             &mut root,\n\t\t             TEXT_START,\n\t\t             TEXT_END,\n\t\t             page::EntryBits::ReadExecute.val(),\n\t\t);\n\t\t// Map rodata section\n\t\t// We put the ROdata section into the text section, so they can\n\t\t// potentially overlap however, we only care that it's read\n\t\t// only.\n\t\tid_map_range(\n\t\t             &mut root,\n\t\t             RODATA_START,\n\t\t             RODATA_END,\n\t\t             page::EntryBits::ReadExecute.val(),\n\t\t);\n\t\t// Map data section\n\t\tid_map_range(\n\t\t             &mut root,\n\t\t             DATA_START,\n\t\t             DATA_END,\n\t\t             page::EntryBits::ReadWrite.val(),\n\t\t);\n\t\t// Map bss section\n\t\tid_map_range(\n\t\t             &mut root,\n\t\t             BSS_START,\n\t\t             BSS_END,\n\t\t             page::EntryBits::ReadWrite.val(),\n\t\t);\n\t\t// Map kernel stack\n\t\tid_map_range(\n\t\t             &mut root,\n\t\t             KERNEL_STACK_START,\n\t\t             KERNEL_STACK_END,\n\t\t             page::EntryBits::ReadWrite.val(),\n\t\t);\n\t}\n\n\t// UART\n\tid_map_range(\n\t             &mut root,\n\t             0x1000_0000,\n\t             0x1000_0100,\n\t             page::EntryBits::ReadWrite.val(),\n\t);\n\n\t// CLINT\n\t//  -> MSIP\n\tid_map_range(\n\t             &mut root,\n\t             0x0200_0000,\n\t             0x0200_ffff,\n\t             page::EntryBits::ReadWrite.val(),\n\t);\n\t// PLIC\n\tid_map_range(\n\t             &mut root,\n\t             0x0c00_0000,\n\t             0x0c00_2000,\n\t             page::EntryBits::ReadWrite.val(),\n\t);\n\tid_map_range(\n\t             &mut root,\n\t             0x0c20_0000,\n\t             0x0c20_8000,\n\t             page::EntryBits::ReadWrite.val(),\n\t);\n\t// When we return from here, we'll go back to boot.S and switch into\n\t// supervisor mode We will return the SATP register to be written when\n\t// we return. root_u is the root page table's address. When stored into\n\t// the SATP register, this is divided by 4 KiB (right shift by 12 bits).\n\t// We enable the MMU by setting mode 8. Bits 63, 62, 61, 60 determine\n\t// the mode.\n\t// 0 = Bare (no translation)\n\t// 8 = Sv39\n\t// 9 = Sv48\n\t// build_satp has these parameters: mode, asid, page table address.\n\tlet satp_value = cpu::build_satp(cpu::SatpMode::Sv39, 0, root_u);\n\tunsafe {\n\t\t// We have to store the kernel's table. The tables will be moved\n\t\t// back and forth between the kernel's table and user\n\t\t// applicatons' tables. Note that we're writing the physical address\n\t\t// of the trap frame.\n\t\tcpu::mscratch_write(\n\t\t                    (&mut cpu::KERNEL_TRAP_FRAME[0]\n\t\t                     as *mut cpu::TrapFrame)\n\t\t                    as usize,\n\t\t);\n\t\tcpu::sscratch_write(cpu::mscratch_read());\n\t\tcpu::KERNEL_TRAP_FRAME[0].satp = satp_value;\n\t\t// Move the stack pointer to the very bottom. The stack is\n\t\t// actually in a non-mapped page. The stack is decrement-before\n\t\t// push and increment after pop. Therefore, the stack will be\n\t\t// allocated (decremented) before it is stored.\n\t\tcpu::KERNEL_TRAP_FRAME[0].trap_stack =\n\t\t\tpage::zalloc(1).add(page::PAGE_SIZE);\n\t\tid_map_range(\n\t\t             &mut root,\n\t\t             cpu::KERNEL_TRAP_FRAME[0].trap_stack\n\t\t                                      .sub(page::PAGE_SIZE,)\n\t\t             as usize,\n\t\t             cpu::KERNEL_TRAP_FRAME[0].trap_stack as usize,\n\t\t             page::EntryBits::ReadWrite.val(),\n\t\t);\n\t\t// The trap frame itself is stored in the mscratch register.\n\t\tid_map_range(\n\t\t             &mut root,\n\t\t             cpu::mscratch_read(),\n\t\t             cpu::mscratch_read()\n\t\t             + core::mem::size_of::<cpu::TrapFrame,>(),\n\t\t             page::EntryBits::ReadWrite.val(),\n\t\t);\n\t\tpage::print_page_allocations();\n\t\tlet p = cpu::KERNEL_TRAP_FRAME[0].trap_stack as usize - 1;\n\t\tlet m = page::virt_to_phys(&root, p).unwrap_or(0);\n\t\tprintln!(\"Walk 0x{:x} = 0x{:x}\", p, m);\n\t}\n\t// The following shows how we're going to walk to translate a virtual\n\t// address into a physical address. We will use this whenever a user\n\t// space application requires services. Since the user space application\n\t// only knows virtual addresses, we have to translate silently behind\n\t// the scenes.\n\tprintln!(\"Setting 0x{:x}\", satp_value);\n\tprintln!(\"Scratch reg = 0x{:x}\", cpu::mscratch_read());\n\tcpu::satp_write(satp_value);\n\tcpu::satp_fence_asid(0);\n}\n\n#[no_mangle]\nextern \"C\" fn kinit_hart(hartid: usize) {\n\t// All non-0 harts initialize here.\n\tunsafe {\n\t\t// We have to store the kernel's table. The tables will be moved\n\t\t// back and forth between the kernel's table and user\n\t\t// applicatons' tables.\n\t\tcpu::mscratch_write(\n\t\t                    (&mut cpu::KERNEL_TRAP_FRAME[hartid]\n\t\t                     as *mut cpu::TrapFrame)\n\t\t                    as usize,\n\t\t);\n\t\t// Copy the same mscratch over to the supervisor version of the\n\t\t// same register.\n\t\tcpu::sscratch_write(cpu::mscratch_read());\n\t\tcpu::KERNEL_TRAP_FRAME[hartid].hartid = hartid;\n\t\t// We can't do the following until zalloc() is locked, but we\n\t\t// don't have locks, yet :( cpu::KERNEL_TRAP_FRAME[hartid].satp\n\t\t// = cpu::KERNEL_TRAP_FRAME[0].satp;\n\t\t// cpu::KERNEL_TRAP_FRAME[hartid].trap_stack = page::zalloc(1);\n\t}\n}\n\n#[no_mangle]\nextern \"C\" fn kmain() {\n\t// kmain() starts in supervisor mode. So, we should have the trap\n\t// vector setup and the MMU turned on when we get here.\n\n\t// We initialized my_uart in machine mode under kinit for debugging\n\t// prints, but this just grabs a pointer to it.\n\tlet mut my_uart = uart::Uart::new(0x1000_0000);\n\t// Create a new scope so that we can test the global allocator and\n\t// deallocator\n\t{\n\t\t// We have the global allocator, so let's see if that works!\n\t\tlet k = Box::<u32>::new(100);\n\t\tprintln!(\"Boxed value = {}\", *k);\n\t\t// The following comes from the Rust documentation:\n\t\t// some bytes, in a vector\n\t\tlet sparkle_heart = vec![240, 159, 146, 150];\n\t\t// We know these bytes are valid, so we'll use `unwrap()`.\n\t\t// This will MOVE the vector.\n\t\tlet sparkle_heart = String::from_utf8(sparkle_heart).unwrap();\n\t\tprintln!(\"String = {}\", sparkle_heart);\n\t\tprintln!(\"\\n\\nAllocations of a box, vector, and string\");\n\t\tkmem::print_table();\n\t}\n\tprintln!(\"\\n\\nEverything should now be free:\");\n\tkmem::print_table();\n\n\tunsafe {\n\t\t// Set the next machine timer to fire.\n\t\tlet mtimecmp = 0x0200_4000 as *mut u64;\n\t\tlet mtime = 0x0200_bff8 as *const u64;\n\t\t// The frequency given by QEMU is 10_000_000 Hz, so this sets\n\t\t// the next interrupt to fire one second from now.\n\t\tmtimecmp.write_volatile(mtime.read_volatile() + 10_000_000);\n\n\t\t// Let's cause a page fault and see what happens. This should trap\n\t\t// to m_trap under trap.rs\n\t\tlet v = 0x0 as *mut u64;\n\t\tv.write_volatile(0);\n\t}\n\t// If we get here, the Box, vec, and String should all be freed since\n\t// they go out of scope. This calls their \"Drop\" trait.\n\t// Now see if we can read stuff:\n\t// Usually we can use #[test] modules in Rust, but it would convolute\n\t// the task at hand, and it requires us to create the testing harness\n\t// since the embedded testing system is part of the \"std\" library.\n\tloop {\n\t\tif let Some(c) = my_uart.get() {\n\t\t\tmatch c {\n\t\t\t\t8 => {\n\t\t\t\t\t// This is a backspace, so we\n\t\t\t\t\t// essentially have to write a space and\n\t\t\t\t\t// backup again:\n\t\t\t\t\tprint!(\"{} {}\", 8 as char, 8 as char);\n\t\t\t\t},\n\t\t\t\t10 | 13 => {\n\t\t\t\t\t// Newline or carriage-return\n\t\t\t\t\tprintln!();\n\t\t\t\t},\n\t\t\t\t_ => {\n\t\t\t\t\tprint!(\"{}\", c as char);\n\t\t\t\t},\n\t\t\t}\n\t\t}\n\t}\n}\n\n// ///////////////////////////////////\n// / RUST MODULES\n// ///////////////////////////////////\n\npub mod cpu;\npub mod kmem;\npub mod page;\npub mod trap;\npub mod uart;\n"
  },
  {
    "path": "risc_v/chapters/ch4/src/page.rs",
    "content": "// page.rs\n// Memory routines\n// Stephen Marz\n// 6 October 2019\n\nuse core::{mem::size_of, ptr::null_mut};\n\n// ////////////////////////////////\n// // Allocation routines\n// ////////////////////////////////\nextern \"C\" {\n\tstatic HEAP_START: usize;\n\tstatic HEAP_SIZE: usize;\n}\n\n// We will use ALLOC_START to mark the start of the actual\n// memory we can dish out.\nstatic mut ALLOC_START: usize = 0;\nconst PAGE_ORDER: usize = 12;\npub const PAGE_SIZE: usize = 1 << 12;\n\n/// Align (set to a multiple of some power of two)\n/// This takes an order which is the exponent to 2^order\n/// Therefore, all alignments must be made as a power of two.\n/// This function always rounds up.\npub const fn align_val(val: usize, order: usize) -> usize {\n\tlet o = (1usize << order) - 1;\n\t(val + o) & !o\n}\n\n#[repr(u8)]\npub enum PageBits {\n\tEmpty = 0,\n\tTaken = 1 << 0,\n\tLast = 1 << 1,\n}\n\nimpl PageBits {\n\t// We convert PageBits to a u8 a lot, so this is\n\t// for convenience.\n\tpub fn val(self) -> u8 {\n\t\tself as u8\n\t}\n}\n\n// Each page is described by the Page structure. Linux does this\n// as well, where each 4096-byte chunk of memory has a structure\n// associated with it. However, there structure is much larger.\npub struct Page {\n\tflags: u8,\n}\n\nimpl Page {\n\t// If this page has been marked as the final allocation,\n\t// this function returns true. Otherwise, it returns false.\n\tpub fn is_last(&self) -> bool {\n\t\tif self.flags & PageBits::Last.val() != 0 {\n\t\t\ttrue\n\t\t}\n\t\telse {\n\t\t\tfalse\n\t\t}\n\t}\n\n\t// If the page is marked as being taken (allocated), then\n\t// this function returns true. Otherwise, it returns false.\n\tpub fn is_taken(&self) -> bool {\n\t\tif self.flags & PageBits::Taken.val() != 0 {\n\t\t\ttrue\n\t\t}\n\t\telse {\n\t\t\tfalse\n\t\t}\n\t}\n\n\t// This is the opposite of is_taken().\n\tpub fn is_free(&self) -> bool {\n\t\t!self.is_taken()\n\t}\n\n\t// Clear the Page structure and all associated allocations.\n\tpub fn clear(&mut self) {\n\t\tself.flags = PageBits::Empty.val();\n\t}\n\n\t// Set a certain flag. We ran into trouble here since PageBits\n\t// is an enumeration and we haven't implemented the BitOr Trait\n\t// on it.\n\tpub fn set_flag(&mut self, flag: PageBits) {\n\t\tself.flags |= flag.val();\n\t}\n\n\tpub fn clear_flag(&mut self, flag: PageBits) {\n\t\tself.flags &= !(flag.val());\n\t}\n}\n\n/// Initialize the allocation system. There are several ways that we can\n/// implement the page allocator:\n/// 1. Free list (singly linked list where it starts at the first free\n/// allocation) 2. Bookkeeping list (structure contains a taken and length)\n/// 3. Allocate one Page structure per 4096 bytes (this is what I chose)\n/// 4. Others\npub fn init() {\n\tunsafe {\n\t\t// let desc_per_page = PAGE_SIZE / size_of::<Page>();\n\t\tlet num_pages = HEAP_SIZE / PAGE_SIZE;\n\t\t// let num_desc_pages = num_pages / desc_per_page;\n\t\tlet ptr = HEAP_START as *mut Page;\n\t\t// Clear all pages to make sure that they aren't accidentally\n\t\t// taken\n\t\tfor i in 0..num_pages {\n\t\t\t(*ptr.add(i)).clear();\n\t\t}\n\t\t// Determine where the actual useful memory starts. This will be\n\t\t// after all Page structures. We also must align the ALLOC_START\n\t\t// to a page-boundary (PAGE_SIZE = 4096). ALLOC_START =\n\t\t// (HEAP_START + num_pages * size_of::<Page>() + PAGE_SIZE - 1)\n\t\t// & !(PAGE_SIZE - 1);\n\t\tALLOC_START = align_val(\n\t\t                        HEAP_START\n\t\t                        + num_pages * size_of::<Page>(),\n\t\t                        PAGE_ORDER,\n\t\t);\n\t}\n}\n\n/// Allocate a page or multiple pages\n/// pages: the number of PAGE_SIZE pages to allocate\npub fn alloc(pages: usize) -> *mut u8 {\n\t// We have to find a contiguous allocation of pages\n\tassert!(pages > 0);\n\tunsafe {\n\t\t// We create a Page structure for each page on the heap. We\n\t\t// actually might have more since HEAP_SIZE moves and so does\n\t\t// the size of our structure, but we'll only waste a few bytes.\n\t\tlet num_pages = HEAP_SIZE / PAGE_SIZE;\n\t\tlet ptr = HEAP_START as *mut Page;\n\t\tfor i in 0..num_pages - pages {\n\t\t\tlet mut found = false;\n\t\t\t// Check to see if this Page is free. If so, we have our\n\t\t\t// first candidate memory address.\n\t\t\tif (*ptr.add(i)).is_free() {\n\t\t\t\t// It was FREE! Yay!\n\t\t\t\tfound = true;\n\t\t\t\tfor j in i..i + pages {\n\t\t\t\t\t// Now check to see if we have a\n\t\t\t\t\t// contiguous allocation for all of the\n\t\t\t\t\t// request pages. If not, we should\n\t\t\t\t\t// check somewhere else.\n\t\t\t\t\tif (*ptr.add(j)).is_taken() {\n\t\t\t\t\t\tfound = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// We've checked to see if there are enough contiguous\n\t\t\t// pages to form what we need. If we couldn't, found\n\t\t\t// will be false, otherwise it will be true, which means\n\t\t\t// we've found valid memory we can allocate.\n\t\t\tif found {\n\t\t\t\tfor k in i..i + pages - 1 {\n\t\t\t\t\t(*ptr.add(k)).set_flag(PageBits::Taken);\n\t\t\t\t}\n\t\t\t\t// The marker for the last page is\n\t\t\t\t// PageBits::Last This lets us know when we've\n\t\t\t\t// hit the end of this particular allocation.\n\t\t\t\t(*ptr.add(i+pages-1)).set_flag(PageBits::Taken);\n\t\t\t\t(*ptr.add(i+pages-1)).set_flag(PageBits::Last);\n\t\t\t\t// The Page structures themselves aren't the\n\t\t\t\t// useful memory. Instead, there is 1 Page\n\t\t\t\t// structure per 4096 bytes starting at\n\t\t\t\t// ALLOC_START.\n\t\t\t\treturn (ALLOC_START + PAGE_SIZE * i)\n\t\t\t\t       as *mut u8;\n\t\t\t}\n\t\t}\n\t}\n\n\t// If we get here, that means that no contiguous allocation was\n\t// found.\n\tnull_mut()\n}\n\n/// Allocate and zero a page or multiple pages\n/// pages: the number of pages to allocate\n/// Each page is PAGE_SIZE which is calculated as 1 << PAGE_ORDER\n/// On RISC-V, this typically will be 4,096 bytes.\npub fn zalloc(pages: usize) -> *mut u8 {\n\t// Allocate and zero a page.\n\t// First, let's get the allocation\n\tlet ret = alloc(pages);\n\tif !ret.is_null() {\n\t\tlet size = (PAGE_SIZE * pages) / 8;\n\t\tlet big_ptr = ret as *mut u64;\n\t\tfor i in 0..size {\n\t\t\t// We use big_ptr so that we can force an\n\t\t\t// sd (store doubleword) instruction rather than\n\t\t\t// the sb. This means 8x fewer stores than before.\n\t\t\t// Typically we have to be concerned about remaining\n\t\t\t// bytes, but fortunately 4096 % 8 = 0, so we\n\t\t\t// won't have any remaining bytes.\n\t\t\tunsafe {\n\t\t\t\t(*big_ptr.add(i)) = 0;\n\t\t\t}\n\t\t}\n\t}\n\tret\n}\n\n/// Deallocate a page by its pointer\n/// The way we've structured this, it will automatically coalesce\n/// contiguous pages.\npub fn dealloc(ptr: *mut u8) {\n\t// Make sure we don't try to free a null pointer.\n\tassert!(!ptr.is_null());\n\tunsafe {\n\t\tlet addr =\n\t\t\tHEAP_START + (ptr as usize - ALLOC_START) / PAGE_SIZE;\n\t\t// Make sure that the address makes sense. The address we\n\t\t// calculate here is the page structure, not the HEAP address!\n\t\tassert!(addr >= HEAP_START && addr < HEAP_START + HEAP_SIZE);\n\t\tlet mut p = addr as *mut Page;\n\t\t// Keep clearing pages until we hit the last page.\n\t\twhile (*p).is_taken() && !(*p).is_last() {\n\t\t\t(*p).clear();\n\t\t\tp = p.add(1);\n\t\t}\n\t\t// If the following assertion fails, it is most likely\n\t\t// caused by a double-free.\n\t\tassert!(\n\t\t        (*p).is_last() == true,\n\t\t        \"Possible double-free detected! (Not taken found \\\n\t\t         before last)\"\n\t\t);\n\t\t// If we get here, we've taken care of all previous pages and\n\t\t// we are on the last page.\n\t\t(*p).clear();\n\t}\n}\n\n/// Print all page allocations\n/// This is mainly used for debugging.\npub fn print_page_allocations() {\n\tunsafe {\n\t\tlet num_pages = (HEAP_SIZE - (ALLOC_START - HEAP_START)) / PAGE_SIZE;\n\t\tlet mut beg = HEAP_START as *const Page;\n\t\tlet end = beg.add(num_pages);\n\t\tlet alloc_beg = ALLOC_START;\n\t\tlet alloc_end = ALLOC_START + num_pages * PAGE_SIZE;\n\t\tprintln!();\n\t\tprintln!(\n\t\t         \"PAGE ALLOCATION TABLE\\nMETA: {:p} -> {:p}\\nPHYS: \\\n\t\t          0x{:x} -> 0x{:x}\",\n\t\t         beg, end, alloc_beg, alloc_end\n\t\t);\n\t\tprintln!(\"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\");\n\t\tlet mut num = 0;\n\t\twhile beg < end {\n\t\t\tif (*beg).is_taken() {\n\t\t\t\tlet start = beg as usize;\n\t\t\t\tlet memaddr = ALLOC_START\n\t\t\t\t              + (start - HEAP_START)\n\t\t\t\t                * PAGE_SIZE;\n\t\t\t\tprint!(\"0x{:x} => \", memaddr);\n\t\t\t\tloop {\n\t\t\t\t\tnum += 1;\n\t\t\t\t\tif (*beg).is_last() {\n\t\t\t\t\t\tlet end = beg as usize;\n\t\t\t\t\t\tlet memaddr = ALLOC_START\n\t\t\t\t\t\t              + (end\n\t\t\t\t\t\t                 - HEAP_START)\n\t\t\t\t\t\t                * PAGE_SIZE\n\t\t\t\t\t\t              + PAGE_SIZE - 1;\n\t\t\t\t\t\tprint!(\n\t\t\t\t\t\t       \"0x{:x}: {:>3} page(s)\",\n\t\t\t\t\t\t       memaddr,\n\t\t\t\t\t\t       (end - start + 1)\n\t\t\t\t\t\t);\n\t\t\t\t\t\tprintln!(\".\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tbeg = beg.add(1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbeg = beg.add(1);\n\t\t}\n\t\tprintln!(\"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\");\n\t\tprintln!(\n\t\t         \"Allocated: {:>6} pages ({:>10} bytes).\",\n\t\t         num,\n\t\t         num * PAGE_SIZE\n\t\t);\n\t\tprintln!(\n\t\t         \"Free     : {:>6} pages ({:>10} bytes).\",\n\t\t         num_pages - num,\n\t\t         (num_pages - num) * PAGE_SIZE\n\t\t);\n\t\tprintln!();\n\t}\n}\n\n// ////////////////////////////////\n// // MMU Routines\n// ////////////////////////////////\n\n// Represent (repr) our entry bits as\n// unsigned 64-bit integers.\n#[repr(i64)]\n#[derive(Copy, Clone)]\npub enum EntryBits {\n\tNone = 0,\n\tValid = 1 << 0,\n\tRead = 1 << 1,\n\tWrite = 1 << 2,\n\tExecute = 1 << 3,\n\tUser = 1 << 4,\n\tGlobal = 1 << 5,\n\tAccess = 1 << 6,\n\tDirty = 1 << 7,\n\n\t// Convenience combinations\n\tReadWrite = 1 << 1 | 1 << 2,\n\tReadExecute = 1 << 1 | 1 << 3,\n\tReadWriteExecute = 1 << 1 | 1 << 2 | 1 << 3,\n\n\t// User Convenience Combinations\n\tUserReadWrite = 1 << 1 | 1 << 2 | 1 << 4,\n\tUserReadExecute = 1 << 1 | 1 << 3 | 1 << 4,\n\tUserReadWriteExecute = 1 << 1 | 1 << 2 | 1 << 3 | 1 << 4,\n}\n\n// Helper functions to convert the enumeration\n// into an i64, which is what our page table\n// entries will be.\nimpl EntryBits {\n\tpub fn val(self) -> i64 {\n\t\tself as i64\n\t}\n}\n\n// A single entry. We're using an i64 so that\n// this will sign-extend rather than zero-extend\n// since RISC-V requires that the reserved sections\n// take on the most significant bit.\npub struct Entry {\n\tpub entry: i64,\n}\n\n// The Entry structure describes one of the 512 entries per table, which is\n// described in the RISC-V privileged spec Figure 4.18.\nimpl Entry {\n\tpub fn is_valid(&self) -> bool {\n\t\tself.get_entry() & EntryBits::Valid.val() != 0\n\t}\n\n\t// The first bit (bit index #0) is the V bit for\n\t// valid.\n\tpub fn is_invalid(&self) -> bool {\n\t\t!self.is_valid()\n\t}\n\n\t// A leaf has one or more RWX bits set\n\tpub fn is_leaf(&self) -> bool {\n\t\tself.get_entry() & 0xe != 0\n\t}\n\n\tpub fn is_branch(&self) -> bool {\n\t\t!self.is_leaf()\n\t}\n\n\tpub fn set_entry(&mut self, entry: i64) {\n\t\tself.entry = entry;\n\t}\n\n\tpub fn get_entry(&self) -> i64 {\n\t\tself.entry\n\t}\n}\n\n// Table represents a single table, which contains 512 (2^9), 64-bit entries.\npub struct Table {\n\tpub entries: [Entry; 512],\n}\n\nimpl Table {\n\tpub fn len() -> usize {\n\t\t512\n\t}\n}\n\n/// Map a virtual address to a physical address using 4096-byte page\n/// size.\n/// root: a mutable reference to the root Table\n/// vaddr: The virtual address to map\n/// paddr: The physical address to map\n/// bits: An OR'd bitset containing the bits the leaf should have.\n///       The bits should contain only the following:\n///          Read, Write, Execute, User, and/or Global\n///       The bits MUST include one or more of the following:\n///          Read, Write, Execute\n///       The valid bit automatically gets added.\npub fn map(root: &mut Table,\n           vaddr: usize,\n           paddr: usize,\n           bits: i64,\n           level: usize)\n{\n\t// Make sure that Read, Write, or Execute have been provided\n\t// otherwise, we'll leak memory and always create a page fault.\n\tassert!(bits & 0xe != 0);\n\t// Extract out each VPN from the virtual address\n\t// On the virtual address, each VPN is exactly 9 bits,\n\t// which is why we use the mask 0x1ff = 0b1_1111_1111 (9 bits)\n\tlet vpn = [\n\t           // VPN[0] = vaddr[20:12]\n\t           (vaddr >> 12) & 0x1ff,\n\t           // VPN[1] = vaddr[29:21]\n\t           (vaddr >> 21) & 0x1ff,\n\t           // VPN[2] = vaddr[38:30]\n\t           (vaddr >> 30) & 0x1ff,\n\t];\n\n\t// Just like the virtual address, extract the physical address\n\t// numbers (PPN). However, PPN[2] is different in that it stores\n\t// 26 bits instead of 9. Therefore, we use,\n\t// 0x3ff_ffff = 0b11_1111_1111_1111_1111_1111_1111 (26 bits).\n\tlet ppn = [\n\t           // PPN[0] = paddr[20:12]\n\t           (paddr >> 12) & 0x1ff,\n\t           // PPN[1] = paddr[29:21]\n\t           (paddr >> 21) & 0x1ff,\n\t           // PPN[2] = paddr[55:30]\n\t           (paddr >> 30) & 0x3ff_ffff,\n\t];\n\t// We will use this as a floating reference so that we can set\n\t// individual entries as we walk the table.\n\tlet mut v = &mut root.entries[vpn[2]];\n\t// Now, we're going to traverse the page table and set the bits\n\t// properly. We expect the root to be valid, however we're required to\n\t// create anything beyond the root.\n\t// In Rust, we create a range iterator using the .. operator.\n\t// The .rev() will reverse the iteration since we need to start with\n\t// VPN[2] The .. operator is inclusive on start but exclusive on end.\n\t// So, (0..2) will iterate 0 and 1.\n\tfor i in (level..2).rev() {\n\t\tif !v.is_valid() {\n\t\t\t// Allocate a page\n\t\t\tlet page = zalloc(1);\n\t\t\t// The page is already aligned by 4,096, so store it\n\t\t\t// directly The page is stored in the entry shifted\n\t\t\t// right by 2 places.\n\t\t\tv.set_entry(\n\t\t\t            (page as i64 >> 2)\n\t\t\t            | EntryBits::Valid.val(),\n\t\t\t);\n\t\t}\n\t\tlet entry = ((v.get_entry() & !0x3ff) << 2) as *mut Entry;\n\t\tv = unsafe { entry.add(vpn[i]).as_mut().unwrap() };\n\t}\n\t// When we get here, we should be at VPN[0] and v should be pointing to\n\t// our entry.\n\t// The entry structure is Figure 4.18 in the RISC-V Privileged\n\t// Specification\n\tlet entry = (ppn[2] << 28) as i64 |   // PPN[2] = [53:28]\n\t            (ppn[1] << 19) as i64 |   // PPN[1] = [27:19]\n\t\t\t\t(ppn[0] << 10) as i64 |   // PPN[0] = [18:10]\n\t\t\t\tbits |                    // Specified bits, such as User, Read, Write, etc\n\t\t\t\tEntryBits::Valid.val() |  // Valid bit\n\t\t\t\tEntryBits::Dirty.val() |  // Some machines require this to =1\n\t\t\t\tEntryBits::Access.val()   // Just like dirty, some machines require this\n\t\t\t\t;\n\t// Set the entry. V should be set to the correct pointer by the loop\n\t// above.\n\tv.set_entry(entry);\n}\n\n/// Unmaps and frees all memory associated with a table.\n/// root: The root table to start freeing.\n/// NOTE: This does NOT free root directly. This must be\n/// freed manually.\n/// The reason we don't free the root is because it is\n/// usually embedded into the Process structure.\npub fn unmap(root: &mut Table) {\n\t// Start with level 2\n\tfor lv2 in 0..Table::len() {\n\t\tlet ref entry_lv2 = root.entries[lv2];\n\t\tif entry_lv2.is_valid() && entry_lv2.is_branch() {\n\t\t\t// This is a valid entry, so drill down and free.\n\t\t\tlet memaddr_lv1 = (entry_lv2.get_entry() & !0x3ff) << 2;\n\t\t\tlet table_lv1 = unsafe {\n\t\t\t\t// Make table_lv1 a mutable reference instead of\n\t\t\t\t// a pointer.\n\t\t\t\t(memaddr_lv1 as *mut Table).as_mut().unwrap()\n\t\t\t};\n\t\t\tfor lv1 in 0..Table::len() {\n\t\t\t\tlet ref entry_lv1 = table_lv1.entries[lv1];\n\t\t\t\tif entry_lv1.is_valid() && entry_lv1.is_branch()\n\t\t\t\t{\n\t\t\t\t\tlet memaddr_lv0 = (entry_lv1.get_entry()\n\t\t\t\t\t                   & !0x3ff) << 2;\n\t\t\t\t\t// The next level is level 0, which\n\t\t\t\t\t// cannot have branches, therefore,\n\t\t\t\t\t// we free here.\n\t\t\t\t\tdealloc(memaddr_lv0 as *mut u8);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdealloc(memaddr_lv1 as *mut u8);\n\t\t}\n\t}\n}\n\n/// Walk the page table to convert a virtual address to a\n/// physical address.\n/// If a page fault would occur, this returns None\n/// Otherwise, it returns Some with the physical address.\npub fn virt_to_phys(root: &Table, vaddr: usize) -> Option<usize> {\n\t// Walk the page table pointed to by root\n\tlet vpn = [\n\t           // VPN[0] = vaddr[20:12]\n\t           (vaddr >> 12) & 0x1ff,\n\t           // VPN[1] = vaddr[29:21]\n\t           (vaddr >> 21) & 0x1ff,\n\t           // VPN[2] = vaddr[38:30]\n\t           (vaddr >> 30) & 0x1ff,\n\t];\n\n\tlet mut v = &root.entries[vpn[2]];\n\tfor i in (0..=2).rev() {\n\t\tif v.is_invalid() {\n\t\t\t// This is an invalid entry, page fault.\n\t\t\tbreak;\n\t\t}\n\t\telse if v.is_leaf() {\n\t\t\t// According to RISC-V, a leaf can be at any level.\n\n\t\t\t// The offset mask masks off the PPN. Each PPN is 9\n\t\t\t// bits and they start at bit #12. So, our formula\n\t\t\t// 12 + i * 9\n\t\t\tlet off_mask = (1 << (12 + i * 9)) - 1;\n\t\t\tlet vaddr_pgoff = vaddr & off_mask;\n\t\t\tlet addr = ((v.get_entry() << 2) as usize) & !off_mask;\n\t\t\treturn Some(addr | vaddr_pgoff);\n\t\t}\n\t\t// Set v to the next entry which is pointed to by this\n\t\t// entry. However, the address was shifted right by 2 places\n\t\t// when stored in the page table entry, so we shift it left\n\t\t// to get it back into place.\n\t\tlet entry = ((v.get_entry() & !0x3ff) << 2) as *const Entry;\n\t\t// We do i - 1 here, however we should get None or Some() above\n\t\t// before we do 0 - 1 = -1.\n\t\tv = unsafe { entry.add(vpn[i - 1]).as_ref().unwrap() };\n\t}\n\n\t// If we get here, we've exhausted all valid tables and haven't\n\t// found a leaf.\n\tNone\n}\n"
  },
  {
    "path": "risc_v/chapters/ch4/src/trap.rs",
    "content": "// trap.rs\n// Trap routines\n// Stephen Marz\n// 10 October 2019\n\nuse crate::cpu::TrapFrame;\n\n#[no_mangle]\nextern \"C\" fn m_trap(epc: usize,\n                     tval: usize,\n                     cause: usize,\n                     hart: usize,\n                     status: usize,\n                     frame: &mut TrapFrame)\n                     -> usize\n{\n\t// We're going to handle all traps in machine mode. RISC-V lets\n\t// us delegate to supervisor mode, but switching out SATP (virtual memory)\n\t// gets hairy.\n\tlet is_async = {\n\t\tif cause >> 63 & 1 == 1 {\n\t\t\ttrue\n\t\t}\n\t\telse {\n\t\t\tfalse\n\t\t}\n\t};\n\t// The cause contains the type of trap (sync, async) as well as the cause\n\t// number. So, here we narrow down just the cause number.\n\tlet cause_num = cause & 0xfff;\n\tlet mut return_pc = epc;\n\tif is_async {\n\t\t// Asynchronous trap\n\t\tmatch cause_num {\n\t\t\t3 => {\n\t\t\t\t// Machine software\n\t\t\t\tprintln!(\"Machine software interrupt CPU#{}\", hart);\n\t\t\t},\n\t\t\t7 => unsafe {\n\t\t\t\t// Machine timer\n\t\t\t\tlet mtimecmp = 0x0200_4000 as *mut u64;\n\t\t\t\tlet mtime = 0x0200_bff8 as *const u64;\n\t\t\t\t// The frequency given by QEMU is 10_000_000 Hz, so this sets\n\t\t\t\t// the next interrupt to fire one second from now.\n\t\t\t\tmtimecmp.write_volatile(mtime.read_volatile() + 10_000_000);\n\t\t\t},\n\t\t\t11 => {\n\t\t\t\t// Machine external (interrupt from Platform Interrupt Controller (PLIC))\n\t\t\t\tprintln!(\"Machine external interrupt CPU#{}\", hart);\n\t\t\t},\n\t\t\t_ => {\n\t\t\t\tpanic!(\"Unhandled async trap CPU#{} -> {}\\n\", hart, cause_num);\n\t\t\t}\n\t\t}\n\t}\n\telse {\n\t\t// Synchronous trap\n\t\tmatch cause_num {\n\t\t\t2 => {\n\t\t\t\t// Illegal instruction\n\t\t\t\tpanic!(\"Illegal instruction CPU#{} -> 0x{:08x}: 0x{:08x}\\n\", hart, epc, tval);\n\t\t\t},\n\t\t\t8 => {\n\t\t\t\t// Environment (system) call from User mode\n\t\t\t\tprintln!(\"E-call from User mode! CPU#{} -> 0x{:08x}\", hart, epc);\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t9 => {\n\t\t\t\t// Environment (system) call from Supervisor mode\n\t\t\t\tprintln!(\"E-call from Supervisor mode! CPU#{} -> 0x{:08x}\", hart, epc);\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t11 => {\n\t\t\t\t// Environment (system) call from Machine mode\n\t\t\t\tpanic!(\"E-call from Machine mode! CPU#{} -> 0x{:08x}\\n\", hart, epc);\n\t\t\t},\n\t\t\t// Page faults\n\t\t\t12 => {\n\t\t\t\t// Instruction page fault\n\t\t\t\tprintln!(\"Instruction page fault CPU#{} -> 0x{:08x}: 0x{:08x}\", hart, epc, tval);\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t13 => {\n\t\t\t\t// Load page fault\n\t\t\t\tprintln!(\"Load page fault CPU#{} -> 0x{:08x}: 0x{:08x}\", hart, epc, tval);\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t15 => {\n\t\t\t\t// Store page fault\n\t\t\t\tprintln!(\"Store page fault CPU#{} -> 0x{:08x}: 0x{:08x}\", hart, epc, tval);\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t_ => {\n\t\t\t\tpanic!(\"Unhandled sync trap CPU#{} -> {}\\n\", hart, cause_num);\n\t\t\t}\n\t\t}\n\t};\n\t// Finally, return the updated program counter\n\treturn_pc\n}\n"
  },
  {
    "path": "risc_v/chapters/ch4/src/uart.rs",
    "content": "// uart.rs\n// UART routines and driver\n\nuse core::{convert::TryInto,\n           fmt::{Error, Write}};\n\npub struct Uart {\n\tbase_address: usize,\n}\n\nimpl Write for Uart {\n\tfn write_str(&mut self, out: &str) -> Result<(), Error> {\n\t\tfor c in out.bytes() {\n\t\t\tself.put(c);\n\t\t}\n\t\tOk(())\n\t}\n}\n\nimpl Uart {\n\tpub fn new(base_address: usize) -> Self {\n\t\tUart { base_address }\n\t}\n\n\tpub fn init(&mut self) {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\t// First, set the word length, which\n\t\t\t// are bits 0 and 1 of the line control register (LCR)\n\t\t\t// which is at base_address + 3\n\t\t\t// We can easily write the value 3 here or 0b11, but I'm\n\t\t\t// extending it so that it is clear we're setting two\n\t\t\t// individual fields\n\t\t\t//             Word 0     Word 1\n\t\t\t//             ~~~~~~     ~~~~~~\n\t\t\tlet lcr: u8 = (1 << 0) | (1 << 1);\n\t\t\tptr.add(3).write_volatile(lcr);\n\n\t\t\t// Now, enable the FIFO, which is bit index 0 of the\n\t\t\t// FIFO control register (FCR at offset 2).\n\t\t\t// Again, we can just write 1 here, but when we use left\n\t\t\t// shift, it's easier to see that we're trying to write\n\t\t\t// bit index #0.\n\t\t\tptr.add(2).write_volatile(1 << 0);\n\n\t\t\t// Enable receiver buffer interrupts, which is at bit\n\t\t\t// index 0 of the interrupt enable register (IER at\n\t\t\t// offset 1).\n\t\t\tptr.add(1).write_volatile(1 << 0);\n\n\t\t\t// If we cared about the divisor, the code below would\n\t\t\t// set the divisor from a global clock rate of 22.729\n\t\t\t// MHz (22,729,000 cycles per second) to a signaling\n\t\t\t// rate of 2400 (BAUD). We usually have much faster\n\t\t\t// signalling rates nowadays, but this demonstrates what\n\t\t\t// the divisor actually does. The formula given in the\n\t\t\t// NS16500A specification for calculating the divisor\n\t\t\t// is:\n\t\t\t// divisor = ceil( (clock_hz) / (baud_sps x 16) )\n\t\t\t// So, we substitute our values and get:\n\t\t\t// divisor = ceil( 22_729_000 / (2400 x 16) )\n\t\t\t// divisor = ceil( 22_729_000 / 38_400 )\n\t\t\t// divisor = ceil( 591.901 ) = 592\n\n\t\t\t// The divisor register is two bytes (16 bits), so we\n\t\t\t// need to split the value 592 into two bytes.\n\t\t\t// Typically, we would calculate this based on measuring\n\t\t\t// the clock rate, but again, for our purposes [qemu],\n\t\t\t// this doesn't really do anything.\n\t\t\tlet divisor: u16 = 592;\n\t\t\tlet divisor_least: u8 =\n\t\t\t\t(divisor & 0xff).try_into().unwrap();\n\t\t\tlet divisor_most: u8 =\n\t\t\t\t(divisor >> 8).try_into().unwrap();\n\n\t\t\t// Notice that the divisor register DLL (divisor latch\n\t\t\t// least) and DLM (divisor latch most) have the same\n\t\t\t// base address as the receiver/transmitter and the\n\t\t\t// interrupt enable register. To change what the base\n\t\t\t// address points to, we open the \"divisor latch\" by\n\t\t\t// writing 1 into the Divisor Latch Access Bit (DLAB),\n\t\t\t// which is bit index 7 of the Line Control Register\n\t\t\t// (LCR) which is at base_address + 3.\n\t\t\tptr.add(3).write_volatile(lcr | 1 << 7);\n\n\t\t\t// Now, base addresses 0 and 1 point to DLL and DLM,\n\t\t\t// respectively. Put the lower 8 bits of the divisor\n\t\t\t// into DLL\n\t\t\tptr.add(0).write_volatile(divisor_least);\n\t\t\tptr.add(1).write_volatile(divisor_most);\n\n\t\t\t// Now that we've written the divisor, we never have to\n\t\t\t// touch this again. In hardware, this will divide the\n\t\t\t// global clock (22.729 MHz) into one suitable for 2,400\n\t\t\t// signals per second. So, to once again get access to\n\t\t\t// the RBR/THR/IER registers, we need to close the DLAB\n\t\t\t// bit by clearing it to 0.\n\t\t\tptr.add(3).write_volatile(lcr);\n\t\t}\n\t}\n\n\tpub fn put(&mut self, c: u8) {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\tptr.add(0).write_volatile(c);\n\t\t}\n\t}\n\n\tpub fn get(&mut self) -> Option<u8> {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\tif ptr.add(5).read_volatile() & 1 == 0 {\n\t\t\t\t// The DR bit is 0, meaning no data\n\t\t\t\tNone\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// The DR bit is 1, meaning data!\n\t\t\t\tSome(ptr.add(0).read_volatile())\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "risc_v/chapters/ch5/.cargo/config",
    "content": "[build]\ntarget = \"riscv64gc-unknown-none-elf\"\n\n[target.riscv64gc-unknown-none-elf]\nlinker = \"riscv64-unknown-linux-gnu-gcc\"\n"
  },
  {
    "path": "risc_v/chapters/ch5/.gitignore",
    "content": "os.elf\ntarget/*\nCargo.lock\nhdd.dsk\n"
  },
  {
    "path": "risc_v/chapters/ch5/Cargo.toml",
    "content": "[package]\nname = \"sos\"\nversion = \"0.1.0\"\nauthors = [\"Stephen Marz <stephen.marz@utk.edu>\"]\nedition = \"2018\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[lib]\ncrate-type = [\"staticlib\"]\n\n[dependencies]\n"
  },
  {
    "path": "risc_v/chapters/ch5/Makefile",
    "content": "#####\n## BUILD\n#####\nCC=riscv64-unknown-linux-gnu-gcc\nCFLAGS=-Wall -Wextra -pedantic -Wextra -O0 -g\nCFLAGS+=-static -ffreestanding -nostdlib -fno-rtti -fno-exceptions\nCFLAGS+=-march=rv64gc -mabi=lp64\nINCLUDES=\nLINKER_SCRIPT=-Tsrc/lds/virt.lds\nTYPE=debug\nRUST_TARGET=./target/riscv64gc-unknown-none-elf/$(TYPE)\nLIBS=-L$(RUST_TARGET)\nSOURCES_ASM=$(wildcard src/asm/*.S)\nLIB=-lsos -lgcc\nOUT=os.elf\n\n#####\n## QEMU\n#####\nQEMU=qemu-system-riscv64\nMACH=virt\nCPU=rv64\nCPUS=4\nMEM=128M\nDISK=hdd.dsk\n# DRIVE= -drive if=none,format=raw,file=$(DISK),id=foo -device virtio-blk-device,scsi=off,drive=foo\nDRIVE=\n\n\nall:\n\tcargo build\n\t$(CC) $(CFLAGS) $(LINKER_SCRIPT) $(INCLUDES) -o $(OUT) $(SOURCES_ASM) $(LIBS) $(LIB)\n\t\nrun: all\n\t$(QEMU) -machine $(MACH) -cpu $(CPU) -smp $(CPUS) -m $(MEM) $(DRIVE) -nographic -serial mon:stdio -bios none -kernel $(OUT)\n\n\n.PHONY: clean\nclean:\n\tcargo clean\n\trm -f $(OUT)\n"
  },
  {
    "path": "risc_v/chapters/ch5/make_hdd.sh",
    "content": "#!/bin/sh\n\ndd if=/dev/zero of=hdd.dsk bs=1M count=32\n"
  },
  {
    "path": "risc_v/chapters/ch5/src/asm/boot.S",
    "content": "# boot.S\n# bootloader for SoS\n# Stephen Marz\n# 8 February 2019\n\n# Disable generation of compressed instructions.\n.option norvc\n\n# Define a .text.init section. The .text.init is put at the\n# starting address so that the entry _start is put at the RISC-V\n# address 0x8000_0000.\n.section .text.init\n\n# Execution starts here.\n.global _start\n_start:\n\n\t# Disable linker instruction relaxation for the `la` instruction below.\n\t# This disallows the assembler from assuming that `gp` is already initialized.\n\t# This causes the value stored in `gp` to be calculated from `pc`.\n\t# The job of the global pointer is to give the linker the ability to address\n\t# memory relative to GP instead of as an absolute address.\n.option push\n.option norelax\n\tla\t\tgp, _global_pointer\n.option pop\n\t# SATP should be zero, but let's make sure. Each HART has its own\n\t# SATP register.\n\tcsrw\tsatp, zero\n\t# Any hardware threads (hart) that are not bootstrapping\n\t# need to wait for an IPI\n\tcsrr\tt0, mhartid\n\tbnez\tt0, 3f\n\n\t# Set all bytes in the BSS section to zero.\n\tla \t\ta0, _bss_start\n\tla\t\ta1, _bss_end\n\tbgeu\ta0, a1, 2f\n1:\n\tsd\t\tzero, (a0)\n\taddi\ta0, a0, 8\n\tbltu\ta0, a1, 1b\n2:\n\t# The stack grows from bottom to top, so we put the stack pointer\n\t# to the very end of the stack range.\n\tla\t\tsp, _stack_end\n\t# Setting `mstatus` register:\n\t# 0b01 << 11: Machine's previous protection mode is 2 (MPP=2).\n\tli\t\tt0, 0b11 << 11\n\tcsrw\tmstatus, t0\n\t# Do not allow interrupts while running kinit\n\tcsrw\tmie, zero\n\t# Machine's exception program counter (MEPC) is set to `kinit`.\n\tla\t\tt1, kinit\n\tcsrw\tmepc, t1\n\t# Set the return address to get us into supervisor mode\n\tla\t\tra, 2f\n\t# We use mret here so that the mstatus register is properly updated.\n\tmret\n2:\n\t# We set the return address (ra above) to this label. When kinit() is finished\n\t# in Rust, it will return here.\n\n\t# Setting `mstatus` (supervisor status) register:\n\t# 0b01 << 11 : Previous protection mode is 1 (MPP=01 [Supervisor]).\n\t# 1 << 7     : Previous machine interrupt-enable bit is 1 (MPIE=1 [Enabled])\n\t# 1 << 5     : Previous interrupt-enable bit is 1 (SPIE=1 [Enabled]).\n\t# We set the \"previous\" bits because the mret will write the current bits\n\t# with the previous bits.\n\tli\t\tt0, (0b01 << 11) | (1 << 7) | (1 << 5)\n\tcsrw\tmstatus, t0\n\t# Machine's trap vector base address is set to `m_trap_vector`, for\n\t# \"machine\" trap vector.\n\tla\t\tt2, m_trap_vector\n\tcsrw\tmtvec, t2\n\t# Setting `stvec` (supervisor trap vector) register:\n\t# Essentially this is a function pointer, but the last two bits can be 00 or 01\n\t# 00        : All exceptions set pc to BASE\n\t# 01        : Asynchronous interrupts set pc to BASE + 4 x scause\n\t# la\t\tt3, s_trap_vector\n\t# csrw\tstvec, t3\n\t# Jump to kmain. We put the MPP = 01 for supervisor mode, so after\n\t# mret, we will jump to kmain in supervisor mode.\n\tla\t\tt1, kmain\n\tcsrw\tmepc, t1\n\t# Setting `sie` (supervisor interrupt enable) register:\n\t# This register takes the same bits as mideleg\n\t# 1 << 1    : Supervisor software interrupt enable (SSIE=1 [Enabled])\n\t# 1 << 5    : Supervisor timer interrupt enable (STIE=1 [Enabled])\n\t# 1 << 9    : Supervisor external interrupt enable (SEIE=1 [Enabled])\n\t# 0xaaa = MEIP/SEIP and MTIP/STIP and MSIP/SSIP\n\tli\t\tt2, 0xaaa\n\tcsrw\tmie, t2\n\tla\t\tra, 4f\n\tmret\n3:\n\n\t# Parked harts go here. We need to set these\n\t# to only awaken if it receives a software interrupt,\n\t# which we're going to call the SIPI (Software Intra-Processor Interrupt).\n\t# We call the SIPI by writing the software interrupt into the Core Local Interruptor (CLINT)\n\t# Which is calculated by: base_address + hart * 4\n\t# where base address is 0x0200_0000 (MMIO CLINT base address)\n\t# We only use additional harts to run user-space programs, although this may\n\t# change.\n\n\t# We divide up the stack so the harts aren't clobbering one another.\n\tla\t\tsp, _stack_end\n\tli\t\tt0, 0x10000\n\tcsrr\ta0, mhartid\n\tmul\t\tt0, t0, a0\n\tsub\t\tsp, sp, t0\n\n\t# The parked harts will be put into machine mode with interrupts enabled.\n\tli\t\tt0, 0b11 << 11 | (1 << 7)\n\tcsrw\tmstatus, t0\n\t# Allow for MSIP (Software interrupt). We will write the MSIP from hart #0 to\n\t# awaken these parked harts.\n\tli\t\tt3, (1 << 3)\n\tcsrw\tmie, t3\n\t# Machine's exception program counter (MEPC) is set to the Rust initialization\n\t# code and waiting loop.\n\tla\t\tt1, kinit_hart\n\tcsrw\tmepc, t1\n\t# Machine's trap vector base address is set to `m_trap_vector`, for\n\t# \"machine\" trap vector. The Rust initialization routines will give each\n\t# hart its own trap frame. We can use the same trap function and distinguish\n\t# between each hart by looking at the trap frame.\n\tla\t\tt2, m_trap_vector\n\tcsrw\tmtvec, t2\n\t# Whenever our hart is done initializing, we want it to return to the waiting\n\t# loop, which is just below mret.\n\tla\t\tra, 4f\n\t# We use mret here so that the mstatus register is properly updated.\n\tmret\n\n4:\n\t# wfi = wait for interrupt. This is a hint to the harts to shut everything needed\n\t# down. However, the RISC-V specification allows for wfi to do nothing. Anyway,\n\t# with QEMU, this will save some CPU!\n\twfi\n\tj\t\t4b\n\n\n"
  },
  {
    "path": "risc_v/chapters/ch5/src/asm/mem.S",
    "content": "// mem.S\n// Importation of linker symbols\n\n.section .rodata\n.global HEAP_START\nHEAP_START: .dword _heap_start\n\n.global HEAP_SIZE\nHEAP_SIZE: .dword _heap_size\n\n.global TEXT_START\nTEXT_START: .dword _text_start\n\n.global TEXT_END\nTEXT_END: .dword _text_end\n\n.global DATA_START\nDATA_START: .dword _data_start\n\n.global DATA_END\nDATA_END: .dword _data_end\n\n.global RODATA_START\nRODATA_START: .dword _rodata_start\n\n.global RODATA_END\nRODATA_END: .dword _rodata_end\n\n.global BSS_START\nBSS_START: .dword _bss_start\n\n.global BSS_END\nBSS_END: .dword _bss_end\n\n.global KERNEL_STACK_START\nKERNEL_STACK_START: .dword _stack_start\n\n.global KERNEL_STACK_END\nKERNEL_STACK_END: .dword _stack_end\n\n\n"
  },
  {
    "path": "risc_v/chapters/ch5/src/asm/trap.S",
    "content": "# trap.S\n# Trap handler and global context\n# Steve Operating System\n# Stephen Marz\n# 24 February 2019\n.option norvc\n.altmacro\n.set NUM_GP_REGS, 32  # Number of registers per context\n.set NUM_FP_REGS, 32\n.set REG_SIZE, 8   # Register size (in bytes)\n.set MAX_CPUS, 8   # Maximum number of CPUs\n\n# Use macros for saving and restoring multiple registers\n.macro save_gp i, basereg=t6\n\tsd\tx\\i, ((\\i)*REG_SIZE)(\\basereg)\n.endm\n.macro load_gp i, basereg=t6\n\tld\tx\\i, ((\\i)*REG_SIZE)(\\basereg)\n.endm\n.macro save_fp i, basereg=t6\n\tfsd\tf\\i, ((NUM_GP_REGS+(\\i))*REG_SIZE)(\\basereg)\n.endm\n.macro load_fp i, basereg=t6\n\tfld\tf\\i, ((NUM_GP_REGS+(\\i))*REG_SIZE)(\\basereg)\n.endm\n\n\n.section .text\n.global m_trap_vector\n# This must be aligned by 4 since the last two bits\n# of the mtvec register do not contribute to the address\n# of this vector.\n.align 4\nm_trap_vector:\n\t# All registers are volatile here, we need to save them\n\t# before we do anything.\n\tcsrrw\tt6, mscratch, t6\n\t# csrrw will atomically swap t6 into mscratch and the old\n\t# value of mscratch into t6. This is nice because we just\n\t# switched values and didn't destroy anything -- all atomically!\n\t# in cpu.rs we have a structure of:\n\t#  32 gp regs\t\t0\n\t#  32 fp regs\t\t256\n\t#  SATP register\t512\n\t#  Trap stack       520\n\t#  CPU HARTID\t\t528\n\t# We use t6 as the temporary register because it is the very\n\t# bottom register (x31)\n\t.set \ti, 1\n\t.rept\t30\n\t\tsave_gp\t%i\n\t\t.set\ti, i+1\n\t.endr\n\n\t# Save the actual t6 register, which we swapped into\n\t# mscratch\n\tmv\t\tt5, t6\n\tcsrr\tt6, mscratch\n\tsave_gp 31, t5\n\n\t# Restore the kernel trap frame into mscratch\n\tcsrw\tmscratch, t5\n\n\t# Get ready to go into Rust (trap.rs)\n\t# We don't want to write into the user's stack or whomever\n\t# messed with us here.\n\tcsrr\ta0, mepc\n\tcsrr\ta1, mtval\n\tcsrr\ta2, mcause\n\tcsrr\ta3, mhartid\n\tcsrr\ta4, mstatus\n\tmv\t\ta5, t5\n\tld\t\tsp, 520(a5)\n\tcall\tm_trap\n\n\t# When we get here, we've returned from m_trap, restore registers\n\t# and return.\n\t# m_trap will return the return address via a0.\n\n\tcsrw\tmepc, a0\n\n\t# Now load the trap frame back into t6\n\tcsrr\tt6, mscratch\n\n\t# Restore all GP registers\n\t.set\ti, 1\n\t.rept\t31\n\t\tload_gp %i\n\t\t.set\ti, i+1\n\t.endr\n\n\t# Since we ran this loop 31 times starting with i = 1,\n\t# the last one loaded t6 back to its original value.\n\n\tmret\n\n\n.global make_syscall\nmake_syscall:\n\tecall\n\tret\n"
  },
  {
    "path": "risc_v/chapters/ch5/src/cpu.rs",
    "content": "// cpu.rs\n// CPU and CPU-related routines\n// Also contains the kernel's trap frame\n// Stephen Marz\n// 14 October 2019\n\nuse core::ptr::null_mut;\n\n/// In 64-bit mode, we're given three different modes for the MMU:\n/// 0 - The MMU is off -- no protection and no translation PA = VA\n/// 8 - This is Sv39 mode -- 39-bit virtual addresses\n/// 9 - This is Sv48 mode -- 48-bit virtual addresses\n#[repr(usize)]\npub enum SatpMode {\n\tOff = 0,\n\tSv39 = 8,\n\tSv48 = 9,\n}\n\n/// The trap frame is set into a structure\n/// and packed into each hart's mscratch register.\n/// This allows for quick reference and full\n/// context switch handling.\n#[repr(C)]\n#[derive(Clone, Copy)]\npub struct TrapFrame {\n\tpub regs:       [usize; 32], // 0 - 255\n\tpub fregs:      [usize; 32], // 256 - 511\n\tpub satp:       usize,       // 512 - 519\n\tpub trap_stack: *mut u8,     // 520\n\tpub hartid:     usize,       // 528\n}\n\n/// Rust requires that we initialize our structures\n/// because of the move semantics. What'll happen below\n/// is Rust will construct a new TrapFrame and move it\n/// out of the zero() function below. Rust contains two\n/// different \"selfs\" where self can refer to the object\n/// in memory or Self (capital S) which refers to the\n/// data type of the structure. In the case below, this\n/// is TrapFrame.\nimpl TrapFrame {\n\tpub const fn zero() -> Self {\n\t\tTrapFrame { regs:       [0; 32],\n\t\t            fregs:      [0; 32],\n\t\t            satp:       0,\n\t\t            trap_stack: null_mut(),\n\t\t             hartid:     0, }\n\t}\n}\n\n/// The global kernel trap frame stores 8 separate\n/// frames -- one per CPU hart. We will switch these\n/// in and out and store a dormant trap frame with\n/// the process itself.\npub static mut KERNEL_TRAP_FRAME: [TrapFrame; 8] =\n\t[TrapFrame::zero(); 8];\n\n/// The SATP register contains three fields: mode, address space id, and\n/// the first level table address (level 2 for Sv39). This function\n/// helps make the 64-bit register contents based on those three\n/// fields.\npub const fn build_satp(mode: SatpMode, asid: usize, addr: usize) -> usize {\n\t(mode as usize) << 60\n\t| (asid & 0xffff) << 44\n\t| (addr >> 12) & 0xff_ffff_ffff\n}\n\npub fn mhartid_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, mhartid\" :\"=r\"(rval));\n\t\trval\n\t}\n}\npub fn mie_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, mie\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn mie_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw mie, $0\" :: \"r\"(val));\n\t}\n}\n\npub fn mstatus_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tmstatus, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn mstatus_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, mstatus\":\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn stvec_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tstvec, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn stvec_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, stvec\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn mscratch_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tmscratch, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn mscratch_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, mscratch\" : \"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn mscratch_swap(to: usize) -> usize {\n\tunsafe {\n\t\tlet from;\n\t\tasm!(\"csrrw\t$0, mscratch, $1\" : \"=r\"(from) : \"r\"(to));\n\t\tfrom\n\t}\n}\n\npub fn sscratch_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tsscratch, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn sscratch_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, sscratch\" : \"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn sscratch_swap(to: usize) -> usize {\n\tunsafe {\n\t\tlet from;\n\t\tasm!(\"csrrw\t$0, sscratch, $1\" : \"=r\"(from) : \"r\"(to));\n\t\tfrom\n\t}\n}\n\npub fn sepc_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw sepc, $0\" :: \"r\"(val));\n\t}\n}\n\npub fn sepc_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, sepc\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn satp_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw satp, $0\" :: \"r\"(val));\n\t}\n}\n\npub fn satp_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, satp\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\n/// Take a hammer to the page tables and synchronize\n/// all of them. This essentially flushes the entire\n/// TLB.\npub fn satp_fence(vaddr: usize, asid: usize) {\n\tunsafe {\n\t\tasm!(\"sfence.vma $0, $1\" :: \"r\"(vaddr), \"r\"(asid));\n\t}\n}\n\n/// Synchronize based on the address space identifier\n/// This allows us to fence a particular process rather\n/// than the entire TLB.\n/// The RISC-V documentation calls this a TLB flush +.\n/// Since there are other memory routines involved, they\n/// didn't call it a TLB flush, but it is much like\n/// Intel/AMD's invtlb [] instruction.\npub fn satp_fence_asid(asid: usize) {\n\tunsafe {\n\t\tasm!(\"sfence.vma zero, $0\" :: \"r\"(asid));\n\t}\n}\n"
  },
  {
    "path": "risc_v/chapters/ch5/src/kmem.rs",
    "content": "// kmem.rs\n// Sub-page level: malloc-like allocation system\n// Stephen Marz\n// 7 October 2019\n\nuse crate::page::{align_val, zalloc, Table, PAGE_SIZE};\nuse core::{mem::size_of, ptr::null_mut};\n\n#[repr(usize)]\nenum AllocListFlags {\n\tTaken = 1 << 63,\n}\nimpl AllocListFlags {\n\tpub fn val(self) -> usize {\n\t\tself as usize\n\t}\n}\n\nstruct AllocList {\n\tpub flags_size: usize,\n}\nimpl AllocList {\n\tpub fn is_taken(&self) -> bool {\n\t\tself.flags_size & AllocListFlags::Taken.val() != 0\n\t}\n\n\tpub fn is_free(&self) -> bool {\n\t\t!self.is_taken()\n\t}\n\n\tpub fn set_taken(&mut self) {\n\t\tself.flags_size |= AllocListFlags::Taken.val();\n\t}\n\n\tpub fn set_free(&mut self) {\n\t\tself.flags_size &= !AllocListFlags::Taken.val();\n\t}\n\n\tpub fn set_size(&mut self, sz: usize) {\n\t\tlet k = self.is_taken();\n\t\tself.flags_size = sz & !AllocListFlags::Taken.val();\n\t\tif k {\n\t\t\tself.flags_size |= AllocListFlags::Taken.val();\n\t\t}\n\t}\n\n\tpub fn get_size(&self) -> usize {\n\t\tself.flags_size & !AllocListFlags::Taken.val()\n\t}\n}\n\n// This is the head of the allocation. We start here when\n// we search for a free memory location.\nstatic mut KMEM_HEAD: *mut AllocList = null_mut();\n// In the future, we will have on-demand pages\n// so, we need to keep track of our memory footprint to\n// see if we actually need to allocate more.\nstatic mut KMEM_ALLOC: usize = 0;\nstatic mut KMEM_PAGE_TABLE: *mut Table = null_mut();\n\n// These functions are safe helpers around an unsafe\n// operation.\npub fn get_head() -> *mut u8 {\n\tunsafe { KMEM_HEAD as *mut u8 }\n}\n\npub fn get_page_table() -> *mut Table {\n\tunsafe { KMEM_PAGE_TABLE as *mut Table }\n}\n\npub fn get_num_allocations() -> usize {\n\tunsafe { KMEM_ALLOC }\n}\n\n/// Initialize kernel's memory\n/// This is not to be used to allocate memory\n/// for user processes. If that's the case, use\n/// alloc/dealloc from the page crate.\npub fn init() {\n\tunsafe {\n\t\t// Allocate kernel pages (KMEM_ALLOC)\n\t\tKMEM_ALLOC = 512;\n\t\tlet k_alloc = zalloc(KMEM_ALLOC);\n\t\tassert!(!k_alloc.is_null());\n\t\tKMEM_HEAD = k_alloc as *mut AllocList;\n\t\t(*KMEM_HEAD).set_free();\n\t\t(*KMEM_HEAD).set_size(KMEM_ALLOC * PAGE_SIZE);\n\t\tKMEM_PAGE_TABLE = zalloc(1) as *mut Table;\n\t}\n}\n\n/// Allocate sub-page level allocation based on bytes and zero the memory\npub fn kzmalloc(sz: usize) -> *mut u8 {\n\tlet size = align_val(sz, 3);\n\tlet ret = kmalloc(size);\n\n\tif !ret.is_null() {\n\t\tfor i in 0..size {\n\t\t\tunsafe {\n\t\t\t\t(*ret.add(i)) = 0;\n\t\t\t}\n\t\t}\n\t}\n\tret\n}\n\n/// Allocate sub-page level allocation based on bytes\npub fn kmalloc(sz: usize) -> *mut u8 {\n\tunsafe {\n\t\tlet size = align_val(sz, 3) + size_of::<AllocList>();\n\t\tlet mut head = KMEM_HEAD;\n\t\t// .add() uses pointer arithmetic, so we type-cast into a u8\n\t\t// so that we multiply by an absolute size (KMEM_ALLOC *\n\t\t// PAGE_SIZE).\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\n\t\twhile head < tail {\n\t\t\tif (*head).is_free() && size <= (*head).get_size() {\n\t\t\t\tlet chunk_size = (*head).get_size();\n\t\t\t\tlet rem = chunk_size - size;\n\t\t\t\t(*head).set_taken();\n\t\t\t\tif rem > size_of::<AllocList>() {\n\t\t\t\t\tlet next = (head as *mut u8).add(size)\n\t\t\t\t\t           as *mut AllocList;\n\t\t\t\t\t// There is space remaining here.\n\t\t\t\t\t(*next).set_free();\n\t\t\t\t\t(*next).set_size(rem);\n\t\t\t\t\t(*head).set_size(size);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// If we get here, take the entire chunk\n\t\t\t\t\t(*head).set_size(chunk_size);\n\t\t\t\t}\n\t\t\t\treturn head.add(1) as *mut u8;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// If we get here, what we saw wasn't a free\n\t\t\t\t// chunk, move on to the next.\n\t\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t\t       as *mut AllocList;\n\t\t\t}\n\t\t}\n\t}\n\t// If we get here, we didn't find any free chunks--i.e. there isn't\n\t// enough memory for this. TODO: Add on-demand page allocation.\n\tnull_mut()\n}\n\n/// Free a sub-page level allocation\npub fn kfree(ptr: *mut u8) {\n\tunsafe {\n\t\tif !ptr.is_null() {\n\t\t\tlet p = (ptr as *mut AllocList).offset(-1);\n\t\t\tif (*p).is_taken() {\n\t\t\t\t(*p).set_free();\n\t\t\t}\n\t\t\t// After we free, see if we can combine adjacent free\n\t\t\t// spots to see if we can reduce fragmentation.\n\t\t\tcoalesce();\n\t\t}\n\t}\n}\n\n/// Merge smaller chunks into a bigger chunk\npub fn coalesce() {\n\tunsafe {\n\t\tlet mut head = KMEM_HEAD;\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\n\t\twhile head < tail {\n\t\t\tlet next = (head as *mut u8).add((*head).get_size())\n\t\t\t           as *mut AllocList;\n\t\t\tif (*head).get_size() == 0 {\n\t\t\t\t// If this happens, then we have a bad heap\n\t\t\t\t// (double free or something). However, that\n\t\t\t\t// will cause an infinite loop since the next\n\t\t\t\t// pointer will never move beyond the current\n\t\t\t\t// location.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if next >= tail {\n\t\t\t\t// We calculated the next by using the size\n\t\t\t\t// given as get_size(), however this could push\n\t\t\t\t// us past the tail. In that case, the size is\n\t\t\t\t// wrong, hence we break and stop doing what we\n\t\t\t\t// need to do.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (*head).is_free() && (*next).is_free() {\n\t\t\t\t// This means we have adjacent blocks needing to\n\t\t\t\t// be freed. So, we combine them into one\n\t\t\t\t// allocation.\n\t\t\t\t(*head).set_size(\n\t\t\t\t                 (*head).get_size()\n\t\t\t\t                 + (*next).get_size(),\n\t\t\t\t);\n\t\t\t}\n\t\t\t// If we get here, we might've moved. Recalculate new\n\t\t\t// head.\n\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t       as *mut AllocList;\n\t\t}\n\t}\n}\n\n/// For debugging purposes, print the kmem table\npub fn print_table() {\n\tunsafe {\n\t\tlet mut head = KMEM_HEAD;\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\t\twhile head < tail {\n\t\t\tprintln!(\n\t\t\t         \"{:p}: Length = {:<10} Taken = {}\",\n\t\t\t         head,\n\t\t\t         (*head).get_size(),\n\t\t\t         (*head).is_taken()\n\t\t\t);\n\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t       as *mut AllocList;\n\t\t}\n\t}\n}\n\n// ///////////////////////////////////\n// / GLOBAL ALLOCATOR\n// ///////////////////////////////////\n\n// The global allocator allows us to use the data structures\n// in the core library, such as a linked list or B-tree.\n// We want to use these sparingly since we have a coarse-grained\n// allocator.\nuse core::alloc::{GlobalAlloc, Layout};\n\n// The global allocator is a static constant to a global allocator\n// structure. We don't need any members because we're using this\n// structure just to implement alloc and dealloc.\nstruct OsGlobalAlloc;\n\nunsafe impl GlobalAlloc for OsGlobalAlloc {\n\tunsafe fn alloc(&self, layout: Layout) -> *mut u8 {\n\t\t// We align to the next page size so that when\n\t\t// we divide by PAGE_SIZE, we get exactly the number\n\t\t// of pages necessary.\n\t\tkzmalloc(layout.size())\n\t}\n\n\tunsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {\n\t\t// We ignore layout since our allocator uses ptr_start -> last\n\t\t// to determine the span of an allocation.\n\t\tkfree(ptr);\n\t}\n}\n\n#[global_allocator]\n/// Technically, we don't need the {} at the end, but it\n/// reveals that we're creating a new structure and not just\n/// copying a value.\nstatic GA: OsGlobalAlloc = OsGlobalAlloc {};\n\n#[alloc_error_handler]\n/// If for some reason alloc() in the global allocator gets null_mut(),\n/// then we come here. This is a divergent function, so we call panic to\n/// let the tester know what's going on.\npub fn alloc_error(l: Layout) -> ! {\n\tpanic!(\n\t       \"Allocator failed to allocate {} bytes with {}-byte alignment.\",\n\t       l.size(),\n\t       l.align()\n\t);\n}\n"
  },
  {
    "path": "risc_v/chapters/ch5/src/lds/virt.lds",
    "content": "/*\n virt.lds\n Linker script for outputting to RISC-V QEMU \"virt\" machine.\n Stephen Marz\n 6 October 2019\n*/\n\n/*\n  riscv is the name of the architecture that the linker understands\n  for any RISC-V target (64-bit or 32-bit).\n\n  We will further refine this by using -mabi=lp64 and -march=rv64gc\n*/\nOUTPUT_ARCH( \"riscv\" )\n\n/*\nWe're setting our entry point to a symbol\ncalled _start which is inside of boot.S. This\nessentially stores the address of _start as the\n\"entry point\", or where CPU instructions should start\nexecuting.\n\nIn the rest of this script, we are going to place _start\nright at the beginning of 0x8000_0000 because this is where\nthe virtual machine and many RISC-V boards will start executing.\n*/\nENTRY( _start )\n\n/*\nThe MEMORY section will explain that we have \"ram\" that contains\na section that is 'w' (writeable), 'x' (executable), and 'a' (allocatable).\nWe use '!' to invert 'r' (read-only) and 'i' (initialized). We don't want\nour memory to be read-only, and we're stating that it is NOT initialized\nat the beginning.\n\nThe ORIGIN is the memory address 0x8000_0000. If we look at the virt\nspec or the specification for the RISC-V HiFive Unleashed, this is the\nstarting memory address for our code.\n\nSide note: There might be other boot ROMs at different addresses, but\ntheir job is to get to this point.\n\nFinally LENGTH = 128M tells the linker that we have 128 megabyte of RAM.\nThe linker will double check this to make sure everything can fit.\n\nThe HiFive Unleashed has a lot more RAM than this, but for the virtual \nmachine, I went with 128M since I think that's enough RAM for now.\n\nWe can provide other pieces of memory, such as QSPI, or ROM, but we're\ntelling the linker script here that we have one pool of RAM.\n*/\nMEMORY\n{\n  ram   (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M\n}\n\n/*\nPHDRS is short for \"program headers\", which we specify three here:\ntext - CPU instructions (executable sections)\ndata - Global, initialized variables\nbss  - Global, uninitialized variables (all will be set to 0 by boot.S)\n\nThe command PT_LOAD tells the linker that these sections will be loaded\nfrom the file into memory.\n\nWe can actually stuff all of these into a single program header, but by\nsplitting it up into three, we can actually use the other PT_* commands\nsuch as PT_DYNAMIC, PT_INTERP, PT_NULL to tell the linker where to find\nadditional information.\n\nHowever, for our purposes, every section will be loaded from the program\nheaders.\n*/\nPHDRS\n{\n  text PT_LOAD;\n  data PT_LOAD;\n  bss PT_LOAD;\n}\n\n/*\nWe are now going to organize the memory based on which\nsection it is in. In assembly, we can change the section\nwith the \".section\" directive. However, in C++ and Rust,\nCPU instructions go into text, global constants go into\nrodata, global initialized variables go into data, and\nglobal uninitialized variables go into bss.\n*/\nSECTIONS\n{\n  /*\n    The first part of our RAM layout will be the text section.\n\tSince our CPU instructions are here, and our memory starts at\n\t0x8000_0000, we need our entry point to line up here.\n  */\n  .text : {\n\t  /* \n\t    PROVIDE allows me to access a symbol called _text_start so\n\t\tI know where the text section starts in the operating system.\n\t\tThis should not move, but it is here for convenience.\n\t\tThe period '.' tells the linker to set _text_start to the\n\t\tCURRENT location ('.' = current memory location). This current\n\t\tmemory location moves as we add things.\n\t  */\n\n    PROVIDE(_text_start = .);\n\t/*\n\t  We are going to layout all text sections here, starting with \n\t  .text.init. The asterisk in front of the parentheses means to match\n\t  the .text.init section of ANY object file. Otherwise, we can specify\n\t  which object file should contain the .text.init section, for example,\n\t  boot.o(.text.init) would specifically put the .text.init section of\n\t  our bootloader here.\n\n\t  Because we might want to change the name of our files, we'll leave it\n\t  with a *.\n\n\t  Inside the parentheses is the name of the section. I created my own\n\t  called .text.init to make 100% sure that the _start is put right at the\n\t  beginning. The linker will lay this out in the order it receives it:\n\n\t  .text.init first\n\t  all .text sections next\n\t  any .text.* sections last\n\n\t  .text.* means to match anything after .text. If we didn't already specify\n\t  .text.init, this would've matched here. The assembler and linker can place\n\t  things in \"special\" text sections, so we match any we might come across here.\n\t*/\n    *(.text.init) *(.text .text.*)\n\n\t/*\n\t  Again, with PROVIDE, we're providing a readable symbol called _text_end, which is\n\t  set to the memory address AFTER .text.init, .text, and .text.*'s have been added.\n\t*/\n    PROVIDE(_text_end = .);\n\t/*\n\t  The portion after the right brace is in an odd format. However, this is telling the\n\t  linker what memory portion to put it in. We labeled our RAM, ram, with the constraints\n\t  that it is writeable, allocatable, and executable. The linker will make sure with this\n\t  that we can do all of those things.\n\n\t  >ram - This just tells the linker script to put this entire section (.text) into the\n\t         ram region of memory. To my knowledge, the '>' does not mean \"greater than\". Instead,\n\t\t\t it is a symbol to let the linker know we want to put this in ram.\n\n\t  AT>ram - This sets the LMA (load memory address) region to the same thing. LMA is the final\n\t           translation of a VMA (virtual memory address). With this linker script, we're loading\n\t\t\t   everything into its physical location. We'll let the kernel copy and sort out the \n\t\t\t   virtual memory. That's why >ram and AT>ram are continually the same thing.\n\n\t  :text  - This tells the linker script to put this into the :text program header. We've only\n\t           defined three: text, data, and bss. In this case, we're telling the linker script\n\t\t\t   to go into the text section.\n\t*/\n  } >ram AT>ram :text\n   /*\n     The global pointer allows the linker to position global variables and constants into\n\t independent positions relative to the gp (global pointer) register. The globals start\n\t after the text sections and are only relevant to the rodata, data, and bss sections.\n   */\n   PROVIDE(_global_pointer = .);\n   /*\n     Most compilers create a rodata (read only data) section for global constants. However,\n\t we're going to place ours in the text section. We can actually put this in :data, but\n\t since the .text section is read-only, we can place it there.\n\n\t NOTE: This doesn't actually do anything, yet. The actual \"protection\" cannot be done\n\t at link time. Instead, when we program the memory management unit (MMU), we will be\n\t able to choose which bits (R=read, W=write, X=execute) we want each memory segment\n\t to be able to do.\n   */\n  .rodata : {\n    PROVIDE(_rodata_start = .);\n    *(.rodata .rodata.*)\n    PROVIDE(_rodata_end = .);\n\t/*\n\t   Again, we're placing the rodata section in the memory segment \"ram\" and we're putting\n\t   it in the :text program header. We don't have one for rodata anyway.\n\t*/\n  } >ram AT>ram :text\n\n  .data : {\n\t/*\n\t   . = ALIGN(4096) tells the linker to align the current memory location (which is\n\t   0x8000_0000 + text section + rodata section) to 4096 bytes. This is because our paging\n\t   system's resolution is 4,096 bytes or 4 KiB.\n\t*/\n    . = ALIGN(4096);\n    PROVIDE(_data_start = .);\n\t/*\n\t   sdata and data are essentially the same thing. However, compilers usually use the\n\t   sdata sections for shorter, quicker loading sections. So, usually critical data\n\t   is loaded there. However, we're loading all of this in one fell swoop.\n\t   So, we're looking to put all of the following sections under the umbrella .data:\n\t   .sdata\n\t   .sdata.[anything]\n\t   .data\n\t   .data.[anything]\n\n\t   ...in that order.\n\t*/\n    *(.sdata .sdata.*) *(.data .data.*)\n    PROVIDE(_data_end = .);\n  } >ram AT>ram :data\n\n  .bss : {\n    PROVIDE(_bss_start = .);\n    *(.sbss .sbss.*) *(.bss .bss.*)\n    PROVIDE(_bss_end = .);\n  } >ram AT>ram :bss\n\n  /*\n     The following will be helpful when we allocate the kernel stack (_stack) and\n\t determine where the heap begnis and ends (_heap_start and _heap_start + _heap_size)/\n\t When we do memory allocation, we can use these symbols.\n\n\t We use the symbols instead of hard-coding an address because this is a floating target.\n\t As we add code, the heap moves farther down the memory and gets shorter.\n\n\t _memory_start will be set to 0x8000_0000 here. We use ORIGIN(ram) so that it will take\n\t whatever we set the origin of ram to. Otherwise, we'd have to change it more than once\n\t if we ever stray away from 0x8000_0000 as our entry point.\n  */\n  PROVIDE(_memory_start = ORIGIN(ram));\n  /*\n     Our kernel stack starts at the end of the bss segment (_bss_end). However, we're allocating\n\t 0x80000 bytes (524 KiB) to our kernel stack. This should be PLENTY of space. The reason\n\t we add the memory is because the stack grows from higher memory to lower memory (bottom to top).\n\t Therefore we set the stack at the very bottom of its allocated slot.\n\t When we go to allocate from the stack, we'll subtract the number of bytes we need.\n  */\n  PROVIDE(_stack_start = _bss_end);\n  PROVIDE(_stack_end = _stack_start + 0x8000);\n  PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));\n\n  /* \n     Finally, our heap starts right after the kernel stack. This heap will be used mainly\n\t to dole out memory for user-space applications. However, in some circumstances, it will\n\t be used for kernel memory as well.\n\n\t We don't align here because we let the kernel determine how it wants to do this.\n  */\n  PROVIDE(_heap_start = _stack_end);\n  PROVIDE(_heap_size = _memory_end - _heap_start);\n}\n"
  },
  {
    "path": "risc_v/chapters/ch5/src/lib.rs",
    "content": "// Steve Operating System\n// Stephen Marz\n// 21 Sep 2019\n#![no_std]\n#![feature(panic_info_message,\n           asm,\n           allocator_api,\n           alloc_error_handler,\n           alloc_prelude,\n           const_raw_ptr_to_usize_cast)]\n\n#[macro_use]\nextern crate alloc;\n// This is experimental and requires alloc_prelude as a feature\nuse alloc::prelude::v1::*;\n\n// ///////////////////////////////////\n// / RUST MACROS\n// ///////////////////////////////////\n#[macro_export]\nmacro_rules! print\n{\n\t($($args:tt)+) => ({\n\t\t\tuse core::fmt::Write;\n\t\t\tlet _ = write!(crate::uart::Uart::new(0x1000_0000), $($args)+);\n\t\t\t});\n}\n#[macro_export]\nmacro_rules! println\n{\n\t() => ({\n\t\t   print!(\"\\r\\n\")\n\t\t   });\n\t($fmt:expr) => ({\n\t\t\tprint!(concat!($fmt, \"\\r\\n\"))\n\t\t\t});\n\t($fmt:expr, $($args:tt)+) => ({\n\t\t\tprint!(concat!($fmt, \"\\r\\n\"), $($args)+)\n\t\t\t});\n}\n\n// ///////////////////////////////////\n// / LANGUAGE STRUCTURES / FUNCTIONS\n// ///////////////////////////////////\n#[no_mangle]\nextern \"C\" fn eh_personality() {}\n\n#[panic_handler]\nfn panic(info: &core::panic::PanicInfo) -> ! {\n\tprint!(\"Aborting: \");\n\tif let Some(p) = info.location() {\n\t\tprintln!(\n\t\t         \"line {}, file {}: {}\",\n\t\t         p.line(),\n\t\t         p.file(),\n\t\t         info.message().unwrap()\n\t\t);\n\t}\n\telse {\n\t\tprintln!(\"no information available.\");\n\t}\n\tabort();\n}\n#[no_mangle]\nextern \"C\" fn abort() -> ! {\n\tloop {\n\t\tunsafe {\n\t\t\tasm!(\"wfi\"::::\"volatile\");\n\t\t}\n\t}\n}\n\n// ///////////////////////////////////\n// / CONSTANTS\n// ///////////////////////////////////\n// const STR_Y: &str = \"\\x1b[38;2;79;221;13m✓\\x1b[m\";\n// const STR_N: &str = \"\\x1b[38;2;221;41;13m✘\\x1b[m\";\n\n// The following symbols come from asm/mem.S. We can use\n// the symbols directly, but the address of the symbols\n// themselves are their values, which can cause issues.\n// Instead, I created doubleword values in mem.S in the .rodata and .data\n// sections.\nextern \"C\" {\n\tstatic TEXT_START: usize;\n\tstatic TEXT_END: usize;\n\tstatic DATA_START: usize;\n\tstatic DATA_END: usize;\n\tstatic RODATA_START: usize;\n\tstatic RODATA_END: usize;\n\tstatic BSS_START: usize;\n\tstatic BSS_END: usize;\n\tstatic KERNEL_STACK_START: usize;\n\tstatic KERNEL_STACK_END: usize;\n\tstatic HEAP_START: usize;\n\tstatic HEAP_SIZE: usize;\n}\n/// Identity map range\n/// Takes a contiguous allocation of memory and maps it using PAGE_SIZE\n/// This assumes that start <= end\npub fn id_map_range(root: &mut page::Table,\n                    start: usize,\n                    end: usize,\n                    bits: i64)\n{\n\tlet mut memaddr = start & !(page::PAGE_SIZE - 1);\n\tlet num_kb_pages =\n\t\t(page::align_val(end, 12) - memaddr) / page::PAGE_SIZE;\n\n\t// I named this num_kb_pages for future expansion when\n\t// I decide to allow for GiB (2^30) and 2MiB (2^21) page\n\t// sizes. However, the overlapping memory regions are causing\n\t// nightmares.\n\tfor _ in 0..num_kb_pages {\n\t\tpage::map(root, memaddr, memaddr, bits, 0);\n\t\tmemaddr += 1 << 12;\n\t}\n}\n// ///////////////////////////////////\n// / ENTRY POINT\n// ///////////////////////////////////\n#[no_mangle]\nextern \"C\" fn kinit() {\n\t// We created kinit, which runs in super-duper mode\n\t// 3 called \"machine mode\".\n\t// The job of kinit() is to get us into supervisor mode\n\t// as soon as possible.\n\t// Interrupts are disabled for the duration of kinit()\n\tuart::Uart::new(0x1000_0000).init();\n\tpage::init();\n\tkmem::init();\n\n\t// Map heap allocations\n\tlet root_ptr = kmem::get_page_table();\n\tlet root_u = root_ptr as usize;\n\tlet mut root = unsafe { root_ptr.as_mut().unwrap() };\n\tlet kheap_head = kmem::get_head() as usize;\n\tlet total_pages = kmem::get_num_allocations();\n\tprintln!();\n\tprintln!();\n\tunsafe {\n\t\tprintln!(\"TEXT:   0x{:x} -> 0x{:x}\", TEXT_START, TEXT_END);\n\t\tprintln!(\"RODATA: 0x{:x} -> 0x{:x}\", RODATA_START, RODATA_END);\n\t\tprintln!(\"DATA:   0x{:x} -> 0x{:x}\", DATA_START, DATA_END);\n\t\tprintln!(\"BSS:    0x{:x} -> 0x{:x}\", BSS_START, BSS_END);\n\t\tprintln!(\n\t\t         \"STACK:  0x{:x} -> 0x{:x}\",\n\t\t         KERNEL_STACK_START, KERNEL_STACK_END\n\t\t);\n\t\tprintln!(\n\t\t         \"HEAP:   0x{:x} -> 0x{:x}\",\n\t\t         kheap_head,\n\t\t         kheap_head + total_pages * page::PAGE_SIZE\n\t\t);\n\t}\n\tid_map_range(\n\t             &mut root,\n\t             kheap_head,\n\t             kheap_head + total_pages * page::PAGE_SIZE,\n\t             page::EntryBits::ReadWrite.val(),\n\t);\n\t// Using statics is inherently unsafe.\n\tunsafe {\n\t\t// Map heap descriptors\n\t\tlet num_pages = HEAP_SIZE / page::PAGE_SIZE;\n\t\tid_map_range(\n\t\t             &mut root,\n\t\t             HEAP_START,\n\t\t             HEAP_START + num_pages,\n\t\t             page::EntryBits::ReadWrite.val(),\n\t\t);\n\t\t// Map executable section\n\t\tid_map_range(\n\t\t             &mut root,\n\t\t             TEXT_START,\n\t\t             TEXT_END,\n\t\t             page::EntryBits::ReadExecute.val(),\n\t\t);\n\t\t// Map rodata section\n\t\t// We put the ROdata section into the text section, so they can\n\t\t// potentially overlap however, we only care that it's read\n\t\t// only.\n\t\tid_map_range(\n\t\t             &mut root,\n\t\t             RODATA_START,\n\t\t             RODATA_END,\n\t\t             page::EntryBits::ReadExecute.val(),\n\t\t);\n\t\t// Map data section\n\t\tid_map_range(\n\t\t             &mut root,\n\t\t             DATA_START,\n\t\t             DATA_END,\n\t\t             page::EntryBits::ReadWrite.val(),\n\t\t);\n\t\t// Map bss section\n\t\tid_map_range(\n\t\t             &mut root,\n\t\t             BSS_START,\n\t\t             BSS_END,\n\t\t             page::EntryBits::ReadWrite.val(),\n\t\t);\n\t\t// Map kernel stack\n\t\tid_map_range(\n\t\t             &mut root,\n\t\t             KERNEL_STACK_START,\n\t\t             KERNEL_STACK_END,\n\t\t             page::EntryBits::ReadWrite.val(),\n\t\t);\n\t}\n\n\t// UART\n\tid_map_range(\n\t             &mut root,\n\t             0x1000_0000,\n\t             0x1000_0100,\n\t             page::EntryBits::ReadWrite.val(),\n\t);\n\n\t// CLINT\n\t//  -> MSIP\n\tid_map_range(\n\t             &mut root,\n\t             0x0200_0000,\n\t             0x0200_ffff,\n\t             page::EntryBits::ReadWrite.val(),\n\t);\n\t// PLIC\n\tid_map_range(\n\t             &mut root,\n\t             0x0c00_0000,\n\t             0x0c00_2001,\n\t             page::EntryBits::ReadWrite.val(),\n\t);\n\tid_map_range(\n\t             &mut root,\n\t             0x0c20_0000,\n\t             0x0c20_8001,\n\t             page::EntryBits::ReadWrite.val(),\n\t);\n\t// When we return from here, we'll go back to boot.S and switch into\n\t// supervisor mode We will return the SATP register to be written when\n\t// we return. root_u is the root page table's address. When stored into\n\t// the SATP register, this is divided by 4 KiB (right shift by 12 bits).\n\t// We enable the MMU by setting mode 8. Bits 63, 62, 61, 60 determine\n\t// the mode.\n\t// 0 = Bare (no translation)\n\t// 8 = Sv39\n\t// 9 = Sv48\n\t// build_satp has these parameters: mode, asid, page table address.\n\tlet satp_value = cpu::build_satp(cpu::SatpMode::Sv39, 0, root_u);\n\tunsafe {\n\t\t// We have to store the kernel's table. The tables will be moved\n\t\t// back and forth between the kernel's table and user\n\t\t// applicatons' tables. Note that we're writing the physical address\n\t\t// of the trap frame.\n\t\tcpu::mscratch_write(\n\t\t                    (&mut cpu::KERNEL_TRAP_FRAME[0]\n\t\t                     as *mut cpu::TrapFrame)\n\t\t                    as usize,\n\t\t);\n\t\tcpu::sscratch_write(cpu::mscratch_read());\n\t\tcpu::KERNEL_TRAP_FRAME[0].satp = satp_value;\n\t\t// Move the stack pointer to the very bottom. The stack is\n\t\t// actually in a non-mapped page. The stack is decrement-before\n\t\t// push and increment after pop. Therefore, the stack will be\n\t\t// allocated (decremented) before it is stored.\n\t\tcpu::KERNEL_TRAP_FRAME[0].trap_stack =\n\t\t\tpage::zalloc(1).add(page::PAGE_SIZE);\n\t\tid_map_range(\n\t\t             &mut root,\n\t\t             cpu::KERNEL_TRAP_FRAME[0].trap_stack\n\t\t                                      .sub(page::PAGE_SIZE,)\n\t\t             as usize,\n\t\t             cpu::KERNEL_TRAP_FRAME[0].trap_stack as usize,\n\t\t             page::EntryBits::ReadWrite.val(),\n\t\t);\n\t\t// The trap frame itself is stored in the mscratch register.\n\t\tid_map_range(\n\t\t             &mut root,\n\t\t             cpu::mscratch_read(),\n\t\t             cpu::mscratch_read()\n\t\t             + core::mem::size_of::<cpu::TrapFrame,>(),\n\t\t             page::EntryBits::ReadWrite.val(),\n\t\t);\n\t\tpage::print_page_allocations();\n\t\tlet p = cpu::KERNEL_TRAP_FRAME[0].trap_stack as usize - 1;\n\t\tlet m = page::virt_to_phys(&root, p).unwrap_or(0);\n\t\tprintln!(\"Walk 0x{:x} = 0x{:x}\", p, m);\n\t}\n\t// The following shows how we're going to walk to translate a virtual\n\t// address into a physical address. We will use this whenever a user\n\t// space application requires services. Since the user space application\n\t// only knows virtual addresses, we have to translate silently behind\n\t// the scenes.\n\tprintln!(\"Setting 0x{:x}\", satp_value);\n\tprintln!(\"Scratch reg = 0x{:x}\", cpu::mscratch_read());\n\tcpu::satp_write(satp_value);\n\tcpu::satp_fence_asid(0);\n}\n\n#[no_mangle]\nextern \"C\" fn kinit_hart(hartid: usize) {\n\t// All non-0 harts initialize here.\n\tunsafe {\n\t\t// We have to store the kernel's table. The tables will be moved\n\t\t// back and forth between the kernel's table and user\n\t\t// applicatons' tables.\n\t\tcpu::mscratch_write(\n\t\t                    (&mut cpu::KERNEL_TRAP_FRAME[hartid]\n\t\t                     as *mut cpu::TrapFrame)\n\t\t                    as usize,\n\t\t);\n\t\t// Copy the same mscratch over to the supervisor version of the\n\t\t// same register.\n\t\tcpu::sscratch_write(cpu::mscratch_read());\n\t\tcpu::KERNEL_TRAP_FRAME[hartid].hartid = hartid;\n\t\t// We can't do the following until zalloc() is locked, but we\n\t\t// don't have locks, yet :( cpu::KERNEL_TRAP_FRAME[hartid].satp\n\t\t// = cpu::KERNEL_TRAP_FRAME[0].satp;\n\t\t// cpu::KERNEL_TRAP_FRAME[hartid].trap_stack = page::zalloc(1);\n\t}\n}\n\n#[no_mangle]\nextern \"C\" fn kmain() {\n\t// kmain() starts in supervisor mode. So, we should have the trap\n\t// vector setup and the MMU turned on when we get here.\n\n\t// We initialized my_uart in machine mode under kinit for debugging\n\t// prints, but this just grabs a pointer to it.\n\tlet mut my_uart = uart::Uart::new(0x1000_0000);\n\t// Create a new scope so that we can test the global allocator and\n\t// deallocator\n\t{\n\t\t// We have the global allocator, so let's see if that works!\n\t\tlet k = Box::<u32>::new(100);\n\t\tprintln!(\"Boxed value = {}\", *k);\n\t\t// The following comes from the Rust documentation:\n\t\t// some bytes, in a vector\n\t\tlet sparkle_heart = vec![240, 159, 146, 150];\n\t\t// We know these bytes are valid, so we'll use `unwrap()`.\n\t\t// This will MOVE the vector.\n\t\tlet sparkle_heart = String::from_utf8(sparkle_heart).unwrap();\n\t\tprintln!(\"String = {}\", sparkle_heart);\n\t\tprintln!(\"\\n\\nAllocations of a box, vector, and string\");\n\t\tkmem::print_table();\n\t}\n\tprintln!(\"\\n\\nEverything should now be free:\");\n\tkmem::print_table();\n\n\tunsafe {\n\t\t// Set the next machine timer to fire.\n\t\tlet mtimecmp = 0x0200_4000 as *mut u64;\n\t\tlet mtime = 0x0200_bff8 as *const u64;\n\t\t// The frequency given by QEMU is 10_000_000 Hz, so this sets\n\t\t// the next interrupt to fire one second from now.\n\t\tmtimecmp.write_volatile(mtime.read_volatile() + 10_000_000);\n\n\t\t// Let's cause a page fault and see what happens. This should trap\n\t\t// to m_trap under trap.rs\n\t\tlet v = 0x0 as *mut u64;\n\t\tv.write_volatile(0);\n\t}\n\t// If we get here, the Box, vec, and String should all be freed since\n\t// they go out of scope. This calls their \"Drop\" trait.\n\n\t// Let's set up the interrupt system via the PLIC. We have to set the threshold to something\n\t// that won't mask all interrupts.\n\tprintln!(\"Setting up interrupts and PLIC...\");\n\t// We lower the threshold wall so our interrupts can jump over it.\n\tplic::set_threshold(0);\n\t// VIRTIO = [1..8]\n\t// UART0 = 10\n\t// PCIE = [32..35]\n\t// Enable the UART interrupt.\n\tplic::enable(10);\n\tplic::set_priority(10, 1);\n\tprintln!(\"UART interrupts have been enabled and are awaiting your command\");\n}\n\n// ///////////////////////////////////\n// / RUST MODULES\n// ///////////////////////////////////\n\npub mod cpu;\npub mod kmem;\npub mod page;\npub mod plic;\npub mod trap;\npub mod uart;\n"
  },
  {
    "path": "risc_v/chapters/ch5/src/page.rs",
    "content": "// page.rs\n// Memory routines\n// Stephen Marz\n// 6 October 2019\n\nuse core::{mem::size_of, ptr::null_mut};\n\n// ////////////////////////////////\n// // Allocation routines\n// ////////////////////////////////\nextern \"C\" {\n\tstatic HEAP_START: usize;\n\tstatic HEAP_SIZE: usize;\n}\n\n// We will use ALLOC_START to mark the start of the actual\n// memory we can dish out.\nstatic mut ALLOC_START: usize = 0;\nconst PAGE_ORDER: usize = 12;\npub const PAGE_SIZE: usize = 1 << 12;\n\n/// Align (set to a multiple of some power of two)\n/// This takes an order which is the exponent to 2^order\n/// Therefore, all alignments must be made as a power of two.\n/// This function always rounds up.\npub const fn align_val(val: usize, order: usize) -> usize {\n\tlet o = (1usize << order) - 1;\n\t(val + o) & !o\n}\n\n#[repr(u8)]\npub enum PageBits {\n\tEmpty = 0,\n\tTaken = 1 << 0,\n\tLast = 1 << 1,\n}\n\nimpl PageBits {\n\t// We convert PageBits to a u8 a lot, so this is\n\t// for convenience.\n\tpub fn val(self) -> u8 {\n\t\tself as u8\n\t}\n}\n\n// Each page is described by the Page structure. Linux does this\n// as well, where each 4096-byte chunk of memory has a structure\n// associated with it. However, there structure is much larger.\npub struct Page {\n\tflags: u8,\n}\n\nimpl Page {\n\t// If this page has been marked as the final allocation,\n\t// this function returns true. Otherwise, it returns false.\n\tpub fn is_last(&self) -> bool {\n\t\tif self.flags & PageBits::Last.val() != 0 {\n\t\t\ttrue\n\t\t}\n\t\telse {\n\t\t\tfalse\n\t\t}\n\t}\n\n\t// If the page is marked as being taken (allocated), then\n\t// this function returns true. Otherwise, it returns false.\n\tpub fn is_taken(&self) -> bool {\n\t\tif self.flags & PageBits::Taken.val() != 0 {\n\t\t\ttrue\n\t\t}\n\t\telse {\n\t\t\tfalse\n\t\t}\n\t}\n\n\t// This is the opposite of is_taken().\n\tpub fn is_free(&self) -> bool {\n\t\t!self.is_taken()\n\t}\n\n\t// Clear the Page structure and all associated allocations.\n\tpub fn clear(&mut self) {\n\t\tself.flags = PageBits::Empty.val();\n\t}\n\n\t// Set a certain flag. We ran into trouble here since PageBits\n\t// is an enumeration and we haven't implemented the BitOr Trait\n\t// on it.\n\tpub fn set_flag(&mut self, flag: PageBits) {\n\t\tself.flags |= flag.val();\n\t}\n\n\tpub fn clear_flag(&mut self, flag: PageBits) {\n\t\tself.flags &= !(flag.val());\n\t}\n}\n\n/// Initialize the allocation system. There are several ways that we can\n/// implement the page allocator:\n/// 1. Free list (singly linked list where it starts at the first free\n/// allocation) 2. Bookkeeping list (structure contains a taken and length)\n/// 3. Allocate one Page structure per 4096 bytes (this is what I chose)\n/// 4. Others\npub fn init() {\n\tunsafe {\n\t\t// let desc_per_page = PAGE_SIZE / size_of::<Page>();\n\t\tlet num_pages = HEAP_SIZE / PAGE_SIZE;\n\t\t// let num_desc_pages = num_pages / desc_per_page;\n\t\tlet ptr = HEAP_START as *mut Page;\n\t\t// Clear all pages to make sure that they aren't accidentally\n\t\t// taken\n\t\tfor i in 0..num_pages {\n\t\t\t(*ptr.add(i)).clear();\n\t\t}\n\t\t// Determine where the actual useful memory starts. This will be\n\t\t// after all Page structures. We also must align the ALLOC_START\n\t\t// to a page-boundary (PAGE_SIZE = 4096). ALLOC_START =\n\t\t// (HEAP_START + num_pages * size_of::<Page>() + PAGE_SIZE - 1)\n\t\t// & !(PAGE_SIZE - 1);\n\t\tALLOC_START = align_val(\n\t\t                        HEAP_START\n\t\t                        + num_pages * size_of::<Page>(),\n\t\t                        PAGE_ORDER,\n\t\t);\n\t}\n}\n\n/// Allocate a page or multiple pages\n/// pages: the number of PAGE_SIZE pages to allocate\npub fn alloc(pages: usize) -> *mut u8 {\n\t// We have to find a contiguous allocation of pages\n\tassert!(pages > 0);\n\tunsafe {\n\t\t// We create a Page structure for each page on the heap. We\n\t\t// actually might have more since HEAP_SIZE moves and so does\n\t\t// the size of our structure, but we'll only waste a few bytes.\n\t\tlet num_pages = HEAP_SIZE / PAGE_SIZE;\n\t\tlet ptr = HEAP_START as *mut Page;\n\t\tfor i in 0..num_pages - pages {\n\t\t\tlet mut found = false;\n\t\t\t// Check to see if this Page is free. If so, we have our\n\t\t\t// first candidate memory address.\n\t\t\tif (*ptr.add(i)).is_free() {\n\t\t\t\t// It was FREE! Yay!\n\t\t\t\tfound = true;\n\t\t\t\tfor j in i..i + pages {\n\t\t\t\t\t// Now check to see if we have a\n\t\t\t\t\t// contiguous allocation for all of the\n\t\t\t\t\t// request pages. If not, we should\n\t\t\t\t\t// check somewhere else.\n\t\t\t\t\tif (*ptr.add(j)).is_taken() {\n\t\t\t\t\t\tfound = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// We've checked to see if there are enough contiguous\n\t\t\t// pages to form what we need. If we couldn't, found\n\t\t\t// will be false, otherwise it will be true, which means\n\t\t\t// we've found valid memory we can allocate.\n\t\t\tif found {\n\t\t\t\tfor k in i..i + pages - 1 {\n\t\t\t\t\t(*ptr.add(k)).set_flag(PageBits::Taken);\n\t\t\t\t}\n\t\t\t\t// The marker for the last page is\n\t\t\t\t// PageBits::Last This lets us know when we've\n\t\t\t\t// hit the end of this particular allocation.\n\t\t\t\t(*ptr.add(i+pages-1)).set_flag(PageBits::Taken);\n\t\t\t\t(*ptr.add(i+pages-1)).set_flag(PageBits::Last);\n\t\t\t\t// The Page structures themselves aren't the\n\t\t\t\t// useful memory. Instead, there is 1 Page\n\t\t\t\t// structure per 4096 bytes starting at\n\t\t\t\t// ALLOC_START.\n\t\t\t\treturn (ALLOC_START + PAGE_SIZE * i)\n\t\t\t\t       as *mut u8;\n\t\t\t}\n\t\t}\n\t}\n\n\t// If we get here, that means that no contiguous allocation was\n\t// found.\n\tnull_mut()\n}\n\n/// Allocate and zero a page or multiple pages\n/// pages: the number of pages to allocate\n/// Each page is PAGE_SIZE which is calculated as 1 << PAGE_ORDER\n/// On RISC-V, this typically will be 4,096 bytes.\npub fn zalloc(pages: usize) -> *mut u8 {\n\t// Allocate and zero a page.\n\t// First, let's get the allocation\n\tlet ret = alloc(pages);\n\tif !ret.is_null() {\n\t\tlet size = (PAGE_SIZE * pages) / 8;\n\t\tlet big_ptr = ret as *mut u64;\n\t\tfor i in 0..size {\n\t\t\t// We use big_ptr so that we can force an\n\t\t\t// sd (store doubleword) instruction rather than\n\t\t\t// the sb. This means 8x fewer stores than before.\n\t\t\t// Typically we have to be concerned about remaining\n\t\t\t// bytes, but fortunately 4096 % 8 = 0, so we\n\t\t\t// won't have any remaining bytes.\n\t\t\tunsafe {\n\t\t\t\t(*big_ptr.add(i)) = 0;\n\t\t\t}\n\t\t}\n\t}\n\tret\n}\n\n/// Deallocate a page by its pointer\n/// The way we've structured this, it will automatically coalesce\n/// contiguous pages.\npub fn dealloc(ptr: *mut u8) {\n\t// Make sure we don't try to free a null pointer.\n\tassert!(!ptr.is_null());\n\tunsafe {\n\t\tlet addr =\n\t\t\tHEAP_START + (ptr as usize - ALLOC_START) / PAGE_SIZE;\n\t\t// Make sure that the address makes sense. The address we\n\t\t// calculate here is the page structure, not the HEAP address!\n\t\tassert!(addr >= HEAP_START && addr < HEAP_START + HEAP_SIZE);\n\t\tlet mut p = addr as *mut Page;\n\t\t// Keep clearing pages until we hit the last page.\n\t\twhile (*p).is_taken() && !(*p).is_last() {\n\t\t\t(*p).clear();\n\t\t\tp = p.add(1);\n\t\t}\n\t\t// If the following assertion fails, it is most likely\n\t\t// caused by a double-free.\n\t\tassert!(\n\t\t        (*p).is_last() == true,\n\t\t        \"Possible double-free detected! (Not taken found \\\n\t\t         before last)\"\n\t\t);\n\t\t// If we get here, we've taken care of all previous pages and\n\t\t// we are on the last page.\n\t\t(*p).clear();\n\t}\n}\n\n/// Print all page allocations\n/// This is mainly used for debugging.\npub fn print_page_allocations() {\n\tunsafe {\n\t\tlet num_pages = (HEAP_SIZE - (ALLOC_START - HEAP_START)) / PAGE_SIZE;\n\t\tlet mut beg = HEAP_START as *const Page;\n\t\tlet end = beg.add(num_pages);\n\t\tlet alloc_beg = ALLOC_START;\n\t\tlet alloc_end = ALLOC_START + num_pages * PAGE_SIZE;\n\t\tprintln!();\n\t\tprintln!(\n\t\t         \"PAGE ALLOCATION TABLE\\nMETA: {:p} -> {:p}\\nPHYS: \\\n\t\t          0x{:x} -> 0x{:x}\",\n\t\t         beg, end, alloc_beg, alloc_end\n\t\t);\n\t\tprintln!(\"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\");\n\t\tlet mut num = 0;\n\t\twhile beg < end {\n\t\t\tif (*beg).is_taken() {\n\t\t\t\tlet start = beg as usize;\n\t\t\t\tlet memaddr = ALLOC_START\n\t\t\t\t              + (start - HEAP_START)\n\t\t\t\t                * PAGE_SIZE;\n\t\t\t\tprint!(\"0x{:x} => \", memaddr);\n\t\t\t\tloop {\n\t\t\t\t\tnum += 1;\n\t\t\t\t\tif (*beg).is_last() {\n\t\t\t\t\t\tlet end = beg as usize;\n\t\t\t\t\t\tlet memaddr = ALLOC_START\n\t\t\t\t\t\t              + (end\n\t\t\t\t\t\t                 - HEAP_START)\n\t\t\t\t\t\t                * PAGE_SIZE\n\t\t\t\t\t\t              + PAGE_SIZE - 1;\n\t\t\t\t\t\tprint!(\n\t\t\t\t\t\t       \"0x{:x}: {:>3} page(s)\",\n\t\t\t\t\t\t       memaddr,\n\t\t\t\t\t\t       (end - start + 1)\n\t\t\t\t\t\t);\n\t\t\t\t\t\tprintln!(\".\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tbeg = beg.add(1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbeg = beg.add(1);\n\t\t}\n\t\tprintln!(\"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\");\n\t\tprintln!(\n\t\t         \"Allocated: {:>6} pages ({:>10} bytes).\",\n\t\t         num,\n\t\t         num * PAGE_SIZE\n\t\t);\n\t\tprintln!(\n\t\t         \"Free     : {:>6} pages ({:>10} bytes).\",\n\t\t         num_pages - num,\n\t\t         (num_pages - num) * PAGE_SIZE\n\t\t);\n\t\tprintln!();\n\t}\n}\n\n// ////////////////////////////////\n// // MMU Routines\n// ////////////////////////////////\n\n// Represent (repr) our entry bits as\n// unsigned 64-bit integers.\n#[repr(i64)]\n#[derive(Copy, Clone)]\npub enum EntryBits {\n\tNone = 0,\n\tValid = 1 << 0,\n\tRead = 1 << 1,\n\tWrite = 1 << 2,\n\tExecute = 1 << 3,\n\tUser = 1 << 4,\n\tGlobal = 1 << 5,\n\tAccess = 1 << 6,\n\tDirty = 1 << 7,\n\n\t// Convenience combinations\n\tReadWrite = 1 << 1 | 1 << 2,\n\tReadExecute = 1 << 1 | 1 << 3,\n\tReadWriteExecute = 1 << 1 | 1 << 2 | 1 << 3,\n\n\t// User Convenience Combinations\n\tUserReadWrite = 1 << 1 | 1 << 2 | 1 << 4,\n\tUserReadExecute = 1 << 1 | 1 << 3 | 1 << 4,\n\tUserReadWriteExecute = 1 << 1 | 1 << 2 | 1 << 3 | 1 << 4,\n}\n\n// Helper functions to convert the enumeration\n// into an i64, which is what our page table\n// entries will be.\nimpl EntryBits {\n\tpub fn val(self) -> i64 {\n\t\tself as i64\n\t}\n}\n\n// A single entry. We're using an i64 so that\n// this will sign-extend rather than zero-extend\n// since RISC-V requires that the reserved sections\n// take on the most significant bit.\npub struct Entry {\n\tpub entry: i64,\n}\n\n// The Entry structure describes one of the 512 entries per table, which is\n// described in the RISC-V privileged spec Figure 4.18.\nimpl Entry {\n\tpub fn is_valid(&self) -> bool {\n\t\tself.get_entry() & EntryBits::Valid.val() != 0\n\t}\n\n\t// The first bit (bit index #0) is the V bit for\n\t// valid.\n\tpub fn is_invalid(&self) -> bool {\n\t\t!self.is_valid()\n\t}\n\n\t// A leaf has one or more RWX bits set\n\tpub fn is_leaf(&self) -> bool {\n\t\tself.get_entry() & 0xe != 0\n\t}\n\n\tpub fn is_branch(&self) -> bool {\n\t\t!self.is_leaf()\n\t}\n\n\tpub fn set_entry(&mut self, entry: i64) {\n\t\tself.entry = entry;\n\t}\n\n\tpub fn get_entry(&self) -> i64 {\n\t\tself.entry\n\t}\n}\n\n// Table represents a single table, which contains 512 (2^9), 64-bit entries.\npub struct Table {\n\tpub entries: [Entry; 512],\n}\n\nimpl Table {\n\tpub fn len() -> usize {\n\t\t512\n\t}\n}\n\n/// Map a virtual address to a physical address using 4096-byte page\n/// size.\n/// root: a mutable reference to the root Table\n/// vaddr: The virtual address to map\n/// paddr: The physical address to map\n/// bits: An OR'd bitset containing the bits the leaf should have.\n///       The bits should contain only the following:\n///          Read, Write, Execute, User, and/or Global\n///       The bits MUST include one or more of the following:\n///          Read, Write, Execute\n///       The valid bit automatically gets added.\npub fn map(root: &mut Table,\n           vaddr: usize,\n           paddr: usize,\n           bits: i64,\n           level: usize)\n{\n\t// Make sure that Read, Write, or Execute have been provided\n\t// otherwise, we'll leak memory and always create a page fault.\n\tassert!(bits & 0xe != 0);\n\t// Extract out each VPN from the virtual address\n\t// On the virtual address, each VPN is exactly 9 bits,\n\t// which is why we use the mask 0x1ff = 0b1_1111_1111 (9 bits)\n\tlet vpn = [\n\t           // VPN[0] = vaddr[20:12]\n\t           (vaddr >> 12) & 0x1ff,\n\t           // VPN[1] = vaddr[29:21]\n\t           (vaddr >> 21) & 0x1ff,\n\t           // VPN[2] = vaddr[38:30]\n\t           (vaddr >> 30) & 0x1ff,\n\t];\n\n\t// Just like the virtual address, extract the physical address\n\t// numbers (PPN). However, PPN[2] is different in that it stores\n\t// 26 bits instead of 9. Therefore, we use,\n\t// 0x3ff_ffff = 0b11_1111_1111_1111_1111_1111_1111 (26 bits).\n\tlet ppn = [\n\t           // PPN[0] = paddr[20:12]\n\t           (paddr >> 12) & 0x1ff,\n\t           // PPN[1] = paddr[29:21]\n\t           (paddr >> 21) & 0x1ff,\n\t           // PPN[2] = paddr[55:30]\n\t           (paddr >> 30) & 0x3ff_ffff,\n\t];\n\t// We will use this as a floating reference so that we can set\n\t// individual entries as we walk the table.\n\tlet mut v = &mut root.entries[vpn[2]];\n\t// Now, we're going to traverse the page table and set the bits\n\t// properly. We expect the root to be valid, however we're required to\n\t// create anything beyond the root.\n\t// In Rust, we create a range iterator using the .. operator.\n\t// The .rev() will reverse the iteration since we need to start with\n\t// VPN[2] The .. operator is inclusive on start but exclusive on end.\n\t// So, (0..2) will iterate 0 and 1.\n\tfor i in (level..2).rev() {\n\t\tif !v.is_valid() {\n\t\t\t// Allocate a page\n\t\t\tlet page = zalloc(1);\n\t\t\t// The page is already aligned by 4,096, so store it\n\t\t\t// directly The page is stored in the entry shifted\n\t\t\t// right by 2 places.\n\t\t\tv.set_entry(\n\t\t\t            (page as i64 >> 2)\n\t\t\t            | EntryBits::Valid.val(),\n\t\t\t);\n\t\t}\n\t\tlet entry = ((v.get_entry() & !0x3ff) << 2) as *mut Entry;\n\t\tv = unsafe { entry.add(vpn[i]).as_mut().unwrap() };\n\t}\n\t// When we get here, we should be at VPN[0] and v should be pointing to\n\t// our entry.\n\t// The entry structure is Figure 4.18 in the RISC-V Privileged\n\t// Specification\n\tlet entry = (ppn[2] << 28) as i64 |   // PPN[2] = [53:28]\n\t            (ppn[1] << 19) as i64 |   // PPN[1] = [27:19]\n\t\t\t\t(ppn[0] << 10) as i64 |   // PPN[0] = [18:10]\n\t\t\t\tbits |                    // Specified bits, such as User, Read, Write, etc\n\t\t\t\tEntryBits::Valid.val() |  // Valid bit\n\t\t\t\tEntryBits::Dirty.val() |  // Some machines require this to =1\n\t\t\t\tEntryBits::Access.val()   // Just like dirty, some machines require this\n\t\t\t\t;\n\t// Set the entry. V should be set to the correct pointer by the loop\n\t// above.\n\tv.set_entry(entry);\n}\n\n/// Unmaps and frees all memory associated with a table.\n/// root: The root table to start freeing.\n/// NOTE: This does NOT free root directly. This must be\n/// freed manually.\n/// The reason we don't free the root is because it is\n/// usually embedded into the Process structure.\npub fn unmap(root: &mut Table) {\n\t// Start with level 2\n\tfor lv2 in 0..Table::len() {\n\t\tlet ref entry_lv2 = root.entries[lv2];\n\t\tif entry_lv2.is_valid() && entry_lv2.is_branch() {\n\t\t\t// This is a valid entry, so drill down and free.\n\t\t\tlet memaddr_lv1 = (entry_lv2.get_entry() & !0x3ff) << 2;\n\t\t\tlet table_lv1 = unsafe {\n\t\t\t\t// Make table_lv1 a mutable reference instead of\n\t\t\t\t// a pointer.\n\t\t\t\t(memaddr_lv1 as *mut Table).as_mut().unwrap()\n\t\t\t};\n\t\t\tfor lv1 in 0..Table::len() {\n\t\t\t\tlet ref entry_lv1 = table_lv1.entries[lv1];\n\t\t\t\tif entry_lv1.is_valid() && entry_lv1.is_branch()\n\t\t\t\t{\n\t\t\t\t\tlet memaddr_lv0 = (entry_lv1.get_entry()\n\t\t\t\t\t                   & !0x3ff) << 2;\n\t\t\t\t\t// The next level is level 0, which\n\t\t\t\t\t// cannot have branches, therefore,\n\t\t\t\t\t// we free here.\n\t\t\t\t\tdealloc(memaddr_lv0 as *mut u8);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdealloc(memaddr_lv1 as *mut u8);\n\t\t}\n\t}\n}\n\n/// Walk the page table to convert a virtual address to a\n/// physical address.\n/// If a page fault would occur, this returns None\n/// Otherwise, it returns Some with the physical address.\npub fn virt_to_phys(root: &Table, vaddr: usize) -> Option<usize> {\n\t// Walk the page table pointed to by root\n\tlet vpn = [\n\t           // VPN[0] = vaddr[20:12]\n\t           (vaddr >> 12) & 0x1ff,\n\t           // VPN[1] = vaddr[29:21]\n\t           (vaddr >> 21) & 0x1ff,\n\t           // VPN[2] = vaddr[38:30]\n\t           (vaddr >> 30) & 0x1ff,\n\t];\n\n\tlet mut v = &root.entries[vpn[2]];\n\tfor i in (0..=2).rev() {\n\t\tif v.is_invalid() {\n\t\t\t// This is an invalid entry, page fault.\n\t\t\tbreak;\n\t\t}\n\t\telse if v.is_leaf() {\n\t\t\t// According to RISC-V, a leaf can be at any level.\n\n\t\t\t// The offset mask masks off the PPN. Each PPN is 9\n\t\t\t// bits and they start at bit #12. So, our formula\n\t\t\t// 12 + i * 9\n\t\t\tlet off_mask = (1 << (12 + i * 9)) - 1;\n\t\t\tlet vaddr_pgoff = vaddr & off_mask;\n\t\t\tlet addr = ((v.get_entry() << 2) as usize) & !off_mask;\n\t\t\treturn Some(addr | vaddr_pgoff);\n\t\t}\n\t\t// Set v to the next entry which is pointed to by this\n\t\t// entry. However, the address was shifted right by 2 places\n\t\t// when stored in the page table entry, so we shift it left\n\t\t// to get it back into place.\n\t\tlet entry = ((v.get_entry() & !0x3ff) << 2) as *const Entry;\n\t\t// We do i - 1 here, however we should get None or Some() above\n\t\t// before we do 0 - 1 = -1.\n\t\tv = unsafe { entry.add(vpn[i - 1]).as_ref().unwrap() };\n\t}\n\n\t// If we get here, we've exhausted all valid tables and haven't\n\t// found a leaf.\n\tNone\n}\n"
  },
  {
    "path": "risc_v/chapters/ch5/src/plic.rs",
    "content": "// plic.rs\n// Platform Level Interrupt Controller (PLIC)\n// Stephen Marz\n// 1 Nov 2019\n\nconst PLIC_PRIORITY: usize = 0x0c00_0000;\nconst PLIC_PENDING: usize = 0x0c00_1000;\nconst PLIC_INT_ENABLE: usize = 0x0c00_2000;\nconst PLIC_THRESHOLD: usize = 0x0c20_0000;\nconst PLIC_CLAIM: usize = 0x0c20_0004;\n\n// Each register is 4-bytes (u32)\n// The PLIC is an external interrupt controller. The one\n// used by QEMU virt is the same as the SiFive PLIC.\n// https://sifive.cdn.prismic.io/sifive%2F834354f0-08e6-423c-bf1f-0cb58ef14061_fu540-c000-v1.0.pdf\n\n// Chapter 10 explains the priority, pending, interrupt enable, threshold and claims\n\n// The virt machine has the following external interrupts (from Qemu source):\n// Interrupt 0 is a \"null\" interrupt and is hardwired to 0.\n// VIRTIO = [1..8]\n// UART0 = 10\n// PCIE = [32..35]\n\n\n/// Get the next available interrupt. This is the \"claim\" process.\n/// The plic will automatically sort by priority and hand us the\n/// ID of the interrupt. For example, if the UART is interrupting\n/// and it's next, we will get the value 10.\npub fn next() -> Option<u32> {\n    let claim_reg = PLIC_CLAIM as *const u32;\n    let claim_no;\n    // The claim register is filled with the highest-priority, enabled interrupt.\n    unsafe {\n        claim_no = claim_reg.read_volatile();\n    }\n    if claim_no == 0 {\n        // The interrupt 0 is hardwired to 0, which tells us that there is no\n        // interrupt to claim, hence we return None.\n        None\n    }\n    else {\n        // If we get here, we've gotten a non-0 interrupt.\n        Some(claim_no)\n    }\n}\n\n/// Complete a pending interrupt by id. The id should come\n/// from the next() function above.\npub fn complete(id: u32) {\n    let complete_reg = PLIC_CLAIM as *mut u32;\n    unsafe {\n        // We actually write a u32 into the entire complete_register.\n        // This is the same register as the claim register, but it can\n        // differentiate based on whether we're reading or writing.\n        complete_reg.write_volatile(id);\n    }\n}\n\n/// Set the global threshold. The threshold can be a value [0..7].\n/// The PLIC will mask any interrupts at or below the given threshold.\n/// This means that a threshold of 7 will mask ALL interrupts and\n/// a threshold of 0 will allow ALL interrupts.\npub fn set_threshold(tsh: u8) {\n    // We do tsh because we're using a u8, but our maximum number\n    // is a 3-bit 0b111. So, we and with 7 (0b111) to just get the\n    // last three bits.\n    let actual_tsh = tsh & 7;\n    let tsh_reg = PLIC_THRESHOLD as *mut u32;\n    unsafe {\n        tsh_reg.write_volatile(actual_tsh as u32);\n    }\n}\n\n/// See if a given interrupt id is pending.\npub fn is_pending(id: u32) -> bool {\n    let pend = PLIC_PENDING as *const u32;\n    let actual_id = 1 << id;\n    let pend_ids;\n    unsafe {\n        pend_ids = pend.read_volatile();\n    }\n    actual_id & pend_ids != 0\n}\n\n/// Enable a given interrupt id\npub fn enable(id: u32) {\n    let enables = PLIC_INT_ENABLE as *mut u32;\n    let actual_id = 1 << id;\n    unsafe {\n        // Unlike the complete and claim registers, the plic_int_enable\n        // register is a bitset where the id is the bit index. The register\n        // is a 32-bit register, so that gives us enables for interrupts\n        // 31 through 1 (0 is hardwired to 0).\n        enables.write_volatile(enables.read_volatile() | actual_id);\n    }\n}\n\n/// Set a given interrupt priority to the given priority.\n/// The priority must be [0..7]\npub fn set_priority(id: u32, prio: u8) {\n    let actual_prio = prio as u32 & 7;\n    let prio_reg = PLIC_PRIORITY as *mut u32;\n    unsafe {\n        // The offset for the interrupt id is:\n        // PLIC_PRIORITY + 4 * id\n        // Since we're using pointer arithmetic on a u32 type,\n        // it will automatically multiply the id by 4.\n        prio_reg.add(id as usize).write_volatile(actual_prio);\n    }\n}\n\n"
  },
  {
    "path": "risc_v/chapters/ch5/src/trap.rs",
    "content": "// trap.rs\n// Trap routines\n// Stephen Marz\n// 10 October 2019\n\nuse crate::cpu::TrapFrame;\nuse crate::{plic, uart};\n\n#[no_mangle]\n/// The m_trap stands for \"machine trap\". Right now, we are handling\n/// all traps at machine mode. In this mode, we can figure out what's\n/// going on and send a trap where it needs to be. Remember, in machine\n/// mode and in this trap, interrupts are disabled and the MMU is off.\nextern \"C\" fn m_trap(epc: usize,\n                     tval: usize,\n                     cause: usize,\n                     hart: usize,\n                     status: usize,\n                     frame: &mut TrapFrame)\n                     -> usize\n{\n\t// We're going to handle all traps in machine mode. RISC-V lets\n\t// us delegate to supervisor mode, but switching out SATP (virtual memory)\n\t// gets hairy.\n\tlet is_async = {\n\t\tif cause >> 63 & 1 == 1 {\n\t\t\ttrue\n\t\t}\n\t\telse {\n\t\t\tfalse\n\t\t}\n\t};\n\t// The cause contains the type of trap (sync, async) as well as the cause\n\t// number. So, here we narrow down just the cause number.\n\tlet cause_num = cause & 0xfff;\n\tlet mut return_pc = epc;\n\tif is_async {\n\t\t// Asynchronous trap\n\t\tmatch cause_num {\n\t\t\t3 => {\n\t\t\t\t// Machine software\n\t\t\t\tprintln!(\"Machine software interrupt CPU#{}\", hart);\n\t\t\t},\n\t\t\t7 => unsafe {\n\t\t\t\t// Machine timer\n\t\t\t\tlet mtimecmp = 0x0200_4000 as *mut u64;\n\t\t\t\tlet mtime = 0x0200_bff8 as *const u64;\n\t\t\t\t// The frequency given by QEMU is 10_000_000 Hz, so this sets\n\t\t\t\t// the next interrupt to fire one second from now.\n\t\t\t\tmtimecmp.write_volatile(mtime.read_volatile() + 10_000_000);\n\t\t\t},\n\t\t\t11 => {\n\t\t\t\t// Machine external (interrupt from Platform Interrupt Controller (PLIC))\n\t\t\t\t// println!(\"Machine external interrupt CPU#{}\", hart);\n\t\t\t\t// We will check the next interrupt. If the interrupt isn't available, this will\n\t\t\t\t// give us None. However, that would mean we got a spurious interrupt, unless we\n\t\t\t\t// get an interrupt from a non-PLIC source. This is the main reason that the PLIC\n\t\t\t\t// hardwires the id 0 to 0, so that we can use it as an error case.\n\t\t\t\tif let Some(interrupt) = plic::next() {\n\t\t\t\t\t// If we get here, we've got an interrupt from the claim register. The PLIC will\n\t\t\t\t\t// automatically prioritize the next interrupt, so when we get it from claim, it\n\t\t\t\t\t// will be the next in priority order.\n\t\t\t\t\tmatch interrupt {\n\t\t\t\t\t\t10 => { // Interrupt 10 is the UART interrupt.\n\t\t\t\t\t\t\t// We would typically set this to be handled out of the interrupt context,\n\t\t\t\t\t\t\t// but we're testing here! C'mon!\n\t\t\t\t\t\t\t// We haven't yet used the singleton pattern for my_uart, but remember, this\n\t\t\t\t\t\t\t// just simply wraps 0x1000_0000 (UART).\n\t\t\t\t\t\t\tlet mut my_uart = uart::Uart::new(0x1000_0000);\n\t\t\t\t\t\t\t// If we get here, the UART better have something! If not, what happened??\n\t\t\t\t\t\t\tif let Some(c) = my_uart.get() {\n\t\t\t\t\t\t\t\t// If you recognize this code, it used to be in the lib.rs under kmain(). That\n\t\t\t\t\t\t\t\t// was because we needed to poll for UART data. Now that we have interrupts,\n\t\t\t\t\t\t\t\t// here it goes!\n\t\t\t\t\t\t\t\tmatch c {\n\t\t\t\t\t\t\t\t\t8 => {\n\t\t\t\t\t\t\t\t\t\t// This is a backspace, so we\n\t\t\t\t\t\t\t\t\t\t// essentially have to write a space and\n\t\t\t\t\t\t\t\t\t\t// backup again:\n\t\t\t\t\t\t\t\t\t\tprint!(\"{} {}\", 8 as char, 8 as char);\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t10 | 13 => {\n\t\t\t\t\t\t\t\t\t\t// Newline or carriage-return\n\t\t\t\t\t\t\t\t\t\tprintln!();\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t_ => {\n\t\t\t\t\t\t\t\t\t\tprint!(\"{}\", c as char);\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t\t},\n\t\t\t\t\t\t// Non-UART interrupts go here and do nothing.\n\t\t\t\t\t\t_ => {\n\t\t\t\t\t\t\tprintln!(\"Non-UART external interrupt: {}\", interrupt);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// We've claimed it, so now say that we've handled it. This resets the interrupt pending\n\t\t\t\t\t// and allows the UART to interrupt again. Otherwise, the UART will get \"stuck\".\n\t\t\t\t\tplic::complete(interrupt);\n\t\t\t\t}\n\t\t\t},\n\t\t\t_ => {\n\t\t\t\tpanic!(\"Unhandled async trap CPU#{} -> {}\\n\", hart, cause_num);\n\t\t\t}\n\t\t}\n\t}\n\telse {\n\t\t// Synchronous trap\n\t\tmatch cause_num {\n\t\t\t2 => {\n\t\t\t\t// Illegal instruction\n\t\t\t\tpanic!(\"Illegal instruction CPU#{} -> 0x{:08x}: 0x{:08x}\\n\", hart, epc, tval);\n\t\t\t},\n\t\t\t8 => {\n\t\t\t\t// Environment (system) call from User mode\n\t\t\t\tprintln!(\"E-call from User mode! CPU#{} -> 0x{:08x}\", hart, epc);\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t9 => {\n\t\t\t\t// Environment (system) call from Supervisor mode\n\t\t\t\tprintln!(\"E-call from Supervisor mode! CPU#{} -> 0x{:08x}\", hart, epc);\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t11 => {\n\t\t\t\t// Environment (system) call from Machine mode\n\t\t\t\tpanic!(\"E-call from Machine mode! CPU#{} -> 0x{:08x}\\n\", hart, epc);\n\t\t\t},\n\t\t\t// Page faults\n\t\t\t12 => {\n\t\t\t\t// Instruction page fault\n\t\t\t\tprintln!(\"Instruction page fault CPU#{} -> 0x{:08x}: 0x{:08x}\", hart, epc, tval);\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t13 => {\n\t\t\t\t// Load page fault\n\t\t\t\tprintln!(\"Load page fault CPU#{} -> 0x{:08x}: 0x{:08x}\", hart, epc, tval);\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t15 => {\n\t\t\t\t// Store page fault\n\t\t\t\tprintln!(\"Store page fault CPU#{} -> 0x{:08x}: 0x{:08x}\", hart, epc, tval);\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t_ => {\n\t\t\t\tpanic!(\"Unhandled sync trap CPU#{} -> {}\\n\", hart, cause_num);\n\t\t\t}\n\t\t}\n\t};\n\t// Finally, return the updated program counter\n\treturn_pc\n}\n"
  },
  {
    "path": "risc_v/chapters/ch5/src/uart.rs",
    "content": "// uart.rs\n// UART routines and driver\n\nuse core::{convert::TryInto,\n           fmt::{Error, Write}};\n\npub struct Uart {\n\tbase_address: usize,\n}\n\nimpl Write for Uart {\n\tfn write_str(&mut self, out: &str) -> Result<(), Error> {\n\t\tfor c in out.bytes() {\n\t\t\tself.put(c);\n\t\t}\n\t\tOk(())\n\t}\n}\n\nimpl Uart {\n\tpub fn new(base_address: usize) -> Self {\n\t\tUart { base_address }\n\t}\n\n\tpub fn init(&mut self) {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\t// First, set the word length, which\n\t\t\t// are bits 0 and 1 of the line control register (LCR)\n\t\t\t// which is at base_address + 3\n\t\t\t// We can easily write the value 3 here or 0b11, but I'm\n\t\t\t// extending it so that it is clear we're setting two\n\t\t\t// individual fields\n\t\t\t//             Word 0     Word 1\n\t\t\t//             ~~~~~~     ~~~~~~\n\t\t\tlet lcr: u8 = (1 << 0) | (1 << 1);\n\t\t\tptr.add(3).write_volatile(lcr);\n\n\t\t\t// Now, enable the FIFO, which is bit index 0 of the\n\t\t\t// FIFO control register (FCR at offset 2).\n\t\t\t// Again, we can just write 1 here, but when we use left\n\t\t\t// shift, it's easier to see that we're trying to write\n\t\t\t// bit index #0.\n\t\t\tptr.add(2).write_volatile(1 << 0);\n\n\t\t\t// Enable receiver buffer interrupts, which is at bit\n\t\t\t// index 0 of the interrupt enable register (IER at\n\t\t\t// offset 1).\n\t\t\tptr.add(1).write_volatile(1 << 0);\n\n\t\t\t// If we cared about the divisor, the code below would\n\t\t\t// set the divisor from a global clock rate of 22.729\n\t\t\t// MHz (22,729,000 cycles per second) to a signaling\n\t\t\t// rate of 2400 (BAUD). We usually have much faster\n\t\t\t// signalling rates nowadays, but this demonstrates what\n\t\t\t// the divisor actually does. The formula given in the\n\t\t\t// NS16500A specification for calculating the divisor\n\t\t\t// is:\n\t\t\t// divisor = ceil( (clock_hz) / (baud_sps x 16) )\n\t\t\t// So, we substitute our values and get:\n\t\t\t// divisor = ceil( 22_729_000 / (2400 x 16) )\n\t\t\t// divisor = ceil( 22_729_000 / 38_400 )\n\t\t\t// divisor = ceil( 591.901 ) = 592\n\n\t\t\t// The divisor register is two bytes (16 bits), so we\n\t\t\t// need to split the value 592 into two bytes.\n\t\t\t// Typically, we would calculate this based on measuring\n\t\t\t// the clock rate, but again, for our purposes [qemu],\n\t\t\t// this doesn't really do anything.\n\t\t\tlet divisor: u16 = 592;\n\t\t\tlet divisor_least: u8 =\n\t\t\t\t(divisor & 0xff).try_into().unwrap();\n\t\t\tlet divisor_most: u8 =\n\t\t\t\t(divisor >> 8).try_into().unwrap();\n\n\t\t\t// Notice that the divisor register DLL (divisor latch\n\t\t\t// least) and DLM (divisor latch most) have the same\n\t\t\t// base address as the receiver/transmitter and the\n\t\t\t// interrupt enable register. To change what the base\n\t\t\t// address points to, we open the \"divisor latch\" by\n\t\t\t// writing 1 into the Divisor Latch Access Bit (DLAB),\n\t\t\t// which is bit index 7 of the Line Control Register\n\t\t\t// (LCR) which is at base_address + 3.\n\t\t\tptr.add(3).write_volatile(lcr | 1 << 7);\n\n\t\t\t// Now, base addresses 0 and 1 point to DLL and DLM,\n\t\t\t// respectively. Put the lower 8 bits of the divisor\n\t\t\t// into DLL\n\t\t\tptr.add(0).write_volatile(divisor_least);\n\t\t\tptr.add(1).write_volatile(divisor_most);\n\n\t\t\t// Now that we've written the divisor, we never have to\n\t\t\t// touch this again. In hardware, this will divide the\n\t\t\t// global clock (22.729 MHz) into one suitable for 2,400\n\t\t\t// signals per second. So, to once again get access to\n\t\t\t// the RBR/THR/IER registers, we need to close the DLAB\n\t\t\t// bit by clearing it to 0.\n\t\t\tptr.add(3).write_volatile(lcr);\n\t\t}\n\t}\n\n\tpub fn put(&mut self, c: u8) {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\tptr.add(0).write_volatile(c);\n\t\t}\n\t}\n\n\tpub fn get(&mut self) -> Option<u8> {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\tif ptr.add(5).read_volatile() & 1 == 0 {\n\t\t\t\t// The DR bit is 0, meaning no data\n\t\t\t\tNone\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// The DR bit is 1, meaning data!\n\t\t\t\tSome(ptr.add(0).read_volatile())\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "risc_v/chapters/ch6/.cargo/config",
    "content": "[build]\ntarget = \"riscv64gc-unknown-none-elf\"\n\n[target.riscv64gc-unknown-none-elf]\nlinker = \"riscv64-unknown-linux-gnu-gcc\"\n"
  },
  {
    "path": "risc_v/chapters/ch6/.gitignore",
    "content": "os.elf\ntarget/*\nCargo.lock\nhdd.dsk\n"
  },
  {
    "path": "risc_v/chapters/ch6/Cargo.toml",
    "content": "[package]\nname = \"sos\"\nversion = \"0.1.0\"\nauthors = [\"Stephen Marz <stephen.marz@utk.edu>\"]\nedition = \"2018\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[lib]\ncrate-type = [\"staticlib\"]\n\n[dependencies]\n"
  },
  {
    "path": "risc_v/chapters/ch6/Makefile",
    "content": "#####\n## BUILD\n#####\nCC=riscv64-unknown-linux-gnu-gcc\nCFLAGS=-Wall -Wextra -pedantic -Wextra -O0 -g\nCFLAGS+=-static -ffreestanding -nostdlib -fno-rtti -fno-exceptions\nCFLAGS+=-march=rv64gc -mabi=lp64\nINCLUDES=\nLINKER_SCRIPT=-Tsrc/lds/virt.lds\nTYPE=debug\nRUST_TARGET=./target/riscv64gc-unknown-none-elf/$(TYPE)\nLIBS=-L$(RUST_TARGET)\nSOURCES_ASM=$(wildcard src/asm/*.S)\nLIB=-lsos -lgcc\nOUT=os.elf\n\n#####\n## QEMU\n#####\nQEMU=qemu-system-riscv64\nMACH=virt\nCPU=rv64\nCPUS=4\nMEM=128M\nDISK=hdd.dsk\n# DRIVE= -drive if=none,format=raw,file=$(DISK),id=foo -device virtio-blk-device,scsi=off,drive=foo\nDRIVE=\n\n\nall:\n\tcargo build\n\t$(CC) $(CFLAGS) $(LINKER_SCRIPT) $(INCLUDES) -o $(OUT) $(SOURCES_ASM) $(LIBS) $(LIB)\n\t\nrun: all\n\t$(QEMU) -machine $(MACH) -cpu $(CPU) -smp $(CPUS) -m $(MEM) $(DRIVE) -nographic -serial mon:stdio -bios none -kernel $(OUT)\n\n\n.PHONY: clean\nclean:\n\tcargo clean\n\trm -f $(OUT)\n"
  },
  {
    "path": "risc_v/chapters/ch6/make_hdd.sh",
    "content": "#!/bin/sh\n\ndd if=/dev/zero of=hdd.dsk bs=1M count=32\n"
  },
  {
    "path": "risc_v/chapters/ch6/src/asm/boot.S",
    "content": "# boot.S\n# bootloader for SoS\n# Stephen Marz\n# 8 February 2019\n\n# Disable generation of compressed instructions.\n.option norvc\n\n# Define a .text.init section. The .text.init is put at the\n# starting address so that the entry _start is put at the RISC-V\n# address 0x8000_0000.\n.section .text.init\n\n# Execution starts here.\n.global _start\n_start:\n\n\t# Disable linker instruction relaxation for the `la` instruction below.\n\t# This disallows the assembler from assuming that `gp` is already initialized.\n\t# This causes the value stored in `gp` to be calculated from `pc`.\n\t# The job of the global pointer is to give the linker the ability to address\n\t# memory relative to GP instead of as an absolute address.\n.option push\n.option norelax\n\tla\t\tgp, _global_pointer\n.option pop\n\t# SATP should be zero, but let's make sure. Each HART has its own\n\t# SATP register.\n\tcsrw\tsatp, zero\n\t# Any hardware threads (hart) that are not bootstrapping\n\t# need to wait for an IPI\n\tcsrr\tt0, mhartid\n\tbnez\tt0, 3f\n\n\t# Set all bytes in the BSS section to zero.\n\tla \t\ta0, _bss_start\n\tla\t\ta1, _bss_end\n\tbgeu\ta0, a1, 2f\n1:\n\tsd\t\tzero, (a0)\n\taddi\ta0, a0, 8\n\tbltu\ta0, a1, 1b\n2:\n\t# The stack grows from bottom to top, so we put the stack pointer\n\t# to the very end of the stack range.\n\tla\t\tsp, _stack_end\n\t# Setting `mstatus` register:\n\t# 0b01 << 11: Machine's previous protection mode is 2 (MPP=2).\n\tli\t\tt0, 0b11 << 11\n\tcsrw\tmstatus, t0\n\t# Do not allow interrupts while running kinit\n\tcsrw\tmie, zero\n\t# Machine's exception program counter (MEPC) is set to `kinit`.\n\tla\t\tt1, kinit\n\tcsrw\tmepc, t1\n\t# Set the return address to get us into supervisor mode\n\tla\t\tra, 2f\n\t# We use mret here so that the mstatus register is properly updated.\n\tmret\n2:\n\t# We set the return address (ra above) to this label. When kinit() is finished\n\t# in Rust, it will return here.\n\n\t# Setting `mstatus` (supervisor status) register:\n\t# 0b01 << 11 : Previous protection mode is 1 (MPP=01 [Supervisor]).\n\t# 1 << 7     : Previous machine interrupt-enable bit is 1 (MPIE=1 [Enabled])\n\t# 1 << 5     : Previous interrupt-enable bit is 1 (SPIE=1 [Enabled]).\n\t# We set the \"previous\" bits because the mret will write the current bits\n\t# with the previous bits.\n\tli\t\tt0, (0b00 << 11) | (1 << 7) | (1 << 5)\n\tcsrw\tmstatus, t0\n\t# Machine's trap vector base address is set to `m_trap_vector`, for\n\t# \"machine\" trap vector.\n\tla\t\tt2, m_trap_vector\n\tcsrw\tmtvec, t2\n\t# Jump to first process. We put the MPP = 00 for user mode, so after\n\t# mret, we will jump to the first process' addresss in user mode.\n\tcsrw\tmepc, a0\n\tli\t\tt2, 0xaaa\n\tcsrw\tmie, t2\n\tla\t\tra, 4f\n\tmret\n3:\n\n\t# Parked harts go here. We need to set these\n\t# to only awaken if it receives a software interrupt,\n\t# which we're going to call the SIPI (Software Intra-Processor Interrupt).\n\t# We call the SIPI by writing the software interrupt into the Core Local Interruptor (CLINT)\n\t# Which is calculated by: base_address + hart * 4\n\t# where base address is 0x0200_0000 (MMIO CLINT base address)\n\t# We only use additional harts to run user-space programs, although this may\n\t# change.\n\n\t# We divide up the stack so the harts aren't clobbering one another.\n\tla\t\tsp, _stack_end\n\tli\t\tt0, 0x10000\n\tcsrr\ta0, mhartid\n\tmul\t\tt0, t0, a0\n\tsub\t\tsp, sp, t0\n\n\t# The parked harts will be put into machine mode with interrupts enabled.\n\tli\t\tt0, 0b11 << 11 | (1 << 7)\n\tcsrw\tmstatus, t0\n\t# Allow for MSIP (Software interrupt). We will write the MSIP from hart #0 to\n\t# awaken these parked harts.\n\tli\t\tt3, (1 << 3)\n\tcsrw\tmie, t3\n\t# Machine's exception program counter (MEPC) is set to the Rust initialization\n\t# code and waiting loop.\n\tla\t\tt1, kinit_hart\n\tcsrw\tmepc, t1\n\t# Machine's trap vector base address is set to `m_trap_vector`, for\n\t# \"machine\" trap vector. The Rust initialization routines will give each\n\t# hart its own trap frame. We can use the same trap function and distinguish\n\t# between each hart by looking at the trap frame.\n\tla\t\tt2, m_trap_vector\n\tcsrw\tmtvec, t2\n\t# Whenever our hart is done initializing, we want it to return to the waiting\n\t# loop, which is just below mret.\n\tla\t\tra, 4f\n\t# We use mret here so that the mstatus register is properly updated.\n\tmret\n\n4:\n\t# wfi = wait for interrupt. This is a hint to the harts to shut everything needed\n\t# down. However, the RISC-V specification allows for wfi to do nothing. Anyway,\n\t# with QEMU, this will save some CPU!\n\twfi\n\tj\t\t4b\n\n\n"
  },
  {
    "path": "risc_v/chapters/ch6/src/asm/mem.S",
    "content": "// mem.S\n// Importation of linker symbols\n\n.section .rodata\n.global HEAP_START\nHEAP_START: .dword _heap_start\n\n.global HEAP_SIZE\nHEAP_SIZE: .dword _heap_size\n\n.global TEXT_START\nTEXT_START: .dword _text_start\n\n.global TEXT_END\nTEXT_END: .dword _text_end\n\n.global DATA_START\nDATA_START: .dword _data_start\n\n.global DATA_END\nDATA_END: .dword _data_end\n\n.global RODATA_START\nRODATA_START: .dword _rodata_start\n\n.global RODATA_END\nRODATA_END: .dword _rodata_end\n\n.global BSS_START\nBSS_START: .dword _bss_start\n\n.global BSS_END\nBSS_END: .dword _bss_end\n\n.global KERNEL_STACK_START\nKERNEL_STACK_START: .dword _stack_start\n\n.global KERNEL_STACK_END\nKERNEL_STACK_END: .dword _stack_end\n\n\n"
  },
  {
    "path": "risc_v/chapters/ch6/src/asm/trap.S",
    "content": "# trap.S\n# Trap handler and global context\n# Steve Operating System\n# Stephen Marz\n# 24 February 2019\n.option norvc\n.altmacro\n.set NUM_GP_REGS, 32  # Number of registers per context\n.set NUM_FP_REGS, 32\n.set REG_SIZE, 8   # Register size (in bytes)\n.set MAX_CPUS, 8   # Maximum number of CPUs\n\n# Use macros for saving and restoring multiple registers\n.macro save_gp i, basereg=t6\n\tsd\tx\\i, ((\\i)*REG_SIZE)(\\basereg)\n.endm\n.macro load_gp i, basereg=t6\n\tld\tx\\i, ((\\i)*REG_SIZE)(\\basereg)\n.endm\n.macro save_fp i, basereg=t6\n\tfsd\tf\\i, ((NUM_GP_REGS+(\\i))*REG_SIZE)(\\basereg)\n.endm\n.macro load_fp i, basereg=t6\n\tfld\tf\\i, ((NUM_GP_REGS+(\\i))*REG_SIZE)(\\basereg)\n.endm\n\n\n.section .text\n.global m_trap_vector\n# This must be aligned by 4 since the last two bits\n# of the mtvec register do not contribute to the address\n# of this vector.\n.align 4\nm_trap_vector:\n\t# All registers are volatile here, we need to save them\n\t# before we do anything.\n\tcsrrw\tt6, mscratch, t6\n\t# csrrw will atomically swap t6 into mscratch and the old\n\t# value of mscratch into t6. This is nice because we just\n\t# switched values and didn't destroy anything -- all atomically!\n\t# in cpu.rs we have a structure of:\n\t#  32 gp regs\t\t0\n\t#  32 fp regs\t\t256\n\t#  SATP register\t512\n\t#  Trap stack       520\n\t#  CPU HARTID\t\t528\n\t# We use t6 as the temporary register because it is the very\n\t# bottom register (x31)\n\t.set \ti, 1\n\t.rept\t30\n\t\tsave_gp\t%i\n\t\t.set\ti, i+1\n\t.endr\n\n\t# Save the actual t6 register, which we swapped into\n\t# mscratch\n\tmv\t\tt5, t6\n\tcsrr\tt6, mscratch\n\tsave_gp 31, t5\n\n\t# Restore the kernel trap frame into mscratch\n\tcsrw\tmscratch, t5\n\n\t# Get ready to go into Rust (trap.rs)\n\t# We don't want to write into the user's stack or whomever\n\t# messed with us here.\n\tcsrr\ta0, mepc\n\tcsrr\ta1, mtval\n\tcsrr\ta2, mcause\n\tcsrr\ta3, mhartid\n\tcsrr\ta4, mstatus\n\tmv\t\ta5, t5\n\tld\t\tsp, 520(a5)\n\tcall\tm_trap\n\n\t# When we get here, we've returned from m_trap, restore registers\n\t# and return.\n\t# m_trap will return the return address via a0.\n\n\tcsrw\tmepc, a0\n\n\t# Now load the trap frame back into t6\n\tcsrr\tt6, mscratch\n\n\t# Restore all GP registers\n\t.set\ti, 1\n\t.rept\t31\n\t\tload_gp %i\n\t\t.set\ti, i+1\n\t.endr\n\n\t# Since we ran this loop 31 times starting with i = 1,\n\t# the last one loaded t6 back to its original value.\n\n\tmret\n\n\n.global make_syscall\nmake_syscall:\n\tecall\n\tret\n"
  },
  {
    "path": "risc_v/chapters/ch6/src/cpu.rs",
    "content": "// cpu.rs\n// CPU and CPU-related routines\n// Also contains the kernel's trap frame\n// Stephen Marz\n// 14 October 2019\n\nuse core::ptr::null_mut;\n\n/// In 64-bit mode, we're given three different modes for the MMU:\n/// 0 - The MMU is off -- no protection and no translation PA = VA\n/// 8 - This is Sv39 mode -- 39-bit virtual addresses\n/// 9 - This is Sv48 mode -- 48-bit virtual addresses\n#[repr(usize)]\npub enum SatpMode {\n\tOff = 0,\n\tSv39 = 8,\n\tSv48 = 9,\n}\n\n/// The trap frame is set into a structure\n/// and packed into each hart's mscratch register.\n/// This allows for quick reference and full\n/// context switch handling.\n#[repr(C)]\n#[derive(Clone, Copy)]\npub struct TrapFrame {\n\tpub regs:       [usize; 32], // 0 - 255\n\tpub fregs:      [usize; 32], // 256 - 511\n\tpub satp:       usize,       // 512 - 519\n\tpub trap_stack: *mut u8,     // 520\n\tpub hartid:     usize,       // 528\n}\n\n/// Rust requires that we initialize our structures\n/// because of the move semantics. What'll happen below\n/// is Rust will construct a new TrapFrame and move it\n/// out of the zero() function below. Rust contains two\n/// different \"selfs\" where self can refer to the object\n/// in memory or Self (capital S) which refers to the\n/// data type of the structure. In the case below, this\n/// is TrapFrame.\nimpl TrapFrame {\n\tpub const fn zero() -> Self {\n\t\tTrapFrame { regs:       [0; 32],\n\t\t            fregs:      [0; 32],\n\t\t            satp:       0,\n\t\t            trap_stack: null_mut(),\n\t\t             hartid:     0, }\n\t}\n}\n\n/// The global kernel trap frame stores 8 separate\n/// frames -- one per CPU hart. We will switch these\n/// in and out and store a dormant trap frame with\n/// the process itself.\npub static mut KERNEL_TRAP_FRAME: [TrapFrame; 8] =\n\t[TrapFrame::zero(); 8];\n\n/// The SATP register contains three fields: mode, address space id, and\n/// the first level table address (level 2 for Sv39). This function\n/// helps make the 64-bit register contents based on those three\n/// fields.\npub const fn build_satp(mode: SatpMode, asid: usize, addr: usize) -> usize {\n\t(mode as usize) << 60\n\t| (asid & 0xffff) << 44\n\t| (addr >> 12) & 0xff_ffff_ffff\n}\n\npub fn mhartid_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, mhartid\" :\"=r\"(rval));\n\t\trval\n\t}\n}\npub fn mie_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, mie\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn mie_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw mie, $0\" :: \"r\"(val));\n\t}\n}\n\npub fn mstatus_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tmstatus, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn mstatus_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, mstatus\":\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn stvec_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tstvec, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn stvec_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, stvec\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn mscratch_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tmscratch, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn mscratch_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, mscratch\" : \"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn mscratch_swap(to: usize) -> usize {\n\tunsafe {\n\t\tlet from;\n\t\tasm!(\"csrrw\t$0, mscratch, $1\" : \"=r\"(from) : \"r\"(to));\n\t\tfrom\n\t}\n}\n\npub fn sscratch_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tsscratch, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn sscratch_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, sscratch\" : \"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn sscratch_swap(to: usize) -> usize {\n\tunsafe {\n\t\tlet from;\n\t\tasm!(\"csrrw\t$0, sscratch, $1\" : \"=r\"(from) : \"r\"(to));\n\t\tfrom\n\t}\n}\n\npub fn sepc_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw sepc, $0\" :: \"r\"(val));\n\t}\n}\n\npub fn sepc_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, sepc\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn satp_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw satp, $0\" :: \"r\"(val));\n\t}\n}\n\npub fn satp_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, satp\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\n/// Take a hammer to the page tables and synchronize\n/// all of them. This essentially flushes the entire\n/// TLB.\npub fn satp_fence(vaddr: usize, asid: usize) {\n\tunsafe {\n\t\tasm!(\"sfence.vma $0, $1\" :: \"r\"(vaddr), \"r\"(asid));\n\t}\n}\n\n/// Synchronize based on the address space identifier\n/// This allows us to fence a particular process rather\n/// than the entire TLB.\n/// The RISC-V documentation calls this a TLB flush +.\n/// Since there are other memory routines involved, they\n/// didn't call it a TLB flush, but it is much like\n/// Intel/AMD's invtlb [] instruction.\npub fn satp_fence_asid(asid: usize) {\n\tunsafe {\n\t\tasm!(\"sfence.vma zero, $0\" :: \"r\"(asid));\n\t}\n}\n"
  },
  {
    "path": "risc_v/chapters/ch6/src/kmem.rs",
    "content": "// kmem.rs\n// Sub-page level: malloc-like allocation system\n// Stephen Marz\n// 7 October 2019\n\nuse crate::page::{align_val, zalloc, Table, PAGE_SIZE};\nuse core::{mem::size_of, ptr::null_mut};\n\n#[repr(usize)]\nenum AllocListFlags {\n\tTaken = 1 << 63,\n}\nimpl AllocListFlags {\n\tpub fn val(self) -> usize {\n\t\tself as usize\n\t}\n}\n\nstruct AllocList {\n\tpub flags_size: usize,\n}\nimpl AllocList {\n\tpub fn is_taken(&self) -> bool {\n\t\tself.flags_size & AllocListFlags::Taken.val() != 0\n\t}\n\n\tpub fn is_free(&self) -> bool {\n\t\t!self.is_taken()\n\t}\n\n\tpub fn set_taken(&mut self) {\n\t\tself.flags_size |= AllocListFlags::Taken.val();\n\t}\n\n\tpub fn set_free(&mut self) {\n\t\tself.flags_size &= !AllocListFlags::Taken.val();\n\t}\n\n\tpub fn set_size(&mut self, sz: usize) {\n\t\tlet k = self.is_taken();\n\t\tself.flags_size = sz & !AllocListFlags::Taken.val();\n\t\tif k {\n\t\t\tself.flags_size |= AllocListFlags::Taken.val();\n\t\t}\n\t}\n\n\tpub fn get_size(&self) -> usize {\n\t\tself.flags_size & !AllocListFlags::Taken.val()\n\t}\n}\n\n// This is the head of the allocation. We start here when\n// we search for a free memory location.\nstatic mut KMEM_HEAD: *mut AllocList = null_mut();\n// In the future, we will have on-demand pages\n// so, we need to keep track of our memory footprint to\n// see if we actually need to allocate more.\nstatic mut KMEM_ALLOC: usize = 0;\nstatic mut KMEM_PAGE_TABLE: *mut Table = null_mut();\n\n// These functions are safe helpers around an unsafe\n// operation.\npub fn get_head() -> *mut u8 {\n\tunsafe { KMEM_HEAD as *mut u8 }\n}\n\npub fn get_page_table() -> *mut Table {\n\tunsafe { KMEM_PAGE_TABLE as *mut Table }\n}\n\npub fn get_num_allocations() -> usize {\n\tunsafe { KMEM_ALLOC }\n}\n\n/// Initialize kernel's memory\n/// This is not to be used to allocate memory\n/// for user processes. If that's the case, use\n/// alloc/dealloc from the page crate.\npub fn init() {\n\tunsafe {\n\t\t// Allocate kernel pages (KMEM_ALLOC)\n\t\tKMEM_ALLOC = 512;\n\t\tlet k_alloc = zalloc(KMEM_ALLOC);\n\t\tassert!(!k_alloc.is_null());\n\t\tKMEM_HEAD = k_alloc as *mut AllocList;\n\t\t(*KMEM_HEAD).set_free();\n\t\t(*KMEM_HEAD).set_size(KMEM_ALLOC * PAGE_SIZE);\n\t\tKMEM_PAGE_TABLE = zalloc(1) as *mut Table;\n\t}\n}\n\n/// Allocate sub-page level allocation based on bytes and zero the memory\npub fn kzmalloc(sz: usize) -> *mut u8 {\n\tlet size = align_val(sz, 3);\n\tlet ret = kmalloc(size);\n\n\tif !ret.is_null() {\n\t\tfor i in 0..size {\n\t\t\tunsafe {\n\t\t\t\t(*ret.add(i)) = 0;\n\t\t\t}\n\t\t}\n\t}\n\tret\n}\n\n/// Allocate sub-page level allocation based on bytes\npub fn kmalloc(sz: usize) -> *mut u8 {\n\tunsafe {\n\t\tlet size = align_val(sz, 3) + size_of::<AllocList>();\n\t\tlet mut head = KMEM_HEAD;\n\t\t// .add() uses pointer arithmetic, so we type-cast into a u8\n\t\t// so that we multiply by an absolute size (KMEM_ALLOC *\n\t\t// PAGE_SIZE).\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\n\t\twhile head < tail {\n\t\t\tif (*head).is_free() && size <= (*head).get_size() {\n\t\t\t\tlet chunk_size = (*head).get_size();\n\t\t\t\tlet rem = chunk_size - size;\n\t\t\t\t(*head).set_taken();\n\t\t\t\tif rem > size_of::<AllocList>() {\n\t\t\t\t\tlet next = (head as *mut u8).add(size)\n\t\t\t\t\t           as *mut AllocList;\n\t\t\t\t\t// There is space remaining here.\n\t\t\t\t\t(*next).set_free();\n\t\t\t\t\t(*next).set_size(rem);\n\t\t\t\t\t(*head).set_size(size);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// If we get here, take the entire chunk\n\t\t\t\t\t(*head).set_size(chunk_size);\n\t\t\t\t}\n\t\t\t\treturn head.add(1) as *mut u8;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// If we get here, what we saw wasn't a free\n\t\t\t\t// chunk, move on to the next.\n\t\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t\t       as *mut AllocList;\n\t\t\t}\n\t\t}\n\t}\n\t// If we get here, we didn't find any free chunks--i.e. there isn't\n\t// enough memory for this. TODO: Add on-demand page allocation.\n\tnull_mut()\n}\n\n/// Free a sub-page level allocation\npub fn kfree(ptr: *mut u8) {\n\tunsafe {\n\t\tif !ptr.is_null() {\n\t\t\tlet p = (ptr as *mut AllocList).offset(-1);\n\t\t\tif (*p).is_taken() {\n\t\t\t\t(*p).set_free();\n\t\t\t}\n\t\t\t// After we free, see if we can combine adjacent free\n\t\t\t// spots to see if we can reduce fragmentation.\n\t\t\tcoalesce();\n\t\t}\n\t}\n}\n\n/// Merge smaller chunks into a bigger chunk\npub fn coalesce() {\n\tunsafe {\n\t\tlet mut head = KMEM_HEAD;\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\n\t\twhile head < tail {\n\t\t\tlet next = (head as *mut u8).add((*head).get_size())\n\t\t\t           as *mut AllocList;\n\t\t\tif (*head).get_size() == 0 {\n\t\t\t\t// If this happens, then we have a bad heap\n\t\t\t\t// (double free or something). However, that\n\t\t\t\t// will cause an infinite loop since the next\n\t\t\t\t// pointer will never move beyond the current\n\t\t\t\t// location.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if next >= tail {\n\t\t\t\t// We calculated the next by using the size\n\t\t\t\t// given as get_size(), however this could push\n\t\t\t\t// us past the tail. In that case, the size is\n\t\t\t\t// wrong, hence we break and stop doing what we\n\t\t\t\t// need to do.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (*head).is_free() && (*next).is_free() {\n\t\t\t\t// This means we have adjacent blocks needing to\n\t\t\t\t// be freed. So, we combine them into one\n\t\t\t\t// allocation.\n\t\t\t\t(*head).set_size(\n\t\t\t\t                 (*head).get_size()\n\t\t\t\t                 + (*next).get_size(),\n\t\t\t\t);\n\t\t\t}\n\t\t\t// If we get here, we might've moved. Recalculate new\n\t\t\t// head.\n\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t       as *mut AllocList;\n\t\t}\n\t}\n}\n\n/// For debugging purposes, print the kmem table\npub fn print_table() {\n\tunsafe {\n\t\tlet mut head = KMEM_HEAD;\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\t\twhile head < tail {\n\t\t\tprintln!(\n\t\t\t         \"{:p}: Length = {:<10} Taken = {}\",\n\t\t\t         head,\n\t\t\t         (*head).get_size(),\n\t\t\t         (*head).is_taken()\n\t\t\t);\n\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t       as *mut AllocList;\n\t\t}\n\t}\n}\n\n// ///////////////////////////////////\n// / GLOBAL ALLOCATOR\n// ///////////////////////////////////\n\n// The global allocator allows us to use the data structures\n// in the core library, such as a linked list or B-tree.\n// We want to use these sparingly since we have a coarse-grained\n// allocator.\nuse core::alloc::{GlobalAlloc, Layout};\n\n// The global allocator is a static constant to a global allocator\n// structure. We don't need any members because we're using this\n// structure just to implement alloc and dealloc.\nstruct OsGlobalAlloc;\n\nunsafe impl GlobalAlloc for OsGlobalAlloc {\n\tunsafe fn alloc(&self, layout: Layout) -> *mut u8 {\n\t\t// We align to the next page size so that when\n\t\t// we divide by PAGE_SIZE, we get exactly the number\n\t\t// of pages necessary.\n\t\tkzmalloc(layout.size())\n\t}\n\n\tunsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {\n\t\t// We ignore layout since our allocator uses ptr_start -> last\n\t\t// to determine the span of an allocation.\n\t\tkfree(ptr);\n\t}\n}\n\n#[global_allocator]\n/// Technically, we don't need the {} at the end, but it\n/// reveals that we're creating a new structure and not just\n/// copying a value.\nstatic GA: OsGlobalAlloc = OsGlobalAlloc {};\n\n#[alloc_error_handler]\n/// If for some reason alloc() in the global allocator gets null_mut(),\n/// then we come here. This is a divergent function, so we call panic to\n/// let the tester know what's going on.\npub fn alloc_error(l: Layout) -> ! {\n\tpanic!(\n\t       \"Allocator failed to allocate {} bytes with {}-byte alignment.\",\n\t       l.size(),\n\t       l.align()\n\t);\n}\n"
  },
  {
    "path": "risc_v/chapters/ch6/src/lds/virt.lds",
    "content": "/*\n virt.lds\n Linker script for outputting to RISC-V QEMU \"virt\" machine.\n Stephen Marz\n 6 October 2019\n*/\n\n/*\n  riscv is the name of the architecture that the linker understands\n  for any RISC-V target (64-bit or 32-bit).\n\n  We will further refine this by using -mabi=lp64 and -march=rv64gc\n*/\nOUTPUT_ARCH( \"riscv\" )\n\n/*\nWe're setting our entry point to a symbol\ncalled _start which is inside of boot.S. This\nessentially stores the address of _start as the\n\"entry point\", or where CPU instructions should start\nexecuting.\n\nIn the rest of this script, we are going to place _start\nright at the beginning of 0x8000_0000 because this is where\nthe virtual machine and many RISC-V boards will start executing.\n*/\nENTRY( _start )\n\n/*\nThe MEMORY section will explain that we have \"ram\" that contains\na section that is 'w' (writeable), 'x' (executable), and 'a' (allocatable).\nWe use '!' to invert 'r' (read-only) and 'i' (initialized). We don't want\nour memory to be read-only, and we're stating that it is NOT initialized\nat the beginning.\n\nThe ORIGIN is the memory address 0x8000_0000. If we look at the virt\nspec or the specification for the RISC-V HiFive Unleashed, this is the\nstarting memory address for our code.\n\nSide note: There might be other boot ROMs at different addresses, but\ntheir job is to get to this point.\n\nFinally LENGTH = 128M tells the linker that we have 128 megabyte of RAM.\nThe linker will double check this to make sure everything can fit.\n\nThe HiFive Unleashed has a lot more RAM than this, but for the virtual \nmachine, I went with 128M since I think that's enough RAM for now.\n\nWe can provide other pieces of memory, such as QSPI, or ROM, but we're\ntelling the linker script here that we have one pool of RAM.\n*/\nMEMORY\n{\n  ram   (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M\n}\n\n/*\nPHDRS is short for \"program headers\", which we specify three here:\ntext - CPU instructions (executable sections)\ndata - Global, initialized variables\nbss  - Global, uninitialized variables (all will be set to 0 by boot.S)\n\nThe command PT_LOAD tells the linker that these sections will be loaded\nfrom the file into memory.\n\nWe can actually stuff all of these into a single program header, but by\nsplitting it up into three, we can actually use the other PT_* commands\nsuch as PT_DYNAMIC, PT_INTERP, PT_NULL to tell the linker where to find\nadditional information.\n\nHowever, for our purposes, every section will be loaded from the program\nheaders.\n*/\nPHDRS\n{\n  text PT_LOAD;\n  data PT_LOAD;\n  bss PT_LOAD;\n}\n\n/*\nWe are now going to organize the memory based on which\nsection it is in. In assembly, we can change the section\nwith the \".section\" directive. However, in C++ and Rust,\nCPU instructions go into text, global constants go into\nrodata, global initialized variables go into data, and\nglobal uninitialized variables go into bss.\n*/\nSECTIONS\n{\n  /*\n    The first part of our RAM layout will be the text section.\n\tSince our CPU instructions are here, and our memory starts at\n\t0x8000_0000, we need our entry point to line up here.\n  */\n  .text : {\n\t  /* \n\t    PROVIDE allows me to access a symbol called _text_start so\n\t\tI know where the text section starts in the operating system.\n\t\tThis should not move, but it is here for convenience.\n\t\tThe period '.' tells the linker to set _text_start to the\n\t\tCURRENT location ('.' = current memory location). This current\n\t\tmemory location moves as we add things.\n\t  */\n\n    PROVIDE(_text_start = .);\n\t/*\n\t  We are going to layout all text sections here, starting with \n\t  .text.init. The asterisk in front of the parentheses means to match\n\t  the .text.init section of ANY object file. Otherwise, we can specify\n\t  which object file should contain the .text.init section, for example,\n\t  boot.o(.text.init) would specifically put the .text.init section of\n\t  our bootloader here.\n\n\t  Because we might want to change the name of our files, we'll leave it\n\t  with a *.\n\n\t  Inside the parentheses is the name of the section. I created my own\n\t  called .text.init to make 100% sure that the _start is put right at the\n\t  beginning. The linker will lay this out in the order it receives it:\n\n\t  .text.init first\n\t  all .text sections next\n\t  any .text.* sections last\n\n\t  .text.* means to match anything after .text. If we didn't already specify\n\t  .text.init, this would've matched here. The assembler and linker can place\n\t  things in \"special\" text sections, so we match any we might come across here.\n\t*/\n    *(.text.init) *(.text .text.*)\n\n\t/*\n\t  Again, with PROVIDE, we're providing a readable symbol called _text_end, which is\n\t  set to the memory address AFTER .text.init, .text, and .text.*'s have been added.\n\t*/\n    PROVIDE(_text_end = .);\n\t/*\n\t  The portion after the right brace is in an odd format. However, this is telling the\n\t  linker what memory portion to put it in. We labeled our RAM, ram, with the constraints\n\t  that it is writeable, allocatable, and executable. The linker will make sure with this\n\t  that we can do all of those things.\n\n\t  >ram - This just tells the linker script to put this entire section (.text) into the\n\t         ram region of memory. To my knowledge, the '>' does not mean \"greater than\". Instead,\n\t\t\t it is a symbol to let the linker know we want to put this in ram.\n\n\t  AT>ram - This sets the LMA (load memory address) region to the same thing. LMA is the final\n\t           translation of a VMA (virtual memory address). With this linker script, we're loading\n\t\t\t   everything into its physical location. We'll let the kernel copy and sort out the \n\t\t\t   virtual memory. That's why >ram and AT>ram are continually the same thing.\n\n\t  :text  - This tells the linker script to put this into the :text program header. We've only\n\t           defined three: text, data, and bss. In this case, we're telling the linker script\n\t\t\t   to go into the text section.\n\t*/\n  } >ram AT>ram :text\n   /*\n     The global pointer allows the linker to position global variables and constants into\n\t independent positions relative to the gp (global pointer) register. The globals start\n\t after the text sections and are only relevant to the rodata, data, and bss sections.\n   */\n   PROVIDE(_global_pointer = .);\n   /*\n     Most compilers create a rodata (read only data) section for global constants. However,\n\t we're going to place ours in the text section. We can actually put this in :data, but\n\t since the .text section is read-only, we can place it there.\n\n\t NOTE: This doesn't actually do anything, yet. The actual \"protection\" cannot be done\n\t at link time. Instead, when we program the memory management unit (MMU), we will be\n\t able to choose which bits (R=read, W=write, X=execute) we want each memory segment\n\t to be able to do.\n   */\n  .rodata : {\n    PROVIDE(_rodata_start = .);\n    *(.rodata .rodata.*)\n    PROVIDE(_rodata_end = .);\n\t/*\n\t   Again, we're placing the rodata section in the memory segment \"ram\" and we're putting\n\t   it in the :text program header. We don't have one for rodata anyway.\n\t*/\n  } >ram AT>ram :text\n\n  .data : {\n\t/*\n\t   . = ALIGN(4096) tells the linker to align the current memory location (which is\n\t   0x8000_0000 + text section + rodata section) to 4096 bytes. This is because our paging\n\t   system's resolution is 4,096 bytes or 4 KiB.\n\t*/\n    . = ALIGN(4096);\n    PROVIDE(_data_start = .);\n\t/*\n\t   sdata and data are essentially the same thing. However, compilers usually use the\n\t   sdata sections for shorter, quicker loading sections. So, usually critical data\n\t   is loaded there. However, we're loading all of this in one fell swoop.\n\t   So, we're looking to put all of the following sections under the umbrella .data:\n\t   .sdata\n\t   .sdata.[anything]\n\t   .data\n\t   .data.[anything]\n\n\t   ...in that order.\n\t*/\n    *(.sdata .sdata.*) *(.data .data.*)\n    PROVIDE(_data_end = .);\n  } >ram AT>ram :data\n\n  .bss : {\n    PROVIDE(_bss_start = .);\n    *(.sbss .sbss.*) *(.bss .bss.*)\n    PROVIDE(_bss_end = .);\n  } >ram AT>ram :bss\n\n  /*\n     The following will be helpful when we allocate the kernel stack (_stack) and\n\t determine where the heap begnis and ends (_heap_start and _heap_start + _heap_size)/\n\t When we do memory allocation, we can use these symbols.\n\n\t We use the symbols instead of hard-coding an address because this is a floating target.\n\t As we add code, the heap moves farther down the memory and gets shorter.\n\n\t _memory_start will be set to 0x8000_0000 here. We use ORIGIN(ram) so that it will take\n\t whatever we set the origin of ram to. Otherwise, we'd have to change it more than once\n\t if we ever stray away from 0x8000_0000 as our entry point.\n  */\n  PROVIDE(_memory_start = ORIGIN(ram));\n  /*\n     Our kernel stack starts at the end of the bss segment (_bss_end). However, we're allocating\n\t 0x80000 bytes (524 KiB) to our kernel stack. This should be PLENTY of space. The reason\n\t we add the memory is because the stack grows from higher memory to lower memory (bottom to top).\n\t Therefore we set the stack at the very bottom of its allocated slot.\n\t When we go to allocate from the stack, we'll subtract the number of bytes we need.\n  */\n  PROVIDE(_stack_start = _bss_end);\n  PROVIDE(_stack_end = _stack_start + 0x8000);\n  PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));\n\n  /* \n     Finally, our heap starts right after the kernel stack. This heap will be used mainly\n\t to dole out memory for user-space applications. However, in some circumstances, it will\n\t be used for kernel memory as well.\n\n\t We don't align here because we let the kernel determine how it wants to do this.\n  */\n  PROVIDE(_heap_start = _stack_end);\n  PROVIDE(_heap_size = _memory_end - _heap_start);\n}\n"
  },
  {
    "path": "risc_v/chapters/ch6/src/lib.rs",
    "content": "// Steve Operating System\n// Stephen Marz\n// 21 Sep 2019\n#![no_std]\n#![feature(panic_info_message,\n           asm,\n           allocator_api,\n           alloc_error_handler,\n           alloc_prelude,\n           const_raw_ptr_to_usize_cast)]\n\n// #[macro_use]\nextern crate alloc;\n// This is experimental and requires alloc_prelude as a feature\n// use alloc::prelude::v1::*;\n\n// ///////////////////////////////////\n// / RUST MACROS\n// ///////////////////////////////////\n#[macro_export]\nmacro_rules! print\n{\n\t($($args:tt)+) => ({\n\t\t\tuse core::fmt::Write;\n\t\t\tlet _ = write!(crate::uart::Uart::new(0x1000_0000), $($args)+);\n\t\t\t});\n}\n#[macro_export]\nmacro_rules! println\n{\n\t() => ({\n\t\t   print!(\"\\r\\n\")\n\t\t   });\n\t($fmt:expr) => ({\n\t\t\tprint!(concat!($fmt, \"\\r\\n\"))\n\t\t\t});\n\t($fmt:expr, $($args:tt)+) => ({\n\t\t\tprint!(concat!($fmt, \"\\r\\n\"), $($args)+)\n\t\t\t});\n}\n\n// ///////////////////////////////////\n// / LANGUAGE STRUCTURES / FUNCTIONS\n// ///////////////////////////////////\n#[no_mangle]\nextern \"C\" fn eh_personality() {}\n\n#[panic_handler]\nfn panic(info: &core::panic::PanicInfo) -> ! {\n\tprint!(\"Aborting: \");\n\tif let Some(p) = info.location() {\n\t\tprintln!(\n\t\t         \"line {}, file {}: {}\",\n\t\t         p.line(),\n\t\t         p.file(),\n\t\t         info.message().unwrap()\n\t\t);\n\t}\n\telse {\n\t\tprintln!(\"no information available.\");\n\t}\n\tabort();\n}\n#[no_mangle]\nextern \"C\" fn abort() -> ! {\n\tloop {\n\t\tunsafe {\n\t\t\tasm!(\"wfi\"::::\"volatile\");\n\t\t}\n\t}\n}\n\n// ///////////////////////////////////\n// / CONSTANTS\n// ///////////////////////////////////\n// const STR_Y: &str = \"\\x1b[38;2;79;221;13m✓\\x1b[m\";\n// const STR_N: &str = \"\\x1b[38;2;221;41;13m✘\\x1b[m\";\n\n// The following symbols come from asm/mem.S. We can use\n// the symbols directly, but the address of the symbols\n// themselves are their values, which can cause issues.\n// Instead, I created doubleword values in mem.S in the .rodata and .data\n// sections.\n/*\nextern \"C\" {\n\tstatic TEXT_START: usize;\n\tstatic TEXT_END: usize;\n\tstatic DATA_START: usize;\n\tstatic DATA_END: usize;\n\tstatic RODATA_START: usize;\n\tstatic RODATA_END: usize;\n\tstatic BSS_START: usize;\n\tstatic BSS_END: usize;\n\tstatic KERNEL_STACK_START: usize;\n\tstatic KERNEL_STACK_END: usize;\n\tstatic HEAP_START: usize;\n\tstatic HEAP_SIZE: usize;\n}\n*/\n/// Identity map range\n/// Takes a contiguous allocation of memory and maps it using PAGE_SIZE\n/// This assumes that start <= end\npub fn id_map_range(root: &mut page::Table,\n                    start: usize,\n                    end: usize,\n                    bits: i64)\n{\n\tlet mut memaddr = start & !(page::PAGE_SIZE - 1);\n\tlet num_kb_pages =\n\t\t(page::align_val(end, 12) - memaddr) / page::PAGE_SIZE;\n\n\t// I named this num_kb_pages for future expansion when\n\t// I decide to allow for GiB (2^30) and 2MiB (2^21) page\n\t// sizes. However, the overlapping memory regions are causing\n\t// nightmares.\n\tfor _ in 0..num_kb_pages {\n\t\tpage::map(root, memaddr, memaddr, bits, 0);\n\t\tmemaddr += 1 << 12;\n\t}\n}\n// ///////////////////////////////////\n// / ENTRY POINT\n// ///////////////////////////////////\n#[no_mangle]\nextern \"C\" fn kinit() -> usize {\n\tuart::Uart::new(0x1000_0000).init();\n\tpage::init();\n\tkmem::init();\n\tlet ret = process::init();\n\tprintln!(\"Init process created at address 0x{:08x}\", ret);\n\t// We lower the threshold wall so our interrupts can jump over it.\n\tplic::set_threshold(0);\n\t// VIRTIO = [1..8]\n\t// UART0 = 10\n\t// PCIE = [32..35]\n\t// Enable the UART interrupt.\n\tplic::enable(10);\n\tplic::set_priority(10, 1);\n\tprintln!(\"UART interrupts have been enabled and are awaiting your command.\");\n\tprintln!(\"Getting ready for first process.\");\n\tprintln!(\"Issuing the first context-switch timer.\");\n\tunsafe {\n\t\tlet mtimecmp = 0x0200_4000 as *mut u64;\n\t\tlet mtime = 0x0200_bff8 as *const u64;\n\t\t// The frequency given by QEMU is 10_000_000 Hz, so this sets\n\t\t// the next interrupt to fire one second from now.\n\t\tmtimecmp.write_volatile(mtime.read_volatile() + 10_000_000);\n\t}\n\t// When we return, we put the return value into mepc and start there. This\n\t// should be init's starting point.\n\tret\n}\n#[no_mangle]\nextern \"C\" fn kinit_hart(hartid: usize) {\n\t// All non-0 harts initialize here.\n\tunsafe {\n\t\t// We have to store the kernel's table. The tables will be moved\n\t\t// back and forth between the kernel's table and user\n\t\t// applicatons' tables.\n\t\tcpu::mscratch_write(\n\t\t                    (&mut cpu::KERNEL_TRAP_FRAME[hartid]\n\t\t                     as *mut cpu::TrapFrame)\n\t\t                    as usize,\n\t\t);\n\t\t// Copy the same mscratch over to the supervisor version of the\n\t\t// same register.\n\t\tcpu::sscratch_write(cpu::mscratch_read());\n\t\tcpu::KERNEL_TRAP_FRAME[hartid].hartid = hartid;\n\t\t// We can't do the following until zalloc() is locked, but we\n\t\t// don't have locks, yet :( cpu::KERNEL_TRAP_FRAME[hartid].satp\n\t\t// = cpu::KERNEL_TRAP_FRAME[0].satp;\n\t\t// cpu::KERNEL_TRAP_FRAME[hartid].trap_stack = page::zalloc(1);\n\t}\n}\n\n// ///////////////////////////////////\n// / RUST MODULES\n// ///////////////////////////////////\n\npub mod cpu;\npub mod kmem;\npub mod page;\npub mod plic;\npub mod process;\npub mod trap;\npub mod uart;\n"
  },
  {
    "path": "risc_v/chapters/ch6/src/page.rs",
    "content": "// page.rs\n// Memory routines\n// Stephen Marz\n// 6 October 2019\n\nuse core::{mem::size_of, ptr::null_mut};\n\n// ////////////////////////////////\n// // Allocation routines\n// ////////////////////////////////\nextern \"C\" {\n\tstatic HEAP_START: usize;\n\tstatic HEAP_SIZE: usize;\n}\n\n// We will use ALLOC_START to mark the start of the actual\n// memory we can dish out.\nstatic mut ALLOC_START: usize = 0;\nconst PAGE_ORDER: usize = 12;\npub const PAGE_SIZE: usize = 1 << 12;\n\n/// Align (set to a multiple of some power of two)\n/// This takes an order which is the exponent to 2^order\n/// Therefore, all alignments must be made as a power of two.\n/// This function always rounds up.\npub const fn align_val(val: usize, order: usize) -> usize {\n\tlet o = (1usize << order) - 1;\n\t(val + o) & !o\n}\n\n#[repr(u8)]\npub enum PageBits {\n\tEmpty = 0,\n\tTaken = 1 << 0,\n\tLast = 1 << 1,\n}\n\nimpl PageBits {\n\t// We convert PageBits to a u8 a lot, so this is\n\t// for convenience.\n\tpub fn val(self) -> u8 {\n\t\tself as u8\n\t}\n}\n\n// Each page is described by the Page structure. Linux does this\n// as well, where each 4096-byte chunk of memory has a structure\n// associated with it. However, there structure is much larger.\npub struct Page {\n\tflags: u8,\n}\n\nimpl Page {\n\t// If this page has been marked as the final allocation,\n\t// this function returns true. Otherwise, it returns false.\n\tpub fn is_last(&self) -> bool {\n\t\tif self.flags & PageBits::Last.val() != 0 {\n\t\t\ttrue\n\t\t}\n\t\telse {\n\t\t\tfalse\n\t\t}\n\t}\n\n\t// If the page is marked as being taken (allocated), then\n\t// this function returns true. Otherwise, it returns false.\n\tpub fn is_taken(&self) -> bool {\n\t\tif self.flags & PageBits::Taken.val() != 0 {\n\t\t\ttrue\n\t\t}\n\t\telse {\n\t\t\tfalse\n\t\t}\n\t}\n\n\t// This is the opposite of is_taken().\n\tpub fn is_free(&self) -> bool {\n\t\t!self.is_taken()\n\t}\n\n\t// Clear the Page structure and all associated allocations.\n\tpub fn clear(&mut self) {\n\t\tself.flags = PageBits::Empty.val();\n\t}\n\n\t// Set a certain flag. We ran into trouble here since PageBits\n\t// is an enumeration and we haven't implemented the BitOr Trait\n\t// on it.\n\tpub fn set_flag(&mut self, flag: PageBits) {\n\t\tself.flags |= flag.val();\n\t}\n\n\tpub fn clear_flag(&mut self, flag: PageBits) {\n\t\tself.flags &= !(flag.val());\n\t}\n}\n\n/// Initialize the allocation system. There are several ways that we can\n/// implement the page allocator:\n/// 1. Free list (singly linked list where it starts at the first free\n/// allocation) 2. Bookkeeping list (structure contains a taken and length)\n/// 3. Allocate one Page structure per 4096 bytes (this is what I chose)\n/// 4. Others\npub fn init() {\n\tunsafe {\n\t\t// let desc_per_page = PAGE_SIZE / size_of::<Page>();\n\t\tlet num_pages = HEAP_SIZE / PAGE_SIZE;\n\t\t// let num_desc_pages = num_pages / desc_per_page;\n\t\tlet ptr = HEAP_START as *mut Page;\n\t\t// Clear all pages to make sure that they aren't accidentally\n\t\t// taken\n\t\tfor i in 0..num_pages {\n\t\t\t(*ptr.add(i)).clear();\n\t\t}\n\t\t// Determine where the actual useful memory starts. This will be\n\t\t// after all Page structures. We also must align the ALLOC_START\n\t\t// to a page-boundary (PAGE_SIZE = 4096). ALLOC_START =\n\t\t// (HEAP_START + num_pages * size_of::<Page>() + PAGE_SIZE - 1)\n\t\t// & !(PAGE_SIZE - 1);\n\t\tALLOC_START = align_val(\n\t\t                        HEAP_START\n\t\t                        + num_pages * size_of::<Page>(),\n\t\t                        PAGE_ORDER,\n\t\t);\n\t}\n}\n\n/// Allocate a page or multiple pages\n/// pages: the number of PAGE_SIZE pages to allocate\npub fn alloc(pages: usize) -> *mut u8 {\n\t// We have to find a contiguous allocation of pages\n\tassert!(pages > 0);\n\tunsafe {\n\t\t// We create a Page structure for each page on the heap. We\n\t\t// actually might have more since HEAP_SIZE moves and so does\n\t\t// the size of our structure, but we'll only waste a few bytes.\n\t\tlet num_pages = HEAP_SIZE / PAGE_SIZE;\n\t\tlet ptr = HEAP_START as *mut Page;\n\t\tfor i in 0..num_pages - pages {\n\t\t\tlet mut found = false;\n\t\t\t// Check to see if this Page is free. If so, we have our\n\t\t\t// first candidate memory address.\n\t\t\tif (*ptr.add(i)).is_free() {\n\t\t\t\t// It was FREE! Yay!\n\t\t\t\tfound = true;\n\t\t\t\tfor j in i..i + pages {\n\t\t\t\t\t// Now check to see if we have a\n\t\t\t\t\t// contiguous allocation for all of the\n\t\t\t\t\t// request pages. If not, we should\n\t\t\t\t\t// check somewhere else.\n\t\t\t\t\tif (*ptr.add(j)).is_taken() {\n\t\t\t\t\t\tfound = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// We've checked to see if there are enough contiguous\n\t\t\t// pages to form what we need. If we couldn't, found\n\t\t\t// will be false, otherwise it will be true, which means\n\t\t\t// we've found valid memory we can allocate.\n\t\t\tif found {\n\t\t\t\tfor k in i..i + pages - 1 {\n\t\t\t\t\t(*ptr.add(k)).set_flag(PageBits::Taken);\n\t\t\t\t}\n\t\t\t\t// The marker for the last page is\n\t\t\t\t// PageBits::Last This lets us know when we've\n\t\t\t\t// hit the end of this particular allocation.\n\t\t\t\t(*ptr.add(i+pages-1)).set_flag(PageBits::Taken);\n\t\t\t\t(*ptr.add(i+pages-1)).set_flag(PageBits::Last);\n\t\t\t\t// The Page structures themselves aren't the\n\t\t\t\t// useful memory. Instead, there is 1 Page\n\t\t\t\t// structure per 4096 bytes starting at\n\t\t\t\t// ALLOC_START.\n\t\t\t\treturn (ALLOC_START + PAGE_SIZE * i)\n\t\t\t\t       as *mut u8;\n\t\t\t}\n\t\t}\n\t}\n\n\t// If we get here, that means that no contiguous allocation was\n\t// found.\n\tnull_mut()\n}\n\n/// Allocate and zero a page or multiple pages\n/// pages: the number of pages to allocate\n/// Each page is PAGE_SIZE which is calculated as 1 << PAGE_ORDER\n/// On RISC-V, this typically will be 4,096 bytes.\npub fn zalloc(pages: usize) -> *mut u8 {\n\t// Allocate and zero a page.\n\t// First, let's get the allocation\n\tlet ret = alloc(pages);\n\tif !ret.is_null() {\n\t\tlet size = (PAGE_SIZE * pages) / 8;\n\t\tlet big_ptr = ret as *mut u64;\n\t\tfor i in 0..size {\n\t\t\t// We use big_ptr so that we can force an\n\t\t\t// sd (store doubleword) instruction rather than\n\t\t\t// the sb. This means 8x fewer stores than before.\n\t\t\t// Typically we have to be concerned about remaining\n\t\t\t// bytes, but fortunately 4096 % 8 = 0, so we\n\t\t\t// won't have any remaining bytes.\n\t\t\tunsafe {\n\t\t\t\t(*big_ptr.add(i)) = 0;\n\t\t\t}\n\t\t}\n\t}\n\tret\n}\n\n/// Deallocate a page by its pointer\n/// The way we've structured this, it will automatically coalesce\n/// contiguous pages.\npub fn dealloc(ptr: *mut u8) {\n\t// Make sure we don't try to free a null pointer.\n\tassert!(!ptr.is_null());\n\tunsafe {\n\t\tlet addr =\n\t\t\tHEAP_START + (ptr as usize - ALLOC_START) / PAGE_SIZE;\n\t\t// Make sure that the address makes sense. The address we\n\t\t// calculate here is the page structure, not the HEAP address!\n\t\tassert!(addr >= HEAP_START && addr < HEAP_START + HEAP_SIZE);\n\t\tlet mut p = addr as *mut Page;\n\t\t// Keep clearing pages until we hit the last page.\n\t\twhile (*p).is_taken() && !(*p).is_last() {\n\t\t\t(*p).clear();\n\t\t\tp = p.add(1);\n\t\t}\n\t\t// If the following assertion fails, it is most likely\n\t\t// caused by a double-free.\n\t\tassert!(\n\t\t        (*p).is_last() == true,\n\t\t        \"Possible double-free detected! (Not taken found \\\n\t\t         before last)\"\n\t\t);\n\t\t// If we get here, we've taken care of all previous pages and\n\t\t// we are on the last page.\n\t\t(*p).clear();\n\t}\n}\n\n/// Print all page allocations\n/// This is mainly used for debugging.\npub fn print_page_allocations() {\n\tunsafe {\n\t\tlet num_pages = (HEAP_SIZE - (ALLOC_START - HEAP_START)) / PAGE_SIZE;\n\t\tlet mut beg = HEAP_START as *const Page;\n\t\tlet end = beg.add(num_pages);\n\t\tlet alloc_beg = ALLOC_START;\n\t\tlet alloc_end = ALLOC_START + num_pages * PAGE_SIZE;\n\t\tprintln!();\n\t\tprintln!(\n\t\t         \"PAGE ALLOCATION TABLE\\nMETA: {:p} -> {:p}\\nPHYS: \\\n\t\t          0x{:x} -> 0x{:x}\",\n\t\t         beg, end, alloc_beg, alloc_end\n\t\t);\n\t\tprintln!(\"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\");\n\t\tlet mut num = 0;\n\t\twhile beg < end {\n\t\t\tif (*beg).is_taken() {\n\t\t\t\tlet start = beg as usize;\n\t\t\t\tlet memaddr = ALLOC_START\n\t\t\t\t              + (start - HEAP_START)\n\t\t\t\t                * PAGE_SIZE;\n\t\t\t\tprint!(\"0x{:x} => \", memaddr);\n\t\t\t\tloop {\n\t\t\t\t\tnum += 1;\n\t\t\t\t\tif (*beg).is_last() {\n\t\t\t\t\t\tlet end = beg as usize;\n\t\t\t\t\t\tlet memaddr = ALLOC_START\n\t\t\t\t\t\t              + (end\n\t\t\t\t\t\t                 - HEAP_START)\n\t\t\t\t\t\t                * PAGE_SIZE\n\t\t\t\t\t\t              + PAGE_SIZE - 1;\n\t\t\t\t\t\tprint!(\n\t\t\t\t\t\t       \"0x{:x}: {:>3} page(s)\",\n\t\t\t\t\t\t       memaddr,\n\t\t\t\t\t\t       (end - start + 1)\n\t\t\t\t\t\t);\n\t\t\t\t\t\tprintln!(\".\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tbeg = beg.add(1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbeg = beg.add(1);\n\t\t}\n\t\tprintln!(\"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\");\n\t\tprintln!(\n\t\t         \"Allocated: {:>6} pages ({:>10} bytes).\",\n\t\t         num,\n\t\t         num * PAGE_SIZE\n\t\t);\n\t\tprintln!(\n\t\t         \"Free     : {:>6} pages ({:>10} bytes).\",\n\t\t         num_pages - num,\n\t\t         (num_pages - num) * PAGE_SIZE\n\t\t);\n\t\tprintln!();\n\t}\n}\n\n// ////////////////////////////////\n// // MMU Routines\n// ////////////////////////////////\n\n// Represent (repr) our entry bits as\n// unsigned 64-bit integers.\n#[repr(i64)]\n#[derive(Copy, Clone)]\npub enum EntryBits {\n\tNone = 0,\n\tValid = 1 << 0,\n\tRead = 1 << 1,\n\tWrite = 1 << 2,\n\tExecute = 1 << 3,\n\tUser = 1 << 4,\n\tGlobal = 1 << 5,\n\tAccess = 1 << 6,\n\tDirty = 1 << 7,\n\n\t// Convenience combinations\n\tReadWrite = 1 << 1 | 1 << 2,\n\tReadExecute = 1 << 1 | 1 << 3,\n\tReadWriteExecute = 1 << 1 | 1 << 2 | 1 << 3,\n\n\t// User Convenience Combinations\n\tUserReadWrite = 1 << 1 | 1 << 2 | 1 << 4,\n\tUserReadExecute = 1 << 1 | 1 << 3 | 1 << 4,\n\tUserReadWriteExecute = 1 << 1 | 1 << 2 | 1 << 3 | 1 << 4,\n}\n\n// Helper functions to convert the enumeration\n// into an i64, which is what our page table\n// entries will be.\nimpl EntryBits {\n\tpub fn val(self) -> i64 {\n\t\tself as i64\n\t}\n}\n\n// A single entry. We're using an i64 so that\n// this will sign-extend rather than zero-extend\n// since RISC-V requires that the reserved sections\n// take on the most significant bit.\npub struct Entry {\n\tpub entry: i64,\n}\n\n// The Entry structure describes one of the 512 entries per table, which is\n// described in the RISC-V privileged spec Figure 4.18.\nimpl Entry {\n\tpub fn is_valid(&self) -> bool {\n\t\tself.get_entry() & EntryBits::Valid.val() != 0\n\t}\n\n\t// The first bit (bit index #0) is the V bit for\n\t// valid.\n\tpub fn is_invalid(&self) -> bool {\n\t\t!self.is_valid()\n\t}\n\n\t// A leaf has one or more RWX bits set\n\tpub fn is_leaf(&self) -> bool {\n\t\tself.get_entry() & 0xe != 0\n\t}\n\n\tpub fn is_branch(&self) -> bool {\n\t\t!self.is_leaf()\n\t}\n\n\tpub fn set_entry(&mut self, entry: i64) {\n\t\tself.entry = entry;\n\t}\n\n\tpub fn get_entry(&self) -> i64 {\n\t\tself.entry\n\t}\n}\n\n// Table represents a single table, which contains 512 (2^9), 64-bit entries.\npub struct Table {\n\tpub entries: [Entry; 512],\n}\n\nimpl Table {\n\tpub fn len() -> usize {\n\t\t512\n\t}\n}\n\n/// Map a virtual address to a physical address using 4096-byte page\n/// size.\n/// root: a mutable reference to the root Table\n/// vaddr: The virtual address to map\n/// paddr: The physical address to map\n/// bits: An OR'd bitset containing the bits the leaf should have.\n///       The bits should contain only the following:\n///          Read, Write, Execute, User, and/or Global\n///       The bits MUST include one or more of the following:\n///          Read, Write, Execute\n///       The valid bit automatically gets added.\npub fn map(root: &mut Table,\n           vaddr: usize,\n           paddr: usize,\n           bits: i64,\n           level: usize)\n{\n\t// Make sure that Read, Write, or Execute have been provided\n\t// otherwise, we'll leak memory and always create a page fault.\n\tassert!(bits & 0xe != 0);\n\t// Extract out each VPN from the virtual address\n\t// On the virtual address, each VPN is exactly 9 bits,\n\t// which is why we use the mask 0x1ff = 0b1_1111_1111 (9 bits)\n\tlet vpn = [\n\t           // VPN[0] = vaddr[20:12]\n\t           (vaddr >> 12) & 0x1ff,\n\t           // VPN[1] = vaddr[29:21]\n\t           (vaddr >> 21) & 0x1ff,\n\t           // VPN[2] = vaddr[38:30]\n\t           (vaddr >> 30) & 0x1ff,\n\t];\n\n\t// Just like the virtual address, extract the physical address\n\t// numbers (PPN). However, PPN[2] is different in that it stores\n\t// 26 bits instead of 9. Therefore, we use,\n\t// 0x3ff_ffff = 0b11_1111_1111_1111_1111_1111_1111 (26 bits).\n\tlet ppn = [\n\t           // PPN[0] = paddr[20:12]\n\t           (paddr >> 12) & 0x1ff,\n\t           // PPN[1] = paddr[29:21]\n\t           (paddr >> 21) & 0x1ff,\n\t           // PPN[2] = paddr[55:30]\n\t           (paddr >> 30) & 0x3ff_ffff,\n\t];\n\t// We will use this as a floating reference so that we can set\n\t// individual entries as we walk the table.\n\tlet mut v = &mut root.entries[vpn[2]];\n\t// Now, we're going to traverse the page table and set the bits\n\t// properly. We expect the root to be valid, however we're required to\n\t// create anything beyond the root.\n\t// In Rust, we create a range iterator using the .. operator.\n\t// The .rev() will reverse the iteration since we need to start with\n\t// VPN[2] The .. operator is inclusive on start but exclusive on end.\n\t// So, (0..2) will iterate 0 and 1.\n\tfor i in (level..2).rev() {\n\t\tif !v.is_valid() {\n\t\t\t// Allocate a page\n\t\t\tlet page = zalloc(1);\n\t\t\t// The page is already aligned by 4,096, so store it\n\t\t\t// directly The page is stored in the entry shifted\n\t\t\t// right by 2 places.\n\t\t\tv.set_entry(\n\t\t\t            (page as i64 >> 2)\n\t\t\t            | EntryBits::Valid.val(),\n\t\t\t);\n\t\t}\n\t\tlet entry = ((v.get_entry() & !0x3ff) << 2) as *mut Entry;\n\t\tv = unsafe { entry.add(vpn[i]).as_mut().unwrap() };\n\t}\n\t// When we get here, we should be at VPN[0] and v should be pointing to\n\t// our entry.\n\t// The entry structure is Figure 4.18 in the RISC-V Privileged\n\t// Specification\n\tlet entry = (ppn[2] << 28) as i64 |   // PPN[2] = [53:28]\n\t            (ppn[1] << 19) as i64 |   // PPN[1] = [27:19]\n\t\t\t\t(ppn[0] << 10) as i64 |   // PPN[0] = [18:10]\n\t\t\t\tbits |                    // Specified bits, such as User, Read, Write, etc\n\t\t\t\tEntryBits::Valid.val() |  // Valid bit\n\t\t\t\tEntryBits::Dirty.val() |  // Some machines require this to =1\n\t\t\t\tEntryBits::Access.val()   // Just like dirty, some machines require this\n\t\t\t\t;\n\t// Set the entry. V should be set to the correct pointer by the loop\n\t// above.\n\tv.set_entry(entry);\n}\n\n/// Unmaps and frees all memory associated with a table.\n/// root: The root table to start freeing.\n/// NOTE: This does NOT free root directly. This must be\n/// freed manually.\n/// The reason we don't free the root is because it is\n/// usually embedded into the Process structure.\npub fn unmap(root: &mut Table) {\n\t// Start with level 2\n\tfor lv2 in 0..Table::len() {\n\t\tlet ref entry_lv2 = root.entries[lv2];\n\t\tif entry_lv2.is_valid() && entry_lv2.is_branch() {\n\t\t\t// This is a valid entry, so drill down and free.\n\t\t\tlet memaddr_lv1 = (entry_lv2.get_entry() & !0x3ff) << 2;\n\t\t\tlet table_lv1 = unsafe {\n\t\t\t\t// Make table_lv1 a mutable reference instead of\n\t\t\t\t// a pointer.\n\t\t\t\t(memaddr_lv1 as *mut Table).as_mut().unwrap()\n\t\t\t};\n\t\t\tfor lv1 in 0..Table::len() {\n\t\t\t\tlet ref entry_lv1 = table_lv1.entries[lv1];\n\t\t\t\tif entry_lv1.is_valid() && entry_lv1.is_branch()\n\t\t\t\t{\n\t\t\t\t\tlet memaddr_lv0 = (entry_lv1.get_entry()\n\t\t\t\t\t                   & !0x3ff) << 2;\n\t\t\t\t\t// The next level is level 0, which\n\t\t\t\t\t// cannot have branches, therefore,\n\t\t\t\t\t// we free here.\n\t\t\t\t\tdealloc(memaddr_lv0 as *mut u8);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdealloc(memaddr_lv1 as *mut u8);\n\t\t}\n\t}\n}\n\n/// Walk the page table to convert a virtual address to a\n/// physical address.\n/// If a page fault would occur, this returns None\n/// Otherwise, it returns Some with the physical address.\npub fn virt_to_phys(root: &Table, vaddr: usize) -> Option<usize> {\n\t// Walk the page table pointed to by root\n\tlet vpn = [\n\t           // VPN[0] = vaddr[20:12]\n\t           (vaddr >> 12) & 0x1ff,\n\t           // VPN[1] = vaddr[29:21]\n\t           (vaddr >> 21) & 0x1ff,\n\t           // VPN[2] = vaddr[38:30]\n\t           (vaddr >> 30) & 0x1ff,\n\t];\n\n\tlet mut v = &root.entries[vpn[2]];\n\tfor i in (0..=2).rev() {\n\t\tif v.is_invalid() {\n\t\t\t// This is an invalid entry, page fault.\n\t\t\tbreak;\n\t\t}\n\t\telse if v.is_leaf() {\n\t\t\t// According to RISC-V, a leaf can be at any level.\n\n\t\t\t// The offset mask masks off the PPN. Each PPN is 9\n\t\t\t// bits and they start at bit #12. So, our formula\n\t\t\t// 12 + i * 9\n\t\t\tlet off_mask = (1 << (12 + i * 9)) - 1;\n\t\t\tlet vaddr_pgoff = vaddr & off_mask;\n\t\t\tlet addr = ((v.get_entry() << 2) as usize) & !off_mask;\n\t\t\treturn Some(addr | vaddr_pgoff);\n\t\t}\n\t\t// Set v to the next entry which is pointed to by this\n\t\t// entry. However, the address was shifted right by 2 places\n\t\t// when stored in the page table entry, so we shift it left\n\t\t// to get it back into place.\n\t\tlet entry = ((v.get_entry() & !0x3ff) << 2) as *const Entry;\n\t\t// We do i - 1 here, however we should get None or Some() above\n\t\t// before we do 0 - 1 = -1.\n\t\tv = unsafe { entry.add(vpn[i - 1]).as_ref().unwrap() };\n\t}\n\n\t// If we get here, we've exhausted all valid tables and haven't\n\t// found a leaf.\n\tNone\n}\n"
  },
  {
    "path": "risc_v/chapters/ch6/src/plic.rs",
    "content": "// plic.rs\n// Platform Level Interrupt Controller (PLIC)\n// Stephen Marz\n// 1 Nov 2019\n\nconst PLIC_PRIORITY: usize = 0x0c00_0000;\nconst PLIC_PENDING: usize = 0x0c00_1000;\nconst PLIC_INT_ENABLE: usize = 0x0c00_2000;\nconst PLIC_THRESHOLD: usize = 0x0c20_0000;\nconst PLIC_CLAIM: usize = 0x0c20_0004;\n\n// Each register is 4-bytes (u32)\n// The PLIC is an external interrupt controller. The one\n// used by QEMU virt is the same as the SiFive PLIC.\n// https://sifive.cdn.prismic.io/sifive%2F834354f0-08e6-423c-bf1f-0cb58ef14061_fu540-c000-v1.0.pdf\n\n// Chapter 10 explains the priority, pending, interrupt enable, threshold and claims\n\n// The virt machine has the following external interrupts (from Qemu source):\n// Interrupt 0 is a \"null\" interrupt and is hardwired to 0.\n// VIRTIO = [1..8]\n// UART0 = 10\n// PCIE = [32..35]\n\n\n/// Get the next available interrupt. This is the \"claim\" process.\n/// The plic will automatically sort by priority and hand us the\n/// ID of the interrupt. For example, if the UART is interrupting\n/// and it's next, we will get the value 10.\npub fn next() -> Option<u32> {\n    let claim_reg = PLIC_CLAIM as *const u32;\n    let claim_no;\n    // The claim register is filled with the highest-priority, enabled interrupt.\n    unsafe {\n        claim_no = claim_reg.read_volatile();\n    }\n    if claim_no == 0 {\n        // The interrupt 0 is hardwired to 0, which tells us that there is no\n        // interrupt to claim, hence we return None.\n        None\n    }\n    else {\n        // If we get here, we've gotten a non-0 interrupt.\n        Some(claim_no)\n    }\n}\n\n/// Complete a pending interrupt by id. The id should come\n/// from the next() function above.\npub fn complete(id: u32) {\n    let complete_reg = PLIC_CLAIM as *mut u32;\n    unsafe {\n        // We actually write a u32 into the entire complete_register.\n        // This is the same register as the claim register, but it can\n        // differentiate based on whether we're reading or writing.\n        complete_reg.write_volatile(id);\n    }\n}\n\n/// Set the global threshold. The threshold can be a value [0..7].\n/// The PLIC will mask any interrupts at or below the given threshold.\n/// This means that a threshold of 7 will mask ALL interrupts and\n/// a threshold of 0 will allow ALL interrupts.\npub fn set_threshold(tsh: u8) {\n    // We do tsh because we're using a u8, but our maximum number\n    // is a 3-bit 0b111. So, we and with 7 (0b111) to just get the\n    // last three bits.\n    let actual_tsh = tsh & 7;\n    let tsh_reg = PLIC_THRESHOLD as *mut u32;\n    unsafe {\n        tsh_reg.write_volatile(actual_tsh as u32);\n    }\n}\n\n/// See if a given interrupt id is pending.\npub fn is_pending(id: u32) -> bool {\n    let pend = PLIC_PENDING as *const u32;\n    let actual_id = 1 << id;\n    let pend_ids;\n    unsafe {\n        pend_ids = pend.read_volatile();\n    }\n    actual_id & pend_ids != 0\n}\n\n/// Enable a given interrupt id\npub fn enable(id: u32) {\n    let enables = PLIC_INT_ENABLE as *mut u32;\n    let actual_id = 1 << id;\n    unsafe {\n        // Unlike the complete and claim registers, the plic_int_enable\n        // register is a bitset where the id is the bit index. The register\n        // is a 32-bit register, so that gives us enables for interrupts\n        // 31 through 1 (0 is hardwired to 0).\n        enables.write_volatile(enables.read_volatile() | actual_id);\n    }\n}\n\n/// Set a given interrupt priority to the given priority.\n/// The priority must be [0..7]\npub fn set_priority(id: u32, prio: u8) {\n    let actual_prio = prio as u32 & 7;\n    let prio_reg = PLIC_PRIORITY as *mut u32;\n    unsafe {\n        // The offset for the interrupt id is:\n        // PLIC_PRIORITY + 4 * id\n        // Since we're using pointer arithmetic on a u32 type,\n        // it will automatically multiply the id by 4.\n        prio_reg.add(id as usize).write_volatile(actual_prio);\n    }\n}\n\n"
  },
  {
    "path": "risc_v/chapters/ch6/src/process.rs",
    "content": "// process.rs\n// Kernel and user processes\n// Stephen Marz\n// 27 Nov 2019\n\nuse crate::{cpu::{build_satp,\n                  mscratch_write,\n                  satp_fence_asid,\n                  satp_write,\n                  SatpMode,\n                  TrapFrame},\n            page::{alloc,\n                   dealloc,\n                   map,\n                   unmap,\n                   zalloc,\n                   EntryBits,\n                   Table,\n                   PAGE_SIZE}};\nuse alloc::collections::vec_deque::VecDeque;\n\n// How many pages are we going to give a process for their\n// stack?\nconst STACK_PAGES: usize = 2;\n// We want to adjust the stack to be at the bottom of the memory allocation\n// regardless of where it is on the kernel heap.\nconst STACK_ADDR: usize = 0xf_0000_0000;\n// All processes will have a defined starting point in virtual memory.\nconst PROCESS_STARTING_ADDR: usize = 0x2000_0000;\n\n// Here, we store a process list. It uses the global allocator\n// that we made before and its job is to store all processes.\n// We will have this list OWN the process. So, anytime we want\n// the process, we will consult the process list.\n// Using an Option here is one method of creating a \"lazy static\".\n// Rust requires that all statics be initialized, but all\n// initializations must be at compile-time. We cannot allocate\n// a VecDeque at compile time, so we are somewhat forced to\n// do this.\nstatic mut PROCESS_LIST: Option<VecDeque<Process>> = None;\n// We can search through the process list to get a new PID, but\n// it's probably easier and faster just to increase the pid:\nstatic mut NEXT_PID: u16 = 1;\n\n/// We will eventually move this function out of here, but its\n/// job is just to take a slot in the process list.\nfn init_process() {\n\t// We can't do much here until we have system calls because\n\t// we're running in User space.\n\tloop {}\n}\n\n/// Add a process given a function address and then\n/// push it onto the LinkedList. Uses Process::new_default\n/// to create a new stack, etc.\npub fn add_process_default(pr: fn()) {\n\tunsafe {\n\t\t// This is the Rust-ism that really trips up C++ programmers.\n\t\t// PROCESS_LIST is wrapped in an Option<> enumeration, which\n\t\t// means that the Option owns the Deque. We can only borrow from\n\t\t// it or move ownership to us. In this case, we choose the\n\t\t// latter, where we move ownership to us, add a process, and\n\t\t// then move ownership back to the PROCESS_LIST.\n\t\t// This allows mutual exclusion as anyone else trying to grab\n\t\t// the process list will get None rather than the Deque.\n\t\tif let Some(mut pl) = PROCESS_LIST.take() {\n\t\t\t// .take() will replace PROCESS_LIST with None and give\n\t\t\t// us the only copy of the Deque.\n\t\t\tlet p = Process::new_default(pr);\n\t\t\tpl.push_back(p);\n\t\t\t// Now, we no longer need the owned Deque, so we hand it\n\t\t\t// back by replacing the PROCESS_LIST's None with the\n\t\t\t// Some(pl).\n\t\t\tPROCESS_LIST.replace(pl);\n\t\t}\n\t\t// TODO: When we get to multi-hart processing, we need to keep\n\t\t// trying to grab the process list. We can do this with an\n\t\t// atomic instruction. but right now, we're a single-processor\n\t\t// computer.\n\t}\n}\n\n/// This should only be called once, and its job is to create\n/// the init process. Right now, this process is in the kernel,\n/// but later, it should call the shell.\npub fn init() -> usize {\n\tunsafe {\n\t\tPROCESS_LIST = Some(VecDeque::with_capacity(5));\n\t\tadd_process_default(init_process);\n\t\t// Ugh....Rust is giving me fits over here!\n\t\t// I just want a memory address to the trap frame, but\n\t\t// due to the borrow rules of Rust, I'm fighting here. So,\n\t\t// instead, let's move the value out of PROCESS_LIST, get\n\t\t// the address, and then move it right back in.\n\t\tlet pl = PROCESS_LIST.take().unwrap();\n\t\tlet p = pl.front().unwrap().frame;\n\t\tlet frame = &p as *const TrapFrame as usize;\n\t\tmscratch_write(frame);\n\t\tsatp_write(build_satp(\n\t\t\tSatpMode::Sv39,\n\t\t\t1,\n\t\t\tpl.front().unwrap().root as usize,\n\t\t),);\n\t\t// Synchronize PID 1. We use ASID as the PID.\n\t\tsatp_fence_asid(1);\n\t\t// Put the process list back in the global.\n\t\tPROCESS_LIST.replace(pl);\n\t\t// Return the first instruction's address to execute.\n\t\t// Since we use the MMU, all start here.\n\t\tPROCESS_STARTING_ADDR\n\t}\n}\n\n// Our process must be able to sleep, wait, or run.\n// Running - means that when the scheduler finds this process, it can run it.\n// Sleeping - means that the process is waiting on a certain amount of time.\n// Waiting - means that the process is waiting on I/O\n// Dead - We should never get here, but we can flag a process as Dead and clean\n//        it out of the list later.\npub enum ProcessState {\n\tRunning,\n\tSleeping,\n\tWaiting,\n\tDead,\n}\n\n// Let's represent this in C ABI. We do this\n// because we need to access some of the fields\n// in assembly. Rust gets to choose how it orders\n// the fields unless we represent the structure in\n// C-style ABI.\n#[repr(C)]\npub struct Process {\n\tframe:           TrapFrame,\n\tstack:           *mut u8,\n\tprogram_counter: usize,\n\tpid:             u16,\n\troot:            *mut Table,\n\tstate:           ProcessState,\n\tdata:            ProcessData,\n}\n\nimpl Process {\n\tpub fn new_default(func: fn()) -> Self {\n\t\tlet func_addr = func as usize;\n\t\t// We will convert NEXT_PID below into an atomic increment when\n\t\t// we start getting into multi-hart processing. For now, we want\n\t\t// a process. Get it to work, then improve it!\n\t\tlet mut ret_proc =\n\t\t\tProcess { frame:           TrapFrame::zero(),\n\t\t\t          stack:           alloc(STACK_PAGES),\n\t\t\t          program_counter: PROCESS_STARTING_ADDR,\n\t\t\t          pid:             unsafe { NEXT_PID },\n\t\t\t          root:            zalloc(1) as *mut Table,\n\t\t\t          state:           ProcessState::Waiting,\n\t\t\t          data:            ProcessData::zero(), };\n\t\tunsafe {\n\t\t\tNEXT_PID += 1;\n\t\t}\n\t\t// Now we move the stack pointer to the bottom of the\n\t\t// allocation. The spec shows that register x2 (2) is the stack\n\t\t// pointer.\n\t\t// We could use ret_proc.stack.add, but that's an unsafe\n\t\t// function which would require an unsafe block. So, convert it\n\t\t// to usize first and then add PAGE_SIZE is better.\n\t\t// We also need to set the stack adjustment so that it is at the\n\t\t// bottom of the memory and far away from heap allocations.\n\t\tret_proc.frame.regs[2] = STACK_ADDR + PAGE_SIZE * STACK_PAGES;\n\t\t// Map the stack on the MMU\n\t\tlet pt;\n\t\tunsafe {\n\t\t\tpt = &mut *ret_proc.root;\n\t\t}\n\t\tlet saddr = ret_proc.stack as usize;\n\t\t// We need to map the stack onto the user process' virtual\n\t\t// memory This gets a little hairy because we need to also map\n\t\t// the function code too.\n\t\tfor i in 0..STACK_PAGES {\n\t\t\tlet addr = i * PAGE_SIZE;\n\t\t\tmap(\n\t\t\t    pt,\n\t\t\t    STACK_ADDR + addr,\n\t\t\t    saddr + addr,\n\t\t\t    EntryBits::UserReadWrite.val(),\n\t\t\t    0,\n\t\t\t);\n\t\t}\n\t\t// Map the program counter on the MMU\n\t\tmap(\n\t\t    pt,\n\t\t    PROCESS_STARTING_ADDR,\n\t\t    func_addr,\n\t\t    EntryBits::UserReadExecute.val(),\n\t\t    0,\n\t\t);\n\t\tmap(\n\t\t    pt,\n\t\t    PROCESS_STARTING_ADDR + 0x1001,\n\t\t    func_addr + 0x1001,\n\t\t    EntryBits::UserReadExecute.val(),\n\t\t    0,\n\t\t);\n\t\tret_proc\n\t}\n}\n\nimpl Drop for Process {\n\t/// Since we're storing ownership of a Process in the linked list,\n\t/// we can cause it to deallocate automatically when it is removed.\n\tfn drop(&mut self) {\n\t\t// We allocate the stack as a page.\n\t\tdealloc(self.stack);\n\t\t// This is unsafe, but it's at the drop stage, so we won't\n\t\t// be using this again.\n\t\tunsafe {\n\t\t\t// Remember that unmap unmaps all levels of page tables\n\t\t\t// except for the root. It also deallocates the memory\n\t\t\t// associated with the tables.\n\t\t\tunmap(&mut *self.root);\n\t\t}\n\t\tdealloc(self.root as *mut u8);\n\t}\n}\n\n// The private data in a process contains information\n// that is relevant to where we are, including the path\n// and open file descriptors.\npub struct ProcessData {\n\tcwd_path: [u8; 128],\n}\n\n// This is private data that we can query with system calls.\n// If we want to implement CFQ (completely fair queuing), which\n// is a per-process block queuing algorithm, we can put that here.\nimpl ProcessData {\n\tpub fn zero() -> Self {\n\t\tProcessData { cwd_path: [0; 128], }\n\t}\n}\n"
  },
  {
    "path": "risc_v/chapters/ch6/src/trap.rs",
    "content": "// trap.rs\n// Trap routines\n// Stephen Marz\n// 10 October 2019\n\nuse crate::cpu::TrapFrame;\nuse crate::{plic, uart};\n\n#[no_mangle]\n/// The m_trap stands for \"machine trap\". Right now, we are handling\n/// all traps at machine mode. In this mode, we can figure out what's\n/// going on and send a trap where it needs to be. Remember, in machine\n/// mode and in this trap, interrupts are disabled and the MMU is off.\nextern \"C\" fn m_trap(epc: usize,\n                     tval: usize,\n                     cause: usize,\n                     hart: usize,\n                     status: usize,\n                     frame: *mut TrapFrame)\n                     -> usize\n{\n\t// We're going to handle all traps in machine mode. RISC-V lets\n\t// us delegate to supervisor mode, but switching out SATP (virtual memory)\n\t// gets hairy.\n\tlet is_async = {\n\t\tif cause >> 63 & 1 == 1 {\n\t\t\ttrue\n\t\t}\n\t\telse {\n\t\t\tfalse\n\t\t}\n\t};\n\t// The cause contains the type of trap (sync, async) as well as the cause\n\t// number. So, here we narrow down just the cause number.\n\tlet cause_num = cause & 0xfff;\n\tlet mut return_pc = epc;\n\tif is_async {\n\t\t// Asynchronous trap\n\t\tmatch cause_num {\n\t\t\t3 => {\n\t\t\t\t// Machine software\n\t\t\t\tprintln!(\"Machine software interrupt CPU#{}\", hart);\n\t\t\t},\n\t\t\t7 => unsafe {\n\t\t\t\t// This is the context-switch timer.\n\t\t\t\t// We would typically invoke the scheduler here to pick another\n\t\t\t\t// process to run.\n\t\t\t\t// Machine timer\n\t\t\t\tprintln!(\"CTX\");\n\t\t\t\tlet mtimecmp = 0x0200_4000 as *mut u64;\n\t\t\t\tlet mtime = 0x0200_bff8 as *const u64;\n\t\t\t\t// The frequency given by QEMU is 10_000_000 Hz, so this sets\n\t\t\t\t// the next interrupt to fire one second from now.\n\t\t\t\t// This is much too slow for normal operations, but it gives us\n\t\t\t\t// a visual of what's happening behind the scenes.\n\t\t\t\tmtimecmp.write_volatile(mtime.read_volatile() + 10_000_000);\n\t\t\t},\n\t\t\t11 => {\n\t\t\t\t// Machine external (interrupt from Platform Interrupt Controller (PLIC))\n\t\t\t\t// println!(\"Machine external interrupt CPU#{}\", hart);\n\t\t\t\t// We will check the next interrupt. If the interrupt isn't available, this will\n\t\t\t\t// give us None. However, that would mean we got a spurious interrupt, unless we\n\t\t\t\t// get an interrupt from a non-PLIC source. This is the main reason that the PLIC\n\t\t\t\t// hardwires the id 0 to 0, so that we can use it as an error case.\n\t\t\t\tif let Some(interrupt) = plic::next() {\n\t\t\t\t\t// If we get here, we've got an interrupt from the claim register. The PLIC will\n\t\t\t\t\t// automatically prioritize the next interrupt, so when we get it from claim, it\n\t\t\t\t\t// will be the next in priority order.\n\t\t\t\t\tmatch interrupt {\n\t\t\t\t\t\t10 => { // Interrupt 10 is the UART interrupt.\n\t\t\t\t\t\t\t// We would typically set this to be handled out of the interrupt context,\n\t\t\t\t\t\t\t// but we're testing here! C'mon!\n\t\t\t\t\t\t\t// We haven't yet used the singleton pattern for my_uart, but remember, this\n\t\t\t\t\t\t\t// just simply wraps 0x1000_0000 (UART).\n\t\t\t\t\t\t\tlet mut my_uart = uart::Uart::new(0x1000_0000);\n\t\t\t\t\t\t\t// If we get here, the UART better have something! If not, what happened??\n\t\t\t\t\t\t\tif let Some(c) = my_uart.get() {\n\t\t\t\t\t\t\t\t// If you recognize this code, it used to be in the lib.rs under kmain(). That\n\t\t\t\t\t\t\t\t// was because we needed to poll for UART data. Now that we have interrupts,\n\t\t\t\t\t\t\t\t// here it goes!\n\t\t\t\t\t\t\t\tmatch c {\n\t\t\t\t\t\t\t\t\t8 => {\n\t\t\t\t\t\t\t\t\t\t// This is a backspace, so we\n\t\t\t\t\t\t\t\t\t\t// essentially have to write a space and\n\t\t\t\t\t\t\t\t\t\t// backup again:\n\t\t\t\t\t\t\t\t\t\tprint!(\"{} {}\", 8 as char, 8 as char);\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t10 | 13 => {\n\t\t\t\t\t\t\t\t\t\t// Newline or carriage-return\n\t\t\t\t\t\t\t\t\t\tprintln!();\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t_ => {\n\t\t\t\t\t\t\t\t\t\tprint!(\"{}\", c as char);\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t\t},\n\t\t\t\t\t\t// Non-UART interrupts go here and do nothing.\n\t\t\t\t\t\t_ => {\n\t\t\t\t\t\t\tprintln!(\"Non-UART external interrupt: {}\", interrupt);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// We've claimed it, so now say that we've handled it. This resets the interrupt pending\n\t\t\t\t\t// and allows the UART to interrupt again. Otherwise, the UART will get \"stuck\".\n\t\t\t\t\tplic::complete(interrupt);\n\t\t\t\t}\n\t\t\t},\n\t\t\t_ => {\n\t\t\t\tpanic!(\"Unhandled async trap CPU#{} -> {}\\n\", hart, cause_num);\n\t\t\t}\n\t\t}\n\t}\n\telse {\n\t\t// Synchronous trap\n\t\tmatch cause_num {\n\t\t\t2 => {\n\t\t\t\t// Illegal instruction\n\t\t\t\tpanic!(\"Illegal instruction CPU#{} -> 0x{:08x}: 0x{:08x}\\n\", hart, epc, tval);\n\t\t\t},\n\t\t\t8 => {\n\t\t\t\t// Environment (system) call from User mode\n\t\t\t\tprintln!(\"E-call from User mode! CPU#{} -> 0x{:08x}\", hart, epc);\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t9 => {\n\t\t\t\t// Environment (system) call from Supervisor mode\n\t\t\t\tprintln!(\"E-call from Supervisor mode! CPU#{} -> 0x{:08x}\", hart, epc);\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t11 => {\n\t\t\t\t// Environment (system) call from Machine mode\n\t\t\t\tpanic!(\"E-call from Machine mode! CPU#{} -> 0x{:08x}\\n\", hart, epc);\n\t\t\t},\n\t\t\t// Page faults\n\t\t\t12 => {\n\t\t\t\t// Instruction page fault\n\t\t\t\tprintln!(\"Instruction page fault CPU#{} -> 0x{:08x}: 0x{:08x}\", hart, epc, tval);\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t13 => {\n\t\t\t\t// Load page fault\n\t\t\t\tprintln!(\"Load page fault CPU#{} -> 0x{:08x}: 0x{:08x}\", hart, epc, tval);\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t15 => {\n\t\t\t\t// Store page fault\n\t\t\t\tprintln!(\"Store page fault CPU#{} -> 0x{:08x}: 0x{:08x}\", hart, epc, tval);\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t_ => {\n\t\t\t\tpanic!(\"Unhandled sync trap CPU#{} -> {}\\n\", hart, cause_num);\n\t\t\t}\n\t\t}\n\t};\n\t// Finally, return the updated program counter\n\treturn_pc\n}\n"
  },
  {
    "path": "risc_v/chapters/ch6/src/uart.rs",
    "content": "// uart.rs\n// UART routines and driver\n\nuse core::{convert::TryInto,\n           fmt::{Error, Write}};\n\npub struct Uart {\n\tbase_address: usize,\n}\n\nimpl Write for Uart {\n\tfn write_str(&mut self, out: &str) -> Result<(), Error> {\n\t\tfor c in out.bytes() {\n\t\t\tself.put(c);\n\t\t}\n\t\tOk(())\n\t}\n}\n\nimpl Uart {\n\tpub fn new(base_address: usize) -> Self {\n\t\tUart { base_address }\n\t}\n\n\tpub fn init(&mut self) {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\t// First, set the word length, which\n\t\t\t// are bits 0 and 1 of the line control register (LCR)\n\t\t\t// which is at base_address + 3\n\t\t\t// We can easily write the value 3 here or 0b11, but I'm\n\t\t\t// extending it so that it is clear we're setting two\n\t\t\t// individual fields\n\t\t\t//             Word 0     Word 1\n\t\t\t//             ~~~~~~     ~~~~~~\n\t\t\tlet lcr: u8 = (1 << 0) | (1 << 1);\n\t\t\tptr.add(3).write_volatile(lcr);\n\n\t\t\t// Now, enable the FIFO, which is bit index 0 of the\n\t\t\t// FIFO control register (FCR at offset 2).\n\t\t\t// Again, we can just write 1 here, but when we use left\n\t\t\t// shift, it's easier to see that we're trying to write\n\t\t\t// bit index #0.\n\t\t\tptr.add(2).write_volatile(1 << 0);\n\n\t\t\t// Enable receiver buffer interrupts, which is at bit\n\t\t\t// index 0 of the interrupt enable register (IER at\n\t\t\t// offset 1).\n\t\t\tptr.add(1).write_volatile(1 << 0);\n\n\t\t\t// If we cared about the divisor, the code below would\n\t\t\t// set the divisor from a global clock rate of 22.729\n\t\t\t// MHz (22,729,000 cycles per second) to a signaling\n\t\t\t// rate of 2400 (BAUD). We usually have much faster\n\t\t\t// signalling rates nowadays, but this demonstrates what\n\t\t\t// the divisor actually does. The formula given in the\n\t\t\t// NS16500A specification for calculating the divisor\n\t\t\t// is:\n\t\t\t// divisor = ceil( (clock_hz) / (baud_sps x 16) )\n\t\t\t// So, we substitute our values and get:\n\t\t\t// divisor = ceil( 22_729_000 / (2400 x 16) )\n\t\t\t// divisor = ceil( 22_729_000 / 38_400 )\n\t\t\t// divisor = ceil( 591.901 ) = 592\n\n\t\t\t// The divisor register is two bytes (16 bits), so we\n\t\t\t// need to split the value 592 into two bytes.\n\t\t\t// Typically, we would calculate this based on measuring\n\t\t\t// the clock rate, but again, for our purposes [qemu],\n\t\t\t// this doesn't really do anything.\n\t\t\tlet divisor: u16 = 592;\n\t\t\tlet divisor_least: u8 =\n\t\t\t\t(divisor & 0xff).try_into().unwrap();\n\t\t\tlet divisor_most: u8 =\n\t\t\t\t(divisor >> 8).try_into().unwrap();\n\n\t\t\t// Notice that the divisor register DLL (divisor latch\n\t\t\t// least) and DLM (divisor latch most) have the same\n\t\t\t// base address as the receiver/transmitter and the\n\t\t\t// interrupt enable register. To change what the base\n\t\t\t// address points to, we open the \"divisor latch\" by\n\t\t\t// writing 1 into the Divisor Latch Access Bit (DLAB),\n\t\t\t// which is bit index 7 of the Line Control Register\n\t\t\t// (LCR) which is at base_address + 3.\n\t\t\tptr.add(3).write_volatile(lcr | 1 << 7);\n\n\t\t\t// Now, base addresses 0 and 1 point to DLL and DLM,\n\t\t\t// respectively. Put the lower 8 bits of the divisor\n\t\t\t// into DLL\n\t\t\tptr.add(0).write_volatile(divisor_least);\n\t\t\tptr.add(1).write_volatile(divisor_most);\n\n\t\t\t// Now that we've written the divisor, we never have to\n\t\t\t// touch this again. In hardware, this will divide the\n\t\t\t// global clock (22.729 MHz) into one suitable for 2,400\n\t\t\t// signals per second. So, to once again get access to\n\t\t\t// the RBR/THR/IER registers, we need to close the DLAB\n\t\t\t// bit by clearing it to 0.\n\t\t\tptr.add(3).write_volatile(lcr);\n\t\t}\n\t}\n\n\tpub fn put(&mut self, c: u8) {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tloop {\n\t\t\t// Wait until previous data is flushed\n\t\t\tif unsafe { ptr.add(5).read_volatile() } & (1 << 5) != 0 {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tunsafe {\n\t\t\t// Write data\n\t\t\tptr.add(0).write_volatile(c);\n\t\t}\n\t}\n\n\tpub fn get(&mut self) -> Option<u8> {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\tif ptr.add(5).read_volatile() & 1 == 0 {\n\t\t\t\t// The DR bit is 0, meaning no data\n\t\t\t\tNone\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// The DR bit is 1, meaning data!\n\t\t\t\tSome(ptr.add(0).read_volatile())\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "risc_v/chapters/ch7/.cargo/config",
    "content": "[build]\ntarget = \"riscv64gc-unknown-none-elf\"\n\n[target.riscv64gc-unknown-none-elf]\nlinker = \"riscv64-unknown-linux-gnu-gcc\"\n"
  },
  {
    "path": "risc_v/chapters/ch7/.gitignore",
    "content": "os.elf\ntarget/*\nCargo.lock\nhdd.dsk\n"
  },
  {
    "path": "risc_v/chapters/ch7/Cargo.toml",
    "content": "[package]\nname = \"sos\"\nversion = \"0.1.0\"\nauthors = [\"Stephen Marz <stephen.marz@utk.edu>\"]\nedition = \"2018\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[lib]\ncrate-type = [\"staticlib\"]\n\n[dependencies]\n"
  },
  {
    "path": "risc_v/chapters/ch7/Makefile",
    "content": "#####\n## BUILD\n#####\nCC=riscv64-unknown-linux-gnu-gcc\nCFLAGS=-Wall -Wextra -pedantic -Wextra -O0 -g\nCFLAGS+=-static -ffreestanding -nostdlib -fno-rtti -fno-exceptions\nCFLAGS+=-march=rv64gc -mabi=lp64\nINCLUDES=\nLINKER_SCRIPT=-Tsrc/lds/virt.lds\nTYPE=debug\nRUST_TARGET=./target/riscv64gc-unknown-none-elf/$(TYPE)\nLIBS=-L$(RUST_TARGET)\nSOURCES_ASM=$(wildcard src/asm/*.S)\nLIB=-lsos -lgcc\nOUT=os.elf\n\n#####\n## QEMU\n#####\nQEMU=qemu-system-riscv64\nMACH=virt\nCPU=rv64\nCPUS=4\nMEM=128M\nDISK=hdd.dsk\n# DRIVE= -drive if=none,format=raw,file=$(DISK),id=foo -device virtio-blk-device,scsi=off,drive=foo\nDRIVE=\n\n\nall:\n\tcargo build\n\t$(CC) $(CFLAGS) $(LINKER_SCRIPT) $(INCLUDES) -o $(OUT) $(SOURCES_ASM) $(LIBS) $(LIB)\n\t\nrun: all\n\t$(QEMU) -machine $(MACH) -cpu $(CPU) -smp $(CPUS) -m $(MEM) $(DRIVE) -nographic -serial mon:stdio -bios none -kernel $(OUT)\n\n\n.PHONY: clean\nclean:\n\tcargo clean\n\trm -f $(OUT)\n"
  },
  {
    "path": "risc_v/chapters/ch7/make_hdd.sh",
    "content": "#!/bin/sh\n\ndd if=/dev/zero of=hdd.dsk bs=1M count=32\n"
  },
  {
    "path": "risc_v/chapters/ch7/src/asm/boot.S",
    "content": "# boot.S\n# bootloader for SoS\n# Stephen Marz\n# 8 February 2019\n\n# Disable generation of compressed instructions.\n.option norvc\n\n# Define a .text.init section. The .text.init is put at the\n# starting address so that the entry _start is put at the RISC-V\n# address 0x8000_0000.\n.section .text.init\n\n# Execution starts here.\n.global _start\n_start:\n\n\t# Disable linker instruction relaxation for the `la` instruction below.\n\t# This disallows the assembler from assuming that `gp` is already initialized.\n\t# This causes the value stored in `gp` to be calculated from `pc`.\n\t# The job of the global pointer is to give the linker the ability to address\n\t# memory relative to GP instead of as an absolute address.\n.option push\n.option norelax\n\tla\t\tgp, _global_pointer\n.option pop\n\t# SATP should be zero, but let's make sure. Each HART has its own\n\t# SATP register.\n\tcsrw\tsatp, zero\n\t# Any hardware threads (hart) that are not bootstrapping\n\t# need to wait for an IPI\n\tcsrr\tt0, mhartid\n\tbnez\tt0, 3f\n\n\t# Set all bytes in the BSS section to zero.\n\tla \t\ta0, _bss_start\n\tla\t\ta1, _bss_end\n\tbgeu\ta0, a1, 2f\n1:\n\tsd\t\tzero, (a0)\n\taddi\ta0, a0, 8\n\tbltu\ta0, a1, 1b\n2:\n\t# The stack grows from bottom to top, so we put the stack pointer\n\t# to the very end of the stack range.\n\tla\t\tsp, _stack_end\n\t# Setting `mstatus` register:\n\t# 0b01 << 11: Machine's previous protection mode is 2 (MPP=2).\n\tli\t\tt0, 0b11 << 11\n\tcsrw\tmstatus, t0\n\t# Do not allow interrupts while running kinit\n\tcsrw\tmie, zero\n\t# Machine's exception program counter (MEPC) is set to `kinit`.\n\tla\t\tt1, kinit\n\tcsrw\tmepc, t1\n\t# Set the return address to get us into supervisor mode\n\tla\t\tra, 2f\n\t# We use mret here so that the mstatus register is properly updated.\n\tmret\n2:\n\t# We set the return address (ra above) to this label. When kinit() is finished\n\t# in Rust, it will return here.\n\n\t# Setting `mstatus` (supervisor status) register:\n\t# 0b01 << 11 : Previous protection mode is 1 (MPP=01 [Supervisor]).\n\t# 1 << 7     : Previous machine interrupt-enable bit is 1 (MPIE=1 [Enabled])\n\t# 1 << 5     : Previous interrupt-enable bit is 1 (SPIE=1 [Enabled]).\n\t# We set the \"previous\" bits because the mret will write the current bits\n\t# with the previous bits.\n\tli\t\tt0, (0b00 << 11) | (1 << 7) | (1 << 5)\n\tcsrw\tmstatus, t0\n\t# Machine's trap vector base address is set to `m_trap_vector`, for\n\t# \"machine\" trap vector.\n\tla\t\tt2, m_trap_vector\n\tcsrw\tmtvec, t2\n\t# Jump to first process. We put the MPP = 00 for user mode, so after\n\t# mret, we will jump to the first process' addresss in user mode.\n\tcsrw\tmepc, a0\n\tli\t\tt2, 0xaaa\n\tcsrw\tmie, t2\n\tla\t\tra, 4f\n\tmret\n3:\n\n\t# Parked harts go here. We need to set these\n\t# to only awaken if it receives a software interrupt,\n\t# which we're going to call the SIPI (Software Intra-Processor Interrupt).\n\t# We call the SIPI by writing the software interrupt into the Core Local Interruptor (CLINT)\n\t# Which is calculated by: base_address + hart * 4\n\t# where base address is 0x0200_0000 (MMIO CLINT base address)\n\t# We only use additional harts to run user-space programs, although this may\n\t# change.\n\n\t# We divide up the stack so the harts aren't clobbering one another.\n\tla\t\tsp, _stack_end\n\tli\t\tt0, 0x10000\n\tcsrr\ta0, mhartid\n\tmul\t\tt0, t0, a0\n\tsub\t\tsp, sp, t0\n\n\t# The parked harts will be put into machine mode with interrupts enabled.\n\tli\t\tt0, 0b11 << 11 | (1 << 7)\n\tcsrw\tmstatus, t0\n\t# Allow for MSIP (Software interrupt). We will write the MSIP from hart #0 to\n\t# awaken these parked harts.\n\tli\t\tt3, (1 << 3)\n\tcsrw\tmie, t3\n\t# Machine's exception program counter (MEPC) is set to the Rust initialization\n\t# code and waiting loop.\n\tla\t\tt1, kinit_hart\n\tcsrw\tmepc, t1\n\t# Machine's trap vector base address is set to `m_trap_vector`, for\n\t# \"machine\" trap vector. The Rust initialization routines will give each\n\t# hart its own trap frame. We can use the same trap function and distinguish\n\t# between each hart by looking at the trap frame.\n\tla\t\tt2, m_trap_vector\n\tcsrw\tmtvec, t2\n\t# Whenever our hart is done initializing, we want it to return to the waiting\n\t# loop, which is just below mret.\n\tla\t\tra, 4f\n\t# We use mret here so that the mstatus register is properly updated.\n\tmret\n\n4:\n\t# wfi = wait for interrupt. This is a hint to the harts to shut everything needed\n\t# down. However, the RISC-V specification allows for wfi to do nothing. Anyway,\n\t# with QEMU, this will save some CPU!\n\twfi\n\tj\t\t4b\n\n\n"
  },
  {
    "path": "risc_v/chapters/ch7/src/asm/mem.S",
    "content": "// mem.S\n// Importation of linker symbols\n\n.section .rodata\n.global HEAP_START\nHEAP_START: .dword _heap_start\n\n.global HEAP_SIZE\nHEAP_SIZE: .dword _heap_size\n\n.global TEXT_START\nTEXT_START: .dword _text_start\n\n.global TEXT_END\nTEXT_END: .dword _text_end\n\n.global DATA_START\nDATA_START: .dword _data_start\n\n.global DATA_END\nDATA_END: .dword _data_end\n\n.global RODATA_START\nRODATA_START: .dword _rodata_start\n\n.global RODATA_END\nRODATA_END: .dword _rodata_end\n\n.global BSS_START\nBSS_START: .dword _bss_start\n\n.global BSS_END\nBSS_END: .dword _bss_end\n\n.global KERNEL_STACK_START\nKERNEL_STACK_START: .dword _stack_start\n\n.global KERNEL_STACK_END\nKERNEL_STACK_END: .dword _stack_end\n\n\n"
  },
  {
    "path": "risc_v/chapters/ch7/src/asm/trap.S",
    "content": "# trap.S\n# Trap handler and global context\n# Steve Operating System\n# Stephen Marz\n# 24 February 2019\n.option norvc\n.altmacro\n.set NUM_GP_REGS, 32  # Number of registers per context\n.set NUM_FP_REGS, 32\n.set REG_SIZE, 8   # Register size (in bytes)\n.set MAX_CPUS, 8   # Maximum number of CPUs\n\n# Use macros for saving and restoring multiple registers\n.macro save_gp i, basereg=t6\n\tsd\tx\\i, ((\\i)*REG_SIZE)(\\basereg)\n.endm\n.macro load_gp i, basereg=t6\n\tld\tx\\i, ((\\i)*REG_SIZE)(\\basereg)\n.endm\n.macro save_fp i, basereg=t6\n\tfsd\tf\\i, ((NUM_GP_REGS+(\\i))*REG_SIZE)(\\basereg)\n.endm\n.macro load_fp i, basereg=t6\n\tfld\tf\\i, ((NUM_GP_REGS+(\\i))*REG_SIZE)(\\basereg)\n.endm\n\n\n.section .text\n.global m_trap_vector\n# This must be aligned by 4 since the last two bits\n# of the mtvec register do not contribute to the address\n# of this vector.\n.align 4\nm_trap_vector:\n\t# All registers are volatile here, we need to save them\n\t# before we do anything.\n\tcsrrw\tt6, mscratch, t6\n\t# csrrw will atomically swap t6 into mscratch and the old\n\t# value of mscratch into t6. This is nice because we just\n\t# switched values and didn't destroy anything -- all atomically!\n\t# in cpu.rs we have a structure of:\n\t#  32 gp regs\t\t0\n\t#  32 fp regs\t\t256\n\t#  SATP register\t512\n\t#  Trap stack       520\n\t#  CPU HARTID\t\t528\n\t# We use t6 as the temporary register because it is the very\n\t# bottom register (x31)\n\t.set \ti, 1\n\t.rept\t30\n\t\tsave_gp\t%i\n\t\t.set\ti, i+1\n\t.endr\n\n\t# Save the actual t6 register, which we swapped into\n\t# mscratch\n\tmv\t\tt5, t6\n\tcsrr\tt6, mscratch\n\tsave_gp 31, t5\n\n\t# Restore the kernel trap frame into mscratch\n\tcsrw\tmscratch, t5\n\n\t# Get ready to go into Rust (trap.rs)\n\t# We don't want to write into the user's stack or whomever\n\t# messed with us here.\n\tcsrr\ta0, mepc\n\tcsrr\ta1, mtval\n\tcsrr\ta2, mcause\n\tcsrr\ta3, mhartid\n\tcsrr\ta4, mstatus\n\tmv\t\ta5, t5\n\tld\t\tsp, 520(a5)\n\tcall\tm_trap\n\n\t# When we get here, we've returned from m_trap, restore registers\n\t# and return.\n\t# m_trap will return the return address via a0.\n\n\tcsrw\tmepc, a0\n\n\t# Now load the trap frame back into t6\n\tcsrr\tt6, mscratch\n\n\t# Restore all GP registers\n\t.set\ti, 1\n\t.rept\t31\n\t\tload_gp %i\n\t\t.set\ti, i+1\n\t.endr\n\n\t# Since we ran this loop 31 times starting with i = 1,\n\t# the last one loaded t6 back to its original value.\n\n\tmret\n\n\n.global make_syscall\nmake_syscall:\n\tecall\n\tret\n"
  },
  {
    "path": "risc_v/chapters/ch7/src/cpu.rs",
    "content": "// cpu.rs\n// CPU and CPU-related routines\n// Also contains the kernel's trap frame\n// Stephen Marz\n// 14 October 2019\n\nuse core::ptr::null_mut;\n\n/// In 64-bit mode, we're given three different modes for the MMU:\n/// 0 - The MMU is off -- no protection and no translation PA = VA\n/// 8 - This is Sv39 mode -- 39-bit virtual addresses\n/// 9 - This is Sv48 mode -- 48-bit virtual addresses\n#[repr(usize)]\npub enum SatpMode {\n\tOff = 0,\n\tSv39 = 8,\n\tSv48 = 9,\n}\n\n/// The trap frame is set into a structure\n/// and packed into each hart's mscratch register.\n/// This allows for quick reference and full\n/// context switch handling.\n#[repr(C)]\n#[derive(Clone, Copy)]\npub struct TrapFrame {\n\tpub regs:       [usize; 32], // 0 - 255\n\tpub fregs:      [usize; 32], // 256 - 511\n\tpub satp:       usize,       // 512 - 519\n\tpub trap_stack: *mut u8,     // 520\n\tpub hartid:     usize,       // 528\n}\n\n/// Rust requires that we initialize our structures\n/// because of the move semantics. What'll happen below\n/// is Rust will construct a new TrapFrame and move it\n/// out of the zero() function below. Rust contains two\n/// different \"selfs\" where self can refer to the object\n/// in memory or Self (capital S) which refers to the\n/// data type of the structure. In the case below, this\n/// is TrapFrame.\nimpl TrapFrame {\n\tpub const fn zero() -> Self {\n\t\tTrapFrame { regs:       [0; 32],\n\t\t            fregs:      [0; 32],\n\t\t            satp:       0,\n\t\t            trap_stack: null_mut(),\n\t\t             hartid:     0, }\n\t}\n}\n\n/// The global kernel trap frame stores 8 separate\n/// frames -- one per CPU hart. We will switch these\n/// in and out and store a dormant trap frame with\n/// the process itself.\npub static mut KERNEL_TRAP_FRAME: [TrapFrame; 8] =\n\t[TrapFrame::zero(); 8];\n\n/// The SATP register contains three fields: mode, address space id, and\n/// the first level table address (level 2 for Sv39). This function\n/// helps make the 64-bit register contents based on those three\n/// fields.\npub const fn build_satp(mode: SatpMode, asid: usize, addr: usize) -> usize {\n\t(mode as usize) << 60\n\t| (asid & 0xffff) << 44\n\t| (addr >> 12) & 0xff_ffff_ffff\n}\n\npub fn mhartid_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, mhartid\" :\"=r\"(rval));\n\t\trval\n\t}\n}\npub fn mie_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, mie\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn mie_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw mie, $0\" :: \"r\"(val));\n\t}\n}\n\npub fn mstatus_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tmstatus, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn mstatus_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, mstatus\":\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn stvec_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tstvec, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn stvec_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, stvec\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn mscratch_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tmscratch, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn mscratch_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, mscratch\" : \"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn mscratch_swap(to: usize) -> usize {\n\tunsafe {\n\t\tlet from;\n\t\tasm!(\"csrrw\t$0, mscratch, $1\" : \"=r\"(from) : \"r\"(to));\n\t\tfrom\n\t}\n}\n\npub fn sscratch_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tsscratch, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn sscratch_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, sscratch\" : \"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn sscratch_swap(to: usize) -> usize {\n\tunsafe {\n\t\tlet from;\n\t\tasm!(\"csrrw\t$0, sscratch, $1\" : \"=r\"(from) : \"r\"(to));\n\t\tfrom\n\t}\n}\n\npub fn sepc_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw sepc, $0\" :: \"r\"(val));\n\t}\n}\n\npub fn sepc_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, sepc\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn satp_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw satp, $0\" :: \"r\"(val));\n\t}\n}\n\npub fn satp_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, satp\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\n/// Take a hammer to the page tables and synchronize\n/// all of them. This essentially flushes the entire\n/// TLB.\npub fn satp_fence(vaddr: usize, asid: usize) {\n\tunsafe {\n\t\tasm!(\"sfence.vma $0, $1\" :: \"r\"(vaddr), \"r\"(asid));\n\t}\n}\n\n/// Synchronize based on the address space identifier\n/// This allows us to fence a particular process rather\n/// than the entire TLB.\n/// The RISC-V documentation calls this a TLB flush +.\n/// Since there are other memory routines involved, they\n/// didn't call it a TLB flush, but it is much like\n/// Intel/AMD's invtlb [] instruction.\npub fn satp_fence_asid(asid: usize) {\n\tunsafe {\n\t\tasm!(\"sfence.vma zero, $0\" :: \"r\"(asid));\n\t}\n}\n"
  },
  {
    "path": "risc_v/chapters/ch7/src/kmem.rs",
    "content": "// kmem.rs\n// Sub-page level: malloc-like allocation system\n// Stephen Marz\n// 7 October 2019\n\nuse crate::page::{align_val, zalloc, Table, PAGE_SIZE};\nuse core::{mem::size_of, ptr::null_mut};\n\n#[repr(usize)]\nenum AllocListFlags {\n\tTaken = 1 << 63,\n}\nimpl AllocListFlags {\n\tpub fn val(self) -> usize {\n\t\tself as usize\n\t}\n}\n\nstruct AllocList {\n\tpub flags_size: usize,\n}\nimpl AllocList {\n\tpub fn is_taken(&self) -> bool {\n\t\tself.flags_size & AllocListFlags::Taken.val() != 0\n\t}\n\n\tpub fn is_free(&self) -> bool {\n\t\t!self.is_taken()\n\t}\n\n\tpub fn set_taken(&mut self) {\n\t\tself.flags_size |= AllocListFlags::Taken.val();\n\t}\n\n\tpub fn set_free(&mut self) {\n\t\tself.flags_size &= !AllocListFlags::Taken.val();\n\t}\n\n\tpub fn set_size(&mut self, sz: usize) {\n\t\tlet k = self.is_taken();\n\t\tself.flags_size = sz & !AllocListFlags::Taken.val();\n\t\tif k {\n\t\t\tself.flags_size |= AllocListFlags::Taken.val();\n\t\t}\n\t}\n\n\tpub fn get_size(&self) -> usize {\n\t\tself.flags_size & !AllocListFlags::Taken.val()\n\t}\n}\n\n// This is the head of the allocation. We start here when\n// we search for a free memory location.\nstatic mut KMEM_HEAD: *mut AllocList = null_mut();\n// In the future, we will have on-demand pages\n// so, we need to keep track of our memory footprint to\n// see if we actually need to allocate more.\nstatic mut KMEM_ALLOC: usize = 0;\nstatic mut KMEM_PAGE_TABLE: *mut Table = null_mut();\n\n// These functions are safe helpers around an unsafe\n// operation.\npub fn get_head() -> *mut u8 {\n\tunsafe { KMEM_HEAD as *mut u8 }\n}\n\npub fn get_page_table() -> *mut Table {\n\tunsafe { KMEM_PAGE_TABLE as *mut Table }\n}\n\npub fn get_num_allocations() -> usize {\n\tunsafe { KMEM_ALLOC }\n}\n\n/// Initialize kernel's memory\n/// This is not to be used to allocate memory\n/// for user processes. If that's the case, use\n/// alloc/dealloc from the page crate.\npub fn init() {\n\tunsafe {\n\t\t// Allocate kernel pages (KMEM_ALLOC)\n\t\tKMEM_ALLOC = 512;\n\t\tlet k_alloc = zalloc(KMEM_ALLOC);\n\t\tassert!(!k_alloc.is_null());\n\t\tKMEM_HEAD = k_alloc as *mut AllocList;\n\t\t(*KMEM_HEAD).set_free();\n\t\t(*KMEM_HEAD).set_size(KMEM_ALLOC * PAGE_SIZE);\n\t\tKMEM_PAGE_TABLE = zalloc(1) as *mut Table;\n\t}\n}\n\n/// Allocate sub-page level allocation based on bytes and zero the memory\npub fn kzmalloc(sz: usize) -> *mut u8 {\n\tlet size = align_val(sz, 3);\n\tlet ret = kmalloc(size);\n\n\tif !ret.is_null() {\n\t\tfor i in 0..size {\n\t\t\tunsafe {\n\t\t\t\t(*ret.add(i)) = 0;\n\t\t\t}\n\t\t}\n\t}\n\tret\n}\n\n/// Allocate sub-page level allocation based on bytes\npub fn kmalloc(sz: usize) -> *mut u8 {\n\tunsafe {\n\t\tlet size = align_val(sz, 3) + size_of::<AllocList>();\n\t\tlet mut head = KMEM_HEAD;\n\t\t// .add() uses pointer arithmetic, so we type-cast into a u8\n\t\t// so that we multiply by an absolute size (KMEM_ALLOC *\n\t\t// PAGE_SIZE).\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\n\t\twhile head < tail {\n\t\t\tif (*head).is_free() && size <= (*head).get_size() {\n\t\t\t\tlet chunk_size = (*head).get_size();\n\t\t\t\tlet rem = chunk_size - size;\n\t\t\t\t(*head).set_taken();\n\t\t\t\tif rem > size_of::<AllocList>() {\n\t\t\t\t\tlet next = (head as *mut u8).add(size)\n\t\t\t\t\t           as *mut AllocList;\n\t\t\t\t\t// There is space remaining here.\n\t\t\t\t\t(*next).set_free();\n\t\t\t\t\t(*next).set_size(rem);\n\t\t\t\t\t(*head).set_size(size);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// If we get here, take the entire chunk\n\t\t\t\t\t(*head).set_size(chunk_size);\n\t\t\t\t}\n\t\t\t\treturn head.add(1) as *mut u8;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// If we get here, what we saw wasn't a free\n\t\t\t\t// chunk, move on to the next.\n\t\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t\t       as *mut AllocList;\n\t\t\t}\n\t\t}\n\t}\n\t// If we get here, we didn't find any free chunks--i.e. there isn't\n\t// enough memory for this. TODO: Add on-demand page allocation.\n\tnull_mut()\n}\n\n/// Free a sub-page level allocation\npub fn kfree(ptr: *mut u8) {\n\tunsafe {\n\t\tif !ptr.is_null() {\n\t\t\tlet p = (ptr as *mut AllocList).offset(-1);\n\t\t\tif (*p).is_taken() {\n\t\t\t\t(*p).set_free();\n\t\t\t}\n\t\t\t// After we free, see if we can combine adjacent free\n\t\t\t// spots to see if we can reduce fragmentation.\n\t\t\tcoalesce();\n\t\t}\n\t}\n}\n\n/// Merge smaller chunks into a bigger chunk\npub fn coalesce() {\n\tunsafe {\n\t\tlet mut head = KMEM_HEAD;\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\n\t\twhile head < tail {\n\t\t\tlet next = (head as *mut u8).add((*head).get_size())\n\t\t\t           as *mut AllocList;\n\t\t\tif (*head).get_size() == 0 {\n\t\t\t\t// If this happens, then we have a bad heap\n\t\t\t\t// (double free or something). However, that\n\t\t\t\t// will cause an infinite loop since the next\n\t\t\t\t// pointer will never move beyond the current\n\t\t\t\t// location.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if next >= tail {\n\t\t\t\t// We calculated the next by using the size\n\t\t\t\t// given as get_size(), however this could push\n\t\t\t\t// us past the tail. In that case, the size is\n\t\t\t\t// wrong, hence we break and stop doing what we\n\t\t\t\t// need to do.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (*head).is_free() && (*next).is_free() {\n\t\t\t\t// This means we have adjacent blocks needing to\n\t\t\t\t// be freed. So, we combine them into one\n\t\t\t\t// allocation.\n\t\t\t\t(*head).set_size(\n\t\t\t\t                 (*head).get_size()\n\t\t\t\t                 + (*next).get_size(),\n\t\t\t\t);\n\t\t\t}\n\t\t\t// If we get here, we might've moved. Recalculate new\n\t\t\t// head.\n\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t       as *mut AllocList;\n\t\t}\n\t}\n}\n\n/// For debugging purposes, print the kmem table\npub fn print_table() {\n\tunsafe {\n\t\tlet mut head = KMEM_HEAD;\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\t\twhile head < tail {\n\t\t\tprintln!(\n\t\t\t         \"{:p}: Length = {:<10} Taken = {}\",\n\t\t\t         head,\n\t\t\t         (*head).get_size(),\n\t\t\t         (*head).is_taken()\n\t\t\t);\n\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t       as *mut AllocList;\n\t\t}\n\t}\n}\n\n// ///////////////////////////////////\n// / GLOBAL ALLOCATOR\n// ///////////////////////////////////\n\n// The global allocator allows us to use the data structures\n// in the core library, such as a linked list or B-tree.\n// We want to use these sparingly since we have a coarse-grained\n// allocator.\nuse core::alloc::{GlobalAlloc, Layout};\n\n// The global allocator is a static constant to a global allocator\n// structure. We don't need any members because we're using this\n// structure just to implement alloc and dealloc.\nstruct OsGlobalAlloc;\n\nunsafe impl GlobalAlloc for OsGlobalAlloc {\n\tunsafe fn alloc(&self, layout: Layout) -> *mut u8 {\n\t\t// We align to the next page size so that when\n\t\t// we divide by PAGE_SIZE, we get exactly the number\n\t\t// of pages necessary.\n\t\tkzmalloc(layout.size())\n\t}\n\n\tunsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {\n\t\t// We ignore layout since our allocator uses ptr_start -> last\n\t\t// to determine the span of an allocation.\n\t\tkfree(ptr);\n\t}\n}\n\n#[global_allocator]\n/// Technically, we don't need the {} at the end, but it\n/// reveals that we're creating a new structure and not just\n/// copying a value.\nstatic GA: OsGlobalAlloc = OsGlobalAlloc {};\n\n#[alloc_error_handler]\n/// If for some reason alloc() in the global allocator gets null_mut(),\n/// then we come here. This is a divergent function, so we call panic to\n/// let the tester know what's going on.\npub fn alloc_error(l: Layout) -> ! {\n\tpanic!(\n\t       \"Allocator failed to allocate {} bytes with {}-byte alignment.\",\n\t       l.size(),\n\t       l.align()\n\t);\n}\n"
  },
  {
    "path": "risc_v/chapters/ch7/src/lds/virt.lds",
    "content": "/*\n virt.lds\n Linker script for outputting to RISC-V QEMU \"virt\" machine.\n Stephen Marz\n 6 October 2019\n*/\n\n/*\n  riscv is the name of the architecture that the linker understands\n  for any RISC-V target (64-bit or 32-bit).\n\n  We will further refine this by using -mabi=lp64 and -march=rv64gc\n*/\nOUTPUT_ARCH( \"riscv\" )\n\n/*\nWe're setting our entry point to a symbol\ncalled _start which is inside of boot.S. This\nessentially stores the address of _start as the\n\"entry point\", or where CPU instructions should start\nexecuting.\n\nIn the rest of this script, we are going to place _start\nright at the beginning of 0x8000_0000 because this is where\nthe virtual machine and many RISC-V boards will start executing.\n*/\nENTRY( _start )\n\n/*\nThe MEMORY section will explain that we have \"ram\" that contains\na section that is 'w' (writeable), 'x' (executable), and 'a' (allocatable).\nWe use '!' to invert 'r' (read-only) and 'i' (initialized). We don't want\nour memory to be read-only, and we're stating that it is NOT initialized\nat the beginning.\n\nThe ORIGIN is the memory address 0x8000_0000. If we look at the virt\nspec or the specification for the RISC-V HiFive Unleashed, this is the\nstarting memory address for our code.\n\nSide note: There might be other boot ROMs at different addresses, but\ntheir job is to get to this point.\n\nFinally LENGTH = 128M tells the linker that we have 128 megabyte of RAM.\nThe linker will double check this to make sure everything can fit.\n\nThe HiFive Unleashed has a lot more RAM than this, but for the virtual \nmachine, I went with 128M since I think that's enough RAM for now.\n\nWe can provide other pieces of memory, such as QSPI, or ROM, but we're\ntelling the linker script here that we have one pool of RAM.\n*/\nMEMORY\n{\n  ram   (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M\n}\n\n/*\nPHDRS is short for \"program headers\", which we specify three here:\ntext - CPU instructions (executable sections)\ndata - Global, initialized variables\nbss  - Global, uninitialized variables (all will be set to 0 by boot.S)\n\nThe command PT_LOAD tells the linker that these sections will be loaded\nfrom the file into memory.\n\nWe can actually stuff all of these into a single program header, but by\nsplitting it up into three, we can actually use the other PT_* commands\nsuch as PT_DYNAMIC, PT_INTERP, PT_NULL to tell the linker where to find\nadditional information.\n\nHowever, for our purposes, every section will be loaded from the program\nheaders.\n*/\nPHDRS\n{\n  text PT_LOAD;\n  data PT_LOAD;\n  bss PT_LOAD;\n}\n\n/*\nWe are now going to organize the memory based on which\nsection it is in. In assembly, we can change the section\nwith the \".section\" directive. However, in C++ and Rust,\nCPU instructions go into text, global constants go into\nrodata, global initialized variables go into data, and\nglobal uninitialized variables go into bss.\n*/\nSECTIONS\n{\n  /*\n    The first part of our RAM layout will be the text section.\n\tSince our CPU instructions are here, and our memory starts at\n\t0x8000_0000, we need our entry point to line up here.\n  */\n  .text : {\n\t  /* \n\t    PROVIDE allows me to access a symbol called _text_start so\n\t\tI know where the text section starts in the operating system.\n\t\tThis should not move, but it is here for convenience.\n\t\tThe period '.' tells the linker to set _text_start to the\n\t\tCURRENT location ('.' = current memory location). This current\n\t\tmemory location moves as we add things.\n\t  */\n\n    PROVIDE(_text_start = .);\n\t/*\n\t  We are going to layout all text sections here, starting with \n\t  .text.init. The asterisk in front of the parentheses means to match\n\t  the .text.init section of ANY object file. Otherwise, we can specify\n\t  which object file should contain the .text.init section, for example,\n\t  boot.o(.text.init) would specifically put the .text.init section of\n\t  our bootloader here.\n\n\t  Because we might want to change the name of our files, we'll leave it\n\t  with a *.\n\n\t  Inside the parentheses is the name of the section. I created my own\n\t  called .text.init to make 100% sure that the _start is put right at the\n\t  beginning. The linker will lay this out in the order it receives it:\n\n\t  .text.init first\n\t  all .text sections next\n\t  any .text.* sections last\n\n\t  .text.* means to match anything after .text. If we didn't already specify\n\t  .text.init, this would've matched here. The assembler and linker can place\n\t  things in \"special\" text sections, so we match any we might come across here.\n\t*/\n    *(.text.init) *(.text .text.*)\n\n\t/*\n\t  Again, with PROVIDE, we're providing a readable symbol called _text_end, which is\n\t  set to the memory address AFTER .text.init, .text, and .text.*'s have been added.\n\t*/\n    PROVIDE(_text_end = .);\n\t/*\n\t  The portion after the right brace is in an odd format. However, this is telling the\n\t  linker what memory portion to put it in. We labeled our RAM, ram, with the constraints\n\t  that it is writeable, allocatable, and executable. The linker will make sure with this\n\t  that we can do all of those things.\n\n\t  >ram - This just tells the linker script to put this entire section (.text) into the\n\t         ram region of memory. To my knowledge, the '>' does not mean \"greater than\". Instead,\n\t\t\t it is a symbol to let the linker know we want to put this in ram.\n\n\t  AT>ram - This sets the LMA (load memory address) region to the same thing. LMA is the final\n\t           translation of a VMA (virtual memory address). With this linker script, we're loading\n\t\t\t   everything into its physical location. We'll let the kernel copy and sort out the \n\t\t\t   virtual memory. That's why >ram and AT>ram are continually the same thing.\n\n\t  :text  - This tells the linker script to put this into the :text program header. We've only\n\t           defined three: text, data, and bss. In this case, we're telling the linker script\n\t\t\t   to go into the text section.\n\t*/\n  } >ram AT>ram :text\n   /*\n     The global pointer allows the linker to position global variables and constants into\n\t independent positions relative to the gp (global pointer) register. The globals start\n\t after the text sections and are only relevant to the rodata, data, and bss sections.\n   */\n   PROVIDE(_global_pointer = .);\n   /*\n     Most compilers create a rodata (read only data) section for global constants. However,\n\t we're going to place ours in the text section. We can actually put this in :data, but\n\t since the .text section is read-only, we can place it there.\n\n\t NOTE: This doesn't actually do anything, yet. The actual \"protection\" cannot be done\n\t at link time. Instead, when we program the memory management unit (MMU), we will be\n\t able to choose which bits (R=read, W=write, X=execute) we want each memory segment\n\t to be able to do.\n   */\n  .rodata : {\n    PROVIDE(_rodata_start = .);\n    *(.rodata .rodata.*)\n    PROVIDE(_rodata_end = .);\n\t/*\n\t   Again, we're placing the rodata section in the memory segment \"ram\" and we're putting\n\t   it in the :text program header. We don't have one for rodata anyway.\n\t*/\n  } >ram AT>ram :text\n\n  .data : {\n\t/*\n\t   . = ALIGN(4096) tells the linker to align the current memory location (which is\n\t   0x8000_0000 + text section + rodata section) to 4096 bytes. This is because our paging\n\t   system's resolution is 4,096 bytes or 4 KiB.\n\t*/\n    . = ALIGN(4096);\n    PROVIDE(_data_start = .);\n\t/*\n\t   sdata and data are essentially the same thing. However, compilers usually use the\n\t   sdata sections for shorter, quicker loading sections. So, usually critical data\n\t   is loaded there. However, we're loading all of this in one fell swoop.\n\t   So, we're looking to put all of the following sections under the umbrella .data:\n\t   .sdata\n\t   .sdata.[anything]\n\t   .data\n\t   .data.[anything]\n\n\t   ...in that order.\n\t*/\n    *(.sdata .sdata.*) *(.data .data.*)\n    PROVIDE(_data_end = .);\n  } >ram AT>ram :data\n\n  .bss : {\n    PROVIDE(_bss_start = .);\n    *(.sbss .sbss.*) *(.bss .bss.*)\n    PROVIDE(_bss_end = .);\n  } >ram AT>ram :bss\n\n  /*\n     The following will be helpful when we allocate the kernel stack (_stack) and\n\t determine where the heap begnis and ends (_heap_start and _heap_start + _heap_size)/\n\t When we do memory allocation, we can use these symbols.\n\n\t We use the symbols instead of hard-coding an address because this is a floating target.\n\t As we add code, the heap moves farther down the memory and gets shorter.\n\n\t _memory_start will be set to 0x8000_0000 here. We use ORIGIN(ram) so that it will take\n\t whatever we set the origin of ram to. Otherwise, we'd have to change it more than once\n\t if we ever stray away from 0x8000_0000 as our entry point.\n  */\n  PROVIDE(_memory_start = ORIGIN(ram));\n  /*\n     Our kernel stack starts at the end of the bss segment (_bss_end). However, we're allocating\n\t 0x80000 bytes (524 KiB) to our kernel stack. This should be PLENTY of space. The reason\n\t we add the memory is because the stack grows from higher memory to lower memory (bottom to top).\n\t Therefore we set the stack at the very bottom of its allocated slot.\n\t When we go to allocate from the stack, we'll subtract the number of bytes we need.\n  */\n  PROVIDE(_stack_start = _bss_end);\n  PROVIDE(_stack_end = _stack_start + 0x8000);\n  PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));\n\n  /* \n     Finally, our heap starts right after the kernel stack. This heap will be used mainly\n\t to dole out memory for user-space applications. However, in some circumstances, it will\n\t be used for kernel memory as well.\n\n\t We don't align here because we let the kernel determine how it wants to do this.\n  */\n  PROVIDE(_heap_start = _stack_end);\n  PROVIDE(_heap_size = _memory_end - _heap_start);\n}\n"
  },
  {
    "path": "risc_v/chapters/ch7/src/lib.rs",
    "content": "// Steve Operating System\n// Stephen Marz\n// 21 Sep 2019\n#![no_std]\n#![feature(panic_info_message,\n           asm,\n           allocator_api,\n           alloc_error_handler,\n           alloc_prelude,\n           const_raw_ptr_to_usize_cast)]\n\n// #[macro_use]\nextern crate alloc;\n// This is experimental and requires alloc_prelude as a feature\n// use alloc::prelude::v1::*;\n\n// ///////////////////////////////////\n// / RUST MACROS\n// ///////////////////////////////////\n#[macro_export]\nmacro_rules! print\n{\n\t($($args:tt)+) => ({\n\t\t\tuse core::fmt::Write;\n\t\t\tlet _ = write!(crate::uart::Uart::new(0x1000_0000), $($args)+);\n\t\t\t});\n}\n#[macro_export]\nmacro_rules! println\n{\n\t() => ({\n\t\t   print!(\"\\r\\n\")\n\t\t   });\n\t($fmt:expr) => ({\n\t\t\tprint!(concat!($fmt, \"\\r\\n\"))\n\t\t\t});\n\t($fmt:expr, $($args:tt)+) => ({\n\t\t\tprint!(concat!($fmt, \"\\r\\n\"), $($args)+)\n\t\t\t});\n}\n\n// ///////////////////////////////////\n// / LANGUAGE STRUCTURES / FUNCTIONS\n// ///////////////////////////////////\n#[no_mangle]\nextern \"C\" fn eh_personality() {}\n\n#[panic_handler]\nfn panic(info: &core::panic::PanicInfo) -> ! {\n\tprint!(\"Aborting: \");\n\tif let Some(p) = info.location() {\n\t\tprintln!(\n\t\t         \"line {}, file {}: {}\",\n\t\t         p.line(),\n\t\t         p.file(),\n\t\t         info.message().unwrap()\n\t\t);\n\t}\n\telse {\n\t\tprintln!(\"no information available.\");\n\t}\n\tabort();\n}\n#[no_mangle]\nextern \"C\" fn abort() -> ! {\n\tloop {\n\t\tunsafe {\n\t\t\tasm!(\"wfi\"::::\"volatile\");\n\t\t}\n\t}\n}\n\n// ///////////////////////////////////\n// / CONSTANTS\n// ///////////////////////////////////\n// const STR_Y: &str = \"\\x1b[38;2;79;221;13m✓\\x1b[m\";\n// const STR_N: &str = \"\\x1b[38;2;221;41;13m✘\\x1b[m\";\n\n// The following symbols come from asm/mem.S. We can use\n// the symbols directly, but the address of the symbols\n// themselves are their values, which can cause issues.\n// Instead, I created doubleword values in mem.S in the .rodata and .data\n// sections.\n/*\nextern \"C\" {\n\tstatic TEXT_START: usize;\n\tstatic TEXT_END: usize;\n\tstatic DATA_START: usize;\n\tstatic DATA_END: usize;\n\tstatic RODATA_START: usize;\n\tstatic RODATA_END: usize;\n\tstatic BSS_START: usize;\n\tstatic BSS_END: usize;\n\tstatic KERNEL_STACK_START: usize;\n\tstatic KERNEL_STACK_END: usize;\n\tstatic HEAP_START: usize;\n\tstatic HEAP_SIZE: usize;\n}\n*/\n/// Identity map range\n/// Takes a contiguous allocation of memory and maps it using PAGE_SIZE\n/// This assumes that start <= end\npub fn id_map_range(root: &mut page::Table,\n                    start: usize,\n                    end: usize,\n                    bits: i64)\n{\n\tlet mut memaddr = start & !(page::PAGE_SIZE - 1);\n\tlet num_kb_pages =\n\t\t(page::align_val(end, 12) - memaddr) / page::PAGE_SIZE;\n\n\t// I named this num_kb_pages for future expansion when\n\t// I decide to allow for GiB (2^30) and 2MiB (2^21) page\n\t// sizes. However, the overlapping memory regions are causing\n\t// nightmares.\n\tfor _ in 0..num_kb_pages {\n\t\tpage::map(root, memaddr, memaddr, bits, 0);\n\t\tmemaddr += 1 << 12;\n\t}\n}\n// ///////////////////////////////////\n// / ENTRY POINT\n// ///////////////////////////////////\n#[no_mangle]\nextern \"C\" fn kinit() -> usize {\n\tuart::Uart::new(0x1000_0000).init();\n\tpage::init();\n\tkmem::init();\n\tlet ret = process::init();\n\tprintln!(\"Init process created at address 0x{:08x}\", ret);\n\t// We lower the threshold wall so our interrupts can jump over it.\n\tplic::set_threshold(0);\n\t// VIRTIO = [1..8]\n\t// UART0 = 10\n\t// PCIE = [32..35]\n\t// Enable the UART interrupt.\n\tplic::enable(10);\n\tplic::set_priority(10, 1);\n\tprintln!(\"UART interrupts have been enabled and are awaiting your command.\");\n\tprintln!(\"Getting ready for first process.\");\n\tprintln!(\"Issuing the first context-switch timer.\");\n\tunsafe {\n\t\tlet mtimecmp = 0x0200_4000 as *mut u64;\n\t\tlet mtime = 0x0200_bff8 as *const u64;\n\t\t// The frequency given by QEMU is 10_000_000 Hz, so this sets\n\t\t// the next interrupt to fire one second from now.\n\t\tmtimecmp.write_volatile(mtime.read_volatile() + 10_000_000);\n\t}\n\t// When we return, we put the return value into mepc and start there. This\n\t// should be init's starting point.\n\tret\n}\n#[no_mangle]\nextern \"C\" fn kinit_hart(hartid: usize) {\n\t// All non-0 harts initialize here.\n\tunsafe {\n\t\t// We have to store the kernel's table. The tables will be moved\n\t\t// back and forth between the kernel's table and user\n\t\t// applicatons' tables.\n\t\tcpu::mscratch_write(\n\t\t                    (&mut cpu::KERNEL_TRAP_FRAME[hartid]\n\t\t                     as *mut cpu::TrapFrame)\n\t\t                    as usize,\n\t\t);\n\t\t// Copy the same mscratch over to the supervisor version of the\n\t\t// same register.\n\t\tcpu::sscratch_write(cpu::mscratch_read());\n\t\tcpu::KERNEL_TRAP_FRAME[hartid].hartid = hartid;\n\t\t// We can't do the following until zalloc() is locked, but we\n\t\t// don't have locks, yet :( cpu::KERNEL_TRAP_FRAME[hartid].satp\n\t\t// = cpu::KERNEL_TRAP_FRAME[0].satp;\n\t\t// cpu::KERNEL_TRAP_FRAME[hartid].trap_stack = page::zalloc(1);\n\t}\n}\n\n// ///////////////////////////////////\n// / RUST MODULES\n// ///////////////////////////////////\n\npub mod cpu;\npub mod kmem;\npub mod page;\npub mod plic;\npub mod process;\npub mod syscall;\npub mod trap;\npub mod uart;\n"
  },
  {
    "path": "risc_v/chapters/ch7/src/page.rs",
    "content": "// page.rs\n// Memory routines\n// Stephen Marz\n// 6 October 2019\n\nuse core::{mem::size_of, ptr::null_mut};\n\n// ////////////////////////////////\n// // Allocation routines\n// ////////////////////////////////\nextern \"C\" {\n\tstatic HEAP_START: usize;\n\tstatic HEAP_SIZE: usize;\n}\n\n// We will use ALLOC_START to mark the start of the actual\n// memory we can dish out.\nstatic mut ALLOC_START: usize = 0;\nconst PAGE_ORDER: usize = 12;\npub const PAGE_SIZE: usize = 1 << 12;\n\n/// Align (set to a multiple of some power of two)\n/// This takes an order which is the exponent to 2^order\n/// Therefore, all alignments must be made as a power of two.\n/// This function always rounds up.\npub const fn align_val(val: usize, order: usize) -> usize {\n\tlet o = (1usize << order) - 1;\n\t(val + o) & !o\n}\n\n#[repr(u8)]\npub enum PageBits {\n\tEmpty = 0,\n\tTaken = 1 << 0,\n\tLast = 1 << 1,\n}\n\nimpl PageBits {\n\t// We convert PageBits to a u8 a lot, so this is\n\t// for convenience.\n\tpub fn val(self) -> u8 {\n\t\tself as u8\n\t}\n}\n\n// Each page is described by the Page structure. Linux does this\n// as well, where each 4096-byte chunk of memory has a structure\n// associated with it. However, there structure is much larger.\npub struct Page {\n\tflags: u8,\n}\n\nimpl Page {\n\t// If this page has been marked as the final allocation,\n\t// this function returns true. Otherwise, it returns false.\n\tpub fn is_last(&self) -> bool {\n\t\tif self.flags & PageBits::Last.val() != 0 {\n\t\t\ttrue\n\t\t}\n\t\telse {\n\t\t\tfalse\n\t\t}\n\t}\n\n\t// If the page is marked as being taken (allocated), then\n\t// this function returns true. Otherwise, it returns false.\n\tpub fn is_taken(&self) -> bool {\n\t\tif self.flags & PageBits::Taken.val() != 0 {\n\t\t\ttrue\n\t\t}\n\t\telse {\n\t\t\tfalse\n\t\t}\n\t}\n\n\t// This is the opposite of is_taken().\n\tpub fn is_free(&self) -> bool {\n\t\t!self.is_taken()\n\t}\n\n\t// Clear the Page structure and all associated allocations.\n\tpub fn clear(&mut self) {\n\t\tself.flags = PageBits::Empty.val();\n\t}\n\n\t// Set a certain flag. We ran into trouble here since PageBits\n\t// is an enumeration and we haven't implemented the BitOr Trait\n\t// on it.\n\tpub fn set_flag(&mut self, flag: PageBits) {\n\t\tself.flags |= flag.val();\n\t}\n\n\tpub fn clear_flag(&mut self, flag: PageBits) {\n\t\tself.flags &= !(flag.val());\n\t}\n}\n\n/// Initialize the allocation system. There are several ways that we can\n/// implement the page allocator:\n/// 1. Free list (singly linked list where it starts at the first free\n/// allocation) 2. Bookkeeping list (structure contains a taken and length)\n/// 3. Allocate one Page structure per 4096 bytes (this is what I chose)\n/// 4. Others\npub fn init() {\n\tunsafe {\n\t\t// let desc_per_page = PAGE_SIZE / size_of::<Page>();\n\t\tlet num_pages = HEAP_SIZE / PAGE_SIZE;\n\t\t// let num_desc_pages = num_pages / desc_per_page;\n\t\tlet ptr = HEAP_START as *mut Page;\n\t\t// Clear all pages to make sure that they aren't accidentally\n\t\t// taken\n\t\tfor i in 0..num_pages {\n\t\t\t(*ptr.add(i)).clear();\n\t\t}\n\t\t// Determine where the actual useful memory starts. This will be\n\t\t// after all Page structures. We also must align the ALLOC_START\n\t\t// to a page-boundary (PAGE_SIZE = 4096). ALLOC_START =\n\t\t// (HEAP_START + num_pages * size_of::<Page>() + PAGE_SIZE - 1)\n\t\t// & !(PAGE_SIZE - 1);\n\t\tALLOC_START = align_val(\n\t\t                        HEAP_START\n\t\t                        + num_pages * size_of::<Page>(),\n\t\t                        PAGE_ORDER,\n\t\t);\n\t}\n}\n\n/// Allocate a page or multiple pages\n/// pages: the number of PAGE_SIZE pages to allocate\npub fn alloc(pages: usize) -> *mut u8 {\n\t// We have to find a contiguous allocation of pages\n\tassert!(pages > 0);\n\tunsafe {\n\t\t// We create a Page structure for each page on the heap. We\n\t\t// actually might have more since HEAP_SIZE moves and so does\n\t\t// the size of our structure, but we'll only waste a few bytes.\n\t\tlet num_pages = HEAP_SIZE / PAGE_SIZE;\n\t\tlet ptr = HEAP_START as *mut Page;\n\t\tfor i in 0..num_pages - pages {\n\t\t\tlet mut found = false;\n\t\t\t// Check to see if this Page is free. If so, we have our\n\t\t\t// first candidate memory address.\n\t\t\tif (*ptr.add(i)).is_free() {\n\t\t\t\t// It was FREE! Yay!\n\t\t\t\tfound = true;\n\t\t\t\tfor j in i..i + pages {\n\t\t\t\t\t// Now check to see if we have a\n\t\t\t\t\t// contiguous allocation for all of the\n\t\t\t\t\t// request pages. If not, we should\n\t\t\t\t\t// check somewhere else.\n\t\t\t\t\tif (*ptr.add(j)).is_taken() {\n\t\t\t\t\t\tfound = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// We've checked to see if there are enough contiguous\n\t\t\t// pages to form what we need. If we couldn't, found\n\t\t\t// will be false, otherwise it will be true, which means\n\t\t\t// we've found valid memory we can allocate.\n\t\t\tif found {\n\t\t\t\tfor k in i..i + pages - 1 {\n\t\t\t\t\t(*ptr.add(k)).set_flag(PageBits::Taken);\n\t\t\t\t}\n\t\t\t\t// The marker for the last page is\n\t\t\t\t// PageBits::Last This lets us know when we've\n\t\t\t\t// hit the end of this particular allocation.\n\t\t\t\t(*ptr.add(i+pages-1)).set_flag(PageBits::Taken);\n\t\t\t\t(*ptr.add(i+pages-1)).set_flag(PageBits::Last);\n\t\t\t\t// The Page structures themselves aren't the\n\t\t\t\t// useful memory. Instead, there is 1 Page\n\t\t\t\t// structure per 4096 bytes starting at\n\t\t\t\t// ALLOC_START.\n\t\t\t\treturn (ALLOC_START + PAGE_SIZE * i)\n\t\t\t\t       as *mut u8;\n\t\t\t}\n\t\t}\n\t}\n\n\t// If we get here, that means that no contiguous allocation was\n\t// found.\n\tnull_mut()\n}\n\n/// Allocate and zero a page or multiple pages\n/// pages: the number of pages to allocate\n/// Each page is PAGE_SIZE which is calculated as 1 << PAGE_ORDER\n/// On RISC-V, this typically will be 4,096 bytes.\npub fn zalloc(pages: usize) -> *mut u8 {\n\t// Allocate and zero a page.\n\t// First, let's get the allocation\n\tlet ret = alloc(pages);\n\tif !ret.is_null() {\n\t\tlet size = (PAGE_SIZE * pages) / 8;\n\t\tlet big_ptr = ret as *mut u64;\n\t\tfor i in 0..size {\n\t\t\t// We use big_ptr so that we can force an\n\t\t\t// sd (store doubleword) instruction rather than\n\t\t\t// the sb. This means 8x fewer stores than before.\n\t\t\t// Typically we have to be concerned about remaining\n\t\t\t// bytes, but fortunately 4096 % 8 = 0, so we\n\t\t\t// won't have any remaining bytes.\n\t\t\tunsafe {\n\t\t\t\t(*big_ptr.add(i)) = 0;\n\t\t\t}\n\t\t}\n\t}\n\tret\n}\n\n/// Deallocate a page by its pointer\n/// The way we've structured this, it will automatically coalesce\n/// contiguous pages.\npub fn dealloc(ptr: *mut u8) {\n\t// Make sure we don't try to free a null pointer.\n\tassert!(!ptr.is_null());\n\tunsafe {\n\t\tlet addr =\n\t\t\tHEAP_START + (ptr as usize - ALLOC_START) / PAGE_SIZE;\n\t\t// Make sure that the address makes sense. The address we\n\t\t// calculate here is the page structure, not the HEAP address!\n\t\tassert!(addr >= HEAP_START && addr < HEAP_START + HEAP_SIZE);\n\t\tlet mut p = addr as *mut Page;\n\t\t// Keep clearing pages until we hit the last page.\n\t\twhile (*p).is_taken() && !(*p).is_last() {\n\t\t\t(*p).clear();\n\t\t\tp = p.add(1);\n\t\t}\n\t\t// If the following assertion fails, it is most likely\n\t\t// caused by a double-free.\n\t\tassert!(\n\t\t        (*p).is_last() == true,\n\t\t        \"Possible double-free detected! (Not taken found \\\n\t\t         before last)\"\n\t\t);\n\t\t// If we get here, we've taken care of all previous pages and\n\t\t// we are on the last page.\n\t\t(*p).clear();\n\t}\n}\n\n/// Print all page allocations\n/// This is mainly used for debugging.\npub fn print_page_allocations() {\n\tunsafe {\n\t\tlet num_pages = (HEAP_SIZE - (ALLOC_START - HEAP_START)) / PAGE_SIZE;\n\t\tlet mut beg = HEAP_START as *const Page;\n\t\tlet end = beg.add(num_pages);\n\t\tlet alloc_beg = ALLOC_START;\n\t\tlet alloc_end = ALLOC_START + num_pages * PAGE_SIZE;\n\t\tprintln!();\n\t\tprintln!(\n\t\t         \"PAGE ALLOCATION TABLE\\nMETA: {:p} -> {:p}\\nPHYS: \\\n\t\t          0x{:x} -> 0x{:x}\",\n\t\t         beg, end, alloc_beg, alloc_end\n\t\t);\n\t\tprintln!(\"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\");\n\t\tlet mut num = 0;\n\t\twhile beg < end {\n\t\t\tif (*beg).is_taken() {\n\t\t\t\tlet start = beg as usize;\n\t\t\t\tlet memaddr = ALLOC_START\n\t\t\t\t              + (start - HEAP_START)\n\t\t\t\t                * PAGE_SIZE;\n\t\t\t\tprint!(\"0x{:x} => \", memaddr);\n\t\t\t\tloop {\n\t\t\t\t\tnum += 1;\n\t\t\t\t\tif (*beg).is_last() {\n\t\t\t\t\t\tlet end = beg as usize;\n\t\t\t\t\t\tlet memaddr = ALLOC_START\n\t\t\t\t\t\t              + (end\n\t\t\t\t\t\t                 - HEAP_START)\n\t\t\t\t\t\t                * PAGE_SIZE\n\t\t\t\t\t\t              + PAGE_SIZE - 1;\n\t\t\t\t\t\tprint!(\n\t\t\t\t\t\t       \"0x{:x}: {:>3} page(s)\",\n\t\t\t\t\t\t       memaddr,\n\t\t\t\t\t\t       (end - start + 1)\n\t\t\t\t\t\t);\n\t\t\t\t\t\tprintln!(\".\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tbeg = beg.add(1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbeg = beg.add(1);\n\t\t}\n\t\tprintln!(\"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\");\n\t\tprintln!(\n\t\t         \"Allocated: {:>6} pages ({:>10} bytes).\",\n\t\t         num,\n\t\t         num * PAGE_SIZE\n\t\t);\n\t\tprintln!(\n\t\t         \"Free     : {:>6} pages ({:>10} bytes).\",\n\t\t         num_pages - num,\n\t\t         (num_pages - num) * PAGE_SIZE\n\t\t);\n\t\tprintln!();\n\t}\n}\n\n// ////////////////////////////////\n// // MMU Routines\n// ////////////////////////////////\n\n// Represent (repr) our entry bits as\n// unsigned 64-bit integers.\n#[repr(i64)]\n#[derive(Copy, Clone)]\npub enum EntryBits {\n\tNone = 0,\n\tValid = 1 << 0,\n\tRead = 1 << 1,\n\tWrite = 1 << 2,\n\tExecute = 1 << 3,\n\tUser = 1 << 4,\n\tGlobal = 1 << 5,\n\tAccess = 1 << 6,\n\tDirty = 1 << 7,\n\n\t// Convenience combinations\n\tReadWrite = 1 << 1 | 1 << 2,\n\tReadExecute = 1 << 1 | 1 << 3,\n\tReadWriteExecute = 1 << 1 | 1 << 2 | 1 << 3,\n\n\t// User Convenience Combinations\n\tUserReadWrite = 1 << 1 | 1 << 2 | 1 << 4,\n\tUserReadExecute = 1 << 1 | 1 << 3 | 1 << 4,\n\tUserReadWriteExecute = 1 << 1 | 1 << 2 | 1 << 3 | 1 << 4,\n}\n\n// Helper functions to convert the enumeration\n// into an i64, which is what our page table\n// entries will be.\nimpl EntryBits {\n\tpub fn val(self) -> i64 {\n\t\tself as i64\n\t}\n}\n\n// A single entry. We're using an i64 so that\n// this will sign-extend rather than zero-extend\n// since RISC-V requires that the reserved sections\n// take on the most significant bit.\npub struct Entry {\n\tpub entry: i64,\n}\n\n// The Entry structure describes one of the 512 entries per table, which is\n// described in the RISC-V privileged spec Figure 4.18.\nimpl Entry {\n\tpub fn is_valid(&self) -> bool {\n\t\tself.get_entry() & EntryBits::Valid.val() != 0\n\t}\n\n\t// The first bit (bit index #0) is the V bit for\n\t// valid.\n\tpub fn is_invalid(&self) -> bool {\n\t\t!self.is_valid()\n\t}\n\n\t// A leaf has one or more RWX bits set\n\tpub fn is_leaf(&self) -> bool {\n\t\tself.get_entry() & 0xe != 0\n\t}\n\n\tpub fn is_branch(&self) -> bool {\n\t\t!self.is_leaf()\n\t}\n\n\tpub fn set_entry(&mut self, entry: i64) {\n\t\tself.entry = entry;\n\t}\n\n\tpub fn get_entry(&self) -> i64 {\n\t\tself.entry\n\t}\n}\n\n// Table represents a single table, which contains 512 (2^9), 64-bit entries.\npub struct Table {\n\tpub entries: [Entry; 512],\n}\n\nimpl Table {\n\tpub fn len() -> usize {\n\t\t512\n\t}\n}\n\n/// Map a virtual address to a physical address using 4096-byte page\n/// size.\n/// root: a mutable reference to the root Table\n/// vaddr: The virtual address to map\n/// paddr: The physical address to map\n/// bits: An OR'd bitset containing the bits the leaf should have.\n///       The bits should contain only the following:\n///          Read, Write, Execute, User, and/or Global\n///       The bits MUST include one or more of the following:\n///          Read, Write, Execute\n///       The valid bit automatically gets added.\npub fn map(root: &mut Table,\n           vaddr: usize,\n           paddr: usize,\n           bits: i64,\n           level: usize)\n{\n\t// Make sure that Read, Write, or Execute have been provided\n\t// otherwise, we'll leak memory and always create a page fault.\n\tassert!(bits & 0xe != 0);\n\t// Extract out each VPN from the virtual address\n\t// On the virtual address, each VPN is exactly 9 bits,\n\t// which is why we use the mask 0x1ff = 0b1_1111_1111 (9 bits)\n\tlet vpn = [\n\t           // VPN[0] = vaddr[20:12]\n\t           (vaddr >> 12) & 0x1ff,\n\t           // VPN[1] = vaddr[29:21]\n\t           (vaddr >> 21) & 0x1ff,\n\t           // VPN[2] = vaddr[38:30]\n\t           (vaddr >> 30) & 0x1ff,\n\t];\n\n\t// Just like the virtual address, extract the physical address\n\t// numbers (PPN). However, PPN[2] is different in that it stores\n\t// 26 bits instead of 9. Therefore, we use,\n\t// 0x3ff_ffff = 0b11_1111_1111_1111_1111_1111_1111 (26 bits).\n\tlet ppn = [\n\t           // PPN[0] = paddr[20:12]\n\t           (paddr >> 12) & 0x1ff,\n\t           // PPN[1] = paddr[29:21]\n\t           (paddr >> 21) & 0x1ff,\n\t           // PPN[2] = paddr[55:30]\n\t           (paddr >> 30) & 0x3ff_ffff,\n\t];\n\t// We will use this as a floating reference so that we can set\n\t// individual entries as we walk the table.\n\tlet mut v = &mut root.entries[vpn[2]];\n\t// Now, we're going to traverse the page table and set the bits\n\t// properly. We expect the root to be valid, however we're required to\n\t// create anything beyond the root.\n\t// In Rust, we create a range iterator using the .. operator.\n\t// The .rev() will reverse the iteration since we need to start with\n\t// VPN[2] The .. operator is inclusive on start but exclusive on end.\n\t// So, (0..2) will iterate 0 and 1.\n\tfor i in (level..2).rev() {\n\t\tif !v.is_valid() {\n\t\t\t// Allocate a page\n\t\t\tlet page = zalloc(1);\n\t\t\t// The page is already aligned by 4,096, so store it\n\t\t\t// directly The page is stored in the entry shifted\n\t\t\t// right by 2 places.\n\t\t\tv.set_entry(\n\t\t\t            (page as i64 >> 2)\n\t\t\t            | EntryBits::Valid.val(),\n\t\t\t);\n\t\t}\n\t\tlet entry = ((v.get_entry() & !0x3ff) << 2) as *mut Entry;\n\t\tv = unsafe { entry.add(vpn[i]).as_mut().unwrap() };\n\t}\n\t// When we get here, we should be at VPN[0] and v should be pointing to\n\t// our entry.\n\t// The entry structure is Figure 4.18 in the RISC-V Privileged\n\t// Specification\n\tlet entry = (ppn[2] << 28) as i64 |   // PPN[2] = [53:28]\n\t            (ppn[1] << 19) as i64 |   // PPN[1] = [27:19]\n\t\t\t\t(ppn[0] << 10) as i64 |   // PPN[0] = [18:10]\n\t\t\t\tbits |                    // Specified bits, such as User, Read, Write, etc\n\t\t\t\tEntryBits::Valid.val() |  // Valid bit\n\t\t\t\tEntryBits::Dirty.val() |  // Some machines require this to =1\n\t\t\t\tEntryBits::Access.val()   // Just like dirty, some machines require this\n\t\t\t\t;\n\t// Set the entry. V should be set to the correct pointer by the loop\n\t// above.\n\tv.set_entry(entry);\n}\n\n/// Unmaps and frees all memory associated with a table.\n/// root: The root table to start freeing.\n/// NOTE: This does NOT free root directly. This must be\n/// freed manually.\n/// The reason we don't free the root is because it is\n/// usually embedded into the Process structure.\npub fn unmap(root: &mut Table) {\n\t// Start with level 2\n\tfor lv2 in 0..Table::len() {\n\t\tlet ref entry_lv2 = root.entries[lv2];\n\t\tif entry_lv2.is_valid() && entry_lv2.is_branch() {\n\t\t\t// This is a valid entry, so drill down and free.\n\t\t\tlet memaddr_lv1 = (entry_lv2.get_entry() & !0x3ff) << 2;\n\t\t\tlet table_lv1 = unsafe {\n\t\t\t\t// Make table_lv1 a mutable reference instead of\n\t\t\t\t// a pointer.\n\t\t\t\t(memaddr_lv1 as *mut Table).as_mut().unwrap()\n\t\t\t};\n\t\t\tfor lv1 in 0..Table::len() {\n\t\t\t\tlet ref entry_lv1 = table_lv1.entries[lv1];\n\t\t\t\tif entry_lv1.is_valid() && entry_lv1.is_branch()\n\t\t\t\t{\n\t\t\t\t\tlet memaddr_lv0 = (entry_lv1.get_entry()\n\t\t\t\t\t                   & !0x3ff) << 2;\n\t\t\t\t\t// The next level is level 0, which\n\t\t\t\t\t// cannot have branches, therefore,\n\t\t\t\t\t// we free here.\n\t\t\t\t\tdealloc(memaddr_lv0 as *mut u8);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdealloc(memaddr_lv1 as *mut u8);\n\t\t}\n\t}\n}\n\n/// Walk the page table to convert a virtual address to a\n/// physical address.\n/// If a page fault would occur, this returns None\n/// Otherwise, it returns Some with the physical address.\npub fn virt_to_phys(root: &Table, vaddr: usize) -> Option<usize> {\n\t// Walk the page table pointed to by root\n\tlet vpn = [\n\t           // VPN[0] = vaddr[20:12]\n\t           (vaddr >> 12) & 0x1ff,\n\t           // VPN[1] = vaddr[29:21]\n\t           (vaddr >> 21) & 0x1ff,\n\t           // VPN[2] = vaddr[38:30]\n\t           (vaddr >> 30) & 0x1ff,\n\t];\n\n\tlet mut v = &root.entries[vpn[2]];\n\tfor i in (0..=2).rev() {\n\t\tif v.is_invalid() {\n\t\t\t// This is an invalid entry, page fault.\n\t\t\tbreak;\n\t\t}\n\t\telse if v.is_leaf() {\n\t\t\t// According to RISC-V, a leaf can be at any level.\n\n\t\t\t// The offset mask masks off the PPN. Each PPN is 9\n\t\t\t// bits and they start at bit #12. So, our formula\n\t\t\t// 12 + i * 9\n\t\t\tlet off_mask = (1 << (12 + i * 9)) - 1;\n\t\t\tlet vaddr_pgoff = vaddr & off_mask;\n\t\t\tlet addr = ((v.get_entry() << 2) as usize) & !off_mask;\n\t\t\treturn Some(addr | vaddr_pgoff);\n\t\t}\n\t\t// Set v to the next entry which is pointed to by this\n\t\t// entry. However, the address was shifted right by 2 places\n\t\t// when stored in the page table entry, so we shift it left\n\t\t// to get it back into place.\n\t\tlet entry = ((v.get_entry() & !0x3ff) << 2) as *const Entry;\n\t\t// We do i - 1 here, however we should get None or Some() above\n\t\t// before we do 0 - 1 = -1.\n\t\tv = unsafe { entry.add(vpn[i - 1]).as_ref().unwrap() };\n\t}\n\n\t// If we get here, we've exhausted all valid tables and haven't\n\t// found a leaf.\n\tNone\n}\n"
  },
  {
    "path": "risc_v/chapters/ch7/src/plic.rs",
    "content": "// plic.rs\n// Platform Level Interrupt Controller (PLIC)\n// Stephen Marz\n// 1 Nov 2019\n\nconst PLIC_PRIORITY: usize = 0x0c00_0000;\nconst PLIC_PENDING: usize = 0x0c00_1000;\nconst PLIC_INT_ENABLE: usize = 0x0c00_2000;\nconst PLIC_THRESHOLD: usize = 0x0c20_0000;\nconst PLIC_CLAIM: usize = 0x0c20_0004;\n\n// Each register is 4-bytes (u32)\n// The PLIC is an external interrupt controller. The one\n// used by QEMU virt is the same as the SiFive PLIC.\n// https://sifive.cdn.prismic.io/sifive%2F834354f0-08e6-423c-bf1f-0cb58ef14061_fu540-c000-v1.0.pdf\n\n// Chapter 10 explains the priority, pending, interrupt enable, threshold and claims\n\n// The virt machine has the following external interrupts (from Qemu source):\n// Interrupt 0 is a \"null\" interrupt and is hardwired to 0.\n// VIRTIO = [1..8]\n// UART0 = 10\n// PCIE = [32..35]\n\n\n/// Get the next available interrupt. This is the \"claim\" process.\n/// The plic will automatically sort by priority and hand us the\n/// ID of the interrupt. For example, if the UART is interrupting\n/// and it's next, we will get the value 10.\npub fn next() -> Option<u32> {\n    let claim_reg = PLIC_CLAIM as *const u32;\n    let claim_no;\n    // The claim register is filled with the highest-priority, enabled interrupt.\n    unsafe {\n        claim_no = claim_reg.read_volatile();\n    }\n    if claim_no == 0 {\n        // The interrupt 0 is hardwired to 0, which tells us that there is no\n        // interrupt to claim, hence we return None.\n        None\n    }\n    else {\n        // If we get here, we've gotten a non-0 interrupt.\n        Some(claim_no)\n    }\n}\n\n/// Complete a pending interrupt by id. The id should come\n/// from the next() function above.\npub fn complete(id: u32) {\n    let complete_reg = PLIC_CLAIM as *mut u32;\n    unsafe {\n        // We actually write a u32 into the entire complete_register.\n        // This is the same register as the claim register, but it can\n        // differentiate based on whether we're reading or writing.\n        complete_reg.write_volatile(id);\n    }\n}\n\n/// Set the global threshold. The threshold can be a value [0..7].\n/// The PLIC will mask any interrupts at or below the given threshold.\n/// This means that a threshold of 7 will mask ALL interrupts and\n/// a threshold of 0 will allow ALL interrupts.\npub fn set_threshold(tsh: u8) {\n    // We do tsh because we're using a u8, but our maximum number\n    // is a 3-bit 0b111. So, we and with 7 (0b111) to just get the\n    // last three bits.\n    let actual_tsh = tsh & 7;\n    let tsh_reg = PLIC_THRESHOLD as *mut u32;\n    unsafe {\n        tsh_reg.write_volatile(actual_tsh as u32);\n    }\n}\n\n/// See if a given interrupt id is pending.\npub fn is_pending(id: u32) -> bool {\n    let pend = PLIC_PENDING as *const u32;\n    let actual_id = 1 << id;\n    let pend_ids;\n    unsafe {\n        pend_ids = pend.read_volatile();\n    }\n    actual_id & pend_ids != 0\n}\n\n/// Enable a given interrupt id\npub fn enable(id: u32) {\n    let enables = PLIC_INT_ENABLE as *mut u32;\n    let actual_id = 1 << id;\n    unsafe {\n        // Unlike the complete and claim registers, the plic_int_enable\n        // register is a bitset where the id is the bit index. The register\n        // is a 32-bit register, so that gives us enables for interrupts\n        // 31 through 1 (0 is hardwired to 0).\n        enables.write_volatile(enables.read_volatile() | actual_id);\n    }\n}\n\n/// Set a given interrupt priority to the given priority.\n/// The priority must be [0..7]\npub fn set_priority(id: u32, prio: u8) {\n    let actual_prio = prio as u32 & 7;\n    let prio_reg = PLIC_PRIORITY as *mut u32;\n    unsafe {\n        // The offset for the interrupt id is:\n        // PLIC_PRIORITY + 4 * id\n        // Since we're using pointer arithmetic on a u32 type,\n        // it will automatically multiply the id by 4.\n        prio_reg.add(id as usize).write_volatile(actual_prio);\n    }\n}\n\n"
  },
  {
    "path": "risc_v/chapters/ch7/src/process.rs",
    "content": "// process.rs\n// Kernel and user processes\n// Stephen Marz\n// 27 Nov 2019\n\nuse crate::{cpu::{build_satp,\n                  mscratch_write,\n                  satp_fence_asid,\n                  satp_write,\n                  SatpMode,\n                  TrapFrame},\n            page::{alloc,\n                   dealloc,\n                   map,\n                   unmap,\n                   zalloc,\n                   EntryBits,\n                   Table,\n                   PAGE_SIZE}};\nuse alloc::collections::vec_deque::VecDeque;\n\n// How many pages are we going to give a process for their\n// stack?\nconst STACK_PAGES: usize = 2;\n// We want to adjust the stack to be at the bottom of the memory allocation\n// regardless of where it is on the kernel heap.\nconst STACK_ADDR: usize = 0xf_0000_0000;\n// All processes will have a defined starting point in virtual memory.\nconst PROCESS_STARTING_ADDR: usize = 0x2000_0000;\n\n// Here, we store a process list. It uses the global allocator\n// that we made before and its job is to store all processes.\n// We will have this list OWN the process. So, anytime we want\n// the process, we will consult the process list.\n// Using an Option here is one method of creating a \"lazy static\".\n// Rust requires that all statics be initialized, but all\n// initializations must be at compile-time. We cannot allocate\n// a VecDeque at compile time, so we are somewhat forced to\n// do this.\npub static mut PROCESS_LIST: Option<VecDeque<Process>> = None;\n// We can search through the process list to get a new PID, but\n// it's probably easier and faster just to increase the pid:\nstatic mut NEXT_PID: u16 = 1;\n\n/// We will eventually move this function out of here, but its\n/// job is just to take a slot in the process list.\nfn init_process() {\n\t// We can't do much here until we have system calls because\n\t// we're running in User space.\n\tloop {}\n}\n\n/// Add a process given a function address and then\n/// push it onto the LinkedList. Uses Process::new_default\n/// to create a new stack, etc.\npub fn add_process_default(pr: fn()) {\n\tunsafe {\n\t\t// This is the Rust-ism that really trips up C++ programmers.\n\t\t// PROCESS_LIST is wrapped in an Option<> enumeration, which\n\t\t// means that the Option owns the Deque. We can only borrow from\n\t\t// it or move ownership to us. In this case, we choose the\n\t\t// latter, where we move ownership to us, add a process, and\n\t\t// then move ownership back to the PROCESS_LIST.\n\t\t// This allows mutual exclusion as anyone else trying to grab\n\t\t// the process list will get None rather than the Deque.\n\t\tif let Some(mut pl) = PROCESS_LIST.take() {\n\t\t\t// .take() will replace PROCESS_LIST with None and give\n\t\t\t// us the only copy of the Deque.\n\t\t\tlet p = Process::new_default(pr);\n\t\t\tpl.push_back(p);\n\t\t\t// Now, we no longer need the owned Deque, so we hand it\n\t\t\t// back by replacing the PROCESS_LIST's None with the\n\t\t\t// Some(pl).\n\t\t\tPROCESS_LIST.replace(pl);\n\t\t}\n\t\t// TODO: When we get to multi-hart processing, we need to keep\n\t\t// trying to grab the process list. We can do this with an\n\t\t// atomic instruction. but right now, we're a single-processor\n\t\t// computer.\n\t}\n}\n\n/// This should only be called once, and its job is to create\n/// the init process. Right now, this process is in the kernel,\n/// but later, it should call the shell.\npub fn init() -> usize {\n\tunsafe {\n\t\tPROCESS_LIST = Some(VecDeque::with_capacity(5));\n\t\tadd_process_default(init_process);\n\t\t// Ugh....Rust is giving me fits over here!\n\t\t// I just want a memory address to the trap frame, but\n\t\t// due to the borrow rules of Rust, I'm fighting here. So,\n\t\t// instead, let's move the value out of PROCESS_LIST, get\n\t\t// the address, and then move it right back in.\n\t\tlet pl = PROCESS_LIST.take().unwrap();\n\t\tlet p = pl.front().unwrap().frame;\n\t\tlet frame = &p as *const TrapFrame as usize;\n\t\tmscratch_write(frame);\n\t\tsatp_write(build_satp(\n\t\t\tSatpMode::Sv39,\n\t\t\t1,\n\t\t\tpl.front().unwrap().root as usize,\n\t\t),);\n\t\t// Synchronize PID 1. We use ASID as the PID.\n\t\tsatp_fence_asid(1);\n\t\t// Put the process list back in the global.\n\t\tPROCESS_LIST.replace(pl);\n\t\t// Return the first instruction's address to execute.\n\t\t// Since we use the MMU, all start here.\n\t\tPROCESS_STARTING_ADDR\n\t}\n}\n\n// Our process must be able to sleep, wait, or run.\n// Running - means that when the scheduler finds this process, it can run it.\n// Sleeping - means that the process is waiting on a certain amount of time.\n// Waiting - means that the process is waiting on I/O\n// Dead - We should never get here, but we can flag a process as Dead and clean\n//        it out of the list later.\npub enum ProcessState {\n\tRunning,\n\tSleeping,\n\tWaiting,\n\tDead,\n}\n\n// Let's represent this in C ABI. We do this\n// because we need to access some of the fields\n// in assembly. Rust gets to choose how it orders\n// the fields unless we represent the structure in\n// C-style ABI.\n#[repr(C)]\npub struct Process {\n\tframe:           TrapFrame,\n\tstack:           *mut u8,\n\tprogram_counter: usize,\n\tpid:             u16,\n\troot:            *mut Table,\n\tstate:           ProcessState,\n\tdata:            ProcessData,\n}\n\nimpl Process {\n\tpub fn new_default(func: fn()) -> Self {\n\t\tlet func_addr = func as usize;\n\t\t// We will convert NEXT_PID below into an atomic increment when\n\t\t// we start getting into multi-hart processing. For now, we want\n\t\t// a process. Get it to work, then improve it!\n\t\tlet mut ret_proc =\n\t\t\tProcess { frame:           TrapFrame::zero(),\n\t\t\t          stack:           alloc(STACK_PAGES),\n\t\t\t          program_counter: PROCESS_STARTING_ADDR,\n\t\t\t          pid:             unsafe { NEXT_PID },\n\t\t\t          root:            zalloc(1) as *mut Table,\n\t\t\t          state:           ProcessState::Waiting,\n\t\t\t          data:            ProcessData::zero(), };\n\t\tunsafe {\n\t\t\tNEXT_PID += 1;\n\t\t}\n\t\t// Now we move the stack pointer to the bottom of the\n\t\t// allocation. The spec shows that register x2 (2) is the stack\n\t\t// pointer.\n\t\t// We could use ret_proc.stack.add, but that's an unsafe\n\t\t// function which would require an unsafe block. So, convert it\n\t\t// to usize first and then add PAGE_SIZE is better.\n\t\t// We also need to set the stack adjustment so that it is at the\n\t\t// bottom of the memory and far away from heap allocations.\n\t\tret_proc.frame.regs[2] = STACK_ADDR + PAGE_SIZE * STACK_PAGES;\n\t\t// Map the stack on the MMU\n\t\tlet pt;\n\t\tunsafe {\n\t\t\tpt = &mut *ret_proc.root;\n\t\t}\n\t\tlet saddr = ret_proc.stack as usize;\n\t\t// We need to map the stack onto the user process' virtual\n\t\t// memory This gets a little hairy because we need to also map\n\t\t// the function code too.\n\t\tfor i in 0..STACK_PAGES {\n\t\t\tlet addr = i * PAGE_SIZE;\n\t\t\tmap(\n\t\t\t    pt,\n\t\t\t    STACK_ADDR + addr,\n\t\t\t    saddr + addr,\n\t\t\t    EntryBits::UserReadWrite.val(),\n\t\t\t    0,\n\t\t\t);\n\t\t}\n\t\t// Map the program counter on the MMU\n\t\tmap(\n\t\t    pt,\n\t\t    PROCESS_STARTING_ADDR,\n\t\t    func_addr,\n\t\t    EntryBits::UserReadExecute.val(),\n\t\t    0,\n\t\t);\n\t\tmap(\n\t\t    pt,\n\t\t    PROCESS_STARTING_ADDR + 0x1001,\n\t\t    func_addr + 0x1001,\n\t\t    EntryBits::UserReadExecute.val(),\n\t\t    0,\n\t\t);\n\t\tret_proc\n\t}\n}\n\nimpl Drop for Process {\n\t/// Since we're storing ownership of a Process in the linked list,\n\t/// we can cause it to deallocate automatically when it is removed.\n\tfn drop(&mut self) {\n\t\t// We allocate the stack as a page.\n\t\tdealloc(self.stack);\n\t\t// This is unsafe, but it's at the drop stage, so we won't\n\t\t// be using this again.\n\t\tunsafe {\n\t\t\t// Remember that unmap unmaps all levels of page tables\n\t\t\t// except for the root. It also deallocates the memory\n\t\t\t// associated with the tables.\n\t\t\tunmap(&mut *self.root);\n\t\t}\n\t\tdealloc(self.root as *mut u8);\n\t}\n}\n\n// The private data in a process contains information\n// that is relevant to where we are, including the path\n// and open file descriptors.\npub struct ProcessData {\n\tcwd_path: [u8; 128],\n}\n\n// This is private data that we can query with system calls.\n// If we want to implement CFQ (completely fair queuing), which\n// is a per-process block queuing algorithm, we can put that here.\nimpl ProcessData {\n\tpub fn zero() -> Self {\n\t\tProcessData { cwd_path: [0; 128], }\n\t}\n}\n"
  },
  {
    "path": "risc_v/chapters/ch7/src/sched.rs",
    "content": "// sched.rs\n// Simple process scheduler\n// Stephen Marz\n// 27 Dec 2019\n\nuse crate::process::{PROCESS_LIST, Process};\nuse crate::cpu::mscratch_write;\n\npub fn schedule() {\n\n}\n\npub fn switch_to(from: &mut Process, to: &mut Process) {\n    \n}\n"
  },
  {
    "path": "risc_v/chapters/ch7/src/syscall.rs",
    "content": "// syscall.rs\n// System calls\n// Stephen Marz\n// 3 Jan 2020\n\nuse crate::cpu::TrapFrame;\n\npub fn do_syscall(mepc: usize, frame: *mut TrapFrame) -> usize {\n    let syscall_number;\n    unsafe {\n        // A0 is X10, so it's register number 10.\n        syscall_number = (*frame).regs[10];\n    }\n    match syscall_number {\n        0 => {\n            // Exit\n            mepc + 4\n        },\n        _ => {\n            print!(\"Unknown syscall number {}\", syscall_number);\n            mepc + 4\n        }\n    }\n}"
  },
  {
    "path": "risc_v/chapters/ch7/src/trap.rs",
    "content": "// trap.rs\n// Trap routines\n// Stephen Marz\n// 10 October 2019\n\nuse crate::cpu::TrapFrame;\nuse crate::{plic, uart};\nuse crate::syscall::do_syscall;\n\n#[no_mangle]\n/// The m_trap stands for \"machine trap\". Right now, we are handling\n/// all traps at machine mode. In this mode, we can figure out what's\n/// going on and send a trap where it needs to be. Remember, in machine\n/// mode and in this trap, interrupts are disabled and the MMU is off.\nextern \"C\" fn m_trap(epc: usize,\n                     tval: usize,\n                     cause: usize,\n                     hart: usize,\n                     status: usize,\n                     frame: *mut TrapFrame)\n                     -> usize\n{\n\t// We're going to handle all traps in machine mode. RISC-V lets\n\t// us delegate to supervisor mode, but switching out SATP (virtual memory)\n\t// gets hairy.\n\tlet is_async = {\n\t\tif cause >> 63 & 1 == 1 {\n\t\t\ttrue\n\t\t}\n\t\telse {\n\t\t\tfalse\n\t\t}\n\t};\n\t// The cause contains the type of trap (sync, async) as well as the cause\n\t// number. So, here we narrow down just the cause number.\n\tlet cause_num = cause & 0xfff;\n\tlet mut return_pc = epc;\n\tif is_async {\n\t\t// Asynchronous trap\n\t\tmatch cause_num {\n\t\t\t3 => {\n\t\t\t\t// Machine software\n\t\t\t\tprintln!(\"Machine software interrupt CPU#{}\", hart);\n\t\t\t},\n\t\t\t7 => unsafe {\n\t\t\t\t// This is the context-switch timer.\n\t\t\t\t// We would typically invoke the scheduler here to pick another\n\t\t\t\t// process to run.\n\t\t\t\t// Machine timer\n\t\t\t\tprintln!(\"CTX\");\n\t\t\t\tlet mtimecmp = 0x0200_4000 as *mut u64;\n\t\t\t\tlet mtime = 0x0200_bff8 as *const u64;\n\t\t\t\t// The frequency given by QEMU is 10_000_000 Hz, so this sets\n\t\t\t\t// the next interrupt to fire one second from now.\n\t\t\t\t// This is much too slow for normal operations, but it gives us\n\t\t\t\t// a visual of what's happening behind the scenes.\n\t\t\t\tmtimecmp.write_volatile(mtime.read_volatile() + 10_000_000);\n\t\t\t},\n\t\t\t11 => {\n\t\t\t\t// Machine external (interrupt from Platform Interrupt Controller (PLIC))\n\t\t\t\t// println!(\"Machine external interrupt CPU#{}\", hart);\n\t\t\t\t// We will check the next interrupt. If the interrupt isn't available, this will\n\t\t\t\t// give us None. However, that would mean we got a spurious interrupt, unless we\n\t\t\t\t// get an interrupt from a non-PLIC source. This is the main reason that the PLIC\n\t\t\t\t// hardwires the id 0 to 0, so that we can use it as an error case.\n\t\t\t\tif let Some(interrupt) = plic::next() {\n\t\t\t\t\t// If we get here, we've got an interrupt from the claim register. The PLIC will\n\t\t\t\t\t// automatically prioritize the next interrupt, so when we get it from claim, it\n\t\t\t\t\t// will be the next in priority order.\n\t\t\t\t\tmatch interrupt {\n\t\t\t\t\t\t10 => { // Interrupt 10 is the UART interrupt.\n\t\t\t\t\t\t\t// We would typically set this to be handled out of the interrupt context,\n\t\t\t\t\t\t\t// but we're testing here! C'mon!\n\t\t\t\t\t\t\t// We haven't yet used the singleton pattern for my_uart, but remember, this\n\t\t\t\t\t\t\t// just simply wraps 0x1000_0000 (UART).\n\t\t\t\t\t\t\tlet mut my_uart = uart::Uart::new(0x1000_0000);\n\t\t\t\t\t\t\t// If we get here, the UART better have something! If not, what happened??\n\t\t\t\t\t\t\tif let Some(c) = my_uart.get() {\n\t\t\t\t\t\t\t\t// If you recognize this code, it used to be in the lib.rs under kmain(). That\n\t\t\t\t\t\t\t\t// was because we needed to poll for UART data. Now that we have interrupts,\n\t\t\t\t\t\t\t\t// here it goes!\n\t\t\t\t\t\t\t\tmatch c {\n\t\t\t\t\t\t\t\t\t8 => {\n\t\t\t\t\t\t\t\t\t\t// This is a backspace, so we\n\t\t\t\t\t\t\t\t\t\t// essentially have to write a space and\n\t\t\t\t\t\t\t\t\t\t// backup again:\n\t\t\t\t\t\t\t\t\t\tprint!(\"{} {}\", 8 as char, 8 as char);\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t10 | 13 => {\n\t\t\t\t\t\t\t\t\t\t// Newline or carriage-return\n\t\t\t\t\t\t\t\t\t\tprintln!();\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t_ => {\n\t\t\t\t\t\t\t\t\t\tprint!(\"{}\", c as char);\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t\t},\n\t\t\t\t\t\t// Non-UART interrupts go here and do nothing.\n\t\t\t\t\t\t_ => {\n\t\t\t\t\t\t\tprintln!(\"Non-UART external interrupt: {}\", interrupt);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// We've claimed it, so now say that we've handled it. This resets the interrupt pending\n\t\t\t\t\t// and allows the UART to interrupt again. Otherwise, the UART will get \"stuck\".\n\t\t\t\t\tplic::complete(interrupt);\n\t\t\t\t}\n\t\t\t},\n\t\t\t_ => {\n\t\t\t\tpanic!(\"Unhandled async trap CPU#{} -> {}\\n\", hart, cause_num);\n\t\t\t}\n\t\t}\n\t}\n\telse {\n\t\t// Synchronous trap\n\t\tmatch cause_num {\n\t\t\t2 => {\n\t\t\t\t// Illegal instruction\n\t\t\t\tpanic!(\"Illegal instruction CPU#{} -> 0x{:08x}: 0x{:08x}\\n\", hart, epc, tval);\n\t\t\t},\n\t\t\t8 => {\n\t\t\t\t// Environment (system) call from User mode\n\t\t\t\tprintln!(\"E-call from User mode! CPU#{} -> 0x{:08x}\", hart, epc);\n\t\t\t\treturn_pc = do_syscall(return_pc, frame);\n\t\t\t},\n\t\t\t9 => {\n\t\t\t\t// Environment (system) call from Supervisor mode\n\t\t\t\tprintln!(\"E-call from Supervisor mode! CPU#{} -> 0x{:08x}\", hart, epc);\n\t\t\t\treturn_pc = do_syscall(return_pc, frame);\n\t\t\t},\n\t\t\t11 => {\n\t\t\t\t// Environment (system) call from Machine mode\n\t\t\t\tpanic!(\"E-call from Machine mode! CPU#{} -> 0x{:08x}\\n\", hart, epc);\n\t\t\t},\n\t\t\t// Page faults\n\t\t\t12 => {\n\t\t\t\t// Instruction page fault\n\t\t\t\tprintln!(\"Instruction page fault CPU#{} -> 0x{:08x}: 0x{:08x}\", hart, epc, tval);\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t13 => {\n\t\t\t\t// Load page fault\n\t\t\t\tprintln!(\"Load page fault CPU#{} -> 0x{:08x}: 0x{:08x}\", hart, epc, tval);\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t15 => {\n\t\t\t\t// Store page fault\n\t\t\t\tprintln!(\"Store page fault CPU#{} -> 0x{:08x}: 0x{:08x}\", hart, epc, tval);\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t_ => {\n\t\t\t\tpanic!(\"Unhandled sync trap CPU#{} -> {}\\n\", hart, cause_num);\n\t\t\t}\n\t\t}\n\t};\n\t// Finally, return the updated program counter\n\treturn_pc\n}\n"
  },
  {
    "path": "risc_v/chapters/ch7/src/uart.rs",
    "content": "// uart.rs\n// UART routines and driver\n\nuse core::{convert::TryInto,\n           fmt::{Error, Write}};\n\npub struct Uart {\n\tbase_address: usize,\n}\n\nimpl Write for Uart {\n\tfn write_str(&mut self, out: &str) -> Result<(), Error> {\n\t\tfor c in out.bytes() {\n\t\t\tself.put(c);\n\t\t}\n\t\tOk(())\n\t}\n}\n\nimpl Uart {\n\tpub fn new(base_address: usize) -> Self {\n\t\tUart { base_address }\n\t}\n\n\tpub fn init(&mut self) {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\t// First, set the word length, which\n\t\t\t// are bits 0 and 1 of the line control register (LCR)\n\t\t\t// which is at base_address + 3\n\t\t\t// We can easily write the value 3 here or 0b11, but I'm\n\t\t\t// extending it so that it is clear we're setting two\n\t\t\t// individual fields\n\t\t\t//             Word 0     Word 1\n\t\t\t//             ~~~~~~     ~~~~~~\n\t\t\tlet lcr: u8 = (1 << 0) | (1 << 1);\n\t\t\tptr.add(3).write_volatile(lcr);\n\n\t\t\t// Now, enable the FIFO, which is bit index 0 of the\n\t\t\t// FIFO control register (FCR at offset 2).\n\t\t\t// Again, we can just write 1 here, but when we use left\n\t\t\t// shift, it's easier to see that we're trying to write\n\t\t\t// bit index #0.\n\t\t\tptr.add(2).write_volatile(1 << 0);\n\n\t\t\t// Enable receiver buffer interrupts, which is at bit\n\t\t\t// index 0 of the interrupt enable register (IER at\n\t\t\t// offset 1).\n\t\t\tptr.add(1).write_volatile(1 << 0);\n\n\t\t\t// If we cared about the divisor, the code below would\n\t\t\t// set the divisor from a global clock rate of 22.729\n\t\t\t// MHz (22,729,000 cycles per second) to a signaling\n\t\t\t// rate of 2400 (BAUD). We usually have much faster\n\t\t\t// signalling rates nowadays, but this demonstrates what\n\t\t\t// the divisor actually does. The formula given in the\n\t\t\t// NS16500A specification for calculating the divisor\n\t\t\t// is:\n\t\t\t// divisor = ceil( (clock_hz) / (baud_sps x 16) )\n\t\t\t// So, we substitute our values and get:\n\t\t\t// divisor = ceil( 22_729_000 / (2400 x 16) )\n\t\t\t// divisor = ceil( 22_729_000 / 38_400 )\n\t\t\t// divisor = ceil( 591.901 ) = 592\n\n\t\t\t// The divisor register is two bytes (16 bits), so we\n\t\t\t// need to split the value 592 into two bytes.\n\t\t\t// Typically, we would calculate this based on measuring\n\t\t\t// the clock rate, but again, for our purposes [qemu],\n\t\t\t// this doesn't really do anything.\n\t\t\tlet divisor: u16 = 592;\n\t\t\tlet divisor_least: u8 =\n\t\t\t\t(divisor & 0xff).try_into().unwrap();\n\t\t\tlet divisor_most: u8 =\n\t\t\t\t(divisor >> 8).try_into().unwrap();\n\n\t\t\t// Notice that the divisor register DLL (divisor latch\n\t\t\t// least) and DLM (divisor latch most) have the same\n\t\t\t// base address as the receiver/transmitter and the\n\t\t\t// interrupt enable register. To change what the base\n\t\t\t// address points to, we open the \"divisor latch\" by\n\t\t\t// writing 1 into the Divisor Latch Access Bit (DLAB),\n\t\t\t// which is bit index 7 of the Line Control Register\n\t\t\t// (LCR) which is at base_address + 3.\n\t\t\tptr.add(3).write_volatile(lcr | 1 << 7);\n\n\t\t\t// Now, base addresses 0 and 1 point to DLL and DLM,\n\t\t\t// respectively. Put the lower 8 bits of the divisor\n\t\t\t// into DLL\n\t\t\tptr.add(0).write_volatile(divisor_least);\n\t\t\tptr.add(1).write_volatile(divisor_most);\n\n\t\t\t// Now that we've written the divisor, we never have to\n\t\t\t// touch this again. In hardware, this will divide the\n\t\t\t// global clock (22.729 MHz) into one suitable for 2,400\n\t\t\t// signals per second. So, to once again get access to\n\t\t\t// the RBR/THR/IER registers, we need to close the DLAB\n\t\t\t// bit by clearing it to 0.\n\t\t\tptr.add(3).write_volatile(lcr);\n\t\t}\n\t}\n\n\tpub fn put(&mut self, c: u8) {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\tptr.add(0).write_volatile(c);\n\t\t}\n\t}\n\n\tpub fn get(&mut self) -> Option<u8> {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\tif ptr.add(5).read_volatile() & 1 == 0 {\n\t\t\t\t// The DR bit is 0, meaning no data\n\t\t\t\tNone\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// The DR bit is 1, meaning data!\n\t\t\t\tSome(ptr.add(0).read_volatile())\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "risc_v/chapters/ch8/.cargo/config",
    "content": "[build]\ntarget = \"riscv64gc-unknown-none-elf\"\n\n[target.riscv64gc-unknown-none-elf]\nlinker = \"riscv64-unknown-linux-gnu-gcc\"\n"
  },
  {
    "path": "risc_v/chapters/ch8/.gitignore",
    "content": "os.elf\ntarget/*\n"
  },
  {
    "path": "risc_v/chapters/ch8/Cargo.toml",
    "content": "[package]\nname = \"sos\"\nversion = \"0.1.0\"\nauthors = [\"Stephen Marz <stephen.marz@utk.edu>\"]\nedition = \"2018\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[lib]\ncrate-type = [\"staticlib\"]\n\n[dependencies]\n"
  },
  {
    "path": "risc_v/chapters/ch8/Makefile",
    "content": "#####\n## BUILD\n#####\nCC=riscv64-unknown-linux-gnu-gcc\nCFLAGS=-Wall -Wextra -pedantic -Wextra -O0 -g\nCFLAGS+=-static -ffreestanding -nostdlib -fno-rtti -fno-exceptions\nCFLAGS+=-march=rv64gc -mabi=lp64\nINCLUDES=\nLINKER_SCRIPT=-Tsrc/lds/virt.lds\nTYPE=debug\nRUST_TARGET=./target/riscv64gc-unknown-none-elf/$(TYPE)\nLIBS=-L$(RUST_TARGET)\nSOURCES_ASM=$(wildcard src/asm/*.S)\nLIB=-lsos -lgcc\nOUT=os.elf\n\n#####\n## QEMU\n#####\nQEMU=qemu-system-riscv64\nMACH=virt\nCPU=rv64\nCPUS=4\nMEM=128M\nDISK=hdd.dsk\n# DRIVE= -drive if=none,format=raw,file=$(DISK),id=foo -device virtio-blk-device,scsi=off,drive=foo\nDRIVE=\n\n\nall:\n\tcargo build\n\t$(CC) $(CFLAGS) $(LINKER_SCRIPT) $(INCLUDES) -o $(OUT) $(SOURCES_ASM) $(LIBS) $(LIB)\n\t\nrun: all\n\t$(QEMU) -machine $(MACH) -cpu $(CPU) -smp $(CPUS) -m $(MEM) $(DRIVE) -nographic -serial mon:stdio -bios none -kernel $(OUT)\n\n\n.PHONY: clean\nclean:\n\tcargo clean\n\trm -f $(OUT)\n"
  },
  {
    "path": "risc_v/chapters/ch8/make_hdd.sh",
    "content": "#!/bin/sh\n\ndd if=/dev/zero of=hdd.dsk bs=1M count=32\n"
  },
  {
    "path": "risc_v/chapters/ch8/src/asm/boot.S",
    "content": "# boot.S\n# bootloader for SoS\n# Stephen Marz\n# 8 February 2019\n\n# Disable generation of compressed instructions.\n.option norvc\n\n# Define a .text.init section. The .text.init is put at the\n# starting address so that the entry _start is put at the RISC-V\n# address 0x8000_0000.\n.section .text.init\n\n# Execution starts here.\n.global _start\n_start:\n\n\t# Disable linker instruction relaxation for the `la` instruction below.\n\t# This disallows the assembler from assuming that `gp` is already initialized.\n\t# This causes the value stored in `gp` to be calculated from `pc`.\n\t# The job of the global pointer is to give the linker the ability to address\n\t# memory relative to GP instead of as an absolute address.\n.option push\n.option norelax\n\tla\t\tgp, _global_pointer\n.option pop\n\t# SATP should be zero, but let's make sure. Each HART has its own\n\t# SATP register.\n\tcsrw\tsatp, zero\n\t# Any hardware threads (hart) that are not bootstrapping\n\t# need to wait for an IPI\n\tcsrr\tt0, mhartid\n\tbnez\tt0, 3f\n\n\t# Set all bytes in the BSS section to zero.\n\tla \t\ta0, _bss_start\n\tla\t\ta1, _bss_end\n\tbgeu\ta0, a1, 2f\n1:\n\tsd\t\tzero, (a0)\n\taddi\ta0, a0, 8\n\tbltu\ta0, a1, 1b\n2:\n\t# The stack grows from bottom to top, so we put the stack pointer\n\t# to the very end of the stack range.\n\tla\t\tsp, _stack_end\n\t# Setting `mstatus` register:\n\t# 0b01 << 11: Machine's previous protection mode is 2 (MPP=2).\n\tli\t\tt0, 0b11 << 11\n\tcsrw\tmstatus, t0\n\t# Do not allow interrupts while running kinit\n\tcsrw\tmie, zero\n\t# Machine's exception program counter (MEPC) is set to `kinit`.\n\tla\t\tt1, kinit\n\tcsrw\tmepc, t1\n\t# Set the return address to get us into supervisor mode\n\tla\t\tra, 2f\n\t# We use mret here so that the mstatus register is properly updated.\n\tmret\n2:\n\t# We set the return address (ra above) to this label. When kinit() is finished\n\t# in Rust, it will return here.\n\n\t# Setting `mstatus` (supervisor status) register:\n\t# 0b01 << 11 : Previous protection mode is 1 (MPP=01 [Supervisor]).\n\t# 1 << 7     : Previous machine interrupt-enable bit is 1 (MPIE=1 [Enabled])\n\t# 1 << 5     : Previous interrupt-enable bit is 1 (SPIE=1 [Enabled]).\n\t# We set the \"previous\" bits because the mret will write the current bits\n\t# with the previous bits.\n\tli\t\tt0, (0b00 << 11) | (1 << 7) | (1 << 5)\n\tcsrw\tmstatus, t0\n\t# Machine's trap vector base address is set to `m_trap_vector`, for\n\t# \"machine\" trap vector.\n\tla\t\tt2, m_trap_vector\n\tcsrw\tmtvec, t2\n\t# Jump to first process. We put the MPP = 00 for user mode, so after\n\t# mret, we will jump to the first process' addresss in user mode.\n\tla\t\tra, 4f\n\tmret\n3:\n\n\t# Parked harts go here. We need to set these\n\t# to only awaken if it receives a software interrupt,\n\t# which we're going to call the SIPI (Software Intra-Processor Interrupt).\n\t# We call the SIPI by writing the software interrupt into the Core Local Interruptor (CLINT)\n\t# Which is calculated by: base_address + hart * 4\n\t# where base address is 0x0200_0000 (MMIO CLINT base address)\n\t# We only use additional harts to run user-space programs, although this may\n\t# change.\n\n\t# We divide up the stack so the harts aren't clobbering one another.\n\tla\t\tsp, _stack_end\n\tli\t\tt0, 0x10000\n\tcsrr\ta0, mhartid\n\tmul\t\tt0, t0, a0\n\tsub\t\tsp, sp, t0\n\n\t# The parked harts will be put into machine mode with interrupts enabled.\n\tli\t\tt0, 0b11 << 11 | (1 << 7)\n\tcsrw\tmstatus, t0\n\t# Allow for MSIP (Software interrupt). We will write the MSIP from hart #0 to\n\t# awaken these parked harts.\n\tli\t\tt3, (1 << 3)\n\tcsrw\tmie, t3\n\t# Machine's exception program counter (MEPC) is set to the Rust initialization\n\t# code and waiting loop.\n\tla\t\tt1, kinit_hart\n\tcsrw\tmepc, t1\n\t# Machine's trap vector base address is set to `m_trap_vector`, for\n\t# \"machine\" trap vector. The Rust initialization routines will give each\n\t# hart its own trap frame. We can use the same trap function and distinguish\n\t# between each hart by looking at the trap frame.\n\tla\t\tt2, m_trap_vector\n\tcsrw\tmtvec, t2\n\t# Whenever our hart is done initializing, we want it to return to the waiting\n\t# loop, which is just below mret.\n\tla\t\tra, 4f\n\t# We use mret here so that the mstatus register is properly updated.\n\tmret\n\n4:\n\t# wfi = wait for interrupt. This is a hint to the harts to shut everything needed\n\t# down. However, the RISC-V specification allows for wfi to do nothing. Anyway,\n\t# with QEMU, this will save some CPU!\n\twfi\n\tj\t\t4b\n\n\n"
  },
  {
    "path": "risc_v/chapters/ch8/src/asm/mem.S",
    "content": "// mem.S\n// Importation of linker symbols\n\n.section .rodata\n.global HEAP_START\nHEAP_START: .dword _heap_start\n\n.global HEAP_SIZE\nHEAP_SIZE: .dword _heap_size\n\n.global TEXT_START\nTEXT_START: .dword _text_start\n\n.global TEXT_END\nTEXT_END: .dword _text_end\n\n.global DATA_START\nDATA_START: .dword _data_start\n\n.global DATA_END\nDATA_END: .dword _data_end\n\n.global RODATA_START\nRODATA_START: .dword _rodata_start\n\n.global RODATA_END\nRODATA_END: .dword _rodata_end\n\n.global BSS_START\nBSS_START: .dword _bss_start\n\n.global BSS_END\nBSS_END: .dword _bss_end\n\n.global KERNEL_STACK_START\nKERNEL_STACK_START: .dword _stack_start\n\n.global KERNEL_STACK_END\nKERNEL_STACK_END: .dword _stack_end\n\n\n"
  },
  {
    "path": "risc_v/chapters/ch8/src/asm/trap.S",
    "content": "# trap.S\n# Trap handler and global context\n# Steve Operating System\n# Stephen Marz\n# 24 February 2019\n.option norvc\n.altmacro\n.set NUM_GP_REGS, 32  # Number of registers per context\n.set REG_SIZE, 8   # Register size (in bytes)\n\n# Use macros for saving and restoring multiple registers\n.macro save_gp i, basereg=t6\n\tsd\tx\\i, ((\\i)*REG_SIZE)(\\basereg)\n.endm\n.macro load_gp i, basereg=t6\n\tld\tx\\i, ((\\i)*REG_SIZE)(\\basereg)\n.endm\n.macro save_fp i, basereg=t6\n\tfsd\tf\\i, ((NUM_GP_REGS+(\\i))*REG_SIZE)(\\basereg)\n.endm\n.macro load_fp i, basereg=t6\n\tfld\tf\\i, ((NUM_GP_REGS+(\\i))*REG_SIZE)(\\basereg)\n.endm\n\n.section .text\n.global m_trap_vector\n# This must be aligned by 4 since the last two bits\n# of the mtvec register do not contribute to the address\n# of this vector.\n.align 4\nm_trap_vector:\n\t# All registers are volatile here, we need to save them\n\t# before we do anything.\n\tcsrrw\tt6, mscratch, t6\n\t# csrrw will atomically swap t6 into mscratch and the old\n\t# value of mscratch into t6. This is nice because we just\n\t# switched values and didn't destroy anything -- all atomically!\n\t# in cpu.rs we have a structure of:\n\t#  32 gp regs\t\t0\n\t#  32 fp regs\t\t256\n\t#  SATP register\t512\n\t#  Trap stack       520\n\t#  CPU HARTID\t\t528\n\t# We use t6 as the temporary register because it is the very\n\t# bottom register (x31)\n\t.set \ti, 0\n\t.rept\t31\n\t\tsave_gp\t%i\n\t\t.set\ti, i+1\n\t.endr\n\n\t# Save the actual t6 register, which we swapped into\n\t# mscratch\n\tmv\t\tt5, t6\n\tcsrr\tt6, mscratch\n\tsave_gp 31, t5\n\n\t# Restore the kernel trap frame into mscratch\n\tcsrw\tmscratch, t5\n\n\t# Get ready to go into Rust (trap.rs)\n\t# We don't want to write into the user's stack or whomever\n\t# messed with us here.\n\t# csrw\tmie, zero\n\tcsrr\ta0, mepc\n\tcsrr\ta1, mtval\n\tcsrr\ta2, mcause\n\tcsrr\ta3, mhartid\n\tcsrr\ta4, mstatus\n\tcsrr\ta5, mscratch\n\tla\t\tt0, KERNEL_STACK_END\n\tld\t\tsp, 0(t0)\n\tcall\tm_trap\n\n\t# When we get here, we've returned from m_trap, restore registers\n\t# and return.\n\t# m_trap will return the return address via a0.\n\n\tcsrw\tmepc, a0\n\t# Now load the trap frame back into t6\n\tcsrr\tt6, mscratch\n\n\t# Restore all GP registers\n\t.set\ti, 1\n\t.rept\t31\n\t\tload_gp %i\n\t\t.set\ti, i+1\n\t.endr\n\n\t# Since we ran this loop 31 times starting with i = 1,\n\t# the last one loaded t6 back to its original value.\n\tmret\n\n.global switch_to_user\nswitch_to_user:\n    # a0 - Frame address\n\t# a1 - Program counter\n\t# a2 - SATP Register\n    csrw    mscratch, a0\n\n\t# 1 << 7 is MPIE\n\t# Since user mode is 00, we don't need to set anything\n\t# in MPP (bits 12:11)\n\tli\t\tt0, 1 << 7 | 1 << 5\n\tcsrw\tmstatus, t0\n\tcsrw\tmepc, a1\n\tcsrw\tsatp, a2\n\tli\t\tt1, 0xaaa\n\tcsrw\tmie, t1\n\tla\t\tt2, m_trap_vector\n\tcsrw\tmtvec, t2\n\t# This fence forces the MMU to flush the TLB. However, since\n\t# we're using the PID as the address space identifier, we might\n\t# only need this when we create a process. Right now, this ensures\n\t# correctness, however it isn't the most efficient.\n\tsfence.vma\n\t# A0 is the context frame, so we need to reload it back\n\t# and mret so we can start running the program.\n\tmv\tt6, a0\n\t.set\ti, 1\n\t.rept\t31\n\t\tload_gp %i, t6\n\t\t.set\ti, i+1\n\t.endr\n\t# j .\n    mret\n\n\n.global make_syscall\nmake_syscall:\n\tecall\n\tret\n"
  },
  {
    "path": "risc_v/chapters/ch8/src/cpu.rs",
    "content": "// cpu.rs\n// CPU and CPU-related routines\n// Also contains the kernel's trap frame\n// Stephen Marz\n// 14 October 2019\n\nuse core::ptr::null_mut;\n\n/// In 64-bit mode, we're given three different modes for the MMU:\n/// 0 - The MMU is off -- no protection and no translation PA = VA\n/// 8 - This is Sv39 mode -- 39-bit virtual addresses\n/// 9 - This is Sv48 mode -- 48-bit virtual addresses\n#[repr(usize)]\npub enum SatpMode {\n\tOff = 0,\n\tSv39 = 8,\n\tSv48 = 9,\n}\n\n/// The trap frame is set into a structure\n/// and packed into each hart's mscratch register.\n/// This allows for quick reference and full\n/// context switch handling.\n#[repr(C)]\n#[derive(Clone, Copy)]\npub struct TrapFrame {\n\tpub regs:       [usize; 32], // 0 - 255\n\tpub fregs:      [usize; 32], // 256 - 511\n\tpub satp:       usize,       // 512 - 519\n\tpub trap_stack: *mut u8,     // 520\n\tpub hartid:     usize,       // 528\n}\n\n/// Rust requires that we initialize our structures\n/// because of the move semantics. What'll happen below\n/// is Rust will construct a new TrapFrame and move it\n/// out of the zero() function below. Rust contains two\n/// different \"selfs\" where self can refer to the object\n/// in memory or Self (capital S) which refers to the\n/// data type of the structure. In the case below, this\n/// is TrapFrame.\nimpl TrapFrame {\n\tpub const fn zero() -> Self {\n\t\tTrapFrame { regs:       [0; 32],\n\t\t            fregs:      [0; 32],\n\t\t            satp:       0,\n\t\t            trap_stack: null_mut(),\n\t\t             hartid:     0, }\n\t}\n}\n\n/// The global kernel trap frame stores 8 separate\n/// frames -- one per CPU hart. We will switch these\n/// in and out and store a dormant trap frame with\n/// the process itself.\npub static mut KERNEL_TRAP_FRAME: [TrapFrame; 8] =\n\t[TrapFrame::zero(); 8];\n\n/// The SATP register contains three fields: mode, address space id, and\n/// the first level table address (level 2 for Sv39). This function\n/// helps make the 64-bit register contents based on those three\n/// fields.\npub const fn build_satp(mode: SatpMode, asid: usize, addr: usize) -> usize {\n\t(mode as usize) << 60\n\t| (asid & 0xffff) << 44\n\t| (addr >> 12) & 0xff_ffff_ffff\n}\n\npub fn mhartid_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, mhartid\" :\"=r\"(rval));\n\t\trval\n\t}\n}\npub fn mie_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, mie\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn mie_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw mie, $0\" :: \"r\"(val));\n\t}\n}\n\npub fn mstatus_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tmstatus, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn mstatus_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, mstatus\":\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn stvec_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tstvec, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn stvec_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, stvec\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn mscratch_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tmscratch, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn mscratch_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, mscratch\" : \"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn mscratch_swap(to: usize) -> usize {\n\tunsafe {\n\t\tlet from;\n\t\tasm!(\"csrrw\t$0, mscratch, $1\" : \"=r\"(from) : \"r\"(to));\n\t\tfrom\n\t}\n}\n\npub fn sscratch_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tsscratch, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn sscratch_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, sscratch\" : \"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn sscratch_swap(to: usize) -> usize {\n\tunsafe {\n\t\tlet from;\n\t\tasm!(\"csrrw\t$0, sscratch, $1\" : \"=r\"(from) : \"r\"(to));\n\t\tfrom\n\t}\n}\n\npub fn mepc_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw mepc, $0\" :: \"r\"(val));\n\t}\n}\n\npub fn mepc_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, mepc\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn sepc_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw sepc, $0\" :: \"r\"(val));\n\t}\n}\n\npub fn sepc_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, sepc\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn satp_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw satp, $0\" :: \"r\"(val));\n\t}\n}\n\npub fn satp_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, satp\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\n/// Take a hammer to the page tables and synchronize\n/// all of them. This essentially flushes the entire\n/// TLB.\npub fn satp_fence(vaddr: usize, asid: usize) {\n\tunsafe {\n\t\tasm!(\"sfence.vma $0, $1\" :: \"r\"(vaddr), \"r\"(asid));\n\t}\n}\n\n/// Synchronize based on the address space identifier\n/// This allows us to fence a particular process rather\n/// than the entire TLB.\n/// The RISC-V documentation calls this a TLB flush +.\n/// Since there are other memory routines involved, they\n/// didn't call it a TLB flush, but it is much like\n/// Intel/AMD's invtlb [] instruction.\npub fn satp_fence_asid(asid: usize) {\n\tunsafe {\n\t\tasm!(\"sfence.vma zero, $0\" :: \"r\"(asid));\n\t}\n}\n"
  },
  {
    "path": "risc_v/chapters/ch8/src/kmem.rs",
    "content": "// kmem.rs\n// Sub-page level: malloc-like allocation system\n// Stephen Marz\n// 7 October 2019\n\nuse crate::page::{align_val, zalloc, Table, PAGE_SIZE};\nuse core::{mem::size_of, ptr::null_mut};\n\n#[repr(usize)]\nenum AllocListFlags {\n\tTaken = 1 << 63,\n}\nimpl AllocListFlags {\n\tpub fn val(self) -> usize {\n\t\tself as usize\n\t}\n}\n\nstruct AllocList {\n\tpub flags_size: usize,\n}\nimpl AllocList {\n\tpub fn is_taken(&self) -> bool {\n\t\tself.flags_size & AllocListFlags::Taken.val() != 0\n\t}\n\n\tpub fn is_free(&self) -> bool {\n\t\t!self.is_taken()\n\t}\n\n\tpub fn set_taken(&mut self) {\n\t\tself.flags_size |= AllocListFlags::Taken.val();\n\t}\n\n\tpub fn set_free(&mut self) {\n\t\tself.flags_size &= !AllocListFlags::Taken.val();\n\t}\n\n\tpub fn set_size(&mut self, sz: usize) {\n\t\tlet k = self.is_taken();\n\t\tself.flags_size = sz & !AllocListFlags::Taken.val();\n\t\tif k {\n\t\t\tself.flags_size |= AllocListFlags::Taken.val();\n\t\t}\n\t}\n\n\tpub fn get_size(&self) -> usize {\n\t\tself.flags_size & !AllocListFlags::Taken.val()\n\t}\n}\n\n// This is the head of the allocation. We start here when\n// we search for a free memory location.\nstatic mut KMEM_HEAD: *mut AllocList = null_mut();\n// In the future, we will have on-demand pages\n// so, we need to keep track of our memory footprint to\n// see if we actually need to allocate more.\nstatic mut KMEM_ALLOC: usize = 0;\nstatic mut KMEM_PAGE_TABLE: *mut Table = null_mut();\n\n// These functions are safe helpers around an unsafe\n// operation.\npub fn get_head() -> *mut u8 {\n\tunsafe { KMEM_HEAD as *mut u8 }\n}\n\npub fn get_page_table() -> *mut Table {\n\tunsafe { KMEM_PAGE_TABLE as *mut Table }\n}\n\npub fn get_num_allocations() -> usize {\n\tunsafe { KMEM_ALLOC }\n}\n\n/// Initialize kernel's memory\n/// This is not to be used to allocate memory\n/// for user processes. If that's the case, use\n/// alloc/dealloc from the page crate.\npub fn init() {\n\tunsafe {\n\t\t// Allocate kernel pages (KMEM_ALLOC)\n\t\tKMEM_ALLOC = 512;\n\t\tlet k_alloc = zalloc(KMEM_ALLOC);\n\t\tassert!(!k_alloc.is_null());\n\t\tKMEM_HEAD = k_alloc as *mut AllocList;\n\t\t(*KMEM_HEAD).set_free();\n\t\t(*KMEM_HEAD).set_size(KMEM_ALLOC * PAGE_SIZE);\n\t\tKMEM_PAGE_TABLE = zalloc(1) as *mut Table;\n\t}\n}\n\n/// Allocate sub-page level allocation based on bytes and zero the memory\npub fn kzmalloc(sz: usize) -> *mut u8 {\n\tlet size = align_val(sz, 3);\n\tlet ret = kmalloc(size);\n\n\tif !ret.is_null() {\n\t\tfor i in 0..size {\n\t\t\tunsafe {\n\t\t\t\t(*ret.add(i)) = 0;\n\t\t\t}\n\t\t}\n\t}\n\tret\n}\n\n/// Allocate sub-page level allocation based on bytes\npub fn kmalloc(sz: usize) -> *mut u8 {\n\tunsafe {\n\t\tlet size = align_val(sz, 3) + size_of::<AllocList>();\n\t\tlet mut head = KMEM_HEAD;\n\t\t// .add() uses pointer arithmetic, so we type-cast into a u8\n\t\t// so that we multiply by an absolute size (KMEM_ALLOC *\n\t\t// PAGE_SIZE).\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\n\t\twhile head < tail {\n\t\t\tif (*head).is_free() && size <= (*head).get_size() {\n\t\t\t\tlet chunk_size = (*head).get_size();\n\t\t\t\tlet rem = chunk_size - size;\n\t\t\t\t(*head).set_taken();\n\t\t\t\tif rem > size_of::<AllocList>() {\n\t\t\t\t\tlet next = (head as *mut u8).add(size)\n\t\t\t\t\t           as *mut AllocList;\n\t\t\t\t\t// There is space remaining here.\n\t\t\t\t\t(*next).set_free();\n\t\t\t\t\t(*next).set_size(rem);\n\t\t\t\t\t(*head).set_size(size);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// If we get here, take the entire chunk\n\t\t\t\t\t(*head).set_size(chunk_size);\n\t\t\t\t}\n\t\t\t\treturn head.add(1) as *mut u8;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// If we get here, what we saw wasn't a free\n\t\t\t\t// chunk, move on to the next.\n\t\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t\t       as *mut AllocList;\n\t\t\t}\n\t\t}\n\t}\n\t// If we get here, we didn't find any free chunks--i.e. there isn't\n\t// enough memory for this. TODO: Add on-demand page allocation.\n\tnull_mut()\n}\n\n/// Free a sub-page level allocation\npub fn kfree(ptr: *mut u8) {\n\tunsafe {\n\t\tif !ptr.is_null() {\n\t\t\tlet p = (ptr as *mut AllocList).offset(-1);\n\t\t\tif (*p).is_taken() {\n\t\t\t\t(*p).set_free();\n\t\t\t}\n\t\t\t// After we free, see if we can combine adjacent free\n\t\t\t// spots to see if we can reduce fragmentation.\n\t\t\tcoalesce();\n\t\t}\n\t}\n}\n\n/// Merge smaller chunks into a bigger chunk\npub fn coalesce() {\n\tunsafe {\n\t\tlet mut head = KMEM_HEAD;\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\n\t\twhile head < tail {\n\t\t\tlet next = (head as *mut u8).add((*head).get_size())\n\t\t\t           as *mut AllocList;\n\t\t\tif (*head).get_size() == 0 {\n\t\t\t\t// If this happens, then we have a bad heap\n\t\t\t\t// (double free or something). However, that\n\t\t\t\t// will cause an infinite loop since the next\n\t\t\t\t// pointer will never move beyond the current\n\t\t\t\t// location.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if next >= tail {\n\t\t\t\t// We calculated the next by using the size\n\t\t\t\t// given as get_size(), however this could push\n\t\t\t\t// us past the tail. In that case, the size is\n\t\t\t\t// wrong, hence we break and stop doing what we\n\t\t\t\t// need to do.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (*head).is_free() && (*next).is_free() {\n\t\t\t\t// This means we have adjacent blocks needing to\n\t\t\t\t// be freed. So, we combine them into one\n\t\t\t\t// allocation.\n\t\t\t\t(*head).set_size(\n\t\t\t\t                 (*head).get_size()\n\t\t\t\t                 + (*next).get_size(),\n\t\t\t\t);\n\t\t\t}\n\t\t\t// If we get here, we might've moved. Recalculate new\n\t\t\t// head.\n\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t       as *mut AllocList;\n\t\t}\n\t}\n}\n\n/// For debugging purposes, print the kmem table\npub fn print_table() {\n\tunsafe {\n\t\tlet mut head = KMEM_HEAD;\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\t\twhile head < tail {\n\t\t\tprintln!(\n\t\t\t         \"{:p}: Length = {:<10} Taken = {}\",\n\t\t\t         head,\n\t\t\t         (*head).get_size(),\n\t\t\t         (*head).is_taken()\n\t\t\t);\n\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t       as *mut AllocList;\n\t\t}\n\t}\n}\n\n// ///////////////////////////////////\n// / GLOBAL ALLOCATOR\n// ///////////////////////////////////\n\n// The global allocator allows us to use the data structures\n// in the core library, such as a linked list or B-tree.\n// We want to use these sparingly since we have a coarse-grained\n// allocator.\nuse core::alloc::{GlobalAlloc, Layout};\n\n// The global allocator is a static constant to a global allocator\n// structure. We don't need any members because we're using this\n// structure just to implement alloc and dealloc.\nstruct OsGlobalAlloc;\n\nunsafe impl GlobalAlloc for OsGlobalAlloc {\n\tunsafe fn alloc(&self, layout: Layout) -> *mut u8 {\n\t\t// We align to the next page size so that when\n\t\t// we divide by PAGE_SIZE, we get exactly the number\n\t\t// of pages necessary.\n\t\tkzmalloc(layout.size())\n\t}\n\n\tunsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {\n\t\t// We ignore layout since our allocator uses ptr_start -> last\n\t\t// to determine the span of an allocation.\n\t\tkfree(ptr);\n\t}\n}\n\n#[global_allocator]\n/// Technically, we don't need the {} at the end, but it\n/// reveals that we're creating a new structure and not just\n/// copying a value.\nstatic GA: OsGlobalAlloc = OsGlobalAlloc {};\n\n#[alloc_error_handler]\n/// If for some reason alloc() in the global allocator gets null_mut(),\n/// then we come here. This is a divergent function, so we call panic to\n/// let the tester know what's going on.\npub fn alloc_error(l: Layout) -> ! {\n\tpanic!(\n\t       \"Allocator failed to allocate {} bytes with {}-byte alignment.\",\n\t       l.size(),\n\t       l.align()\n\t);\n}\n"
  },
  {
    "path": "risc_v/chapters/ch8/src/lds/virt.lds",
    "content": "/*\n virt.lds\n Linker script for outputting to RISC-V QEMU \"virt\" machine.\n Stephen Marz\n 6 October 2019\n*/\n\n/*\n  riscv is the name of the architecture that the linker understands\n  for any RISC-V target (64-bit or 32-bit).\n\n  We will further refine this by using -mabi=lp64 and -march=rv64gc\n*/\nOUTPUT_ARCH( \"riscv\" )\n\n/*\nWe're setting our entry point to a symbol\ncalled _start which is inside of boot.S. This\nessentially stores the address of _start as the\n\"entry point\", or where CPU instructions should start\nexecuting.\n\nIn the rest of this script, we are going to place _start\nright at the beginning of 0x8000_0000 because this is where\nthe virtual machine and many RISC-V boards will start executing.\n*/\nENTRY( _start )\n\n/*\nThe MEMORY section will explain that we have \"ram\" that contains\na section that is 'w' (writeable), 'x' (executable), and 'a' (allocatable).\nWe use '!' to invert 'r' (read-only) and 'i' (initialized). We don't want\nour memory to be read-only, and we're stating that it is NOT initialized\nat the beginning.\n\nThe ORIGIN is the memory address 0x8000_0000. If we look at the virt\nspec or the specification for the RISC-V HiFive Unleashed, this is the\nstarting memory address for our code.\n\nSide note: There might be other boot ROMs at different addresses, but\ntheir job is to get to this point.\n\nFinally LENGTH = 128M tells the linker that we have 128 megabyte of RAM.\nThe linker will double check this to make sure everything can fit.\n\nThe HiFive Unleashed has a lot more RAM than this, but for the virtual \nmachine, I went with 128M since I think that's enough RAM for now.\n\nWe can provide other pieces of memory, such as QSPI, or ROM, but we're\ntelling the linker script here that we have one pool of RAM.\n*/\nMEMORY\n{\n  ram   (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M\n}\n\n/*\nPHDRS is short for \"program headers\", which we specify three here:\ntext - CPU instructions (executable sections)\ndata - Global, initialized variables\nbss  - Global, uninitialized variables (all will be set to 0 by boot.S)\n\nThe command PT_LOAD tells the linker that these sections will be loaded\nfrom the file into memory.\n\nWe can actually stuff all of these into a single program header, but by\nsplitting it up into three, we can actually use the other PT_* commands\nsuch as PT_DYNAMIC, PT_INTERP, PT_NULL to tell the linker where to find\nadditional information.\n\nHowever, for our purposes, every section will be loaded from the program\nheaders.\n*/\nPHDRS\n{\n  text PT_LOAD;\n  data PT_LOAD;\n  bss PT_LOAD;\n}\n\n/*\nWe are now going to organize the memory based on which\nsection it is in. In assembly, we can change the section\nwith the \".section\" directive. However, in C++ and Rust,\nCPU instructions go into text, global constants go into\nrodata, global initialized variables go into data, and\nglobal uninitialized variables go into bss.\n*/\nSECTIONS\n{\n  /*\n    The first part of our RAM layout will be the text section.\n\tSince our CPU instructions are here, and our memory starts at\n\t0x8000_0000, we need our entry point to line up here.\n  */\n  .text : {\n\t  /* \n\t    PROVIDE allows me to access a symbol called _text_start so\n\t\tI know where the text section starts in the operating system.\n\t\tThis should not move, but it is here for convenience.\n\t\tThe period '.' tells the linker to set _text_start to the\n\t\tCURRENT location ('.' = current memory location). This current\n\t\tmemory location moves as we add things.\n\t  */\n\n    PROVIDE(_text_start = .);\n\t/*\n\t  We are going to layout all text sections here, starting with \n\t  .text.init. The asterisk in front of the parentheses means to match\n\t  the .text.init section of ANY object file. Otherwise, we can specify\n\t  which object file should contain the .text.init section, for example,\n\t  boot.o(.text.init) would specifically put the .text.init section of\n\t  our bootloader here.\n\n\t  Because we might want to change the name of our files, we'll leave it\n\t  with a *.\n\n\t  Inside the parentheses is the name of the section. I created my own\n\t  called .text.init to make 100% sure that the _start is put right at the\n\t  beginning. The linker will lay this out in the order it receives it:\n\n\t  .text.init first\n\t  all .text sections next\n\t  any .text.* sections last\n\n\t  .text.* means to match anything after .text. If we didn't already specify\n\t  .text.init, this would've matched here. The assembler and linker can place\n\t  things in \"special\" text sections, so we match any we might come across here.\n\t*/\n    *(.text.init) *(.text .text.*)\n\n\t/*\n\t  Again, with PROVIDE, we're providing a readable symbol called _text_end, which is\n\t  set to the memory address AFTER .text.init, .text, and .text.*'s have been added.\n\t*/\n    PROVIDE(_text_end = .);\n\t/*\n\t  The portion after the right brace is in an odd format. However, this is telling the\n\t  linker what memory portion to put it in. We labeled our RAM, ram, with the constraints\n\t  that it is writeable, allocatable, and executable. The linker will make sure with this\n\t  that we can do all of those things.\n\n\t  >ram - This just tells the linker script to put this entire section (.text) into the\n\t         ram region of memory. To my knowledge, the '>' does not mean \"greater than\". Instead,\n\t\t\t it is a symbol to let the linker know we want to put this in ram.\n\n\t  AT>ram - This sets the LMA (load memory address) region to the same thing. LMA is the final\n\t           translation of a VMA (virtual memory address). With this linker script, we're loading\n\t\t\t   everything into its physical location. We'll let the kernel copy and sort out the \n\t\t\t   virtual memory. That's why >ram and AT>ram are continually the same thing.\n\n\t  :text  - This tells the linker script to put this into the :text program header. We've only\n\t           defined three: text, data, and bss. In this case, we're telling the linker script\n\t\t\t   to go into the text section.\n\t*/\n  } >ram AT>ram :text\n   /*\n     The global pointer allows the linker to position global variables and constants into\n\t independent positions relative to the gp (global pointer) register. The globals start\n\t after the text sections and are only relevant to the rodata, data, and bss sections.\n   */\n   PROVIDE(_global_pointer = .);\n   /*\n     Most compilers create a rodata (read only data) section for global constants. However,\n\t we're going to place ours in the text section. We can actually put this in :data, but\n\t since the .text section is read-only, we can place it there.\n\n\t NOTE: This doesn't actually do anything, yet. The actual \"protection\" cannot be done\n\t at link time. Instead, when we program the memory management unit (MMU), we will be\n\t able to choose which bits (R=read, W=write, X=execute) we want each memory segment\n\t to be able to do.\n   */\n  .rodata : {\n    PROVIDE(_rodata_start = .);\n    *(.rodata .rodata.*)\n    PROVIDE(_rodata_end = .);\n\t/*\n\t   Again, we're placing the rodata section in the memory segment \"ram\" and we're putting\n\t   it in the :text program header. We don't have one for rodata anyway.\n\t*/\n  } >ram AT>ram :text\n\n  .data : {\n\t/*\n\t   . = ALIGN(4096) tells the linker to align the current memory location (which is\n\t   0x8000_0000 + text section + rodata section) to 4096 bytes. This is because our paging\n\t   system's resolution is 4,096 bytes or 4 KiB.\n\t*/\n    . = ALIGN(4096);\n    PROVIDE(_data_start = .);\n\t/*\n\t   sdata and data are essentially the same thing. However, compilers usually use the\n\t   sdata sections for shorter, quicker loading sections. So, usually critical data\n\t   is loaded there. However, we're loading all of this in one fell swoop.\n\t   So, we're looking to put all of the following sections under the umbrella .data:\n\t   .sdata\n\t   .sdata.[anything]\n\t   .data\n\t   .data.[anything]\n\n\t   ...in that order.\n\t*/\n    *(.sdata .sdata.*) *(.data .data.*)\n    PROVIDE(_data_end = .);\n  } >ram AT>ram :data\n\n  .bss : {\n    PROVIDE(_bss_start = .);\n    *(.sbss .sbss.*) *(.bss .bss.*)\n    PROVIDE(_bss_end = .);\n  } >ram AT>ram :bss\n\n  /*\n     The following will be helpful when we allocate the kernel stack (_stack) and\n\t determine where the heap begnis and ends (_heap_start and _heap_start + _heap_size)/\n\t When we do memory allocation, we can use these symbols.\n\n\t We use the symbols instead of hard-coding an address because this is a floating target.\n\t As we add code, the heap moves farther down the memory and gets shorter.\n\n\t _memory_start will be set to 0x8000_0000 here. We use ORIGIN(ram) so that it will take\n\t whatever we set the origin of ram to. Otherwise, we'd have to change it more than once\n\t if we ever stray away from 0x8000_0000 as our entry point.\n  */\n  PROVIDE(_memory_start = ORIGIN(ram));\n  /*\n     Our kernel stack starts at the end of the bss segment (_bss_end). However, we're allocating\n\t 0x80000 bytes (524 KiB) to our kernel stack. This should be PLENTY of space. The reason\n\t we add the memory is because the stack grows from higher memory to lower memory (bottom to top).\n\t Therefore we set the stack at the very bottom of its allocated slot.\n\t When we go to allocate from the stack, we'll subtract the number of bytes we need.\n  */\n  PROVIDE(_stack_start = _bss_end);\n  PROVIDE(_stack_end = _stack_start + 0x8000);\n  PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));\n\n  /* \n     Finally, our heap starts right after the kernel stack. This heap will be used mainly\n\t to dole out memory for user-space applications. However, in some circumstances, it will\n\t be used for kernel memory as well.\n\n\t We don't align here because we let the kernel determine how it wants to do this.\n  */\n  PROVIDE(_heap_start = _stack_end);\n  PROVIDE(_heap_size = _memory_end - _heap_start);\n}\n"
  },
  {
    "path": "risc_v/chapters/ch8/src/lib.rs",
    "content": "// Steve Operating System\n// Stephen Marz\n// 21 Sep 2019\n#![no_std]\n#![feature(panic_info_message,\n           asm,\n           allocator_api,\n           alloc_error_handler,\n           alloc_prelude,\n           const_raw_ptr_to_usize_cast)]\n\n// #[macro_use]\nextern crate alloc;\n// This is experimental and requires alloc_prelude as a feature\n// use alloc::prelude::v1::*;\n\n// ///////////////////////////////////\n// / RUST MACROS\n// ///////////////////////////////////\n#[macro_export]\nmacro_rules! print\n{\n\t($($args:tt)+) => ({\n\t\t\tuse core::fmt::Write;\n\t\t\tlet _ = write!(crate::uart::Uart::new(0x1000_0000), $($args)+);\n\t\t\t});\n}\n#[macro_export]\nmacro_rules! println\n{\n\t() => ({\n\t\t   print!(\"\\r\\n\")\n\t\t   });\n\t($fmt:expr) => ({\n\t\t\tprint!(concat!($fmt, \"\\r\\n\"))\n\t\t\t});\n\t($fmt:expr, $($args:tt)+) => ({\n\t\t\tprint!(concat!($fmt, \"\\r\\n\"), $($args)+)\n\t\t\t});\n}\n\n// ///////////////////////////////////\n// / LANGUAGE STRUCTURES / FUNCTIONS\n// ///////////////////////////////////\n#[no_mangle]\nextern \"C\" fn eh_personality() {}\n\n#[panic_handler]\nfn panic(info: &core::panic::PanicInfo) -> ! {\n\tprint!(\"Aborting: \");\n\tif let Some(p) = info.location() {\n\t\tprintln!(\n\t\t         \"line {}, file {}: {}\",\n\t\t         p.line(),\n\t\t         p.file(),\n\t\t         info.message().unwrap()\n\t\t);\n\t}\n\telse {\n\t\tprintln!(\"no information available.\");\n\t}\n\tabort();\n}\n#[no_mangle]\nextern \"C\" fn abort() -> ! {\n\tloop {\n\t\tunsafe {\n\t\t\tasm!(\"wfi\"::::\"volatile\");\n\t\t}\n\t}\n}\n\n// ///////////////////////////////////\n// / CONSTANTS\n// ///////////////////////////////////\n// const STR_Y: &str = \"\\x1b[38;2;79;221;13m✓\\x1b[m\";\n// const STR_N: &str = \"\\x1b[38;2;221;41;13m✘\\x1b[m\";\n\n// The following symbols come from asm/mem.S. We can use\n// the symbols directly, but the address of the symbols\n// themselves are their values, which can cause issues.\n// Instead, I created doubleword values in mem.S in the .rodata and .data\n// sections.\n/*\nextern \"C\" {\n\tstatic TEXT_START: usize;\n\tstatic TEXT_END: usize;\n\tstatic DATA_START: usize;\n\tstatic DATA_END: usize;\n\tstatic RODATA_START: usize;\n\tstatic RODATA_END: usize;\n\tstatic BSS_START: usize;\n\tstatic BSS_END: usize;\n\tstatic KERNEL_STACK_START: usize;\n\tstatic KERNEL_STACK_END: usize;\n\tstatic HEAP_START: usize;\n\tstatic HEAP_SIZE: usize;\n}\n*/\n/// Identity map range\n/// Takes a contiguous allocation of memory and maps it using PAGE_SIZE\n/// This assumes that start <= end\npub fn id_map_range(root: &mut page::Table,\n                    start: usize,\n                    end: usize,\n                    bits: i64)\n{\n\tlet mut memaddr = start & !(page::PAGE_SIZE - 1);\n\tlet num_kb_pages =\n\t\t(page::align_val(end, 12) - memaddr) / page::PAGE_SIZE;\n\n\t// I named this num_kb_pages for future expansion when\n\t// I decide to allow for GiB (2^30) and 2MiB (2^21) page\n\t// sizes. However, the overlapping memory regions are causing\n\t// nightmares.\n\tfor _ in 0..num_kb_pages {\n\t\tpage::map(root, memaddr, memaddr, bits, 0);\n\t\tmemaddr += 1 << 12;\n\t}\n}\nextern \"C\" {\n\tfn switch_to_user(frame: usize, mepc: usize, satp: usize) -> !;\n}\n// ///////////////////////////////////\n// / ENTRY POINT\n// ///////////////////////////////////\n#[no_mangle]\nextern \"C\" fn kinit() {\n\tuart::Uart::new(0x1000_0000).init();\n\tpage::init();\n\tkmem::init();\n\tlet ret = process::init();\n\tprintln!(\"Init process created at address 0x{:08x}\", ret);\n\t// We lower the threshold wall so our interrupts can jump over it.\n\tplic::set_threshold(0);\n\t// VIRTIO = [1..8]\n\t// UART0 = 10\n\t// PCIE = [32..35]\n\t// Enable the UART interrupt.\n\tplic::enable(10);\n\tplic::set_priority(10, 1);\n\tprintln!(\"UART interrupts have been enabled and are awaiting your command.\");\n\tprintln!(\"Getting ready for first process.\");\n\tprintln!(\"Issuing the first context-switch timer.\");\n\tunsafe {\n\t\tlet mtimecmp = 0x0200_4000 as *mut u64;\n\t\tlet mtime = 0x0200_bff8 as *const u64;\n\t\t// The frequency given by QEMU is 10_000_000 Hz, so this sets\n\t\t// the next interrupt to fire one second from now.\n\t\tmtimecmp.write_volatile(mtime.read_volatile() + 1_000_000);\n\t}\n\tlet (frame, mepc, satp) = sched::schedule();\n\tunsafe {\n\t\tswitch_to_user(frame, mepc, satp);\n\t}\n\t// switch_to_user will not return, so we should never get here\n\tprintln!(\"WE DIDN'T SCHEDULE?! THIS ISN'T RIGHT!\");\n}\n#[no_mangle]\nextern \"C\" fn kinit_hart(hartid: usize) {\n\t// All non-0 harts initialize here.\n\tunsafe {\n\t\t// We have to store the kernel's table. The tables will be moved\n\t\t// back and forth between the kernel's table and user\n\t\t// applicatons' tables.\n\t\tcpu::mscratch_write(\n\t\t                    (&mut cpu::KERNEL_TRAP_FRAME[hartid]\n\t\t                     as *mut cpu::TrapFrame)\n\t\t                    as usize,\n\t\t);\n\t\t// Copy the same mscratch over to the supervisor version of the\n\t\t// same register.\n\t\tcpu::sscratch_write(cpu::mscratch_read());\n\t\tcpu::KERNEL_TRAP_FRAME[hartid].hartid = hartid;\n\t\t// We can't do the following until zalloc() is locked, but we\n\t\t// don't have locks, yet :( cpu::KERNEL_TRAP_FRAME[hartid].satp\n\t\t// = cpu::KERNEL_TRAP_FRAME[0].satp;\n\t\t// cpu::KERNEL_TRAP_FRAME[hartid].trap_stack = page::zalloc(1);\n\t}\n}\n\n// ///////////////////////////////////\n// / RUST MODULES\n// ///////////////////////////////////\n\npub mod cpu;\npub mod kmem;\npub mod page;\npub mod plic;\npub mod process;\npub mod sched;\npub mod syscall;\npub mod trap;\npub mod uart;\n"
  },
  {
    "path": "risc_v/chapters/ch8/src/page.rs",
    "content": "// page.rs\n// Memory routines\n// Stephen Marz\n// 6 October 2019\n\nuse core::{mem::size_of, ptr::null_mut};\n\n// ////////////////////////////////\n// // Allocation routines\n// ////////////////////////////////\nextern \"C\" {\n\tstatic HEAP_START: usize;\n\tstatic HEAP_SIZE: usize;\n}\n\n// We will use ALLOC_START to mark the start of the actual\n// memory we can dish out.\nstatic mut ALLOC_START: usize = 0;\nconst PAGE_ORDER: usize = 12;\npub const PAGE_SIZE: usize = 1 << 12;\n\n/// Align (set to a multiple of some power of two)\n/// This takes an order which is the exponent to 2^order\n/// Therefore, all alignments must be made as a power of two.\n/// This function always rounds up.\npub const fn align_val(val: usize, order: usize) -> usize {\n\tlet o = (1usize << order) - 1;\n\t(val + o) & !o\n}\n\n#[repr(u8)]\npub enum PageBits {\n\tEmpty = 0,\n\tTaken = 1 << 0,\n\tLast = 1 << 1,\n}\n\nimpl PageBits {\n\t// We convert PageBits to a u8 a lot, so this is\n\t// for convenience.\n\tpub fn val(self) -> u8 {\n\t\tself as u8\n\t}\n}\n\n// Each page is described by the Page structure. Linux does this\n// as well, where each 4096-byte chunk of memory has a structure\n// associated with it. However, there structure is much larger.\npub struct Page {\n\tflags: u8,\n}\n\nimpl Page {\n\t// If this page has been marked as the final allocation,\n\t// this function returns true. Otherwise, it returns false.\n\tpub fn is_last(&self) -> bool {\n\t\tif self.flags & PageBits::Last.val() != 0 {\n\t\t\ttrue\n\t\t}\n\t\telse {\n\t\t\tfalse\n\t\t}\n\t}\n\n\t// If the page is marked as being taken (allocated), then\n\t// this function returns true. Otherwise, it returns false.\n\tpub fn is_taken(&self) -> bool {\n\t\tif self.flags & PageBits::Taken.val() != 0 {\n\t\t\ttrue\n\t\t}\n\t\telse {\n\t\t\tfalse\n\t\t}\n\t}\n\n\t// This is the opposite of is_taken().\n\tpub fn is_free(&self) -> bool {\n\t\t!self.is_taken()\n\t}\n\n\t// Clear the Page structure and all associated allocations.\n\tpub fn clear(&mut self) {\n\t\tself.flags = PageBits::Empty.val();\n\t}\n\n\t// Set a certain flag. We ran into trouble here since PageBits\n\t// is an enumeration and we haven't implemented the BitOr Trait\n\t// on it.\n\tpub fn set_flag(&mut self, flag: PageBits) {\n\t\tself.flags |= flag.val();\n\t}\n\n\tpub fn clear_flag(&mut self, flag: PageBits) {\n\t\tself.flags &= !(flag.val());\n\t}\n}\n\n/// Initialize the allocation system. There are several ways that we can\n/// implement the page allocator:\n/// 1. Free list (singly linked list where it starts at the first free\n/// allocation) 2. Bookkeeping list (structure contains a taken and length)\n/// 3. Allocate one Page structure per 4096 bytes (this is what I chose)\n/// 4. Others\npub fn init() {\n\tunsafe {\n\t\t// let desc_per_page = PAGE_SIZE / size_of::<Page>();\n\t\tlet num_pages = HEAP_SIZE / PAGE_SIZE;\n\t\t// let num_desc_pages = num_pages / desc_per_page;\n\t\tlet ptr = HEAP_START as *mut Page;\n\t\t// Clear all pages to make sure that they aren't accidentally\n\t\t// taken\n\t\tfor i in 0..num_pages {\n\t\t\t(*ptr.add(i)).clear();\n\t\t}\n\t\t// Determine where the actual useful memory starts. This will be\n\t\t// after all Page structures. We also must align the ALLOC_START\n\t\t// to a page-boundary (PAGE_SIZE = 4096). ALLOC_START =\n\t\t// (HEAP_START + num_pages * size_of::<Page>() + PAGE_SIZE - 1)\n\t\t// & !(PAGE_SIZE - 1);\n\t\tALLOC_START = align_val(\n\t\t                        HEAP_START\n\t\t                        + num_pages * size_of::<Page>(),\n\t\t                        PAGE_ORDER,\n\t\t);\n\t}\n}\n\n/// Allocate a page or multiple pages\n/// pages: the number of PAGE_SIZE pages to allocate\npub fn alloc(pages: usize) -> *mut u8 {\n\t// We have to find a contiguous allocation of pages\n\tassert!(pages > 0);\n\tunsafe {\n\t\t// We create a Page structure for each page on the heap. We\n\t\t// actually might have more since HEAP_SIZE moves and so does\n\t\t// the size of our structure, but we'll only waste a few bytes.\n\t\tlet num_pages = HEAP_SIZE / PAGE_SIZE;\n\t\tlet ptr = HEAP_START as *mut Page;\n\t\tfor i in 0..num_pages - pages {\n\t\t\tlet mut found = false;\n\t\t\t// Check to see if this Page is free. If so, we have our\n\t\t\t// first candidate memory address.\n\t\t\tif (*ptr.add(i)).is_free() {\n\t\t\t\t// It was FREE! Yay!\n\t\t\t\tfound = true;\n\t\t\t\tfor j in i..i + pages {\n\t\t\t\t\t// Now check to see if we have a\n\t\t\t\t\t// contiguous allocation for all of the\n\t\t\t\t\t// request pages. If not, we should\n\t\t\t\t\t// check somewhere else.\n\t\t\t\t\tif (*ptr.add(j)).is_taken() {\n\t\t\t\t\t\tfound = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// We've checked to see if there are enough contiguous\n\t\t\t// pages to form what we need. If we couldn't, found\n\t\t\t// will be false, otherwise it will be true, which means\n\t\t\t// we've found valid memory we can allocate.\n\t\t\tif found {\n\t\t\t\tfor k in i..i + pages - 1 {\n\t\t\t\t\t(*ptr.add(k)).set_flag(PageBits::Taken);\n\t\t\t\t}\n\t\t\t\t// The marker for the last page is\n\t\t\t\t// PageBits::Last This lets us know when we've\n\t\t\t\t// hit the end of this particular allocation.\n\t\t\t\t(*ptr.add(i+pages-1)).set_flag(PageBits::Taken);\n\t\t\t\t(*ptr.add(i+pages-1)).set_flag(PageBits::Last);\n\t\t\t\t// The Page structures themselves aren't the\n\t\t\t\t// useful memory. Instead, there is 1 Page\n\t\t\t\t// structure per 4096 bytes starting at\n\t\t\t\t// ALLOC_START.\n\t\t\t\treturn (ALLOC_START + PAGE_SIZE * i)\n\t\t\t\t       as *mut u8;\n\t\t\t}\n\t\t}\n\t}\n\n\t// If we get here, that means that no contiguous allocation was\n\t// found.\n\tnull_mut()\n}\n\n/// Allocate and zero a page or multiple pages\n/// pages: the number of pages to allocate\n/// Each page is PAGE_SIZE which is calculated as 1 << PAGE_ORDER\n/// On RISC-V, this typically will be 4,096 bytes.\npub fn zalloc(pages: usize) -> *mut u8 {\n\t// Allocate and zero a page.\n\t// First, let's get the allocation\n\tlet ret = alloc(pages);\n\tif !ret.is_null() {\n\t\tlet size = (PAGE_SIZE * pages) / 8;\n\t\tlet big_ptr = ret as *mut u64;\n\t\tfor i in 0..size {\n\t\t\t// We use big_ptr so that we can force an\n\t\t\t// sd (store doubleword) instruction rather than\n\t\t\t// the sb. This means 8x fewer stores than before.\n\t\t\t// Typically we have to be concerned about remaining\n\t\t\t// bytes, but fortunately 4096 % 8 = 0, so we\n\t\t\t// won't have any remaining bytes.\n\t\t\tunsafe {\n\t\t\t\t(*big_ptr.add(i)) = 0;\n\t\t\t}\n\t\t}\n\t}\n\tret\n}\n\n/// Deallocate a page by its pointer\n/// The way we've structured this, it will automatically coalesce\n/// contiguous pages.\npub fn dealloc(ptr: *mut u8) {\n\t// Make sure we don't try to free a null pointer.\n\tassert!(!ptr.is_null());\n\tunsafe {\n\t\tlet addr =\n\t\t\tHEAP_START + (ptr as usize - ALLOC_START) / PAGE_SIZE;\n\t\t// Make sure that the address makes sense. The address we\n\t\t// calculate here is the page structure, not the HEAP address!\n\t\tassert!(addr >= HEAP_START && addr < HEAP_START + HEAP_SIZE);\n\t\tlet mut p = addr as *mut Page;\n\t\t// Keep clearing pages until we hit the last page.\n\t\twhile (*p).is_taken() && !(*p).is_last() {\n\t\t\t(*p).clear();\n\t\t\tp = p.add(1);\n\t\t}\n\t\t// If the following assertion fails, it is most likely\n\t\t// caused by a double-free.\n\t\tassert!(\n\t\t        (*p).is_last() == true,\n\t\t        \"Possible double-free detected! (Not taken found \\\n\t\t         before last)\"\n\t\t);\n\t\t// If we get here, we've taken care of all previous pages and\n\t\t// we are on the last page.\n\t\t(*p).clear();\n\t}\n}\n\n/// Print all page allocations\n/// This is mainly used for debugging.\npub fn print_page_allocations() {\n\tunsafe {\n\t\tlet num_pages = (HEAP_SIZE - (ALLOC_START - HEAP_START)) / PAGE_SIZE;\n\t\tlet mut beg = HEAP_START as *const Page;\n\t\tlet end = beg.add(num_pages);\n\t\tlet alloc_beg = ALLOC_START;\n\t\tlet alloc_end = ALLOC_START + num_pages * PAGE_SIZE;\n\t\tprintln!();\n\t\tprintln!(\n\t\t         \"PAGE ALLOCATION TABLE\\nMETA: {:p} -> {:p}\\nPHYS: \\\n\t\t          0x{:x} -> 0x{:x}\",\n\t\t         beg, end, alloc_beg, alloc_end\n\t\t);\n\t\tprintln!(\"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\");\n\t\tlet mut num = 0;\n\t\twhile beg < end {\n\t\t\tif (*beg).is_taken() {\n\t\t\t\tlet start = beg as usize;\n\t\t\t\tlet memaddr = ALLOC_START\n\t\t\t\t              + (start - HEAP_START)\n\t\t\t\t                * PAGE_SIZE;\n\t\t\t\tprint!(\"0x{:x} => \", memaddr);\n\t\t\t\tloop {\n\t\t\t\t\tnum += 1;\n\t\t\t\t\tif (*beg).is_last() {\n\t\t\t\t\t\tlet end = beg as usize;\n\t\t\t\t\t\tlet memaddr = ALLOC_START\n\t\t\t\t\t\t              + (end\n\t\t\t\t\t\t                 - HEAP_START)\n\t\t\t\t\t\t                * PAGE_SIZE\n\t\t\t\t\t\t              + PAGE_SIZE - 1;\n\t\t\t\t\t\tprint!(\n\t\t\t\t\t\t       \"0x{:x}: {:>3} page(s)\",\n\t\t\t\t\t\t       memaddr,\n\t\t\t\t\t\t       (end - start + 1)\n\t\t\t\t\t\t);\n\t\t\t\t\t\tprintln!(\".\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tbeg = beg.add(1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbeg = beg.add(1);\n\t\t}\n\t\tprintln!(\"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\");\n\t\tprintln!(\n\t\t         \"Allocated: {:>6} pages ({:>10} bytes).\",\n\t\t         num,\n\t\t         num * PAGE_SIZE\n\t\t);\n\t\tprintln!(\n\t\t         \"Free     : {:>6} pages ({:>10} bytes).\",\n\t\t         num_pages - num,\n\t\t         (num_pages - num) * PAGE_SIZE\n\t\t);\n\t\tprintln!();\n\t}\n}\n\n// ////////////////////////////////\n// // MMU Routines\n// ////////////////////////////////\n\n// Represent (repr) our entry bits as\n// unsigned 64-bit integers.\n#[repr(i64)]\n#[derive(Copy, Clone)]\npub enum EntryBits {\n\tNone = 0,\n\tValid = 1 << 0,\n\tRead = 1 << 1,\n\tWrite = 1 << 2,\n\tExecute = 1 << 3,\n\tUser = 1 << 4,\n\tGlobal = 1 << 5,\n\tAccess = 1 << 6,\n\tDirty = 1 << 7,\n\n\t// Convenience combinations\n\tReadWrite = 1 << 1 | 1 << 2,\n\tReadExecute = 1 << 1 | 1 << 3,\n\tReadWriteExecute = 1 << 1 | 1 << 2 | 1 << 3,\n\n\t// User Convenience Combinations\n\tUserReadWrite = 1 << 1 | 1 << 2 | 1 << 4,\n\tUserReadExecute = 1 << 1 | 1 << 3 | 1 << 4,\n\tUserReadWriteExecute = 1 << 1 | 1 << 2 | 1 << 3 | 1 << 4,\n}\n\n// Helper functions to convert the enumeration\n// into an i64, which is what our page table\n// entries will be.\nimpl EntryBits {\n\tpub fn val(self) -> i64 {\n\t\tself as i64\n\t}\n}\n\n// A single entry. We're using an i64 so that\n// this will sign-extend rather than zero-extend\n// since RISC-V requires that the reserved sections\n// take on the most significant bit.\npub struct Entry {\n\tpub entry: i64,\n}\n\n// The Entry structure describes one of the 512 entries per table, which is\n// described in the RISC-V privileged spec Figure 4.18.\nimpl Entry {\n\tpub fn is_valid(&self) -> bool {\n\t\tself.get_entry() & EntryBits::Valid.val() != 0\n\t}\n\n\t// The first bit (bit index #0) is the V bit for\n\t// valid.\n\tpub fn is_invalid(&self) -> bool {\n\t\t!self.is_valid()\n\t}\n\n\t// A leaf has one or more RWX bits set\n\tpub fn is_leaf(&self) -> bool {\n\t\tself.get_entry() & 0xe != 0\n\t}\n\n\tpub fn is_branch(&self) -> bool {\n\t\t!self.is_leaf()\n\t}\n\n\tpub fn set_entry(&mut self, entry: i64) {\n\t\tself.entry = entry;\n\t}\n\n\tpub fn get_entry(&self) -> i64 {\n\t\tself.entry\n\t}\n}\n\n// Table represents a single table, which contains 512 (2^9), 64-bit entries.\npub struct Table {\n\tpub entries: [Entry; 512],\n}\n\nimpl Table {\n\tpub fn len() -> usize {\n\t\t512\n\t}\n}\n\n/// Map a virtual address to a physical address using 4096-byte page\n/// size.\n/// root: a mutable reference to the root Table\n/// vaddr: The virtual address to map\n/// paddr: The physical address to map\n/// bits: An OR'd bitset containing the bits the leaf should have.\n///       The bits should contain only the following:\n///          Read, Write, Execute, User, and/or Global\n///       The bits MUST include one or more of the following:\n///          Read, Write, Execute\n///       The valid bit automatically gets added.\npub fn map(root: &mut Table,\n           vaddr: usize,\n           paddr: usize,\n           bits: i64,\n           level: usize)\n{\n\t// Make sure that Read, Write, or Execute have been provided\n\t// otherwise, we'll leak memory and always create a page fault.\n\tassert!(bits & 0xe != 0);\n\t// Extract out each VPN from the virtual address\n\t// On the virtual address, each VPN is exactly 9 bits,\n\t// which is why we use the mask 0x1ff = 0b1_1111_1111 (9 bits)\n\tlet vpn = [\n\t           // VPN[0] = vaddr[20:12]\n\t           (vaddr >> 12) & 0x1ff,\n\t           // VPN[1] = vaddr[29:21]\n\t           (vaddr >> 21) & 0x1ff,\n\t           // VPN[2] = vaddr[38:30]\n\t           (vaddr >> 30) & 0x1ff,\n\t];\n\n\t// Just like the virtual address, extract the physical address\n\t// numbers (PPN). However, PPN[2] is different in that it stores\n\t// 26 bits instead of 9. Therefore, we use,\n\t// 0x3ff_ffff = 0b11_1111_1111_1111_1111_1111_1111 (26 bits).\n\tlet ppn = [\n\t           // PPN[0] = paddr[20:12]\n\t           (paddr >> 12) & 0x1ff,\n\t           // PPN[1] = paddr[29:21]\n\t           (paddr >> 21) & 0x1ff,\n\t           // PPN[2] = paddr[55:30]\n\t           (paddr >> 30) & 0x3ff_ffff,\n\t];\n\t// We will use this as a floating reference so that we can set\n\t// individual entries as we walk the table.\n\tlet mut v = &mut root.entries[vpn[2]];\n\t// Now, we're going to traverse the page table and set the bits\n\t// properly. We expect the root to be valid, however we're required to\n\t// create anything beyond the root.\n\t// In Rust, we create a range iterator using the .. operator.\n\t// The .rev() will reverse the iteration since we need to start with\n\t// VPN[2] The .. operator is inclusive on start but exclusive on end.\n\t// So, (0..2) will iterate 0 and 1.\n\tfor i in (level..2).rev() {\n\t\tif !v.is_valid() {\n\t\t\t// Allocate a page\n\t\t\tlet page = zalloc(1);\n\t\t\t// The page is already aligned by 4,096, so store it\n\t\t\t// directly The page is stored in the entry shifted\n\t\t\t// right by 2 places.\n\t\t\tv.set_entry(\n\t\t\t            (page as i64 >> 2)\n\t\t\t            | EntryBits::Valid.val(),\n\t\t\t);\n\t\t}\n\t\tlet entry = ((v.get_entry() & !0x3ff) << 2) as *mut Entry;\n\t\tv = unsafe { entry.add(vpn[i]).as_mut().unwrap() };\n\t}\n\t// When we get here, we should be at VPN[0] and v should be pointing to\n\t// our entry.\n\t// The entry structure is Figure 4.18 in the RISC-V Privileged\n\t// Specification\n\tlet entry = (ppn[2] << 28) as i64 |   // PPN[2] = [53:28]\n\t            (ppn[1] << 19) as i64 |   // PPN[1] = [27:19]\n\t\t\t\t(ppn[0] << 10) as i64 |   // PPN[0] = [18:10]\n\t\t\t\tbits |                    // Specified bits, such as User, Read, Write, etc\n\t\t\t\tEntryBits::Valid.val() |  // Valid bit\n\t\t\t\tEntryBits::Dirty.val() |  // Some machines require this to =1\n\t\t\t\tEntryBits::Access.val()   // Just like dirty, some machines require this\n\t\t\t\t;\n\t// Set the entry. V should be set to the correct pointer by the loop\n\t// above.\n\tv.set_entry(entry);\n}\n\n/// Unmaps and frees all memory associated with a table.\n/// root: The root table to start freeing.\n/// NOTE: This does NOT free root directly. This must be\n/// freed manually.\n/// The reason we don't free the root is because it is\n/// usually embedded into the Process structure.\npub fn unmap(root: &mut Table) {\n\t// Start with level 2\n\tfor lv2 in 0..Table::len() {\n\t\tlet ref entry_lv2 = root.entries[lv2];\n\t\tif entry_lv2.is_valid() && entry_lv2.is_branch() {\n\t\t\t// This is a valid entry, so drill down and free.\n\t\t\tlet memaddr_lv1 = (entry_lv2.get_entry() & !0x3ff) << 2;\n\t\t\tlet table_lv1 = unsafe {\n\t\t\t\t// Make table_lv1 a mutable reference instead of\n\t\t\t\t// a pointer.\n\t\t\t\t(memaddr_lv1 as *mut Table).as_mut().unwrap()\n\t\t\t};\n\t\t\tfor lv1 in 0..Table::len() {\n\t\t\t\tlet ref entry_lv1 = table_lv1.entries[lv1];\n\t\t\t\tif entry_lv1.is_valid() && entry_lv1.is_branch()\n\t\t\t\t{\n\t\t\t\t\tlet memaddr_lv0 = (entry_lv1.get_entry()\n\t\t\t\t\t                   & !0x3ff) << 2;\n\t\t\t\t\t// The next level is level 0, which\n\t\t\t\t\t// cannot have branches, therefore,\n\t\t\t\t\t// we free here.\n\t\t\t\t\tdealloc(memaddr_lv0 as *mut u8);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdealloc(memaddr_lv1 as *mut u8);\n\t\t}\n\t}\n}\n\n/// Walk the page table to convert a virtual address to a\n/// physical address.\n/// If a page fault would occur, this returns None\n/// Otherwise, it returns Some with the physical address.\npub fn virt_to_phys(root: &Table, vaddr: usize) -> Option<usize> {\n\t// Walk the page table pointed to by root\n\tlet vpn = [\n\t           // VPN[0] = vaddr[20:12]\n\t           (vaddr >> 12) & 0x1ff,\n\t           // VPN[1] = vaddr[29:21]\n\t           (vaddr >> 21) & 0x1ff,\n\t           // VPN[2] = vaddr[38:30]\n\t           (vaddr >> 30) & 0x1ff,\n\t];\n\n\tlet mut v = &root.entries[vpn[2]];\n\tfor i in (0..=2).rev() {\n\t\tif v.is_invalid() {\n\t\t\t// This is an invalid entry, page fault.\n\t\t\tbreak;\n\t\t}\n\t\telse if v.is_leaf() {\n\t\t\t// According to RISC-V, a leaf can be at any level.\n\n\t\t\t// The offset mask masks off the PPN. Each PPN is 9\n\t\t\t// bits and they start at bit #12. So, our formula\n\t\t\t// 12 + i * 9\n\t\t\tlet off_mask = (1 << (12 + i * 9)) - 1;\n\t\t\tlet vaddr_pgoff = vaddr & off_mask;\n\t\t\tlet addr = ((v.get_entry() << 2) as usize) & !off_mask;\n\t\t\treturn Some(addr | vaddr_pgoff);\n\t\t}\n\t\t// Set v to the next entry which is pointed to by this\n\t\t// entry. However, the address was shifted right by 2 places\n\t\t// when stored in the page table entry, so we shift it left\n\t\t// to get it back into place.\n\t\tlet entry = ((v.get_entry() & !0x3ff) << 2) as *const Entry;\n\t\t// We do i - 1 here, however we should get None or Some() above\n\t\t// before we do 0 - 1 = -1.\n\t\tv = unsafe { entry.add(vpn[i - 1]).as_ref().unwrap() };\n\t}\n\n\t// If we get here, we've exhausted all valid tables and haven't\n\t// found a leaf.\n\tNone\n}\n"
  },
  {
    "path": "risc_v/chapters/ch8/src/plic.rs",
    "content": "// plic.rs\n// Platform Level Interrupt Controller (PLIC)\n// Stephen Marz\n// 1 Nov 2019\n\nconst PLIC_PRIORITY: usize = 0x0c00_0000;\nconst PLIC_PENDING: usize = 0x0c00_1000;\nconst PLIC_INT_ENABLE: usize = 0x0c00_2000;\nconst PLIC_THRESHOLD: usize = 0x0c20_0000;\nconst PLIC_CLAIM: usize = 0x0c20_0004;\n\n// Each register is 4-bytes (u32)\n// The PLIC is an external interrupt controller. The one\n// used by QEMU virt is the same as the SiFive PLIC.\n// https://sifive.cdn.prismic.io/sifive%2F834354f0-08e6-423c-bf1f-0cb58ef14061_fu540-c000-v1.0.pdf\n\n// Chapter 10 explains the priority, pending, interrupt enable, threshold and claims\n\n// The virt machine has the following external interrupts (from Qemu source):\n// Interrupt 0 is a \"null\" interrupt and is hardwired to 0.\n// VIRTIO = [1..8]\n// UART0 = 10\n// PCIE = [32..35]\n\n\n/// Get the next available interrupt. This is the \"claim\" process.\n/// The plic will automatically sort by priority and hand us the\n/// ID of the interrupt. For example, if the UART is interrupting\n/// and it's next, we will get the value 10.\npub fn next() -> Option<u32> {\n    let claim_reg = PLIC_CLAIM as *const u32;\n    let claim_no;\n    // The claim register is filled with the highest-priority, enabled interrupt.\n    unsafe {\n        claim_no = claim_reg.read_volatile();\n    }\n    if claim_no == 0 {\n        // The interrupt 0 is hardwired to 0, which tells us that there is no\n        // interrupt to claim, hence we return None.\n        None\n    }\n    else {\n        // If we get here, we've gotten a non-0 interrupt.\n        Some(claim_no)\n    }\n}\n\n/// Complete a pending interrupt by id. The id should come\n/// from the next() function above.\npub fn complete(id: u32) {\n    let complete_reg = PLIC_CLAIM as *mut u32;\n    unsafe {\n        // We actually write a u32 into the entire complete_register.\n        // This is the same register as the claim register, but it can\n        // differentiate based on whether we're reading or writing.\n        complete_reg.write_volatile(id);\n    }\n}\n\n/// Set the global threshold. The threshold can be a value [0..7].\n/// The PLIC will mask any interrupts at or below the given threshold.\n/// This means that a threshold of 7 will mask ALL interrupts and\n/// a threshold of 0 will allow ALL interrupts.\npub fn set_threshold(tsh: u8) {\n    // We do tsh because we're using a u8, but our maximum number\n    // is a 3-bit 0b111. So, we and with 7 (0b111) to just get the\n    // last three bits.\n    let actual_tsh = tsh & 7;\n    let tsh_reg = PLIC_THRESHOLD as *mut u32;\n    unsafe {\n        tsh_reg.write_volatile(actual_tsh as u32);\n    }\n}\n\n/// See if a given interrupt id is pending.\npub fn is_pending(id: u32) -> bool {\n    let pend = PLIC_PENDING as *const u32;\n    let actual_id = 1 << id;\n    let pend_ids;\n    unsafe {\n        pend_ids = pend.read_volatile();\n    }\n    actual_id & pend_ids != 0\n}\n\n/// Enable a given interrupt id\npub fn enable(id: u32) {\n    let enables = PLIC_INT_ENABLE as *mut u32;\n    let actual_id = 1 << id;\n    unsafe {\n        // Unlike the complete and claim registers, the plic_int_enable\n        // register is a bitset where the id is the bit index. The register\n        // is a 32-bit register, so that gives us enables for interrupts\n        // 31 through 1 (0 is hardwired to 0).\n        enables.write_volatile(enables.read_volatile() | actual_id);\n    }\n}\n\n/// Set a given interrupt priority to the given priority.\n/// The priority must be [0..7]\npub fn set_priority(id: u32, prio: u8) {\n    let actual_prio = prio as u32 & 7;\n    let prio_reg = PLIC_PRIORITY as *mut u32;\n    unsafe {\n        // The offset for the interrupt id is:\n        // PLIC_PRIORITY + 4 * id\n        // Since we're using pointer arithmetic on a u32 type,\n        // it will automatically multiply the id by 4.\n        prio_reg.add(id as usize).write_volatile(actual_prio);\n    }\n}\n\n"
  },
  {
    "path": "risc_v/chapters/ch8/src/process.rs",
    "content": "// process.rs\n// Kernel and user processes\n// Stephen Marz\n// 27 Nov 2019\n\nuse crate::{cpu::TrapFrame,\n            page::{alloc,\n                   dealloc,\n                   map,\n                   unmap,\n                   zalloc,\n                   EntryBits,\n                   Table,\n                   PAGE_SIZE}};\nuse alloc::collections::vec_deque::VecDeque;\n\n// How many pages are we going to give a process for their\n// stack?\nconst STACK_PAGES: usize = 2;\n// We want to adjust the stack to be at the bottom of the memory allocation\n// regardless of where it is on the kernel heap.\nconst STACK_ADDR: usize = 0x1_0000_0000;\n// All processes will have a defined starting point in virtual memory.\nconst PROCESS_STARTING_ADDR: usize = 0x8000_0000;\n\n// Here, we store a process list. It uses the global allocator\n// that we made before and its job is to store all processes.\n// We will have this list OWN the process. So, anytime we want\n// the process, we will consult the process list.\n// Using an Option here is one method of creating a \"lazy static\".\n// Rust requires that all statics be initialized, but all\n// initializations must be at compile-time. We cannot allocate\n// a VecDeque at compile time, so we are somewhat forced to\n// do this.\npub static mut PROCESS_LIST: Option<VecDeque<Process>> = None;\n// We can search through the process list to get a new PID, but\n// it's probably easier and faster just to increase the pid:\nstatic mut NEXT_PID: u16 = 1;\n\nextern \"C\" {\n\tfn make_syscall(a: usize) -> usize;\n}\n\n/// We will eventually move this function out of here, but its\n/// job is just to take a slot in the process list.\nfn init_process() {\n\t// We can't do much here until we have system calls because\n\t// we're running in User space.\n\tlet mut i: usize = 0;\n\tloop {\n\t\ti += 1;\n\t\tif i > 70_000_000 {\n\t\t\tunsafe {\n\t\t\t\tmake_syscall(1);\n\t\t\t}\n\t\t\ti = 0;\n\t\t}\n\t}\n}\n\n/// Add a process given a function address and then\n/// push it onto the LinkedList. Uses Process::new_default\n/// to create a new stack, etc.\npub fn add_process_default(pr: fn()) {\n\tunsafe {\n\t\t// This is the Rust-ism that really trips up C++ programmers.\n\t\t// PROCESS_LIST is wrapped in an Option<> enumeration, which\n\t\t// means that the Option owns the Deque. We can only borrow from\n\t\t// it or move ownership to us. In this case, we choose the\n\t\t// latter, where we move ownership to us, add a process, and\n\t\t// then move ownership back to the PROCESS_LIST.\n\t\t// This allows mutual exclusion as anyone else trying to grab\n\t\t// the process list will get None rather than the Deque.\n\t\tif let Some(mut pl) = PROCESS_LIST.take() {\n\t\t\t// .take() will replace PROCESS_LIST with None and give\n\t\t\t// us the only copy of the Deque.\n\t\t\tlet p = Process::new_default(pr);\n\t\t\tpl.push_back(p);\n\t\t\t// Now, we no longer need the owned Deque, so we hand it\n\t\t\t// back by replacing the PROCESS_LIST's None with the\n\t\t\t// Some(pl).\n\t\t\tPROCESS_LIST.replace(pl);\n\t\t}\n\t\t// TODO: When we get to multi-hart processing, we need to keep\n\t\t// trying to grab the process list. We can do this with an\n\t\t// atomic instruction. but right now, we're a single-processor\n\t\t// computer.\n\t}\n}\n\n/// This should only be called once, and its job is to create\n/// the init process. Right now, this process is in the kernel,\n/// but later, it should call the shell.\npub fn init() -> usize {\n\tunsafe {\n\t\tPROCESS_LIST = Some(VecDeque::with_capacity(15));\n\t\tadd_process_default(init_process);\n\t\t// Ugh....Rust is giving me fits over here!\n\t\t// I just want a memory address to the trap frame, but\n\t\t// due to the borrow rules of Rust, I'm fighting here. So,\n\t\t// instead, let's move the value out of PROCESS_LIST, get\n\t\t// the address, and then move it right back in.\n\t\tlet pl = PROCESS_LIST.take().unwrap();\n\t\tlet p = pl.front().unwrap().frame;\n\t\tlet func_vaddr = pl.front().unwrap().program_counter;\n\t\tlet frame = p as *const TrapFrame as usize;\n\t\tprintln!(\"Init's frame is at 0x{:08x}\", frame);\n\t\t// Put the process list back in the global.\n\t\tPROCESS_LIST.replace(pl);\n\t\t// Return the first instruction's address to execute.\n\t\t// Since we use the MMU, all start here.\n\t\tfunc_vaddr\n\t}\n}\n\n// Our process must be able to sleep, wait, or run.\n// Running - means that when the scheduler finds this process, it can run it.\n// Sleeping - means that the process is waiting on a certain amount of time.\n// Waiting - means that the process is waiting on I/O\n// Dead - We should never get here, but we can flag a process as Dead and clean\n//        it out of the list later.\n#[repr(u8)]\npub enum ProcessState {\n\tRunning,\n\tSleeping,\n\tWaiting,\n\tDead,\n}\n\n// Let's represent this in C ABI. We do this\n// because we need to access some of the fields\n// in assembly. Rust gets to choose how it orders\n// the fields unless we represent the structure in\n// C-style ABI.\n#[repr(C)]\npub struct Process {\n\tframe:           *mut TrapFrame,\n\tstack:           *mut u8,\n\tprogram_counter: usize,\n\tpid:             u16,\n\troot:            *mut Table,\n\tstate:           ProcessState,\n\tdata:            ProcessData,\n\tsleep_until:\t usize,\n}\n\nimpl Process {\n\tpub fn get_frame_address(&self) -> usize {\n\t\tself.frame as usize\n\t}\n\tpub fn get_program_counter(&self) -> usize {\n\t\tself.program_counter\n\t}\n\tpub fn get_table_address(&self) -> usize {\n\t\tself.root as usize\n\t}\n\tpub fn get_state(&self) -> &ProcessState {\n\t\t&self.state\n\t}\n\tpub fn get_pid(&self) -> u16 {\n\t\tself.pid\n\t}\n\tpub fn get_sleep_until(&self) -> usize {\n\t\tself.sleep_until\n\t}\n\tpub fn new_default(func: fn()) -> Self {\n\t\tlet func_addr = func as usize;\n\t\tlet func_vaddr = func_addr; //- 0x6000_0000;\n\t\t// println!(\"func_addr = {:x} -> {:x}\", func_addr, func_vaddr);\n\t\t// We will convert NEXT_PID below into an atomic increment when\n\t\t// we start getting into multi-hart processing. For now, we want\n\t\t// a process. Get it to work, then improve it!\n\t\tlet mut ret_proc =\n\t\t\tProcess { frame:           zalloc(1) as *mut TrapFrame,\n\t\t\t          stack:           alloc(STACK_PAGES),\n\t\t\t          program_counter: func_vaddr,\n\t\t\t          pid:             unsafe { NEXT_PID },\n\t\t\t          root:            zalloc(1) as *mut Table,\n\t\t\t          state:           ProcessState::Running,\n\t\t\t\t\t  data:            ProcessData::zero(), \n\t\t\t\t\t  sleep_until:     0\n\t\t\t\t\t};\n\t\tunsafe {\n\t\t\tNEXT_PID += 1;\n\t\t}\n\t\t// Now we move the stack pointer to the bottom of the\n\t\t// allocation. The spec shows that register x2 (2) is the stack\n\t\t// pointer.\n\t\t// We could use ret_proc.stack.add, but that's an unsafe\n\t\t// function which would require an unsafe block. So, convert it\n\t\t// to usize first and then add PAGE_SIZE is better.\n\t\t// We also need to set the stack adjustment so that it is at the\n\t\t// bottom of the memory and far away from heap allocations.\n\t\tlet saddr = ret_proc.stack as usize;\n\t\tunsafe {\n\t\t\t(*ret_proc.frame).regs[2] = STACK_ADDR + PAGE_SIZE * STACK_PAGES;\n\t\t}\n\t\t// Map the stack on the MMU\n\t\tlet pt;\n\t\tunsafe {\n\t\t\tpt = &mut *ret_proc.root;\n\t\t}\n\t\t// We need to map the stack onto the user process' virtual\n\t\t// memory This gets a little hairy because we need to also map\n\t\t// the function code too.\n\t\tfor i in 0..STACK_PAGES {\n\t\t\tlet addr = i * PAGE_SIZE;\n\t\t\tmap(\n\t\t\t    pt,\n\t\t\t    STACK_ADDR + addr,\n\t\t\t    saddr + addr,\n\t\t\t    EntryBits::UserReadWrite.val(),\n\t\t\t    0,\n\t\t\t);\n\t\t\tprintln!(\"Set stack from 0x{:016x} -> 0x{:016x}\", STACK_ADDR + addr, saddr + addr);\n\t\t}\n\t\t// Map the program counter on the MMU and other bits\n\t\tfor i in 0..=100 {\n\t\t\tlet modifier = i * 0x1000;\n\t\t\tmap(\n\t\t\t\tpt,\n\t\t\t\tfunc_vaddr + modifier,\n\t\t\t\tfunc_addr + modifier,\n\t\t\t\tEntryBits::UserReadWriteExecute.val(),\n\t\t\t\t0,\n\t\t\t);\n\t\t}\n\t\t// This is the make_syscall function\n\t\t// The reason we need this is because we're running a process\n\t\t// that is inside of the kernel. When we start loading from a block\n\t\t// devices, we can load the instructions anywhere in memory. \n\t\tmap(pt, 0x8000_0000, 0x8000_0000, EntryBits::UserReadExecute.val(), 0);\n\t\tret_proc\n\t}\n}\n\nimpl Drop for Process {\n\t/// Since we're storing ownership of a Process in the linked list,\n\t/// we can cause it to deallocate automatically when it is removed.\n\tfn drop(&mut self) {\n\t\t// We allocate the stack as a page.\n\t\tdealloc(self.stack);\n\t\t// This is unsafe, but it's at the drop stage, so we won't\n\t\t// be using this again.\n\t\tunsafe {\n\t\t\t// Remember that unmap unmaps all levels of page tables\n\t\t\t// except for the root. It also deallocates the memory\n\t\t\t// associated with the tables.\n\t\t\tunmap(&mut *self.root);\n\t\t}\n\t\tdealloc(self.root as *mut u8);\n\t}\n}\n\n// The private data in a process contains information\n// that is relevant to where we are, including the path\n// and open file descriptors.\npub struct ProcessData {\n\tcwd_path: [u8; 128],\n}\n\n// This is private data that we can query with system calls.\n// If we want to implement CFQ (completely fair queuing), which\n// is a per-process block queuing algorithm, we can put that here.\nimpl ProcessData {\n\tpub fn zero() -> Self {\n\t\tProcessData { cwd_path: [0; 128], }\n\t}\n}\n"
  },
  {
    "path": "risc_v/chapters/ch8/src/sched.rs",
    "content": "// sched.rs\n// Simple process scheduler\n// Stephen Marz\n// 27 Dec 2019\n\nuse crate::{process::{ProcessState, PROCESS_LIST}};\n\npub fn schedule() ->  (usize, usize, usize) {\n\tunsafe {\n\t\tif let Some(mut pl) = PROCESS_LIST.take() {\n\t\t\tpl.rotate_left(1);\n\t\t\tlet mut frame_addr: usize = 0;\n\t\t\tlet mut mepc: usize = 0;\n\t\t\tlet mut satp: usize = 0;\n\t\t\tlet mut pid: usize = 0;\n\t\t\tif let Some(prc) = pl.front() {\n\t\t\t\tmatch prc.get_state() {\n\t\t\t\t\tProcessState::Running => {\n\t\t\t\t\t\tframe_addr =\n\t\t\t\t\t\t\tprc.get_frame_address();\n\t\t\t\t\t\tmepc = prc.get_program_counter();\n\t\t\t\t\t\tsatp = prc.get_table_address() >> 12;\n\t\t\t\t\t\tpid = prc.get_pid() as usize;\n\t\t\t\t\t},\n\t\t\t\t\tProcessState::Sleeping => {\n\t\t\t\t\t\t\n\t\t\t\t\t},\n\t\t\t\t\t_ => {},\n\t\t\t\t}\n\t\t\t}\n\t\t\tprintln!(\"Scheduling {}\", pid);\n\t\t\tPROCESS_LIST.replace(pl);\n\t\t\tif frame_addr != 0 {\n\t\t\t\t// MODE 8 is 39-bit virtual address MMU\n\t\t\t\t// I'm using the PID as the address space identifier to hopefully\n\t\t\t\t// help with (not?) flushing the TLB whenever we switch processes.\n\t\t\t\tif satp != 0 {\n\t\t\t\t\treturn (frame_addr, mepc, (8 << 60) | (pid << 44) | satp);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\treturn (frame_addr, mepc, 0);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t(0, 0, 0)\n}\n"
  },
  {
    "path": "risc_v/chapters/ch8/src/syscall.rs",
    "content": "// syscall.rs\n// System calls\n// Stephen Marz\n// 3 Jan 2020\n\nuse crate::cpu::TrapFrame;\n\npub fn do_syscall(mepc: usize, frame: *mut TrapFrame) -> usize {\n    let syscall_number;\n    unsafe {\n        // A0 is X10, so it's register number 10.\n        syscall_number = (*frame).regs[10];\n        // for i in 0..32 {\n        //     print!(\"regs[{:02}] = 0x{:08x}    \", i, (*frame).regs[i]);\n        //     if (i+1) % 4 == 0 {\n        //         println!();\n        //     }\n        // }    \n    }\n    match syscall_number {\n        0 => {\n            // Exit\n            // Currently, we cannot kill a process, it runs forever. We will delete\n            // the process later and free the resources, but for now, we want to get\n            // used to how processes will be scheduled on the CPU.\n            mepc + 4\n        },\n        1 => {\n            println!(\"Test syscall\");\n            mepc + 4\n        },\n        _ => {\n            println!(\"Unknown syscall number {}\", syscall_number);\n            mepc + 4\n        }\n    }\n}"
  },
  {
    "path": "risc_v/chapters/ch8/src/trap.rs",
    "content": "// trap.rs\n// Trap routines\n// Stephen Marz\n// 10 October 2019\n\nuse crate::cpu::TrapFrame;\nuse crate::{plic, uart};\nuse crate::syscall::do_syscall;\nuse crate::sched::schedule;\n\nextern \"C\" {\n\tfn switch_to_user(frame: usize, mepc: usize, satp: usize) -> !;\n}\n\n#[no_mangle]\n/// The m_trap stands for \"machine trap\". Right now, we are handling\n/// all traps at machine mode. In this mode, we can figure out what's\n/// going on and send a trap where it needs to be. Remember, in machine\n/// mode and in this trap, interrupts are disabled and the MMU is off.\nextern \"C\" fn m_trap(epc: usize,\n                     tval: usize,\n                     cause: usize,\n                     hart: usize,\n                     status: usize,\n                     frame: *mut TrapFrame)\n                     -> usize\n{\n\t// We're going to handle all traps in machine mode. RISC-V lets\n\t// us delegate to supervisor mode, but switching out SATP (virtual memory)\n\t// gets hairy.\n\tlet is_async = {\n\t\tif cause >> 63 & 1 == 1 {\n\t\t\ttrue\n\t\t}\n\t\telse {\n\t\t\tfalse\n\t\t}\n\t};\n\t// The cause contains the type of trap (sync, async) as well as the cause\n\t// number. So, here we narrow down just the cause number.\n\tlet cause_num = cause & 0xfff;\n\tlet mut return_pc = epc;\n\tif is_async {\n\t\t// Asynchronous trap\n\t\tmatch cause_num {\n\t\t\t3 => {\n\t\t\t\t// Machine software\n\t\t\t\tprintln!(\"Machine software interrupt CPU#{}\", hart);\n\t\t\t},\n\t\t\t7 => unsafe {\n\t\t\t\t// This is the context-switch timer.\n\t\t\t\t// We would typically invoke the scheduler here to pick another\n\t\t\t\t// process to run.\n\t\t\t\t// Machine timer\n\t\t\t\t// println!(\"CTX\");\n\t\t\t\tlet (frame, mepc, satp) = schedule();\n\t\t\t\tlet mtimecmp = 0x0200_4000 as *mut u64;\n\t\t\t\tlet mtime = 0x0200_bff8 as *const u64;\n\t\t\t\t// The frequency given by QEMU is 10_000_000 Hz, so this sets\n\t\t\t\t// the next interrupt to fire one second from now.\n\t\t\t\t// This is much too slow for normal operations, but it gives us\n\t\t\t\t// a visual of what's happening behind the scenes.\n\t\t\t\tmtimecmp.write_volatile(mtime.read_volatile() + 10_000_000);\n\t\t\t\tunsafe {\n\t\t\t\t\tswitch_to_user(frame, mepc, satp);\n\t\t\t\t}\n\t\t\t},\n\t\t\t11 => {\n\t\t\t\t// Machine external (interrupt from Platform Interrupt Controller (PLIC))\n\t\t\t\t// println!(\"Machine external interrupt CPU#{}\", hart);\n\t\t\t\t// We will check the next interrupt. If the interrupt isn't available, this will\n\t\t\t\t// give us None. However, that would mean we got a spurious interrupt, unless we\n\t\t\t\t// get an interrupt from a non-PLIC source. This is the main reason that the PLIC\n\t\t\t\t// hardwires the id 0 to 0, so that we can use it as an error case.\n\t\t\t\tif let Some(interrupt) = plic::next() {\n\t\t\t\t\t// If we get here, we've got an interrupt from the claim register. The PLIC will\n\t\t\t\t\t// automatically prioritize the next interrupt, so when we get it from claim, it\n\t\t\t\t\t// will be the next in priority order.\n\t\t\t\t\tmatch interrupt {\n\t\t\t\t\t\t10 => { // Interrupt 10 is the UART interrupt.\n\t\t\t\t\t\t\t// We would typically set this to be handled out of the interrupt context,\n\t\t\t\t\t\t\t// but we're testing here! C'mon!\n\t\t\t\t\t\t\t// We haven't yet used the singleton pattern for my_uart, but remember, this\n\t\t\t\t\t\t\t// just simply wraps 0x1000_0000 (UART).\n\t\t\t\t\t\t\tlet mut my_uart = uart::Uart::new(0x1000_0000);\n\t\t\t\t\t\t\t// If we get here, the UART better have something! If not, what happened??\n\t\t\t\t\t\t\tif let Some(c) = my_uart.get() {\n\t\t\t\t\t\t\t\t// If you recognize this code, it used to be in the lib.rs under kmain(). That\n\t\t\t\t\t\t\t\t// was because we needed to poll for UART data. Now that we have interrupts,\n\t\t\t\t\t\t\t\t// here it goes!\n\t\t\t\t\t\t\t\tmatch c {\n\t\t\t\t\t\t\t\t\t8 => {\n\t\t\t\t\t\t\t\t\t\t// This is a backspace, so we\n\t\t\t\t\t\t\t\t\t\t// essentially have to write a space and\n\t\t\t\t\t\t\t\t\t\t// backup again:\n\t\t\t\t\t\t\t\t\t\tprint!(\"{} {}\", 8 as char, 8 as char);\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t10 | 13 => {\n\t\t\t\t\t\t\t\t\t\t// Newline or carriage-return\n\t\t\t\t\t\t\t\t\t\tprintln!();\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t\t_ => {\n\t\t\t\t\t\t\t\t\t\tprint!(\"{}\", c as char);\n\t\t\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t\t\t},\n\t\t\t\t\t\t// Non-UART interrupts go here and do nothing.\n\t\t\t\t\t\t_ => {\n\t\t\t\t\t\t\tprintln!(\"Non-UART external interrupt: {}\", interrupt);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t// We've claimed it, so now say that we've handled it. This resets the interrupt pending\n\t\t\t\t\t// and allows the UART to interrupt again. Otherwise, the UART will get \"stuck\".\n\t\t\t\t\tplic::complete(interrupt);\n\t\t\t\t}\n\t\t\t},\n\t\t\t_ => {\n\t\t\t\tpanic!(\"Unhandled async trap CPU#{} -> {}\\n\", hart, cause_num);\n\t\t\t}\n\t\t}\n\t}\n\telse {\n\t\t// Synchronous trap\n\t\tmatch cause_num {\n\t\t\t2 => {\n\t\t\t\t// Illegal instruction\n\t\t\t\tpanic!(\"Illegal instruction CPU#{} -> 0x{:08x}: 0x{:08x}\\n\", hart, epc, tval);\n\t\t\t\t// We need while trues here until we have a functioning \"delete from scheduler\"\n\t\t\t\twhile true {}\n\t\t\t},\n\t\t\t8 => {\n\t\t\t\t// Environment (system) call from User mode\n\t\t\t\t// println!(\"E-call from User mode! CPU#{} -> 0x{:08x}\", hart, epc);\n\t\t\t\treturn_pc = do_syscall(return_pc, frame);\n\t\t\t},\n\t\t\t9 => {\n\t\t\t\t// Environment (system) call from Supervisor mode\n\t\t\t\tprintln!(\"E-call from Supervisor mode! CPU#{} -> 0x{:08x}\", hart, epc);\n\t\t\t\treturn_pc = do_syscall(return_pc, frame);\n\t\t\t},\n\t\t\t11 => {\n\t\t\t\t// Environment (system) call from Machine mode\n\t\t\t\tpanic!(\"E-call from Machine mode! CPU#{} -> 0x{:08x}\\n\", hart, epc);\n\t\t\t},\n\t\t\t// Page faults\n\t\t\t12 => {\n\t\t\t\t// Instruction page fault\n\t\t\t\tprintln!(\"Instruction page fault CPU#{} -> 0x{:08x}: 0x{:08x}\", hart, epc, tval);\n\t\t\t\t// We need while trues here until we have a functioning \"delete from scheduler\"\n\t\t\t\twhile true {}\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t13 => {\n\t\t\t\t// Load page fault\n\t\t\t\tprintln!(\"Load page fault CPU#{} -> 0x{:08x}: 0x{:08x}\", hart, epc, tval);\n\t\t\t\t// We need while trues here until we have a functioning \"delete from scheduler\"\n\t\t\t\twhile true {}\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t15 => {\n\t\t\t\t// Store page fault\n\t\t\t\tprintln!(\"Store page fault CPU#{} -> 0x{:08x}: 0x{:08x}\", hart, epc, tval);\n\t\t\t\t// We need while trues here until we have a functioning \"delete from scheduler\"\n\t\t\t\twhile true {}\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t_ => {\n\t\t\t\tpanic!(\"Unhandled sync trap CPU#{} -> {}\\n\", hart, cause_num);\n\t\t\t}\n\t\t}\n\t};\n\t// Finally, return the updated program counter\n\treturn_pc\n}\n"
  },
  {
    "path": "risc_v/chapters/ch8/src/uart.rs",
    "content": "// uart.rs\n// UART routines and driver\n\nuse core::{convert::TryInto,\n           fmt::{Error, Write}};\n\npub struct Uart {\n\tbase_address: usize,\n}\n\nimpl Write for Uart {\n\tfn write_str(&mut self, out: &str) -> Result<(), Error> {\n\t\tfor c in out.bytes() {\n\t\t\tself.put(c);\n\t\t}\n\t\tOk(())\n\t}\n}\n\nimpl Uart {\n\tpub fn new(base_address: usize) -> Self {\n\t\tUart { base_address }\n\t}\n\n\tpub fn init(&mut self) {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\t// First, set the word length, which\n\t\t\t// are bits 0 and 1 of the line control register (LCR)\n\t\t\t// which is at base_address + 3\n\t\t\t// We can easily write the value 3 here or 0b11, but I'm\n\t\t\t// extending it so that it is clear we're setting two\n\t\t\t// individual fields\n\t\t\t//             Word 0     Word 1\n\t\t\t//             ~~~~~~     ~~~~~~\n\t\t\tlet lcr: u8 = (1 << 0) | (1 << 1);\n\t\t\tptr.add(3).write_volatile(lcr);\n\n\t\t\t// Now, enable the FIFO, which is bit index 0 of the\n\t\t\t// FIFO control register (FCR at offset 2).\n\t\t\t// Again, we can just write 1 here, but when we use left\n\t\t\t// shift, it's easier to see that we're trying to write\n\t\t\t// bit index #0.\n\t\t\tptr.add(2).write_volatile(1 << 0);\n\n\t\t\t// Enable receiver buffer interrupts, which is at bit\n\t\t\t// index 0 of the interrupt enable register (IER at\n\t\t\t// offset 1).\n\t\t\tptr.add(1).write_volatile(1 << 0);\n\n\t\t\t// If we cared about the divisor, the code below would\n\t\t\t// set the divisor from a global clock rate of 22.729\n\t\t\t// MHz (22,729,000 cycles per second) to a signaling\n\t\t\t// rate of 2400 (BAUD). We usually have much faster\n\t\t\t// signalling rates nowadays, but this demonstrates what\n\t\t\t// the divisor actually does. The formula given in the\n\t\t\t// NS16500A specification for calculating the divisor\n\t\t\t// is:\n\t\t\t// divisor = ceil( (clock_hz) / (baud_sps x 16) )\n\t\t\t// So, we substitute our values and get:\n\t\t\t// divisor = ceil( 22_729_000 / (2400 x 16) )\n\t\t\t// divisor = ceil( 22_729_000 / 38_400 )\n\t\t\t// divisor = ceil( 591.901 ) = 592\n\n\t\t\t// The divisor register is two bytes (16 bits), so we\n\t\t\t// need to split the value 592 into two bytes.\n\t\t\t// Typically, we would calculate this based on measuring\n\t\t\t// the clock rate, but again, for our purposes [qemu],\n\t\t\t// this doesn't really do anything.\n\t\t\tlet divisor: u16 = 592;\n\t\t\tlet divisor_least: u8 =\n\t\t\t\t(divisor & 0xff).try_into().unwrap();\n\t\t\tlet divisor_most: u8 =\n\t\t\t\t(divisor >> 8).try_into().unwrap();\n\n\t\t\t// Notice that the divisor register DLL (divisor latch\n\t\t\t// least) and DLM (divisor latch most) have the same\n\t\t\t// base address as the receiver/transmitter and the\n\t\t\t// interrupt enable register. To change what the base\n\t\t\t// address points to, we open the \"divisor latch\" by\n\t\t\t// writing 1 into the Divisor Latch Access Bit (DLAB),\n\t\t\t// which is bit index 7 of the Line Control Register\n\t\t\t// (LCR) which is at base_address + 3.\n\t\t\tptr.add(3).write_volatile(lcr | 1 << 7);\n\n\t\t\t// Now, base addresses 0 and 1 point to DLL and DLM,\n\t\t\t// respectively. Put the lower 8 bits of the divisor\n\t\t\t// into DLL\n\t\t\tptr.add(0).write_volatile(divisor_least);\n\t\t\tptr.add(1).write_volatile(divisor_most);\n\n\t\t\t// Now that we've written the divisor, we never have to\n\t\t\t// touch this again. In hardware, this will divide the\n\t\t\t// global clock (22.729 MHz) into one suitable for 2,400\n\t\t\t// signals per second. So, to once again get access to\n\t\t\t// the RBR/THR/IER registers, we need to close the DLAB\n\t\t\t// bit by clearing it to 0.\n\t\t\tptr.add(3).write_volatile(lcr);\n\t\t}\n\t}\n\n\tpub fn put(&mut self, c: u8) {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\tptr.add(0).write_volatile(c);\n\t\t}\n\t}\n\n\tpub fn get(&mut self) -> Option<u8> {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\tif ptr.add(5).read_volatile() & 1 == 0 {\n\t\t\t\t// The DR bit is 0, meaning no data\n\t\t\t\tNone\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// The DR bit is 1, meaning data!\n\t\t\t\tSome(ptr.add(0).read_volatile())\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "risc_v/chapters/ch9/.cargo/config",
    "content": "[build]\ntarget = \"riscv64gc-unknown-none-elf\"\n\n[target.riscv64gc-unknown-none-elf]\nlinker = \"riscv64-unknown-linux-gnu-gcc\"\n"
  },
  {
    "path": "risc_v/chapters/ch9/.gitignore",
    "content": "os.elf\ntarget/*\nCargo.lock\nhdd.dsk\n"
  },
  {
    "path": "risc_v/chapters/ch9/Cargo.toml",
    "content": "[package]\nname = \"sos\"\nversion = \"0.1.0\"\nauthors = [\"Stephen Marz <stephen.marz@utk.edu>\"]\nedition = \"2018\"\n\n# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html\n\n[lib]\ncrate-type = [\"staticlib\"]\n\n[dependencies]\n"
  },
  {
    "path": "risc_v/chapters/ch9/Makefile",
    "content": "#####\n## BUILD\n#####\nCC=riscv64-unknown-linux-gnu-gcc\nCFLAGS=-Wall -Wextra -pedantic -Wextra -O0 -g\nCFLAGS+=-static -ffreestanding -nostdlib -fno-rtti -fno-exceptions\nCFLAGS+=-march=rv64gc -mabi=lp64\nINCLUDES=\nLINKER_SCRIPT=-Tsrc/lds/virt.lds\nTYPE=debug\nRUST_TARGET=./target/riscv64gc-unknown-none-elf/$(TYPE)\nLIBS=-L$(RUST_TARGET)\nSOURCES_ASM=$(wildcard src/asm/*.S)\nLIB=-lsos -lgcc\nOUT=os.elf\n\n#####\n## QEMU\n#####\nQEMU=qemu-system-riscv64\nMACH=virt\nCPU=rv64\nCPUS=4\nMEM=128M\nDISK=hdd.dsk\nDRIVE= -drive if=none,format=raw,file=$(DISK),id=foo -device virtio-blk-device,scsi=off,drive=foo\nOPTS=-nographic -serial mon:stdio -bios none -device virtio-rng-device -device virtio-gpu-device\nOPTS+=-device virtio-net-device -device virtio-tablet-device -device virtio-keyboard-device\n#DRIVE=\n\n\nall:\n\tcargo build\n\t$(CC) $(CFLAGS) $(LINKER_SCRIPT) $(INCLUDES) -o $(OUT) $(SOURCES_ASM) $(LIBS) $(LIB)\n\t\nrun: all\n\t$(QEMU) -machine $(MACH) -cpu $(CPU) -smp $(CPUS) -m $(MEM) $(DRIVE) $(OPTS) -kernel $(OUT)\n\n\n.PHONY: clean\nclean:\n\tcargo clean\n\trm -f $(OUT)\n"
  },
  {
    "path": "risc_v/chapters/ch9/make_hdd.sh",
    "content": "#!/bin/sh\n\ndd if=/dev/urandom of=hdd.dsk bs=1M count=32\n"
  },
  {
    "path": "risc_v/chapters/ch9/src/asm/boot.S",
    "content": "# boot.S\n# bootloader for SoS\n# Stephen Marz\n# 8 February 2019\n\n# Disable generation of compressed instructions.\n.option norvc\n\n# Define a .text.init section. The .text.init is put at the\n# starting address so that the entry _start is put at the RISC-V\n# address 0x8000_0000.\n.section .text.init\n\n# Execution starts here.\n.global _start\n_start:\n\n\t# Disable linker instruction relaxation for the `la` instruction below.\n\t# This disallows the assembler from assuming that `gp` is already initialized.\n\t# This causes the value stored in `gp` to be calculated from `pc`.\n\t# The job of the global pointer is to give the linker the ability to address\n\t# memory relative to GP instead of as an absolute address.\n.option push\n.option norelax\n\tla\t\tgp, _global_pointer\n.option pop\n\t# SATP should be zero, but let's make sure. Each HART has its own\n\t# SATP register.\n\tcsrw\tsatp, zero\n\t# Any hardware threads (hart) that are not bootstrapping\n\t# need to wait for an IPI\n\tcsrr\tt0, mhartid\n\tbnez\tt0, 3f\n\n\t# Set all bytes in the BSS section to zero.\n\tla \t\ta0, _bss_start\n\tla\t\ta1, _bss_end\n\tbgeu\ta0, a1, 2f\n1:\n\tsd\t\tzero, (a0)\n\taddi\ta0, a0, 8\n\tbltu\ta0, a1, 1b\n2:\n\t# The stack grows from bottom to top, so we put the stack pointer\n\t# to the very end of the stack range.\n\tla\t\tsp, _stack_end\n\t# Setting `mstatus` register:\n\t# 0b01 << 11: Machine's previous protection mode is 2 (MPP=2).\n\tli\t\tt0, 0b11 << 11\n\tcsrw\tmstatus, t0\n\t# Do not allow interrupts while running kinit\n\tcsrw\tmie, zero\n\t# Machine's exception program counter (MEPC) is set to `kinit`.\n\tla\t\tt1, kinit\n\tcsrw\tmepc, t1\n\t# Set the return address to get us into supervisor mode\n\tla\t\tra, 2f\n\t# We use mret here so that the mstatus register is properly updated.\n\tmret\n2:\n\t# We set the return address (ra above) to this label. When kinit() is finished\n\t# in Rust, it will return here.\n\n\t# Setting `mstatus` (supervisor status) register:\n\t# 0b01 << 11 : Previous protection mode is 1 (MPP=01 [Supervisor]).\n\t# 1 << 7     : Previous machine interrupt-enable bit is 1 (MPIE=1 [Enabled])\n\t# 1 << 5     : Previous interrupt-enable bit is 1 (SPIE=1 [Enabled]).\n\t# We set the \"previous\" bits because the mret will write the current bits\n\t# with the previous bits.\n\tli\t\tt0, (0b00 << 11) | (1 << 7) | (1 << 5)\n\tcsrw\tmstatus, t0\n\t# Machine's trap vector base address is set to `m_trap_vector`, for\n\t# \"machine\" trap vector.\n\tla\t\tt2, m_trap_vector\n\tcsrw\tmtvec, t2\n\t# Jump to first process. We put the MPP = 00 for user mode, so after\n\t# mret, we will jump to the first process' addresss in user mode.\n\tla\t\tra, 4f\n\tmret\n3:\n\n\t# Parked harts go here. We need to set these\n\t# to only awaken if it receives a software interrupt,\n\t# which we're going to call the SIPI (Software Intra-Processor Interrupt).\n\t# We call the SIPI by writing the software interrupt into the Core Local Interruptor (CLINT)\n\t# Which is calculated by: base_address + hart * 4\n\t# where base address is 0x0200_0000 (MMIO CLINT base address)\n\t# We only use additional harts to run user-space programs, although this may\n\t# change.\n\n\t# We divide up the stack so the harts aren't clobbering one another.\n\tla\t\tsp, _stack_end\n\tli\t\tt0, 0x10000\n\tcsrr\ta0, mhartid\n\tmul\t\tt0, t0, a0\n\tsub\t\tsp, sp, t0\n\n\t# The parked harts will be put into machine mode with interrupts enabled.\n\tli\t\tt0, 0b11 << 11 | (1 << 7)\n\tcsrw\tmstatus, t0\n\t# Allow for MSIP (Software interrupt). We will write the MSIP from hart #0 to\n\t# awaken these parked harts.\n\tli\t\tt3, (1 << 3)\n\tcsrw\tmie, t3\n\t# Machine's exception program counter (MEPC) is set to the Rust initialization\n\t# code and waiting loop.\n\tla\t\tt1, kinit_hart\n\tcsrw\tmepc, t1\n\t# Machine's trap vector base address is set to `m_trap_vector`, for\n\t# \"machine\" trap vector. The Rust initialization routines will give each\n\t# hart its own trap frame. We can use the same trap function and distinguish\n\t# between each hart by looking at the trap frame.\n\tla\t\tt2, m_trap_vector\n\tcsrw\tmtvec, t2\n\t# Whenever our hart is done initializing, we want it to return to the waiting\n\t# loop, which is just below mret.\n\tla\t\tra, 4f\n\t# We use mret here so that the mstatus register is properly updated.\n\tmret\n\n4:\n\t# wfi = wait for interrupt. This is a hint to the harts to shut everything needed\n\t# down. However, the RISC-V specification allows for wfi to do nothing. Anyway,\n\t# with QEMU, this will save some CPU!\n\twfi\n\tj\t\t4b\n\n\n"
  },
  {
    "path": "risc_v/chapters/ch9/src/asm/mem.S",
    "content": "// mem.S\n// Importation of linker symbols\n\n.section .rodata\n.global HEAP_START\nHEAP_START: .dword _heap_start\n\n.global HEAP_SIZE\nHEAP_SIZE: .dword _heap_size\n\n.global TEXT_START\nTEXT_START: .dword _text_start\n\n.global TEXT_END\nTEXT_END: .dword _text_end\n\n.global DATA_START\nDATA_START: .dword _data_start\n\n.global DATA_END\nDATA_END: .dword _data_end\n\n.global RODATA_START\nRODATA_START: .dword _rodata_start\n\n.global RODATA_END\nRODATA_END: .dword _rodata_end\n\n.global BSS_START\nBSS_START: .dword _bss_start\n\n.global BSS_END\nBSS_END: .dword _bss_end\n\n.global KERNEL_STACK_START\nKERNEL_STACK_START: .dword _stack_start\n\n.global KERNEL_STACK_END\nKERNEL_STACK_END: .dword _stack_end\n\n\n"
  },
  {
    "path": "risc_v/chapters/ch9/src/asm/trap.S",
    "content": "# trap.S\n# Trap handler and global context\n# Steve Operating System\n# Stephen Marz\n# 24 February 2019\n.option norvc\n.altmacro\n.set NUM_GP_REGS, 32  # Number of registers per context\n.set REG_SIZE, 8   # Register size (in bytes)\n\n# Use macros for saving and restoring multiple registers\n.macro save_gp i, basereg=t6\n\tsd\tx\\i, ((\\i)*REG_SIZE)(\\basereg)\n.endm\n.macro load_gp i, basereg=t6\n\tld\tx\\i, ((\\i)*REG_SIZE)(\\basereg)\n.endm\n.macro save_fp i, basereg=t6\n\tfsd\tf\\i, ((NUM_GP_REGS+(\\i))*REG_SIZE)(\\basereg)\n.endm\n.macro load_fp i, basereg=t6\n\tfld\tf\\i, ((NUM_GP_REGS+(\\i))*REG_SIZE)(\\basereg)\n.endm\n\n.section .text\n.global m_trap_vector\n# This must be aligned by 4 since the last two bits\n# of the mtvec register do not contribute to the address\n# of this vector.\n.align 4\nm_trap_vector:\n\t# All registers are volatile here, we need to save them\n\t# before we do anything.\n\tcsrrw\tt6, mscratch, t6\n\t# csrrw will atomically swap t6 into mscratch and the old\n\t# value of mscratch into t6. This is nice because we just\n\t# switched values and didn't destroy anything -- all atomically!\n\t# in cpu.rs we have a structure of:\n\t#  32 gp regs\t\t0\n\t#  32 fp regs\t\t256\n\t#  SATP register\t512\n\t#  Trap stack       520\n\t#  CPU HARTID\t\t528\n\t# We use t6 as the temporary register because it is the very\n\t# bottom register (x31)\n\t.set \ti, 0\n\t.rept\t31\n\t\tsave_gp\t%i\n\t\t.set\ti, i+1\n\t.endr\n\n\t# Save the actual t6 register, which we swapped into\n\t# mscratch\n\tmv\t\tt5, t6\n\tcsrr\tt6, mscratch\n\tsave_gp 31, t5\n\n\t# Restore the kernel trap frame into mscratch\n\tcsrw\tmscratch, t5\n\t# csrw\tmie, zero\n\t# Get ready to go into Rust (trap.rs)\n\t# We don't want to write into the user's stack or whomever\n\t# messed with us here.\n\t# csrw\tmie, zero\n\tcsrr\ta0, mepc\n\tsd\t\ta0, 520(t5)\n\tcsrr\ta1, mtval\n\tcsrr\ta2, mcause\n\tcsrr\ta3, mhartid\n\tcsrr\ta4, mstatus\n\tcsrr\ta5, mscratch\n\tla\t\tt0, KERNEL_STACK_END\n\tld\t\tsp, 0(t0)\n\tcall\tm_trap\n\n\t# When we get here, we've returned from m_trap, restore registers\n\t# and return.\n\t# m_trap will return the return address via a0.\n\n\tcsrw\tmepc, a0\n\t# Now load the trap frame back into t6\n\tcsrr\tt6, mscratch\n\n\t# Restore all GP registers\n\t.set\ti, 1\n\t.rept\t31\n\t\tload_gp %i\n\t\t.set\ti, i+1\n\t.endr\n\n\t# Since we ran this loop 31 times starting with i = 1,\n\t# the last one loaded t6 back to its original value.\n\tmret\n\n.global switch_to_user\nswitch_to_user:\n    # a0 - Frame address\n\t# a1 - Program counter\n\t# a2 - SATP Register\n    csrw    mscratch, a0\n\n\t// Load program counter\n\tld\ta1, 520(a0)\n\t// Load satp\n\tld\ta2, 512(a0)\n\n\t# 1 << 7 is MPIE\n\t# Since user mode is 00, we don't need to set anything\n\t# in MPP (bits 12:11)\n\tli\t\tt0, 1 << 7 | 1 << 5\n\tcsrw\tmstatus, t0\n\tcsrw\tmepc, a1\n\tcsrw\tsatp, a2\n\tli\t\tt1, 0xaaa\n\tcsrw\tmie, t1\n\tla\t\tt2, m_trap_vector\n\tcsrw\tmtvec, t2\n\t# This fence forces the MMU to flush the TLB. However, since\n\t# we're using the PID as the address space identifier, we might\n\t# only need this when we create a process. Right now, this ensures\n\t# correctness, however it isn't the most efficient.\n\t# sfence.vma\n\t# A0 is the context frame, so we need to reload it back\n\t# and mret so we can start running the program.\n\tmv\tt6, a0\n\t.set\ti, 1\n\t.rept\t31\n\t\tload_gp %i, t6\n\t\t.set\ti, i+1\n\t.endr\n\t# j .\n    mret\n\n\n.global make_syscall\nmake_syscall:\n\tecall\n\tret\n"
  },
  {
    "path": "risc_v/chapters/ch9/src/block.rs",
    "content": "// block.rs\n// Block device using VirtIO protocol\n// Stephen Marz\n// 10 March 2020\n\nuse crate::{kmem::{kfree, kmalloc},\n            page::{zalloc, PAGE_SIZE},\n            virtio,\n            virtio::{Descriptor, MmioOffsets, Queue, StatusField, VIRTIO_RING_SIZE}};\nuse core::mem::size_of;\n\n#[repr(C)]\npub struct Geometry {\n\tcylinders: u16,\n\theads:     u8,\n\tsectors:   u8,\n}\n\n#[repr(C)]\npub struct Topology {\n\tphysical_block_exp: u8,\n\talignment_offset:   u8,\n\tmin_io_size:        u16,\n\topt_io_size:        u32,\n}\n\n// There is a configuration space for VirtIO that begins\n// at offset 0x100 and continues to the size of the configuration.\n// The structure below represents the configuration for a\n// block device. Really, all that this OS cares about is the\n// capacity.\n#[repr(C)]\npub struct Config {\n\tcapacity:                 u64,\n\tsize_max:                 u32,\n\tseg_max:                  u32,\n\tgeometry:                 Geometry,\n\tblk_size:                 u32,\n\ttopology:                 Topology,\n\twriteback:                u8,\n\tunused0:                  [u8; 3],\n\tmax_discard_sector:       u32,\n\tmax_discard_seg:          u32,\n\tdiscard_sector_alignment: u32,\n\tmax_write_zeroes_sectors: u32,\n\tmax_write_zeroes_seg:     u32,\n\twrite_zeroes_may_unmap:   u8,\n\tunused1:                  [u8; 3],\n}\n\n// The header/data/status is a block request\n// packet. We send the header to tell the direction\n// (blktype: IN/OUT) and then the starting sector\n// we want to read. Then, we put the data buffer\n// as the Data structure and finally an 8-bit\n// status. The device will write one of three values\n// in here: 0 = success, 1 = io error, 2 = unsupported\n// operation.\n#[repr(C)]\npub struct Header {\n\tblktype:  u32,\n\treserved: u32,\n\tsector:   u64,\n}\n\n#[repr(C)]\npub struct Data {\n\tdata: *mut u8,\n}\n\n#[repr(C)]\npub struct Status {\n\tstatus: u8,\n}\n\n#[repr(C)]\npub struct Request {\n\theader: Header,\n\tdata:   Data,\n\tstatus: Status,\n\thead:   u16,\n}\n\n// Internal block device structure\n// We keep our own used_idx and idx for\n// descriptors. There is a shared index, but that\n// tells us or the device if we've kept up with where\n// we are for the available (us) or used (device) ring.\npub struct BlockDevice {\n\tqueue:        *mut Queue,\n\tdev:          *mut u32,\n\tidx:          u16,\n\tack_used_idx: u16,\n\tread_only:    bool,\n}\n\n// Type values\npub const VIRTIO_BLK_T_IN: u32 = 0;\npub const VIRTIO_BLK_T_OUT: u32 = 1;\npub const VIRTIO_BLK_T_FLUSH: u32 = 4;\npub const VIRTIO_BLK_T_DISCARD: u32 = 11;\npub const VIRTIO_BLK_T_WRITE_ZEROES: u32 = 13;\n\n// Status values\npub const VIRTIO_BLK_S_OK: u8 = 0;\npub const VIRTIO_BLK_S_IOERR: u8 = 1;\npub const VIRTIO_BLK_S_UNSUPP: u8 = 2;\n\n// Feature bits\npub const VIRTIO_BLK_F_SIZE_MAX: u32 = 1;\npub const VIRTIO_BLK_F_SEG_MAX: u32 = 2;\npub const VIRTIO_BLK_F_GEOMETRY: u32 = 4;\npub const VIRTIO_BLK_F_RO: u32 = 5;\npub const VIRTIO_BLK_F_BLK_SIZE: u32 = 6;\npub const VIRTIO_BLK_F_FLUSH: u32 = 9;\npub const VIRTIO_BLK_F_TOPOLOGY: u32 = 10;\npub const VIRTIO_BLK_F_CONFIG_WCE: u32 = 11;\npub const VIRTIO_BLK_F_DISCARD: u32 = 13;\npub const VIRTIO_BLK_F_WRITE_ZEROES: u32 = 14;\n\n// Much like with processes, Rust requires some initialization\n// when we declare a static. In this case, we use the Option\n// value type to signal that the variable exists, but not the\n// queue itself. We will replace this with an actual queue when\n// we initialize the block system.\nstatic mut BLOCK_DEVICES: [Option<BlockDevice>; 8] = [None, None, None, None, None, None, None, None];\n\npub fn setup_block_device(ptr: *mut u32) -> bool {\n\tunsafe {\n\t\t// We can get the index of the device based on its address.\n\t\t// 0x1000_1000 is index 0\n\t\t// 0x1000_2000 is index 1\n\t\t// ...\n\t\t// 0x1000_8000 is index 7\n\t\t// To get the number that changes over, we shift right 12 places (3 hex digits)\n\t\tlet idx = (ptr as usize - virtio::MMIO_VIRTIO_START) >> 12;\n\t\t// [Driver] Device Initialization\n\t\t// 1. Reset the device (write 0 into status)\n\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(0);\n\t\tlet mut status_bits = StatusField::Acknowledge.val32();\n\t\t// 2. Set ACKNOWLEDGE status bit\n\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);\n\t\t// 3. Set the DRIVER status bit\n\t\tstatus_bits |= StatusField::DriverOk.val32();\n\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);\n\t\t// 4. Read device feature bits, write subset of feature\n\t\t// bits understood by OS and driver    to the device.\n\t\tlet host_features = ptr.add(MmioOffsets::HostFeatures.scale32()).read_volatile();\n\t\tlet guest_features = host_features & !(1 << VIRTIO_BLK_F_RO);\n\t\tlet ro = host_features & (1 << VIRTIO_BLK_F_RO) != 0;\n\t\tptr.add(MmioOffsets::GuestFeatures.scale32()).write_volatile(guest_features);\n\t\t// 5. Set the FEATURES_OK status bit\n\t\tstatus_bits |= StatusField::FeaturesOk.val32();\n\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);\n\t\t// 6. Re-read status to ensure FEATURES_OK is still set.\n\t\t// Otherwise, it doesn't support our features.\n\t\tlet status_ok = ptr.add(MmioOffsets::Status.scale32()).read_volatile();\n\t\t// If the status field no longer has features_ok set,\n\t\t// that means that the device couldn't accept\n\t\t// the features that we request. Therefore, this is\n\t\t// considered a \"failed\" state.\n\t\tif false == StatusField::features_ok(status_ok) {\n\t\t\tprint!(\"features fail...\");\n\t\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(StatusField::Failed.val32());\n\t\t\treturn false;\n\t\t}\n\t\t// 7. Perform device-specific setup.\n\t\t// Set the queue num. We have to make sure that the\n\t\t// queue size is valid because the device can only take\n\t\t// a certain size.\n\t\tlet qnmax = ptr.add(MmioOffsets::QueueNumMax.scale32()).read_volatile();\n\t\tptr.add(MmioOffsets::QueueNum.scale32()).write_volatile(VIRTIO_RING_SIZE as u32);\n\t\tif VIRTIO_RING_SIZE as u32 > qnmax {\n\t\t\tprint!(\"queue size fail...\");\n\t\t\treturn false;\n\t\t}\n\t\t// First, if the block device array is empty, create it!\n\t\t// We add 4095 to round this up and then do an integer\n\t\t// divide to truncate the decimal. We don't add 4096,\n\t\t// because if it is exactly 4096 bytes, we would get two\n\t\t// pages, not one.\n\t\tlet num_pages = (size_of::<Queue>() + PAGE_SIZE - 1) / PAGE_SIZE;\n\t\t// println!(\"np = {}\", num_pages);\n\t\t// We allocate a page for each device. This will the the\n\t\t// descriptor where we can communicate with the block\n\t\t// device. We will still use an MMIO register (in\n\t\t// particular, QueueNotify) to actually tell the device\n\t\t// we put something in memory. We also have to be\n\t\t// careful with memory ordering. We don't want to\n\t\t// issue a notify before all memory writes have\n\t\t// finished. We will look at that later, but we need\n\t\t// what is called a memory \"fence\" or barrier.\n\t\tptr.add(MmioOffsets::QueueSel.scale32()).write_volatile(0);\n\t\t// Alignment is very important here. This is the memory address\n\t\t// alignment between the available and used rings. If this is wrong,\n\t\t// then we and the device will refer to different memory addresses\n\t\t// and hence get the wrong data in the used ring.\n\t\t// ptr.add(MmioOffsets::QueueAlign.scale32()).write_volatile(2);\n\t\tlet queue_ptr = zalloc(num_pages) as *mut Queue;\n\t\tlet queue_pfn = queue_ptr as u32;\n\t\tptr.add(MmioOffsets::GuestPageSize.scale32()).write_volatile(PAGE_SIZE as u32);\n\t\t// QueuePFN is a physical page number, however it\n\t\t// appears for QEMU we have to write the entire memory\n\t\t// address. This is a physical memory address where we\n\t\t// (the OS) and the block device have in common for\n\t\t// making and receiving requests.\n\t\tptr.add(MmioOffsets::QueuePfn.scale32()).write_volatile(queue_pfn / PAGE_SIZE as u32);\n\t\t// We need to store all of this data as a \"BlockDevice\"\n\t\t// structure We will be referring to this structure when\n\t\t// making block requests AND when handling responses.\n\t\tlet bd = BlockDevice { queue:        queue_ptr,\n\t\t                       dev:          ptr,\n\t\t                       idx:          0,\n\t\t                       ack_used_idx: 0,\n\t\t                       read_only:    ro, };\n\t\tBLOCK_DEVICES[idx] = Some(bd);\n\n\t\t// 8. Set the DRIVER_OK status bit. Device is now \"live\"\n\t\tstatus_bits |= StatusField::DriverOk.val32();\n\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);\n\n\t\ttrue\n\t}\n}\n\npub fn fill_next_descriptor(bd: &mut BlockDevice, desc: Descriptor) -> u16 {\n\tunsafe {\n\t\t// The ring structure increments here first. This allows us to skip\n\t\t// index 0, which then in the used ring will show that .id > 0. This\n\t\t// is one way to error check. We will eventually get back to 0 as\n\t\t// this index is cyclical. However, it shows if the first read/write\n\t\t// actually works.\n\t\tbd.idx = (bd.idx + 1) % VIRTIO_RING_SIZE as u16;\n\t\t(*bd.queue).desc[bd.idx as usize] = desc;\n\t\tif (*bd.queue).desc[bd.idx as usize].flags & virtio::VIRTIO_DESC_F_NEXT != 0 {\n\t\t\t// If the next flag is set, we need another descriptor.\n\t\t\t(*bd.queue).desc[bd.idx as usize].next = (bd.idx + 1) % VIRTIO_RING_SIZE as u16;\n\t\t}\n\t\tbd.idx\n\t}\n}\n/// This is now a common block operation for both reads and writes. Therefore,\n/// when one thing needs to change, we can change it for both reads and writes.\n/// There is a lot of error checking that I haven't done. The block device reads\n/// sectors at a time, which are 512 bytes. Therefore, our buffer must be capable\n/// of storing multiples of 512 bytes depending on the size. The size is also\n/// a multiple of 512, but we don't really check that.\n/// We DO however, check that we aren't writing to an R/O device. This would\n/// cause a I/O error if we tried to write to a R/O device.\npub fn block_op(dev: usize, buffer: *mut u8, size: u32, offset: u64, write: bool) {\n\tunsafe {\n\t\tif let Some(bdev) = BLOCK_DEVICES[dev - 1].as_mut() {\n\t\t\t// Check to see if we are trying to write to a read only device.\n\t\t\tif true == bdev.read_only && true == write {\n\t\t\t\tprintln!(\"Trying to write to read/only!\");\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tlet sector = offset / 512;\n\t\t\t// TODO: Before we get here, we are NOT allowed to schedule a read or\n\t\t\t// write OUTSIDE of the disk's size. So, we can read capacity from\n\t\t\t// the configuration space to ensure we stay within bounds.\n\t\t\tlet blk_request_size = size_of::<Request>();\n\t\t\tlet blk_request = kmalloc(blk_request_size) as *mut Request;\n\t\t\tlet desc = Descriptor { addr:  &(*blk_request).header as *const Header as u64,\n\t\t\t                        len:   size_of::<Header>() as u32,\n\t\t\t                        flags: virtio::VIRTIO_DESC_F_NEXT,\n\t\t\t                        next:  0, };\n\t\t\tlet head_idx = fill_next_descriptor(bdev, desc);\n\t\t\t(*blk_request).header.sector = sector;\n\t\t\t// A write is an \"out\" direction, whereas a read is an \"in\" direction.\n\t\t\t(*blk_request).header.blktype = if true == write {\n\t\t\t\tVIRTIO_BLK_T_OUT\n\t\t\t}\n\t\t\telse {\n\t\t\t\tVIRTIO_BLK_T_IN\n\t\t\t};\n\t\t\t// We put 111 in the status. Whenever the device finishes, it will write into\n\t\t\t// status. If we read status and it is 111, we know that it wasn't written to by\n\t\t\t// the device.\n\t\t\t(*blk_request).data.data = buffer;\n\t\t\t(*blk_request).header.reserved = 0;\n\t\t\t(*blk_request).status.status = 111;\n\t\t\tlet desc = Descriptor { addr:  buffer as u64,\n\t\t\t                        len:   size,\n\t\t\t                        flags: virtio::VIRTIO_DESC_F_NEXT\n\t\t\t                               | if false == write {\n\t\t\t\t                               virtio::VIRTIO_DESC_F_WRITE\n\t\t\t                               }\n\t\t\t                               else {\n\t\t\t\t                               0\n\t\t\t                               },\n\t\t\t                        next:  0, };\n\t\t\tlet _data_idx = fill_next_descriptor(bdev, desc);\n\t\t\tlet desc = Descriptor { addr:  &(*blk_request).status as *const Status as u64,\n\t\t\t                        len:   size_of::<Status>() as u32,\n\t\t\t                        flags: virtio::VIRTIO_DESC_F_WRITE,\n\t\t\t                        next:  0, };\n\t\t\tlet _status_idx = fill_next_descriptor(bdev, desc);\n\t\t\t(*bdev.queue).avail.ring[(*bdev.queue).avail.idx as usize] = head_idx;\n\t\t\t(*bdev.queue).avail.idx = ((*bdev.queue).avail.idx + 1) % virtio::VIRTIO_RING_SIZE as u16;\n\t\t\t// The only queue a block device has is 0, which is the request\n\t\t\t// queue.\n\t\t\tbdev.dev.add(MmioOffsets::QueueNotify.scale32()).write_volatile(0);\n\t\t}\n\t}\n}\n\npub fn read(dev: usize, buffer: *mut u8, size: u32, offset: u64) {\n\tblock_op(dev, buffer, size, offset, false);\n}\n\npub fn write(dev: usize, buffer: *mut u8, size: u32, offset: u64) {\n\tblock_op(dev, buffer, size, offset, true);\n}\n\n/// Here we handle block specific interrupts. Here, we need to check\n/// the used ring and wind it up until we've handled everything.\n/// This is how the device tells us that it's finished a request.\npub fn pending(bd: &mut BlockDevice) {\n\t// Here we need to check the used ring and then free the resources\n\t// given by the descriptor id.\n\tunsafe {\n\t\tlet ref queue = *bd.queue;\n\t\twhile bd.ack_used_idx != queue.used.idx {\n\t\t\tlet ref elem = queue.used.ring[bd.ack_used_idx as usize];\n\t\t\tbd.ack_used_idx = (bd.ack_used_idx + 1) % VIRTIO_RING_SIZE as u16;\n\t\t\tlet rq = queue.desc[elem.id as usize].addr as *const Request;\n\t\t\tkfree(rq as *mut u8);\n\t\t\t// TODO: Awaken the process that will need this I/O. This is\n\t\t\t// the purpose of the waiting state.\n\t\t}\n\t}\n}\n\n/// The trap code will route PLIC interrupts 1..=8 for virtio devices. When\n/// virtio determines that this is a block device, it sends it here.\npub fn handle_interrupt(idx: usize) {\n\tunsafe {\n\t\tif let Some(bdev) = BLOCK_DEVICES[idx].as_mut() {\n\t\t\tpending(bdev);\n\t\t}\n\t\telse {\n\t\t\tprintln!(\"Invalid block device for interrupt {}\", idx + 1);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "risc_v/chapters/ch9/src/cpu.rs",
    "content": "// cpu.rs\n// CPU and CPU-related routines\n// Also contains the kernel's trap frame\n// Stephen Marz\n// 14 October 2019\n\n// The frequency of QEMU is 10 MHz\npub const FREQ: u64 = 10_000_000;\n// Let's do this 250 times per second for switching\npub const CONTEXT_SWITCH_TIME: u64 = FREQ / 250;\n\n/// In 64-bit mode, we're given three different modes for the MMU:\n/// 0 - The MMU is off -- no protection and no translation PA = VA\n/// 8 - This is Sv39 mode -- 39-bit virtual addresses\n/// 9 - This is Sv48 mode -- 48-bit virtual addresses\n#[repr(usize)]\npub enum SatpMode {\n\tOff = 0,\n\tSv39 = 8,\n\tSv48 = 9,\n}\n\n/// The trap frame is set into a structure\n/// and packed into each hart's mscratch register.\n/// This allows for quick reference and full\n/// context switch handling.\n#[repr(C)]\n#[derive(Clone, Copy)]\npub struct TrapFrame {\n\tpub regs:       [usize; 32], // 0 - 255\n\tpub fregs:      [usize; 32], // 256 - 511\n\tpub satp:       usize,       // 512 - 519\n\tpub pc:         usize,       // 520\n\tpub hartid:     usize,       // 528\n\tpub qm:         usize,\t\t // 536\n}\n\n/// Rust requires that we initialize our structures\n/// because of the move semantics. What'll happen below\n/// is Rust will construct a new TrapFrame and move it\n/// out of the zero() function below. Rust contains two\n/// different \"selfs\" where self can refer to the object\n/// in memory or Self (capital S) which refers to the\n/// data type of the structure. In the case below, this\n/// is TrapFrame.\nimpl TrapFrame {\n\tpub const fn new() -> Self {\n\t\tTrapFrame { regs:       [0; 32],\n\t\t            fregs:      [0; 32],\n\t\t            satp:       0,\n\t\t            pc:\t\t    0,\n\t\t\t\t\thartid:     0, \n\t\t\t\t\tqm:         1,\n\t\t\t\t}\n\t}\n}\n\n/// The SATP register contains three fields: mode, address space id, and\n/// the first level table address (level 2 for Sv39). This function\n/// helps make the 64-bit register contents based on those three\n/// fields.\npub const fn build_satp(mode: SatpMode, asid: usize, addr: usize) -> usize {\n\t(mode as usize) << 60\n\t| (asid & 0xffff) << 44\n\t| (addr >> 12) & 0xff_ffff_ffff\n}\n\npub fn mhartid_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, mhartid\" :\"=r\"(rval));\n\t\trval\n\t}\n}\npub fn mie_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, mie\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn mie_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw mie, $0\" :: \"r\"(val));\n\t}\n}\n\npub fn mstatus_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tmstatus, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn mstatus_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, mstatus\":\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn stvec_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tstvec, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn stvec_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, stvec\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn mscratch_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tmscratch, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn mscratch_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, mscratch\" : \"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn mscratch_swap(to: usize) -> usize {\n\tunsafe {\n\t\tlet from;\n\t\tasm!(\"csrrw\t$0, mscratch, $1\" : \"=r\"(from) : \"r\"(to));\n\t\tfrom\n\t}\n}\n\npub fn sscratch_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw\tsscratch, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn sscratch_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr\t$0, sscratch\" : \"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn sscratch_swap(to: usize) -> usize {\n\tunsafe {\n\t\tlet from;\n\t\tasm!(\"csrrw\t$0, sscratch, $1\" : \"=r\"(from) : \"r\"(to));\n\t\tfrom\n\t}\n}\n\npub fn mepc_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw mepc, $0\" :: \"r\"(val));\n\t}\n}\n\npub fn mepc_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, mepc\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn sepc_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw sepc, $0\" :: \"r\"(val));\n\t}\n}\n\npub fn sepc_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, sepc\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn satp_write(val: usize) {\n\tunsafe {\n\t\tasm!(\"csrw satp, $0\" :: \"r\"(val));\n\t}\n}\n\npub fn satp_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tasm!(\"csrr $0, satp\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\n/// Take a hammer to the page tables and synchronize\n/// all of them. This essentially flushes the entire\n/// TLB.\npub fn satp_fence(vaddr: usize, asid: usize) {\n\tunsafe {\n\t\tasm!(\"sfence.vma $0, $1\" :: \"r\"(vaddr), \"r\"(asid));\n\t}\n}\n\n/// Synchronize based on the address space identifier\n/// This allows us to fence a particular process rather\n/// than the entire TLB.\n/// The RISC-V documentation calls this a TLB flush +.\n/// Since there are other memory routines involved, they\n/// didn't call it a TLB flush, but it is much like\n/// Intel/AMD's invtlb [] instruction.\npub fn satp_fence_asid(asid: usize) {\n\tunsafe {\n\t\tasm!(\"sfence.vma zero, $0\" :: \"r\"(asid));\n\t}\n}\n"
  },
  {
    "path": "risc_v/chapters/ch9/src/kmem.rs",
    "content": "// kmem.rs\n// Sub-page level: malloc-like allocation system\n// Stephen Marz\n// 7 October 2019\n\nuse crate::page::{align_val, zalloc, Table, PAGE_SIZE};\nuse core::{mem::size_of, ptr::null_mut};\n\n#[repr(usize)]\nenum AllocListFlags {\n\tTaken = 1 << 63,\n}\nimpl AllocListFlags {\n\tpub fn val(self) -> usize {\n\t\tself as usize\n\t}\n}\n\nstruct AllocList {\n\tpub flags_size: usize,\n}\nimpl AllocList {\n\tpub fn is_taken(&self) -> bool {\n\t\tself.flags_size & AllocListFlags::Taken.val() != 0\n\t}\n\n\tpub fn is_free(&self) -> bool {\n\t\t!self.is_taken()\n\t}\n\n\tpub fn set_taken(&mut self) {\n\t\tself.flags_size |= AllocListFlags::Taken.val();\n\t}\n\n\tpub fn set_free(&mut self) {\n\t\tself.flags_size &= !AllocListFlags::Taken.val();\n\t}\n\n\tpub fn set_size(&mut self, sz: usize) {\n\t\tlet k = self.is_taken();\n\t\tself.flags_size = sz & !AllocListFlags::Taken.val();\n\t\tif k {\n\t\t\tself.flags_size |= AllocListFlags::Taken.val();\n\t\t}\n\t}\n\n\tpub fn get_size(&self) -> usize {\n\t\tself.flags_size & !AllocListFlags::Taken.val()\n\t}\n}\n\n// This is the head of the allocation. We start here when\n// we search for a free memory location.\nstatic mut KMEM_HEAD: *mut AllocList = null_mut();\n// In the future, we will have on-demand pages\n// so, we need to keep track of our memory footprint to\n// see if we actually need to allocate more.\nstatic mut KMEM_ALLOC: usize = 0;\nstatic mut KMEM_PAGE_TABLE: *mut Table = null_mut();\n\n// These functions are safe helpers around an unsafe\n// operation.\npub fn get_head() -> *mut u8 {\n\tunsafe { KMEM_HEAD as *mut u8 }\n}\n\npub fn get_page_table() -> *mut Table {\n\tunsafe { KMEM_PAGE_TABLE as *mut Table }\n}\n\npub fn get_num_allocations() -> usize {\n\tunsafe { KMEM_ALLOC }\n}\n\n/// Initialize kernel's memory\n/// This is not to be used to allocate memory\n/// for user processes. If that's the case, use\n/// alloc/dealloc from the page crate.\npub fn init() {\n\tunsafe {\n\t\t// Allocate kernel pages (KMEM_ALLOC)\n\t\tKMEM_ALLOC = 512;\n\t\tlet k_alloc = zalloc(KMEM_ALLOC);\n\t\tassert!(!k_alloc.is_null());\n\t\tKMEM_HEAD = k_alloc as *mut AllocList;\n\t\t(*KMEM_HEAD).set_free();\n\t\t(*KMEM_HEAD).set_size(KMEM_ALLOC * PAGE_SIZE);\n\t\tKMEM_PAGE_TABLE = zalloc(1) as *mut Table;\n\t}\n}\n\n/// Allocate sub-page level allocation based on bytes and zero the memory\npub fn kzmalloc(sz: usize) -> *mut u8 {\n\tlet size = align_val(sz, 3);\n\tlet ret = kmalloc(size);\n\n\tif !ret.is_null() {\n\t\tfor i in 0..size {\n\t\t\tunsafe {\n\t\t\t\t(*ret.add(i)) = 0;\n\t\t\t}\n\t\t}\n\t}\n\tret\n}\n\n/// Allocate sub-page level allocation based on bytes\npub fn kmalloc(sz: usize) -> *mut u8 {\n\tunsafe {\n\t\tlet size = align_val(sz, 3) + size_of::<AllocList>();\n\t\tlet mut head = KMEM_HEAD;\n\t\t// .add() uses pointer arithmetic, so we type-cast into a u8\n\t\t// so that we multiply by an absolute size (KMEM_ALLOC *\n\t\t// PAGE_SIZE).\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\n\t\twhile head < tail {\n\t\t\tif (*head).is_free() && size <= (*head).get_size() {\n\t\t\t\tlet chunk_size = (*head).get_size();\n\t\t\t\tlet rem = chunk_size - size;\n\t\t\t\t(*head).set_taken();\n\t\t\t\tif rem > size_of::<AllocList>() {\n\t\t\t\t\tlet next = (head as *mut u8).add(size)\n\t\t\t\t\t           as *mut AllocList;\n\t\t\t\t\t// There is space remaining here.\n\t\t\t\t\t(*next).set_free();\n\t\t\t\t\t(*next).set_size(rem);\n\t\t\t\t\t(*head).set_size(size);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// If we get here, take the entire chunk\n\t\t\t\t\t(*head).set_size(chunk_size);\n\t\t\t\t}\n\t\t\t\treturn head.add(1) as *mut u8;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// If we get here, what we saw wasn't a free\n\t\t\t\t// chunk, move on to the next.\n\t\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t\t       as *mut AllocList;\n\t\t\t}\n\t\t}\n\t}\n\t// If we get here, we didn't find any free chunks--i.e. there isn't\n\t// enough memory for this. TODO: Add on-demand page allocation.\n\tnull_mut()\n}\n\n/// Free a sub-page level allocation\npub fn kfree(ptr: *mut u8) {\n\tunsafe {\n\t\tif !ptr.is_null() {\n\t\t\tlet p = (ptr as *mut AllocList).offset(-1);\n\t\t\tif (*p).is_taken() {\n\t\t\t\t(*p).set_free();\n\t\t\t}\n\t\t\t// After we free, see if we can combine adjacent free\n\t\t\t// spots to see if we can reduce fragmentation.\n\t\t\tcoalesce();\n\t\t}\n\t}\n}\n\n/// Merge smaller chunks into a bigger chunk\npub fn coalesce() {\n\tunsafe {\n\t\tlet mut head = KMEM_HEAD;\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\n\t\twhile head < tail {\n\t\t\tlet next = (head as *mut u8).add((*head).get_size())\n\t\t\t           as *mut AllocList;\n\t\t\tif (*head).get_size() == 0 {\n\t\t\t\t// If this happens, then we have a bad heap\n\t\t\t\t// (double free or something). However, that\n\t\t\t\t// will cause an infinite loop since the next\n\t\t\t\t// pointer will never move beyond the current\n\t\t\t\t// location.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if next >= tail {\n\t\t\t\t// We calculated the next by using the size\n\t\t\t\t// given as get_size(), however this could push\n\t\t\t\t// us past the tail. In that case, the size is\n\t\t\t\t// wrong, hence we break and stop doing what we\n\t\t\t\t// need to do.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (*head).is_free() && (*next).is_free() {\n\t\t\t\t// This means we have adjacent blocks needing to\n\t\t\t\t// be freed. So, we combine them into one\n\t\t\t\t// allocation.\n\t\t\t\t(*head).set_size(\n\t\t\t\t                 (*head).get_size()\n\t\t\t\t                 + (*next).get_size(),\n\t\t\t\t);\n\t\t\t}\n\t\t\t// If we get here, we might've moved. Recalculate new\n\t\t\t// head.\n\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t       as *mut AllocList;\n\t\t}\n\t}\n}\n\n/// For debugging purposes, print the kmem table\npub fn print_table() {\n\tunsafe {\n\t\tlet mut head = KMEM_HEAD;\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\t\twhile head < tail {\n\t\t\tprintln!(\n\t\t\t         \"{:p}: Length = {:<10} Taken = {}\",\n\t\t\t         head,\n\t\t\t         (*head).get_size(),\n\t\t\t         (*head).is_taken()\n\t\t\t);\n\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t       as *mut AllocList;\n\t\t}\n\t}\n}\n\n// ///////////////////////////////////\n// / GLOBAL ALLOCATOR\n// ///////////////////////////////////\n\n// The global allocator allows us to use the data structures\n// in the core library, such as a linked list or B-tree.\n// We want to use these sparingly since we have a coarse-grained\n// allocator.\nuse core::alloc::{GlobalAlloc, Layout};\n\n// The global allocator is a static constant to a global allocator\n// structure. We don't need any members because we're using this\n// structure just to implement alloc and dealloc.\nstruct OsGlobalAlloc;\n\nunsafe impl GlobalAlloc for OsGlobalAlloc {\n\tunsafe fn alloc(&self, layout: Layout) -> *mut u8 {\n\t\t// We align to the next page size so that when\n\t\t// we divide by PAGE_SIZE, we get exactly the number\n\t\t// of pages necessary.\n\t\tkzmalloc(layout.size())\n\t}\n\n\tunsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {\n\t\t// We ignore layout since our allocator uses ptr_start -> last\n\t\t// to determine the span of an allocation.\n\t\tkfree(ptr);\n\t}\n}\n\n#[global_allocator]\n/// Technically, we don't need the {} at the end, but it\n/// reveals that we're creating a new structure and not just\n/// copying a value.\nstatic GA: OsGlobalAlloc = OsGlobalAlloc {};\n\n#[alloc_error_handler]\n/// If for some reason alloc() in the global allocator gets null_mut(),\n/// then we come here. This is a divergent function, so we call panic to\n/// let the tester know what's going on.\npub fn alloc_error(l: Layout) -> ! {\n\tpanic!(\n\t       \"Allocator failed to allocate {} bytes with {}-byte alignment.\",\n\t       l.size(),\n\t       l.align()\n\t);\n}\n"
  },
  {
    "path": "risc_v/chapters/ch9/src/lds/virt.lds",
    "content": "/*\n virt.lds\n Linker script for outputting to RISC-V QEMU \"virt\" machine.\n Stephen Marz\n 6 October 2019\n*/\n\n/*\n  riscv is the name of the architecture that the linker understands\n  for any RISC-V target (64-bit or 32-bit).\n\n  We will further refine this by using -mabi=lp64 and -march=rv64gc\n*/\nOUTPUT_ARCH( \"riscv\" )\n\n/*\nWe're setting our entry point to a symbol\ncalled _start which is inside of boot.S. This\nessentially stores the address of _start as the\n\"entry point\", or where CPU instructions should start\nexecuting.\n\nIn the rest of this script, we are going to place _start\nright at the beginning of 0x8000_0000 because this is where\nthe virtual machine and many RISC-V boards will start executing.\n*/\nENTRY( _start )\n\n/*\nThe MEMORY section will explain that we have \"ram\" that contains\na section that is 'w' (writeable), 'x' (executable), and 'a' (allocatable).\nWe use '!' to invert 'r' (read-only) and 'i' (initialized). We don't want\nour memory to be read-only, and we're stating that it is NOT initialized\nat the beginning.\n\nThe ORIGIN is the memory address 0x8000_0000. If we look at the virt\nspec or the specification for the RISC-V HiFive Unleashed, this is the\nstarting memory address for our code.\n\nSide note: There might be other boot ROMs at different addresses, but\ntheir job is to get to this point.\n\nFinally LENGTH = 128M tells the linker that we have 128 megabyte of RAM.\nThe linker will double check this to make sure everything can fit.\n\nThe HiFive Unleashed has a lot more RAM than this, but for the virtual \nmachine, I went with 128M since I think that's enough RAM for now.\n\nWe can provide other pieces of memory, such as QSPI, or ROM, but we're\ntelling the linker script here that we have one pool of RAM.\n*/\nMEMORY\n{\n  ram   (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 128M\n}\n\n/*\nPHDRS is short for \"program headers\", which we specify three here:\ntext - CPU instructions (executable sections)\ndata - Global, initialized variables\nbss  - Global, uninitialized variables (all will be set to 0 by boot.S)\n\nThe command PT_LOAD tells the linker that these sections will be loaded\nfrom the file into memory.\n\nWe can actually stuff all of these into a single program header, but by\nsplitting it up into three, we can actually use the other PT_* commands\nsuch as PT_DYNAMIC, PT_INTERP, PT_NULL to tell the linker where to find\nadditional information.\n\nHowever, for our purposes, every section will be loaded from the program\nheaders.\n*/\nPHDRS\n{\n  text PT_LOAD;\n  data PT_LOAD;\n  bss PT_LOAD;\n}\n\n/*\nWe are now going to organize the memory based on which\nsection it is in. In assembly, we can change the section\nwith the \".section\" directive. However, in C++ and Rust,\nCPU instructions go into text, global constants go into\nrodata, global initialized variables go into data, and\nglobal uninitialized variables go into bss.\n*/\nSECTIONS\n{\n  /*\n    The first part of our RAM layout will be the text section.\n\tSince our CPU instructions are here, and our memory starts at\n\t0x8000_0000, we need our entry point to line up here.\n  */\n  .text : {\n\t  /* \n\t    PROVIDE allows me to access a symbol called _text_start so\n\t\tI know where the text section starts in the operating system.\n\t\tThis should not move, but it is here for convenience.\n\t\tThe period '.' tells the linker to set _text_start to the\n\t\tCURRENT location ('.' = current memory location). This current\n\t\tmemory location moves as we add things.\n\t  */\n\n    PROVIDE(_text_start = .);\n\t/*\n\t  We are going to layout all text sections here, starting with \n\t  .text.init. The asterisk in front of the parentheses means to match\n\t  the .text.init section of ANY object file. Otherwise, we can specify\n\t  which object file should contain the .text.init section, for example,\n\t  boot.o(.text.init) would specifically put the .text.init section of\n\t  our bootloader here.\n\n\t  Because we might want to change the name of our files, we'll leave it\n\t  with a *.\n\n\t  Inside the parentheses is the name of the section. I created my own\n\t  called .text.init to make 100% sure that the _start is put right at the\n\t  beginning. The linker will lay this out in the order it receives it:\n\n\t  .text.init first\n\t  all .text sections next\n\t  any .text.* sections last\n\n\t  .text.* means to match anything after .text. If we didn't already specify\n\t  .text.init, this would've matched here. The assembler and linker can place\n\t  things in \"special\" text sections, so we match any we might come across here.\n\t*/\n    *(.text.init) *(.text .text.*)\n\n\t/*\n\t  Again, with PROVIDE, we're providing a readable symbol called _text_end, which is\n\t  set to the memory address AFTER .text.init, .text, and .text.*'s have been added.\n\t*/\n    PROVIDE(_text_end = .);\n\t/*\n\t  The portion after the right brace is in an odd format. However, this is telling the\n\t  linker what memory portion to put it in. We labeled our RAM, ram, with the constraints\n\t  that it is writeable, allocatable, and executable. The linker will make sure with this\n\t  that we can do all of those things.\n\n\t  >ram - This just tells the linker script to put this entire section (.text) into the\n\t         ram region of memory. To my knowledge, the '>' does not mean \"greater than\". Instead,\n\t\t\t it is a symbol to let the linker know we want to put this in ram.\n\n\t  AT>ram - This sets the LMA (load memory address) region to the same thing. LMA is the final\n\t           translation of a VMA (virtual memory address). With this linker script, we're loading\n\t\t\t   everything into its physical location. We'll let the kernel copy and sort out the \n\t\t\t   virtual memory. That's why >ram and AT>ram are continually the same thing.\n\n\t  :text  - This tells the linker script to put this into the :text program header. We've only\n\t           defined three: text, data, and bss. In this case, we're telling the linker script\n\t\t\t   to go into the text section.\n\t*/\n  } >ram AT>ram :text\n   /*\n     The global pointer allows the linker to position global variables and constants into\n\t independent positions relative to the gp (global pointer) register. The globals start\n\t after the text sections and are only relevant to the rodata, data, and bss sections.\n   */\n   PROVIDE(_global_pointer = .);\n   /*\n     Most compilers create a rodata (read only data) section for global constants. However,\n\t we're going to place ours in the text section. We can actually put this in :data, but\n\t since the .text section is read-only, we can place it there.\n\n\t NOTE: This doesn't actually do anything, yet. The actual \"protection\" cannot be done\n\t at link time. Instead, when we program the memory management unit (MMU), we will be\n\t able to choose which bits (R=read, W=write, X=execute) we want each memory segment\n\t to be able to do.\n   */\n  .rodata : {\n    PROVIDE(_rodata_start = .);\n    *(.rodata .rodata.*)\n    PROVIDE(_rodata_end = .);\n\t/*\n\t   Again, we're placing the rodata section in the memory segment \"ram\" and we're putting\n\t   it in the :text program header. We don't have one for rodata anyway.\n\t*/\n  } >ram AT>ram :text\n\n  .data : {\n\t/*\n\t   . = ALIGN(4096) tells the linker to align the current memory location (which is\n\t   0x8000_0000 + text section + rodata section) to 4096 bytes. This is because our paging\n\t   system's resolution is 4,096 bytes or 4 KiB.\n\t*/\n    . = ALIGN(4096);\n    PROVIDE(_data_start = .);\n\t/*\n\t   sdata and data are essentially the same thing. However, compilers usually use the\n\t   sdata sections for shorter, quicker loading sections. So, usually critical data\n\t   is loaded there. However, we're loading all of this in one fell swoop.\n\t   So, we're looking to put all of the following sections under the umbrella .data:\n\t   .sdata\n\t   .sdata.[anything]\n\t   .data\n\t   .data.[anything]\n\n\t   ...in that order.\n\t*/\n    *(.sdata .sdata.*) *(.data .data.*)\n    PROVIDE(_data_end = .);\n  } >ram AT>ram :data\n\n  .bss : {\n    PROVIDE(_bss_start = .);\n    *(.sbss .sbss.*) *(.bss .bss.*)\n    PROVIDE(_bss_end = .);\n  } >ram AT>ram :bss\n\n  /*\n     The following will be helpful when we allocate the kernel stack (_stack) and\n\t determine where the heap begnis and ends (_heap_start and _heap_start + _heap_size)/\n\t When we do memory allocation, we can use these symbols.\n\n\t We use the symbols instead of hard-coding an address because this is a floating target.\n\t As we add code, the heap moves farther down the memory and gets shorter.\n\n\t _memory_start will be set to 0x8000_0000 here. We use ORIGIN(ram) so that it will take\n\t whatever we set the origin of ram to. Otherwise, we'd have to change it more than once\n\t if we ever stray away from 0x8000_0000 as our entry point.\n  */\n  PROVIDE(_memory_start = ORIGIN(ram));\n  /*\n     Our kernel stack starts at the end of the bss segment (_bss_end). However, we're allocating\n\t 0x80000 bytes (524 KiB) to our kernel stack. This should be PLENTY of space. The reason\n\t we add the memory is because the stack grows from higher memory to lower memory (bottom to top).\n\t Therefore we set the stack at the very bottom of its allocated slot.\n\t When we go to allocate from the stack, we'll subtract the number of bytes we need.\n  */\n  PROVIDE(_stack_start = _bss_end);\n  PROVIDE(_stack_end = _stack_start + 0x8000);\n  PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));\n\n  /* \n     Finally, our heap starts right after the kernel stack. This heap will be used mainly\n\t to dole out memory for user-space applications. However, in some circumstances, it will\n\t be used for kernel memory as well.\n\n\t We don't align here because we let the kernel determine how it wants to do this.\n  */\n  PROVIDE(_heap_start = _stack_end);\n  PROVIDE(_heap_size = _memory_end - _heap_start);\n}\n"
  },
  {
    "path": "risc_v/chapters/ch9/src/lib.rs",
    "content": "// Steve Operating System\n// Stephen Marz\n// 21 Sep 2019\n#![no_std]\n#![feature(panic_info_message,\n           asm,\n           allocator_api,\n           alloc_error_handler,\n           alloc_prelude,\n           const_raw_ptr_to_usize_cast)]\n\n// #[macro_use]\nextern crate alloc;\n// This is experimental and requires alloc_prelude as a feature\n// use alloc::prelude::v1::*;\n\n// ///////////////////////////////////\n// / RUST MACROS\n// ///////////////////////////////////\n#[macro_export]\nmacro_rules! print\n{\n\t($($args:tt)+) => ({\n\t\t\tuse core::fmt::Write;\n\t\t\tlet _ = write!(crate::uart::Uart::new(0x1000_0000), $($args)+);\n\t\t\t});\n}\n#[macro_export]\nmacro_rules! println\n{\n\t() => ({\n\t\t   print!(\"\\r\\n\")\n\t\t   });\n\t($fmt:expr) => ({\n\t\t\tprint!(concat!($fmt, \"\\r\\n\"))\n\t\t\t});\n\t($fmt:expr, $($args:tt)+) => ({\n\t\t\tprint!(concat!($fmt, \"\\r\\n\"), $($args)+)\n\t\t\t});\n}\n\n// ///////////////////////////////////\n// / LANGUAGE STRUCTURES / FUNCTIONS\n// ///////////////////////////////////\n#[no_mangle]\nextern \"C\" fn eh_personality() {}\n\n#[panic_handler]\nfn panic(info: &core::panic::PanicInfo) -> ! {\n\tprint!(\"Aborting: \");\n\tif let Some(p) = info.location() {\n\t\tprintln!(\n\t\t         \"line {}, file {}: {}\",\n\t\t         p.line(),\n\t\t         p.file(),\n\t\t         info.message().unwrap()\n\t\t);\n\t}\n\telse {\n\t\tprintln!(\"no information available.\");\n\t}\n\tabort();\n}\n#[no_mangle]\nextern \"C\" fn abort() -> ! {\n\tloop {\n\t\tunsafe {\n\t\t\tasm!(\"wfi\"::::\"volatile\");\n\t\t}\n\t}\n}\n\n// ///////////////////////////////////\n// / CONSTANTS\n// ///////////////////////////////////\n// const STR_Y: &str = \"\\x1b[38;2;79;221;13m✓\\x1b[m\";\n// const STR_N: &str = \"\\x1b[38;2;221;41;13m✘\\x1b[m\";\n\n// The following symbols come from asm/mem.S. We can use\n// the symbols directly, but the address of the symbols\n// themselves are their values, which can cause issues.\n// Instead, I created doubleword values in mem.S in the .rodata and .data\n// sections.\n// extern \"C\" {\n// static TEXT_START: usize;\n// static TEXT_END: usize;\n// static DATA_START: usize;\n// static DATA_END: usize;\n// static RODATA_START: usize;\n// static RODATA_END: usize;\n// static BSS_START: usize;\n// static BSS_END: usize;\n// static KERNEL_STACK_START: usize;\n// static KERNEL_STACK_END: usize;\n// static HEAP_START: usize;\n// static HEAP_SIZE: usize;\n// }\n/// Identity map range\n/// Takes a contiguous allocation of memory and maps it using PAGE_SIZE\n/// This assumes that start <= end\npub fn id_map_range(root: &mut page::Table,\n                    start: usize,\n                    end: usize,\n                    bits: i64)\n{\n\tlet mut memaddr = start & !(page::PAGE_SIZE - 1);\n\tlet num_kb_pages =\n\t\t(page::align_val(end, 12) - memaddr) / page::PAGE_SIZE;\n\n\t// I named this num_kb_pages for future expansion when\n\t// I decide to allow for GiB (2^30) and 2MiB (2^21) page\n\t// sizes. However, the overlapping memory regions are causing\n\t// nightmares.\n\tfor _ in 0..num_kb_pages {\n\t\tpage::map(root, memaddr, memaddr, bits, 0);\n\t\tmemaddr += 1 << 12;\n\t}\n}\nextern \"C\" {\n\tfn switch_to_user(frame: usize) -> !;\n}\nfn rust_switch_to_user(frame: usize) -> ! {\n\tunsafe {\n\t\tswitch_to_user(frame);\n\t}\n}\n// ///////////////////////////////////\n// / ENTRY POINT\n// ///////////////////////////////////\n#[no_mangle]\nextern \"C\" fn kinit() {\n\tuart::Uart::new(0x1000_0000).init();\n\tpage::init();\n\tkmem::init();\n\tprocess::init();\n\t// We lower the threshold wall so our interrupts can jump over it.\n\t// Any priority > 0 will be able to be \"heard\"\n\tplic::set_threshold(0);\n\t// VIRTIO = [1..8]\n\t// UART0 = 10\n\t// PCIE = [32..35]\n\t// Enable PLIC interrupts.\n\tfor i in 1..=10 {\n\t\tplic::enable(i);\n\t\tplic::set_priority(i, 1);\n\t}\n\t// Set up virtio. This requires a working heap and page-grained allocator.\n\tvirtio::probe();\n\t// This just tests the block device. We know that it connects backwards (8, 7, ..., 1).\n\tlet buffer = kmem::kmalloc(1024);\n\t// Offset 1024 is the first block, which is the superblock. In the minix 3 file system, the first\n\t// block is the \"boot block\", which in our case will be 0.\n\tblock::read(8, buffer, 512, 1024);\n\tlet mut i = 0;\n\tloop {\n\t\tif i > 100_000_000 {\n\t\t\tbreak;\n\t\t}\n\t\ti += 1;\n\t}\n\tprintln!(\"Test hdd.dsk:\");\n\tunsafe {\n\t\tprint!(\"  \");\n\t\tfor i in 0..16 {\n\t\t\tprint!(\"{:02x} \", buffer.add(i).read());\n\t\t}\n\t\tprintln!();\n\t\tprint!(\"  \");\n\t\tfor i in 0..16 {\n\t\t\tprint!(\"{:02x} \", buffer.add(16+i).read());\n\t\t}\n\t\tprintln!();\n\t\tprint!(\"  \");\n\t\tfor i in 0..16 {\n\t\t\tprint!(\"{:02x} \", buffer.add(32+i).read());\n\t\t}\n\t\tprintln!();\n\t\tprint!(\"  \");\n\t\tfor i in 0..16 {\n\t\t\tprint!(\"{:02x} \", buffer.add(48+i).read());\n\t\t}\n\t\tprintln!();\n\t\tbuffer.add(0).write(0xaa);\n\t\tbuffer.add(1).write(0xbb);\n\t\tbuffer.add(2).write(0x7a);\n\n\t}\n\tblock::write(8, buffer, 512, 0);\n\t// Free the testing buffer.\n\tkmem::kfree(buffer);\n\t// We schedule the next context switch using a multiplier of 1\n\ttrap::schedule_next_context_switch(1);\n\trust_switch_to_user(sched::schedule());\n\t// switch_to_user will not return, so we should never get here\n}\n#[no_mangle]\nextern \"C\" fn kinit_hart(_hartid: usize) {\n\t// We aren't going to do anything here until we get SMP going.\n\t// All non-0 harts initialize here.\n}\n\n// ///////////////////////////////////\n// / RUST MODULES\n// ///////////////////////////////////\n\npub mod block;\npub mod cpu;\npub mod kmem;\npub mod page;\npub mod plic;\npub mod process;\npub mod rng;\npub mod sched;\npub mod syscall;\npub mod trap;\npub mod uart;\npub mod virtio;\n"
  },
  {
    "path": "risc_v/chapters/ch9/src/page.rs",
    "content": "// page.rs\n// Memory routines\n// Stephen Marz\n// 6 October 2019\n\nuse core::{mem::size_of, ptr::null_mut};\n\n// ////////////////////////////////\n// // Allocation routines\n// ////////////////////////////////\nextern \"C\" {\n\tstatic HEAP_START: usize;\n\tstatic HEAP_SIZE: usize;\n}\n\n// We will use ALLOC_START to mark the start of the actual\n// memory we can dish out.\nstatic mut ALLOC_START: usize = 0;\nconst PAGE_ORDER: usize = 12;\npub const PAGE_SIZE: usize = 1 << 12;\n\n/// Align (set to a multiple of some power of two)\n/// This takes an order which is the exponent to 2^order\n/// Therefore, all alignments must be made as a power of two.\n/// This function always rounds up.\npub const fn align_val(val: usize, order: usize) -> usize {\n\tlet o = (1usize << order) - 1;\n\t(val + o) & !o\n}\n\n#[repr(u8)]\npub enum PageBits {\n\tEmpty = 0,\n\tTaken = 1 << 0,\n\tLast = 1 << 1,\n}\n\nimpl PageBits {\n\t// We convert PageBits to a u8 a lot, so this is\n\t// for convenience.\n\tpub fn val(self) -> u8 {\n\t\tself as u8\n\t}\n}\n\n// Each page is described by the Page structure. Linux does this\n// as well, where each 4096-byte chunk of memory has a structure\n// associated with it. However, there structure is much larger.\npub struct Page {\n\tflags: u8,\n}\n\nimpl Page {\n\t// If this page has been marked as the final allocation,\n\t// this function returns true. Otherwise, it returns false.\n\tpub fn is_last(&self) -> bool {\n\t\tif self.flags & PageBits::Last.val() != 0 {\n\t\t\ttrue\n\t\t}\n\t\telse {\n\t\t\tfalse\n\t\t}\n\t}\n\n\t// If the page is marked as being taken (allocated), then\n\t// this function returns true. Otherwise, it returns false.\n\tpub fn is_taken(&self) -> bool {\n\t\tif self.flags & PageBits::Taken.val() != 0 {\n\t\t\ttrue\n\t\t}\n\t\telse {\n\t\t\tfalse\n\t\t}\n\t}\n\n\t// This is the opposite of is_taken().\n\tpub fn is_free(&self) -> bool {\n\t\t!self.is_taken()\n\t}\n\n\t// Clear the Page structure and all associated allocations.\n\tpub fn clear(&mut self) {\n\t\tself.flags = PageBits::Empty.val();\n\t}\n\n\t// Set a certain flag. We ran into trouble here since PageBits\n\t// is an enumeration and we haven't implemented the BitOr Trait\n\t// on it.\n\tpub fn set_flag(&mut self, flag: PageBits) {\n\t\tself.flags |= flag.val();\n\t}\n\n\tpub fn clear_flag(&mut self, flag: PageBits) {\n\t\tself.flags &= !(flag.val());\n\t}\n}\n\n/// Initialize the allocation system. There are several ways that we can\n/// implement the page allocator:\n/// 1. Free list (singly linked list where it starts at the first free\n/// allocation) 2. Bookkeeping list (structure contains a taken and length)\n/// 3. Allocate one Page structure per 4096 bytes (this is what I chose)\n/// 4. Others\npub fn init() {\n\tunsafe {\n\t\t// let desc_per_page = PAGE_SIZE / size_of::<Page>();\n\t\tlet num_pages = HEAP_SIZE / PAGE_SIZE;\n\t\t// let num_desc_pages = num_pages / desc_per_page;\n\t\tlet ptr = HEAP_START as *mut Page;\n\t\t// Clear all pages to make sure that they aren't accidentally\n\t\t// taken\n\t\tfor i in 0..num_pages {\n\t\t\t(*ptr.add(i)).clear();\n\t\t}\n\t\t// Determine where the actual useful memory starts. This will be\n\t\t// after all Page structures. We also must align the ALLOC_START\n\t\t// to a page-boundary (PAGE_SIZE = 4096). ALLOC_START =\n\t\t// (HEAP_START + num_pages * size_of::<Page>() + PAGE_SIZE - 1)\n\t\t// & !(PAGE_SIZE - 1);\n\t\tALLOC_START = align_val(\n\t\t                        HEAP_START\n\t\t                        + num_pages * size_of::<Page>(),\n\t\t                        PAGE_ORDER,\n\t\t);\n\t}\n}\n\n/// Allocate a page or multiple pages\n/// pages: the number of PAGE_SIZE pages to allocate\npub fn alloc(pages: usize) -> *mut u8 {\n\t// We have to find a contiguous allocation of pages\n\tassert!(pages > 0);\n\tunsafe {\n\t\t// We create a Page structure for each page on the heap. We\n\t\t// actually might have more since HEAP_SIZE moves and so does\n\t\t// the size of our structure, but we'll only waste a few bytes.\n\t\tlet num_pages = HEAP_SIZE / PAGE_SIZE;\n\t\tlet ptr = HEAP_START as *mut Page;\n\t\tfor i in 0..num_pages - pages {\n\t\t\tlet mut found = false;\n\t\t\t// Check to see if this Page is free. If so, we have our\n\t\t\t// first candidate memory address.\n\t\t\tif (*ptr.add(i)).is_free() {\n\t\t\t\t// It was FREE! Yay!\n\t\t\t\tfound = true;\n\t\t\t\tfor j in i..i + pages {\n\t\t\t\t\t// Now check to see if we have a\n\t\t\t\t\t// contiguous allocation for all of the\n\t\t\t\t\t// request pages. If not, we should\n\t\t\t\t\t// check somewhere else.\n\t\t\t\t\tif (*ptr.add(j)).is_taken() {\n\t\t\t\t\t\tfound = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// We've checked to see if there are enough contiguous\n\t\t\t// pages to form what we need. If we couldn't, found\n\t\t\t// will be false, otherwise it will be true, which means\n\t\t\t// we've found valid memory we can allocate.\n\t\t\tif found {\n\t\t\t\tfor k in i..i + pages - 1 {\n\t\t\t\t\t(*ptr.add(k)).set_flag(PageBits::Taken);\n\t\t\t\t}\n\t\t\t\t// The marker for the last page is\n\t\t\t\t// PageBits::Last This lets us know when we've\n\t\t\t\t// hit the end of this particular allocation.\n\t\t\t\t(*ptr.add(i+pages-1)).set_flag(PageBits::Taken);\n\t\t\t\t(*ptr.add(i+pages-1)).set_flag(PageBits::Last);\n\t\t\t\t// The Page structures themselves aren't the\n\t\t\t\t// useful memory. Instead, there is 1 Page\n\t\t\t\t// structure per 4096 bytes starting at\n\t\t\t\t// ALLOC_START.\n\t\t\t\treturn (ALLOC_START + PAGE_SIZE * i)\n\t\t\t\t       as *mut u8;\n\t\t\t}\n\t\t}\n\t}\n\n\t// If we get here, that means that no contiguous allocation was\n\t// found.\n\tnull_mut()\n}\n\n/// Allocate and zero a page or multiple pages\n/// pages: the number of pages to allocate\n/// Each page is PAGE_SIZE which is calculated as 1 << PAGE_ORDER\n/// On RISC-V, this typically will be 4,096 bytes.\npub fn zalloc(pages: usize) -> *mut u8 {\n\t// Allocate and zero a page.\n\t// First, let's get the allocation\n\tlet ret = alloc(pages);\n\tif !ret.is_null() {\n\t\tlet size = (PAGE_SIZE * pages) / 8;\n\t\tlet big_ptr = ret as *mut u64;\n\t\tfor i in 0..size {\n\t\t\t// We use big_ptr so that we can force an\n\t\t\t// sd (store doubleword) instruction rather than\n\t\t\t// the sb. This means 8x fewer stores than before.\n\t\t\t// Typically we have to be concerned about remaining\n\t\t\t// bytes, but fortunately 4096 % 8 = 0, so we\n\t\t\t// won't have any remaining bytes.\n\t\t\tunsafe {\n\t\t\t\t(*big_ptr.add(i)) = 0;\n\t\t\t}\n\t\t}\n\t}\n\tret\n}\n\n/// Deallocate a page by its pointer\n/// The way we've structured this, it will automatically coalesce\n/// contiguous pages.\npub fn dealloc(ptr: *mut u8) {\n\t// Make sure we don't try to free a null pointer.\n\tassert!(!ptr.is_null());\n\tunsafe {\n\t\tlet addr =\n\t\t\tHEAP_START + (ptr as usize - ALLOC_START) / PAGE_SIZE;\n\t\t// Make sure that the address makes sense. The address we\n\t\t// calculate here is the page structure, not the HEAP address!\n\t\tassert!(addr >= HEAP_START && addr < HEAP_START + HEAP_SIZE);\n\t\tlet mut p = addr as *mut Page;\n\t\t// Keep clearing pages until we hit the last page.\n\t\twhile (*p).is_taken() && !(*p).is_last() {\n\t\t\t(*p).clear();\n\t\t\tp = p.add(1);\n\t\t}\n\t\t// If the following assertion fails, it is most likely\n\t\t// caused by a double-free.\n\t\tassert!(\n\t\t        (*p).is_last() == true,\n\t\t        \"Possible double-free detected! (Not taken found \\\n\t\t         before last)\"\n\t\t);\n\t\t// If we get here, we've taken care of all previous pages and\n\t\t// we are on the last page.\n\t\t(*p).clear();\n\t}\n}\n\n/// Print all page allocations\n/// This is mainly used for debugging.\npub fn print_page_allocations() {\n\tunsafe {\n\t\tlet num_pages = (HEAP_SIZE - (ALLOC_START - HEAP_START)) / PAGE_SIZE;\n\t\tlet mut beg = HEAP_START as *const Page;\n\t\tlet end = beg.add(num_pages);\n\t\tlet alloc_beg = ALLOC_START;\n\t\tlet alloc_end = ALLOC_START + num_pages * PAGE_SIZE;\n\t\tprintln!();\n\t\tprintln!(\n\t\t         \"PAGE ALLOCATION TABLE\\nMETA: {:p} -> {:p}\\nPHYS: \\\n\t\t          0x{:x} -> 0x{:x}\",\n\t\t         beg, end, alloc_beg, alloc_end\n\t\t);\n\t\tprintln!(\"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\");\n\t\tlet mut num = 0;\n\t\twhile beg < end {\n\t\t\tif (*beg).is_taken() {\n\t\t\t\tlet start = beg as usize;\n\t\t\t\tlet memaddr = ALLOC_START\n\t\t\t\t              + (start - HEAP_START)\n\t\t\t\t                * PAGE_SIZE;\n\t\t\t\tprint!(\"0x{:x} => \", memaddr);\n\t\t\t\tloop {\n\t\t\t\t\tnum += 1;\n\t\t\t\t\tif (*beg).is_last() {\n\t\t\t\t\t\tlet end = beg as usize;\n\t\t\t\t\t\tlet memaddr = ALLOC_START\n\t\t\t\t\t\t              + (end\n\t\t\t\t\t\t                 - HEAP_START)\n\t\t\t\t\t\t                * PAGE_SIZE\n\t\t\t\t\t\t              + PAGE_SIZE - 1;\n\t\t\t\t\t\tprint!(\n\t\t\t\t\t\t       \"0x{:x}: {:>3} page(s)\",\n\t\t\t\t\t\t       memaddr,\n\t\t\t\t\t\t       (end - start + 1)\n\t\t\t\t\t\t);\n\t\t\t\t\t\tprintln!(\".\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tbeg = beg.add(1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbeg = beg.add(1);\n\t\t}\n\t\tprintln!(\"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\");\n\t\tprintln!(\n\t\t         \"Allocated: {:>6} pages ({:>10} bytes).\",\n\t\t         num,\n\t\t         num * PAGE_SIZE\n\t\t);\n\t\tprintln!(\n\t\t         \"Free     : {:>6} pages ({:>10} bytes).\",\n\t\t         num_pages - num,\n\t\t         (num_pages - num) * PAGE_SIZE\n\t\t);\n\t\tprintln!();\n\t}\n}\n\n// ////////////////////////////////\n// // MMU Routines\n// ////////////////////////////////\n\n// Represent (repr) our entry bits as\n// unsigned 64-bit integers.\n#[repr(i64)]\n#[derive(Copy, Clone)]\npub enum EntryBits {\n\tNone = 0,\n\tValid = 1 << 0,\n\tRead = 1 << 1,\n\tWrite = 1 << 2,\n\tExecute = 1 << 3,\n\tUser = 1 << 4,\n\tGlobal = 1 << 5,\n\tAccess = 1 << 6,\n\tDirty = 1 << 7,\n\n\t// Convenience combinations\n\tReadWrite = 1 << 1 | 1 << 2,\n\tReadExecute = 1 << 1 | 1 << 3,\n\tReadWriteExecute = 1 << 1 | 1 << 2 | 1 << 3,\n\n\t// User Convenience Combinations\n\tUserReadWrite = 1 << 1 | 1 << 2 | 1 << 4,\n\tUserReadExecute = 1 << 1 | 1 << 3 | 1 << 4,\n\tUserReadWriteExecute = 1 << 1 | 1 << 2 | 1 << 3 | 1 << 4,\n}\n\n// Helper functions to convert the enumeration\n// into an i64, which is what our page table\n// entries will be.\nimpl EntryBits {\n\tpub fn val(self) -> i64 {\n\t\tself as i64\n\t}\n}\n\n// A single entry. We're using an i64 so that\n// this will sign-extend rather than zero-extend\n// since RISC-V requires that the reserved sections\n// take on the most significant bit.\npub struct Entry {\n\tpub entry: i64,\n}\n\n// The Entry structure describes one of the 512 entries per table, which is\n// described in the RISC-V privileged spec Figure 4.18.\nimpl Entry {\n\tpub fn is_valid(&self) -> bool {\n\t\tself.get_entry() & EntryBits::Valid.val() != 0\n\t}\n\n\t// The first bit (bit index #0) is the V bit for\n\t// valid.\n\tpub fn is_invalid(&self) -> bool {\n\t\t!self.is_valid()\n\t}\n\n\t// A leaf has one or more RWX bits set\n\tpub fn is_leaf(&self) -> bool {\n\t\tself.get_entry() & 0xe != 0\n\t}\n\n\tpub fn is_branch(&self) -> bool {\n\t\t!self.is_leaf()\n\t}\n\n\tpub fn set_entry(&mut self, entry: i64) {\n\t\tself.entry = entry;\n\t}\n\n\tpub fn get_entry(&self) -> i64 {\n\t\tself.entry\n\t}\n}\n\n// Table represents a single table, which contains 512 (2^9), 64-bit entries.\npub struct Table {\n\tpub entries: [Entry; 512],\n}\n\nimpl Table {\n\tpub fn len() -> usize {\n\t\t512\n\t}\n}\n\n/// Map a virtual address to a physical address using 4096-byte page\n/// size.\n/// root: a mutable reference to the root Table\n/// vaddr: The virtual address to map\n/// paddr: The physical address to map\n/// bits: An OR'd bitset containing the bits the leaf should have.\n///       The bits should contain only the following:\n///          Read, Write, Execute, User, and/or Global\n///       The bits MUST include one or more of the following:\n///          Read, Write, Execute\n///       The valid bit automatically gets added.\npub fn map(root: &mut Table,\n           vaddr: usize,\n           paddr: usize,\n           bits: i64,\n           level: usize)\n{\n\t// Make sure that Read, Write, or Execute have been provided\n\t// otherwise, we'll leak memory and always create a page fault.\n\tassert!(bits & 0xe != 0);\n\t// Extract out each VPN from the virtual address\n\t// On the virtual address, each VPN is exactly 9 bits,\n\t// which is why we use the mask 0x1ff = 0b1_1111_1111 (9 bits)\n\tlet vpn = [\n\t           // VPN[0] = vaddr[20:12]\n\t           (vaddr >> 12) & 0x1ff,\n\t           // VPN[1] = vaddr[29:21]\n\t           (vaddr >> 21) & 0x1ff,\n\t           // VPN[2] = vaddr[38:30]\n\t           (vaddr >> 30) & 0x1ff,\n\t];\n\n\t// Just like the virtual address, extract the physical address\n\t// numbers (PPN). However, PPN[2] is different in that it stores\n\t// 26 bits instead of 9. Therefore, we use,\n\t// 0x3ff_ffff = 0b11_1111_1111_1111_1111_1111_1111 (26 bits).\n\tlet ppn = [\n\t           // PPN[0] = paddr[20:12]\n\t           (paddr >> 12) & 0x1ff,\n\t           // PPN[1] = paddr[29:21]\n\t           (paddr >> 21) & 0x1ff,\n\t           // PPN[2] = paddr[55:30]\n\t           (paddr >> 30) & 0x3ff_ffff,\n\t];\n\t// We will use this as a floating reference so that we can set\n\t// individual entries as we walk the table.\n\tlet mut v = &mut root.entries[vpn[2]];\n\t// Now, we're going to traverse the page table and set the bits\n\t// properly. We expect the root to be valid, however we're required to\n\t// create anything beyond the root.\n\t// In Rust, we create a range iterator using the .. operator.\n\t// The .rev() will reverse the iteration since we need to start with\n\t// VPN[2] The .. operator is inclusive on start but exclusive on end.\n\t// So, (0..2) will iterate 0 and 1.\n\tfor i in (level..2).rev() {\n\t\tif !v.is_valid() {\n\t\t\t// Allocate a page\n\t\t\tlet page = zalloc(1);\n\t\t\t// The page is already aligned by 4,096, so store it\n\t\t\t// directly The page is stored in the entry shifted\n\t\t\t// right by 2 places.\n\t\t\tv.set_entry(\n\t\t\t            (page as i64 >> 2)\n\t\t\t            | EntryBits::Valid.val(),\n\t\t\t);\n\t\t}\n\t\tlet entry = ((v.get_entry() & !0x3ff) << 2) as *mut Entry;\n\t\tv = unsafe { entry.add(vpn[i]).as_mut().unwrap() };\n\t}\n\t// When we get here, we should be at VPN[0] and v should be pointing to\n\t// our entry.\n\t// The entry structure is Figure 4.18 in the RISC-V Privileged\n\t// Specification\n\tlet entry = (ppn[2] << 28) as i64 |   // PPN[2] = [53:28]\n\t            (ppn[1] << 19) as i64 |   // PPN[1] = [27:19]\n\t\t\t\t(ppn[0] << 10) as i64 |   // PPN[0] = [18:10]\n\t\t\t\tbits |                    // Specified bits, such as User, Read, Write, etc\n\t\t\t\tEntryBits::Valid.val() |  // Valid bit\n\t\t\t\tEntryBits::Dirty.val() |  // Some machines require this to =1\n\t\t\t\tEntryBits::Access.val()   // Just like dirty, some machines require this\n\t\t\t\t;\n\t// Set the entry. V should be set to the correct pointer by the loop\n\t// above.\n\tv.set_entry(entry);\n}\n\n/// Unmaps and frees all memory associated with a table.\n/// root: The root table to start freeing.\n/// NOTE: This does NOT free root directly. This must be\n/// freed manually.\n/// The reason we don't free the root is because it is\n/// usually embedded into the Process structure.\npub fn unmap(root: &mut Table) {\n\t// Start with level 2\n\tfor lv2 in 0..Table::len() {\n\t\tlet ref entry_lv2 = root.entries[lv2];\n\t\tif entry_lv2.is_valid() && entry_lv2.is_branch() {\n\t\t\t// This is a valid entry, so drill down and free.\n\t\t\tlet memaddr_lv1 = (entry_lv2.get_entry() & !0x3ff) << 2;\n\t\t\tlet table_lv1 = unsafe {\n\t\t\t\t// Make table_lv1 a mutable reference instead of\n\t\t\t\t// a pointer.\n\t\t\t\t(memaddr_lv1 as *mut Table).as_mut().unwrap()\n\t\t\t};\n\t\t\tfor lv1 in 0..Table::len() {\n\t\t\t\tlet ref entry_lv1 = table_lv1.entries[lv1];\n\t\t\t\tif entry_lv1.is_valid() && entry_lv1.is_branch()\n\t\t\t\t{\n\t\t\t\t\tlet memaddr_lv0 = (entry_lv1.get_entry()\n\t\t\t\t\t                   & !0x3ff) << 2;\n\t\t\t\t\t// The next level is level 0, which\n\t\t\t\t\t// cannot have branches, therefore,\n\t\t\t\t\t// we free here.\n\t\t\t\t\tdealloc(memaddr_lv0 as *mut u8);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdealloc(memaddr_lv1 as *mut u8);\n\t\t}\n\t}\n}\n\n/// Walk the page table to convert a virtual address to a\n/// physical address.\n/// If a page fault would occur, this returns None\n/// Otherwise, it returns Some with the physical address.\npub fn virt_to_phys(root: &Table, vaddr: usize) -> Option<usize> {\n\t// Walk the page table pointed to by root\n\tlet vpn = [\n\t           // VPN[0] = vaddr[20:12]\n\t           (vaddr >> 12) & 0x1ff,\n\t           // VPN[1] = vaddr[29:21]\n\t           (vaddr >> 21) & 0x1ff,\n\t           // VPN[2] = vaddr[38:30]\n\t           (vaddr >> 30) & 0x1ff,\n\t];\n\n\tlet mut v = &root.entries[vpn[2]];\n\tfor i in (0..=2).rev() {\n\t\tif v.is_invalid() {\n\t\t\t// This is an invalid entry, page fault.\n\t\t\tbreak;\n\t\t}\n\t\telse if v.is_leaf() {\n\t\t\t// According to RISC-V, a leaf can be at any level.\n\n\t\t\t// The offset mask masks off the PPN. Each PPN is 9\n\t\t\t// bits and they start at bit #12. So, our formula\n\t\t\t// 12 + i * 9\n\t\t\tlet off_mask = (1 << (12 + i * 9)) - 1;\n\t\t\tlet vaddr_pgoff = vaddr & off_mask;\n\t\t\tlet addr = ((v.get_entry() << 2) as usize) & !off_mask;\n\t\t\treturn Some(addr | vaddr_pgoff);\n\t\t}\n\t\t// Set v to the next entry which is pointed to by this\n\t\t// entry. However, the address was shifted right by 2 places\n\t\t// when stored in the page table entry, so we shift it left\n\t\t// to get it back into place.\n\t\tlet entry = ((v.get_entry() & !0x3ff) << 2) as *const Entry;\n\t\t// We do i - 1 here, however we should get None or Some() above\n\t\t// before we do 0 - 1 = -1.\n\t\tv = unsafe { entry.add(vpn[i - 1]).as_ref().unwrap() };\n\t}\n\n\t// If we get here, we've exhausted all valid tables and haven't\n\t// found a leaf.\n\tNone\n}\n"
  },
  {
    "path": "risc_v/chapters/ch9/src/plic.rs",
    "content": "// plic.rs\n// Platform Level Interrupt Controller (PLIC)\n// Stephen Marz\n// 1 Nov 2019\n\nuse crate::uart::Uart;\nuse crate::virtio;\n\nconst PLIC_PRIORITY: usize = 0x0c00_0000;\nconst PLIC_PENDING: usize = 0x0c00_1000;\nconst PLIC_INT_ENABLE: usize = 0x0c00_2000;\nconst PLIC_THRESHOLD: usize = 0x0c20_0000;\nconst PLIC_CLAIM: usize = 0x0c20_0004;\n\n// Each register is 4-bytes (u32)\n// The PLIC is an external interrupt controller. The one\n// used by QEMU virt is the same as the SiFive PLIC.\n// https://sifive.cdn.prismic.io/sifive%2F834354f0-08e6-423c-bf1f-0cb58ef14061_fu540-c000-v1.0.pdf\n\n// Chapter 10 explains the priority, pending, interrupt enable, threshold and claims\n\n// The virt machine has the following external interrupts (from Qemu source):\n// Interrupt 0 is a \"null\" interrupt and is hardwired to 0.\n// VIRTIO = [1..8]\n// UART0 = 10\n// PCIE = [32..35]\n\n\n/// Get the next available interrupt. This is the \"claim\" process.\n/// The plic will automatically sort by priority and hand us the\n/// ID of the interrupt. For example, if the UART is interrupting\n/// and it's next, we will get the value 10.\npub fn next() -> Option<u32> {\n    let claim_reg = PLIC_CLAIM as *const u32;\n    let claim_no;\n    // The claim register is filled with the highest-priority, enabled interrupt.\n    unsafe {\n        claim_no = claim_reg.read_volatile();\n    }\n    if claim_no == 0 {\n        // The interrupt 0 is hardwired to 0, which tells us that there is no\n        // interrupt to claim, hence we return None.\n        None\n    }\n    else {\n        // If we get here, we've gotten a non-0 interrupt.\n        Some(claim_no)\n    }\n}\n\n/// Complete a pending interrupt by id. The id should come\n/// from the next() function above.\npub fn complete(id: u32) {\n    let complete_reg = PLIC_CLAIM as *mut u32;\n    unsafe {\n        // We actually write a u32 into the entire complete_register.\n        // This is the same register as the claim register, but it can\n        // differentiate based on whether we're reading or writing.\n        complete_reg.write_volatile(id);\n    }\n}\n\n/// Set the global threshold. The threshold can be a value [0..7].\n/// The PLIC will mask any interrupts at or below the given threshold.\n/// This means that a threshold of 7 will mask ALL interrupts and\n/// a threshold of 0 will allow ALL interrupts.\npub fn set_threshold(tsh: u8) {\n    // We do tsh because we're using a u8, but our maximum number\n    // is a 3-bit 0b111. So, we and with 7 (0b111) to just get the\n    // last three bits.\n    let actual_tsh = tsh & 7;\n    let tsh_reg = PLIC_THRESHOLD as *mut u32;\n    unsafe {\n        tsh_reg.write_volatile(actual_tsh as u32);\n    }\n}\n\n/// See if a given interrupt id is pending.\npub fn is_pending(id: u32) -> bool {\n    let pend = PLIC_PENDING as *const u32;\n    let actual_id = 1 << id;\n    let pend_ids;\n    unsafe {\n        pend_ids = pend.read_volatile();\n    }\n    actual_id & pend_ids != 0\n}\n\n/// Enable a given interrupt id\npub fn enable(id: u32) {\n    let enables = PLIC_INT_ENABLE as *mut u32;\n    let actual_id = 1 << id;\n    unsafe {\n        // Unlike the complete and claim registers, the plic_int_enable\n        // register is a bitset where the id is the bit index. The register\n        // is a 32-bit register, so that gives us enables for interrupts\n        // 31 through 1 (0 is hardwired to 0).\n        enables.write_volatile(enables.read_volatile() | actual_id);\n    }\n}\n\n/// Set a given interrupt priority to the given priority.\n/// The priority must be [0..7]\npub fn set_priority(id: u32, prio: u8) {\n    let actual_prio = prio as u32 & 7;\n    let prio_reg = PLIC_PRIORITY as *mut u32;\n    unsafe {\n        // The offset for the interrupt id is:\n        // PLIC_PRIORITY + 4 * id\n        // Since we're using pointer arithmetic on a u32 type,\n        // it will automatically multiply the id by 4.\n        prio_reg.add(id as usize).write_volatile(actual_prio);\n    }\n}\n\npub fn handle_interrupt() {\n    if let Some(interrupt) = next() {\n        // If we get here, we've got an interrupt from the claim register. The PLIC will\n        // automatically prioritize the next interrupt, so when we get it from claim, it\n        // will be the next in priority order.\n        match interrupt {\n            1..=8 => {\n                virtio::handle_interrupt(interrupt);\n            },\n            10 => { // Interrupt 10 is the UART interrupt.\n                // We would typically set this to be handled out of the interrupt context,\n                // but we're testing here! C'mon!\n                // We haven't yet used the singleton pattern for my_uart, but remember, this\n                // just simply wraps 0x1000_0000 (UART).\n                let mut my_uart = Uart::new(0x1000_0000);\n                // If we get here, the UART better have something! If not, what happened??\n                if let Some(c) = my_uart.get() {\n                    // If you recognize this code, it used to be in the lib.rs under kmain(). That\n                    // was because we needed to poll for UART data. Now that we have interrupts,\n                    // here it goes!\n                    match c {\n                        8 => {\n                            // This is a backspace, so we\n                            // essentially have to write a space and\n                            // backup again:\n                            print!(\"{} {}\", 8 as char, 8 as char);\n                        },\n                        10 | 13 => {\n                            // Newline or carriage-return\n                            println!();\n                        },\n                        _ => {\n                            print!(\"{}\", c as char);\n                        },\n                    }\n                }\n        \n            },\n            // Non-UART interrupts go here and do nothing.\n            _ => {\n                println!(\"Non-UART external interrupt: {}\", interrupt);\n            }\n        }\n        // We've claimed it, so now say that we've handled it. This resets the interrupt pending\n        // and allows the UART to interrupt again. Otherwise, the UART will get \"stuck\".\n        complete(interrupt);\n    }\n}\n"
  },
  {
    "path": "risc_v/chapters/ch9/src/process.rs",
    "content": "// process.rs\n// Kernel and user processes\n// Stephen Marz\n// 27 Nov 2019\n\nuse crate::{cpu::{TrapFrame, satp_fence_asid, build_satp, SatpMode},\n            page::{alloc,\n                   dealloc,\n                   map,\n                   unmap,\n                   zalloc,\n                   EntryBits,\n                   Table,\n                   PAGE_SIZE}};\nuse alloc::collections::vec_deque::VecDeque;\n\n// How many pages are we going to give a process for their\n// stack?\nconst STACK_PAGES: usize = 2;\n// We want to adjust the stack to be at the bottom of the memory allocation\n// regardless of where it is on the kernel heap.\nconst STACK_ADDR: usize = 0x1_0000_0000;\n// All processes will have a defined starting point in virtual memory.\n// We will use this later when we load processes from disk.\n// const PROCESS_STARTING_ADDR: usize = 0x2000_0000;\n\n// Here, we store a process list. It uses the global allocator\n// that we made before and its job is to store all processes.\n// We will have this list OWN the process. So, anytime we want\n// the process, we will consult the process list.\n// Using an Option here is one method of creating a \"lazy static\".\n// Rust requires that all statics be initialized, but all\n// initializations must be at compile-time. We cannot allocate\n// a VecDeque at compile time, so we are somewhat forced to\n// do this.\npub static mut PROCESS_LIST: Option<VecDeque<Process>> = None;\n// We can search through the process list to get a new PID, but\n// it's probably easier and faster just to increase the pid:\nstatic mut NEXT_PID: u16 = 1;\n\nextern \"C\" {\n\tfn make_syscall(a: usize) -> usize;\n}\n\n/// We will eventually move this function out of here, but its\n/// job is just to take a slot in the process list.\nfn init_process() {\n\t// We can't do much here until we have system calls because\n\t// we're running in User space.\n\tlet mut i: usize = 0;\n\tloop {\n\t\ti += 1;\n\t\tif i > 100_000_000 {\n\t\t\tunsafe {\n\t\t\t\tmake_syscall(1);\n\t\t\t}\n\t\t\ti = 0;\n\t\t}\n\t}\n}\n\n/// Add a process given a function address and then\n/// push it onto the LinkedList. Uses Process::new_default\n/// to create a new stack, etc.\npub fn add_process_default(pr: fn()) {\n\tunsafe {\n\t\t// This is the Rust-ism that really trips up C++ programmers.\n\t\t// PROCESS_LIST is wrapped in an Option<> enumeration, which\n\t\t// means that the Option owns the Deque. We can only borrow from\n\t\t// it or move ownership to us. In this case, we choose the\n\t\t// latter, where we move ownership to us, add a process, and\n\t\t// then move ownership back to the PROCESS_LIST.\n\t\t// This allows mutual exclusion as anyone else trying to grab\n\t\t// the process list will get None rather than the Deque.\n\t\tif let Some(mut pl) = PROCESS_LIST.take() {\n\t\t\t// .take() will replace PROCESS_LIST with None and give\n\t\t\t// us the only copy of the Deque.\n\t\t\tlet p = Process::new_default(pr);\n\t\t\tpl.push_back(p);\n\t\t\t// Now, we no longer need the owned Deque, so we hand it\n\t\t\t// back by replacing the PROCESS_LIST's None with the\n\t\t\t// Some(pl).\n\t\t\tPROCESS_LIST.replace(pl);\n\t\t}\n\t\t// TODO: When we get to multi-hart processing, we need to keep\n\t\t// trying to grab the process list. We can do this with an\n\t\t// atomic instruction. but right now, we're a single-processor\n\t\t// computer.\n\t}\n}\n\n/// This should only be called once, and its job is to create\n/// the init process. Right now, this process is in the kernel,\n/// but later, it should call the shell.\npub fn init() -> usize {\n\tunsafe {\n\t\tPROCESS_LIST = Some(VecDeque::with_capacity(15));\n\t\tadd_process_default(init_process);\n\t\t// Ugh....Rust is giving me fits over here!\n\t\t// I just want a memory address to the trap frame, but\n\t\t// due to the borrow rules of Rust, I'm fighting here. So,\n\t\t// instead, let's move the value out of PROCESS_LIST, get\n\t\t// the address, and then move it right back in.\n\t\tlet pl = PROCESS_LIST.take().unwrap();\n\t\tlet p = pl.front().unwrap().frame;\n\t\t// let frame = p as *const TrapFrame as usize;\n\t\t// println!(\"Init's frame is at 0x{:08x}\", frame);\n\t\t// Put the process list back in the global.\n\t\tPROCESS_LIST.replace(pl);\n\t\t// Return the first instruction's address to execute.\n\t\t// Since we use the MMU, all start here.\n\t\t(*p).pc\n\t}\n}\n\n// Our process must be able to sleep, wait, or run.\n// Running - means that when the scheduler finds this process, it can run it.\n// Sleeping - means that the process is waiting on a certain amount of time.\n// Waiting - means that the process is waiting on I/O\n// Dead - We should never get here, but we can flag a process as Dead and clean\n//        it out of the list later.\n#[repr(u8)]\npub enum ProcessState {\n\tRunning,\n\tSleeping,\n\tWaiting,\n\tDead,\n}\n\n// Let's represent this in C ABI. We do this\n// because we need to access some of the fields\n// in assembly. Rust gets to choose how it orders\n// the fields unless we represent the structure in\n// C-style ABI.\n#[repr(C)]\npub struct Process {\n\tframe:           *mut TrapFrame,\n\tstack:           *mut u8,\n\tpid:             u16,\n\troot:            *mut Table,\n\tstate:           ProcessState,\n\tdata:            ProcessData,\n\tsleep_until:\t usize,\n}\n\nimpl Process {\n\tpub fn get_frame_address(&self) -> usize {\n\t\tself.frame as usize\n\t}\n\tpub fn get_program_counter(&self) -> usize {\n\t\tunsafe { (*self.frame).pc }\n\t}\n\tpub fn get_table_address(&self) -> usize {\n\t\tself.root as usize\n\t}\n\tpub fn get_state(&self) -> &ProcessState {\n\t\t&self.state\n\t}\n\tpub fn get_pid(&self) -> u16 {\n\t\tself.pid\n\t}\n\tpub fn get_sleep_until(&self) -> usize {\n\t\tself.sleep_until\n\t}\n\tpub fn new_default(func: fn()) -> Self {\n\t\tlet func_addr = func as usize;\n\t\tlet func_vaddr = func_addr; //- 0x6000_0000;\n\t\t// println!(\"func_addr = {:x} -> {:x}\", func_addr, func_vaddr);\n\t\t// We will convert NEXT_PID below into an atomic increment when\n\t\t// we start getting into multi-hart processing. For now, we want\n\t\t// a process. Get it to work, then improve it!\n\t\tlet mut ret_proc =\n\t\t\tProcess { frame:           zalloc(1) as *mut TrapFrame,\n\t\t\t          stack:           alloc(STACK_PAGES),\n\t\t\t          pid:             unsafe { NEXT_PID },\n\t\t\t          root:            zalloc(1) as *mut Table,\n\t\t\t          state:           ProcessState::Running,\n\t\t\t\t\t  data:            ProcessData::zero(), \n\t\t\t\t\t  sleep_until:     0\n\t\t\t\t\t};\n\t\tunsafe {\n\t\t\tsatp_fence_asid(NEXT_PID as usize);\n\t\t\tNEXT_PID += 1;\n\t\t}\n\t\t// Now we move the stack pointer to the bottom of the\n\t\t// allocation. The spec shows that register x2 (2) is the stack\n\t\t// pointer.\n\t\t// We could use ret_proc.stack.add, but that's an unsafe\n\t\t// function which would require an unsafe block. So, convert it\n\t\t// to usize first and then add PAGE_SIZE is better.\n\t\t// We also need to set the stack adjustment so that it is at the\n\t\t// bottom of the memory and far away from heap allocations.\n\t\tlet saddr = ret_proc.stack as usize;\n\t\tunsafe {\n\t\t\t(*ret_proc.frame).pc = func_vaddr;\n\t\t\t(*ret_proc.frame).regs[2] = STACK_ADDR + PAGE_SIZE * STACK_PAGES;\n\t\t}\n\t\t// Map the stack on the MMU\n\t\tlet pt;\n\t\tunsafe {\n\t\t\tpt = &mut *ret_proc.root;\n\t\t\t(*ret_proc.frame).satp = build_satp(SatpMode::Sv39, ret_proc.pid as usize, ret_proc.root as usize);\n\t\t}\n\t\t// We need to map the stack onto the user process' virtual\n\t\t// memory This gets a little hairy because we need to also map\n\t\t// the function code too.\n\t\tfor i in 0..STACK_PAGES {\n\t\t\tlet addr = i * PAGE_SIZE;\n\t\t\tmap(\n\t\t\t    pt,\n\t\t\t    STACK_ADDR + addr,\n\t\t\t    saddr + addr,\n\t\t\t    EntryBits::UserReadWrite.val(),\n\t\t\t    0,\n\t\t\t);\n\t\t\t// println!(\"Set stack from 0x{:016x} -> 0x{:016x}\", STACK_ADDR + addr, saddr + addr);\n\t\t}\n\t\t// Map the program counter on the MMU and other bits\n\t\tfor i in 0..=100 {\n\t\t\tlet modifier = i * 0x1000;\n\t\t\tmap(\n\t\t\t\tpt,\n\t\t\t\tfunc_vaddr + modifier,\n\t\t\t\tfunc_addr + modifier,\n\t\t\t\tEntryBits::UserReadWriteExecute.val(),\n\t\t\t\t0,\n\t\t\t);\n\t\t}\n\t\t// This is the make_syscall function\n\t\t// The reason we need this is because we're running a process\n\t\t// that is inside of the kernel. When we start loading from a block\n\t\t// devices, we can load the instructions anywhere in memory. \n\t\tmap(pt, 0x8000_0000, 0x8000_0000, EntryBits::UserReadExecute.val(), 0);\n\t\tret_proc\n\t}\n}\n\nimpl Drop for Process {\n\t/// Since we're storing ownership of a Process in the linked list,\n\t/// we can cause it to deallocate automatically when it is removed.\n\tfn drop(&mut self) {\n\t\t// We allocate the stack as a page.\n\t\tdealloc(self.stack);\n\t\t// This is unsafe, but it's at the drop stage, so we won't\n\t\t// be using this again.\n\t\tunsafe {\n\t\t\t// Remember that unmap unmaps all levels of page tables\n\t\t\t// except for the root. It also deallocates the memory\n\t\t\t// associated with the tables.\n\t\t\tunmap(&mut *self.root);\n\t\t}\n\t\tdealloc(self.root as *mut u8);\n\t}\n}\n\n// The private data in a process contains information\n// that is relevant to where we are, including the path\n// and open file descriptors.\n// We will allow dead code for now until we have a need for the\n// private process data. This is essentially our resource control block (RCB).\n#[allow(dead_code)]\npub struct ProcessData {\n\tcwd_path: [u8; 128],\n}\n\n// This is private data that we can query with system calls.\n// If we want to implement CFQ (completely fair queuing), which\n// is a per-process block queuing algorithm, we can put that here.\nimpl ProcessData {\n\tpub fn zero() -> Self {\n\t\tProcessData { cwd_path: [0; 128], }\n\t}\n}\n"
  },
  {
    "path": "risc_v/chapters/ch9/src/rng.rs",
    "content": "// rng.rs\n// Random number generator using VirtIO\n// Stephen Marz\n// 16 March 2020\n\npub fn setup_entropy_device(_ptr: *mut u32) -> bool {\n\tfalse\n}"
  },
  {
    "path": "risc_v/chapters/ch9/src/sched.rs",
    "content": "// sched.rs\n// Simple process scheduler\n// Stephen Marz\n// 27 Dec 2019\n\nuse crate::process::{ProcessState, PROCESS_LIST};\n\npub fn schedule() -> usize {\n\tunsafe {\n\t\tif let Some(mut pl) = PROCESS_LIST.take() {\n\t\t\tpl.rotate_left(1);\n\t\t\tlet mut frame_addr: usize = 0;\n\t\t\t// let mut mepc: usize = 0;\n\t\t\t// let mut satp: usize = 0;\n\t\t\t// let mut pid: usize = 0;\n\t\t\tif let Some(prc) = pl.front() {\n\t\t\t\tmatch prc.get_state() {\n\t\t\t\t\tProcessState::Running => {\n\t\t\t\t\t\tframe_addr =\n\t\t\t\t\t\t\tprc.get_frame_address();\n\t\t\t\t\t\t// satp = prc.get_table_address();\n\t\t\t\t\t\t// pid = prc.get_pid() as usize;\n\t\t\t\t\t},\n\t\t\t\t\tProcessState::Sleeping => {},\n\t\t\t\t\t_ => {},\n\t\t\t\t}\n\t\t\t}\n\t\t\t// println!(\"Scheduling {}\", pid);\n\t\t\tPROCESS_LIST.replace(pl);\n\t\t\tif frame_addr != 0 {\n\t\t\t\t// MODE 8 is 39-bit virtual address MMU\n\t\t\t\t// I'm using the PID as the address space\n\t\t\t\t// identifier to hopefully help with (not?)\n\t\t\t\t// flushing the TLB whenever we switch\n\t\t\t\t// processes.\n\t\t\t\treturn frame_addr;\n\t\t\t}\n\t\t}\n\t}\n\t0\n}\n"
  },
  {
    "path": "risc_v/chapters/ch9/src/syscall.rs",
    "content": "// syscall.rs\n// System calls\n// Stephen Marz\n// 3 Jan 2020\n\nuse crate::cpu::TrapFrame;\n\npub fn do_syscall(mepc: usize, frame: *mut TrapFrame) -> usize {\n    let syscall_number;\n    unsafe {\n        // A0 is X10, so it's register number 10.\n        syscall_number = (*frame).regs[10];\n        // for i in 0..32 {\n        //     print!(\"regs[{:02}] = 0x{:08x}    \", i, (*frame).regs[i]);\n        //     if (i+1) % 4 == 0 {\n        //         println!();\n        //     }\n        // }    \n    }\n    match syscall_number {\n        0 => {\n            // Exit\n            // Currently, we cannot kill a process, it runs forever. We will delete\n            // the process later and free the resources, but for now, we want to get\n            // used to how processes will be scheduled on the CPU.\n            mepc + 4\n        },\n        1 => {\n            println!(\"Test syscall\");\n            mepc + 4\n        },\n        _ => {\n            println!(\"Unknown syscall number {}\", syscall_number);\n            mepc + 4\n        }\n    }\n}"
  },
  {
    "path": "risc_v/chapters/ch9/src/trap.rs",
    "content": "// trap.rs\n// Trap routines\n// Stephen Marz\n// 10 October 2019\n\nuse crate::cpu::{CONTEXT_SWITCH_TIME, TrapFrame};\nuse crate::plic;\nuse crate::syscall::do_syscall;\nuse crate::sched::schedule;\nuse crate::rust_switch_to_user;\n\n#[no_mangle]\n/// The m_trap stands for \"machine trap\". Right now, we are handling\n/// all traps at machine mode. In this mode, we can figure out what's\n/// going on and send a trap where it needs to be. Remember, in machine\n/// mode and in this trap, interrupts are disabled and the MMU is off.\nextern \"C\" fn m_trap(epc: usize,\n                     tval: usize,\n                     cause: usize,\n                     hart: usize,\n                     _status: usize,\n                     frame: *mut TrapFrame)\n                     -> usize\n{\n\t// We're going to handle all traps in machine mode. RISC-V lets\n\t// us delegate to supervisor mode, but switching out SATP (virtual memory)\n\t// gets hairy.\n\tlet is_async = {\n\t\tif cause >> 63 & 1 == 1 {\n\t\t\ttrue\n\t\t}\n\t\telse {\n\t\t\tfalse\n\t\t}\n\t};\n\t// The cause contains the type of trap (sync, async) as well as the cause\n\t// number. So, here we narrow down just the cause number.\n\tlet cause_num = cause & 0xfff;\n\tlet mut return_pc = epc;\n\tif is_async {\n\t\t// Asynchronous trap\n\t\tmatch cause_num {\n\t\t\t3 => {\n\t\t\t\t// Machine software\n\t\t\t\tprintln!(\"Machine software interrupt CPU #{}\", hart);\n\t\t\t},\n\t\t\t7 => {\n\t\t\t\t// This is the context-switch timer.\n\t\t\t\t// We would typically invoke the scheduler here to pick another\n\t\t\t\t// process to run.\n\t\t\t\t// Machine timer\n\t\t\t\t// println!(\"CTX\");\n\t\t\t\tlet frame = schedule();\n\t\t\t\tschedule_next_context_switch(1);\n\t\t\t\trust_switch_to_user(frame);\n\t\t\t},\n\t\t\t11 => {\n\t\t\t\t// Machine external (interrupt from Platform Interrupt Controller (PLIC))\n\t\t\t\t// println!(\"Machine external interrupt CPU#{}\", hart);\n\t\t\t\t// We will check the next interrupt. If the interrupt isn't available, this will\n\t\t\t\t// give us None. However, that would mean we got a spurious interrupt, unless we\n\t\t\t\t// get an interrupt from a non-PLIC source. This is the main reason that the PLIC\n\t\t\t\t// hardwires the id 0 to 0, so that we can use it as an error case.\n\t\t\t\tplic::handle_interrupt();\n\t\t\t},\n\t\t\t_ => {\n\t\t\t\tpanic!(\"Unhandled async trap CPU#{} -> {}\\n\", hart, cause_num);\n\t\t\t}\n\t\t}\n\t}\n\telse {\n\t\t// Synchronous trap\n\t\tmatch cause_num {\n\t\t\t2 => {\n\t\t\t\t// Illegal instruction\n\t\t\t\tpanic!(\"Illegal instruction CPU#{} -> 0x{:08x}: 0x{:08x}\\n\", hart, epc, tval);\n\t\t\t\t// We need while trues here until we have a functioning \"delete from scheduler\"\n\t\t\t\t// I use while true because Rust will warn us that it looks stupid.\n\t\t\t\t// This is what I want so that I remember to remove this and replace\n\t\t\t\t// them later.\n\t\t\t\twhile true {}\n\t\t\t},\n\t\t\t8 => {\n\t\t\t\t// Environment (system) call from User mode\n\t\t\t\t// println!(\"E-call from User mode! CPU#{} -> 0x{:08x}\", hart, epc);\n\t\t\t\treturn_pc = do_syscall(return_pc, frame);\n\t\t\t},\n\t\t\t9 => {\n\t\t\t\t// Environment (system) call from Supervisor mode\n\t\t\t\tprintln!(\"E-call from Supervisor mode! CPU#{} -> 0x{:08x}\", hart, epc);\n\t\t\t\treturn_pc = do_syscall(return_pc, frame);\n\t\t\t},\n\t\t\t11 => {\n\t\t\t\t// Environment (system) call from Machine mode\n\t\t\t\tpanic!(\"E-call from Machine mode! CPU#{} -> 0x{:08x}\\n\", hart, epc);\n\t\t\t},\n\t\t\t// Page faults\n\t\t\t12 => {\n\t\t\t\t// Instruction page fault\n\t\t\t\tprintln!(\"Instruction page fault CPU#{} -> 0x{:08x}: 0x{:08x}\", hart, epc, tval);\n\t\t\t\t// We need while trues here until we have a functioning \"delete from scheduler\"\n\t\t\t\twhile true {}\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t13 => {\n\t\t\t\t// Load page fault\n\t\t\t\tprintln!(\"Load page fault CPU#{} -> 0x{:08x}: 0x{:08x}\", hart, epc, tval);\n\t\t\t\t// We need while trues here until we have a functioning \"delete from scheduler\"\n\t\t\t\twhile true {}\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t15 => {\n\t\t\t\t// Store page fault\n\t\t\t\tprintln!(\"Store page fault CPU#{} -> 0x{:08x}: 0x{:08x}\", hart, epc, tval);\n\t\t\t\t// We need while trues here until we have a functioning \"delete from scheduler\"\n\t\t\t\twhile true {}\n\t\t\t\treturn_pc += 4;\n\t\t\t},\n\t\t\t_ => {\n\t\t\t\tpanic!(\"Unhandled sync trap CPU#{} -> {}\\n\", hart, cause_num);\n\t\t\t}\n\t\t}\n\t};\n\t// Finally, return the updated program counter\n\treturn_pc\n}\n\npub const MMIO_MTIMECMP: *mut u64 = 0x0200_4000usize as *mut u64;\npub const MMIO_MTIME: *const u64 = 0x0200_BFF8 as *const u64;\n\npub fn schedule_next_context_switch(qm: u16) {\n\t// This is much too slow for normal operations, but it gives us\n\t// a visual of what's happening behind the scenes.\n\tunsafe {\n\t\tMMIO_MTIMECMP.write_volatile(MMIO_MTIME.read_volatile().wrapping_add(CONTEXT_SWITCH_TIME * qm as u64));\n\t}\n}\n"
  },
  {
    "path": "risc_v/chapters/ch9/src/uart.rs",
    "content": "// uart.rs\n// UART routines and driver\n\nuse core::{convert::TryInto,\n           fmt::{Error, Write}};\n\npub struct Uart {\n\tbase_address: usize,\n}\n\nimpl Write for Uart {\n\tfn write_str(&mut self, out: &str) -> Result<(), Error> {\n\t\tfor c in out.bytes() {\n\t\t\tself.put(c);\n\t\t}\n\t\tOk(())\n\t}\n}\n\nimpl Uart {\n\tpub fn new(base_address: usize) -> Self {\n\t\tUart { base_address }\n\t}\n\n\tpub fn init(&mut self) {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\t// First, set the word length, which\n\t\t\t// are bits 0 and 1 of the line control register (LCR)\n\t\t\t// which is at base_address + 3\n\t\t\t// We can easily write the value 3 here or 0b11, but I'm\n\t\t\t// extending it so that it is clear we're setting two\n\t\t\t// individual fields\n\t\t\t//             Word 0     Word 1\n\t\t\t//             ~~~~~~     ~~~~~~\n\t\t\tlet lcr: u8 = (1 << 0) | (1 << 1);\n\t\t\tptr.add(3).write_volatile(lcr);\n\n\t\t\t// Now, enable the FIFO, which is bit index 0 of the\n\t\t\t// FIFO control register (FCR at offset 2).\n\t\t\t// Again, we can just write 1 here, but when we use left\n\t\t\t// shift, it's easier to see that we're trying to write\n\t\t\t// bit index #0.\n\t\t\tptr.add(2).write_volatile(1 << 0);\n\n\t\t\t// Enable receiver buffer interrupts, which is at bit\n\t\t\t// index 0 of the interrupt enable register (IER at\n\t\t\t// offset 1).\n\t\t\tptr.add(1).write_volatile(1 << 0);\n\n\t\t\t// If we cared about the divisor, the code below would\n\t\t\t// set the divisor from a global clock rate of 22.729\n\t\t\t// MHz (22,729,000 cycles per second) to a signaling\n\t\t\t// rate of 2400 (BAUD). We usually have much faster\n\t\t\t// signalling rates nowadays, but this demonstrates what\n\t\t\t// the divisor actually does. The formula given in the\n\t\t\t// NS16500A specification for calculating the divisor\n\t\t\t// is:\n\t\t\t// divisor = ceil( (clock_hz) / (baud_sps x 16) )\n\t\t\t// So, we substitute our values and get:\n\t\t\t// divisor = ceil( 22_729_000 / (2400 x 16) )\n\t\t\t// divisor = ceil( 22_729_000 / 38_400 )\n\t\t\t// divisor = ceil( 591.901 ) = 592\n\n\t\t\t// The divisor register is two bytes (16 bits), so we\n\t\t\t// need to split the value 592 into two bytes.\n\t\t\t// Typically, we would calculate this based on measuring\n\t\t\t// the clock rate, but again, for our purposes [qemu],\n\t\t\t// this doesn't really do anything.\n\t\t\tlet divisor: u16 = 592;\n\t\t\tlet divisor_least: u8 =\n\t\t\t\t(divisor & 0xff).try_into().unwrap();\n\t\t\tlet divisor_most: u8 =\n\t\t\t\t(divisor >> 8).try_into().unwrap();\n\n\t\t\t// Notice that the divisor register DLL (divisor latch\n\t\t\t// least) and DLM (divisor latch most) have the same\n\t\t\t// base address as the receiver/transmitter and the\n\t\t\t// interrupt enable register. To change what the base\n\t\t\t// address points to, we open the \"divisor latch\" by\n\t\t\t// writing 1 into the Divisor Latch Access Bit (DLAB),\n\t\t\t// which is bit index 7 of the Line Control Register\n\t\t\t// (LCR) which is at base_address + 3.\n\t\t\tptr.add(3).write_volatile(lcr | 1 << 7);\n\n\t\t\t// Now, base addresses 0 and 1 point to DLL and DLM,\n\t\t\t// respectively. Put the lower 8 bits of the divisor\n\t\t\t// into DLL\n\t\t\tptr.add(0).write_volatile(divisor_least);\n\t\t\tptr.add(1).write_volatile(divisor_most);\n\n\t\t\t// Now that we've written the divisor, we never have to\n\t\t\t// touch this again. In hardware, this will divide the\n\t\t\t// global clock (22.729 MHz) into one suitable for 2,400\n\t\t\t// signals per second. So, to once again get access to\n\t\t\t// the RBR/THR/IER registers, we need to close the DLAB\n\t\t\t// bit by clearing it to 0.\n\t\t\tptr.add(3).write_volatile(lcr);\n\t\t}\n\t}\n\n\tpub fn put(&mut self, c: u8) {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\tptr.add(0).write_volatile(c);\n\t\t}\n\t}\n\n\tpub fn get(&mut self) -> Option<u8> {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\tif ptr.add(5).read_volatile() & 1 == 0 {\n\t\t\t\t// The DR bit is 0, meaning no data\n\t\t\t\tNone\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// The DR bit is 1, meaning data!\n\t\t\t\tSome(ptr.add(0).read_volatile())\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "risc_v/chapters/ch9/src/virtio.rs",
    "content": "// virtio.rs\n// VirtIO routines for the VirtIO protocol\n// Stephen Marz\n// 10 March 2020\n\nuse crate::{block, block::setup_block_device, page::PAGE_SIZE};\nuse crate::rng::setup_entropy_device;\nuse core::mem::size_of;\n\n// Flags\n// Descriptor flags have VIRTIO_DESC_F as a prefix\n// Available flags have VIRTIO_AVAIL_F\n\npub const VIRTIO_DESC_F_NEXT: u16 = 1;\npub const VIRTIO_DESC_F_WRITE: u16 = 2;\npub const VIRTIO_DESC_F_INDIRECT: u16 = 4;\n\npub const VIRTIO_AVAIL_F_NO_INTERRUPT: u16 = 1;\n\npub const VIRTIO_USED_F_NO_NOTIFY: u16 = 1;\n\n// According to the documentation, this must be a power\n// of 2 for the new style. So, I'm changing this to use\n// 1 << instead because that will enforce this standard.\npub const VIRTIO_RING_SIZE: usize = 1 << 7;\n\n// VirtIO structures\n\n// The descriptor holds the data that we need to send to \n// the device. The address is a physical address and NOT\n// a virtual address. The len is in bytes and the flags are\n// specified above. Any descriptor can be chained, hence the\n// next field, but only if the F_NEXT flag is specified.\n#[repr(C)]\npub struct Descriptor {\n\tpub addr:  u64,\n\tpub len:   u32,\n\tpub flags: u16,\n\tpub next:  u16,\n}\n\n#[repr(C)]\npub struct Available {\n\tpub flags: u16,\n\tpub idx:   u16,\n\tpub ring:  [u16; VIRTIO_RING_SIZE],\n\tpub event: u16,\n}\n\n#[repr(C)]\npub struct UsedElem {\n\tpub id:  u32,\n\tpub len: u32,\n}\n\n#[repr(C)]\npub struct Used {\n\tpub flags: u16,\n\tpub idx:   u16,\n\tpub ring:  [UsedElem; VIRTIO_RING_SIZE],\n\tpub event: u16,\n}\n\n#[repr(C)]\npub struct Queue {\n\tpub desc:  [Descriptor; VIRTIO_RING_SIZE],\n\tpub avail: Available,\n\t// Calculating padding, we need the used ring to start on a page boundary. We take the page size, subtract the\n\t// amount the descriptor ring takes then subtract the available structure and ring.\n\tpub padding0: [u8; PAGE_SIZE - size_of::<Descriptor>() * VIRTIO_RING_SIZE - size_of::<Available>()],\n\tpub used:     Used,\n}\n\n// The MMIO transport is \"legacy\" in QEMU, so these registers represent\n// the legacy interface.\n#[repr(usize)]\npub enum MmioOffsets {\n\tMagicValue = 0x000,\n\tVersion = 0x004,\n\tDeviceId = 0x008,\n\tVendorId = 0x00c,\n\tHostFeatures = 0x010,\n\tHostFeaturesSel = 0x014,\n\tGuestFeatures = 0x020,\n\tGuestFeaturesSel = 0x024,\n\tGuestPageSize = 0x028,\n\tQueueSel = 0x030,\n\tQueueNumMax = 0x034,\n\tQueueNum = 0x038,\n\tQueueAlign = 0x03c,\n\tQueuePfn = 0x040,\n\tQueueNotify = 0x050,\n\tInterruptStatus = 0x060,\n\tInterruptAck = 0x064,\n\tStatus = 0x070,\n\tConfig = 0x100,\n}\n\n#[repr(usize)]\npub enum DeviceTypes {\n\tNone = 0,\n\tNetwork = 1,\n\tBlock = 2,\n\tConsole = 3,\n\tEntropy = 4,\n\tGpu = 16,\n\tInput = 18,\n\tMemory = 24,\n}\n\n// Enumerations in Rust aren't easy to convert back\n// and forth. Furthermore, we're going to use a u32\n// pointer, so we need to \"undo\" the scaling that\n// Rust will do with the .add() function.\nimpl MmioOffsets {\n\tpub fn val(self) -> usize {\n\t\tself as usize\n\t}\n\n\tpub fn scaled(self, scale: usize) -> usize {\n\t\tself.val() / scale\n\t}\n\n\tpub fn scale32(self) -> usize {\n\t\tself.scaled(4)\n\t}\n}\n\npub enum StatusField {\n\tAcknowledge = 1,\n\tDriver = 2,\n\tFailed = 128,\n\tFeaturesOk = 8,\n\tDriverOk = 4,\n\tDeviceNeedsReset = 64,\n}\n\n// The status field will be compared to the status register. So,\n// I've made some helper functions to checking that register easier.\nimpl StatusField {\n\tpub fn val(self) -> usize {\n\t\tself as usize\n\t}\n\n\tpub fn val32(self) -> u32 {\n\t\tself as u32\n\t}\n\n\tpub fn test(sf: u32, bit: StatusField) -> bool {\n\t\tsf & bit.val32() != 0\n\t}\n\n\tpub fn is_failed(sf: u32) -> bool {\n\t\tStatusField::test(sf, StatusField::Failed)\n\t}\n\n\tpub fn needs_reset(sf: u32) -> bool {\n\t\tStatusField::test(sf, StatusField::DeviceNeedsReset)\n\t}\n\n\tpub fn driver_ok(sf: u32) -> bool {\n\t\tStatusField::test(sf, StatusField::DriverOk)\n\t}\n\n\tpub fn features_ok(sf: u32) -> bool {\n\t\tStatusField::test(sf, StatusField::FeaturesOk)\n\t}\n}\n\n// We probably shouldn't put these here, but it'll help\n// with probing the bus, etc. These are architecture specific\n// which is why I say that.\npub const MMIO_VIRTIO_START: usize = 0x1000_1000;\npub const MMIO_VIRTIO_END: usize = 0x1000_8000;\npub const MMIO_VIRTIO_STRIDE: usize = 0x1000;\npub const MMIO_VIRTIO_MAGIC: u32 = 0x74_72_69_76;\n\n// The VirtioDevice is essentially a structure we can put into an array\n// to determine what virtio devices are attached to the system. Right now,\n// we're using the 1..=8  linearity of the VirtIO devices on QEMU to help\n// with reducing the data structure itself. Otherwise, we might be forced\n// to use an MMIO pointer.\npub struct VirtioDevice {\n\tpub devtype: DeviceTypes,\n}\n\nimpl VirtioDevice {\n\tpub const fn new() -> Self {\n\t\tVirtioDevice { devtype: DeviceTypes::None, }\n\t}\n\n\tpub const fn new_with(devtype: DeviceTypes) -> Self {\n\t\tVirtioDevice { devtype }\n\t}\n}\n\nstatic mut VIRTIO_DEVICES: [Option<VirtioDevice>; 8] = [None, None, None, None, None, None, None, None];\n\n/// Probe the VirtIO bus for devices that might be\n/// out there.\npub fn probe() {\n\t// Rust's for loop uses an Iterator object, which now has a step_by\n\t// modifier to change how much it steps. Also recall that ..= means up\n\t// to AND including MMIO_VIRTIO_END.\n\tfor addr in (MMIO_VIRTIO_START..=MMIO_VIRTIO_END).step_by(MMIO_VIRTIO_STRIDE) {\n\t\tprint!(\"Virtio probing 0x{:08x}...\", addr);\n\t\tlet magicvalue;\n\t\tlet deviceid;\n\t\tlet ptr = addr as *mut u32;\n\t\tunsafe {\n\t\t\tmagicvalue = ptr.read_volatile();\n\t\t\tdeviceid = ptr.add(2).read_volatile();\n\t\t}\n\t\t// 0x74_72_69_76 is \"virt\" in little endian, so in reality\n\t\t// it is triv. All VirtIO devices have this attached to the\n\t\t// MagicValue register (offset 0x000)\n\t\tif MMIO_VIRTIO_MAGIC != magicvalue {\n\t\t\tprintln!(\"not virtio.\");\n\t\t}\n\t\t// If we are a virtio device, we now need to see if anything\n\t\t// is actually attached to it. The DeviceID register will\n\t\t// contain what type of device this is. If this value is 0,\n\t\t// then it is not connected.\n\t\telse if 0 == deviceid {\n\t\t\tprintln!(\"not connected.\");\n\t\t}\n\t\t// If we get here, we have a connected virtio device. Now we have\n\t\t// to figure out what kind it is so we can do device-specific setup.\n\t\telse {\n\t\t\tmatch deviceid {\n\t\t\t\t// DeviceID 1 is a network device\n\t\t\t\t1 => {\n\t\t\t\t\tprint!(\"network device...\");\n\t\t\t\t\tif false == setup_network_device(ptr) {\n\t\t\t\t\t\tprintln!(\"setup failed.\");\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tprintln!(\"setup succeeded!\");\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t// DeviceID 2 is a block device\n\t\t\t\t2 => {\n\t\t\t\t\tprint!(\"block device...\");\n\t\t\t\t\tif false == setup_block_device(ptr) {\n\t\t\t\t\t\tprintln!(\"setup failed.\");\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tlet idx = (addr - MMIO_VIRTIO_START) >> 12;\n\t\t\t\t\t\tunsafe {\n\t\t\t\t\t\t\tVIRTIO_DEVICES[idx] =\n\t\t\t\t\t\t\t\tSome(VirtioDevice::new_with(DeviceTypes::Block));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tprintln!(\"setup succeeded!\");\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t// DeviceID 4 is a random number generator device\n\t\t\t\t4 => {\n\t\t\t\t\tprint!(\"entropy device...\");\n\t\t\t\t\tif false == setup_entropy_device(ptr) {\n\t\t\t\t\t\tprintln!(\"setup failed.\");\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tprintln!(\"setup succeeded!\");\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t// DeviceID 16 is a GPU device\n\t\t\t\t16 => {\n\t\t\t\t\tprint!(\"GPU device...\");\n\t\t\t\t\tif false == setup_gpu_device(ptr) {\n\t\t\t\t\t\tprintln!(\"setup failed.\");\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tprintln!(\"setup succeeded!\");\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t// DeviceID 18 is an input device\n\t\t\t\t18 => {\n\t\t\t\t\tprint!(\"input device...\");\n\t\t\t\t\tif false == setup_input_device(ptr) {\n\t\t\t\t\t\tprintln!(\"setup failed.\");\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tprintln!(\"setup succeeded!\");\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t_ => println!(\"unknown device type.\"),\n\t\t\t}\n\t\t}\n\t}\n}\n\npub fn setup_network_device(_ptr: *mut u32) -> bool {\n\tfalse\n}\n\npub fn setup_gpu_device(_ptr: *mut u32) -> bool {\n\tfalse\n}\n\npub fn setup_input_device(_ptr: *mut u32) -> bool {\n\tfalse\n}\n\n// The External pin (PLIC) trap will lead us here if it is\n// determined that interrupts 1..=8 are what caused the interrupt.\n// In here, we try to figure out where to direct the interrupt\n// and then handle it.\npub fn handle_interrupt(interrupt: u32) {\n\tlet idx = interrupt as usize - 1;\n\tunsafe {\n\t\tif let Some(vd) = &VIRTIO_DEVICES[idx] {\n\t\t\tmatch vd.devtype {\n\t\t\t\tDeviceTypes::Block => {\n\t\t\t\t\tblock::handle_interrupt(idx);\n\t\t\t\t},\n\t\t\t\t_ => {\n\t\t\t\t\tprintln!(\"Invalid device generated interrupt!\");\n\t\t\t\t},\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tprintln!(\"Spurious interrupt {}\", interrupt);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "risc_v/src/asm/boot.S",
    "content": "# boot.S\n# bootloader for SoS\n# Stephen Marz\n# 8 February 2019\n\n# Disable generation of compressed instructions.\n.option norvc\n\n# Define a .text.init section. The .text.init is put at the\n# starting address so that the entry _start is put at the RISC-V\n# address 0x8000_0000.\n.section .text.init\n\n# Execution starts here.\n.global _start\n_start:\n\n\t# Disable linker instruction relaxation for the `la` instruction below.\n\t# This disallows the assembler from assuming that `gp` is already initialized.\n\t# This causes the value stored in `gp` to be calculated from `pc`.\n\t# The job of the global pointer is to give the linker the ability to address\n\t# memory relative to GP instead of as an absolute address.\n.option push\n.option norelax\n\tla\t\tgp, _global_pointer\n.option pop\n\t# SATP should be zero, but let's make sure. Each HART has its own\n\t# SATP register.\n\tcsrw\tsatp, zero\n\t# Any hardware threads (hart) that are not bootstrapping\n\t# need to wait for an IPI\n\tcsrr\tt0, mhartid\n\tbnez\tt0, 3f\n\n\t# Set all bytes in the BSS section to zero.\n\tla \t\ta0, _bss_start\n\tla\t\ta1, _bss_end\n\tbgeu\ta0, a1, 2f\n1:\n\tsd\t\tzero, (a0)\n\taddi\ta0, a0, 8\n\tbltu\ta0, a1, 1b\n2:\n\t# The stack grows from bottom to top, so we put the stack pointer\n\t# to the very end of the stack range.\n\tla\t\tsp, _stack_end\n\t# Setting `mstatus` register:\n\t# 0b01 << 11: Machine's previous protection mode is 2 (MPP=2).\n\tli\t\tt0, 0b11 << 11 | (1 << 13)\n\tcsrw\tmstatus, t0\n\t# Do not allow interrupts while running kinit\n\tcsrw\tmie, zero\n\t# Machine's exception program counter (MEPC) is set to `kinit`.\n\tla\t\tt1, kinit\n\tcsrw\tmepc, t1\n\t# Set the return address to get us into supervisor mode\n\tla\t\tra, 2f\n\t# We use mret here so that the mstatus register is properly updated.\n\tmret\n2:\n\t# We set the return address (ra above) to this label. When kinit() is finished\n\t# in Rust, it will return here.\n\n\t# Setting `mstatus` (supervisor status) register:\n\t# 0b01 << 11 : Previous protection mode is 1 (MPP=01 [Supervisor]).\n\t# 1 << 7     : Previous machine interrupt-enable bit is 1 (MPIE=1 [Enabled])\n\t# 1 << 5     : Previous interrupt-enable bit is 1 (SPIE=1 [Enabled]).\n\t# We set the \"previous\" bits because the mret will write the current bits\n\t# with the previous bits.\n\tli\t\tt0, (0b00 << 11) | (1 << 7) | (1 << 5) | (1 << 13)\n\tcsrw\tmstatus, t0\n\t# Machine's trap vector base address is set to `m_trap_vector`, for\n\t# \"machine\" trap vector.\n\tla\t\tt2, m_trap_vector\n\tcsrw\tmtvec, t2\n\t# Jump to first process. We put the MPP = 00 for user mode, so after\n\t# mret, we will jump to the first process' addresss in user mode.\n\tla\t\tra, 4f\n\tmret\n3:\n\n\t# Parked harts go here. We need to set these\n\t# to only awaken if it receives a software interrupt,\n\t# which we're going to call the SIPI (Software Intra-Processor Interrupt).\n\t# We call the SIPI by writing the software interrupt into the Core Local Interruptor (CLINT)\n\t# Which is calculated by: base_address + hart * 4\n\t# where base address is 0x0200_0000 (MMIO CLINT base address)\n\t# We only use additional harts to run user-space programs, although this may\n\t# change.\n\n\t# We divide up the stack so the harts aren't clobbering one another.\n\tla\t\tsp, _stack_end\n\tli\t\tt0, 0x10000\n\tcsrr\ta0, mhartid\n\tmul\t\tt0, t0, a0\n\tsub\t\tsp, sp, t0\n\n\t# The parked harts will be put into machine mode with interrupts enabled.\n\tli\t\tt0, 0b11 << 11 | (1 << 7) | (1 << 13)\n\tcsrw\tmstatus, t0\n\t# Allow for MSIP (Software interrupt). We will write the MSIP from hart #0 to\n\t# awaken these parked harts.\n\tli\t\tt3, (1 << 3)\n\tcsrw\tmie, t3\n\t# Machine's exception program counter (MEPC) is set to the Rust initialization\n\t# code and waiting loop.\n\tla\t\tt1, kinit_hart\n\tcsrw\tmepc, t1\n\t# Machine's trap vector base address is set to `m_trap_vector`, for\n\t# \"machine\" trap vector. The Rust initialization routines will give each\n\t# hart its own trap frame. We can use the same trap function and distinguish\n\t# between each hart by looking at the trap frame.\n\tla\t\tt2, m_trap_vector\n\tcsrw\tmtvec, t2\n\t# Whenever our hart is done initializing, we want it to return to the waiting\n\t# loop, which is just below mret.\n\tla\t\tra, 4f\n\t# We use mret here so that the mstatus register is properly updated.\n\tmret\n\n4:\n\t# wfi = wait for interrupt. This is a hint to the harts to shut everything needed\n\t# down. However, the RISC-V specification allows for wfi to do nothing. Anyway,\n\t# with QEMU, this will save some CPU!\n\twfi\n\tj\t\t4b\n\n\n"
  },
  {
    "path": "risc_v/src/asm/mem.S",
    "content": "// mem.S\n// Importation of linker symbols\n\n.section .rodata\n.global HEAP_START\nHEAP_START: .dword _heap_start\n\n.global HEAP_SIZE\nHEAP_SIZE: .dword _heap_size\n\n.global TEXT_START\nTEXT_START: .dword _text_start\n\n.global TEXT_END\nTEXT_END: .dword _text_end\n\n.global DATA_START\nDATA_START: .dword _data_start\n\n.global DATA_END\nDATA_END: .dword _data_end\n\n.global RODATA_START\nRODATA_START: .dword _rodata_start\n\n.global RODATA_END\nRODATA_END: .dword _rodata_end\n\n.global BSS_START\nBSS_START: .dword _bss_start\n\n.global BSS_END\nBSS_END: .dword _bss_end\n\n.global KERNEL_STACK_START\nKERNEL_STACK_START: .dword _stack_start\n\n.global KERNEL_STACK_END\nKERNEL_STACK_END: .dword _stack_end\n\n\n"
  },
  {
    "path": "risc_v/src/asm/trap.S",
    "content": "# trap.S\n# Trap handler and global context\n# Steve Operating System\n# Stephen Marz\n# 24 February 2019\n.option norvc\n.altmacro\n.set NUM_GP_REGS, 32  # Number of registers per context\n.set REG_SIZE, 8   # Register size (in bytes)\n\n# Use macros for saving and restoring multiple registers\n.macro save_gp i, basereg=t6\n\tsd\tx\\i, ((\\i)*REG_SIZE)(\\basereg)\n.endm\n.macro load_gp i, basereg=t6\n\tld\tx\\i, ((\\i)*REG_SIZE)(\\basereg)\n.endm\n.macro save_fp i, basereg=t6\n\tfsd\tf\\i, ((NUM_GP_REGS+(\\i))*REG_SIZE)(\\basereg)\n.endm\n.macro load_fp i, basereg=t6\n\tfld\tf\\i, ((NUM_GP_REGS+(\\i))*REG_SIZE)(\\basereg)\n.endm\n\n.section .text\n.global m_trap_vector\n# This must be aligned by 4 since the last two bits\n# of the mtvec register do not contribute to the address\n# of this vector.\n.align 4\nm_trap_vector:\n\t# All registers are volatile here, we need to save them\n\t# before we do anything.\n\tcsrrw\tt6, mscratch, t6\n\t# csrrw will atomically swap t6 into mscratch and the old\n\t# value of mscratch into t6. This is nice because we just\n\t# switched values and didn't destroy anything -- all atomically!\n\t# in cpu.rs we have a structure of:\n\t#  32 gp regs\t\t0\n\t#  32 fp regs\t\t256\n\t# We use t6 as the temporary register because it is the very\n\t# bottom register (x31)\n\t.set \ti, 0\n\t.rept\t31\n\t\tsave_gp\t%i\n\t\t.set\ti, i+1\n\t.endr\n\n\t# Save the actual t6 register, which we swapped into\n\t# mscratch\n\tmv\t\tt5, t6\n\tcsrr\tt6, mscratch\n\tsave_gp 31, t5\n\n\t# Restore the kernel trap frame into mscratch\n\tcsrw\tmscratch, t5\n\n\tcsrr\tt1, mstatus\n\tsrli\tt0, t1, 13\n\tandi\tt0, t0, 3\n\tli\t\tt3, 3\n\tbne\t\tt0, t3, 1f\n\t# Save floating point registers\n\t.set \ti, 0\n\t.rept\t32\n\t\tsave_fp\t%i, t5\n\t\t.set\ti, i+1\n\t.endr\n1:\n\t# Get ready to go into Rust (trap.rs)\n\t# We don't want to write into the user's stack or whomever\n\t# messed with us here.\n\t# csrw\tmie, zero\n\n\tcsrr\ta0, mepc\n\tsd\t\ta0, 520(t5)\n\tcsrr\ta1, mtval\n\tcsrr\ta2, mcause\n\tcsrr\ta3, mhartid\n\tcsrr\ta4, mstatus\n\tcsrr\ta5, mscratch\n\tla\t\tt0, KERNEL_STACK_END\n\tld\t\tsp, 0(t0)\n\tcall\tm_trap\n\n\t# When we get here, we've returned from m_trap, restore registers\n\t# and return.\n\t# m_trap will return the return address via a0.\n\n\tcsrw\tmepc, a0\n\t# Now load the trap frame back into t6\n\tcsrr\tt6, mscratch\n\n\tcsrr\tt1, mstatus\n\tsrli\tt0, t1, 13\n\tandi\tt0, t0, 3\n\tli\t\tt3, 3\n\tbne\t\tt0, t3, 1f\n\t.set\ti, 0\n\t.rept\t32\n\t\tload_fp %i\n\t\t.set i, i+1\n\t.endr\n1:\n\t# Restore all GP registers\n\t.set\ti, 1\n\t.rept\t31\n\t\tload_gp %i\n\t\t.set\ti, i+1\n\t.endr\n\n\t# Since we ran this loop 31 times starting with i = 1,\n\t# the last one loaded t6 back to its original value.\n\tmret\n\n.global switch_to_user\nswitch_to_user:\n    # a0 - Frame address\n\t# a1 - Program counter\n\t# a2 - SATP Register\n    csrw    mscratch, a0\n\n\t# Load program counter\n\tld\t\ta1, 520(a0)\n\t# Load satp\n\tld\t\ta2, 512(a0)\n\t# Load processor mode\n\tld\t\ta3, 552(a0)\n\t# Pid\n\t# ld\t\ta4, 544(a0)\n\n\t# 1 << 7 is MPIE\n\t# Since user mode is 00, we don't need to set anything\n\t# in MPP (bits 12:11)\n\tli\t\tt0, 1 << 7 | 1 << 5 | 1 << 13\n\t# Combine enable bits with mode bits.\n\tslli\ta3, a3, 11\n\tor\t\tt0, t0, a3\n\tcsrw\tmstatus, t0\n\tcsrw\tmepc, a1\n\tcsrw\tsatp, a2\n\tli\t\tt1, 0xaaa\n\tcsrw\tmie, t1\n\tla\t\tt2, m_trap_vector\n\tcsrw\tmtvec, t2\n\t# This fence forces the MMU to flush the TLB. However, since\n\t# we're using the PID as the address space identifier, we might\n\t# only need this when we create a process. Right now, this ensures\n\t# correctness, however it isn't the most efficient.\n\t# sfence.vma\n\t# A0 is the context frame, so we need to reload it back\n\t# and mret so we can start running the program.\n\tmv\tt6, a0\n\t.set\ti, 0\n\t.rept\t32\n\t\tload_fp %i\n\t\t.set i, i+1\n\t.endr\n1:\n\t.set\ti, 1\n\t.rept\t31\n\t\tload_gp %i, t6\n\t\t.set\ti, i+1\n\t.endr\n\n    mret\n\n\n.global make_syscall\nmake_syscall:\n\t# We're setting this up to work with libgloss\n\t# They want a7 to be the system call number and all parameters\n\t# in a0 - a5\n\tmv\ta7, a0\n\tmv\ta0, a1\n\tmv\ta1, a2\n\tmv\ta2, a3\n\tmv\ta3, a4\n\tmv\ta4, a5\n\tmv\ta5, a6\n\tecall\n\tret\n"
  },
  {
    "path": "risc_v/src/assembly.rs",
    "content": "// assembly.rs\n// Assembly imports module\n// Stephen Marz\n// 20 April 2020\n\n// This came from the Rust book documenting global_asm!. \n// They show using include_str! with it to\n// import a full assembly file, which is what I want here.\nglobal_asm!(include_str!(\"asm/boot.S\"));\nglobal_asm!(include_str!(\"asm/mem.S\"));\nglobal_asm!(include_str!(\"asm/trap.S\"));\n\n"
  },
  {
    "path": "risc_v/src/block.rs",
    "content": "// block.rs\n// Block device using VirtIO protocol\n// Stephen Marz\n// 10 March 2020\n\nuse crate::{kmem::{kfree, kmalloc},\n            page::{zalloc, PAGE_SIZE},\n            process::{add_kernel_process_args,\n                      get_by_pid,\n                      set_running,\n                      set_waiting},\n            virtio,\n            virtio::{Descriptor,\n                     MmioOffsets,\n                     Queue,\n                     StatusField,\n                     VIRTIO_RING_SIZE}};\nuse core::mem::size_of;\nuse alloc::boxed::Box;\n\n#[repr(C)]\npub struct Geometry {\n\tcylinders: u16,\n\theads:     u8,\n\tsectors:   u8,\n}\n\n#[repr(C)]\npub struct Topology {\n\tphysical_block_exp: u8,\n\talignment_offset:   u8,\n\tmin_io_size:        u16,\n\topt_io_size:        u32,\n}\n\n// There is a configuration space for VirtIO that begins\n// at offset 0x100 and continues to the size of the configuration.\n// The structure below represents the configuration for a\n// block device. Really, all that this OS cares about is the\n// capacity.\n#[repr(C)]\npub struct Config {\n\tcapacity:                 u64,\n\tsize_max:                 u32,\n\tseg_max:                  u32,\n\tgeometry:                 Geometry,\n\tblk_size:                 u32,\n\ttopology:                 Topology,\n\twriteback:                u8,\n\tunused0:                  [u8; 3],\n\tmax_discard_sector:       u32,\n\tmax_discard_seg:          u32,\n\tdiscard_sector_alignment: u32,\n\tmax_write_zeroes_sectors: u32,\n\tmax_write_zeroes_seg:     u32,\n\twrite_zeroes_may_unmap:   u8,\n\tunused1:                  [u8; 3],\n}\n\n// The header/data/status is a block request\n// packet. We send the header to tell the direction\n// (blktype: IN/OUT) and then the starting sector\n// we want to read. Then, we put the data buffer\n// as the Data structure and finally an 8-bit\n// status. The device will write one of three values\n// in here: 0 = success, 1 = io error, 2 = unsupported\n// operation.\n#[repr(C)]\npub struct Header {\n\tblktype:  u32,\n\treserved: u32,\n\tsector:   u64,\n}\n\n#[repr(C)]\npub struct Data {\n\tdata: *mut u8,\n}\n\n#[repr(C)]\npub struct Status {\n\tstatus: u8,\n}\n\n#[repr(C)]\npub struct Request {\n\theader: Header,\n\tdata:   Data,\n\tstatus: Status,\n\thead:   u16,\n\n\t// Do not change anything above this line.\n\t// This is the PID of watcher. We store the PID\n\t// because it is possible that the process DIES\n\t// before we get here. If we used a pointer, we\n\t// may dereference invalid memory.\n\twatcher: u16,\n}\n\n// Internal block device structure\n// We keep our own used_idx and idx for\n// descriptors. There is a shared index, but that\n// tells us or the device if we've kept up with where\n// we are for the available (us) or used (device) ring.\npub struct BlockDevice {\n\tqueue:        *mut Queue,\n\tdev:          *mut u32,\n\tidx:          u16,\n\tack_used_idx: u16,\n\tread_only:    bool,\n}\n\n// Type values\npub const VIRTIO_BLK_T_IN: u32 = 0;\npub const VIRTIO_BLK_T_OUT: u32 = 1;\npub const VIRTIO_BLK_T_FLUSH: u32 = 4;\npub const VIRTIO_BLK_T_DISCARD: u32 = 11;\npub const VIRTIO_BLK_T_WRITE_ZEROES: u32 = 13;\n\n// Status values\npub const VIRTIO_BLK_S_OK: u8 = 0;\npub const VIRTIO_BLK_S_IOERR: u8 = 1;\npub const VIRTIO_BLK_S_UNSUPP: u8 = 2;\n\n// Feature bits\npub const VIRTIO_BLK_F_SIZE_MAX: u32 = 1;\npub const VIRTIO_BLK_F_SEG_MAX: u32 = 2;\npub const VIRTIO_BLK_F_GEOMETRY: u32 = 4;\npub const VIRTIO_BLK_F_RO: u32 = 5;\npub const VIRTIO_BLK_F_BLK_SIZE: u32 = 6;\npub const VIRTIO_BLK_F_FLUSH: u32 = 9;\npub const VIRTIO_BLK_F_TOPOLOGY: u32 = 10;\npub const VIRTIO_BLK_F_CONFIG_WCE: u32 = 11;\npub const VIRTIO_BLK_F_DISCARD: u32 = 13;\npub const VIRTIO_BLK_F_WRITE_ZEROES: u32 = 14;\n\n// We might get several types of errors, but they can be enumerated here.\npub enum BlockErrors {\n\tSuccess = 0,\n\tBlockDeviceNotFound,\n\tInvalidArgument,\n\tReadOnly,\n}\n\n// Much like with processes, Rust requires some initialization\n// when we declare a static. In this case, we use the Option\n// value type to signal that the variable exists, but not the\n// queue itself. We will replace this with an actual queue when\n// we initialize the block system.\nstatic mut BLOCK_DEVICES: [Option<BlockDevice>; 8] =\n\t[None, None, None, None, None, None, None, None];\n\npub fn setup_block_device(ptr: *mut u32) -> bool {\n\tunsafe {\n\t\t// We can get the index of the device based on its address.\n\t\t// 0x1000_1000 is index 0\n\t\t// 0x1000_2000 is index 1\n\t\t// ...\n\t\t// 0x1000_8000 is index 7\n\t\t// To get the number that changes over, we shift right 12 places\n\t\t// (3 hex digits)\n\t\tlet idx = (ptr as usize - virtio::MMIO_VIRTIO_START) >> 12;\n\t\t// [Driver] Device Initialization\n\t\t// 1. Reset the device (write 0 into status)\n\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(0);\n\t\tlet mut status_bits = StatusField::Acknowledge.val32();\n\t\t// 2. Set ACKNOWLEDGE status bit\n\t\tptr.add(MmioOffsets::Status.scale32())\n\t\t   .write_volatile(status_bits);\n\t\t// 3. Set the DRIVER status bit\n\t\tstatus_bits |= StatusField::DriverOk.val32();\n\t\tptr.add(MmioOffsets::Status.scale32())\n\t\t   .write_volatile(status_bits);\n\t\t// 4. Read device feature bits, write subset of feature\n\t\t// bits understood by OS and driver    to the device.\n\t\tlet host_features =\n\t\t\tptr.add(MmioOffsets::HostFeatures.scale32())\n\t\t\t   .read_volatile();\n\t\tlet guest_features = host_features & !(1 << VIRTIO_BLK_F_RO);\n\t\tlet ro = host_features & (1 << VIRTIO_BLK_F_RO) != 0;\n\t\tptr.add(MmioOffsets::GuestFeatures.scale32())\n\t\t   .write_volatile(guest_features);\n\t\t// 5. Set the FEATURES_OK status bit\n\t\tstatus_bits |= StatusField::FeaturesOk.val32();\n\t\tptr.add(MmioOffsets::Status.scale32())\n\t\t   .write_volatile(status_bits);\n\t\t// 6. Re-read status to ensure FEATURES_OK is still set.\n\t\t// Otherwise, it doesn't support our features.\n\t\tlet status_ok =\n\t\t\tptr.add(MmioOffsets::Status.scale32()).read_volatile();\n\t\t// If the status field no longer has features_ok set,\n\t\t// that means that the device couldn't accept\n\t\t// the features that we request. Therefore, this is\n\t\t// considered a \"failed\" state.\n\t\tif false == StatusField::features_ok(status_ok) {\n\t\t\tprint!(\"features fail...\");\n\t\t\tptr.add(MmioOffsets::Status.scale32())\n\t\t\t   .write_volatile(StatusField::Failed.val32());\n\t\t\treturn false;\n\t\t}\n\t\t// 7. Perform device-specific setup.\n\t\t// Set the queue num. We have to make sure that the\n\t\t// queue size is valid because the device can only take\n\t\t// a certain size.\n\t\tlet qnmax = ptr.add(MmioOffsets::QueueNumMax.scale32())\n\t\t               .read_volatile();\n\t\tptr.add(MmioOffsets::QueueNum.scale32())\n\t\t   .write_volatile(VIRTIO_RING_SIZE as u32);\n\t\tif VIRTIO_RING_SIZE as u32 > qnmax {\n\t\t\tprint!(\"queue size fail...\");\n\t\t\treturn false;\n\t\t}\n\t\t// First, if the block device array is empty, create it!\n\t\t// We add 4095 to round this up and then do an integer\n\t\t// divide to truncate the decimal. We don't add 4096,\n\t\t// because if it is exactly 4096 bytes, we would get two\n\t\t// pages, not one.\n\t\tlet num_pages =\n\t\t\t(size_of::<Queue>() + PAGE_SIZE - 1) / PAGE_SIZE;\n\t\t// println!(\"np = {}\", num_pages);\n\t\t// We allocate a page for each device. This will the the\n\t\t// descriptor where we can communicate with the block\n\t\t// device. We will still use an MMIO register (in\n\t\t// particular, QueueNotify) to actually tell the device\n\t\t// we put something in memory. We also have to be\n\t\t// careful with memory ordering. We don't want to\n\t\t// issue a notify before all memory writes have\n\t\t// finished. We will look at that later, but we need\n\t\t// what is called a memory \"fence\" or barrier.\n\t\tptr.add(MmioOffsets::QueueSel.scale32()).write_volatile(0);\n\t\t// Alignment is very important here. This is the memory address\n\t\t// alignment between the available and used rings. If this is\n\t\t// wrong, then we and the device will refer to different memory\n\t\t// addresses and hence get the wrong data in the used ring.\n\t\t// ptr.add(MmioOffsets::QueueAlign.scale32()).write_volatile(2);\n\t\tlet queue_ptr = zalloc(num_pages) as *mut Queue;\n\t\tlet queue_pfn = queue_ptr as u32;\n\t\tptr.add(MmioOffsets::GuestPageSize.scale32())\n\t\t   .write_volatile(PAGE_SIZE as u32);\n\t\t// QueuePFN is a physical page number, however it\n\t\t// appears for QEMU we have to write the entire memory\n\t\t// address. This is a physical memory address where we\n\t\t// (the OS) and the block device have in common for\n\t\t// making and receiving requests.\n\t\tptr.add(MmioOffsets::QueuePfn.scale32())\n\t\t   .write_volatile(queue_pfn / PAGE_SIZE as u32);\n\t\t// We need to store all of this data as a \"BlockDevice\"\n\t\t// structure We will be referring to this structure when\n\t\t// making block requests AND when handling responses.\n\t\tlet bd = BlockDevice { queue:        queue_ptr,\n\t\t                       dev:          ptr,\n\t\t                       idx:          0,\n\t\t                       ack_used_idx: 0,\n\t\t                       read_only:    ro, };\n\t\tBLOCK_DEVICES[idx] = Some(bd);\n\n\t\t// 8. Set the DRIVER_OK status bit. Device is now \"live\"\n\t\tstatus_bits |= StatusField::DriverOk.val32();\n\t\tptr.add(MmioOffsets::Status.scale32())\n\t\t   .write_volatile(status_bits);\n\n\t\ttrue\n\t}\n}\n\npub fn fill_next_descriptor(bd: &mut BlockDevice, desc: Descriptor) -> u16 {\n\tunsafe {\n\t\t// The ring structure increments here first. This allows us to\n\t\t// skip index 0, which then in the used ring will show that .id\n\t\t// > 0. This is one way to error check. We will eventually get\n\t\t// back to 0 as this index is cyclical. However, it shows if the\n\t\t// first read/write actually works.\n\t\tbd.idx = (bd.idx + 1) % VIRTIO_RING_SIZE as u16;\n\t\t(*bd.queue).desc[bd.idx as usize] = desc;\n\t\tif (*bd.queue).desc[bd.idx as usize].flags\n\t\t   & virtio::VIRTIO_DESC_F_NEXT\n\t\t   != 0\n\t\t{\n\t\t\t// If the next flag is set, we need another descriptor.\n\t\t\t(*bd.queue).desc[bd.idx as usize].next =\n\t\t\t\t(bd.idx + 1) % VIRTIO_RING_SIZE as u16;\n\t\t}\n\t\tbd.idx\n\t}\n}\n/// This is now a common block operation for both reads and writes. Therefore,\n/// when one thing needs to change, we can change it for both reads and writes.\n/// There is a lot of error checking that I haven't done. The block device reads\n/// sectors at a time, which are 512 bytes. Therefore, our buffer must be\n/// capable of storing multiples of 512 bytes depending on the size. The size is\n/// also a multiple of 512, but we don't really check that.\n/// We DO however, check that we aren't writing to an R/O device. This would\n/// cause a I/O error if we tried to write to a R/O device.\npub fn block_op(dev: usize,\n                buffer: *mut u8,\n                size: u32,\n                offset: u64,\n                write: bool,\n                watcher: u16)\n                -> Result<u32, BlockErrors>\n{\n\tunsafe {\n\t\tif let Some(bdev) = BLOCK_DEVICES[dev - 1].as_mut() {\n\t\t\t// Check to see if we are trying to write to a read only\n\t\t\t// device.\n\t\t\tif bdev.read_only && write {\n\t\t\t\tprintln!(\"Trying to write to read/only!\");\n\t\t\t\treturn Err(BlockErrors::ReadOnly);\n\t\t\t}\n\t\t\tif size % 512 != 0 {\n\t\t\t\treturn Err(BlockErrors::InvalidArgument);\n\t\t\t}\n\t\t\tlet sector = offset / 512;\n\t\t\t// TODO: Before we get here, we are NOT allowed to\n\t\t\t// schedule a read or write OUTSIDE of the disk's size.\n\t\t\t// So, we can read capacity from the configuration space\n\t\t\t// to ensure we stay within bounds.\n\t\t\tlet blk_request_size = size_of::<Request>();\n\t\t\tlet blk_request =\n\t\t\t\tkmalloc(blk_request_size) as *mut Request;\n\t\t\tlet desc =\n\t\t\t\tDescriptor { addr:  &(*blk_request).header\n\t\t\t\t                    as *const Header\n\t\t\t\t                    as u64,\n\t\t\t\t             len:   size_of::<Header>() as u32,\n\t\t\t\t             flags: virtio::VIRTIO_DESC_F_NEXT,\n\t\t\t\t             next:  0, };\n\t\t\tlet head_idx = fill_next_descriptor(bdev, desc);\n\t\t\t(*blk_request).header.sector = sector;\n\t\t\t// A write is an \"out\" direction, whereas a read is an\n\t\t\t// \"in\" direction.\n\t\t\t(*blk_request).header.blktype = if write {\n\t\t\t\tVIRTIO_BLK_T_OUT\n\t\t\t}\n\t\t\telse {\n\t\t\t\tVIRTIO_BLK_T_IN\n\t\t\t};\n\t\t\t// We put 111 in the status. Whenever the device\n\t\t\t// finishes, it will write into status. If we read\n\t\t\t// status and it is 111, we know that it wasn't written\n\t\t\t// to by the device.\n\t\t\t(*blk_request).data.data = buffer;\n\t\t\t(*blk_request).header.reserved = 0;\n\t\t\t(*blk_request).status.status = 111;\n\t\t\t(*blk_request).watcher = watcher;\n\t\t\tlet desc =\n\t\t\t\tDescriptor { addr:  buffer as u64,\n\t\t\t\t             len:   size,\n\t\t\t\t             flags: virtio::VIRTIO_DESC_F_NEXT\n\t\t\t\t                    | if !write {\n\t\t\t\t\t                    virtio::VIRTIO_DESC_F_WRITE\n\t\t\t\t                    }\n\t\t\t\t                    else {\n\t\t\t\t\t                    0\n\t\t\t\t                    },\n\t\t\t\t             next:  0, };\n\t\t\tlet _data_idx = fill_next_descriptor(bdev, desc);\n\t\t\tlet desc =\n\t\t\t\tDescriptor { addr:  &(*blk_request).status\n\t\t\t\t                    as *const Status\n\t\t\t\t                    as u64,\n\t\t\t\t             len:   size_of::<Status>() as u32,\n\t\t\t\t             flags: virtio::VIRTIO_DESC_F_WRITE,\n\t\t\t\t             next:  0, };\n\t\t\tlet _status_idx = fill_next_descriptor(bdev, desc);\n\t\t\t(*bdev.queue).avail.ring[(*bdev.queue).avail.idx\n\t\t\t                         as usize\n\t\t\t                         % virtio::VIRTIO_RING_SIZE] = head_idx;\n\t\t\t(*bdev.queue).avail.idx =\n\t\t\t\t(*bdev.queue).avail.idx.wrapping_add(1);\n\t\t\t// The only queue a block device has is 0, which is the\n\t\t\t// request queue.\n\t\t\tbdev.dev\n\t\t\t    .add(MmioOffsets::QueueNotify.scale32())\n\t\t\t    .write_volatile(0);\n\t\t\tOk(size)\n\t\t}\n\t\telse {\n\t\t\tErr(BlockErrors::BlockDeviceNotFound)\n\t\t}\n\t}\n}\n\npub fn read(dev: usize,\n            buffer: *mut u8,\n            size: u32,\n            offset: u64)\n            -> Result<u32, BlockErrors>\n{\n\tblock_op(dev, buffer, size, offset, false, 0)\n}\n\npub fn write(dev: usize,\n             buffer: *mut u8,\n             size: u32,\n             offset: u64)\n             -> Result<u32, BlockErrors>\n{\n\tblock_op(dev, buffer, size, offset, true, 0)\n}\n\n/// Here we handle block specific interrupts. Here, we need to check\n/// the used ring and wind it up until we've handled everything.\n/// This is how the device tells us that it's finished a request.\npub fn pending(bd: &mut BlockDevice) {\n\t// Here we need to check the used ring and then free the resources\n\t// given by the descriptor id.\n\tunsafe {\n\t\tlet ref queue = *bd.queue;\n\t\twhile bd.ack_used_idx != queue.used.idx {\n\t\t\tlet ref elem = queue.used.ring\n\t\t\t\t[bd.ack_used_idx as usize % VIRTIO_RING_SIZE];\n\t\t\tbd.ack_used_idx = bd.ack_used_idx.wrapping_add(1);\n\t\t\t// Requests stay resident on the heap until this\n\t\t\t// function, so we can recapture the address here\n\t\t\tlet rq = queue.desc[elem.id as usize].addr\n\t\t\t         as *const Request;\n\n\t\t\t// A process might be waiting for this interrupt. Awaken\n\t\t\t// the process attached here.\n\t\t\tlet pid_of_watcher = (*rq).watcher;\n\t\t\t// A PID of 0 means that we don't have a watcher.\n\t\t\tif pid_of_watcher > 0 {\n\t\t\t\tset_running(pid_of_watcher);\n\t\t\t\tlet proc = get_by_pid(pid_of_watcher);\n\t\t\t\t(*(*proc).frame).regs[10] = (*rq).status.status as usize;\n\t\t\t\t// TODO: Set GpA0 to the value of the return\n\t\t\t\t// status.\n\t\t\t}\n\t\t\tkfree(rq as *mut u8);\n\t\t}\n\t}\n}\n\n/// The trap code will route PLIC interrupts 1..=8 for virtio devices. When\n/// virtio determines that this is a block device, it sends it here.\npub fn handle_interrupt(idx: usize) {\n\tunsafe {\n\t\tif let Some(bdev) = BLOCK_DEVICES[idx].as_mut() {\n\t\t\tpending(bdev);\n\t\t}\n\t\telse {\n\t\t\tprintln!(\n\t\t\t         \"Invalid block device for interrupt {}\",\n\t\t\t         idx + 1\n\t\t\t);\n\t\t}\n\t}\n}\n\n// ///////////////////////////////////////////////\n// //  BLOCK PROCESSES (KERNEL PROCESSES)\n// ///////////////////////////////////////////////\nstruct ProcArgs {\n\tpub pid:    u16,\n\tpub dev:    usize,\n\tpub buffer: *mut u8,\n\tpub size:   u32,\n\tpub offset: u64,\n}\n\n/// This will be a\nfn read_proc(args_addr: usize) {\n\tlet args = unsafe { Box::from_raw(args_addr as *mut ProcArgs) };\n\tlet _ = block_op(\n\t                 args.dev,\n\t                 args.buffer,\n\t                 args.size,\n\t                 args.offset,\n\t                 false,\n\t                 args.pid,\n\t);\n\t// This should be handled by the RA now.\n\t// syscall_exit();\n}\n\npub fn process_read(pid: u16,\n                    dev: usize,\n                    buffer: *mut u8,\n                    size: u32,\n                    offset: u64)\n{\n\t// println!(\"Block read {}, {}, 0x{:x}, {}, {}\", pid, dev, buffer as\n\t// usize, size, offset);\n\tlet args = ProcArgs {\n\t\tpid,\n\t\tdev,\n\t\tbuffer,\n\t\tsize,\n\t\toffset,\n\t};\n\tlet boxed_args = Box::new(args);\n\tset_waiting(pid);\n\tlet _ = add_kernel_process_args(\n\t                                read_proc,\n\t                                Box::into_raw(boxed_args) as usize,\n\t);\n}\n\nfn write_proc(args_addr: usize) {\n\tlet args = unsafe { Box::from_raw(args_addr as *mut ProcArgs) };\n\n\tlet _ = block_op(\n\t                 args.dev,\n\t                 args.buffer,\n\t                 args.size,\n\t                 args.offset,\n\t                 true,\n\t                 args.pid,\n\t);\n\t// syscall_exit();\n}\n\npub fn process_write(pid: u16,\n                     dev: usize,\n                     buffer: *mut u8,\n                     size: u32,\n                     offset: u64)\n{\n\tlet args = ProcArgs {\n\t\tpid,\n\t\tdev,\n\t\tbuffer,\n\t\tsize,\n\t\toffset,\n\t};\n\tlet boxed_args = Box::new(args);\n\tset_waiting(pid);\n\tlet _ = add_kernel_process_args(\n\t                                write_proc,\n\t                                Box::into_raw(boxed_args) as usize,\n\t);\n}\n"
  },
  {
    "path": "risc_v/src/buffer.rs",
    "content": "// buffer.rs\n// BlockBuffer is so useful, we put it here instead\n// of in the file system.\n// Stephen Marz\n\nuse crate::{cpu::memcpy, kmem::{kmalloc, kfree}};\nuse core::{ptr::null_mut, ops::{Index, IndexMut}};\n// We need a Buffer that can automatically be created and destroyed\n// in the lifetime of our read and write functions. In C, this would entail\n// goto statements that \"unravel\" all of the allocations that we made. Take\n// a look at the read() function to see why I thought this way would be better.\npub struct Buffer {\n\tbuffer: *mut u8,\n\tlen: usize\n}\n\nimpl Buffer {\n\tpub fn new(sz: usize) -> Self {\n\t\tSelf { \n\t\t\tbuffer: kmalloc(sz), \n\t\t\tlen: sz\n\t\t}\n\t}\n\n\tpub fn get_mut(&mut self) -> *mut u8 {\n\t\tself.buffer\n\t}\n\n\tpub fn get(&self) -> *const u8 {\n\t\tself.buffer\n\t}\n\n\tpub fn len(&self) -> usize {\n\t\tself.len\n\t}\n}\n\nimpl Default for Buffer {\n\tfn default() -> Self {\n\t\tSelf::new(1024)\n\t}\n}\n\nimpl Index<usize> for Buffer {\n\ttype Output = u8;\n\tfn index(&self, idx: usize) -> &Self::Output {\n\t\tunsafe {\n\t\t\tself.get().add(idx).as_ref().unwrap()\n\t\t}\n\t}\n}\n\nimpl IndexMut<usize> for Buffer {\n\tfn index_mut(&mut self, idx: usize) -> &mut Self::Output {\n\t\tunsafe {\n\t\t\tself.get_mut().add(idx).as_mut().unwrap()\n\t\t}\n\t}\n\n}\n\nimpl Clone for Buffer {\n\tfn clone(&self) -> Self {\n\t\tlet mut new = Self {\n\t\t\tbuffer: kmalloc(self.len()),\n\t\t\tlen: self.len()\n\t\t};\n\t\tunsafe {\n\t\t\tmemcpy(new.get_mut(), self.get(), self.len());\n\t\t}\n\t\tnew\n\t}\n}\n\n// This is why we have the Buffer. Instead of having to unwind\n// all other buffers, we drop here when the block buffer goes out of scope.\nimpl Drop for Buffer {\n\tfn drop(&mut self) {\n\t\tif !self.buffer.is_null() {\n\t\t\tkfree(self.buffer);\n\t\t\tself.buffer = null_mut();\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "risc_v/src/console.rs",
    "content": "// console.rs\n// Console utilities for buffering\n// Stephen Marz\n// 4 June 2020\n\nuse alloc::collections::VecDeque;\nuse crate::lock::Mutex;\nuse crate::process::{get_by_pid, set_running};\n\npub static mut IN_BUFFER: Option<VecDeque<u8>> = None;\npub static mut OUT_BUFFER: Option<VecDeque<u8>> = None;\n\npub static mut IN_LOCK: Mutex = Mutex::new();\npub static mut OUT_LOCK: Mutex = Mutex::new();\n\npub const DEFAULT_OUT_BUFFER_SIZE: usize = 10_000;\npub const DEFAULT_IN_BUFFER_SIZE: usize = 1_000;\n\npub static mut CONSOLE_QUEUE: Option<VecDeque<u16>> = None;\n\npub fn init() {\n    unsafe {\n        IN_BUFFER.replace(VecDeque::with_capacity(DEFAULT_IN_BUFFER_SIZE));\n        OUT_BUFFER.replace(VecDeque::with_capacity(DEFAULT_OUT_BUFFER_SIZE));\n    }\n}\n\n/// Push a u8 (character) onto the output buffer\n/// If the buffer is full, silently drop.\npub fn push_stdout(c: u8) {\n    unsafe {\n        OUT_LOCK.spin_lock();\n        if let Some(mut buf) = OUT_BUFFER.take() {\n            if buf.len() < DEFAULT_OUT_BUFFER_SIZE {\n                buf.push_back(c);\n            }\n            OUT_BUFFER.replace(buf);\n        }\n        OUT_LOCK.unlock();\n    }\n}\n\npub fn pop_stdout() -> u8 {\n    let mut ret = None;\n    unsafe {\n        OUT_LOCK.spin_lock();\n        if let Some(mut buf) = OUT_BUFFER.take() {\n            ret = buf.pop_front();\n            OUT_BUFFER.replace(buf);\n        }\n        OUT_LOCK.unlock();\n    }\n    ret.unwrap_or(0)\n}\n\npub fn push_stdin(c: u8) {\n    unsafe {\n        IN_LOCK.spin_lock();\n        if let Some(mut buf) = IN_BUFFER.take() {\n            if buf.len() < DEFAULT_IN_BUFFER_SIZE {\n                buf.push_back(c);\n                if c == 10 || c == 11 {\n                    if let Some(mut q) = CONSOLE_QUEUE.take() {\n                        for i in q.drain(..) {\n                            set_running(i);\n                            // We also need to put stuff in here.\n                        }\n                        CONSOLE_QUEUE.replace(q);\n                    }\n                }\n            }\n            IN_BUFFER.replace(buf);\n        }\n        IN_LOCK.unlock();\n    }\n}\n\npub fn pop_stdin() -> u8 {\n    let mut ret = None;\n    unsafe {\n        IN_LOCK.spin_lock();\n        if let Some(mut buf) = IN_BUFFER.take() {\n            ret = buf.pop_front();\n            IN_BUFFER.replace(buf);\n        }\n        IN_LOCK.unlock();\n    }\n    ret.unwrap_or(0)\n}\n\npub fn push_queue(pid: u16) {\n    unsafe {\n        if let Some(mut q) = CONSOLE_QUEUE.take() {\n            q.push_back(pid);\n            CONSOLE_QUEUE.replace(q);\n        }\n    }\n}\n"
  },
  {
    "path": "risc_v/src/cpu.rs",
    "content": "// cpu.rs\n// CPU and CPU-related routines\n// Also contains the kernel's trap frame\n// Stephen Marz\n// 14 October 2019\n\n// The frequency of QEMU is 10 MHz\npub const FREQ: u64 = 10_000_000;\n// Let's do this 250 times per second for switching\npub const CONTEXT_SWITCH_TIME: u64 = FREQ / 500;\n\n/// In 64-bit mode, we're given three different modes for the MMU:\n/// 0 - The MMU is off -- no protection and no translation PA = VA\n/// 8 - This is Sv39 mode -- 39-bit virtual addresses\n/// 9 - This is Sv48 mode -- 48-bit virtual addresses\n#[repr(usize)]\npub enum SatpMode {\n\tOff = 0,\n\tSv39 = 8,\n\tSv48 = 9,\n}\n\n#[repr(usize)]\npub enum CpuMode {\n\tUser = 0,\n\tSupervisor = 1,\n\tMachine = 3,\n}\n\n#[repr(usize)]\npub enum Registers {\n\tZero = 0,\n\tRa,\n\tSp,\n\tGp,\n\tTp,\n\tT0,\n\tT1,\n\tT2,\n\tS0,\n\tS1,\n\tA0, /* 10 */\n\tA1,\n\tA2,\n\tA3,\n\tA4,\n\tA5,\n\tA6,\n\tA7,\n\tS2,\n\tS3,\n\tS4, /* 20 */\n\tS5,\n\tS6,\n\tS7,\n\tS8,\n\tS9,\n\tS10,\n\tS11,\n\tT3,\n\tT4,\n\tT5, /* 30 */\n\tT6\n}\n\npub const fn gp(r: Registers) -> usize {\n\tr as usize\n}\n\n// Floating point registers\n#[repr(usize)]\npub enum FRegisters {\n\tFt0,\n\tFt1,\n\tFt2,\n\tFt3,\n\tFt4,\n\tFt5,\n\tFt6,\n\tFt7,\n\tFs0,\n\tFs1,\n\tFa0, /* 10 */\n\tFa1,\n\tFa2,\n\tFa3,\n\tFa4,\n\tFa5,\n\tFa6,\n\tFa7,\n\tFs2,\n\tFs3,\n\tFs4, /* 20 */\n\tFs5,\n\tFs6,\n\tFs7,\n\tFs8,\n\tFs9,\n\tFs10,\n\tFs11,\n\tFt8,\n\tFt9,\n\tFt10, /* 30 */\n\tFt11\n}\n\n/// The trap frame is set into a structure\n/// and packed into each hart's mscratch register.\n/// This allows for quick reference and full\n/// context switch handling.\n/// To make offsets easier, everything will be a usize (8 bytes)\n#[repr(C)]\n#[derive(Clone, Copy)]\npub struct TrapFrame {\n\tpub regs:   [usize; 32], // 0 - 255\n\tpub fregs:  [usize; 32], // 256 - 511\n\tpub satp:   usize,       // 512 - 519\n\tpub pc:     usize,       // 520\n\tpub hartid: usize,       // 528\n\tpub qm:     usize,       // 536\n\tpub pid:    usize,       // 544\n\tpub mode:   usize,       // 552\n}\n\n/// Rust requires that we initialize our structures\n/// because of the move semantics. What'll happen below\n/// is Rust will construct a new TrapFrame and move it\n/// out of the zero() function below. Rust contains two\n/// different \"selfs\" where self can refer to the object\n/// in memory or Self (capital S) which refers to the\n/// data type of the structure. In the case below, this\n/// is TrapFrame.\nimpl TrapFrame {\n\tpub const fn new() -> Self {\n\t\tTrapFrame { regs:   [0; 32],\n\t\t            fregs:  [0; 32],\n\t\t            satp:   0,\n\t\t            pc:     0,\n\t\t            hartid: 0,\n\t\t            qm:     1,\n\t\t            pid:    0,\n\t\t            mode:   0, }\n\t}\n}\n\n/// The SATP register contains three fields: mode, address space id, and\n/// the first level table address (level 2 for Sv39). This function\n/// helps make the 64-bit register contents based on those three\n/// fields.\npub const fn build_satp(mode: SatpMode, asid: usize, addr: usize) -> usize {\n\t(mode as usize) << 60\n\t| (asid & 0xffff) << 44\n\t| (addr >> 12) & 0xff_ffff_ffff\n}\n\npub fn mhartid_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tllvm_asm!(\"csrr $0, mhartid\" :\"=r\"(rval));\n\t\trval\n\t}\n}\npub fn mie_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tllvm_asm!(\"csrr $0, mie\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn mie_write(val: usize) {\n\tunsafe {\n\t\tllvm_asm!(\"csrw mie, $0\" :: \"r\"(val));\n\t}\n}\n\npub fn mstatus_write(val: usize) {\n\tunsafe {\n\t\tllvm_asm!(\"csrw\tmstatus, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn mstatus_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tllvm_asm!(\"csrr\t$0, mstatus\":\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn stvec_write(val: usize) {\n\tunsafe {\n\t\tllvm_asm!(\"csrw\tstvec, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn stvec_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tllvm_asm!(\"csrr\t$0, stvec\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn mscratch_write(val: usize) {\n\tunsafe {\n\t\tllvm_asm!(\"csrw\tmscratch, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn mscratch_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tllvm_asm!(\"csrr\t$0, mscratch\" : \"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn mscratch_swap(to: usize) -> usize {\n\tunsafe {\n\t\tlet from;\n\t\tllvm_asm!(\"csrrw\t$0, mscratch, $1\" : \"=r\"(from) : \"r\"(to));\n\t\tfrom\n\t}\n}\n\npub fn sscratch_write(val: usize) {\n\tunsafe {\n\t\tllvm_asm!(\"csrw\tsscratch, $0\" ::\"r\"(val));\n\t}\n}\n\npub fn sscratch_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tllvm_asm!(\"csrr\t$0, sscratch\" : \"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn sscratch_swap(to: usize) -> usize {\n\tunsafe {\n\t\tlet from;\n\t\tllvm_asm!(\"csrrw\t$0, sscratch, $1\" : \"=r\"(from) : \"r\"(to));\n\t\tfrom\n\t}\n}\n\npub fn mepc_write(val: usize) {\n\tunsafe {\n\t\tllvm_asm!(\"csrw mepc, $0\" :: \"r\"(val));\n\t}\n}\n\npub fn mepc_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tllvm_asm!(\"csrr $0, mepc\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn sepc_write(val: usize) {\n\tunsafe {\n\t\tllvm_asm!(\"csrw sepc, $0\" :: \"r\"(val));\n\t}\n}\n\npub fn sepc_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tllvm_asm!(\"csrr $0, sepc\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\npub fn satp_write(val: usize) {\n\tunsafe {\n\t\tllvm_asm!(\"csrw satp, $0\" :: \"r\"(val));\n\t}\n}\n\npub fn satp_read() -> usize {\n\tunsafe {\n\t\tlet rval;\n\t\tllvm_asm!(\"csrr $0, satp\" :\"=r\"(rval));\n\t\trval\n\t}\n}\n\n/// Take a hammer to the page tables and synchronize\n/// all of them. This essentially flushes the entire\n/// TLB.\npub fn satp_fence(vaddr: usize, asid: usize) {\n\tunsafe {\n\t\tllvm_asm!(\"sfence.vma $0, $1\" :: \"r\"(vaddr), \"r\"(asid));\n\t}\n}\n\n/// Synchronize based on the address space identifier\n/// This allows us to fence a particular process rather\n/// than the entire TLB.\n/// The RISC-V documentation calls this a TLB flush +.\n/// Since there are other memory routines involved, they\n/// didn't call it a TLB flush, but it is much like\n/// Intel/AMD's invtlb [] instruction.\npub fn satp_fence_asid(asid: usize) {\n\tunsafe {\n\t\tllvm_asm!(\"sfence.vma zero, $0\" :: \"r\"(asid));\n\t}\n}\n\nconst MMIO_MTIME: *const u64 = 0x0200_BFF8 as *const u64;\n\npub fn get_mtime() -> usize {\n\tunsafe { (*MMIO_MTIME) as usize }\n}\n\n/// Copy one data from one memory location to another.\npub unsafe fn memcpy(dest: *mut u8, src: *const u8, bytes: usize) {\n\tlet bytes_as_8 = bytes / 8;\n\tlet dest_as_8 = dest as *mut u64;\n\tlet src_as_8 = src as *const u64;\n\n\tfor i in 0..bytes_as_8 {\n\t\t*(dest_as_8.add(i)) = *(src_as_8.add(i));\n\t}\n\tlet bytes_completed = bytes_as_8 * 8;\n\tlet bytes_remaining = bytes - bytes_completed;\n\tfor i in bytes_completed..bytes_remaining {\n\t\t*(dest.add(i)) = *(src.add(i));\n\t}\n}\n\n/// Dumps the registers of a given trap frame. This is NOT the\n/// current CPU registers!\npub fn dump_registers(frame: *const TrapFrame) {\n\tprint!(\"   \");\n\tfor i in 1..32 {\n\t\tif i % 4 == 0 {\n\t\t\tprintln!();\n\t\t\tprint!(\"   \");\n\t\t}\n\t\tprint!(\"x{:2}:{:08x}   \", i, unsafe { (*frame).regs[i] });\n\t}\n\tprintln!();\n}\n"
  },
  {
    "path": "risc_v/src/elf.rs",
    "content": "// elf.rs\n// Routines for reading and parsing ELF\n// (Executable and Linkable Format) files.\n// 26-April-2020\n// Stephen Marz\n\nuse crate::{buffer::Buffer,\n            cpu::{build_satp, memcpy, satp_fence_asid, CpuMode, Registers, SatpMode, TrapFrame},\n            page::{map, zalloc, EntryBits, Table, PAGE_SIZE},\n            process::{Process, ProcessData, ProcessState, NEXT_PID, STACK_ADDR, STACK_PAGES}};\nuse alloc::collections::VecDeque;\n// Every ELF file starts with ELF \"magic\", which is a sequence of four bytes 0x7f followed by capital ELF, which is 0x45, 0x4c, and 0x46 respectively.\npub const MAGIC: u32 = 0x464c_457f;\n\n/// The ELF header contains information about placement and numbers of the important sections within our file.\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct Header {\n\tpub magic:             u32,\n\tpub bitsize:           u8,\n\tpub endian:            u8,\n\tpub ident_abi_version: u8,\n\tpub target_platform:   u8,\n\tpub abi_version:       u8,\n\tpub padding:           [u8; 7],\n\tpub obj_type:          u16,\n\tpub machine:           u16, // 0xf3 for RISC-V\n\tpub version:           u32,\n\tpub entry_addr:        usize,\n\tpub phoff:             usize,\n\tpub shoff:             usize,\n\tpub flags:             u32,\n\tpub ehsize:            u16,\n\tpub phentsize:         u16,\n\tpub phnum:             u16,\n\tpub shentsize:         u16,\n\tpub shnum:             u16,\n\tpub shstrndx:          u16\n}\n\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct ProgramHeader {\n\tpub seg_type: u32,\n\tpub flags:    u32,\n\tpub off:      usize,\n\tpub vaddr:    usize,\n\tpub paddr:    usize,\n\tpub filesz:   usize,\n\tpub memsz:    usize,\n\tpub align:    usize\n}\n\npub const TYPE_EXEC: u16 = 2;\n\npub const PROG_READ: u32 = 4;\npub const PROG_WRITE: u32 = 2;\npub const PROG_EXECUTE: u32 = 1;\n\npub const MACHINE_RISCV: u16 = 0xf3;\npub const PH_SEG_TYPE_NULL: u32 = 0;\npub const PH_SEG_TYPE_LOAD: u32 = 1;\npub const PH_SEG_TYPE_DYNAMIC: u32 = 2;\npub const PH_SEG_TYPE_INTERP: u32 = 3;\npub const PH_SEG_TYPE_NOTE: u32 = 4;\n\npub struct Program {\n\tpub header: ProgramHeader,\n\tpub data:   Buffer\n}\n\npub enum LoadErrors {\n\tMagic,\n\tMachine,\n\tTypeExec,\n\tFileRead\n}\n\npub struct File {\n\tpub header:   Header,\n\tpub programs: VecDeque<Program>\n}\n\nimpl File {\n\tpub fn load(buffer: &Buffer) -> Result<Self, LoadErrors> {\n\t\tlet elf_hdr;\n\t\tunsafe {\n\t\t\t// Load the ELF\n\t\t\telf_hdr = (buffer.get() as *const Header).as_ref().unwrap();\n\t\t}\n\t\t// The ELF magic is 0x75, followed by ELF\n\t\tif elf_hdr.magic != MAGIC {\n\t\t\treturn Err(LoadErrors::Magic);\n\t\t}\n\t\t// We need to make sure we're built for RISC-V\n\t\tif elf_hdr.machine != MACHINE_RISCV {\n\t\t\treturn Err(LoadErrors::Machine);\n\t\t}\n\t\t// ELF has several types. However, we can only load\n\t\t// executables.\n\t\tif elf_hdr.obj_type != TYPE_EXEC {\n\t\t\treturn Err(LoadErrors::TypeExec);\n\t\t}\n\t\tlet ph_tab = unsafe { buffer.get().add(elf_hdr.phoff) } as *const ProgramHeader;\n\t\t// There are phnum number of program headers. We need to go through\n\t\t// each one and load it into memory, if necessary.\n\t\tlet mut ret = Self { header:   *elf_hdr,\n\t\t                     programs: VecDeque::new() };\n\t\tfor i in 0..elf_hdr.phnum as usize {\n\t\t\tunsafe {\n\t\t\t\tlet ph = ph_tab.add(i).as_ref().unwrap();\n\t\t\t\t// If the segment isn't marked as LOAD (loaded into memory),\n\t\t\t\t// then there is no point to this. Most executables use a LOAD\n\t\t\t\t// type for their program headers.\n\t\t\t\tif ph.seg_type != PH_SEG_TYPE_LOAD {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\t// If there's nothing in this section, don't load it.\n\t\t\t\tif ph.memsz == 0 {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tlet mut ph_buffer = Buffer::new(ph.memsz);\n\n\t\t\t\tmemcpy(ph_buffer.get_mut(), buffer.get().add(ph.off), ph.memsz);\n\t\t\t\tret.programs.push_back(Program { header: *ph,\n\t\t\t\t                                 data:   ph_buffer });\n\t\t\t}\n\t\t}\n\t\tOk(ret)\n\t}\n\n\t// load\n\tpub fn load_proc(buffer: &Buffer) -> Result<Process, LoadErrors> {\n\t\tlet elf_fl = Self::load(&buffer);\n\t\tif elf_fl.is_err() {\n\t\t\treturn Err(elf_fl.err().unwrap());\n\t\t}\n\t\tlet elf_fl = elf_fl.ok().unwrap();\n\t\tlet mut sz = 0usize;\n\t\t// Get the size, in memory, that we're going to need for the program storage.\n\t\tfor p in elf_fl.programs.iter() {\n\t\t\tsz += p.header.memsz;\n\t\t}\n\t\t// We add two pages since we could possibly split the front and back pages, hence\n\t\t// necessitating the need for two extra pages. This can get wasteful, but for now\n\t\t// if we don't do this, we could end up mapping into the MMU table!\n\t\tlet program_pages = (sz + PAGE_SIZE * 2) / PAGE_SIZE;\n\t\t// I did this to demonstrate the expressive nature of Rust. Kinda cool, no?\n\t\tlet my_pid = unsafe {\n\t\t\tlet p = NEXT_PID + 1;\n\t\t\tNEXT_PID += 1;\n\t\t\tp\n\t\t};\n\t\tlet mut my_proc = Process { frame:       zalloc(1) as *mut TrapFrame,\n\t\t                            stack:       zalloc(STACK_PAGES),\n\t\t                            pid:         my_pid,\n\t\t                            mmu_table:        zalloc(1) as *mut Table,\n\t\t                            state:       ProcessState::Running,\n\t\t                            data:        ProcessData::new(),\n\t\t                            sleep_until: 0,\n\t\t\t\t\t\t\t\t\tprogram:     zalloc(program_pages),\n\t\t\t\t\t\t\t\t\tbrk:         0,\n\t\t\t\t\t\t\t\t };\n\n\t\tlet program_mem = my_proc.program;\n\t\tlet table = unsafe { my_proc.mmu_table.as_mut().unwrap() };\n\t\t// The ELF has several \"program headers\". This usually mimics the .text,\n\t\t// .rodata, .data, and .bss sections, but not necessarily.\n\t\t// What we do here is map the program headers into the process' page\n\t\t// table.\n\t\tfor p in elf_fl.programs.iter() {\n\t\t\t// The program header table starts where the ELF header says it is\n\t\t\t// given by the field phoff (program header offset).\n\t\t\t// Copy the buffer we got from the filesystem into the program\n\t\t\t// memory we're going to map to the user. The memsz field in the\n\t\t\t// program header tells us how many bytes will need to be loaded.\n\t\t\t// The ph.off is the offset to load this into.\n\t\t\tunsafe {\n\t\t\t\tmemcpy(program_mem.add(p.header.off), p.data.get(), p.header.memsz);\n\t\t\t}\n\t\t\t// We start off with the user bit set.\n\t\t\tlet mut bits = EntryBits::User.val();\n\t\t\t// This sucks, but we check each bit in the flags to see\n\t\t\t// if we need to add it to the PH permissions.\n\t\t\tif p.header.flags & PROG_EXECUTE != 0 {\n\t\t\t\tbits |= EntryBits::Execute.val();\n\t\t\t}\n\t\t\tif p.header.flags & PROG_READ != 0 {\n\t\t\t\tbits |= EntryBits::Read.val();\n\t\t\t}\n\t\t\tif p.header.flags & PROG_WRITE != 0 {\n\t\t\t\tbits |= EntryBits::Write.val();\n\t\t\t}\n\t\t\t// Now we map the program counter. The virtual address\n\t\t\t// is provided in the ELF program header.\n\t\t\tlet pages = (p.header.memsz + PAGE_SIZE) / PAGE_SIZE;\n\t\t\tfor i in 0..pages {\n\t\t\t\tlet vaddr = p.header.vaddr + i * PAGE_SIZE;\n\t\t\t\t// The ELF specifies a paddr, but not when we\n\t\t\t\t// use the vaddr!\n\t\t\t\tlet paddr = program_mem as usize + p.header.off + i * PAGE_SIZE;\n\t\t\t\t// There is no checking here! This is very dangerous, and I have already\n\t\t\t\t// been bitten by it. I mapped too far and mapped userspace into the MMU\n\t\t\t\t// table, which is AWFUL!\n\t\t\t\tmap(table, vaddr, paddr, bits, 0);\n\t\t\t\tif vaddr > my_proc.brk {\n\t\t\t\t\tmy_proc.brk = vaddr;\n\t\t\t\t}\n\t\t\t\t// println!(\"DEBUG: Map 0x{:08x} to 0x{:08x} {:02x}\", vaddr, paddr, bits);\n\t\t\t}\n\t\t\tmy_proc.brk += 0x1000;\n\t\t}\n\t\t// This will map all of the program pages. Notice that in linker.lds in\n\t\t// userspace we set the entry point address to 0x2000_0000. This is the\n\t\t// same address as PROCESS_STARTING_ADDR, and they must match.\n\t\t// Map the stack\n\t\tlet ptr = my_proc.stack as *mut u8;\n\t\tfor i in 0..STACK_PAGES {\n\t\t\tlet vaddr = STACK_ADDR + i * PAGE_SIZE;\n\t\t\tlet paddr = ptr as usize + i * PAGE_SIZE;\n\t\t\t// We create the stack. We don't load a stack from the disk.\n\t\t\t// This is why I don't need to make the stack executable.\n\t\t\tmap(table, vaddr, paddr, EntryBits::UserReadWrite.val(), 0);\n\t\t}\n\t\t// Set everything up in the trap frame\n\t\tunsafe {\n\t\t\t// The program counter is a virtual memory address and is loaded\n\t\t\t// into mepc when we execute mret.\n\t\t\t(*my_proc.frame).pc = elf_fl.header.entry_addr;\n\t\t\t// Stack pointer. The stack starts at the bottom and works its\n\t\t\t// way up, so we have to set the stack pointer to the bottom.\n\t\t\t(*my_proc.frame).regs[Registers::Sp as usize] = STACK_ADDR as usize + STACK_PAGES * PAGE_SIZE - 0x1000;\n\t\t\t// USER MODE! This is how we set what'll go into mstatus when we\n\t\t\t// run the process.\n\t\t\t(*my_proc.frame).mode = CpuMode::User as usize;\n\t\t\t(*my_proc.frame).pid = my_proc.pid as usize;\n\t\t\t// The SATP register is used for the MMU, so we need to\n\t\t\t// map our table into that register. The switch_to_user\n\t\t\t// function will load .satp into the actual register\n\t\t\t// when the time comes.\n\t\t\t(*my_proc.frame).satp = build_satp(SatpMode::Sv39, my_proc.pid as usize, my_proc.mmu_table as usize);\n\t\t}\n\t\t// The ASID field of the SATP register is only 16-bits, and we reserved\n\t\t// 0 for the kernel, even though we run the kernel in machine mode for\n\t\t// now. Since we don't reuse PIDs, this means that we can only spawn\n\t\t// 65534 processes.\n\t\tsatp_fence_asid(my_pid as usize);\n\t\tOk(my_proc)\n\t}\n}\n"
  },
  {
    "path": "risc_v/src/fs.rs",
    "content": "// minixfs.rs\n// Minix 3 Filesystem Implementation\n// Stephen Marz\n// 16 March 2020\n\nuse crate::{cpu::Registers,\n            process::{add_kernel_process_args, get_by_pid, set_running, set_waiting},\n            syscall::syscall_block_read};\n\nuse crate::{buffer::Buffer, cpu::memcpy};\nuse alloc::{boxed::Box, collections::BTreeMap, string::String};\nuse core::mem::size_of;\n\npub const MAGIC: u16 = 0x4d5a;\npub const BLOCK_SIZE: u32 = 1024;\npub const NUM_IPTRS: usize = BLOCK_SIZE as usize / 4;\npub const S_IFDIR: u16 = 0o040_000;\npub const S_IFREG: u16 = 0o100_000;\n/// The superblock describes the file system on the disk. It gives\n/// us all the information we need to read the file system and navigate\n/// the file system, including where to find the inodes and zones (blocks).\n#[repr(C)]\npub struct SuperBlock {\n\tpub ninodes:         u32,\n\tpub pad0:            u16,\n\tpub imap_blocks:     u16,\n\tpub zmap_blocks:     u16,\n\tpub first_data_zone: u16,\n\tpub log_zone_size:   u16,\n\tpub pad1:            u16,\n\tpub max_size:        u32,\n\tpub zones:           u32,\n\tpub magic:           u16,\n\tpub pad2:            u16,\n\tpub block_size:      u16,\n\tpub disk_version:    u8\n}\n\n/// An inode stores the \"meta-data\" to a file. The mode stores the permissions\n/// AND type of file. This is how we differentiate a directory from a file. A file\n/// size is in here too, which tells us how many blocks we need to read. Finally, the\n/// zones array points to where we can find the blocks, which is where the data\n/// is contained for the file.\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct Inode {\n\tpub mode:   u16,\n\tpub nlinks: u16,\n\tpub uid:    u16,\n\tpub gid:    u16,\n\tpub size:   u32,\n\tpub atime:  u32,\n\tpub mtime:  u32,\n\tpub ctime:  u32,\n\tpub zones:  [u32; 10]\n}\n\n/// Notice that an inode does not contain the name of a file. This is because\n/// more than one file name may refer to the same inode. These are called \"hard links\"\n/// Instead, a DirEntry essentially associates a file name with an inode as shown in\n/// the structure below.\n#[repr(C)]\npub struct DirEntry {\n\tpub inode: u32,\n\tpub name:  [u8; 60]\n}\n\n/// The MinixFileSystem implements the FileSystem trait for the VFS.\npub struct MinixFileSystem;\n// The plan for this in the future is to have a single inode cache. What we\n// will do is have a cache of Node structures which will combine the Inode\n// with the block drive.\nstatic mut MFS_INODE_CACHE: [Option<BTreeMap<String, Inode>>; 8] = [None, None, None, None, None, None, None, None];\n\nimpl MinixFileSystem {\n\t/// Inodes are the meta-data of a file, including the mode (permissions and type) and\n\t/// the file's size. They are stored above the data zones, but to figure out where we\n\t/// need to go to get the inode, we first need the superblock, which is where we can\n\t/// find all of the information about the filesystem itself.\n\tpub fn get_inode(bdev: usize, inode_num: u32) -> Option<Inode> {\n\t\t// When we read, everything needs to be a multiple of a sector (512 bytes)\n\t\t// So, we need to have memory available that's at least 512 bytes, even if\n\t\t// we only want 10 bytes or 32 bytes (size of an Inode).\n\t\tlet mut buffer = Buffer::new(1024);\n\n\t\t// Here is a little memory trick. We have a reference and it will refer to the\n\t\t// top portion of our buffer. Since we won't be using the super block and inode\n\t\t// simultaneously, we can overlap the memory regions.\n\n\t\t// For Rust-ers, I'm showing two ways here. The first way is to get a reference\n\t\t// from a pointer. You will see the &* a lot in Rust for references. Rust\n\t\t// makes dereferencing a pointer cumbersome, which lends to not using them.\n\t\tlet super_block = unsafe { &*(buffer.get_mut() as *mut SuperBlock) };\n\t\t// I opted for a pointer here instead of a reference because we will be offsetting the inode by a certain amount.\n\t\tlet inode = buffer.get_mut() as *mut Inode;\n\t\t// Read from the block device. The size is 1 sector (512 bytes) and our offset is past\n\t\t// the boot block (first 1024 bytes). This is where the superblock sits.\n\t\tsyc_read(bdev, buffer.get_mut(), 512, 1024);\n\t\tif super_block.magic == MAGIC {\n\t\t\t// If we get here, we successfully read what we think is the super block.\n\t\t\t// The math here is 2 - one for the boot block, one for the super block. Then we\n\t\t\t// have to skip the bitmaps blocks. We have a certain number of inode map blocks (imap)\n\t\t\t// and zone map blocks (zmap).\n\t\t\t// The inode comes to us as a NUMBER, not an index. So, we need to subtract 1.\n\t\t\tlet inode_offset = (2 + super_block.imap_blocks + super_block.zmap_blocks) as usize * BLOCK_SIZE as usize\n\t\t\t                   + ((inode_num as usize - 1) / (BLOCK_SIZE as usize / size_of::<Inode>())) * BLOCK_SIZE as usize;\n\n\t\t\t// Now, we read the inode itself.\n\t\t\t// The block driver requires that our offset be a multiple of 512. We do that with the\n\t\t\t// inode_offset. However, we're going to be reading a group of inodes.\n\t\t\tsyc_read(bdev, buffer.get_mut(), 1024, inode_offset as u32);\n\n\t\t\t// There are 1024 / size_of<Inode>() inodes in each read that we can do. However, we need to figure out which inode in that group we need to read. We just take the % of this to find out.\n\t\t\tlet read_this_node = (inode_num as usize - 1) % (BLOCK_SIZE as usize / size_of::<Inode>());\n\n\t\t\t// We copy the inode over. This might not be the best thing since the Inode will\n\t\t\t// eventually have to change after writing.\n\t\t\treturn unsafe { Some(*(inode.add(read_this_node))) };\n\t\t}\n\t\t// If we get here, some result wasn't OK. Either the super block\n\t\t// or the inode itself.\n\t\tNone\n\t}\n}\n\nimpl MinixFileSystem {\n\t/// Init is where we would cache the superblock and inode to avoid having to read\n\t/// it over and over again, like we do for read right now.\n\tfn cache_at(btm: &mut BTreeMap<String, Inode>, cwd: &String, inode_num: u32, bdev: usize) {\n\t\tlet ino = Self::get_inode(bdev, inode_num).unwrap();\n\t\tlet mut buf = Buffer::new(((ino.size + BLOCK_SIZE - 1) & !BLOCK_SIZE) as usize);\n\t\tlet dirents = buf.get() as *const DirEntry;\n\t\tlet sz = Self::read(bdev, &ino, buf.get_mut(), BLOCK_SIZE, 0);\n\t\tlet num_dirents = sz as usize / size_of::<DirEntry>();\n\t\t// We start at 2 because the first two entries are . and ..\n\t\tfor i in 2..num_dirents {\n\t\t\tunsafe {\n\t\t\t\tlet ref d = *dirents.add(i);\n\t\t\t\tlet d_ino = Self::get_inode(bdev, d.inode).unwrap();\n\t\t\t\tlet mut new_cwd = String::with_capacity(120);\n\t\t\t\tfor i in cwd.bytes() {\n\t\t\t\t\tnew_cwd.push(i as char);\n\t\t\t\t}\n\t\t\t\t// Add a directory separator between this inode and the next.\n\t\t\t\t// If we're the root (inode 1), we don't want to double up the\n\t\t\t\t// frontslash, so only do it for non-roots.\n\t\t\t\tif inode_num != 1 {\n\t\t\t\t\tnew_cwd.push('/');\n\t\t\t\t}\n\t\t\t\tfor i in 0..60 {\n\t\t\t\t\tif d.name[i] == 0 {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tnew_cwd.push(d.name[i] as char);\n\t\t\t\t}\n\t\t\t\tnew_cwd.shrink_to_fit();\n\t\t\t\tif d_ino.mode & S_IFDIR != 0 {\n\t\t\t\t\t// This is a directory, cache these. This is a recursive call,\n\t\t\t\t\t// which I don't really like.\n\t\t\t\t\tSelf::cache_at(btm, &new_cwd, d.inode, bdev);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tbtm.insert(new_cwd, d_ino);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// Run this ONLY in a process!\n\tpub fn init(bdev: usize) {\n\t\tif unsafe { MFS_INODE_CACHE[bdev - 1].is_none() } {\n\t\t\tlet mut btm = BTreeMap::new();\n\t\t\tlet cwd = String::from(\"/\");\n\n\t\t\t// Let's look at the root (inode #1)\n\t\t\tSelf::cache_at(&mut btm, &cwd, 1, bdev);\n\t\t\tunsafe {\n\t\t\t\tMFS_INODE_CACHE[bdev - 1] = Some(btm);\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tprintln!(\"KERNEL: Initialized an already initialized filesystem {}\", bdev);\n\t\t}\n\t}\n\n\t/// The goal of open is to traverse the path given by path. If we cache the inodes\n\t/// in RAM, it might make this much quicker. For now, this doesn't do anything since\n\t/// we're just testing read based on if we know the Inode we're looking for.\n\tpub fn open(bdev: usize, path: &str) -> Result<Inode, FsError> {\n\t\tif let Some(cache) = unsafe { MFS_INODE_CACHE[bdev - 1].take() } {\n\t\t\tlet ret;\n\t\t\tif let Some(inode) = cache.get(path) {\n\t\t\t\tret = Ok(*inode);\n\t\t\t}\n\t\t\telse {\n\t\t\t\tret = Err(FsError::FileNotFound);\n\t\t\t}\n\t\t\tunsafe {\n\t\t\t\tMFS_INODE_CACHE[bdev - 1].replace(cache);\n\t\t\t}\n\t\t\tret\n\t\t}\n\t\telse {\n\t\t\tErr(FsError::FileNotFound)\n\t\t}\n\t}\n\n\tpub fn read(bdev: usize, inode: &Inode, buffer: *mut u8, size: u32, offset: u32) -> u32 {\n\t\t// Our strategy here is to use blocks to see when we need to start reading\n\t\t// based on the offset. That's offset_block. Then, the actual byte within\n\t\t// that block that we need is offset_byte.\n\t\tlet mut blocks_seen = 0u32;\n\t\tlet offset_block = offset / BLOCK_SIZE;\n\t\tlet mut offset_byte = offset % BLOCK_SIZE;\n\t\t// First, the _size parameter (now in bytes_left) is the size of the buffer, not\n\t\t// necessarily the size of the file. If our buffer is bigger than the file, we're OK.\n\t\t// If our buffer is smaller than the file, then we can only read up to the buffer size.\n\t\tlet mut bytes_left = if size > inode.size {\n\t\t\tinode.size\n\t\t}\n\t\telse {\n\t\t\tsize\n\t\t};\n\t\tlet mut bytes_read = 0u32;\n\t\t// The block buffer automatically drops when we quit early due to an error or we've read enough. This will be the holding port when we go out and read a block. Recall that even if we want 10 bytes, we have to read the entire block (really only 512 bytes of the block) first. So, we use the block_buffer as the middle man, which is then copied into the buffer.\n\t\tlet mut block_buffer = Buffer::new(BLOCK_SIZE as usize);\n\t\t// Triply indirect zones point to a block of pointers (BLOCK_SIZE / 4). Each one of those pointers points to another block of pointers (BLOCK_SIZE / 4). Each one of those pointers yet again points to another block of pointers (BLOCK_SIZE / 4). This is why we have indirect, iindirect (doubly), and iiindirect (triply).\n\t\tlet mut indirect_buffer = Buffer::new(BLOCK_SIZE as usize);\n\t\tlet mut iindirect_buffer = Buffer::new(BLOCK_SIZE as usize);\n\t\tlet mut iiindirect_buffer = Buffer::new(BLOCK_SIZE as usize);\n\t\t// I put the pointers *const u32 here. That means we will allocate the indirect, doubly indirect, and triply indirect even for small files. I initially had these in their respective scopes, but that required us to recreate the indirect buffer for doubly indirect and both the indirect and doubly indirect buffers for the triply indirect. Not sure which is better, but I probably wasted brain cells on this.\n\t\tlet izones = indirect_buffer.get() as *const u32;\n\t\tlet iizones = iindirect_buffer.get() as *const u32;\n\t\tlet iiizones = iiindirect_buffer.get() as *const u32;\n\n\t\t// ////////////////////////////////////////////\n\t\t// // DIRECT ZONES\n\t\t// ////////////////////////////////////////////\n\t\t// In Rust, our for loop automatically \"declares\" i from 0 to < 7. The syntax\n\t\t// 0..7 means 0 through to 7 but not including 7. If we want to include 7, we\n\t\t// would use the syntax 0..=7.\n\t\tfor i in 0..7 {\n\t\t\t// There are 7 direct zones in the Minix 3 file system. So, we can just read them one by one. Any zone that has the value 0 is skipped and we check the next zones. This might happen as we start writing and truncating.\n\t\t\tif inode.zones[i] == 0 {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// We really use this to keep track of when we need to actually start reading\n\t\t\t// But an if statement probably takes more time than just incrementing it.\n\t\t\tif offset_block <= blocks_seen {\n\t\t\t\t// If we get here, then our offset is within our window that we want to see.\n\t\t\t\t// We need to go to the direct pointer's index. That'll give us a block INDEX.\n\t\t\t\t// That makes it easy since all we have to do is multiply the block size\n\t\t\t\t// by whatever we get. If it's 0, we skip it and move on.\n\t\t\t\tlet zone_offset = inode.zones[i] * BLOCK_SIZE;\n\t\t\t\t// We read the zone, which is where the data is located. The zone offset is simply the block\n\t\t\t\t// size times the zone number. This makes it really easy to read!\n\t\t\t\tsyc_read(bdev, block_buffer.get_mut(), BLOCK_SIZE, zone_offset);\n\n\t\t\t\t// There's a little bit of math to see how much we need to read. We don't want to read\n\t\t\t\t// more than the buffer passed in can handle, and we don't want to read if we haven't\n\t\t\t\t// taken care of the offset. For example, an offset of 10000 with a size of 2 means we\n\t\t\t\t// can only read bytes 10,000 and 10,001.\n\t\t\t\tlet read_this_many = if BLOCK_SIZE - offset_byte > bytes_left {\n\t\t\t\t\tbytes_left\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tBLOCK_SIZE - offset_byte\n\t\t\t\t};\n\t\t\t\t// Once again, here we actually copy the bytes into the final destination, the buffer. This memcpy\n\t\t\t\t// is written in cpu.rs.\n\t\t\t\tunsafe {\n\t\t\t\t\tmemcpy(buffer.add(bytes_read as usize), block_buffer.get().add(offset_byte as usize), read_this_many as usize);\n\t\t\t\t}\n\t\t\t\t// Regardless of whether we have an offset or not, we reset the offset byte back to 0. This\n\t\t\t\t// probably will get set to 0 many times, but who cares?\n\t\t\t\toffset_byte = 0;\n\t\t\t\t// Reset the statistics to see how many bytes we've read versus how many are left.\n\t\t\t\tbytes_read += read_this_many;\n\t\t\t\tbytes_left -= read_this_many;\n\t\t\t\t// If no more bytes are left, then we're done.\n\t\t\t\tif bytes_left == 0 {\n\t\t\t\t\treturn bytes_read;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// The blocks_seen is for the offset. We need to skip a certain number of blocks FIRST before getting\n\t\t\t// to the offset. The reason we need to read the zones is because we need to skip zones of 0, and they\n\t\t\t// do not contribute as a \"seen\" block.\n\t\t\tblocks_seen += 1;\n\t\t}\n\t\t// ////////////////////////////////////////////\n\t\t// // SINGLY INDIRECT ZONES\n\t\t// ////////////////////////////////////////////\n\t\t// Each indirect zone is a list of pointers, each 4 bytes. These then\n\t\t// point to zones where the data can be found. Just like with the direct zones,\n\t\t// we need to make sure the zone isn't 0. A zone of 0 means skip it.\n\t\tif inode.zones[7] != 0 {\n\t\t\tsyc_read(bdev, indirect_buffer.get_mut(), BLOCK_SIZE, BLOCK_SIZE * inode.zones[7]);\n\t\t\tlet izones = indirect_buffer.get() as *const u32;\n\t\t\tfor i in 0..NUM_IPTRS {\n\t\t\t\t// Where do I put unsafe? Dereferencing the pointers and memcpy are the unsafe functions.\n\t\t\t\tunsafe {\n\t\t\t\t\tif izones.add(i).read() != 0 {\n\t\t\t\t\t\tif offset_block <= blocks_seen {\n\t\t\t\t\t\t\tsyc_read(bdev, block_buffer.get_mut(), BLOCK_SIZE, BLOCK_SIZE * izones.add(i).read());\n\t\t\t\t\t\t\tlet read_this_many = if BLOCK_SIZE - offset_byte > bytes_left {\n\t\t\t\t\t\t\t\tbytes_left\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\tBLOCK_SIZE - offset_byte\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\tmemcpy(buffer.add(bytes_read as usize), block_buffer.get().add(offset_byte as usize), read_this_many as usize);\n\t\t\t\t\t\t\tbytes_read += read_this_many;\n\t\t\t\t\t\t\tbytes_left -= read_this_many;\n\t\t\t\t\t\t\toffset_byte = 0;\n\t\t\t\t\t\t\tif bytes_left == 0 {\n\t\t\t\t\t\t\t\treturn bytes_read;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tblocks_seen += 1;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// ////////////////////////////////////////////\n\t\t// // DOUBLY INDIRECT ZONES\n\t\t// ////////////////////////////////////////////\n\t\tif inode.zones[8] != 0 {\n\t\t\tsyc_read(bdev, indirect_buffer.get_mut(), BLOCK_SIZE, BLOCK_SIZE * inode.zones[8]);\n\t\t\tunsafe {\n\t\t\t\tfor i in 0..NUM_IPTRS {\n\t\t\t\t\tif izones.add(i).read() != 0 {\n\t\t\t\t\t\tsyc_read(bdev, iindirect_buffer.get_mut(), BLOCK_SIZE, BLOCK_SIZE * izones.add(i).read());\n\t\t\t\t\t\tfor j in 0..NUM_IPTRS {\n\t\t\t\t\t\t\tif iizones.add(j).read() != 0 {\n\t\t\t\t\t\t\t\t// Notice that this inner code is the same for all end-zone pointers. I'm thinking about\n\t\t\t\t\t\t\t\t// moving this out of here into a function of its own, but that might make it harder\n\t\t\t\t\t\t\t\t// to follow.\n\t\t\t\t\t\t\t\tif offset_block <= blocks_seen {\n\t\t\t\t\t\t\t\t\tsyc_read(bdev, block_buffer.get_mut(), BLOCK_SIZE, BLOCK_SIZE * iizones.add(j).read());\n\t\t\t\t\t\t\t\t\tlet read_this_many = if BLOCK_SIZE - offset_byte > bytes_left {\n\t\t\t\t\t\t\t\t\t\tbytes_left\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\t\tBLOCK_SIZE - offset_byte\n\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\tmemcpy(\n\t\t\t\t\t\t\t\t\t       buffer.add(bytes_read as usize),\n\t\t\t\t\t\t\t\t\t       block_buffer.get().add(offset_byte as usize),\n\t\t\t\t\t\t\t\t\t       read_this_many as usize\n\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\tbytes_read += read_this_many;\n\t\t\t\t\t\t\t\t\tbytes_left -= read_this_many;\n\t\t\t\t\t\t\t\t\toffset_byte = 0;\n\t\t\t\t\t\t\t\t\tif bytes_left == 0 {\n\t\t\t\t\t\t\t\t\t\treturn bytes_read;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tblocks_seen += 1;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// ////////////////////////////////////////////\n\t\t// // TRIPLY INDIRECT ZONES\n\t\t// ////////////////////////////////////////////\n\t\tif inode.zones[9] != 0 {\n\t\t\tsyc_read(bdev, indirect_buffer.get_mut(), BLOCK_SIZE, BLOCK_SIZE * inode.zones[9]);\n\t\t\tunsafe {\n\t\t\t\tfor i in 0..NUM_IPTRS {\n\t\t\t\t\tif izones.add(i).read() != 0 {\n\t\t\t\t\t\tsyc_read(bdev, iindirect_buffer.get_mut(), BLOCK_SIZE, BLOCK_SIZE * izones.add(i).read());\n\t\t\t\t\t\tfor j in 0..NUM_IPTRS {\n\t\t\t\t\t\t\tif iizones.add(j).read() != 0 {\n\t\t\t\t\t\t\t\tsyc_read(bdev, iiindirect_buffer.get_mut(), BLOCK_SIZE, BLOCK_SIZE * iizones.add(j).read());\n\t\t\t\t\t\t\t\tfor k in 0..NUM_IPTRS {\n\t\t\t\t\t\t\t\t\tif iiizones.add(k).read() != 0 {\n\t\t\t\t\t\t\t\t\t\t// Hey look! This again.\n\t\t\t\t\t\t\t\t\t\tif offset_block <= blocks_seen {\n\t\t\t\t\t\t\t\t\t\t\tsyc_read(bdev, block_buffer.get_mut(), BLOCK_SIZE, BLOCK_SIZE * iiizones.add(k).read());\n\t\t\t\t\t\t\t\t\t\t\tlet read_this_many = if BLOCK_SIZE - offset_byte > bytes_left {\n\t\t\t\t\t\t\t\t\t\t\t\tbytes_left\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t\telse {\n\t\t\t\t\t\t\t\t\t\t\t\tBLOCK_SIZE - offset_byte\n\t\t\t\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t\t\t\t\t\tmemcpy(\n\t\t\t\t\t\t\t\t\t\t\t       buffer.add(bytes_read as usize),\n\t\t\t\t\t\t\t\t\t\t\t       block_buffer.get().add(offset_byte as usize),\n\t\t\t\t\t\t\t\t\t\t\t       read_this_many as usize\n\t\t\t\t\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\t\t\t\t\tbytes_read += read_this_many;\n\t\t\t\t\t\t\t\t\t\t\tbytes_left -= read_this_many;\n\t\t\t\t\t\t\t\t\t\t\toffset_byte = 0;\n\t\t\t\t\t\t\t\t\t\t\tif bytes_left == 0 {\n\t\t\t\t\t\t\t\t\t\t\t\treturn bytes_read;\n\t\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t\t\tblocks_seen += 1;\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Anyone else love this stairstep style? I probably should put the pointers in a function by themselves,\n\t\t// but I think that'll make it more difficult to see what's actually happening.\n\n\t\tbytes_read\n\t}\n\n\tpub fn write(&mut self, _desc: &Inode, _buffer: *const u8, _offset: u32, _size: u32) -> u32 {\n\t\t0\n\t}\n\n\tpub fn stat(&self, inode: &Inode) -> Stat {\n\t\tStat { mode: inode.mode,\n\t\t       size: inode.size,\n\t\t       uid:  inode.uid,\n\t\t       gid:  inode.gid }\n\t}\n}\n\n/// This is a wrapper function around the syscall_block_read. This allows me to do\n/// other things before I call the system call (or after). However, all the things I\n/// wanted to do are no longer there, so this is a worthless function.\nfn syc_read(bdev: usize, buffer: *mut u8, size: u32, offset: u32) -> u8 {\n\tsyscall_block_read(bdev, buffer, size, offset)\n}\n\n// We have to start a process when reading from a file since the block\n// device will block. We only want to block in a process context, not an\n// interrupt context.\nstruct ProcArgs {\n\tpub pid:    u16,\n\tpub dev:    usize,\n\tpub buffer: *mut u8,\n\tpub size:   u32,\n\tpub offset: u32,\n\tpub node:   u32\n}\n\n// This is the actual code ran inside of the read process.\nfn read_proc(args_addr: usize) {\n\tlet args = unsafe { Box::from_raw(args_addr as *mut ProcArgs) };\n\n\t// Start the read! Since we're in a kernel process, we can block by putting this\n\t// process into a waiting state and wait until the block driver returns.\n\tlet inode = MinixFileSystem::get_inode(args.dev, args.node);\n\tlet bytes = MinixFileSystem::read(args.dev, &inode.unwrap(), args.buffer, args.size, args.offset);\n\n\t// Let's write the return result into regs[10], which is A0.\n\tunsafe {\n\t\tlet ptr = get_by_pid(args.pid);\n\t\tif !ptr.is_null() {\n\t\t\t(*(*ptr).frame).regs[Registers::A0 as usize] = bytes as usize;\n\t\t}\n\t}\n\t// This is the process making the system call. The system itself spawns another process\n\t// which goes out to the block device. Since we're passed the read call, we need to awaken\n\t// the process and get it ready to go. The only thing this process needs to clean up is the\n\t// tfree(), but the user process doesn't care about that.\n\tset_running(args.pid);\n}\n\n/// System calls will call process_read, which will spawn off a kernel process to read\n/// the requested data.\npub fn process_read(pid: u16, dev: usize, node: u32, buffer: *mut u8, size: u32, offset: u32) {\n\t// println!(\"FS read {}, {}, 0x{:x}, {}, {}\", pid, dev, buffer as usize, size, offset);\n\tlet args = ProcArgs { pid,\n\t                      dev,\n\t                      buffer,\n\t                      size,\n\t                      offset,\n\t                      node };\n\tlet boxed_args = Box::new(args);\n\tset_waiting(pid);\n\tlet _ = add_kernel_process_args(read_proc, Box::into_raw(boxed_args) as usize);\n}\n\n/// Stats on a file. This generally mimics an inode\n/// since that's the information we want anyway.\n/// However, inodes are filesystem specific, and we\n/// want a more generic stat.\npub struct Stat {\n\tpub mode: u16,\n\tpub size: u32,\n\tpub uid:  u16,\n\tpub gid:  u16\n}\n\npub enum FsError {\n\tSuccess,\n\tFileNotFound,\n\tPermission,\n\tIsFile,\n\tIsDirectory\n}\n"
  },
  {
    "path": "risc_v/src/gpu.rs",
    "content": "// gpu.rs\n// Graphics stuff\n// Stephen Marz\n// 12 May 2020\n\n#![allow(dead_code)]\nuse crate::{page::{zalloc, PAGE_SIZE},\n\t\t\tkmem::{kmalloc, kfree},\n            virtio,\n            virtio::{MmioOffsets, Queue, StatusField, VIRTIO_RING_SIZE, Descriptor, VIRTIO_DESC_F_WRITE, VIRTIO_DESC_F_NEXT}};\nuse core::{mem::size_of, ptr::null_mut};\n// use alloc::boxed::Box;\n\nconst F_VIRGL: u32 = 0;\nconst F_EDID: u32 = 1;\nconst EVENT_DISPLAY: u32 = 1 << 0;\n#[repr(C)]\nstruct Config {\n\t//events_read signals pending events to the driver. The driver MUST NOT write to this field.\n\t// events_clear clears pending events in the device. Writing a ’1’ into a bit will clear the corresponding bit in events_read mimicking write-to-clear behavior.\n\t//num_scanouts specifies the maximum number of scanouts supported by the device. Minimum value is 1, maximum value is 16.\n\tevents_read: u32,\n\tevents_clear: u32,\n\tnum_scanouts: u32,\n\treserved: u32,\n}\n#[repr(u32)]\nenum CtrlType {\n\t// 2d commands\n\tCmdGetDisplayInfo = 0x0100,\n\tCmdResourceCreate2d,\n\tCmdResourceUref,\n\tCmdSetScanout,\n\tCmdResourceFlush,\n\tCmdTransferToHost2d,\n\tCmdResourceAttachBacking,\n\tCmdResourceDetachBacking,\n\tCmdGetCapsetInfo,\n\tCmdGetCapset,\n\tCmdGetEdid,\n\t// cursor commands\n\tCmdUpdateCursor = 0x0300,\n\tCmdMoveCursor,\n\t// success responses\n\tRespOkNoData = 0x1100,\n\tRespOkDisplayInfo,\n\tRespOkCapsetInfo,\n\tRespOkCapset,\n\tRespOkEdid,\n\t// error responses\n\tRespErrUnspec = 0x1200,\n\tRespErrOutOfMemory,\n\tRespErrInvalidScanoutId,\n\tRespErrInvalidResourceId,\n\tRespErrInvalidContextId,\n\tRespErrInvalidParameter,\n}\n\nconst FLAG_FENCE: u32 = 1 << 0;\n#[repr(C)]\nstruct CtrlHeader {\n\tctrl_type: CtrlType,\n\tflags: u32,\n\tfence_id: u64,\n\tctx_id: u32,\n\tpadding: u32\n}\n\nconst MAX_SCANOUTS: usize = 16;\n#[repr(C)]\n#[derive(Clone, Copy)]\npub struct Rect {\n\tpub x: u32,\n\tpub y: u32,\n\tpub width: u32,\n\tpub height: u32,\n}\n\nimpl Rect {\n\tpub const fn new(x: u32, y: u32, width: u32, height: u32) -> Self {\n\t\tSelf {\n\t\t\tx, y, width, height\n\t\t}\n\t}\n}\n#[repr(C)]\nstruct DisplayOne {\n\tr: Rect,\n\tenabled: u32,\n\tflags: u32,\n}\n\n#[repr(C)]\nstruct RespDisplayInfo {\n\thdr: CtrlHeader,\n\tpmodes: [DisplayOne; MAX_SCANOUTS],\n}\n#[repr(C)]\nstruct GetEdid {\n\thdr: CtrlHeader,\n\tscanout: u32,\n\tpadding: u32,\n}\n#[repr(C)]\nstruct RespEdid {\n\thdr: CtrlHeader,\n\tsize: u32,\n\tpadding: u32,\n\tedid: [u8; 1024],\n}\n#[repr(u32)]\nenum Formats {\n\tB8G8R8A8Unorm = 1,\n\tB8G8R8X8Unorm = 2,\n\tA8R8G8B8Unorm = 3,\n\tX8R8G8B8Unorm = 4,\n\tR8G8B8A8Unorm = 67,\n\tX8B8G8R8Unorm = 68,\n\tA8B8G8R8Unorm = 121,\n\tR8G8B8X8Unorm = 134,\n}\n\n#[repr(C)]\nstruct ResourceCreate2d {\n\thdr: CtrlHeader,\n\tresource_id: u32,\n\tformat: Formats,\n\twidth: u32,\n\theight: u32,\n}\n#[repr(C)]\nstruct ResourceUnref {\n\thdr: CtrlHeader,\n\tresource_id: u32,\n\tpadding: u32,\n}\n#[repr(C)]\nstruct SetScanout {\n\thdr: CtrlHeader,\n\tr: Rect,\n\tscanout_id: u32,\n\tresource_id: u32,\n}\n#[repr(C)]\nstruct ResourceFlush {\n\thdr: CtrlHeader,\n\tr: Rect,\n\tresource_id: u32,\n\tpadding: u32,\n}\n\n#[repr(C)]\nstruct TransferToHost2d {\n\thdr: CtrlHeader,\n\tr: Rect,\n\toffset: u64,\n\tresource_id: u32,\n\tpadding: u32,\n}\n#[repr(C)]\nstruct AttachBacking {\n\thdr: CtrlHeader,\n\tresource_id: u32,\n\tnr_entries: u32,\n}\n\n#[repr(C)]\nstruct MemEntry {\n\taddr: u64,\n\tlength: u32,\n\tpadding: u32,\n}\n\n#[repr(C)]\nstruct DetachBacking {\n\thdr: CtrlHeader,\n\tresource_id: u32,\n\tpadding: u32,\n}\n#[repr(C)]\nstruct CursorPos {\n\tscanout_id: u32,\n\tx: u32,\n\ty: u32,\n\tpadding: u32,\n}\n\n#[repr(C)]\nstruct UpdateCursor {\n\thdr: CtrlHeader,\n\tpos: CursorPos,\n\tresource_id: u32,\n\thot_x: u32,\n\thot_y: u32,\n\tpadding: u32,\n}\n\n#[derive(Clone, Copy)]\npub struct Pixel {\n\tpub r: u8,\n\tpub g: u8,\n\tpub b: u8,\n\tpub a: u8,\n}\nimpl Pixel {\n\tpub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self {\n\t\tSelf {\n\t\t\tr, g, b, a\n\t\t}\n\t}\n}\n\n// This is not in the specification, but this makes\n// it easier for us to do just a single kfree.\nstruct Request<RqT, RpT> {\n\trequest: RqT,\n\tresponse: RpT,\n}\n\nimpl<RqT, RpT> Request<RqT, RpT> {\n\tpub fn new(request: RqT) -> *mut Self {\n\t\tlet sz = size_of::<RqT>() + size_of::<RpT>();\n\t\tlet ptr = kmalloc(sz) as *mut Self;\n\t\tunsafe {\n\t\t\t(*ptr).request = request;\n\t\t}\n\t\tptr\n\t}\n}\n\nstruct Request3<RqT, RmT, RpT> {\n\trequest: RqT,\n\tmementries: RmT,\n\tresponse: RpT,\n}\n\nimpl<RqT, RmT, RpT> Request3<RqT, RmT, RpT> {\n\tpub fn new(request: RqT, meminfo: RmT) -> *mut Self {\n\t\tlet sz = size_of::<RqT>() + size_of::<RmT>() + size_of::<RpT>();\n\t\tlet ptr = kmalloc(sz) as *mut Self;\n\t\tunsafe {\n\t\t\t(*ptr).request = request;\n\t\t\t(*ptr).mementries = meminfo;\n\t\t}\n\t\tptr\n\t}\n}\n\npub struct Device {\n\tqueue:        *mut Queue,\n\tdev:          *mut u32,\n\tidx:          u16,\n\tack_used_idx: u16,\n\tframebuffer:  *mut Pixel,\n\twidth:        u32,\n\theight:       u32,\n}\n\nimpl Device {\n\tpub const fn new() -> Self {\n\t\tSelf { queue:        null_mut(),\n\t\t       dev:          null_mut(),\n\t\t       idx:          0,\n\t\t\t   ack_used_idx: 0, \n\t\t\t   framebuffer:  null_mut(),\n\t\t\t   width: 640,\n\t\t\t   height: 480\n\t\t}\n\t}\n\tpub fn get_framebuffer(&self) -> *mut Pixel {\n\t\tself.framebuffer\n\t}\n\tpub fn get_width(&self) -> u32 {\n\t\tself.width\n\t}\n\tpub fn get_height(&self) -> u32 {\n\t\tself.height\n\t}\n}\n\npub static mut GPU_DEVICES: [Option<Device>; 8] = [\n\tNone,\n\tNone,\n\tNone,\n\tNone,\n\tNone,\n\tNone,\n\tNone,\n\tNone,\n];\n\npub fn fill_rect(dev: &mut Device, rect: Rect, color: Pixel) {\n\tfor row in rect.y..(rect.y+rect.height) {\n\t\tfor col in rect.x..(rect.x+rect.width) {\n\t\t\tlet byte = row as usize * dev.width as usize + col as usize;\n\t\t\tunsafe {\n\t\t\t\tdev.framebuffer.add(byte).write(color);\n\t\t\t}\n\t\t}\n\t}\n}\n\npub fn stroke_rect(dev: &mut Device, rect: Rect, color: Pixel, size: u32) {\n\t// Essentially fill the four sides.\n\t// Top\n\tfill_rect(dev, Rect::new(\n\t\trect.x,\n\t\trect.y,\n\t\trect.width,\n\t\tsize\n\t), color);\n\t// Bottom\n\tfill_rect(dev, Rect::new(\n\t\trect.x,\n\t\trect.y+rect.height,\n\t\trect.width,\n\t\tsize\n\t), color);\n\t// Left\n\tfill_rect(dev, Rect::new(\n\t\trect.x,\n\t\trect.y,\n\t\tsize,\n\t\trect.height\n\t), color);\n\n\t// Right\n\tfill_rect(dev, Rect::new(\n\t\trect.x+rect.width,\n\t\trect.y,\n\t\tsize,\n\t\trect.height+size\n\t), color);\n}\n\npub fn init(gdev: usize)  {\n\tif let Some(mut dev) = unsafe { GPU_DEVICES[gdev-1].take() } {\n\t\t// Put some crap in the framebuffer:\n\t\t// First clear the buffer to white?\n\t\tfill_rect(&mut dev, Rect::new(0, 0, 640, 480), Pixel::new(2, 2, 2, 255));\n\t\t// fill_rect(&mut dev, Rect::new(15, 15, 200, 200), Pixel::new(255, 130, 0, 255));\n\t\t// stroke_rect(&mut dev, Rect::new( 255, 15, 150, 150), Pixel::new( 0, 0, 0, 255), 5);\n\t\t// draw_cosine(&mut dev, Rect::new(0, 300, 550, 60), Pixel::new(255, 15, 15, 255));\n\t\t// //// STEP 1: Create a host resource using create 2d\n\t\tlet rq = Request::new(ResourceCreate2d {\n\t\t\thdr: CtrlHeader {\n\t\t\t\tctrl_type: CtrlType::CmdResourceCreate2d,\n\t\t\t\tflags: 0,\n\t\t\t\tfence_id: 0,\n\t\t\t\tctx_id: 0,\n\t\t\t\tpadding: 0,\n\t\t\t},\n\t\t\tresource_id: 1,\n\t\t\tformat: Formats::R8G8B8A8Unorm,\n\t\t\twidth: dev.width,\n\t\t\theight: dev.height,\n\t\t});\n\t\tlet desc_c2d = Descriptor {\n\t\t\taddr: unsafe { &(*rq).request as *const ResourceCreate2d as u64 },\n\t\t\tlen: size_of::<ResourceCreate2d>() as u32,\n\t\t\tflags: VIRTIO_DESC_F_NEXT,\n\t\t\tnext: (dev.idx + 1) % VIRTIO_RING_SIZE as u16,\n\t\t};\n\t\tlet desc_c2d_resp = Descriptor {\n\t\t\taddr: unsafe { &(*rq).response as *const CtrlHeader as u64 },\n\t\t\tlen: size_of::<CtrlHeader>() as u32,\n\t\t\tflags: VIRTIO_DESC_F_WRITE,\n\t\t\tnext: 0,\n\t\t};\n\t\tunsafe {\n\t\t\tlet head = dev.idx;\n\t\t\t(*dev.queue).desc[dev.idx as usize] = desc_c2d;\n\t\t\tdev.idx = (dev.idx + 1) % VIRTIO_RING_SIZE as u16;\n\t\t\t(*dev.queue).desc[dev.idx as usize] = desc_c2d_resp;\n\t\t\tdev.idx = (dev.idx + 1) % VIRTIO_RING_SIZE as u16;\n\t\t\t(*dev.queue).avail.ring[(*dev.queue).avail.idx as usize % VIRTIO_RING_SIZE] = head;\n\t\t\t(*dev.queue).avail.idx =\n\t\t\t\t(*dev.queue).avail.idx.wrapping_add(1);\n\t\t}\n\t\t// //// STEP 2: Attach backing\n\t\tlet rq = Request3::new(AttachBacking {\n\t\t\thdr: CtrlHeader {\n\t\t\t\tctrl_type: CtrlType::CmdResourceAttachBacking,\n\t\t\t\tflags: 0,\n\t\t\t\tfence_id: 0,\n\t\t\t\tctx_id: 0,\n\t\t\t\tpadding: 0,\n\t\t\t},\n\t\t\tresource_id: 1,\n\t\t\tnr_entries: 1,\n\t\t},\n\t\tMemEntry {\n\t\t\taddr: dev.framebuffer as u64,\n\t\t\tlength: dev.width * dev.height * size_of::<Pixel>() as u32,\n\t\t\tpadding: 0, \n\t\t}\n\t\t);\n\t\tlet desc_ab = Descriptor {\n\t\t\taddr: unsafe { &(*rq).request as *const AttachBacking as u64 },\n\t\t\tlen: size_of::<AttachBacking>() as u32,\n\t\t\tflags: VIRTIO_DESC_F_NEXT,\n\t\t\tnext: (dev.idx + 1) % VIRTIO_RING_SIZE as u16,\n\t\t};\n\t\tlet desc_ab_mementry = Descriptor {\n\t\t\taddr: unsafe { &(*rq).mementries as *const MemEntry as u64 },\n\t\t\tlen: size_of::<MemEntry>() as u32,\n\t\t\tflags: VIRTIO_DESC_F_NEXT,\n\t\t\tnext: (dev.idx + 2) % VIRTIO_RING_SIZE as u16,\n\t\t};\n\t\tlet desc_ab_resp = Descriptor {\n\t\t\taddr: unsafe { &(*rq).response as *const CtrlHeader as u64 },\n\t\t\tlen: size_of::<CtrlHeader>() as u32,\n\t\t\tflags: VIRTIO_DESC_F_WRITE,\n\t\t\tnext: 0,\n\t\t};\n\t\tunsafe {\n\t\t\tlet head = dev.idx;\n\t\t\t(*dev.queue).desc[dev.idx as usize] = desc_ab;\n\t\t\tdev.idx = (dev.idx + 1) % VIRTIO_RING_SIZE as u16;\n\t\t\t(*dev.queue).desc[dev.idx as usize] = desc_ab_mementry;\n\t\t\tdev.idx = (dev.idx + 1) % VIRTIO_RING_SIZE as u16;\n\t\t\t(*dev.queue).desc[dev.idx as usize] = desc_ab_resp;\n\t\t\tdev.idx = (dev.idx + 1) % VIRTIO_RING_SIZE as u16;\n\t\t\t(*dev.queue).avail.ring[(*dev.queue).avail.idx as usize % VIRTIO_RING_SIZE] = head;\n\t\t\t(*dev.queue).avail.idx =\n\t\t\t\t(*dev.queue).avail.idx.wrapping_add(1);\n\t\t}\n\t\t// //// STEP 3: Set scanout\n\t\tlet rq = Request::new(SetScanout {\n\t\t\thdr: CtrlHeader {\n\t\t\t\tctrl_type: CtrlType::CmdSetScanout,\n\t\t\t\tflags: 0,\n\t\t\t\tfence_id: 0,\n\t\t\t\tctx_id: 0,\n\t\t\t\tpadding: 0,\n\t\t\t},\n\t\t\tr: Rect::new(0, 0, dev.width, dev.height),\n\t\t\tresource_id: 1,\n\t\t\tscanout_id: 0,\n\t\t});\n\t\tlet desc_sso = Descriptor {\n\t\t\taddr: unsafe { &(*rq).request as *const SetScanout as u64 },\n\t\t\tlen: size_of::<SetScanout>() as u32,\n\t\t\tflags: VIRTIO_DESC_F_NEXT,\n\t\t\tnext: (dev.idx + 1) % VIRTIO_RING_SIZE as u16,\n\t\t};\n\t\tlet desc_sso_resp = Descriptor {\n\t\t\taddr: unsafe { &(*rq).response as *const CtrlHeader as u64 },\n\t\t\tlen: size_of::<CtrlHeader>() as u32,\n\t\t\tflags: VIRTIO_DESC_F_WRITE,\n\t\t\tnext: 0,\n\t\t};\n\t\tunsafe {\n\t\t\tlet head = dev.idx;\n\t\t\t(*dev.queue).desc[dev.idx as usize] = desc_sso;\n\t\t\tdev.idx = (dev.idx + 1) % VIRTIO_RING_SIZE as u16;\n\t\t\t(*dev.queue).desc[dev.idx as usize] = desc_sso_resp;\n\t\t\tdev.idx = (dev.idx + 1) % VIRTIO_RING_SIZE as u16;\n\t\t\t(*dev.queue).avail.ring[(*dev.queue).avail.idx as usize % VIRTIO_RING_SIZE] = head;\n\t\t\t(*dev.queue).avail.idx =\n\t\t\t\t(*dev.queue).avail.idx.wrapping_add(1);\n\t\t}\n\t\t// //// STEP 4: Transfer to host\n\t\tlet rq = Request::new(TransferToHost2d {\n\t\t\thdr: CtrlHeader {\n\t\t\t\tctrl_type: CtrlType::CmdTransferToHost2d,\n\t\t\t\tflags: 0,\n\t\t\t\tfence_id: 0,\n\t\t\t\tctx_id: 0,\n\t\t\t\tpadding: 0,\n\t\t\t},\n\t\t\tr: Rect::new(0, 0, dev.width, dev.height),\n\t\t\toffset: 0,\n\t\t\tresource_id: 1,\n\t\t\tpadding: 0,\n\t\t});\n\t\tlet desc_t2h = Descriptor {\n\t\t\taddr: unsafe { &(*rq).request as *const TransferToHost2d as u64 },\n\t\t\tlen: size_of::<TransferToHost2d>() as u32,\n\t\t\tflags: VIRTIO_DESC_F_NEXT,\n\t\t\tnext: (dev.idx + 1) % VIRTIO_RING_SIZE as u16,\n\t\t};\n\t\tlet desc_t2h_resp = Descriptor {\n\t\t\taddr: unsafe { &(*rq).response as *const CtrlHeader as u64 },\n\t\t\tlen: size_of::<CtrlHeader>() as u32,\n\t\t\tflags: VIRTIO_DESC_F_WRITE,\n\t\t\tnext: 0,\n\t\t};\n\t\tunsafe {\n\t\t\tlet head = dev.idx;\n\t\t\t(*dev.queue).desc[dev.idx as usize] = desc_t2h;\n\t\t\tdev.idx = (dev.idx + 1) % VIRTIO_RING_SIZE as u16;\n\t\t\t(*dev.queue).desc[dev.idx as usize] = desc_t2h_resp;\n\t\t\tdev.idx = (dev.idx + 1) % VIRTIO_RING_SIZE as u16;\n\t\t\t(*dev.queue).avail.ring[(*dev.queue).avail.idx as usize % VIRTIO_RING_SIZE] = head;\n\t\t\t(*dev.queue).avail.idx =\n\t\t\t\t(*dev.queue).avail.idx.wrapping_add(1);\n\t\t}\n\t\t// Step 5: Flush\n\t\tlet rq = Request::new(ResourceFlush {\n\t\t\thdr: CtrlHeader {\n\t\t\t\tctrl_type: CtrlType::CmdResourceFlush,\n\t\t\t\tflags: 0,\n\t\t\t\tfence_id: 0,\n\t\t\t\tctx_id: 0,\n\t\t\t\tpadding: 0,\n\t\t\t},\n\t\t\tr: Rect::new(0, 0, dev.width, dev.height),\n\t\t\tresource_id: 1,\n\t\t\tpadding: 0,\n\t\t});\n\t\tlet desc_rf = Descriptor {\n\t\t\taddr: unsafe { &(*rq).request as *const ResourceFlush as u64 },\n\t\t\tlen: size_of::<ResourceFlush>() as u32,\n\t\t\tflags: VIRTIO_DESC_F_NEXT,\n\t\t\tnext: (dev.idx + 1) % VIRTIO_RING_SIZE as u16,\n\t\t};\n\t\tlet desc_rf_resp = Descriptor {\n\t\t\taddr: unsafe { &(*rq).response as *const CtrlHeader as u64 },\n\t\t\tlen: size_of::<CtrlHeader>() as u32,\n\t\t\tflags: VIRTIO_DESC_F_WRITE,\n\t\t\tnext: 0,\n\t\t};\n\t\tunsafe {\n\t\t\tlet head = dev.idx;\n\t\t\t(*dev.queue).desc[dev.idx as usize] = desc_rf;\n\t\t\tdev.idx = (dev.idx + 1) % VIRTIO_RING_SIZE as u16;\n\t\t\t(*dev.queue).desc[dev.idx as usize] = desc_rf_resp;\n\t\t\tdev.idx = (dev.idx + 1) % VIRTIO_RING_SIZE as u16;\n\t\t\t(*dev.queue).avail.ring[(*dev.queue).avail.idx as usize % VIRTIO_RING_SIZE] = head;\n\t\t\t(*dev.queue).avail.idx =\n\t\t\t\t(*dev.queue).avail.idx.wrapping_add(1);\n\t\t}\n\t\t// Run Queue\n\t\tunsafe {\n\t\t\tdev.dev\n\t\t\t.add(MmioOffsets::QueueNotify.scale32())\n\t\t\t.write_volatile(0);\n\t\t\tGPU_DEVICES[gdev-1].replace(dev);\n\t\t}\n\t}\n}\n\n/// Invalidate and transfer a rectangular portion of the screen.\n/// I found out that width and height are actually x2, y2...oh well.\npub fn transfer(gdev: usize, x: u32, y: u32, width: u32, height: u32) {\n\tif let Some(mut dev) = unsafe { GPU_DEVICES[gdev-1].take() } {\n\t\tlet rq = Request::new(TransferToHost2d {\n\t\t\thdr: CtrlHeader {\n\t\t\t\tctrl_type: CtrlType::CmdTransferToHost2d,\n\t\t\t\tflags: 0,\n\t\t\t\tfence_id: 0,\n\t\t\t\tctx_id: 0,\n\t\t\t\tpadding: 0,\n\t\t\t},\n\t\t\tr: Rect::new(x, y, width, height),\n\t\t\toffset: 0,\n\t\t\tresource_id: 1,\n\t\t\tpadding: 0,\n\t\t});\n\t\tlet desc_t2h = Descriptor {\n\t\t\taddr: unsafe { &(*rq).request as *const TransferToHost2d as u64 },\n\t\t\tlen: size_of::<TransferToHost2d>() as u32,\n\t\t\tflags: VIRTIO_DESC_F_NEXT,\n\t\t\tnext: (dev.idx + 1) % VIRTIO_RING_SIZE as u16,\n\t\t};\n\t\tlet desc_t2h_resp = Descriptor {\n\t\t\taddr: unsafe { &(*rq).response as *const CtrlHeader as u64 },\n\t\t\tlen: size_of::<CtrlHeader>() as u32,\n\t\t\tflags: VIRTIO_DESC_F_WRITE,\n\t\t\tnext: 0,\n\t\t};\n\t\tunsafe {\n\t\t\tlet head = dev.idx;\n\t\t\t(*dev.queue).desc[dev.idx as usize] = desc_t2h;\n\t\t\tdev.idx = (dev.idx + 1) % VIRTIO_RING_SIZE as u16;\n\t\t\t(*dev.queue).desc[dev.idx as usize] = desc_t2h_resp;\n\t\t\tdev.idx = (dev.idx + 1) % VIRTIO_RING_SIZE as u16;\n\t\t\t(*dev.queue).avail.ring[(*dev.queue).avail.idx as usize % VIRTIO_RING_SIZE] = head;\n\t\t\t(*dev.queue).avail.idx =\n\t\t\t\t(*dev.queue).avail.idx.wrapping_add(1);\n\t\t}\n\t\t// Step 5: Flush\n\t\tlet rq = Request::new(ResourceFlush {\n\t\t\thdr: CtrlHeader {\n\t\t\t\tctrl_type: CtrlType::CmdResourceFlush,\n\t\t\t\tflags: 0,\n\t\t\t\tfence_id: 0,\n\t\t\t\tctx_id: 0,\n\t\t\t\tpadding: 0,\n\t\t\t},\n\t\t\tr: Rect::new(x, y, width, height),\n\t\t\tresource_id: 1,\n\t\t\tpadding: 0,\n\t\t});\n\t\tlet desc_rf = Descriptor {\n\t\t\taddr: unsafe { &(*rq).request as *const ResourceFlush as u64 },\n\t\t\tlen: size_of::<ResourceFlush>() as u32,\n\t\t\tflags: VIRTIO_DESC_F_NEXT,\n\t\t\tnext: (dev.idx + 1) % VIRTIO_RING_SIZE as u16,\n\t\t};\n\t\tlet desc_rf_resp = Descriptor {\n\t\t\taddr: unsafe { &(*rq).response as *const CtrlHeader as u64 },\n\t\t\tlen: size_of::<CtrlHeader>() as u32,\n\t\t\tflags: VIRTIO_DESC_F_WRITE,\n\t\t\tnext: 0,\n\t\t};\n\t\tunsafe {\n\t\t\tlet head = dev.idx;\n\t\t\t(*dev.queue).desc[dev.idx as usize] = desc_rf;\n\t\t\tdev.idx = (dev.idx + 1) % VIRTIO_RING_SIZE as u16;\n\t\t\t(*dev.queue).desc[dev.idx as usize] = desc_rf_resp;\n\t\t\tdev.idx = (dev.idx + 1) % VIRTIO_RING_SIZE as u16;\n\t\t\t(*dev.queue).avail.ring[(*dev.queue).avail.idx as usize % VIRTIO_RING_SIZE] = head;\n\t\t\t(*dev.queue).avail.idx =\n\t\t\t\t(*dev.queue).avail.idx.wrapping_add(1);\n\t\t}\n\t\t// Run Queue\n\t\tunsafe {\n\t\t\tdev.dev\n\t\t\t.add(MmioOffsets::QueueNotify.scale32())\n\t\t\t.write_volatile(0);\n\t\t\tGPU_DEVICES[gdev-1].replace(dev);\n\t\t}\n\t}\n}\n\npub fn setup_gpu_device(ptr: *mut u32) -> bool {\n\tunsafe {\n\t\t// We can get the index of the device based on its address.\n\t\t// 0x1000_1000 is index 0\n\t\t// 0x1000_2000 is index 1\n\t\t// ...\n\t\t// 0x1000_8000 is index 7\n\t\t// To get the number that changes over, we shift right 12 places (3 hex digits)\n\t\tlet idx = (ptr as usize - virtio::MMIO_VIRTIO_START) >> 12;\n\t\t// [Driver] Device Initialization\n\t\t// 1. Reset the device (write 0 into status)\n\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(0);\n\t\tlet mut status_bits = StatusField::Acknowledge.val32();\n\t\t// 2. Set ACKNOWLEDGE status bit\n\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);\n\t\t// 3. Set the DRIVER status bit\n\t\tstatus_bits |= StatusField::DriverOk.val32();\n\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);\n\t\t// 4. Read device feature bits, write subset of feature\n\t\t// bits understood by OS and driver    to the device.\n\t\tlet host_features = ptr.add(MmioOffsets::HostFeatures.scale32()).read_volatile();\n\t\tptr.add(MmioOffsets::GuestFeatures.scale32()).write_volatile(host_features);\n\t\t// 5. Set the FEATURES_OK status bit\n\t\tstatus_bits |= StatusField::FeaturesOk.val32();\n\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);\n\t\t// 6. Re-read status to ensure FEATURES_OK is still set.\n\t\t// Otherwise, it doesn't support our features.\n\t\tlet status_ok = ptr.add(MmioOffsets::Status.scale32()).read_volatile();\n\t\t// If the status field no longer has features_ok set,\n\t\t// that means that the device couldn't accept\n\t\t// the features that we request. Therefore, this is\n\t\t// considered a \"failed\" state.\n\t\tif false == StatusField::features_ok(status_ok) {\n\t\t\tprint!(\"features fail...\");\n\t\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(StatusField::Failed.val32());\n\t\t\treturn false;\n\t\t}\n\t\t// 7. Perform device-specific setup.\n\t\t// Set the queue num. We have to make sure that the\n\t\t// queue size is valid because the device can only take\n\t\t// a certain size.\n\t\tlet qnmax = ptr.add(MmioOffsets::QueueNumMax.scale32()).read_volatile();\n\t\tptr.add(MmioOffsets::QueueNum.scale32()).write_volatile(VIRTIO_RING_SIZE as u32);\n\t\tif VIRTIO_RING_SIZE as u32 > qnmax {\n\t\t\tprint!(\"queue size fail...\");\n\t\t\treturn false;\n\t\t}\n\t\t// First, if the block device array is empty, create it!\n\t\t// We add 4095 to round this up and then do an integer\n\t\t// divide to truncate the decimal. We don't add 4096,\n\t\t// because if it is exactly 4096 bytes, we would get two\n\t\t// pages, not one.\n\t\tlet num_pages = (size_of::<Queue>() + PAGE_SIZE - 1) / PAGE_SIZE;\n\t\t// println!(\"np = {}\", num_pages);\n\t\t// We allocate a page for each device. This will the the\n\t\t// descriptor where we can communicate with the block\n\t\t// device. We will still use an MMIO register (in\n\t\t// particular, QueueNotify) to actually tell the device\n\t\t// we put something in memory. We also have to be\n\t\t// careful with memory ordering. We don't want to\n\t\t// issue a notify before all memory writes have\n\t\t// finished. We will look at that later, but we need\n\t\t// what is called a memory \"fence\" or barrier.\n\t\tptr.add(MmioOffsets::QueueSel.scale32()).write_volatile(0);\n\t\t// TODO: Set up queue #1 (cursorq)\n\n\t\t// Alignment is very important here. This is the memory address\n\t\t// alignment between the available and used rings. If this is wrong,\n\t\t// then we and the device will refer to different memory addresses\n\t\t// and hence get the wrong data in the used ring.\n\t\t// ptr.add(MmioOffsets::QueueAlign.scale32()).write_volatile(2);\n\t\tlet queue_ptr = zalloc(num_pages) as *mut Queue;\n\t\tlet queue_pfn = queue_ptr as u32;\n\t\tptr.add(MmioOffsets::GuestPageSize.scale32()).write_volatile(PAGE_SIZE as u32);\n\t\t// QueuePFN is a physical page number, however it\n\t\t// appears for QEMU we have to write the entire memory\n\t\t// address. This is a physical memory address where we\n\t\t// (the OS) and the block device have in common for\n\t\t// making and receiving requests.\n\t\tptr.add(MmioOffsets::QueuePfn.scale32()).write_volatile(queue_pfn / PAGE_SIZE as u32);\n\t\t// 8. Set the DRIVER_OK status bit. Device is now \"live\"\n\t\tstatus_bits |= StatusField::DriverOk.val32();\n\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);\n\n\t\t// We are going to give the framebuffer to user space, so this needs to be page aligned\n\t\t// so that we can map it into the user space's MMU. This is why we don't want kmalloc here!\n\t\tlet num_pages = (PAGE_SIZE * 2+640*480*size_of::<Pixel>())/PAGE_SIZE;\n\t\tlet page_alloc = zalloc(num_pages) as *mut Pixel;\n\t\tlet dev = Device {\n\t\t\tqueue: queue_ptr,\n\t\t\tdev: ptr,\n\t\t\tidx: 0,\n\t\t\tack_used_idx: 0,\n\t\t\tframebuffer: page_alloc,\n\t\t\twidth: 640,\n\t\t\theight: 480,\n\t\t};\n\n\t\tGPU_DEVICES[idx] = Some(dev);\n\n\t\ttrue\n\t}\n}\n\npub fn pending(dev: &mut Device) {\n\t// Here we need to check the used ring and then free the resources\n\t// given by the descriptor id.\n\tunsafe {\n\t\tlet ref queue = *dev.queue;\n\t\twhile dev.ack_used_idx != queue.used.idx {\n\t\t\tlet ref elem = queue.used.ring\n\t\t\t\t[dev.ack_used_idx as usize % VIRTIO_RING_SIZE];\n\t\t\t// println!(\"Ack {}, elem {}, len {}\", dev.ack_used_idx, elem.id, elem.len);\n\t\t\tlet ref desc = queue.desc[elem.id as usize];\n\t\t\t// Requests stay resident on the heap until this\n\t\t\t// function, so we can recapture the address here\n\t\t\tkfree(desc.addr as *mut u8);\n\t\t\tdev.ack_used_idx = dev.ack_used_idx.wrapping_add(1);\n\n\t\t}\n\t}\n}\n\npub fn handle_interrupt(idx: usize) {\n\tunsafe {\n\t\tif let Some(bdev) = GPU_DEVICES[idx].as_mut() {\n\t\t\tpending(bdev);\n\t\t}\n\t\telse {\n\t\t\tprintln!(\n\t\t\t         \"Invalid GPU device for interrupt {}\",\n\t\t\t         idx + 1\n\t\t\t);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "risc_v/src/input.rs",
    "content": "// input.rs\n// Input handling.\n// Stephen Marz\n\nuse crate::virtio::{Queue, MmioOffsets, MMIO_VIRTIO_START, StatusField, VIRTIO_RING_SIZE, Descriptor, VIRTIO_DESC_F_WRITE, VIRTIO_F_RING_EVENT_IDX};\nuse crate::kmem::kmalloc;\nuse crate::page::{PAGE_SIZE, zalloc};\nuse core::mem::size_of;\nuse alloc::collections::VecDeque;\n\npub static mut ABS_EVENTS: Option<VecDeque<Event>> = None;\n// pub static mut ABS_OBSERVERS: Option<VecDeque<u16>> = None;\npub static mut KEY_EVENTS: Option<VecDeque<Event>> = None;\n// pub static mut KEY_OBSERVERS: Option<VecDeque<u16>> = None;\n\nconst EVENT_BUFFER_ELEMENTS: usize = 64;\n\npub enum InputType {\n\tNone,\n\tAbs(u32, u32, u32, u32, u32),\n\tKey(u32, u32)\n}\n\n#[repr(C)]\n#[derive(Copy, Clone)]\npub struct Event {\n    pub event_type: EventType,\n    pub code: u16,\n    pub value: u32,\n}\n#[repr(u8)]\n#[derive(Copy, Clone)]\npub enum ConfigSelect {\n    UNSET = 0x00,\n    IdName = 0x01,\n    IdSerial = 0x02,\n    IdDevids = 0x03,\n    PropBits = 0x10,\n    EvBits = 0x11,\n    AbsInfo = 0x12,\n}\n#[repr(C)]\n#[derive(Clone, Copy)]\npub struct AbsInfo {\n    pub min: u32,\n    pub max: u32,\n    pub fuzz: u32,\n    pub flat: u32,\n    pub res: u32,\n}\n#[repr(C)]\n#[derive(Clone, Copy)]\npub struct DevIds {\n    pub bustype: u16,\n    pub vendor: u16,\n    pub product: u16,\n    pub version: u16,\n}\n\n#[repr(C)]\n#[derive(Clone, Copy)]\npub union ConfigUnion {\n    pub string: [u8; 128],\n    pub bitmap: [i8; 128],\n    pub abs: AbsInfo,\n    pub ids: DevIds,\n}\n\n#[repr(C)]\n#[derive(Clone, Copy)]\npub struct Config {\n    pub select: ConfigSelect,\n    pub subsel: u8,\n    pub size: u8,\n    reserved: [u8; 5],\n    pub config: ConfigUnion,\n}\n\n#[repr(u16)]\n#[derive(Copy, Clone)]\npub enum EventType {\n    Syn = 0x00,\n    Key = 0x01,\n    Rel = 0x02,\n    Abs = 0x03,\n    Msc = 0x04,\n    Sw = 0x05,\n    Led = 0x11,\n    Snd = 0x12,\n    Rep = 0x14,\n    Ff = 0x15,\n    Pwr = 0x16,\n    FfStatus = 0x17,\n    Max = 0x1f,\n}\n\nconst EVENT_SIZE: usize = size_of::<Event>();\n\npub struct Device {\n\tevent_queue:  *mut Queue,\n\tstatus_queue: *mut Queue,  \n\tevent_idx:          u16,\n\tevent_ack_used_idx: u16,\n\tevent_buffer: *mut Event,\n\tstatus_ack_used_idx: u16,\n}\n\npub static mut INPUT_DEVICES: [Option<Device>; 8] = [\n\tNone,\n\tNone,\n\tNone,\n\tNone,\n\tNone,\n\tNone,\n\tNone,\n\tNone,\n];\n\npub fn setup_input_device(ptr: *mut u32) -> bool {\n\tunsafe {\n\t\t// We can get the index of the device based on its address.\n\t\t// 0x1000_1000 is index 0\n\t\t// 0x1000_2000 is index 1\n\t\t// ...\n\t\t// 0x1000_8000 is index 7\n\t\t// To get the number that changes over, we shift right 12 places (3 hex digits)\n\t\tlet idx = (ptr as usize - MMIO_VIRTIO_START) >> 12;\n\t\t// [Driver] Device Initialization\n\t\t// 1. Reset the device (write 0 into status)\n\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(0);\n\t\tlet mut status_bits = StatusField::Acknowledge.val32();\n\t\t// 2. Set ACKNOWLEDGE status bit\n\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);\n\t\t// 3. Set the DRIVER status bit\n\t\tstatus_bits |= StatusField::DriverOk.val32();\n\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);\n\t\t// 4. Read device feature bits, write subset of feature\n\t\t// bits understood by OS and driver    to the device.\n\t\tlet mut host_features = ptr.add(MmioOffsets::HostFeatures.scale32()).read_volatile();\n\t\t// Turn off EVENT_IDX\n\t\thost_features &= !(1 << VIRTIO_F_RING_EVENT_IDX);\n\t\tptr.add(MmioOffsets::GuestFeatures.scale32()).write_volatile(host_features);\n\t\t// 5. Set the FEATURES_OK status bit\n\t\tstatus_bits |= StatusField::FeaturesOk.val32();\n\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);\n\t\t// 6. Re-read status to ensure FEATURES_OK is still set.\n\t\t// Otherwise, it doesn't support our features.\n\t\tlet status_ok = ptr.add(MmioOffsets::Status.scale32()).read_volatile();\n\t\t// If the status field no longer has features_ok set,\n\t\t// that means that the device couldn't accept\n\t\t// the features that we request. Therefore, this is\n\t\t// considered a \"failed\" state.\n\t\tif false == StatusField::features_ok(status_ok) {\n\t\t\tprint!(\"features fail...\");\n\t\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(StatusField::Failed.val32());\n\t\t\treturn false;\n\t\t}\n\t\t// 7. Perform device-specific setup.\n\t\t// Set the queue num. We have to make sure that the\n\t\t// queue size is valid because the device can only take\n\t\t// a certain size.\n\t\tlet qnmax = ptr.add(MmioOffsets::QueueNumMax.scale32()).read_volatile();\n\t\tptr.add(MmioOffsets::QueueNum.scale32()).write_volatile(VIRTIO_RING_SIZE as u32);\n\t\tif VIRTIO_RING_SIZE as u32 > qnmax {\n\t\t\tprint!(\"queue size fail...\");\n\t\t\treturn false;\n\t\t}\n\t\t// First, if the block device array is empty, create it!\n\t\t// We add 4095 to round this up and then do an integer\n\t\t// divide to truncate the decimal. We don't add 4096,\n\t\t// because if it is exactly 4096 bytes, we would get two\n\t\t// pages, not one.\n\t\tlet num_pages = (size_of::<Queue>() + PAGE_SIZE - 1) / PAGE_SIZE;\n\t\t// println!(\"np = {}\", num_pages);\n\t\t// We allocate a page for each device. This will the the\n\t\t// descriptor where we can communicate with the block\n\t\t// device. We will still use an MMIO register (in\n\t\t// particular, QueueNotify) to actually tell the device\n\t\t// we put something in memory. We also have to be\n\t\t// careful with memory ordering. We don't want to\n\t\t// issue a notify before all memory writes have\n\t\t// finished. We will look at that later, but we need\n\t\t// what is called a memory \"fence\" or barrier.\n\t\tptr.add(MmioOffsets::QueueSel.scale32()).write_volatile(0);\n\n\t\t// Alignment is very important here. This is the memory address\n\t\t// alignment between the available and used rings. If this is wrong,\n\t\t// then we and the device will refer to different memory addresses\n\t\t// and hence get the wrong data in the used ring.\n\t\t// ptr.add(MmioOffsets::QueueAlign.scale32()).write_volatile(2);\n\t\tlet event_queue_ptr = zalloc(num_pages) as *mut Queue;\n\t\tlet queue_pfn = event_queue_ptr as u32;\n\t\tptr.add(MmioOffsets::GuestPageSize.scale32()).write_volatile(PAGE_SIZE as u32);\n\t\tptr.add(MmioOffsets::QueuePfn.scale32()).write_volatile(queue_pfn / PAGE_SIZE as u32);\n\t\t// Status queue\n\t\tptr.add(MmioOffsets::QueueSel.scale32()).write_volatile(1);\n\t\t// Alignment is very important here. This is the memory address\n\t\t// alignment between the available and used rings. If this is wrong,\n\t\t// then we and the device will refer to different memory addresses\n\t\t// and hence get the wrong data in the used ring.\n\t\t// ptr.add(MmioOffsets::QueueAlign.scale32()).write_volatile(2);\n\t\tlet status_queue_ptr = zalloc(num_pages) as *mut Queue;\n\t\tlet queue_pfn = status_queue_ptr as u32;\n\t\tptr.add(MmioOffsets::GuestPageSize.scale32()).write_volatile(PAGE_SIZE as u32);\n\t\tptr.add(MmioOffsets::QueuePfn.scale32()).write_volatile(queue_pfn / PAGE_SIZE as u32);\n\t\t// 8. Set the DRIVER_OK status bit. Device is now \"live\"\n\t\tstatus_bits |= StatusField::DriverOk.val32();\n\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);\n\n        // let config_ptr = ptr.add(MmioOffsets::Config.scale32()) as *mut Config;\n\n        // let mut config = config_ptr.read_volatile();\n\n        // config.select = ConfigSelect::AbsInfo;\n        // config.subsel = 0;\n\n        // config_ptr.write_volatile(config);\n\t\t// let id = config_ptr.read_volatile().config.abs;\n\t\t// println!(\"Min: {}, Max: {}, fuzz: {}, flat: {}, res: {}\", id.min, id.max, id.fuzz, id.flat, id.res);\n\n\t\tlet mut dev = Device {\n\t\t\tevent_queue: event_queue_ptr,\n\t\t\tstatus_queue: status_queue_ptr,\n\t\t\tstatus_ack_used_idx: 0,\n\t\t\tevent_idx: 0,\n\t\t\tevent_ack_used_idx: 0,\n\t\t\tevent_buffer: kmalloc(EVENT_SIZE * EVENT_BUFFER_ELEMENTS) as *mut Event,\n\t\t};\n\t\tfor i in 0..EVENT_BUFFER_ELEMENTS {\n\t\t\trepopulate_event(&mut dev, i);\n\t\t}\n\t\tINPUT_DEVICES[idx] = Some(dev);\n\t\tABS_EVENTS = Some(VecDeque::with_capacity(100));\n\t\t// ABS_OBSERVERS = Some(VecDeque::new());\n\t\tKEY_EVENTS = Some(VecDeque::with_capacity(10));\n\t\t// KEY_OBSERVERS = Some(VecDeque::new());\n\n\t\ttrue\n\t}\n}\n\nunsafe fn repopulate_event(dev: &mut Device, buffer: usize) {\n// Populate eventq with buffers, these must be at least the size of struct virtio_input_event.\n\tlet desc = Descriptor {\n\t\taddr: dev.event_buffer.add(buffer) as u64,\n\t\tlen: EVENT_SIZE as u32,\n\t\tflags: VIRTIO_DESC_F_WRITE,\n\t\tnext: 0\n\t};\n\tlet head = dev.event_idx as u16;\n\t(*dev.event_queue).desc[dev.event_idx as usize] = desc;\n\tdev.event_idx = (dev.event_idx + 1) % VIRTIO_RING_SIZE as u16;\n\t(*dev.event_queue).avail.ring[(*dev.event_queue).avail.idx as usize % VIRTIO_RING_SIZE] = head;\n\t(*dev.event_queue).avail.idx = (*dev.event_queue).avail.idx.wrapping_add(1);\n}\n\nfn pending(dev: &mut Device) {\n\t// Here we need to check the used ring and then free the resources\n\t// given by the descriptor id.\n\tunsafe {\n\t\t// Check the event queue first\n\t\tlet ref queue = *dev.event_queue;\n\t\twhile dev.event_ack_used_idx != queue.used.idx {\n\t\t\tlet ref elem = queue.used.ring[dev.event_ack_used_idx as usize % VIRTIO_RING_SIZE];\n\t\t\tlet ref desc = queue.desc[elem.id as usize];\n\t\t\tlet event = (desc.addr as *const Event).as_ref().unwrap();\n\t\t\t// print!(\"EAck {}, elem {}, len {}, addr 0x{:08x}: \", dev.event_ack_used_idx, elem.id, elem.len, desc.addr as usize);\n\t\t\t// println!(\"Type = {:x}, Code = {:x}, Value = {:x}\", event.event_type, event.code, event.value);\n\t\t\trepopulate_event(dev, elem.id as usize);\n\t\t\tdev.event_ack_used_idx = dev.event_ack_used_idx.wrapping_add(1);\n\t\t\tmatch event.event_type {\n\t\t\t\tEventType::Abs => {\n\t\t\t\t\tlet mut ev = ABS_EVENTS.take().unwrap();\n\t\t\t\t\tev.push_back(*event);\n\t\t\t\t\tABS_EVENTS.replace(ev);\t\n\t\t\t\t},\n\t\t\t\tEventType::Key => {\n\t\t\t\t\tlet mut ev = KEY_EVENTS.take().unwrap();\n\t\t\t\t\tev.push_back(*event);\n\t\t\t\t\tKEY_EVENTS.replace(ev);\t\n\t\t\t\t},\n\t\t\t\t_ => {\n\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t// Next, the status queue\n\t\tlet ref queue = *dev.status_queue;\n\t\twhile dev.status_ack_used_idx != queue.used.idx {\n\t\t\tlet ref elem = queue.used.ring[dev.status_ack_used_idx as usize % VIRTIO_RING_SIZE];\n\t\t\tprint!(\"SAck {}, elem {}, len {}: \", dev.status_ack_used_idx, elem.id, elem.len);\n\t\t\tlet ref desc = queue.desc[elem.id as usize];\n\t\t\tlet event = (desc.addr as *const Event).as_ref().unwrap();\n\t\t\tprintln!(\"Type = {:x}, Code = {:x}, Value = {:x}\", event.event_type as u8, event.code, event.value);\n\t\t\tdev.status_ack_used_idx = dev.status_ack_used_idx.wrapping_add(1);\n\t\t}\n\t}\n}\n\npub fn handle_interrupt(idx: usize) {\n\tunsafe {\n\t\tif let Some(bdev) = INPUT_DEVICES[idx].as_mut() {\n\t\t\tpending(bdev);\n\t\t}\n\t\telse {\n\t\t\tprintln!(\n\t\t\t         \"Invalid input device for interrupt {}\",\n\t\t\t         idx + 1\n\t\t\t);\n\t\t}\n\t}\n}\n\n"
  },
  {
    "path": "risc_v/src/kmem.rs",
    "content": "// kmem.rs\n// Sub-page level: malloc-like allocation system\n// Stephen Marz\n// 7 October 2019\n\nuse crate::page::{align_val, zalloc, Table, PAGE_SIZE};\nuse core::{mem::size_of, ptr::null_mut};\n\n#[repr(usize)]\nenum AllocListFlags {\n\tTaken = 1 << 63,\n}\nimpl AllocListFlags {\n\tpub fn val(self) -> usize {\n\t\tself as usize\n\t}\n}\n\nstruct AllocList {\n\tpub flags_size: usize,\n}\nimpl AllocList {\n\tpub fn is_taken(&self) -> bool {\n\t\tself.flags_size & AllocListFlags::Taken.val() != 0\n\t}\n\n\tpub fn is_free(&self) -> bool {\n\t\t!self.is_taken()\n\t}\n\n\tpub fn set_taken(&mut self) {\n\t\tself.flags_size |= AllocListFlags::Taken.val();\n\t}\n\n\tpub fn set_free(&mut self) {\n\t\tself.flags_size &= !AllocListFlags::Taken.val();\n\t}\n\n\tpub fn set_size(&mut self, sz: usize) {\n\t\tlet k = self.is_taken();\n\t\tself.flags_size = sz & !AllocListFlags::Taken.val();\n\t\tif k {\n\t\t\tself.flags_size |= AllocListFlags::Taken.val();\n\t\t}\n\t}\n\n\tpub fn get_size(&self) -> usize {\n\t\tself.flags_size & !AllocListFlags::Taken.val()\n\t}\n}\n\n// This is the head of the allocation. We start here when\n// we search for a free memory location.\nstatic mut KMEM_HEAD: *mut AllocList = null_mut();\n// In the future, we will have on-demand pages\n// so, we need to keep track of our memory footprint to\n// see if we actually need to allocate more.\nstatic mut KMEM_ALLOC: usize = 0;\nstatic mut KMEM_PAGE_TABLE: *mut Table = null_mut();\n\n// These functions are safe helpers around an unsafe\n// operation.\npub fn get_head() -> *mut u8 {\n\tunsafe { KMEM_HEAD as *mut u8 }\n}\n\npub fn get_page_table() -> *mut Table {\n\tunsafe { KMEM_PAGE_TABLE as *mut Table }\n}\n\npub fn get_num_allocations() -> usize {\n\tunsafe { KMEM_ALLOC }\n}\n\n/// Initialize kernel's memory\n/// This is not to be used to allocate memory\n/// for user processes. If that's the case, use\n/// alloc/dealloc from the page crate.\npub fn init() {\n\tunsafe {\n\t\t// Allocate kernel pages (KMEM_ALLOC)\n\t\tKMEM_ALLOC = 2048;\n\t\tlet k_alloc = zalloc(KMEM_ALLOC);\n\t\tassert!(!k_alloc.is_null());\n\t\tKMEM_HEAD = k_alloc as *mut AllocList;\n\t\t(*KMEM_HEAD).set_free();\n\t\t(*KMEM_HEAD).set_size(KMEM_ALLOC * PAGE_SIZE);\n\t\tKMEM_PAGE_TABLE = zalloc(1) as *mut Table;\n\t}\n}\n\n/// Allocate sub-page level allocation based on bytes and zero the memory\npub fn kzmalloc(sz: usize) -> *mut u8 {\n\tlet size = align_val(sz, 3);\n\tlet ret = kmalloc(size);\n\n\tif !ret.is_null() {\n\t\tfor i in 0..size {\n\t\t\tunsafe {\n\t\t\t\t(*ret.add(i)) = 0;\n\t\t\t}\n\t\t}\n\t}\n\tret\n}\n\n/// Allocate sub-page level allocation based on bytes\npub fn kmalloc(sz: usize) -> *mut u8 {\n\tunsafe {\n\t\tlet size = align_val(sz, 3) + size_of::<AllocList>();\n\t\tlet mut head = KMEM_HEAD;\n\t\t// .add() uses pointer arithmetic, so we type-cast into a u8\n\t\t// so that we multiply by an absolute size (KMEM_ALLOC *\n\t\t// PAGE_SIZE).\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\n\t\twhile head < tail {\n\t\t\tif (*head).is_free() && size <= (*head).get_size() {\n\t\t\t\tlet chunk_size = (*head).get_size();\n\t\t\t\tlet rem = chunk_size - size;\n\t\t\t\t(*head).set_taken();\n\t\t\t\tif rem > size_of::<AllocList>() {\n\t\t\t\t\tlet next = (head as *mut u8).add(size)\n\t\t\t\t\t           as *mut AllocList;\n\t\t\t\t\t// There is space remaining here.\n\t\t\t\t\t(*next).set_free();\n\t\t\t\t\t(*next).set_size(rem);\n\t\t\t\t\t(*head).set_size(size);\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t// If we get here, take the entire chunk\n\t\t\t\t\t(*head).set_size(chunk_size);\n\t\t\t\t}\n\t\t\t\treturn head.add(1) as *mut u8;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// If we get here, what we saw wasn't a free\n\t\t\t\t// chunk, move on to the next.\n\t\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t\t       as *mut AllocList;\n\t\t\t}\n\t\t}\n\t}\n\t// If we get here, we didn't find any free chunks--i.e. there isn't\n\t// enough memory for this. TODO: Add on-demand page allocation.\n\tnull_mut()\n}\n\n/// Free a sub-page level allocation\npub fn kfree(ptr: *mut u8) {\n\tunsafe {\n\t\tif !ptr.is_null() {\n\t\t\tlet p = (ptr as *mut AllocList).offset(-1);\n\t\t\tif (*p).is_taken() {\n\t\t\t\t(*p).set_free();\n\t\t\t}\n\t\t\t// After we free, see if we can combine adjacent free\n\t\t\t// spots to see if we can reduce fragmentation.\n\t\t\tcoalesce();\n\t\t}\n\t}\n}\n\n/// Merge smaller chunks into a bigger chunk\npub fn coalesce() {\n\tunsafe {\n\t\tlet mut head = KMEM_HEAD;\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\n\t\twhile head < tail {\n\t\t\tlet next = (head as *mut u8).add((*head).get_size())\n\t\t\t           as *mut AllocList;\n\t\t\tif (*head).get_size() == 0 {\n\t\t\t\t// If this happens, then we have a bad heap\n\t\t\t\t// (double free or something). However, that\n\t\t\t\t// will cause an infinite loop since the next\n\t\t\t\t// pointer will never move beyond the current\n\t\t\t\t// location.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if next >= tail {\n\t\t\t\t// We calculated the next by using the size\n\t\t\t\t// given as get_size(), however this could push\n\t\t\t\t// us past the tail. In that case, the size is\n\t\t\t\t// wrong, hence we break and stop doing what we\n\t\t\t\t// need to do.\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\telse if (*head).is_free() && (*next).is_free() {\n\t\t\t\t// This means we have adjacent blocks needing to\n\t\t\t\t// be freed. So, we combine them into one\n\t\t\t\t// allocation.\n\t\t\t\t(*head).set_size(\n\t\t\t\t                 (*head).get_size()\n\t\t\t\t                 + (*next).get_size(),\n\t\t\t\t);\n\t\t\t}\n\t\t\t// If we get here, we might've moved. Recalculate new\n\t\t\t// head.\n\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t       as *mut AllocList;\n\t\t}\n\t}\n}\n\n/// For debugging purposes, print the kmem table\npub fn print_table() {\n\tunsafe {\n\t\tlet mut head = KMEM_HEAD;\n\t\tlet tail = (KMEM_HEAD as *mut u8).add(KMEM_ALLOC * PAGE_SIZE)\n\t\t           as *mut AllocList;\n\t\twhile head < tail {\n\t\t\tprintln!(\n\t\t\t         \"{:p}: Length = {:<10} Taken = {}\",\n\t\t\t         head,\n\t\t\t         (*head).get_size(),\n\t\t\t         (*head).is_taken()\n\t\t\t);\n\t\t\thead = (head as *mut u8).add((*head).get_size())\n\t\t\t       as *mut AllocList;\n\t\t}\n\t}\n}\n\n// ///////////////////////////////////\n// / GLOBAL ALLOCATOR\n// ///////////////////////////////////\n\n// The global allocator allows us to use the data structures\n// in the core library, such as a linked list or B-tree.\n// We want to use these sparingly since we have a coarse-grained\n// allocator.\nuse core::alloc::{GlobalAlloc, Layout};\n\n// The global allocator is a static constant to a global allocator\n// structure. We don't need any members because we're using this\n// structure just to implement alloc and dealloc.\nstruct OsGlobalAlloc;\n\nunsafe impl GlobalAlloc for OsGlobalAlloc {\n\tunsafe fn alloc(&self, layout: Layout) -> *mut u8 {\n\t\t// We align to the next page size so that when\n\t\t// we divide by PAGE_SIZE, we get exactly the number\n\t\t// of pages necessary.\n\t\tkzmalloc(layout.size())\n\t}\n\n\tunsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {\n\t\t// We ignore layout since our allocator uses ptr_start -> last\n\t\t// to determine the span of an allocation.\n\t\tkfree(ptr);\n\t}\n}\n\n#[global_allocator]\n/// Technically, we don't need the {} at the end, but it\n/// reveals that we're creating a new structure and not just\n/// copying a value.\nstatic GA: OsGlobalAlloc = OsGlobalAlloc {};\n\n#[alloc_error_handler]\n/// If for some reason alloc() in the global allocator gets null_mut(),\n/// then we come here. This is a divergent function, so we call panic to\n/// let the tester know what's going on.\npub fn alloc_error(l: Layout) -> ! {\n\tpanic!(\n\t       \"Allocator failed to allocate {} bytes with {}-byte alignment.\",\n\t       l.size(),\n\t       l.align()\n\t);\n}\n"
  },
  {
    "path": "risc_v/src/lds/virt.lds",
    "content": "/*\n virt.lds\n Linker script for outputting to RISC-V QEMU \"virt\" machine.\n Stephen Marz\n 6 October 2019\n*/\n\n/*\n  riscv is the name of the architecture that the linker understands\n  for any RISC-V target (64-bit or 32-bit).\n\n  We will further refine this by using -mabi=lp64 and -march=rv64gc\n*/\nOUTPUT_ARCH( \"riscv\" )\n\n/*\nWe're setting our entry point to a symbol\ncalled _start which is inside of boot.S. This\nessentially stores the address of _start as the\n\"entry point\", or where CPU instructions should start\nexecuting.\n\nIn the rest of this script, we are going to place _start\nright at the beginning of 0x8000_0000 because this is where\nthe virtual machine and many RISC-V boards will start executing.\n*/\nENTRY( _start )\n\n/*\nThe MEMORY section will explain that we have \"ram\" that contains\na section that is 'w' (writeable), 'x' (executable), and 'a' (allocatable).\nWe use '!' to invert 'r' (read-only) and 'i' (initialized). We don't want\nour memory to be read-only, and we're stating that it is NOT initialized\nat the beginning.\n\nThe ORIGIN is the memory address 0x8000_0000. If we look at the virt\nspec or the specification for the RISC-V HiFive Unleashed, this is the\nstarting memory address for our code.\n\nSide note: There might be other boot ROMs at different addresses, but\ntheir job is to get to this point.\n\nFinally LENGTH = 128M tells the linker that we have 128 megabyte of RAM.\nThe linker will double check this to make sure everything can fit.\n\nThe HiFive Unleashed has a lot more RAM than this, but for the virtual \nmachine, I went with 128M since I think that's enough RAM for now.\n\nWe can provide other pieces of memory, such as QSPI, or ROM, but we're\ntelling the linker script here that we have one pool of RAM.\n*/\nMEMORY\n{\n  ram  (wxa) : ORIGIN = 0x80000000, LENGTH = 128M\n}\n\n/*\nPHDRS is short for \"program headers\", which we specify three here:\ntext - CPU instructions (executable sections)\ndata - Global, initialized variables\nbss  - Global, uninitialized variables (all will be set to 0 by boot.S)\n\nThe command PT_LOAD tells the linker that these sections will be loaded\nfrom the file into memory.\n\nWe can actually stuff all of these into a single program header, but by\nsplitting it up into three, we can actually use the other PT_* commands\nsuch as PT_DYNAMIC, PT_INTERP, PT_NULL to tell the linker where to find\nadditional information.\n\nHowever, for our purposes, every section will be loaded from the program\nheaders.\n*/\nPHDRS\n{\n  text PT_LOAD;\n  data PT_LOAD;\n  bss PT_LOAD;\n}\n\n/*\nWe are now going to organize the memory based on which\nsection it is in. In assembly, we can change the section\nwith the \".section\" directive. However, in C++ and Rust,\nCPU instructions go into text, global constants go into\nrodata, global initialized variables go into data, and\nglobal uninitialized variables go into bss.\n*/\nSECTIONS\n{\n  /*\n    The first part of our RAM layout will be the text section.\n\tSince our CPU instructions are here, and our memory starts at\n\t0x8000_0000, we need our entry point to line up here.\n  */\n  .text : {\n\t  /* \n\t    PROVIDE allows me to access a symbol called _text_start so\n\t\tI know where the text section starts in the operating system.\n\t\tThis should not move, but it is here for convenience.\n\t\tThe period '.' tells the linker to set _text_start to the\n\t\tCURRENT location ('.' = current memory location). This current\n\t\tmemory location moves as we add things.\n\t  */\n\n    PROVIDE(_text_start = .);\n\t/*\n\t  We are going to layout all text sections here, starting with \n\t  .text.init. The asterisk in front of the parentheses means to match\n\t  the .text.init section of ANY object file. Otherwise, we can specify\n\t  which object file should contain the .text.init section, for example,\n\t  boot.o(.text.init) would specifically put the .text.init section of\n\t  our bootloader here.\n\n\t  Because we might want to change the name of our files, we'll leave it\n\t  with a *.\n\n\t  Inside the parentheses is the name of the section. I created my own\n\t  called .text.init to make 100% sure that the _start is put right at the\n\t  beginning. The linker will lay this out in the order it receives it:\n\n\t  .text.init first\n\t  all .text sections next\n\t  any .text.* sections last\n\n\t  .text.* means to match anything after .text. If we didn't already specify\n\t  .text.init, this would've matched here. The assembler and linker can place\n\t  things in \"special\" text sections, so we match any we might come across here.\n\t*/\n    *(.text.init) *(.text .text.*)\n\t/*\n\t  Again, with PROVIDE, we're providing a readable symbol called _text_end, which is\n\t  set to the memory address AFTER .text.init, .text, and .text.*'s have been added.\n\t*/\n    PROVIDE(_text_end = .);\n\t/*\n\t  The portion after the right brace is in an odd format. However, this is telling the\n\t  linker what memory portion to put it in. We labeled our RAM, ram, with the constraints\n\t  that it is writeable, allocatable, and executable. The linker will make sure with this\n\t  that we can do all of those things.\n\n\t  >ram - This just tells the linker script to put this entire section (.text) into the\n\t         ram region of memory. To my knowledge, the '>' does not mean \"greater than\". Instead,\n\t\t\t it is a symbol to let the linker know we want to put this in ram.\n\n\t  AT>ram - This sets the LMA (load memory address) region to the same thing. LMA is the final\n\t           translation of a VMA (virtual memory address). With this linker script, we're loading\n\t\t\t   everything into its physical location. We'll let the kernel copy and sort out the \n\t\t\t   virtual memory. That's why >ram and AT>ram are continually the same thing.\n\n\t  :text  - This tells the linker script to put this into the :text program header. We've only\n\t           defined three: text, data, and bss. In this case, we're telling the linker script\n\t\t\t   to go into the text section.\n\t*/\n  } >ram AT>ram :text\n   /*\n     The global pointer allows the linker to position global variables and constants into\n\t independent positions relative to the gp (global pointer) register. The globals start\n\t after the text sections and are only relevant to the rodata, data, and bss sections.\n   */\n   PROVIDE(_global_pointer = .);\n   /*\n     Most compilers create a rodata (read only data) section for global constants. However,\n\t we're going to place ours in the text section. We can actually put this in :data, but\n\t since the .text section is read-only, we can place it there.\n\n\t NOTE: This doesn't actually do anything, yet. The actual \"protection\" cannot be done\n\t at link time. Instead, when we program the memory management unit (MMU), we will be\n\t able to choose which bits (R=read, W=write, X=execute) we want each memory segment\n\t to be able to do.\n   */\n  .rodata : {\n    PROVIDE(_rodata_start = .);\n    *(.rodata .rodata.*)\n    PROVIDE(_rodata_end = .);\n\t/*\n\t   Again, we're placing the rodata section in the memory segment \"ram\" and we're putting\n\t   it in the :text program header. We don't have one for rodata anyway.\n\t*/\n  } >ram AT>ram :text\n\n  .data : {\n\t/*\n\t   . = ALIGN(4096) tells the linker to align the current memory location (which is\n\t   0x8000_0000 + text section + rodata section) to 4096 bytes. This is because our paging\n\t   system's resolution is 4,096 bytes or 4 KiB.\n\t*/\n    . = ALIGN(4096);\n    PROVIDE(_data_start = .);\n\t/*\n\t   sdata and data are essentially the same thing. However, compilers usually use the\n\t   sdata sections for shorter, quicker loading sections. So, usually critical data\n\t   is loaded there. However, we're loading all of this in one fell swoop.\n\t   So, we're looking to put all of the following sections under the umbrella .data:\n\t   .sdata\n\t   .sdata.[anything]\n\t   .data\n\t   .data.[anything]\n\n\t   ...in that order.\n\t*/\n    *(.sdata .sdata.*) *(.data .data.*)\n    PROVIDE(_data_end = .);\n  } >ram AT>ram :data\n\n  .bss : {\n    PROVIDE(_bss_start = .);\n    *(.sbss .sbss.*) *(.bss .bss.*)\n    PROVIDE(_bss_end = .);\n  } >ram AT>ram :bss\n\n  /*\n     The following will be helpful when we allocate the kernel stack (_stack) and\n\t determine where the heap begnis and ends (_heap_start and _heap_start + _heap_size)/\n\t When we do memory allocation, we can use these symbols.\n\n\t We use the symbols instead of hard-coding an address because this is a floating target.\n\t As we add code, the heap moves farther down the memory and gets shorter.\n\n\t _memory_start will be set to 0x8000_0000 here. We use ORIGIN(ram) so that it will take\n\t whatever we set the origin of ram to. Otherwise, we'd have to change it more than once\n\t if we ever stray away from 0x8000_0000 as our entry point.\n  */\n  PROVIDE(_memory_start = ORIGIN(ram));\n  /*\n     Our kernel stack starts at the end of the bss segment (_bss_end). However, we're allocating\n\t 0x80000 bytes (524 KiB) to our kernel stack. This should be PLENTY of space. The reason\n\t we add the memory is because the stack grows from higher memory to lower memory (bottom to top).\n\t Therefore we set the stack at the very bottom of its allocated slot.\n\t When we go to allocate from the stack, we'll subtract the number of bytes we need.\n  */\n  PROVIDE(_stack_start = _bss_end);\n  PROVIDE(_stack_end = _stack_start + 0x80000);\n  PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));\n\n  /* \n     Finally, our heap starts right after the kernel stack. This heap will be used mainly\n\t to dole out memory for user-space applications. However, in some circumstances, it will\n\t be used for kernel memory as well.\n\n\t We don't align here because we let the kernel determine how it wants to do this.\n  */\n  PROVIDE(_heap_start = _stack_end);\n  PROVIDE(_heap_size = _memory_end - _heap_start);\n}\n"
  },
  {
    "path": "risc_v/src/lock.rs",
    "content": "// lock.rs\n// Locking routines\n// Stephen Marz\n// 26 Apr 2020\n\nuse crate::syscall::syscall_sleep;\n\npub const DEFAULT_LOCK_SLEEP: usize = 10000;\n#[repr(u32)]\npub enum MutexState {\n\tUnlocked = 0,\n\tLocked = 1\n}\n\n#[repr(C)]\npub struct Mutex {\n\tstate: MutexState\n}\n\nimpl<'a> Mutex {\n\tpub const fn new() -> Self {\n\t\tSelf { state: MutexState::Unlocked }\n\t}\n\n\tpub fn val(&'a self) -> &'a MutexState {\n\t\t&self.state\n\t}\n\n\t/// Try to lock the Mutex. If the mutex is already locked, this function returns false, otherwise it will return true if the mutex was acquired.\n\tpub fn try_lock(&mut self) -> bool {\n\t\tunsafe {\n\t\t\tlet state: MutexState;\n\t\t\tllvm_asm!(\"amoswap.w.aq $0, $1, ($2)\\n\" : \"=r\"(state) : \"r\"(1), \"r\"(self) :: \"volatile\");\n\t\t\tmatch state {\n\t\t\t\t// amoswap returns the OLD state of the lock.  If it was already locked, we didn't acquire it.\n\t\t\t\tMutexState::Locked => false,\n\t\t\t\tMutexState::Unlocked => true\n\t\t\t}\n\t\t}\n\t}\n\n\t/// Do NOT sleep lock inside of an interrupt context!\n\t/// Never use a sleep lock for the process list. Sleeping requires\n\t/// the process list to function, so you'll deadlock if you do.\n\tpub fn sleep_lock(&mut self) {\n\t\twhile !self.try_lock() {\n\t\t\tsyscall_sleep(DEFAULT_LOCK_SLEEP);\n\t\t}\n\t}\n\n\t/// Can safely be used inside of an interrupt context.\n\tpub fn spin_lock(&mut self) {\n\t\twhile !self.try_lock() {}\n\t}\n\n\t/// Unlock a mutex without regard for its previous state.\n\tpub fn unlock(&mut self) {\n\t\tunsafe {\n\t\t\tllvm_asm!(\"amoswap.w.rl zero, zero, ($0)\" :: \"r\"(self) :: \"volatile\");\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "risc_v/src/main.rs",
    "content": "// Steve Operating System\n// Stephen Marz\n// 21 Sep 2019\n#![no_main]\n#![no_std]\n#![feature(panic_info_message,\n           asm,\n\t\t   llvm_asm,\n\t\t   global_asm,\n           allocator_api,\n           alloc_error_handler,\n           alloc_prelude,\n\t\t   const_raw_ptr_to_usize_cast,\n\t\t   lang_items)]\n\n#[lang = \"eh_personality\"] extern fn eh_personality() {}\n\n// #[macro_use]\nextern crate alloc;\n// This is experimental and requires alloc_prelude as a feature\n// use alloc::prelude::v1::*;\n\n// ///////////////////////////////////\n// / RUST MACROS\n// ///////////////////////////////////\n#[macro_export]\nmacro_rules! print\n{\n\t($($args:tt)+) => ({\n\t\t\tuse core::fmt::Write;\n\t\t\tlet _ = write!(crate::uart::Uart::new(0x1000_0000), $($args)+);\n\t\t\t});\n}\n#[macro_export]\nmacro_rules! println\n{\n\t() => ({\n\t\t   print!(\"\\r\\n\")\n\t\t   });\n\t($fmt:expr) => ({\n\t\t\tprint!(concat!($fmt, \"\\r\\n\"))\n\t\t\t});\n\t($fmt:expr, $($args:tt)+) => ({\n\t\t\tprint!(concat!($fmt, \"\\r\\n\"), $($args)+)\n\t\t\t});\n}\n\n// ///////////////////////////////////\n// / LANGUAGE STRUCTURES / FUNCTIONS\n// ///////////////////////////////////\n\n#[panic_handler]\nfn panic(info: &core::panic::PanicInfo) -> ! {\n\tprint!(\"Aborting: \");\n\tif let Some(p) = info.location() {\n\t\tprintln!(\n\t\t         \"line {}, file {}: {}\",\n\t\t         p.line(),\n\t\t         p.file(),\n\t\t         info.message().unwrap()\n\t\t);\n\t}\n\telse {\n\t\tprintln!(\"no information available.\");\n\t}\n\tabort();\n}\n#[no_mangle]\nextern \"C\" fn abort() -> ! {\n\tloop {\n\t\tunsafe {\n\t\t\tllvm_asm!(\"wfi\"::::\"volatile\");\n\t\t}\n\t}\n}\n\nextern \"C\" {\n\tfn switch_to_user(frame: usize) -> !;\n}\n\n/// Switch to user is an assembly function that loads\n/// a frame. Since it will jump to another program counter,\n/// it will never return back here. We don't care if we leak\n/// the stack, since we will recapture the stack during m_trap.\nfn rust_switch_to_user(frame: usize) -> ! {\n\tunsafe {\n\t\tswitch_to_user(frame);\n\t}\n}\n// ///////////////////////////////////\n// / ENTRY POINT\n// ///////////////////////////////////\n#[no_mangle]\nextern \"C\" fn kinit() {\n\tuart::Uart::new(0x1000_0000).init();\n\tpage::init();\n\tkmem::init();\n\tprocess::init();\n\t// We lower the threshold wall so our interrupts can jump over it.\n\t// Any priority > 0 will be able to be \"heard\"\n\tplic::set_threshold(0);\n\t// VIRTIO = [1..8]\n\t// UART0 = 10\n\t// PCIE = [32..35]\n\t// Enable PLIC interrupts.\n\tfor i in 1..=10 {\n\t\tplic::enable(i);\n\t\tplic::set_priority(i, 1);\n\t}\n\t// Set up virtio. This requires a working heap and page-grained allocator.\n\tvirtio::probe();\n\n\tconsole::init();\n\tprocess::add_kernel_process(test::test);\n\t// Get the GPU going\n\tgpu::init(6);\n\t// We schedule the next context switch using a multiplier of 1\n\t// Block testing code removed.\n\ttrap::schedule_next_context_switch(1);\n\trust_switch_to_user(sched::schedule());\n\t// switch_to_user will not return, so we should never get here\n}\n#[no_mangle]\nextern \"C\" fn kinit_hart(_hartid: usize) {\n\t// We aren't going to do anything here until we get SMP going.\n\t// All non-0 harts initialize here.\n}\n\n// ///////////////////////////////////\n// / RUST MODULES\n// ///////////////////////////////////\n\npub mod assembly;\npub mod block;\npub mod buffer;\npub mod console;\npub mod cpu;\npub mod elf;\npub mod fs;\npub mod gpu;\npub mod input;\npub mod kmem;\npub mod lock;\npub mod page;\npub mod plic;\npub mod process;\npub mod rng;\npub mod sched;\npub mod syscall;\npub mod trap;\npub mod uart;\npub mod vfs;\npub mod virtio;\npub mod test;\n\n\n"
  },
  {
    "path": "risc_v/src/page.rs",
    "content": "// page.rs\n// Memory routines\n// Stephen Marz\n// 6 October 2019\n\nuse core::{mem::size_of, ptr::null_mut};\n\n// ////////////////////////////////\n// // Allocation routines\n// ////////////////////////////////\nextern \"C\" {\n\tstatic HEAP_START: usize;\n\tstatic HEAP_SIZE: usize;\n}\n\n// We will use ALLOC_START to mark the start of the actual\n// memory we can dish out.\nstatic mut ALLOC_START: usize = 0;\nconst PAGE_ORDER: usize = 12;\npub const PAGE_SIZE: usize = 1 << 12;\n\n/// Align (set to a multiple of some power of two)\n/// This takes an order which is the exponent to 2^order\n/// Therefore, all alignments must be made as a power of two.\n/// This function always rounds up.\npub const fn align_val(val: usize, order: usize) -> usize {\n\tlet o = (1usize << order) - 1;\n\t(val + o) & !o\n}\n\n#[repr(u8)]\npub enum PageBits {\n\tEmpty = 0,\n\tTaken = 1 << 0,\n\tLast = 1 << 1,\n}\n\nimpl PageBits {\n\t// We convert PageBits to a u8 a lot, so this is\n\t// for convenience.\n\tpub fn val(self) -> u8 {\n\t\tself as u8\n\t}\n}\n\n// Each page is described by the Page structure. Linux does this\n// as well, where each 4096-byte chunk of memory has a structure\n// associated with it. However, there structure is much larger.\npub struct Page {\n\tflags: u8,\n}\n\nimpl Page {\n\t// If this page has been marked as the final allocation,\n\t// this function returns true. Otherwise, it returns false.\n\tpub fn is_last(&self) -> bool {\n\t\tself.flags & PageBits::Last.val() != 0\n\t}\n\n\t// If the page is marked as being taken (allocated), then\n\t// this function returns true. Otherwise, it returns false.\n\tpub fn is_taken(&self) -> bool {\n\t\tself.flags & PageBits::Taken.val() != 0\n\t}\n\n\t// This is the opposite of is_taken().\n\tpub fn is_free(&self) -> bool {\n\t\t!self.is_taken()\n\t}\n\n\t// Clear the Page structure and all associated allocations.\n\tpub fn clear(&mut self) {\n\t\tself.flags = PageBits::Empty.val();\n\t}\n\n\t// Set a certain flag. We ran into trouble here since PageBits\n\t// is an enumeration and we haven't implemented the BitOr Trait\n\t// on it.\n\tpub fn set_flag(&mut self, flag: PageBits) {\n\t\tself.flags |= flag.val();\n\t}\n\n\tpub fn clear_flag(&mut self, flag: PageBits) {\n\t\tself.flags &= !(flag.val());\n\t}\n}\n\n/// Initialize the allocation system. There are several ways that we can\n/// implement the page allocator:\n/// 1. Free list (singly linked list where it starts at the first free\n/// allocation) 2. Bookkeeping list (structure contains a taken and length)\n/// 3. Allocate one Page structure per 4096 bytes (this is what I chose)\n/// 4. Others\npub fn init() {\n\tunsafe {\n\t\t// let desc_per_page = PAGE_SIZE / size_of::<Page>();\n\t\tlet num_pages = HEAP_SIZE / PAGE_SIZE;\n\t\t// let num_desc_pages = num_pages / desc_per_page;\n\t\tlet ptr = HEAP_START as *mut Page;\n\t\t// Clear all pages to make sure that they aren't accidentally\n\t\t// taken\n\t\tfor i in 0..num_pages {\n\t\t\t(*ptr.add(i)).clear();\n\t\t}\n\t\t// Determine where the actual useful memory starts. This will be\n\t\t// after all Page structures. We also must align the ALLOC_START\n\t\t// to a page-boundary (PAGE_SIZE = 4096). ALLOC_START =\n\t\t// (HEAP_START + num_pages * size_of::<Page>() + PAGE_SIZE - 1)\n\t\t// & !(PAGE_SIZE - 1);\n\t\tALLOC_START = align_val(\n\t\t                        HEAP_START\n\t\t                        + num_pages * size_of::<Page>(),\n\t\t                        PAGE_ORDER,\n\t\t);\n\t}\n}\n\n/// Allocate a page or multiple pages\n/// pages: the number of PAGE_SIZE pages to allocate\npub fn alloc(pages: usize) -> *mut u8 {\n\t// We have to find a contiguous allocation of pages\n\tassert!(pages > 0);\n\tunsafe {\n\t\t// We create a Page structure for each page on the heap. We\n\t\t// actually might have more since HEAP_SIZE moves and so does\n\t\t// the size of our structure, but we'll only waste a few bytes.\n\t\tlet num_pages = HEAP_SIZE / PAGE_SIZE;\n\t\tlet ptr = HEAP_START as *mut Page;\n\t\tfor i in 0..num_pages - pages {\n\t\t\tlet mut found = false;\n\t\t\t// Check to see if this Page is free. If so, we have our\n\t\t\t// first candidate memory address.\n\t\t\tif (*ptr.add(i)).is_free() {\n\t\t\t\t// It was FREE! Yay!\n\t\t\t\tfound = true;\n\t\t\t\tfor j in i..i + pages {\n\t\t\t\t\t// Now check to see if we have a\n\t\t\t\t\t// contiguous allocation for all of the\n\t\t\t\t\t// request pages. If not, we should\n\t\t\t\t\t// check somewhere else.\n\t\t\t\t\tif (*ptr.add(j)).is_taken() {\n\t\t\t\t\t\tfound = false;\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t// We've checked to see if there are enough contiguous\n\t\t\t// pages to form what we need. If we couldn't, found\n\t\t\t// will be false, otherwise it will be true, which means\n\t\t\t// we've found valid memory we can allocate.\n\t\t\tif found {\n\t\t\t\tfor k in i..i + pages - 1 {\n\t\t\t\t\t(*ptr.add(k)).set_flag(PageBits::Taken);\n\t\t\t\t}\n\t\t\t\t// The marker for the last page is\n\t\t\t\t// PageBits::Last This lets us know when we've\n\t\t\t\t// hit the end of this particular allocation.\n\t\t\t\t(*ptr.add(i+pages-1)).set_flag(PageBits::Taken);\n\t\t\t\t(*ptr.add(i+pages-1)).set_flag(PageBits::Last);\n\t\t\t\t// The Page structures themselves aren't the\n\t\t\t\t// useful memory. Instead, there is 1 Page\n\t\t\t\t// structure per 4096 bytes starting at\n\t\t\t\t// ALLOC_START.\n\t\t\t\treturn (ALLOC_START + PAGE_SIZE * i)\n\t\t\t\t       as *mut u8;\n\t\t\t}\n\t\t}\n\t}\n\n\t// If we get here, that means that no contiguous allocation was\n\t// found.\n\tnull_mut()\n}\n\n/// Allocate and zero a page or multiple pages\n/// pages: the number of pages to allocate\n/// Each page is PAGE_SIZE which is calculated as 1 << PAGE_ORDER\n/// On RISC-V, this typically will be 4,096 bytes.\npub fn zalloc(pages: usize) -> *mut u8 {\n\t// Allocate and zero a page.\n\t// First, let's get the allocation\n\tlet ret = alloc(pages);\n\tif !ret.is_null() {\n\t\tlet size = (PAGE_SIZE * pages) / 8;\n\t\tlet big_ptr = ret as *mut u64;\n\t\tfor i in 0..size {\n\t\t\t// We use big_ptr so that we can force an\n\t\t\t// sd (store doubleword) instruction rather than\n\t\t\t// the sb. This means 8x fewer stores than before.\n\t\t\t// Typically we have to be concerned about remaining\n\t\t\t// bytes, but fortunately 4096 % 8 = 0, so we\n\t\t\t// won't have any remaining bytes.\n\t\t\tunsafe {\n\t\t\t\t(*big_ptr.add(i)) = 0;\n\t\t\t}\n\t\t}\n\t}\n\tret\n}\n\n/// Deallocate a page by its pointer\n/// The way we've structured this, it will automatically coalesce\n/// contiguous pages.\npub fn dealloc(ptr: *mut u8) {\n\t// Make sure we don't try to free a null pointer.\n\tassert!(!ptr.is_null());\n\tunsafe {\n\t\tlet addr =\n\t\t\tHEAP_START + (ptr as usize - ALLOC_START) / PAGE_SIZE;\n\t\t// Make sure that the address makes sense. The address we\n\t\t// calculate here is the page structure, not the HEAP address!\n\t\tassert!(addr >= HEAP_START && addr < ALLOC_START);\n\t\tlet mut p = addr as *mut Page;\n\t\t// println!(\"PTR in is {:p}, addr is 0x{:x}\", ptr, addr);\n\t\tassert!((*p).is_taken(), \"Freeing a non-taken page?\");\n\t\t// Keep clearing pages until we hit the last page.\n\t\twhile (*p).is_taken() && !(*p).is_last() {\n\t\t\t(*p).clear();\n\t\t\tp = p.add(1);\n\t\t}\n\t\t// If the following assertion fails, it is most likely\n\t\t// caused by a double-free.\n\t\tassert!(\n\t\t        (*p).is_last() == true,\n\t\t        \"Possible double-free detected! (Not taken found \\\n\t\t         before last)\"\n\t\t);\n\t\t// If we get here, we've taken care of all previous pages and\n\t\t// we are on the last page.\n\t\t(*p).clear();\n\t}\n}\n\n/// Print all page allocations\n/// This is mainly used for debugging.\npub fn print_page_allocations() {\n\tunsafe {\n\t\tlet num_pages = (HEAP_SIZE - (ALLOC_START - HEAP_START)) / PAGE_SIZE;\n\t\tlet mut beg = HEAP_START as *const Page;\n\t\tlet end = beg.add(num_pages);\n\t\tlet alloc_beg = ALLOC_START;\n\t\tlet alloc_end = ALLOC_START + num_pages * PAGE_SIZE;\n\t\tprintln!();\n\t\tprintln!(\n\t\t         \"PAGE ALLOCATION TABLE\\nMETA: {:p} -> {:p}\\nPHYS: \\\n\t\t          0x{:x} -> 0x{:x}\",\n\t\t         beg, end, alloc_beg, alloc_end\n\t\t);\n\t\tprintln!(\"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\");\n\t\tlet mut num = 0;\n\t\twhile beg < end {\n\t\t\tif (*beg).is_taken() {\n\t\t\t\tlet start = beg as usize;\n\t\t\t\tlet memaddr = ALLOC_START\n\t\t\t\t              + (start - HEAP_START)\n\t\t\t\t                * PAGE_SIZE;\n\t\t\t\tprint!(\"0x{:x} => \", memaddr);\n\t\t\t\tloop {\n\t\t\t\t\tnum += 1;\n\t\t\t\t\tif (*beg).is_last() {\n\t\t\t\t\t\tlet end = beg as usize;\n\t\t\t\t\t\tlet memaddr = ALLOC_START\n\t\t\t\t\t\t              + (end\n\t\t\t\t\t\t                 - HEAP_START)\n\t\t\t\t\t\t                * PAGE_SIZE\n\t\t\t\t\t\t              + PAGE_SIZE - 1;\n\t\t\t\t\t\tprint!(\n\t\t\t\t\t\t       \"0x{:x}: {:>3} page(s)\",\n\t\t\t\t\t\t       memaddr,\n\t\t\t\t\t\t       (end - start + 1)\n\t\t\t\t\t\t);\n\t\t\t\t\t\tprintln!(\".\");\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tbeg = beg.add(1);\n\t\t\t\t}\n\t\t\t}\n\t\t\tbeg = beg.add(1);\n\t\t}\n\t\tprintln!(\"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\");\n\t\tprintln!(\n\t\t         \"Allocated: {:>6} pages ({:>10} bytes).\",\n\t\t         num,\n\t\t         num * PAGE_SIZE\n\t\t);\n\t\tprintln!(\n\t\t         \"Free     : {:>6} pages ({:>10} bytes).\",\n\t\t         num_pages - num,\n\t\t         (num_pages - num) * PAGE_SIZE\n\t\t);\n\t\tprintln!();\n\t}\n}\n\n// ////////////////////////////////\n// // MMU Routines\n// ////////////////////////////////\n\n// Represent (repr) our entry bits as\n// unsigned 64-bit integers.\n#[repr(usize)]\n#[derive(Copy, Clone)]\npub enum EntryBits {\n\tNone = 0,\n\tValid = 1 << 0,\n\tRead = 1 << 1,\n\tWrite = 1 << 2,\n\tExecute = 1 << 3,\n\tUser = 1 << 4,\n\tGlobal = 1 << 5,\n\tAccess = 1 << 6,\n\tDirty = 1 << 7,\n\n\t// Convenience combinations\n\tReadWrite = 1 << 1 | 1 << 2,\n\tReadExecute = 1 << 1 | 1 << 3,\n\tReadWriteExecute = 1 << 1 | 1 << 2 | 1 << 3,\n\n\t// User Convenience Combinations\n\tUserReadWrite = 1 << 1 | 1 << 2 | 1 << 4,\n\tUserReadExecute = 1 << 1 | 1 << 3 | 1 << 4,\n\tUserReadWriteExecute = 1 << 1 | 1 << 2 | 1 << 3 | 1 << 4,\n}\n\n// Helper functions to convert the enumeration\n// into an i64, which is what our page table\n// entries will be.\nimpl EntryBits {\n\tpub fn val(self) -> usize {\n\t\tself as usize\n\t}\n}\n\n// A single entry.\npub struct Entry {\n\tpub entry: usize,\n}\n\n// The Entry structure describes one of the 512 entries per table, which is\n// described in the RISC-V privileged spec Figure 4.18.\nimpl Entry {\n\tpub fn is_valid(&self) -> bool {\n\t\tself.get_entry() & EntryBits::Valid.val() != 0\n\t}\n\n\t// The first bit (bit index #0) is the V bit for\n\t// valid.\n\tpub fn is_invalid(&self) -> bool {\n\t\t!self.is_valid()\n\t}\n\n\t// A leaf has one or more RWX bits set\n\tpub fn is_leaf(&self) -> bool {\n\t\tself.get_entry() & 0xe != 0\n\t}\n\n\tpub fn is_branch(&self) -> bool {\n\t\t!self.is_leaf()\n\t}\n\n\tpub fn set_entry(&mut self, entry: usize) {\n\t\tself.entry = entry;\n\t}\n\n\tpub fn get_entry(&self) -> usize {\n\t\tself.entry\n\t}\n}\n\n// Table represents a single table, which contains 512 (2^9), 64-bit entries.\npub struct Table {\n\tpub entries: [Entry; 512],\n}\n\nimpl Table {\n\tpub fn len() -> usize {\n\t\t512\n\t}\n}\n\n/// Map a virtual address to a physical address using 4096-byte page\n/// size.\n/// root: a mutable reference to the root Table\n/// vaddr: The virtual address to map\n/// paddr: The physical address to map\n/// bits: An OR'd bitset containing the bits the leaf should have.\n///       The bits should contain only the following:\n///          Read, Write, Execute, User, and/or Global\n///       The bits MUST include one or more of the following:\n///          Read, Write, Execute\n///       The valid bit automatically gets added.\npub fn map(root: &mut Table,\n           vaddr: usize,\n           paddr: usize,\n           bits: usize,\n           level: usize)\n{\n\t// Make sure that Read, Write, or Execute have been provided\n\t// otherwise, we'll leak memory and always create a page fault.\n\tassert!(bits & 0xe != 0);\n\t// Extract out each VPN from the virtual address\n\t// On the virtual address, each VPN is exactly 9 bits,\n\t// which is why we use the mask 0x1ff = 0b1_1111_1111 (9 bits)\n\tlet vpn = [\n\t           // VPN[0] = vaddr[20:12]\n\t           (vaddr >> 12) & 0x1ff,\n\t           // VPN[1] = vaddr[29:21]\n\t           (vaddr >> 21) & 0x1ff,\n\t           // VPN[2] = vaddr[38:30]\n\t           (vaddr >> 30) & 0x1ff,\n\t];\n\n\t// Just like the virtual address, extract the physical address\n\t// numbers (PPN). However, PPN[2] is different in that it stores\n\t// 26 bits instead of 9. Therefore, we use,\n\t// 0x3ff_ffff = 0b11_1111_1111_1111_1111_1111_1111 (26 bits).\n\tlet ppn = [\n\t           // PPN[0] = paddr[20:12]\n\t           (paddr >> 12) & 0x1ff,\n\t           // PPN[1] = paddr[29:21]\n\t           (paddr >> 21) & 0x1ff,\n\t           // PPN[2] = paddr[55:30]\n\t           (paddr >> 30) & 0x3ff_ffff,\n\t];\n\t// We will use this as a floating reference so that we can set\n\t// individual entries as we walk the table.\n\tlet mut v = &mut root.entries[vpn[2]];\n\t// Now, we're going to traverse the page table and set the bits\n\t// properly. We expect the root to be valid, however we're required to\n\t// create anything beyond the root.\n\t// In Rust, we create a range iterator using the .. operator.\n\t// The .rev() will reverse the iteration since we need to start with\n\t// VPN[2] The .. operator is inclusive on start but exclusive on end.\n\t// So, (0..2) will iterate 0 and 1.\n\tfor i in (level..2).rev() {\n\t\tif !v.is_valid() {\n\t\t\t// Allocate a page\n\t\t\tlet page = zalloc(1);\n\t\t\t// The page is already aligned by 4,096, so store it\n\t\t\t// directly The page is stored in the entry shifted\n\t\t\t// right by 2 places.\n\t\t\tv.set_entry(\n\t\t\t            (page as usize >> 2)\n\t\t\t            | EntryBits::Valid.val(),\n\t\t\t);\n\t\t}\n\t\tlet entry = ((v.get_entry() & !0x3ff) << 2) as *mut Entry;\n\t\tv = unsafe { entry.add(vpn[i]).as_mut().unwrap() };\n\t}\n\t// When we get here, we should be at VPN[0] and v should be pointing to\n\t// our entry.\n\t// The entry structure is Figure 4.18 in the RISC-V Privileged\n\t// Specification\n\tlet entry = (ppn[2] << 28) |   // PPN[2] = [53:28]\n\t            (ppn[1] << 19) |   // PPN[1] = [27:19]\n\t\t\t\t(ppn[0] << 10) |   // PPN[0] = [18:10]\n\t\t\t\tbits |                    // Specified bits, such as User, Read, Write, etc\n\t\t\t\tEntryBits::Valid.val() |  // Valid bit\n\t\t\t\tEntryBits::Dirty.val() |  // Some machines require this to =1\n\t\t\t\tEntryBits::Access.val()   // Just like dirty, some machines require this\n\t\t\t\t;\n\t// Set the entry. V should be set to the correct pointer by the loop\n\t// above.\n\tv.set_entry(entry);\n}\n\n/// Unmaps and frees all memory associated with a table.\n/// root: The root table to start freeing.\n/// NOTE: This does NOT free root directly. This must be\n/// freed manually.\n/// The reason we don't free the root is because it is\n/// usually embedded into the Process structure.\npub fn unmap(root: &mut Table) {\n\t// Start with level 2\n\tfor lv2 in 0..Table::len() {\n\t\tlet ref entry_lv2 = root.entries[lv2];\n\t\tif entry_lv2.is_valid() && entry_lv2.is_branch() {\n\t\t\t// This is a valid entry, so drill down and free.\n\t\t\tlet memaddr_lv1 = (entry_lv2.get_entry() & !0x3ff) << 2;\n\t\t\tlet table_lv1 = unsafe {\n\t\t\t\t// Make table_lv1 a mutable reference instead of\n\t\t\t\t// a pointer.\n\t\t\t\t(memaddr_lv1 as *mut Table).as_mut().unwrap()\n\t\t\t};\n\t\t\tfor lv1 in 0..Table::len() {\n\t\t\t\tlet ref entry_lv1 = table_lv1.entries[lv1];\n\t\t\t\tif entry_lv1.is_valid() && entry_lv1.is_branch()\n\t\t\t\t{\n\t\t\t\t\tlet memaddr_lv0 = (entry_lv1.get_entry()\n\t\t\t\t\t                   & !0x3ff) << 2;\n\t\t\t\t\t// The next level is level 0, which\n\t\t\t\t\t// cannot have branches, therefore,\n\t\t\t\t\t// we free here.\n\t\t\t\t\tdealloc(memaddr_lv0 as *mut u8);\n\t\t\t\t}\n\t\t\t}\n\t\t\tdealloc(memaddr_lv1 as *mut u8);\n\t\t}\n\t}\n}\n\n/// Walk the page table to convert a virtual address to a\n/// physical address.\n/// If a page fault would occur, this returns None\n/// Otherwise, it returns Some with the physical address.\npub fn virt_to_phys(root: &Table, vaddr: usize) -> Option<usize> {\n\t// Walk the page table pointed to by root\n\tlet vpn = [\n\t           // VPN[0] = vaddr[20:12]\n\t           (vaddr >> 12) & 0x1ff,\n\t           // VPN[1] = vaddr[29:21]\n\t           (vaddr >> 21) & 0x1ff,\n\t           // VPN[2] = vaddr[38:30]\n\t           (vaddr >> 30) & 0x1ff,\n\t];\n\n\tlet mut v = &root.entries[vpn[2]];\n\tfor i in (0..=2).rev() {\n\t\tif v.is_invalid() {\n\t\t\t// This is an invalid entry, page fault.\n\t\t\tbreak;\n\t\t}\n\t\telse if v.is_leaf() {\n\t\t\t// According to RISC-V, a leaf can be at any level.\n\n\t\t\t// The offset mask masks off the PPN. Each PPN is 9\n\t\t\t// bits and they start at bit #12. So, our formula\n\t\t\t// 12 + i * 9\n\t\t\tlet off_mask = (1 << (12 + i * 9)) - 1;\n\t\t\tlet vaddr_pgoff = vaddr & off_mask;\n\t\t\tlet addr = ((v.get_entry() << 2) as usize) & !off_mask;\n\t\t\treturn Some(addr | vaddr_pgoff);\n\t\t}\n\t\t// Set v to the next entry which is pointed to by this\n\t\t// entry. However, the address was shifted right by 2 places\n\t\t// when stored in the page table entry, so we shift it left\n\t\t// to get it back into place.\n\t\tlet entry = ((v.get_entry() & !0x3ff) << 2) as *const Entry;\n\t\t// We do i - 1 here, however we should get None or Some() above\n\t\t// before we do 0 - 1 = -1.\n\t\tv = unsafe { entry.add(vpn[i - 1]).as_ref().unwrap() };\n\t}\n\n\t// If we get here, we've exhausted all valid tables and haven't\n\t// found a leaf.\n\tNone\n}\n"
  },
  {
    "path": "risc_v/src/plic.rs",
    "content": "// plic.rs\n// Platform Level Interrupt Controller (PLIC)\n// Stephen Marz\n// 1 Nov 2019\n\nuse crate::uart;\nuse crate::virtio;\n\nconst PLIC_PRIORITY: usize = 0x0c00_0000;\nconst PLIC_PENDING: usize = 0x0c00_1000;\nconst PLIC_INT_ENABLE: usize = 0x0c00_2000;\nconst PLIC_THRESHOLD: usize = 0x0c20_0000;\nconst PLIC_CLAIM: usize = 0x0c20_0004;\n\n// Each register is 4-bytes (u32)\n// The PLIC is an external interrupt controller. The one\n// used by QEMU virt is the same as the SiFive PLIC.\n// https://sifive.cdn.prismic.io/sifive%2F834354f0-08e6-423c-bf1f-0cb58ef14061_fu540-c000-v1.0.pdf\n\n// Chapter 10 explains the priority, pending, interrupt enable, threshold and claims\n\n// The virt machine has the following external interrupts (from Qemu source):\n// Interrupt 0 is a \"null\" interrupt and is hardwired to 0.\n// VIRTIO = [1..8]\n// UART0 = 10\n// PCIE = [32..35]\n\n\n/// Get the next available interrupt. This is the \"claim\" process.\n/// The plic will automatically sort by priority and hand us the\n/// ID of the interrupt. For example, if the UART is interrupting\n/// and it's next, we will get the value 10.\npub fn next() -> Option<u32> {\n    let claim_reg = PLIC_CLAIM as *const u32;\n    let claim_no;\n    // The claim register is filled with the highest-priority, enabled interrupt.\n    unsafe {\n        claim_no = claim_reg.read_volatile();\n    }\n    if claim_no == 0 {\n        // The interrupt 0 is hardwired to 0, which tells us that there is no\n        // interrupt to claim, hence we return None.\n        None\n    }\n    else {\n        // If we get here, we've gotten a non-0 interrupt.\n        Some(claim_no)\n    }\n}\n\n/// Complete a pending interrupt by id. The id should come\n/// from the next() function above.\npub fn complete(id: u32) {\n    let complete_reg = PLIC_CLAIM as *mut u32;\n    unsafe {\n        // We actually write a u32 into the entire complete_register.\n        // This is the same register as the claim register, but it can\n        // differentiate based on whether we're reading or writing.\n        complete_reg.write_volatile(id);\n    }\n}\n\n/// Set the global threshold. The threshold can be a value [0..7].\n/// The PLIC will mask any interrupts at or below the given threshold.\n/// This means that a threshold of 7 will mask ALL interrupts and\n/// a threshold of 0 will allow ALL interrupts.\npub fn set_threshold(tsh: u8) {\n    // We do tsh because we're using a u8, but our maximum number\n    // is a 3-bit 0b111. So, we and with 7 (0b111) to just get the\n    // last three bits.\n    let actual_tsh = tsh & 7;\n    let tsh_reg = PLIC_THRESHOLD as *mut u32;\n    unsafe {\n        tsh_reg.write_volatile(actual_tsh as u32);\n    }\n}\n\n/// See if a given interrupt id is pending.\npub fn is_pending(id: u32) -> bool {\n    let pend = PLIC_PENDING as *const u32;\n    let actual_id = 1 << id;\n    let pend_ids;\n    unsafe {\n        pend_ids = pend.read_volatile();\n    }\n    actual_id & pend_ids != 0\n}\n\n/// Enable a given interrupt id\npub fn enable(id: u32) {\n    let enables = PLIC_INT_ENABLE as *mut u32;\n    let actual_id = 1 << id;\n    unsafe {\n        // Unlike the complete and claim registers, the plic_int_enable\n        // register is a bitset where the id is the bit index. The register\n        // is a 32-bit register, so that gives us enables for interrupts\n        // 31 through 1 (0 is hardwired to 0).\n        enables.write_volatile(enables.read_volatile() | actual_id);\n    }\n}\n\n/// Set a given interrupt priority to the given priority.\n/// The priority must be [0..7]\npub fn set_priority(id: u32, prio: u8) {\n    let actual_prio = prio as u32 & 7;\n    let prio_reg = PLIC_PRIORITY as *mut u32;\n    unsafe {\n        // The offset for the interrupt id is:\n        // PLIC_PRIORITY + 4 * id\n        // Since we're using pointer arithmetic on a u32 type,\n        // it will automatically multiply the id by 4.\n        prio_reg.add(id as usize).write_volatile(actual_prio);\n    }\n}\n\npub fn handle_interrupt() {\n    if let Some(interrupt) = next() {\n        // If we get here, we've got an interrupt from the claim register. The PLIC will\n        // automatically prioritize the next interrupt, so when we get it from claim, it\n        // will be the next in priority order.\n        match interrupt {\n            1..=8 => {\n                virtio::handle_interrupt(interrupt);\n            }\n            10 => { // Interrupt 10 is the UART interrupt.\n                uart::handle_interrupt();\n            }\n            _ => {\n                println!(\"Unknown external interrupt: {}\", interrupt);\n            }\n        }\n        // We've claimed it, so now say that we've handled it. This resets the interrupt pending\n        // and allows the UART to interrupt again. Otherwise, the UART will get \"stuck\".\n        complete(interrupt);\n    }\n}\n"
  },
  {
    "path": "risc_v/src/process.rs",
    "content": "// process.rs\n// Kernel and user processes\n// Stephen Marz\n// 27 Nov 2019\n\nuse crate::{cpu::{get_mtime,\n                  CpuMode,\n\t\t\t\t  TrapFrame,\n\t\t\t\t  Registers},\n\t\t\tfs::Inode,\n            page::{dealloc,\n                   unmap,\n\t\t\t\t   zalloc,\n\t\t\t\t   Table},\n            syscall::{syscall_exit, syscall_yield}};\nuse alloc::{string::String, collections::{vec_deque::VecDeque, BTreeMap}};\nuse core::ptr::null_mut;\nuse crate::lock::Mutex;\n\n// How many pages are we going to give a process for their\n// stack?\npub const STACK_PAGES: usize = 35;\n// We want to adjust the stack to be at the bottom of the memory allocation\n// regardless of where it is on the kernel heap.\npub const STACK_ADDR: usize = 0x1_0000_0000;\n// All processes will have a defined starting point in virtual memory.\n// We will use this later when we load processes from disk.\npub const PROCESS_STARTING_ADDR: usize = 0x2000_0000;\n\n// Here, we store a process list. It uses the global allocator\n// that we made before and its job is to store all processes.\n// We will have this list OWN the process. So, anytime we want\n// the process, we will consult the process list.\n// Using an Option here is one method of creating a \"lazy static\".\n// Rust requires that all statics be initialized, but all\n// initializations must be at compile-time. We cannot allocate\n// a VecDeque at compile time, so we are somewhat forced to\n// do this.\npub static mut PROCESS_LIST: Option<VecDeque<Process>> = None;\npub static mut PROCESS_LIST_MUTEX: Mutex = Mutex::new();\n// We can search through the process list to get a new PID, but\n// it's probably easier and faster just to increase the pid:\npub static mut NEXT_PID: u16 = 1;\n\n// The following set_* and get_by_pid functions are C-style functions\n// They probably need to be re-written in a more Rusty style, but for\n// now they are how we control processes by PID.\n\n/// Set a process' state to running. This doesn't do any checks.\n/// If this PID is not found, this returns false. Otherwise, it\n/// returns true.\npub fn set_running(pid: u16) -> bool {\n\t// Yes, this is O(n). A better idea here would be a static list\n\t// of process pointers.\n\tlet mut retval = false;\n\tunsafe {\n\t\tif let Some(mut pl) = PROCESS_LIST.take() {\n\t\t\tfor proc in pl.iter_mut() {\n\t\t\t\tif proc.pid == pid {\n\t\t\t\t\tproc.state = ProcessState::Running;\n\t\t\t\t\tretval = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Now, we no longer need the owned Deque, so we hand it\n\t\t\t// back by replacing the PROCESS_LIST's None with the\n\t\t\t// Some(pl).\n\t\t\tPROCESS_LIST.replace(pl);\n\t\t}\n\t}\n\tretval\n}\n\n/// Set a process' state to waiting. This doesn't do any checks.\n/// If this PID is not found, this returns false. Otherwise, it\n/// returns true.\npub fn set_waiting(pid: u16) -> bool {\n\t// Yes, this is O(n). A better idea here would be a static list\n\t// of process pointers.\n\tlet mut retval = false;\n\tunsafe {\n\t\tif let Some(mut pl) = PROCESS_LIST.take() {\n\t\t\tfor proc in pl.iter_mut() {\n\t\t\t\tif proc.pid == pid {\n\t\t\t\t\tproc.state = ProcessState::Waiting;\n\t\t\t\t\tretval = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Now, we no longer need the owned Deque, so we hand it\n\t\t\t// back by replacing the PROCESS_LIST's None with the\n\t\t\t// Some(pl).\n\t\t\tPROCESS_LIST.replace(pl);\n\t\t}\n\t}\n\tretval\n}\n\n/// Sleep a process\npub fn set_sleeping(pid: u16, duration: usize) -> bool {\n\t// Yes, this is O(n). A better idea here would be a static list\n\t// of process pointers.\n\tlet mut retval = false;\n\tunsafe {\n\t\tif let Some(mut pl) = PROCESS_LIST.take() {\n\t\t\tfor proc in pl.iter_mut() {\n\t\t\t\tif proc.pid == pid {\n\t\t\t\t\tproc.state = ProcessState::Sleeping;\n\t\t\t\t\tproc.sleep_until = get_mtime() + duration;\n\t\t\t\t\tretval = true;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Now, we no longer need the owned Deque, so we hand it\n\t\t\t// back by replacing the PROCESS_LIST's None with the\n\t\t\t// Some(pl).\n\t\t\tPROCESS_LIST.replace(pl);\n\t\t}\n\t}\n\tretval\n}\n\n/// Delete a process given by pid. If this process doesn't exist,\n/// this function does nothing.\npub fn delete_process(pid: u16) {\n\tunsafe {\n\t\tif let Some(mut pl) = PROCESS_LIST.take() {\n\t\t\tfor i in 0..pl.len() {\n\t\t\t\tlet p = pl.get_mut(i).unwrap();\n\t\t\t\tif (*(*p).frame).pid as u16 == pid {\n\t\t\t\t\t// When the structure gets dropped, all\n\t\t\t\t\t// of the allocations get deallocated.\n\t\t\t\t\tpl.remove(i);\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Now, we no longer need the owned Deque, so we hand it\n\t\t\t// back by replacing the PROCESS_LIST's None with the\n\t\t\t// Some(pl).\n\t\t\tPROCESS_LIST.replace(pl);\n\t\t}\n\t}\n}\n\n/// Get a process by PID. Since we leak the process list, this is\n/// unsafe since the process can be deleted and we'll still have a pointer.\npub unsafe fn get_by_pid(pid: u16) -> *mut Process {\n\tlet mut ret = null_mut();\n\tif let Some(mut pl) = PROCESS_LIST.take() {\n\t\tfor i in pl.iter_mut() {\n\t\t\tif (*(i.frame)).pid as u16 == pid {\n\t\t\t\tret = i as *mut Process;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tPROCESS_LIST.replace(pl);\n\t}\n\tret\n}\n\n/// We will eventually move this function out of here, but its\n/// job is just to take a slot in the process list.\nfn init_process() {\n\t// We can't do much here until we have system calls because\n\t// we're running in User space.\n\tprintln!(\"Init process started...\");\n\tloop {\n\t\t// Alright, I forgot. We cannot put init to sleep since the\n\t\t// scheduler will loop until it finds a process to run. Since\n\t\t// the scheduler is called in an interrupt context, nothing else\n\t\t// can happen until a process becomes available.\n\t\tsyscall_yield();\n\t}\n}\n\n/// Add a kernel process.\npub fn add_kernel_process(func: fn()) -> u16 {\n\t// This is the Rust-ism that really trips up C++ programmers.\n\t// PROCESS_LIST is wrapped in an Option<> enumeration, which\n\t// means that the Option owns the Deque. We can only borrow from\n\t// it or move ownership to us. In this case, we choose the\n\t// latter, where we move ownership to us, add a process, and\n\t// then move ownership back to the PROCESS_LIST.\n\t// This allows mutual exclusion as anyone else trying to grab\n\t// the process list will get None rather than the Deque.\n\t// .take() will replace PROCESS_LIST with None and give\n\t// us the only copy of the Deque.\n\tlet func_addr = func as usize;\n\tlet func_vaddr = func_addr; //- 0x6000_0000;\n\t\t\t// println!(\"func_addr = {:x} -> {:x}\", func_addr, func_vaddr);\n\t\t\t// We will convert NEXT_PID below into an atomic increment when\n\t\t\t// we start getting into multi-hart processing. For now, we want\n\t\t\t// a process. Get it to work, then improve it!\n\tlet my_pid = unsafe { NEXT_PID };\n\tlet mut ret_proc =\n\t\tProcess { frame:       zalloc(1) as *mut TrapFrame,\n\t\t\t\t\tstack:       zalloc(STACK_PAGES),\n\t\t\t\t\tpid:         my_pid,\n\t\t\t\t\tmmu_table:   zalloc(1) as *mut Table,\n\t\t\t\t\tstate:       ProcessState::Running,\n\t\t\t\t\tdata:        ProcessData::new(),\n\t\t\t\t\tsleep_until: 0,\n\t\t\t\t\tprogram:     null_mut(),\n\t\t\t\t\tbrk:         0,\n\t\t\t\t\t};\n\tunsafe {\n\t\tNEXT_PID += 1;\n\t}\n\t// Now we move the stack pointer to the bottom of the\n\t// allocation. The spec shows that register x2 (2) is the stack\n\t// pointer.\n\t// We could use ret_proc.stack.add, but that's an unsafe\n\t// function which would require an unsafe block. So, convert it\n\t// to usize first and then add PAGE_SIZE is better.\n\t// We also need to set the stack adjustment so that it is at the\n\t// bottom of the memory and far away from heap allocations.\n\tunsafe {\n\t\t(*ret_proc.frame).pc = func_vaddr;\n\t\t// 1 is the return address register. This makes it so we\n\t\t// don't have to do syscall_exit() when a kernel process\n\t\t// finishes.\n\t\t(*ret_proc.frame).regs[Registers::Ra as usize] = ra_delete_proc as usize;\n\t\t(*ret_proc.frame).regs[Registers::Sp as usize] =\n\t\t\tret_proc.stack as usize + STACK_PAGES * 4096;\n\t\t(*ret_proc.frame).mode = CpuMode::Machine as usize;\n\t\t(*ret_proc.frame).pid = ret_proc.pid as usize;\n\t}\n\n\tif let Some(mut pl) = unsafe { PROCESS_LIST.take() } {\n\t\tpl.push_back(ret_proc);\n\t\t// Now, we no longer need the owned Deque, so we hand it\n\t\t// back by replacing the PROCESS_LIST's None with the\n\t\t// Some(pl).\n\t\tunsafe {\n\t\t\tPROCESS_LIST.replace(pl);\n\t\t}\n\t\tmy_pid\n\t}\n\telse {\n\t\tunsafe { PROCESS_LIST_MUTEX.unlock(); }\n\t\t// TODO: When we get to multi-hart processing, we need to keep\n\t\t// trying to grab the process list. We can do this with an\n\t\t// atomic instruction. but right now, we're a single-processor\n\t\t// computer.\n\t\t0\n\t}\n}\n\n/// A kernel process is just a function inside of the kernel. Each\n/// function will perform a \"ret\" or return through the return address\n/// (ra) register. This function address is what it will return to, which\n/// in turn calls exit. If we don't exit, the process will most likely\n/// fault.\nfn ra_delete_proc() {\n\tsyscall_exit();\n}\n\n/// This is the same as the add_kernel_process function, except you can pass\n/// arguments. Typically, this will be a memory address on the heap where\n/// arguments can be found.\npub fn add_kernel_process_args(func: fn(args_ptr: usize), args: usize) -> u16 {\n\t// This is the Rust-ism that really trips up C++ programmers.\n\t// PROCESS_LIST is wrapped in an Option<> enumeration, which\n\t// means that the Option owns the Deque. We can only borrow from\n\t// it or move ownership to us. In this case, we choose the\n\t// latter, where we move ownership to us, add a process, and\n\t// then move ownership back to the PROCESS_LIST.\n\t// This allows mutual exclusion as anyone else trying to grab\n\t// the process list will get None rather than the Deque.\n\tunsafe {PROCESS_LIST_MUTEX.spin_lock(); }\n\tif let Some(mut pl) = unsafe { PROCESS_LIST.take() } {\n\t\t// .take() will replace PROCESS_LIST with None and give\n\t\t// us the only copy of the Deque.\n\t\tlet func_addr = func as usize;\n\t\tlet func_vaddr = func_addr; //- 0x6000_0000;\n\t\t\t    // println!(\"func_addr = {:x} -> {:x}\", func_addr, func_vaddr);\n\t\t\t    // We will convert NEXT_PID below into an atomic increment when\n\t\t\t    // we start getting into multi-hart processing. For now, we want\n\t\t\t    // a process. Get it to work, then improve it!\n\t\tlet my_pid = unsafe { NEXT_PID };\n\t\tlet mut ret_proc =\n\t\t\tProcess { frame:       zalloc(1) as *mut TrapFrame,\n\t\t\t          stack:       zalloc(STACK_PAGES),\n\t\t\t          pid:         my_pid,\n\t\t\t          mmu_table:        zalloc(1) as *mut Table,\n\t\t\t          state:       ProcessState::Running,\n\t\t\t          data:        ProcessData::new(),\n\t\t\t\t\t  sleep_until: 0, \n\t\t\t\t\t  program:\t\tnull_mut(),\n\t\t\t\t\t  brk:         0,\n\t\t\t\t\t};\n\t\tunsafe {\n\t\t\tNEXT_PID += 1;\n\t\t}\n\t\t// Now we move the stack pointer to the bottom of the\n\t\t// allocation. The spec shows that register x2 (2) is the stack\n\t\t// pointer.\n\t\t// We could use ret_proc.stack.add, but that's an unsafe\n\t\t// function which would require an unsafe block. So, convert it\n\t\t// to usize first and then add PAGE_SIZE is better.\n\t\t// We also need to set the stack adjustment so that it is at the\n\t\t// bottom of the memory and far away from heap allocations.\n\t\tunsafe {\n\t\t\t(*ret_proc.frame).pc = func_vaddr;\n\t\t\t(*ret_proc.frame).regs[Registers::A0 as usize] = args;\n\t\t\t// 1 is the return address register. This makes it so we\n\t\t\t// don't have to do syscall_exit() when a kernel process\n\t\t\t// finishes.\n\t\t\t(*ret_proc.frame).regs[Registers::Ra as usize] = ra_delete_proc as usize;\n\t\t\t(*ret_proc.frame).regs[Registers::Sp as usize] =\n\t\t\t\tret_proc.stack as usize + STACK_PAGES * 4096;\n\t\t\t(*ret_proc.frame).mode = CpuMode::Machine as usize;\n\t\t\t(*ret_proc.frame).pid = ret_proc.pid as usize;\n\t\t}\n\t\tpl.push_back(ret_proc);\n\t\t// Now, we no longer need the owned Deque, so we hand it\n\t\t// back by replacing the PROCESS_LIST's None with the\n\t\t// Some(pl).\n\t\tunsafe {\n\t\t\tPROCESS_LIST.replace(pl);\n\t\t\tPROCESS_LIST_MUTEX.unlock();\n\t\t}\n\t\tmy_pid\n\t}\n\telse {\n\t\tunsafe {\n\t\t\tPROCESS_LIST_MUTEX.unlock();\n\t\t}\n\t\t// TODO: When we get to multi-hart processing, we need to keep\n\t\t// trying to grab the process list. We can do this with an\n\t\t// atomic instruction. but right now, we're a single-processor\n\t\t// computer.\n\t\t0\n\t}\n}\n\n/// This should only be called once, and its job is to create\n/// the init process. Right now, this process is in the kernel,\n/// but later, it should call the shell.\npub fn init() -> usize {\n\tunsafe {\n\t\tPROCESS_LIST_MUTEX.spin_lock();\n\t\tPROCESS_LIST = Some(VecDeque::with_capacity(15));\n\t\t// add_process_default(init_process);\n\t\tadd_kernel_process(init_process);\n\t\t// Ugh....Rust is giving me fits over here!\n\t\t// I just want a memory address to the trap frame, but\n\t\t// due to the borrow rules of Rust, I'm fighting here. So,\n\t\t// instead, let's move the value out of PROCESS_LIST, get\n\t\t// the address, and then move it right back in.\n\t\tlet pl = PROCESS_LIST.take().unwrap();\n\t\tlet p = pl.front().unwrap().frame;\n\t\t// let frame = p as *const TrapFrame as usize;\n\t\t// println!(\"Init's frame is at 0x{:08x}\", frame);\n\t\t// Put the process list back in the global.\n\t\tPROCESS_LIST.replace(pl);\n\t\tPROCESS_LIST_MUTEX.unlock();\n\t\t// Return the first instruction's address to execute.\n\t\t// Since we use the MMU, all start here.\n\t\t(*p).pc\n\t}\n}\n\n// Our process must be able to sleep, wait, or run.\n// Running - means that when the scheduler finds this process, it can run it.\n// Sleeping - means that the process is waiting on a certain amount of time.\n// Waiting - means that the process is waiting on I/O\n// Dead - We should never get here, but we can flag a process as Dead and clean\n//        it out of the list later.\npub enum ProcessState {\n\tRunning,\n\tSleeping,\n\tWaiting,\n\tDead,\n}\n\npub struct Process {\n\tpub frame:       *mut TrapFrame,\n\tpub stack:       *mut u8,\n\tpub pid:         u16,\n\tpub mmu_table:   *mut Table,\n\tpub state:       ProcessState,\n\tpub data:        ProcessData,\n\tpub sleep_until: usize,\n\tpub program:\t *mut u8,\n\tpub brk:         usize,\n}\n\nimpl Drop for Process {\n\t/// Since we're storing ownership of a Process in the linked list,\n\t/// we can cause it to deallocate automatically when it is removed.\n\tfn drop(&mut self) {\n\t\t// We allocate the stack as a page.\n\t\tdealloc(self.stack);\n\t\t// This is unsafe, but it's at the drop stage, so we won't\n\t\t// be using this again.\n\t\tunsafe {\n\t\t\t// Remember that unmap unmaps all levels of page tables\n\t\t\t// except for the root. It also deallocates the memory\n\t\t\t// associated with the tables.\n\t\t\tunmap(&mut *self.mmu_table);\n\t\t}\n\t\tdealloc(self.mmu_table as *mut u8);\n\t\tdealloc(self.frame as *mut u8);\n\t\tfor i in self.data.pages.drain(..) {\n\t\t\tdealloc(i as *mut u8);\n\t\t}\n\t\t// Kernel processes don't have a program, instead the program is linked\n\t\t// directly in the kernel.\n\t\tif !self.program.is_null() {\n\t\t\tdealloc(self.program);\n\t\t}\n\t}\n}\n\npub enum Descriptor {\n\tFile(Inode),\n\tDevice(usize),\n\tFramebuffer,\n\tButtonEvents,\n\tAbsoluteEvents,\n\tConsole,\n\tNetwork,\n\tUnknown,\n}\n\n// The private data in a process contains information\n// that is relevant to where we are, including the path\n// and open file descriptors.\n// We will allow dead code for now until we have a need for the\n// private process data. This is essentially our resource control block (RCB).\n#[allow(dead_code)]\npub struct ProcessData {\n\tpub environ: BTreeMap<String, String>,\n\tpub fdesc: BTreeMap<u16, Descriptor>,\n\tpub cwd: String,\n\tpub pages: VecDeque<usize>,\n}\n\n// This is private data that we can query with system calls.\n// If we want to implement CFQ (completely fair queuing), which\n// is a per-process block queuing algorithm, we can put that here.\nimpl ProcessData {\n\tpub fn new() -> Self {\n\t\tProcessData { \n\t\t\tenviron: BTreeMap::new(),\n\t\t\tfdesc: BTreeMap::new(),\n\t\t\tcwd: String::from(\"/\"),\n\t\t\tpages: VecDeque::new(),\n\t\t }\n\t}\n}\n"
  },
  {
    "path": "risc_v/src/rng.rs",
    "content": "// rng.rs\n// Random number generator using VirtIO\n// Stephen Marz\n// 16 March 2020\n\n#![allow(dead_code)]\nuse crate::{kmem::{kfree, kmalloc},\n            page::{zalloc, PAGE_SIZE},\n            virtio,\n            virtio::{Descriptor, MmioOffsets, Queue, StatusField, VIRTIO_RING_SIZE}};\nuse core::{mem::size_of, ptr::null_mut};\n\npub struct EntropyDevice {\n\tqueue:        *mut Queue,\n\tdev:          *mut u32,\n\tidx:          u16,\n\tack_used_idx: u16,\n}\nimpl EntropyDevice {\n\tpub const fn new() -> Self {\n\t\tEntropyDevice { queue:        null_mut(),\n\t\t                dev:          null_mut(),\n\t\t                idx:          0,\n\t\t                ack_used_idx: 0, }\n\t}\n}\n\nstatic mut ENTROPY_DEVICES: [Option<EntropyDevice>; 8] = [\n\tNone,\n\tNone,\n\tNone,\n\tNone,\n\tNone,\n\tNone,\n\tNone,\n\tNone,\n];\n\npub fn setup_entropy_device(ptr: *mut u32) -> bool {\n\tunsafe {\n\t\t// We can get the index of the device based on its address.\n\t\t// 0x1000_1000 is index 0\n\t\t// 0x1000_2000 is index 1\n\t\t// ...\n\t\t// 0x1000_8000 is index 7\n\t\t// To get the number that changes over, we shift right 12 places (3 hex digits)\n\t\tlet idx = (ptr as usize - virtio::MMIO_VIRTIO_START) >> 12;\n\t\t// [Driver] Device Initialization\n\t\t// 1. Reset the device (write 0 into status)\n\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(0);\n\t\tlet mut status_bits = StatusField::Acknowledge.val32();\n\t\t// 2. Set ACKNOWLEDGE status bit\n\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);\n\t\t// 3. Set the DRIVER status bit\n\t\tstatus_bits |= StatusField::DriverOk.val32();\n\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);\n\t\t// 4. Read device feature bits, write subset of feature\n\t\t// bits understood by OS and driver    to the device.\n\t\tlet host_features = ptr.add(MmioOffsets::HostFeatures.scale32()).read_volatile();\n\t\tptr.add(MmioOffsets::GuestFeatures.scale32()).write_volatile(host_features);\n\t\t// 5. Set the FEATURES_OK status bit\n\t\tstatus_bits |= StatusField::FeaturesOk.val32();\n\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);\n\t\t// 6. Re-read status to ensure FEATURES_OK is still set.\n\t\t// Otherwise, it doesn't support our features.\n\t\tlet status_ok = ptr.add(MmioOffsets::Status.scale32()).read_volatile();\n\t\t// If the status field no longer has features_ok set,\n\t\t// that means that the device couldn't accept\n\t\t// the features that we request. Therefore, this is\n\t\t// considered a \"failed\" state.\n\t\tif false == StatusField::features_ok(status_ok) {\n\t\t\tprint!(\"features fail...\");\n\t\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(StatusField::Failed.val32());\n\t\t\treturn false;\n\t\t}\n\t\t// 7. Perform device-specific setup.\n\t\t// Set the queue num. We have to make sure that the\n\t\t// queue size is valid because the device can only take\n\t\t// a certain size.\n\t\tlet qnmax = ptr.add(MmioOffsets::QueueNumMax.scale32()).read_volatile();\n\t\tptr.add(MmioOffsets::QueueNum.scale32()).write_volatile(VIRTIO_RING_SIZE as u32);\n\t\tif VIRTIO_RING_SIZE as u32 > qnmax {\n\t\t\tprint!(\"queue size fail...\");\n\t\t\treturn false;\n\t\t}\n\t\t// First, if the block device array is empty, create it!\n\t\t// We add 4095 to round this up and then do an integer\n\t\t// divide to truncate the decimal. We don't add 4096,\n\t\t// because if it is exactly 4096 bytes, we would get two\n\t\t// pages, not one.\n\t\tlet num_pages = (size_of::<Queue>() + PAGE_SIZE - 1) / PAGE_SIZE;\n\t\t// println!(\"np = {}\", num_pages);\n\t\t// We allocate a page for each device. This will the the\n\t\t// descriptor where we can communicate with the block\n\t\t// device. We will still use an MMIO register (in\n\t\t// particular, QueueNotify) to actually tell the device\n\t\t// we put something in memory. We also have to be\n\t\t// careful with memory ordering. We don't want to\n\t\t// issue a notify before all memory writes have\n\t\t// finished. We will look at that later, but we need\n\t\t// what is called a memory \"fence\" or barrier.\n\t\tptr.add(MmioOffsets::QueueSel.scale32()).write_volatile(0);\n\t\t// Alignment is very important here. This is the memory address\n\t\t// alignment between the available and used rings. If this is wrong,\n\t\t// then we and the device will refer to different memory addresses\n\t\t// and hence get the wrong data in the used ring.\n\t\t// ptr.add(MmioOffsets::QueueAlign.scale32()).write_volatile(2);\n\t\tlet queue_ptr = zalloc(num_pages) as *mut Queue;\n\t\tlet queue_pfn = queue_ptr as u32;\n\t\tptr.add(MmioOffsets::GuestPageSize.scale32()).write_volatile(PAGE_SIZE as u32);\n\t\t// QueuePFN is a physical page number, however it\n\t\t// appears for QEMU we have to write the entire memory\n\t\t// address. This is a physical memory address where we\n\t\t// (the OS) and the block device have in common for\n\t\t// making and receiving requests.\n\t\tptr.add(MmioOffsets::QueuePfn.scale32()).write_volatile(queue_pfn / PAGE_SIZE as u32);\n\t\t// 8. Set the DRIVER_OK status bit. Device is now \"live\"\n\t\tstatus_bits |= StatusField::DriverOk.val32();\n\t\tptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);\n\n\t\tlet rngdev = EntropyDevice {\n\t\t\tqueue: queue_ptr,\n\t\t\tdev: ptr,\n\t\t\tidx: 0,\n\t\t\tack_used_idx: 0,\n\t\t};\n\n\t\tENTROPY_DEVICES[idx] = Some(rngdev);\n\n\t\ttrue\n\t}\n}\n\npub fn get_random() -> u64 {\n\tunsafe {\n\t\tfor i in ENTROPY_DEVICES.iter() {\n\t\t\tif let Some(_edev) = i {\n\t\t\t\tlet ptr = kmalloc(8);\n\t\t\t\tlet _desc = Descriptor { addr:  ptr as u64,\n\t\t\t\t\t\t\t\t\t\tlen:   8,\n\t\t\t\t\t\t\t\t\t\tflags: virtio::VIRTIO_DESC_F_WRITE,\n\t\t\t\t\t\t\t\t\t\tnext:  0, };\n\t\t\t\tlet _val = *ptr as u64;\n\t\t\t\tkfree(ptr);\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\n\t0u64.wrapping_sub(1)\n}\n"
  },
  {
    "path": "risc_v/src/sched.rs",
    "content": "// sched.rs\n// Simple process scheduler\n// Stephen Marz\n// 27 Dec 2019\n\nuse crate::process::{ProcessState, PROCESS_LIST, PROCESS_LIST_MUTEX};\nuse crate::cpu::get_mtime;\n\npub fn schedule() -> usize {\n\tlet mut frame_addr: usize = 0x1111;\n\tunsafe {\n\t\t// If we can't get the lock, then usually this means a kernel\n\t\t// process has the lock. So, we return 0. This has a special\n\t\t// meaning to whomever calls the scheduler to say \"nobody else got scheduled\"\n\t\tif PROCESS_LIST_MUTEX.try_lock() == false {\n\t\t\treturn 0;\n\t\t}\n\t\tif let Some(mut pl) = PROCESS_LIST.take() {\n\t\t\t// Rust allows us to label loops so that break statements can be\n\t\t\t// targeted.\n\t\t\t'procfindloop: loop {\n\t\t\t\tpl.rotate_left(1);\n\t\t\t\tif let Some(prc) = pl.front_mut() {\n\t\t\t\t\tmatch prc.state {\n\t\t\t\t\t\tProcessState::Running => {\n\t\t\t\t\t\t\tframe_addr = prc.frame as usize;\n\t\t\t\t\t\t\tbreak 'procfindloop;\n\t\t\t\t\t\t},\n\t\t\t\t\t\tProcessState::Sleeping => {\n\t\t\t\t\t\t\t// Awaken sleeping processes whose sleep until is in\n\t\t\t\t\t\t\t// the past.\n\t\t\t\t\t\t\tif prc.sleep_until <= get_mtime() {\n\t\t\t\t\t\t\t\tprc.state = ProcessState::Running;\n\t\t\t\t\t\t\t\tframe_addr = prc.frame as usize;\n\t\t\t\t\t\t\t\tbreak 'procfindloop;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t},\n\t\t\t\t\t\t_ => {},\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tPROCESS_LIST.replace(pl);\n\t\t}\n\t\telse {\n\t\t\tprintln!(\"could not take process list\");\n\t\t}\n\t\tPROCESS_LIST_MUTEX.unlock();\n\t}\n\tframe_addr\n}\n"
  },
  {
    "path": "risc_v/src/syscall.rs",
    "content": "// syscall.rs\n// System calls\n// Stephen Marz\n// 3 Jan 2020\n\nuse crate::{block::block_op,\n            buffer::Buffer,\n            cpu::{dump_registers, Registers, TrapFrame, gp},\n            elf,\n            fs,\n            gpu,\n            input::{Event, ABS_EVENTS, KEY_EVENTS},\n            page::{map, virt_to_phys, EntryBits, Table, PAGE_SIZE, zalloc},\n\t\t\tprocess::{add_kernel_process_args, delete_process, get_by_pid, set_sleeping, set_waiting, PROCESS_LIST, PROCESS_LIST_MUTEX, Descriptor}};\nuse crate::console::{IN_LOCK, IN_BUFFER, push_queue};\nuse alloc::{boxed::Box, string::String};\n\n/// do_syscall is called from trap.rs to invoke a system call. No discernment is\n/// made here whether this is a U-mode, S-mode, or M-mode system call.\n/// Since we can't do anything unless we dereference the passed pointer,\n/// I went ahead and made the entire function unsafe.\n/// If we return 0 from this function, the m_trap function will schedule\n/// the next process--consider this a yield. A non-0 is the program counter\n/// we want to go back to.\npub unsafe fn do_syscall(mepc: usize, frame: *mut TrapFrame) {\n\t// Libgloss expects the system call number in A7, so let's follow\n\t// their lead.\n\t// A7 is X17, so it's register number 17.\n\tlet syscall_number = (*frame).regs[gp(Registers::A7)];\n\t// skip the ecall\n\t(*frame).pc = mepc + 4;\n\tmatch syscall_number {\n\t\t93 | 94 => {\n\t\t\t// exit and exit_group\n\t\t\tdelete_process((*frame).pid as u16);\n\t\t}\n\t\t1 => {\n\t\t\t//yield\n\t\t\t// We don't do anything, but we don't want to print \"unknown system call\"\n\t\t}\n\t\t2 => {\n\t\t\t// Easy putchar\n\t\t\tprint!(\"{}\", (*frame).regs[Registers::A0 as usize] as u8 as char);\n\t\t}\n\t\t8 => {\n\t\t\tdump_registers(frame);\n\t\t}\n\t\t10 => {\n\t\t\t// Sleep\n\t\t\tset_sleeping((*frame).pid as u16, (*frame).regs[Registers::A0 as usize]);\n\t\t}\n\t\t11 => {\n\t\t\t// execv\n\t\t\t// A0 = path\n\t\t\t// A1 = argv\n\t\t\tlet mut path_addr = (*frame).regs[Registers::A0 as usize];\n\t\t\t// If the MMU is turned on, translate.\n\t\t\tif (*frame).satp >> 60 != 0 {\n\t\t\t\tlet p = get_by_pid((*frame).pid as u16);\n\t\t\t\tlet table = ((*p).mmu_table).as_ref().unwrap();\n\t\t\t\tpath_addr = virt_to_phys(table, path_addr).unwrap();\n\t\t\t}\n\t\t\t// Our path address here is now a physical address. If it came in virtual,\n\t\t\t// it is now physical.\n\t\t\tlet path_bytes = path_addr as *const u8;\n\t\t\tlet mut path = String::new();\n\t\t\tlet mut iterator: usize = 0;\n\t\t\t// I really have to figure out how to change an array of bytes\n\t\t\t// to a string. For now, this is very C-style and mimics strcpy.\n\t\t\tloop {\n\t\t\t\tlet ch = *path_bytes.add(iterator);\n\t\t\t\tif ch == 0 {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\titerator += 1;\n\t\t\t\tpath.push(ch as char);\n\t\t\t}\n\t\t\t// See if we can find the path.\n\t\t\tif let Ok(inode) = fs::MinixFileSystem::open(8, &path) {\n\t\t\t\tlet inode_heap = Box::new(inode);\n\t\t\t\t// The Box above moves the Inode to a new memory location on the heap.\n\t\t\t\t// This needs to be on the heap since we are about to hand over control\n\t\t\t\t// to a kernel process.\n\t\t\t\t// THERE is an issue here. If we fail somewhere inside the kernel process,\n\t\t\t\t// we shouldn't delete our process here. However, since this is asynchronous\n\t\t\t\t// our process will still get deleted and the error won't be reported.\n\t\t\t\t// We have to make sure we relinquish Box control here by using into_raw.\n\t\t\t\t// Otherwise, the Box will free the memory associated with this inode.\n\t\t\t\tadd_kernel_process_args(exec_func, Box::into_raw(inode_heap) as usize);\n\t\t\t\t// This deletes us, which is what we want.\n\t\t\t\tdelete_process((*frame).pid as u16);\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// If we get here, the path couldn't be found, or for some reason\n\t\t\t\t// open failed. So, we return -1 and move on.\n\t\t\t\tprintln!(\"Could not open path '{}'.\", path);\n\t\t\t\t(*frame).regs[Registers::A0 as usize] = -1isize as usize;\n\t\t\t}\n\t\t}\n\t\t17 => { //getcwd\n\t\t\tlet mut buf = (*frame).regs[gp(Registers::A0)] as *mut u8;\n\t\t\tlet size = (*frame).regs[gp(Registers::A1)];\n\t\t\tlet process = get_by_pid((*frame).pid as u16).as_ref().unwrap();\n\t\t\tlet mut iter = 0usize;\n\t\t\tif (*frame).satp >> 60 != 0 {\n\t\t\t\tlet table = ((*process).mmu_table).as_mut().unwrap();\n\t\t\t\tlet paddr = virt_to_phys(table, buf as usize);\n\t\t\t\tif let Some(bufaddr) = paddr {\n\t\t\t\t\tbuf = bufaddr as *mut u8;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\t(*frame).regs[gp(Registers::A0)] = -1isize as usize;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t}\n\t\t\tfor i in process.data.cwd.as_bytes() {\n\t\t\t\tif iter == 0 || iter >= size {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tbuf.add(iter).write(*i);\n\t\t\t\titer += 1;\n\t\t\t}\n\t\t}\n\t\t48 => {\n\t\t// #define SYS_faccessat 48\n\t\t\t(*frame).regs[gp(Registers::A0)] = -1isize as usize;\n\t\t}\n\t\t57 => {\n\t\t\t// #define SYS_close 57\n\t\t\tlet fd = (*frame).regs[gp(Registers::A0)] as u16;\n\t\t\tlet process = get_by_pid((*frame).pid as u16).as_mut().unwrap();\n\t\t\tif process.data.fdesc.contains_key(&fd) {\n\t\t\t\tprocess.data.fdesc.remove(&fd);\n\t\t\t\t(*frame).regs[gp(Registers::A0)] = 0;\n\t\t\t}\n\t\t\telse {\n\t\t\t\t(*frame).regs[gp(Registers::A0)] = -1isize as usize;\n\t\t\t}\n\t\t\t// Flush?\n\t\t}\n\t\t63 => { // sys_read\n\t\t\tlet fd = (*frame).regs[gp(Registers::A0)] as u16;\n\t\t\tlet mut buf = (*frame).regs[gp(Registers::A1)] as *mut u8;\n\t\t\tlet size = (*frame).regs[gp(Registers::A2)];\n\t\t\tlet process = get_by_pid((*frame).pid as u16).as_mut().unwrap();\n\t\t\tlet mut ret = 0usize;\n\t\t\t// If we return 0, the trap handler will schedule\n\t\t\t// another process.\n\t\t\tif fd == 0 { // stdin\n\t\t\t\tIN_LOCK.spin_lock();\n\t\t\t\tif let Some(mut inb) = IN_BUFFER.take() {\n\t\t\t\t\tlet num_elements = if inb.len() >= size { size } else { inb.len() };\n\t\t\t\t\tlet mut buf_ptr = buf as *mut u8;\n\t\t\t\t\tif num_elements == 0 {\n\t\t\t\t\t\tpush_queue((*frame).pid as u16);\n\t\t\t\t\t\tset_waiting((*frame).pid as u16);\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tfor i in inb.drain(0..num_elements) {\n\t\t\t\t\t\t\tif (*frame).satp >> 60 != 0 {\n\t\t\t\t\t\t\t\tlet table = ((*process).mmu_table).as_mut().unwrap();\n\t\t\t\t\t\t\t\tlet buf_addr = virt_to_phys(table, buf as usize);\n\t\t\t\t\t\t\t\tif buf_addr.is_none() {\n\t\t\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tbuf_ptr = buf_addr.unwrap() as *mut u8;\n\t\t\t\t\t\t\t\tbuf_ptr.write(i);\n\t\t\t\t\t\t\t\tret += 1;\n\t\t\t\t\t\t\t\tprintln!(\"R: {}\", ret);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbuf = buf.add(1);\n\t\t\t\t\t\t\tbuf_ptr = buf_ptr.add(1);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tIN_BUFFER.replace(inb);\n\t\t\t\t}\n\t\t\t\tIN_LOCK.unlock();\n\t\t\t}\n\t\t\t(*frame).regs[gp(Registers::A0)] = ret;\n\t\t}\n\t\t64 => { // sys_write\n\t\t\tlet fd = (*frame).regs[gp(Registers::A0)] as u16;\n\t\t\tlet buf = (*frame).regs[gp(Registers::A1)] as *const u8;\n\t\t\tlet size = (*frame).regs[gp(Registers::A2)];\n\t\t\tlet process = get_by_pid((*frame).pid as u16).as_ref().unwrap();\n\t\t\tif fd == 1 || fd == 2 {\n\t\t\t\t// stdout / stderr\n\t\t\t\t// println!(\"WRITE {}, 0x{:08x}, {}\", fd, bu/f as usize, size);\n\t\t\t\tlet mut iter = 0;\n\t\t\t\tfor i in 0..size {\n\t\t\t\t\titer += 1;\n\t\t\t\t\tif (*frame).satp >> 60 != 0 {\n\t\t\t\t\t\tlet table = ((*process).mmu_table).as_mut().unwrap();\n\t\t\t\t\t\t// We don't need to do the following until we reach a page boundary,\n\t\t\t\t\t\t// however that code isn't written, yet.\n\t\t\t\t\t\tlet paddr = virt_to_phys(table, buf.add(i) as usize);\n\t\t\t\t\t\tif let Some(bufaddr) = paddr {\n\t\t\t\t\t\t\tprint!(\"{}\", *(bufaddr as *const u8) as char);\n\t\t\t\t\t\t}\n\t\t\t\t\t\telse {\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t(*frame).regs[gp(Registers::A0)] = iter as usize;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tlet descriptor = process.data.fdesc.get(&fd);\n\t\t\t\tif descriptor.is_none() {\n\t\t\t\t\t(*frame).regs[gp(Registers::A0)] = 0;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tlet descriptor = descriptor.unwrap();\n\t\t\t\t\tmatch descriptor {\n\t\t\t\t\t\tDescriptor::Framebuffer => {\n\n\t\t\t\t\t\t}\n\t\t\t\t\t\tDescriptor::File(inode) => {\n\n\t\t\t\t\t\t\n\t\t\t\t\t\t}\n\t\t\t\t\t\t_ => {\n\t\t\t\t\t\t\t// unsupported\n\t\t\t\t\t\t\t(*frame).regs[gp(Registers::A0)] = 0;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t66 => {\n\t\t\t(*frame).regs[gp(Registers::A0)] = -1isize as usize;\n\t\t}\n\t\t// #define SYS_fstat 80\n\t\t80 => {\n\t\t\t// int fstat(int filedes, struct stat *buf)\n\t\t\t(*frame).regs[gp(Registers::A0)] = 0;\n\t\t}\n\t\t172 => {\n\t\t\t// A0 = pid\n\t\t\t(*frame).regs[Registers::A0 as usize] = (*frame).pid;\n\t\t}\n\t\t180 => {\n\t\t\tset_waiting((*frame).pid as u16);\n\t\t\tlet _ = block_op(\n\t\t\t                 (*frame).regs[Registers::A0 as usize],\n\t\t\t                 (*frame).regs[Registers::A1 as usize] as *mut u8,\n\t\t\t                 (*frame).regs[Registers::A2 as usize] as u32,\n\t\t\t                 (*frame).regs[Registers::A3 as usize] as u64,\n\t\t\t                 false,\n\t\t\t                 (*frame).pid as u16\n\t\t\t);\n\t\t}\n\t\t214 => { // brk\n\t\t\t// #define SYS_brk 214\n\t\t\t// void *brk(void *addr);\n\t\t\tlet addr = (*frame).regs[gp(Registers::A0)];\n\t\t\tlet process = get_by_pid((*frame).pid as u16).as_mut().unwrap();\n\t\t\t// println!(\"Break move from 0x{:08x} to 0x{:08x}\", process.brk, addr);\n\t\t\tif addr > process.brk {\n\t\t\t\tif (*frame).satp >> 60 != 0 {\n\t\t\t\t\tlet table = ((*process).mmu_table).as_mut().unwrap();\n\t\t\t\t\tlet diff = (addr + PAGE_SIZE - process.brk) / PAGE_SIZE;\n\t\t\t\t\tfor i in 0..diff {\n\t\t\t\t\t\tlet new_addr = zalloc(1) as usize;\n\t\t\t\t\t\tprocess.data.pages.push_back(new_addr);\n\t\t\t\t\t\tmap(table, process.brk + (i << 12), new_addr, EntryBits::UserReadWrite.val(), 0);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tprocess.brk = addr;\n\t\t\t}\n\t\t\t(*frame).regs[gp(Registers::A0)] = process.brk;\n\t\t}\n\t\t// System calls 1000 and above are \"special\" system calls for our OS. I'll\n\t\t// try to mimic the normal system calls below 1000 so that this OS is compatible\n\t\t// with libraries.\n\t\t1000 => {\n\t\t\t// get framebuffer\n\t\t\t// syscall_get_framebuffer(device)\n\t\t\tlet dev = (*frame).regs[Registers::A0 as usize];\n\t\t\t(*frame).regs[Registers::A0 as usize] = 0;\n\t\t\tif dev > 0 && dev <= 8 {\n\t\t\t\tif let Some(p) = gpu::GPU_DEVICES[dev - 1].take() {\n\t\t\t\t\tlet ptr = p.get_framebuffer() as usize;\n\t\t\t\t\tif (*frame).satp >> 60 != 0 {\n\t\t\t\t\t\tlet process = get_by_pid((*frame).pid as u16);\n\t\t\t\t\t\tlet table = ((*process).mmu_table).as_mut().unwrap();\n\t\t\t\t\t\tlet num_pages = (p.get_width() * p.get_height() * 4) as usize / PAGE_SIZE;\n\t\t\t\t\t\tfor i in 0..num_pages {\n\t\t\t\t\t\t\tlet vaddr = 0x3000_0000 + (i << 12);\n\t\t\t\t\t\t\tlet paddr = ptr + (i << 12);\n\t\t\t\t\t\t\tmap(table, vaddr, paddr, EntryBits::UserReadWrite as usize, 0);\n\t\t\t\t\t\t}\n\t\t\t\t\t\tgpu::GPU_DEVICES[dev - 1].replace(p);\n\t\t\t\t\t}\n\t\t\t\t\t(*frame).regs[Registers::A0 as usize] = 0x3000_0000;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\t1001 => {\n\t\t\t// transfer rectangle and invalidate\n\t\t\tlet dev = (*frame).regs[Registers::A0 as usize];\n\t\t\tlet x = (*frame).regs[Registers::A1 as usize] as u32;\n\t\t\tlet y = (*frame).regs[Registers::A2 as usize] as u32;\n\t\t\tlet width = (*frame).regs[Registers::A3 as usize] as u32;\n\t\t\tlet height = (*frame).regs[Registers::A4 as usize] as u32;\n\t\t\tgpu::transfer(dev, x, y, width, height);\n\t\t}\n\t\t1002 => {\n\t\t\t// wait for keyboard events\n\t\t\tlet mut ev = KEY_EVENTS.take().unwrap();\n\t\t\tlet max_events = (*frame).regs[Registers::A1 as usize];\n\t\t\tlet vaddr = (*frame).regs[Registers::A0 as usize] as *const Event;\n\t\t\tif (*frame).satp >> 60 != 0 {\n\t\t\t\tlet process = get_by_pid((*frame).pid as u16);\n\t\t\t\tlet table = (*process).mmu_table.as_mut().unwrap();\n\t\t\t\t(*frame).regs[Registers::A0 as usize] = 0;\n\t\t\t\tlet num_events = if max_events <= ev.len() {\n\t\t\t\t\tmax_events\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tev.len()\n\t\t\t\t};\n\t\t\t\tfor i in 0..num_events {\n\t\t\t\t\tlet paddr = virt_to_phys(table, vaddr.add(i) as usize);\n\t\t\t\t\tif paddr.is_none() {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tlet paddr = paddr.unwrap() as *mut Event;\n\t\t\t\t\t*paddr = ev.pop_front().unwrap();\n\t\t\t\t\t(*frame).regs[Registers::A0 as usize] += 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tKEY_EVENTS.replace(ev);\n\t\t}\n\t\t1004 => {\n\t\t\t// wait for abs events\n\t\t\tlet mut ev = ABS_EVENTS.take().unwrap();\n\t\t\tlet max_events = (*frame).regs[Registers::A1 as usize];\n\t\t\tlet vaddr = (*frame).regs[Registers::A0 as usize] as *const Event;\n\t\t\tif (*frame).satp >> 60 != 0 {\n\t\t\t\tlet process = get_by_pid((*frame).pid as u16);\n\t\t\t\tlet table = ((*process).mmu_table as *mut Table).as_mut().unwrap();\n\t\t\t\t(*frame).regs[Registers::A0 as usize] = 0;\n\t\t\t\tfor i in 0..if max_events <= ev.len() {\n\t\t\t\t\tmax_events\n\t\t\t\t}\n\t\t\t\telse {\n\t\t\t\t\tev.len()\n\t\t\t\t} {\n\t\t\t\t\tlet paddr = virt_to_phys(table, vaddr.add(i) as usize);\n\t\t\t\t\tif paddr.is_none() {\n\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t\tlet paddr = paddr.unwrap() as *mut Event;\n\t\t\t\t\t*paddr = ev.pop_front().unwrap();\n\t\t\t\t\t(*frame).regs[Registers::A0 as usize] += 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\tABS_EVENTS.replace(ev);\n\t\t}\n\t\t1024 => {\n\t\t\t// #define SYS_open 1024\n\t\t\tlet mut path = (*frame).regs[gp(Registers::A0)];\n\t\t\tlet _perm = (*frame).regs[gp(Registers::A1)];\n\t\t\tlet process = get_by_pid((*frame).pid as u16).as_mut().unwrap();\n\t\t\tif (*frame).satp >> 60 != 0 {\n\t\t\t\tlet table = process.mmu_table.as_mut().unwrap();\n\t\t\t\tlet paddr = virt_to_phys(table, path);\n\t\t\t\tif paddr.is_none() {\n\t\t\t\t\t(*frame).regs[gp(Registers::A0)] = -1isize as usize;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tpath = paddr.unwrap();\n\t\t\t}\n\t\t\tlet path_ptr = path as *const u8;\n\t\t\tlet mut str_path = String::new();\n\t\t\tfor i in 0..256 {\n\t\t\t\tlet c = path_ptr.add(i).read();\n\t\t\t\tif c == 0 {\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t\tstr_path.push(c as char);\n\t\t\t}\n\t\t\t// Allocate a blank file descriptor\n\t\t\tlet mut max_fd = 2;\n\t\t\tfor k in process.data.fdesc.keys() {\n\t\t\t\tif *k > max_fd {\n\t\t\t\t\tmax_fd = *k;\n\t\t\t\t}\n\t\t\t}\n\t\t\tmax_fd += 1;\n\t\t\tmatch str_path.as_str() {\n\t\t\t\t\"/dev/fb\" => {\n\t\t\t\t\t// framebuffer\n\t\t\t\t\tprocess.data.fdesc.insert(max_fd, Descriptor::Framebuffer);\n\t\t\t\t}\n\t\t\t\t\"/dev/butev\" => {\n\t\t\t\t\tprocess.data.fdesc.insert(max_fd, Descriptor::ButtonEvents);\n\t\t\t\t}\n\t\t\t\t\"/dev/absev\" => {\n\t\t\t\t\tprocess.data.fdesc.insert(max_fd, Descriptor::AbsoluteEvents);\n\t\t\t\t}\n\t\t\t\t_ => {\n\t\t\t\t\tlet res = fs::MinixFileSystem::open(8, &str_path);\n\t\t\t\t\tif res.is_err() {\n\t\t\t\t\t\t(*frame).regs[gp(Registers::A0)] = -1isize as usize;\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tlet inode = res.ok().unwrap();\n\t\t\t\t\t\tprocess.data.fdesc.insert(max_fd, Descriptor::File(inode));\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\t(*frame).regs[gp(Registers::A0)] = max_fd as usize;\n\t\t}\n\t\t1062 => {\n\t\t\t// gettime\n\t\t\t(*frame).regs[Registers::A0 as usize] = crate::cpu::get_mtime();\n\t\t}\n\t\t_ => {\n\t\t\tprintln!(\"Unknown syscall number {}\", syscall_number);\n\t\t}\n\t}\n}\n\nextern \"C\" {\n\tfn make_syscall(sysno: usize, arg0: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) -> usize;\n}\n\nfn do_make_syscall(sysno: usize, arg0: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) -> usize {\n\tunsafe { make_syscall(sysno, arg0, arg1, arg2, arg3, arg4, arg5) }\n}\n\npub fn syscall_yield() {\n\tlet _ = do_make_syscall(1, 0, 0, 0, 0, 0, 0);\n}\n\npub fn syscall_exit() {\n\tlet _ = do_make_syscall(93, 0, 0, 0, 0, 0, 0);\n}\n\npub fn syscall_execv(path: *const u8, argv: usize) -> usize {\n\tdo_make_syscall(11, path as usize, argv, 0, 0, 0, 0)\n}\n\npub fn syscall_fs_read(dev: usize, inode: u32, buffer: *mut u8, size: u32, offset: u32) -> usize {\n\tdo_make_syscall(63, dev, inode as usize, buffer as usize, size as usize, offset as usize, 0)\n}\n\npub fn syscall_block_read(dev: usize, buffer: *mut u8, size: u32, offset: u32) -> u8 {\n\tdo_make_syscall(180, dev, buffer as usize, size as usize, offset as usize, 0, 0) as u8\n}\n\npub fn syscall_sleep(duration: usize) {\n\tlet _ = do_make_syscall(10, duration, 0, 0, 0, 0, 0);\n}\n\npub fn syscall_get_pid() -> u16 {\n\tdo_make_syscall(172, 0, 0, 0, 0, 0, 0) as u16\n}\n\n/// This is a helper function ran as a process in kernel space\n/// to finish loading and executing a process.\nfn exec_func(args: usize) {\n\tunsafe {\n\t\t// We got the inode from the syscall. Its Box rid itself of control, so\n\t\t// we take control back here. The Box now owns the Inode and will complete\n\t\t// freeing the heap memory allocated for it.\n\t\tlet inode = Box::from_raw(args as *mut fs::Inode);\n\t\tlet mut buffer = Buffer::new(inode.size as usize);\n\t\t// This is why we need to be in a process context. The read() call may sleep as it\n\t\t// waits for the block driver to return.\n\t\tfs::MinixFileSystem::read(8, &inode, buffer.get_mut(), inode.size, 0);\n\t\t// Now we have the data, so the following will load the ELF file and give us a process.\n\t\tlet proc = elf::File::load_proc(&buffer);\n\t\tif proc.is_err() {\n\t\t\tprintln!(\"Failed to launch process.\");\n\t\t}\n\t\telse {\n\t\t\tlet process = proc.ok().unwrap();\n\t\t\t// If we hold this lock, we can still be preempted, but the scheduler will\n\t\t\t// return control to us. This required us to use try_lock in the scheduler.\n\t\t\tPROCESS_LIST_MUTEX.sleep_lock();\n\t\t\tif let Some(mut proc_list) = PROCESS_LIST.take() {\n\t\t\t\tproc_list.push_back(process);\n\t\t\t\tPROCESS_LIST.replace(proc_list);\n\t\t\t}\n\t\t\tPROCESS_LIST_MUTEX.unlock();\n\t\t}\n\t}\n}\n// These system call numbers come from libgloss so that we can use newlib\n// for our system calls.\n// Libgloss wants the system call number in A7 and arguments in A0..A6\n// #define SYS_dup 23\n// #define SYS_fcntl 25\n// #define SYS_faccessat 48\n// #define SYS_chdir 49\n// #define SYS_openat 56\n// #define SYS_getdents 61\n// #define SYS_lseek 62\n// #define SYS_read 63\n// #define SYS_pread 67\n// #define SYS_pwrite 68\n// #define SYS_fstatat 79\n\n// #define SYS_kill 129\n// #define SYS_rt_sigaction 134\n// #define SYS_times 153\n// #define SYS_uname 160\n// #define SYS_gettimeofday 169\n// #define SYS_getpid 172\n// #define SYS_getuid 174\n// #define SYS_geteuid 175\n// #define SYS_getgid 176\n// #define SYS_getegid 177\n// #define SYS_munmap 215\n// #define SYS_mremap 216\n// #define SYS_mmap 222\n// #define SYS_link 1025\n// #define SYS_unlink 1026\n// #define SYS_mkdir 1030\n// #define SYS_access 1033\n// #define SYS_stat 1038\n// #define SYS_lstat 1039\n// #define SYS_time 1062\n// #define SYS_getmainvars 2011\n"
  },
  {
    "path": "risc_v/src/test.rs",
    "content": "// test.rs\nuse crate::fs::MinixFileSystem;\nuse crate::syscall;\n/// Test block will load raw binaries into memory to execute them. This function\n/// will load ELF files and try to execute them.\npub fn test() {\n\t// The majority of the testing code needs to move into a system call (execv maybe?)\n\tMinixFileSystem::init(8);\n\tlet path = \"/shell\\0\".as_bytes().as_ptr();\n\tsyscall::syscall_execv(path,0);\n\tprintln!(\"I should never get here, execv should destroy our process.\");\n}\n\n"
  },
  {
    "path": "risc_v/src/trap.rs",
    "content": "// trap.rs\n// Trap routines\n// Stephen Marz\n// 10 October 2019\n\nuse crate::{cpu::{TrapFrame, CONTEXT_SWITCH_TIME},\n            plic,\n            process::delete_process,\n            rust_switch_to_user,\n            sched::schedule,\n            syscall::do_syscall};\n\n#[no_mangle]\n/// The m_trap stands for \"machine trap\". Right now, we are handling\n/// all traps at machine mode. In this mode, we can figure out what's\n/// going on and send a trap where it needs to be. Remember, in machine\n/// mode and in this trap, interrupts are disabled and the MMU is off.\nextern \"C\" fn m_trap(epc: usize,\n                     tval: usize,\n                     cause: usize,\n                     hart: usize,\n                     _status: usize,\n                     frame: *mut TrapFrame)\n                     -> usize\n{\n\t// We're going to handle all traps in machine mode. RISC-V lets\n\t// us delegate to supervisor mode, but switching out SATP (virtual memory)\n\t// gets hairy.\n\tlet is_async = {\n\t\tif cause >> 63 & 1 == 1 {\n\t\t\ttrue\n\t\t}\n\t\telse {\n\t\t\tfalse\n\t\t}\n\t};\n\t// The cause contains the type of trap (sync, async) as well as the cause\n\t// number. So, here we narrow down just the cause number.\n\tlet cause_num = cause & 0xfff;\n\tlet mut return_pc = epc;\n\tif is_async {\n\t\t// Asynchronous trap\n\t\tmatch cause_num {\n\t\t\t3 => {\n\t\t\t\t// We will use this to awaken our other CPUs so they can process\n\t\t\t\t// processes.\n\t\t\t\tprintln!(\"Machine software interrupt CPU #{}\", hart);\n\t\t\t}\n\t\t\t7 => {\n\t\t\t\t// This is the context-switch timer.\n\t\t\t\t// We would typically invoke the scheduler here to pick another\n\t\t\t\t// process to run.\n\t\t\t\t// Machine timer\n\t\t\t\tlet new_frame = schedule();\n\t\t\t\tschedule_next_context_switch(1);\n\t\t\t\tif new_frame != 0 {\n\t\t\t\t\trust_switch_to_user(new_frame);\n\t\t\t\t}\n\t\t\t}\n\t\t\t11 => {\n\t\t\t\t// Machine external (interrupt from Platform Interrupt Controller (PLIC))\n\t\t\t\t// println!(\"Machine external interrupt CPU#{}\", hart);\n\t\t\t\t// We will check the next interrupt. If the interrupt isn't available, this will\n\t\t\t\t// give us None. However, that would mean we got a spurious interrupt, unless we\n\t\t\t\t// get an interrupt from a non-PLIC source. This is the main reason that the PLIC\n\t\t\t\t// hardwires the id 0 to 0, so that we can use it as an error case.\n\t\t\t\tplic::handle_interrupt();\n\t\t\t}\n\t\t\t_ => {\n\t\t\t\tpanic!(\"Unhandled async trap CPU#{} -> {}\\n\", hart, cause_num);\n\t\t\t}\n\t\t}\n\t}\n\telse {\n\t\t// Synchronous trap\n\t\tmatch cause_num {\n\t\t\t2 => unsafe {\n\t\t\t\t// Illegal instruction\n\t\t\t\tprintln!(\"Illegal instruction CPU#{} -> 0x{:08x}: 0x{:08x}\\n\", hart, epc, tval);\n\t\t\t\t// We need while trues here until we have a functioning \"delete from scheduler\"\n\t\t\t\t// I use while true because Rust will warn us that it looks stupid.\n\t\t\t\t// This is what I want so that I remember to remove this and replace\n\t\t\t\t// them later.\n\t\t\t\tdelete_process((*frame).pid as u16);\n\t\t\t\tlet frame = schedule();\n\t\t\t\tschedule_next_context_switch(1);\n\t\t\t\trust_switch_to_user(frame);\n\t\t\t}\n\t\t\t3 => {\n\t\t\t\t// breakpoint\n\t\t\t\tprintln!(\"BKPT\\n\\n\");\n\t\t\t\treturn_pc += 2;\n\t\t\t}\n\t\t\t7 => unsafe {\n\t\t\t\tprintln!(\"Error with pid {}, at PC 0x{:08x}, mepc 0x{:08x}\", (*frame).pid, (*frame).pc, epc);\n\t\t\t\tdelete_process((*frame).pid as u16);\n\t\t\t\tlet frame = schedule();\n\t\t\t\tschedule_next_context_switch(1);\n\t\t\t\trust_switch_to_user(frame);\n\t\t\t}\n\t\t\t8 | 9 | 11 => unsafe {\n\t\t\t\t// Environment (system) call from User, Supervisor, and Machine modes\n\t\t\t\t// println!(\"E-call from User mode! CPU#{} -> 0x{:08x}\", hart, epc);\n\t\t\t\tdo_syscall(return_pc, frame);\n\t\t\t\tlet frame = schedule();\n\t\t\t\tschedule_next_context_switch(1);\n\t\t\t\trust_switch_to_user(frame);\n\t\t\t}\n\t\t\t// Page faults\n\t\t\t12 => unsafe {\n\t\t\t\t// Instruction page fault\n\t\t\t\tprintln!(\"Instruction page fault CPU#{} -> 0x{:08x}: 0x{:08x}\", hart, epc, tval);\n\t\t\t\tdelete_process((*frame).pid as u16);\n\t\t\t\tlet frame = schedule();\n\t\t\t\tschedule_next_context_switch(1);\n\t\t\t\trust_switch_to_user(frame);\n\t\t\t}\n\t\t\t13 => unsafe {\n\t\t\t\t// Load page fault\n\t\t\t\tprintln!(\"Load page fault CPU#{} -> 0x{:08x}: 0x{:08x}\", hart, epc, tval);\n\t\t\t\tdelete_process((*frame).pid as u16);\n\t\t\t\tlet frame = schedule();\n\t\t\t\tschedule_next_context_switch(1);\n\t\t\t\trust_switch_to_user(frame);\n\t\t\t}\n\t\t\t15 => unsafe {\n\t\t\t\t// Store page fault\n\t\t\t\tprintln!(\"Store page fault CPU#{} -> 0x{:08x}: 0x{:08x}\", hart, epc, tval);\n\t\t\t\tdelete_process((*frame).pid as u16);\n\t\t\t\tlet frame = schedule();\n\t\t\t\tschedule_next_context_switch(1);\n\t\t\t\trust_switch_to_user(frame);\n\t\t\t}\n\t\t\t_ => {\n\t\t\t\tpanic!(\n\t\t\t\t       \"Unhandled sync trap {}. CPU#{} -> 0x{:08x}: 0x{:08x}\\n\",\n\t\t\t\t       cause_num, hart, epc, tval\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t};\n\t// Finally, return the updated program counter\n\treturn_pc\n}\n\npub const MMIO_MTIMECMP: *mut u64 = 0x0200_4000usize as *mut u64;\npub const MMIO_MTIME: *const u64 = 0x0200_BFF8 as *const u64;\n\npub fn schedule_next_context_switch(qm: u16) {\n\tunsafe {\n\t\tMMIO_MTIMECMP.write_volatile(MMIO_MTIME.read_volatile().wrapping_add(CONTEXT_SWITCH_TIME * qm as u64));\n\t}\n}\n"
  },
  {
    "path": "risc_v/src/uart.rs",
    "content": "// uart.rs\n// UART routines and driver\n\nuse core::{convert::TryInto,\n\t\t   fmt::{Error, Write}};\nuse crate::console::push_stdin;\n\npub struct Uart {\n\tbase_address: usize,\n}\n\nimpl Write for Uart {\n\tfn write_str(&mut self, out: &str) -> Result<(), Error> {\n\t\tfor c in out.bytes() {\n\t\t\tself.put(c);\n\t\t}\n\t\tOk(())\n\t}\n}\n\nimpl Uart {\n\tpub fn new(base_address: usize) -> Self {\n\t\tUart { base_address }\n\t}\n\n\tpub fn init(&mut self) {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\t// First, set the word length, which\n\t\t\t// are bits 0 and 1 of the line control register (LCR)\n\t\t\t// which is at base_address + 3\n\t\t\t// We can easily write the value 3 here or 0b11, but I'm\n\t\t\t// extending it so that it is clear we're setting two\n\t\t\t// individual fields\n\t\t\t//             Word 0     Word 1\n\t\t\t//             ~~~~~~     ~~~~~~\n\t\t\tlet lcr: u8 = (1 << 0) | (1 << 1);\n\t\t\tptr.add(3).write_volatile(lcr);\n\n\t\t\t// Now, enable the FIFO, which is bit index 0 of the\n\t\t\t// FIFO control register (FCR at offset 2).\n\t\t\t// Again, we can just write 1 here, but when we use left\n\t\t\t// shift, it's easier to see that we're trying to write\n\t\t\t// bit index #0.\n\t\t\tptr.add(2).write_volatile(1 << 0);\n\n\t\t\t// Enable receiver buffer interrupts, which is at bit\n\t\t\t// index 0 of the interrupt enable register (IER at\n\t\t\t// offset 1).\n\t\t\tptr.add(1).write_volatile(1 << 0);\n\n\t\t\t// If we cared about the divisor, the code below would\n\t\t\t// set the divisor from a global clock rate of 22.729\n\t\t\t// MHz (22,729,000 cycles per second) to a signaling\n\t\t\t// rate of 2400 (BAUD). We usually have much faster\n\t\t\t// signalling rates nowadays, but this demonstrates what\n\t\t\t// the divisor actually does. The formula given in the\n\t\t\t// NS16500A specification for calculating the divisor\n\t\t\t// is:\n\t\t\t// divisor = ceil( (clock_hz) / (baud_sps x 16) )\n\t\t\t// So, we substitute our values and get:\n\t\t\t// divisor = ceil( 22_729_000 / (2400 x 16) )\n\t\t\t// divisor = ceil( 22_729_000 / 38_400 )\n\t\t\t// divisor = ceil( 591.901 ) = 592\n\n\t\t\t// The divisor register is two bytes (16 bits), so we\n\t\t\t// need to split the value 592 into two bytes.\n\t\t\t// Typically, we would calculate this based on measuring\n\t\t\t// the clock rate, but again, for our purposes [qemu],\n\t\t\t// this doesn't really do anything.\n\t\t\tlet divisor: u16 = 592;\n\t\t\tlet divisor_least: u8 =\n\t\t\t\t(divisor & 0xff).try_into().unwrap();\n\t\t\tlet divisor_most: u8 =\n\t\t\t\t(divisor >> 8).try_into().unwrap();\n\n\t\t\t// Notice that the divisor register DLL (divisor latch\n\t\t\t// least) and DLM (divisor latch most) have the same\n\t\t\t// base address as the receiver/transmitter and the\n\t\t\t// interrupt enable register. To change what the base\n\t\t\t// address points to, we open the \"divisor latch\" by\n\t\t\t// writing 1 into the Divisor Latch Access Bit (DLAB),\n\t\t\t// which is bit index 7 of the Line Control Register\n\t\t\t// (LCR) which is at base_address + 3.\n\t\t\tptr.add(3).write_volatile(lcr | 1 << 7);\n\n\t\t\t// Now, base addresses 0 and 1 point to DLL and DLM,\n\t\t\t// respectively. Put the lower 8 bits of the divisor\n\t\t\t// into DLL\n\t\t\tptr.add(0).write_volatile(divisor_least);\n\t\t\tptr.add(1).write_volatile(divisor_most);\n\n\t\t\t// Now that we've written the divisor, we never have to\n\t\t\t// touch this again. In hardware, this will divide the\n\t\t\t// global clock (22.729 MHz) into one suitable for 2,400\n\t\t\t// signals per second. So, to once again get access to\n\t\t\t// the RBR/THR/IER registers, we need to close the DLAB\n\t\t\t// bit by clearing it to 0.\n\t\t\tptr.add(3).write_volatile(lcr);\n\t\t}\n\t}\n\n\tpub fn put(&mut self, c: u8) {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\tptr.add(0).write_volatile(c);\n\t\t}\n\t}\n\n\tpub fn get(&mut self) -> Option<u8> {\n\t\tlet ptr = self.base_address as *mut u8;\n\t\tunsafe {\n\t\t\tif ptr.add(5).read_volatile() & 1 == 0 {\n\t\t\t\t// The DR bit is 0, meaning no data\n\t\t\t\tNone\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// The DR bit is 1, meaning data!\n\t\t\t\tSome(ptr.add(0).read_volatile())\n\t\t\t}\n\t\t}\n\t}\n}\n\npub fn handle_interrupt() {\n\t// We would typically set this to be handled out of the interrupt context,\n\t// but we're testing here! C'mon!\n\t// We haven't yet used the singleton pattern for my_uart, but remember, this\n\t// just simply wraps 0x1000_0000 (UART).\n\tlet mut my_uart = Uart::new(0x1000_0000);\n\t// If we get here, the UART better have something! If not, what happened??\n\tif let Some(c) = my_uart.get() {\n\t\t// If you recognize this code, it used to be in the lib.rs under kmain(). That\n\t\t// was because we needed to poll for UART data. Now that we have interrupts,\n\t\t// here it goes!\n\t\tpush_stdin(c);\n\t\tmatch c {\n\t\t\t8 => {\n\t\t\t\t// This is a backspace, so we\n\t\t\t\t// essentially have to write a space and\n\t\t\t\t// backup again:\n\t\t\t\tprint!(\"{} {}\", 8 as char, 8 as char);\n\t\t\t},\n\t\t\t10 | 13 => {\n\t\t\t\t// Newline or carriage-return\n\t\t\t\tprintln!();\n\t\t\t},\n\t\t\t_ => {\n\t\t\t\tprint!(\"{}\", c as char);\n\t\t\t},\n\t\t}\t\n\t}\n}\n"
  },
  {
    "path": "risc_v/src/vfs.rs",
    "content": "// vfs.rs\n// Virtual File System\n// Stephen Marz\n// 4 June 2020\n\n"
  },
  {
    "path": "risc_v/src/virtio.rs",
    "content": "// virtio.rs\n// VirtIO routines for the VirtIO protocol\n// Stephen Marz\n// 10 March 2020\n\nuse crate::{block, block::setup_block_device, page::PAGE_SIZE};\nuse crate::rng::setup_entropy_device;\nuse crate::{gpu, gpu::setup_gpu_device};\nuse crate::{input, input::setup_input_device};\nuse core::mem::size_of;\n\n// Flags\n// Descriptor flags have VIRTIO_DESC_F as a prefix\n// Available flags have VIRTIO_AVAIL_F\n\npub const VIRTIO_F_RING_INDIRECT_DESC: u32 = 28;\npub const VIRTIO_F_RING_EVENT_IDX: u32 = 29;\npub const VIRTIO_F_VERSION_1: u32 = 32;\n\npub const VIRTIO_DESC_F_NEXT: u16 = 1;\npub const VIRTIO_DESC_F_WRITE: u16 = 2;\npub const VIRTIO_DESC_F_INDIRECT: u16 = 4;\n\npub const VIRTIO_AVAIL_F_NO_INTERRUPT: u16 = 1;\n\npub const VIRTIO_USED_F_NO_NOTIFY: u16 = 1;\n\n// According to the documentation, this must be a power\n// of 2 for the new style. So, I'm changing this to use\n// 1 << instead because that will enforce this standard.\npub const VIRTIO_RING_SIZE: usize = 1 << 7;\n\n// VirtIO structures\n\n// The descriptor holds the data that we need to send to \n// the device. The address is a physical address and NOT\n// a virtual address. The len is in bytes and the flags are\n// specified above. Any descriptor can be chained, hence the\n// next field, but only if the F_NEXT flag is specified.\n#[repr(C)]\npub struct Descriptor {\n\tpub addr:  u64,\n\tpub len:   u32,\n\tpub flags: u16,\n\tpub next:  u16,\n}\n\n#[repr(C)]\npub struct Available {\n\tpub flags: u16,\n\tpub idx:   u16,\n\tpub ring:  [u16; VIRTIO_RING_SIZE],\n\tpub event: u16,\n}\n\n#[repr(C)]\npub struct UsedElem {\n\tpub id:  u32,\n\tpub len: u32,\n}\n\n#[repr(C)]\npub struct Used {\n\tpub flags: u16,\n\tpub idx:   u16,\n\tpub ring:  [UsedElem; VIRTIO_RING_SIZE],\n\tpub event: u16,\n}\n\n#[repr(C)]\npub struct Queue {\n\tpub desc:  [Descriptor; VIRTIO_RING_SIZE],\n\tpub avail: Available,\n\t// Calculating padding, we need the used ring to start on a page boundary. We take the page size, subtract the\n\t// amount the descriptor ring takes then subtract the available structure and ring.\n\tpub padding0: [u8; PAGE_SIZE - size_of::<Descriptor>() * VIRTIO_RING_SIZE - size_of::<Available>()],\n\tpub used:     Used,\n}\n\n// The MMIO transport is \"legacy\" in QEMU, so these registers represent\n// the legacy interface.\n#[repr(usize)]\npub enum MmioOffsets {\n\tMagicValue = 0x000,\n\tVersion = 0x004,\n\tDeviceId = 0x008,\n\tVendorId = 0x00c,\n\tHostFeatures = 0x010,\n\tHostFeaturesSel = 0x014,\n\tGuestFeatures = 0x020,\n\tGuestFeaturesSel = 0x024,\n\tGuestPageSize = 0x028,\n\tQueueSel = 0x030,\n\tQueueNumMax = 0x034,\n\tQueueNum = 0x038,\n\tQueueAlign = 0x03c,\n\tQueuePfn = 0x040,\n\tQueueNotify = 0x050,\n\tInterruptStatus = 0x060,\n\tInterruptAck = 0x064,\n\tStatus = 0x070,\n\tConfig = 0x100,\n}\n\n// This currently isn't used, but if anyone wants to try their hand at putting a structure\n// to the MMIO address space, you can use the following. Remember that this is volatile!\n#[repr(C)]\npub struct MmioDevice {\n\tmagic_value: u32,\n\tversion: u32,\n\tdevice_id: u32,\n\tvendor_id: u32,\n\thost_features: u32,\n\thost_features_sel: u32,\n\trsv1: [u8; 8],\n\tguest_features: u32,\n\tguest_features_sel: u32,\n\tguest_page_size: u32,\n\trsv2: [u8; 4],\n\tqueue_sel: u32,\n\tqueue_num_max: u32,\n\tqueue_num: u32,\n\tqueue_align: u32,\n\tqueue_pfn: u64,\n\trsv3: [u8; 8],\n\tqueue_notify: u32,\n\trsv4: [u8; 12],\n\tinterrupt_status: u32,\n\tinterrupt_ack: u32,\n\trsv5: [u8; 8],\n\tstatus: u32,\n\t//rsv6: [u8; 140],\n\t//uint32_t config[1];\n\t// The config space starts at 0x100, but it is device dependent.\n}\n\n#[repr(usize)]\npub enum DeviceTypes {\n\tNone = 0,\n\tNetwork = 1,\n\tBlock = 2,\n\tConsole = 3,\n\tEntropy = 4,\n\tGpu = 16,\n\tInput = 18,\n\tMemory = 24,\n}\n\n// Enumerations in Rust aren't easy to convert back\n// and forth. Furthermore, we're going to use a u32\n// pointer, so we need to \"undo\" the scaling that\n// Rust will do with the .add() function.\nimpl MmioOffsets {\n\tpub fn val(self) -> usize {\n\t\tself as usize\n\t}\n\n\tpub fn scaled(self, scale: usize) -> usize {\n\t\tself.val() / scale\n\t}\n\n\tpub fn scale32(self) -> usize {\n\t\tself.scaled(4)\n\t}\n\n}\n\npub enum StatusField {\n\tAcknowledge = 1,\n\tDriver = 2,\n\tFailed = 128,\n\tFeaturesOk = 8,\n\tDriverOk = 4,\n\tDeviceNeedsReset = 64,\n}\n\n// The status field will be compared to the status register. So,\n// I've made some helper functions to checking that register easier.\nimpl StatusField {\n\tpub fn val(self) -> usize {\n\t\tself as usize\n\t}\n\n\tpub fn val32(self) -> u32 {\n\t\tself as u32\n\t}\n\n\tpub fn test(sf: u32, bit: StatusField) -> bool {\n\t\tsf & bit.val32() != 0\n\t}\n\n\tpub fn is_failed(sf: u32) -> bool {\n\t\tStatusField::test(sf, StatusField::Failed)\n\t}\n\n\tpub fn needs_reset(sf: u32) -> bool {\n\t\tStatusField::test(sf, StatusField::DeviceNeedsReset)\n\t}\n\n\tpub fn driver_ok(sf: u32) -> bool {\n\t\tStatusField::test(sf, StatusField::DriverOk)\n\t}\n\n\tpub fn features_ok(sf: u32) -> bool {\n\t\tStatusField::test(sf, StatusField::FeaturesOk)\n\t}\n}\n\n// We probably shouldn't put these here, but it'll help\n// with probing the bus, etc. These are architecture specific\n// which is why I say that.\npub const MMIO_VIRTIO_START: usize = 0x1000_1000;\npub const MMIO_VIRTIO_END: usize = 0x1000_8000;\npub const MMIO_VIRTIO_STRIDE: usize = 0x1000;\npub const MMIO_VIRTIO_MAGIC: u32 = 0x74_72_69_76;\n\n// The VirtioDevice is essentially a structure we can put into an array\n// to determine what virtio devices are attached to the system. Right now,\n// we're using the 1..=8  linearity of the VirtIO devices on QEMU to help\n// with reducing the data structure itself. Otherwise, we might be forced\n// to use an MMIO pointer.\npub struct VirtioDevice {\n\tpub devtype: DeviceTypes,\n}\n\nimpl VirtioDevice {\n\tpub const fn new() -> Self {\n\t\tVirtioDevice { devtype: DeviceTypes::None, }\n\t}\n\n\tpub const fn new_with(devtype: DeviceTypes) -> Self {\n\t\tVirtioDevice { devtype }\n\t}\n}\n\nstatic mut VIRTIO_DEVICES: [Option<VirtioDevice>; 8] = [None, None, None, None, None, None, None, None];\n\n/// Probe the VirtIO bus for devices that might be\n/// out there.\npub fn probe() {\n\t// Rust's for loop uses an Iterator object, which now has a step_by\n\t// modifier to change how much it steps. Also recall that ..= means up\n\t// to AND including MMIO_VIRTIO_END.\n\tfor addr in (MMIO_VIRTIO_START..=MMIO_VIRTIO_END).step_by(MMIO_VIRTIO_STRIDE) {\n\t\tprint!(\"Virtio probing 0x{:08x}...\", addr);\n\t\tlet magicvalue;\n\t\tlet deviceid;\n\t\tlet ptr = addr as *mut u32;\n\t\tunsafe {\n\t\t\tmagicvalue = ptr.read_volatile();\n\t\t\tdeviceid = ptr.add(2).read_volatile();\n\t\t}\n\t\t// 0x74_72_69_76 is \"virt\" in little endian, so in reality\n\t\t// it is triv. All VirtIO devices have this attached to the\n\t\t// MagicValue register (offset 0x000)\n\t\tif MMIO_VIRTIO_MAGIC != magicvalue {\n\t\t\tprintln!(\"not virtio.\");\n\t\t}\n\t\t// If we are a virtio device, we now need to see if anything\n\t\t// is actually attached to it. The DeviceID register will\n\t\t// contain what type of device this is. If this value is 0,\n\t\t// then it is not connected.\n\t\telse if 0 == deviceid {\n\t\t\tprintln!(\"not connected.\");\n\t\t}\n\t\t// If we get here, we have a connected virtio device. Now we have\n\t\t// to figure out what kind it is so we can do device-specific setup.\n\t\telse {\n\t\t\tmatch deviceid {\n\t\t\t\t// DeviceID 1 is a network device\n\t\t\t\t1 => {\n\t\t\t\t\tprint!(\"network device...\");\n\t\t\t\t\tif false == setup_network_device(ptr) {\n\t\t\t\t\t\tprintln!(\"setup failed.\");\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tprintln!(\"setup succeeded!\");\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t// DeviceID 2 is a block device\n\t\t\t\t2 => {\n\t\t\t\t\tprint!(\"block device...\");\n\t\t\t\t\tif false == setup_block_device(ptr) {\n\t\t\t\t\t\tprintln!(\"setup failed.\");\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tlet idx = (addr - MMIO_VIRTIO_START) >> 12;\n\t\t\t\t\t\tunsafe {\n\t\t\t\t\t\t\tVIRTIO_DEVICES[idx] =\n\t\t\t\t\t\t\t\tSome(VirtioDevice::new_with(DeviceTypes::Block));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tprintln!(\"setup succeeded!\");\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t// DeviceID 4 is a random number generator device\n\t\t\t\t4 => {\n\t\t\t\t\tprint!(\"entropy device...\");\n\t\t\t\t\tif false == setup_entropy_device(ptr) {\n\t\t\t\t\t\tprintln!(\"setup failed.\");\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tprintln!(\"setup succeeded!\");\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t// DeviceID 16 is a GPU device\n\t\t\t\t16 => {\n\t\t\t\t\tprint!(\"GPU device...\");\n\t\t\t\t\tif false == setup_gpu_device(ptr) {\n\t\t\t\t\t\tprintln!(\"setup failed.\");\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tlet idx = (addr - MMIO_VIRTIO_START) >> 12;\n\t\t\t\t\t\tunsafe {\n\t\t\t\t\t\t\tVIRTIO_DEVICES[idx] =\n\t\t\t\t\t\t\t\tSome(VirtioDevice::new_with(DeviceTypes::Gpu));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tprintln!(\"setup succeeded!\");\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t// DeviceID 18 is an input device\n\t\t\t\t18 => {\n\t\t\t\t\tprint!(\"input device...\");\n\t\t\t\t\tif false == setup_input_device(ptr) {\n\t\t\t\t\t\tprintln!(\"setup failed.\");\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tlet idx = (addr - MMIO_VIRTIO_START) >> 12;\n\t\t\t\t\t\tunsafe {\n\t\t\t\t\t\t\tVIRTIO_DEVICES[idx] =\n\t\t\t\t\t\t\t\tSome(VirtioDevice::new_with(DeviceTypes::Input));\n\t\t\t\t\t\t}\n\t\t\t\t\t\tprintln!(\"setup succeeded!\");\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\t_ => println!(\"unknown device type.\"),\n\t\t\t}\n\t\t}\n\t}\n}\n\npub fn setup_network_device(_ptr: *mut u32) -> bool {\n\tfalse\n}\n\n// The External pin (PLIC) trap will lead us here if it is\n// determined that interrupts 1..=8 are what caused the interrupt.\n// In here, we try to figure out where to direct the interrupt\n// and then handle it.\npub fn handle_interrupt(interrupt: u32) {\n\tlet idx = interrupt as usize - 1;\n\tunsafe {\n\t\tif let Some(vd) = &VIRTIO_DEVICES[idx] {\n\t\t\tmatch vd.devtype {\n\t\t\t\tDeviceTypes::Block => {\n\t\t\t\t\tblock::handle_interrupt(idx);\n\t\t\t\t},\n\t\t\t\tDeviceTypes::Gpu => {\n\t\t\t\t\tgpu::handle_interrupt(idx);\n\t\t\t\t},\n\t\t\t\tDeviceTypes::Input => {\n\t\t\t\t\tinput::handle_interrupt(idx);\n\t\t\t\t},\n\t\t\t\t_ => {\n\t\t\t\t\tprintln!(\"Invalid device generated interrupt!\");\n\t\t\t\t},\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tprintln!(\"Spurious interrupt {}\", interrupt);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "risc_v/userspace/.gitignore",
    "content": "helloworld\nhelloworld.elf\nsleepy\nsleepy.elf\nshell\nshell.elf\nfb\nfb.elf\n"
  },
  {
    "path": "risc_v/userspace/Makefile",
    "content": "CROSS=riscv64-unknown-elf-\nCXX=g++\nCXXFLAGS=-Wall -O3 -static -I.\nSOURCES=$(wildcard *.cpp)\nOUT=$(patsubst %.cpp,%,$(SOURCES))\n\nall: $(OUT)\n\n\n%: %.cpp Makefile\n\t$(CROSS)$(CXX) $(CXXFLAGS) -o $@ $<\n\n\nclean:\n\trm -f $(OUT)\n"
  },
  {
    "path": "risc_v/userspace/fb.cpp",
    "content": "#include <sys/stat.h>\n#include <fcntl.h>\n#include <unistd.h>\n#include <cmath>\n#include <cstdio>\n#include <input-event-codes.h>\n\n\n#define MAX_EVENTS 100\n#define min(x, y) ((x < y) ? x : y)\n#define max(x, y) ((x > y) ? x : y)\n\nusing u8 = unsigned char;\nusing i8 = signed char;\nusing u16 = unsigned short;\nusing i16 = signed short;\nusing u32 = unsigned int;\nusing i32 = signed int;\nusing u64 = unsigned long;\nusing i64 = signed long;\nusing f64 = double;\nusing f32 = float;\n\nstruct Pixel {\n\tu8 r;\n\tu8 g;\n\tu8 b;\n\tu8 a;\n};\n\nstruct Event {\n\tu16 event_type;\n\tu16 code;\n\tu32 value;\n};\n\nvoid fill_rect(Pixel *fb, u32 x, u32 y, u32 width, u32 height, Pixel &color);\nvoid stroke_rect(Pixel *fb, u32 x, u32 y, u32 width, u32 height, Pixel &color, u32 size);\nvoid set_pixel(Pixel *fb, u32 x, u32 y, Pixel &color);\nvoid draw_cosine(Pixel *fb, u32 x, u32 y, u32 width, u32 height, Pixel &color);\nvoid draw_circle(Pixel *fb, u32 x, u32 y, f64 r, Pixel &color);\n\nconst u64 noevt_slptm = 10000;\nconst u64 evt_slptm   = 10000;\n\n#define FB_DEV \"/dev/fb\"\n#define BUT_DEV \"/dev/butev\"\n#define ABS_DEV \"/dev/absev\"\n\nstruct Rect {\n\tu32 x;\n\tu32 y;\n\tu32 width;\n\tu32 height;\n};\n\nconstexpr u32 lerp(u32 val, u32 mx1, u32 mx2) {\n\tf64 r = val / static_cast<f64>(mx1);\n\treturn r * mx2;\n}\n\nint main()\n{\n\tEvent *events = new Event[100];\n\tbool pressed = false;\n\tint fb = open(FB_DEV, O_RDWR);\n\tint but = open(BUT_DEV, O_RDONLY);\n\tint abs = open(ABS_DEV, O_RDONLY);\n\tif (fb < 0) {\n\t\tprintf(\"Unable to open framebuffer %s.\\n\", FB_DEV);\n\t\treturn -1;\n\t}\n\tif (but < 0) {\n\t\tprintf(\"Unable to open button events %s.\\n\", BUT_DEV);\n\t\treturn -1;\n\t}\n\tif (abs < 0) {\n\t\tprintf(\"Unable to open absolute events %s.\\n\", ABS_DEV);\n\t\treturn -1;\n\n\t}\n\tclose(fb);\n\tclose(but);\n\tclose(abs);\n\tdelete [] events;\n\treturn 0;\n}\n\nvoid set_pixel(Pixel *fb, u32 x, u32 y, Pixel &color) {\n\tif (x < 640 && y < 480) {\n\t\tfb[y * 640 + x] = color;\n\t}\n}\n\nvoid fill_rect(Pixel *fb, u32 x, u32 y, u32 width, u32 height, Pixel &color) {\n\tfor (u32 row = y; row < (y+height);row++) {\n\t\tfor (u32 col = x; col < (x+width);col++) {\n\t\t\tset_pixel(fb, col, row, color);\n\t\t}\n\t}\n}\n\nvoid stroke_rect(Pixel *fb, u32 x, u32 y, u32 width, u32 height, Pixel &color, u32 size) {\n   // Essentially fill the four sides.\n   // Top\n   fill_rect(fb, x, y, width, size, color);\n   // Bottom\n   fill_rect(fb, x, y + height, width, size, color);\n   // Left\n   fill_rect(fb, x, y, size, height, color);\n   // Right\n   fill_rect(fb, x + width, y, size, height + size, color);\n}\n\nvoid draw_cosine(Pixel *fb, u32 x, u32 y, u32 width, u32 height, Pixel &color) {\n\tfor (u32 i = 1; i <= width;i++) {\n\t\tf64 fy = -cos(i % 360);\n\t\tf64 yy = fy / 2.0 * height;\n\t\tu32 nx = x + i;\n\t\tu32 ny = yy + y;\n\t\t// printf(\"Cos %u = %lf, x: %u, y: %u\\n\", (i % 360), fy, nx, ny);\n\t\tfill_rect(fb, nx, ny, 2, 2, color);\n\t}\n}\n\nvoid draw_circle(Pixel *fb, u32 x, u32 y, f64 r, Pixel &color)\n{\n\n}\n\n"
  },
  {
    "path": "risc_v/userspace/helloworld.cpp",
    "content": "#include <cstdio>\n#include <cmath>\n\nconst int SIZE = 100000;\ndouble myarray[SIZE];\nint another_array[5] = {1, 2, 3, 4, 5};\n\nint main()\n{\n\tprintf(\"I'm a C++ program, and I'm running in user space. How about a big, Hello World\\n\");\n\tprintf(\"My array is at 0x%p\\n\", myarray);\n\tprintf(\"I'm going to start crunching some numbers, so gimme a minute.\\n\");\n\tfor (int i = 0;i < SIZE;i++) {\n\t\tmyarray[i] = another_array[i % 5];\n\t}\n\tfor (int i = 0;i < SIZE;i++) {\n\t\tmyarray[i % SIZE] += cos(i);\n\t}\n\tprintf(\"Ok, I'm done crunching. Wanna see myarray[0]? It's %lf\\n\", myarray[0]);\n\treturn 0;\n}\n"
  },
  {
    "path": "risc_v/userspace/input-event-codes.h",
    "content": "/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */\n/*\n * Input event codes\n *\n *    *** IMPORTANT ***\n * This file is not only included from C-code but also from devicetree source\n * files. As such this file MUST only contain comments and defines.\n *\n * Copyright (c) 1999-2002 Vojtech Pavlik\n * Copyright (c) 2015 Hans de Goede <hdegoede@redhat.com>\n *\n * This program is free software; you can redistribute it and/or modify it\n * under the terms of the GNU General Public License version 2 as published by\n * the Free Software Foundation.\n */\n#ifndef _UAPI_INPUT_EVENT_CODES_H\n#define _UAPI_INPUT_EVENT_CODES_H\n\n/*\n * Device properties and quirks\n */\n\n#define INPUT_PROP_POINTER\t\t0x00\t/* needs a pointer */\n#define INPUT_PROP_DIRECT\t\t0x01\t/* direct input devices */\n#define INPUT_PROP_BUTTONPAD\t\t0x02\t/* has button(s) under pad */\n#define INPUT_PROP_SEMI_MT\t\t0x03\t/* touch rectangle only */\n#define INPUT_PROP_TOPBUTTONPAD\t\t0x04\t/* softbuttons at top of pad */\n#define INPUT_PROP_POINTING_STICK\t0x05\t/* is a pointing stick */\n#define INPUT_PROP_ACCELEROMETER\t0x06\t/* has accelerometer */\n\n#define INPUT_PROP_MAX\t\t\t0x1f\n#define INPUT_PROP_CNT\t\t\t(INPUT_PROP_MAX + 1)\n\n/*\n * Event types\n */\n\n#define EV_SYN\t\t\t0x00\n#define EV_KEY\t\t\t0x01\n#define EV_REL\t\t\t0x02\n#define EV_ABS\t\t\t0x03\n#define EV_MSC\t\t\t0x04\n#define EV_SW\t\t\t0x05\n#define EV_LED\t\t\t0x11\n#define EV_SND\t\t\t0x12\n#define EV_REP\t\t\t0x14\n#define EV_FF\t\t\t0x15\n#define EV_PWR\t\t\t0x16\n#define EV_FF_STATUS\t\t0x17\n#define EV_MAX\t\t\t0x1f\n#define EV_CNT\t\t\t(EV_MAX+1)\n\n/*\n * Synchronization events.\n */\n\n#define SYN_REPORT\t\t0\n#define SYN_CONFIG\t\t1\n#define SYN_MT_REPORT\t\t2\n#define SYN_DROPPED\t\t3\n#define SYN_MAX\t\t\t0xf\n#define SYN_CNT\t\t\t(SYN_MAX+1)\n\n/*\n * Keys and buttons\n *\n * Most of the keys/buttons are modeled after USB HUT 1.12\n * (see http://www.usb.org/developers/hidpage).\n * Abbreviations in the comments:\n * AC - Application Control\n * AL - Application Launch Button\n * SC - System Control\n */\n\n#define KEY_RESERVED\t\t0\n#define KEY_ESC\t\t\t1\n#define KEY_1\t\t\t2\n#define KEY_2\t\t\t3\n#define KEY_3\t\t\t4\n#define KEY_4\t\t\t5\n#define KEY_5\t\t\t6\n#define KEY_6\t\t\t7\n#define KEY_7\t\t\t8\n#define KEY_8\t\t\t9\n#define KEY_9\t\t\t10\n#define KEY_0\t\t\t11\n#define KEY_MINUS\t\t12\n#define KEY_EQUAL\t\t13\n#define KEY_BACKSPACE\t\t14\n#define KEY_TAB\t\t\t15\n#define KEY_Q\t\t\t16\n#define KEY_W\t\t\t17\n#define KEY_E\t\t\t18\n#define KEY_R\t\t\t19\n#define KEY_T\t\t\t20\n#define KEY_Y\t\t\t21\n#define KEY_U\t\t\t22\n#define KEY_I\t\t\t23\n#define KEY_O\t\t\t24\n#define KEY_P\t\t\t25\n#define KEY_LEFTBRACE\t\t26\n#define KEY_RIGHTBRACE\t\t27\n#define KEY_ENTER\t\t28\n#define KEY_LEFTCTRL\t\t29\n#define KEY_A\t\t\t30\n#define KEY_S\t\t\t31\n#define KEY_D\t\t\t32\n#define KEY_F\t\t\t33\n#define KEY_G\t\t\t34\n#define KEY_H\t\t\t35\n#define KEY_J\t\t\t36\n#define KEY_K\t\t\t37\n#define KEY_L\t\t\t38\n#define KEY_SEMICOLON\t\t39\n#define KEY_APOSTROPHE\t\t40\n#define KEY_GRAVE\t\t41\n#define KEY_LEFTSHIFT\t\t42\n#define KEY_BACKSLASH\t\t43\n#define KEY_Z\t\t\t44\n#define KEY_X\t\t\t45\n#define KEY_C\t\t\t46\n#define KEY_V\t\t\t47\n#define KEY_B\t\t\t48\n#define KEY_N\t\t\t49\n#define KEY_M\t\t\t50\n#define KEY_COMMA\t\t51\n#define KEY_DOT\t\t\t52\n#define KEY_SLASH\t\t53\n#define KEY_RIGHTSHIFT\t\t54\n#define KEY_KPASTERISK\t\t55\n#define KEY_LEFTALT\t\t56\n#define KEY_SPACE\t\t57\n#define KEY_CAPSLOCK\t\t58\n#define KEY_F1\t\t\t59\n#define KEY_F2\t\t\t60\n#define KEY_F3\t\t\t61\n#define KEY_F4\t\t\t62\n#define KEY_F5\t\t\t63\n#define KEY_F6\t\t\t64\n#define KEY_F7\t\t\t65\n#define KEY_F8\t\t\t66\n#define KEY_F9\t\t\t67\n#define KEY_F10\t\t\t68\n#define KEY_NUMLOCK\t\t69\n#define KEY_SCROLLLOCK\t\t70\n#define KEY_KP7\t\t\t71\n#define KEY_KP8\t\t\t72\n#define KEY_KP9\t\t\t73\n#define KEY_KPMINUS\t\t74\n#define KEY_KP4\t\t\t75\n#define KEY_KP5\t\t\t76\n#define KEY_KP6\t\t\t77\n#define KEY_KPPLUS\t\t78\n#define KEY_KP1\t\t\t79\n#define KEY_KP2\t\t\t80\n#define KEY_KP3\t\t\t81\n#define KEY_KP0\t\t\t82\n#define KEY_KPDOT\t\t83\n\n#define KEY_ZENKAKUHANKAKU\t85\n#define KEY_102ND\t\t86\n#define KEY_F11\t\t\t87\n#define KEY_F12\t\t\t88\n#define KEY_RO\t\t\t89\n#define KEY_KATAKANA\t\t90\n#define KEY_HIRAGANA\t\t91\n#define KEY_HENKAN\t\t92\n#define KEY_KATAKANAHIRAGANA\t93\n#define KEY_MUHENKAN\t\t94\n#define KEY_KPJPCOMMA\t\t95\n#define KEY_KPENTER\t\t96\n#define KEY_RIGHTCTRL\t\t97\n#define KEY_KPSLASH\t\t98\n#define KEY_SYSRQ\t\t99\n#define KEY_RIGHTALT\t\t100\n#define KEY_LINEFEED\t\t101\n#define KEY_HOME\t\t102\n#define KEY_UP\t\t\t103\n#define KEY_PAGEUP\t\t104\n#define KEY_LEFT\t\t105\n#define KEY_RIGHT\t\t106\n#define KEY_END\t\t\t107\n#define KEY_DOWN\t\t108\n#define KEY_PAGEDOWN\t\t109\n#define KEY_INSERT\t\t110\n#define KEY_DELETE\t\t111\n#define KEY_MACRO\t\t112\n#define KEY_MUTE\t\t113\n#define KEY_VOLUMEDOWN\t\t114\n#define KEY_VOLUMEUP\t\t115\n#define KEY_POWER\t\t116\t/* SC System Power Down */\n#define KEY_KPEQUAL\t\t117\n#define KEY_KPPLUSMINUS\t\t118\n#define KEY_PAUSE\t\t119\n#define KEY_SCALE\t\t120\t/* AL Compiz Scale (Expose) */\n\n#define KEY_KPCOMMA\t\t121\n#define KEY_HANGEUL\t\t122\n#define KEY_HANGUEL\t\tKEY_HANGEUL\n#define KEY_HANJA\t\t123\n#define KEY_YEN\t\t\t124\n#define KEY_LEFTMETA\t\t125\n#define KEY_RIGHTMETA\t\t126\n#define KEY_COMPOSE\t\t127\n\n#define KEY_STOP\t\t128\t/* AC Stop */\n#define KEY_AGAIN\t\t129\n#define KEY_PROPS\t\t130\t/* AC Properties */\n#define KEY_UNDO\t\t131\t/* AC Undo */\n#define KEY_FRONT\t\t132\n#define KEY_COPY\t\t133\t/* AC Copy */\n#define KEY_OPEN\t\t134\t/* AC Open */\n#define KEY_PASTE\t\t135\t/* AC Paste */\n#define KEY_FIND\t\t136\t/* AC Search */\n#define KEY_CUT\t\t\t137\t/* AC Cut */\n#define KEY_HELP\t\t138\t/* AL Integrated Help Center */\n#define KEY_MENU\t\t139\t/* Menu (show menu) */\n#define KEY_CALC\t\t140\t/* AL Calculator */\n#define KEY_SETUP\t\t141\n#define KEY_SLEEP\t\t142\t/* SC System Sleep */\n#define KEY_WAKEUP\t\t143\t/* System Wake Up */\n#define KEY_FILE\t\t144\t/* AL Local Machine Browser */\n#define KEY_SENDFILE\t\t145\n#define KEY_DELETEFILE\t\t146\n#define KEY_XFER\t\t147\n#define KEY_PROG1\t\t148\n#define KEY_PROG2\t\t149\n#define KEY_WWW\t\t\t150\t/* AL Internet Browser */\n#define KEY_MSDOS\t\t151\n#define KEY_COFFEE\t\t152\t/* AL Terminal Lock/Screensaver */\n#define KEY_SCREENLOCK\t\tKEY_COFFEE\n#define KEY_ROTATE_DISPLAY\t153\t/* Display orientation for e.g. tablets */\n#define KEY_DIRECTION\t\tKEY_ROTATE_DISPLAY\n#define KEY_CYCLEWINDOWS\t154\n#define KEY_MAIL\t\t155\n#define KEY_BOOKMARKS\t\t156\t/* AC Bookmarks */\n#define KEY_COMPUTER\t\t157\n#define KEY_BACK\t\t158\t/* AC Back */\n#define KEY_FORWARD\t\t159\t/* AC Forward */\n#define KEY_CLOSECD\t\t160\n#define KEY_EJECTCD\t\t161\n#define KEY_EJECTCLOSECD\t162\n#define KEY_NEXTSONG\t\t163\n#define KEY_PLAYPAUSE\t\t164\n#define KEY_PREVIOUSSONG\t165\n#define KEY_STOPCD\t\t166\n#define KEY_RECORD\t\t167\n#define KEY_REWIND\t\t168\n#define KEY_PHONE\t\t169\t/* Media Select Telephone */\n#define KEY_ISO\t\t\t170\n#define KEY_CONFIG\t\t171\t/* AL Consumer Control Configuration */\n#define KEY_HOMEPAGE\t\t172\t/* AC Home */\n#define KEY_REFRESH\t\t173\t/* AC Refresh */\n#define KEY_EXIT\t\t174\t/* AC Exit */\n#define KEY_MOVE\t\t175\n#define KEY_EDIT\t\t176\n#define KEY_SCROLLUP\t\t177\n#define KEY_SCROLLDOWN\t\t178\n#define KEY_KPLEFTPAREN\t\t179\n#define KEY_KPRIGHTPAREN\t180\n#define KEY_NEW\t\t\t181\t/* AC New */\n#define KEY_REDO\t\t182\t/* AC Redo/Repeat */\n\n#define KEY_F13\t\t\t183\n#define KEY_F14\t\t\t184\n#define KEY_F15\t\t\t185\n#define KEY_F16\t\t\t186\n#define KEY_F17\t\t\t187\n#define KEY_F18\t\t\t188\n#define KEY_F19\t\t\t189\n#define KEY_F20\t\t\t190\n#define KEY_F21\t\t\t191\n#define KEY_F22\t\t\t192\n#define KEY_F23\t\t\t193\n#define KEY_F24\t\t\t194\n\n#define KEY_PLAYCD\t\t200\n#define KEY_PAUSECD\t\t201\n#define KEY_PROG3\t\t202\n#define KEY_PROG4\t\t203\n#define KEY_DASHBOARD\t\t204\t/* AL Dashboard */\n#define KEY_SUSPEND\t\t205\n#define KEY_CLOSE\t\t206\t/* AC Close */\n#define KEY_PLAY\t\t207\n#define KEY_FASTFORWARD\t\t208\n#define KEY_BASSBOOST\t\t209\n#define KEY_PRINT\t\t210\t/* AC Print */\n#define KEY_HP\t\t\t211\n#define KEY_CAMERA\t\t212\n#define KEY_SOUND\t\t213\n#define KEY_QUESTION\t\t214\n#define KEY_EMAIL\t\t215\n#define KEY_CHAT\t\t216\n#define KEY_SEARCH\t\t217\n#define KEY_CONNECT\t\t218\n#define KEY_FINANCE\t\t219\t/* AL Checkbook/Finance */\n#define KEY_SPORT\t\t220\n#define KEY_SHOP\t\t221\n#define KEY_ALTERASE\t\t222\n#define KEY_CANCEL\t\t223\t/* AC Cancel */\n#define KEY_BRIGHTNESSDOWN\t224\n#define KEY_BRIGHTNESSUP\t225\n#define KEY_MEDIA\t\t226\n\n#define KEY_SWITCHVIDEOMODE\t227\t/* Cycle between available video\n\t\t\t\t\t   outputs (Monitor/LCD/TV-out/etc) */\n#define KEY_KBDILLUMTOGGLE\t228\n#define KEY_KBDILLUMDOWN\t229\n#define KEY_KBDILLUMUP\t\t230\n\n#define KEY_SEND\t\t231\t/* AC Send */\n#define KEY_REPLY\t\t232\t/* AC Reply */\n#define KEY_FORWARDMAIL\t\t233\t/* AC Forward Msg */\n#define KEY_SAVE\t\t234\t/* AC Save */\n#define KEY_DOCUMENTS\t\t235\n\n#define KEY_BATTERY\t\t236\n\n#define KEY_BLUETOOTH\t\t237\n#define KEY_WLAN\t\t238\n#define KEY_UWB\t\t\t239\n\n#define KEY_UNKNOWN\t\t240\n\n#define KEY_VIDEO_NEXT\t\t241\t/* drive next video source */\n#define KEY_VIDEO_PREV\t\t242\t/* drive previous video source */\n#define KEY_BRIGHTNESS_CYCLE\t243\t/* brightness up, after max is min */\n#define KEY_BRIGHTNESS_AUTO\t244\t/* Set Auto Brightness: manual\n\t\t\t\t\t  brightness control is off,\n\t\t\t\t\t  rely on ambient */\n#define KEY_BRIGHTNESS_ZERO\tKEY_BRIGHTNESS_AUTO\n#define KEY_DISPLAY_OFF\t\t245\t/* display device to off state */\n\n#define KEY_WWAN\t\t246\t/* Wireless WAN (LTE, UMTS, GSM, etc.) */\n#define KEY_WIMAX\t\tKEY_WWAN\n#define KEY_RFKILL\t\t247\t/* Key that controls all radios */\n\n#define KEY_MICMUTE\t\t248\t/* Mute / unmute the microphone */\n\n/* Code 255 is reserved for special needs of AT keyboard driver */\n\n#define BTN_MISC\t\t0x100\n#define BTN_0\t\t\t0x100\n#define BTN_1\t\t\t0x101\n#define BTN_2\t\t\t0x102\n#define BTN_3\t\t\t0x103\n#define BTN_4\t\t\t0x104\n#define BTN_5\t\t\t0x105\n#define BTN_6\t\t\t0x106\n#define BTN_7\t\t\t0x107\n#define BTN_8\t\t\t0x108\n#define BTN_9\t\t\t0x109\n\n#define BTN_MOUSE\t\t0x110\n#define BTN_LEFT\t\t0x110\n#define BTN_RIGHT\t\t0x111\n#define BTN_MIDDLE\t\t0x112\n#define BTN_SIDE\t\t0x113\n#define BTN_EXTRA\t\t0x114\n#define BTN_FORWARD\t\t0x115\n#define BTN_BACK\t\t0x116\n#define BTN_TASK\t\t0x117\n\n#define BTN_JOYSTICK\t\t0x120\n#define BTN_TRIGGER\t\t0x120\n#define BTN_THUMB\t\t0x121\n#define BTN_THUMB2\t\t0x122\n#define BTN_TOP\t\t\t0x123\n#define BTN_TOP2\t\t0x124\n#define BTN_PINKIE\t\t0x125\n#define BTN_BASE\t\t0x126\n#define BTN_BASE2\t\t0x127\n#define BTN_BASE3\t\t0x128\n#define BTN_BASE4\t\t0x129\n#define BTN_BASE5\t\t0x12a\n#define BTN_BASE6\t\t0x12b\n#define BTN_DEAD\t\t0x12f\n\n#define BTN_GAMEPAD\t\t0x130\n#define BTN_SOUTH\t\t0x130\n#define BTN_A\t\t\tBTN_SOUTH\n#define BTN_EAST\t\t0x131\n#define BTN_B\t\t\tBTN_EAST\n#define BTN_C\t\t\t0x132\n#define BTN_NORTH\t\t0x133\n#define BTN_X\t\t\tBTN_NORTH\n#define BTN_WEST\t\t0x134\n#define BTN_Y\t\t\tBTN_WEST\n#define BTN_Z\t\t\t0x135\n#define BTN_TL\t\t\t0x136\n#define BTN_TR\t\t\t0x137\n#define BTN_TL2\t\t\t0x138\n#define BTN_TR2\t\t\t0x139\n#define BTN_SELECT\t\t0x13a\n#define BTN_START\t\t0x13b\n#define BTN_MODE\t\t0x13c\n#define BTN_THUMBL\t\t0x13d\n#define BTN_THUMBR\t\t0x13e\n\n#define BTN_DIGI\t\t0x140\n#define BTN_TOOL_PEN\t\t0x140\n#define BTN_TOOL_RUBBER\t\t0x141\n#define BTN_TOOL_BRUSH\t\t0x142\n#define BTN_TOOL_PENCIL\t\t0x143\n#define BTN_TOOL_AIRBRUSH\t0x144\n#define BTN_TOOL_FINGER\t\t0x145\n#define BTN_TOOL_MOUSE\t\t0x146\n#define BTN_TOOL_LENS\t\t0x147\n#define BTN_TOOL_QUINTTAP\t0x148\t/* Five fingers on trackpad */\n#define BTN_STYLUS3\t\t0x149\n#define BTN_TOUCH\t\t0x14a\n#define BTN_STYLUS\t\t0x14b\n#define BTN_STYLUS2\t\t0x14c\n#define BTN_TOOL_DOUBLETAP\t0x14d\n#define BTN_TOOL_TRIPLETAP\t0x14e\n#define BTN_TOOL_QUADTAP\t0x14f\t/* Four fingers on trackpad */\n\n#define BTN_WHEEL\t\t0x150\n#define BTN_GEAR_DOWN\t\t0x150\n#define BTN_GEAR_UP\t\t0x151\n\n#define KEY_OK\t\t\t0x160\n#define KEY_SELECT\t\t0x161\n#define KEY_GOTO\t\t0x162\n#define KEY_CLEAR\t\t0x163\n#define KEY_POWER2\t\t0x164\n#define KEY_OPTION\t\t0x165\n#define KEY_INFO\t\t0x166\t/* AL OEM Features/Tips/Tutorial */\n#define KEY_TIME\t\t0x167\n#define KEY_VENDOR\t\t0x168\n#define KEY_ARCHIVE\t\t0x169\n#define KEY_PROGRAM\t\t0x16a\t/* Media Select Program Guide */\n#define KEY_CHANNEL\t\t0x16b\n#define KEY_FAVORITES\t\t0x16c\n#define KEY_EPG\t\t\t0x16d\n#define KEY_PVR\t\t\t0x16e\t/* Media Select Home */\n#define KEY_MHP\t\t\t0x16f\n#define KEY_LANGUAGE\t\t0x170\n#define KEY_TITLE\t\t0x171\n#define KEY_SUBTITLE\t\t0x172\n#define KEY_ANGLE\t\t0x173\n#define KEY_FULL_SCREEN\t\t0x174\t/* AC View Toggle */\n#define KEY_ZOOM\t\tKEY_FULL_SCREEN\n#define KEY_MODE\t\t0x175\n#define KEY_KEYBOARD\t\t0x176\n#define KEY_ASPECT_RATIO\t0x177\t/* HUTRR37: Aspect */\n#define KEY_SCREEN\t\tKEY_ASPECT_RATIO\n#define KEY_PC\t\t\t0x178\t/* Media Select Computer */\n#define KEY_TV\t\t\t0x179\t/* Media Select TV */\n#define KEY_TV2\t\t\t0x17a\t/* Media Select Cable */\n#define KEY_VCR\t\t\t0x17b\t/* Media Select VCR */\n#define KEY_VCR2\t\t0x17c\t/* VCR Plus */\n#define KEY_SAT\t\t\t0x17d\t/* Media Select Satellite */\n#define KEY_SAT2\t\t0x17e\n#define KEY_CD\t\t\t0x17f\t/* Media Select CD */\n#define KEY_TAPE\t\t0x180\t/* Media Select Tape */\n#define KEY_RADIO\t\t0x181\n#define KEY_TUNER\t\t0x182\t/* Media Select Tuner */\n#define KEY_PLAYER\t\t0x183\n#define KEY_TEXT\t\t0x184\n#define KEY_DVD\t\t\t0x185\t/* Media Select DVD */\n#define KEY_AUX\t\t\t0x186\n#define KEY_MP3\t\t\t0x187\n#define KEY_AUDIO\t\t0x188\t/* AL Audio Browser */\n#define KEY_VIDEO\t\t0x189\t/* AL Movie Browser */\n#define KEY_DIRECTORY\t\t0x18a\n#define KEY_LIST\t\t0x18b\n#define KEY_MEMO\t\t0x18c\t/* Media Select Messages */\n#define KEY_CALENDAR\t\t0x18d\n#define KEY_RED\t\t\t0x18e\n#define KEY_GREEN\t\t0x18f\n#define KEY_YELLOW\t\t0x190\n#define KEY_BLUE\t\t0x191\n#define KEY_CHANNELUP\t\t0x192\t/* Channel Increment */\n#define KEY_CHANNELDOWN\t\t0x193\t/* Channel Decrement */\n#define KEY_FIRST\t\t0x194\n#define KEY_LAST\t\t0x195\t/* Recall Last */\n#define KEY_AB\t\t\t0x196\n#define KEY_NEXT\t\t0x197\n#define KEY_RESTART\t\t0x198\n#define KEY_SLOW\t\t0x199\n#define KEY_SHUFFLE\t\t0x19a\n#define KEY_BREAK\t\t0x19b\n#define KEY_PREVIOUS\t\t0x19c\n#define KEY_DIGITS\t\t0x19d\n#define KEY_TEEN\t\t0x19e\n#define KEY_TWEN\t\t0x19f\n#define KEY_VIDEOPHONE\t\t0x1a0\t/* Media Select Video Phone */\n#define KEY_GAMES\t\t0x1a1\t/* Media Select Games */\n#define KEY_ZOOMIN\t\t0x1a2\t/* AC Zoom In */\n#define KEY_ZOOMOUT\t\t0x1a3\t/* AC Zoom Out */\n#define KEY_ZOOMRESET\t\t0x1a4\t/* AC Zoom */\n#define KEY_WORDPROCESSOR\t0x1a5\t/* AL Word Processor */\n#define KEY_EDITOR\t\t0x1a6\t/* AL Text Editor */\n#define KEY_SPREADSHEET\t\t0x1a7\t/* AL Spreadsheet */\n#define KEY_GRAPHICSEDITOR\t0x1a8\t/* AL Graphics Editor */\n#define KEY_PRESENTATION\t0x1a9\t/* AL Presentation App */\n#define KEY_DATABASE\t\t0x1aa\t/* AL Database App */\n#define KEY_NEWS\t\t0x1ab\t/* AL Newsreader */\n#define KEY_VOICEMAIL\t\t0x1ac\t/* AL Voicemail */\n#define KEY_ADDRESSBOOK\t\t0x1ad\t/* AL Contacts/Address Book */\n#define KEY_MESSENGER\t\t0x1ae\t/* AL Instant Messaging */\n#define KEY_DISPLAYTOGGLE\t0x1af\t/* Turn display (LCD) on and off */\n#define KEY_BRIGHTNESS_TOGGLE\tKEY_DISPLAYTOGGLE\n#define KEY_SPELLCHECK\t\t0x1b0   /* AL Spell Check */\n#define KEY_LOGOFF\t\t0x1b1   /* AL Logoff */\n\n#define KEY_DOLLAR\t\t0x1b2\n#define KEY_EURO\t\t0x1b3\n\n#define KEY_FRAMEBACK\t\t0x1b4\t/* Consumer - transport controls */\n#define KEY_FRAMEFORWARD\t0x1b5\n#define KEY_CONTEXT_MENU\t0x1b6\t/* GenDesc - system context menu */\n#define KEY_MEDIA_REPEAT\t0x1b7\t/* Consumer - transport control */\n#define KEY_10CHANNELSUP\t0x1b8\t/* 10 channels up (10+) */\n#define KEY_10CHANNELSDOWN\t0x1b9\t/* 10 channels down (10-) */\n#define KEY_IMAGES\t\t0x1ba\t/* AL Image Browser */\n\n#define KEY_DEL_EOL\t\t0x1c0\n#define KEY_DEL_EOS\t\t0x1c1\n#define KEY_INS_LINE\t\t0x1c2\n#define KEY_DEL_LINE\t\t0x1c3\n\n#define KEY_FN\t\t\t0x1d0\n#define KEY_FN_ESC\t\t0x1d1\n#define KEY_FN_F1\t\t0x1d2\n#define KEY_FN_F2\t\t0x1d3\n#define KEY_FN_F3\t\t0x1d4\n#define KEY_FN_F4\t\t0x1d5\n#define KEY_FN_F5\t\t0x1d6\n#define KEY_FN_F6\t\t0x1d7\n#define KEY_FN_F7\t\t0x1d8\n#define KEY_FN_F8\t\t0x1d9\n#define KEY_FN_F9\t\t0x1da\n#define KEY_FN_F10\t\t0x1db\n#define KEY_FN_F11\t\t0x1dc\n#define KEY_FN_F12\t\t0x1dd\n#define KEY_FN_1\t\t0x1de\n#define KEY_FN_2\t\t0x1df\n#define KEY_FN_D\t\t0x1e0\n#define KEY_FN_E\t\t0x1e1\n#define KEY_FN_F\t\t0x1e2\n#define KEY_FN_S\t\t0x1e3\n#define KEY_FN_B\t\t0x1e4\n\n#define KEY_BRL_DOT1\t\t0x1f1\n#define KEY_BRL_DOT2\t\t0x1f2\n#define KEY_BRL_DOT3\t\t0x1f3\n#define KEY_BRL_DOT4\t\t0x1f4\n#define KEY_BRL_DOT5\t\t0x1f5\n#define KEY_BRL_DOT6\t\t0x1f6\n#define KEY_BRL_DOT7\t\t0x1f7\n#define KEY_BRL_DOT8\t\t0x1f8\n#define KEY_BRL_DOT9\t\t0x1f9\n#define KEY_BRL_DOT10\t\t0x1fa\n\n#define KEY_NUMERIC_0\t\t0x200\t/* used by phones, remote controls, */\n#define KEY_NUMERIC_1\t\t0x201\t/* and other keypads */\n#define KEY_NUMERIC_2\t\t0x202\n#define KEY_NUMERIC_3\t\t0x203\n#define KEY_NUMERIC_4\t\t0x204\n#define KEY_NUMERIC_5\t\t0x205\n#define KEY_NUMERIC_6\t\t0x206\n#define KEY_NUMERIC_7\t\t0x207\n#define KEY_NUMERIC_8\t\t0x208\n#define KEY_NUMERIC_9\t\t0x209\n#define KEY_NUMERIC_STAR\t0x20a\n#define KEY_NUMERIC_POUND\t0x20b\n#define KEY_NUMERIC_A\t\t0x20c\t/* Phone key A - HUT Telephony 0xb9 */\n#define KEY_NUMERIC_B\t\t0x20d\n#define KEY_NUMERIC_C\t\t0x20e\n#define KEY_NUMERIC_D\t\t0x20f\n\n#define KEY_CAMERA_FOCUS\t0x210\n#define KEY_WPS_BUTTON\t\t0x211\t/* WiFi Protected Setup key */\n\n#define KEY_TOUCHPAD_TOGGLE\t0x212\t/* Request switch touchpad on or off */\n#define KEY_TOUCHPAD_ON\t\t0x213\n#define KEY_TOUCHPAD_OFF\t0x214\n\n#define KEY_CAMERA_ZOOMIN\t0x215\n#define KEY_CAMERA_ZOOMOUT\t0x216\n#define KEY_CAMERA_UP\t\t0x217\n#define KEY_CAMERA_DOWN\t\t0x218\n#define KEY_CAMERA_LEFT\t\t0x219\n#define KEY_CAMERA_RIGHT\t0x21a\n\n#define KEY_ATTENDANT_ON\t0x21b\n#define KEY_ATTENDANT_OFF\t0x21c\n#define KEY_ATTENDANT_TOGGLE\t0x21d\t/* Attendant call on or off */\n#define KEY_LIGHTS_TOGGLE\t0x21e\t/* Reading light on or off */\n\n#define BTN_DPAD_UP\t\t0x220\n#define BTN_DPAD_DOWN\t\t0x221\n#define BTN_DPAD_LEFT\t\t0x222\n#define BTN_DPAD_RIGHT\t\t0x223\n\n#define KEY_ALS_TOGGLE\t\t0x230\t/* Ambient light sensor */\n#define KEY_ROTATE_LOCK_TOGGLE\t0x231\t/* Display rotation lock */\n\n#define KEY_BUTTONCONFIG\t\t0x240\t/* AL Button Configuration */\n#define KEY_TASKMANAGER\t\t0x241\t/* AL Task/Project Manager */\n#define KEY_JOURNAL\t\t0x242\t/* AL Log/Journal/Timecard */\n#define KEY_CONTROLPANEL\t\t0x243\t/* AL Control Panel */\n#define KEY_APPSELECT\t\t0x244\t/* AL Select Task/Application */\n#define KEY_SCREENSAVER\t\t0x245\t/* AL Screen Saver */\n#define KEY_VOICECOMMAND\t\t0x246\t/* Listening Voice Command */\n#define KEY_ASSISTANT\t\t0x247\t/* AL Context-aware desktop assistant */\n#define KEY_KBD_LAYOUT_NEXT\t0x248\t/* AC Next Keyboard Layout Select */\n\n#define KEY_BRIGHTNESS_MIN\t\t0x250\t/* Set Brightness to Minimum */\n#define KEY_BRIGHTNESS_MAX\t\t0x251\t/* Set Brightness to Maximum */\n\n#define KEY_KBDINPUTASSIST_PREV\t\t0x260\n#define KEY_KBDINPUTASSIST_NEXT\t\t0x261\n#define KEY_KBDINPUTASSIST_PREVGROUP\t\t0x262\n#define KEY_KBDINPUTASSIST_NEXTGROUP\t\t0x263\n#define KEY_KBDINPUTASSIST_ACCEPT\t\t0x264\n#define KEY_KBDINPUTASSIST_CANCEL\t\t0x265\n\n/* Diagonal movement keys */\n#define KEY_RIGHT_UP\t\t\t0x266\n#define KEY_RIGHT_DOWN\t\t\t0x267\n#define KEY_LEFT_UP\t\t\t0x268\n#define KEY_LEFT_DOWN\t\t\t0x269\n\n#define KEY_ROOT_MENU\t\t\t0x26a /* Show Device's Root Menu */\n/* Show Top Menu of the Media (e.g. DVD) */\n#define KEY_MEDIA_TOP_MENU\t\t0x26b\n#define KEY_NUMERIC_11\t\t\t0x26c\n#define KEY_NUMERIC_12\t\t\t0x26d\n/*\n * Toggle Audio Description: refers to an audio service that helps blind and\n * visually impaired consumers understand the action in a program. Note: in\n * some countries this is referred to as \"Video Description\".\n */\n#define KEY_AUDIO_DESC\t\t\t0x26e\n#define KEY_3D_MODE\t\t\t0x26f\n#define KEY_NEXT_FAVORITE\t\t0x270\n#define KEY_STOP_RECORD\t\t\t0x271\n#define KEY_PAUSE_RECORD\t\t0x272\n#define KEY_VOD\t\t\t\t0x273 /* Video on Demand */\n#define KEY_UNMUTE\t\t\t0x274\n#define KEY_FASTREVERSE\t\t\t0x275\n#define KEY_SLOWREVERSE\t\t\t0x276\n/*\n * Control a data application associated with the currently viewed channel,\n * e.g. teletext or data broadcast application (MHEG, MHP, HbbTV, etc.)\n */\n#define KEY_DATA\t\t\t0x277\n#define KEY_ONSCREEN_KEYBOARD\t\t0x278\n\n#define BTN_TRIGGER_HAPPY\t\t0x2c0\n#define BTN_TRIGGER_HAPPY1\t\t0x2c0\n#define BTN_TRIGGER_HAPPY2\t\t0x2c1\n#define BTN_TRIGGER_HAPPY3\t\t0x2c2\n#define BTN_TRIGGER_HAPPY4\t\t0x2c3\n#define BTN_TRIGGER_HAPPY5\t\t0x2c4\n#define BTN_TRIGGER_HAPPY6\t\t0x2c5\n#define BTN_TRIGGER_HAPPY7\t\t0x2c6\n#define BTN_TRIGGER_HAPPY8\t\t0x2c7\n#define BTN_TRIGGER_HAPPY9\t\t0x2c8\n#define BTN_TRIGGER_HAPPY10\t\t0x2c9\n#define BTN_TRIGGER_HAPPY11\t\t0x2ca\n#define BTN_TRIGGER_HAPPY12\t\t0x2cb\n#define BTN_TRIGGER_HAPPY13\t\t0x2cc\n#define BTN_TRIGGER_HAPPY14\t\t0x2cd\n#define BTN_TRIGGER_HAPPY15\t\t0x2ce\n#define BTN_TRIGGER_HAPPY16\t\t0x2cf\n#define BTN_TRIGGER_HAPPY17\t\t0x2d0\n#define BTN_TRIGGER_HAPPY18\t\t0x2d1\n#define BTN_TRIGGER_HAPPY19\t\t0x2d2\n#define BTN_TRIGGER_HAPPY20\t\t0x2d3\n#define BTN_TRIGGER_HAPPY21\t\t0x2d4\n#define BTN_TRIGGER_HAPPY22\t\t0x2d5\n#define BTN_TRIGGER_HAPPY23\t\t0x2d6\n#define BTN_TRIGGER_HAPPY24\t\t0x2d7\n#define BTN_TRIGGER_HAPPY25\t\t0x2d8\n#define BTN_TRIGGER_HAPPY26\t\t0x2d9\n#define BTN_TRIGGER_HAPPY27\t\t0x2da\n#define BTN_TRIGGER_HAPPY28\t\t0x2db\n#define BTN_TRIGGER_HAPPY29\t\t0x2dc\n#define BTN_TRIGGER_HAPPY30\t\t0x2dd\n#define BTN_TRIGGER_HAPPY31\t\t0x2de\n#define BTN_TRIGGER_HAPPY32\t\t0x2df\n#define BTN_TRIGGER_HAPPY33\t\t0x2e0\n#define BTN_TRIGGER_HAPPY34\t\t0x2e1\n#define BTN_TRIGGER_HAPPY35\t\t0x2e2\n#define BTN_TRIGGER_HAPPY36\t\t0x2e3\n#define BTN_TRIGGER_HAPPY37\t\t0x2e4\n#define BTN_TRIGGER_HAPPY38\t\t0x2e5\n#define BTN_TRIGGER_HAPPY39\t\t0x2e6\n#define BTN_TRIGGER_HAPPY40\t\t0x2e7\n\n/* We avoid low common keys in module aliases so they don't get huge. */\n#define KEY_MIN_INTERESTING\tKEY_MUTE\n#define KEY_MAX\t\t\t0x2ff\n#define KEY_CNT\t\t\t(KEY_MAX+1)\n\n/*\n * Relative axes\n */\n\n#define REL_X\t\t\t0x00\n#define REL_Y\t\t\t0x01\n#define REL_Z\t\t\t0x02\n#define REL_RX\t\t\t0x03\n#define REL_RY\t\t\t0x04\n#define REL_RZ\t\t\t0x05\n#define REL_HWHEEL\t\t0x06\n#define REL_DIAL\t\t0x07\n#define REL_WHEEL\t\t0x08\n#define REL_MISC\t\t0x09\n/*\n * 0x0a is reserved and should not be used in input drivers.\n * It was used by HID as REL_MISC+1 and userspace needs to detect if\n * the next REL_* event is correct or is just REL_MISC + n.\n * We define here REL_RESERVED so userspace can rely on it and detect\n * the situation described above.\n */\n#define REL_RESERVED\t\t0x0a\n#define REL_WHEEL_HI_RES\t0x0b\n#define REL_HWHEEL_HI_RES\t0x0c\n#define REL_MAX\t\t\t0x0f\n#define REL_CNT\t\t\t(REL_MAX+1)\n\n/*\n * Absolute axes\n */\n\n#define ABS_X\t\t\t0x00\n#define ABS_Y\t\t\t0x01\n#define ABS_Z\t\t\t0x02\n#define ABS_RX\t\t\t0x03\n#define ABS_RY\t\t\t0x04\n#define ABS_RZ\t\t\t0x05\n#define ABS_THROTTLE\t\t0x06\n#define ABS_RUDDER\t\t0x07\n#define ABS_WHEEL\t\t0x08\n#define ABS_GAS\t\t\t0x09\n#define ABS_BRAKE\t\t0x0a\n#define ABS_HAT0X\t\t0x10\n#define ABS_HAT0Y\t\t0x11\n#define ABS_HAT1X\t\t0x12\n#define ABS_HAT1Y\t\t0x13\n#define ABS_HAT2X\t\t0x14\n#define ABS_HAT2Y\t\t0x15\n#define ABS_HAT3X\t\t0x16\n#define ABS_HAT3Y\t\t0x17\n#define ABS_PRESSURE\t\t0x18\n#define ABS_DISTANCE\t\t0x19\n#define ABS_TILT_X\t\t0x1a\n#define ABS_TILT_Y\t\t0x1b\n#define ABS_TOOL_WIDTH\t\t0x1c\n\n#define ABS_VOLUME\t\t0x20\n\n#define ABS_MISC\t\t0x28\n\n/*\n * 0x2e is reserved and should not be used in input drivers.\n * It was used by HID as ABS_MISC+6 and userspace needs to detect if\n * the next ABS_* event is correct or is just ABS_MISC + n.\n * We define here ABS_RESERVED so userspace can rely on it and detect\n * the situation described above.\n */\n#define ABS_RESERVED\t\t0x2e\n\n#define ABS_MT_SLOT\t\t0x2f\t/* MT slot being modified */\n#define ABS_MT_TOUCH_MAJOR\t0x30\t/* Major axis of touching ellipse */\n#define ABS_MT_TOUCH_MINOR\t0x31\t/* Minor axis (omit if circular) */\n#define ABS_MT_WIDTH_MAJOR\t0x32\t/* Major axis of approaching ellipse */\n#define ABS_MT_WIDTH_MINOR\t0x33\t/* Minor axis (omit if circular) */\n#define ABS_MT_ORIENTATION\t0x34\t/* Ellipse orientation */\n#define ABS_MT_POSITION_X\t0x35\t/* Center X touch position */\n#define ABS_MT_POSITION_Y\t0x36\t/* Center Y touch position */\n#define ABS_MT_TOOL_TYPE\t0x37\t/* Type of touching device */\n#define ABS_MT_BLOB_ID\t\t0x38\t/* Group a set of packets as a blob */\n#define ABS_MT_TRACKING_ID\t0x39\t/* Unique ID of initiated contact */\n#define ABS_MT_PRESSURE\t\t0x3a\t/* Pressure on contact area */\n#define ABS_MT_DISTANCE\t\t0x3b\t/* Contact hover distance */\n#define ABS_MT_TOOL_X\t\t0x3c\t/* Center X tool position */\n#define ABS_MT_TOOL_Y\t\t0x3d\t/* Center Y tool position */\n\n#define ABS_MAX\t\t\t0x3f\n#define ABS_CNT\t\t\t(ABS_MAX+1)\n\n/*\n * Switch events\n */\n\n#define SW_LID\t\t\t0x00  /* set = lid shut */\n#define SW_TABLET_MODE\t\t0x01  /* set = tablet mode */\n#define SW_HEADPHONE_INSERT\t0x02  /* set = inserted */\n#define SW_RFKILL_ALL\t\t0x03  /* rfkill master switch, type \"any\"\n\t\t\t\t\t set = radio enabled */\n#define SW_RADIO\t\tSW_RFKILL_ALL\t/* deprecated */\n#define SW_MICROPHONE_INSERT\t0x04  /* set = inserted */\n#define SW_DOCK\t\t\t0x05  /* set = plugged into dock */\n#define SW_LINEOUT_INSERT\t0x06  /* set = inserted */\n#define SW_JACK_PHYSICAL_INSERT 0x07  /* set = mechanical switch set */\n#define SW_VIDEOOUT_INSERT\t0x08  /* set = inserted */\n#define SW_CAMERA_LENS_COVER\t0x09  /* set = lens covered */\n#define SW_KEYPAD_SLIDE\t\t0x0a  /* set = keypad slide out */\n#define SW_FRONT_PROXIMITY\t0x0b  /* set = front proximity sensor active */\n#define SW_ROTATE_LOCK\t\t0x0c  /* set = rotate locked/disabled */\n#define SW_LINEIN_INSERT\t0x0d  /* set = inserted */\n#define SW_MUTE_DEVICE\t\t0x0e  /* set = device disabled */\n#define SW_PEN_INSERTED\t\t0x0f  /* set = pen inserted */\n#define SW_MAX\t\t\t0x0f\n#define SW_CNT\t\t\t(SW_MAX+1)\n\n/*\n * Misc events\n */\n\n#define MSC_SERIAL\t\t0x00\n#define MSC_PULSELED\t\t0x01\n#define MSC_GESTURE\t\t0x02\n#define MSC_RAW\t\t\t0x03\n#define MSC_SCAN\t\t0x04\n#define MSC_TIMESTAMP\t\t0x05\n#define MSC_MAX\t\t\t0x07\n#define MSC_CNT\t\t\t(MSC_MAX+1)\n\n/*\n * LEDs\n */\n\n#define LED_NUML\t\t0x00\n#define LED_CAPSL\t\t0x01\n#define LED_SCROLLL\t\t0x02\n#define LED_COMPOSE\t\t0x03\n#define LED_KANA\t\t0x04\n#define LED_SLEEP\t\t0x05\n#define LED_SUSPEND\t\t0x06\n#define LED_MUTE\t\t0x07\n#define LED_MISC\t\t0x08\n#define LED_MAIL\t\t0x09\n#define LED_CHARGING\t\t0x0a\n#define LED_MAX\t\t\t0x0f\n#define LED_CNT\t\t\t(LED_MAX+1)\n\n/*\n * Autorepeat values\n */\n\n#define REP_DELAY\t\t0x00\n#define REP_PERIOD\t\t0x01\n#define REP_MAX\t\t\t0x01\n#define REP_CNT\t\t\t(REP_MAX+1)\n\n/*\n * Sounds\n */\n\n#define SND_CLICK\t\t0x00\n#define SND_BELL\t\t0x01\n#define SND_TONE\t\t0x02\n#define SND_MAX\t\t\t0x07\n#define SND_CNT\t\t\t(SND_MAX+1)\n\n#endif\n"
  },
  {
    "path": "risc_v/userspace/shell.cpp",
    "content": "#include <cstdio>\n#include <unistd.h>\nint main()\n{\n\tprintf(\"Started shell.\\n\");\n\tchar data[100];\n\twhile (1) {\n\t\tprintf(\"Enter value: \");\n\t\tint r = read(0, data, 100);\n\t\tif (r > 0) {\n\t\t\tprintf(\"Got %s\\n\", data);\n\t\t}\n\t}\n\treturn 0;\n}\n"
  },
  {
    "path": "risc_v/userspace/sleepy.cpp",
    "content": "#include <cstdio>\nint main()\n{\n\tprintf(\"I'm going to bed.\\nYou can watch me sleep for 100 switches using 'top'\\n\");\n\treturn 0;\n}\n"
  },
  {
    "path": "risc_v/userspace/startlib/.gitignore",
    "content": "*.a\n*.o\n"
  },
  {
    "path": "risc_v/userspace/startlib/Makefile",
    "content": "CROSS=riscv64-unknown-linux-gnu-\nCXX=g++\nOBJCOPY=objcopy\nAR=ar\nCXXFLAGS=-Wall -O0 -ffreestanding -nostartfiles -nostdlib -I. -march=rv64g -mabi=lp64d\nOUT=libstart.a\nSOURCES_S=$(wildcard *.S)\nSOURCES_CPP=$(wildcard *.cpp)\nOBJS=$(patsubst %.S,%.o,$(SOURCES_S)) $(patsubst %.cpp,%.o,$(SOURCES_CPP))\nall: $(OUT)\n\n$(OUT): $(OBJS) Makefile\n\trm -f $(OUT)\n\t$(AR) rcv $(OUT) $(OBJS)\n\n%.o: %.S\n\t$(CROSS)$(CXX) $(CXXFLAGS) -c $< -o $@\n\n%.o: %.cpp\n\t$(CROSS)$(CXX) $(CXXFLAGS) -c $< -o $@\n\n.PHONY: clean\n\nclean:\n\trm -f $(OUT) $(OBJS)\n"
  },
  {
    "path": "risc_v/userspace/startlib/linker.lds",
    "content": "OUTPUT_ARCH( \"riscv\" )\n\nENTRY( _start )\n\nMEMORY\n{\n  ram   (wxa!ri) : ORIGIN = 0x20000000, LENGTH = 128M\n}\n\nPHDRS\n{\n  text PT_LOAD;\n  rodata PT_LOAD;\n  data PT_LOAD;\n  bss PT_LOAD;\n}\n\nSECTIONS\n{\n  .text : {\n    PROVIDE(_text_start = .);\n    *(.text.init) *(.text .text.*)\n    PROVIDE(_text_end = .);\n  } >ram AT>ram :text\n   PROVIDE(_global_pointer = .);\n  .rodata : {\n    PROVIDE(_rodata_start = .);\n    *(.rodata .rodata.*)\n    PROVIDE(_rodata_end = .);\n  } >ram AT>ram :rodata\n\n  .data : {\n    PROVIDE(_data_start = .);\n    *(.sdata .sdata.*) *(.data .data.*)\n    PROVIDE(_data_end = .);\n  } >ram AT>ram :data\n\n  .bss :{\n    PROVIDE(_bss_start = .);\n    *(.sbss .sbss.*) *(.bss .bss.*)\n    PROVIDE(_bss_end = .);\n  } >ram AT>ram :bss\n\n  PROVIDE(_memory_start = ORIGIN(ram));\n  PROVIDE(_stack = _bss_end + 0x80000);\n  PROVIDE(_memory_end = ORIGIN(ram) + LENGTH(ram));\n  PROVIDE(_heap_start = _stack);\n  PROVIDE(_heap_size = _memory_end - _stack);\n}\n"
  },
  {
    "path": "risc_v/userspace/startlib/printf.cpp",
    "content": "///////////////////////////////////////////////////////////////////////////////\n// \\author (c) Marco Paland (info@paland.com)\n//             2014-2019, PALANDesign Hannover, Germany\n//\n// \\license The MIT License (MIT)\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// \\brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on\n//        embedded systems with a very limited resources. These routines are thread\n//        safe and reentrant!\n//        Use this instead of the bloated standard/newlib printf cause these use\n//        malloc for printf (and may not be thread safe).\n//\n///////////////////////////////////////////////////////////////////////////////\n\n#include <stdbool.h>\n#include <stdint.h>\n#include <printf.h>\n#include <syscall.h>\n\n// #define USE_DIRECT_UART\n// \n// 'ntoa' conversion buffer size, this must be big enough to hold one converted\n// numeric number including padded zeros (dynamically created on stack)\n// default: 32 byte\n#ifndef PRINTF_NTOA_BUFFER_SIZE\n#define PRINTF_NTOA_BUFFER_SIZE 32U\n#endif\n\n// 'ftoa' conversion buffer size, this must be big enough to hold one converted\n// float number including padded zeros (dynamically created on stack)\n// default: 32 byte\n#ifndef PRINTF_FTOA_BUFFER_SIZE\n#define PRINTF_FTOA_BUFFER_SIZE 32U\n#endif\n\n// support for the floating point type (%f)\n// default: activated\n//#ifndef PRINTF_DISABLE_SUPPORT_FLOAT\n#define PRINTF_SUPPORT_FLOAT\n//#endif\n\n// support for exponential floating point notation (%e/%g)\n// default: activated\n//#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL\n#define PRINTF_SUPPORT_EXPONENTIAL\n//#endif\n\n// define the default floating point precision\n// default: 6 digits\n#ifndef PRINTF_DEFAULT_FLOAT_PRECISION\n#define PRINTF_DEFAULT_FLOAT_PRECISION 6U\n#endif\n\n// define the largest float suitable to print with %f\n// default: 1e9\n#ifndef PRINTF_MAX_FLOAT\n#define PRINTF_MAX_FLOAT 1e9\n#endif\n\n// support for the long long types (%llu or %p)\n// default: activated\n// #ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG\n#define PRINTF_SUPPORT_LONG_LONG\n// #endif\n\n// support for the ptrdiff_t type (%t)\n// ptrdiff_t is normally defined in <stddef.h> as long or long long type\n// default: activated\n// #ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T\n#define PRINTF_SUPPORT_PTRDIFF_T\n// #endif\n\n///////////////////////////////////////////////////////////////////////////////\n\n// internal flag definitions\n#define FLAGS_ZEROPAD (1U << 0U)\n#define FLAGS_LEFT (1U << 1U)\n#define FLAGS_PLUS (1U << 2U)\n#define FLAGS_SPACE (1U << 3U)\n#define FLAGS_HASH (1U << 4U)\n#define FLAGS_UPPERCASE (1U << 5U)\n#define FLAGS_CHAR (1U << 6U)\n#define FLAGS_SHORT (1U << 7U)\n#define FLAGS_LONG (1U << 8U)\n#define FLAGS_LONG_LONG (1U << 9U)\n#define FLAGS_PRECISION (1U << 10U)\n#define FLAGS_ADAPT_EXP (1U << 11U)\n\n// import float.h for DBL_MAX\n#if defined(PRINTF_SUPPORT_FLOAT)\n#include <float.h>\n#endif\n\n#include <syscall.h>\n\n// output function type\ntypedef void (*out_fct_type)(char character, void *buffer, size_t idx, size_t maxlen);\n\n// wrapper (used as buffer) for output function type\ntypedef struct\n{\n\tvoid (*fct)(char character, void *arg);\n\tvoid *arg;\n} out_fct_wrap_type;\n\n// internal buffer output\nstatic inline void _out_buffer(char character, void *buffer, size_t idx, size_t maxlen)\n{\n\tif (idx < maxlen)\n\t{\n\t\t((char *)buffer)[idx] = character;\n\t}\n}\n\nvoid _putchar(char c)\n{\n\tsyscall_put_char(c);\n}\n\n// internal null output\nstatic inline void _out_null(char character, void *buffer, size_t idx, size_t maxlen)\n{\n\t(void)character;\n\t(void)buffer;\n\t(void)idx;\n\t(void)maxlen;\n}\n\n// internal _putchar wrapper\nstatic inline void _out_char(char character, void *buffer, size_t idx, size_t maxlen)\n{\n\t(void)buffer;\n\t(void)idx;\n\t(void)maxlen;\n\tif (character)\n\t{\n\t\t_putchar(character);\n\t}\n}\n\n// internal output function wrapper\nstatic inline void _out_fct(char character, void *buffer, size_t idx, size_t maxlen)\n{\n\t(void)idx;\n\t(void)maxlen;\n\tif (character)\n\t{\n\t\t// buffer is the output fct pointer\n\t\t((out_fct_wrap_type *)buffer)->fct(character, ((out_fct_wrap_type *)buffer)->arg);\n\t}\n}\n\n// internal secure strlen\n// \\return The length of the string (excluding the terminating 0) limited by 'maxsize'\nstatic inline unsigned int _strnlen_s(const char *str, size_t maxsize)\n{\n\tconst char *s;\n\tfor (s = str; *s && maxsize--; ++s)\n\t\t;\n\treturn (unsigned int)(s - str);\n}\n\n// internal test if char is a digit (0-9)\n// \\return true if char is a digit\nstatic inline bool _is_digit(char ch)\n{\n\treturn (ch >= '0') && (ch <= '9');\n}\n\n// internal ASCII string to unsigned int conversion\nstatic unsigned int _atoi(const char **str)\n{\n\tunsigned int i = 0U;\n\twhile (_is_digit(**str))\n\t{\n\t\ti = i * 10U + (unsigned int)(*((*str)++) - '0');\n\t}\n\treturn i;\n}\n\n// output the specified string in reverse, taking care of any zero-padding\nstatic size_t _out_rev(out_fct_type out, char *buffer, size_t idx, size_t maxlen, const char *buf, size_t len, unsigned int width, unsigned int flags)\n{\n\tconst size_t start_idx = idx;\n\n\t// pad spaces up to given width\n\tif (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD))\n\t{\n\t\tfor (size_t i = len; i < width; i++)\n\t\t{\n\t\t\tout(' ', buffer, idx++, maxlen);\n\t\t}\n\t}\n\n\t// reverse string\n\twhile (len)\n\t{\n\t\tout(buf[--len], buffer, idx++, maxlen);\n\t}\n\n\t// append pad spaces up to given width\n\tif (flags & FLAGS_LEFT)\n\t{\n\t\twhile (idx - start_idx < width)\n\t\t{\n\t\t\tout(' ', buffer, idx++, maxlen);\n\t\t}\n\t}\n\n\treturn idx;\n}\n\n// internal itoa format\nstatic size_t _ntoa_format(out_fct_type out, char *buffer, size_t idx, size_t maxlen, char *buf, size_t len, bool negative, unsigned int base, unsigned int prec, unsigned int width, unsigned int flags)\n{\n\t// pad leading zeros\n\tif (!(flags & FLAGS_LEFT))\n\t{\n\t\tif (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE))))\n\t\t{\n\t\t\twidth--;\n\t\t}\n\t\twhile ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE))\n\t\t{\n\t\t\tbuf[len++] = '0';\n\t\t}\n\t\twhile ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE))\n\t\t{\n\t\t\tbuf[len++] = '0';\n\t\t}\n\t}\n\n\t// handle hash\n\tif (flags & FLAGS_HASH)\n\t{\n\t\tif (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width)))\n\t\t{\n\t\t\tlen--;\n\t\t\tif (len && (base == 16U))\n\t\t\t{\n\t\t\t\tlen--;\n\t\t\t}\n\t\t}\n\t\tif ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE))\n\t\t{\n\t\t\tbuf[len++] = 'x';\n\t\t}\n\t\telse if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE))\n\t\t{\n\t\t\tbuf[len++] = 'X';\n\t\t}\n\t\telse if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE))\n\t\t{\n\t\t\tbuf[len++] = 'b';\n\t\t}\n\t\tif (len < PRINTF_NTOA_BUFFER_SIZE)\n\t\t{\n\t\t\tbuf[len++] = '0';\n\t\t}\n\t}\n\n\tif (len < PRINTF_NTOA_BUFFER_SIZE)\n\t{\n\t\tif (negative)\n\t\t{\n\t\t\tbuf[len++] = '-';\n\t\t}\n\t\telse if (flags & FLAGS_PLUS)\n\t\t{\n\t\t\tbuf[len++] = '+'; // ignore the space if the '+' exists\n\t\t}\n\t\telse if (flags & FLAGS_SPACE)\n\t\t{\n\t\t\tbuf[len++] = ' ';\n\t\t}\n\t}\n\n\treturn _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);\n}\n\n// internal itoa for 'long' type\nstatic size_t _ntoa_long(out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base, unsigned int prec, unsigned int width, unsigned int flags)\n{\n\tchar buf[PRINTF_NTOA_BUFFER_SIZE];\n\tsize_t len = 0U;\n\n\t// no hash for 0 values\n\tif (!value)\n\t{\n\t\tflags &= ~FLAGS_HASH;\n\t}\n\n\t// write if precision != 0 and value is != 0\n\tif (!(flags & FLAGS_PRECISION) || value)\n\t{\n\t\tdo\n\t\t{\n\t\t\tconst char digit = (char)(value % base);\n\t\t\tbuf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;\n\t\t\tvalue /= base;\n\t\t} while (value && (len < PRINTF_NTOA_BUFFER_SIZE));\n\t}\n\n\treturn _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);\n}\n\n// internal itoa for 'long long' type\n#if defined(PRINTF_SUPPORT_LONG_LONG)\nstatic size_t _ntoa_long_long(out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative, unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags)\n{\n\tchar buf[PRINTF_NTOA_BUFFER_SIZE];\n\tsize_t len = 0U;\n\n\t// no hash for 0 values\n\tif (!value)\n\t{\n\t\tflags &= ~FLAGS_HASH;\n\t}\n\n\t// write if precision != 0 and value is != 0\n\tif (!(flags & FLAGS_PRECISION) || value)\n\t{\n\t\tdo\n\t\t{\n\t\t\tconst char digit = (char)(value % base);\n\t\t\tbuf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;\n\t\t\tvalue /= base;\n\t\t} while (value && (len < PRINTF_NTOA_BUFFER_SIZE));\n\t}\n\n\treturn _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);\n}\n#endif // PRINTF_SUPPORT_LONG_LONG\n\n#if defined(PRINTF_SUPPORT_FLOAT)\n\n#if defined(PRINTF_SUPPORT_EXPONENTIAL)\n// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT\nstatic size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags);\n#endif\n\n// internal ftoa for fixed decimal floating point\nstatic size_t _ftoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags)\n{\n\tchar buf[PRINTF_FTOA_BUFFER_SIZE];\n\tsize_t len = 0U;\n\tdouble diff = 0.0;\n\n\t// powers of 10\n\tstatic const double pow10[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};\n\n\t// test for special values\n\tif (value != value)\n\t\treturn _out_rev(out, buffer, idx, maxlen, \"nan\", 3, width, flags);\n\tif (value < -DBL_MAX)\n\t\treturn _out_rev(out, buffer, idx, maxlen, \"fni-\", 4, width, flags);\n\tif (value > DBL_MAX)\n\t\treturn _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? \"fni+\" : \"fni\", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags);\n\n\t// test for very large values\n\t// standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad\n\tif ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT))\n\t{\n#if defined(PRINTF_SUPPORT_EXPONENTIAL)\n\t\treturn _etoa(out, buffer, idx, maxlen, value, prec, width, flags);\n#else\n\t\treturn 0U;\n#endif\n\t}\n\n\t// test for negative\n\tbool negative = false;\n\tif (value < 0)\n\t{\n\t\tnegative = true;\n\t\tvalue = 0 - value;\n\t}\n\n\t// set default precision, if not set explicitly\n\tif (!(flags & FLAGS_PRECISION))\n\t{\n\t\tprec = PRINTF_DEFAULT_FLOAT_PRECISION;\n\t}\n\t// limit precision to 9, cause a prec >= 10 can lead to overflow errors\n\twhile ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U))\n\t{\n\t\tbuf[len++] = '0';\n\t\tprec--;\n\t}\n\n\tint whole = (int)value;\n\tdouble tmp = (value - whole) * pow10[prec];\n\tunsigned long frac = (unsigned long)tmp;\n\tdiff = tmp - frac;\n\n\tif (diff > 0.5)\n\t{\n\t\t++frac;\n\t\t// handle rollover, e.g. case 0.99 with prec 1 is 1.0\n\t\tif (frac >= pow10[prec])\n\t\t{\n\t\t\tfrac = 0;\n\t\t\t++whole;\n\t\t}\n\t}\n\telse if (diff < 0.5)\n\t{\n\t}\n\telse if ((frac == 0U) || (frac & 1U))\n\t{\n\t\t// if halfway, round up if odd OR if last digit is 0\n\t\t++frac;\n\t}\n\n\tif (prec == 0U)\n\t{\n\t\tdiff = value - (double)whole;\n\t\tif ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1))\n\t\t{\n\t\t\t// exactly 0.5 and ODD, then round up\n\t\t\t// 1.5 -> 2, but 2.5 -> 2\n\t\t\t++whole;\n\t\t}\n\t}\n\telse\n\t{\n\t\tunsigned int count = prec;\n\t\t// now do fractional part, as an unsigned number\n\t\twhile (len < PRINTF_FTOA_BUFFER_SIZE)\n\t\t{\n\t\t\t--count;\n\t\t\tbuf[len++] = (char)(48U + (frac % 10U));\n\t\t\tif (!(frac /= 10U))\n\t\t\t{\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\t// add extra 0s\n\t\twhile ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U))\n\t\t{\n\t\t\tbuf[len++] = '0';\n\t\t}\n\t\tif (len < PRINTF_FTOA_BUFFER_SIZE)\n\t\t{\n\t\t\t// add decimal\n\t\t\tbuf[len++] = '.';\n\t\t}\n\t}\n\n\t// do whole part, number is reversed\n\twhile (len < PRINTF_FTOA_BUFFER_SIZE)\n\t{\n\t\tbuf[len++] = (char)(48 + (whole % 10));\n\t\tif (!(whole /= 10))\n\t\t{\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// pad leading zeros\n\tif (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD))\n\t{\n\t\tif (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE))))\n\t\t{\n\t\t\twidth--;\n\t\t}\n\t\twhile ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE))\n\t\t{\n\t\t\tbuf[len++] = '0';\n\t\t}\n\t}\n\n\tif (len < PRINTF_FTOA_BUFFER_SIZE)\n\t{\n\t\tif (negative)\n\t\t{\n\t\t\tbuf[len++] = '-';\n\t\t}\n\t\telse if (flags & FLAGS_PLUS)\n\t\t{\n\t\t\tbuf[len++] = '+'; // ignore the space if the '+' exists\n\t\t}\n\t\telse if (flags & FLAGS_SPACE)\n\t\t{\n\t\t\tbuf[len++] = ' ';\n\t\t}\n\t}\n\n\treturn _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);\n}\n\n#if defined(PRINTF_SUPPORT_EXPONENTIAL)\n// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse <m.jasperse@gmail.com>\nstatic size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags)\n{\n\t// check for NaN and special values\n\tif ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX))\n\t{\n\t\treturn _ftoa(out, buffer, idx, maxlen, value, prec, width, flags);\n\t}\n\n\t// determine the sign\n\tconst bool negative = value < 0;\n\tif (negative)\n\t{\n\t\tvalue = -value;\n\t}\n\n\t// default precision\n\tif (!(flags & FLAGS_PRECISION))\n\t{\n\t\tprec = PRINTF_DEFAULT_FLOAT_PRECISION;\n\t}\n\n\t// determine the decimal exponent\n\t// based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)\n\tunion {\n\t\tuint64_t U;\n\t\tdouble F;\n\t} conv;\n\n\tconv.F = value;\n\tint exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023;\t  // effectively log2\n\tconv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2)\n\t// now approximate log10 from the log2 integer part and an expansion of ln around 1.5\n\tint expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168);\n\t// now we want to compute 10^expval but we want to be sure it won't overflow\n\texp2 = (int)(expval * 3.321928094887362 + 0.5);\n\tconst double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;\n\tconst double z2 = z * z;\n\tconv.U = (uint64_t)(exp2 + 1023) << 52U;\n\t// compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex\n\tconv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));\n\t// correct for rounding errors\n\tif (value < conv.F)\n\t{\n\t\texpval--;\n\t\tconv.F /= 10;\n\t}\n\n\t// the exponent format is \"%+03d\" and largest value is \"307\", so set aside 4-5 characters\n\tunsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U;\n\n\t// in \"%g\" mode, \"prec\" is the number of *significant figures* not decimals\n\tif (flags & FLAGS_ADAPT_EXP)\n\t{\n\t\t// do we want to fall-back to \"%f\" mode?\n\t\tif ((value >= 1e-4) && (value < 1e6))\n\t\t{\n\t\t\tif ((int)prec > expval)\n\t\t\t{\n\t\t\t\tprec = (unsigned)((int)prec - expval - 1);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tprec = 0;\n\t\t\t}\n\t\t\tflags |= FLAGS_PRECISION; // make sure _ftoa respects precision\n\t\t\t// no characters in exponent\n\t\t\tminwidth = 0U;\n\t\t\texpval = 0;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// we use one sigfig for the whole part\n\t\t\tif ((prec > 0) && (flags & FLAGS_PRECISION))\n\t\t\t{\n\t\t\t\t--prec;\n\t\t\t}\n\t\t}\n\t}\n\n\t// will everything fit?\n\tunsigned int fwidth = width;\n\tif (width > minwidth)\n\t{\n\t\t// we didn't fall-back so subtract the characters required for the exponent\n\t\tfwidth -= minwidth;\n\t}\n\telse\n\t{\n\t\t// not enough characters, so go back to default sizing\n\t\tfwidth = 0U;\n\t}\n\tif ((flags & FLAGS_LEFT) && minwidth)\n\t{\n\t\t// if we're padding on the right, DON'T pad the floating part\n\t\tfwidth = 0U;\n\t}\n\n\t// rescale the float value\n\tif (expval)\n\t{\n\t\tvalue /= conv.F;\n\t}\n\n\t// output the floating part\n\tconst size_t start_idx = idx;\n\tidx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP);\n\n\t// output the exponent part\n\tif (minwidth)\n\t{\n\t\t// output the exponential symbol\n\t\tout((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen);\n\t\t// output the exponent value\n\t\tidx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth - 1, FLAGS_ZEROPAD | FLAGS_PLUS);\n\t\t// might need to right-pad spaces\n\t\tif (flags & FLAGS_LEFT)\n\t\t{\n\t\t\twhile (idx - start_idx < width)\n\t\t\t\tout(' ', buffer, idx++, maxlen);\n\t\t}\n\t}\n\treturn idx;\n}\n#endif // PRINTF_SUPPORT_EXPONENTIAL\n#endif // PRINTF_SUPPORT_FLOAT\n\n// internal vsnprintf\nstatic int _vsnprintf(out_fct_type out, char *buffer, const size_t maxlen, const char *format, va_list va)\n{\n\tunsigned int flags, width, precision, n;\n\tsize_t idx = 0U;\n\n\tif (!buffer)\n\t{\n\t\t// use null output function\n\t\tout = _out_null;\n\t}\n\n\twhile (*format)\n\t{\n\t\t// format specifier?  %[flags][width][.precision][length]\n\t\tif (*format != '%')\n\t\t{\n\t\t\t// no\n\t\t\tout(*format, buffer, idx++, maxlen);\n\t\t\tformat++;\n\t\t\tcontinue;\n\t\t}\n\t\telse\n\t\t{\n\t\t\t// yes, evaluate it\n\t\t\tformat++;\n\t\t}\n\n\t\t// evaluate flags\n\t\tflags = 0U;\n\t\tdo\n\t\t{\n\t\t\tswitch (*format)\n\t\t\t{\n\t\t\tcase '0':\n\t\t\t\tflags |= FLAGS_ZEROPAD;\n\t\t\t\tformat++;\n\t\t\t\tn = 1U;\n\t\t\t\tbreak;\n\t\t\tcase '-':\n\t\t\t\tflags |= FLAGS_LEFT;\n\t\t\t\tformat++;\n\t\t\t\tn = 1U;\n\t\t\t\tbreak;\n\t\t\tcase '+':\n\t\t\t\tflags |= FLAGS_PLUS;\n\t\t\t\tformat++;\n\t\t\t\tn = 1U;\n\t\t\t\tbreak;\n\t\t\tcase ' ':\n\t\t\t\tflags |= FLAGS_SPACE;\n\t\t\t\tformat++;\n\t\t\t\tn = 1U;\n\t\t\t\tbreak;\n\t\t\tcase '#':\n\t\t\t\tflags |= FLAGS_HASH;\n\t\t\t\tformat++;\n\t\t\t\tn = 1U;\n\t\t\t\tbreak;\n\t\t\tdefault:\n\t\t\t\tn = 0U;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t} while (n);\n\n\t\t// evaluate width field\n\t\twidth = 0U;\n\t\tif (_is_digit(*format))\n\t\t{\n\t\t\twidth = _atoi(&format);\n\t\t}\n\t\telse if (*format == '*')\n\t\t{\n\t\t\tconst int w = va_arg(va, int);\n\t\t\tif (w < 0)\n\t\t\t{\n\t\t\t\tflags |= FLAGS_LEFT; // reverse padding\n\t\t\t\twidth = (unsigned int)-w;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\twidth = (unsigned int)w;\n\t\t\t}\n\t\t\tformat++;\n\t\t}\n\n\t\t// evaluate precision field\n\t\tprecision = 0U;\n\t\tif (*format == '.')\n\t\t{\n\t\t\tflags |= FLAGS_PRECISION;\n\t\t\tformat++;\n\t\t\tif (_is_digit(*format))\n\t\t\t{\n\t\t\t\tprecision = _atoi(&format);\n\t\t\t}\n\t\t\telse if (*format == '*')\n\t\t\t{\n\t\t\t\tconst int prec = (int)va_arg(va, int);\n\t\t\t\tprecision = prec > 0 ? (unsigned int)prec : 0U;\n\t\t\t\tformat++;\n\t\t\t}\n\t\t}\n\n\t\t// evaluate length field\n\t\tswitch (*format)\n\t\t{\n\t\tcase 'l':\n\t\t\tflags |= FLAGS_LONG;\n\t\t\tformat++;\n\t\t\tif (*format == 'l')\n\t\t\t{\n\t\t\t\tflags |= FLAGS_LONG_LONG;\n\t\t\t\tformat++;\n\t\t\t}\n\t\t\tbreak;\n\t\tcase 'h':\n\t\t\tflags |= FLAGS_SHORT;\n\t\t\tformat++;\n\t\t\tif (*format == 'h')\n\t\t\t{\n\t\t\t\tflags |= FLAGS_CHAR;\n\t\t\t\tformat++;\n\t\t\t}\n\t\t\tbreak;\n#if defined(PRINTF_SUPPORT_PTRDIFF_T)\n\t\tcase 't':\n\t\t\tflags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);\n\t\t\tformat++;\n\t\t\tbreak;\n#endif\n\t\tcase 'j':\n\t\t\tflags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);\n\t\t\tformat++;\n\t\t\tbreak;\n\t\tcase 'z':\n\t\t\tflags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);\n\t\t\tformat++;\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tbreak;\n\t\t}\n\n\t\t// evaluate specifier\n\t\tswitch (*format)\n\t\t{\n\t\tcase 'd':\n\t\tcase 'i':\n\t\tcase 'u':\n\t\tcase 'x':\n\t\tcase 'X':\n\t\tcase 'o':\n\t\tcase 'b':\n\t\t{\n\t\t\t// set the base\n\t\t\tunsigned int base;\n\t\t\tif (*format == 'x' || *format == 'X')\n\t\t\t{\n\t\t\t\tbase = 16U;\n\t\t\t}\n\t\t\telse if (*format == 'o')\n\t\t\t{\n\t\t\t\tbase = 8U;\n\t\t\t}\n\t\t\telse if (*format == 'b')\n\t\t\t{\n\t\t\t\tbase = 2U;\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\tbase = 10U;\n\t\t\t\tflags &= ~FLAGS_HASH; // no hash for dec format\n\t\t\t}\n\t\t\t// uppercase\n\t\t\tif (*format == 'X')\n\t\t\t{\n\t\t\t\tflags |= FLAGS_UPPERCASE;\n\t\t\t}\n\n\t\t\t// no plus or space flag for u, x, X, o, b\n\t\t\tif ((*format != 'i') && (*format != 'd'))\n\t\t\t{\n\t\t\t\tflags &= ~(FLAGS_PLUS | FLAGS_SPACE);\n\t\t\t}\n\n\t\t\t// ignore '0' flag when precision is given\n\t\t\tif (flags & FLAGS_PRECISION)\n\t\t\t{\n\t\t\t\tflags &= ~FLAGS_ZEROPAD;\n\t\t\t}\n\n\t\t\t// convert the integer\n\t\t\tif ((*format == 'i') || (*format == 'd'))\n\t\t\t{\n\t\t\t\t// signed\n\t\t\t\tif (flags & FLAGS_LONG_LONG)\n\t\t\t\t{\n#if defined(PRINTF_SUPPORT_LONG_LONG)\n\t\t\t\t\tconst long long value = va_arg(va, long long);\n\t\t\t\t\tidx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);\n#endif\n\t\t\t\t}\n\t\t\t\telse if (flags & FLAGS_LONG)\n\t\t\t\t{\n\t\t\t\t\tconst long value = va_arg(va, long);\n\t\t\t\t\tidx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tconst int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int);\n\t\t\t\t\tidx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);\n\t\t\t\t}\n\t\t\t}\n\t\t\telse\n\t\t\t{\n\t\t\t\t// unsigned\n\t\t\t\tif (flags & FLAGS_LONG_LONG)\n\t\t\t\t{\n#if defined(PRINTF_SUPPORT_LONG_LONG)\n\t\t\t\t\tidx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags);\n#endif\n\t\t\t\t}\n\t\t\t\telse if (flags & FLAGS_LONG)\n\t\t\t\t{\n\t\t\t\t\tidx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags);\n\t\t\t\t}\n\t\t\t\telse\n\t\t\t\t{\n\t\t\t\t\tconst unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int);\n\t\t\t\t\tidx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags);\n\t\t\t\t}\n\t\t\t}\n\t\t\tformat++;\n\t\t\tbreak;\n\t\t}\n#if defined(PRINTF_SUPPORT_FLOAT)\n\t\tcase 'f':\n\t\tcase 'F':\n\t\t\tif (*format == 'F')\n\t\t\t\tflags |= FLAGS_UPPERCASE;\n\t\t\tidx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);\n\t\t\tformat++;\n\t\t\tbreak;\n#if defined(PRINTF_SUPPORT_EXPONENTIAL)\n\t\tcase 'e':\n\t\tcase 'E':\n\t\tcase 'g':\n\t\tcase 'G':\n\t\t\tif ((*format == 'g') || (*format == 'G'))\n\t\t\t\tflags |= FLAGS_ADAPT_EXP;\n\t\t\tif ((*format == 'E') || (*format == 'G'))\n\t\t\t\tflags |= FLAGS_UPPERCASE;\n\t\t\tidx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);\n\t\t\tformat++;\n\t\t\tbreak;\n#endif // PRINTF_SUPPORT_EXPONENTIAL\n#endif // PRINTF_SUPPORT_FLOAT\n\t\tcase 'c':\n\t\t{\n\t\t\tunsigned int l = 1U;\n\t\t\t// pre padding\n\t\t\tif (!(flags & FLAGS_LEFT))\n\t\t\t{\n\t\t\t\twhile (l++ < width)\n\t\t\t\t{\n\t\t\t\t\tout(' ', buffer, idx++, maxlen);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// char output\n\t\t\tout((char)va_arg(va, int), buffer, idx++, maxlen);\n\t\t\t// post padding\n\t\t\tif (flags & FLAGS_LEFT)\n\t\t\t{\n\t\t\t\twhile (l++ < width)\n\t\t\t\t{\n\t\t\t\t\tout(' ', buffer, idx++, maxlen);\n\t\t\t\t}\n\t\t\t}\n\t\t\tformat++;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase 's':\n\t\t{\n\t\t\tconst char *p = va_arg(va, char *);\n\t\t\tunsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1);\n\t\t\t// pre padding\n\t\t\tif (flags & FLAGS_PRECISION)\n\t\t\t{\n\t\t\t\tl = (l < precision ? l : precision);\n\t\t\t}\n\t\t\tif (!(flags & FLAGS_LEFT))\n\t\t\t{\n\t\t\t\twhile (l++ < width)\n\t\t\t\t{\n\t\t\t\t\tout(' ', buffer, idx++, maxlen);\n\t\t\t\t}\n\t\t\t}\n\t\t\t// string output\n\t\t\twhile ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--))\n\t\t\t{\n\t\t\t\tout(*(p++), buffer, idx++, maxlen);\n\t\t\t}\n\t\t\t// post padding\n\t\t\tif (flags & FLAGS_LEFT)\n\t\t\t{\n\t\t\t\twhile (l++ < width)\n\t\t\t\t{\n\t\t\t\t\tout(' ', buffer, idx++, maxlen);\n\t\t\t\t}\n\t\t\t}\n\t\t\tformat++;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase 'p':\n\t\t{\n\t\t\twidth = sizeof(void *) * 2U;\n\t\t\tflags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE;\n#if defined(PRINTF_SUPPORT_LONG_LONG)\n\t\t\tconst bool is_ll = sizeof(uintptr_t) == sizeof(long long);\n\t\t\tif (is_ll)\n\t\t\t{\n\t\t\t\tidx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void *), false, 16U, precision, width, flags);\n\t\t\t}\n\t\t\telse\n\t\t\t{\n#endif\n\t\t\t\tidx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void *)), false, 16U, precision, width, flags);\n#if defined(PRINTF_SUPPORT_LONG_LONG)\n\t\t\t}\n#endif\n\t\t\tformat++;\n\t\t\tbreak;\n\t\t}\n\n\t\tcase '%':\n\t\t\tout('%', buffer, idx++, maxlen);\n\t\t\tformat++;\n\t\t\tbreak;\n\n\t\tdefault:\n\t\t\tout(*format, buffer, idx++, maxlen);\n\t\t\tformat++;\n\t\t\tbreak;\n\t\t}\n\t}\n\n\t// termination\n\tout((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);\n\n\t// return written chars without terminating \\0\n\treturn (int)idx;\n}\n\n///////////////////////////////////////////////////////////////////////////////\n\nint printf(const char *format, ...)\n{\n\tva_list va;\n\tva_start(va, format);\n\tchar buffer[1];\n\tconst int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va);\n\tva_end(va);\n\treturn ret;\n}\n\nint sprintf(char *buffer, const char *format, ...)\n{\n\tva_list va;\n\tva_start(va, format);\n\tconst int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va);\n\tva_end(va);\n\treturn ret;\n}\n\nint snprintf(char *buffer, size_t count, const char *format, ...)\n{\n\tva_list va;\n\tva_start(va, format);\n\tconst int ret = _vsnprintf(_out_buffer, buffer, count, format, va);\n\tva_end(va);\n\treturn ret;\n}\n\nint vprintf_(const char *format, va_list va)\n{\n\tchar buffer[1];\n\treturn _vsnprintf(_out_char, buffer, (size_t)-1, format, va);\n}\n\nint vsnprintf_(char *buffer, size_t count, const char *format, va_list va)\n{\n\treturn _vsnprintf(_out_buffer, buffer, count, format, va);\n}\n\nint fctprintf(void (*out)(char character, void *arg), void *arg, const char *format, ...)\n{\n\tva_list va;\n\tva_start(va, format);\n\tconst out_fct_wrap_type out_fct_wrap = {out, arg};\n\tconst int ret = _vsnprintf(_out_fct, (char *)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va);\n\tva_end(va);\n\treturn ret;\n}\n"
  },
  {
    "path": "risc_v/userspace/startlib/printf.h",
    "content": "///////////////////////////////////////////////////////////////////////////////\n// \\author (c) Marco Paland (info@paland.com)\n//             2014-2019, PALANDesign Hannover, Germany\n//\n// \\license The MIT License (MIT)\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n// \n// The above copyright notice and this permission notice shall be included in\n// all copies or substantial portions of the Software.\n// \n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\n// THE SOFTWARE.\n//\n// \\brief Tiny printf, sprintf and snprintf implementation, optimized for speed on\n//        embedded systems with a very limited resources.\n//        Use this instead of bloated standard/newlib printf.\n//        These routines are thread safe and reentrant.\n//\n///////////////////////////////////////////////////////////////////////////////\n#pragma once\n#ifndef _PRINTF_H_\n#define _PRINTF_H_\n\n#include <stdarg.h>\n#include <stddef.h>\n\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n\n/**\n * Output a character to a custom device like UART, used by the printf() function\n * This function is declared here only. You have to write your custom implementation somewhere\n * \\param character Character to output\n */\nvoid _putchar(char character);\n\n\n/**\n * Tiny printf implementation\n * You have to implement _putchar if you use printf()\n * To avoid conflicts with the regular printf() API it is overridden by macro defines\n * and internal underscore-appended functions like printf_() are used\n * \\param format A string that specifies the format of the output\n * \\return The number of characters that are written into the array, not counting the terminating null character\n */\n//int cosc361_printf(const char* format, ...);\nint printf(const char *format, ...);\n\n/**\n * Tiny sprintf implementation\n * Due to security reasons (buffer overflow) YOU SHOULD CONSIDER USING (V)SNPRINTF INSTEAD!\n * \\param buffer A pointer to the buffer where to store the formatted string. MUST be big enough to store the output!\n * \\param format A string that specifies the format of the output\n * \\return The number of characters that are WRITTEN into the buffer, not counting the terminating null character\n */\nint sprintf(char* buffer, const char* format, ...);\n\n\n/**\n * Tiny snprintf/vsnprintf implementation\n * \\param buffer A pointer to the buffer where to store the formatted string\n * \\param count The maximum number of characters to store in the buffer, including a terminating null character\n * \\param format A string that specifies the format of the output\n * \\param va A value identifying a variable arguments list\n * \\return The number of characters that COULD have been written into the buffer, not counting the terminating\n *         null character. A value equal or larger than count indicates truncation. Only when the returned value\n *         is non-negative and less than count, the string has been completely written.\n */\nint snprintf(char* buffer, size_t count, const char* format, ...);\nint vsnprintf(char* buffer, size_t count, const char* format, va_list va);\n\n\n/**\n * Tiny vprintf implementation\n * \\param format A string that specifies the format of the output\n * \\param va A value identifying a variable arguments list\n * \\return The number of characters that are WRITTEN into the buffer, not counting the terminating null character\n */\n#define vprintf vprintf_\nint vprintf_(const char* format, va_list va);\n\n\n/**\n * printf with output function\n * You may use this as dynamic alternative to printf() with its fixed _putchar() output\n * \\param out An output function which takes one character and an argument pointer\n * \\param arg An argument pointer for user data passed to output function\n * \\param format A string that specifies the format of the output\n * \\return The number of characters that are sent to the output function, not counting the terminating null character\n */\nint fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...);\n\n\n#ifdef __cplusplus\n}\n#endif\n\n\n#endif  // _PRINTF_H_\n"
  },
  {
    "path": "risc_v/userspace/startlib/start.S",
    "content": "\n.section .text\n.global _start\n_start:\n.option push\n.option norelax\n\tla\tgp, __global_pointer$\n.option pop\n\tcall\tmain\n\t# Exit system call after main\n\tli\ta0, 93\n\tj \tmake_syscall\n.type _start, function\n.size _start, .-_start\n"
  },
  {
    "path": "risc_v/userspace/startlib/syscall.S",
    "content": ".section .text\n.global make_syscall\nmake_syscall:\n\tmv a7, a0\n\tmv a0, a1\n\tmv a1, a2\n\tmv a2, a3\n\tmv a3, a4\n\tmv a4, a5\n\tmv a5, a6\n\tecall\n\tret\n.type make_syscall, function\n.size make_syscall, .-make_syscall\n"
  },
  {
    "path": "risc_v/userspace/startlib/syscall.h",
    "content": "#pragma once\n\nextern \"C\"\n{\n\tunsigned long make_syscall(unsigned long sysno,\n\t\t\t\t   unsigned long a1=0,\n\t\t\t\t   unsigned long a2=0,\n\t\t\t\t   unsigned long a3=0,\n\t\t\t\t   unsigned long a4=0,\n\t\t\t\t   unsigned long a5=0,\n\t\t\t\t   unsigned long a6=0);\n}\n#define syscall_exit()\t\tmake_syscall(93)\n#define syscall_get_char()\tmake_syscall(1)\n#define syscall_put_char(x)\tmake_syscall(2, (unsigned long)x)\n#define syscall_yield()\t\tmake_syscall(9)\n#define syscall_sleep(x)\tmake_syscall(10, (unsigned long)x)\n#define syscall_get_fb(x)\tmake_syscall(1000, (unsigned long)x)\n#define syscall_inv_rect(d, x, y, w, h) make_syscall(1001, (unsigned long) d, (unsigned long)x, (unsigned long)y, (unsigned long)w, (unsigned long)h)\n#define syscall_get_key(x, y)\tmake_syscall(1002, (unsigned long)x, (unsigned long)y)\n#define syscall_get_abs(x, y)\tmake_syscall(1004, (unsigned long)x, (unsigned long)y)\n#define syscall_get_time()  make_syscall(1062)\n\n"
  },
  {
    "path": "risc_v/userspace/upload.sh",
    "content": "#!/bin/sh\n\nif [ $# -ne 1 ]; then\n\techo \"You provied $# parameters, need 1\"\n\texit 1\nfi\n\nif [ ! -r $1 ]; then\n\techo \"Unknown file $1\"\n\texit 2\nfi\n\nif [ $UID -ne 0 ]; then\n\techo \"You are not running as root, this might not work.\"\nfi\n\nlosetup /dev/loop0 ../hdd.dsk\nmount /dev/loop0 /mnt\ncp $1 /mnt\numount /dev/loop0\nlosetup -d /dev/loop0\n"
  }
]