Repository: sos-os/kernel Branch: master Commit: 641b83fb0785 Files: 125 Total size: 487.0 KB Directory structure: gitextract_7fg8ki17/ ├── .editorconfig ├── .gitignore ├── .gitmodules ├── .travis.yml ├── BUILDING.md ├── Brewfile ├── CHANGES.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── Makefile ├── Notes.md ├── README.md ├── Vagrantfile ├── Xargo.toml ├── boot/ │ ├── Cargo.toml │ ├── README.md │ └── src/ │ └── lib.rs ├── build.rs ├── cpu/ │ ├── Cargo.toml │ └── src/ │ ├── armv7/ │ │ └── mod.rs │ ├── lib.rs │ ├── x86/ │ │ ├── cpu.rs │ │ └── mod.rs │ ├── x86_64/ │ │ ├── context.rs │ │ ├── mod.rs │ │ ├── msr.rs │ │ └── task.rs │ └── x86_all/ │ ├── control_regs/ │ │ ├── cr0.rs │ │ ├── cr4.rs │ │ └── mod.rs │ ├── dtable.rs │ ├── flags.rs │ ├── interrupts/ │ │ ├── idt/ │ │ │ ├── gate32.rs │ │ │ ├── gate64.rs │ │ │ ├── mod.rs │ │ │ └── tests.rs │ │ ├── mod.rs │ │ └── pics.rs │ ├── mod.rs │ ├── segment.rs │ └── timer.rs ├── elf/ │ ├── Cargo.toml │ ├── README.md │ └── src/ │ ├── file.rs │ ├── lib.rs │ ├── program.rs │ └── section.rs ├── memory/ │ ├── Cargo.toml │ └── src/ │ ├── arch/ │ │ ├── mod.rs │ │ ├── x86/ │ │ │ └── mod.rs │ │ └── x86_64/ │ │ └── mod.rs │ ├── lib.rs │ └── macros.rs ├── paging/ │ ├── Cargo.toml │ └── src/ │ ├── arch/ │ │ ├── mod.rs │ │ └── x86_64/ │ │ ├── cr3.rs │ │ ├── mod.rs │ │ ├── table.rs │ │ ├── temp.rs │ │ └── tlb.rs │ ├── lib.rs │ └── stack.rs ├── params/ │ ├── Cargo.toml │ └── src/ │ ├── lib.rs │ └── mem.rs ├── rustfmt.toml ├── scripts/ │ ├── README.md │ ├── install-env-linux.sh │ ├── install-env-mac.sh │ └── install-env.sh ├── sos_alloc/ │ ├── Cargo.toml │ └── src/ │ ├── borrow.rs │ ├── buddy/ │ │ ├── math.rs │ │ ├── mod.rs │ │ ├── system.rs │ │ └── test.rs │ ├── bump_ptr.rs │ ├── first_fit.rs │ ├── frame/ │ │ ├── mem_map.rs │ │ └── mod.rs │ ├── free.rs │ ├── lib.rs │ ├── place.rs │ └── system.rs ├── sos_intrusive/ │ ├── Cargo.toml │ └── src/ │ ├── lib.rs │ ├── list/ │ │ ├── mod.rs │ │ └── test.rs │ ├── rawlink.rs │ └── stack/ │ ├── mod.rs │ └── test.rs ├── src/ │ ├── arch/ │ │ ├── README.md │ │ ├── mod.rs │ │ ├── x86_64/ │ │ │ ├── boot.asm │ │ │ ├── drivers/ │ │ │ │ ├── mod.rs │ │ │ │ ├── serial.rs │ │ │ │ └── vga.rs │ │ │ ├── grub.cfg │ │ │ ├── interrupts.rs │ │ │ ├── linker.ld │ │ │ └── mod.rs │ │ └── x86_all/ │ │ ├── bda.rs │ │ └── multiboot2.rs │ ├── heap.rs │ ├── io/ │ │ ├── keyboard.rs │ │ ├── mod.rs │ │ └── term.rs │ ├── logger.rs │ └── main.rs ├── targets/ │ ├── x86_32-sos-bootstrap-gnu.json │ └── x86_64-sos-kernel-gnu.json ├── tokamak.toml ├── util/ │ ├── Cargo.toml │ └── src/ │ ├── io.rs │ ├── lib.rs │ └── macros/ │ ├── mod.rs │ └── newtype_impl.rs └── vga/ ├── Cargo.toml └── src/ ├── kinfo.rs ├── lib.rs ├── panic.rs └── status.rs ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # EditorConfig helps developers define and maintain consistent # coding styles between different editors and IDEs # editorconfig.org root = true ### Makefiles ####################################################### [Makefile] indent_style = tab # Makefiles must be indented with tabs. ### Rust source code files ########################################## # Based on @xtian's .editorconfig file at # https://gist.github.com/xtian/339112af8a11d0583d81 # and on the Rust style guidelines at # https://github.com/rust-lang/rust/tree/master/src/doc/style/style/ [*.rs] end_of_line = lf charset = utf-8 trim_trailing_whitespace = true indent_style = space indent_size = 4 ### Assembly language files ######################################### [*.asm] end_of_line = lf charset = utf-8 trim_trailing_whitespace = true indent_style = space indent_size = 4 ================================================ FILE: .gitignore ================================================ # Created by https://www.gitignore.io/api/assembler,rust,emacs,osx,sublimetext,tags,vagrant,vim,linux ### Kernel binaries ### *.iso *.bin *.elf # serial output serial-*.log ### Assembler ### *.[sS] *.bc ### Rust ### # Compiled files *.o *.so *.rlib *.dll # Executables *.exe # Generated by Cargo /target/ **/target/ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock Cargo.lock ### Emacs ### # -*- mode: gitignore; -*- *~ \#*\# /.emacs.desktop /.emacs.desktop.lock *.elc auto-save-list tramp .\#* # Org-mode .org-id-locations *_archive # flymake-mode *_flymake.* # eshell files /eshell/history /eshell/lastdir # elpa packages /elpa/ # reftex files *.rel # AUCTeX auto folder /auto/ # cask packages .cask/ # Flycheck flycheck_*.el # server auth directory /server/ ### OSX ### .DS_Store .AppleDouble .LSOverride # Icon must end with two \r Icon # Thumbnails ._* # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk ### SublimeText ### # cache files for sublime text *.tmlanguage.cache *.tmPreferences.cache *.stTheme.cache # workspace files are user-specific *.sublime-workspace # project files should be checked into the repository, unless a significant # proportion of contributors will probably not be using SublimeText # *.sublime-project # sftp configuration file sftp-config.json ### Tags ### # Ignore tags created by etags, ctags, gtags (GNU global) and cscope TAGS .TAGS !TAGS/ tags .tags !tags/ gtags.files GTAGS GRTAGS GPATH cscope.files cscope.out cscope.in.out cscope.po.out ### Vagrant ### .vagrant/ ### Vim ### # swap [._]*.s[a-w][a-z] [._]s[a-w][a-z] # session Session.vim # temporary .netrwhist *~ # auto-generated tag files tags .tags1 ### GDB ### .gdb_history ### Linux ### *~ # temporary files which can be created if a process still has a handle open of a deleted file .fuse_hidden* # KDE directory preferences .directory # Linux trash folder which might appear on any partition or disk .Trash-* .idea/markdown-navigator.xml .idea/markdown-navigator/profiles_settings.xml .idea/modules.xml .idea/misc.xml .idea/sos-kernel.iml .idea/vcs.xml .idea/workspace.xml .vscode/tasks.json ================================================ FILE: .gitmodules ================================================ ================================================ FILE: .travis.yml ================================================ language: rust rust: nightly # matrix: # allow_failures: # - os: osx # fast_finish: true cache: cargo: true directories: - /usr/local/bin/x86_64-pc-elf-* - /usr/local/bin/grub* notifications: webhooks: urls: - https://webhooks.gitter.im/e/59a4cbad9f4177de9a91 on_success: change on_failure: always on_start: never email: on_success: never on_failure: change slack: secure: CTfCGsKzNyb7SqEGeXYCnwE0jZLcANMjgdZpnpDMHtyBzOb0EJ1VXe5FpCVZ74jPD0De/eWwxT3aVnxZGnIn24gIB4K+3km4s+mSiecfhtP6lhHKaxy25NNKqcVJMt+52GgBYRvF9lPDdQR/I+YPznKeKkGCI8KMK8Y61RTMF1gWHezqsDirVemS+qp4YP8MrtcUk8W0yw+K522MuCLBFBy/LX1LrkZjkM1yyDIWmS7UerU1A0mWmDgiwfO33jJPtJKj64HrVwbOjl9ZH9SkEMeHumBsocxZ+3EPx8qygzJfEAMbWV6nu816e5Jg58UBsKrm5rPKdHmSshrvme7L79mGgfaROKF94OzIL67HFs+3vFXG5GspQz6myA+HtQMCO03dS7VydB6//Tpr9okWHsCBGgRgDnuvXWk/HG2kSP1uSDhrxqmBya/kTSaOa+QRqUnF6XxknxX5DCbGKIVY9fN5+IxDBVnigVYAlHe+NreT3xNeLd7E3p1TL865SYpU4szJ1KLtq+JX+DAAYF94UoIt4fD/kQ0tYhwKSJtUNfFRs5MubqewNpkw/Pt2d9gPIw9+hP7c6w/4gd3lDuVfMwhZwsXfRX9OvEemFET4dK+VHy5DqH00DJ/3tQaJ2dDeIHUtFMq1V93aVjspMDj+dwVctQ5DhIXHaYKmrc4yQkA= addons: apt: packages: - nasm - build-essential - xorriso # - libssh2-1 before_install: | objcopy=$(which objcopy) && strip=$(which strip) && mkdir -p $HOME/bin && ln -s $objcopy $HOME/bin/x86_64-elf-objcopy && ln -s $strip $HOME/bin/x86_64-elf-strip install: ### install the "rust-src" rustup component ############################### # xargo uses this for building sysroots # before installing rust-src we need to get `rustup` - curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain nightly - source ~/.cargo/env # add the rust-src component - rustup component add rust-src ### install `xargo` ###################################################### - cargo install xargo --force - export PATH="$PATH:$(rustc --print sysroot)/cargo/bin:$HOME/.cargo/bin" script: make test jobs: include: - stage: build script: make kernel deploy: provider: releases api_key: secure: DQ6EChkuPdDbCkPQIOVmq7SBxTG04GeoUXeguCb3RcX+lT5FqTG/2bAnElAxE+KHkLpxBav+CIaHyPQzT93LN70PSV0Vny8Oo3OCOprSn3gph12ZXPeebFk+EFArju3N/Du/cxlNK6zQ71Sw1Jr+nmutjpCGkN2ki04A6iY5f0cLmgsUXZxVBzMY4mb2tihAvH+0HgR96mmQ6xEvWU3mXnHvgpJhSwSHJlZao7C51vq0sr5bKPNVVHfwN7OlqG3uJYWcnFIlrkGbZyVtNZ//CjtGPhKufn+l1JOdQo47/Lh+iHPUNrWGsvNiy7yXrBdsVKI0oJwfssHrMPrXyWphk9wrD8ID1wpKXPxDikVB3uSU1OfQbSSsH+aF7i3mNwJSG4ORTjZeuBQpC4F9ZUAr2uS6RR+bW9tnb6/ZRQA0vqQVS1oi5miIHLe9FbMccOZLWYyHM6Lts3cd4zEldZTvPUmO7M5oP4Woo0DPRWvq48f7QW9x0ewjVRjfUkPZ8+H5BfFfqXQXZYtDRfTruVaYtS/We3QggvLaNZJOmq+jJQqrSARK5tB/HD8YZvu9Z1XU8UTcm6ORvlEsAMIk7+orUU1L4ibO/Tr1wEU9fgS2k9rCDPhfMljgn2EqBRfULnwqbzSwPrZmKyJlVZ6GsJtCAC1XQQCbZIMVShVbvL6JEKU= file: build/os-x86_64.iso skip_cleanup: true on: tags: true repo: hawkw/sos-kernel - stage: docs install: skip script: cargo doc deploy: provider: pages skip_cleanup: true github_token: $GH_TOKEN # Set in travis-ci.org dashboard target_branch: gh-pages local_dir: target/doc on: branch: master repo: hawkw/sos-kernel env: global: # override the default `--features unstable` used for the nightly branch (optional) - TRAVIS_CARGO_NIGHTLY_FEATURE="" - PATH="$PATH:$HOME/bin" - secure: Vv4bFCkxM3o7FpKjcxTzQRWdkp5aSfrkmFcxSRLYFhm8tyeh+TRiC2+ucNfscFeOzPTC/hTwatbbpdTIaAZKpyrD/gB4hNiGFdKV1x1pw23KQkiPmQdFKxAHtIS20jeIa44q6kPltTQtZXXuM8s0xc6r+C14Q6igd4HkD0nLhAlKIQpcS/srXoeMheL3vNIroasHUwFgpv1LLJnMqYhCHzNk2Y6CaxYygg5Cy2gS9G1nfxdNVfF4iaUXYZKgU0s3C1GL0ApTtUxW0uT/NS0UBZwttsq9NvwmZSZQIXpfTnn0wHd4cl8MhokUQNEumFouU5rlEIcFS9ulohxaHnn50cqfos+seblKs2E+MJoX8rBO5Kz3dNE3+otZw9CpImskO4EKA2MYwtTC3Ds4uVoleS9PskCMz32jKXqp6WqZkJgpimHrdv0jNBy2ucTMdw91XjRXLA557GPRa/28TPpKkMYrSB02c2IadftexWKDP5Q9uMsfTEgIpXzx/XRQvPoxtJo+rOn9hTbyHWexKEMAuic12FBoYFOMSbRKtpT8kjtd8WeBzc1RmXZYpg5td5EN3v/2fmvM6F6Sz/db4HgNZIccC/j/fWvXN7njRl8bgMC002/tkQs0Ugl1aE5onaK9DTMHquqCLnd9CADwXdQf1YsfxryjhhBfd1WIfuqe1Ww= ================================================ FILE: BUILDING.md ================================================ setting up a build environment ============================ Unfortunately, one of the unescapable truths of OS development is that building a kernel is a pain. Even though Rust's toolchain makes building SOS much _less_ of a pain than if the kernel were written in C, setting up a build environment to build SOS is still a fairly complex process. In order to make this a bit easier, I've written some shell scripts to help install and configure SOS's build dependencies. You can run these scripts with the following `make` target: ``` $ make env ``` Once you've run `make env`, you should have a correctly configured build environment, and you should be ready to build SOS. If you'd rather install and configure everything yourself, or your system isn't supported by the automatic install scripts, you can follow these instructions to set up a suitable environment for building SOS: installing Rust --------------- In order to build SOS, you need an up-to-date version of the Rust compiler. SOS uses several gated features that can only be built on the nightly Rust release channel. The suggested way to install Rust is using [Rustup](https://www.rustup.rs), a tool for managing multiple versions of Rust. If you don't have Rustup installed, you can install it by running this command in your terminal: ``` $ curl https://sh.rustup.rs -sSf | sh ``` Once Rustup is installed, run ``` $ rustup update nightly ``` to ensure the nightly release branch is up to date. If you've set the stable or beta Rust release channels as the global default, you should run ``` $ rustup override nightly ``` in the SOS root directory, to set the nightly release channel as the default for SOS. installing build dependencies ----------------------------- Once you have Rust installed, you will need the following additional dependencies: + `ld` + `grub-mkrescue` & `xorriso` + `qemu` for running the kernel under emulation + `mtools` Depending on your OS, you'll want to install these dependencies somewhat differently. ### linux On Debian you can install them with ``` $ sudo apt-get install xorriso qemu mtools build-essential ``` On Arch Linux you can install them with ``` $ sudo pacman -S --needed binutils grub libisoburn qemu mtools ``` And on Fedora with ``` $ sudo dnf instal xorriso qemu ``` On Gentoo it's ``` $ sudo emerge app-emulation/qemu dev-libs/libisoburn sys-boot/grub ``` You will also need `x86_64-elf-objcopy` and `x86_64-elf-strip` executables. On x86_64 Linux you can simply symlink them to your regular `objcopy` and `strip`. If you are using Gentoo but on another architecture you can build a cross-compiled version of `binutils` (which contains `objcopy` and `strip`) with ``` $ sudo emerge sys-devel/crossdev $ sudo crossdev -s0 --target x86_64-elf ``` ### macOS Installing dev dependencies on macOS is slightly trickier, as you will also need a cross-compiled version of GNU `binutils` to build SOS. Cross-compiling `binutils` will require some additional dependencies. You can install a majority of dependencies using the [Homebrew](https://github.com/Homebrew/brew) package manager. I've included a `Brewfile` for automatically installing these dependencies. To use the `Brewfile`, run the following sequence of commands: ``` $ brew update $ brew tap Homebrew/bundle $ brew bundle ``` Once you've installed the `Brewfile`, you'll need to cross-compile `binutils`. I've included a script to automatically download and cross-compile `binutils`, which you can run by typing: ``` $ ./scripts/install-mac.sh ``` Alternatively, if you want to do it manually, Phil Oppermann has a tutorial [here](http://os.phil-opp.com/cross-compile-binutils.html). ### windows Seriously? Windows isn't supported; I can't possibly advise it. installing `xargo` ----------------- Once you've installed Rust and the dev dependencies, you'll need to install [`xargo`](https://github.com/japaric/xargo), a tool for cross-compiling Rust programs. You can install `xargo` quite easily by running ``` $ cargo install xargo ``` ================================================ FILE: Brewfile ================================================ # for making a bootable ISO brew 'xorriso' # for running the OS brew 'qemu' # x86_64-pc-elf cross-compile toolchain tap 'hawkw/x86_64-pc-elf' brew 'x86_64-pc-elf-gcc' # GRUB tap 'hawkw/grub' brew 'grub', args: ['with-x86_64-pc-elf', 'HEAD'] ================================================ FILE: CHANGES.md ================================================ ## elf v0.0.2 (2017-03-15) #### Bug Fixes * **elf:** * add assertion that extract_from_slice offset is T-aligned ([e7df6620](e7df6620)) * fix arbitrary lifetime in elf::extract_from_slice ([60ed6a15](60ed6a15)) * fix a typo in elf::file::Class ([99332049](99332049)) * fix validation of elf section word size ([15eb3664](15eb3664)) * make section header name offset a Word ([1583b718](1583b718)) * fix a typo in elf::file::Class ([cbb9ca9d](cbb9ca9d)) * fix validation of elf section word size ([cec0a13b](cec0a13b)) * make section header name offset a Word ([89c4edd3](89c4edd3)) * **x86_64:** make kernel_init use re-written section headers ([19f75fe9](19f75fe9)) #### Features * **elf:** * rewrite SectionHeader to be trait-based ([9d2eb03c](9d2eb03c), closes [#93](93)) * add default type parameters for Image, default word type ([4c09e7ad](4c09e7ad)) * changed extract_from_slice() to return a Result ([5e359b01](5e359b01)) * start on convert::TryFrom<&'a [u8]> for elf image ([34fba817](34fba817)) * add program headers slice ref to elf image ([caaf3245](caaf3245)) * add struct representing 32-bit program header ([cc3f38af](cc3f38af)) * add struct representation of 64-bit program header ([ee718532](ee718532)) * begin implementing elf program header ([00d49e41](00d49e41)) * add getters for more file header fields ([888d99fa](888d99fa)) * add getters for returning file header fields as usize ([e2cf28dc](e2cf28dc)) * add function to get the section header string table ([48036533](48036533)) * ELF String Table made indexable ([c03e36f0](c03e36f0)) * first pass on parsing ELF string tables ([5c266f0f](5c266f0f), closes [#83](83)) * nicer handling of ELF sections with invalid type fields ([9083e9ba](9083e9ba)) * add fmt::Display implementation for ELF sections ([4eb34a3a](4eb34a3a)) * add getters for more file header fields ([ae29fe71](ae29fe71)) * add getters for returning file header fields as usize ([3b6078fd](3b6078fd)) * add function to get the section header string table ([a8002040](a8002040)) * ELF String Table made indexable ([966d683b](966d683b)) * first pass on parsing ELF string tables ([9e5b195d](9e5b195d), closes [#83](83)) * nicer handling of ELF sections with invalid type fields ([8c07e373](8c07e373)) * add fmt::Display implementation for ELF sections ([50c04cea](50c04cea)) * **multiboot2:** add IntoIter implementations for tags ([4a546e7a](4a546e7a)) * **x86_all:** * multiboot2 mem areas iterator doesn't skip ACPI areas ([3310f1fc](3310f1fc)) * multiboot2 mem areas iterator doesn't skip ACPI areas ([2487f494](2487f494)) ================================================ FILE: CODE_OF_CONDUCT.md ================================================ # Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at [eliza@elizas.website]. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ ================================================ FILE: CONTRIBUTING.md ================================================ Contributing to SOS =================== **Looking for a first issue?** You might want to start out by looking at [issues tagged "easy"](https://github.com/hawkw/sos-kernel/issues?q=is%3Aissue+is%3Aopen+label%3Aeasy). These are issues that, while important, will probably require less knowledge of Rust, systems programming in general, or SOS, and might make good jumping-off points for potential contibutors. ### Table of Contents + [What do I need to know before contributing?](#what-do-i-need-to-know-before-contributing) - [Code of Conduct](#code-of-conduct) - [Licensing](#licensing) - [Setting Up a Dev Environment](#setting-up-a-dev-environment) + [Project Goals & Objectives](#project-goals--objectives) + [Conventions & Style Guides](#conventions--style-guides) - [Git Conventions](#git-conventions) * [Pull Requests](#pull-requests) * [Commit Messages](#commit-messages) - [Coding Style](#coding-style) * [Tools to assist with coding style](#tools-to-assist-with-coding-style) What do I need to know before contributing? =========================================== ### Code of Conduct This project adheres to the Contributor Covenant [code of conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [eliza@elizas.website](mailto:eliza@elizas.website). ### Licensing SOS is dual-licensed under the [MIT](LICENSE-MIT) and [Apache 2](LICENSE-APACHE) open-source licenses. By contributing code to SOS, you agree to waive all copyright claims on your contribution and allow it to be distributed under these licenses. ### Setting Up a Dev Environment Building an OS is often a fairly difficult process, and can require a number of specific tools, libraries, and other dependencies installed and configured on the host system. In order to make contributing to SOS as easy as possible, we've tried to streamline the development environment setup process as much as possible, but there are still a few steps required before you can build SOS. Please see [BUILDING.md](BUILDING.md) for detailed instructions on how to build SOS. In addition, the [tools to assist with coding style](#tools-to-assist-with-coding-style) section in this document provides information on optional tools that can be used to ensure your contributions conform to SOS' preferred coding style. Project Goals & Objectives ========================== + Minimise assembly language code + Ideally, files ending in `.asm` should be used ONLY for the boot sequence + When platform-specific assembly code is required after the boot sequence, it should be placed in [inline assembly](https://doc.rust-lang.org/book/inline-assembly.html) in Rust functions, not in `.asm` files. + This has the following advantages: + It requires less linker configuration and makes building SOS much less of a pain. + It makes it much easier for Rust code to call code written in assembly language without mucking around with FFI. + It encourages us to write as much code as possible in a safe, high-level language, and write assembly only when it is strictly required. + Eventually, be able to boot on x86, x86_64, and ARMv7 machines. + Move as much out of kernel space as seems reasonable. Conventions & Style Guides ========================== Git Conventions --------------- ### Pull requests In order to be accepted and merged, a pull request must meet the following conditions. ##### Pull requests MUST + Build successfully on [Travis](https://travis-ci.org/hawkw/sos-kernel) + Include RustDoc comments for any public-facing API functions or types + Include tests for any added features + Reference any closed issues with the text "Closes #XX" or "Fixes #XX" in the pull request description ##### Pull requests MUST NOT + Include any failing tests + Decrease overall project test coverage + Have any outstanding changes requested by a reviewer. ### Commit messages Commit messages should follow the [Angular.js Commit Message Conventions](https://github.com/conventional-changelog/conventional-changelog/blob/a5505865ff3dd710cf757f50530e73ef0ca641da/conventions/angular.md). We use [`clog`](https://github.com/clog-tool/clog-cli) for automatically generating changelogs, and commit messages must be in a format that `clog` can parse. It is recommended that contributors read the linked documentation for the Angular commit message convention in full –– it's not that long. For the impatient, here are some of the most important guidelines: ##### Commit messages MUST + Be in present tense + Follow the form `(): ` + where `` is one of: * **feat**: A new feature * **fix**: A bug fix * **docs**: Documentation only changes * **style**: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc) * **refactor**: A code change that neither fixes a bug or adds a feature * **perf**: A code change that improves performance * **test**: Adding missing tests * **chore**: Changes to the build process or auxiliary tools and libraries such as documentation generation + and `` (optionally) specifies the specific element or component of the project that was changed. ##### Commit messages MUST NOT + Include lines exceeding 100 characters ##### Commit messages MAY + Include the text `[skip ci]` if changing non-Rustdoc documentation. + This will cause Travis CI to skip building that commit. + Commits which change RustDoc documentation in `.rs` source code files should still be built on CI -- `[skip ci]` should only be used for commits which change external documentation files such as `README.md` + Commits which change configuration files for tools not used by Travis may also skip the CI build, at the discretion of the committer. Code Style ---------- Rust code should: + Follow the [Rust style guidelines](https://github.com/rust-lang/rust/tree/master/src/doc/style/style) and the guidelines in the ["Effective Rust" section](https://doc.rust-lang.org/book/effective-rust.html) of the Rust Book, except when contradicted by this document. + In particular, it should... + ...be indented with 4 spaces + ...not end files with trailing whitespace + ...follow the [Rust naming conventions](https://github.com/rust-lang/rust/tree/master/src/doc/style/style/) + An `.editorconfig` file is available for [compatible text editors](http://editorconfig.org/#download). + Use [comma-first style](https://gist.github.com/isaacs/357981) for all comma-delimited constructs. + Not exceed 80 characters per line. The following deviations from the style guide are permitted: + [Comma-first style](https://gist.github.com/isaacs/357981) _may_ be used for all comma-delimited constructs. For example: ```rust let a_list = [ a , b , c ]; ``` and ```rust let a_list = [ a, b, c, d , e, f, g, h ]; ``` are considered good style. + When wrapping `where` clauses, place them at the same indentation level as the corresponding `fn` or `impl` statement. For example: ```rust // Considered good style fn foo(a: A) where A: Something { ... } ``` and ```rust // Considered good style fn bar(a: A) -> B where A: Something , B: Something + SomethingElse { ... } ``` are considered good style, while ```rust // NOT considered good style fn baz(a: A) -> B where A: Something , B: SomethingElse { ... } ``` is not. ### Tools to Assist With Coding Style #### EditorConfig An [`.editorconfig` file](.editorconfig) is available for [compatible text editors](http://editorconfig.org/#download). If the EditorConfig plugin is installed in your text editor, it will use this file to automatically configure certain formatting settings for the `an-editor` repository. #### rustfmt [`rustfmt`](https://github.com/rust-lang-nursery/rustfmt) is a tool for automatically formatting Rust source code according to style guidelines. This repository provides a [`rustfmt.toml`](rustfmt.toml) file for automatically configuring `rustfmt` to use our style guidelines. `rustfmt` may be installed by running ```bash $ cargo install rustfmt ``` and invoked on a crate by running ```bash $ cargo fmt ``` Additionally, there are `rustfmt` plugins [available](https://github.com/rust-lang-nursery/rustfmt#running-rustfmt-from-your-editor) for many popular editors and IDEs. `rustfmt` may also be added as a [git pre-commit hook](https://git-scm.com/book/uz/v2/Customizing-Git-Git-Hooks) to ensure that all commits conform to the style guidelines. ================================================ FILE: Cargo.toml ================================================ [package] name = "sos_kernel" version = "0.1.0" authors = [ # hacked around by "Eliza Weisman " , # based on code by "Philipp Oppermann " ] build = "build.rs" # build = false # [[bin]] # name = "sos_kernel_full" # [lib] # crate-type = ["staticlib"] # [profile.dev] opt-level = 3 debug = true rpath = false lto = false debug-assertions = true codegen-units = 1 panic = "abort" [profile.release] opt-level = 3 debug = true rpath = false lto = false panic = "abort" [features] default = [] trace = [] [dependencies] rlibc = "0.1.4" spin = "0.4.6" once = "0.3.2" bitflags = "0.7" cpu = { path = "cpu" } memory = { path = "memory" } util = { path = "util" } elf = { path = "elf" } paging = { path = "paging" } params = { path = "params" } [dependencies.log] version = "0.3.6" default-features = false features = ["release_max_level_info"] [dependencies.lazy_static] version = "0.2.11" features = ["spin_no_std"] [dependencies.vga] path = "vga" features = ["kinfo", "system_term"] [dependencies.sos_alloc] path = "sos_alloc" features = ["buddy", "system", "borrow", "buddy_as_system"] [dependencies.clippy] version = "0.0.60" optional = true ================================================ FILE: LICENSE-APACHE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: LICENSE-MIT ================================================ Copyright (c) 2015 The SOS Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: Makefile ================================================ arch ?= x86_64 target ?= $(arch)-sos-kernel-gnu boot_target := x86_32-sos-bootstrap-gnu boot_outdir := boot/target/$(boot_target) iso := target/$(target)/debug/sos-$(arch).iso kernel := target/$(target)/debug/sos_kernel isofiles := target/$(target)/debug/isofiles boot := $(boot_outdir)/debug/libboot.a release_iso := target/$(target)/release/sos-$(arch).iso release_kernel := target/$(target)/release/sos_kernel release_isofiles := target/$(target)/release/isofiles release_boot := $(boot_outdir)/release/libboot.a grub_cfg := src/arch/$(arch)/grub.cfg TIMESTAMP := $(shell /bin/date "+%Y-%m-%d-%H:%M:%S") # wildcard paths wild_iso := target/$(target)/%/sos-$(arch).iso wild_kernel := target/$(target)/%/sos_kernel wild_isofiles := target/$(target)/%/isofiles #COLORS GREEN := $(shell tput -Txterm setaf 2) WHITE := $(shell tput -Txterm setaf 7) YELLOW := $(shell tput -Txterm setaf 3) RESET := $(shell tput -Txterm sgr0) # Add the following 'help' target to your Makefile # And add help text after each target name starting with '\#\#' # A category can be added with @category HELP_FUN = \ %help; \ while(<>) { push @{$$help{$$2 // 'options'}}, [$$1, $$3] if /^([a-zA-Z\-]+)\s*:.*\#\#(?:@([a-zA-Z\-]+))?\s(.*)$$/ }; \ print "usage: make [target]\n\n"; \ for (sort keys %help) { \ print "${WHITE}$$_:${RESET}\n"; \ for (@{$$help{$$_}}) { \ $$sep = " " x (20 - length $$_->[0]); \ print " ${YELLOW}$$_->[0]${RESET}$$sep${GREEN}$$_->[1]${RESET}\n"; \ }; \ print "\n"; } .PHONY: all clean kernel run iso cargo help gdb test doc release-iso release-run release-kernel exception: $(iso) ##@build Run the kernel, dumping the state from QEMU if an exception occurs @qemu-system-x86_64 -s -hda $(iso) -d int -no-reboot -serial file:$(CURDIR)/target/$(target)/serial-$(TIMESTAMP).log cargo: doc: ##@utilities Make RustDoc documentation @xargo doc help: ##@miscellaneous Show this help. @perl -e '$(HELP_FUN)' $(MAKEFILE_LIST) all: help env: ##@utilities Install dev environment dependencies ./scripts/install-env.sh clean: ##@utilities Delete all build artefacts. @xargo clean @cd boot && xargo clean kernel: $(kernel).bin ##@build Compile the debug kernel binary iso: $(iso) ##@build Compile the kernel binary and make an ISO image run: run-debug ##@build Make the kernel ISO image and boot QEMU from it. release-kernel: $(release_kernel).bin ##@release Compile the release kernel binary release-iso: $(release_iso) ##@release Compile the release kernel binary and make an ISO image release-run: run-release ##@release Make the release kernel ISO image and boot QEMU from it. debug: $(iso) ##@build Run the kernel, redirecting serial output to a logfile. @qemu-system-x86_64 -s -S -hda $(iso) -serial file:$(CURDIR)/target/$(target)/serial-$(TIMESTAMP).log test: ##@build Test crate dependencies @cargo test -p sos_intrusive # @xargo test -p alloc @cd alloc && cargo test run-%: $(wild_iso) @qemu-system-x86_64 -s -hda $< $(wild_iso): $(wild_kernel).bin $(wild_isofiles) $(grub_cfg) @cp $< $(word 2,$^)/boot/ @cp $(grub_cfg) $(word 2,$^)/boot/grub grub-mkrescue -o $@ $(word 2,$^)/ @rm -r $(word 2,$^) $(wild_isofiles): @mkdir -p $@/boot/grub $(boot): @cd boot && RUST_TARGET_PATH="$(PWD)/targets" xargo rustc \ --target $(boot_target) \ -- --crate-type=staticlib # Place 32-bit bootstrap code into a 64-bit ELF @x86_64-pc-elf-objcopy -O elf64-x86-64 \ $(boot_outdir)/debug/libboot32.a \ $(boot_outdir)/debug/libboot.a @x86_64-pc-elf-objcopy --strip-debug -G _start \ $(boot_outdir)/debug/libboot.a # @cd $(boot_outdir)/debug && ar -crus libboot.a boot.o $(release_boot): @cd boot && RUST_TARGET_PATH="$(PWD)/targets" xargo rustc \ --target $(boot_target) \ -- --release \ --crate-type=staticlib # Place 32-bit bootstrap code into a 64-bit ELF @x86_64-pc-elf-objcopy -O elf64-x86-64 \ $(boot_outdir)/release/libboot32.a \ $(boot_outdir)/release/libboot.a @x86_64-pc-elf-objcopy --strip-debug -G _start \ $(boot_outdir)/release/libboot.a $(release_kernel): $(release_boot) @RUST_TARGET_PATH="$(PWD)/targets" xargo build --target $(target) --release $(release_kernel).bin: $(release_kernel) @cp $(release_kernel) $(release_kernel).bin $(release_iso): $(release_kernel).bin $(grub_cfg) @mkdir -p $(release_isofiles)/boot/grub @cp $(release_kernel).bin $(release_isofiles)/boot/ @cp $(grub_cfg) $(release_isofiles)/boot/grub @grub-mkrescue -o $(release_iso) $(release_isofiles)/ @rm -r $(release_isofiles) $(kernel): $(boot) @RUST_TARGET_PATH="$(PWD)/targets" xargo build --target $(target) $(kernel).debug: $(kernel) @x86_64-elf-objcopy --only-keep-debug $(kernel) $(kernel).debug $(kernel).bin: $(kernel) $(kernel).debug @x86_64-elf-strip -g -o $(kernel).bin $(kernel) @x86_64-elf-objcopy --add-gnu-debuglink=$(kernel).debug $(kernel) gdb: $(kernel).bin $(kernel).debug ##@utilities Connect to a running QEMU instance with gdb. @rust-os-gdb -ex "target remote tcp:127.0.0.1:1234" $(kernel) ================================================ FILE: Notes.md ================================================ Notes ===== On designing a userland ----------------------- + provide user code written in Rust with a fluent Rust API + [capability-based security](https://en.wikipedia.org/wiki/Capability-based_security) #### prior art + `ioctl` is a great example of _exactly what not to do_ - every system call should be its own call. requiring the programmer to keep track of both syscalls and values to pass to `ioctl` is a pain. + `kqueue` is good ================================================ FILE: README.md ================================================ # Stupid Operating System [![Build Status](https://travis-ci.org/hawkw/sos-kernel.svg?branch=master)](https://travis-ci.org/hawkw/kernel) [![MIT License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://github.com/sos-os/kernel/LICENSE-MIT) [![Latest RustDoc](https://img.shields.io/badge/rustdoc-latest-orange.svg)](http://www.elizas.website/sos-kernel/sos_kernel/index.html) [![Gitter](https://img.shields.io/gitter/room/sos-os/kernel.svg)](https://gitter.im/sos-os) SOS is a simple, tiny toy OS implemented in Rust. I'm writing this mostly for fun, to learn more about OS design and kernel hacking, so don't expect anything new or exciting out of this project. Inspiration, and a reasonable amount of code, taken from @phil-opp's great [series of blog posts](http://os.phil-opp.com) on the subject, Charlie Somerville's [rustboot](https://github.com/charliesome/rustboot), and Samy Pessé's [_How to Make an Operating System_](https://www.gitbook.com/book/samypesse/how-to-create-an-operating-system/details). design goals ------------ + **POSIX compliance is not a goal** (though it would be cool) + **Hybrid/loosely microkernel** (i.e., move code to user space *when convenient/practical*) + Possibly provide the **Rust stdlib** at the OS level. + **JVM-style** memory allocation? + Possibly experiment with a **[Plan 9-esque](https://en.wikipedia.org/wiki/9P_(protocol)) networking stack** eventually? building & running ------------------ I've included a simple [`Makefile`](Makefile) to automate building and running SOS. This README lists most of the important make targets, but there's also a `$ make help` command, which will print a list of all available targets. ### setting up your build environment In order to build SOS, you'll need to properly configure your build environment. Since this process is fairly complex, I've provided some automatic installation shell scripts to make it a bit more painless. + `$ make env` will install and configure build dependencies If you don't trust the scripts, or if you're curious to know what they're doing, you can also follow the manual install instructions in [`BUILDING.md`](BUILDING.md). ### building & running the OS + `$ make kernel` compiles & links the kernel binary + `$ make iso` makes the kernel and builds a bootable ISO image + `$ make run` compiles the kernel, makes the ISO, and boots QEMU from the ISO ================================================ FILE: Vagrantfile ================================================ # -*- mode: ruby -*- # vi: set ft=ruby : Vagrant.configure(2) do |config| config.vm.box = "hashicorp/precise64" config.vm.provision "shell", inline: <<-SHELL sudo apt-get update sudo apt-get install -y build-essential sudo apt-get install -y curl sudo apt-get install nasm -y sudo apt-get install xorriso -y sudo apt-get install git -y sudo apt-get install vim -y sudo apt-get install -y qemu su - vagrant -c 'curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain nightly' ex +'$s@$@\rexport PATH=~/.cargo/bin/:$PATH@' -cwq /etc/bash.bashrc SHELL config.ssh.forward_x11 = true end ================================================ FILE: Xargo.toml ================================================ [target.x86_64-sos-kernel-gnu.dependencies] alloc = {} # std = {} ================================================ FILE: boot/Cargo.toml ================================================ [package] name = "boot32" version = "0.1.0" authors = ["Eliza Weisman "] [lib] crate-type = ["staticlib"] # [[bin]] # name = "libboot32.a" [features] default = ["log"] log = [] [profile.dev] opt-level = 3 debug = true rpath = false lto = false debug-assertions = true codegen-units = 1 panic = "abort" [profile.release] opt-level = 3 debug = true rpath = false lto = false panic = "abort" [dependencies] rlibc = "0.1.4" ================================================ FILE: boot/README.md ================================================ # SOS x86_64 bootstrap this crate contains code for the 32-bit protected mode boot routine for x86_64 CPUs. when we boot up an x86_64 system using GRUB & multiboot, we end up with the CPU running in 32-bit 'protected mode', rather than 64-bit 'long mode'. before we can jump into long mode, we have to perform a handful of setup tasks in protected mode. this boot routine lives in a separate crate, with a 32-bit `target.json`. when building SOS for x86_64, we have to compile this crate separately and then link the resulting 32-bit object with the rest of the 64-bit kernel object. this boot crate is only necessary for x86_64. for other architectures, all boot code can live in the main kernel crate. ================================================ FILE: boot/src/lib.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2016-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! # SOS x86_64 bootstrap //! //! This crate contains code for the 32-bit protected mode boot routine for //! x86_64 CPUs. When we boot up an x86_64 system using GRUB & multiboot, we //! end up with the CPU running in 32-bit 'protected mode', rather than 64-bit //! 'long mode'. Before we can jump into long mode, we have to perform a //! handful of setup tasks in protected mode. //! //! This boot routine lives in a separate crate, with a 32-bit `target.json`. //! When building SOS for x86_64, we have to compile this crate separately and //! then link the resulting 32-bit object with the rest of the 64-bit kernel //! object. //! //! This boot crate is only necessary for x86_64. For other architectures, all //! boot code can live in the main kernel crate. //! //! For more information, refer to: //! + the [intermezzOS book] //! + the [OSDev Wiki] article on long mode //! + Philipp Oppermann's [blog post] //! //! [intermezzOS book]: http://intermezzos.github.io/book/transitioning-to-long-mode.html //! [OSDev Wiki]: http://wiki.osdev.org/Long_Mode#Long_Mode //! [blog post]: http://os.phil-opp.com/entering-longmode.html #![crate_name = "boot32"] #![feature(asm)] #![feature(lang_items)] #![feature(naked_functions)] #![feature(linkage)] #![feature(struct_field_attributes)] #![feature(stmt_expr_attributes)] #![no_std] extern crate rlibc; const TABLE_LENGTH: usize = 512; /// The size of a "huge" page const HUGE_PAGE_SIZE: u64 = 2 * 1024 * 1024; // 2 MiB /// Page table entry flags for a page that is present and writable. const ENTRY_FLAGS_PW: u64 = 0b11; /// A page table is an array of page table entries. type Table = [TableEntry; TABLE_LENGTH]; trait PageTable: Sized { /// Install this page table as the top-level page table. #[inline(always)] unsafe fn set_top_level(&'static self) { asm!("mov cr3, $0" :: "r"(self) :: "intel"); } } impl PageTable for Table { } /// A page table entry is a 64-bit unsigned integer. /// /// We represent this as a [newtype] rather than a type alias, because we want /// to implement specific functionality on it. /// /// [newtype]: https://aturon.github.io/features/types/newtype.html #[repr(C)] struct TableEntry(u64); impl TableEntry { /// Set this table entry to map to a lower-level page table. // TODO: use a marker type to ensure this always maps to a _lower-level_ // table? #[inline(always)] unsafe fn map_to_table(&mut self, to: &'static Table) { *self = TableEntry(to as *const _ as u64 | ENTRY_FLAGS_PW); } /// Set this table entry to map to a huge page with the given number. #[inline(always)] unsafe fn map_to_page(&mut self, number: usize) { // Page table entry flags for a page that is huge, present, and writable. const ENTRY_FLAGS_HUGE: u64 = 0b10000011; // the start address is the page number times the page's size let addr = number as u64 * HUGE_PAGE_SIZE; *self = TableEntry(addr | ENTRY_FLAGS_HUGE); } } use core::convert; macro_rules! set_flags { (%$register:ident $( |= $body:expr);+ ) => { let mut $register: usize; asm!( concat!("mov $0, ", stringify!($register)) : "=r"($register) ::: "intel"); $($register |= $body;)+ asm!( concat!("mov ", stringify!($register), ", $0") :: "r"($register) :: "intel"); } } #[repr(C, packed)] pub struct Gdt { _null: u64 , code: u64 } #[repr(C, packed)] pub struct GdtPointer { /// the length of the GDT pub limit: u16 , /// pointer to the GDT pub base: &'static Gdt } impl GdtPointer { #[cold] #[inline(always)] unsafe fn load (&self) { asm!("lgdt ($0)" :: "r"(self) : "memory"); } } impl convert::From<&'static Gdt> for GdtPointer { #[cold] #[inline(always)] fn from(gdt: &'static Gdt) -> Self { use core::mem::size_of_val; GdtPointer { limit: size_of_val(gdt) as u16 - 1 , base: gdt } } } #[link_name = ".gdt64"] #[link_section = ".gdt"] #[no_mangle] pub static GDT: Gdt = Gdt { _null: 0 , code: (1<<44) | (1<<47) | (1<<41) | (1<<43) | (1<<53) }; #[inline(always)] fn boot_write(s: &[u8]) { unsafe { use core::ptr::write_volatile; let vga_buf = 0xb8000 as *mut u16; for (n, c) in s.iter().enumerate() { write_volatile(vga_buf.offset(n as isize), 0x0200 + *c as u16); } } } extern "C" { static mut pml4_table: Table; static mut pdp_table: Table; static mut pd_table: Table; } #[cold] #[inline(always)] #[naked] unsafe fn create_page_tables() { //-- map the PML4 and PDP tables ----------------------------------------- // recursive map last PML4 entry pml4_table[511].map_to_table(&pml4_table); // map first PML4 entry to PDP table pml4_table[0].map_to_table(&pdp_table); // map first PDPT entry to PD table pdp_table[0].map_to_table(&pd_table); boot_write(b"3.1"); //-- map the PD table ---------------------------------------------------- for (page_number, entry) in pd_table.iter_mut().enumerate() { entry.map_to_page(page_number); } boot_write(b"3.2"); } #[cold] #[inline(always)] unsafe fn set_long_mode() { // load PML4 addr to cr3 &pml4_table.set_top_level(); boot_write(b"3.3"); // // enable PAE flag in cr4 // set_flags!(%cr4 |= 1 << 5 ); asm!("mov eax, cr4 or eax, 1 << 5 mov cr4, eax" ::: "memory" : "intel", "volatile"); boot_write(b"3.4"); // set the long mode bit in EFER MSR (model specific register) asm!( "mov ecx, 0xC0000080 rdmsr or eax, 1 << 8 wrmsr" :::"memory": "intel", "volatile"); boot_write(b"3.5"); // enable paging in cr0 // set_flags!(%cr0 |= 1 << 31; // |= 1 << 16 ); asm!("mov eax, cr0 or eax, 0x80000000 mov cr0, eax" :::"memory": "intel", "volatile"); boot_write(b"3.6") } /// Test whether or not this system supports Multiboot 2 #[cold] #[inline(always)] unsafe fn is_multiboot_supported() -> bool { const MULTIBOOT_MAGIC: usize = 0x36d76289; let eax: usize; asm!("mov eax, $0" : "=r"(eax) ::: "intel"); eax == MULTIBOOT_MAGIC } #[cold] #[no_mangle] #[naked] pub unsafe extern "C" fn _start() { boot_write(b"0"); asm!("cli"); // 1. Move Multiboot info pointer to edi asm!("mov edi, ebx" :::: "intel"); boot_write(b"1"); // 2. make sure the system supports SOS // TODO: port this from boot.asm if !is_multiboot_supported() { loop { boot_write(b"ERROR: multiboot not supported!"); } } boot_write(b"2"); // 3. if everything is okay, create the page tables and start long mode create_page_tables(); set_long_mode(); // 4. load the 64-bit GDT GdtPointer::from(&GDT).load(); boot_write(b"4"); // 6. jump to the 64-bit boot subroutine. asm!("ljmpl $$8, $$long_mode_init"); } #[cold] #[lang = "panic_fmt"] fn panic_fmt() -> ! { loop { boot_write(b"panic! panic! panic!"); } } ================================================ FILE: build.rs ================================================ use std::env; fn main() { let profile = env::var("PROFILE").unwrap(); if profile != "test" { // target triple let target = env::var("TARGET").unwrap(); // extract the architecture name from the target triple let arch_name = target.split("-").next() .expect("Couldn't parse target triple!"); let boot_path = if arch_name == "x86_64" { format!("boot/target/x86_32-sos-bootstrap-gnu/{}/", profile) } else { panic!("target arch {} not yet supported, sorry!", arch_name); }; println!("cargo:rustc-link-search=native={}", boot_path); println!("cargo:rustc-link-lib=static=boot"); } } ================================================ FILE: cpu/Cargo.toml ================================================ [package] name = "cpu" version = "0.0.1" authors = [ "Eliza Weisman " ] [profile.dev] opt-level = 3 debug = true rpath = false lto = false debug-assertions = true codegen-units = 1 panic = "abort" [profile.release] opt-level = 3 debug = true rpath = false lto = false panic = "abort" [dependencies] util = { path = "../util" } bitflags = "0.7" spin = "0.4.6" [dependencies.vga] path = "../vga" features = ["kinfo", "system_term"] [dependencies.memory] path = "../memory" [dependencies.log] version = "0.3.6" default-features = false features = ["release_max_level_info"] [dependencies.lazy_static] version = "0.2.11" features = ["spin_no_std"] ================================================ FILE: cpu/src/armv7/mod.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! ARM v7 architecture-specific implementation // TODO: NYI pub const ARCH_BITS: u8 = 64; ================================================ FILE: cpu/src/lib.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2016 Eliza eisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // #![crate_name = "cpu"] #![crate_type = "lib"] #![feature(const_fn)] #![feature(asm)] #![feature(naked_functions)] #![feature(slice_patterns)] #![feature(linkage)] #![feature(stmt_expr_attributes)] #![cfg_attr(target_arch = "x86_64", feature(abi_x86_interrupt))] #![no_std] //-- non-SOS dependencies ---------------------------------------------------- extern crate spin; #[macro_use] extern crate log; #[macro_use] extern crate bitflags; //-- SOS dependencies -------------------------------------------------------- extern crate util; extern crate memory; #[macro_use] extern crate vga; use core::marker::PhantomData; //-- re-exports -------------------------------------------------------------- // 64-bit x86_64 (long mode) #[cfg(target_arch="x86_64")] mod x86_64; #[cfg(target_arch="x86_64")] pub use self::x86_64::*; // 32-bit x86 (protected mode) // TODO: NYI #[cfg(target_arch = "x86")] mod x86; #[cfg(target_arch = "x86")] pub use self::x86::*; // ARM v7 // TODO: NYI #[cfg(target_arch = "armv7")] mod armv7; #[cfg(target_arch = "armv7")] pub use self::x86::*; /// A CPU I/O port. /// /// This is a typed wrapper around an [`UnsafePort`](struct.UnsafePort.html). /// Unlike an `UnsafePort`, this pub struct Port { raw_port: UnsafePort , typ: PhantomData } macro_rules! make_ports { ( $( $t:ty, $read:ident, $out:ident ),+ ) => { $( impl Port<$t> { #[inline] pub const fn new(number: u16) -> Self { // TODO: can we check if the port number is valid unsafe { Port { raw_port: UnsafePort::new(number) , typ: PhantomData::<$t> } } } #[inline] pub fn read(&self) -> $t { unsafe { self.raw_port.$read() } } #[inline] pub fn write(&self, data: $t) { unsafe { self.raw_port.$out(data) } } } )+ } } make_ports! { u8, in8, out8 , u16, in16, out16 , u32, in32, out32 } #[cfg(arch="x86_64")] make_ports! { u64, in64, out64 } ================================================ FILE: cpu/src/x86/cpu.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Code for interacting with the `x86` CPU. //! //! Currently this module contains a quick implementation of CPU port //! input and output, and little else. #[path = "../x86_all/cpu/mod.rs"] mod cpu_all; pub use self::cpu_all::*; #[path = "../x86_all/interrupts/mod.rs"] pub mod interrupts; ================================================ FILE: cpu/src/x86/mod.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! `x86` architecture-specific implementation. //! //! This module contains code for `x86` 32-bit protected-mode systems. pub mod cpu; pub mod memory; // pub mod keyboard; pub const ARCH_BITS: u8 = 32; ================================================ FILE: cpu/src/x86_64/context.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! `x86_64` execution contexts. //! //! This is inteded to be general-purpose and composable, so that the same //! code can be reused for interrupts and for multithreading. use core::mem; use core::fmt; use super::flags::{Flags as RFlags}; use super::segment; /// Registers pushed to the stack when handling an interrupt or context switch. #[repr(C, packed)] #[derive(Copy, Clone)] pub struct Registers { pub rsi: u64 , pub rdi: u64 , pub r11: u64 , pub r10: u64 , pub r9: u64 , pub r8: u64 , pub rdx: u64 , pub rcx: u64 , pub rax: u64 } impl Registers { /// Transform this struct into an array of `u64`s /// (if you would ever want to do this) /// TODO: rewrite this to be a `convert::Into` implementation. // - eliza, 03/09/2017 pub unsafe fn to_array(&self) -> [u64; 9] { // [ self.rsi, self.rdi, self.r11 // , self.r10, self.r9, self.r8 // , self.rdx, self.rcx, self.rax // ] // using transmute is probably faster and we're already unsafe... mem::transmute(*self) } /// Create a new empty set of Registers pub const fn empty() -> Self { Registers { rsi: 0, rdi: 0, r11: 0 , r10: 0, r9: 0, r8: 0 , rdx: 0, rcx: 0, rax: 0 } } /// Push the caller-saved registers to the stack /// (such as when handling a context switch or interrupt). /// /// THIS FUNCTION IS NAKED. DO NOT CALL IT NORMALLY. #[naked] #[inline(always)] pub unsafe fn push() { asm!( "push rax push rcx push rdx push r8 push r9 push r10 push r11 push rdi push rsi" :::: "intel" , "volatile"); } /// Push the caller-saved registers off the stack /// (such as when handling a context switch or interrupt). /// /// THIS FUNCTION IS NAKED. DO NOT CALL IT NORMALLY. #[naked] #[inline(always)] pub unsafe fn pop() { asm!( "pop rsi pop rdi pop r11 pop r10 pop r9 pop r8 pop rdx pop rcx pop rax" :::: "intel" , "volatile"); } } impl fmt::Debug for Registers { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f , " RSI: {:#018x} RDI: {:#018x} R11: {:#018x}\n \ R10: {:#018x} R9: {:#018x} R8: {:#018x}\n \ RDX: {:#018x} RCX: {:#018x} RAX: {:#018x}" , self.rsi, self.rdi, self.r11 , self.r10, self.r9, self.r8 , self.rdx, self.rcx, self.rax) } } #[repr(C, packed)] pub struct InterruptFrame { // this is the actual value of the interrupt stack frame context, // not the old one (which is wrong). note that the old one seems to cause // stack misalignment. // -- eliza, october 4th, 2016 /// Value of the instruction pointer (`$rip`) register pub rip: *const u8 , /// Value of the code segment (`$cs`) register pub cs: segment::Selector , __pad_1: u32 , __pad_2: u16 , /// Value of the CPU flags (`$rflags`) register pub rflags: RFlags , /// Value of the stack pointer (`$rsp`) register // TODO: should this actually be a pointer? pub rsp: *const u8 , /// Value of the stack segment (`$ss`) register pub ss: segment::Selector , __pad_3: u32 , __pad_4: u16 } #[cfg(test)] mod test { #[test] fn test_interrupt_frame_correct_size() { use core::mem::size_of; use super::InterruptFrame; assert_eq!(size_of::(), 32); } } impl fmt::Debug for InterruptFrame { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f , "Interrupt Frame: \ \n instruction pointer: {:p} \ \n code segment: {} \ \n rflags: {:?} \ \n stack pointer: {:p} \ \n stack segment: {}" , self.rip // , self.__pad_1, self.__pad_2 , self.cs , self.rflags , self.rsp // , self.__pad_3, self.__pad_4 , self.ss) } } /// Thread execution context #[repr(C, packed)] pub struct Context { /// Value of the stack pointer (`rsp`) register pub rsp: *mut u8 , /// Value of the caller-saved registers pub registers: Registers , /// Value of the instruction pointer (`rip`) register pub rip: *mut u8 //, pub stack: [u8] // TODO: should be box } impl Context { pub fn empty() -> Self { unsafe { Context { rsp: mem::transmute(0u64) , registers: Registers::empty() , rip: mem::transmute(0u64) //, stack: [0u8; 8] } } } } ================================================ FILE: cpu/src/x86_64/mod.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2016 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Code for interacting with the `x86_64` CPU. //! //! This module contains code for interrupts, paging, context switches, //! CPU I/O (from `x86_all`), and reading/writing the x86 control registers. //! #[path = "../x86_all/mod.rs"] mod cpu_all; pub mod context; pub mod task; pub mod msr; pub use self::context::Registers; pub use self::cpu_all::*; ================================================ FILE: cpu/src/x86_64/msr.rs ================================================ // // SOS: the Stupid Operating System // by Hawk Weisman (hi@hawkweisman.me) // // Copyright (c) 2015 Hawk Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Code for interacting with the Model-Specific Registers (MSRs). /// Extended Feature Enable Register (EFER) on IA-32 pub const IA32_EFER: u32 = 0xc0000080; /// Write `value` to the specified `msr` /// /// # Arguments /// + `msr`: which MSR to write to /// + `value`: the bits to write pub unsafe fn write(msr: u32, value: u64) { let low = value as u32; let high = (value >> 32) as u32; asm!( "wrmsr" :: "{ecx}" (msr), "{eax}" (low), "{edx}" (high) : "memory" : "volatile" ); } /// Read 64 bits from the specified `msr` pub unsafe fn read(msr: u32) -> u64 { let low: u32; let high: u32; asm!( "rdmsr" : "={eax}" (low), "={edx}" (high) : "{ecx}" (msr) : "memory" : "volatile" ); ((high as u64) << 32) | (low as u64) } /// Enable the NXE (No Execute) in the IA-32 EFER register. /// /// This allows us to set the NXE bit on pages. pub unsafe fn enable_nxe() { let nxe_bit = 1 << 11; let efer = read(IA32_EFER) | nxe_bit; write(IA32_EFER, efer); } ================================================ FILE: cpu/src/x86_64/task.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Tasking use ::segment; use memory::VAddr; /// A 64-bit Task State Descriptor #[repr(packed)] pub struct StateDescriptor { pub upper: segment::Descriptor , pub lower: u64 } /// A 64-bit Task State Segment #[repr(C, packed)] #[derive(Debug)] pub struct StateSegment { _reserved_1: u32 , /// 64-bit values of the stack pointers (`%rsp`) for privilege rings 0-2 // TODO: should this be an array or just three u64s? pub rsp: [VAddr; 3] , _reserved_2: u32 , /// 64-bit values of the interrupt stack table registers pub ist: [VAddr; 7] , _reserved_3: u64 , _reserved_4: u16 , /// the base offset of the IO map pub iomap_base_offset: u16 } impl StateSegment { /// Returns a new, empty TSS pub const fn new() -> Self { StateSegment { _reserved_1: 0 , rsp: [ VAddr::new(0); 3] , _reserved_2: 0 , ist: [ VAddr::new(0); 7 ] , _reserved_3: 0 , _reserved_4: 0 , iomap_base_offset: 0 } } } ================================================ FILE: cpu/src/x86_all/control_regs/cr0.rs ================================================ use core::fmt; bitflags! { /// Flag present in `%cr0`. /// /// See [the OS Dev Wiki](http://wiki.osdev.org/CR0#CR0) for more /// information. pub flags Flags: usize { /// Protected Mode Enable /// /// If 1, system is in protected mode. Otherwise, the system is in real /// mode. const PE = 1 << 0 , /// Monitor co-processor /// /// Controls interaction of `WAIT`/`FWAIT` instructions with `TS` flag /// in `%cr0`. const MP = 1 << 1 , /// FPU Emulation /// /// If set, no x87 floating point unit present, if clear, x87 FPU /// present. const EM = 1 << 2 , /// Task Switched /// /// Allows saving x87 task context upon a task switch only after x87 /// instruction used. const TS = 1 << 3 , /// Extension Type /// /// On a 386 CPU, indicated whether the math coprocessor was an 80287 /// or an 80387. const ET = 1 << 4 , /// Numeric Error /// /// Enable internal x87 floating point error reporting when set, else /// enables PC style x87 error detection. const NE = 1 << 5 , /// Write Protect /// /// When set, the CPU can't write to read-only pages when privilege /// level is 0. const WP = 1 << 16 , /// Alignment Mask /// /// Alignment check enabled if `AM` set, `AC` flag (in `%eflags` /// register) set, and privilege level is 3 const AM = 1 << 18 , /// Not Write-Through /// /// Globally enables/disable write-through caching const NW = 1 << 29 , /// Cache Disable /// /// Globally enables/disable the memory cache const CD = 1 << 30 , /// Paging /// /// If 1, enable paging and use the `%cr3` register, else disable /// paging. const PG = 1 << 31 } } cpu_flag! { doc="If set, enable paging; if unset, disable paging.", PG, is_paging_enabled, enable_paging } cpu_flag! { doc="If set, enable the write protect bit; if unset, disable write \ protect.", WP, is_write_protected, enable_write_protect } ///// Set the write protect bit in `%cr0`. //pub fn enable_write_protect() { // let mut flags: Flags = read(); // if !flags.contains(WP) { // flags.insert(WP); // unsafe { write(flags) } // } //} // ///// Unset the write protect bit in `%cr0`. //pub fn disable_write_protect() { // let mut flags: Flags = read(); // if flags.contains(WP) { // flags.remove(WP); // unsafe { write(flags) } // } //} // ///// Set the paging bit in `%cr0`. //pub fn enable_paging() { // let mut flags: Flags = read(); // if !flags.contains(WP) { // flags.insert(WP); // unsafe { write(flags) } // } //} // ///// Unset the paging bit in `%cr0`. //pub fn disable_paging() { // let mut flags: Flags = read(); // if flags.contains(PG) { // flags.remove(PG); // unsafe { write(flags) } // } //} /// Read the current value from `%cr0`. /// /// # Safety /// + Reading from control registers while not in kernel mode will cause /// a general protection fault. pub unsafe fn read() -> Flags { let result: usize; asm!( "mov $0, cr0" : "=r"(result) ::: "intel" ); Flags { bits: result } } /// Write a value to `%cr0`. /// /// # Safety /// + Control registers should generally not be modified during normal /// operation. pub unsafe fn write(flags: Flags) { asm!( "mov cr0, $0" :: "r"(flags.bits) :: "intel"); } impl fmt::LowerHex for Flags { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:#08x}", self.bits) } } ================================================ FILE: cpu/src/x86_all/control_regs/cr4.rs ================================================ use core::fmt; /// Read the current value from `$cr4`. /// /// # Safety /// + Reading from control registers while not in kernel mode will cause /// a general protection fault. pub unsafe fn read() -> Flags { let result: usize; asm!( "mov $0, cr4" : "=r"(result) ::: "intel" ); Flags { bits: result } } /// Write a value to `$cr4`. /// /// # Safety /// + Control registers should generally not be modified during normal /// operation. pub unsafe fn write(flags: Flags) { asm!( "mov cr4, $0" :: "r"(flags.bits) :: "intel"); } bitflags! { /// Bitflags present in `$cr4` /// /// Documentation taken from /// [wikipedia](https://en.wikipedia.org/wiki/Control_register#CR4). pub flags Flags: usize { /// Virtual 8086 Mode Extensions /// /// If set, enables support for the virtual interrupt flag (VIF) in /// virtual-8086 mode. const VME = 1 << 0 , /// Protected-mode Virtual Interrupts /// /// If set, enables support for the virtual interrupt flag (VIF) in /// protected mode. const PVI = 1 << 1 , /// Time Stamp Disable /// /// If 1, the `RTDSC` instruction can only be executed in Ring 0 const TSD = 1 << 2 , /// Debugging Extensions /// /// If set, enables debug register based breaks on I/O space access const DE = 1 << 3 , /// Page Size Extension /// /// If unset, page size is 4 KiB, else page size is increased to 4 MiB /// (if PAE is enabled or the processor is in Long Mode this bit is /// ignored). const PSE = 1 << 4 , /// Physical Address Extension /// /// If set, changes page table layout to translate 32-bit virtual /// addresses into extended 36-bit physical addresses. const PAE = 1 << 5 , /// Machine Check Exception /// /// If set, Machine Check exceptions are enabled. const MCE = 1 << 6 , /// Page Global Enabled /// /// If set, address translations (PDE or PTE records) may be shared /// between address spaces. const PGE = 1 << 7 , /// Performance-Monitoring Counter enable /// /// If set, the `RDPMC` instruction can be executed at any privilege /// level, else `RDPMC` can only be used in ring 0. const PCE = 1 << 8 , /// Operating system support for `FXSAVE` and `FXRSTOR` instructions /// /// If set, enables SSE instructions and fast FPU save and restore. const OSFXSR = 1 << 9 , /// Operating System Support for Unmasked SIMD Floating-Point Exceptions /// /// If set, enables unmasked SSE exceptions. const OSXMMEXCPT = 1 << 10 , /// Virtual Machine Extensions Enable const VMXE = 1 << 13 , /// Safer Mode Extensions Enable const SMXE = 1 << 14 , /// Enables the instructions RDFSBASE, RDGSBASE, WRFSBASE, and WRGSBASE. const FSGSBASE = 1 << 16 , /// PCID Enable /// /// If set, enables process-context identifiers (PCIDs). const PCIDE = 1 << 17 , /// `XSAVE` and Processor Extended States Enable const OSXSAVE = 1 << 18 , /// Supervisor Mode Execution Protection Enable /// /// If set, execution of code in a higher ring generates a fault const SMEP = 1 << 20 , /// Supervisor Mode Access Protection Enable /// /// If set, access of data in a higher ring generates a faul const SMAP = 1 << 21 , /// Protection Key Enable const PKE = 1 << 22 } } impl fmt::LowerHex for Flags { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:#08x}", self.bits) } } cpu_flag! { doc="If disabled, the `RTDSC` instruction can only be executed in Ring 0.", TSD, is_timestamp_disabled, disable_timestamp } ================================================ FILE: cpu/src/x86_all/control_regs/mod.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! `x86` and `x86_64` control registers #![warn(missing_docs)] use core::fmt; /// `%cr0` contains flags that modify basic processor operation. pub mod cr0; /// `%cr4` contains flags that control protected mode execution. pub mod cr4; /// A struct bundling together a snapshot of the control registers state. #[derive(Copy,Clone,Debug)] pub struct CrState { /// `%cr0` contains flags that control the CPU's operations pub cr0: cr0::Flags , /// `%cr2` contains the page fault linear address pub cr2: usize , /// `%cr3` contains the page table root pointer /// /// TODO: can this be rewritten as a pointer? pub cr3: usize , /// `%cr4` contains flags that control operations in /// protected mode pub cr4: cr4::Flags } impl fmt::Display for CrState { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "CR0: {:#08x} CR2: {:#08x} CR3: {:#08x} CR4: {:#08x}" , self.cr0, self.cr2, self.cr3, self.cr4) } } /// Dump the current contents of the control registers to a `CrState`. pub fn dump() -> CrState { let cr0_: usize; let cr2_: usize; let cr3_: usize; let cr4_: usize; unsafe { asm!( "mov $0, cr0 mov $1, cr2 mov $2, cr3 mov $3, cr4" : "=r"(cr0_) , "=r"(cr2_) , "=r"(cr3_) , "=r"(cr4_) ::: "intel" , "volatile"); } CrState { cr0: cr0::Flags::from_bits_truncate(cr0_) , cr2: cr2_, cr3: cr3_ , cr4: cr4::Flags::from_bits_truncate(cr4_) } } /// `$cr2` contains the page fault linear address pub mod cr2 { /// Read the current value from `$cr2`. /// /// # Safety /// + Reading from control registers while not in kernel mode will cause /// a general protection fault. pub unsafe fn read() -> usize { let result: usize; asm!( "mov $0, cr2" : "=r"(result) ::: "intel" ); result } /// Write a value to `$cr2`. /// /// # Safety /// + Control registers should generally not be modified during normal /// operation. pub unsafe fn write(value: usize) { asm!( "mov cr2, $0" :: "r"(value) :: "intel"); } } /// `%cr3` contains the page table root pointer pub mod cr3 { use memory::{PAddr, PhysicalPage}; // #[cfg(target_arch = "x86_64")] // use paging::table::{Table, PML4Level}; /// Read the current value from `$cr3`. /// /// # Safety /// + Reading from control registers while not in kernel mode will cause /// a general protection fault. #[cfg(target_arch = "x86_64")] pub unsafe fn read() -> PAddr { let result: u64; asm!( "mov $0, cr3" : "=r"(result) ::: "intel" ); PAddr::from(result) } /// Read the current value from `$cr3`. /// /// # Safety /// + Reading from control registers while not in kernel mode will cause /// a general protection fault. #[cfg(target_arch = "x86")] pub unsafe fn read() -> PAddr { let result: u32; asm!( "mov $0, cr3" : "=r"(result) ::: "intel" ); PAddr::from(result) } /// Write a value to `$cr3`. /// /// # Safety /// + Control registers should generally not be modified during normal /// operation. #[cfg(target_arch = "x86_64")] pub unsafe fn write(addr: PAddr) { let value: u64 = addr.into(); asm!( "mov cr3, $0" :: "r"(value) : "memory" : "intel"); } /// Write a value to `$cr3`. /// /// # Safety /// + Control registers should generally not be modified during normal /// operation. #[cfg(target_arch = "x86")] pub unsafe fn write(addr: PAddr) { let value: u32 = addr.into(); asm!( "mov cr3, $0" :: "r"(value) :: "intel"); } /// Returns the current Page Directory base frame. /// /// # Safety /// + Reading from control registers while not in kernel mode will cause /// a general protection fault. pub unsafe fn current_pagetable_frame() -> PhysicalPage { PhysicalPage::containing_addr(read()) } /// Returns the current Page Directory base frame. /// /// # Safety /// + Reading from control registers while not in kernel mode will cause /// a general protection fault. #[inline] pub unsafe fn set_pagetable_frame(frame: PhysicalPage) { write(frame.base_addr()) } } ================================================ FILE: cpu/src/x86_all/dtable.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. //! `x86` and `x86_64` descriptor tables (IDT, GDT, or LDT) //! //! For more information, refer to the _Intel® 64 and IA-32 Architectures //! Software Developer’s Manual_, Vol. 3A, section 3.2, "Using Segments", and //! section 6.10, "Interrupt Descriptor Table (IDT)". #![deny(missing_docs)] // use memory::PAddr; use core::mem::size_of; /// A pointer to a descriptor table. /// This is a format suitable #[repr(C, packed)] pub struct Pointer { /// the length of the descriptor table pub limit: u16 , /// pointer to the region in memory /// containing the descriptor table. pub base: *const T } unsafe impl Sync for Pointer { } /// A descriptor table (IDT or GDT). /// /// The IA32 architecture uses two descriptor table structures, the GDT /// (Global Descriptor Table), which is used for configuring segmentation, /// and the IDT (Interrupt Descriptor Table), which tells the CPU where /// interrupt service routines are located. /// /// As SOS relies on paging rather than segmentation for memory protection on /// both 32-bit and 64-bit systems, we use the GDT only minimally. However, the /// CPU still requires a correctly configured GDT to run in protected mode, even /// if it is not actually used. /// /// This trait specifies base functionality common to both types of descriptor /// table. pub trait DTable: Sized { /// The type of an entry in this descriptor table. /// /// For an IDT, these are /// interrupt [`Gate`](../interrupts/idt/gate/struct.Gate.html)s, /// while for a GDT or LDT, they are segment /// [`Descriptor`](../segment/struct.Descriptor.html)s. // TODO: can there be a trait for DTable entries? // - eliza, 10/06/2016 type Entry: Sized; /// Get the IDT pointer struct to pass to `lidt` or `lgdt` /// /// This expects that the object implementing `DTable` not contain /// additional data before or after the actual `DTable`, if you wish /// to attach information to a descriptor table besides the array of /// entries that it consists of, it will be necessary to encose the /// descriptor table in another `struct` or `enum` type. // TODO: can we have an associated `Entry` type + a function to get the // number of entries in the DTable, instead? that way, we could // calculate the limit using that information, allowing Rust code // to place more variables after the array in the DTable structure. // // If we wanted to be really clever, we could probably also have a // method to get a pointer to a first entry (or enforce that the // DTable supports indexing?) and then we could get a pointer only // to the array segment of the DTable, while still allowing variables // to be placed before/after the array. // // I'm not sure if we actually want to support this – is there really // a use-case for it? I suppose it would also make our size calc. // more correct in case Rust ever puts additional data around a // DTable rray, but I imagine it will probably never do that... // – eliza, 06/03/2016 // #[inline] fn get_ptr(&self) -> Pointer { Pointer { limit: (size_of::() * self.entry_count()) as u16 , base: self as *const _ } } /// Returns the number of Entries in the `DTable`. /// /// This is used for calculating the limit. fn entry_count(&self) -> usize; /// Load the descriptor table with the appropriate load instruction fn load(&'static self); } ================================================ FILE: cpu/src/x86_all/flags.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Flags present in the `%eflags`/`%rflags` register on x86 CPUs. use super::PrivilegeLevel; bitflags! { /// Contents of the `%eflags`/`%rflags` register. /// /// Note that on early x86 systems, this is a 16-bit register (`%flags`), /// but we only support the 32-bit `%eflags` and 64-bit `%rflags`, since /// SOS is a protected mode/long mode OS only. pub flags Flags: usize { /// Carry flag const CF = 1 << 0 , /// Parity flag const PF = 1 << 2 , /// Adjust flag const AF = 1 << 4 , /// Zero flag const ZF = 1 << 6 , /// Sign flag const SF = 1 << 7 , /// Trap flag (single step) /// /// If 1, IT'S A TRAP! const TF = 1 << 8 , /// Interrupt enable flag const IF = 1 << 9 , /// Direction flag const DF = 1 << 10 , /// Overflow flag const OF = 1 << 11 , const IOPL_RING_0 = 0 << 12 , const IOPL_RING_1 = 1 << 12 , const IOPL_RING_2 = 2 << 12 , const IOPL_RING_3 = 3 << 12 , /// I/0 Privilege Level /// /// This flag is always one on the 8086 and 186. const IOPL = IOPL_RING_0.bits | IOPL_RING_1.bits | IOPL_RING_2.bits | IOPL_RING_3.bits , /// Nested task flag /// /// Always 1 on 8086 and 186s. const NT = 1 << 14 , /// Should always be 1 const RESERVED = 1 << 15 , /// Resume flag /// /// Present on 386 and later. const RF = 1 << 16 , /// Virtual 8086 Mode flag /// /// Of course, this is only present on 386 and later. const VM = 1 << 17 , /// Alignment Check /// /// Present on 486SX and later. const AC = 1 << 18 , /// Virtual Interrupt flag /// /// Present on Pentium and later. const VIF = 1 << 19 , /// Virtual Interrupt Pending /// /// Present on Pentium and later. const VIP = 1 << 20 , /// Able to use `CPUID` instruction. /// /// Present on Pentium and later. const ID = 1 << 21 } } impl Flags { pub fn iopl(&self) -> PrivilegeLevel { use core::mem::transmute; let bits = (*self & IOPL).bits >> 12; unsafe { transmute(bits as u16) } } } /// Read the current value from `$eflags`/`%rflags`. pub fn read() -> Flags { let result: usize; unsafe { asm!( "pushf pop $0" : "=r"(result) ::: "intel" ); } Flags { bits: result } } ================================================ FILE: cpu/src/x86_all/interrupts/idt/gate32.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! 32-bit IDT gate implementation use ::segment; use super::{GateFlags}; use super::super::{InterruptHandler, ErrorCodeHandler}; use core::mem::transmute; extern { /// Offset of the 32-bit GDT main code segment. /// Exported by `boot.asm` #[link_name="gdt32_offset"] static GDT_OFFSET: u16; } /// An IDT entry is called a gate. /// /// Based on [code](http://wiki.osdev.org/Interrupt_Descriptor_Table#Structure) /// from the OS Dev Wiki. #[repr(C, packed)] #[derive(Copy,Clone, Default)] pub struct Gate { /// bits 0 - 15 of the offset pub offset_lower: u16 , /// code segment selector (GDT or LDT) pub selector: segment::Selector , /// always zero _zero: u8 , /// indicates the gate's type and attributes. /// the second half indicates the type: /// + `0b1100`: Call gate /// + `0b1110`: Interrupt gate /// + `0b1111`: Trap Gate pub flags: GateFlags , /// bits 16 - 31 of the offset pub offset_upper: u16 } impl GateFlags { /// Returns a new trap gate pub const fn new_trap() -> Self { GateFlags { bits: super::TRAP_GATE_16.bits | super::PRESENT.bits } } /// Returns a new interrupt gate pub const fn new_interrupt() -> Self { GateFlags { bits: super::INT_GATE_16.bits | super::PRESENT.bits } } } impl Gate { /// Creates a new IDT gate marked as `absent`. /// /// This is basically just for filling the new IDT table /// with valid (but useless) gates upon init. /// /// Actually triggering an absent interrupt will send a General Protection /// fault (13). pub const fn absent() -> Self { Gate { offset_lower: 0 , selector: 0 , _zero: 0 , flags: GateFlags { bits: 0 } , offset_upper: 0 } } } impl convert::From for Gate { /// Creates a new IDT gate pointing at the given handler function. /// /// The `handler` function must have been created with valid interrupt /// calling conventions. fn from(handler: ErrorCodeHandler) -> Self { unsafe { let (low, mid): (u16, u16) = mem::transmute(handler); Gate { offset_lower: low , selector: segment::Selector::from_raw(GDT_OFFSET) , _zero: 0 , type_attr: GateFlags::new_interrupt() , offset_upper: high , _reserved: 0 } } } } impl convert::From for Gate { /// Creates a new IDT gate pointing at the given handler function. /// /// The `handler` function must have been created with valid interrupt /// calling conventions. fn from(handler: ErrorCodeHandler) -> Self { unsafe { let (low, mid): (u16, u16) = mem::transmute(handler); Gate { offset_lower: low , selector: segment::Selector::from_raw(GDT_OFFSET) , _zero: 0 , type_attr: GateFlags::new_interrupt() , offset_upper: high , _reserved: 0 } } } } ================================================ FILE: cpu/src/x86_all/interrupts/idt/gate64.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! 64-bit IDT gate implementation use ::segment; use super::{GateFlags}; use super::super::{InterruptHandler, ErrorCodeHandler}; use core::{convert, mem, ops}; use core::marker::PhantomData; impl GateFlags { /// Returns a new trap gate pub const fn new_trap() -> Self { GateFlags { bits: super::TRAP_GATE_32.bits | super::PRESENT.bits } } /// Returns a new call gate pub const fn new_task() -> Self { GateFlags { bits: super::TASK_GATE_32.bits | super::PRESENT.bits } } /// Returns a new interrupt gate pub const fn new_interrupt() -> Self { GateFlags { bits: super::INT_GATE_32.bits | super::PRESENT.bits } } } /// An IDT entry is called a gate. /// /// Based on [code](http://wiki.osdev.org/Interrupt_Descriptor_Table#Structure) /// from the OS Dev Wiki. /// /// Refer also to "6.14.1 64-Bit Mode IDT" and "Table 3-2. System-Segment and /// Gate-Descriptor Types" in the _Intel® 64 and IA-32 Architectures /// Software Developer’s Manual_ #[repr(C, packed)] #[derive(Copy, Clone)] pub struct Gate { /// bits 0 - 15 of the offset pub offset_lower: u16 , /// code segment selector (GDT or LDT) pub selector: segment::Selector , /// always zero _zero: u8 , /// indicates the gate's type and attributes. /// the second half indicates the type: /// + `0b1100`: Call gate /// + `0b1110`: Interrupt gate /// + `0b1111`: Trap Gate pub flags: GateFlags , /// bits 16 - 31 of the offset pub offset_mid: u16 , /// bits 32 - 63 of the offset pub offset_upper: u32 , /// always zero (according to the spec, this is "reserved") _reserved: u32 , _handler_type: PhantomData } impl Gate { /// Creates a new IDT gate marked as `absent`. /// /// This is basically just for filling the new IDT table /// with valid (but useless) gates upon init. /// /// Actually triggering an absent interrupt will send a General Protection /// fault (13). pub const fn absent() -> Self { Gate { offset_lower: 0 , selector: segment::Selector::from_raw(0) , _zero: 0 , flags: GateFlags { bits: 0b1000_1110 } , offset_mid: 0 , offset_upper: 0 , _reserved: 0 , _handler_type: PhantomData } } /// Set the handler function corresponding to this gate. #[inline] pub fn set_handler(&mut self, handler: F) -> &mut Self where Self: convert::From { *self = Self::from(handler); self } /// Sets the TRAP GATE flag to true #[inline] pub fn set_trap(&mut self) -> &mut Self { self.flags.insert(super::TRAP_GATE_32); self } } impl Default for Gate { fn default() -> Self { Gate { offset_lower: 0 , selector: segment::Selector::from_raw(0) , _zero: 0 , flags: GateFlags { bits: 0b1000_1110 } , offset_mid: 0 , offset_upper: 0 , _reserved: 0 , _handler_type: PhantomData } } } impl ops::Deref for Gate { type Target = GateFlags; #[inline] fn deref(&self) -> &Self::Target { &self.flags } } impl ops::DerefMut for Gate { #[inline] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.flags } } impl convert::From for Gate { /// Creates a new IDT gate pointing at the given handler function. /// /// The `handler` function must have been created with valid interrupt /// calling conventions. fn from(handler: InterruptHandler) -> Self { unsafe { // trust me on this, `mem::transmute()` is glorious black magic let (low, mid, high): (u16, u16, u32) = mem::transmute(handler); Gate { offset_lower: low , offset_mid: mid , offset_upper: high , selector: segment::Selector::from_cs() , flags: GateFlags::new_interrupt() , ..Default::default() } } } } impl convert::From for Gate { /// Creates a new IDT gate pointing at the given handler function. /// /// The `handler` function must have been created with valid interrupt /// calling conventions. fn from(handler: ErrorCodeHandler) -> Self { unsafe { // trust me on this, `mem::transmute()` is glorious black magic let (low, mid, high): (u16, u16, u32) = mem::transmute(handler); Gate { offset_lower: low , offset_mid: mid , offset_upper: high , selector: segment::Selector::from_cs() , flags: GateFlags::new_interrupt() , ..Default::default() } } } } impl convert::From<*const u8> for Gate { /// Creates a new IDT gate pointing at the given handler function. /// /// The `handler` function must have been created with valid interrupt /// calling conventions. /// /// This should probably not be used, if it can possibly be avoided. // TODO: it would be really nice if we didn't need this any more. // after the Revolution, once handlers are created in Rust-land with // naked functions... fn from(handler: *const u8) -> Self { unsafe { let (low, mid, high): (u16, u16, u32) = mem::transmute(handler); Gate { offset_lower: low , offset_mid: mid , offset_upper: high , selector: segment::Selector::from_cs() , flags: GateFlags::new_interrupt() , ..Default::default() } } } } ================================================ FILE: cpu/src/x86_all/interrupts/idt/mod.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Common functionality for the `x86` and `x86_64` Interrupt Descriptor Table. #![warn(missing_docs)] use core::{mem, convert, ops}; use ::dtable::DTable; use ::PrivilegeLevel; /// Number of entries in the system's Interrupt Descriptor Table. pub const ENTRIES: usize = 256; #[cfg(test)] mod tests; //==------------------------------------------------------------------------== // IDT Gates #[cfg(target_arch = "x86")] #[path = "gate32.rs"] pub mod gate; #[cfg(target_arch = "x86_64")] #[path = "gate64.rs"] pub mod gate; pub use self::gate::*; bitflags! { /// Bitflags field in an IDT gate. /// /// The structure of the flags field is as follows: /// /// ```ignore /// 7 0 /// +---+---+---+---+---+---+---+---+ /// | P | DPL | S | GateType | /// +---+---+---+---+---+---+---+---+ /// ``` /// /// Fields have the following meanings: /// /// + `P`: One bit, indicating if the ISR is present. Set to 0 for unused /// interrupts. /// + `DPL`: Two bits, indicating the escriptor's priveliege level as an /// integer, with zero being Ring 0. /// + `S`: One bit, set if the descriptor refers to an interrupt in the /// storage segment. /// + `GateType`: Four bits, indicating the type of the interrupt with the /// following values (architecture-dependent): /// - `0101`: 80386 32-bit task gate /// - `0110`: 80286 16-bit interrupt gate /// - `0111`: 80286 16-bit trap gate /// - `1110`: 80386 32-bit interrupt gate /// - `1111`: 80386 32-bit trap gate /// /// For more information, refer to the _Intel® 64 and IA-32 Architectures /// Software Developer’s Manual_, Vol. 3A, section 6.11, "IDT Descriptors"; /// and to the OS Dev Wiki /// [article](http://wiki.osdev.org/Interrupts_Descriptor_Table) /// "Interrupts Descriptor Table". pub flags GateFlags: u8 { /// Set to 0 for unused interrupts. /// /// Indicates whether or not this gate is present. /// An interrupt on a non-present gate will trigger a /// General Protection Fault. const PRESENT = 0b1000_0000 , /// Bit indicating that the descriptor priveliege level is Ring 0 const DPL_RING_0 = 0b0000_0000 , /// Bit indicating that the descriptor priveliege level is Ring 1 const DPL_RING_1 = 0b0010_0000 , /// Bit indicating that the descriptor priveliege level is Ring 2 const DPL_RING_2 = 0b0100_0000 , /// Bit indicating that the descriptor priveliege level is Ring 3 const DPL_RING_3 = 0b0110_0000 , /// Descriptor priveliege level bitfield. const DPL = DPL_RING_0.bits | DPL_RING_1.bits | DPL_RING_2.bits | DPL_RING_3.bits , /// Storage segment flag. /// /// Set to 0 for interrupt gates. const SEGMENT = 0b0001_0000 , /// Set if this `Gate` points to a 32-bit ISR. const LONG_MODE = 0b0000_1000 , /// Set if this is an interrupt gate. const INT_GATE_16 = 0b0000_0110 , /// Set if this is an interrupt gate and points to a 32-bit ISR. const INT_GATE_32 = INT_GATE_16.bits | LONG_MODE.bits , /// Set if this is a trap gate. const TRAP_GATE_16 = 0b0000_0111 , /// Set if this is a trap gate that points to a 32-bit ISR const TRAP_GATE_32 = TRAP_GATE_16.bits | LONG_MODE.bits , /// Set if this is a 32-bit task gate. const TASK_GATE_32 = 0b0000_0101 | LONG_MODE.bits } } impl GateFlags { /// Returns true if this `Gate` is a trap gate #[inline] pub fn is_trap(&self) -> bool { self.contains(TRAP_GATE_16) } /// Returns true if this `Gate` points to a present ISR #[inline] pub fn is_present(&self) -> bool { self.contains(PRESENT) } /// Sets the present bit for this gate #[inline] pub fn set_present(&mut self, present: bool) -> &mut Self { if present { self.insert(PRESENT) } else { self.remove(PRESENT) } self } /// Checks the gate's privilege #[inline] pub fn get_dpl(&self) -> PrivilegeLevel { unsafe { mem::transmute((*self & DPL).bits as u16 >> 5) } } /// Sets the privilege level of the gate pub fn set_dpl(&mut self, dpl: PrivilegeLevel) -> &mut Self { self.insert(GateFlags::from_bits_truncate((dpl as u8) << 5)); self } } impl Default for GateFlags { fn default() -> Self { GateFlags { bits: 0 } } } //==------------------------------------------------------------------------== use super::ErrorCodeHandler; // IDT implementation /// An Interrupt Descriptor Table /// /// The IDT is either 64-bit or 32-bit. #[repr(C)] pub struct Idt { pub divide_by_zero: Gate , /// debug interrupt handler - reserved pub debug: Gate , pub nmi: Gate , pub breakpoint: Gate , pub overflow: Gate , pub bound_exceeded: Gate , pub undefined_opcode: Gate , pub device_not_available: Gate , pub double_fault: Gate , _coprocessor_segment_overrun: Gate , pub invalid_tss: Gate , pub segment_not_present: Gate , pub stack_segment_fault: Gate , pub general_protection_fault: Gate , pub page_fault: Gate , _reserved: Gate , pub floating_point_error: Gate , pub alignment_check: Gate , pub machine_check: Gate , pub simd_fp_exception: Gate , pub virtualization: Gate , _reserved_2: [Gate; 9] , pub security_exception: Gate , _reserved_3: Gate , /// user-defined interrupts pub interrupts: [Gate; ENTRIES - super::NUM_EXCEPTIONS] } impl Default for Idt { #[inline] fn default() -> Self { Idt { interrupts: [Default::default(); ENTRIES - super::NUM_EXCEPTIONS] , ..Default::default() } } } impl ops::Index for Idt { type Output = Gate; #[inline] fn index(&self, index: usize) -> &Gate { unsafe { &mem::transmute::<&Self, &[Gate; ENTRIES]>(self)[index] } } } impl ops::IndexMut for Idt { #[inline] fn index_mut(&mut self, index: usize) -> &mut Self::Output { unsafe { &mut mem::transmute::<&mut Self, &mut [Gate; ENTRIES]>(self)[index] } } } impl Idt { /// Construct a new IDT with all interrupt gates set to [`absent`]. /// /// [`absent`]: struct.Gate.absent.html pub const fn new() -> Self { Idt { divide_by_zero: Gate::absent() , debug: Gate::absent() , nmi: Gate::absent() , breakpoint: Gate::absent() , overflow: Gate::absent() , bound_exceeded: Gate::absent() , undefined_opcode: Gate::absent() , device_not_available: Gate::absent() , double_fault: Gate::absent() , _coprocessor_segment_overrun: Gate::absent() , invalid_tss: Gate::absent() , segment_not_present: Gate::absent() , stack_segment_fault: Gate::absent() , general_protection_fault: Gate::absent() , page_fault: Gate::absent() , _reserved: Gate::absent() , floating_point_error: Gate::absent() , alignment_check: Gate::absent() , machine_check: Gate::absent() , simd_fp_exception: Gate::absent() , virtualization: Gate::absent() , _reserved_2: [Gate::absent(); 9] , security_exception: Gate::absent() , _reserved_3: Gate::absent() , interrupts: [Gate::absent(); ENTRIES - super::NUM_EXCEPTIONS] } } /// Enable interrupts pub unsafe fn enable_interrupts() { asm!("sti") } /// Disable interrupts pub unsafe fn disable_interrupts() { asm!("cli") } /// Add a new interrupt gate pointing to the given handler #[inline] pub fn add_handler( &mut self , idx: usize , handler: Handler) -> &mut Self where Gate: convert::From { self.add_gate(idx, Gate::from(handler)) } /// Add a [`Gate`](struct.Gate.html) to the IDT. #[inline] pub fn add_gate(&mut self, idx: usize, gate: Gate) -> &mut Self { self[idx] = gate; self } } impl DTable for Idt { type Entry = Gate; #[inline] fn entry_count(&self) -> usize { ENTRIES } #[inline] fn load(&'static self) { unsafe { asm!( "lidt ($0)" :: "r"(&self.get_ptr()) : "memory" ); } kinfoln!(dots: " . . ", target: "Loading IDT", "[ OKAY ]"); } } ================================================ FILE: cpu/src/x86_all/interrupts/idt/tests.rs ================================================ #[test] fn test_task_gate_32() { assert_eq!(super::TASK_GATE_32.bits, 5)} #[test] fn test_int_gate_32() { assert_eq!(super::INT_GATE_32.bits, 14)} #[test] fn test_trap_gate_32() { assert_eq!(super::TRAP_GATE_32.bits, 16)} ================================================ FILE: cpu/src/x86_all/interrupts/mod.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Intterupt handling on x86 machines. //! //! This module provides support for interrupt handling on both `x86` and //! `x86_64` as a black box. Code that depends on this can use the same API //! regardless of system word size. #![warn(missing_docs)] pub mod idt; pub mod pics; use vga::{CONSOLE, Color}; use core::fmt; use core::fmt::Write; use context::InterruptFrame; /// Number of interrupt vectors corresponding to CPU exceptions. /// /// These are the first 32 vectors in the IDT. pub const NUM_EXCEPTIONS: usize = 32; /// An ISR that handles a regular interrupt pub type InterruptHandler = extern "x86-interrupt" fn (&InterruptFrame); /// An ISR that handles an error with an error code pub type ErrorCodeHandler = extern "x86-interrupt" fn (&InterruptFrame, usize); /// A description of a CPU exception #[derive(Debug)] pub struct ExceptionInfo { /// The name of the exception pub name: &'static str , /// The mnemomic code for the exception pub mnemonic: &'static str , /// The type of IRQ for this exception /// - fault /// - trap /// - interrupt pub irq_type: &'static str , /// The source triggering the exception. /// /// Typically this refers to what opcode(s) can /// cause this exception. pub source: &'static str } bitflags! { flags PageFaultErrorCode: u32 { /// If 1, the error was caused by a page that was present. /// Otherwise, the page was non-present. const PRESENT = 1 << 0 , /// If 1, the error was caused by a read. If 0, the cause was a write. const READ_WRITE = 1 << 1 , /// If 1, the error was caused during user-mode execution. /// If 0, the processor was in kernel mode. const USER_MODE = 1 << 2 , /// If 1, the fault was caused by reserved bits set to 1 during a fetch. const RESERVED = 1 << 3 , /// If 1, the fault was caused during an instruction fetch. const INST_FETCH = 1 << 4 , /// If 1, there was a protection key violation. const PROTECTION = 1 << 5 } } impl fmt::Display for PageFaultErrorCode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "Caused by {}{}{} during a {}{} executing in {} mode." , if self.contains(PRESENT) { "a present page" } else { "a non-present page" } , if self.contains(PROTECTION) { " protection-key violation" } else { "" } , if self.contains(RESERVED) { " reserved bits set to one "} else { "" } , if self.contains(READ_WRITE) { "read" } else { "write" } , if self.contains(INST_FETCH) { " in an instruction fetch"} else { "" } , if self.contains(USER_MODE) { "user" } else { "kernel" } ) } } /// Handler for the system timer interrupt pub extern "x86-interrupt" fn timer(_frame: &InterruptFrame) { // do nothing, just signal the pics to end the IRQ // println!("timer!"); unsafe { pics::end_pic_interrupt(0x20); } } /// Handles page fault exceptions #[no_mangle] #[inline(never)] pub extern "x86-interrupt" fn page_fault( frame: &InterruptFrame, error_code: usize) { let _ = write!( CONSOLE.lock() .set_colors(Color::White, Color::Blue) // .clear() , "IT'S NOT MY FAULT: Page Fault at {:p} \ \nError code: {:#x}\n\n{}\n{:?}" , (*frame).rip , error_code , PageFaultErrorCode::from_bits_truncate(error_code as u32) , *frame ); // TODO: stack dumps please loop { } } /// Test interrupt handler for ensuring that the IDT is configured correctly. #[no_mangle] #[inline(never)] pub extern "x86-interrupt" fn test(_frame: &InterruptFrame) { // assert_eq!(state.int_id, 0x80); kinfoln!(dots: " . . ", target: "Testing interrupt handling:", "[ OKAY ]"); // send the PICs the end interrupt signal unsafe { pics::end_pic_interrupt(0xff); } } ================================================ FILE: cpu/src/x86_all/interrupts/pics.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Support for the 8259 Programmable Interrupt Controller. //! //! The 8259 PIC controls all of our IO IRQs. The PIC recieves IRQ requests //! and feeds them to the CPU in order. An x86 system typically has two PICs, //! PIC1 and PIC2, each of which provides 8 IRQs. Two PICs provide us with 15 //! unique IRQs, because one interrupt on the leader PIC is linked to interrupts //! on the follower PIC. //! //! Mistakes were made, and Intel boneheadedly decided that it was a good idea //! to map the vector offset of PIC1 to 8, so that it maps to interrupts //! 8 ... 15 in the IDT. This conflicts with some of the interrupt numbers //! used by CPU exceptions. Therefore, we must remap the PIC vectors so that //! PIC1 starts at 32 and PIC2 at 40. use Port; use spin::Mutex; use core::mem::transmute; /// Starting offset for PIC1 const OFFSET: u8 = 0x20; /// Command port for the follower PIC (PIC2) const FOLLOWER_CMD_PORT: u16 = 0xA0; /// Command port for the leader PIC (PIC1) const LEADER_CMD_PORT: u16 = OFFSET as u16; /// Commands to send to the PIC #[repr(u8)] #[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)] enum Command { /// Command to set the PIC to 8086 mode Mode8086 = 0x01 , /// Command to initialize a PIC Init = 0x11 , /// Command that ends an interrupt request EndIRQ = 0x20 , /// Command to read the Interrupt Request Register ReadIRR = 0x0a , /// Command to read the Interrupt Service Register ReadISR = 0x0b } /// List of IRQs on the x86. /// /// See [here](https://en.wikibooks.org/wiki/X86_Assembly/Programmable_Interrupt_Controller) for more info. #[repr(u8)] #[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)] pub enum IRQ { /// System timer IRQ Timer = OFFSET , /// PS/2 keyboard controller PS2Keyboard = 1 + OFFSET , /// PIC2 cascade IRQ Cascade = 2 + OFFSET , /// COM2 serial port COM2 = 3 + OFFSET , /// COM1 serial port COM1 = 4 + OFFSET , /// Line printer 2 LPT2 = 5 + OFFSET , /// Floppy disc controller Floppy = 6 + OFFSET , /// Line printer 1 LPT1 = 7 + OFFSET , /// CMOS clock RTCTimer = 8 + OFFSET , /// PS/2 mouse controller PS2Mouse = 12 + OFFSET , /// Floating-point Coprocessor FPU = 13 + OFFSET , /// ATA channel 1 PrimaryATA = 14 + OFFSET , /// ATA channel 2 SecondaryATA = 15 + OFFSET } /// A 8259 Programmable Interrupt Controller. pub struct PIC { /// The base offset to which interrupts on this PIC are mapped offset: u8 , /// The port on the CPU that sends commands to this PIC. command_port: Port , /// The port that sends and recieves data from the PIC data_port: Port } impl PIC { /// Construct a new leader PIC pub const fn leader() -> PIC { PIC { offset: OFFSET , command_port: Port::::new(LEADER_CMD_PORT) , data_port: Port::::new(LEADER_CMD_PORT + 1) } } /// Construct a new follower PIC pub const fn follower() -> PIC { PIC { offset: OFFSET + 8 , command_port: Port::::new(FOLLOWER_CMD_PORT) , data_port: Port::::new(FOLLOWER_CMD_PORT + 1) } } /// Returns true if this PIC is the leader PIC #[inline] pub fn is_leader(&self) -> bool { self.offset == OFFSET } /// Send a command to the PIC #[inline] fn send_command(&self, command: Command) { self.command_port .write(command as u8) } /// Send a byte of data to the PIC #[inline] pub fn send_data(&self, data: u8) { self.data_port .write(data) } /// Send the "initialize" command to this PIC #[inline] pub fn initialize(&self) { self.send_command(Command::Init) } /// Read the contents of the ISR (Interrupt Service Register) from this PIC #[inline] pub fn read_isr(&self) -> u8 { self.send_command(Command::ReadISR); self.data_port.read() } /// Read the contents of the IRR (Interrupt Request Register) from this PIC #[inline] pub fn read_irr(&self) -> u8 { self.send_command(Command::ReadIRR); self.data_port.read() } } /// Trait for something which is capable of handling a PIC IRQ trait IRQHandler { /// Returns whether or not this handler handles the given IRQ fn handles(&self, irq: IRQ) -> bool; /// End an interrupt request fn end_interrupt(&self, irq: IRQ); } impl IRQHandler for PIC { fn handles(&self, irq: IRQ) -> bool { self.offset <= (irq as u8) && (irq as u8) < self.offset + 8 } fn end_interrupt(&self, _: IRQ) { let _ = self.send_command(Command::EndIRQ); } } /// A pair of PICs in cascade mode. /// /// This is the standard configuration on all modern x86 systems. struct BothPICs (PIC, PIC); impl BothPICs { /// Constructs the system's pair of PICs const fn new() -> Self { BothPICs (PIC::leader(), PIC::follower()) } /// Initialize the system's PICs. pub unsafe fn initialize(&mut self) { let wait_port = Port::::new(0x80); let wait = || { wait_port.write(0); }; // helper macro to avoid writing repetitive code macro_rules! send { (pic0 => $data:expr) => { self.0.send_data($data); wait(); }; (pic1 => $data:expr) => { self.1.send_data($data); wait(); }; } // Read the default interrupt masks from PIC1 and PIC2 let (saved_mask1, saved_mask2) = (self.0.data_port.read(), self.1.data_port.read()); // Send both PICs the 'initialize' command. self.0.initialize(); wait(); self.1.initialize(); wait(); // Each PIC then expects us to send it the following: // 1. the PIC's new vector offset send!(pic0 => self.0.offset); send!(pic1 => self.1.offset); // 2. number to configure PIC cascading send!(pic0 => 0x04); send!(pic1 => 0x02); // 3. command for 8086 mode send!(pic0 => Command::Mode8086 as u8); send!(pic1 => Command::Mode8086 as u8); // 4. finally, the mask we saved earlier send!(pic0 => saved_mask1); send!(pic1 => saved_mask2); } } impl IRQHandler for BothPICs { fn handles(&self, irq: IRQ) -> bool { self.0.handles(irq) || self.1.handles(irq) } fn end_interrupt(&self, irq: IRQ) { if self.1.handles(irq) { self.1.end_interrupt(irq); } self.0.end_interrupt(irq); } } /// Global PIC instance and mutex static PICS: Mutex = Mutex::new(BothPICs::new()); /// Initialize the system's Programmable Interrupt Controller /// /// # Safety /// - This should only ever be called by the kernel boot process. /// Initializing the PICs once they have already been inicialized /// will probably cause Bad Things to take place. pub unsafe fn initialize() { PICS.lock() .initialize(); kinfoln!(dots: " . . ", target: "Initializing PICs", "[ OKAY ]"); } /// If an interrupt is being handled by the PICs, end that interrupt. /// /// This is called by the interrupt handler at the end of all interrupts. /// If the interrupt is not a PIC interrupt, it silently does nothing. /// /// # Safety /// - This should only be called by interrupt handler functions. pub unsafe fn end_pic_interrupt(interrupt_id: u8) { let pics = PICS.lock(); let irq: IRQ = transmute(interrupt_id); if pics.handles(irq) { pics.end_interrupt(irq) } } ================================================ FILE: cpu/src/x86_all/mod.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Common functionality for `x86` and `x86_64` CPUs //! //! Note that while we support both the `x86` and `x86_64` platforms, //! we only support 32-bit `x86` machines. SOS is a protected mode or //! long mode OS, and will not run on early x86 machines such as 286s. use util::{io, Void}; macro_rules! cpu_flag { ($doc:meta, $flag:ident, $get:ident, $set:ident) => { #[$doc] pub unsafe fn $get() -> bool { read().contains($flag) } #[$doc] pub unsafe fn $set(set: bool) { let mut flags: Flags = read(); if set { flags.insert($flag); } else { flags.remove($flag); } write(flags) } }; ($doc:meta, $flag:ident, $get:ident) => { #[$doc] pub unsafe fn $get() -> bool { read().contains($flag) } } } pub mod control_regs; pub mod segment; pub mod dtable; pub mod flags; pub mod timer; pub mod interrupts; /// Represents an x86 privilege level. #[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Ord, Eq)] #[repr(u16)] pub enum PrivilegeLevel { /// Ring 0 is the most privileged ring KernelMode = 0 , Ring1 = 1 , Ring2 = 2 , /// Ring 3 is the least privileged ring UserMode = 3 } impl PrivilegeLevel { /// Returns the current I/O Privilege Level from `%eflags`/`%rflags`. #[inline] pub fn current_iopl() -> Self { flags::read().iopl() } } pub struct UnsafePort(u16); impl UnsafePort { pub const unsafe fn new(number: u16) -> UnsafePort { UnsafePort(number) } /// Read a byte (8 bits) from this port pub unsafe fn in8(&self) -> u8 { let result: u8; asm!( "in al, dx" : "={al}"(result) : "{dx}"(self.0) :: "intel" , "volatile" ); result } /// Read a word (16 bits) from this port pub unsafe fn in16(&self) -> u16 { let result: u16; asm!( "in ax, dx" : "={ax}"(result) : "{dx}"(self.0) :: "intel" , "volatile" ); result } /// Read a long word (32 bits) from this port pub unsafe fn in32(&self) -> u32 { let result: u32; asm!( "in eax, dx" : "={eax}"(result) : "{dx}"(self.0) :: "intel" , "volatile" ); result } pub unsafe fn out8(&self, value: u8) { asm!( "out dx, al" :: "{dx}"(self.0) , "{al}"(value) :: "intel" , "volatile" ); } pub unsafe fn out16(&self, value: u16) { asm!( "out dx, ax" :: "{dx}"(self.0) , "{ax}"(value) :: "intel" , "volatile" ); } pub unsafe fn out32(&self, value: u32) { asm!( "out dx, eax" :: "{dx}"(self.0) , "{eax}"(value) :: "intel" , "volatile" ); } } impl io::Read for UnsafePort { type Error = Void; /// Reads a single byte into the given buffer fn read(&mut self, buf: &mut [u8]) -> Result { unsafe { Ok(match &mut *buf { // if the length of the buffer is 0, then obviously // no bytes were read &mut [] => 0 // otherwise, read one byte into the head of the buffer , &mut [ref mut head, _..] => { *head = self.in8(); 1 } }) } } /// Reads a new byte into each position in the buffer. fn read_all(&mut self, buf: &mut [u8]) -> Result { let mut read_bytes = 0; for idx in buf.iter_mut() { // for each index in the buffer, read another byte from the port unsafe { *idx = self.in8(); } // and increment the number of bytes read (this should be faster // than calling `buf.len()` later; as we only need 1 loop) read_bytes += 1; } Ok(read_bytes) } } impl io::Write for UnsafePort { type Error = Void; fn write(&mut self, buf: &[u8]) -> Result { let mut written_bytes = 0; for byte in buf { // write each byte in the buffer to the port unsafe { self.out8(*byte); } // and increment the number of bytes written (this should be faster // than calling `buf.len()` later; as we only need 1 loop) written_bytes += 1; } Ok(written_bytes) } } ================================================ FILE: cpu/src/x86_all/segment.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2016 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Code for using the `x86` and `x86_64` segmentation hardware. //! //! For more information, refer to the _Intel® 64 and IA-32 Architectures //! Software Developer’s Manual_, Vol. 3A, section 3.2, "Using Segments". //! Some of the documentation present in this module was taken from the Intel //! manual. //! // because some extern types have bitflags members, which cannot // be marked repr(C) but should compile down to an unsigned integer #![allow(improper_ctypes)] #![deny(missing_docs)] use core::{fmt, mem}; use super::{PrivilegeLevel, dtable}; /// The number of entries in the GDT #[cfg(target_arch = "x86_64")] pub const GDT_SIZE: usize = 2; /// Structure representing a Global Descriptor Table #[cfg(target_arch = "x86_64")] #[repr(C, packed)] pub struct Gdt { _null: Descriptor , /// The code segment descriptor pub code: Descriptor // , /// The data segment descriptor // pub data: Descriptor } /// The number of entries in the GDT #[cfg(target_arch = "x86")] pub const GDT_SIZE: usize = 512; #[cfg(target_arch = "x86")] pub type Gdt = [Descriptor; GDT_SIZE]; impl dtable::DTable for Gdt { type Entry = Descriptor; /// Returns the number of Entries in the `DTable`. /// /// This is used for calculating the limit. #[inline(always)] fn entry_count(&self) -> usize { GDT_SIZE } /// Load the GDT table with the `lgdt` instruction. #[inline] fn load(&self) { unsafe { asm!( "lgdt ($0)" :: "r"(&self.get_ptr()) : "memory" ); } } } extern { /// A Global Descriptor Table (GDT) /// /// This is used for configuring segmentation. Since we use paging rather than /// segmentation for memory protection, we never actually _use_ the GDT, but /// x86 requires that it be properly configured nonetheless. So, here it is. #[cfg(target_arch = "x86_64")] #[link_section = ".gdt64"] pub static GDT: Gdt; } bitflags! { /// A segment selector is a 16-bit identifier for a segment. /// /// It does not point directly to the segment, but instead points to the /// segment descriptor that defines the segment. /// /// A segment selector contains the following items: /// /// + *Requested Privilege Level (RPL)*: bits 0 and 1. /// Specifies the privelege level of the selector. /// + *Table Indicator*: bit 2. Specifies which descriptor table to use. /// + *Index*: bits 3 through 15. Selects one of 8192 descriptors in the /// GDT or LDT. The processor multiplies the index value by 8 (the number /// of bytes in a segment descriptor) and adds the result to the base /// address of the GDT or LDT (from the `%gdtr` or `%ldtr` register, /// respectively). #[repr(C)] pub flags Selector: u16 { /// Set if the RPL is in Ring 0 const RPL_RING_0 = 0b00 , /// Set if the RPL is in Ring 1 const RPL_RING_1 = 0b01 , /// Set if the RPL is in Ring 2 const RPL_RING_2 = 0b10 , /// Set if the RPL is in Ring 3 const RPL_RING_3 = 0b11 , /// Requested Privelege Level (RPL) bits const RPL = RPL_RING_0.bits | RPL_RING_1.bits | RPL_RING_2.bits | RPL_RING_3.bits , /// If the Table Indicator (TI) is 0, use the GDT const TI_GDT = 0 << 3 , /// If the TI is 1, use the LDT const TI_LDT = 1 << 3 } } impl Selector { /// Create a new `Selector` /// /// # Arguments /// - `index`: the index in the GDT or LDT pub const fn new(index: u16) -> Self { Selector { bits: index << 3 } } /// Create a new `Selector` from raw bits pub const fn from_raw(bits: u16) -> Self { Selector { bits: bits } } /// Returns the current value of the code segment register. pub fn from_cs() -> Self { let cs: u16; unsafe { asm!( "mov $0, cs" : "=r"(cs) ::: "intel" ) }; Selector::from_bits_truncate(cs) } /// Extracts the index from a segment selector #[inline] pub fn index(&self) -> u16 { self.bits >> 3 } /// Sets this segment selector to be a GDT segment. /// /// If the segment is already a GDT segment, this will quietly do nothing. #[inline] pub fn set_global(&mut self) -> &mut Self { self.remove(TI_LDT); self } /// Sets this segment selector to be an LDT segment. /// /// If the segment is already an LDT segment, this will quietly do nothing. #[inline] pub fn set_local(&mut self) -> &mut Self { self.insert(TI_GDT); self } /// Sets the Requested Priveliege Level (RPL) /// /// The RPL must be in the range between 0 and 3. #[inline] pub fn set_rpl(&mut self, rpl: PrivilegeLevel) -> &mut Self { self.bits &= rpl as u16; self } /// Checks the segment's privelige. #[inline] pub fn get_rpl(&self) -> PrivilegeLevel { unsafe { mem::transmute(*self & RPL) } } /// Load this selector into the stack segment register (`ss`). pub unsafe fn load_ss(&self) { asm!( "mov ss, $0" :: "r"(self.bits) : "memory" : "intel"); } /// Load this selector into the data segment register (`ds`). pub unsafe fn load_ds(&self) { asm!( "mov ds, $0" :: "r"(self.bits) : "memory" : "intel"); } /// Load this selector into the `es` segment register. pub unsafe fn load_es(&self) { asm!( "mov es, $0" :: "r"(self.bits) : "memory" : "intel"); } /// Load this selector into the `fs` segment register. pub unsafe fn load_fs(&self) { asm!( "mov fs, $0" :: "r"(self.bits) : "memory" : "intel"); } /// Load this selector into the `gs` segment register. pub unsafe fn load_gs(&self) { asm!( "mov gs, $0" :: "r"(self.bits) : "memory" : "intel"); } /// Load this selector into the code segment register. /// /// N.B. that as we cannot `mov` directly to `cs`, we have to do this /// differently. We push the selector and return value onto the stack, /// and use `lret` to reload `cs`. #[cfg(target_arch = "x86_64")] pub unsafe fn load_cs(&self) { asm!( "push $0 lea rax, [rip + 1] push rax iret 1:" :: "r"(self.bits as u64) : "rax", "memory" : "intel"); } } impl Default for Selector { #[inline] fn default() -> Self { Selector::from_cs() } } impl fmt::Display for Selector { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // TODO: this could be much less ugly. let ring = if self.contains(RPL_RING_3) { "3" } else if self.contains(RPL_RING_2) { "2" } else if self.contains(RPL_RING_1) { "1" } else if self.contains(RPL_RING_0) { "0" } else { unreachable!() }; let table = if self.contains(TI_GDT) { "GDT" } else { "LDT" }; write!(f, "{}[{}], Ring {}", table, self.index(), ring) } } /// A segment descriptor is an entry in an IDT or GDT. /// /// A segment descriptor is a data structure in a GDT or LDT that provides the /// processor with the size and location of a segment, as well as access control /// and status information. Segment descriptors are typically created by /// compilers, linkers, loaders, or the operating system or executive, but not /// application programs. /// #[repr(C, packed)] pub struct Descriptor { /// The last 8 bits of the base address pub base_high: u8 , /// The next 16 bits are bitflags pub flags: Flags , /// The middle 8 bits of the base address pub base_mid: u8 , /// The first 16 bits of the base address pub base_low: u16 , /// the segment limit pub limit: u16 } impl Descriptor { /// Constructs a new null `Descriptor` pub const fn null() -> Self { Descriptor { base_high: 0 , flags: Flags::null() , base_mid: 0 , base_low: 0 , limit: 0 } } /// Constructs a new `Descriptor` from a `limit` and a `base` address pub fn new(base: u32, limit: u32) -> Self { let (hi, mid, lo): (u8, u8, u16) = unsafe { mem::transmute(base) }; let (limit_lo, limit_hi): (u16, u16) = unsafe { mem::transmute(limit) }; // I hope this is right... let flags = (limit_hi & 0b1111) << 8; Descriptor { base_high: hi , flags: Flags::from_bits_truncate(flags) , base_mid: mid , base_low: lo , limit: limit_lo } } /// Extract the limit part from the flags and limit fields. #[inline] pub fn get_limit(&self) -> u32 { // TODO: i hope this is right... self.flags.get_limit_part() & self.limit as u32 } } impl Default for Descriptor { #[inline] fn default() -> Self { Descriptor::null() } } bitflags! { /// Segment descriptor bitflags field. /// /// Some of the bitflags vary based on the type of segment. Currently /// the API for this is a bit of a mess. pub flags Flags: u16 { /// 1 if this is a code or data segment that has been accessed const CODE_DATA_ACC = 1 << 0 , /// Four bits that indcate the type of the segment const SEGMENT_TYPE = 0b0000_0000_0000_1111 , /// 1 if this is a data/code segment, 0 if this is a system segment const DESCR_TYPE = 1 << 4 , /// Two bits indicating the descriptor priveliege level const DPL = 0b0000_0000_0110_0000 , /// 1 if this segment is present. const PRESENT = 1 << 7 , /// bits 16...19 of the limit const LIMIT = 0b0000_1111_0000_0000 , /// 1 if this segment is available for use by system software const AVAILABLE = 1 << 12 , /// 0 if this is a 16- or 32-bit segment, 1 if it is a 64-bit segment const LENGTH = 1 << 13 , /// 0 if this is a 16-bit segment, 1 if it is a 32-bit segment const DEFAULT_SIZE = 1 << 14 , /// 0 if the limit of this segment is given in bytes, 1 if it is given /// in 4092-byte pages const GRANULARITY = 1 << 15 , /// If this is a code or data segment and the accessed bit is set, /// it has been accessed. const ACCESSED = DESCR_TYPE.bits & CODE_DATA_ACC.bits } } impl Flags { /// Returns a new set of `Flag`s with all bits set to 0. pub const fn null() -> Self { Flags { bits: 0 } } /// Returns a new set of `Flag`s from a raw `u16` pub const fn from_raw(bits: u16) -> Self { Flags { bits: bits } } /// Get the Descriptor Privilege Level (DPL) from the flags #[inline] pub fn get_dpl(&self) -> PrivilegeLevel { unsafe { mem::transmute((*self & DPL).bits >> 5) } } /// Returns true if this segment is a system segment. /// /// Returns false if it is a code or data segment. #[inline] pub fn is_system(&self) -> bool { !self.contains(DESCR_TYPE) } /// Returns false if this segment is present #[inline] pub fn is_present(&self) -> bool { !self.contains(PRESENT) } /// Returns false if this segment is available to system software #[inline] pub fn is_available(&self) -> bool { self.contains(AVAILABLE) } /// Returns true if this is a code or data segment that has been accessed. /// /// Returns false if it has not been accessed OR if it is a system segment. #[inline] pub fn is_accessed(&self) -> bool { self.contains(ACCESSED) } /// Returns the system type indicator, if this is a system segment. /// /// # Returns /// + `Some(SysType)` if this is a system segment /// + `None` if this is a code or data segment pub fn get_system_type(&self) -> Option { if self.is_system() { Some(unsafe { mem::transmute((*self & SEGMENT_TYPE).bits) }) } else { None } } /// Returns the code type indicator. /// /// # Returns /// + `Some(CodeType)` if this is not a system segment /// + `None` if this is a system segment pub fn get_code_type(&self) -> Option { if self.is_system() { None } else { Some(CodeFlags::from_bits_truncate(self.bits)) } } /// Returns the data type indicator. /// /// # Returns /// + `Some(DataType)` if this is not a system segment /// + `None` if this is a system segment pub fn get_data_type(&self) -> Option { if self.is_system() { None } else { Some(DataFlags::from_bits_truncate(self.bits)) } } #[inline] fn get_limit_part(&self) -> u32 { ((*self & LIMIT).bits as u32) << 8 } } /// Possible ways to interpret the type bits of a segment selector. #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum Type { /// The type bits interpreted as a system segment System(SysType) , /// The type bits interpreted as a code segment Code(CodeFlags) , /// The type bits interpreted as a data segment Data(DataFlags) } /// Possible types of for a system segment. #[derive(Copy, Clone, Eq, PartialEq, Debug)] #[repr(u16)] pub enum SysType { /// System segment used for storing a local descriptor table. Ldt = 0b0010 , /// An available translation stack segment. TssAvailable = 0b1001 , /// A busy translation stack segment TssBusy = 0b1011 , /// A call gate system segment CallGate = 0b1100 , /// An interrupt gate system segment InterruptGate = 0b1110 , /// A trap gate system segment TrapGate = 0b1111 } bitflags! { /// The type-specific section of a data segment's flags pub flags DataFlags: u16 { /// 0 if this segment hasn't been accessed, 1 if it has const DATA_ACCESSED = 0b0001 , /// 0 if this segment is read-only, 1 if it is read-write const WRITE = 0b0010 , /// 1 if this segment expands down const EXPAND_DOWN = 0b0100 } } impl DataFlags { /// Returns true if the data segment is read-only #[inline] pub fn is_read_only(&self) -> bool { !self.contains(WRITE) } /// Returns true if the data segment has been accessed #[inline] pub fn is_accessed(&self) -> bool { self.contains(DATA_ACCESSED) } /// Returns true if the data segment expands down #[inline] pub fn is_expand_down(&self) -> bool { self.contains(EXPAND_DOWN) } } bitflags! { /// The type-specific section of a code segment's flags pub flags CodeFlags: u16 { /// 0 if this segment hasn't been accessed, 1 if it has const CODE_ACCESSED = 0b0001 , /// 0 if this segment is read-only, 1 if it is read-write const READ = 0b0010 , /// 0 if this segment is not executable, 1 if it is executable const EXECUTE = 0b1000 , /// 0 if this segment is non-conforming, 1 if it is conforming const CONFORMING = 0b0100 , /// Whether this segment is execute-only const EXEC_ONLY = EXECUTE.bits & !READ.bits } } impl CodeFlags { /// Returns true if the code segment is execute-only (not readable) #[inline] pub fn is_exec_only(&self) -> bool { self.contains(EXEC_ONLY) } /// Returns true if the code segment is readable #[inline] pub fn is_readable(&self) -> bool { self.contains(READ) } /// Returns true if the code segment has been accessed. #[inline] pub fn is_accessed(&self) -> bool { self.contains(CODE_ACCESSED) } /// Returns true if the code segment is conforming. #[inline] pub fn is_conforming(&self) -> bool { self.contains(CONFORMING) } } ================================================ FILE: cpu/src/x86_all/timer.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2016 Eliza eisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Code for interacting with the system timer & timestamp register. #![warn(missing_docs)] pub mod timestamp { //! x86 Timestamp register use core::mem; /// Read the current value of the timestamp counter. /// /// # Safety /// + This will cause a General Protection Fault if the TSD flag in register /// `%cr4` is set and the CPL is greater than 0. pub unsafe fn rtdsc() -> u64 { let (high, low): (u32, u32); asm!( "rdtsc" : "={eax}" (low), "={edx}" (high)); mem::transmute((high, low)) } /// Read the current timestamp, after other instructions have been executed. /// /// # Safety /// + This will cause a General Protection Fault if the TSD flag in register /// `%cr4` is set and the CPL is greater than 0. pub unsafe fn rtdscp() -> u64 { let (high, low): (u32, u32); asm!( "rdtscp" : "={eax}" (low), "={edx}" (high) ::: "volatile"); mem::transmute((high, low)) } /// Returns true if timestamps are currently available. #[inline] pub fn is_available() -> Result<(), &'static str> { use ::control_regs::cr4; use ::PrivilegeLevel; if PrivilegeLevel::current_iopl() != PrivilegeLevel::KernelMode { Err("Reading timestamp register requires kernel mode.") } else if // it's safe to do this since we already know we are in kernel mode. unsafe { cr4::is_timestamp_disabled() } { Err("Timestamp Disable bit in %cr4 is set") } else { Ok(()) } } /// Returns the current timestamp, or an error #[inline] pub fn get_timestamp() -> Result { is_available().map(|_| unsafe { rtdsc() }) } /// Returns the current timestamp or an error, after other instructions have /// been executed. #[inline] pub fn wait_get_timestamp() -> Result { is_available().map(|_| unsafe { rtdscp() }) } } ================================================ FILE: elf/Cargo.toml ================================================ [package] name = "elf" version = "0.0.2" authors = [ "Eliza Weisman " ] [profile.dev] opt-level = 3 debug = true rpath = false lto = false debug-assertions = true codegen-units = 1 panic = "abort" [profile.release] opt-level = 3 debug = true rpath = false lto = false panic = "abort" [dependencies] bitflags = "0.7" memory = { path = "../memory" } [dependencies.macro-attr] git = "https://github.com/DanielKeep/rust-custom-derive.git" version = "0.2.1" default-features = false ================================================ FILE: elf/README.md ================================================ elf: ELF parsing and loading ============================ a library for parsing, navigating, and loading 32- and 64-bit [Executable and Linkable File](http://wiki.osdev.org/ELF)s. ### why are we writing our own ELF lib? although there are a number of other nice [ELF](https://github.com/nrc/xmas-elf) [libraries](https://github.com/m4b/goblin) for Rust, I'm writing my own for the following handful of reasons: + requirements specific to our use-case (no std, no allocation, &c) + up to date with recent Rust language features (not all ELF libs are actively maintained) + compatibility with SOS' types & representations without translation layers (e.g., uses our `PAddr` type.) + it's fun & I want to do it myself! ================================================ FILE: elf/src/file.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! ELF file header use super::{ElfResult, ElfWord, Section, section}; use super::ValidatesWord; use section::Header as SectionHeader; use core::{fmt, mem, convert}; use core::ops::Range; /// Trait representing an ELF File Header. /// /// This trait allows [HeaderRepr] to provide a consistent API regardless /// of whether the header section uses 32- or 64-bit [ELF word]s. A number of /// field values in the header of various sizes are converted to `usize` by /// this API so that they can be used as indices, etc. /// /// For more information on ELF File Headers, refer to: /// + the ELF [specification] /// + the [OS Dev Wiki] /// /// [ELF word]: ../type.ElfWord.html /// [HeaderRepr]: struct.HeaderRepr.html /// [specification](http://www.sco.com/developers/gabi/latest/ch4.eheader.html) /// [OS Dev Wiki](http://wiki.osdev.org/ELF#Header) pub trait Header: Sized { type Word: ElfWord; /// Attempt to extract an ELF file header from a slice of bytes. fn from_slice<'a>(input: &'a [u8]) -> ElfResult<&'a Self>; /// Attempt to extract a section header from a slice of bytes. /// TODO: can/should the index be `usize`? // - eliza, 03/08/2017 fn parse_section<'a>(&'a self, input: &'a [u8], idx: u16) -> ElfResult<&'a Section>; fn sh_range(&self) -> Range { let start = self.sh_offset(); start .. start + (self.sh_entry_size() * self.sh_count()) } fn ph_range(&self) -> Range { let start = self.ph_offset(); start .. start + (self.ph_entry_size() * self.ph_count()) } /// Calculate the index for a [section header] /// /// TODO: should this check the index is reasonable & return a `Result` // - eliza, 03/10/2017 /// [section header]: ../section/struct.Header.html fn section_index(&self, idx: usize) -> Range { let size = self.sh_entry_size(); let start = self.sh_offset() + (idx * size); start .. start + size } /// Calculate the index for a program header /// /// TODO: should this check the index is reasonable & return a `Result` // - eliza, 03/10/2017 fn program_header_index(&self, idx: usize) -> Range { let size = self.ph_entry_size(); let start = self.ph_offset() + (idx * size); start .. start + size } // Field accessors ------------------------------------------- fn ident(&self) -> Ident; fn get_type(&self) -> Type; fn machine(&self) -> Machine; /// Offset of the program entry point fn entry_point(&self) -> usize; /// Offset of the start of program headers fn ph_offset(&self) -> usize; /// Number of program headers. fn ph_count(&self) -> usize; /// Size of a program header. fn ph_entry_size(&self) -> usize; /// Offset of the start of [section header]s. /// /// [section header]: ../section/struct.Header.html fn sh_offset(&self) -> usize; /// Number of [section header]s. /// /// [section header]: ../section/struct.Header.html fn sh_count(&self) -> usize; /// Size of a [section header]. /// /// [section header]: ../section/struct.Header.html fn sh_entry_size(&self) -> usize; /// TODO: can this return the flags type? // - eliza, 03/08/2017 fn flags(&self) -> u32; /// Index of the section header [string table]. /// /// [string table]: ../section/struct.StrTable.html"] fn sh_str_idx(&self) -> usize; } macro_rules! Header { (($($size:ty),+) $(pub)* enum $name:ident $($tail:tt)* ) => { Header! { @impl $name, $($size)+ } }; (($($size:ty),+) $(pub)* struct $name:ident $($tail:tt)*) => { Header! { @impl $name, $($size)+ } }; (@impl $name:ident, $($size:ty)+) => { $(impl Header for $name<$size> { type Word = $size; /// Attempt to extract an ELF file header from a slice of bytes. fn from_slice<'a>(input: &'a [u8]) -> ElfResult<&'a Self> { if input.len() < mem::size_of::() { Err("Input too short to extract ELF header") } else { unsafe { super::extract_from_slice::(input, 0, 1) .map(|x| &x[0]) } } } /// Attempt to extract a [section header] from a slice of bytes. /// /// TODO: should this move to the `File` type since it owns the /// byte slice (which then wouldn't have to be passed as an /// argument)? // - eliza, 03/10/2017 /// /// [section header]: ../section/struct.Header.html fn parse_section<'a>(&'a self, input: &'a [u8], idx: u16) -> ElfResult<&'a Section> where section::HeaderRepr: SectionHeader { if idx < section::SHN_LORESERVE { Err("Cannot parse reserved section.") } else { // use ValidatesWord to check if this section's Class field // will let us interpret the section with the requested // word length. this is a bit of a hack around the type // system not letting me do this the way I wanted to to.... let validator: &ValidatesWord = &self.ident.class; validator.check()?; let raw = &input[self.section_index(idx as usize)]; unsafe { Ok(&*(raw as *const [u8] as *const _ as *const section::HeaderRepr)) } } } #[inline] fn get_type(&self) -> Type { self.elftype.as_type() } impl_getters! { #[doc = "Index for the start of [section header]s. \ [section header]: ../section/struct.Header.html"] fn sh_offset(&self) -> usize; fn sh_entry_size(&self) -> usize; fn sh_count(&self) -> usize; #[doc = "Index for the start of program headers"] fn ph_offset(&self) -> usize; fn ph_entry_size(&self) -> usize; fn ph_count(&self) -> usize; #[doc = "Index for the program entry point"] fn entry_point(&self) -> usize; #[doc = "Index of the section header [string table] \ [string table]: ../section/struct.StrTable.html"] fn sh_str_idx(&self) -> usize; fn flags(&self) -> u32; fn ident(&self) -> Ident; fn machine(&self) -> Machine; } } impl<'a> convert::TryFrom<&'a [u8]> for &'a $name<$size> { type Error = &'static str; #[inline] fn try_from(slice: &'a [u8]) -> ElfResult { <$name<$size> as Header>::from_slice(slice) } } )+ } } macro_attr! { /// Raw representation of an ELF file header. #[derive(Copy, Clone, Debug, Header!(u32, u64))] #[repr(C, packed)] pub struct HeaderRepr { /// the ELF [file identifier](struct.Ident.html) pub ident: Ident , elftype: TypeRepr , pub machine: Machine , /// Program entry point entry_point: W , /// Offset for start of program headers ph_offset: W , /// Offset for start of [section header]s. /// [section header]: ../section/struct.Header.html sh_offset: W , pub flags: u32 , pub header_size: u16 , pub ph_entry_size: u16 , pub ph_count: u16 , pub sh_entry_size: u16 , pub sh_count: u16 , /// Index of the section header string table sh_str_idx: u16 } } /// ELF header magic pub const MAGIC: Magic = [0x7f, b'E', b'L', b'F']; /// Type of header magic pub type Magic = [u8; 4]; /// ELF identifier (`e_ident` in the ELF standard) #[derive(Copy, Clone, Debug)] #[repr(C, packed)] pub struct Ident { /// ELF magic numbers. Must be equal to the [ELF magic], `[0x7, E, L, F]`. /// /// [ELF magic]: constant.MAGIC.html pub magic: Magic , /// ELF [file class](enum.Class.html) (32- or 64-bit) pub class: Class , /// ELF [data encoding](enum.DataEncoding.html) (big- or little-endian) pub encoding: DataEncoding , /// ELF file [version](enum.Version.html) pub version: Version , /// What [operating system ABI] this file was compiled for. /// /// [operating system ABI]: enum.OsAbi.html pub abi: OsAbi , /// ABI version (often this is just padding) pub abi_version: u8 , _padding: [u8; 7] } impl Ident { #[inline] pub fn check_magic(&self) -> bool { self.magic == MAGIC } /// Returns true if the identifier section identifies a valid ELF file. #[inline] pub fn is_valid(&self) -> bool { // the ELF magic number is correct self.check_magic() && // the file class is either 32- or 64-bits self.class.is_valid() && // the data encoding is either big- or little-endian self.encoding.is_valid() } } /// Identifies the class of the ELF file #[derive(Copy, Clone, PartialEq, Debug)] #[repr(u8)] pub enum Class { /// Invalid ELF class file (`ELFCLASSNONE` in the standard) None = 0 , /// 32-bit ELF file (`ELFCLASS32` in the standard) Elf32 = 1 , /// 64-bit ELF file (`ELFCLASS64` in the standard) Elf64 = 2 } impl Class { /// Returns true if the class field for this file is valid. #[inline] pub fn is_valid(&self) -> bool { match *self { Class::None => false , _ => true } } } impl ValidatesWord for Class { #[inline] fn check(&self) -> ElfResult<()> { use self::Class::*; match *self { None => Err("Invalid ELF type ELFCLASSNONE!") , Elf32 => Err("Cannot extract 64-bit section from 32-bit ELF") , Elf64 => Ok(()) } } } impl ValidatesWord for Class { #[inline] fn check(&self) -> ElfResult<()> { use self::Class::*; match *self { None => Err("Invalid ELF type ELFCLASSNONE!") , Elf64 => // TODO: is this actually true? // - eliza, 03/08/2017 Err("Cannot extract 32-bit section from 64-bit ELF") , Elf32 => Ok(()) } } } /// Identifies the data encoding of the ELF file #[derive(Copy, Clone, PartialEq, Debug)] #[repr(u8)] pub enum DataEncoding { /// Invalid data encoding (`ELFDATANONE` in the standard) None = 0 , /// Twos-complement little-endian data encoding /// (`ELFDATA2LSB` in the standard) LittleEndian = 1 , /// Twos-complement big-endian data encoding /// (`ELFDATA2MSB` in the standard) BigEndian = 2 } impl DataEncoding { /// Returns true if the data encoding field for this file is valid. #[inline] pub fn is_valid(&self) -> bool { match *self { DataEncoding::None => false , _ => true } } } /// Operating system ABI #[derive(Copy, Clone, Debug)] #[repr(u8)] pub enum OsAbi { /// Ox00 also represents "none" SystemV = 0x00 , HpUx = 0x01 , NetBsd = 0x02 , Linux = 0x03 , Solaris = 0x06 , Aix = 0x07 , Irix = 0x08 , FreeBsd = 0x09 , OpenBsd = 0x0C , OpenVms = 0x0D } /// Identifies the version of the ELF file #[derive(Copy, Clone, PartialEq, Debug)] #[repr(u8)] pub enum Version { None = 0, Current = 1 } #[derive(Clone, Copy, PartialEq)] struct TypeRepr(u16); impl TypeRepr { /// TODO: rewrite this as a `convert::Into` implementation /// - eliza, 03/09/2017 pub fn as_type(&self) -> Type { match self.0 { 0 => Type::None , 1 => Type::Relocatable , 2 => Type::Executable , 3 => Type::SharedObject , 4 => Type::Core , anything => Type::Other(anything) } } } impl fmt::Debug for TypeRepr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.as_type().fmt(f) } } #[derive(Clone, Copy, PartialEq, Debug)] pub enum Type { None , Relocatable , Executable , SharedObject , Core , Other(u16) } #[allow(non_camel_case_types)] #[derive(Clone, Copy, PartialEq, Debug)] #[repr(u16)] pub enum Machine { None = 0 , Sparc = 0x02 , X86 = 0x03 , Mips = 0x08 , PowerPc = 0x14 , Arm = 0x28 , SuperH = 0x2A , Ia64 = 0x32 , X86_64 = 0x3E , AArch64 = 0xB7 } ================================================ FILE: elf/src/lib.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Parsing and loading Executable and Linkable Format (ELF) 32- and 64-bit //! binaries. //! //! For more information on the ELF format, refer to: //! //! + [Wikipedia](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format) //! + The [OS Dev Wiki](http://wiki.osdev.org/ELF) //! + The [ELF Format Specification](elfspec) //! //! [elfspec]: http://www.skyfree.org/linux/references/ELF_Format.pdf #![feature(core_intrinsics)] #![feature(try_from)] #![no_std] #[macro_use] extern crate bitflags; #[macro_use] extern crate macro_attr; extern crate memory; use core::{ ops, mem, slice, convert }; use core::convert::TryFrom; use memory::{ FrameRange, PhysicalPage }; macro_rules! impl_getters { ($(#[$attr:meta])* pub fn $name:ident(&self) -> PAddr; $($rest:tt)*) => { $(#[$attr])* #[inline] pub fn $name(&self) -> ::memory::PAddr { use ::memory::{PAddr, Addr}; PAddr::from(self.$name as ::Repr) } impl_getters!{ $( $rest )* } }; ($(#[$attr:meta])* fn $name:ident(&self) -> PAddr; $($rest:tt)*) => { $(#[$attr])* #[inline] fn $name(&self) -> ::memory::PAddr { use ::memory::{PAddr, Addr}; PAddr::from(self.$name as ::Repr) } impl_getters!{ $( $rest )* } }; ($(#[$attr:meta])* pub fn $name:ident(&self) -> PAddr;) => { $(#[$attr])* #[inline] pub fn $name(&self) -> ::memory::PAddr { use ::memory::{PAddr, Addr}; PAddr::from(self.$name as ::Repr) } impl_getters!{ $( $rest )* } }; ($(#[$attr:meta])* fn $name:ident(&self) -> PAddr;) => { $(#[$attr])* #[inline] fn $name(&self) -> ::memory::PAddr { use ::memory::{PAddr, Addr}; PAddr::from(self.$name as ::Repr) } impl_getters!{ $( $rest )* } }; ($(#[$attr:meta])* pub fn $name:ident(&self) -> $ty:ty; $($rest:tt)*) => { $(#[$attr])* #[inline] pub fn $name(&self) -> $ty { self.$name as $ty } impl_getters!{ $( $rest )* } }; ($(#[$attr:meta])* fn $name:ident(&self) -> $ty:ty; $($rest:tt)*) => { $(#[$attr])* #[inline] fn $name(&self) -> $ty { self.$name as $ty } impl_getters!{ $( $rest )* } }; ( $(#[$attr:meta])* pub fn $name: ident (&self)-> $ty:ty; ) => { $(#[$attr])* #[inline] pub fn $name(&self) -> $ty { self.$name as $ty } }; ( $(#[$attr:meta])* fn $name: ident (&self)-> $ty:ty; ) => { $(#[$attr])* #[inline] fn $name(&self) -> $ty { self.$name as $ty } }; () => {}; } pub mod section; pub mod file; pub mod program; /// An ELF section header. pub type Section = section::Header; pub type ProgramHeader = program::Header; /// An ELF header file. pub type FileHeader = file::HeaderRepr; /// TODO: should ELF have its own error type? pub type ElfResult = Result; pub trait ElfWord: Sized + Copy + Clone + ops::Add + ops::Sub + ops::Mul + ops::Div + ops::Shl + ops::Shr { } impl ElfWord for u64 { } impl ElfWord for u32 { } #[cfg(target_pointer_width = "32")] type DefaultWord = u32; #[cfg(target_pointer_width = "64")] type DefaultWord = u64; /// Hack to make the type-system let me do what I want trait ValidatesWord { fn check(&self) -> ElfResult<()>; } /// A handle on a parsed ELF binary /// TODO: do we want this to own a HashMap of section names to section headers, /// to speed up section lookup? // - eliza, 03/08/2017 #[derive(Debug)] pub struct Image< 'bytes // lifetime of the byte slice , Word = DefaultWord // default to machine's pointer size , ProgHeader = ProgramHeader // same word type , SectHeader = Section , Header = FileHeader // must have same word type > // jesus christ where Word: ElfWord + 'bytes , ProgHeader: program::Header + Sized + 'bytes , SectHeader: section::Header + Sized + 'bytes , Header: file::Header + 'bytes { /// the binary's [file header](file/trait.Header.html) pub header: &'bytes Header , /// references to each [section header](section/struct.Header.html) pub sections: &'bytes [SectHeader] , /// references to each [program header](program/trait.Header.html) pub program_headers: &'bytes [ProgHeader] , /// the raw binary contents of the ELF binary. /// note that this includes the _entire_ binary contents of the file, /// so the file header and each section header is included in this slice. binary: &'bytes [u8] } impl <'a, Word, ProgHeader, SectHeader, Header> Image<'a, Word, ProgHeader, SectHeader, Header> where Word: ElfWord + 'a , ProgHeader: program::Header + Sized + 'a , SectHeader: section::Header + Sized + 'a , Header: file::Header + 'a { /// Returns the section header [string table]. /// /// [string table]: section/struct.StrTable.html pub fn sh_str_table(&'a self) -> section::StrTable<'a> { // TODO: do we want to validate that the string table index is // reasonable (e.g. it's not longer than the binary)? // - eliza, 03/08/2017 // TODO: do we want to cache a ref to the string table? // - eliza, 03/08/2017 section::StrTable::from(&self.binary[self.header.sh_str_idx()..]) } } impl<'a, Word, PH, SH, FH> TryFrom<&'a [u8]> for Image<'a, Word, PH, SH, FH> where Word: ElfWord + 'a , PH: program::Header + 'a , SH: section::Header + 'a , FH: file::Header + 'a , &'a FH: convert::TryFrom<&'a [u8], Error = &'static str> { type Error = &'static str; fn try_from(bytes: &'a [u8]) -> ElfResult { let header: &'a FH = <&'a FH>::try_from(bytes)?; let sections = unsafe { extract_from_slice::( &bytes[header.sh_range()] , 0 , header.sh_count() )? }; let prog_headers = unsafe { extract_from_slice::( &bytes[header.ph_range()] , 0 , header.ph_count() )? }; Ok(Image { header: header , sections: sections , program_headers: prog_headers , binary: bytes }) } } /// Extract `n` instances of type `T` from a byte slice. /// /// This is essentially just a _slightly_ safer wrapper around /// [`slice::from_raw_parts`]. Unlike `from_raw_parts`, this function takes /// a valid byte slice, rather than a pointer. Therefore, some of the safety /// issues with `from_raw_parts` are avoided: /// /// + the lifetime (`'slice`) of the returned slice should be the same as the /// lifetime of the input slice (`data`), rather than inferred arbitrarily. /// + this function will panic rather than reading past the end of the slice. /// /// # Arguments /// /// + `data`: the byte slice to extract a slice of `&[T]`s from /// + `offset`: a start offset into `data` /// + `n`: the number of instances of `T` which should be contained /// in `data[offset..]` /// /// # Safety /// /// While this function is safer than [`slice::from_raw_parts`], /// it is still unsafe for the following reasons: /// /// + The contents of `data` may not be able to be interpreted as instances of /// type `T`. /// /// # Caveats /// /// + If `n` == 0, this will give you an `&[]`. Just a warning. // thanks to Max for making me figure this out. /// + `offset` must be aligned on a `T`-sized boundary. /// /// # Panics /// /// + If the index `offset` is longer than `T` /// /// TODO: rewrite this as a `TryFrom` implementation (see issue #85) // - eliza, 03/09/2017 /// wait, possibly we should NOT do that. actually we should /// almost certainly not do that. since this function is unsafe, /// but `TryFrom` is not, and because this would be WAY generic. // - eliza, 03/09/2017 /// TODO: is this general enough to move into util? // - eliza, 03/09/2017 /// TODO: refactor this to take a `RangeArgument`? // - eliza, 03/13/2017 /// or, we could just remove the offset and expect the caller to /// offset the slice? // - eliza, 03/14/2017 /// /// [`slice::from_raw_parts`]: https://doc.rust-lang.org/stable/std/slice/fn.from_raw_parts.html unsafe fn extract_from_slice<'slice, T: Sized>( data: &'slice [u8] , offset: usize , n: usize) -> ElfResult<&'slice [T]> { if offset % mem::align_of::() != 0 { // TODO: these error messages don't contain as much information as they // used to, since the return type is `&'static str` that can't be // dynamically formatted as the panic was. refactor this? // (e.g. should ELF get its own error type?) // - eliza, 03/15/2017 Err("extract_from_slice: Offset not aligned on type T sized boundary!") // assert!( // , "Offset {} not aligned on a {}-sized boundary (must be \ // divisible by {})." // , offset, type_name::(), mem::align_of::() // ); } else if data.len() - offset < mem::size_of::() * n { Err("extract_from_slice: Slice too short to contain n instances of T!") // assert!( // , "Slice too short to contain {} objects of type {}" // , n, type_name::() // ); } else { Ok(slice::from_raw_parts(data[offset..].as_ptr() as *const T, n)) } } impl<'a, W: ElfWord> convert::Into for &'a Section { #[inline] fn into(self) -> FrameRange { use memory::Page; let start = PhysicalPage::containing(self.address()); let end = PhysicalPage::containing(self.end_address()); start .. end } } ================================================ FILE: elf/src/program.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // use super::ElfWord; /// Trait representing an ELF Program Header. /// /// This trait allows [HeaderRepr] to provide a consistent API regardless /// of whether the header section uses 32- or 64-bit [ELF word]s. A number of /// field values in the header of various sizes are converted to `usize` by /// this API so that they can be used as indices, etc. /// /// For more information on ELF Program Headers, refer to: /// + the ELF [specification] /// + the [OS Dev Wiki] /// /// [ELF word]: ../type.ElfWord.html /// [HeaderRepr]: struct.HeaderRepr.html /// [specification](http://www.sco.com/developers/gabi/latest/ch5.pheader.html) /// [OS Dev Wiki](http://wiki.osdev.org/ELF#Header) pub trait Header: Sized { type Word: ElfWord; /// Returns the [type](enum.Type.html) of this program header. /// /// This member tells what kind of segment this header describes or how to /// interpret the array element's information. fn ty(&self) -> Type; /// Returns this segment's start offset from the beginning of the binary. fn offset(&self) -> usize; /// Returns the virtual address of the first byte in this segment. fn vaddr(&self) -> Self::Word; /// Returns the physical address of the first byte in this segment. fn paddr(&self) -> Self::Word; /// Returns the number of bytes in the file image of the segment. /// /// This may be zero. fn file_size(&self) -> usize; /// Returns the number of bytes in the memory image of the segment. /// /// This may be zero. fn mem_size(&self) -> usize; /// Returns the [flags] for this segment. /// /// [flags]: struct.Flags.html fn flags(&self) -> Flags; fn align(&self) -> usize; } macro_rules! Header { (($($size:ty),+) $(pub)* enum $name:ident $($tail:tt)* ) => { Header! { @impl $name, $($size)+ } }; (($($size:ty),+) $(pub)* struct $name:ident $($tail:tt)*) => { Header! { @impl $name, $($size)+ } }; (@impl $name:ident, $size:ty) => { impl Header for $name { type Word = $size; impl_getters! { fn ty(&self) -> Type; fn flags(&self) -> Flags; fn offset(&self) -> usize; fn vaddr(&self) -> Self::Word; fn paddr(&self) -> Self::Word; fn file_size(&self) -> usize; fn mem_size(&self) -> usize; fn align(&self) -> usize; } } }; } /// The type field of an ELF program header #[repr(u32)] #[derive(Copy, Clone, Debug)] pub enum Type { /// `PT_NULL`: Program header table entry unused Null = 0 , /// `PT_LOAD`: Loadable program segment Loadable = 1 , /// `PT_DYNAMIC`: Dynamic linking information Dynamic = 2 , /// `PT_INTERP`: Program interpreter Interpreter = 3 , /// `PT_NOTE`: Auxiliary information Note = 4 , /// `PT_SHLIB` ShLib = 5 , /// `PT_PHDR`: Program Header table HeaderTable = 6 , /// `PT_TLS`: Thread-local storage ThreadLocal = 7 , /// GCC `.eh_frame_hdr` segment GnuEhFrame = 0x6474e550 , /// Indicates stack executability GnuStack = 0x6474e551 , /// Read-only after relocation GnuRelRo = 0x6474e552 } bitflags! { pub flags Flags: u32 { const NONE = 0 , const EXECUTABLE = 1 << 0 , const WRITABLE = 1 << 1 , const READABLE = 1 << 2 } } macro_attr! { /// A 64-bit ELF Program Header #[derive(Copy, Clone, Debug, Header!(u64))] pub struct HeaderRepr64 { pub ty: Type , pub flags: Flags , pub offset: u64 , pub vaddr: u64 , pub paddr: u64 , pub file_size: u64 , pub mem_size: u64 , pub align: u64 } } macro_attr! { /// A 32-bit ELF Program Header #[derive(Copy, Clone, Debug, Header!(u32))] pub struct HeaderRepr32 { pub ty: Type , pub offset: u32 , pub vaddr: u32 , pub paddr: u32 , pub file_size: u32 , pub mem_size: u32 , pub flags: Flags , pub align: u32 } } ================================================ FILE: elf/src/section.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // use super::{ElfResult, ElfWord}; use core::{convert, fmt, ops}; use core::iter::IntoIterator; use memory::{Addr, PAddr}; // Distinguished section indices. pub const SHN_UNDEF: u16 = 0; pub const SHN_LORESERVE: u16 = 0xff00; pub const SHN_LOPROC: u16 = 0xff00; pub const SHN_HIPROC: u16 = 0xff1f; pub const SHN_LOOS: u16 = 0xff20; pub const SHN_HIOS: u16 = 0xff3f; pub const SHN_ABS: u16 = 0xfff1; pub const SHN_COMMON: u16 = 0xfff2; pub const SHN_XINDEX: u16 = 0xffff; pub const SHN_HIRESERVE: u16 = 0xffff; pub const SHT_LOOS: u32 = 0x60000000; pub const SHT_HIOS: u32 = 0x6fffffff; pub const SHT_LOPROC: u32 = 0x70000000; pub const SHT_HIPROC: u32 = 0x7fffffff; pub const SHT_LOUSER: u32 = 0x80000000; pub const SHT_HIUSER: u32 = 0xffffffff; /// Represents an ELF section header /// /// Refer to [Figure 4-8], "Section Header", from Chapter 4 of the ELF standard /// for more information. /// /// [Figure 4-8]: (http://www.sco.com/developers/gabi/latest/ch4.sheader.html#section_header) // #[derive(Clone, Copy, Debug)] // pub enum Header<'a> { // ThirtyTwo(&'a HeaderRepr) // , SixtyFour(&'a HeaderRepr) // } pub trait Header: fmt::Debug { type Word: ElfWord; // /// Returns the start address of this section // fn addr(&self) -> PAddr; /// Returns the end address of this section /// TODO: refactor this to return a Range instead? // - eliza, 03/14/2017 #[inline] fn end_address(&self) -> PAddr { self.address() + self.length() as ::Repr } /// Returns true if this section is writable. #[inline] fn is_writable(&self) -> bool { self.flags().contains(SHF_WRITE) } /// Returns true if this section occupies memory during program execution. #[inline] fn is_allocated(&self) -> bool { self.flags().contains(SHF_ALLOC) } /// Returns true if this section contains executable instructions. #[inline] fn is_executable(&self) -> bool { self.flags().contains(SHF_EXECINSTR) } /// Returns true if this section can be merged. #[inline] fn is_mergeable(&self) -> bool { self.flags().contains(SHF_MERGE) } /// Returns true if this section contains data that is of a uniform size. #[inline] fn is_uniform(&self) -> bool { let flags = self.flags(); flags.contains(SHF_MERGE) && !flags.contains(SHF_STRINGS) } /// Look up the name of this section in the passed string table. #[inline] fn get_name<'a>(&self, strtab: StrTable<'a>) -> &'a str { unimplemented!() } // Field accessors ------------------------------------------------- fn name_offset(&self) -> u32; /// This member categorizes the section's contents and semantics. fn get_type(&self) -> ElfResult; fn flags(&self) -> Flags; fn address(&self) -> PAddr; fn offset(&self) -> usize; /// TODO: should offset + length make a Range? // - eliza, 03/14/2017 fn length(&self) -> usize; fn link(&self) -> u32; fn info(&self) -> u32; fn address_align(&self) -> Self::Word; fn entry_length(&self) -> usize; } macro_rules! Header { (($($size:ty),+) $(pub)* enum $name:ident $($tail:tt)* ) => { Header! { @impl $name, $($size)+ } }; (($($size:ty),+) $(pub)* struct $name:ident $($tail:tt)*) => { Header! { @impl $name, $($size)+ } }; (@impl $name:ident, $($size:ty)+) => { $(impl Header for $name<$size> { type Word = $size; /// Returns the type of this section #[inline] fn get_type(&self) -> ElfResult { self.ty.as_type() } impl_getters! { fn name_offset(&self) -> u32; fn flags(&self) -> Flags; /// TODO: shold this return a PAddr? // - eliza, 03/14/2017 fn address(&self) -> PAddr; fn offset(&self) -> usize; /// TODO: should offset + length make a Range? // - eliza, 03/14/2017 fn length(&self) -> usize; fn link(&self) -> u32; fn info(&self) -> u32; fn address_align(&self) -> Self::Word; fn entry_length(&self) -> usize; } })+ }; } impl fmt::Display for Header where Word: ElfWord , Header: fmt::Debug { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // TODO: do we want to actually get the section's name from the string // table and display it here? // - eliza, 03/05/2017 // TODO: do we want to print the header's flags, or would that make the // format too long? // - eliza, 03/05/2017 if let Ok(ty) = self.get_type() { // the ELF section was valid write!(f, "{:?} section at {:?} ... {:?}" , ty, self.address(), self.end_address()) } else { // we couldn't successfully extract a type from the ELF section! write!(f, "Bad ELF section {:?}", self) } } } macro_attr! { /// Raw representation of an ELF section header in an ELF binary. /// /// Refer to [Figure 4-8], "Section Header", from Chapter 4 of the ELF /// standard for more information. /// /// [Figure 4-8]: (http://www.sco.com/developers/gabi/latest/ch4.sheader.html#section_header) // TODO: add docs for all fields! // - eliza, 03/05/2017 #[derive(Debug, Header!(u32, u64))] #[repr(C)] pub struct HeaderRepr { /// This member specifies the name of the section. /// /// Its value is an index into the section header string table section, /// giving the location of a null-terminated string. name_offset: u32 , /// This member categorizes the section's contents and semantics. ty: TypeRepr , flags: Flags , address: Word , offset: Word , length: Word , link: u32 , info: u32 , address_align: Word , entry_length: Word } } bitflags! { // TODO: add documentation to the flags // - eliza, 03/05/2017 pub flags Flags: usize { // Flags (SectionHeader::flags) const SHF_WRITE = 0x1 , const SHF_ALLOC = 0x2 , const SHF_EXECINSTR = 0x4 , const SHF_MERGE = 0x10 , const SHF_STRINGS = 0x20 , const SHF_INFO_LINK = 0x40 , const SHF_LINK_ORDER = 0x80 , const SHF_OS_NONCONFORMING = 0x100 , const SHF_GROUP = 0x200 , const SHF_TLS = 0x400 , const SHF_COMPRESSED = 0x800 , const SHF_MASKOS = 0x0ff00000 , const SHF_MASKPROC = 0xf0000000 } } impl fmt::LowerHex for Flags { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.bits.fmt(f) } } bitflags! { pub flags GroupFlags: u32 { const GRP_COMDAT = 0x1 , const GRP_MASKOS = 0x0ff00000 , const GRP_MASKPROC = 0xf0000000 } } pub enum Contents<'a> { Empty , Undefined(&'a [u8]) , Group { flags: &'a u32 , indicies: &'a[u32] } } /// Underlying representation of a section header type field /// /// Unfortunately, we cannot have enums with open ranges yet, so we have /// to convert between the ELF file underlying representation and our /// type-safe representation. /// /// Refer to [Figure 4-9]: "Section Types, `sh_type`" in Section 4 of the /// ELF standard for more information. /// /// [Figure 4-9]: http://www.sco.com/developers/gabi/latest/ch4.sheader.html#sh_type #[derive(Debug, Copy, Clone)] struct TypeRepr(u32); impl TypeRepr { /// TODO: rewrite this as a `TryFrom` implementation (see issue #85) // - eliza, 03/09/2017 #[inline] fn as_type(&self) -> ElfResult { match self.0 { 0 => Ok(Type::Null) , 1 => Ok(Type::ProgramBits) , 2 => Ok(Type::SymbolTable) , 3 => Ok(Type::StringTable) , 4 => Ok(Type::Rela) , 5 => Ok(Type::HashTable) , 6 => Ok(Type::Dynamic) , 7 => Ok(Type::Notes) , 8 => Ok(Type::NoBits) , 9 => Ok(Type::Rel) , 10 => Ok(Type::Shlib) , 11 => Ok(Type::DynSymTable) , 14 => Ok(Type::InitArray) , 15 => Ok(Type::FiniArray) , 16 => Ok(Type::PreInitArray) , x @ SHT_LOOS ... SHT_HIOS => Ok(Type::OsSpecific(x)) , x @ SHT_LOPROC ... SHT_HIPROC => Ok(Type::ProcessorSpecific(x)) , x @ SHT_LOUSER ... SHT_HIUSER => Ok(Type::User(x)) , _ => Err("Invalid section type!") } } } /// Enum representing an ELF file section type. /// /// Refer to [Figure 4-9]: "Section Types, `sh_type`" in Section 4 of the /// ELF standard for more information. /// /// [Figure 4-9]: http://www.sco.com/developers/gabi/latest/ch4.sheader.html#sh_type #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] pub enum Type { /// Section type 0: `SHT_NULL` /// /// This value marks the section header as inactive; it does not have an /// associated section. Other members of the section header have /// undefined values. Null , /// Section type 1: `SHT_PROGBITS` /// /// The section holds information defined by the program, whose format and /// meaning are determined solely by the program. ProgramBits , /// Section type 2: `SHT_SYMTAB` /// /// Typically, `SHT_SYMTAB` provides symbols for link editing, though it /// may also be used for dynamic linking. As a complete symbol table, it /// may contain many symbols unneces- sary for dynamic linking. /// /// Consequently, an object file may also contain a `SHT_DYNSYM` section, /// which holds a minimal set of dynamic linking symbols, to save space. SymbolTable , /// Section type 3: `SHT_STRTAB` /// /// The section holds a string table. An object file may have multiple /// string table sections. StringTable , /// Section type 4: `SHT_RELA` /// /// The section holds relocation entries with explicit addends, such as /// type `Elf32_Rela` for the 32-bit class of object files. An object file /// may have multiple relocation sections. Rela , /// Section type 5: `SHT_HASH` /// /// The section holds a symbol hash table. All objects participating in /// dynamic linking must contain a symbol hash table. Currently, an object /// file may have only one hash table, but this restriction may be relaxed /// in the future. HashTable , /// Section type 6: `SHT_DYNAMIC` /// /// The section holds information for dynamic linking. Currently, an object /// file may have only one dynamic section, but this restriction may be /// relaxed in the future. Dynamic , /// Section type 7: `SHT_NOTE` /// /// The section holds information that marks the file in some way. Notes , /// Section type 8: `SHT_NOBITS` /// /// A section of this type occupies no space in the file but otherwise /// resembles `SHT_PROGBITS`. Although this section contains no bytes, the /// `sh_offset` member contains the conceptual file offset. NoBits , /// Section type 9: `SHT_REL` /// /// The section holds relocation entries without explicit addends, such as /// type `Elf32_Rel` for the 32-bit class of object files. An object file /// may have multiple reloca- tion sections. Rel , /// Section type 10: `SHT_SHLIB` /// /// This section type is reserved but has unspecified semantics. Programs /// that contain a section of this type do not conform to the ABI. Shlib , /// Section type 11: `SHT_DYNSYM` /// /// Typically, `SHT_SYMTAB` provides symbols for link editing, though it /// may also be used for dynamic linking. As a complete symbol table, it /// may contain many symbols unneces- sary for dynamic linking. /// /// Consequently, an object file may also contain a `SHT_DYNSYM` section, /// which holds a minimal set of dynamic linking symbols, to save space. DynSymTable , InitArray , FiniArray , PreInitArray , Group , SymbolTableShIndex , OsSpecific(u32) , ProcessorSpecific(u32) , User(u32) } /// Iterator over ELF64 sections #[derive(Clone,Debug)] pub struct Sections<'a, W: 'a> where W: ElfWord { curr: &'a HeaderRepr , remaining: u32 , size: u32 } impl<'a, W: 'a> Sections<'a, W> where W: ElfWord { pub fn new(curr: &'a HeaderRepr, remaining: u32, size: u32) -> Sections<'a, W> { Sections { curr: curr, remaining: remaining, size: size } } } impl<'a, W> Iterator for Sections<'a, W> where W: ElfWord , HeaderRepr: Header { type Item = &'a Header; fn next(&mut self) -> Option { if self.remaining == 0 { None } else { let current = self.curr; self.curr = unsafe { (self.curr as *const HeaderRepr).offset(1) .as_ref() .expect("Expected an ELF section header, but got a null \ pointer!\nThis shouldn't happen!") }; self.remaining -= 1; if current.get_type().unwrap() == Type::Null { self.next() } else { Some(current) } } } } /// Characters in the ELF string table are 8-bit ASCII characters. type ElfChar = u8; /// An ELF string table. /// /// Refer to the String Table [entry] in Section 4 of the ELF standard /// for more information. /// /// [entry]: http://www.sco.com/developers/gabi/latest/ch4.strtab.html // TODO: this should be indexable by string number, possibly? // - eliza, 03/07/2017 // TODO: add a function to get the name of the section (which always lives) // at index 0. // - eliza, 03/07/2017 #[derive(Clone, Debug)] pub struct StrTable<'a>(&'a [ElfChar]); impl<'a> convert::From<&'a [ElfChar]> for StrTable<'a> { #[inline(always)] fn from(binary: &'a [ElfChar]) -> Self { StrTable(binary) } } impl<'a> StrTable<'a> { /// Returns the string at a given index in the string table, /// if there is one. // TODO: these docs are Bad // - eliza, 03/07/2017 // TODO: this def. shouldn't be u64, but i didn't want to annotate the // string table type with ElfWord...figure this out // - eliza, 03/07/2017 // TODO: can this be replaced with an ops::Index implementation? // but then we can't implement Deref to a slice any more? // - eliza, 03/07/2017 pub fn at_index(&'a self, i: usize) -> Option<&'a str> { use core::str::from_utf8_unchecked; if i <= self.len() { read_to_null(&self[i..]) .map(|bytes| unsafe { // TODO: should this be checked, or do we assume the ELF // binary has only well-formed strings? this could be // a Security Thing... // - eliza, 03/07/2017 from_utf8_unchecked(bytes) // TODO: can the conversion to a Rust string be moved to // `read_to_null()`? we also do this in the iterator // - eliza, 03/07/2017 }) } else { None } } } // impl<'a> StrTable<'a> { // #[inline] fn len(&self) -> usize { self.0.len} // // } impl<'a> ops::Deref for StrTable<'a> { type Target = [ElfChar]; #[inline] fn deref(&self) -> &Self::Target { self.0 } } impl<'a> IntoIterator for StrTable<'a> { type IntoIter = Strings<'a>; type Item = &'a str; // TODO: this doesn't strictly need to consume the StringTable... // - eliza, 03/07/2017 #[inline] fn into_iter(self) -> Self::IntoIter { Strings(&self.0) } } /// Returns true if `ch` is the null-terminator character #[inline] fn is_null(ch: &ElfChar) -> bool { *ch == b'\0' } /// Read a series of bytes from a slice to the first null-terminator // TODO: can this be moved to the StrTable type? no big deal but it would be // somewhat prettier... // - eliza, 03/07/2017 #[inline] fn read_to_null<'a>(bytes: &'a [ElfChar]) -> Option<&'a [ElfChar]> { bytes.iter().position(is_null) .map(|i| &bytes[..i] ) } /// Iterator over the strings in an ELF string table #[derive(Clone, Debug)] pub struct Strings<'a>(&'a [ElfChar]); impl<'a> Iterator for Strings<'a> { type Item = &'a str; fn next(&mut self) -> Option { use core::str::from_utf8_unchecked; if self.0.len() == 0 { // if there are no bytes remaining in the iterator, then we've // iterated over all the strings in the string table (or it was // empty to begin with). // // N.B. that `read_to_null()` _will_ return None in this case, so // this check isn't strictly necessary; it just saves us from // having to create an iterator if the slice is empty, so I'm // calling it an "optimisation". None } else { // otherwise, try to read the iterator's slice to the first null // character... read_to_null(self.0).map(|bytes| { // ...if we found a null character, remove the string's bytes // from the slice in the iterator (since we're returning that // string), and return a string slice containing those bytes // interpreted as UTF-8 (which should be equivalent to ASCII) self.0 = &self.0[bytes.len() + 1..]; unsafe { // TODO: should this be checked, or do we assume the ELF // binary has only well-formed strings? this could be // a Security Thing... // - eliza, 03/07/2017 from_utf8_unchecked(bytes) } }) } } #[inline] fn size_hint(&self) -> (usize, Option) { (0, Some(self.0.len() / 2)) } } ================================================ FILE: memory/Cargo.toml ================================================ [package] name = "memory" version = "0.0.1" authors = [ "Eliza Weisman " ] [profile.dev] opt-level = 3 debug = true rpath = false lto = false debug-assertions = true codegen-units = 1 panic = "abort" [profile.release] opt-level = 3 debug = true rpath = false lto = false panic = "abort" [dependencies] util = { path = "../util" } bitflags = "0.7" spin = "0.4.6" [dependencies.vga] path = "../vga" features = ["kinfo"] [dependencies.lazy_static] version = "^0.2.11" features = ["spin_no_std"] [dependencies.macro-attr] git = "https://github.com/DanielKeep/rust-custom-derive.git" version = "0.2.1" default-features = false # [dependencies.newtype_derive] # version = "0.1.6" # default-features = false [dependencies.log] version = "^0.3.6" default-features = false features = ["release_max_level_info"] ================================================ FILE: memory/src/arch/mod.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // // 64-bit x86_64 (long mode) #[cfg(target_arch="x86_64")] mod x86_64; #[cfg(target_arch="x86_64")] pub use self::x86_64::*; // 32-bit x86 (protected mode) // TODO: NYI #[cfg(target_arch = "x86")] mod x86; #[cfg(target_arch = "x86")] pub use self::x86::*; ================================================ FILE: memory/src/arch/x86/mod.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // use ::memory::VAddr; /// A physical (linear) memory address is a 32-bit unsigned integer #[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] pub struct PAddr(u32); impl Addr for PAddr { } impl_addr! { PAddr, u32 } impl convert::Into for PAddr { #[inline] fn into(self) -> u32 { self.as_u32() } } impl convert::From for PAddr { #[inline] fn from(u: u32) -> Self { PAddr::from_u32(u) } } impl convert::From<*mut u8> for PAddr { #[inline] fn from(ptr: *mut u8) -> Self { PAddr::from_ptr(ptr) } } impl PAddr { #[inline] pub fn from_ptr(ptr: *mut u8) -> Self { PAddr(ptr as u32) } #[inline] pub const fn from_u32(u: u32) -> Self { PAddr(u) } #[inline] pub const fn as_u32(&self) -> u32 { self.0 } } ================================================ FILE: memory/src/arch/x86_64/mod.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Architecture-specific memory management. use ::{Addr, Page}; use core::{fmt, ops, mem}; pub const PAGE_SHIFT: u8 = 12; /// The size of a page (4KiB), in bytes pub const PAGE_SIZE: u64 = 1 << PAGE_SHIFT; // 4k /// The size of a large page (2MiB) in bytes pub const LARGE_PAGE_SIZE: u64 = 1024 * 1024 * 2; /// The size of a huge page (2GiB) in bytes pub const HUGE_PAGE_SIZE: u64 = 1024 * 1024 * 1024; macro_attr! { /// A physical (linear) memory address is a 64-bit unsigned integer #[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd, Addr!(u64, 'P'))] #[repr(C)] pub struct PAddr(u64); } macro_attr! { /// A frame (physical page) // TODO: consider renaming this to `Frame` (less typing)? // - eliza, 2/28/2017 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Page!(PAddr) )] pub struct PhysicalPage { pub number: u64 } } impl fmt::Debug for PhysicalPage { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "frame #{} at {:#p}", self.number, self.base_addr()) } } impl ops::Add for PhysicalPage { type Output = Self; #[inline] fn add(self, rhs: usize) -> Self { PhysicalPage { number: self.number + rhs as u64 } } } impl ops::Sub for PhysicalPage { type Output = Self; #[inline] fn sub(self, rhs: usize) -> Self { PhysicalPage { number: self.number - rhs as u64 } } } impl ops::AddAssign for PhysicalPage { #[inline] fn add_assign(&mut self, rhs: usize) { self.number += rhs as u64; } } impl ops::SubAssign for PhysicalPage { #[inline] fn sub_assign(&mut self, rhs: usize) { self.number -= rhs as u64; } } impl PhysicalPage { /// Returns the physical address where this frame starts. #[inline] pub const fn base_addr(&self) -> PAddr { PAddr(self.number << PAGE_SHIFT) } /// Returns a new frame containing `addr` #[inline] pub const fn containing_addr(addr: PAddr) -> PhysicalPage { PhysicalPage { number: addr.0 >> PAGE_SHIFT } } /// Convert the frame into a raw pointer to the frame's base address #[inline] pub unsafe fn as_ptr(&self) -> *const T { mem::transmute(self.base_addr()) } /// Convert the frame into a raw mutable pointer to the frame's base address #[inline] pub unsafe fn as_mut_ptr(&self) -> *mut T { *self.base_addr() as *mut u8 as *mut T } } ================================================ FILE: memory/src/lib.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2016 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Kernel memory management. //! //! This module contains all of the non-arch-specific paging code, and //! re-exports memory-related definitions. #![crate_name = "memory"] #![feature(const_fn)] #![feature(asm)] #![feature(step_trait)] #![feature(linkage)] #![no_std] #[macro_use] extern crate macro_attr; #[macro_use] extern crate util; // #[cfg(not(test))] #[macro_use] extern crate vga; // extern crate alloc as liballoc; // TODO: workaround #[macro_use] pub mod macros; pub mod arch; // use alloc::buddy; // use ::params::InitParams; use core::{ops, cmp, convert, fmt}; use util::Align; pub use arch::{PAddr, PAGE_SHIFT, PAGE_SIZE}; /// Trait representing an address, whether physical or virtual. pub trait Addr: ops::Add + ops::Sub + ops::Mul + ops::Div + ops::Shl + ops::Shr + ops::BitOr + ops::BitAnd + convert::From<*mut u8> + convert::From<*const u8> + Sized { type Repr: Align; fn align_down(&self, align: Self::Repr) -> Self; fn align_up(&self, align: Self::Repr) -> Self; /// Returns true if this address is aligned on a page boundary. fn is_page_aligned(&self) -> bool; } //impl Addr for VAddr { } //impl_addr! { VAddr, usize } macro_attr! { /// A virtual address is a machine-sized unsigned integer #[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd, Addr!(usize, 'V'))] pub struct VAddr(usize); } impl VAddr { /// Convert this virtual address to a pointer #[inline] pub fn from_ptr(ptr: *mut T) -> Self { VAddr(ptr as usize) } /// Convert a `usize` to a virtual address #[inline] pub const fn from_usize(u: usize) -> Self { VAddr(u) } /// Convert this virtual address to a `usize`. #[inline] pub const fn as_usize(&self) -> usize { self.0 } /// Calculate the index in the PML4 table corresponding to this address. #[inline] pub fn pml4_index(&self) -> usize { *((self >> 39) & 0b111111111 as usize) } /// Calculate the index in the PDPT table corresponding to this address. #[inline] pub fn pdpt_index(&self) -> usize { *((self >> 30) & 0b111111111) } /// Calculate the index in the PD table corresponding to this address. #[inline] pub fn pd_index(&self) -> usize { *((self >> 21) & 0b111111111) } /// Calculate the index in the PT table corresponding to this address. #[inline] pub fn pt_index(&self) -> usize { *((self >> 12) & 0b111111111) } } use core::ops::Range; pub use arch::PhysicalPage; pub type PageRange = Range; pub type FrameRange = Range; /// Trait for a page. These can be virtual pages or physical frames. pub trait Page where Self: Sized , Self: ops::AddAssign + ops::SubAssign , Self: ops::Add + ops::Sub , Self: cmp::PartialEq + cmp::PartialOrd , Self: Copy + Clone { /// The type of address used to address this `Page`. /// /// Typically, this is a `PAddr` or `VAddr` (but it could be a "MAddr") /// in schemes where we differentiate between physical and machine /// addresses. If we ever have those. type Address: Addr; /// Returns a new `Page` containing the given `Address`. /// /// N.B. that since trait functions cannot be `const`, implementors of /// this trait may wish to provide implementations of this function /// outside of the `impl` block and then wrap them here. fn containing(addr: Self::Address) -> Self; /// Returns the base `Address` where this page starts. fn base(&self) -> Self::Address; /// Returns the end `Address` of this `Page`. fn end_address(&self) -> Self::Address; ///// Convert the frame into a raw pointer to the frame's base address //#[inline] //unsafe fn as_ptr(&self) -> *const T { // mem::transmute(self.base()) //} // ///// Convert the frame into a raw mutable pointer to the frame's base address //#[inline] //unsafe fn as_mut_ptr(&self) -> *mut T { // mem::transmute(self.base()) //} /// Returns a `PageRange` of this `Page` and the next `n` pages. #[inline] fn range_of(&self, n: usize) -> Range { self.range_until(*self + n) } /// Returns a `PageRange` on the frames from this frame until the end frame #[inline] fn range_until(&self, end: Self) -> Range { Range { start: *self, end: end } } fn number(&self) -> usize; } macro_attr!{ /// A virtual page #[derive( Copy, Clone, Eq, Ord, PartialEq, PartialOrd, Page!(VAddr) )] pub struct VirtualPage { pub number: usize } } impl VirtualPage { fn containing_addr( addr: VAddr) -> Self { use ::PAGE_SHIFT; assert!( (addr < 0x0000_8000_0000_0000) || (addr >= 0xffff_8000_0000_0000) , "invalid address : 0x{:x}", addr ); Self { number: addr.0 >> PAGE_SHIFT } } } impl fmt::Debug for VirtualPage { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "page #{}", self.number) } } // ///// A range of `Page`s. //#[derive(Copy, Clone, Debug, Eq, PartialEq)] //pub struct Range

//where P: Page { start: P, end: P } // pub trait MemRange { /// Returns the number of `Page`s in this ranage #[inline] fn length(&self) -> usize; /// Remove `n` pages from the beginning of this `PageRange` fn drop_front(&mut self, n: usize) -> &mut Self; /// Remove `n` pages from the end of this `PageRange` fn drop_back(&mut self, n: usize) -> &mut Self; /// Add `n` pages at the front of this `PageRange` fn add_front(&mut self, n: usize) -> &mut Self; /// Add `n` pages at the back of this `PageRange` fn add_back(&mut self, n: usize) -> &mut Self; } //pub const fn start(&self) -> P { self.start } // ///// Returns a `PageRange` between two pages //pub const fn between(start: P, end: P) -> Range

{ // Range { start: start, end: end } //} // // /// Returns an iterator over this `PageRange` // pub fn iter<'a>(&'a self) -> RangeIter<'a, P> { // RangeIter { range: self, current: self.start.clone() } // } impl

MemRange for Range

where P: Page { /// Returns the number of `Page`s in this ranage #[inline] fn length(&self) -> usize { self.end.number() - self.start.number() } /// Remove `n` pages from the beginning of this `PageRange` fn drop_front(&mut self, n: usize) -> &mut Self { assert!(n < self.length()); self.start += n; self } /// Remove `n` pages from the end of this `PageRange` fn drop_back(&mut self, n: usize) -> &mut Self { assert!(n < self.length()); self.end -= n; self } /// Add `n` pages at the front of this `PageRange` fn add_front(&mut self, n: usize) -> &mut Self { self.start -= n; self } /// Add `n` pages at the back of this `PageRange` fn add_back(&mut self, n: usize) -> &mut Self { self.end += n; self } } /// An iterator over a range of pages pub struct RangeIter<'a, P> where P: Page , P: 'a { range: &'a Range

, current: P } impl<'a, P> Iterator for RangeIter<'a, P> where P: Page , P: Clone { type Item = P; fn next(&mut self) -> Option

{ let end = self.range.end; assert!(self.range.start <= end); if self.current < end { let page = self.current.clone(); self.current += 1; Some(page) } else { None } } } ================================================ FILE: memory/src/macros.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Macros to make our custom address types require a lot less repetitive code. #[macro_export] macro_rules! Addr { (($size:ty, $letter:expr) $(pub)* enum $name:ident $($tail:tt)*) => { Addr! { @impl $name, $size, $letter } }; (($size:ty, $letter:expr) $(pub)* struct $name:ident $($tail:tt)*) => { Addr! { @impl $name, $size, $letter } }; (@impl $ty:ident, $size:ty, $letter:expr) => { impl ::core::fmt::Debug for $ty { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { write!(f, "{}x{:x}", $letter, self.0) } } impl ::core::fmt::Pointer for $ty { #[inline] fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { write!(f, "{:?}", self) } } impl ::core::convert::Into<$size> for $ty { #[inline] fn into(self) -> $size { self.0 } } impl ::core::convert::From<$size> for $ty { #[inline] fn from(n: $size) -> Self { $ty(n) } } impl ::core::convert::From<*mut T> for $ty { #[inline] fn from(ptr: *mut T) -> Self { $ty(ptr as $size) } } impl ::core::convert::From<*const T> for $ty { #[inline] fn from(ptr: *const T) -> Self { $ty(ptr as $size) } } impl ::core::ops::Deref for $ty { type Target = $size; #[inline] fn deref(&self) -> &Self::Target { &self.0 } } impl $ty { #[inline(always)] pub const fn as_mut_ptr(&self) -> *mut T { self.0 as *mut _ } #[inline(always)] pub const fn as_ptr(&self) -> *const T { self.0 as *const _ } #[inline(always)] pub const fn new(value: $size) -> Self { $ty(value) } } impl_ops! { Add, add, + for $ty, $size Sub, sub, - for $ty, $size Div, div, / for $ty, $size Mul, mul, * for $ty, $size Shl, shl, >> for $ty, $size Shr, shr, << for $ty, $size Rem, rem, % for $ty, $size BitAnd, bitand, & for $ty, $size BitOr, bitor, | for $ty, $size BitXor, bitxor, ^ for $ty, $size } impl_assign_ops! { AddAssign, add_assign, += for $ty, $size SubAssign, sub_assign, -= for $ty, $size DivAssign, div_assign, /= for $ty, $size MulAssign, mul_assign, *= for $ty, $size ShlAssign, shl_assign, >>= for $ty, $size ShrAssign, shr_assign, <<= for $ty, $size RemAssign, rem_assign, %= for $ty, $size BitAndAssign, bitand_assign, &= for $ty, $size BitOrAssign, bitor_assign, |= for $ty, $size BitXorAssign, bitxor_assign, ^= for $ty, $size } impl_fmt! { Binary for $ty Display for $ty Octal for $ty LowerHex for $ty UpperHex for $ty } impl ::core::cmp::PartialEq<$size> for $ty { #[inline] fn eq(&self, rhs: &$size) -> bool { self.0 == *rhs } } impl ::core::cmp::PartialOrd<$size> for $ty { #[inline] fn partial_cmp(&self, rhs: &$size) -> Option<::core::cmp::Ordering> { self.0.partial_cmp(rhs) } } impl Addr for $ty { type Repr = $size; #[inline] fn align_down(&self, align: Self::Repr) -> Self { use util::Align; $ty ( self.0.align_down(align) ) } #[inline] fn align_up(&self, align: Self::Repr) -> Self { use util::Align; // assert!(align.is_page_aligned()); $ty ( self.0.align_up(align) ) } #[inline] fn is_page_aligned(&self) -> bool { **self % PAGE_SIZE as ::Repr == 0 as ::Repr } } } } macro_rules! impl_page_ops { ($($name:ident, $fun:ident, $op:tt for $ty:ident, $size:ty)*) => {$( impl ::core::ops::$name<$ty> for $ty { type Output = Self; #[inline] fn $fun(self, rhs: $ty) -> Self { expr!( $ty { number: expr!(self.number $op rhs.number) }) } } impl ::core::ops::$name<$size> for $ty { type Output = $ty; #[inline] fn $fun(self, rhs: $size) -> Self { expr!($ty{ number: expr!(self.number $op rhs) }) } } forward_ref_binop! { $name, $fun for $ty, $ty } forward_ref_binop! { $name, $fun for $ty, $size } )*} } macro_rules! impl_page_assign_ops { ($($name:ident, $fun:ident, $op:tt for $ty:ident, $size:ty)*) => {$( impl ::core::ops::$name<$ty> for $ty { #[inline] fn $fun(&mut self, rhs: $ty) { expr!(self.number $op rhs.number); } } impl ::core::ops::$name<$size> for $ty { #[inline] fn $fun(&mut self, rhs: $size) { expr!(self.number $op rhs); } } )*} } macro_rules! Page { (($addr:ty) $(pub)* struct $name:ident $($tail:tt)*) => { Page! { @impl $name, $addr } }; (@impl $ty:ident, $addr:ty) => { impl Page for $ty { type Address = $addr; /// Create a new `Page` containing the given address. // TODO: rewrite this as `up`/`down` using the page shift, instead. #[inline(always)] fn containing(addr: $addr) -> Self { $ty::containing_addr(addr) } /// Return the start address of this page #[inline] fn base(&self) -> $addr { Self::Address::from( self.number << PAGE_SHIFT as ::Repr ) } /// Return the end address of this page #[inline] fn end_address(&self) -> $addr { self.base() + PAGE_SIZE as ::Repr } #[inline] fn number(&self) -> usize { self.number as usize } } // impl ::core::num::One for $ty { // #[inline] fn one() -> Self { // $ty { number: // <<$addr as Addr>::Repr as ::core::num::One>::one() // } // } // } impl ::core::iter::Step for $ty { #[inline] fn add_usize(&self, n: usize) -> Option { self.number .add_usize(n) .map(|s| $ty { number: s}) } #[inline] #[allow(trivial_numeric_casts)] fn steps_between(start: &$ty, end: &$ty) -> Option { use ::core::iter::Step; <<$addr as Addr>::Repr as Step>::steps_between( &start.number , &end.number ) } #[inline] fn sub_one(&self) -> Self { self - 1 } #[inline] fn add_one(&self) -> Self { self + 1 } #[inline] fn replace_one(&mut self) -> Self { unimplemented!() } #[inline] fn replace_zero(&mut self) -> Self { unimplemented!() } } impl ::core::convert::From for $ty where ::Address: ::core::convert::From { #[inline] fn from(addr: A) -> Self { $ty::containing(::Address::from(addr)) } } impl_page_ops! { Add, add, + for $ty, <<$ty as Page>::Address as Addr>::Repr Sub, sub, - for $ty, <<$ty as Page>::Address as Addr>::Repr Div, div, / for $ty, <<$ty as Page>::Address as Addr>::Repr Mul, mul, * for $ty, <<$ty as Page>::Address as Addr>::Repr Shl, shl, >> for $ty,<<$ty as Page>::Address as Addr>::Repr Shr, shr, << for $ty, <<$ty as Page>::Address as Addr>::Repr Rem, rem, % for $ty, <<$ty as Page>::Address as Addr>::Repr BitAnd, bitand, & for $ty, <<$ty as Page>::Address as Addr>::Repr BitOr, bitor, | for $ty, <<$ty as Page>::Address as Addr>::Repr BitXor, bitxor, ^ for $ty, <<$ty as Page>::Address as Addr>::Repr } impl_page_assign_ops! { AddAssign, add_assign, += for $ty, <<$ty as Page>::Address as Addr>::Repr SubAssign, sub_assign, -= for $ty, <<$ty as Page>::Address as Addr>::Repr DivAssign, div_assign, /= for $ty, <<$ty as Page>::Address as Addr>::Repr MulAssign, mul_assign, *= for $ty, <<$ty as Page>::Address as Addr>::Repr ShlAssign, shl_assign, >>= for $ty, <<$ty as Page>::Address as Addr>::Repr ShrAssign, shr_assign, <<= for $ty, <<$ty as Page>::Address as Addr>::Repr RemAssign, rem_assign, %= for $ty, <<$ty as Page>::Address as Addr>::Repr BitAndAssign, bitand_assign, &= for $ty, <<$ty as Page>::Address as Addr>::Repr BitOrAssign, bitor_assign, |= for $ty, <<$ty as Page>::Address as Addr>::Repr BitXorAssign, bitxor_assign, ^= for $ty, <<$ty as Page>::Address as Addr>::Repr } } } ================================================ FILE: paging/Cargo.toml ================================================ [package] name = "paging" version = "0.0.1" authors = [ "Eliza Weisman " ] [profile.dev] opt-level = 3 debug = true rpath = false lto = false debug-assertions = true codegen-units = 1 panic = "abort" [profile.release] opt-level = 3 debug = true rpath = false lto = false panic = "abort" [dependencies] util = { path = "../util" } memory = { path = "../memory" } sos_alloc = { path = "../sos_alloc" } elf = { path = "../elf" } cpu = { path = "../cpu" } params = { path = "../params" } bitflags = "0.7" spin = "0.4.6" [dependencies.vga] path = "../vga" features = ["kinfo"] [dependencies.lazy_static] version = "0.2.11" features = ["spin_no_std"] [dependencies.macro-attr] git = "https://github.com/DanielKeep/rust-custom-derive.git" version = "0.2.1" default-features = false # [dependencies.newtype_derive] # version = "0.1.6" # default-features = false [dependencies.log] version = "0.3.6" default-features = false features = ["release_max_level_info"] ================================================ FILE: paging/src/arch/mod.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (hi@hawkweisman.me) // // Copyright (c) 2015-2016 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // // 64-bit x86_64 (long mode) #[cfg(target_arch="x86_64")] mod x86_64; #[cfg(target_arch="x86_64")] pub use self::x86_64::*; // 32-bit x86 (protected mode) // TODO: NYI #[cfg(target_arch = "x86")] mod x86; #[cfg(target_arch = "x86")] pub use self::x86::*; ================================================ FILE: paging/src/arch/x86_64/cr3.rs ================================================ use super::table::{Table, PML4Level}; use cpu::control_regs::cr3::{read, write}; pub use cpu::control_regs::cr3::*; /// Returns the current Page Meta-Level 4 table /// /// # Safety /// + Reading from control registers while not in kernel mode will cause /// a general protection fault. /// + Returns a `*mut` pointer with an arbitrary lifetime. #[cfg(target_arch = "x86_64")] #[inline] pub unsafe fn current_pml4() -> *mut Table { read().as_mut_ptr::>() } /// Sets the current Page Meta-Level 4 Table /// /// # Safety /// + Control registers should generally not be modified during normal /// operation. #[cfg(target_arch = "x86_64")] pub unsafe fn set_pml4(pml4: Table) { write(pml4.frame().base_addr()) } ================================================ FILE: paging/src/arch/x86_64/mod.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (hi@hawkweisman.me) // // Copyright (c) 2015-2016 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Paging //! //! The `x86_64` architecture uses a four-level page table structure. The top //! page table is called the Page Meta-Level 4 (PML4) table, followed by //! the Page Directory Pointer Table (PDPT), Page Directory (PD) table, and //! finally the bottom-level Page Table (PT). use core::{fmt, ops}; use core::ptr::Unique; use alloc::FrameAllocator; use memory::{Addr, PAGE_SIZE, PAddr, Page, PhysicalPage, VAddr, VirtualPage}; use params::InitParams; use ::{Mapper, MapResult, MapErr}; use self::table::*; use self::temp::TempPage; pub mod table; pub mod tlb; pub mod temp; pub mod cr3; #[derive(Debug)] pub struct ActivePageTable { pml4: ActivePML4 } impl ops::Deref for ActivePageTable { type Target = ActivePML4; fn deref(&self) -> &ActivePML4 { &self.pml4 } } impl ops::DerefMut for ActivePageTable { fn deref_mut(&mut self) -> &mut ActivePML4 { &mut self.pml4 } } impl ActivePageTable { pub unsafe fn new() -> ActivePageTable { ActivePageTable { pml4: ActivePML4::new() } } /// Execute a closure with the recursive mapping temporarily changed to a /// new page table pub fn using( &mut self , table: &mut InactivePageTable , temp_page: &mut temp::TempPage , f: F) -> MapResult where F: FnOnce(&mut ActivePML4) -> MapResult { let result: MapResult; use self::tlb::flush_all; { // back up the current PML4 frame let prev_pml4_frame = unsafe { // this is safe to execute; we are in kernel mode cr3::current_pagetable_frame() }; // map temporary_page to current p4 table let pml4 = temp_page.map_to_table(prev_pml4_frame.clone(), self)?; // remap the 511th PML4 entry (the recursive entry) to map to the // frame containing the new PML4. self.pml4_mut()[511].set(table.pml4_frame, PRESENT | WRITABLE); unsafe { // this is safe to execute; we are in kernel mode flush_all(); } // execute the closure result = f(self); // remap the 511th entry to point back to the original frame pml4[511].set(prev_pml4_frame, PRESENT | WRITABLE); unsafe { // this is safe to execute; we are in kernel mode flush_all(); } } let _ = temp_page.unmap(self)?; return result } /// Replace the current `ActivePageTable` with the given `InactivePageTable` /// /// # Arguments /// + `new_table`: the `InactivePageTable` that will replace the current /// `ActivePageTable`. /// /// # Returns /// + the old active page table as an `InactivePageTable`. pub fn replace_with(&mut self, new_table: InactivePageTable) -> InactivePageTable { unsafe { trace!("replacing {:?} with {:?}", self, new_table); // this is safe to execute; we are in kernel mode let old_pml4_frame = cr3::current_pagetable_frame(); trace!("current pml4 frame is {:?}", old_pml4_frame); cr3::set_pagetable_frame(new_table.pml4_frame); trace!("set new pml4 frame to {:?}", new_table.pml4_frame); InactivePageTable { pml4_frame: old_pml4_frame } } } } /// Struct representing the currently active PML4 instance. /// /// The `ActivePML4` is a `Unique` reference to a PML4-level page table. It's /// unique because, well, there can only be one active PML4 at a given time. /// pub struct ActivePML4(Unique>); impl fmt::Debug for ActivePML4 { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "Active {:?}", unsafe { self.0.as_ref() }) } } /// The active PML4 table is the single point of entry for page mapping. impl Mapper for ActivePML4 { type Flags = EntryFlags; fn translate(&self, vaddr: VAddr) -> Option { let offset = *vaddr % PAGE_SIZE as usize; self.translate_page(Page::containing(vaddr)) .map(|frame| PAddr::from(frame.number + offset as u64) ) } fn translate_page(&self, page: VirtualPage) -> Option { let pdpt = self.pml4().next_table(page); let huge_page = || { pdpt.and_then(|pdpt| pdpt[page] .do_huge(PDLevel::index_of(page) + PTLevel::index_of(page)) .or_else(|| { pdpt.next_table(page).and_then(|pd| pd[page].do_huge(PTLevel::index_of(page)) ) }) ) }; pdpt.and_then(|pdpt| pdpt.next_table(page)) .and_then(|pd| pd.next_table(page)) .and_then(|pt| pt[page].get_frame()) .or_else(huge_page) } /// Modifies the page tables so that `page` maps to `frame`. /// /// # Arguments /// + `page`: the virtual `Page` to map /// + `frame`: the physical `Frame` that `Page` should map to. /// + `flags`: the page table entry flags. /// + `alloc`: a memory allocator fn map( &mut self, page: VirtualPage, frame: PhysicalPage , flags: EntryFlags, alloc: &mut A) -> MapResult<()> where A: FrameAllocator { // base virtual address of page being mapped // let addr = page.base(); // access or create all the lower-level page tables. let mut page_table // get the PML4 = self.pml4_mut() // get or create the PDPT table at the page's PML4 index .create_next(page, alloc) // get or create the PD table at the page's PDPT index .and_then(|pdpt| pdpt.create_next(page, alloc)) // get or create the page table at the page's PD table index .and_then(|pd| pd.create_next(page, alloc))?; trace!(" . . Map: Got page table"); // check if the page at that index is not currently in use, as we // cannot map a page which is currently in use. if page_table[page].is_unused() { // set the page table entry at that index page_table[page].set(frame, flags | table::PRESENT); Ok(()) } else { Err(MapErr::AlreadyInUse { message: "map frame" , page: page , frame: frame }) } } fn identity_map(&mut self, frame: PhysicalPage, flags: EntryFlags , alloc: &mut A) -> MapResult<()> where A: FrameAllocator { self.map( Page::containing(VAddr::from(*frame.base_addr() as usize)) , frame , flags , alloc ) } fn map_to_any( &mut self , page: VirtualPage , flags: EntryFlags , alloc: &mut A) -> MapResult<()> where A: FrameAllocator { let frame = unsafe { alloc.allocate() } .map_err(|err| MapErr::Alloc { message: "map to any" , page: page , cause: err })?; self.map(page, frame, flags, alloc) } /// Unmap the given `VirtualPage`. /// /// All freed frames are returned to the given `FrameAllocator`. fn unmap(&mut self, page: VirtualPage, alloc: &mut A) -> MapResult<()> where A: FrameAllocator { use self::tlb::Flush; // get the page table entry corresponding to the page. let page_table = self.pml4_mut() .next_table_mut(page) .and_then(|pdpt| pdpt.next_table_mut(page)) .and_then(|pd| pd.next_table_mut(page)) .ok_or(MapErr::Other { message: "unmap" , page: page , cause: "huge pages not supported" })?; // index the entry from the table let entry = &mut page_table[page]; trace!("got page table entry for {:?}", page); // get the pointed frame for the page table entry. let frame = entry.get_frame() .ok_or(MapErr::Other { message: "unmap" , page: page , cause: "it was not mapped" })?; trace!("page table entry for {:?} points to {:?}", page, frame); // mark the page table entry as unused entry.set_unused(); trace!("set page table entry for {:?} as unused", page); // deallocate the frame and flush the translation lookaside buffer // this is safe because we're in kernel mode unsafe { page.invlpg() }; trace!("flushed TLB"); unsafe { // this is hopefully safe because nobody else should be using an // allocated page frame alloc.deallocate(frame); trace!("deallocated page {:?}", frame); } // TODO: check if page tables containing the unmapped page are empty // and deallocate them too? Ok(()) } } impl ActivePML4 { pub unsafe fn new() -> Self { ActivePML4(Unique::new(PML4_PTR)) } fn pml4(&self) -> &Table { unsafe { self.0.as_ref() } } fn pml4_mut(&mut self) -> &mut Table { unsafe { self.0.as_mut() } } /// Returns true if the given page is mapped. #[inline] pub fn is_mapped(&self, page: &VirtualPage) -> bool { self.translate_page(*page).is_some() } } /// An inactive page table that the CPU is not currently using #[derive(Debug)] pub struct InactivePageTable { pml4_frame: PhysicalPage } impl InactivePageTable { pub fn new( frame: PhysicalPage , active_table: &mut ActivePageTable , temp: &mut TempPage) -> MapResult { { trace!("Mapping page {} to frame {}", temp.number, frame.number); let table = temp.map_to_table(frame.clone(), active_table)?; trace!( " . . . Mapped temp page to table frame ."); table.zero(); trace!( " . . . Zeroed inactive table frame."); table[511].set( frame.clone(), PRESENT | WRITABLE); trace!(" . . . Set active table to point to new inactive table.") } let _ = temp.unmap(active_table)?; trace!(" . . Unmapped temp page."); Ok(InactivePageTable { pml4_frame: frame }) } } pub fn test_paging(alloc: &mut A) -> MapResult<()> where A: FrameAllocator { info!("testing paging"); // This testing code shamelessly stolen from Phil Oppermann. let mut pml4 = unsafe { ActivePML4::new() }; // address 0 is mapped trace!("Some = {:?}", pml4.translate(VAddr::from(0))); // second PT entry trace!("Some = {:?}", pml4.translate(VAddr::from(4096))); // second PD entry trace!("Some = {:?}", pml4.translate(VAddr::from(512 * 4096))); // 300th PD entry trace!("Some = {:?}", pml4.translate(VAddr::from(300 * 512 * 4096))); // second PDPT entry trace!("None = {:?}", pml4.translate(VAddr::from(512 * 512 * 4096))); // last mapped byte trace!("Some = {:?}", pml4.translate(VAddr::from(512 * 512 * 4096 - 1))); let addr = VAddr::from(42 * 512 * 512 * 4096); // 42th PDPT entry let page = VirtualPage::containing(addr); let frame = unsafe { alloc.allocate().expect("no more frames") }; trace!("None = {:?}, map to {:?}", pml4.translate(addr), frame); let _ = pml4.map(page, frame, EntryFlags::empty(), alloc)?; trace!("Some = {:?}", pml4.translate(addr)); trace!( "next free frame: {:?}" , unsafe { alloc.allocate() }); //trace!("{:#x}", *(Page::containing(addr).as_ptr())); let _ = pml4.unmap(Page::containing(addr), alloc)?; trace!("None = {:?}", pml4.translate(addr)); Ok(()) } /// Remaps the kernel using 4KiB pages. pub fn kernel_remap(params: &InitParams, alloc: &mut A) -> MapResult where A: FrameAllocator { use elf::Section; // create a temporary page for switching page tables // page number chosen fairly arbitrarily. const TEMP_PAGE_NUMBER: usize = 0xfacade; let mut temp_page = TempPage::new(TEMP_PAGE_NUMBER, alloc); trace!("Created temporary page."); // old and new page tables let mut current_table = unsafe { ActivePageTable::new() }; trace!("Got current page table."); let mut new_table = unsafe { InactivePageTable::new( alloc.allocate() .map_err(|err| MapErr::Alloc { message: "create the new page table" , page: *temp_page , cause: err })? , &mut current_table , &mut temp_page )? }; kinfoln!(dots: " . . ", "Created new {:?}", new_table); // actually remap the kernel -------------------------------------------- current_table.using(&mut new_table, &mut temp_page, |pml4| { // extract allocated ELF sections let sections = params.elf_sections() .filter(|s| s.is_allocated()); kinfoln!(dots: " . . ", "Remapping kernel ELF sections."); for section in sections { // remap ELF sections attempt!( if section.address().is_page_aligned() { let flags = EntryFlags::from(section); let start_frame = PhysicalPage::from(section.address()); let end_frame = PhysicalPage::from(section.end_address()); for frame in start_frame .. end_frame { let _ = pml4.identity_map(frame, flags, alloc)?; } Ok(()) } else { Err(MapErr::NoPage:: { message: "identity map section" , cause: "the start address was not page aligned" }) } => dots: " . . . ", "Identity mapping {}", section ); } // remap VGA buffer let vga_buffer_frame = PhysicalPage::containing(PAddr::from(0xb8000)); attempt!( pml4.identity_map(vga_buffer_frame, WRITABLE, alloc) => dots: " . . ", "Identity mapping VGA buffer" ); // remap Multiboot info kinfoln!( dots: " . . ", "Identity mapping multiboot info" ); let multiboot_start = PhysicalPage::from(params.multiboot_start()); let multiboot_end = PhysicalPage::from(params.multiboot_end()); for frame in multiboot_start .. multiboot_end { let _ = pml4.identity_map(frame, PRESENT, alloc)?; // .expect("couldn't identity map Multiboot {:?}", frame); } Ok(()) })?; trace!("replacing old page table with new page table"); // switch page tables --------------------------------------------------- let old_table = current_table.replace_with(new_table); kinfoln!(dots: " . . ", "Successfully switched to remapped page table!"); // create guard page at the location of the old PML4 table let old_pml4_vaddr = VAddr::from(*(old_table.pml4_frame.base()) as usize); let old_pml4_page = VirtualPage::containing(old_pml4_vaddr); let _ = current_table.unmap(old_pml4_page, alloc)?; trace!("Unmapped guard page at {:?}", old_pml4_page.base()); Ok(current_table) } ================================================ FILE: paging/src/arch/x86_64/table.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (hi@hawkweisman.me) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // use alloc::FrameAllocator; use ::elf; use memory::{Addr, PAGE_SIZE, PAddr, Page, PhysicalPage, VAddr, VirtualPage}; use core::marker::PhantomData; use core::ops::{Index, IndexMut}; use core::{convert, fmt, intrinsics}; use ::{ MapResult, MapErr}; /// The number of entries in a page table. pub const N_ENTRIES: usize = 512; /// Size of a page table (in bytes) pub const PAGE_TABLE_SIZE: usize = N_ENTRIES * PAGE_SIZE as usize; /// Base virtual address of the PML4 table pub const PML4_VADDR: u64 = 0xffffffff_fffff000; /// A pointer to the PML4 table pub const PML4_PTR: *mut Table = PML4_VADDR as *mut _; /// Mask to apply to a page table entry to isolate the flags pub const ENTRY_FLAGS_MASK: u64 = (PAGE_SIZE as u64 - 1) as u64; /// A page table #[repr(C)] pub struct Table where L: TableLevel { /// The entries in the page table. entries: [Entry; N_ENTRIES] , _level_marker: PhantomData } impl fmt::Debug for Table { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{} at {:#p}" , unsafe { intrinsics::type_name::() } , self) } } // TODO: this can be moved to x86_all; it's the number of level traits that is // x86-64 specific. // - eliza, 5/29/2017 pub trait TableLevel { /// How much to shift an address by to find its index in this table. const ADDR_SHIFT_AMOUNT: usize; /// How much to shift a page number by to find its index in this level table const PAGE_SHIFT_AMOUNT: usize; /// Mask for indices const INDEX_MASK: usize = 0o777; } pub trait IndexOf { fn index_of(i: I) -> usize; } impl IndexOf for T where T: TableLevel { /// Returns the index in this table for the given virtual address #[inline] fn index_of(addr: VAddr) -> usize { (addr.as_usize() >> Self::ADDR_SHIFT_AMOUNT) & Self::INDEX_MASK } } impl IndexOf for T where T: TableLevel { /// Returns the index in this table for the given virtual page #[inline] fn index_of(page: VirtualPage) -> usize { (page.number >> Self::PAGE_SHIFT_AMOUNT) & Self::INDEX_MASK } } impl IndexOf for T where T: TableLevel { // lol // i really hope that the compiler understands that this function does // absolutely nothing... #[inline(always)] fn index_of(i: usize) -> usize { i } } pub enum PML4Level {} pub enum PDPTLevel {} pub enum PDLevel {} pub enum PTLevel {} impl TableLevel for PML4Level { // TODO: make sure these values are correct! // - eliza, 5/29/2017 const ADDR_SHIFT_AMOUNT: usize = 39; const PAGE_SHIFT_AMOUNT: usize = 27; } impl TableLevel for PDPTLevel { const ADDR_SHIFT_AMOUNT: usize = 30; const PAGE_SHIFT_AMOUNT: usize = 18; } impl TableLevel for PDLevel { const ADDR_SHIFT_AMOUNT: usize = 21; const PAGE_SHIFT_AMOUNT: usize = 9; } impl TableLevel for PTLevel { const ADDR_SHIFT_AMOUNT: usize = 12; const PAGE_SHIFT_AMOUNT: usize = 0; } pub trait Sublevel: TableLevel { type Next: TableLevel; } impl Sublevel for PML4Level { type Next = PDPTLevel; } impl Sublevel for PDPTLevel { type Next = PDLevel; } impl Sublevel for PDLevel { type Next = PTLevel; } impl Table { #[inline] pub fn page_table_for(&self, page: VirtualPage) -> Option<&Table> { self.next_table(page) .and_then(|pdpt| pdpt.next_table(page)) .and_then(|pd| pd.next_table(page)) } #[inline] pub fn page_table_mut_for(&mut self, page: VirtualPage) -> Option<&mut Table> { self.next_table_mut(page) .and_then(|pdpt| pdpt.next_table_mut(page)) .and_then(|pd| pd.next_table_mut(page)) } } impl Index for Table where L: TableLevel , L: IndexOf { type Output = Entry; #[inline] fn index(&self, i: I) -> &Entry { &self.entries[L::index_of(i)] } } impl IndexMut for Table where L: TableLevel , L: IndexOf { #[inline] fn index_mut(&mut self, i: I) -> &mut Entry { &mut self.entries[L::index_of(i)] } } impl Table { /// Zeroes out the page table by setting all entries "unused" pub fn zero(&mut self) -> &mut Self { trace!("zeroing {:?}", self); for entry in self.entries.iter_mut() { entry.set_unused(); } trace!("zeroed {:?} successfully", self); self } /// Return the start physical address of this `Table` #[inline] pub fn start_paddr(&self) -> PAddr { PAddr::from(self as *const Self) } /// Return the `PhysicalPage` containing this table. #[inline] pub fn frame(&self) -> PhysicalPage { PhysicalPage::containing(self.start_paddr()) } } impl Table { /// Returns the address of the next table, or None if none exists. #[inline] fn next_table_addr(&self, i: usize) -> Option { let flags = self[i].flags(); if flags.contains(PRESENT) && !flags.contains(HUGE_PAGE) { let table_addr = self as *const _ as usize; Some(VAddr::from(table_addr << 9) | (i << 12)) } else { None } } /// Returns the next table, or `None` if none exists #[inline] pub fn next_table(&self, i: I) -> Option<&Table> where L: IndexOf { self.next_table_addr(L::index_of(i)) .map(|table_addr| unsafe { &*(table_addr.as_ptr()) }) } /// Mutably borrows the next table. #[inline] pub fn next_table_mut(&self, i: I) -> Option<& mut Table> where L: IndexOf , I: fmt::Debug { trace!("{:?}, {:?}", self, i); self.next_table_addr(L::index_of(i)) .map(|table_addr| unsafe { &mut *(table_addr.as_mut_ptr()) }) } /// Returns the next table, creating it if it does not exist. pub fn create_next(&mut self, i: VirtualPage, alloc: &mut A) -> MapResult<&mut Table> where A: FrameAllocator { //println!("in create_next"); if self.next_table(i).is_none() { if self[i].is_huge() { return Err(MapErr::Other { message: "create next table" , page: i , cause: "huge pages not supported" }) } //print!("allocating..."); let frame = unsafe { alloc.allocate() } .map_err(|err| MapErr::Alloc { message: "create next table" , page: i , cause: err })?; //println!("done."); self[i].set(frame, PRESENT | WRITABLE); //println!("setted."); self.next_table_mut(i).map(Table::zero) } else { self.next_table_mut(i) }.ok_or(MapErr::TableNotFound { message: "create next table" , page: i , what: unsafe { intrinsics::type_name::() } }) } } bitflags! { pub flags EntryFlags: u64 { /// Present flag. /// Must be 1 to map a 2-MByte page or reference a page table. const PRESENT = 1 << 0, /// Writable flag. /// If 0, writes may not be allowed to the 2-MB region controlled /// by this entry const WRITABLE = 1 << 1 , const USER_ACCESSIBLE = 1 << 2 , const WRITE_THROUGH = 1 << 3 , const NO_CACHE = 1 << 4 , const ACCESSED = 1 << 5 , const DIRTY = 1 << 6 , const HUGE_PAGE = 1 << 7 , const GLOBAL = 1 << 8 , const NO_EXECUTE = 1 << 63 } } impl EntryFlags { /// Returns true if this page is huge #[inline] pub fn is_huge(&self) -> bool { self.contains(HUGE_PAGE) } /// Returns true if this page is present #[inline] pub fn is_present(&self) -> bool { self.contains(PRESENT) } #[inline] pub fn set_present(&mut self, present: bool) -> &mut Self { if present { self.insert(PRESENT) } else { self.remove(PRESENT) } self } #[inline] pub fn set_writable(&mut self, writable: bool) -> &mut Self { if writable { self.insert(WRITABLE) } else { self.remove(WRITABLE) } self } #[inline] pub fn set_executable(&mut self, executable: bool) -> &mut Self { if executable { self.remove(NO_EXECUTE) } else { self.insert(NO_EXECUTE) } self } } #[derive(Debug)] pub struct Entry(u64); impl Entry { pub fn new(addr: PAddr) -> Self { assert!(addr.is_page_aligned()); Entry(*addr) } // TODO: this is one of the worst names I have ever given a thing #[inline] pub fn do_huge(&self, offset: usize) -> Option { if self.is_huge() { self.get_frame() .map(|start_frame| { assert!( start_frame.number as usize % N_ENTRIES == 0 , "Start frame must be aligned on a 1GB boundary!"); start_frame + offset }) } else { None } } /// Returns true if this is an unused entry #[inline] pub fn is_unused(&self) -> bool { self.0 == 0 } /// Sets this entry to be unused #[inline(never)] pub fn set_unused(&mut self) { self.0 = 0; } /// Returns true if this page is huge #[inline] pub fn is_huge(&self) -> bool { self.flags().is_huge() } /// Access the entry's bitflags. #[inline] pub fn flags(&self) -> EntryFlags { EntryFlags::from_bits_truncate(self.0) } /// Returns the physical address pointed to by this page table entry #[inline] pub fn get_addr(&self) -> PAddr { PAddr::from(self.0 & PML4_VADDR) } /// Returns the frame in memory pointed to by this page table entry. pub fn get_frame(&self) -> Option { if self.flags().is_present() { // If the entry is present, mask out bits 12-51 and Some(PhysicalPage::containing(self.get_addr())) } else { None } } pub fn set(&mut self, frame: PhysicalPage, flags: EntryFlags) { let addr: u64 = frame.base_addr().into(); assert!(addr & !0x000fffff_fffff000 == 0); self.0 = addr | flags.bits(); } } impl<'a> convert::From<&'a elf::Section> for EntryFlags { fn from(section: &'a elf::Section) -> Self { *EntryFlags::empty() .set_present(section.is_allocated()) .set_writable(section.is_writable()) .set_executable(section.is_executable()) } } ================================================ FILE: paging/src/arch/x86_64/temp.rs ================================================ use memory::{PAGE_SIZE, Page, PhysicalPage, VAddr, VirtualPage, FrameRange}; use alloc::{AllocResult, AllocErr, Layout, FrameAllocator}; use core::ops; use super::ActivePageTable; use super::table::{Table, PTLevel}; use ::{Mapper, MapResult}; #[derive(Debug)] pub struct TempPage { page: VirtualPage , frames: FrameCache } // Deref conversions for `TempPage` allow us to pass it to functions expecting // a `VirtualPage`. impl ops::Deref for TempPage { type Target = VirtualPage; #[inline] fn deref(&self) -> &VirtualPage { &self.page } } impl ops::DerefMut for TempPage { #[inline] fn deref_mut(&mut self) -> &mut VirtualPage { &mut self.page } } impl TempPage { /// Create a new `TempPage`. /// /// # Arguments /// + `number`: the page number for the temporary page /// + `alloc`: a `FrameAllocator` for allocating the frames to use /// for the temporary page. pub fn new(number: usize, alloc: &mut A) -> Self where A: FrameAllocator { TempPage { page: VirtualPage { number: number } , frames: FrameCache::new(alloc) } } /// Map the `TempPage` to the given frame in the `ActivePageTable`. /// /// # Arguments /// + `frame`: the `PhysicalPage` to map to /// + `table`: the `ActivePageTable` /// /// # Returns /// + The `VAddr` of the mapped page. pub fn map_to( &mut self , frame: PhysicalPage , table: &mut ActivePageTable) -> MapResult { //assert!( !table.is_mapped(self) //, "Cannot map {:?}, as it is already mapped", self); use super::table::WRITABLE; trace!(" . . TempPage::map_to({:?})", frame); table.map(self.page, frame, WRITABLE, &mut self.frames) .map(|_| { self.page.base() }) } pub fn map_to_table( &mut self , frame: PhysicalPage , table: &mut ActivePageTable) -> MapResult<&mut Table> { self.map_to(frame, table) .map(|addr| unsafe { &mut *(addr.as_mut_ptr::>()) }) } pub fn unmap(&mut self, table: &mut ActivePageTable) -> MapResult<()> { trace!("unmapping temp page {:?}", self); // assert!( table.is_mapped(self) // , "Cannot unmap {:?}, as it is not mapped", self); table.unmap(self.page, &mut self.frames) .map(|_| { trace!("temp page unmapped") }) } } #[derive(Debug)] pub struct FrameCache([Option; 3]); impl FrameCache { pub fn new(alloc: &mut A) -> Self where A: FrameAllocator { unsafe { let frames = [ alloc.allocate().ok() , alloc.allocate().ok() , alloc.allocate().ok() ]; FrameCache(frames) } } } impl FrameAllocator for FrameCache { unsafe fn allocate(&mut self) -> AllocResult { self.0.iter_mut() .find( |frame| frame.is_some()) .and_then(|mut frame| frame.take()) .map(|frame| { trace!("frameCache: alloced {:?}", &frame); frame}) .ok_or(AllocErr::Exhausted { request: Layout::from_size_align( PAGE_SIZE as usize , PAGE_SIZE as usize) }) } unsafe fn deallocate(&mut self, frame: PhysicalPage) { self.0.iter_mut() .find( |slot| slot.is_none()) .and_then(|mut slot| { *slot = Some(frame); Some(()) }) .expect("FrameCache can only hold three frames!"); } unsafe fn allocate_range(&mut self, _num: usize) -> AllocResult { unimplemented!() } unsafe fn deallocate_range(&mut self, _range: FrameRange) { unimplemented!() } } ================================================ FILE: paging/src/arch/x86_64/tlb.rs ================================================ use memory::VAddr; use super::{Page, VirtualPage}; /// Invalidate the TLB completely by reloading the CR3 register. /// /// # Safety /// + Causes a general protection fault if not executed in kernel mode. pub unsafe fn flush_all() { use cpu::control_regs::cr3; cr3::write(cr3::read()); } /// Something which may be flushed from the TLB pub trait Flush { /// Invalidate this object in the TLB using the `invlpg` instruction. /// /// # Safety /// + Causes a general protection fault if not executed in kernel mode. unsafe fn invlpg(self); // // /// Invalidate this object in the TLB instruction if we are in kernel mode. // /// // /// # Returns // /// + True if the object was flushed // /// + False if it was not flushed. // #[inline] // fn flush(&self) -> bool { // use cpu::PrivilegeLevel; // // if PrivilegeLevel::current_iopl() != PrivilegeLevel::KernelMode { // false // can't flush, we are not in kernel mode // } else { // // this is safe since we know we are in kernel mode // unsafe { self.invlpg() }; // true // } // } } impl Flush for VAddr { #[inline] unsafe fn invlpg(self) { asm!( "invlpg [$0]" : : "r" (*self) : "memory" : "intel", "volatile" ); } } impl Flush for VirtualPage { #[inline] unsafe fn invlpg(self) { self.base().invlpg() } } ================================================ FILE: paging/src/lib.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (hi@hawkweisman.me) // // Copyright (c) 2015-2016 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! SOS Paging //! //! This is in its own crate so that it can depend on both the `memory` and //! `alloc` crates. #![feature(asm)] #![feature(unique)] #![feature(associated_consts, const_fn)] #![feature(core_intrinsics)] #![no_std] #[macro_use] extern crate bitflags; #[macro_use] extern crate log; #[macro_use] extern crate vga; extern crate spin; extern crate util; extern crate memory; extern crate sos_alloc as alloc; extern crate cpu; extern crate elf; extern crate params; pub mod arch; pub mod stack; pub use self::arch::{kernel_remap, test_paging}; use memory::{Page, PAddr, PhysicalPage, VAddr, VirtualPage}; use alloc::{FrameAllocator, AllocErr}; use core::fmt; pub type MapResult = Result; #[derive(Clone)] pub enum MapErr { Alloc { message: &'static str, page: P, cause: AllocErr } , Other { message: &'static str, page: P, cause: &'static str } , TableNotFound { message: &'static str, page: VirtualPage, what: &'static str } , AlreadyInUse { message: &'static str, page: VirtualPage, frame: PhysicalPage } , NoPage { message: &'static str, cause: &'static str} } impl

fmt::Debug for MapErr

where P: Page + fmt::Debug { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { unimplemented!() } } pub trait Mapper { type Flags; /// Translates a virtual address to the corresponding physical address. /// /// # Return /// + `Some(PAddr)` containing the physical address corresponding to /// `vaddr`, if it is mapped. /// + `None`: if the address is not mapped. fn translate(&self, vaddr: VAddr) -> Option; /// Translates a virtual page to a physical frame. fn translate_page(&self, page: VirtualPage) -> Option; /// Modifies the page tables so that `page` maps to `frame`. /// /// # Arguments /// + `page`: the virtual `Page` to map /// + `frame`: the physical `Frame` that `Page` should map to. /// + `flags`: the page table entry flags. /// + `alloc`: a memory allocator fn map( &mut self, page: VirtualPage, frame: PhysicalPage , flags: Self::Flags, alloc: &mut A ) -> MapResult<()> where A: FrameAllocator; /// Identity map a given `frame`. /// /// # Arguments /// + `frame`: the physical `Frame` to identity map /// + `flags`: the page table entry flags. /// + `alloc`: a memory allocator fn identity_map( &mut self, frame: PhysicalPage , flags: Self::Flags, alloc: &mut A ) -> MapResult<()> where A: FrameAllocator; /// Map the given `VirtualPage` to any free frame. /// /// This is like the fire and forget version of `map_to`: we just pick the /// first available free frame and map the page to it. /// /// # Arguments /// + `page`: the`VirtualPage` to map /// + `flags`: the page table entry flags. /// + `alloc`: a memory allocator fn map_to_any( &mut self, page: VirtualPage , flags: Self::Flags , alloc: &mut A) -> MapResult<()> where A: FrameAllocator; /// Unmap the given `VirtualPage`. /// /// All freed frames are returned to the given `FrameAllocator`. fn unmap(&mut self, page: VirtualPage, alloc: &mut A) -> MapResult<()> where A: FrameAllocator; } ================================================ FILE: paging/src/stack.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Stack allocator use alloc::{AllocResult, AllocErr, FrameAllocator, Layout}; use memory::{PageRange, VAddr}; use ::Mapper; use arch::ActivePageTable; use core::ops::Range; pub type Stack = Range; pub trait StackAllocator { fn allocate( &mut self , page_table: &mut ActivePageTable , frames: &mut A , num_pages: usize) -> AllocResult where A: FrameAllocator; } impl StackAllocator for PageRange { fn allocate( &mut self , page_table: &mut ActivePageTable , frames: &mut A , num_pages: usize) -> AllocResult where A: FrameAllocator { use memory::{PAGE_SIZE, Page}; use arch::table::WRITABLE; let exhausted = || { AllocErr::Exhausted { request: Layout::from_size_align( PAGE_SIZE as usize * num_pages , PAGE_SIZE as usize) } }; if num_pages == 0 { Err(AllocErr::Unsupported { details: "Why would you try to allocate a zero-page stack?" }) } else { // clone a working copy of the stack allocator's page range // we will only write it back if we successfully allocate a new // stack let mut working_pages = self.clone(); // try to get a guard page working_pages.next().ok_or_else(&exhausted)?; let start_page = working_pages.next().ok_or_else(&exhausted)?; let end_page = if num_pages == 1 { start_page } else { working_pages.nth(num_pages - 2) .ok_or_else(&exhausted)? }; // successfully allocated! write back the working page range *self = working_pages; for page in start_page .. end_page { page_table.map_to_any(page, WRITABLE, frames); } let stack_top = end_page.end_address(); Ok(stack_top .. start_page.base()) } } } ================================================ FILE: params/Cargo.toml ================================================ [package] name = "params" version = "0.0.1" authors = [ "Eliza Weisman " ] [profile.dev] opt-level = 3 debug = true rpath = false lto = false debug-assertions = true codegen-units = 1 panic = "abort" [profile.release] opt-level = 3 debug = true rpath = false lto = false panic = "abort" [dependencies] util = { path = "../util" } memory = { path = "../memory" } elf = { path = "../elf" } [dependencies.arrayvec] version = "0.3.16" default-features = false ================================================ FILE: params/src/lib.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (hi@hawkweisman.me) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! SOS init parameters //! //! This crate is intended to facilitate the sharing of initialization //! parameters between "higher-level" SOS subcrates (such as [`alloc`] and //! [`paging`]) in a platform-independent way. //! //! [`alloc`](../alloc) //! [`paging`](../paging) #![no_std] #![deny(missing_docs)] #![feature(step_trait)] extern crate memory; extern crate elf; extern crate arrayvec; use memory::{ PAddr, Page, PhysicalPage, FrameRange }; use core::default::Default; use core::iter::Step; use core::slice::Iter as SliceIter; use arrayvec::{ArrayVec}; pub mod mem; const MAX_MEM_AREAS: usize = 32; /// If we are on x86_64 or armv7 this uses the 64-bit ELF word #[cfg(target_pointer_width = "64")] pub type ElfSections = elf::section::Sections<'static, u64>; /// If we are on x86, this uses the 32-bit ELF word #[cfg(target_pointer_width = "32")] pub type ElfSections = elf::section::Sections<'static, u32>; /// Parameters used during the init process #[derive(Clone, Debug)] pub struct InitParams { /// The base of the kernel memory range pub kernel_base: PAddr , /// The top of the kernel memory range pub kernel_top: PAddr , /// The base of the memory range for the kernel heap pub heap_base: PAddr , /// The top of the memory range to use for the kernel heap pub heap_top: PAddr , /// The base of the memory range for the kernel stack pub stack_base: PAddr , /// The top of the memory range to use for the kernel stack pub stack_top: PAddr , /// The start address of the Multiboot info structure, if it exists. /// /// N.B. that this is currently never `None`, as we only support multiboot. /// However, this may change at a later date. pub multiboot_start: Option , /// The end address of the Multiboot info structure, if it exists. /// /// N.B. that this is currently never `None`, as we only support multiboot. /// However, this may change at a later date. pub multiboot_end: Option , /// Map of memory areas pub mem_map: ArrayVec<[mem::Area; MAX_MEM_AREAS]> , /// Map of elf sections // todo: construct using convert::From pub elf_sections: Option } impl Default for InitParams { fn default() -> Self { // use memory::arch::{HEAP_BASE, HEAP_TOP, STACK_BASE, STACK_TOP}; InitParams { kernel_base: PAddr::from(0x0) // NOTE: this is, of course, Extremely Wrong, but the // `Default` impl is not going to make _correct_ // params, just fill in default values for other // fns that make params. // TODO: should this be an Option instead? , kernel_top: PAddr::from(0x0) , heap_base: PAddr::from(0x0) , heap_top: PAddr::from(0x0) , stack_base: PAddr::from(0x0) , stack_top: PAddr::from(0x0) , multiboot_start: None , multiboot_end: None , mem_map: ArrayVec::<[mem::Area; MAX_MEM_AREAS]>::new() , elf_sections: None } } } impl InitParams { /// Returns an iterator over the kernel's ELF sections // TODO: is this cross-platform? are we using ELF on all our supported // architectures? i think we are, but we should ensure this is the // case... // – eliza, 1/22/2017 pub fn elf_sections(&self) -> ElfSections { self.elf_sections.clone() .expect("Attempted to access ELF sections on a \ non-ELF kernel!") } /// Returns the start address of the multiboot info struct /// /// # Panics /// If this is a non-Multiboot kernel // TODO: instead of panicking, return Option! // - eliza, 5/26/2017 #[inline] pub fn multiboot_start(&self) -> PAddr { self.multiboot_start .expect("Attempted to access Multiboot info structure on a \ non-Multiboot kernel!") } /// Returns the end address of the multiboot info struct /// /// # Panics /// If this is a non-Multiboot kernel // TODO: instead of panicking, return Option! // - eliza, 5/26/2017 pub fn multiboot_end(&self) -> PAddr { self.multiboot_end .expect("Attempted to access Multiboot info structure on a \ non-Multiboot kernel!") } /// Returns the range of frames containing the kernel binary. /// /// The kernel _should_ start on the first address in the frame range, /// since the kernel should be page aligned. // TODO: this should be an array vector or linked list of frame ranges // possibly? // - eliza, 2/28/2017 #[inline] pub fn kernel_frames(&self) -> FrameRange { // TODO: assert that the kernel base addr is page aligned here? // this should maybe be a debug assertion? // - eliza, 1/22/2017 PhysicalPage::containing(self.kernel_base) .. PhysicalPage::containing(self.kernel_top).add_one() } /// Returns the range of frames containing the kernel heap /// /// The heap _should_ start on the first address in the frame range, /// since the heap should be page aligned. #[inline] pub fn heap_frames(&self) -> FrameRange { // TODO: assert that the heap base addr is page aligned here? // this should maybe be a debug assertion? // - eliza, 1/22/2017 PhysicalPage::containing(self.heap_base) .. PhysicalPage::containing(self.heap_top).add_one() } /// Returns the range of frames containing the kernel stack. #[inline] pub fn stack_frames(&self) -> FrameRange { unimplemented!() } /// returns an iterator over the memory map #[inline] pub fn mem_map(&self) -> mem::Map { self.mem_map.iter() } } ================================================ FILE: params/src/mem.rs ================================================ //! Memory parameters use memory::PAddr; use core::ops::Range; use core::slice::Iter; /// A memory map is an iterator over memory areas pub type Map<'a> = Iter<'a, Area>; /// A memory area #[derive(Debug, Copy, Clone)] pub struct Area { /// The start address of this memory area pub start_addr: PAddr , /// The end address of this memory area pub end_addr: PAddr , /// Whether or not the memory area is usable pub is_usable: bool } ================================================ FILE: rustfmt.toml ================================================ where_indent = "Inherit" struct_lit_style = "Visual" chain_base_indent = "Tabbed" chain_indent = "Visual" reorder_imports = true wrap_comments = true fn_brace_style = "PreferSameLine" item_brace_style = "PreferSameLine" use_try_shorthand = true ================================================ FILE: scripts/README.md ================================================ this directory contains a handful of scripts to help install the needed tools to build SOS. these scripts are run by the `make env` task in the `Makefile`. if you'd rather install manually, [BUILDING.md](https://github.com/hawkw/sos-kernel/blob/master/BUILDING.md) describes what you'll need to build SOS and how to install it. i've tried my best to make sure these scripts work correctly on most common platforms, but due to the wide amount of variation in possible build environments, i haven't tested them everywhere. therefore, i can't guarantee they'll work on _your_ system. if something goes wrong, please don't hesitate to [let me know](https://github.com/hawkw/sos-kernel/issues/new). -- eliza ================================================ FILE: scripts/install-env-linux.sh ================================================ #!/bin/bash # # SOS: the Stupid Operating System # by Eliza Weisman (eliza@elizas.website) # # Copyright (c) 2015-2017 Eliza Weisman # Released under the terms of the MIT license. See `LICENSE` in the root # directory of this repository for more information. # # Portions of this script are adapted from a script by Steve Klabnik for # the IntermezzOS project, available at # http://intermezzos.github.io/book/appendix/osx-install.html # set -e # this script will install the required dependencies and tools # to build the SOS kernel. CONTINUE=false distro=$(lsb_release -is) platform=$(uname) bold=$(tput bold) normal=$(tput sgr0) echo "${bold}install-env-linux:${normal} Distro is ${distro}" case $distro in Ubuntu | Debian) echo "${bold}install-env-linux:${normal} Installing with apt-get." echo "${bold}install-env-linux:${normal} This will require sudo." echo "${bold}install-env-linux:${normal} Do you want to continue? (y/n)" read -r response if [[ $response =~ ^([yY][eE][sS]|[yY])$ ]]; then CONTINUE=true fi if ! $CONTINUE; then echo "${bold}install-env-linux:${normal} Okay, cancelling installation." exit fi sudo apt-get install xorriso qemu build-essential mtools | sed "s/^/${bold}apt-get:${normal} /" ;; Arch | ManjaroLinux) echo "${bold}install-env-linux:${normal} Installing with pacman." echo "${bold}install-env-linux:${normal} This will require sudo." echo "${bold}install-env-linux:${normal} Do you want to continue? (y/n)" read -r response if [[ $response =~ ^([yY][eE][sS]|[yY])$ ]]; then CONTINUE=true fi if ! $CONTINUE; then echo "${bold}install-env-linux:${normal} Okay, cancelling installation." exit fi sudo pacman -S --needed binutils grub libisoburn qemu mtools | sed "s/^/${bold}pacman:${normal} /" ;; esac # todo: support non-x86_64 architectures here (later) gcc=$(which gcc) echo "${bold}install-env-linux:${normal} Linking ${gcc} to /usr/bin/x86_64-pc-elf-gcc." sudo ln -s $gcc /usr/bin/x86_64-pc-elf-gcc objcopy=$(which objcopy) echo "${bold}install-env-linux:${normal} Linking ${objcopy} to /usr/bin/x86_64-elf-objcopy." sudo ln -s $objcopy x86_64-elf-objcopy strip=$(which strip) echo "${bold}install-env-linux:${normal} Linking ${strip} to /usr/bin/x86_64-elf-strip." sudo ln -s $strip x86_64-elf-strip ================================================ FILE: scripts/install-env-mac.sh ================================================ #!/bin/bash # # SOS: the Stupid Operating System # by Eliza Weisman (eliza@elizas.website) # # Copyright (c) 2015-2017 Eliza Weisman # Released under the terms of the MIT license. See `LICENSE` in the root # directory of this repository for more information. # # Portions of this script are adapted from a script by Steve Klabnik for # the IntermezzOS project, available at # http://intermezzos.github.io/book/appendix/osx-install.html # set -e # this script will install the required dependencies and tools # to build the SOS kernel. bold=$(tput bold) normal=$(tput sgr0) # check if `brew` is installed command -v brew >/dev/null 2>&1 if [ $? -eq 1 ] then echo "${bold}install-mac:${normal} Homebrew is not installed." echo "${bold}install-mac:${normal} Please go to http://brew.sh/ to install it before continuing." exit fi export TARGET=x86_64-pc-elf mkdir -p $HOME/src # dependencies installable with brew echo "${bold}install-mac:${normal} Installing dependencies using Homebrew..." brew update | sed "s/^/${bold}brew:${normal} /" brew tap Homebrew/bundle | sed "s/^/${bold}brew:${normal} /" brew bundle | sed "s/^/${bold}brew:${normal} /" CARGO_CONFIG="$HOME/.cargo/config" GREP_TARGET_LINKER="\[target\.x86_64\-sos\-kernel\-gnu\]" TARGET_LINKER="\n\n[target.x86_64-sos-kernel-gnu]\nlinker = \"/usr/local/bin/x86_64-pc-elf-gcc\"" if grep -q $GREP_TARGET_LINKER "$CARGO_CONFIG"; then echo "${bold}install-mac:${normal} Target linker already present in $CARGO_CONFIG. Done." else echo "${bold}install-mac:${normal} Adding target linker to $CARGO_CONFIG..." printf "$TARGET_LINKER" >> "$CARGO_CONFIG" fi ================================================ FILE: scripts/install-env.sh ================================================ #!/bin/bash # # SOS: the Stupid Operating System # by Eliza Weisman (eliza@elizas.website) # # Copyright (c) 2015-2017 Eliza Weisman # Released under the terms of the MIT license. See `LICENSE` in the root # directory of this repository for more information. # set -e # this script will install the required dependencies and tools # to build the SOS kernel. CONTINUE=false platform=$(uname) bold=$(tput bold) normal=$(tput sgr0) echo "" echo "${bold}install-env:${normal} Stupid Operating System Dev Environment Setup *** " echo "" echo "${bold}install-env:${normal} This script is about to download and install software on your computer." if [[ $PLATFORM == 'Darwin' ]]; then echo "${bold}install-env:${normal} Since you are on macOS, this install process will not require sudo" else echo "${bold}install-env:${normal} Depending on your OS and package manager, this process may require sudo." fi echo "${bold}install-env:${normal} Please take the time to read the script source code and ensure you are" echo "${bold}install-env:${normal} aware of what software will be installed before continuing." echo "" echo "${bold}install-env:${normal} Do you want to continue? (y/n)" read -r response if [[ $response =~ ^([yY][eE][sS]|[yY])$ ]]; then CONTINUE=true fi if ! $CONTINUE; then echo "${bold}install-env:${normal} Okay, cancelling installation." exit fi set +e echo "${bold}install-env:${normal} Checking if Rust is installed..." command -v rustc >/dev/null 2>&1 if [[ $? -eq 0 ]]; then command -v rustup >/dev/null 2>&1 if [[ $? -eq 1 ]]; then echo "${bold}install-env:${normal} Rust is installed, but it is not managed by \`rustup\`." echo "${bold}install-env:${normal} Your current Rust installation is not supported." echo "${bold}install-env:${normal} Please visit https://www.rustup.rs to re-install using \`rustup\`." echo "${bold}install-env:${normal} Exiting." exit 1 else echo "${bold}install-env:${normal} Rust is already installed." fi else set -e echo "" echo "${bold}install-env:${normal} installing Rust" echo "" curl https://sh.rustup.rs -sSf | sh fi if [ -f ~/.bash_profile ] then echo "export PATH=$PATH:$HOME/.cargo/bin" >> ~/.bash_profile fi echo "${bold}install-env:${normal} Updating Rust version" rustup update nightly echo "${bold}install-env:${normal} Overriding default Rust to nightly for SOS" rustup override add nightly echo "${bold}install-env:${normal} Installing rust-src component" rustup component add rust-src echo "${bold}install-env:${normal} Installing platform-specific dependencies." case $platform in Darwin) echo "${bold}install-env:${normal} Detected OS as macOS." ./scripts/install-env-mac.sh ;; Linux) echo "${bold}install-env:${normal} Detected OS as Linux." ./scripts/install-env-linux.sh ;; *) echo "${bold}install-env:${normal} Unknown OS, exiting." exit 1 ;; esac set +e echo "${bold}install-env:${normal} Checking if xargo is installed..." command -v xargo >/dev/null 2>&1 if [[ $? -eq 1 ]]; then echo "${bold}install-env:${normal} xargo is already installed." exit 0 else set -e echo "" echo "${bold}install-env:${normal} Installing \`xargo\`." echo "" cargo install xargo fi ================================================ FILE: sos_alloc/Cargo.toml ================================================ [package] name = "sos_alloc" version = "0.1.0" authors = ["Eliza Weisman "] [features] default = ["buddy", "bump_ptr", "borrow"] buddy = ["sos_intrusive"] buddy_as_system = ["buddy", "once"] system = [] bump_ptr = [] placement_in = ["system"] borrow = [] first_fit = ["arrayvec"] bench = [] [dependencies.log] version = "0.3.6" default-features = false features = ["release_max_level_info"] [dependencies.memory] path = "../memory" [dependencies.spin] version = "^0.4.6" [dependencies.sos_intrusive] path = "../sos_intrusive" optional = true [dependencies.arrayvec] version = "0.3.16" default-features = false optional = true [dependencies.once] version = "^0.3.3" optional = true [dependencies.params] path = "../params" ================================================ FILE: sos_alloc/src/borrow.rs ================================================ use super::{Address, Allocator, Layout}; use ptr::Unique; use ops::{Deref, DerefMut}; use spin::Mutex; pub trait Lender { type Borrowed; fn borrow(&self) -> Self::Borrowed; } /// A borrowed handle on a heap allocation with a specified lifetime. /// /// This automatically deallocates the allocated object when the borrow's /// lifetime ends. It also ensures that the borrow only lives as long as the /// allocator that provided it, and that the borrow is dropped if the allocator /// is dropped. // TODO: can we replace this with one generic implementation that works for // borrowed pointers, objects, and arrays? can that code be reused for // BorrowedFrame/BorrowedFrameRange as well? // - eliza, 02/21/2017 pub struct BorrowedPtr<'alloc, A> where A: Allocator , A: 'alloc { ptr: Unique , layout: Layout , allocator: &'alloc Mutex } impl<'alloc, A> BorrowedPtr<'alloc, A> where A: Allocator , A: 'alloc { #[inline] pub fn new( ptr: Address , layout: Layout , allocator: &'alloc Mutex) -> Self { BorrowedPtr { ptr: unsafe { Unique::new(ptr) } , layout: layout , allocator: allocator } } } impl<'alloc, A> Deref for BorrowedPtr<'alloc, A> where A: Allocator , A: 'alloc { type Target = Unique; fn deref(&self) -> &Self::Target { &self.ptr } } impl<'alloc, A> Drop for BorrowedPtr<'alloc, A> where A: Allocator , A: 'alloc { fn drop(&mut self) { unsafe { self.allocator.lock().dealloc(self.ptr.as_ptr(), self.layout.clone()) } } } /// A borrowed handle on a heap allocation with a specified lifetime. /// /// This automatically deallocates the allocated object when the borrow's /// lifetime ends. It also ensures that the borrow only lives as long as the /// allocator that provided it, and that the borrow is dropped if the allocator /// is dropped. pub struct Borrowed<'alloc, A, T> where A: Allocator , A: 'alloc { value: Unique , allocator: &'alloc Mutex } impl<'alloc, A, T> Borrowed<'alloc, A, T> where A: Allocator , A: 'alloc { #[inline] pub fn new( value: Unique, allocator: &'alloc Mutex) -> Self { Borrowed { value: value , allocator: allocator } } } impl<'alloc, A, T> Deref for Borrowed<'alloc, A, T> where A: Allocator , A: 'alloc { type Target = T; fn deref(&self) -> &Self::Target { unsafe { self.value.as_ref() } } } impl<'alloc, A, T> DerefMut for Borrowed<'alloc, A, T> where A: Allocator , A: 'alloc { fn deref_mut(&mut self) -> &mut Self::Target { unsafe { self.value.as_mut() } } } impl<'alloc, A, T> Drop for Borrowed<'alloc, A, T> where A: Allocator , A: 'alloc { fn drop(&mut self) { use mem::drop; let address = self.value.as_ptr() as Address; // ensure we drop the object _before_ deallocating it, so that // the object's destructor gets run first // i hope this is correct... drop(self.value.as_ptr()); unsafe { self.allocator.lock() .dealloc( address , Layout::for_value(self.value.as_ref())) } } } ================================================ FILE: sos_alloc/src/buddy/math.rs ================================================ #[cfg(target_arch="x86")] const B: [usize; 5] = [ 0x2, 0xC, 0xF0 , 0xFF00 , 0xFFFF0000]; #[cfg(target_arch="x86")] const S: [usize; 5] = [ 1, 2, 4, 8, 16 ]; #[cfg(target_arch="x86_64")] const B: [usize; 6] = [ 0x2, 0xC, 0xF0 , 0xFF00, 0xFFFF0000 , 0xFFFFFFFF00000000]; #[cfg(target_arch="x86_64")] const S: [usize; 6] = [ 1, 2, 4, 8, 16, 32 ]; pub trait PowersOf2 { fn is_pow2(&self) -> bool; fn next_pow2(&self) -> Self; fn log2(&self) -> Self; } impl PowersOf2 for usize { fn is_pow2(&self) -> bool { *self != 0 && (self & (self - 1)) == 0 } /// Returns the next power of 2 fn next_pow2(&self) -> usize { let mut v = *self; if v == 0 { 1 } else { v -= 1; v = v | (v >> 1); v = v | (v >> 2); v = v | (v >> 4); v = v | (v >> 8); v = v | (v >> 16); v + 1 } } #[cfg(not(any(target_arch="x86_64", target_arch="x86")))] fn log2(&self) -> usize { // This is the "obvious" log base 2 implementation. The lookup table // -based approach would be much faster, but we can't use it for // `usize` since we aren't sure what size a usize is without conditional // compilation. let mut res = 0; let mut num = *self >> 1; while num != 0 { res += 1; num >>= 1; } res } #[cfg(any(target_arch="x86_64", target_arch="x86"))] fn log2(&self) -> usize { let mut r: usize = 0; let mut v = *self; for i in (0..S.len()).rev() { if v & B[i] != 0 { v >>= S[i]; r |= S[i]; } } r } } #[cfg(test)] mod tests { use super::*; #[cfg(feature = "bench")] use test::{self, Bencher}; #[test] fn test_next_pow2() { assert_eq!(2usize.next_pow2(), 2); assert_eq!(0usize.next_pow2(), 1); assert_eq!(3usize.next_pow2(), 4); assert_eq!(5678usize.next_pow2(), 8192); assert_eq!(1, 0.next_pow2()); assert_eq!(1, 1.next_pow2()); assert_eq!(2, 2.next_pow2()); assert_eq!(4, 3.next_pow2()); assert_eq!(4, 4.next_pow2()); assert_eq!(8, 5.next_pow2()); assert_eq!(8, 8.next_pow2()); assert_eq!(16, 9.next_pow2()); assert_eq!(16, 16.next_pow2()); assert_eq!(32, 17.next_pow2()); assert_eq!(32, 32.next_pow2()); assert_eq!(8388608, 8376263.next_pow2()); } #[test] fn test_is_pow2() { assert_eq!(false, 0.is_pow2()); assert_eq!(true, 1.is_pow2()); assert_eq!(true, 2.is_pow2()); assert_eq!(false, 3.is_pow2()); assert_eq!(true, 4.is_pow2()); assert_eq!(false, 255.is_pow2()); assert_eq!(true, 256.is_pow2()); assert_eq!(false, 257.is_pow2()); assert_eq!(false, 4294967295.is_pow2()); } #[test] fn test_log2() { assert_eq!(0, 0.log2()); assert_eq!(0, 1.log2()); assert_eq!(1, 2.log2()); assert_eq!(5, 32.log2()); assert_eq!(10, 1024.log2()); } #[cfg(feature = "bench")] #[bench] fn our_next_pow2(b: &mut Bencher) { use collections::Vec; b.iter(|| { let n = test::black_box(100_000); (0..n).map(|x: usize| x.next_pow2()) .collect::>() // force the map to return so it doesn't get optimised away }) } #[cfg(feature = "bench")] #[bench] fn std_next_power_of_two(b: &mut Bencher) { use collections::Vec; b.iter(|| { let n = test::black_box(100_000); (0..n).map(|x: usize| x.next_power_of_two()) .collect::>() // force the map to return so it doesn't get optimised away }) } #[cfg(feature = "bench")] #[bench] fn our_is_pow2(b: &mut Bencher) { use collections::Vec; b.iter(|| { let n = test::black_box(100_000); (0..n).map(|ref x: usize| x.is_pow2()) .collect::>() // force the map to return so it doesn't get optimised away }) } #[cfg(feature = "bench")] #[bench] fn std_is_power_of_two(b: &mut Bencher) { use collections::Vec; b.iter(|| { let n = test::black_box(100_000); (0..n).map(|x: usize| x.next_power_of_two()) .collect::>() // force the map to return so it doesn't get optimised away }) } } ================================================ FILE: sos_alloc/src/buddy/mod.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Simple buddy-block allocator #![warn(missing_docs)] mod math; #[cfg(feature = "buddy_as_system")] pub mod system; #[cfg(feature = "buddy_as_system")] pub use self::system::BuddyFrameAllocator; use super::{Allocator, Layout, Address, AllocErr}; use self::math::PowersOf2; use core::mem; use core::cmp::{max, min}; use core::ptr::Unique; use intrusive::list::{List, Node}; use intrusive::rawlink::RawLink; use memory::PAGE_SIZE; #[cfg(test)] mod test; /// A `FreeList` is a list of unique free blocks pub type FreeList = List, FreeBlock>; /// A free block header stores a pointer to the next and previous free blocks. pub struct FreeBlock { next: RawLink , prev: RawLink } impl FreeBlock { #[inline] unsafe fn as_ptr(&self) -> *mut u8 { mem::transmute(self) } } impl Node for FreeBlock { #[inline] fn prev(&self) -> &RawLink { &self.prev } #[inline] fn next(&self) -> &RawLink { &self.next } #[inline] fn prev_mut(&mut self) -> &mut RawLink { &mut self.prev } #[inline] fn next_mut(&mut self) -> &mut RawLink { &mut self.next } } // Variadic macro for taking the maximum of n > 2 numbers. // because I'm lazy. macro_rules! max { ($x:expr) => ($x); ($x:expr, $($xs:expr),+) => (max($x, max!($($xs),+))); } /// Structure with data for implementing the buddy block allocation strategy. pub struct Heap<'a> { /// Address of the base of the heap. This must be aligned /// on a `MIN_ALIGN` boundary. pub start_addr: Unique , /// The allocator's free list free_lists: &'a mut [FreeList] , /// Number of blocks in the heap (must be a power of 2) pub heap_size: usize , /// Minimum block size pub min_block_size: usize } impl<'a> Heap<'a> { /// Construct a new `Heap`. /// /// # Arguments /// + `start_addr`: a pointer to the start location of the heap /// + `free_lists`: an array of [`FreeList`]s. The cardinality /// of the `free_lists` array should be equal to the maximum /// allocateable order. /// + `heap_size`: the size of the heap (in bytes) /// /// # Returns /// + A new `Heap`, obviously. /// /// # Panics /// + If `start_addr` is a null pointer or is not page-aligned /// + If the array of `free_lists` is empty /// + If the `heap_size` is too small to contain at least one block, or is /// not a power of two. /// + If the calculated minimum block size is to small to contain a /// [`FreeBlock`] header /// /// # Safety /// + If `start_addr` is not valid, you will have a bad time /// /// [`FreeList`]: type.FreeList.html /// [`FreeBlock`]: struct.FreeBlock.html pub unsafe fn new( start_addr: Address , free_lists: &'a mut [FreeList] , heap_size: usize) -> Heap<'a> { // Cache the number of free lists hopefully saving performance. let n_free_lists = free_lists.len(); assert!( !start_addr.is_null() , "Heap start address cannot be null." ); assert!( n_free_lists > 0 , "Allocator must have at least one free list."); // assert!( start_addr as usize & (PAGE_SIZE-1) as usize == 0 // , "Heap start address must be aligned on a 4k boundary."); let min_block_size = heap_size >> (n_free_lists - 1); assert!( heap_size >= min_block_size , "Heap must be large enough to contain at least one block."); assert!( min_block_size >= mem::size_of::() , "Minimum block size must be large enough to contain \ the free block header."); assert!( heap_size.is_pow2() , "Heap size must be a power of 2."); // // We must have one free list per possible heap block size. // assert_eq!(min_block_size * // (2u32.pow(free_lists.len() as u32 - 1)) as usize, // heap_size); // Zero out the free lists in case we were passed existing data. for list in free_lists.iter_mut() { *list = FreeList::new(); } let mut heap = Heap { start_addr: Unique::new(start_addr) , free_lists: free_lists , heap_size: heap_size , min_block_size: min_block_size }; // the order needed to allocate the entire heap as a single block let root_order = heap.alloc_order(&Layout::from_size_align(heap_size, 1)) .expect("Couldn't determine heap root allocation order!\ This should be (as far as I know) impossible.\ Something is seriously amiss."); // Push the entire heap onto the free lists as the first block. heap.push_block(start_addr, root_order); heap } /// Add a block of max order /// /// # Safety /// + This function has no way to guarantee that the given `block` of /// uninitialized memory is not already in use. pub unsafe fn add_block(&mut self, block: Address) { // TODO: assert the passed block is not a null pointer? // - eliza, 1/23/2017 let order = self.free_lists.len() -1; self.push_block(block, order); } /// Computes the size of an allocation request. /// /// # Arguments /// + `size`: A `usize` containing the size of the request /// + `align`: A `usize` containing the alignment of the request /// /// # Returns /// + `None` if the request is invalid /// + `Some(usize)` containing the size needed if the request is valid #[inline] pub fn alloc_size(&self, layout: &Layout) -> Result { // Pre-check if this is a valid allocation request: // - allocations must be aligned on power of 2 boundaries // - we cannot allocate requests with alignments greater than the // base alignment of the heap without jumping through a bunch of // hoops. let align = layout.align(); debug_assert!(align.is_power_of_two()); if align > PAGE_SIZE as usize { Err(AllocErr::Unsupported { details: "Cannot allocate requests with alignments greater \ than the base alignment of the heap!" }) // If the request is valid, compute the size we need to allocate } else { // the allocation size for the request is the next power of 2 // after the size of the request, the alignment of the request, // or the minimum block size (whichever is greatest). let alloc_size = max!( layout.size(), self.min_block_size, align) .next_power_of_two(); if alloc_size > self.heap_size { // if the calculated size is greater than the size of the heap, // we (obviously) cannot allocate this request. Err(AllocErr::Unsupported { details: "Cannot allocate requests larger than the heap!" }) } else { // otherwise, return the calculated size. Ok(alloc_size) } } } /// Computes the order of an allocation request. /// /// The "order" of an allocation refers to the number of times we need to /// double the minimum block size to get a large enough block for that /// allocation. #[inline] pub fn alloc_order(&self, layout: &Layout) -> Result { trace!(target: "alloc", "TRACE: alloc_order() called"); self.alloc_size(layout) .map(|s| { trace!( target: "alloc" , "in alloc_order(): alloc_size() returned {}", s); s.log2() - self.min_block_size.log2() }) } /// Computes the size allocated for a request of the given order. #[inline] fn order_alloc_size(&self, order: usize) -> usize { 1 << (self.min_block_size.log2() + order) } #[inline] unsafe fn push_block(&mut self, ptr: *mut u8, order: usize) { self.free_lists[order] .push_front(Unique::new(ptr as *mut FreeBlock)) } #[inline] unsafe fn pop_block(&mut self, order: usize) -> Option<*mut u8>{ self.free_lists[order] .pop_front() .map(|block| block.as_ref().as_ptr()) } /// Splits a block /// /// # Arguments /// + `block`: a pointer to the block to split /// + `old_order`: the current order of `block` /// + `new_order`: the requested order for the split block /// /// # Safety /// + This function has no guarantee that `block` is not a null pointer. /// + This function has no guarantee that `block` is not already in use -- /// if it is invoked on an already allocated block, that block may be /// clobbered without warning. /// + This function has no guarantee that `old_order` is the correct order /// for `block`. /// /// # Panics /// + If `new_order` is less than `old_order`: we make a block _larger_ by /// splitting it. /// + If `old_order` is larger than the maximum order of this allocator unsafe fn split_block( &mut self , block: Address , old_order: usize , new_order: usize ) { // TODO: assert the passed block is not a null pointer? // - eliza, 1/23/2017 trace!( target: "alloc" , "split_block() was called, target order: {}.", new_order); assert!( new_order < old_order , "Cannot split a block larger than its current order!"); assert!( old_order <= self.free_lists.len() , "Order is too large for this allocator!"); let mut split_size = self.order_alloc_size(old_order); // let mut curr_order = order; for order in (new_order..old_order).rev() { split_size >>= 1; // let split_size = self.order_alloc_size(order); self.push_block(block.offset(split_size as isize), order); trace!( target: "alloc" , "split block successfully, order: {}, split size: {}" , order, split_size ); } } /// Finds the buddy block for a given block. /// /// # Arguments /// + `order`: the order of the block to find a buddy for /// + `block`: a pointer to the block to find a buddy for /// /// # Returns /// + `Some(*mut u8)` pointing to the buddy block if a buddy was found /// + `None` if the block was the size of the entire heap pub unsafe fn get_buddy( &self , order: usize , block: Address) -> Option

{ // Determine the size of the block allocated for the given order let block_size = self.order_alloc_size(order); if block_size < self.heap_size { let start_addr = self.start_addr.as_ptr(); debug_assert!( !start_addr.is_null(), "Start address of a (supposedly) valid heap was null. Something\ has gone horribly, horribly wrong!"); // Determine the block's position in the heap. let block_pos = (block as usize) - (start_addr as usize); // Calculate the block's buddy by XORing the block's position // in the heap with its size. let block_offset = (block_pos ^ block_size) as isize; Some(start_addr.offset(block_offset)) } else { // If the block is the size of the entire heap, it (obviously) // cannot have a buddy block. None } } /// Finds and removes the target block from the free list. /// /// # Arguments /// + `order`: the order of the free list to remove the block from_raw /// + `block`: a pointer to the block to remove /// /// # Returns /// + `true` if the block was found and removed from the free List /// + `false` if the block was not found pub fn remove_block(&mut self, order: usize, block: Address) -> bool { self.free_lists[order] .cursor_mut() .find_and_remove(|b| b as *const FreeBlock as *const u8 == block) .is_some() } } unsafe impl<'a> Allocator for Heap<'a> { /// Returns a pointer suitable for holding data described by /// `layout`, meeting its size and alignment guarantees. /// /// The returned block of storage may or may not have its contents /// initialized. (Extension subtraits might restrict this /// behavior, e.g. to ensure initialization.) /// /// Returning `Err` indicates that either memory is exhausted or `layout` /// does not meet allocator's size or alignment constraints. /// unsafe fn alloc(&mut self, layout: Layout) -> Result { trace!(target: "alloc", "allocate() was called!"); // First, compute the allocation order for this request self.alloc_order(&layout) .and_then(|order| if order > self.free_lists.len() - 1 { Err(AllocErr::Exhausted { request: layout.clone() }) } else { Ok(order) } ) // If the allocation order is defined, then we try to allocate // a block of that order. Otherwise, the request is invalid. .and_then(|min_order| { trace!( target: "alloc" , "in allocate(): min alloc order is {}", min_order); // Starting at the minimum possible order... // TODO: this is ugly and not FP, rewrite. for order in min_order..self.free_lists.len() { if let Some(block) = self.pop_block(order) { trace!(target: "alloc", "in allocate(): found block"); if order > min_order { trace!( target: "alloc" , "in allocate(): order {} is less than \ minimum ({}), splitting." , order, min_order); self.split_block(block, order, min_order); trace!( target: "alloc" , "in allocate(): split_block() done"); } return Ok(block) } } Err(AllocErr::Exhausted { request: layout }) }) } /// Release an allocated block of memory. /// /// The `size` and `align` parameters _must_ be the same as the original /// size and alignment of the frame being deallocated, otherwise our /// heap will become corrupted. /// /// # Arguments /// + `frame`: a pointer to the block of memory to deallocate /// + `size`: the size of the block being deallocated /// + `align`: the alignment of the block being deallocated unsafe fn dealloc(&mut self, ptr: Address, layout: Layout) { let min_order = self.alloc_order(&layout).unwrap(); // Check if the deallocated block's buddy block is also free. // If it is, merge the two blocks. let mut new_block = ptr; for order in min_order..self.free_lists.len() { // If there is a buddy for this block of the given order... if let Some(buddy) = self.get_buddy(order, ptr) { // ...and if the buddy was free... if self.remove_block(order, buddy) { // ...merge the buddy with the new block (just use // the lower address), and keep going. new_block = min(new_block, buddy); continue; } } // Otherwise, if we've run out of free buddies, push the new // merged block onto the free lsit and return. self.push_block(new_block, order); return; } } } ================================================ FILE: sos_alloc/src/buddy/system.rs ================================================ //! This module integrates the buddy heap allocator into the Rust runtime. use spin::Mutex; use core::ptr; use ::{Allocator, Layout}; use super::{Heap, FreeList}; /// The number of free lists for the kernel heap pub const NUM_FREE_LISTS: usize = 19; static ALLOC: Mutex>> = Mutex::new(None); static mut KERNEL_FREE_LISTS: [FreeList; NUM_FREE_LISTS] // TODO: I really wish there was a less awful way to do this... = [ FreeList::new(), FreeList::new(), FreeList::new() , FreeList::new(), FreeList::new(), FreeList::new() , FreeList::new(), FreeList::new(), FreeList::new() , FreeList::new(), FreeList::new(), FreeList::new() , FreeList::new(), FreeList::new(), FreeList::new() , FreeList::new(), FreeList::new(), FreeList::new() , FreeList::new() , ]; /// Initialize the system heap at the given start address /// /// # Arguments /// + `start_addr`: a pointer to the start address of the kernel heap /// + `heap_size`: the maximum size (in bytes) of the kernel heap /// /// # Panics /// + If called once the kernel heap is already initialized pub unsafe fn init_heap(start_addr: *mut u8, heap_size: usize ) { assert_has_not_been_called!("the kernel heap may not be initialized \ more than once!"); trace!(target: "alloc", "init_heap() was called."); *(ALLOC.lock()) = Some(Heap::new( start_addr , &mut KERNEL_FREE_LISTS , heap_size)); } // -- integrate the heap allocator into the Rust runtime ------------------ #[allow(missing_docs)] #[no_mangle] pub extern "C" fn __rust_allocate(size: usize, align: usize) -> *mut u8 { trace!("__rust_allocate() was called."); unsafe { ALLOC.lock().as_mut() .expect("Cannot allocate memory, no system allocator exists!") .alloc(Layout::from_size_align(size, align)) .map(|blck| { // TODO: can we use `inspect()` here instead? // - eliza, 1/23/2017 trace!( target: "alloc" , "__rust_allocate: allocated {:?}", blck); blck }) // TODO: how to handle various error conditions here in // ways the stdlib expects? // - eliza, 02/02/2017 .unwrap() } } #[allow(missing_docs)] #[no_mangle] pub extern "C" fn __rust_deallocate( ptr: *mut u8, old_size: usize , align: usize ) { unsafe { ALLOC.lock().as_mut() .expect("Cannot deallocate memory, no system allocator exists!") .dealloc(ptr, Layout::from_size_align(old_size, align)) } } #[allow(missing_docs)] #[no_mangle] pub extern "C" fn __rust_reallocate( ptr: *mut u8, old_size: usize , size: usize, align: usize ) -> *mut u8 { unsafe { ALLOC.lock().as_mut() .expect("Cannot reallocate memory, no system allocator exists!") .realloc( ptr , Layout::from_size_align(old_size, align) , Layout::from_size_align(size, align)) // TODO: how to handle various error conditions here in // ways the stdlib expects? // - eliza, 02/02/2017 .unwrap() } } /// This is currently unsupported, so we just silently ignore it /// and return the old size. #[no_mangle] pub extern "C" fn __rust_reallocate_inplace( _ptr: *mut u8 , old_size: usize , _: usize, _: usize ) -> usize { old_size } #[allow(missing_docs)] #[no_mangle] pub extern "C" fn __rust_usable_size(size: usize, _: usize) -> usize { size } /// A frame allocator using the system's buddy-block heap allocator // quick first pass on using the heap allocator as a frame allocator // TODO: this is Extremely Bad And Ugly And Awful. pleae make better. // – eliza, 1/21/2017 pub struct BuddyFrameAllocator; impl BuddyFrameAllocator { /// Construct a new `BuddyFrameAllocator` pub const fn new() -> Self { BuddyFrameAllocator } } // // impl FrameAllocator for BuddyFrameAllocator { // // unsafe fn allocate(&self) -> Option { // ALLOC.lock().as_mut() // .expect("Cannot allocate frame, no system allocator exists!") // .allocate(PAGE_SIZE as usize, PAGE_SIZE as usize) // .map(|block| { // let addr = VAddr::from_ptr(block); // // TODO: make this not be bad and ugly. // PhysicalPage::containing_addr( // PAddr::from(addr.as_usize() as u64)) // }) // // } // // unsafe fn deallocate(&self, frame: PhysicalPage) { // ALLOC.lock().as_mut() // .expect("Cannot deallocate frame, no system allocator exists!") // .deallocate( frame.as_mut_ptr() // , PAGE_SIZE as usize // , PAGE_SIZE as usize); // // } // // unsafe fn allocate_range(&self, _num: usize) -> Option { // unimplemented!() // } // // unsafe fn deallocate_range(&self, range: FrameRange) { // for frame in range { // ALLOC.lock().as_mut() // .expect("Cannot deallocate frames, no system allocator exists") // .deallocate( frame.as_mut_ptr() // , PAGE_SIZE as usize // , PAGE_SIZE as usize); // } // } // // // } ================================================ FILE: sos_alloc/src/buddy/test.rs ================================================ use super::*; use ::{Allocator, Layout}; use core::ptr; extern "C" { /// We need this to allocate aligned memory for our heap. #[cfg(target_os = "macos")] #[link_name = "je_posix_memalign"] fn memalign(alignment: usize, size: usize) -> *mut u8; #[cfg(not(target_os = "macos"))] fn memalign(alignment: usize, size: usize) -> *mut u8; // Release our memory. fn free(ptr: *mut u8); } const HEAP_ALIGN: usize = 4096; const HEAP_SIZE: usize = 256; #[test] fn test_allocation_size_and_order() { unsafe { let mem = memalign(HEAP_ALIGN, HEAP_SIZE); let mut free_lists: [FreeList; 5] = [ FreeList::new(), FreeList::new() , FreeList::new(), FreeList::new() , FreeList::new() ]; let heap = Heap::new( mem, &mut free_lists, HEAP_SIZE ); // TEST NEEDED: Can't align beyond MIN_HEAP_ALIGN. // Can't align beyond heap_size. assert!(heap.alloc_size(&Layout::from_size_align(256, 256*2)).is_err()); macro_rules! assert_size { ($(size: $size: expr, align: $align:expr, $result:expr),*) => { $(assert_eq!( $result , heap.alloc_size(&Layout::from_size_align($size, $align))); )* } } macro_rules! assert_order { ($(size: $size: expr, align: $align:expr, $result:expr),*) => { $(assert_eq!( $result , heap.alloc_order(&Layout::from_size_align($size, $align))); )* } } // Simple allocations just round up to next block size. assert_size!{ size: 0, align: 1, Ok(16) , size: 1, align: 1, Ok(16) , size: 16, align: 1, Ok(16) , size: 17, align: 1, Ok(32) , size: 32, align: 32, Ok(32) , size: 256, align: 256, Ok(256) }; // Aligned allocations use alignment as block size. assert_size!(size: 16, align: 64, Ok(64)); // Block orders. assert_order!{ size: 0, align: 1, Ok(0) , size: 1, align: 1, Ok(0) , size: 16, align: 16, Ok(0) , size: 32, align: 32, Ok(1) , size: 64, align: 64, Ok(2) , size: 128, align: 128, Ok(3) , size: 256, align: 256, Ok(4) // , size: 512, align: 512, Err }; assert!(heap.alloc_order(&Layout::from_size_align(512,512)).is_err()); free(mem); } } #[test] fn test_get_buddy() { unsafe { let mem = memalign(HEAP_ALIGN, HEAP_SIZE); let mut free_lists: [FreeList; 5] = [ FreeList::new(), FreeList::new() , FreeList::new(), FreeList::new() , FreeList::new() ]; let heap = Heap::new( mem , &mut free_lists , HEAP_SIZE ); let block_16_0 = mem; let block_16_1 = mem.offset(16); assert_eq!(Some(block_16_1), heap.get_buddy(0, block_16_0)); assert_eq!(Some(block_16_0), heap.get_buddy(0, block_16_1)); let block_32_0 = mem; let block_32_1 = mem.offset(32); assert_eq!(Some(block_32_1), heap.get_buddy(1, block_32_0)); assert_eq!(Some(block_32_0), heap.get_buddy(1, block_32_1)); let block_32_2 = mem.offset(64); let block_32_3 = mem.offset(96); assert_eq!(Some(block_32_3), heap.get_buddy(1, block_32_2)); assert_eq!(Some(block_32_2), heap.get_buddy(1, block_32_3)); let block_256_0 = mem; assert_eq!(None, heap.get_buddy(4, block_256_0)); free(mem); } } #[test] fn test_alloc_and_dealloc() { unsafe { let mem = memalign(HEAP_ALIGN, HEAP_SIZE); let mut free_lists: [FreeList; 5] = [ FreeList::new(), FreeList::new() , FreeList::new(), FreeList::new() , FreeList::new() ]; let mut heap = Heap::new( mem , &mut free_lists , HEAP_SIZE ); let block_128_0 = heap.alloc(Layout::from_size_align(128, 128)); assert_eq!(Ok(mem.offset(0)), block_128_0); heap.dealloc(block_128_0.unwrap(),Layout::from_size_align(128, 128)); let block_16_0 = heap.alloc(Layout::from_size_align(8, 8)); assert_eq!(Ok(mem), block_16_0); let bigger_than_heap = heap.alloc(Layout::from_size_align(4096, HEAP_SIZE)); assert!(bigger_than_heap.is_err()); let bigger_than_free = heap.alloc(Layout::from_size_align(HEAP_SIZE, HEAP_SIZE)); assert!(bigger_than_free.is_err()); let block_16_1 = heap.alloc(Layout::from_size_align(8, 8)); assert_eq!(Ok(mem.offset(16)), block_16_1); let block_16_2 = heap.alloc(Layout::from_size_align(8, 8)); assert_eq!(Ok(mem.offset(32)), block_16_2); let block_32_2 = heap.alloc(Layout::from_size_align(32, 32)); assert_eq!(Ok(mem.offset(64)), block_32_2); let block_16_3 = heap.alloc(Layout::from_size_align(8, 8)) ; assert_eq!(Ok(mem.offset(48)), block_16_3); // let too_fragmented = heap.alloc(Layout::from_size_align(64, 64); // assert_eq!(None, too_fragmented); heap.dealloc(block_16_0.unwrap(), Layout::from_size_align(8, 8)); heap.dealloc(block_16_3.unwrap(), Layout::from_size_align(8, 8)); heap.dealloc(block_16_1.unwrap(), Layout::from_size_align(8, 8)); heap.dealloc(block_16_2.unwrap(), Layout::from_size_align(8, 8)); heap.dealloc(block_32_2.unwrap(), Layout::from_size_align(32, 32)); let block_128_0 = heap.alloc(Layout::from_size_align(128, 128)); assert_eq!(Ok(mem.offset(0)), block_128_0); let block_128_1 = heap.alloc(Layout::from_size_align(128, 128)); assert_eq!(Ok(mem.offset(128)), block_128_1); heap.dealloc(block_128_1.unwrap(), Layout::from_size_align(128, 128)); heap.dealloc(block_128_0.unwrap(), Layout::from_size_align(128, 128)); // And allocate the whole heap, just to make sure everything // got cleaned up correctly. let block_256_0 = heap.alloc(Layout::from_size_align(256, 256)).unwrap(); assert_eq!(mem.offset(0), block_256_0); free(mem); } } ================================================ FILE: sos_alloc/src/bump_ptr.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (hi@hawkweisman.me) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // use memory::{Addr, PAddr}; use super::{Address, Allocator, AllocErr, Layout}; /// A simple bump pointer allocator. /// /// This allocator has few "moving parts" and is very fast. However, it doesn't /// allow objects to be deallocated. We use this allocator for early kernel /// objects before we can set up a more sophisticated heap allocator. #[derive(Debug)] pub struct BumpPtr { start: PAddr , end: PAddr , ptr: PAddr } impl BumpPtr { pub const fn new(start: PAddr, end: PAddr) -> Self { BumpPtr { start: start , end: end , ptr: start } } } unsafe impl Allocator for BumpPtr { unsafe fn alloc(&mut self, layout: Layout) -> Result { let start = self.ptr.align_up(layout.align() as ::Repr); // TODO: can this be a saturating add? let end = start + layout.size() as ::Repr; if end > self.end { Err(AllocErr::Exhausted{ request: layout.clone() }) } else { // bump self.ptr = end; Ok(start.as_mut_ptr()) } } unsafe fn dealloc(&mut self, _ptr: Address, _layout: Layout) { // just leak it } } ================================================ FILE: sos_alloc/src/first_fit.rs ================================================ use arrayvec::ArrayVec; use memory::{Page, MemRange, PhysicalPage, FrameRange}; use super::FrameAllocator; use spin::Mutex; const SIZE: usize = 256; /// A simple first-fit allocator for allocating page frames. pub struct FirstFit<'a> { frames: &'a Mutex> } impl<'a> FrameAllocator for FirstFit<'a> { unsafe fn allocate(&self) -> Option { unimplemented!() } unsafe fn deallocate(&self, _frame: PhysicalPage) { unimplemented!() } unsafe fn allocate_range(&self, num: usize) -> Option { let mut frames = self.frames.lock(); frames.iter() .position(|range| range.length() >= num) .map(|i| { if num < frames[i].length() { frames[i].drop_front(num); } else { frames.remove(i); } frames[i].start.range_of(num) }) } unsafe fn deallocate_range(&self, _range: FrameRange) { unimplemented!() } } ================================================ FILE: sos_alloc/src/frame/mem_map.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! A simple allocator using the [`InitParams`] memory map. //! //! This is basically just a bump pointer allocator for frames; since //! it doesn't support deallocating frames. use super::{Frame, FrameRange, Allocator}; use ::{AllocResult, AllocErr, Layout}; use params::{InitParams, mem}; use memory::{Page, PAGE_SIZE, PAddr}; use core::iter::Step; use core::convert::From; /// A simple area allocator. /// /// This is based on the memory area allocation scheme described /// by Phil Oppermann at [http://os.phil-opp.com/allocating-frames.html]. /// /// This is Not A Good Allocation Scheme, as it does not currently support /// reallocation of freed frames. The plan is that it will only be used /// initially, and after we've allocated everything once, we'll switch over /// to a better allocator. pub struct MemMapAllocator<'a> { next_free: Frame , current_area: Option<&'a mem::Area> , areas: mem::Map<'a> , kernel_frames: FrameRange , mb_frames: FrameRange } impl<'a> MemMapAllocator<'a> { fn next_area(&mut self) { // println!("In next_area"); self.current_area = self.areas.clone() .filter(|a| Frame::containing(a.end_addr) >= self.next_free) .min_by_key(|a| a.start_addr) .map(|area| { let start = Frame::containing(area.start_addr); if self.next_free > start { self.next_free = start }; area }) } } impl<'a> From<&'a InitParams> for MemMapAllocator<'a> { fn from(params: &'a InitParams) -> Self { let mut new_allocator = MemMapAllocator { next_free: Frame::containing(PAddr::new(0x12000)) , current_area: None , areas: params.mem_map() , kernel_frames: params.kernel_frames() // TODO: handle non-multiboot case , mb_frames: Frame::containing(params.multiboot_start()) .. Frame::containing(params.multiboot_end()).add_one() }; trace!("creating mem map allocator"); trace!("kernel frames: {:?}", new_allocator.kernel_frames); trace!("multiboot frames: {:?}", new_allocator.mb_frames); new_allocator.next_area(); new_allocator } } impl<'a> Allocator for MemMapAllocator<'a> { // type Frame = Frame; unsafe fn allocate(&mut self) -> AllocResult { // // println!("In alloc method"); if let Some(area) = self.current_area { let frame = Frame { number: self.next_free.number }; match frame { // all frames in the current memory area are in use f if f > Frame::containing(area.end_addr) => { // so we advance to the next free area // println!("All frames in current area in use."); self.next_area(); // println!("...and returning None"); } , // this frame is in use by the kernel. f if f >= self.kernel_frames.start && f <= self.kernel_frames.end => { // skip ahead to the end of the kernel // println!("In kernel frame, skipping."); self.next_free = self.kernel_frames.end.add_one(); // println!("...and returning None"); } , // this frame is part of the multiboot info. f if f >= self.mb_frames.start && f <= self.mb_frames.end=> { // skip ahead to the end of the multiboot info. // println!("In multiboot frame, skipping..."); self.next_free = self.mb_frames.end.add_one(); // println!("...and returning None"); } , // this frame is free. frame => { // advance the next free frame and return this frame. // println!("In free frame, advancing..."); self.next_free = self.next_free.add_one(); // println!("...and returning {:?}", frame); trace!("allocated {:?}", frame); return Ok(frame) } }; self.allocate() } else { // println!("No free frames remain!"); Err(AllocErr::Exhausted { request: Layout::from_size_align( PAGE_SIZE as usize, PAGE_SIZE as usize) }) } } /// Deallocate a frame unsafe fn deallocate(&mut self, _frame: Frame) { // just leak it } /// Allocate a range of frames unsafe fn allocate_range(&mut self, _num: usize) -> AllocResult { unimplemented!() } /// Deallocate a range of frames unsafe fn deallocate_range(&mut self, _range: FrameRange) { //just leak it } } ================================================ FILE: sos_alloc/src/frame/mod.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Frame allocation #![warn(missing_docs)] use memory::{FrameRange, PhysicalPage as Frame}; use super::AllocResult; use core::ops; use spin::Mutex; pub mod mem_map; /// An allocator for allocating physical frames. pub trait Allocator: Sized { /// Allocate a new frame unsafe fn allocate(&mut self) -> AllocResult; /// Deallocate a frame unsafe fn deallocate(&mut self, frame: Frame); /// Allocate a range of frames unsafe fn allocate_range(&mut self, num: usize) -> AllocResult; /// Deallocate a range of frames unsafe fn deallocate_range(&mut self, range: FrameRange); } /// An allocator capable of lending [borrowed frame]s /// /// [borrowed frame]: struct.BorrowedFrame.html pub trait Lender where A: Allocator { /// Borrow a `Frame` from this allocator. ///e /// The `BorrowedFrame` will live as long as this allocator, and will /// contain a handle on a `Frame` that will be automatically deallocated /// when the `BorrowedFrame` is dropped. /// /// # Returns /// + `Some(BorrowedFrame)` if there are frames remaining in this /// allocator. /// + `None` if the allocator is out of frames. // TODO: do we want to refactor this into returning Results? // - eliza, 02/21/2017 fn borrow(&self) -> AllocResult>; /// Borrow a `FrameRange` from this allocator. /// /// The `BorrowedFrameRange` will live as long as this allocator, and will /// contain a handle on a range of `Frame`s that will be automatically /// deallocated when the `BorrowedFrameRange` is dropped. /// /// # Arguments /// + `num`: The number of frames to allocate. /// /// # Returns /// + `Some(BorrowedFrameRange)` if there are enough `Frame`s /// remaining in the allocator to fulfill the allocation /// request. /// + `None` if there are not enough frames in the allocator to fulfill the /// allocation request. // TODO: do we want to refactor this into returning Results? // - eliza, 02/21/2017 fn borrow_range(&self, num: usize) -> AllocResult>; } impl Lender for Mutex { fn borrow(&self) -> AllocResult> { // TODO: can this be rewritten to just use `self.borrow_range(1)`? // - eliza, 02/21/2017 unsafe { self.lock().allocate() } .map(|frame| BorrowedFrame { frame: frame , allocator: self }) } fn borrow_range(&self, num: usize) -> AllocResult> { unsafe { self.lock().allocate_range(num) } .map(|range| BorrowedFrameRange { range: range , allocator: self }) } } /// A borrowed handle on a frame with a specified lifetime. /// /// This automatically deallocates the frame when the borrow's lifetime /// ends. It also ensures that the borrow only lives as long as the allocator /// that provided it, and that the borrow is dropped if the allocator is /// dropped. pub struct BorrowedFrame<'alloc, A> where A: Allocator , A: 'alloc { frame: Frame , allocator: &'alloc Mutex } impl<'alloc, A> ops::Deref for BorrowedFrame<'alloc, A> where A: Allocator , A: 'alloc { type Target = Frame; fn deref(&self) -> &Self::Target { &self.frame } } impl<'alloc, A> ops::DerefMut for BorrowedFrame<'alloc, A> where A: Allocator , A: 'alloc { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.frame } } impl<'alloc, A> Drop for BorrowedFrame<'alloc, A> where A: Allocator , A: 'alloc { fn drop(&mut self) { unsafe { self.allocator.lock().deallocate(self.frame) } } } /// Identical to a `BorrowedFrame` but borrowing a range of `Frame`s. pub struct BorrowedFrameRange<'alloc, A> where A: Allocator , A: 'alloc { range: FrameRange , allocator: &'alloc Mutex } impl<'alloc, A> ops::Deref for BorrowedFrameRange<'alloc, A> where A: Allocator , A: 'alloc { type Target = FrameRange; fn deref(&self) -> &Self::Target { &self.range } } impl<'alloc, A> ops::DerefMut for BorrowedFrameRange<'alloc, A> where A: Allocator , A: 'alloc { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.range } } impl<'alloc, A> Drop for BorrowedFrameRange<'alloc, A> where A: Allocator , A: 'alloc { fn drop(&mut self) { unsafe { self.allocator.lock().deallocate_range(self.range.clone()) } } } ================================================ FILE: sos_alloc/src/free.rs ================================================ use core::ptr::Unique; use core::mem; use intrusive::list::{List as IList, Node}; use intrusive::rawlink::RawLink; /// A `FreeList` is a list of unique free blocks pub type List = IList, Block>; /// A free block header stores a pointer to the next and previous free blocks. /// /// A `Block` can be any size, as long as pub struct Block { next: RawLink , prev: RawLink } impl Block { #[inline] pub unsafe fn as_ptr(&self) -> *mut u8 { mem::transmute(self) } } impl Node for Block { #[inline] fn prev(&self) -> &RawLink { &self.prev } #[inline] fn next(&self) -> &RawLink { &self.next } #[inline] fn prev_mut(&mut self) -> &mut RawLink { &mut self.prev } #[inline] fn next_mut(&mut self) -> &mut RawLink { &mut self.next } } ================================================ FILE: sos_alloc/src/lib.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // // Allocator API copyright 2015 The Rust Project Developers. // See the COPYRIGHT file at http://rust-lang.org/COPYRIGHT. // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. //! SOS memory allocation library //! //! This is in its own crate so it can be used by kernel-space and user-space //! OS components. //! //! We reproduce the Rust [`Allocator`] trait described in Rust [RFC 1398], so //! that our allocators can implement it. Once RFC 1398 is implemented, we will //! be able to remove the [`Allocator`] and [`Layout`] types from this crate. //! //! For more information on the Allocator API, refer to: //! + the text of [RFC 1398] //! + rust-lang/rust [tracking issue] //! //! [`Allocator`]: trait.Allocator.html //! [`Layout`]: struct.Layout.html //! [RFC 1398]: https://github.com/rust-lang/rfcs/blob/master/text/1398-kinds-of-allocators.md //! [tracking issue]: https://github.com/rust-lang/rust/issues/32838 #![crate_name = "sos_alloc"] #![crate_type = "lib"] // The compiler needs to be instructed that this crate is an allocator in order // to realize that when this is linked in another allocator like jemalloc // should not be linked in #![cfg_attr( feature = "system", feature(allocator) )] #![cfg_attr( feature = "system", allocator )] #![cfg_attr(feature = "clippy", feature(plugin))] #![cfg_attr(feature = "clippy", plugin(clippy))] #![cfg_attr( feature = "placement_in" , feature(placement_new_protocol, placement_in_syntax))] // Allocators are not allowed to depend on the standard library which in turn // requires an allocator in order to avoid circular dependencies. This crate, // however, can use all of libcore. #![no_std] #![feature(const_fn)] #![feature(unique)] #![feature(core_intrinsics)] #![feature(step_trait)] #![cfg_attr(all(test, feature = "bench"), feature(test))] #![cfg_attr(test, feature(collections))] #[cfg(all(test, feature = "bench"))] extern crate test; #[cfg(test)] extern crate collections; extern crate memory; #[cfg(feature = "first_fit")] extern crate arrayvec; #[cfg(feature = "buddy")] extern crate sos_intrusive as intrusive; extern crate spin; #[cfg(feature = "buddy_as_system")] #[macro_use] extern crate once; #[macro_use] extern crate log; extern crate params; use core::{cmp, ops, ptr, mem}; use ptr::Unique; pub type AllocResult = Result; pub type Size = usize; pub type Capacity = usize; pub type Alignment = usize; pub type Address = *mut u8; pub mod frame; pub use frame::{Allocator as FrameAllocator, Lender as FrameLender}; /// Represents the combination of a starting address and /// a total capacity of the returned block. pub struct Excess(Address, Capacity); fn size_align() -> (usize, usize) { (mem::size_of::(), mem::align_of::()) } /// Category for a memory record. /// /// An instance of `Layout` describes a particular layout of memory. /// You build a `Layout` up as an input to give to an allocator. /// /// All layouts have an associated non-negative size and positive alignment. #[derive(Clone, Debug, PartialEq, Eq)] pub struct Layout { // size of the requested block of memory, measured in bytes. size: Size, // alignment of the requested block of memory, measured in bytes. // we ensure that this is always a power-of-two, because API's // like `posix_memalign` require it and it is a reasonable // constraint to impose on Layout constructors. // // (However, we do not analogously require `align >= sizeof(void*)`, // even though that is *also* a requirement of `posix_memalign`.) align: Alignment, } // FIXME: audit default implementations for overflow errors, // (potentially switching to overflowing_add and // overflowing_mul as necessary). impl Layout { // (private constructor) pub fn from_size_align(size: usize, align: usize) -> Layout { assert!(align.is_power_of_two()); assert!(align > 0); Layout { size: size, align: align } } /// The minimum size in bytes for a memory block of this layout. pub fn size(&self) -> usize { self.size } /// The minimum byte alignment for a memory block of this layout. pub fn align(&self) -> usize { self.align } /// Constructs a `Layout` suitable for holding a value of type `T`. pub fn new() -> Self { let (size, align) = size_align::(); Layout::from_size_align(size, align) } /// Produces layout describing a record that could be used to /// allocate backing structure for `T` (which could be a trait /// or other unsized type like a slice). pub fn for_value(t: &T) -> Self { let (size, align) = (mem::size_of_val(t), mem::align_of_val(t)); Layout::from_size_align(size, align) } /// Creates a layout describing the record that can hold a value /// of the same layout as `self`, but that also is aligned to /// alignment `align` (measured in bytes). /// /// If `self` already meets the prescribed alignment, then returns /// `self`. /// /// Note that this method does not add any padding to the overall /// size, regardless of whether the returned layout has a different /// alignment. In other words, if `K` has size 16, `K.align_to(32)` /// will *still* have size 16. pub fn align_to(&self, align: Alignment) -> Self { if align > self.align { let pow2_align = align.checked_next_power_of_two().unwrap(); debug_assert!(pow2_align > 0); // (this follows from self.align > 0...) Layout { align: pow2_align, ..*self } } else { self.clone() } } /// Returns the amount of padding we must insert after `self` /// to ensure that the following address will satisfy `align` /// (measured in bytes). /// /// Behavior undefined if `align` is not a power-of-two. /// /// Note that in practice, this is only useable if `align <= /// self.align` otherwise, the amount of inserted padding would /// need to depend on the particular starting address for the /// whole record, because `self.align` would not provide /// sufficient constraint. pub fn padding_needed_for(&self, align: Alignment) -> usize { debug_assert!(align <= self.align()); let len = self.size(); let len_rounded_up = (len + align - 1) & !(align - 1); return len_rounded_up - len; } /// Creates a layout describing the record for `n` instances of /// `self`, with a suitable amount of padding between each to /// ensure that each instance is given its requested size and /// alignment. On success, returns `(k, offs)` where `k` is the /// layout of the array and `offs` is the distance between the start /// of each element in the array. /// /// On arithmetic overflow, returns `None`. pub fn repeat(&self, n: usize) -> Option<(Self, usize)> { let padded_size = match self.size.checked_add(self.padding_needed_for(self.align)) { None => return None, Some(padded_size) => padded_size, }; let alloc_size = match padded_size.checked_mul(n) { None => return None, Some(alloc_size) => alloc_size, }; Some((Layout::from_size_align(alloc_size, self.align), padded_size)) } /// Creates a layout describing the record for `self` followed by /// `next`, including any necessary padding to ensure that `next` /// will be properly aligned. Note that the result layout will /// satisfy the alignment properties of both `self` and `next`. /// /// Returns `Some((k, offset))`, where `k` is layout of the concatenated /// record and `offset` is the relative location, in bytes, of the /// start of the `next` embedded witnin the concatenated record /// (assuming that the record itself starts at offset 0). /// /// On arithmetic overflow, returns `None`. pub fn extend(&self, next: Self) -> Option<(Self, usize)> { let new_align = cmp::max(self.align, next.align); let realigned = Layout { align: new_align, ..*self }; let pad = realigned.padding_needed_for(new_align); let offset = self.size() + pad; let new_size = offset + next.size(); Some((Layout::from_size_align(new_size, new_align), offset)) } /// Creates a layout describing the record for `n` instances of /// `self`, with no padding between each instance. /// /// On arithmetic overflow, returns `None`. pub fn repeat_packed(&self, n: usize) -> Option { let scaled = match self.size().checked_mul(n) { None => return None, Some(scaled) => scaled, }; let size = { assert!(scaled > 0); scaled }; Some(Layout { size: size, align: self.align }) } /// Creates a layout describing the record for `self` followed by /// `next` with no additional padding between the two. Since no /// padding is inserted, the alignment of `next` is irrelevant, /// and is not incoporated *at all* into the resulting layout. /// /// Returns `(k, offset)`, where `k` is layout of the concatenated /// record and `offset` is the relative location, in bytes, of the /// start of the `next` embedded witnin the concatenated record /// (assuming that the record itself starts at offset 0). /// /// (The `offset` is always the same as `self.size()`; we use this /// signature out of convenience in matching the signature of /// `fn extend`.) /// /// On arithmetic overflow, returns `None`. pub fn extend_packed(&self, next: Self) -> Option<(Self, usize)> { let new_size = match self.size().checked_add(next.size()) { None => return None, Some(new_size) => new_size, }; Some((Layout { size: new_size, ..*self }, self.size())) } // Below family of methods *assume* inputs are pre- or // post-validated in some manner. (The implementations here ///do indirectly validate, but that is not part of their /// specification.) // // Since invalid inputs could yield ill-formed layouts, these // methods are `unsafe`. /// Creates layout describing the record for a single instance of `T`. pub unsafe fn new_unchecked() -> Self { let (size, align) = size_align::(); Layout::from_size_align(size, align) } /// Creates a layout describing the record for `self` followed by /// `next`, including any necessary padding to ensure that `next` /// will be properly aligned. Note that the result layout will /// satisfy the alignment properties of both `self` and `next`. /// /// Returns `(k, offset)`, where `k` is layout of the concatenated /// record and `offset` is the relative location, in bytes, of the /// start of the `next` embedded witnin the concatenated record /// (assuming that the record itself starts at offset 0). /// /// Requires no arithmetic overflow from inputs. pub unsafe fn extend_unchecked(&self, next: Self) -> (Self, usize) { self.extend(next).unwrap() } /// Creates a layout describing the record for `n` instances of /// `self`, with a suitable amount of padding between each. /// /// Requires non-zero `n` and no arithmetic overflow from inputs. /// (See also the `fn array` checked variant.) pub unsafe fn repeat_unchecked(&self, n: usize) -> (Self, usize) { self.repeat(n).unwrap() } /// Creates a layout describing the record for `n` instances of /// `self`, with no padding between each instance. /// /// Requires non-zero `n` and no arithmetic overflow from inputs. /// (See also the `fn array_packed` checked variant.) pub unsafe fn repeat_packed_unchecked(&self, n: usize) -> Self { self.repeat_packed(n).unwrap() } /// Creates a layout describing the record for `self` followed by /// `next` with no additional padding between the two. Since no /// padding is inserted, the alignment of `next` is irrelevant, /// and is not incoporated *at all* into the resulting layout. /// /// Returns `(k, offset)`, where `k` is layout of the concatenated /// record and `offset` is the relative location, in bytes, of the /// start of the `next` embedded witnin the concatenated record /// (assuming that the record itself starts at offset 0). /// /// (The `offset` is always the same as `self.size()`; we use this /// signature out of convenience in matching the signature of /// `fn extend`.) /// /// Requires no arithmetic overflow from inputs. /// (See also the `fn extend_packed` checked variant.) pub unsafe fn extend_packed_unchecked(&self, next: Self) -> (Self, usize) { self.extend_packed(next).unwrap() } /// Creates a layout describing the record for a `[T; n]`. /// /// On zero `n`, zero-sized `T`, or arithmetic overflow, returns `None`. pub fn array(n: usize) -> Option { Layout::new::() .repeat(n) .map(|(k, offs)| { debug_assert!(offs == mem::size_of::()); k }) } /// Creates a layout describing the record for a `[T; n]`. /// /// Requires nonzero `n`, nonzero-sized `T`, and no arithmetic /// overflow; otherwise behavior undefined. pub fn array_unchecked(n: usize) -> Self { Layout::array::(n).unwrap() } } /// The `AllocErr` error specifies whether an allocation failure is /// specifically due to resource exhaustion or if it is due to /// something wrong when combining the given input arguments with this /// allocator. #[derive(Clone, PartialEq, Eq, Debug)] pub enum AllocErr { /// Error due to hitting some resource limit or otherwise running /// out of memory. This condition strongly implies that *some* /// series of deallocations would allow a subsequent reissuing of /// the original allocation request to succeed. Exhausted { request: Layout }, /// Error due to allocator being fundamentally incapable of /// satisfying the original request. This condition implies that /// such an allocation request will never succeed on the given /// allocator, regardless of environment, memory pressure, or /// other contextual condtions. /// /// For example, an allocator that does not support zero-sized /// blocks can return this error variant. Unsupported { details: &'static str }, } impl AllocErr { pub fn invalid_input(details: &'static str) -> Self { AllocErr::Unsupported { details: details } } pub fn is_memory_exhausted(&self) -> bool { if let AllocErr::Exhausted { .. } = *self { true } else { false } } pub fn is_request_unsupported(&self) -> bool { if let AllocErr::Unsupported { .. } = *self { true } else { false } } } /// The `CannotReallocInPlace` error is used when `fn realloc_in_place` /// was unable to reuse the given memory block for a requested layout. #[derive(Clone, PartialEq, Eq, Debug)] pub struct CannotReallocInPlace; /// An implementation of `Allocator` can allocate, reallocate, and /// deallocate arbitrary blocks of data described via `Layout`. /// /// Some of the methods require that a layout *fit* a memory block. /// What it means for a layout to "fit" a memory block means is that /// the following two conditions must hold: /// /// 1. The block's starting address must be aligned to `layout.align()`. /// /// 2. The block's size must fall in the range `[use_min, use_max]`, where: /// /// * `use_min` is `self.usable_size(layout).0`, and /// /// * `use_max` is the capacity that was (or would have been) /// returned when (if) the block was allocated via a call to /// `alloc_excess` or `realloc_excess`. /// /// Note that: /// /// * the size of the layout most recently used to allocate the block /// is guaranteed to be in the range `[use_min, use_max]`, and /// /// * a lower-bound on `use_max` can be safely approximated by a call to /// `usable_size`. /// pub unsafe trait Allocator { /// Returns a pointer suitable for holding data described by /// `layout`, meeting its size and alignment guarantees. /// /// The returned block of storage may or may not have its contents /// initialized. (Extension subtraits might restrict this /// behavior, e.g. to ensure initialization.) /// /// Returning `Err` indicates that either memory is exhausted or `layout` /// does not meet allocator's size or alignment constraints. /// /// Implementations are encouraged to return `Err` on memory /// exhaustion rather than panicking or aborting, but this is /// not a strict requirement. (Specifically: it is *legal* to use /// this trait to wrap an underlying native allocation library /// that aborts on memory exhaustion.) unsafe fn alloc(&mut self, layout: Layout) -> Result; /// Deallocate the memory referenced by `ptr`. /// /// `ptr` must have previously been provided via this allocator, /// and `layout` must *fit* the provided block (see above); /// otherwise yields undefined behavior. unsafe fn dealloc(&mut self, ptr: Address, layout: Layout); /// Allocator-specific method for signalling an out-of-memory /// condition. /// /// Implementations of the `oom` method are discouraged from /// infinitely regressing in nested calls to `oom`. In /// practice this means implementors should eschew allocating, /// especially from `self` (directly or indirectly). /// /// Implementions of this trait's allocation methods are discouraged /// from panicking (or aborting) in the event of memory exhaustion; /// instead they should return an appropriate error from the /// invoked method, and let the client decide whether to invoke /// this `oom` method. fn oom(&mut self, _: AllocErr) -> ! { unsafe { ::core::intrinsics::abort() } } // == ALLOCATOR-SPECIFIC QUANTITIES AND LIMITS == // usable_size /// Returns bounds on the guaranteed usable size of a successful /// allocation created with the specified `layout`. /// /// In particular, for a given layout `k`, if `usable_size(k)` returns /// `(l, m)`, then one can use a block of layout `k` as if it has any /// size in the range `[l, m]` (inclusive). /// /// (All implementors of `fn usable_size` must ensure that /// `l <= k.size() <= m`) /// /// Both the lower- and upper-bounds (`l` and `m` respectively) are /// provided: An allocator based on size classes could misbehave /// if one attempts to deallocate a block without providing a /// correct value for its size (i.e., one within the range `[l, m]`). /// /// Clients who wish to make use of excess capacity are encouraged /// to use the `alloc_excess` and `realloc_excess` instead, as /// this method is constrained to conservatively report a value /// less than or equal to the minimum capacity for *all possible* /// calls to those methods. /// /// However, for clients that do not wish to track the capacity /// returned by `alloc_excess` locally, this method is likely to /// produce useful results. unsafe fn usable_size(&self, layout: &Layout) -> (Capacity, Capacity) { (layout.size(), layout.size()) } // == METHODS FOR MEMORY REUSE == // realloc. alloc_excess, realloc_excess /// Returns a pointer suitable for holding data described by /// `new_layout`, meeting its size and alignment guarantees. To /// accomplish this, this may extend or shrink the allocation /// referenced by `ptr` to fit `new_layout`. /// /// * `ptr` must have previously been provided via this allocator. /// /// * `layout` must *fit* the `ptr` (see above). (The `new_layout` /// argument need not fit it.) /// /// Behavior undefined if either of latter two constraints are unmet. /// /// In addition, `new_layout` should not impose a different alignment /// constraint than `layout`. (In other words, `new_layout.align()` /// should equal `layout.align()`.) /// However, behavior is well-defined (though underspecified) when /// this constraint is violated; further discussion below. /// /// If this returns `Ok`, then ownership of the memory block /// referenced by `ptr` has been transferred to this /// allocator. The memory may or may not have been freed, and /// should be considered unusable (unless of course it was /// transferred back to the caller again via the return value of /// this method). /// /// Returns `Err` only if `new_layout` does not meet the allocator's /// size and alignment constraints of the allocator or the /// alignment of `layout`, or if reallocation otherwise fails. (Note /// that did not say "if and only if" -- in particular, an /// implementation of this method *can* return `Ok` if /// `new_layout.align() != old_layout.align()`; or it can return `Err` /// in that scenario, depending on whether this allocator /// can dynamically adjust the alignment constraint for the block.) /// /// If this method returns `Err`, then ownership of the memory /// block has not been transferred to this allocator, and the /// contents of the memory block are unaltered. unsafe fn realloc(&mut self, ptr: Address, layout: Layout, new_layout: Layout) -> Result { let (min, max) = self.usable_size(&layout); let s = new_layout.size(); // All Layout alignments are powers of two, so a comparison // suffices here (rather than resorting to a `%` operation). if min <= s && s <= max && new_layout.align() <= layout.align() { return Ok(ptr); } else { let new_size = new_layout.size(); let old_size = layout.size(); let result = self.alloc(new_layout); if let Ok(new_ptr) = result { ptr::copy(ptr as *const u8, new_ptr, cmp::min(old_size, new_size)); self.dealloc(ptr, layout); } result } } /// Behaves like `fn alloc`, but also returns the whole size of /// the returned block. For some `layout` inputs, like arrays, this /// may include extra storage usable for additional data. unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { let usable_size = self.usable_size(&layout); self.alloc(layout).map(|p| Excess(p, usable_size.1)) } /// Behaves like `fn realloc`, but also returns the whole size of /// the returned block. For some `layout` inputs, like arrays, this /// may include extra storage usable for additional data. unsafe fn realloc_excess(&mut self, ptr: Address, layout: Layout, new_layout: Layout) -> Result { let usable_size = self.usable_size(&new_layout); self.realloc(ptr, layout, new_layout) .map(|p| Excess(p, usable_size.1)) } /// Attempts to extend the allocation referenced by `ptr` to fit `new_layout`. /// /// * `ptr` must have previously been provided via this allocator. /// /// * `layout` must *fit* the `ptr` (see above). (The `new_layout` /// argument need not fit it.) /// /// Behavior undefined if either of latter two constraints are unmet. /// /// If this returns `Ok`, then the allocator has asserted that the /// memory block referenced by `ptr` now fits `new_layout`, and thus can /// be used to carry data of that layout. (The allocator is allowed to /// expend effort to accomplish this, such as extending the memory block to /// include successor blocks, or virtual memory tricks.) /// /// If this returns `Err`, then the allocator has made no assertion /// about whether the memory block referenced by `ptr` can or cannot /// fit `new_layout`. /// /// In either case, ownership of the memory block referenced by `ptr` /// has not been transferred, and the contents of the memory block /// are unaltered. unsafe fn realloc_in_place(&mut self, ptr: Address, layout: Layout, new_layout: Layout) -> Result<(), CannotReallocInPlace> { let (_, _, _) = (ptr, layout, new_layout); Err(CannotReallocInPlace) } // == COMMON USAGE PATTERNS == // alloc_one, dealloc_one, alloc_array, realloc_array. dealloc_array /// Allocates a block suitable for holding an instance of `T`. /// /// Captures a common usage pattern for allocators. /// /// The returned block is suitable for passing to the /// `alloc`/`realloc` methods of this allocator. /// /// May return `Err` for zero-sized `T`. unsafe fn alloc_one(&mut self) -> Result, AllocErr> where Self: Sized { let k = Layout::new::(); if k.size() > 0 { self.alloc(k).map(|p|Unique::new(*p as *mut T)) } else { Err(AllocErr::invalid_input("zero-sized type invalid for alloc_one")) } } /// Deallocates a block suitable for holding an instance of `T`. /// /// The given block must have been produced by this allocator, /// and must be suitable for storing a `T` (in terms of alignment /// as well as minimum and maximum size); otherwise yields /// undefined behavior. /// /// Captures a common usage pattern for allocators. unsafe fn dealloc_one(&mut self, mut ptr: Unique) where Self: Sized { let raw_ptr = ptr.as_mut() as *mut T as *mut u8; self.dealloc(raw_ptr, Layout::new::()); } /// Allocates a block suitable for holding `n` instances of `T`. /// /// Captures a common usage pattern for allocators. /// /// The returned block is suitable for passing to the /// `alloc`/`realloc` methods of this allocator. /// /// May return `Err` for zero-sized `T` or `n == 0`. /// /// Always returns `Err` on arithmetic overflow. unsafe fn alloc_array(&mut self, n: usize) -> Result, AllocErr> where Self: Sized { match Layout::array::(n) { Some(ref layout) if layout.size() > 0 => { self.alloc(layout.clone()) .map(|p| { // println!("alloc_array layout: {:?} yielded p: {:?}", layout, p); Unique::new(p as *mut T) }) } _ => Err(AllocErr::invalid_input("invalid layout for alloc_array")), } } /// Reallocates a block previously suitable for holding `n_old` /// instances of `T`, returning a block suitable for holding /// `n_new` instances of `T`. /// /// Captures a common usage pattern for allocators. /// /// The returned block is suitable for passing to the /// `alloc`/`realloc` methods of this allocator. /// /// May return `Err` for zero-sized `T` or `n == 0`. /// /// Always returns `Err` on arithmetic overflow. unsafe fn realloc_array(&mut self, ptr: Unique, n_old: usize, n_new: usize) -> Result, AllocErr> where Self: Sized { match ( Layout::array::(n_old) , Layout::array::(n_new) , ptr.as_ptr()) { (Some(ref k_old), Some(ref k_new), ptr) if k_old.size() > 0 && k_new.size() > 0 => { self.realloc(ptr as *mut u8, k_old.clone(), k_new.clone()) .map(|p|Unique::new(p as *mut T)) } _ => { Err(AllocErr::invalid_input("invalid layout for realloc_array")) } } } /// Deallocates a block suitable for holding `n` instances of `T`. /// /// Captures a common usage pattern for allocators. unsafe fn dealloc_array(&mut self, ptr: Unique, n: usize) -> Result<(), AllocErr> where Self: Sized { let raw_ptr = ptr.as_ptr() as *mut u8; match Layout::array::(n) { Some(ref k) if k.size() > 0 => { Ok(self.dealloc(raw_ptr, k.clone())) } _ => { Err(AllocErr::invalid_input("invalid layout for dealloc_array")) } } } // UNCHECKED METHOD VARIANTS /// Returns a pointer suitable for holding data described by /// `layout`, meeting its size and alignment guarantees. /// /// The returned block of storage may or may not have its contents /// initialized. (Extension subtraits might restrict this /// behavior, e.g. to ensure initialization.) /// /// Returns `None` if request unsatisfied. /// /// Behavior undefined if input does not meet size or alignment /// constraints of this allocator. unsafe fn alloc_unchecked(&mut self, layout: Layout) -> Option
{ // (default implementation carries checks, but impl's are free to omit them.) self.alloc(layout).ok() } /// Returns a pointer suitable for holding data described by /// `new_layout`, meeting its size and alignment guarantees. To /// accomplish this, may extend or shrink the allocation /// referenced by `ptr` to fit `new_layout`. //// /// (In other words, ownership of the memory block associated with /// `ptr` is first transferred back to this allocator, but the /// same block may or may not be transferred back as the result of /// this call.) /// /// * `ptr` must have previously been provided via this allocator. /// /// * `layout` must *fit* the `ptr` (see above). (The `new_layout` /// argument need not fit it.) /// /// * `new_layout` must meet the allocator's size and alignment /// constraints. In addition, `new_layout.align()` must equal /// `layout.align()`. (Note that this is a stronger constraint /// that that imposed by `fn realloc`.) /// /// Behavior undefined if any of latter three constraints are unmet. /// /// If this returns `Some`, then the memory block referenced by /// `ptr` may have been freed and should be considered unusable. /// /// Returns `None` if reallocation fails; in this scenario, the /// original memory block referenced by `ptr` is unaltered. unsafe fn realloc_unchecked(&mut self, ptr: Address, layout: Layout, new_layout: Layout) -> Option
{ // (default implementation carries checks, but impl's are free to omit them.) self.realloc(ptr, layout, new_layout).ok() } /// Behaves like `fn alloc_unchecked`, but also returns the whole /// size of the returned block. unsafe fn alloc_excess_unchecked(&mut self, layout: Layout) -> Option { self.alloc_excess(layout).ok() } /// Behaves like `fn realloc_unchecked`, but also returns the /// whole size of the returned block. unsafe fn realloc_excess_unchecked(&mut self, ptr: Address, layout: Layout, new_layout: Layout) -> Option { self.realloc_excess(ptr, layout, new_layout).ok() } /// Allocates a block suitable for holding `n` instances of `T`. /// /// Captures a common usage pattern for allocators. /// /// Requires inputs are non-zero and do not cause arithmetic /// overflow, and `T` is not zero sized; otherwise yields /// undefined behavior. unsafe fn alloc_array_unchecked(&mut self, n: usize) -> Option> where Self: Sized { let layout = Layout::array_unchecked::(n); self.alloc_unchecked(layout).map(|p|Unique::new(*p as *mut T)) } /// Reallocates a block suitable for holding `n_old` instances of `T`, /// returning a block suitable for holding `n_new` instances of `T`. /// /// Captures a common usage pattern for allocators. /// /// Requires inputs are non-zero and do not cause arithmetic /// overflow, and `T` is not zero sized; otherwise yields /// undefined behavior. unsafe fn realloc_array_unchecked(&mut self, ptr: Unique, n_old: usize, n_new: usize) -> Option> where Self: Sized { let (k_old, k_new, ptr) = ( Layout::array_unchecked::(n_old) , Layout::array_unchecked::(n_new) , ptr.as_ptr()); self.realloc_unchecked(ptr as *mut u8, k_old, k_new) .map(|p|Unique::new(*p as *mut T)) } /// Deallocates a block suitable for holding `n` instances of `T`. /// /// Captures a common usage pattern for allocators. /// /// Requires inputs are non-zero and do not cause arithmetic /// overflow, and `T` is not zero sized; otherwise yields /// undefined behavior. unsafe fn dealloc_array_unchecked(&mut self, ptr: Unique, n: usize) where Self: Sized { let layout = Layout::array_unchecked::(n); self.dealloc(ptr.as_ptr() as *mut u8, layout); } } #[cfg(feature = "borrow")] pub mod borrow; #[cfg(feature = "buddy")] pub mod buddy; #[cfg(feature = "first_fit")] pub mod first_fit; #[cfg(feature = "bump_ptr")] pub mod bump_ptr; #[cfg(feature = "system")] pub mod system; #[cfg(feature = "system")] pub use system::*; #[cfg(feature = "placement_in")] pub mod place; #[cfg(feature = "placement_in")] pub use place::*; ================================================ FILE: sos_alloc/src/place.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (hi@hawkweisman.me) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Support for placement expressions & box placement. //! //! See Rust [issue #27779] for more information. //! //! [issue #27779]: https://github.com/rust-lang/rust/issues/27779 use core::ops::{BoxPlace, Place, Placer, InPlace}; use core::ptr::Unique; use core::marker::PhantomData; use super::{Layout, Allocator}; use spin::Mutex; /// A [`Place`] representing an intermediate allocoation attempt. /// /// [`Place`]: https://doc.rust-lang.org/std/ops/trait.Place.html pub struct IntermediateAlloc<'alloc, A, T> where T: ?Sized , A: Allocator { ptr: Unique , layout: Layout , alloc: &'alloc Mutex } impl Placer<'alloc, Data, A> for &'alloc Mutex where A: Allocator , Data: ? Sized { type Place = IntermediateAlloc<'alloc, A, Data>; /// Creates a fresh place from `self`. fn make_place(self) -> Self::Place { IntermediateAlloc { ptr: self.lock() .alloc_one::() .unwrap() , layout: Layout::new::() , alloc: self } } } impl<'alloc, A, Data> Place for IntermediateAlloc<'alloc, A, Data> where Data: ?Sized , A: Allocator { #[inline] fn pointer(&mut self) -> *mut Data { self.ptr } } impl<'alloc, A, Data> InPlace for IntermediateAlloc<'alloc, A, Data> where Data: ?Sized , A: Allocator { /// `Owner` is the type of the end value of `in (PLACE) EXPR` /// /// Note that when `in (PLACE) EXPR` is solely used for /// side-effecting an existing data-structure, /// e.g. `Vec::emplace_back`, then `Owner` need not carry any /// information at all (e.g. it can be the unit type `()` in that /// case). type Owner = Data; /// Converts self into the final value, shifting /// deallocation/cleanup responsibilities (if any remain), over to /// the returned instance of `Owner` and forgetting self. unsafe fn finalize(self) -> Self::Owner { unimplemented!() } } ================================================ FILE: sos_alloc/src/system.rs ================================================ use spin::Mutex; use super::{Address, Allocator, AllocErr, Layout, AllocResult}; use core::ops::Deref; #[cfg(feature = "borrow")] use borrow::{Borrowed, BorrowedPtr}; #[cfg(feature = "bump_ptr")] use bump_ptr::BumpPtr; #[cfg(feature = "buddy")] use buddy::Heap as BuddyHeap; pub enum Tier<'a> { Uninitialized , #[cfg(feature = "bump_ptr")] Bump(BumpPtr) , #[cfg(feature = "buddy")] Buddy(BuddyHeap<'a>) } #[cfg(all(feature = "bump_ptr", feature="buddy"))] impl Deref for Tier<'static> { type Target = Allocator + 'static ; fn deref(&self) -> &Self::Target{ match self { &Tier::Bump(ref alloc) => alloc , &Tier::Buddy(ref alloc) => alloc , _ => panic!("no allocator!") } } } #[cfg(all(feature = "bump_ptr", feature = "buddy"))] unsafe impl<'a> Allocator for Tier<'a> { #[inline(always)] unsafe fn alloc(&mut self, layout: Layout) -> AllocResult
{ match *self { Tier::Bump(ref mut alloc) => alloc.alloc(layout) , Tier::Buddy(ref mut alloc) => alloc.alloc(layout) , _ => Err(AllocErr::Unsupported { details: "System allocator uninitialized!" }) } } #[inline(always)] unsafe fn dealloc(&mut self, ptr: Address, layout: Layout) { match *self { Tier::Bump(ref mut alloc) => alloc.dealloc(ptr, layout) , Tier::Buddy(ref mut alloc) => alloc.dealloc(ptr, layout) , _ => { // just leak it? not sure if we should panic here... } } } } pub struct SystemAllocator(Mutex>); #[cfg(feature = "borrow")] impl SystemAllocator { /// Borrow a raw allocation from the system allocator /// /// The borrowed allocation handle will automagically deallocate the /// allocation at the end of its lifetime pub fn borrow_ptr<'alloc>(&'alloc self, layout: Layout) -> AllocResult>> { let ptr = unsafe { self.0.lock().alloc(layout.clone())? }; Ok(BorrowedPtr::new(ptr, layout, &self.0)) } /// Borrow an object allocation from the system allocator. /// /// The borrowed allocation handle will automagically deallocate the /// allocated object at the end of its lifetime pub fn borrow<'alloc, T>(&'alloc self) -> AllocResult, T>> { let value = unsafe { self.0.lock().alloc_one::()? }; Ok(Borrowed::new(value, &self.0 )) } } ================================================ FILE: sos_intrusive/Cargo.toml ================================================ [package] name = "sos_intrusive" version = "0.1.0" authors = ["Eliza Weisman "] [features] default = [] no-std = [] ================================================ FILE: sos_intrusive/src/lib.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! # SOS Intrusive Collections //! //! _Intrusive_ data structures are data structures whose elements are //! "aware" of the structures in which they are stored. That is to say //! that data related to the layout and structure of an intrusive collection //! is stored by the elements in the collection, rather than internally to //! them. //! //! Intrusive data structures are useful for low-level programming in Rust //! since they do not explicitly allocate memory. This means that we can use //! intrusive structures to implement the kernel memory allocator and other //! kernel subsystems which require structures such as lists prior to //! the initialization of the kernel heap. //! //! This crate currently provides an intrusive linked-list implementation. //! //! # Features //! + `use-std`: use the Rust standard library (`std`), rather than `core`. #![crate_name = "sos_intrusive"] #![crate_type = "lib"] #![feature( const_fn , const_ptr_null_mut )] #![feature(ptr_internals)] #![feature(unique )] #![cfg_attr(not(feature = "use-std"), no_std )] #![cfg_attr(feature = "clippy", feature(plugin))] #![cfg_attr(feature = "clippy", plugin(clippy))] #![cfg_attr(test, feature(box_syntax))] pub mod rawlink; pub use rawlink::RawLink; pub mod list; pub use list::List; #[cfg(test)] extern crate std; ================================================ FILE: sos_intrusive/src/list/mod.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! An intrusive linked list implementation using `RawLink`s. //! //! An _intrusive_ list is a list structure wherein the type of element stored //! in the list holds references to other nodes. This means that we don't have //! to store a separate node data type that holds the stored elements and //! pointers to other nodes, reducing the amount of memory allocated. We can //! use intrusive lists in code that runs without the kernel memory allocator, //! like the allocator implementation itself, since each list element manages //! its own memory. use super::rawlink::RawLink; use core::marker::PhantomData; use core::ptr::Unique; use core::iter; #[cfg(test)] mod test; pub unsafe trait OwnedRef { unsafe fn from_raw(ptr: *mut T) -> Self; unsafe fn take(self); fn get(&self) -> &T; fn get_mut(&mut self) -> &mut T; } /// This trait defines a node in an intrusive list. /// /// A Node must be capable of providing mutable and immutable references to /// the previous and next nodes in the list. pub trait Node: Sized { fn next(&self) -> &RawLink; fn prev(&self) -> &RawLink; fn next_mut(&mut self) -> &mut RawLink; fn prev_mut(&mut self) -> &mut RawLink; } /// The `List` struct is our way of interacting with an intrusive list. /// /// It stores a pointer to the head and tail of the list, the length of the /// list, and a `PhantomData` marker for the list's `OwnedRef` type. It /// provides the methods for pushing, popping, and indexing the list. pub struct List where T: OwnedRef , N: Node { head: RawLink , tail: RawLink , _ty_marker: PhantomData , length: usize } // impl Node for List // where T: OwnedRef // , T: Node { // // fn next(&self) -> &RawLink { &self.head } // fn prev(&self) -> &RawLink { &self.tail } // // fn next_mut(&mut self) -> &mut RawLink { self.head } // fn prev_mut(&mut self) -> &mut RawLink { self.tail } // } impl List where T: OwnedRef , N: Node { /// Construct a new `List` with zero elements pub const fn new() -> Self { List { head: RawLink::none() , tail: RawLink::none() , _ty_marker: PhantomData , length: 0 } } /// Returns the length of the list #[inline] pub fn len(&self) -> usize { self.length } /// Borrows the first element of the list as an `Option` /// /// # Returns /// - `Some(&N)` if the list has elements /// - `None` if the list is empty. #[inline] pub fn front(&self) -> Option<&N> { unsafe { self.head.resolve() } } /// Borrows the last element of the list as an `Option` /// /// # Returns /// - `Some(&N)` if the list has elements /// - `None` if the list is empty. #[inline] pub fn back(&self) -> Option<&N> { unsafe { self.tail.resolve() } } /// Mutably borrows the first element of the list as an `Option` /// /// # Returns /// - `Some(&mut N)` if the list has elements /// - `None` if the list is empty. #[inline] pub fn front_mut(&mut self) -> Option<&mut N> { unsafe { self.head.resolve_mut() } } /// Mutably borrows the last element of the list as an `Option` /// /// # Returns /// - `Some(&mut N)` if the list has elements /// - `None` if the list is empty. #[inline] pub fn back_mut(&mut self) -> Option<&mut N> { unsafe { self.tail.resolve_mut() } } /// Returns true if the list is empty. #[inline] pub fn is_empty(&self) -> bool { self.head.is_none() } /// Push an element to the front of the list. // TODO: should this really be called "prepend"? pub fn push_front(&mut self, mut item: T) { unsafe { match self.head.resolve_mut() { None => { // If this node's head is empty, set the pushed item's // links to None, and make this node's tail point to the // pushed item *item.get_mut().next_mut() = RawLink::none(); *item.get_mut().prev_mut() = RawLink::none(); self.tail = RawLink::some(item.get_mut()); } , Some(head) => { // If this node is not empty, set the pushed item's tail // to point at the head node, and make the head node's tail // point to the pushed item *item.get_mut().next_mut() = RawLink::some(head); *item.get_mut().prev_mut() = RawLink::none(); *head.prev_mut() = RawLink::some(item.get_mut()); } } // then, set this node's head pointer to point to the pushed item self.head = RawLink::some(item.get_mut()); item.take(); self.length += 1; } } /// Push an element to the back of the list. // TODO: should this really be called "append"? // (the Rust standard library uses `append` to refer to the "drain all the // elements of another list and push them to this list" operation, but I // think that that function is more properly called `concat`...) pub fn push_back(&mut self, mut item: T) { unsafe { match self.tail.resolve_mut() { None => { // If this node's tail is empty, set the pushed item's // links to None, and make this node's head point to the // pushed item *item.get_mut().next_mut() = RawLink::none(); *item.get_mut().prev_mut() = RawLink::none(); self.head = RawLink::some(item.get_mut()); } , Some(tail) => { // If this node is not empty, set the pushed item's head // to point at the tail node, and make the tail node's head // point to the pushed item *item.get_mut().next_mut() = RawLink::none(); *item.get_mut().prev_mut() = RawLink::some(tail); *tail.next_mut() = RawLink::some(item.get_mut()); } } // then, set this node's head pointer to point to the pushed item self.tail = RawLink::some(item.get_mut()); item.take(); self.length += 1; } } /// Removes and returns the element at the front of the list. /// /// # Returns /// - `Some(T)` containing the element at the front of the list if the /// list is not empty /// - `None` if the list is empty pub fn pop_front(&mut self) -> Option { unsafe { self.head.take().resolve_mut() .map(|head| { // mem::swap( &mut self.head // , head.next_mut().resolve_mut() // .map(|next| next.prev_mut()) // .unwrap_or(&mut RawLink::none()) ); match head.next_mut().resolve_mut() { None => self.tail = RawLink::none() , Some(next) => { *next.prev_mut() = RawLink::none(); self.head = RawLink::some(next); } } self.length -= 1; T::from_raw(head) }) } } /// Removes and returns the element at the end of the list. /// /// # Returns /// - `Some(T)` containing the element at the end of the list if the /// list is not empty /// - `None` if the list is empty pub fn pop_back(&mut self) -> Option { unsafe { self.tail.take().resolve_mut() .map(|tail| { match tail.prev_mut().resolve_mut() { None => self.head = RawLink::none() , Some(prev) => { *prev.next_mut() = RawLink::none(); self.tail = RawLink::some(prev); } } self.length -= 1; T::from_raw(tail) }) } } /// Borrows the element at the front of the list /// /// # Returns /// - `Some(&T)` containing the element at the end of the list if the /// list is not empty /// - `None` if the list is empty pub fn peek_front(&self) -> Option<&N> { unsafe { self.tail.resolve() } } /// Returns a cursor for iterating over or modifying the list. pub fn cursor_mut<'a>(&'a mut self) -> ListCursorMut<'a, T, N> { ListCursorMut { list: self , current: RawLink::none() } } } impl iter::FromIterator for List where T: OwnedRef , N: Node { fn from_iter>(iterator: I) -> Self { let mut list: Self = List::new(); for item in iterator { list.push_front(item) } list } } pub trait Cursor { type Item; fn next(&mut self) -> Option; fn prev(&mut self) -> Option; fn get(&self) -> Option; fn seek_forward(&mut self, n: usize) -> Option; fn seek_backward(&mut self, n: usize) -> Option; } /// A cursor for an intrusive linked list. /// /// A cursor functions similarly to an iterator, except that it can seek back /// and forth rather than just advancing through the list, and it can mutate /// the element "under" the cursor. /// /// A cursor begins before the first element in the list, and once it has been /// advanced past the last element of the list, it "loops around" back to the /// first element. // TODO: can we implement `Iterator` for cursors? pub struct ListCursorMut<'a, T, N> where T: OwnedRef , T: 'a , N: Node , N: 'a { list: &'a mut List , current: RawLink } impl<'a, T, N> ListCursorMut<'a, T, N> where T: OwnedRef , T: 'a , N: Node , N: 'a { /// Advances the cursor to the next element and borrows it mutably. /// /// If the cursor is at the end of the list, this advances it back to the /// first element. /// /// # Returns /// - `Some(&mut N)` if the list is not empty /// - `None` if the list is empty pub fn next(&mut self) -> Option<&mut N> { unsafe { match self.current.take().resolve_mut() { // The cursor has no current element, so we are sitting in the // cursor's start position. The next element should be the head // of the list... None => self.list.head.resolve_mut() .and_then(|head| { // if we resolved a head element, make it the // current element and return a reference to it self.current = RawLink::some(head); self.current.resolve_mut() }) // The cursor did have a current element, so try to advance // to that item's next element , Some(thing) => { // Set the current element under the cursor to either // the element after the old current, or None if this is // the last element. self.current = match thing.next_mut().resolve_mut() { None => RawLink::none() , Some(other_thing) => RawLink::some(other_thing) }; // and return it self.current.resolve_mut() } } } } /// Steps back the cursor to the previous element and borrows it mutably. /// /// # Returns /// - `Some(&mut N)` if the list is not empty /// - `None` if the list is empty pub fn prev(&mut self) -> Option<&mut N> { unimplemented!() } /// Borrows the next element in the list without advancing the cursor. /// /// If the cursor is at the end of the list, this returns the first element /// instead. /// /// # Returns /// - `Some(&N)` if the list is not empty /// - `None` if the list is empty pub fn peek_next(&self) -> Option<&N> { unsafe { self.current.resolve() .map_or( self.list.front() , |curr| curr.next().resolve()) } } /// Borrows the previous element without stepping back the cursor. /// /// If the cursor is at the head of the list, this returns `None`. /// /// # Returns /// - `Some(&N)` if the list is not empty /// - `None` if the list is empty or if the cursor is at the head /// of the list pub fn peek_prev(&self) -> Option<&N> { unsafe { self.current.resolve() .and_then(|curr| curr.prev().resolve()) } } /// Removes the element currently under the cursor and returns it. /// /// # Returns /// - `Some(T)` if the there is an element currently under the cursor /// (i.e., the list is not empty) /// - `None` if the list is empty. pub fn remove(&mut self) -> Option { unsafe { match self.current.resolve_mut() { None => self.list.pop_front() , Some(c) => c.next_mut().take().resolve_mut() .map(|p| { match p.next_mut().resolve_mut() { None => self.list.tail = RawLink::some(c) , Some(n) => { *n.prev_mut() = RawLink::some(c); *c.next_mut() = RawLink::some(n); } } T::from_raw(p) }) } } } /// Searches for and removes the first element matching a predicate. /// /// # Arguments /// - `p`: A predicate (function of the form `&N -> bool`) that returns /// true if the element should be removed and false if it should not. /// /// # Returns /// - `Some(T)` if an element matching the predicate was found /// - `None` if no elements matched the predicate (or if the list is /// empty.) pub fn find_and_remove

(&mut self, predicate: P) -> Option where P: Fn(&N) -> bool { while self.peek_next().is_some() { if predicate(self.peek_next().unwrap()) == true { return self.remove() } else { self.next(); } } None } /// Advances the cursor `n` elements and mutably borrows the final element. /// /// This will wrap the cursor around the list if `n` > the length of /// the list. pub fn seek_forward(&mut self, n: usize) -> Option<&mut N> { for _ in 0 .. (n - 1) { self.next(); } self.next() } /// Moves the cursor back `n` times and mutably borrows the final element. /// /// This will wrap the cursor around the list if `n` > the length of /// the list. pub fn seek_backward(&mut self, n: usize) -> Option<&mut N> { for _ in 0 .. (n - 1) { self.prev(); } self.prev() } } // impl<'a, T, N> Iterator for ListCursorMut<'a, T, N> // where T: OwnedRef // , T: 'a // , N: Node // , N: 'a { // type Item = &'a mut N; // // fn next<'b: 'a>(&'b mut self) -> Option<&'a mut N> { // self.next() // } // } // // unsafe impl OwnedRef for Unique where T: Node { // // #[inline] // fn take(self) {} // // unsafe fn from_raw(ptr: *mut T) -> Self { // Unique::new(ptr) // } // } // // unsafe impl<'a, T> OwnedRef for &'a mut T { // # // #[inline] unsafe fn from_raw(raw: *mut T) -> &'a mut T { // &mut *raw // } // // #[inline] unsafe fn take(self) { // forget(self); // } // } // unsafe impl OwnedRef for Unique { #[inline] fn get(&self) -> &T { unsafe { self.as_ref() } } #[inline] fn get_mut(&mut self) -> &mut T { unsafe { self.as_mut() } } #[inline] unsafe fn take(self) {} unsafe fn from_raw(ptr: *mut T) -> Self { Unique::new(ptr) // TODO: probably don't panic here. .expect("null pointer passed to OwnedRef::from_raw!") } } #[cfg(any(test, feature = "use-std"))] unsafe impl OwnedRef for ::std::boxed::Box { fn get(&self) -> &T { &**self } fn get_mut(&mut self) -> &mut T { &mut **self } #[inline] unsafe fn take(self) { ::std::boxed::Box::into_raw(self); } unsafe fn from_raw(ptr: *mut T) -> Self { ::std::boxed::Box::from_raw(ptr) } } ================================================ FILE: sos_intrusive/src/list/test.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // use list::Node; use rawlink::RawLink; #[derive(Debug)] pub struct NumberedNode { pub number: usize, prev: RawLink, next: RawLink, } impl NumberedNode { pub fn new(number: usize) -> Self { NumberedNode { number: number, prev: RawLink::none(), next: RawLink::none(), } } } impl Node for NumberedNode { fn prev(&self) -> &RawLink { &self.prev } fn next(&self) -> &RawLink { &self.next } fn prev_mut(&mut self) -> &mut RawLink { &mut self.prev } fn next_mut(&mut self) -> &mut RawLink { &mut self.next } } impl PartialEq for NumberedNode { fn eq(&self, rhs: &Self) -> bool { self.number == rhs.number } } mod boxed { use std::boxed::Box; use list::List; use super::*; type TestList = List, NumberedNode>; #[test] fn not_empty_after_push() { let mut list = TestList::new(); assert_eq!(list.front(), None); assert_eq!(list.back(), None); assert!(list.is_empty()); list.push_front(box NumberedNode::new(1)); assert!(!list.is_empty()); } #[test] fn contents_after_first_push() { let mut list = TestList::new(); list.push_front(box NumberedNode::new(1)); assert_eq!(list.front().unwrap().number, 1); } #[test] fn head_tail_same_first_push() { let mut list = TestList::new(); list.push_front(box NumberedNode::new(1)); assert_eq!(list.front().unwrap().number, 1); assert_eq!(list.back().unwrap().number, 1); assert_eq!(list.front().unwrap(), list.back().unwrap()); } #[test] fn head_tail_not_same_second_push() { let mut list = TestList::new(); list.push_front(box NumberedNode::new(0)); list.push_front(box NumberedNode::new(1)); assert!(list.front().unwrap() != list.back().unwrap()); } #[test] fn contents_after_pushes() { let mut list = TestList::new(); list.push_front(box NumberedNode::new(0)); list.push_front(box NumberedNode::new(1)); assert_eq!(list.back().unwrap().number, 0); assert_eq!(list.front().unwrap().number, 1); list.push_back(box NumberedNode::new(2)); assert_eq!(list.back().unwrap().number, 2); assert_eq!(list.front().unwrap().number, 1); list.push_back(box NumberedNode::new(3)); assert_eq!(list.back().unwrap().number, 3); assert_eq!(list.front().unwrap().number, 1); assert!(!list.is_empty()); } #[test] fn test_pop_front() { let mut list = TestList::new(); assert_eq!(list.front(), None); assert_eq!(list.back(), None); assert!(list.is_empty()); list.push_front(Box::new(NumberedNode::new(2))); assert!(!list.is_empty()); assert_eq!(list.front(), list.back()); list.push_front(Box::new(NumberedNode::new(1))); list.push_front(Box::new(NumberedNode::new(0))); assert_eq!(list.front().unwrap().number, 0); assert_eq!(list.back().unwrap().number, 2); list.push_back(Box::new(NumberedNode::new(3))); assert_eq!(list.back().unwrap().number, 3); list.push_back(Box::new(NumberedNode::new(4))); assert_eq!(list.back().unwrap().number, 4); assert!(!list.is_empty()); assert_eq!(list.pop_front().unwrap().number, 0); assert_eq!(list.pop_front().unwrap().number, 1); assert_eq!(list.pop_front().unwrap().number, 2); assert_eq!(list.pop_front().unwrap().number, 3); assert_eq!(list.pop_front().unwrap().number, 4); assert!(list.is_empty()); assert_eq!(list.pop_front(), None); } #[test] fn test_pop_back() { let mut list = TestList::new(); assert_eq!(list.front(), None); assert_eq!(list.back(), None); assert!(list.is_empty()); list.push_front(Box::new(NumberedNode::new(2))); assert!(!list.is_empty()); assert_eq!(list.front(), list.back()); list.push_front(Box::new(NumberedNode::new(1))); list.push_front(Box::new(NumberedNode::new(0))); assert_eq!(list.front().unwrap().number, 0); assert_eq!(list.back().unwrap().number, 2); list.push_back(Box::new(NumberedNode::new(3))); assert_eq!(list.back().unwrap().number, 3); list.push_back(Box::new(NumberedNode::new(4))); assert_eq!(list.back().unwrap().number, 4); assert!(!list.is_empty()); assert_eq!(list.pop_back().unwrap().number, 4); assert_eq!(list.pop_back().unwrap().number, 3); assert_eq!(list.pop_back().unwrap().number, 2); assert_eq!(list.pop_back().unwrap().number, 1); assert_eq!(list.pop_back().unwrap().number, 0); assert!(list.is_empty()); assert_eq!(list.pop_back(), None); } } // mod mut_ptr { // use list::List; // use super::*; // // type TestList<'a> = List<&'a mut NumberedNode, NumberedNode>; // // #[test] // fn not_empty_after_push() { // let mut list = TestList::new(); // // assert_eq!(list.front(), None); // assert_eq!(list.back(), None); // // assert!(list.is_empty()); // // list.push_front(&mut NumberedNode::new(1)); // // assert!(!list.is_empty()); // } // // #[test] // fn contents_after_first_push() { // let mut list = TestList::new(); // // list.push_front(&mut NumberedNode::new(1)); // // assert_eq!(list.front().unwrap().number, 1); // } // // // #[test] // fn head_tail_same_first_push() { // let mut list = TestList::new(); // // list.push_front(&mut NumberedNode::new(1)); // // assert_eq!(list.front().unwrap().number, 1); // assert_eq!(list.back().unwrap().number, 1); // assert_eq!(list.front().unwrap(), list.back().unwrap()); // } // // #[test] // fn head_tail_not_same_second_push() { // let mut list = TestList::new(); // // list.push_front(&mut NumberedNode::new(0)); // list.push_front(&mut NumberedNode::new(1)); // // assert!(list.front().unwrap() != list.back().unwrap()); // } // // // #[test] // fn contents_after_pushes() { // let mut list = TestList::new(); // // list.push_front(&mut NumberedNode::new(0)); // list.push_front(&mut NumberedNode::new(1)); // // assert_eq!(list.back().unwrap().number, 0); // assert_eq!(list.front().unwrap().number, 1); // // list.push_back(&mut NumberedNode::new(2)); // assert_eq!(list.back().unwrap().number, 2); // assert_eq!(list.front().unwrap().number, 1); // // list.push_back(&mut NumberedNode::new(3)); // assert_eq!(list.back().unwrap().number, 3); // assert_eq!(list.front().unwrap().number, 1); // // assert!(!list.is_empty()); // } // // } ================================================ FILE: sos_intrusive/src/rawlink.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Implementation of the `RawLink` smart-ish pointer. //! //! A `RawLink` is a zero-cost abstraction that allows a raw pointer to be used //! with an `Option`-esque API. //! //! TODO: implement all monadic operations over `Option`-esque types (i.e. //! `map()`, `and_then()`, etc). use core::ptr; use core::fmt; use core::mem; /// A `RawLink` provides an `Option`-like interface to a raw pointer. #[derive(Debug, Copy, Clone, PartialEq)] pub struct RawLink(*mut T); unsafe impl Send for RawLink where T: 'static , T: Send {} unsafe impl Sync for RawLink where T: Send , T: Sync {} impl Default for RawLink { fn default() -> Self { Self::none() } } impl fmt::Display for RawLink where T: fmt::Display { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.0.is_null() { write!(f, "RawLink::none") } else { unsafe { write!(f, "RawLink::some({})", *self.0) } } } } impl RawLink { /// Equivalent of `Option::None` for a `RawLink` /// /// # Returns /// - A `RawLink` wrapping a null pointer #[inline] pub const fn none() -> RawLink { RawLink(ptr::null_mut()) } /// Equivalent of `Option::Some` for a `RawLink` /// /// # Returns /// - A `RawLink` wrapping a pointer to the specified value #[inline] pub fn some(thing: &mut T) -> RawLink { RawLink(thing) } pub const fn from_raw(ptr: *mut T) -> RawLink { RawLink(ptr) } /// Resolve the `RawLink` to an `Option` /// /// # Returns /// - `Some<&'a T>` if the `RawLink` is not a null pointer /// - `None` if the `RawLink` is a null pointer /// /// # Unsafe due to /// - Returning a reference with an arbitrary lifetime /// - Dereferencing a raw pointer #[inline] pub unsafe fn resolve<'a>(&self) -> Option<&'a T> { self.0.as_ref() } /// Resolve the `RawLink` to an `Option` on a mutable pointer /// /// # Returns /// - `Some<&'a mut T>` if the `RawLink` is not a null pointer /// - `None` if the `RawLink` is a null pointer /// /// # Unsafe due to /// - Returning a reference with an arbitrary lifetime /// - Dereferencing a raw pointer #[inline] pub unsafe fn resolve_mut<'a>(&self) -> Option<&'a mut T> { self.0.as_mut() } /// Please don't do this. /// /// # Returns /// - The underlying raw pointer (`*mut T`) behind this `RawLink` /// /// # Unsafe due to /// - Returning a reference with an arbitrary lifetime /// - Dereferencing a raw pointer /// - Please don't do this #[inline] pub unsafe fn as_raw(&self) -> *mut T { self.0 } #[inline] pub fn is_some(&self) -> bool { !self.is_none() } #[inline] pub fn is_none(&self) -> bool { self.0.is_null() } /// Returns the `RawLink` and replaces it with `RawLink::none()`. #[inline] pub fn take(&mut self) -> Self { mem::replace(self, Self::none()) } pub unsafe fn map U>(self, _f: F) -> RawLink { unimplemented!() } } // // impl ops::Deref for RawLink { // type Target = T; // // fn deref(&self) -> &Self::Target { unsafe { self.resolve().unwrap() } } // } ================================================ FILE: sos_intrusive/src/stack/mod.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! An intrusive singly-linked list implementation using `RawLink`s. //! //! An _intrusive_ list is a list structure wherein the type of element stored //! in the list holds references to other nodes. This means that we don't have //! to store a separate node data type that holds the stored elements and //! pointers to other nodes, reducing the amount of memory allocated. We can //! use intrusive lists in code that runs without the kernel memory allocator, //! like the allocator implementation itself, since each list element manages //! its own memory. use ::{RawLink, OwnedRef}; use core::marker::PhantomData; use core::iter; #[cfg(test)] mod test; /// This trait defines a node in an intrusive list. /// /// A Node must be capable of providing mutable and immutable references to /// the next node in the stack pub trait Node: Sized { fn next(&self) -> &RawLink; fn next_mut(&mut self) -> &mut RawLink; } /// The `Stack` struct is our way of interacting with an intrusive list. /// /// It stores a pointer to the head of the stack, the length of the /// list, and a `PhantomData` marker for the list's `OwnedRef` type. It /// provides the methods for pushing, popping, and indexing the list. pub struct Stack where T: OwnedRef , N: Node { head: RawLink , _ty_marker: PhantomData , length: usize } impl Stack where T: OwnedRef , N: Node { /// Construct a new `Stack` with zero elements pub const fn new() -> Self { Stack { head: RawLink::none() , _ty_marker: PhantomData , length: 0 } } /// Returns the length of the list #[inline] pub fn len(&self) -> usize { self.length } /// Borrows the first element of the list as an `Option` /// /// # Returns /// - `Some(&N)` if the list has elements /// - `None` if the list is empty. #[inline] pub fn peek(&self) -> Option<&N> { unsafe { self.head.resolve() } } /// Mutably borrows the first element of the list as an `Option` /// /// # Returns /// - `Some(&mut N)` if the list has elements /// - `None` if the list is empty. #[inline] pub fn peek_mut(&mut self) -> Option<&mut N> { unsafe { self.head.resolve_mut() } } /// Returns true if the list is empty. #[inline] pub fn is_empty(&self) -> bool { self.head.is_none() } /// Push an element to the front of the stack pub fn push(&mut self, mut item: T) { // set the pushed item to point to the head element of the stack *item.get_mut().next_mut() = self.head.take(); // then, set this node's head pointer to point to the pushed item self.head = RawLink::some(item.get_mut()); unsafe { item.take(); }; self.length += 1; } /// Removes and returns the element at the front of the list. /// /// # Returns /// - `Some(T)` containing the element at the front of the list if the /// list is not empty /// - `None` if the list is empty pub fn pop(&mut self) -> Option { unsafe { self.head.take().resolve_mut() .map(|head| { if let Some(next) = head.next_mut().resolve_mut() { self.head = RawLink::some(next); } self.length -= 1; T::from_raw(head) }) } } } impl iter::FromIterator for Stack where T: OwnedRef , N: Node { fn from_iter>(iterator: I) -> Self { let mut list: Self = Stack::new(); for item in iterator { list.push(item) } list } } ================================================ FILE: sos_intrusive/src/stack/test.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // use stack::Node; use rawlink::RawLink; #[derive(Debug)] pub struct NumberedNode { pub number: usize, next: RawLink, } impl NumberedNode { pub fn new(number: usize) -> Self { NumberedNode { number: number, next: RawLink::none(), } } } impl Node for NumberedNode { fn next(&self) -> &RawLink { &self.next } fn next_mut(&mut self) -> &mut RawLink { &mut self.next } } impl PartialEq for NumberedNode { fn eq(&self, rhs: &Self) -> bool { self.number == rhs.number } } mod boxed { use std::boxed::Box; use super::super::Stack; use super::*; type TestStack = Stack, NumberedNode>; #[test] fn not_empty_after_push() { let mut list = TestStack::new(); assert_eq!(list.peek(), None); assert!(list.is_empty()); list.push(box NumberedNode::new(1)); assert!(!list.is_empty()); } #[test] fn contents_after_first_push() { let mut list = TestStack::new(); list.push(box NumberedNode::new(1)); assert_eq!(list.peek().unwrap().number, 1); } #[test] fn contents_after_pushes() { let mut list = TestStack::new(); list.push(box NumberedNode::new(0)); assert_eq!(list.peek().unwrap().number, 0); list.push(box NumberedNode::new(1)); assert_eq!(list.peek().unwrap().number, 1); assert!(!list.is_empty()); } #[test] fn test_pop_front() { let mut list = TestStack::new(); assert_eq!(list.peek(), None); assert!(list.is_empty()); list.push(Box::new(NumberedNode::new(4))); assert!(!list.is_empty()); assert_eq!(list.peek().unwrap().number, 4); list.push(Box::new(NumberedNode::new(3))); assert!(!list.is_empty()); assert_eq!(list.peek().unwrap().number, 3); list.push(Box::new(NumberedNode::new(2))); assert!(!list.is_empty()); assert_eq!(list.peek().unwrap().number, 2); list.push(Box::new(NumberedNode::new(1))); assert!(!list.is_empty()); assert_eq!(list.peek().unwrap().number, 1); list.push(Box::new(NumberedNode::new(0))); assert!(!list.is_empty()); assert_eq!(list.peek().unwrap().number, 0); assert_eq!(list.pop().unwrap().number, 0); assert_eq!(list.pop().unwrap().number, 1); assert_eq!(list.pop().unwrap().number, 2); assert_eq!(list.pop().unwrap().number, 3); assert_eq!(list.pop().unwrap().number, 4); assert!(list.is_empty()); assert_eq!(list.pop(), None); } } ================================================ FILE: src/arch/README.md ================================================ Architecture-specific implementation: + `arch/x86` contains implementation for x86 32-bit protected mode (not yet implemented) + `arch/x86_64` contains implementation for x86 64-bit long mode CPUs + `arch/x86_all` contains common code for all x86 architectures ================================================ FILE: src/arch/mod.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Architecture-specific implementation. //! //! This module consists of a number of modules containing //! architecture-specific code for each targeted architecture. The `arch` //! module uses conditional compilation to re-export the implementation for //! which the kernel is currently being compiled. //! //! In order for the rest of the kernel to work properly, an //! architecture-specific implementation module should define a number of //! specific items. If these are not defined, the platform-independant kernel //! implementation cannot function properly. //! //! Please note that currently only the architecture-specific implementation //! for `x86_64` (long mode) is implemented. The `armv7` and `x86` (protected //! mode) modules are currently much less complete. // 64-bit x86_64 (long mode) #[cfg(target_arch="x86_64")] mod x86_64; #[cfg(target_arch="x86_64")] pub use self::x86_64::*; // 32-bit x86 (protected mode) // TODO: NYI #[cfg(target_arch = "x86")] mod x86; #[cfg(target_arch = "x86")] pub use self::x86::*; // ARM v7 // TODO: NYI #[cfg(target_arch = "armv7")] mod armv7; #[cfg(target_arch = "armv7")] pub use self::x86::*; ================================================ FILE: src/arch/x86_64/boot.asm ================================================ %define PAGE_TABLE_SIZE 512 * 8 %define PAGE_SIZE 4096 ; page_map: macro to map the first entry in the first argument to the second %macro page_map 2 mov eax, %2 or eax, 0b11 ; present + writable mov [%1], eax %endmacro %macro export 1 global %1 %1: %endmacro global start extern arch_init section .text bits 32 ; == start the kernel ======================================================== ; this is the beginning of our boot process called by GRUB. start: ; 0. Move the stack pointer to the top of the stack. --------------------- mov esp, stack_top ; 1. Move Multiboot info pointer to edi ---------------------------------- mov edi, ebx ; 2. Make sure that the system supports SOS. ----------------------------- call is_multiboot ; check that multiboot is supported call is_cpuid ; check CPUID is supported (to check for long mode) call is_long_mode ; check if long mode (64-bit) is available. ; 3. if everything is okay, create the page tables and start long mode call create_page_tables call set_long_mode ; 4. load the 64-bit GDT lgdt [gdt64.ptr] ; 5. update selectors mov ax, 16 mov ss, ax ; stack selector mov ds, ax ; data selector mov es, ax ; extra selector ; 6. print `OK` to screen and jump to the 64-bit boot subroutine. mov dword [0xb8000], 0x2f4b2f4f jmp gdt64.code:arch_init ; == Tests whether or not multiboot is enabled ============================== is_multiboot: cmp eax, 0x36d76289 jne .no_multiboot ret .no_multiboot: mov al, "0" jmp err ; == Tests whether or not long mode is available ============================== ; If long mode is not available, die (we are a long mode OS). is_long_mode: mov eax, 0x80000000 ; Set the A-register to 0x80000000. cpuid ; CPU identification. cmp eax, 0x80000001 ; Compare the A-register with 0x80000001. jb .no_long_mode ; It is less, there is no long mode. mov eax, 0x80000001 ; Set the A-register to 0x80000001. cpuid ; Do the CPUID thing once more. test edx, 1 << 29 ; Test if the LM-bit, (bit 29), is set in edx. jz .no_long_mode ; If it isn't, there is no long mode, ret ; and we are left with only the void for company. .no_long_mode: mov al, "2" jmp err ; == Tests wether or not CPUID is available ================================== ; If the system does not support CPUID, we cannot boot, since we need to use ; CPUID to check if we can switch to 64-bit long mode is_cpuid: pushfd ; Store the FLAGS-register. pop eax ; Restore the A-register. mov ecx, eax ; Set the C-register to the A-register. xor eax, 1 << 21 ; Flip the ID-bit, which is bit 21. push eax ; Store the A-register. popfd ; Restore the FLAGS-register. pushfd ; Store the FLAGS-register. pop eax ; Restore the A-register. push ecx ; Store the C-register. popfd ; Restore the FLAGS-register. xor eax, ecx ; Do a XOR A-register andC-register. jz .no_cpuid ; The zero flag is set, no CPUID. ret ; CPUID is available for use. .no_cpuid: mov al, "1" jmp err ; == Prints a boot error code to the VGA buffer ============================== err: mov dword [0xb8000], 0x4f524f45 mov byte [0xb8004], al hlt ; == Creates the page tables ================================================= ; Map the following: ; - the first PML4 entry -> PDP ; - the first PDP entry -> PD ; - each PD entry to its own 2mB page create_page_tables: ; recursive map last entry in PML4 --------------------------------------- mov eax, pml4_table or eax, 0b11 mov [pml4_table + 511 * 8], eax page_map pml4_table, pdp_table ; map first PML4 entry to PDP table page_map pdp_table, pd_table ; map first PDP entry to PD table ; map each PD table entry to its own 2mB page mov ecx, 0 .pd_table_map: ; maps the PD table ----------------------------------------- mov eax, 0x200000 ; 2 mB mul ecx ; times the start address of the page or eax, 0b10000011 ; check if present + writable + huge mov [pd_table + ecx * 8], eax ; map nth entry from pd -> own page ; increment counter and check if done inc ecx cmp ecx, 512 jne .pd_table_map ret ; == Sets long mode and enables paging ======================================= ; In order to do this, we must first create the initial page tables. set_long_mode: ; load PML4 addr to cr3 register ----------------------------------------- mov eax, pml4_table mov cr3, eax ; enable PAE-flag in cr4 (Physical Address Extension) -------------------- mov eax, cr4 or eax, 1 << 5 mov cr4, eax ; set the long mode bit in the EFER MSR (model specific register) -------- mov ecx, 0xC0000080 rdmsr or eax, 1 << 8 wrmsr ; enable paging in the cr0 register ------------------------------------- mov eax, cr0 or eax, 1 << 31 or eax, 1 << 16 mov cr0, eax ret section .bss align 4096 ; == page tables ============================================================= ; Page-Map Level-4 Table pml4_table: resb PAGE_TABLE_SIZE ; Page Directory Pointer Table pdp_table: resb PAGE_TABLE_SIZE ; Page-Directory Table pd_table: resb PAGE_TABLE_SIZE ; Page Table page_table: resb PAGE_TABLE_SIZE ; == kernel stack ============================================================= stack_base: resb PAGE_SIZE * 4 ; reserve 2 pages for the kernel stack ; for some unspeakable reason, doubling the kernel stack size ; magically fixes all of the memory allocator bugs? i suspect ; the Malloc Gods interpret the extra stack space as a ; sacrifice. my mind grows weary of this treatchery. ; 𐅃 𐅐𐆂𐅛𐅜𐅀𐅂𐅲𐅯𐅊𐅭𐅙 𐅗 𐅏 𐅽𐅆 𐅲𐆇𐅿𐅚𐆁𐅐𐅶𐅬𐅯𐅴𐅮𐅼 𐅊𐅦 𐅒𐅉 𐅻𐅷𐅘 𐅊𐅗 𐅤𐆁𐅛𐅒𐅎𐅅𐅨𐅓𐅵𐅯𐅺𐅐𐆀 ; 𐅵𐅿 𐅘 𐅈𐅘𐅁 𐅫 𐅟𐅸 𐅥𐅣𐅑𐅼𐅷𐅻𐆁 𐆊 𐆉𐆇𐆅𐅐 𐅦𐅕 𐅢𐅷𐅗𐅤𐅧 𐅣𐅖𐅺 𐅁𐅿𐅩𐅣 𐅥 𐆄𐅱𐅕 𐅈 𐅙𐅀 𐅋 ; 𐅩𐅿𐅋𐅫𐅌𐆆𐅊𐆇 𐅜𐅦𐅲 𐅷 𐅱𐆁𐅓𐅞 stack_top: ; == kernel heap ============================================================= heap_base: resb 4 * 1024 * 1024 ; reserved space for the kernel heap heap_top: section .rodata export gdt64 dq 0 ; zero entry .code: equ $ - gdt64 ; new dq (1<<44) | (1<<47) | (1<<41) | (1<<43) | (1<<53) ; code segment .data: equ $ - gdt64 ; new dq (1<<44) | (1<<47) | (1<<41) ; data segment .ptr: dw $ - gdt64 - 1 dq gdt64 ; GDT offset location exported to Rust kernel export gdt64_offset dq gdt64.code export stack_base_addr dq stack_base export stack_top_addr dq stack_top export heap_base_addr dq heap_base export heap_top_addr dq heap_top export pml4_table_addr dq pml4_table ================================================ FILE: src/arch/x86_64/drivers/mod.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2016 Eliza eisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // pub mod serial; pub mod vga; ================================================ FILE: src/arch/x86_64/drivers/serial.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Serial port driver //! //! Create new serial ports by calling `PortNum::new()`. If the system supports //! that port, `Some(Port)` will be returned. Otherwise, if the system //! does not have that port number (such as `COM3` and `COM4`) on many machines, //! `PortNum::new()` returns `None`. //! //! See [the OS Dev wiki](http://wiki.osdev.org/Serial_Ports) for more //! information. use spin::Mutex; use core::fmt; use ::arch::bda; use cpu::Port; // use ::io; use util::{io, Void}; // // /// Address of the BIOS Data Area (BDA) // /// where the serial port addresses are stored. // const BDA_ADDR: usize = 0x400; // pub struct Serial(Option); impl fmt::Write for Serial { #[inline] fn write_str(&mut self, s: &str) -> fmt::Result { if let Some(ref mut p) = self.0 { p.write_str(s) } else { Err(fmt::Error) } } } lazy_static! { // static ref BDA_SERIAL_INFO: [u16; 4] // = unsafe { *(BDA_ADDR as *const [u16; 4]) }; // TODO: serial ports are currently behind a mutex; can they be read-write // locked instead? I think multiple threads should be able to read // from a serial port at the same time without causing trouble? // - eliza, 10/9/2016 pub static ref COM1: Mutex = Mutex::new(Serial(bda::ports::com1().map(SerialPort::new))); pub static ref COM2: Mutex = Mutex::new(Serial(bda::ports::com2().map(SerialPort::new))); pub static ref COM3: Mutex = Mutex::new(Serial(bda::ports::com3().map(SerialPort::new))); pub static ref COM4: Mutex = Mutex::new(Serial(bda::ports::com4().map(SerialPort::new))); } /// A serial port pub struct SerialPort { data_port: Port , status_port: Port } impl SerialPort { fn new(port: u16) -> SerialPort { // Disable all interrupts Port::::new(port + 1).write(0x00); // Enable DLAB (set baud rate divisor) Port::::new(port + 3).write(0x80); // Set divisor to 38400 baud Port::::new(port + 0).write(0x03); // divisor hi byte Port::::new(port + 1).write(0x00); // divisor lo byte // 8 bits, no parity, one stop bit Port::::new(port + 3).write(0x03); // Enable FIFO, clear them, with 14-byte threshold Port::::new(port + 2).write(0xC7); // IRQs enabled, RTS/DSR set Port::::new(port + 4).write(0x0B); Port::::new(port + 1).write(0x01); SerialPort { data_port: Port::::new(port) , status_port: Port::::new(port + 5) } } /// Returns true if the serial port has recieved data #[inline] pub fn has_byte(&self) -> bool { self.status_port.read() & 1 != 0 } #[inline] pub fn is_empty(&self) -> bool { self.status_port.read() & 0x20 == 0x20 } #[inline] pub fn read_byte(&self) -> u8 { while !self.has_byte() {}; self.data_port.read() } #[inline] pub fn write_byte(&self, byte: u8) { while !self.is_empty() {}; self.data_port.write(byte) } } impl io::Read for SerialPort { type Error = Void; /// Reads a single byte into the given buffer fn read(&mut self, buf: &mut [u8]) -> Result { Ok(match &mut *buf { // if the length of the buffer is 0, then obviously // no bytes were read &mut [] => 0 // otherwise, read one byte into the head of the buffer , &mut [ref mut head, _..] => { *head = self.read_byte(); 1 } }) } /// Reads a new byte into each position in the buffer. fn read_all(&mut self, buf: &mut [u8]) -> Result { let mut read_bytes = 0; for idx in buf.iter_mut() { // for each index in the buffer, read another byte from the port *idx = self.read_byte(); // and increment the number of bytes read (this should be faster // than calling `buf.len()` later; as we only need 1 loop) read_bytes += 1; } Ok(read_bytes) } } impl io::Write for SerialPort { type Error = Void; fn write(&mut self, buf: &[u8]) -> Result { let mut written_bytes = 0; for byte in buf { // write each byte in the buffer to the port self.write_byte(*byte); // and increment the number of bytes written (this should be faster // than calling `buf.len()` later; as we only need 1 loop) written_bytes += 1; } Ok(written_bytes) } } impl fmt::Write for SerialPort { fn write_str(&mut self, s: &str) -> fmt::Result { for byte in s.bytes() { // TODO: more robust error handling here self.write_byte(byte); } Ok(()) } } ================================================ FILE: src/arch/x86_64/drivers/vga.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Arch-specific VGA port port driver use vga::{Palette, Color, Terminal}; use spin::Mutex; // extern { // #[link_section = ".__vga_buffer"] // static mut BUFFER: vga::Buffer; // } /// The system's global VGA terminal pub static CONSOLE: Mutex = Mutex::new(unsafe { Terminal::new( Palette::new(Color::LightGrey, Color::Black ) , 0x8000 )}); pub fn clear() { CONSOLE.lock().clear(); } ================================================ FILE: src/arch/x86_64/grub.cfg ================================================ set timeout=0 set default=0 menuentry "sos" { multiboot2 /boot/sos_kernel.bin boot } ================================================ FILE: src/arch/x86_64/interrupts.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // use cpu::interrupts::pics; use cpu::interrupts::idt::{Gate, Idt}; use cpu::context::InterruptFrame; use cpu::dtable::DTable; //==--------------------------------------------------------------------------== // Top-level interrupt handling /// Initialize interrupt handling. /// /// This function initializes the PICs, populates the IDT with interrupt /// handlers, loads the IDT pointer, and enables interrupts. /// /// This is called from the kernel during the init process. // TODO: make the result returned by this meaningful? #[inline] pub unsafe fn initialize() -> Result<(), ()>{ pics::initialize(); // TODO: consider loading double-fault handler before anything else in case // a double fault occurs during init? IDT.load(); // Load the IDT pointer // // debug!("Testing interrupt handling"); // asm!("int $0" :: "N" (0xff)); Idt::enable_interrupts(); // enable interrupts Ok(()) } macro_rules! exception_inner { ($title:expr, $kind:expr, $source:expr, $f:expr) => { use vga::{CONSOLE, Color}; use core::fmt::Write; let _ = write!( CONSOLE.lock() .set_colors(Color::White, Color::Blue) , "EVERYTHING IS FINE: {}{} at {:p}\n\ Source: {}.\nThis is fine.\n\n\ {:?}" , $title, $kind , (*$f).rip , $source , *$f); }; ($title:expr, $kind:expr, $source:expr, $f:expr, $e:expr) => { use vga::{CONSOLE, Color}; use core::fmt::Write; let _ = write!( CONSOLE.lock() .set_colors(Color::White, Color::Blue) , "EVERYTHING IS FINE: {}{} at {:p}\n\ Source: {}.\n Error code: {:x}\nThis is fine.\n\n\ {:?}" , $title, $kind , (*$f).rip , $source , $e , *$f); }; } macro_rules! exceptions { ( fault: $name:ident, $title:expr, $source:expr, $($tail:tt)* ) => { #[doc=$title] extern "x86-interrupt" fn $name(frame: &InterruptFrame) { exception_inner! ($title, "Fault", $source, frame); loop {} } exceptions! { $($tail)* } }; ( fault (code): $name:ident, $title:expr, $source:expr, $($tail:tt)* ) => { #[doc=$title] extern "x86-interrupt" fn $name( frame: &InterruptFrame , error_code: usize) { exception_inner! ($title, "Fault", $source, frame, error_code); loop {} } exceptions! { $($tail)* } }; ( trap: $name:ident, $title:expr, $source:expr, $($tail:tt)* ) => { #[doc=$title] extern "x86-interrupt" fn $name(frame: &InterruptFrame) { exception_inner! ($title, "Trap", $source, frame); } exceptions! { $($tail)* } }; ( ) => {}; } exceptions! { fault: divide_by_zero, "Divide by Zero Error", "DIV or IDIV instruction", fault: nmi, "Non-Maskable Interrupt", "Non-maskable external interrupt", trap: overflow, "Overflow", "INTO instruction", fault: bound_exceeded, "BOUND range exceeded", "BOUND instruction", fault: undefined_opcode, "Undefined Opcode", "UD2 instruction or reserved opcode", fault: device_not_available, "Device Not Available" , "Floating-point or WAIT/FWAIT instruction \ (no math coprocessor)", fault (code): double_fault, "Double Fault" , "Any instruction that can generate an exception, a NMI, or \ an INTR", fault (code): invalid_tss, "Invalid TSS" , "Task switch or TSS access", fault (code): segment_not_present, "Segment Not Present" , "Loading segment registers or accessing \ system segments", fault (code): general_protection_fault, "General Protection Fault" , "Any memory reference or other protection checks", fault (code): stack_segment_fault, "Stack Segment Fault" , "Stack operations and SS register loads", fault: floating_point_error , "x87 FPU Floating-Point Error (Math Fault)" , "x87 FPU floating-point or WAIT/FWAIT instruction", fault: machine_check, "Machine Check" , "Model-dependent (probably hardware!)", fault (code): alignment_check, "Alignment Check" , "Any data reference in memory", fault: simd_fp_exception, "SIMD Floating-Point Exception" , "SSE/SSE2/SSE3 floating-point instructions", } lazy_static! { static ref IDT: Idt = { let mut idt = Idt::new(); use cpu::interrupts::*; // TODO: log each handler as it's added to the IDT? that way we can // trace faults occurring during IDT population (if any) // - eliza, 5/22/2017 idt.divide_by_zero = Gate::from(divide_by_zero as InterruptHandler); idt.nmi = Gate::from(nmi as InterruptHandler); idt.overflow = Gate::from(overflow as InterruptHandler); idt.overflow.set_trap(); idt.bound_exceeded = Gate::from(bound_exceeded as InterruptHandler); idt.undefined_opcode = Gate::from(undefined_opcode as InterruptHandler); idt.device_not_available = Gate::from(device_not_available as InterruptHandler); idt.double_fault = Gate::from(double_fault as ErrorCodeHandler); idt.invalid_tss = Gate::from(invalid_tss as ErrorCodeHandler); idt.segment_not_present = Gate::from(segment_not_present as ErrorCodeHandler); idt.stack_segment_fault = Gate::from(stack_segment_fault as ErrorCodeHandler); idt.general_protection_fault = Gate::from(general_protection_fault as ErrorCodeHandler); idt.page_fault = Gate::from(page_fault as ErrorCodeHandler); idt.floating_point_error = Gate::from(floating_point_error as InterruptHandler); idt.alignment_check = Gate::from(alignment_check as ErrorCodeHandler); idt.machine_check = Gate::from(machine_check as InterruptHandler); idt.simd_fp_exception = Gate::from(simd_fp_exception as InterruptHandler); idt.breakpoint = Gate::from(breakpoint as InterruptHandler); idt.page_fault = Gate::from(page_fault as ErrorCodeHandler); idt.interrupts[0x20 - 32] = Gate::from(timer as InterruptHandler); idt.interrupts[0x21 - 32] = Gate::from(keyboard as InterruptHandler); idt.interrupts[0xff - 32] = Gate::from(test as InterruptHandler); kinfoln!( dots: " . . ", target: "Adding interrupt handlers to IDT" , "[ OKAY ]"); idt }; } #[no_mangle] #[inline(never)] pub extern "x86-interrupt" fn keyboard(_frame: &InterruptFrame) { use io::keyboard; // println!("keyboard happened"); if let Some(input) = keyboard::read_char() { if input == '\r' { println!(""); } else { print!("{}", input); } } // send the PICs the end interrupt signal unsafe { pics::end_pic_interrupt(0x21); } } #[no_mangle] #[inline(never)] pub extern "x86-interrupt" fn breakpoint(frame: &InterruptFrame) { println!("Breakpoint! Frame: {:#?}", frame); // send the PICs the end interrupt signal unsafe { pics::end_pic_interrupt(0x03); } } /// Empty dummy handler for undefined interrupts. #[no_mangle] #[inline(never)] pub extern "x86-interrupt" fn empty_handler(_frame: &InterruptFrame) { // TODO: it would be nice to know *which vector* the dummy interrupt // fired on, for debugging purposes... // - eliza, 05/25/2017 debug!("an empty interrupt fired!") } ================================================ FILE: src/arch/x86_64/linker.ld ================================================ /* Based on http://blog.phil-opp.com/rust-os/multiboot-kernel.html * * Used to specify a custom linking layout that puts our multiboot header * before everything else. */ ENTRY(_start) SECTIONS { /* Load the kernel reasonably high in memory to avoid special addresses. */ . = 1M; .rodata : { /* This goes first. */ KEEP(*(.multiboot_header)) *(.rodata .rodata.*) . = ALIGN(4K); } .boot : ALIGN(4K) { KEEP(*(.boot._start)) libboot.a(*) KEEP(*(.gdt)) . = ALIGN(4K); } .text : { /* NOTE we use KEEP here to prevent the linker from dropping these symbols */ KEEP(*(.text.arch_init)) *(.text .text.*) . = ALIGN(4K); } .data : { *(.data .data.*) . = ALIGN(4K); } .bss : { *(.bss .bss.*) . = ALIGN(4K); /* Page-Map Level-4 Table (PML4) */ pml4_table = .; . += 4K; /* Page-Directory Pointer Table (PDP) */ pdp_table = .; . += 4K; /* Page-Directory Table (PD) */ pd_table = .; . += 4K; . = ALIGN(4K); stack_base = .; . += 4K * 8; stack_top = .; . = ALIGN(4K); heap_base_addr = .; . += 4K * 2K; heap_top_addr = .; . = ALIGN(4K); } .got : { *(.got) . = ALIGN(4K); } .got.plt : { *(.got.plt) . = ALIGN(4K); } .data.rel.ro : ALIGN(4K) { *(.data.rel.ro.local*) *(.data.rel.ro .data.rel.ro.*) . = ALIGN(4K); } .gcc_except_table : ALIGN(4K) { *(.gcc_except_table) . = ALIGN(4K); } } ================================================ FILE: src/arch/x86_64/mod.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! `x86_64` architecture-specific implementation. // pub mod cpu; pub mod drivers; pub mod interrupts; #[path = "../x86_all/bda.rs"] pub mod bda; #[path = "../x86_all/multiboot2.rs"] pub mod multiboot2; pub const ARCH_BITS: u8 = 64; extern { // TODO: It would be really nice if there was a less ugly way of doing // this... (read: after the Revolution when we add memory regions to the // heap programmatically.) #[link_name = "heap_base_addr"] #[linkage = "external"] pub static HEAP_BASE: *mut u8; #[link_name = "heap_top_addr"] #[linkage = "external"] pub static HEAP_TOP: *mut u8; // Of course, we will still need to export the kernel stack addresses like // this, but it would be nice if they could be, i dont know, not mut u8s // pointers, like God intended. #[link_name = "stack_base"] pub static STACK_BASE: *mut u8; #[link_name = "stack_top"] pub static STACK_TOP: *mut u8; } use memory::PAddr; /// Trampoline to ensure we have a correct stack frame for calling [`arch_init`] /// /// I have no idea why this works, but it does. /// /// [`arch_init`]: fn.arch_init #[naked] #[no_mangle] pub unsafe extern "C" fn long_mode_init() { asm!("movabsq $$(stack_top), %rsp"); asm!("mov ax, 0 mov ss, ax mov ds, ax mov es, ax mov fs, ax mov gs, ax call arch_init" :::: "intel"); } /// Entry point for architecture-specific kernel init /// /// This expects to be passed the address of a valid /// Multiboot 2 info struct. It's the bootloader's responsibility to ensure /// that this is passed in the correct register as expected by the calling /// convention (`edi` on x86). If this isn't there, you can expect to have a /// bad problem and not go to space today. #[no_mangle] pub extern "C" fn arch_init(multiboot_addr: PAddr) { use cpu::{control_regs, msr}; use params::{InitParams, mem}; kinfoln!(dots: " . ", "Beginning `arch_init()` for x86_64"); ::io::term::CONSOLE.lock().clear(); ::logger::initialize() .expect("Could not initialize logger!"); // -- Unpack multiboot tag ------------------------------------------------ kinfoln!( dots: " . " , "trying to unpack multiboot info at {:?}" , multiboot_addr); // try to interpret the structure at the multiboot address as a multiboot // info struct. if it's invalid, fail. let boot_info = unsafe { multiboot2::Info::from(multiboot_addr) .expect("Could not unpack multiboot2 information!") }; // Extract ELF sections tag from the multiboot info let elf_sections_tag = boot_info.elf_sections() .expect("ELF sections tag required!"); kinfoln!(dots: " . ", "Detecting kernel ELF sections:"); // Extract kernel ELF sections from multiboot info let mut n_elf_sections = 0; let kernel_begin = elf_sections_tag.sections() // .filter(|s| s.is_allocated()) .map(|s| { kinfoln!( dots: " . . ", "{}", s ); kinfoln!( dots: " . . . ", "flags: [ {:?} ]", s.flags()); s.address() }) .min() .expect("Could not find kernel start section!\ \nSomething is deeply wrong."); let kernel_end = elf_sections_tag.sections() // .filter(|s| s.is_allocated()) .map(|s| { n_elf_sections += 1; s.end_address() }) .max() .expect("Could not find kernel end section!\ \nSomething is deeply wrong."); kinfoln!( dots: " . ", "Detected {} kernel ELF sections.", n_elf_sections); kinfoln!( dots: " . . ", "Kernel begins at {:#p} and ends at {:#p}." , kernel_begin, kernel_end ); let multiboot_end = multiboot_addr + boot_info.length as u64; kinfoln!( dots: " . . ", "Multiboot info begins at {:#x} and ends at {:#x}." , multiboot_addr, multiboot_end); let mut params = InitParams { kernel_base: kernel_begin , kernel_top: kernel_end , multiboot_start: Some(multiboot_addr) , multiboot_end: Some(multiboot_end) , heap_base: unsafe { PAddr::from(HEAP_BASE) } , heap_top: unsafe { PAddr::from(HEAP_TOP) } , stack_base: unsafe { PAddr::from(STACK_BASE) } , stack_top: unsafe { PAddr::from(STACK_TOP) } , elf_sections: Some(elf_sections_tag.sections()) , ..Default::default() }; // Extract the memory map tag from the multiboot info let mem_map = boot_info.mem_map() .expect("Memory map tag required!"); kinfoln!(dots: " . ", "Detected memory areas:"); for area in mem_map { kinfoln!( dots: " . . ", "{}", area); let a: mem::Area = area.into(); if a.is_usable == true { params.mem_map.push(a); } } //-- enable flags needed for paging ------------------------------------ unsafe { // control_regs::cr0::enable_write_protect(true); // kinfoln!(dots: " . ", "Page write protect ENABED" ); let efer = msr::read(msr::IA32_EFER); trace!("EFER = {:#x}", efer); msr::write(msr::IA32_EFER, efer | (1 << 11)); let efer = msr::read(msr::IA32_EFER); trace!("EFER = {:#x}", efer); kinfoln!(dots: " . ", "Page no execute bit ENABLED"); } kinfoln!(dots: " . ", "Transferring to `kernel_init()`."); ::kernel_init(¶ms); } ================================================ FILE: src/arch/x86_all/bda.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2016 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! BIOS Data Area //! //! see [the OS Dev Wiki] //! (http://wiki.osdev.org/Memory_Map_(x86)#BIOS_Data_Area_.28BDA.29) //! for more information. type Word = u16; pub mod ports { use super::Word; const PORTS_ADDR: usize = 0x0400; lazy_static! { /// BIOS Data Area that stores the addresses of serial and parallel ports static ref PORTS: &'static Ports = unsafe { &*(PORTS_ADDR as *const Ports) }; } unsafe impl Send for Ports {} unsafe impl Sync for Ports {} /// Addresses of ports stored in the BIOS Data Area. /// #[repr(C)] struct Ports { /// Port address of the `COM1` serial port com1: Word , /// Port address of the `COM2` serial port com2: Word , /// Port address of the `COM3` serial port com3: Word , /// Port address of the `COM4` serial port com4: Word , /// Port address of the `LPT1` parallel port lpt1: Word , /// Port address of the `LPT2` parallel port lpt2: Word , /// Port address of the `LPT3` parallel port lpt3: Word } macro_rules! port_addr { ( $($name: ident),+ ) => { $( #[inline] pub fn $name() -> Option { match PORTS.$name { 0 => None , n => Some(n) } } )+ } } port_addr! { com1, com2, com3, com4, lpt1, lpt2, lpt3 } } ================================================ FILE: src/arch/x86_all/multiboot2.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Code for reading & extracting data from Multiboot 2 boot information. //! //! Consult the [Multiboot Specification](http://nongnu.askapache.com/grub/phcoder/multiboot.pdf) //! for more information. use memory::{PAddr, PhysicalPage, FrameRange}; use elf::section::{Sections, HeaderRepr as SectionHeader}; use params::mem; use core::convert::Into; use core::iter::IntoIterator; use core::fmt; const END_TAG_LEN: u32 = 8; const HEADER_LEN: u32 = 24; pub const MAGIC: u32 = 0xe85250d6; #[repr(u32)] pub enum HeaderArch { I386 = 0 , Mips = 4 } #[repr(C)] pub struct Header { pub magic: u32 , pub arch: HeaderArch , pub header_length: u32 , pub checksum: u32 , pub end_tag: Tag } #[linkage = "external"] #[link_section = ".multiboot_header"] pub static HEADER: Header = Header { magic: MAGIC , arch: HeaderArch::I386 , header_length: HEADER_LEN , checksum: -((MAGIC + 0 + HEADER_LEN) as i32) as u32 , end_tag: Tag { ty: TagType::End , length: END_TAG_LEN } }; #[repr(C)] pub struct Info { pub length: u32 , _pad: u32 , tag_start: Tag } impl Info { #[inline] pub fn start_addr(&self) -> PAddr { PAddr::from(self as *const _ as u64) } pub fn end_addr(&self) -> PAddr { PAddr::from(self.start_addr() + self.length as u64) } /// TODO: rewrite this as a `TryFrom` implementation (see issue #85) // - eliza, 03/09/2017 pub unsafe fn from(addr: PAddr) -> Result<&'static Self, &'static str> { let info: &Info = &*(addr.into(): u64 as *const Info); // TODO: check if the multiboot tag *exists* at this location as well? // since if we pass in the wrong address, we'll still make the // "no end tag" error. // // which, i suppose is *technically* correct, but not very // helpful... // - eliza, 03/04/2017 if info.has_end() { Ok(info) } else { Err( "Multiboot info structure at {:?} had no end tag!") } } /// Finds the tag with the given tag type. /// /// This is actually safe since the tag types are constrained by The /// `TagType` enum /// /// # Returns /// - `Some(tag)` if a tag of the given type could be found. /// - `None` if no tag of the given type could be found. pub fn get_tag(&'static self, tag_type: TagType) -> Option<&'static Tag> { self.tags() .find(|t| t.ty == tag_type) } /// Finds the memory map tag. /// /// # Returns /// - `Some(MemMapTag)` if a memory map tag could be found /// - `None` if no tag of the given type could be found. #[inline] pub fn mem_map(&'static self) -> Option<&'static MemMapTag> { self.get_tag(TagType::MemoryMap) .map(|tag| unsafe { &*((tag as *const Tag) as *const MemMapTag) }) } /// Finds the ELF sections tag. /// /// # Returns /// - `Some(ElfSectionsTag)` if a memory map tag could be found /// - `None` if no tag of the given type could be found. #[inline] pub fn elf_sections(&'static self) -> Option<&'static ElfSectionsTag> { self.get_tag(TagType::ELFSections) .map(|tag| unsafe { &*((tag as *const Tag) as *const ElfSectionsTag) }) } /// Returns an iterator over all Multiboot tags. #[inline] fn tags(&'static self) -> Tags { Tags(&self.tag_start as *const Tag) } /// Returns true if the multiboot structure has a valid end tag. fn has_end(&self) -> bool { // TODO: we should be able to use ptr::offset() here? // - eliza, 03/05/2017 let end_tag_addr = self as *const _ as usize + (self.length as usize - END_TAG_LEN as usize); let end_tag = unsafe {&*(end_tag_addr as *const Tag)}; end_tag.ty == TagType::End && end_tag.length == 8 } /// Returns the kernel frame range from the Multiboot 2 ELF Sections pub fn kernel_frames(&'static self) -> Result { let sections_tag = self.elf_sections() .ok_or("ELF sections tag required!")?; let kernel_start = sections_tag.sections() .map(|s| s.address()) .min() .ok_or("Couldn't find kernel start section!")?; let kernel_end = sections_tag.sections() .map(|s| s.address()) .max() .ok_or("Couldn't find kernel end section!")?; Ok(PhysicalPage::from(kernel_start) .. PhysicalPage::from(kernel_end)) } } impl IntoIterator for &'static Info { type IntoIter = Tags; type Item = &'static Tag; #[inline] fn into_iter(self) -> Self::IntoIter { self.tags() } } /// A Multiboot tag. /// /// From the specification: /// /// Boot information consists of a fixed part and a series of tags. /// Its start is 8-bytes aligned. Fixed part is as following: /// /// /// /// +-------------------+ /// u32 | total_size | /// u32 | reserved | /// +-------------------+ /// /// /// `total_size` contains the total size of boot information including this /// field and terminating tag in bytes. /// `reserved` is always set to zero and must be ignored by OS image. /// /// Every tag begins with following fields: /// /// /// +-------------------+ /// u32 | type | /// u32 | size | /// +-------------------+ /// /// `type` contains an identifier of contents of the rest of the tag. `size` /// contains the size of tag including header fields but not including padding. /// Tags follow one another padded when necessary in order for each tag to /// start at 8-bytes aligned address. Tags are terminated by a tag of type `0` /// and size `8`. #[repr(C)] #[derive(Debug)] pub struct Tag { /// the type of this tag. pub ty: TagType , length: u32 } /// Types of Multiboot tags /// /// Refer to Chapter 3 of the Multiboot 2 spec #[repr(u32)] #[derive(Debug, Eq, PartialEq)] pub enum TagType { /// Tag that indicates the end of multiboot tags End = 0 , /// Command line passed to the bootloader CommandLine = 1 , BootloaderName = 2 , Modules = 3 , BasicMemInfo = 4 , BIOSBootDev = 5 , MemoryMap = 6 , VBEInfo = 7 , FramebufferInfo = 8 , ELFSections = 9 , APMTable = 10 } /// An iterator over Multiboot 2 tags. pub struct Tags(*const Tag); impl Tags { #[inline] fn advance(&mut self, size: u32) { let next_addr = self.0 as usize + size as usize; self.0 = (((next_addr-1) & !0x7) + 0x8) as *const _; } } impl Iterator for Tags { type Item = &'static Tag; #[inline] fn next(&mut self) -> Option { match unsafe { &*self.0 } { &Tag { ty: TagType::End, length: END_TAG_LEN } => None , tag => { self.advance(tag.length); Some(tag) } } } } /// A Memory Map tag #[repr(C)] pub struct MemMapTag { tag: Tag , pub entry_size: u32 , pub entry_version: u32 , first_entry: MemArea } impl MemMapTag { /// Returns an iterator over all the memory areas in this tag. #[inline] pub fn areas(&'static self) -> MemAreas { MemAreas { curr: (&self.first_entry) as *const MemArea // TODO: we should be able to use ptr::offset() here? // - eliza, 03/05/2017 , last: ((self as *const MemMapTag as u32) + self.tag.length - self.entry_size) as *const MemArea , size: self.entry_size } } } impl IntoIterator for &'static MemMapTag { type Item = &'static MemArea; type IntoIter = MemAreas; #[inline] fn into_iter(self) -> Self::IntoIter { self.areas() } } /// A tag that stores the boot command line. #[repr(C)] pub struct CommandLineTag { tag: Tag , /// The boot command line. /// /// The command line is a normal C-style zero- /// terminated UTF-8 string. pub command_line: [u8] } #[repr(C)] pub struct ModulesTag { tag: Tag , /// The address at which the module begins. pub mod_begin: PAddr , /// The address at which the module ends. pub mod_end: PAddr , /// A string (typically a command line) pub string: [u8] } #[repr(u32)] #[derive(Debug, Eq, PartialEq, Ord, PartialOrd)] pub enum MemAreaType { Available = 1 , Acpi = 3 , Preserve = 4 } /// A multiboot 2 memory area #[repr(C)] #[derive(Debug)] pub struct MemArea { /// the starting address of the memory area pub base: PAddr , /// the length of the memory area pub length: PAddr , /// the type of the memory area pub ty: MemAreaType , _pad: u32 } impl MemArea { #[inline] pub fn address(&self) -> PAddr { self.base + self.length - 1 } } impl<'a> Into for &'a MemArea { #[inline] fn into(self) -> mem::Area { mem::Area { start_addr: self.base , end_addr: self.address() , is_usable: if let MemAreaType::Available = self.ty { true } else { false } } } } impl fmt::Display for MemArea { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "{:?} from {:#08x} to {:#08x}" , self.ty, self.base, self.address()) } } /// An iterator over memory areas #[derive(Clone)] pub struct MemAreas { curr: *const MemArea , last: *const MemArea , size: u32 } impl Iterator for MemAreas { type Item = &'static MemArea; fn next(&mut self) -> Option<&'static MemArea> { if self.curr > self.last { None } else { let current = unsafe { &*self.curr }; // TODO: we should be able to use ptr::offset() here. // - eliza, 03/05/2017 self.curr = (self.curr as u32 + self.size) as *const MemArea; // if current.ty == MemAreaType::Available { // NOTE: this used to skip over unavailable or ACPI memory areas, // but i've disabled that as we may want to iterate over thsoe // memory areas. we can use `filter` on this iterator to get // only available memory areas. // - eliza, 03/05/2017 Some(current) // } else { // self.next() // } } } } #[cfg(target_pointer_width = "32")] pub type Word = u32; #[cfg(target_pointer_width = "64")] pub type Word = u64; /// A Multiboot 2 ELF sections tag #[derive(Debug)] #[repr(packed)] pub struct ElfSectionsTag { tag: Tag , /// the number of sections pointed to by this tag pub n_sections: u32 , /// the size of each ELF section pub section_size: u32 , stringtable_idx: u32 , first_section: SectionHeader } impl ElfSectionsTag { /// Returns an iterator over the ELF sections pointed to by this tag. // TODO: can the &'static bound be reduced to &'a? is there any reason to? // - eliza, 03/04/2017 #[inline] pub fn sections(&'static self) -> Sections<'static, Word> { Sections::new( &self.first_section , self.n_sections - 1 , self.section_size ) } } impl IntoIterator for &'static ElfSectionsTag { // TODO: can the &'static bound be reduced to &'a? is there any reason to? // - eliza, 03/04/2017 type Item = ::Item; type IntoIter = Sections<'static, Word>; #[inline] fn into_iter(self) -> Self::IntoIter { self.sections() } } ================================================ FILE: src/heap.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // use params::InitParams; /// Initialise the kernel heap. // TODO: this is the Worst Thing In The Universe. De-stupid-ify it. pub unsafe fn initialize<'a>(params: &InitParams) -> Result<&'a str, &'a str> { // let heap_base_ptr = params.heap_base.as_mut_ptr(); let heap_size: u64 = (params.heap_top - params.heap_base).into(); // buddy::system::init_heap(heap_base_ptr, heap_size as usize); Ok("[ OKAY ]") } ================================================ FILE: src/io/keyboard.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! PS/2 keyboard driver use cpu::Port; use spin::Mutex; use core::default::Default; /// PS/2 keyboard scancode #[derive(Copy,Clone,Debug)] pub struct Scancode(u8); impl Scancode { fn to_ascii(&self) -> Option { match self.0 { 0x01 ... 0x0e => Some(TO_ASCII_LOW[self.0 as usize - 0x01]) , 0x0f ... 0x1c => Some(TO_ASCII_MID1[self.0 as usize - 0x0f]) , 0x1e ... 0x28 => Some(TO_ASCII_MID2[self.0 as usize - 0x1e]) , 0x2c ... 0x35 => Some(TO_ASCII_HIGH[self.0 as usize - 0x2c]) , 0x39 => Some(b' ') , _ => None } } } /// A PS/2 keyboard state pub struct Keyboard { /// Port for reading data from the keyboard data_port: Port // , /// Port for sending control signals to the keyboard // control_port: Port , /// The keyboard's modifier keys pub state: Modifiers } impl Keyboard { #[inline] pub fn read_scancode(&self) -> Scancode { Scancode(self.data_port.read()) } } /// Scancodes range 0x01 ... 0x1c const TO_ASCII_LOW: &'static [u8; 17] = b"\x1B1234567890-=\0x02"; const TO_ASCII_MID1: &'static [u8; 14] = b"\tqwertyuiop[]\r"; /// Scancodes range 0x1E ... 0x28 const TO_ASCII_MID2: &'static [u8; 11] = b"asdfghjkl;'"; /// Scancodes range 0x2C ... 0x35 const TO_ASCII_HIGH: &'static [u8; 10] = b"zxcvbnm,./"; bitflags! { pub flags Modifiers: u8 { const L_SHIFT = 0b1000_0000 , const R_SHIFT = 0b0100_0000 , const SHIFT = L_SHIFT.bits | R_SHIFT.bits , const R_CTRL = 0b0010_0000 , const L_CTRL = 0b0001_0000 , const CTRL = L_CTRL.bits | R_CTRL.bits , const R_ALT = 0b0000_1000 , const L_ALT = 0b0000_0100 , const ALT = L_ALT.bits | R_ALT.bits , const CAPSLOCK = 0b0000_0010 , const NUMLOCK = 0b0000_0001 } } impl Default for Modifiers { #[inline] fn default() -> Self { Modifiers::new() } } impl Modifiers { pub const fn new() -> Self { Modifiers { bits: 0b0000_0000 } } /// Returns true if either shift key is pressed. #[inline] pub fn is_shifted(&self) -> bool { self.contains(SHIFT) } /// Returns true if the keyboard's state is currently uppercase. #[inline] pub fn is_uppercase(&self) -> bool { self.is_shifted() ^ self.contains(CAPSLOCK) } /// Updates the modifiers state from a given scancode. fn update(&mut self, scancode: Scancode) { match scancode { Scancode(0x1D) => self.insert(L_CTRL) , Scancode(0x2A) => self.insert(L_SHIFT) , Scancode(0x36) => self.insert(R_SHIFT) , Scancode(0x38) => self.insert(L_ALT) // Caps lock toggles on leading edge , Scancode(0x3A) => self.toggle(CAPSLOCK) , Scancode(0x9D) => self.remove(L_CTRL) , Scancode(0xAA) => self.remove(L_SHIFT) , Scancode(0xB6) => self.remove(R_SHIFT) , Scancode(0xB8) => self.remove(L_ALT) , _ => {} } } /// Apply the keyboard's modifiers to an ASCII scancode. fn modify(&self, ascii: u8) -> u8 { match ascii { b'a' ... b'z' if self.is_uppercase() => ascii - b'a' + b'A' , b'1' ... b'9' if self.is_shifted() => ascii - b'1' + b'!' , b'0' if self.is_shifted() => b')' , _ => ascii } } } /// Our global keyboard state, protected by a mutex. // TODO: can this be thread local? static KEYBOARD: Mutex = Mutex::new(Keyboard { data_port: Port::::new(0x60) , state: Modifiers::new() }); pub fn read_char() -> Option { let mut lock = KEYBOARD.lock(); let code = lock.read_scancode(); lock.state.update(code); code.to_ascii() .map(|ascii| lock.state.modify(ascii) as char) } ================================================ FILE: src/io/mod.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Kernel IO. //! //! This module should eventually abstract over architecture-specific //! implementation. pub mod term; pub mod keyboard; ================================================ FILE: src/io/term.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // extern { pub static __vga_buffer: u8; } /// The system's global VGA terminal pub use vga::CONSOLE; ================================================ FILE: src/logger.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // use log; use log::{LogRecord, LogLevel, LogMetadata, LogLevelFilter}; use arch::drivers::serial; use core::fmt::Write; struct SerialLogger; pub fn initialize() -> Result<(), log::SetLoggerError> { unsafe { log::set_logger_raw(|max_log_level| { max_log_level.set(LogLevelFilter::Trace); &SerialLogger }) } } pub fn shutdown() -> Result<(), log::ShutdownLoggerError> { log::shutdown_logger_raw().map(|_logger| { }) } #[cfg(debug_assertions)] impl log::Log for SerialLogger { #[inline] fn enabled(&self, _metadata: &LogMetadata) -> bool { true // TODO: for now? } #[inline] fn log(&self, record: &LogRecord) { let meta = record.metadata(); match record.level() { LogLevel::Trace if self.enabled(meta) => { let location = record.location(); let _ = write!( *serial::COM1.lock() , "[ TRACE ][ {}:{} ] {}: {}\n" , location.module_path(), location.line() , meta.target() , record.args() ); } , LogLevel::Debug if self.enabled(meta) => { let _ = write!( *serial::COM1.lock() , "[ DEBUG ] {}: {}\n" , meta.target() , record.args() ); } , level => { let target = meta.target(); let args = record.args(); let _ = write!( *serial::COM1.lock() , "[ {} ] {}: {}\n" , level, target, args ); // println!("{}: {}", target, args ); } } } } #[cfg(not(debug_assertions))] impl log::Log for SerialLogger { #[inline] fn enabled(&self, metadata: &LogMetadata) -> bool { false // TODO: for now } #[inline] fn log(&self, record: &LogRecord) { let meta = record.metadata(); if self.enabled(meta) { println!("{}: {}", target, args ); // TODO: should we log for com1 also? } } } ================================================ FILE: src/main.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! # SOS kernel //! This crate contains the kernel for SOS, the Stupid Operating System. //! //! # SOS: the Stupid Operating System //! SOS is a simple, tiny toy OS implemented in Rust. It targets the `x86`, //! `x86_64`, and ARM v7 CPU architectures. //! //! I'm writing this mostly for fun, to learn more about OS design and kernel //! hacking, so don't expect anything new or exciting out of this project. //! //! SOS is copyright 2015-2017 Eliza Weisman, and is released under the terms //! of the MIT license. #![crate_name = "sos_kernel"] #![doc(html_root_url = "https://hawkw.github.io/sos-kernel/")] #![feature( lang_items, asm, naked_functions )] #![feature( linkage )] #![feature( const_fn , slice_patterns , associated_consts , type_ascription , custom_derive )] #![feature(alloc)] #![cfg_attr(feature="clippy", feature(plugin))] #![cfg_attr(feature="clippy", plugin(clippy))] #![cfg_attr( any(target_arch = "x86_64", target_arch="x86") , feature(abi_x86_interrupt))] #![no_std] #![cfg_attr(not(test), no_main)] // -- non-SOS dependencies -------------------------------------------------- #[macro_use] extern crate lazy_static; #[macro_use] extern crate bitflags; #[macro_use] extern crate log; extern crate alloc; extern crate rlibc; extern crate spin; // -- SOS dependencies ------------------------------------------------------ #[macro_use] extern crate vga; extern crate sos_alloc; extern crate cpu; extern crate elf; extern crate paging; extern crate params; extern crate memory; extern crate util; #[macro_use] pub mod io; pub mod heap; pub mod arch; pub mod logger; use params::InitParams; /// SOS version number pub const VERSION_STRING: &'static str = concat!("Stupid Operating System v", env!("CARGO_PKG_VERSION")); /// Kernel main loop pub fn kernel_main() -> ! { // let mut a_vec = collections::vec::Vec::::new(); // info!(target: "test", "Created a vector in kernel space! {:?}", a_vec); // a_vec.push(1); // info!(target: "test", "pushed to vec: {:?}", a_vec); // a_vec.push(2); // info!(target: "test", "pushed to vec: {:?}", a_vec); // let mut frame_allocator = frame_alloc::FrameAllocator::new(); // paging::test_paging(&mut frame_allocator); loop { } } /// Kernel initialization function called into by architecture-specific init /// /// Our initialization process essentially looks like this: /// /// ```text /// +-------------+ /// | bootloader | /// | (multiboot) | /// +------|------+ /// +------V------+ /// | start.asm | /// +------|------+ /// +------|--------------------------------------------------------+ /// | | RUST-LAND KERNEL FUNCTIONS | /// | V | /// | arch_init() ----------> kernel_init() --------> kernel_main() | /// | + collects boot info + initializes interrupts | /// | from arch-specific + initializes the heap | /// | sources + remaps the kernel into the higher | /// | + some CPU-specific half of the address space | /// | configuration | /// +---------------------------------------------------------------+ /// ``` pub fn kernel_init(params: &InitParams) { use sos_alloc::frame::mem_map::MemMapAllocator; use ::paging::kernel_remap; kinfoln!("Hello from the kernel!"); // kinfoln!("Got init params: {:#?}", params ); // -- remap the kernel ---------------------------------------------------- let mut frame_allocator = MemMapAllocator::from(params); kinfoln!(dots: " . ", "Remapping the kernel..."); let page_table = match kernel_remap(¶ms, &mut frame_allocator) { Ok(p) => { kinfoln!(dots: " . ", target: "Remapping the kernel", "[ OKAY ]"); p } , Err(why) => { kinfoln!(dots: " . ", target: "Remapping the kernel", "[ FAIL ]"); panic!( "Could not remap kernel: {:?}", why) } }; attempt!(paging::test_paging(&mut frame_allocator) => dots: " . . ", "Testing paging..."); // -- initialize the heap ------------------------------------------------ attempt!( unsafe { heap::initialize(params) } => dots: " . ", "Intializing heap..."); kinfoln!( dots: " . . " , "Heap begins at {:#x} and ends at {:#x}" , params.heap_base, params.heap_top); // -- initialize interrupts ---------------------------------------------- // attempt!( unsafe { arch::interrupts::initialize() } => // "Initializing interrupts...", dots: " . " ); println!("\n{} {}-bit\n", VERSION_STRING, arch::ARCH_BITS); // -- call into kernel main loop ------------------------------------------ // (currently, this does nothing) kernel_main() } /// This fake `main` function exists only to placate `cargo test`. #[cfg(test)] fn main() { } ================================================ FILE: targets/x86_32-sos-bootstrap-gnu.json ================================================ { "llvm-target": "i386-unknown-unknown-elf", "executables": true, "linker-flavor": "gcc", "target-endian": "little", "target-word-size": "32", "target-pointer-width": "32", "target-c-int-width": "32", "data-layout": "e-m:e-p:32:32-f64:32:64-f80:32-n8:16:32-S128", "os": "sos", "arch": "x86", "features": "-mmx,-sse,-sse2,+soft-float", "code-model":"kernel", "relocation-model": "static", "disable-redzone": true, "eliminate-frame-pointer": false, "no-compiler-rt": true, "linker": "no-linker", "pre-link-args": [], "post-link-args": [], "archive-format": "gnu" } ================================================ FILE: targets/x86_64-sos-kernel-gnu.json ================================================ { "llvm-target": "x86_64-unknown-none-gnu", "executables": true, "target-endian": "little", "target-pointer-width": "64", "target-c-int-width": "32", "data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128", "code-model":"kernel", "relocation-model": "static", "os": "sos", "arch": "x86_64", "linker-flavor": "gcc", "pre-link-args": { "gcc": [ "-Tsrc/arch/x86_64/linker.ld" , "-Wl,-n" , "-nostartfiles" , "-nostdlib" , "-ffreestanding" ] }, "cpu": "x86-64", "features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-3dnow,-3dnowa,-avx,-avx2,+soft-float", "disable-redzone": true, "eliminate-frame-pointer": false, "no-compiler-rt": true, "archive-format": "gnu" } ================================================ FILE: tokamak.toml ================================================ [helper] path = "" # Reserved for future helper path and configurations [options] save_buffers_before_run = true # Saving buffers before every cargo command run general_warnings = true # Show general warnings [project] auto_format_timing = 5 # Run auto formatting for project for specified interval (seconds) ================================================ FILE: util/Cargo.toml ================================================ [package] name = "util" version = "0.0.1" authors = [ "Eliza Weisman " ] [profile.dev] opt-level = 3 debug = true rpath = false lto = false debug-assertions = true codegen-units = 1 panic = "abort" [profile.release] opt-level = 3 debug = true rpath = false lto = false panic = "abort" # [dependencies.vga] # path = "../vga" # features = ["system_term"] # optional = true ================================================ FILE: util/src/io.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2016 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // /// The `Read` trait allows for reading bytes from a source. /// /// Implementors of the `Read` trait are sometimes called 'readers'. /// /// Readers are defined by one required method, `read()`. Each call to `read` /// will attempt to pull bytes from this source into a provided buffer. A /// number of other methods are implemented in terms of `read()`, giving /// implementors a number of ways to read bytes while only needing to implement /// a single method. /// /// This is basically a brain-dead reimplementation of the standard /// library's `Read` trait. Most of the methods available on the /// standard lib's `Read` are not yet implemented. pub trait Read { type Error; /// Pull some bytes from this source into the specified buffer, returning /// how many bytes were read. /// /// This function does not provide any guarantees about whether it blocks /// waiting for data, but if an object needs to block for a read but cannot /// it will typically signal this via an `Err` return value. /// /// If the return value of this method is `Ok(n)`, then it must be /// guaranteed that `0 <= n <= buf.len()`. A nonzero `n` value indicates /// that the buffer `buf` has been filled in with `n` bytes of data from /// this source. If `n` is `0`, then it can indicate one of two scenarios: /// /// 1. This reader has reached its "end of file" and will likely no longer /// be able to produce bytes. Note that this does not mean that the /// reader will *always* no longer be able to produce bytes. /// 2. The buffer specified was 0 bytes in length. /// /// No guarantees are provided about the contents of `buf` when this /// function is called, implementations cannot rely on any property of the /// contents of `buf` being true. It is recommended that implementations /// only write data to `buf` instead of reading its contents. /// /// # Errors /// /// If this function encounters any form of I/O or other error, an error /// variant will be returned. If an error is returned then it must be /// guaranteed that no bytes were read. fn read(&mut self, buf: &mut [u8]) -> Result; /// Read all bytes until EOF in this source, placing them into `buf`. /// /// All bytes read from this source will be appended to the specified buffer /// `buf`. This function will continuously call `read` to append more data /// to `buf` until `read` returns either `Ok(0)` or an error. /// /// If successful, this function will return the total number of bytes read. fn read_all(&mut self, buf: &mut [u8]) -> Result; } /// A trait for objects which are byte-oriented sinks. /// /// Implementors of the `Write` trait are sometimes called 'writers'. /// /// Writers are defined by two required methods, `write()` and `flush()`: /// /// * The `write()` method will attempt to write some data into the object, /// returning how many bytes were successfully written. /// /// * The `flush()` method is useful for adaptors and explicit buffers /// themselves for ensuring that all buffered data has been pushed out to the /// 'true sink'. /// /// This is basically a brain-dead reimplementation of the standard /// library's `Write` trait. Most of the methods available on the /// standard lib's `Write` are not yet implemented. pub trait Write { type Error; /// Write a buffer into this object, returning how many bytes were written. /// /// This function will attempt to write the entire contents of `buf`, but /// the entire write may not succeed, or the write may also generate an /// error. A call to `write` represents *at most one* attempt to write to /// any wrapped object. /// /// Calls to `write` are not guaranteed to block waiting for data to be /// written, and a write which would otherwise block can be indicated through /// an `Err` variant. /// /// If the return value is `Ok(n)` then it must be guaranteed that /// `0 <= n <= buf.len()`. A return value of `0` typically means that the /// underlying object is no longer able to accept bytes and will likely not /// be able to in the future as well, or that the buffer provided is empty. /// /// # Errors /// /// Each call to `write` may generate an I/O error indicating that the /// operation could not be completed. If an error is returned then no bytes /// in the buffer were written to this writer. /// /// It is **not** considered an error if the entire buffer could not be /// written to this writer. fn write(&mut self, buf: &[u8]) -> Result; } ================================================ FILE: util/src/lib.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2016 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! General purpose stuff I couldn't find a better home for. #![crate_name = "util"] #![no_std] #![feature(step_trait)] // #[cfg(not(test))] extern crate vga; use core::{fmt, ops}; use ops::*; use core::iter::Step; // use core::num::One; pub mod io; #[macro_use] pub mod macros; /// The unreachable Void type. pub enum Void {} impl fmt::Debug for Void { fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result { unreachable!() } } pub trait Align: Sized + Copy //+ One + Add + Sub + BitAnd + Not + Step { #[inline] fn align_up(&self, to: Self) -> Self { let align = to.sub_one(); (*self + align) & !align } #[inline] fn align_down(&self, to: Self) -> Self { *self & !to.sub_one() } } impl Align for u64 { } impl Align for u32 { } impl Align for usize { } ================================================ FILE: util/src/macros/mod.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // #![allow(missing_docs)] #[macro_export] macro_rules! expr { ($e:expr) => { $e } } #[macro_use] pub mod newtype_impl; // #[macro_use] pub mod log; ================================================ FILE: util/src/macros/newtype_impl.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // #[macro_export] macro_rules! forward_ref_binop { ($imp:ident, $method:ident for $t:ty, $u:ty) => { impl<'a> ::core::ops::$imp<$u> for &'a $t { type Output = <$t as ::core::ops::$imp<$u>>::Output; #[inline] fn $method(self, other: $u) -> <$t as ::core::ops::$imp<$u>>::Output { ::core::ops::$imp::$method(*self, other) } } impl<'a> ::core::ops::$imp<&'a $u> for $t { type Output = <$t as ::core::ops::$imp<$u>>::Output; #[inline] fn $method(self, other: &'a $u) -> <$t as ::core::ops::$imp<$u>>::Output { ::core::ops::$imp::$method(self, *other) } } impl<'a, 'b> ::core::ops::$imp<&'a $u> for &'b $t { type Output = <$t as ::core::ops::$imp<$u>>::Output; #[inline] fn $method(self, other: &'a $u) -> <$t as ::core::ops::$imp<$u>>::Output { ::core::ops::$imp::$method(*self, *other) } } }; } #[macro_export] macro_rules! impl_ops { ($($name:ident, $fun:ident, $op:tt for $ty:ident, $size:ty)*) => {$( impl ::core::ops::$name<$ty> for $ty { type Output = $ty; #[inline] fn $fun(self, rhs: $ty) -> $ty { $ty(expr!(self.0 $op rhs.0)) } } impl ::core::ops::$name<$size> for $ty { type Output = $ty; #[inline] fn $fun(self, rhs: $size) -> $ty { $ty(expr!(self.0 $op rhs)) } } forward_ref_binop! { $name, $fun for $ty, $ty } forward_ref_binop! { $name, $fun for $ty, $size } )*} } #[macro_export] macro_rules! impl_assign_ops { ($($name:ident, $fun:ident, $op:tt for $ty:ident, $size:ty)*) => {$( impl ::core::ops::$name<$ty> for $ty { #[inline] fn $fun(&mut self, rhs: $ty) { expr!(self.0 $op rhs.0); } } impl ::core::ops::$name<$size> for $ty { #[inline] fn $fun(&mut self, rhs: $size) { expr!(self.0 $op rhs); } } )*} } #[macro_export] macro_rules! impl_fmt { ($($name:ident for $ty:ty)*) => {$( impl ::core::fmt::$name for $ty { fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result { self.0.fmt(f) } } )*} } ================================================ FILE: vga/Cargo.toml ================================================ [package] name = "vga" version = "0.1.0" authors = ["Eliza Weisman "] [features] default = [] system_term = ["spin"] kinfo = ["system_term", "log"] [dependencies.spin] version = "0.4.6" optional = true [dependencies.log] version = "0.3.6" default-features = false features = ["release_max_level_info"] optional = true ================================================ FILE: vga/src/kinfo.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2016 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Macros for kernel-level logging #[macro_export] macro_rules! attempt { ($task:expr => dots: $dots:expr, $msg:expr ) => ({ use $crate::status::Status; print!("{}{}", $dots, $msg); match $task { Ok(result) => { $crate::CONSOLE.lock() .okay() .expect("Could not write to VGA buffer! Something's \ really wrong!"); info!("{} [ OKAY ]", $msg); result } , Err(why) => { $crate::CONSOLE.lock() .fail() .expect("Could not write to VGA buffer! Something's \ really wrong!"); panic!("{:?}", why); } } }); ($task:expr => dots: $dots:expr, $($msg:tt)* ) => { attempt!($task => dots: $dots, format_args!($($msg)*)) }; } #[macro_export] macro_rules! kinfo { ( dots: $dots:expr, target: $target:expr, "[ OKAY ]") => { // { // use core::fmt::Write; // // // suppress warnings because we don't care if there's no serial port // let _ = write!( $crate::arch::drivers::serial::COM1.lock() // , "[info] {}: {} {}" // , module_path!() // , $msg, $status); print!("{:<38}{:>40}", concat!($dots, $target)); { use $crate::status::Status; $crate::CONSOLE.lock().okay() .expect("Could not write to VGA buffer! Something's \ really wrong!"); } info!(target: $target, $status); // } }; ( dots: $dots:expr, target: $target:expr, "[ FAIL ]") => { // { // use core::fmt::Write; // // // suppress warnings because we don't care if there's no serial port // let _ = write!( $crate::arch::drivers::serial::COM1.lock() // , "[info] {}: {} {}" // , module_path!() // , $msg, $status); print!("{:<38}{:>40}", concat!($dots, $target)); { use $crate::status::Status; $crate::CONSOLE.lock().fail() .expect("Could not write to VGA buffer! Something's \ really wrong!"); } info!(target: $target, $status); // } }; ( dots: $dots:expr, $($args:tt)* ) => { // { // use core::fmt::Write; // // // suppress warnings because we don't care if there's no serial port // let _ = write!( $crate::arch::drivers::serial::COM1.lock() // , "[info] {}: {}" // , module_path!() // , format_args!($($args)*)); print!("{}{}", $dots, format_args!($($args)*)); info!( ($args)* ); // } }; ( $($args:tt)* ) => { // { // use core::fmt::Write; // // // suppress warnings because we don't care if there's no serial port // let _ = write!( $crate::arch::drivers::serial::COM1.lock() // , "[info] {}: {}" // , module_path!() // , format_args!($($args)*)); print!( $($args)* ); info!( $($args)* ); // } }; } #[macro_export] macro_rules! kinfoln { ( dots: $dots:expr, target: $target:expr, $status:expr ) => { // { // use core::fmt::Write; // // // suppress warnings because we don't care if there's no serial port // let _ = write!( $crate::arch::drivers::serial::COM1.lock() // , "[info] {}: {} {}" // , module_path!() // , $msg, $status); println!("{:<39}{:>40}", concat!($dots, $target), $status ); info!(target: $target, $status); // } }; ( dots: $dots:expr, $($args:tt)* ) => { // { // use core::fmt::Write; // // // suppress warnings because we don't care if there's no serial port // let _ = write!( $crate::arch::drivers::serial::COM1.lock() // , "[info] {}: {}" // , module_path!() // , format_args!($($args)*)); println!("{}{}", $dots, format_args!($($args)*)); info!( $($args)* ); // } }; ( $($args:tt)* ) => { // { // use core::fmt::Write; // // // suppress warnings because we don't care if there's no serial port // let _ = write!( $crate::arch::drivers::serial::COM1.lock() // , "[info] {}: {}" // , module_path!() // , format_args!($($args)*)); println!( $($args)* ); info!( $($args)* ); // } }; } ================================================ FILE: vga/src/lib.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! # SOS VGA //! Code for interacting with the system's VGA buffer. //! //! # Features //! This crate exposes the following feature flags: //! + `system_term`: Use the VGA buffer as the system terminal. This provides //! implementations of the `print!` and `println!` macros. //! + `log`: provides a `log!` macro for kernel log messages. #![crate_name = "vga"] #![crate_type = "lib"] #![feature( slice_patterns , unique )] #![feature(ptr_internals)] #![feature( const_fn , const_unique_new )] #![cfg_attr(feature = "system_term", feature(lang_items))] #![cfg_attr(feature = "clippy", feature(plugin))] #![cfg_attr(feature = "clippy", plugin(clippy))] #![no_std] #[cfg(feature = "system_term")] extern crate spin; use core::{mem, ptr}; use core::fmt::{Write, Result}; // use core::ptr::Unique; #[cfg(feature = "system_term")] use spin::Mutex; #[cfg(feature = "kinfo")] #[macro_use] extern crate log; #[cfg(all( feature = "system_term" , not(test)))] pub mod panic; /// Macro for printing to the standard output. /// /// Equivalent to the `println!` macro except that a newline is not printed at /// the end of the message. /// /// # Panics /// /// Panics if writing to the VGA console fails. /// #[cfg(feature = "system_term")] #[macro_export] macro_rules! println { ($fmt:expr) => (print!(concat!($fmt, "\n"))); ($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*)); } /// Macro for printing to the standard output. /// /// Equivalent to the `println!` macro except that a newline is not printed at /// the end of the message. /// /// # Panics /// /// Panics if writing to the VGA console fails. #[cfg(feature = "system_term")] #[macro_export] macro_rules! print { ($($arg:tt)*) => ({ use core::fmt::Write; $crate::CONSOLE.lock() .write_fmt(format_args!($($arg)*)) .unwrap(); }); } #[cfg(feature = "kinfo")] #[macro_use] pub mod kinfo; pub mod status; /// The system's global VGA terminal /// TODO: should this live in the kernel instead? #[cfg(feature = "system_term")] pub static CONSOLE: Mutex = Mutex::new(unsafe { Terminal::new( Palette::new(Color::LightGrey, Color::Black ) , 0xB8000 )}); // TODO: support varying VGA screen sizes? pub const X_MAX: usize = 80; pub const Y_MAX: usize = 25; /// The type signature fot the actual VGA buffer pub type Buffer = [[Char; X_MAX]; Y_MAX]; const ANSI_ESCAPE: &'static str = "\x1b"; const FG_MASK: u8 = 0b0000_1111; const BG_MASK: u8 = 0b1111_0000; /// VGA color codes #[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)] #[repr(u8)] pub enum Color { Black = 0 , Blue = 1 , Green = 2 , Cyan = 3 , Red = 4 , Magenta = 5 , Brown = 6 , LightGrey = 7 , DarkGrey = 8 , LightBlue = 9 , LightGreen = 10 , LightCyan = 11 , LightRed = 12 , LightMagenta = 13 , Yellow = 14 , White = 15 } #[derive(Eq, PartialEq, Ord, PartialOrd, Copy, Clone)] pub struct Palette(u8); impl Palette { /// Returns a `Palette` with the given foreground and background color. pub const fn new(fg: Color, bg: Color) -> Self { Palette( (bg as u8) << 4 | (fg as u8) ) } /// Returns a new `Palette` with this palette's background color, and /// the specified foreground color. pub fn set_foreground(&self, fg: Color) -> Self { Palette( ( self.0 & BG_MASK) | (fg as u8 & FG_MASK) ) } /// Returns a new `Palette` with this palette's foreground color, and /// the specified background color. pub fn set_background(&self, bg: Color) -> Self { Palette( ( (bg as u8) << 4 & BG_MASK) | (self.0 & FG_MASK) ) } /// Returns this `Palette`'s foreground color. pub fn foreground(&self) -> Color { unsafe { mem::transmute(self.0 & FG_MASK) } } /// Returns this `Palette`'s background color. pub fn background(&self) -> Color { unsafe { mem::transmute((self.0 & BG_MASK) >> 4) } } } /// A colored VGA character. #[derive(Copy, Clone)] #[repr(C)] pub struct Char { pub ascii: u8 , pub colors: Palette } impl Char { #[inline(always)] pub const fn empty() -> Self { Char { ascii: 0, colors: Palette(0) } } } pub struct Terminal { buffer: ptr::Unique , x: usize , y: usize , colors: Palette } impl Terminal { #[inline] pub fn x_position(&self) -> usize { self.x } /// Constructs a new `Terminal` for abuffer starting at the given address. /// /// # Arguments /// + `colors`: the default color palette for the terminal /// + `buffer_start`: the address of the to the memory location where /// the terminal's VGA buffer begins /// /// # Unsafe due to: /// + Casting a raw address to an array /// + Constructing a `ptr::Unique` from an unchecked mut pointer. pub const unsafe fn new( colors: Palette , buffer_start: usize) -> Terminal { Terminal { x: 0, y: 0 , colors // TODO: this probably shouldn't be unchecked, but this // can't be a const fn otherwise... , buffer: ptr::Unique::new_unchecked(buffer_start as *mut _) } } #[inline] fn buffer(&mut self) -> &mut Buffer { unsafe { self.buffer.as_mut() } } /// Set the color palette used for writing subsequent characters. pub fn set_colors(&mut self, bg: Color, fg: Color) -> &mut Self { self.colors = Palette::new(bg,fg); self } /// Scrolls the terminal one row. fn scroll(&mut self) { // // construct an iterator over the whole buffer // let mut rows = self.buffer() // .iter_mut(); // // // the current row in the buffer // let mut current = rows.next() // .unwrap(); // // while let Some(next) = rows.next() { // // while there are rows remaining in the iterator, swap the // // next row with the current one (moving it back by one) // mem::swap(current, next); // // and advance our pointer to the current row. // current = next; // } let buffer: &mut _ = self.buffer(); for row in 1..Y_MAX { for col in 0 .. X_MAX { unsafe { let last = ptr::read_volatile(&buffer[row][col]); ptr::write_volatile( &mut buffer[row - 1][col], last); } } } // empty the last line in the buffer unsafe { for col in 0..X_MAX { ptr::write_volatile(&mut buffer[Y_MAX - 1][col], Char::empty()); } } } /// Clear the terminal #[inline] pub fn clear(&mut self) -> &mut Self { // to clear the terminal, we just zero out the whole buffer. { let buffer: &mut _ =self.buffer(); for row in 0..Y_MAX { for col in 0 .. X_MAX { unsafe { ptr::write_volatile( &mut buffer[row][col], Char::empty() ); } } } } self.x = 0; self.y = 0; self } /// Write the given byte to the terminal, and advance the cursor position. pub fn write_byte(&mut self, byte: u8) -> &mut Self { if byte == b'\n' { // if the byte is a newline, we just advance to the next line // and reset the column position. self.x = 0; self.y += 1; } else { // otherwise, it's a regular character, so we just set the // byte at the current position in the buffer to that // character (with the current color palette) // let y = self.y; let x = self.x; unsafe { ptr::write_volatile( &mut (self.buffer()[y][x]) , Char { ascii: byte, colors: self.colors } ); } // and advance our column position by one self.x += 1; if self.x >= X_MAX { // if we've reached the end of the line, advance to the next self.x = 0; self.y += 1; } } if self.y >= Y_MAX { // if we've reached the bottom of the terminal, scroll. self.scroll(); self.y -= 1; } self } fn handle_ansi_escape(&self, escape_code: &str) -> Result { match escape_code.as_bytes() { // `\x1b[3Nm` sets the foreground color to N. &[0x1b, b'[', b'3', n, b'm'] => { unsafe { self.colors .set_foreground(mem::transmute(n - 48)); } Ok(()) } // `\x1b[4Nm` sets the background color to N , &[0x1b, b'[', b'4', n, b'm'] => { unsafe { self.colors .set_background(mem::transmute(n - 48)); } Ok(()) } , _ => unimplemented!() } // let escape_seq: &str = bytes.take_while(|b| b != b'm') // .collect::<&str>(); // match escape_seq { // [b'3', n] => unsafe { // self.colors.set_foreground(mem::transmute(n - 48)) // } // } // while let Some(byte) = bytes.next() { // match *byte { // // we've recieved an ANSI escape sequence. // // this basically enters a mediocre FSM for matching ANSI // // control codes. // 0x1b => match *next_ansi_byte!(bytes) { // // handle multi-char ANSI escapes // b'[' => match *next_ansi_byte!(bytes) { // // foreground color code // fg @ 30 ... 37 => { // if !(*next_ansi_byte!(bytes) == b'm') { // unsafe { // let color: vga::Color // = mem::transmute(fg - 30); // self.colors // .set_foreground(color); // } // // } // } // // background color code // , 40 ... 47 => { // // } // , _ => unimplemented!() // } // , _ => unimplemented!() // } // // otherwise, treat the byte as a normal ASCII char // , b => { self.write_byte(b); } // } // } } } struct AnsiEscapeIter<'a> { curr_slice: &'a str , in_escape: bool } impl<'a> AnsiEscapeIter<'a> { pub fn new(s: &'a str) -> Self { AnsiEscapeIter { curr_slice: s , in_escape: false } } } impl<'a> Iterator for AnsiEscapeIter<'a> { type Item = &'a str; fn next(&mut self) -> Option { if self.curr_slice.len() == 0 { // if the remaining string is empty, we just return `None` None } else { // otherwise, find the next index to chunk on. let maybe_idx = if self.in_escape { // if we're in an escape code, we split the chunk at the // index of the next 'm' character, adding 1 so that the // 'm' is in the escape code chunk. self.curr_slice.find('m') .map(|idx| idx + 1) } else { // otherwise, split at the next ANSI escape sequence self.curr_slice.find(ANSI_ESCAPE) }; // if we found another index to chunk on, map over that index; // otherwise, we just yield the rest of the string maybe_idx.map_or( Some(self.curr_slice) // remainder (if no index to chunk on) , |idx| { // otherwise, chunk along that index... let (chunk, next_slice) = self.curr_slice .split_at(idx); self.curr_slice = next_slice; // update current chunk Some(chunk) // return the chunk }) } } } impl Write for Terminal { fn write_str(&mut self, s: &str) -> Result { if s.contains(ANSI_ESCAPE) { // if the segment contains an ANSI escape, construct an iterator // over each chunk containing either an escape sequence or text for segment in AnsiEscapeIter::new(s) { if segment.starts_with(ANSI_ESCAPE) { // if the current segment is an ANSI escape code, // try to handle the escape and fail if it is malformed self.handle_ansi_escape(segment)? } else { // otherwise, just write each chunk in the string. for byte in segment.as_bytes() { self.write_byte(*byte); } } } } else { // otherwise, if there are no ANSI escape codes, // we can just write each byte in the string. for byte in s.as_bytes() { self.write_byte(*byte); } } Ok(()) } } ================================================ FILE: vga/src/panic.rs ================================================ // // SOS: the Stupid Operating System // by Eliza Weisman (eliza@elizas.website) // // Copyright (c) 2015-2017 Eliza Weisman // Released under the terms of the MIT license. See `LICENSE` in the root // directory of this repository for more information. // //! Panic handling. //! //! This module contains the `panic_fmt` language item. This function handles //! panics at runtime. use core::fmt::{Arguments, Write}; use super::{Color, CONSOLE}; /// Called to handle a panic. /// /// Since kernel panics are non-recoverable, this function prints out /// the error message and hangs forever. /// /// Eventually – way in the future – when we have disk I/O and stuff, /// we'll probably want to write out some core dumps here as well. #[lang = "panic_fmt"] #[no_mangle] #[inline(never)] #[cold] pub extern "C" fn rust_begin_unwind( args: Arguments , file: &'static str , line: usize ) -> ! { let _ = write!( CONSOLE.lock() .set_colors(Color::White, Color::Red) , "Something has gone horribly wrong in {} at line {}. \ \n{}\n\ This is fine." , file, line, args ); error!(target: file, "{}", args); loop { } } ================================================ FILE: vga/src/status.rs ================================================ use super::{Terminal, Color}; use core::fmt::{Result, Write}; pub trait Status { fn okay(&mut self) -> Result ; fn fail(&mut self) -> Result ; } impl Status for Terminal { fn okay(&mut self) -> Result { while self.x_position() < 71 { self.write_byte(b' '); } self.write_str("[ ")?; self.set_colors(Color::Green, Color::Black, ); self.write_str("OKAY")?; self.set_colors(Color::LightGrey, Color::Black); self.write_str(" ]\n")?; Ok(()) } fn fail(&mut self) -> Result { while self.x_position() < 71 { self.write_byte(b' '); } self.write_str("[ ")?; self.set_colors(Color::Red, Color::Black); self.write_str("FAIL")?; self.set_colors(Color::LightGrey, Color::Black); self.write_str(" ]\n")?; Ok(()) } }