Merge pull request #1 from redox-os/master

Update
This commit is contained in:
Emanuele Antonio Faraone 2017-08-17 08:37:02 +02:00 committed by GitHub
commit 1595cb9ccb
269 changed files with 799 additions and 21033 deletions

View file

@ -4,7 +4,8 @@
**Actual behavior**: [describe the actual behavior, which is presented through the repro.].
**Build information**: [output of `rustc -V`, `git rev-parse HEAD`, `qemu-i386 -version`, `uname -a`, etc.]
**Build information**: [only when using a self build version: output of `rustc -V`, `git rev-parse HEAD` `qemu-i386 -version`, `uname -a`, etc.]
**Redox release**: [only when using a prebuild version: redox version]
**Blocking/related**: [issues or PRs blocking or being related to this issue.]

6
.gitignore vendored
View file

@ -1,7 +1 @@
Cargo.lock
build
target
initfs/bin
filesystem/bin
filesystem/ref
filesystem/sbin

75
.gitmodules vendored
View file

@ -1,60 +1,21 @@
[submodule "rust"]
path = rust
url = https://github.com/redox-os/rust.git
[submodule "ion"]
path = programs/ion
url = https://github.com/redox-os/ion.git
[submodule "programs/coreutils"]
path = programs/coreutils
url = https://github.com/redox-os/coreutils.git
[submodule "schemes/redoxfs"]
path = schemes/redoxfs
url = https://github.com/redox-os/redoxfs
[submodule "programs/extrautils"]
path = programs/extrautils
url = https://github.com/redox-os/extrautils.git
[submodule "programs/smith"]
path = programs/smith
url = https://github.com/IGI-111/Smith.git
[submodule "programs/userutils"]
path = programs/userutils
url = https://github.com/redox-os/userutils.git
[submodule "programs/netutils"]
path = programs/netutils
url = https://github.com/redox-os/netutils.git
[submodule "schemes/orbital"]
path = schemes/orbital
url = https://github.com/redox-os/orbital.git
[submodule "programs/orbutils"]
path = programs/orbutils
url = https://github.com/redox-os/orbutils.git
[submodule "filesystem/ui"]
path = filesystem/ui
url = https://github.com/redox-os/orbdata.git
[submodule "programs/acid"]
path = programs/acid
url = https://github.com/redox-os/acid.git
[submodule "programs/tar"]
path = programs/tar
url = https://github.com/redox-os/tar-rs.git
[submodule "programs/pkgutils"]
path = programs/pkgutils
url = https://github.com/redox-os/pkgutils.git
[submodule "bootloader"]
path = bootloader
url = https://github.com/redox-os/bootloader.git
[submodule "cookbook"]
path = cookbook
url = https://github.com/redox-os/cookbook.git
[submodule "installer"]
path = installer
url = https://github.com/redox-os/installer.git
[submodule "syscall"]
path = syscall
url = https://github.com/redox-os/syscall.git
[submodule "crates/docgen"]
path = crates/docgen
url = https://github.com/redox-os/docgen.git
[submodule "programs/binutils"]
path = programs/binutils
url = https://github.com/redox-os/binutils.git
[submodule "libc-artifacts"]
path = libc-artifacts
url = https://github.com/redox-os/libc-artifacts.git
[submodule "programs/games"]
path = programs/games
url = https://github.com/redox-os/games.git
[submodule "isolinux"]
path = isolinux
url = https://github.com/redox-os/isolinux.git
[submodule "kernel"]
path = kernel
url = https://github.com/redox-os/kernel.git
[submodule "rust"]
path = rust
url = https://github.com/redox-os/rust.git
[submodule "redoxfs"]
path = redoxfs
url = https://github.com/redox-os/redoxfs.git

View file

@ -5,23 +5,30 @@ rust:
cache: cargo
os:
- linux
#- osx
#matrix:
# allow_failures:
# - os: osx
dist: trusty
before_install:
- if [ `uname` = "Linux" ]; then
sudo apt-get install -qq nasm pkg-config fuse libfuse-dev genisoimage syslinux &&
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then
sudo apt-key adv -q --batch --yes --keyserver keyserver.ubuntu.com --recv-keys AA12E97F0881517F &&
sudo add-apt-repository 'deb https://static.redox-os.org/toolchain/apt ./' &&
sudo apt-get update -qq &&
sudo apt-get purge -qq binutils-doc &&
sudo apt-get install -qq nasm pkg-config fuse libfuse-dev genisoimage syslinux x86-64-unknown-redox-gcc &&
sudo modprobe fuse &&
sudo chmod 666 /dev/fuse &&
sudo chown root:$USER /etc/fuse.conf;
fi
- if [ `uname` = "Darwin" ]; then brew update &&
brew install nasm gcc49 pkg-config Caskroom/cask/osxfuse &&
brew tap glendc/gcc_cross_compilers &&
brew install glendc/gcc_cross_compilers/x64-elf-binutils glendc/gcc_cross_compilers/x64-elf-gcc;
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then
brew update &&
brew install nasm pkg-config Caskroom/cask/osxfuse &&
travis_wait 30 brew install redox-os/gcc_cross_compilers/x86_64-elf-gcc;
fi
- cd cookbook && ./setup.sh && cd ..
script:
- make clean &&
make update &&
make build/harddrive.bin.gz build/livedisk.bin.gz build/livedisk.iso -j 2
- make clean && make travis
notifications:
email: false
webhooks: http://37.139.9.28:54863/travis
@ -29,11 +36,10 @@ deploy:
provider: releases
api_key:
secure: E5w3mgFbW4fAFNJn0FGcvwGKK33d+StC4izDX7dsGPxX/gwAsMnZqabDWpsrj8n/jFI5NdPzuyz4Ojkip4AXrEs0DWfX96d9CSWvJmWIirwwKhALnxZ5cqnHnBXY3wpk9k8MKpdODzKs3ZjM3pPug2jjjp2EHdrEV6iyc8LlnLAJutbtPpNJv0rJrx/TfCZRx70YWKQyx2Lfx5P6Vj+5yoYsKk+SHmKZlIQfj2E1cfC8+/w+fzc9CRTNhM9XFBisKnu9qql3nNhEW8VUNQ9FnltGpunmcTnCmsKzHPfs8Zv6kM/6y3wuoqxwPnIwRu+zsntkjM/eT7Zy3DtTBqJDjq+L5jov50QWOxzjUuFYMv0lAMeMC0PIGn0ECpFs546M+Wqvd7HKgabac0UhilEBPbinOdW+6aOOhbo+Fe2I2ec0XIGxlQpccQeWQUsjjOQ+6QuvnpPE+CbvQaVyrx27rVAkqD44cOP8xqOq2Es651J+Dt0O1OIhLdPB3FxOLCDpEIHU5Ojci1QbUxZgGKjShpo44nNqcTv7v71JrfzFSVG2pF9a35Mpo6bFEkzyQprOyrwH2fcnN+4jyxdJXzdNsgraXsQopWAB5cL/8i7SXMwHy9ivpFaX/zgoHQqpc1a4VjrmTtPA08rLORIllw9CplfvJNsmNmCi2aSeTXR06Xk=
file:
- build/harddrive.bin.gz
- build/livedisk.bin.gz
- build/livedisk.iso
file_glob: true
file: build/travis/*
on:
repo: redox-os/redox
tags: true
condition: $TRAVIS_OS_NAME = linux
skip_cleanup: true

View file

@ -1,17 +1,18 @@
# Contributing to Redox
Thank you for your interest in contributing to Redox! This document is a guide to help newcomers contribute!
There are many ways to help us out and we appreciate all of them.
Thank you for your interest in contributing to Redox! This document will outline the basics of where to start if you wish to contribute to the project. There are many ways to help us out and and we appreciate all of them. We look forward to your contribution!
## Index
* [Communication](#communication)
* [Chat](#chat)
* [Reddit](#reddit)
* [Direct Contributing](#direct-contributing)
* [Low-Hanging Fruit - Easy Targets for Newbies](#easy-targets)
* [GitHub Issues](#gh-issues)
* [GitHub Issues](#issues)
* [Pull Requests](#prs)
* [Discourse](#discourse)
* [Reddit](#reddit)
* [News](#news)
* [Code Contributions](#code-contributions)
* [Low-Hanging Fruit - Easy Targets for Newbies](#easy-targets)
* [Creating a Pull Request](#creating-a-pr)
* [Best Practices/Guidelines](#best-practices)
* [General](#general)
@ -22,10 +23,11 @@ There are many ways to help us out and we appreciate all of them.
* [Git](#git-style-guidelines)
* [Other Ways to Contribute](#other)
* [Graphic Design](#graphic-design)
* [Patreon](#patreon)
## <a name="extern-links"> Other External Links </a>
* [redox-os.org](http://redox-os.org)
* [redox-os.org](https://redox-os.org)
* [rust-os-comparison](https://github.com/flosse/rust-os-comparison)
* [rust-lang.org](http://rust-lang.org)
@ -33,70 +35,77 @@ There are many ways to help us out and we appreciate all of them.
### <a name="chat"> Chat </a>
The quickest and most open way to communicate with the Redox team is on our chat server. Currently, the only way to join it is by sending an email to [info@redox-os.org](mailto:info@redox-os.org), which might take a little while, since it's not automated. We're currently working on an easier way to do this, but this is the most convenient way right now.
The quickest and most open way to communicate with the Redox team is on our chat server. Currently, you can only get an invite by sending an email request to [info@redox-os.org](mailto:info@redox-os.org), which might take a little while, since it's not automated. Simply say you'd like to join the chat. We're working on an better way to do this, but this is the best way right now.
### <a name="issues"> GitHub Issues </a>
A bit more formal way of communication with fellow Redox devs, but a little less quick and convenient like the chat. Submit an issue when you run into problems compiling, testing, or just would like to discuss a certain topic, be it features, code style, code inconsistencies, minor changes and fixes, etc.
### <a name="prs"> Pull Requests </a>
It's fine to just submit a small pull request without first making an issue or asking in the chat, but if it's a significant change that will require a lot of planning and reviewing. Also see [Creating a Pull Request](#creating-a-pr) and [Git Style Guidelines](#git-style-guidelines).
### <a name="discourse"> Discourse </a>
We have a discourse forum at [discourse.redox-os.org](https://discourse.redox-os.org). This is the best way to discuss more general topics that aren't about specific things that need to be addressed one way or another. You can sign up like any other website.
### <a name="reddit"> Reddit </a>
You can find Redox on Reddit in [/r/rust/](https://www.reddit.com/r/rust) and [/r/redox/](https://www.reddit.com/r/redox). The weekly update news is posted on the former.
You can also find Redox on Reddit in [/r/rust/](https://www.reddit.com/r/rust) and [/r/redox/](https://www.reddit.com/r/redox). Redox news and discussion is posted on the latter, and Rust news and discussion, as well as some Redox posts, is on the former.
## <a name="direct-contributing"> Direct Contributing </a>
### <a name="news"> News </a>
News and updates for Redox are posted at [redox-os.org/news](https://redox-os.org/news). It's more one-way than the other things on this list, but it should provide a good summary of what's been going on with the project lately. It's usually updated weekly, but with some exceptions. A mailing list may be included eventually, but it's not set up right now.
## <a name="code-contributions"> Code Contributions </a>
### <a name="easy-targets"> Low-Hanging Fruit - Easy Targets for Newbies </a>
* If you're not fluent in Rust:
#### If you're not fluent in Rust:
* Writing documentation
* Using/testing Redox, filing issues for bugs and needed features
* Web development ([Redox website, separate repo](https://github.com/redox-os/website))
* Writing unit tests (may require minimal knowledge of rust)
* If you are fluent in Rust, but not OS Development:
#### If you are fluent in Rust, but not OS Development:
* Apps development
* Shell ([Ion](https://github.com/redox-os/ion)) development
* Package manager ([Magnet](https://github.com/redox-os/magnet)) development
* Package management ([pkgutils](https://github.com/redox-os/pkgutils)) development
* Other high-level code tasks
* If you are fluent in Rust, and have experience with OS Dev:
#### If you are fluent in Rust, and have experience with OS Dev:
* Familiarize yourself with the repository and codebase
* Grep for `TODO`, `FIXME`, `BUG`, `UNOPTIMIZED`, `REWRITEME`, `DOCME`, and `PRETTYFYME` and fix the code you find.
* Improve and optimize code, especially in the kernel
### <a name="gh-issues"> GitHub Issues </a>
A bit more formal way of communication with fellow Redox devs, but a little less quick and convenient like the chat (unless of course you aren't in it yet, which if you're going to be involved in this project really at all, it is recommended that you request to join). These are for more specific topics.
### <a name="prs"> Pull Requests </a>
It's completely okay to just submit a small pull request without first making an issue or something, but if it's a significant change that will require a lot of planning and reviewing, it's best you start with writing an issue first. Also see [git guidelines](#git-style-guidelines)
### <a name="creating-a-pr"> Creating a Pull Request </a>
1. Fork the repository
2. Clone the original repository to your local PC using one of the following commands based on the protocol you are using:
* HTTPS:`git clone https://github.com/redox-os/redox.git --origin upstream --recursive`
* SSH:`git clone git@github.com:redox-os/redox.git --origin upstream --recursive`
* Then rebase: `git rebase upstream master`
Use HTTPS if you don't know which one to use. (Recommended: learn about SSH if you don't want to have to log in every time you push/pull!)
* Then rebase: `git rebase upstream master`
If you use HTTPS, you will have to log in each time when pushing to your fork. (Recommended: learn about git SSH support, it logs in automatically using SSH keys)
3. Add your fork with
* HTTPS:`git remote add origin https://github.com/your-username/redox.git`
* SSH:`git remote add origin git@github.com:your-username/redox.git`
4. Alternatively, if you already have a fork and copy of the repo, you can simply check to make sure you're up-to-date
* Fetch the upstream:`git fetch upstream master`
* Rebase with local commits:`git rebase upstream/master`
* Update the submodules:`git submodule update --init`
5. Optionally create a separate branch (recommended if you're making multiple changes simultaneously) (`git checkout -b my-branch`)
* Pull the upstream:`git pull upstream --rebase`
* Update the submodules:`git submodule update --recursive --init`
5. Create a separate branch (recommended if you're making multiple changes simultaneously) (`git checkout -b my-branch`)
6. Make changes
7. Commit (`git add . --all; git commit -m "my commit"`)
7. Commit (`git add <item(s) you changed>; git commit`) and write your commit message
8. Optionally run [rustfmt](https://github.com/rust-lang-nursery/rustfmt) on the files you changed and commit again if it did anything (check with `git diff` first)
9. Test your changes with `make qemu` or `make virtualbox` (you might have to use `make qemu kvm=no`, formerly `make qemu_no_kvm`)
9. Test your changes by cleaning (`make clean; git clean -Xfd`) and building with `make qemu` (you might have to use `make qemu kvm=no`) or `make virtualbox`.
(see [Best Practices and Guidelines](#best-practices))
10. Pull from upstream (`git fetch upstream; git rebase upstream/master`) (Note: try not to use `git pull`, it is equivalent to doing `git fetch upstream; git merge master upstream/master`, which is not usually preferred for local/fork repositories, although it is fine in some cases.)
10. Pull from upstream (`git pull upstream --rebase`) (Note: Make sure to include `--rebase`, as it will apply your changes on top of the changes you just pulled, allowing for a much cleaner merge)
11. Repeat step 9 to make sure the rebase still builds and starts
12. Push to your fork (`git push origin my-branch`)
12. Push to your fork (`git push origin <branch>`), `<branch>` being the branch you created earlier
13. Create a pull request
14. Describe your changes
14. If your changes are minor, you can just describe them in a paragraph or less. If they're major, please fill out the provided form.
15. Submit!
## <a name="best-practices"> Best Practices and Guidelines </a>
@ -105,36 +114,36 @@ It's completely okay to just submit a small pull request without first making an
* **Remember to do a `git rebase -i upstream/master` before you send your patch!**
* **Make sure your code is readable, commented, and well-documented.**
* **Don't hesitate to ask for help!**
* **Before implementing something, discuss it! Open an issue, or join the chat.**
* **Don't hesitate to ask for help, comments or suggestions!**
* **Before implementing something, discuss it! Open an issue, or ask in the chat.**
##### On the more technical side:
* Test, test, and test!
* Follow the style conventions
* Follow the style conventions (See [rust style guidelines](#rust-style-guidelines))
* Use `std::mem::replace` and `std::mem::swap` when you can.
* `libredox` should be 1-to-1 with the official `libstd`.
* Use `.into()` and `.to_owned()` over `.to_string()`.
* Prefer `.into()` and `.to_owned()` over `.to_string()`.
* Prefer passing references to the data over owned data. (Don't take `String`, take `&str`. Don't take `Vec<T>` take `&[T]`).
* Use generics, traits, and other abstractions Rust provides.
* Avoid using lossy conversions (for example: don't do `my_u32 as u16 == my_u16`, prefer `my_u32 == my_u16 as my_u32`).
* Prefer in place (`box` keyword) when doing heap allocations.
* Prefer platform independently sized integer over pointer sized integer (`u32` over `usize`, for example).
* Follow the usual idioms of programming, such as "composition over inheritance", "let your program be divided in smaller pieces", and "resource acquisition is initialization".
* When `unsafe` is unnecessary, don't use it. 10 lines longer safe code is better than more compact unsafe code!
* Be sure to mark parts that need work with `TODO`, `FIXME`, `BUG`, `UNOPTIMIZED`, `REWRITEME`, `DOCME`, and `PRETTYFYME`.
* When `unsafe` is unnecessary, don't use it. **Longer safe code is better than shorter unsafe code!**
* Be sure to mark parts that need work with `TODO`, `FIXME`, `BUG`, `UNOPTIMIZED`, `REWRITEME`, `DOCME`, and `PRETTYFYME`. Always elaborate on these messages, too. Nothing is more annoying than seeing a `TODO` and not knowing how to actually fix it.
* Use the compiler hint attributes, such as `#[inline]`, `#[cold]`, etc. when it makes sense to do so.
* Check the [chat](#chat), [the Website](http://redox-os.org), and [the Rust subreddit](https://www.reddit.com/r/rust) frequently.
* Check the [chat](#chat), [the website](http://redox-os.org/news), and [the Rust subreddit](https://www.reddit.com/r/rust) frequently.
### <a name="kernel"> Kernel </a>
* When trying to access a slice, **always** use the `common::GetSlice` trait and the `.get_slice()` method to get a slice without causing the kernel to panic.
The problem with slicing in regular Rust, e.g. `foo[a..b]`, is that if someone tries to access with a range that is out of bounds of an array/string/slice, it will cause a panic at runtime, as a safety measure. Same thing when accessing an element.
* When trying to access a slice, **always** use the `common::GetSlice` trait and the `.get_slice()` method to get a slice without causing the kernel to panic.
The problem with slicing in regular Rust, e.g. `foo[a..b]`, is that if someone tries to access with a range that is out of bounds of an array/string/slice, it will cause a panic at runtime, as a safety measure. Same thing when accessing an element.
Always use `foo.get(n)` instead of `foo[n]` and try to cover for the possibility of `Option::None`. Doing the regular way may work fine for applications, but never in the kernel. No possible panics should ever exist in kernel space, because then the whole OS would just stop working.
### <a name="testing-practices"> Testing Practices </a>
* It's always better to test boot (`make qemu` or `make virtualbox`) every time you make a change, because it is important to see how the OS boots and works after it compiles.
Even though Rust is a safety-oriented language, something as unstable as an in-dev operating system will have problems in many cases and may completely break on even the slightest critical change.
* It's always better to test boot (`make qemu` or `make virtualbox`) every time you make a change, because it is important to see how the OS boots and works after it compiles.
Even though Rust is a safety-oriented language, something as unstable and low-level as an in-dev operating system will almost certainly have problems in many cases and may completely break on even the slightest critical change.
Also, make sure you check how the unmodified version runs on your machine before making any changes. Else, you won't have anything to compare to, and it will generally just lead to confusion. TLDR: Rebuild and test boot often.
* To run the ZFS tests:
@ -150,14 +159,33 @@ Since Rust is a relatively small and new language compared to others like C, the
### <a name="git-style-guidelines"> Git </a>
* Commit messages should describe their changes in present tense, e.g. "`Add stuff to file.ext`" instead of "`added stuff to file.ext`".
* Try to remove useless duplicate/merge commits from PRs as these clutter up history, and may make it hard to read.
* Usually, when syncing your local copy with the master branch, you will want to rebase instead of merge. This is because it will create duplicate commits that don't actually do anything when merged into the master branch.
* When you start to make changes, you will want to create a separate branch, and keep the `master` branch of your fork identical to the main repository, so that you can compare your changes with the main branch and test out a more stable build if you need to.
* You should have a fork of the repository on GitHub and a local copy on your computer. The local copy should have two remotes; `upstream` and `origin`, `upstream` should be set to the main repository and `origin` should be your fork.
* When you start to make changes, you will want to create a separate branch, and keep the `master` branch of your fork identical to the main repository, so that you can compare your changes with the main branch and test out a more stable build if you need to.
* Usually, when syncing your local copy with the master branch, you'll want to rebase instead of merge. This is because it will create duplicate commits that don't actually do anything when merged into the master branch. You can do this in one command with `git pull upstream --rebase`. This will pull from the upstream, then roll back to the current state of the upstream, and "replay" your changes on top of it. Make sure you commit before doing this, though. Git won't be able to rebase if you don't.
* Prefer to omit the `-m` when using `git commit`. This opens your editor and should help get you in the habit of writing longer commit messages.
* Commit messages should describe their changes in present tense, e.g. "`Add stuff to file.ext`" instead of "`added stuff to file.ext`". This makes sense as sometimes when you revert back, then run through commits one-by-one, you want to see what a commit will do, instead of just what the person did when they made the commit. It's also just being consistent.
* Try to remove useless duplicate/merge commits from PRs as these don't do anything except clutter up history and make it harder to read.
## <a name="other"> Other Ways to Contribute </a>
### <a name="graphic-design"> Graphic Design </a>
If you're not big on coding, but you still want to help keep the project going, you can still contribute/support in a variety of ways! We'll try to find a way to use anything you have to offer.
### <a name="design"> Design </a>
If you're a good designer, whether it's 2D graphics, 3D graphics, interfaces, web design, you can help. We need logos, UI design, UI skins, app icons, desktop backgrounds, etc. More information to come on this in the future, for now just join [the chat](#chat) and ask about graphic design.
### <a name="patreon"> Patreon </a>
Our BDFL, [jackpot51](https://github.com/jackpot51), has a [Patreon campaign](https://www.patreon.com/redox_os)! All money recieved will go towards Redox OS development. If you donate, you will be listed in the Redox credits as one of the people that made Redox OS possible. You'll also get other rewards the more you donate. However, please don't donate if you can't afford it.
<!--
POSSIBLE OTHER TOPICS TO INCLUDE
- Merch (maybe in the future)
- Sound Design (things like notifications, popups, etc. for orbital)
- Video Production/Motion Graphics (tutorials, introduction videos, etc.)
- Non-Rust programming, scripting, etc. (if we even have a need for this)
- Hosting/download/git mirrors (this is not needed right now, but when downloads increase it may be necessary)
-->
If you're a good designer, you can help with logos, UI design, app icons, other graphics (e.g. stock desktop backgrounds), etc. More information to come on this, for now just join [the chat](#chat) and ask about graphic design.

View file

@ -1,37 +0,0 @@
[package]
name = "kernel"
version = "0.1.0"
[lib]
name = "kernel"
path = "kernel/lib.rs"
crate-type = ["staticlib"]
[dependencies]
bitflags = "*"
spin = "*"
redox_syscall = { path = "syscall/" }
[dependencies.goblin]
git = "https://github.com/m4b/goblin.git"
default-features = false
features = ["elf32", "elf64"]
[dev-dependencies]
arch_test = { path = "arch/test" }
[target.'cfg(target_arch = "arm")'.dependencies]
arch_arm = { path = "arch/arm" }
[target.'cfg(target_arch = "x86_64")'.dependencies]
arch_x86_64 = { path = "arch/x86_64" }
[features]
default = []
live = []
[profile.dev]
panic = "unwind"
[profile.release]
panic = "abort"

646
Makefile
View file

@ -1,31 +1,5 @@
ARCH?=x86_64
ROOT=$(PWD)
export RUST_TARGET_PATH=$(ROOT)/targets
#TODO: Use libssp
export CFLAGS=-fno-stack-protector -U_FORTIFY_SOURCE
# Kernel variables
KTARGET=$(ARCH)-unknown-none
KBUILD=build/kernel
KRUSTC=./krustc.sh
KRUSTCFLAGS=--target $(KTARGET) -C opt-level=2 -C debuginfo=0 -C soft-float
KRUSTDOC=./krustdoc.sh
KCARGO=RUSTC="$(KRUSTC)" RUSTDOC="$(KRUSTDOC)" cargo
KCARGOFLAGS=--target $(KTARGET) --release -- -C soft-float
# Userspace variables
TARGET=$(ARCH)-unknown-redox
BUILD=build/userspace
RUSTC=./rustc.sh
RUSTCFLAGS=--target $(TARGET).json -C opt-level=2 -C debuginfo=0
RUSTDOC=./rustdoc.sh
CARGO=RUSTC="$(RUSTC)" RUSTDOC="$(RUSTDOC)" cargo
CARGOFLAGS=--target $(TARGET).json --release --
# Default targets
.PHONY: all live iso clean doc ref test update pull qemu bochs drivers schemes binutils coreutils extrautils netutils userutils wireshark FORCE
# Configuration and variables
include mk/config.mk
all: build/harddrive.bin
@ -33,130 +7,15 @@ live: build/livedisk.bin
iso: build/livedisk.iso
FORCE:
clean:
cargo clean
cargo clean --manifest-path rust/src/libstd/Cargo.toml
cargo clean --manifest-path drivers/ahcid/Cargo.toml
cargo clean --manifest-path drivers/e1000d/Cargo.toml
cargo clean --manifest-path drivers/ps2d/Cargo.toml
cargo clean --manifest-path drivers/pcid/Cargo.toml
cargo clean --manifest-path drivers/rtl8168d/Cargo.toml
cargo clean --manifest-path drivers/vesad/Cargo.toml
cargo clean --manifest-path programs/acid/Cargo.toml
cargo clean --manifest-path programs/contain/Cargo.toml
cargo clean --manifest-path programs/init/Cargo.toml
cargo clean --manifest-path programs/ion/Cargo.toml
cargo clean --manifest-path programs/binutils/Cargo.toml
cargo clean --manifest-path programs/coreutils/Cargo.toml
cargo clean --manifest-path programs/extrautils/Cargo.toml
cargo clean --manifest-path programs/games/Cargo.toml
cargo clean --manifest-path programs/netutils/Cargo.toml
cargo clean --manifest-path programs/orbutils/Cargo.toml
cargo clean --manifest-path programs/pkgutils/Cargo.toml
cargo clean --manifest-path programs/userutils/Cargo.toml
cargo clean --manifest-path programs/smith/Cargo.toml
cargo clean --manifest-path programs/tar/Cargo.toml
cargo clean --manifest-path schemes/ethernetd/Cargo.toml
cargo clean --manifest-path schemes/example/Cargo.toml
cargo clean --manifest-path schemes/ipd/Cargo.toml
cargo clean --manifest-path schemes/orbital/Cargo.toml
cargo clean --manifest-path schemes/ptyd/Cargo.toml
cargo clean --manifest-path schemes/randd/Cargo.toml
cargo clean --manifest-path schemes/redoxfs/Cargo.toml
cargo clean --manifest-path schemes/tcpd/Cargo.toml
cargo clean --manifest-path schemes/udpd/Cargo.toml
-$(FUMOUNT) build/filesystem/
rm -rf initfs/bin
rm -rf filesystem/bin filesystem/sbin filesystem/ui/bin
cd cookbook && ./clean.sh
cargo clean --manifest-path cookbook/pkgutils/Cargo.toml
cargo clean --manifest-path installer/Cargo.toml
cargo clean --manifest-path kernel/Cargo.toml
cargo clean --manifest-path redoxfs/Cargo.toml
-$(FUMOUNT) build/filesystem/ || true
rm -rf build
doc: \
doc-kernel \
doc-std
#FORCE to let cargo decide if docs need updating
doc-kernel: $(KBUILD)/libkernel.a FORCE
$(KCARGO) doc --target $(KTARGET).json
doc-std: $(BUILD)/libstd.rlib FORCE
$(CARGO) doc --target $(TARGET).json --manifest-path rust/src/libstd/Cargo.toml
ref: FORCE
rm -rf filesystem/ref/
mkdir -p filesystem/ref/
cargo run --manifest-path crates/docgen/Cargo.toml -- programs/binutils/src/bin/ filesystem/ref/
cargo run --manifest-path crates/docgen/Cargo.toml -- programs/coreutils/src/bin/ filesystem/ref/
cargo run --manifest-path crates/docgen/Cargo.toml -- programs/extrautils/src/bin/ filesystem/ref/
cargo run --manifest-path crates/docgen/Cargo.toml -- programs/netutils/src/ filesystem/ref/
test:
cargo test
cargo test --manifest-path rust/src/libstd/Cargo.toml
cargo test --manifest-path drivers/ahcid/Cargo.toml
cargo test --manifest-path drivers/e1000d/Cargo.toml
cargo test --manifest-path drivers/ps2d/Cargo.toml
cargo test --manifest-path drivers/pcid/Cargo.toml
cargo test --manifest-path drivers/rtl8168d/Cargo.toml
cargo test --manifest-path drivers/vesad/Cargo.toml
cargo test --manifest-path programs/acid/Cargo.toml
cargo test --manifest-path programs/contain/Cargo.toml
cargo test --manifest-path programs/init/Cargo.toml
cargo test --manifest-path programs/ion/Cargo.toml
cargo test --manifest-path programs/binutils/Cargo.toml
cargo test --manifest-path programs/coreutils/Cargo.toml
cargo test --manifest-path programs/extrautils/Cargo.toml
cargo test --manifest-path programs/games/Cargo.toml
cargo test --manifest-path programs/netutils/Cargo.toml
cargo test --manifest-path programs/orbutils/Cargo.toml
cargo test --manifest-path programs/pkgutils/Cargo.toml
cargo test --manifest-path programs/userutils/Cargo.toml
cargo test --manifest-path programs/smith/Cargo.toml
cargo test --manifest-path programs/tar/Cargo.toml
cargo test --manifest-path schemes/ethernetd/Cargo.toml
cargo test --manifest-path schemes/example/Cargo.toml
cargo test --manifest-path schemes/ipd/Cargo.toml
cargo test --manifest-path schemes/orbital/Cargo.toml
cargo test --manifest-path schemes/ptyd/Cargo.toml
cargo test --manifest-path schemes/randd/Cargo.toml
cargo test --manifest-path schemes/redoxfs/Cargo.toml
cargo test --manifest-path schemes/tcpd/Cargo.toml
cargo test --manifest-path schemes/udpd/Cargo.toml
update:
cargo update
cargo update --manifest-path rust/src/libstd/Cargo.toml
cargo update --manifest-path drivers/ahcid/Cargo.toml
cargo update --manifest-path drivers/e1000d/Cargo.toml
cargo update --manifest-path drivers/ps2d/Cargo.toml
cargo update --manifest-path drivers/pcid/Cargo.toml
cargo update --manifest-path drivers/rtl8168d/Cargo.toml
cargo update --manifest-path drivers/vesad/Cargo.toml
cargo update --manifest-path programs/acid/Cargo.toml
cargo update --manifest-path programs/contain/Cargo.toml
cargo update --manifest-path programs/init/Cargo.toml
cargo update --manifest-path programs/ion/Cargo.toml
cargo update --manifest-path programs/binutils/Cargo.toml
cargo update --manifest-path programs/coreutils/Cargo.toml
cargo update --manifest-path programs/extrautils/Cargo.toml
cargo update --manifest-path programs/games/Cargo.toml
cargo update --manifest-path programs/netutils/Cargo.toml
cargo update --manifest-path programs/orbutils/Cargo.toml
cargo update --manifest-path programs/pkgutils/Cargo.toml
cargo update --manifest-path programs/userutils/Cargo.toml
cargo update --manifest-path programs/smith/Cargo.toml
cargo update --manifest-path programs/tar/Cargo.toml
cargo update --manifest-path schemes/ethernetd/Cargo.toml
cargo update --manifest-path schemes/example/Cargo.toml
cargo update --manifest-path schemes/ipd/Cargo.toml
cargo update --manifest-path schemes/orbital/Cargo.toml
cargo update --manifest-path schemes/ptyd/Cargo.toml
cargo update --manifest-path schemes/randd/Cargo.toml
cargo update --manifest-path schemes/redoxfs/Cargo.toml
cargo update --manifest-path schemes/tcpd/Cargo.toml
cargo update --manifest-path schemes/udpd/Cargo.toml
pull:
git pull --rebase --recurse-submodules
git submodule sync
@ -165,472 +24,49 @@ pull:
make clean
make update
# Emulation
QEMU=SDL_VIDEO_X11_DGAMOUSE=0 qemu-system-$(ARCH)
QEMUFLAGS=-serial mon:stdio -d cpu_reset -d guest_errors
ifeq ($(ARCH),arm)
QEMUFLAGS+=-cpu arm1176 -machine integratorcp
QEMUFLAGS+=-nographic
update:
cd cookbook; \
./update.sh "$$(cargo run --manifest-path ../installer/Cargo.toml -- --list-packages ../initfs.toml ../filesystem.toml)"
cargo update --manifest-path cookbook/pkgutils/Cargo.toml
cargo update --manifest-path installer/Cargo.toml
cargo update --manifest-path kernel/Cargo.toml
cargo update --manifest-path redoxfs/Cargo.toml
export CC=$(ARCH)-none-eabi-gcc
export CXX=$(ARCH)-none-eabi-g++
export LD=$(ARCH)-none-eabi-ld
fetch:
cd cookbook; \
./fetch.sh "$$(cargo run --manifest-path ../installer/Cargo.toml -- --list-packages ../initfs.toml ../filesystem.toml)"
KRUSTCFLAGS+=-C linker=$(CC)
KCARGOFLAGS+=-C linker=$(CC)
RUSTCFLAGS+=-C linker=$(CC)
CARGOFLAGS+=-C linker=$(CC)
# Emulation recipes
include mk/qemu.mk
include mk/bochs.mk
include mk/virtualbox.mk
%.list: %
$(ARCH)-none-eabi-objdump -C -D $< > $@
# Kernel recipes
include mk/kernel.mk
build/harddrive.bin: $(KBUILD)/kernel
cp $< $@
# Filesystem recipes
include mk/initfs.mk
include mk/filesystem.mk
qemu: build/harddrive.bin
$(QEMU) $(QEMUFLAGS) -kernel $<
else
QEMUFLAGS+=-smp 4 -m 1024
ifeq ($(iommu),yes)
QEMUFLAGS+=-machine q35,iommu=on
else
QEMUFLAGS+=-machine q35
endif
ifeq ($(net),no)
QEMUFLAGS+=-net none
else
QEMUFLAGS+=-net nic,model=e1000 -net user -net dump,file=build/network.pcap
ifeq ($(net),redir)
QEMUFLAGS+=-redir tcp:8080::8080
endif
endif
ifeq ($(vga),no)
QEMUFLAGS+=-nographic -vga none
endif
#,int,pcall
#-device intel-iommu
# Disk images
include mk/disk.mk
UNAME := $(shell uname)
ifeq ($(UNAME),Darwin)
export CC=$(ARCH)-elf-gcc
export CXX=$(ARCH)-elf-g++
ECHO=/bin/echo
FUMOUNT=sudo umount
export LD=$(ARCH)-elf-ld
export LDFLAGS=--gc-sections
VB_AUDIO=coreaudio
VBM="/Applications/VirtualBox.app/Contents/MacOS/VBoxManage"
else
export CC=gcc
export CXX=g++
ECHO=echo
FUMOUNT=fusermount -u
export LD=ld
export LDFLAGS=--gc-sections
ifneq ($(kvm),no)
QEMUFLAGS+=-enable-kvm -cpu host
endif
VB_AUDIO="pulse"
VBM=VBoxManage
endif
# Travis target
travis: FORCE
INSTALLER_FLAGS= make build/harddrive.bin.gz build/livedisk.iso
rm -rf build/travis
mkdir build/travis
mv build/harddrive.bin.gz build/travis/redox_$(TRAVIS_TAG).bin.gz
mv build/livedisk.iso build/travis/redox_$(TRAVIS_TAG).iso
cd build/travis && sha256sum -b redox_$(TRAVIS_TAG).bin.gz redox_$(TRAVIS_TAG).iso > SHA256SUM
KRUSTCFLAGS+=-C linker=$(CC)
KCARGOFLAGS+=-C linker=$(CC)
RUSTCFLAGS+=-C linker=$(CC)
CARGOFLAGS+=-C linker=$(CC)
# An empty target
FORCE:
# A method of creating a listing for any binary
%.list: %
objdump -C -M intel -D $< > $@
build/harddrive.bin: $(KBUILD)/kernel bootloader/$(ARCH)/** build/filesystem.bin
nasm -f bin -o $@ -D ARCH_$(ARCH) -ibootloader/$(ARCH)/ bootloader/$(ARCH)/harddrive.asm
build/livedisk.bin: $(KBUILD)/kernel_live bootloader/$(ARCH)/**
nasm -f bin -o $@ -D ARCH_$(ARCH) -ibootloader/$(ARCH)/ bootloader/$(ARCH)/livedisk.asm
build/%.bin.gz: build/%.bin
gzip -k -f $<
build/livedisk.iso: build/livedisk.bin.gz
rm -rf build/iso/
mkdir -p build/iso/
cp -RL isolinux build/iso/
cp $< build/iso/livedisk.gz
genisoimage -o $@ -b isolinux/isolinux.bin -c isolinux/boot.cat \
-no-emul-boot -boot-load-size 4 -boot-info-table \
build/iso/
isohybrid $@
qemu: build/harddrive.bin
$(QEMU) $(QEMUFLAGS) -drive file=$<,format=raw
qemu_extra: build/harddrive.bin
if [ ! -e build/extra.bin ]; then dd if=/dev/zero of=build/extra.bin bs=1M count=1024; fi
$(QEMU) $(QEMUFLAGS) -drive file=$<,format=raw -drive file=build/extra.bin,format=raw
qemu_no_build:
$(QEMU) $(QEMUFLAGS) -drive file=build/harddrive.bin,format=raw
qemu_live: build/livedisk.bin
$(QEMU) $(QEMUFLAGS) -device usb-ehci,id=flash_bus -drive id=flash_drive,file=$<,format=raw,if=none -device usb-storage,drive=flash_drive,bus=flash_bus.0
qemu_live_no_build:
$(QEMU) $(QEMUFLAGS) -device usb-ehci,id=flash_bus -drive id=flash_drive,file=build/livedisk.bin,format=raw,if=none -device usb-storage,drive=flash_drive,bus=flash_bus.0
qemu_iso: build/livedisk.iso
$(QEMU) $(QEMUFLAGS) -boot d -cdrom $<
qemu_iso_no_build:
$(QEMU) $(QEMUFLAGS) -boot d -cdrom build/livedisk.iso
endif
bochs: build/harddrive.bin
bochs -f bochs.$(ARCH)
virtualbox: build/harddrive.bin
echo "Delete VM"
-$(VBM) unregistervm Redox --delete; \
if [ $$? -ne 0 ]; \
then \
if [ -d "$$HOME/VirtualBox VMs/Redox" ]; \
then \
echo "Redox directory exists, deleting..."; \
$(RM) -rf "$$HOME/VirtualBox VMs/Redox"; \
fi \
fi
echo "Delete Disk"
-$(RM) harddrive.vdi
echo "Create VM"
$(VBM) createvm --name Redox --register
echo "Set Configuration"
$(VBM) modifyvm Redox --memory 1024
$(VBM) modifyvm Redox --vram 16
if [ "$(net)" != "no" ]; \
then \
$(VBM) modifyvm Redox --nic1 nat; \
$(VBM) modifyvm Redox --nictype1 82540EM; \
$(VBM) modifyvm Redox --cableconnected1 on; \
$(VBM) modifyvm Redox --nictrace1 on; \
$(VBM) modifyvm Redox --nictracefile1 build/network.pcap; \
fi
$(VBM) modifyvm Redox --uart1 0x3F8 4
$(VBM) modifyvm Redox --uartmode1 file build/serial.log
$(VBM) modifyvm Redox --usb off # on
$(VBM) modifyvm Redox --keyboard ps2
$(VBM) modifyvm Redox --mouse ps2
$(VBM) modifyvm Redox --audio $(VB_AUDIO)
$(VBM) modifyvm Redox --audiocontroller ac97
$(VBM) modifyvm Redox --nestedpaging off
echo "Create Disk"
$(VBM) convertfromraw $< build/harddrive.vdi
echo "Attach Disk"
$(VBM) storagectl Redox --name ATA --add sata --controller IntelAHCI --bootable on --portcount 1
$(VBM) storageattach Redox --storagectl ATA --port 0 --device 0 --type hdd --medium build/harddrive.vdi
echo "Run VM"
$(VBM) startvm Redox
# Kernel recipes
$(KBUILD)/libcore.rlib: rust/src/libcore/lib.rs
mkdir -p $(KBUILD)
$(KRUSTC) $(KRUSTCFLAGS) -o $@ $<
$(KBUILD)/librand.rlib: rust/src/librand/lib.rs $(KBUILD)/libcore.rlib
$(KRUSTC) $(KRUSTCFLAGS) -o $@ $<
$(KBUILD)/liballoc.rlib: rust/src/liballoc/lib.rs $(KBUILD)/libcore.rlib
$(KRUSTC) $(KRUSTCFLAGS) -o $@ $<
$(KBUILD)/libstd_unicode.rlib: rust/src/libstd_unicode/lib.rs $(KBUILD)/libcore.rlib
$(KRUSTC) $(KRUSTCFLAGS) -o $@ $<
$(KBUILD)/libcollections.rlib: rust/src/libcollections/lib.rs $(KBUILD)/libcore.rlib $(KBUILD)/liballoc.rlib $(KBUILD)/libstd_unicode.rlib
$(KRUSTC) $(KRUSTCFLAGS) -o $@ $<
$(KBUILD)/libkernel.a: kernel/** $(KBUILD)/libcore.rlib $(KBUILD)/liballoc.rlib $(KBUILD)/libcollections.rlib $(BUILD)/initfs.rs
$(KCARGO) rustc $(KCARGOFLAGS) -C lto -o $@
$(KBUILD)/libkernel_live.a: kernel/** $(KBUILD)/libcore.rlib $(KBUILD)/liballoc.rlib $(KBUILD)/libcollections.rlib $(BUILD)/initfs.rs build/filesystem.bin
$(KCARGO) rustc --lib $(KCARGOFLAGS) --cfg 'feature="live"' -C lto --emit obj=$@
$(KBUILD)/kernel: $(KBUILD)/libkernel.a
$(LD) $(LDFLAGS) -z max-page-size=0x1000 -T arch/$(ARCH)/src/linker.ld -o $@ $<
$(KBUILD)/kernel_live: $(KBUILD)/libkernel_live.a
$(LD) $(LDFLAGS) -z max-page-size=0x1000 -T arch/$(ARCH)/src/linker.ld -o $@ $<
# Userspace recipes
$(BUILD)/libstd.rlib: rust/src/libstd/Cargo.toml rust/src/libstd/**
mkdir -p $(BUILD)
$(CARGO) rustc --verbose --manifest-path $< $(CARGOFLAGS) -L native=libc-artifacts/lib -o $@
cp rust/src/target/$(TARGET)/release/deps/*.rlib $(BUILD)
initfs/bin/%: drivers/%/Cargo.toml drivers/%/src/** $(BUILD)/libstd.rlib
mkdir -p initfs/bin
$(CARGO) rustc --manifest-path $< $(CARGOFLAGS) -o $@
strip $@
initfs/bin/%: programs/%/Cargo.toml programs/%/src/** $(BUILD)/libstd.rlib
mkdir -p initfs/bin
$(CARGO) rustc --manifest-path $< $(CARGOFLAGS) -o $@
#strip $@
initfs/bin/%: schemes/%/Cargo.toml schemes/%/src/** $(BUILD)/libstd.rlib
mkdir -p initfs/bin
$(CARGO) rustc --manifest-path $< --bin $* $(CARGOFLAGS) -o $@
strip $@
$(BUILD)/initfs.rs: \
initfs/bin/init \
initfs/bin/ahcid \
initfs/bin/bgad \
initfs/bin/pcid \
initfs/bin/ps2d \
initfs/bin/redoxfs \
initfs/bin/vesad \
initfs/etc/**
echo 'use collections::BTreeMap;' > $@
echo 'pub fn gen() -> BTreeMap<&'"'"'static [u8], (&'"'"'static [u8], bool)> {' >> $@
echo ' let mut files: BTreeMap<&'"'"'static [u8], (&'"'"'static [u8], bool)> = BTreeMap::new();' >> $@
for folder in `find initfs -type d | sort`; do \
name=$$(echo $$folder | sed 's/initfs//' | cut -d '/' -f2-) ; \
$(ECHO) -n ' files.insert(b"'$$name'", (b"' >> $@ ; \
ls -1 $$folder | sort | awk 'NR > 1 {printf("\\n")} {printf("%s", $$0)}' >> $@ ; \
echo '", true));' >> $@ ; \
done
find initfs -type f -o -type l | cut -d '/' -f2- | sort | awk '{printf(" files.insert(b\"%s\", (include_bytes!(\"../../initfs/%s\"), false));\n", $$0, $$0)}' >> $@
echo ' files' >> $@
echo '}' >> $@
filesystem/sbin/%: drivers/%/Cargo.toml drivers/%/src/** $(BUILD)/libstd.rlib
mkdir -p filesystem/sbin
$(CARGO) rustc --manifest-path $< $(CARGOFLAGS) -o $@
strip $@
filesystem/bin/%: programs/%/Cargo.toml programs/%/src/** $(BUILD)/libstd.rlib
mkdir -p filesystem/bin
$(CARGO) rustc --manifest-path $< --bin $* $(CARGOFLAGS) -o $@
strip $@
filesystem/bin/sh: filesystem/bin/ion
cp $< $@
filesystem/bin/%: programs/binutils/Cargo.toml programs/binutils/src/bin/%.rs $(BUILD)/libstd.rlib
mkdir -p filesystem/bin
$(CARGO) rustc --manifest-path $< --bin $* $(CARGOFLAGS) -o $@
strip $@
filesystem/bin/%: programs/coreutils/Cargo.toml programs/coreutils/src/bin/%.rs $(BUILD)/libstd.rlib
mkdir -p filesystem/bin
$(CARGO) rustc --manifest-path $< --bin $* $(CARGOFLAGS) -o $@
strip $@
filesystem/bin/%: programs/extrautils/Cargo.toml programs/extrautils/src/bin/%.rs $(BUILD)/libstd.rlib
mkdir -p filesystem/bin
$(CARGO) rustc --manifest-path $< --bin $* $(CARGOFLAGS) -o $@
strip $@
filesystem/bin/%: programs/games/Cargo.toml programs/games/src/%/**.rs $(BUILD)/libstd.rlib
mkdir -p filesystem/bin
$(CARGO) rustc --manifest-path $< --bin $* $(CARGOFLAGS) -o $@
strip $@
filesystem/bin/%: programs/netutils/Cargo.toml programs/netutils/src/%/**.rs $(BUILD)/libstd.rlib
mkdir -p filesystem/bin
$(CARGO) rustc --manifest-path $< --bin $* $(CARGOFLAGS) -o $@
strip $@
filesystem/ui/bin/%: programs/orbutils/Cargo.toml programs/orbutils/src/%/**.rs $(BUILD)/libstd.rlib
mkdir -p filesystem/ui/bin
$(CARGO) rustc --manifest-path $< --bin $* $(CARGOFLAGS) -o $@
strip $@
filesystem/bin/%: programs/pkgutils/Cargo.toml programs/pkgutils/src/%/**.rs $(BUILD)/libstd.rlib
mkdir -p filesystem/bin
$(CARGO) rustc --manifest-path $< --bin $* $(CARGOFLAGS) -o $@
strip $@
filesystem/bin/%: programs/userutils/Cargo.toml programs/userutils/src/bin/%.rs $(BUILD)/libstd.rlib
mkdir -p filesystem/bin
$(CARGO) rustc --manifest-path $< --bin $* $(CARGOFLAGS) -o $@
strip $@
filesystem/sbin/%: schemes/%/Cargo.toml schemes/%/src/** $(BUILD)/libstd.rlib
mkdir -p filesystem/sbin
$(CARGO) rustc --manifest-path $< --bin $* $(CARGOFLAGS) -o $@
strip $@
filesystem/sbin/redoxfs-mkfs: schemes/redoxfs/Cargo.toml schemes/redoxfs/src/** $(BUILD)/libstd.rlib
mkdir -p filesystem/bin
$(CARGO) rustc --manifest-path $< --bin redoxfs-mkfs $(CARGOFLAGS) -o $@
strip $@
drivers: \
filesystem/sbin/pcid \
filesystem/sbin/e1000d \
filesystem/sbin/rtl8168d
binutils: \
filesystem/bin/hex \
filesystem/bin/hexdump \
filesystem/bin/strings
coreutils: \
filesystem/bin/basename \
filesystem/bin/cat \
filesystem/bin/chmod \
filesystem/bin/clear \
filesystem/bin/cp \
filesystem/bin/cut \
filesystem/bin/date \
filesystem/bin/dd \
filesystem/bin/df \
filesystem/bin/du \
filesystem/bin/echo \
filesystem/bin/env \
filesystem/bin/false \
filesystem/bin/free \
filesystem/bin/head \
filesystem/bin/kill \
filesystem/bin/ls \
filesystem/bin/mkdir \
filesystem/bin/mv \
filesystem/bin/printenv \
filesystem/bin/ps \
filesystem/bin/pwd \
filesystem/bin/realpath \
filesystem/bin/reset \
filesystem/bin/rmdir \
filesystem/bin/rm \
filesystem/bin/seq \
filesystem/bin/sleep \
filesystem/bin/sort \
filesystem/bin/tail \
filesystem/bin/tee \
filesystem/bin/time \
filesystem/bin/touch \
filesystem/bin/true \
filesystem/bin/wc \
filesystem/bin/yes
#filesystem/bin/shutdown filesystem/bin/test
extrautils: \
filesystem/bin/calc \
filesystem/bin/cksum \
filesystem/bin/cur \
filesystem/bin/grep \
filesystem/bin/less \
filesystem/bin/man \
filesystem/bin/mdless \
filesystem/bin/mtxt \
filesystem/bin/rem \
#filesystem/bin/dmesg filesystem/bin/info filesystem/bin/watch
games: \
filesystem/bin/ice \
filesystem/bin/minesweeper \
filesystem/bin/reblox \
filesystem/bin/rusthello \
filesystem/bin/snake
netutils: \
filesystem/bin/dhcpd \
filesystem/bin/dns \
filesystem/bin/httpd \
filesystem/bin/irc \
filesystem/bin/nc \
filesystem/bin/ntp \
filesystem/bin/wget
orbutils: \
filesystem/ui/bin/browser \
filesystem/ui/bin/calculator \
filesystem/ui/bin/character_map \
filesystem/ui/bin/editor \
filesystem/ui/bin/file_manager \
filesystem/ui/bin/launcher \
filesystem/ui/bin/orblogin \
filesystem/ui/bin/terminal \
filesystem/ui/bin/viewer
pkgutils: \
filesystem/bin/pkg
userutils: \
filesystem/bin/getty \
filesystem/bin/id \
filesystem/bin/login \
filesystem/bin/passwd \
filesystem/bin/su \
filesystem/bin/sudo
schemes: \
filesystem/sbin/ethernetd \
filesystem/sbin/ipd \
filesystem/sbin/orbital \
filesystem/sbin/ptyd \
filesystem/sbin/randd \
filesystem/sbin/redoxfs \
filesystem/sbin/redoxfs-mkfs \
filesystem/sbin/tcpd \
filesystem/sbin/udpd
build/filesystem.bin: \
drivers \
coreutils \
extrautils \
games \
netutils \
orbutils \
pkgutils \
userutils \
schemes \
filesystem/bin/acid \
filesystem/bin/contain \
filesystem/bin/ion \
filesystem/bin/sh \
filesystem/bin/smith \
filesystem/bin/tar
-$(FUMOUNT) build/filesystem/
rm -rf $@ build/filesystem/
dd if=/dev/zero of=$@ bs=1M count=128
cargo run --manifest-path schemes/redoxfs/Cargo.toml --release --bin redoxfs-mkfs $@
mkdir -p build/filesystem/
cargo build --manifest-path schemes/redoxfs/Cargo.toml --release --bin redoxfs
schemes/redoxfs/target/release/redoxfs $@ build/filesystem/
sleep 2
pgrep redoxfs
cp -RL filesystem/* build/filesystem/
chown -R 0:0 build/filesystem
chown -R 1000:1000 build/filesystem/home/user
chmod -R uog+rX build/filesystem
chmod -R u+w build/filesystem
chmod -R og-w build/filesystem
chmod -R 755 build/filesystem/bin
chmod -R u+rwX build/filesystem/root
chmod -R og-rwx build/filesystem/root
chmod -R u+rwX build/filesystem/home/user
chmod -R og-rwx build/filesystem/home/user
chmod +s build/filesystem/bin/passwd
chmod +s build/filesystem/bin/su
chmod +s build/filesystem/bin/sudo
mkdir build/filesystem/tmp
chmod 1777 build/filesystem/tmp
sync
-$(FUMOUNT) build/filesystem/
rm -rf build/filesystem/
mount: FORCE
mkdir -p build/filesystem/
cargo build --manifest-path schemes/redoxfs/Cargo.toml --release --bin redoxfs
schemes/redoxfs/target/release/redoxfs build/harddrive.bin build/filesystem/
sleep 2
pgrep redoxfs
unmount: FORCE
sync
-$(FUMOUNT) build/filesystem/
rm -rf build/filesystem/
# Wireshark
wireshark: FORCE
wireshark build/network.pcap

View file

@ -1,38 +1,40 @@
<p align="center">
<img alt="Redox" height="120" src="https://github.com/redox-os/assets/raw/master/logo.png">
<img alt="Redox" width="346" src="https://github.com/redox-os/assets/raw/master/logos/redox/logo.png">
</p>
**Redox** is an operating system written in Rust, a language with focus on safety and high performance. Redox, following the microkernel design, aims to be secure, usable, and free. Redox is inspired by previous kernels and operating systems, such as SeL4, Minix, Plan 9, and BSD.
Redox _is not_ just a kernel, it's a full-featured Operating System, providing packages (memory allocator, file system, display manager, core utilities, etc.) that together makes up a functional and convenient operating system. You can loosly think of it as the GNU or BSD ecosystem, but in a memory safe language and with modern technology. See [this list](#ecosystem) for overview of the ecosystem.
Redox _is not_ just a kernel, it's a **full-featured Operating System**, providing packages (memory allocator, file system, display manager, core utilities, etc.) that together make up a functional and convenient operating system. You can loosely think of it as the GNU or BSD ecosystem, but in a memory safe language and with modern technology. See [this list](#ecosystem) for overview of the ecosystem.
The website can be found at https://www.redox-os.org.
Please make sure you use the **latest nightly** of `rustc` before building (for more troubleshooting, see ["Help! Redox won't compile!"](#compile-help)).
[![Travis Build Status](https://travis-ci.org/redox-os/redox.svg?branch=master)](https://travis-ci.org/redox-os/redox)
[![Downloads](https://img.shields.io/github/downloads/redox-os/redox/total.svg)](https://github.com/redox-os/redox/releases)
[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE.md)
![Rust Version](https://img.shields.io/badge/rust-1.15.0--nightly%20(71c06a56a%202016--12--18)-lightgrey.svg)
![Rust Version](https://img.shields.io/badge/rust-nightly%202017--07--26-lightgrey.svg)
## Contents
* [What it looks like](#what-it-looks-like)
* [What it looks like](#screenshots)
* [Ecosystem](#ecosystem)
* [Help! Redox won't compile](#compile-help)
* [Contributing to Redox](#contributing)
* [Cloning, Building and running](#cloning-building-running)
* [Quick Setup](#quick-setup)
* [Manual Setup](#manual-setup)
* [Setup Using Docker](#setup-using-docker)
## <a name="what-it-looks-like"> What it looks like </a>
## <a name="screenshots"> What it looks like </a>
<img alt="Redox" height="150" src="https://github.com/redox-os/assets/raw/master/screenshots/Desktop.png">
<img alt="Redox" height="150" src="https://github.com/redox-os/assets/raw/master/screenshots/Fancy_opacity.png">
<img alt="Redox" height="150" src="https://github.com/redox-os/assets/raw/master/screenshots/File_manager.png">
<img alt="Redox" height="150" src="https://github.com/redox-os/assets/raw/master/screenshots/redox running.jpeg">
<img alt="Redox" height="150" src="https://github.com/redox-os/assets/raw/master/screenshots/IMG_1460.PNG">
<img alt="Redox" height="150" src="https://github.com/redox-os/assets/raw/master/screenshots/Sodium_v1.png">
<img alt="Redox" height="150" src="https://github.com/redox-os/assets/raw/master/screenshots/Boot.png">
<img alt="Redox" height="150" src="https://github.com/redox-os/assets/raw/master/screenshots/start.png">
<img alt="Redox" height="150" src="https://github.com/redox-os/assets/raw/master/screenshots/IMG_1459.PNG">
## <a name="ecosystem"> Ecosystem </a>
@ -40,48 +42,49 @@ The ecosystem and software Redox OS provides is listed below.
| Name (lexicographic order) | Maintainer
|-----------------------------------------------------------------------------|---------------------------
| [Ion (shell)](https://github.com/redox-os/ion) | [**@skylerberg**](https://github.com/skylerberg) & [**@jackpot51**](https://github.com/jackpot51)
| [RANSID](https://github.com/redox-os/ransid) | [**@jackpot51**](https://github.com/jackpot51)
| [Sodium (editor)](https://github.com/redox-os/sodium) | [**@ticki**](https://github.com/ticki)
| [Standard library](https://github.com/redox-os/libstd) | [**@jackpot51**](https://github.com/jackpot51)
| [TFS (filesystem)](https://github.com/ticki/tfs) | [**@ticki**](https://github.com/ticki)
| [The Redox book](https://github.com/redox-os/book) | [**@ticki**](https://github.com/ticki)
| [The old kernel](https://github.com/redox-os/old) | abandoned
| [ZFS](https://github.com/redox-os/zfs) | abandoned, superseded by TFS
| [acid tests](https://github.com/redox-os/acid) | [**@jackpot51**](https://github.com/jackpot51) (co.: [**@ticki**](https://github.com/ticki), [**@nilset](https://github.com/nilset))
| [acid (kernel integration tests)](https://github.com/redox-os/acid) | [**@jackpot51**](https://github.com/jackpot51) (co.: [**@ticki**](https://github.com/ticki), [**@nilset](https://github.com/nilset))
| [binutils](https://github.com/redox-os/binutils) | [**@ticki**](https://github.com/ticki)
| [bots (other internal bots)](https://github.com/redox-os/bots) | [**@ticki**](https://github.com/ticki)
| [bots (custom Mattermost bots)](https://github.com/redox-os/bots) | [**@ticki**](https://github.com/ticki)
| [cookbook](https://github.com/redox-os/cookbook) | [**@jackpot51**](https://github.com/jackpot51)
| [coreutils](https://github.com/redox-os/coreutils) | [**@ticki**](https://github.com/ticki) (co.: [**@stratact**](https://github.com/stratact))
| [extrautils](https://github.com/redox-os/extrautils) | [**@ticki**](https://github.com/ticki)
| [games](https://github.com/redox-os/games) | [**@ticki**](https://github.com/ticki)
| [Ion (shell)](https://github.com/redox-os/ion) | [**@skylerberg**](https://github.com/skylerberg) & [**@jackpot51**](https://github.com/jackpot51)
| [kernel](https://github.com/redox-os/kernel) | [**@jackpot51**](https://github.com/jackpot51)
| [libextra](https://github.com/redox-os/libextra) | [**@ticki**](https://github.com/ticki)
| [libpager](https://github.com/redox-os/libpager) | [**@ticki**](https://github.com/ticki)
| [magnet (future package manager)](https://github.com/redox-os/magnet) | [**@ticki**](https://github.com/ticki)
| [libstd (Redox standard library)](https://github.com/redox-os/libstd) | [**@jackpot51**](https://github.com/jackpot51)
| [netutils](https://github.com/redox-os/netutils) | [**@jackpot51**](https://github.com/jackpot51)
| [orbclient](https://github.com/redox-os/orbclient) | [**@jackpot51**](https://github.com/jackpot51)
| [orbclient (Orbital client)](https://github.com/redox-os/orbclient) | [**@jackpot51**](https://github.com/jackpot51)
| [orbdata](https://github.com/redox-os/orbdata) | [**@jackpot51**](https://github.com/jackpot51)
| [orbital](https://github.com/redox-os/orbital) | [**@jackpot51**](https://github.com/jackpot51)
| [orbtk](https://github.com/redox-os/orbtk) | [**@stratact**](https://github.com/stratact)
| [orbutils](https://github.com/redox-os/orbutils) | [**@jackpot51**](https://github.com/jackpot51)
| [Orbital (windowing and compositing system)](https://github.com/redox-os/orbital) | [**@jackpot51**](https://github.com/jackpot51)
| [orbtk (Orbital toolkit)](https://github.com/redox-os/orbtk) | [**@stratact**](https://github.com/stratact)
| [orbutils (Orbital utilities)](https://github.com/redox-os/orbutils) | [**@jackpot51**](https://github.com/jackpot51)
| [pkgutils (current package manager)](https://github.com/redox-os/pkgutils) | [**@jackpot51**](https://github.com/jackpot51)
| [playbot (internal REPL bot)](https://github.com/redox-os/playbot) | [**@ticki**](https://github.com/ticki)
| [ralloc](https://github.com/redox-os/ralloc) | [**@ticki**](https://github.com/ticki)
| [RANSID (Rust ANSI driver)](https://github.com/redox-os/ransid) | [**@jackpot51**](https://github.com/jackpot51)
| [redoxfs (old filesystem)](https://github.com/redox-os/redoxfs) | [**@jackpot51**](https://github.com/jackpot51)
| [syscall](https://github.com/redox-os/syscall) | [**@jackpot51**](https://github.com/jackpot51)
| [Sodium (Vim-inspired text editor)](https://github.com/redox-os/sodium) | [**@ticki**](https://github.com/ticki)
| [userutils](https://github.com/redox-os/userutils) | [**@jackpot51**](https://github.com/jackpot51)
| [TFS (ticki filesystem)](https://github.com/ticki/tfs) | [**@ticki**](https://github.com/ticki)
| [The Redox book](https://github.com/redox-os/book) | [**@ticki**](https://github.com/ticki)
| [The old kernel](https://github.com/redox-os/old) | **abandoned**
| [ZFS](https://github.com/redox-os/zfs) | **abandoned, superseded by [TFS](https://github.com/ticki/tfs)**
## <a name="compile-help"> Help! Redox won't compile! </a>
Sometimes things go wrong when compiling. Try the following before opening an issue:
1. Run `make clean`.
2. Run `git clean -X -f -d`.
3. Make sure you have **the latest version of Rust nightly!** ([rustup.rs](https://www.rustup.rs) is recommended for managing Rust versions).
4. Update **GNU Make**, **NASM** and **QEMU/VirtualBox**.
5. Pull the upstream master branch (`git remote add upstream git@github.com:redox-os/redox.git; git pull upstream master`).
6. Update submodules (`git submodule update --recursive --init`).
1. Make sure you have a redox toolchain (`x86_64-unknown-redox-gcc`).
* You can install from .deb packages(`https://static.redox-os.org/toolchain/apt/`) or build [redox-os/libc](https://github.com/redox-os/libc) manually.
1. Run `rustup update`
1. Run `make clean pull`.
1. Make sure you have **the latest version of Rust nightly!** ([rustup.rs](https://www.rustup.rs) is recommended for managing Rust versions. If you already have it, run `rustup`).
1. Update **GNU Make**, **NASM** and **QEMU/VirtualBox**.
1. Pull the upstream master branch (`git remote add upstream git@github.com:redox-os/redox.git; git pull upstream master`).
1. Update submodules (`git submodule update --recursive --init`).
and then rebuild!
@ -89,9 +92,9 @@ and then rebuild!
If you're interested in this project, and you'd like to help us out, [here](CONTRIBUTING.md) is a list of ways you can do just that.
## <a name="cloning-building-running"> Cloning, Building, and Running </a>
## <a name="cloning-building-running"> Cloning, Building and Running </a>
Redox is big (even compressed)! So cloning Redox takes a lot of bandwidth, and (depending on your data plan) can be costly, so clone at your own risk!
Redox is big, even compressed. Downloading the full history may take a lot of bandwidth, and can even be costly on some data plans. Clone at your own risk!
### <a name="quick-setup" /> Quick Setup </a>
@ -101,6 +104,9 @@ $ cd path/to/your/projects/folder/
# Run bootstrap setup
$ curl -sf https://raw.githubusercontent.com/redox-os/redox/master/bootstrap.sh -o bootstrap.sh && bash -e bootstrap.sh
#Change to project directory
$ cd redox
# Build Redox
$ make all
@ -112,11 +118,11 @@ $ make qemu kvm=no
#### QEMU with KVM
To use QEMU with KVM (kernel-based virtual Machine), which is faster than without KVM, you need a CPU with Intel® Virtualization Technology (Intel® VT) or AMD Virtualization™ (AMD-V™) support. Most systems have this disabled in the BIOS by default, so you may need to reboot and enable the feature in the BIOS.
To use QEMU with KVM (kernel-based virtual Machine), which is faster than without KVM, you need a CPU with Intel® Virtualization Technology (Intel® VT) or AMD Virtualization™ (AMD-V™) support. Most systems have this disabled by default, so you may need to reboot, go into the BIOS, and enable it.
### <a name="manual-setup"> Manual Setup </a>
To manually clone, build and run Redox using a Linux host, run the following commands (with exceptions, be sure to read the comments):
To manually clone, build and run Redox using a Unix-based host, run the following commands (with exceptions, be sure to read the comments):
```bash
$ cd path/to/your/projects/folder/
@ -128,7 +134,7 @@ $ git clone git@github.com:redox-os/redox.git --origin upstream --recursive
$ cd redox/
# Install/update dependencies
$ bash bootstrap.sh -d
$ ./bootstrap.sh -d
# Install rustup.rs
$ curl https://sh.rustup.rs -sSf | sh
@ -146,6 +152,22 @@ $ make all
# Launch using QEMU
$ make qemu
# Launch using QEMU without using KVM (Kernel Virtual Machine). Try if QEMU gives an error.
$ make qemu kvm=no
# Launch using QEMU without using KVM (Kernel Virtual Machine) nor Graphics
make qemu kvm=no vga=no
```
### <a name="setup-using-docker"> Setup using Docker </a>
We also provide docker image. After cloning this repository, please follow README under the `docker` directory.
### Updating the codebase using the Makefile
To update the codebase run:
```
make pull; make fetch
```
`make pull` pulls and updates the submodules, and `make fetch` updates the sources for cookbook recipes.

View file

@ -1,8 +0,0 @@
[package]
name = "arch_arm"
version = "0.1.0"
[dependencies]
bitflags = "*"
hole_list_allocator = { path = "../../crates/hole_list_allocator"}
spin = "*"

View file

@ -1,9 +0,0 @@
#[derive(Debug)]
pub struct Context;
impl Context {
pub fn new() -> Self {
Context
}
}

View file

@ -1,70 +0,0 @@
/// Memcpy
///
/// Copy N bytes of memory from one location to another.
#[no_mangle]
pub unsafe extern fn memcpy(dest: *mut u8, src: *const u8,
n: usize) -> *mut u8 {
let mut i = 0;
while i < n {
*dest.offset(i as isize) = *src.offset(i as isize);
i += 1;
}
dest
}
/// Memmove
///
/// Copy N bytes of memory from src to dest. The memory areas may overlap.
#[no_mangle]
pub unsafe extern fn memmove(dest: *mut u8, src: *const u8,
n: usize) -> *mut u8 {
if src < dest as *const u8 {
let mut i = n;
while i != 0 {
i -= 1;
*dest.offset(i as isize) = *src.offset(i as isize);
}
} else {
let mut i = 0;
while i < n {
*dest.offset(i as isize) = *src.offset(i as isize);
i += 1;
}
}
dest
}
/// Memset
///
/// Fill a block of memory with a specified value.
#[no_mangle]
pub unsafe extern fn memset(s: *mut u8, c: i32, n: usize) -> *mut u8 {
let mut i = 0;
while i < n {
*s.offset(i as isize) = c as u8;
i += 1;
}
s
}
/// Memcmp
///
/// Compare two blocks of memory.
#[no_mangle]
pub unsafe extern fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 {
let mut i = 0;
while i < n {
let a = *s1.offset(i as isize);
let b = *s2.offset(i as isize);
if a != b {
return a as i32 - b as i32
}
i += 1;
}
0
}

View file

@ -1,30 +0,0 @@
//! Interrupt instructions
/// Clear interrupts
#[inline(always)]
pub unsafe fn disable() {
}
/// Set interrupts
#[inline(always)]
pub unsafe fn enable() {
}
/// Set interrupts and halt
#[inline(always)]
pub unsafe fn enable_and_halt() {
halt();
}
/// Halt instruction
#[inline(always)]
pub unsafe fn halt() {
//asm!("wfi" : : : : "volatile");
asm!("nop" : : : : "volatile");
}
/// Get a stack trace
//TODO: Check for stack being mapped before dereferencing
#[inline(never)]
pub unsafe fn stack_trace() {
}

View file

@ -1,39 +0,0 @@
//! Architecture support for ARM
#![feature(asm)]
#![feature(lang_items)]
#![feature(naked_functions)]
#![no_std]
extern crate hole_list_allocator as allocator;
#[macro_use]
extern crate bitflags;
extern crate spin;
/// Print to console
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ({});
}
/// Print with new line to console
#[macro_export]
macro_rules! println {
($fmt:expr) => (print!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
}
/// Context switching
pub mod context;
/// Memset, memcpy, etc.
pub mod externs;
/// Interrupt handling
pub mod interrupt;
/// Panic support
pub mod panic;
/// Initialization function
pub mod start;

View file

@ -1,60 +0,0 @@
ENTRY(kstart)
OUTPUT_ARCH(arm)
OUTPUT_FORMAT(elf32-littlearm)
KERNEL_OFFSET = 0;
SECTIONS {
. = KERNEL_OFFSET;
.text : AT(ADDR(.text) - KERNEL_OFFSET) {
__text_start = .;
*(.text*)
. = ALIGN(4096);
__text_end = .;
}
.rodata : AT(ADDR(.rodata) - KERNEL_OFFSET) {
__rodata_start = .;
*(.rodata*)
. = ALIGN(4096);
__rodata_end = .;
}
.data : AT(ADDR(.data) - KERNEL_OFFSET) {
__data_start = .;
*(.data*)
. = ALIGN(4096);
__data_end = .;
}
.tdata : AT(ADDR(.tdata) - KERNEL_OFFSET) {
__tdata_start = .;
*(.tdata*)
. = ALIGN(4096);
__tdata_end = .;
__tbss_start = .;
*(.tbss*)
. += 8;
. = ALIGN(4096);
__tbss_end = .;
}
.bss : AT(ADDR(.bss) - KERNEL_OFFSET) {
__bss_start = .;
*(.bss*)
. = ALIGN(4096);
__bss_end = .;
}
__end = .;
/DISCARD/ : {
*(.comment*)
*(.debug*)
*(.eh_frame*)
*(.gcc_except_table*)
*(.note*)
*(.rel.eh_frame*)
}
}

View file

@ -1,38 +0,0 @@
//! Intrinsics for panic handling
use interrupt;
#[cfg(not(test))]
#[lang = "eh_personality"]
extern "C" fn eh_personality() {}
#[cfg(not(test))]
/// Required to handle panics
#[lang = "panic_fmt"]
extern "C" fn panic_fmt(fmt: ::core::fmt::Arguments, file: &str, line: u32) -> ! {
println!("PANIC: {}", fmt);
println!("FILE: {}", file);
println!("LINE: {}", line);
unsafe { interrupt::stack_trace(); }
println!("HALT");
loop {
unsafe { interrupt::halt(); }
}
}
#[allow(non_snake_case)]
#[no_mangle]
/// Required to handle panics
pub extern "C" fn _Unwind_Resume() -> ! {
loop {
unsafe { interrupt::halt(); }
}
}
/// Required for linker
#[no_mangle]
pub extern "C" fn __aeabi_unwind_cpp_pr0() {
loop {}
}

View file

@ -1,27 +0,0 @@
const SERIAL_BASE: *mut u8 = 0x16000000 as *mut u8;
const SERIAL_FLAG_REGISTER: *const u8 = 0x16000018 as *const u8;
const SERIAL_BUFFER_FULL: u8 = (1 << 5);
unsafe fn putc (c: u8)
{
/* Wait until the serial buffer is empty */
while *SERIAL_FLAG_REGISTER & SERIAL_BUFFER_FULL == SERIAL_BUFFER_FULL {}
/* Put our character, c, into the serial buffer */
*SERIAL_BASE = c;
}
unsafe fn puts(string: &str)
{
for b in string.bytes() {
putc(b);
}
}
#[no_mangle]
#[naked]
pub unsafe extern fn kstart() -> ! {
asm!("mov sp, 0x18000" : : : : "volatile");
puts("TEST\r\n");
loop {}
}

View file

@ -1,3 +0,0 @@
[package]
name = "arch_test"
version = "0.1.0"

View file

@ -1,43 +0,0 @@
//! Interrupt instructions
static mut INTERRUPTS_ENABLED: bool = false;
/// Clear interrupts
#[inline(always)]
pub unsafe fn disable() {
println!("CLEAR INTERRUPTS");
INTERRUPTS_ENABLED = false;
}
/// Set interrupts
#[inline(always)]
pub unsafe fn enable() {
println!("SET INTERRUPTS");
INTERRUPTS_ENABLED = true;
}
/// Halt instruction
#[inline(always)]
pub unsafe fn halt() {
assert!(INTERRUPTS_ENABLED);
::std::thread::yield_now();
}
/// Pause instruction
#[inline(always)]
pub unsafe fn pause() {
}
/// Set interrupts and nop
#[inline(always)]
pub unsafe fn enable_and_nop() {
enable();
}
/// Set interrupts and halt
#[inline(always)]
pub unsafe fn enable_and_halt() {
enable();
halt();
}

View file

@ -1,43 +0,0 @@
//! Architecture support for testing
pub use std::io;
/// Print to console
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ({
use $crate::io::Write;
let _ = write!($crate::io::stdout(), $($arg)*);
});
}
/// Print with new line to console
#[macro_export]
macro_rules! println {
($fmt:expr) => (print!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
}
/// Create an interrupt function that can safely run rust code
#[macro_export]
macro_rules! interrupt {
($name:ident, $func:block) => {
pub unsafe extern fn $name () {
unsafe fn inner() {
$func
}
// Call inner rust function
inner();
}
};
}
/// Interrupt instructions
pub mod interrupt;
/// Initialization and main function
pub mod main;
/// Time functions
pub mod time;

View file

@ -1,11 +0,0 @@
/// This function is where the kernel sets up IRQ handlers
/// It is increcibly unsafe, and should be minimal in nature
extern {
fn kmain() -> !;
}
#[no_mangle]
pub unsafe extern fn kstart() -> ! {
kmain();
}

View file

@ -1,7 +0,0 @@
pub fn monotonic() -> (u64, u64) {
(0, 0)
}
pub fn realtime() -> (u64, u64) {
(0, 0)
}

View file

@ -1,15 +0,0 @@
[package]
name = "arch_x86_64"
version = "0.1.0"
[dependencies]
bitflags = "*"
hole_list_allocator = { path = "../../crates/hole_list_allocator/" }
io = { path = "../../crates/io/" }
raw-cpuid = { git = "https://github.com/gz/rust-cpuid" }
spin = "*"
redox_syscall = { path = "../../syscall/" }
[dependencies.x86]
version = "0.7"
default-features = false

View file

@ -1,77 +0,0 @@
#[repr(packed)]
pub struct DrhdFault {
pub sts: u32,
pub ctrl: u32,
pub data: u32,
pub addr: [u32; 2],
_rsv: [u64; 2],
pub log: u64,
}
#[repr(packed)]
pub struct DrhdProtectedMemory {
pub en: u32,
pub low_base: u32,
pub low_limit: u32,
pub high_base: u64,
pub high_limit: u64,
}
#[repr(packed)]
pub struct DrhdInvalidation {
pub queue_head: u64,
pub queue_tail: u64,
pub queue_addr: u64,
_rsv: u32,
pub cmpl_sts: u32,
pub cmpl_ctrl: u32,
pub cmpl_data: u32,
pub cmpl_addr: [u32; 2],
}
#[repr(packed)]
pub struct DrhdPageRequest {
pub queue_head: u64,
pub queue_tail: u64,
pub queue_addr: u64,
_rsv: u32,
pub sts: u32,
pub ctrl: u32,
pub data: u32,
pub addr: [u32; 2],
}
#[repr(packed)]
pub struct DrhdMtrrVariable {
pub base: u64,
pub mask: u64,
}
#[repr(packed)]
pub struct DrhdMtrr {
pub cap: u64,
pub def_type: u64,
pub fixed: [u64; 11],
pub variable: [DrhdMtrrVariable; 10],
}
#[repr(packed)]
pub struct Drhd {
pub version: u32,
_rsv: u32,
pub cap: u64,
pub ext_cap: u64,
pub gl_cmd: u32,
pub gl_sts: u32,
pub root_table: u64,
pub ctx_cmd: u64,
_rsv1: u32,
pub fault: DrhdFault,
_rsv2: u32,
pub pm: DrhdProtectedMemory,
pub invl: DrhdInvalidation,
_rsv3: u64,
pub intr_table: u64,
pub page_req: DrhdPageRequest,
pub mtrr: DrhdMtrr,
}

View file

@ -1,182 +0,0 @@
use core::mem;
use super::sdt::Sdt;
use self::drhd::Drhd;
use memory::Frame;
use paging::{entry, ActivePageTable, PhysicalAddress};
pub mod drhd;
/// The DMA Remapping Table
#[derive(Debug)]
pub struct Dmar {
sdt: &'static Sdt,
pub addr_width: u8,
pub flags: u8,
_rsv: [u8; 10],
}
impl Dmar {
pub fn new(sdt: &'static Sdt) -> Option<Dmar> {
if &sdt.signature == b"DMAR" && sdt.data_len() >= 12 { //Not valid if no local address and flags
let addr_width = unsafe { *(sdt.data_address() as *const u8) };
let flags = unsafe { *(sdt.data_address() as *const u8).offset(1) };
let rsv: [u8; 10] = unsafe { *((sdt.data_address() as *const u8).offset(2) as *const [u8; 10]) };
Some(Dmar {
sdt: sdt,
addr_width: addr_width,
flags: flags,
_rsv: rsv,
})
} else {
None
}
}
pub fn iter(&self) -> DmarIter {
DmarIter {
sdt: self.sdt,
i: 12 // Skip address width and flags
}
}
}
///
/// DMAR DMA Remapping Hardware Unit Definition
// TODO: Implement iterator on DmarDrhd scope
#[derive(Debug)]
#[repr(packed)]
pub struct DmarDrhd {
kind: u16,
length: u16,
flags: u8,
_rsv: u8,
segment: u16,
base: u64,
}
impl DmarDrhd {
pub fn get(&self, active_table: &mut ActivePageTable) -> &'static mut Drhd {
active_table.identity_map(Frame::containing_address(PhysicalAddress::new(self.base as usize)), entry::PRESENT | entry::WRITABLE | entry::NO_EXECUTE);
unsafe { &mut *(self.base as *mut Drhd) }
}
}
/// DMAR Reserved Memory Region Reporting
// TODO: Implement iterator on DmarRmrr scope
#[derive(Debug)]
#[repr(packed)]
pub struct DmarRmrr {
kind: u16,
length: u16,
_rsv: u16,
segment: u16,
base: u64,
limit: u64,
}
/// DMAR Root Port ATS Capability Reporting
// TODO: Implement iterator on DmarAtsr scope
#[derive(Debug)]
#[repr(packed)]
pub struct DmarAtsr {
kind: u16,
length: u16,
flags: u8,
_rsv: u8,
segment: u16,
}
/// DMAR Remapping Hardware Static Affinity
#[derive(Debug)]
#[repr(packed)]
pub struct DmarRhsa {
kind: u16,
length: u16,
_rsv: u32,
base: u64,
domain: u32,
}
/// DMAR ACPI Name-space Device Declaration
// TODO: Implement iterator on DmarAndd object name
#[derive(Debug)]
#[repr(packed)]
pub struct DmarAndd {
kind: u16,
length: u16,
_rsv: [u8; 3],
acpi_dev: u8,
}
/// DMAR Entries
#[derive(Debug)]
pub enum DmarEntry {
Drhd(&'static DmarDrhd),
InvalidDrhd(usize),
Rmrr(&'static DmarRmrr),
InvalidRmrr(usize),
Atsr(&'static DmarAtsr),
InvalidAtsr(usize),
Rhsa(&'static DmarRhsa),
InvalidRhsa(usize),
Andd(&'static DmarAndd),
InvalidAndd(usize),
Unknown(u16)
}
pub struct DmarIter {
sdt: &'static Sdt,
i: usize
}
impl Iterator for DmarIter {
type Item = DmarEntry;
fn next(&mut self) -> Option<Self::Item> {
if self.i + 4 <= self.sdt.data_len() {
let entry_type = unsafe { *((self.sdt.data_address() as *const u8).offset(self.i as isize) as *const u16) };
let entry_len = unsafe { *((self.sdt.data_address() as *const u8).offset(self.i as isize + 2) as *const u16) } as usize;
if self.i + entry_len <= self.sdt.data_len() {
let item = match entry_type {
0 => if entry_len >= mem::size_of::<DmarDrhd>() {
DmarEntry::Drhd(unsafe { &*((self.sdt.data_address() + self.i) as *const DmarDrhd) })
} else {
DmarEntry::InvalidDrhd(entry_len)
},
1 => if entry_len >= mem::size_of::<DmarRmrr>() {
DmarEntry::Rmrr(unsafe { &*((self.sdt.data_address() + self.i) as *const DmarRmrr) })
} else {
DmarEntry::InvalidRmrr(entry_len)
},
2 => if entry_len >= mem::size_of::<DmarAtsr>() {
DmarEntry::Atsr(unsafe { &*((self.sdt.data_address() + self.i) as *const DmarAtsr) })
} else {
DmarEntry::InvalidAtsr(entry_len)
},
3 => if entry_len == mem::size_of::<DmarRhsa>() {
DmarEntry::Rhsa(unsafe { &*((self.sdt.data_address() + self.i) as *const DmarRhsa) })
} else {
DmarEntry::InvalidRhsa(entry_len)
},
4 => if entry_len >= mem::size_of::<DmarAndd>() {
DmarEntry::Andd(unsafe { &*((self.sdt.data_address() + self.i) as *const DmarAndd) })
} else {
DmarEntry::InvalidAndd(entry_len)
},
_ => DmarEntry::Unknown(entry_type)
};
self.i += entry_len;
Some(item)
} else {
None
}
} else {
None
}
}
}

View file

@ -1,96 +0,0 @@
use core::{mem, ptr};
use super::sdt::Sdt;
#[repr(packed)]
#[derive(Debug)]
pub struct Fadt {
pub header: Sdt,
pub firmware_ctrl: u32,
pub dsdt: u32,
// field used in ACPI 1.0; no longer in use, for compatibility only
reserved: u8,
pub preferred_power_managament: u8,
pub sci_interrupt: u16,
pub smi_command_port: u32,
pub acpi_enable: u8,
pub acpi_disable: u8,
pub s4_bios_req: u8,
pub pstate_control: u8,
pub pm1a_event_block: u32,
pub pm1b_event_block: u32,
pub pm1a_control_block: u32,
pub pm1b_control_block: u32,
pub pm2_control_block: u32,
pub pm_timer_block: u32,
pub gpe0_block: u32,
pub gpe1_block: u32,
pub pm1_event_length: u8,
pub pm1_control_length: u8,
pub pm2_control_length: u8,
pub pm_timer_length: u8,
pub gpe0_ength: u8,
pub gpe1_length: u8,
pub gpe1_base: u8,
pub c_state_control: u8,
pub worst_c2_latency: u16,
pub worst_c3_latency: u16,
pub flush_size: u16,
pub flush_stride: u16,
pub duty_offset: u8,
pub duty_width: u8,
pub day_alarm: u8,
pub month_alarm: u8,
pub century: u8,
// reserved in ACPI 1.0; used since ACPI 2.0+
pub boot_architecture_flags: u16,
reserved2: u8,
pub flags: u32,
}
/* ACPI 2 structure
#[repr(packed)]
#[derive(Clone, Copy, Debug, Default)]
pub struct GenericAddressStructure {
address_space: u8,
bit_width: u8,
bit_offset: u8,
access_size: u8,
address: u64,
}
{
// 12 byte structure; see below for details
pub reset_reg: GenericAddressStructure,
pub reset_value: u8,
reserved3: [u8; 3],
// 64bit pointers - Available on ACPI 2.0+
pub x_firmware_control: u64,
pub x_dsdt: u64,
pub x_pm1a_event_block: GenericAddressStructure,
pub x_pm1b_event_block: GenericAddressStructure,
pub x_pm1a_control_block: GenericAddressStructure,
pub x_pm1b_control_block: GenericAddressStructure,
pub x_pm2_control_block: GenericAddressStructure,
pub x_pm_timer_block: GenericAddressStructure,
pub x_gpe0_block: GenericAddressStructure,
pub x_gpe1_block: GenericAddressStructure,
}
*/
impl Fadt {
pub fn new(sdt: &'static Sdt) -> Option<Fadt> {
if &sdt.signature == b"FACP" && sdt.length as usize >= mem::size_of::<Fadt>() {
Some(unsafe { ptr::read((sdt as *const Sdt) as *const Fadt) })
} else {
None
}
}
}

View file

@ -1,133 +0,0 @@
use core::mem;
use super::sdt::Sdt;
/// The Multiple APIC Descriptor Table
#[derive(Debug)]
pub struct Madt {
sdt: &'static Sdt,
pub local_address: u32,
pub flags: u32
}
impl Madt {
pub fn new(sdt: &'static Sdt) -> Option<Madt> {
if &sdt.signature == b"APIC" && sdt.data_len() >= 8 { //Not valid if no local address and flags
let local_address = unsafe { *(sdt.data_address() as *const u32) };
let flags = unsafe { *(sdt.data_address() as *const u32).offset(1) };
Some(Madt {
sdt: sdt,
local_address: local_address,
flags: flags
})
} else {
None
}
}
pub fn iter(&self) -> MadtIter {
MadtIter {
sdt: self.sdt,
i: 8 // Skip local controller address and flags
}
}
}
///
/// MADT Local APIC
#[derive(Debug)]
#[repr(packed)]
pub struct MadtLocalApic {
/// Processor ID
pub processor: u8,
/// Local APIC ID
pub id: u8,
/// Flags. 1 means that the processor is enabled
pub flags: u32
}
/// MADT I/O APIC
#[derive(Debug)]
#[repr(packed)]
pub struct MadtIoApic {
/// I/O APIC ID
pub id: u8,
/// reserved
reserved: u8,
/// I/O APIC address
pub address: u32,
/// Global system interrupt base
pub gsi_base: u32
}
/// MADT Interrupt Source Override
#[derive(Debug)]
#[repr(packed)]
pub struct MadtIntSrcOverride {
/// Bus Source
pub bus_source: u8,
/// IRQ Source
pub irq_source: u8,
/// Global system interrupt base
pub gsi_base: u32,
/// Flags
pub flags: u16
}
/// MADT Entries
#[derive(Debug)]
pub enum MadtEntry {
LocalApic(&'static MadtLocalApic),
InvalidLocalApic(usize),
IoApic(&'static MadtIoApic),
InvalidIoApic(usize),
IntSrcOverride(&'static MadtIntSrcOverride),
InvalidIntSrcOverride(usize),
Unknown(u8)
}
pub struct MadtIter {
sdt: &'static Sdt,
i: usize
}
impl Iterator for MadtIter {
type Item = MadtEntry;
fn next(&mut self) -> Option<Self::Item> {
if self.i + 1 < self.sdt.data_len() {
let entry_type = unsafe { *(self.sdt.data_address() as *const u8).offset(self.i as isize) };
let entry_len = unsafe { *(self.sdt.data_address() as *const u8).offset(self.i as isize + 1) } as usize;
if self.i + entry_len <= self.sdt.data_len() {
let item = match entry_type {
0 => if entry_len == mem::size_of::<MadtLocalApic>() + 2 {
MadtEntry::LocalApic(unsafe { &*((self.sdt.data_address() + self.i + 2) as *const MadtLocalApic) })
} else {
MadtEntry::InvalidLocalApic(entry_len)
},
1 => if entry_len == mem::size_of::<MadtIoApic>() + 2 {
MadtEntry::IoApic(unsafe { &*((self.sdt.data_address() + self.i + 2) as *const MadtIoApic) })
} else {
MadtEntry::InvalidIoApic(entry_len)
},
2 => if entry_len == mem::size_of::<MadtIntSrcOverride>() + 2 {
MadtEntry::IntSrcOverride(unsafe { &*((self.sdt.data_address() + self.i + 2) as *const MadtIntSrcOverride) })
} else {
MadtEntry::InvalidIntSrcOverride(entry_len)
},
_ => MadtEntry::Unknown(entry_type)
};
self.i += entry_len;
Some(item)
} else {
None
}
} else {
None
}
}
}

View file

@ -1,284 +0,0 @@
//! # ACPI
//! Code to parse the ACPI tables
use core::intrinsics::{atomic_load, atomic_store};
use core::sync::atomic::Ordering;
use device::local_apic::LOCAL_APIC;
use interrupt;
use memory::{allocate_frames, Frame};
use paging::{entry, ActivePageTable, Page, PhysicalAddress, VirtualAddress};
use start::{kstart_ap, CPU_COUNT, AP_READY};
use self::dmar::{Dmar, DmarEntry};
use self::fadt::Fadt;
use self::madt::{Madt, MadtEntry};
use self::rsdt::Rsdt;
use self::sdt::Sdt;
use self::xsdt::Xsdt;
pub mod dmar;
pub mod fadt;
pub mod madt;
pub mod rsdt;
pub mod sdt;
pub mod xsdt;
const TRAMPOLINE: usize = 0x7E00;
const AP_STARTUP: usize = TRAMPOLINE + 512;
pub fn init_sdt(sdt: &'static Sdt, active_table: &mut ActivePageTable) {
print!(" ");
for &c in sdt.signature.iter() {
print!("{}", c as char);
}
if let Some(fadt) = Fadt::new(sdt) {
println!(": {:#?}", fadt);
} else if let Some(madt) = Madt::new(sdt) {
println!(": {:>08X}: {}", madt.local_address, madt.flags);
let mut local_apic = unsafe { &mut LOCAL_APIC };
let me = local_apic.id() as u8;
if local_apic.x2 {
println!(" X2APIC {}", me);
} else {
println!(" XAPIC {}: {:>08X}", me, local_apic.address);
}
let trampoline_frame = Frame::containing_address(PhysicalAddress::new(TRAMPOLINE));
let trampoline_page = Page::containing_address(VirtualAddress::new(TRAMPOLINE));
// Map trampoline
active_table.map_to(trampoline_page, trampoline_frame, entry::PRESENT | entry::WRITABLE);
active_table.flush(trampoline_page);
for madt_entry in madt.iter() {
println!(" {:?}", madt_entry);
match madt_entry {
MadtEntry::LocalApic(ap_local_apic) => if ap_local_apic.id == me {
println!(" This is my local APIC");
} else {
if ap_local_apic.flags & 1 == 1 {
// Increase CPU ID
CPU_COUNT.fetch_add(1, Ordering::SeqCst);
// Allocate a stack
let stack_start = allocate_frames(64).expect("no more frames in acpi stack_start").start_address().get() + ::KERNEL_OFFSET;
let stack_end = stack_start + 64 * 4096;
let ap_ready = TRAMPOLINE as *mut u64;
let ap_cpu_id = unsafe { ap_ready.offset(1) };
let ap_page_table = unsafe { ap_ready.offset(2) };
let ap_stack_start = unsafe { ap_ready.offset(3) };
let ap_stack_end = unsafe { ap_ready.offset(4) };
let ap_code = unsafe { ap_ready.offset(5) };
// Set the ap_ready to 0, volatile
unsafe { atomic_store(ap_ready, 0) };
unsafe { atomic_store(ap_cpu_id, ap_local_apic.id as u64) };
unsafe { atomic_store(ap_page_table, active_table.address() as u64) };
unsafe { atomic_store(ap_stack_start, stack_start as u64) };
unsafe { atomic_store(ap_stack_end, stack_end as u64) };
unsafe { atomic_store(ap_code, kstart_ap as u64) };
AP_READY.store(false, Ordering::SeqCst);
print!(" AP {}:", ap_local_apic.id);
// Send INIT IPI
{
let mut icr = 0x4500;
if local_apic.x2 {
icr |= (ap_local_apic.id as u64) << 32;
} else {
icr |= (ap_local_apic.id as u64) << 56;
}
print!(" IPI...");
local_apic.set_icr(icr);
}
// Send START IPI
{
//Start at 0x0800:0000 => 0x8000. Hopefully the bootloader code is still there
let ap_segment = (AP_STARTUP >> 12) & 0xFF;
let mut icr = 0x4600 | ap_segment as u64;
if local_apic.x2 {
icr |= (ap_local_apic.id as u64) << 32;
} else {
icr |= (ap_local_apic.id as u64) << 56;
}
print!(" SIPI...");
local_apic.set_icr(icr);
}
// Wait for trampoline ready
print!(" Wait...");
while unsafe { atomic_load(ap_ready) } == 0 {
interrupt::pause();
}
print!(" Trampoline...");
while ! AP_READY.load(Ordering::SeqCst) {
interrupt::pause();
}
println!(" Ready");
active_table.flush_all();
} else {
println!(" CPU Disabled");
}
},
_ => ()
}
}
// Unmap trampoline
active_table.unmap(trampoline_page);
active_table.flush(trampoline_page);
} else if let Some(dmar) = Dmar::new(sdt) {
println!(": {}: {}", dmar.addr_width, dmar.flags);
for dmar_entry in dmar.iter() {
println!(" {:?}", dmar_entry);
match dmar_entry {
DmarEntry::Drhd(dmar_drhd) => {
let drhd = dmar_drhd.get(active_table);
println!("VER: {:X}", drhd.version);
println!("CAP: {:X}", drhd.cap);
println!("EXT_CAP: {:X}", drhd.ext_cap);
println!("GCMD: {:X}", drhd.gl_cmd);
println!("GSTS: {:X}", drhd.gl_sts);
println!("RT: {:X}", drhd.root_table);
},
_ => ()
}
}
} else {
println!(": Unknown");
}
}
/// Parse the ACPI tables to gather CPU, interrupt, and timer information
pub unsafe fn init(active_table: &mut ActivePageTable) -> Option<Acpi> {
let start_addr = 0xE0000;
let end_addr = 0xFFFFF;
// Map all of the ACPI RSDP space
{
let start_frame = Frame::containing_address(PhysicalAddress::new(start_addr));
let end_frame = Frame::containing_address(PhysicalAddress::new(end_addr));
for frame in Frame::range_inclusive(start_frame, end_frame) {
let page = Page::containing_address(VirtualAddress::new(frame.start_address().get()));
active_table.map_to(page, frame, entry::PRESENT | entry::NO_EXECUTE);
active_table.flush(page);
}
}
// Search for RSDP
if let Some(rsdp) = RSDP::search(start_addr, end_addr) {
let get_sdt = |sdt_address: usize, active_table: &mut ActivePageTable| -> (&'static Sdt, bool) {
let mapped = if active_table.translate_page(Page::containing_address(VirtualAddress::new(sdt_address))).is_none() {
let sdt_frame = Frame::containing_address(PhysicalAddress::new(sdt_address));
let sdt_page = Page::containing_address(VirtualAddress::new(sdt_address));
active_table.map_to(sdt_page, sdt_frame, entry::PRESENT | entry::NO_EXECUTE);
active_table.flush(sdt_page);
true
} else {
false
};
(&*(sdt_address as *const Sdt), mapped)
};
let drop_sdt = |sdt: &'static Sdt, mapped: bool, active_table: &mut ActivePageTable| {
let sdt_address = sdt as *const Sdt as usize;
drop(sdt);
if mapped {
let sdt_page = Page::containing_address(VirtualAddress::new(sdt_address));
active_table.unmap(sdt_page);
active_table.flush(sdt_page);
}
};
let (rxsdt, rxmapped) = get_sdt(rsdp.sdt_address(), active_table);
for &c in rxsdt.signature.iter() {
print!("{}", c as char);
}
println!(":");
if let Some(rsdt) = Rsdt::new(rxsdt) {
for sdt_address in rsdt.iter() {
let (sdt, mapped) = get_sdt(sdt_address, active_table);
init_sdt(sdt, active_table);
drop_sdt(sdt, mapped, active_table);
}
} else if let Some(xsdt) = Xsdt::new(rxsdt) {
for sdt_address in xsdt.iter() {
let (sdt, mapped) = get_sdt(sdt_address, active_table);
init_sdt(sdt, active_table);
drop_sdt(sdt, mapped, active_table);
}
} else {
println!("UNKNOWN RSDT OR XSDT SIGNATURE");
}
drop_sdt(rxsdt, rxmapped, active_table);
} else {
println!("NO RSDP FOUND");
}
// Unmap all of the ACPI RSDP space
{
let start_frame = Frame::containing_address(PhysicalAddress::new(start_addr));
let end_frame = Frame::containing_address(PhysicalAddress::new(end_addr));
for frame in Frame::range_inclusive(start_frame, end_frame) {
let page = Page::containing_address(VirtualAddress::new(frame.start_address().get()));
active_table.unmap(page);
active_table.flush(page);
}
}
None
}
pub struct Acpi;
/// RSDP
#[derive(Copy, Clone, Debug)]
#[repr(packed)]
pub struct RSDP {
signature: [u8; 8],
checksum: u8,
oemid: [u8; 6],
revision: u8,
rsdt_address: u32,
length: u32,
xsdt_address: u64,
extended_checksum: u8,
reserved: [u8; 3]
}
impl RSDP {
/// Search for the RSDP
pub fn search(start_addr: usize, end_addr: usize) -> Option<RSDP> {
for i in 0 .. (end_addr + 1 - start_addr)/16 {
let rsdp = unsafe { &*((start_addr + i * 16) as *const RSDP) };
if &rsdp.signature == b"RSD PTR " {
return Some(*rsdp);
}
}
None
}
/// Get the RSDT or XSDT address
pub fn sdt_address(&self) -> usize {
if self.revision >= 2 {
self.xsdt_address as usize
} else {
self.rsdt_address as usize
}
}
}

View file

@ -1,41 +0,0 @@
use core::mem;
use super::sdt::Sdt;
#[derive(Debug)]
pub struct Rsdt(&'static Sdt);
impl Rsdt {
pub fn new(sdt: &'static Sdt) -> Option<Rsdt> {
if &sdt.signature == b"RSDT" {
Some(Rsdt(sdt))
} else {
None
}
}
pub fn iter(&self) -> RsdtIter {
RsdtIter {
sdt: self.0,
i: 0
}
}
}
pub struct RsdtIter {
sdt: &'static Sdt,
i: usize
}
impl Iterator for RsdtIter {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
if self.i < self.sdt.data_len()/mem::size_of::<u32>() {
let item = unsafe { *(self.sdt.data_address() as *const u32).offset(self.i as isize) };
self.i += 1;
Some(item as usize)
} else {
None
}
}
}

View file

@ -1,33 +0,0 @@
use core::mem;
#[derive(Copy, Clone, Debug)]
#[repr(packed)]
pub struct Sdt {
pub signature: [u8; 4],
pub length: u32,
pub revision: u8,
pub checksum: u8,
pub oem_id: [u8; 6],
pub oem_table_id: [u8; 8],
pub oem_revision: u32,
pub creator_id: u32,
pub creator_revision: u32
}
impl Sdt {
/// Get the address of this tables data
pub fn data_address(&'static self) -> usize {
self as *const _ as usize + mem::size_of::<Sdt>()
}
/// Get the length of this tables data
pub fn data_len(&'static self) -> usize {
let total_size = self.length as usize;
let header_size = mem::size_of::<Sdt>();
if total_size >= header_size {
total_size - header_size
} else {
0
}
}
}

View file

@ -1,41 +0,0 @@
use core::mem;
use super::sdt::Sdt;
#[derive(Debug)]
pub struct Xsdt(&'static Sdt);
impl Xsdt {
pub fn new(sdt: &'static Sdt) -> Option<Xsdt> {
if &sdt.signature == b"XSDT" {
Some(Xsdt(sdt))
} else {
None
}
}
pub fn iter(&self) -> XsdtIter {
XsdtIter {
sdt: self.0,
i: 0
}
}
}
pub struct XsdtIter {
sdt: &'static Sdt,
i: usize
}
impl Iterator for XsdtIter {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
if self.i < self.sdt.data_len()/mem::size_of::<u64>() {
let item = unsafe { *(self.sdt.data_address() as *const u64).offset(self.i as isize) };
self.i += 1;
Some(item as usize)
} else {
None
}
}
}

View file

@ -1,14 +0,0 @@
use core::fmt::{self, Write};
use spin::Mutex;
use device::serial::COM1;
pub static CONSOLE: Mutex<Console> = Mutex::new(Console);
pub struct Console;
impl Write for Console {
fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
COM1.lock().write_str(s)
}
}

View file

@ -1,184 +0,0 @@
use core::mem;
use core::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT};
/// This must be used by the kernel to ensure that context switches are done atomically
/// Compare and exchange this to true when beginning a context switch on any CPU
/// The Context::switch_to function will set it back to false, allowing other CPU's to switch
/// This must be done, as no locks can be held on the stack during switch
pub static CONTEXT_SWITCH_LOCK: AtomicBool = ATOMIC_BOOL_INIT;
#[derive(Clone, Debug)]
pub struct Context {
/// FX valid?
loadable: bool,
/// FX location
fx: usize,
/// Page table pointer
cr3: usize,
/// RFLAGS register
rflags: usize,
/// RBX register
rbx: usize,
/// R12 register
r12: usize,
/// R13 register
r13: usize,
/// R14 register
r14: usize,
/// R15 register
r15: usize,
/// Base pointer
rbp: usize,
/// Stack pointer
rsp: usize
}
impl Context {
pub fn new() -> Context {
Context {
loadable: false,
fx: 0,
cr3: 0,
rflags: 0,
rbx: 0,
r12: 0,
r13: 0,
r14: 0,
r15: 0,
rbp: 0,
rsp: 0
}
}
pub fn get_page_table(&self) -> usize {
self.cr3
}
pub fn set_fx(&mut self, address: usize) {
self.fx = address;
}
pub fn set_page_table(&mut self, address: usize) {
self.cr3 = address;
}
pub fn set_stack(&mut self, address: usize) {
self.rsp = address;
}
pub unsafe fn signal_stack(&mut self, handler: extern fn(usize), sig: u8) {
self.push_stack(sig as usize);
self.push_stack(handler as usize);
self.push_stack(signal_handler_wrapper as usize);
}
pub unsafe fn push_stack(&mut self, value: usize) {
self.rsp -= mem::size_of::<usize>();
*(self.rsp as *mut usize) = value;
}
pub unsafe fn pop_stack(&mut self) -> usize {
let value = *(self.rsp as *const usize);
self.rsp += mem::size_of::<usize>();
value
}
/// Switch to the next context by restoring its stack and registers
#[cold]
#[inline(never)]
#[naked]
pub unsafe fn switch_to(&mut self, next: &mut Context) {
asm!("fxsave [$0]" : : "r"(self.fx) : "memory" : "intel", "volatile");
self.loadable = true;
if next.loadable {
asm!("fxrstor [$0]" : : "r"(next.fx) : "memory" : "intel", "volatile");
}else{
asm!("fninit" : : : "memory" : "intel", "volatile");
}
asm!("mov $0, cr3" : "=r"(self.cr3) : : "memory" : "intel", "volatile");
if next.cr3 != self.cr3 {
asm!("mov cr3, $0" : : "r"(next.cr3) : "memory" : "intel", "volatile");
}
asm!("pushfq ; pop $0" : "=r"(self.rflags) : : "memory" : "intel", "volatile");
asm!("push $0 ; popfq" : : "r"(next.rflags) : "memory" : "intel", "volatile");
asm!("mov $0, rbx" : "=r"(self.rbx) : : "memory" : "intel", "volatile");
asm!("mov rbx, $0" : : "r"(next.rbx) : "memory" : "intel", "volatile");
asm!("mov $0, r12" : "=r"(self.r12) : : "memory" : "intel", "volatile");
asm!("mov r12, $0" : : "r"(next.r12) : "memory" : "intel", "volatile");
asm!("mov $0, r13" : "=r"(self.r13) : : "memory" : "intel", "volatile");
asm!("mov r13, $0" : : "r"(next.r13) : "memory" : "intel", "volatile");
asm!("mov $0, r14" : "=r"(self.r14) : : "memory" : "intel", "volatile");
asm!("mov r14, $0" : : "r"(next.r14) : "memory" : "intel", "volatile");
asm!("mov $0, r15" : "=r"(self.r15) : : "memory" : "intel", "volatile");
asm!("mov r15, $0" : : "r"(next.r15) : "memory" : "intel", "volatile");
asm!("mov $0, rsp" : "=r"(self.rsp) : : "memory" : "intel", "volatile");
asm!("mov rsp, $0" : : "r"(next.rsp) : "memory" : "intel", "volatile");
asm!("mov $0, rbp" : "=r"(self.rbp) : : "memory" : "intel", "volatile");
asm!("mov rbp, $0" : : "r"(next.rbp) : "memory" : "intel", "volatile");
}
}
#[repr(packed)]
pub struct SignalHandlerStack {
r11: usize,
r10: usize,
r9: usize,
r8: usize,
rsi: usize,
rdi: usize,
rdx: usize,
rcx: usize,
rax: usize,
handler: extern fn(usize),
sig: usize,
rip: usize,
}
#[naked]
unsafe extern fn signal_handler_wrapper() {
#[inline(never)]
unsafe fn inner(stack: &SignalHandlerStack) {
(stack.handler)(stack.sig);
}
// Push scratch registers
asm!("push rax
push rcx
push rdx
push rdi
push rsi
push r8
push r9
push r10
push r11"
: : : : "intel", "volatile");
// Get reference to stack variables
let rsp: usize;
asm!("" : "={rsp}"(rsp) : : : "intel", "volatile");
// Call inner rust function
inner(&*(rsp as *const SignalHandlerStack));
// Pop scratch registers, error code, and return
asm!("pop r11
pop r10
pop r9
pop r8
pop rsi
pop rdi
pop rdx
pop rcx
pop rax
add rsp, 16"
: : : : "intel", "volatile");
}

View file

@ -1,126 +0,0 @@
extern crate raw_cpuid;
use core::fmt::{Result, Write};
use self::raw_cpuid::CpuId;
pub fn cpu_info<W: Write>(w: &mut W) -> Result {
let cpuid = CpuId::new();
if let Some(info) = cpuid.get_vendor_info() {
write!(w, "Vendor: {}\n", info.as_string())?;
}
if let Some(info) = cpuid.get_extended_function_info() {
if let Some(brand) = info.processor_brand_string() {
write!(w, "Model: {}\n", brand)?;
}
}
if let Some(info) = cpuid.get_processor_frequency_info() {
write!(w, "CPU Base MHz: {}\n", info.processor_base_frequency())?;
write!(w, "CPU Max MHz: {}\n", info.processor_max_frequency())?;
write!(w, "Bus MHz: {}\n", info.bus_frequency())?;
}
write!(w, "Features:")?;
if let Some(info) = cpuid.get_feature_info() {
if info.has_fpu() { write!(w, " fpu")? };
if info.has_vme() { write!(w, " vme")? };
if info.has_de() { write!(w, " de")? };
if info.has_pse() { write!(w, " pse")? };
if info.has_tsc() { write!(w, " tsc")? };
if info.has_msr() { write!(w, " msr")? };
if info.has_pae() { write!(w, " pae")? };
if info.has_mce() { write!(w, " mce")? };
if info.has_cmpxchg8b() { write!(w, " cx8")? };
if info.has_apic() { write!(w, " apic")? };
if info.has_sysenter_sysexit() { write!(w, " sep")? };
if info.has_mtrr() { write!(w, " mtrr")? };
if info.has_pge() { write!(w, " pge")? };
if info.has_mca() { write!(w, " mca")? };
if info.has_cmov() { write!(w, " cmov")? };
if info.has_pat() { write!(w, " pat")? };
if info.has_pse36() { write!(w, " pse36")? };
if info.has_psn() { write!(w, " psn")? };
if info.has_clflush() { write!(w, " clflush")? };
if info.has_ds() { write!(w, " ds")? };
if info.has_acpi() { write!(w, " acpi")? };
if info.has_mmx() { write!(w, " mmx")? };
if info.has_fxsave_fxstor() { write!(w, " fxsr")? };
if info.has_sse() { write!(w, " sse")? };
if info.has_sse2() { write!(w, " sse2")? };
if info.has_ss() { write!(w, " ss")? };
if info.has_htt() { write!(w, " ht")? };
if info.has_tm() { write!(w, " tm")? };
if info.has_pbe() { write!(w, " pbe")? };
if info.has_sse3() { write!(w, " sse3")? };
if info.has_pclmulqdq() { write!(w, " pclmulqdq")? };
if info.has_ds_area() { write!(w, " dtes64")? };
if info.has_monitor_mwait() { write!(w, " monitor")? };
if info.has_cpl() { write!(w, " ds_cpl")? };
if info.has_vmx() { write!(w, " vmx")? };
if info.has_smx() { write!(w, " smx")? };
if info.has_eist() { write!(w, " est")? };
if info.has_tm2() { write!(w, " tm2")? };
if info.has_ssse3() { write!(w, " ssse3")? };
if info.has_cnxtid() { write!(w, " cnxtid")? };
if info.has_fma() { write!(w, " fma")? };
if info.has_cmpxchg16b() { write!(w, " cx16")? };
if info.has_pdcm() { write!(w, " pdcm")? };
if info.has_pcid() { write!(w, " pcid")? };
if info.has_dca() { write!(w, " dca")? };
if info.has_sse41() { write!(w, " sse4_1")? };
if info.has_sse42() { write!(w, " sse4_2")? };
if info.has_x2apic() { write!(w, " x2apic")? };
if info.has_movbe() { write!(w, " movbe")? };
if info.has_popcnt() { write!(w, " popcnt")? };
if info.has_tsc_deadline() { write!(w, " tsc_deadline_timer")? };
if info.has_aesni() { write!(w, " aes")? };
if info.has_xsave() { write!(w, " xsave")? };
if info.has_oxsave() { write!(w, " xsaveopt")? };
if info.has_avx() { write!(w, " avx")? };
if info.has_f16c() { write!(w, " f16c")? };
if info.has_rdrand() { write!(w, " rdrand")? };
}
if let Some(info) = cpuid.get_extended_function_info() {
if info.has_64bit_mode() { write!(w, " lm")? };
if info.has_rdtscp() { write!(w, " rdtscp")? };
if info.has_1gib_pages() { write!(w, " pdpe1gb")? };
if info.has_execute_disable() { write!(w, " nx")? };
if info.has_syscall_sysret() { write!(w, " syscall")? };
if info.has_prefetchw() { write!(w, " prefetchw")? };
if info.has_lzcnt() { write!(w, " lzcnt")? };
if info.has_lahf_sahf() { write!(w, " lahf_lm")? };
if info.has_invariant_tsc() { write!(w, " constant_tsc")? };
}
if let Some(info) = cpuid.get_extended_feature_info() {
if info.has_fsgsbase() { write!(w, " fsgsbase")? };
if info.has_tsc_adjust_msr() { write!(w, " tsc_adjust")? };
if info.has_bmi1() { write!(w, " bmi1")? };
if info.has_hle() { write!(w, " hle")? };
if info.has_avx2() { write!(w, " avx2")? };
if info.has_smep() { write!(w, " smep")? };
if info.has_bmi2() { write!(w, " bmi2")? };
if info.has_rep_movsb_stosb() { write!(w, " erms")? };
if info.has_invpcid() { write!(w, " invpcid")? };
if info.has_rtm() { write!(w, " rtm")? };
if info.has_qm() { write!(w, " qm")? };
if info.has_fpu_cs_ds_deprecated() { write!(w, " fpu_seg")? };
if info.has_mpx() { write!(w, " mpx")? };
}
write!(w, "\n")?;
Ok(())
}

View file

@ -1,114 +0,0 @@
use core::intrinsics::{volatile_load, volatile_store};
use x86::cpuid::CpuId;
use x86::msr::*;
use memory::Frame;
use paging::{entry, ActivePageTable, PhysicalAddress, Page, VirtualAddress};
pub static mut LOCAL_APIC: LocalApic = LocalApic {
address: 0,
x2: false
};
pub unsafe fn init(active_table: &mut ActivePageTable) {
LOCAL_APIC.init(active_table);
}
pub unsafe fn init_ap() {
LOCAL_APIC.init_ap();
}
/// Local APIC
pub struct LocalApic {
pub address: usize,
pub x2: bool
}
impl LocalApic {
unsafe fn init(&mut self, active_table: &mut ActivePageTable) {
self.address = (rdmsr(IA32_APIC_BASE) as usize & 0xFFFF0000) + ::KERNEL_OFFSET;
self.x2 = CpuId::new().get_feature_info().unwrap().has_x2apic();
if ! self.x2 {
let page = Page::containing_address(VirtualAddress::new(self.address));
let frame = Frame::containing_address(PhysicalAddress::new(self.address - ::KERNEL_OFFSET));
active_table.map_to(page, frame, entry::PRESENT | entry::WRITABLE | entry::NO_EXECUTE);
}
self.init_ap();
}
unsafe fn init_ap(&mut self) {
if self.x2 {
wrmsr(IA32_APIC_BASE, rdmsr(IA32_APIC_BASE) | 1 << 10);
wrmsr(IA32_X2APIC_SIVR, 0x100);
} else {
self.write(0xF0, 0x100);
}
}
unsafe fn read(&self, reg: u32) -> u32 {
volatile_load((self.address + reg as usize) as *const u32)
}
unsafe fn write(&mut self, reg: u32, value: u32) {
volatile_store((self.address + reg as usize) as *mut u32, value);
}
pub fn id(&self) -> u32 {
if self.x2 {
unsafe { rdmsr(IA32_X2APIC_APICID) as u32 }
} else {
unsafe { self.read(0x20) }
}
}
pub fn version(&self) -> u32 {
if self.x2 {
unsafe { rdmsr(IA32_X2APIC_VERSION) as u32 }
} else {
unsafe { self.read(0x30) }
}
}
pub fn icr(&self) -> u64 {
if self.x2 {
unsafe { rdmsr(IA32_X2APIC_ICR) }
} else {
unsafe {
(self.read(0x310) as u64) << 32 | self.read(0x300) as u64
}
}
}
pub fn set_icr(&mut self, value: u64) {
if self.x2 {
unsafe { wrmsr(IA32_X2APIC_ICR, value); }
} else {
unsafe {
while self.read(0x300) & 1 << 12 == 1 << 12 {}
self.write(0x310, (value >> 32) as u32);
self.write(0x300, value as u32);
while self.read(0x300) & 1 << 12 == 1 << 12 {}
}
}
}
pub fn ipi(&mut self, apic_id: usize) {
let mut icr = 0x4040;
if self.x2 {
icr |= (apic_id as u64) << 32;
} else {
icr |= (apic_id as u64) << 56;
}
self.set_icr(icr);
}
pub unsafe fn eoi(&mut self) {
if self.x2 {
wrmsr(IA32_X2APIC_EOI, 0);
} else {
self.write(0xB0, 0);
}
}
}

View file

@ -1,16 +0,0 @@
use paging::ActivePageTable;
pub mod cpu;
pub mod local_apic;
pub mod rtc;
pub mod serial;
pub unsafe fn init(active_table: &mut ActivePageTable){
local_apic::init(active_table);
rtc::init();
serial::init();
}
pub unsafe fn init_ap() {
local_apic::init_ap();
}

View file

@ -1,109 +0,0 @@
use io::{Io, Pio};
use time;
pub fn init() {
let mut rtc = Rtc::new();
time::START.lock().0 = rtc.time();
}
fn cvt_bcd(value: usize) -> usize {
(value & 0xF) + ((value / 16) * 10)
}
/// RTC
pub struct Rtc {
addr: Pio<u8>,
data: Pio<u8>,
}
impl Rtc {
/// Create new empty RTC
pub fn new() -> Self {
return Rtc {
addr: Pio::<u8>::new(0x70),
data: Pio::<u8>::new(0x71),
};
}
/// Read
unsafe fn read(&mut self, reg: u8) -> u8 {
self.addr.write(reg);
return self.data.read();
}
/// Wait
unsafe fn wait(&mut self) {
while self.read(0xA) & 0x80 != 0x80 {}
while self.read(0xA) & 0x80 == 0x80 {}
}
/// Get time
pub fn time(&mut self) -> u64 {
let mut second;
let mut minute;
let mut hour;
let mut day;
let mut month;
let mut year;
let register_b;
unsafe {
self.wait();
second = self.read(0) as usize;
minute = self.read(2) as usize;
hour = self.read(4) as usize;
day = self.read(7) as usize;
month = self.read(8) as usize;
year = self.read(9) as usize;
register_b = self.read(0xB);
}
if register_b & 4 != 4 {
second = cvt_bcd(second);
minute = cvt_bcd(minute);
hour = cvt_bcd(hour & 0x7F) | (hour & 0x80);
day = cvt_bcd(day);
month = cvt_bcd(month);
year = cvt_bcd(year);
}
if register_b & 2 != 2 || hour & 0x80 == 0x80 {
hour = ((hour & 0x7F) + 12) % 24;
}
// TODO: Century Register
year += 2000;
// Unix time from clock
let mut secs: u64 = (year as u64 - 1970) * 31536000;
let mut leap_days = (year as u64 - 1972) / 4 + 1;
if year % 4 == 0 {
if month <= 2 {
leap_days -= 1;
}
}
secs += leap_days * 86400;
match month {
2 => secs += 2678400,
3 => secs += 5097600,
4 => secs += 7776000,
5 => secs += 10368000,
6 => secs += 13046400,
7 => secs += 15638400,
8 => secs += 18316800,
9 => secs += 20995200,
10 => secs += 23587200,
11 => secs += 26265600,
12 => secs += 28857600,
_ => (),
}
secs += (day as u64 - 1) * 86400;
secs += hour as u64 * 3600;
secs += minute as u64 * 60;
secs += second as u64;
secs
}
}

View file

@ -1,115 +0,0 @@
use core::fmt::{self, Write};
use spin::Mutex;
use io::{Io, Pio, ReadOnly};
pub static COM1: Mutex<SerialPort> = Mutex::new(SerialPort::new(0x3F8));
pub static COM2: Mutex<SerialPort> = Mutex::new(SerialPort::new(0x2F8));
pub unsafe fn init() {
COM1.lock().init();
COM2.lock().init();
}
bitflags! {
/// Interrupt enable flags
flags IntEnFlags: u8 {
const RECEIVED = 1,
const SENT = 1 << 1,
const ERRORED = 1 << 2,
const STATUS_CHANGE = 1 << 3,
// 4 to 7 are unused
}
}
bitflags! {
/// Line status flags
flags LineStsFlags: u8 {
const INPUT_FULL = 1,
// 1 to 4 unknown
const OUTPUT_EMPTY = 1 << 5,
// 6 and 7 unknown
}
}
#[allow(dead_code)]
pub struct SerialPort {
/// Data register, read to receive, write to send
data: Pio<u8>,
/// Interrupt enable
int_en: Pio<u8>,
/// FIFO control
fifo_ctrl: Pio<u8>,
/// Line control
line_ctrl: Pio<u8>,
/// Modem control
modem_ctrl: Pio<u8>,
/// Line status
line_sts: ReadOnly<Pio<u8>>,
/// Modem status
modem_sts: ReadOnly<Pio<u8>>,
}
impl SerialPort {
const fn new(base: u16) -> SerialPort {
SerialPort {
data: Pio::new(base),
int_en: Pio::new(base + 1),
fifo_ctrl: Pio::new(base + 2),
line_ctrl: Pio::new(base + 3),
modem_ctrl: Pio::new(base + 4),
line_sts: ReadOnly::new(Pio::new(base + 5)),
modem_sts: ReadOnly::new(Pio::new(base + 6))
}
}
fn line_sts(&self) -> LineStsFlags {
LineStsFlags::from_bits_truncate(self.line_sts.read())
}
fn write(&mut self, data: u8) {
while ! self.line_sts().contains(OUTPUT_EMPTY) {}
self.data.write(data)
}
fn init(&mut self) {
//TODO: Cleanup
self.int_en.write(0x00);
self.line_ctrl.write(0x80);
self.data.write(0x03);
self.int_en.write(0x00);
self.line_ctrl.write(0x03);
self.fifo_ctrl.write(0xC7);
self.modem_ctrl.write(0x0B);
self.int_en.write(0x01);
}
pub fn on_receive(&mut self) {
let data = self.data.read();
extern {
fn debug_input(byte: u8);
}
unsafe { debug_input(data) };
}
}
impl Write for SerialPort {
fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
for byte in s.bytes() {
match byte {
8 | 0x7F => {
self.write(8);
self.write(b' ');
self.write(8);
},
_ => {
self.write(byte);
}
}
}
Ok(())
}
}

View file

@ -1,70 +0,0 @@
/// Memcpy
///
/// Copy N bytes of memory from one location to another.
#[no_mangle]
pub unsafe extern fn memcpy(dest: *mut u8, src: *const u8,
n: usize) -> *mut u8 {
let mut i = 0;
while i < n {
*((dest as usize + i) as *mut u8) = *((src as usize + i) as *const u8);
i += 1;
}
dest
}
/// Memmove
///
/// Copy N bytes of memory from src to dest. The memory areas may overlap.
#[no_mangle]
pub unsafe extern fn memmove(dest: *mut u8, src: *const u8,
n: usize) -> *mut u8 {
if src < dest as *const u8 {
let mut i = n;
while i != 0 {
i -= 1;
*((dest as usize + i) as *mut u8) = *((src as usize + i) as *const u8);
}
} else {
let mut i = 0;
while i < n {
*((dest as usize + i) as *mut u8) = *((src as usize + i) as *const u8);
i += 1;
}
}
dest
}
/// Memset
///
/// Fill a block of memory with a specified value.
#[no_mangle]
pub unsafe extern fn memset(dest: *mut u8, c: i32, n: usize) -> *mut u8 {
let mut i = 0;
while i < n {
*((dest as usize + i) as *mut u8) = c as u8;
i += 1;
}
dest
}
/// Memcmp
///
/// Compare two blocks of memory.
#[no_mangle]
pub unsafe extern fn memcmp(s1: *const u8, s2: *const u8, n: usize) -> i32 {
let mut i = 0;
while i < n {
let a = *((s1 as usize + i) as *const u8);
let b = *((s2 as usize + i) as *const u8);
if a != b {
return a as i32 - b as i32
}
i += 1;
}
0
}

View file

@ -1,177 +0,0 @@
//! Global descriptor table
use core::mem;
use x86::dtables::{self, DescriptorTablePointer};
use x86::segmentation::{self, SegmentSelector};
use x86::task::{self, TaskStateSegment};
pub const GDT_NULL: usize = 0;
pub const GDT_KERNEL_CODE: usize = 1;
pub const GDT_KERNEL_DATA: usize = 2;
pub const GDT_KERNEL_TLS: usize = 3;
pub const GDT_USER_CODE: usize = 4;
pub const GDT_USER_DATA: usize = 5;
pub const GDT_USER_TLS: usize = 6;
pub const GDT_TSS: usize = 7;
pub const GDT_TSS_HIGH: usize = 8;
pub const GDT_A_PRESENT: u8 = 1 << 7;
pub const GDT_A_RING_0: u8 = 0 << 5;
pub const GDT_A_RING_1: u8 = 1 << 5;
pub const GDT_A_RING_2: u8 = 2 << 5;
pub const GDT_A_RING_3: u8 = 3 << 5;
pub const GDT_A_SYSTEM: u8 = 1 << 4;
pub const GDT_A_EXECUTABLE: u8 = 1 << 3;
pub const GDT_A_CONFORMING: u8 = 1 << 2;
pub const GDT_A_PRIVILEGE: u8 = 1 << 1;
pub const GDT_A_DIRTY: u8 = 1;
pub const GDT_A_TSS_AVAIL: u8 = 0x9;
pub const GDT_A_TSS_BUSY: u8 = 0xB;
pub const GDT_F_PAGE_SIZE: u8 = 1 << 7;
pub const GDT_F_PROTECTED_MODE: u8 = 1 << 6;
pub const GDT_F_LONG_MODE: u8 = 1 << 5;
static mut INIT_GDTR: DescriptorTablePointer = DescriptorTablePointer {
limit: 0,
base: 0
};
static mut INIT_GDT: [GdtEntry; 4] = [
// Null
GdtEntry::new(0, 0, 0, 0),
// Kernel code
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_EXECUTABLE | GDT_A_PRIVILEGE, GDT_F_LONG_MODE),
// Kernel data
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_LONG_MODE),
// Kernel TLS
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_LONG_MODE)
];
#[thread_local]
pub static mut GDTR: DescriptorTablePointer = DescriptorTablePointer {
limit: 0,
base: 0
};
#[thread_local]
pub static mut GDT: [GdtEntry; 9] = [
// Null
GdtEntry::new(0, 0, 0, 0),
// Kernel code
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_EXECUTABLE | GDT_A_PRIVILEGE, GDT_F_LONG_MODE),
// Kernel data
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_LONG_MODE),
// Kernel TLS
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_0 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_LONG_MODE),
// User code
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_EXECUTABLE | GDT_A_PRIVILEGE, GDT_F_LONG_MODE),
// User data
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_LONG_MODE),
// User TLS
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_SYSTEM | GDT_A_PRIVILEGE, GDT_F_LONG_MODE),
// TSS
GdtEntry::new(0, 0, GDT_A_PRESENT | GDT_A_RING_3 | GDT_A_TSS_AVAIL, 0),
// TSS must be 16 bytes long, twice the normal size
GdtEntry::new(0, 0, 0, 0),
];
#[thread_local]
pub static mut TSS: TaskStateSegment = TaskStateSegment {
reserved: 0,
rsp: [0; 3],
reserved2: 0,
ist: [0; 7],
reserved3: 0,
reserved4: 0,
iomap_base: 0xFFFF
};
/// Initialize GDT
pub unsafe fn init(tcb_offset: usize, stack_offset: usize) {
// Setup the initial GDT with TLS, so we can setup the TLS GDT (a little confusing)
// This means that each CPU will have its own GDT, but we only need to define it once as a thread local
INIT_GDTR.limit = (INIT_GDT.len() * mem::size_of::<GdtEntry>() - 1) as u16;
INIT_GDTR.base = INIT_GDT.as_ptr() as u64;
// Set the TLS segment to the offset of the Thread Control Block
INIT_GDT[GDT_KERNEL_TLS].set_offset(tcb_offset as u32);
// Load the initial GDT, before we have access to thread locals
dtables::lgdt(&INIT_GDTR);
// Load the segment descriptors
segmentation::load_cs(SegmentSelector::new(GDT_KERNEL_CODE as u16));
segmentation::load_ds(SegmentSelector::new(GDT_KERNEL_DATA as u16));
segmentation::load_es(SegmentSelector::new(GDT_KERNEL_DATA as u16));
segmentation::load_fs(SegmentSelector::new(GDT_KERNEL_TLS as u16));
segmentation::load_gs(SegmentSelector::new(GDT_KERNEL_DATA as u16));
segmentation::load_ss(SegmentSelector::new(GDT_KERNEL_DATA as u16));
// Now that we have access to thread locals, setup the AP's individual GDT
GDTR.limit = (GDT.len() * mem::size_of::<GdtEntry>() - 1) as u16;
GDTR.base = GDT.as_ptr() as u64;
// Set the TLS segment to the offset of the Thread Control Block
GDT[GDT_KERNEL_TLS].set_offset(tcb_offset as u32);
// Set the User TLS segment to the offset of the user TCB
GDT[GDT_USER_TLS].set_offset(::USER_TCB_OFFSET as u32);
// We can now access our TSS, which is a thread local
GDT[GDT_TSS].set_offset(&TSS as *const _ as u32);
GDT[GDT_TSS].set_limit(mem::size_of::<TaskStateSegment>() as u32);
// Set the stack pointer when coming back from userspace
TSS.rsp[0] = stack_offset as u64;
// Load the new GDT, which is correctly located in thread local storage
dtables::lgdt(&GDTR);
// Reload the segment descriptors
segmentation::load_cs(SegmentSelector::new(GDT_KERNEL_CODE as u16));
segmentation::load_ds(SegmentSelector::new(GDT_KERNEL_DATA as u16));
segmentation::load_es(SegmentSelector::new(GDT_KERNEL_DATA as u16));
segmentation::load_fs(SegmentSelector::new(GDT_KERNEL_TLS as u16));
segmentation::load_gs(SegmentSelector::new(GDT_KERNEL_DATA as u16));
segmentation::load_ss(SegmentSelector::new(GDT_KERNEL_DATA as u16));
// Load the task register
task::load_ltr(SegmentSelector::new(GDT_TSS as u16));
}
#[derive(Copy, Clone, Debug)]
#[repr(packed)]
pub struct GdtEntry {
pub limitl: u16,
pub offsetl: u16,
pub offsetm: u8,
pub access: u8,
pub flags_limith: u8,
pub offseth: u8
}
impl GdtEntry {
pub const fn new(offset: u32, limit: u32, access: u8, flags: u8) -> Self {
GdtEntry {
limitl: limit as u16,
offsetl: offset as u16,
offsetm: (offset >> 16) as u8,
access: access,
flags_limith: flags & 0xF0 | ((limit >> 16) as u8) & 0x0F,
offseth: (offset >> 24) as u8
}
}
pub fn set_offset(&mut self, offset: u32) {
self.offsetl = offset as u16;
self.offsetm = (offset >> 16) as u8;
self.offseth = (offset >> 24) as u8;
}
pub fn set_limit(&mut self, limit: u32) {
self.limitl = limit as u16;
self.flags_limith = self.flags_limith & 0xF0 | ((limit >> 16) as u8) & 0x0F;
}
}

View file

@ -1,142 +0,0 @@
use core::mem;
use x86::dtables::{self, DescriptorTablePointer};
use interrupt::*;
pub static mut IDTR: DescriptorTablePointer = DescriptorTablePointer {
limit: 0,
base: 0
};
pub static mut IDT: [IdtEntry; 256] = [IdtEntry::new(); 256];
pub unsafe fn init() {
IDTR.limit = (IDT.len() * mem::size_of::<IdtEntry>() - 1) as u16;
IDTR.base = IDT.as_ptr() as u64;
// Set up exceptions
IDT[0].set_func(exception::divide_by_zero);
IDT[1].set_func(exception::debug);
IDT[2].set_func(exception::non_maskable);
IDT[3].set_func(exception::breakpoint);
IDT[4].set_func(exception::overflow);
IDT[5].set_func(exception::bound_range);
IDT[6].set_func(exception::invalid_opcode);
IDT[7].set_func(exception::device_not_available);
IDT[8].set_func(exception::double_fault);
// 9 no longer available
IDT[10].set_func(exception::invalid_tss);
IDT[11].set_func(exception::segment_not_present);
IDT[12].set_func(exception::stack_segment);
IDT[13].set_func(exception::protection);
IDT[14].set_func(exception::page);
// 15 reserved
IDT[16].set_func(exception::fpu);
IDT[17].set_func(exception::alignment_check);
IDT[18].set_func(exception::machine_check);
IDT[19].set_func(exception::simd);
IDT[20].set_func(exception::virtualization);
// 21 through 29 reserved
IDT[30].set_func(exception::security);
// 31 reserved
// Set up IRQs
IDT[32].set_func(irq::pit);
IDT[33].set_func(irq::keyboard);
IDT[34].set_func(irq::cascade);
IDT[35].set_func(irq::com2);
IDT[36].set_func(irq::com1);
IDT[37].set_func(irq::lpt2);
IDT[38].set_func(irq::floppy);
IDT[39].set_func(irq::lpt1);
IDT[40].set_func(irq::rtc);
IDT[41].set_func(irq::pci1);
IDT[42].set_func(irq::pci2);
IDT[43].set_func(irq::pci3);
IDT[44].set_func(irq::mouse);
IDT[45].set_func(irq::fpu);
IDT[46].set_func(irq::ata1);
IDT[47].set_func(irq::ata2);
// Set IPI handler (null)
IDT[0x40].set_func(ipi::ipi);
// Set syscall function
IDT[0x80].set_func(syscall::syscall);
IDT[0x80].set_flags(IDT_PRESENT | IDT_RING_3 | IDT_INTERRUPT);
dtables::lidt(&IDTR);
}
bitflags! {
pub flags IdtFlags: u8 {
const IDT_PRESENT = 1 << 7,
const IDT_RING_0 = 0 << 5,
const IDT_RING_1 = 1 << 5,
const IDT_RING_2 = 2 << 5,
const IDT_RING_3 = 3 << 5,
const IDT_SS = 1 << 4,
const IDT_INTERRUPT = 0xE,
const IDT_TRAP = 0xF,
}
}
#[repr(packed)]
pub struct IdtDescriptor {
size: u16,
offset: u64
}
impl IdtDescriptor {
pub fn set_slice(&mut self, slice: &'static [IdtEntry]) {
self.size = (slice.len() * mem::size_of::<IdtEntry>() - 1) as u16;
self.offset = slice.as_ptr() as u64;
}
pub unsafe fn load(&self) {
asm!("lidt [rax]" : : "{rax}"(self as *const _ as usize) : : "intel", "volatile");
}
}
#[derive(Copy, Clone, Debug)]
#[repr(packed)]
pub struct IdtEntry {
offsetl: u16,
selector: u16,
zero: u8,
attribute: u8,
offsetm: u16,
offseth: u32,
zero2: u32
}
impl IdtEntry {
pub const fn new() -> IdtEntry {
IdtEntry {
offsetl: 0,
selector: 0,
zero: 0,
attribute: 0,
offsetm: 0,
offseth: 0,
zero2: 0
}
}
pub fn set_flags(&mut self, flags: IdtFlags) {
self.attribute = flags.bits;
}
pub fn set_offset(&mut self, selector: u16, base: usize) {
self.selector = selector;
self.offsetl = base as u16;
self.offsetm = (base >> 16) as u16;
self.offseth = (base >> 32) as u32;
}
// A function to set the offset more easily
pub fn set_func(&mut self, func: unsafe extern fn()) {
self.set_flags(IDT_PRESENT | IDT_RING_0 | IDT_INTERRUPT);
self.set_offset(8, func as usize);
}
}

View file

@ -1,123 +0,0 @@
use interrupt::stack_trace;
use syscall::flag::*;
extern {
fn ksignal(signal: usize);
}
interrupt_stack!(divide_by_zero, stack, {
println!("Divide by zero fault at {:>02X}:{:>016X}", stack.cs, stack.rip);
stack_trace();
ksignal(SIGFPE);
});
interrupt_stack!(debug, stack, {
println!("Debug trap at {:>02X}:{:>016X}", stack.cs, stack.rip);
ksignal(SIGTRAP);
});
interrupt_stack!(non_maskable, stack, {
println!("Non-maskable interrupt at {:>02X}:{:>016X}", stack.cs, stack.rip);
});
interrupt_stack!(breakpoint, stack, {
println!("Breakpoint trap at {:>02X}:{:>016X}", stack.cs, stack.rip);
ksignal(SIGTRAP);
});
interrupt_stack!(overflow, stack, {
println!("Overflow trap at {:>02X}:{:>016X}", stack.cs, stack.rip);
ksignal(SIGFPE);
});
interrupt_stack!(bound_range, stack, {
println!("Bound range exceeded fault at {:>02X}:{:>016X}", stack.cs, stack.rip);
stack_trace();
ksignal(SIGSEGV);
});
interrupt_stack!(invalid_opcode, stack, {
println!("Invalid opcode fault at {:>02X}:{:>016X}", stack.cs, stack.rip);
stack_trace();
ksignal(SIGILL);
});
interrupt_stack!(device_not_available, stack, {
println!("Device not available fault at {:>02X}:{:>016X}", stack.cs, stack.rip);
stack_trace();
ksignal(SIGILL);
});
interrupt_error!(double_fault, stack, {
println!("Double fault: {:X} at {:>02X}:{:>016X}", stack.code, stack.cs, stack.rip);
stack_trace();
ksignal(SIGSEGV);
});
interrupt_error!(invalid_tss, stack, {
println!("Invalid TSS fault: {:X} at {:>02X}:{:>016X}", stack.code, stack.cs, stack.rip);
stack_trace();
ksignal(SIGSEGV);
});
interrupt_error!(segment_not_present, stack, {
println!("Segment not present fault: {:X} at {:>02X}:{:>016X}", stack.code, stack.cs, stack.rip);
stack_trace();
ksignal(SIGSEGV);
});
interrupt_error!(stack_segment, stack, {
println!("Stack segment fault: {:X} at {:>02X}:{:>016X}", stack.code, stack.cs, stack.rip);
stack_trace();
ksignal(SIGSEGV);
});
interrupt_error!(protection, stack, {
println!("Protection fault: {:X} at {:>02X}:{:>016X}", stack.code, stack.cs, stack.rip);
stack_trace();
ksignal(SIGSEGV);
});
interrupt_error!(page, stack, {
let cr2: usize;
asm!("mov rax, cr2" : "={rax}"(cr2) : : : "intel", "volatile");
println!("Page fault: {:>02X}:{:>016X} at {:>02X}:{:>016X}", stack.code, cr2, stack.cs, stack.rip);
stack_trace();
ksignal(SIGSEGV);
});
interrupt_stack!(fpu, stack, {
println!("FPU floating point fault at {:>02X}:{:>016X}", stack.cs, stack.rip);
stack_trace();
ksignal(SIGFPE);
});
interrupt_error!(alignment_check, stack, {
println!("Alignment check fault: {:X} at {:>02X}:{:>016X}", stack.code, stack.cs, stack.rip);
stack_trace();
ksignal(SIGBUS);
});
interrupt_stack!(machine_check, stack, {
println!("Machine check fault at {:>02X}:{:>016X}", stack.cs, stack.rip);
stack_trace();
ksignal(SIGBUS);
});
interrupt_stack!(simd, stack, {
println!("SIMD floating point fault at {:>02X}:{:>016X}", stack.cs, stack.rip);
stack_trace();
ksignal(SIGFPE);
});
interrupt_stack!(virtualization, stack, {
println!("Virtualization fault at {:>02X}:{:>016X}", stack.cs, stack.rip);
stack_trace();
ksignal(SIGBUS);
});
interrupt_error!(security, stack, {
println!("Security exception: {:X} at {:>02X}:{:>016X}", stack.code, stack.cs, stack.rip);
stack_trace();
ksignal(SIGBUS);
});

View file

@ -1,5 +0,0 @@
use device::local_apic::LOCAL_APIC;
interrupt!(ipi, {
LOCAL_APIC.eoi();
});

View file

@ -1,115 +0,0 @@
use x86::io;
use device::serial::{COM1, COM2};
use time;
extern {
fn irq_trigger(irq: u8);
}
#[inline(always)]
unsafe fn master_ack() {
io::outb(0x20, 0x20);
}
#[inline(always)]
unsafe fn slave_ack() {
io::outb(0xA0, 0x20);
master_ack();
}
pub unsafe fn acknowledge(irq: usize) {
if irq >= 8 {
slave_ack();
} else {
master_ack();
}
}
interrupt!(pit, {
// Saves CPU time by not sending IRQ event irq_trigger(0);
{
const PIT_RATE: u64 = 2250286;
let mut offset = time::OFFSET.lock();
let sum = offset.1 + PIT_RATE;
offset.1 = sum % 1000000000;
offset.0 += sum / 1000000000;
}
master_ack();
});
interrupt!(keyboard, {
irq_trigger(1);
});
interrupt!(cascade, {
irq_trigger(2);
master_ack();
});
interrupt!(com2, {
irq_trigger(3);
COM2.lock().on_receive();
master_ack();
});
interrupt!(com1, {
irq_trigger(4);
COM1.lock().on_receive();
master_ack();
});
interrupt!(lpt2, {
irq_trigger(5);
master_ack();
});
interrupt!(floppy, {
irq_trigger(6);
master_ack();
});
interrupt!(lpt1, {
irq_trigger(7);
master_ack();
});
interrupt!(rtc, {
irq_trigger(8);
slave_ack();
});
interrupt!(pci1, {
irq_trigger(9);
slave_ack();
});
interrupt!(pci2, {
irq_trigger(10);
});
interrupt!(pci3, {
irq_trigger(11);
});
interrupt!(mouse, {
irq_trigger(12);
});
interrupt!(fpu, {
irq_trigger(13);
slave_ack();
});
interrupt!(ata1, {
irq_trigger(14);
slave_ack();
});
interrupt!(ata2, {
irq_trigger(15);
slave_ack();
});

View file

@ -1,85 +0,0 @@
//! Interrupt instructions
use core::mem;
use paging::{ActivePageTable, VirtualAddress};
pub mod exception;
pub mod ipi;
pub mod irq;
pub mod syscall;
/// Clear interrupts
#[inline(always)]
pub unsafe fn disable() {
asm!("cli" : : : : "intel", "volatile");
}
/// Set interrupts
#[inline(always)]
pub unsafe fn enable() {
asm!("sti" : : : : "intel", "volatile");
}
/// Set interrupts and halt
/// This will atomically wait for the next interrupt
/// Performing enable followed by halt is not guaranteed to be atomic, use this instead!
#[inline(always)]
pub unsafe fn enable_and_halt() {
asm!("sti
hlt"
: : : : "intel", "volatile");
}
/// Set interrupts and nop
/// This will enable interrupts and allow the IF flag to be processed
/// Simply enabling interrupts does not gurantee that they will trigger, use this instead!
#[inline(always)]
pub unsafe fn enable_and_nop() {
asm!("sti
nop"
: : : : "intel", "volatile");
}
/// Halt instruction
#[inline(always)]
pub unsafe fn halt() {
asm!("hlt" : : : : "intel", "volatile");
}
/// Pause instruction
/// Safe because it is similar to a NOP, and has no memory effects
#[inline(always)]
pub fn pause() {
unsafe { asm!("pause" : : : : "intel", "volatile"); }
}
/// Get a stack trace
//TODO: Check for stack being mapped before dereferencing
#[inline(never)]
pub unsafe fn stack_trace() {
let mut rbp: usize;
asm!("" : "={rbp}"(rbp) : : : "intel", "volatile");
println!("TRACE: {:>016X}", rbp);
//Maximum 64 frames
let active_table = ActivePageTable::new();
for _frame in 0..64 {
if let Some(rip_rbp) = rbp.checked_add(mem::size_of::<usize>()) {
if active_table.translate(VirtualAddress::new(rbp)).is_some() && active_table.translate(VirtualAddress::new(rip_rbp)).is_some() {
let rip = *(rip_rbp as *const usize);
if rip == 0 {
println!(" {:>016X}: EMPTY RETURN", rbp);
break;
}
println!(" {:>016X}: {:>016X}", rbp, rip);
rbp = *(rbp as *const usize);
} else {
println!(" {:>016X}: GUARD PAGE", rbp);
break;
}
} else {
println!(" {:>016X}: RBP OVERFLOW", rbp);
}
}
}

View file

@ -1,61 +0,0 @@
#[naked]
pub unsafe extern fn syscall() {
#[inline(never)]
unsafe fn inner() {
extern {
fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize, stack: usize) -> usize;
}
let mut a;
{
let b;
let c;
let d;
let e;
let f;
let stack;
asm!("" : "={rax}"(a), "={rbx}"(b), "={rcx}"(c), "={rdx}"(d), "={rsi}"(e), "={rdi}"(f), "={rbp}"(stack)
: : : "intel", "volatile");
a = syscall(a, b, c, d, e, f, stack);
}
asm!("" : : "{rax}"(a) : : "intel", "volatile");
}
// Push scratch registers, minus rax for the return value
asm!("push rcx
push rdx
push rdi
push rsi
push r8
push r9
push r10
push r11
push fs
mov r11, 0x18
mov fs, r11"
: : : : "intel", "volatile");
inner();
// Interrupt return
asm!("pop fs
pop r11
pop r10
pop r9
pop r8
pop rsi
pop rdi
pop rdx
pop rcx
iretq"
: : : : "intel", "volatile");
}
#[naked]
pub unsafe extern fn clone_ret() -> usize {
asm!("pop rbp"
: : : : "intel", "volatile");
0
}

View file

@ -1,323 +0,0 @@
//! Architecture support for x86_64
#![feature(asm)]
#![feature(concat_idents)]
#![feature(const_fn)]
#![feature(core_intrinsics)]
#![feature(drop_types_in_const)]
#![feature(lang_items)]
#![feature(naked_functions)]
#![feature(thread_local)]
#![feature(unique)]
#![no_std]
extern crate hole_list_allocator as allocator;
#[macro_use]
extern crate bitflags;
extern crate io;
extern crate spin;
extern crate syscall;
pub extern crate x86;
// Because the memory map is so important to not be aliased, it is defined here, in one place
// The lower 256 PML4 entries are reserved for userspace
// Each PML4 entry references up to 512 GB of memory
// The top (511) PML4 is reserved for recursive mapping
// The second from the top (510) PML4 is reserved for the kernel
/// The size of a single PML4
pub const PML4_SIZE: usize = 0x0000_0080_0000_0000;
/// Offset of recursive paging
pub const RECURSIVE_PAGE_OFFSET: usize = (-(PML4_SIZE as isize)) as usize;
/// Offset of kernel
pub const KERNEL_OFFSET: usize = RECURSIVE_PAGE_OFFSET - PML4_SIZE;
/// Offset to kernel heap
pub const KERNEL_HEAP_OFFSET: usize = KERNEL_OFFSET + PML4_SIZE/2;
/// Size of kernel heap
pub const KERNEL_HEAP_SIZE: usize = 256 * 1024 * 1024; // 256 MB
/// Offset to kernel percpu variables
//TODO: Use 64-bit fs offset to enable this pub const KERNEL_PERCPU_OFFSET: usize = KERNEL_HEAP_OFFSET - PML4_SIZE;
pub const KERNEL_PERCPU_OFFSET: usize = 0xC000_0000;
/// Size of kernel percpu variables
pub const KERNEL_PERCPU_SIZE: usize = 64 * 1024; // 64 KB
/// Offset to user image
pub const USER_OFFSET: usize = 0;
/// Offset to user TCB
pub const USER_TCB_OFFSET: usize = 0xB000_0000;
/// Offset to user arguments
pub const USER_ARG_OFFSET: usize = USER_OFFSET + PML4_SIZE/2;
/// Offset to user heap
pub const USER_HEAP_OFFSET: usize = USER_OFFSET + PML4_SIZE;
/// Offset to user grants
pub const USER_GRANT_OFFSET: usize = USER_HEAP_OFFSET + PML4_SIZE;
/// Offset to user stack
pub const USER_STACK_OFFSET: usize = USER_GRANT_OFFSET + PML4_SIZE;
/// Size of user stack
pub const USER_STACK_SIZE: usize = 1024 * 1024; // 1 MB
/// Offset to user TLS
pub const USER_TLS_OFFSET: usize = USER_STACK_OFFSET + PML4_SIZE;
/// Offset to user temporary image (used when cloning)
pub const USER_TMP_OFFSET: usize = USER_TLS_OFFSET + PML4_SIZE;
/// Offset to user temporary heap (used when cloning)
pub const USER_TMP_HEAP_OFFSET: usize = USER_TMP_OFFSET + PML4_SIZE;
/// Offset to user temporary page for grants
pub const USER_TMP_GRANT_OFFSET: usize = USER_TMP_HEAP_OFFSET + PML4_SIZE;
/// Offset to user temporary stack (used when cloning)
pub const USER_TMP_STACK_OFFSET: usize = USER_TMP_GRANT_OFFSET + PML4_SIZE;
/// Offset to user temporary tls (used when cloning)
pub const USER_TMP_TLS_OFFSET: usize = USER_TMP_STACK_OFFSET + PML4_SIZE;
/// Print to console
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ({
use core::fmt::Write;
let _ = write!($crate::console::CONSOLE.lock(), $($arg)*);
});
}
/// Print with new line to console
#[macro_export]
macro_rules! println {
($fmt:expr) => (print!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
}
/// Create an interrupt function that can safely run rust code
#[macro_export]
macro_rules! interrupt {
($name:ident, $func:block) => {
#[naked]
pub unsafe extern fn $name () {
#[inline(never)]
unsafe fn inner() {
$func
}
// Push scratch registers
asm!("push rax
push rcx
push rdx
push rdi
push rsi
push r8
push r9
push r10
push r11
push fs
mov rax, 0x18
mov fs, ax"
: : : : "intel", "volatile");
// Call inner rust function
inner();
// Pop scratch registers and return
asm!("pop fs
pop r11
pop r10
pop r9
pop r8
pop rsi
pop rdi
pop rdx
pop rcx
pop rax
iretq"
: : : : "intel", "volatile");
}
};
}
#[repr(packed)]
pub struct InterruptStack {
fs: usize,
r11: usize,
r10: usize,
r9: usize,
r8: usize,
rsi: usize,
rdi: usize,
rdx: usize,
rcx: usize,
rax: usize,
rip: usize,
cs: usize,
rflags: usize,
}
#[macro_export]
macro_rules! interrupt_stack {
($name:ident, $stack: ident, $func:block) => {
#[naked]
pub unsafe extern fn $name () {
#[inline(never)]
unsafe fn inner($stack: &$crate::InterruptStack) {
$func
}
// Push scratch registers
asm!("push rax
push rcx
push rdx
push rdi
push rsi
push r8
push r9
push r10
push r11
push fs
mov rax, 0x18
mov fs, ax"
: : : : "intel", "volatile");
// Get reference to stack variables
let rsp: usize;
asm!("" : "={rsp}"(rsp) : : : "intel", "volatile");
// Call inner rust function
inner(&*(rsp as *const $crate::InterruptStack));
// Pop scratch registers and return
asm!("pop fs
pop r11
pop r10
pop r9
pop r8
pop rsi
pop rdi
pop rdx
pop rcx
pop rax
iretq"
: : : : "intel", "volatile");
}
};
}
#[repr(packed)]
pub struct InterruptErrorStack {
fs: usize,
r11: usize,
r10: usize,
r9: usize,
r8: usize,
rsi: usize,
rdi: usize,
rdx: usize,
rcx: usize,
rax: usize,
code: usize,
rip: usize,
cs: usize,
rflags: usize,
}
#[macro_export]
macro_rules! interrupt_error {
($name:ident, $stack:ident, $func:block) => {
#[naked]
pub unsafe extern fn $name () {
#[inline(never)]
unsafe fn inner($stack: &$crate::InterruptErrorStack) {
$func
}
// Push scratch registers
asm!("push rax
push rcx
push rdx
push rdi
push rsi
push r8
push r9
push r10
push r11
push fs
mov rax, 0x18
mov fs, ax"
: : : : "intel", "volatile");
// Get reference to stack variables
let rsp: usize;
asm!("" : "={rsp}"(rsp) : : : "intel", "volatile");
// Call inner rust function
inner(&*(rsp as *const $crate::InterruptErrorStack));
// Pop scratch registers, error code, and return
asm!("pop fs
pop r11
pop r10
pop r9
pop r8
pop rsi
pop rdi
pop rdx
pop rcx
pop rax
add rsp, 8
iretq"
: : : : "intel", "volatile");
}
};
}
/// ACPI table parsing
pub mod acpi;
/// Console handling
pub mod console;
/// Context switching
pub mod context;
/// Devices
pub mod device;
/// Memcpy, memmove, etc.
pub mod externs;
/// Global descriptor table
pub mod gdt;
/// Interrupt descriptor table
pub mod idt;
/// Interrupt instructions
pub mod interrupt;
/// Memory management
pub mod memory;
/// Paging
pub mod paging;
/// Panic
pub mod panic;
/// Initialization and start function
pub mod start;
/// Shutdown function
pub mod stop;
/// Time
pub mod time;

View file

@ -1,63 +0,0 @@
ENTRY(kstart)
OUTPUT_FORMAT(elf64-x86-64)
KERNEL_OFFSET = 0xffffff0000100000;
/* KERNEL_OFFSET = 0x100000; */
SECTIONS {
. = KERNEL_OFFSET;
. += SIZEOF_HEADERS;
. = ALIGN(4096);
.text : AT(ADDR(.text) - KERNEL_OFFSET) {
__text_start = .;
*(.text*)
. = ALIGN(4096);
__text_end = .;
}
.rodata : AT(ADDR(.rodata) - KERNEL_OFFSET) {
__rodata_start = .;
*(.rodata*)
. = ALIGN(4096);
__rodata_end = .;
}
.data : AT(ADDR(.data) - KERNEL_OFFSET) {
__data_start = .;
*(.data*)
. = ALIGN(4096);
__data_end = .;
}
.tdata : AT(ADDR(.tdata) - KERNEL_OFFSET) {
__tdata_start = .;
*(.tdata*)
. = ALIGN(4096);
__tdata_end = .;
__tbss_start = .;
*(.tbss*)
. += 8;
. = ALIGN(4096);
__tbss_end = .;
}
.bss : AT(ADDR(.bss) - KERNEL_OFFSET) {
__bss_start = .;
*(.bss*)
. = ALIGN(4096);
__bss_end = .;
}
__end = .;
/DISCARD/ : {
*(.comment*)
*(.debug*)
*(.eh_frame*)
*(.gcc_except_table*)
*(.note*)
*(.rel.eh_frame*)
}
}

View file

@ -1,127 +0,0 @@
//! # Area frame allocator
//! Some code was borrowed from [Phil Opp's Blog](http://os.phil-opp.com/allocating-frames.html)
use paging::PhysicalAddress;
use super::{Frame, FrameAllocator, MemoryArea, MemoryAreaIter};
pub struct AreaFrameAllocator {
next_free_frame: Frame,
current_area: Option<&'static MemoryArea>,
areas: MemoryAreaIter,
kernel_start: Frame,
kernel_end: Frame
}
impl AreaFrameAllocator {
pub fn new(kernel_start: usize, kernel_end: usize, memory_areas: MemoryAreaIter) -> AreaFrameAllocator {
let mut allocator = AreaFrameAllocator {
next_free_frame: Frame::containing_address(PhysicalAddress::new(0)),
current_area: None,
areas: memory_areas,
kernel_start: Frame::containing_address(PhysicalAddress::new(kernel_start)),
kernel_end: Frame::containing_address(PhysicalAddress::new(kernel_end))
};
allocator.choose_next_area();
allocator
}
fn choose_next_area(&mut self) {
self.current_area = self.areas.clone().filter(|area| {
let address = area.base_addr + area.length - 1;
Frame::containing_address(PhysicalAddress::new(address as usize)) >= self.next_free_frame
}).min_by_key(|area| area.base_addr);
if let Some(area) = self.current_area {
let start_frame = Frame::containing_address(PhysicalAddress::new(area.base_addr as usize));
if self.next_free_frame < start_frame {
self.next_free_frame = start_frame;
}
}
}
}
impl FrameAllocator for AreaFrameAllocator {
fn free_frames(&self) -> usize {
let mut count = 0;
for area in self.areas.clone() {
let start_frame = Frame::containing_address(PhysicalAddress::new(area.base_addr as usize));
let end_frame = Frame::containing_address(PhysicalAddress::new((area.base_addr + area.length - 1) as usize));
for frame in Frame::range_inclusive(start_frame, end_frame) {
if frame >= self.kernel_start && frame <= self.kernel_end {
// Inside of kernel range
} else if frame >= self.next_free_frame {
// Frame is in free range
count += 1;
} else {
// Inside of used range
}
}
}
count
}
fn used_frames(&self) -> usize {
let mut count = 0;
for area in self.areas.clone() {
let start_frame = Frame::containing_address(PhysicalAddress::new(area.base_addr as usize));
let end_frame = Frame::containing_address(PhysicalAddress::new((area.base_addr + area.length - 1) as usize));
for frame in Frame::range_inclusive(start_frame, end_frame) {
if frame >= self.kernel_start && frame <= self.kernel_end {
// Inside of kernel range
count += 1
} else if frame >= self.next_free_frame {
// Frame is in free range
} else {
count += 1;
}
}
}
count
}
fn allocate_frames(&mut self, count: usize) -> Option<Frame> {
if count == 0 {
None
} else if let Some(area) = self.current_area {
// "Clone" the frame to return it if it's free. Frame doesn't
// implement Clone, but we can construct an identical frame.
let start_frame = Frame{ number: self.next_free_frame.number };
let end_frame = Frame { number: self.next_free_frame.number + (count - 1) };
// the last frame of the current area
let current_area_last_frame = {
let address = area.base_addr + area.length - 1;
Frame::containing_address(PhysicalAddress::new(address as usize))
};
if end_frame > current_area_last_frame {
// all frames of current area are used, switch to next area
self.choose_next_area();
} else if (start_frame >= self.kernel_start && start_frame <= self.kernel_end)
|| (end_frame >= self.kernel_start && end_frame <= self.kernel_end) {
// `frame` is used by the kernel
self.next_free_frame = Frame {
number: self.kernel_end.number + 1
};
} else {
// frame is unused, increment `next_free_frame` and return it
self.next_free_frame.number += count;
return Some(start_frame);
}
// `frame` was not valid, try it again with the updated `next_free_frame`
self.allocate_frames(count)
} else {
None // no free frames left
}
}
fn deallocate_frames(&mut self, frame: Frame, count: usize) {
//panic!("AreaFrameAllocator::deallocate_frame: not supported: {:?}", frame);
}
}

View file

@ -1,189 +0,0 @@
//! # Memory management
//! Some code was borrowed from [Phil Opp's Blog](http://os.phil-opp.com/allocating-frames.html)
pub use paging::{PAGE_SIZE, PhysicalAddress};
use self::area_frame_allocator::AreaFrameAllocator;
use spin::Mutex;
pub mod area_frame_allocator;
/// The current memory map. It's size is maxed out to 512 entries, due to it being
/// from 0x500 to 0x5000 (800 is the absolute total)
static mut MEMORY_MAP: [MemoryArea; 512] = [MemoryArea { base_addr: 0, length: 0, _type: 0, acpi: 0 }; 512];
/// Memory does not exist
pub const MEMORY_AREA_NULL: u32 = 0;
/// Memory is free to use
pub const MEMORY_AREA_FREE: u32 = 1;
/// Memory is reserved
pub const MEMORY_AREA_RESERVED: u32 = 2;
/// Memory is used by ACPI, and can be reclaimed
pub const MEMORY_AREA_ACPI: u32 = 3;
/// A memory map area
#[derive(Copy, Clone, Debug, Default)]
#[repr(packed)]
pub struct MemoryArea {
pub base_addr: u64,
pub length: u64,
pub _type: u32,
pub acpi: u32
}
#[derive(Clone)]
pub struct MemoryAreaIter {
_type: u32,
i: usize
}
impl MemoryAreaIter {
fn new(_type: u32) -> Self {
MemoryAreaIter {
_type: _type,
i: 0
}
}
}
impl Iterator for MemoryAreaIter {
type Item = &'static MemoryArea;
fn next(&mut self) -> Option<Self::Item> {
while self.i < unsafe { MEMORY_MAP.len() } {
let entry = unsafe { &MEMORY_MAP[self.i] };
self.i += 1;
if entry._type == self._type {
return Some(entry);
}
}
None
}
}
static ALLOCATOR: Mutex<Option<AreaFrameAllocator>> = Mutex::new(None);
/// Init memory module
/// Must be called once, and only once,
pub unsafe fn init(kernel_start: usize, kernel_end: usize) {
// Copy memory map from bootloader location
for (i, mut entry) in MEMORY_MAP.iter_mut().enumerate() {
*entry = *(0x500 as *const MemoryArea).offset(i as isize);
if entry._type != MEMORY_AREA_NULL {
println!("{:?}", entry);
}
}
*ALLOCATOR.lock() = Some(AreaFrameAllocator::new(kernel_start, kernel_end, MemoryAreaIter::new(MEMORY_AREA_FREE)));
}
/// Allocate a frame
pub fn allocate_frame() -> Option<Frame> {
allocate_frames(1)
}
/// Deallocate a frame
pub fn deallocate_frame(frame: Frame) {
deallocate_frames(frame, 1)
}
/// Get the number of frames available
pub fn free_frames() -> usize {
if let Some(ref allocator) = *ALLOCATOR.lock() {
allocator.free_frames()
} else {
panic!("frame allocator not initialized");
}
}
/// Get the number of frames used
pub fn used_frames() -> usize {
if let Some(ref allocator) = *ALLOCATOR.lock() {
allocator.used_frames()
} else {
panic!("frame allocator not initialized");
}
}
/// Allocate a range of frames
pub fn allocate_frames(count: usize) -> Option<Frame> {
if let Some(ref mut allocator) = *ALLOCATOR.lock() {
allocator.allocate_frames(count)
} else {
panic!("frame allocator not initialized");
}
}
/// Deallocate a range of frames frame
pub fn deallocate_frames(frame: Frame, count: usize) {
if let Some(ref mut allocator) = *ALLOCATOR.lock() {
allocator.deallocate_frames(frame, count)
} else {
panic!("frame allocator not initialized");
}
}
/// A frame, allocated by the frame allocator.
/// Do not add more derives, or make anything `pub`!
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Frame {
number: usize
}
impl Frame {
/// Get the address of this frame
pub fn start_address(&self) -> PhysicalAddress {
PhysicalAddress::new(self.number * PAGE_SIZE)
}
//TODO: Set private
pub fn clone(&self) -> Frame {
Frame {
number: self.number
}
}
/// Create a frame containing `address`
pub fn containing_address(address: PhysicalAddress) -> Frame {
Frame {
number: address.get() / PAGE_SIZE
}
}
//TODO: Set private
pub fn range_inclusive(start: Frame, end: Frame) -> FrameIter {
FrameIter {
start: start,
end: end,
}
}
}
pub struct FrameIter {
start: Frame,
end: Frame,
}
impl Iterator for FrameIter {
type Item = Frame;
fn next(&mut self) -> Option<Frame> {
if self.start <= self.end {
let frame = self.start.clone();
self.start.number += 1;
Some(frame)
} else {
None
}
}
}
pub trait FrameAllocator {
fn free_frames(&self) -> usize;
fn used_frames(&self) -> usize;
fn allocate_frames(&mut self, size: usize) -> Option<Frame>;
fn deallocate_frames(&mut self, frame: Frame, size: usize);
}

View file

@ -1,62 +0,0 @@
//! # Page table entry
//! Some code borrowed from [Phil Opp's Blog](http://os.phil-opp.com/modifying-page-tables.html)
use memory::Frame;
use super::PhysicalAddress;
/// A page table entry
pub struct Entry(u64);
bitflags! {
pub flags EntryFlags: u64 {
const PRESENT = 1 << 0,
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,
}
}
pub const ADDRESS_MASK: usize = 0x000f_ffff_ffff_f000;
impl Entry {
/// Is the entry unused?
pub fn is_unused(&self) -> bool {
self.0 == 0
}
/// Make the entry unused
pub fn set_unused(&mut self) {
self.0 = 0;
}
/// Get the address this page references
pub fn address(&self) -> PhysicalAddress {
PhysicalAddress::new(self.0 as usize & ADDRESS_MASK)
}
/// Get the current entry flags
pub fn flags(&self) -> EntryFlags {
EntryFlags::from_bits_truncate(self.0)
}
/// Get the associated frame, if available
pub fn pointed_frame(&self) -> Option<Frame> {
if self.flags().contains(PRESENT) {
Some(Frame::containing_address(self.address()))
} else {
None
}
}
pub fn set(&mut self, frame: Frame, flags: EntryFlags) {
debug_assert!(frame.start_address().get() & !ADDRESS_MASK == 0);
self.0 = (frame.start_address().get() as u64) | flags.bits();
}
}

View file

@ -1,109 +0,0 @@
use core::ptr::Unique;
use memory::{allocate_frame, deallocate_frame, Frame};
use super::{Page, PAGE_SIZE, PhysicalAddress, VirtualAddress};
use super::entry::{self, EntryFlags};
use super::table::{self, Table, Level4};
pub struct Mapper {
p4: Unique<Table<Level4>>,
}
impl Mapper {
/// Create a new page table
pub unsafe fn new() -> Mapper {
Mapper {
p4: Unique::new(table::P4),
}
}
pub fn p4(&self) -> &Table<Level4> {
unsafe { self.p4.get() }
}
pub fn p4_mut(&mut self) -> &mut Table<Level4> {
unsafe { self.p4.get_mut() }
}
/// Map a page to a frame
pub fn map_to(&mut self, page: Page, frame: Frame, flags: EntryFlags) {
let mut p3 = self.p4_mut().next_table_create(page.p4_index());
let mut p2 = p3.next_table_create(page.p3_index());
let mut p1 = p2.next_table_create(page.p2_index());
assert!(p1[page.p1_index()].is_unused(),
"{:X}: Set to {:X}: {:?}, requesting {:X}: {:?}",
page.start_address().get(),
p1[page.p1_index()].address().get(), p1[page.p1_index()].flags(),
frame.start_address().get(), flags);
p1[page.p1_index()].set(frame, flags | entry::PRESENT);
}
/// Map a page to the next free frame
pub fn map(&mut self, page: Page, flags: EntryFlags) {
let frame = allocate_frame().expect("out of frames");
self.map_to(page, frame, flags)
}
/// Update flags for a page
pub fn remap(&mut self, page: Page, flags: EntryFlags) {
let mut p3 = self.p4_mut().next_table_mut(page.p4_index()).expect("failed to remap: no p3");
let mut p2 = p3.next_table_mut(page.p3_index()).expect("failed to remap: no p2");
let mut p1 = p2.next_table_mut(page.p2_index()).expect("failed to remap: no p1");
let frame = p1[page.p1_index()].pointed_frame().expect("failed to remap: not mapped");
p1[page.p1_index()].set(frame, flags | entry::PRESENT);
}
/// Identity map a frame
pub fn identity_map(&mut self, frame: Frame, flags: EntryFlags) {
let page = Page::containing_address(VirtualAddress::new(frame.start_address().get()));
self.map_to(page, frame, flags)
}
/// Unmap a page
pub fn unmap(&mut self, page: Page) {
let p1 = self.p4_mut()
.next_table_mut(page.p4_index())
.and_then(|p3| p3.next_table_mut(page.p3_index()))
.and_then(|p2| p2.next_table_mut(page.p2_index()))
.expect("mapping code does not support huge pages");
let frame = p1[page.p1_index()].pointed_frame().unwrap();
p1[page.p1_index()].set_unused();
// TODO free p(1,2,3) table if empty
deallocate_frame(frame);
}
/// Unmap a page, return frame without free
pub fn unmap_return(&mut self, page: Page) -> Frame {
let p1 = self.p4_mut()
.next_table_mut(page.p4_index())
.and_then(|p3| p3.next_table_mut(page.p3_index()))
.and_then(|p2| p2.next_table_mut(page.p2_index()))
.expect("mapping code does not support huge pages");
let frame = p1[page.p1_index()].pointed_frame().unwrap();
p1[page.p1_index()].set_unused();
frame
}
pub fn translate_page(&self, page: Page) -> Option<Frame> {
self.p4().next_table(page.p4_index())
.and_then(|p3| p3.next_table(page.p3_index()))
.and_then(|p2| p2.next_table(page.p2_index()))
.and_then(|p1| p1[page.p1_index()].pointed_frame())
}
pub fn translate_page_flags(&self, page: Page) -> Option<EntryFlags> {
self.p4().next_table(page.p4_index())
.and_then(|p3| p3.next_table(page.p3_index()))
.and_then(|p2| p2.next_table(page.p2_index()))
.and_then(|p1| Some(p1[page.p1_index()].flags()))
}
/// Translate a virtual address to a physical one
pub fn translate(&self, virtual_address: VirtualAddress) -> Option<PhysicalAddress> {
let offset = virtual_address.get() % PAGE_SIZE;
self.translate_page(Page::containing_address(virtual_address))
.map(|frame| PhysicalAddress::new(frame.start_address().get() + offset))
}
}

View file

@ -1,419 +0,0 @@
//! # Paging
//! Some code was borrowed from [Phil Opp's Blog](http://os.phil-opp.com/modifying-page-tables.html)
use core::mem;
use core::ops::{Deref, DerefMut};
use x86::{msr, tlb};
use memory::{allocate_frame, Frame};
use self::entry::{EntryFlags, PRESENT, GLOBAL, WRITABLE, NO_EXECUTE};
use self::mapper::Mapper;
use self::temporary_page::TemporaryPage;
pub mod entry;
pub mod mapper;
pub mod table;
pub mod temporary_page;
/// Number of entries per page table
pub const ENTRY_COUNT: usize = 512;
/// Size of pages
pub const PAGE_SIZE: usize = 4096;
/// Setup page attribute table
unsafe fn init_pat() {
let uncacheable = 0;
let write_combining = 1;
let write_through = 4;
//let write_protected = 5;
let write_back = 6;
let uncached = 7;
let pat0 = write_back;
let pat1 = write_through;
let pat2 = uncached;
let pat3 = uncacheable;
let pat4 = write_combining;
let pat5 = pat1;
let pat6 = pat2;
let pat7 = pat3;
msr::wrmsr(msr::IA32_PAT, pat7 << 56 | pat6 << 48 | pat5 << 40 | pat4 << 32
| pat3 << 24 | pat2 << 16 | pat1 << 8 | pat0);
}
/// Copy tdata, clear tbss, set TCB self pointer
unsafe fn init_tcb(cpu_id: usize) -> usize {
extern {
/// The starting byte of the thread data segment
static mut __tdata_start: u8;
/// The ending byte of the thread data segment
static mut __tdata_end: u8;
/// The starting byte of the thread BSS segment
static mut __tbss_start: u8;
/// The ending byte of the thread BSS segment
static mut __tbss_end: u8;
}
let tcb_offset;
{
let size = & __tbss_end as *const _ as usize - & __tdata_start as *const _ as usize;
let tbss_offset = & __tbss_start as *const _ as usize - & __tdata_start as *const _ as usize;
let start = ::KERNEL_PERCPU_OFFSET + ::KERNEL_PERCPU_SIZE * cpu_id;
let end = start + size;
tcb_offset = end - mem::size_of::<usize>();
::externs::memcpy(start as *mut u8, & __tdata_start as *const u8, tbss_offset);
::externs::memset((start + tbss_offset) as *mut u8, 0, size - tbss_offset);
*(tcb_offset as *mut usize) = end;
}
tcb_offset
}
/// Initialize paging
///
/// Returns page table and thread control block offset
pub unsafe fn init(cpu_id: usize, stack_start: usize, stack_end: usize) -> (ActivePageTable, usize) {
extern {
/// The starting byte of the text (code) data segment.
static mut __text_start: u8;
/// The ending byte of the text (code) data segment.
static mut __text_end: u8;
/// The starting byte of the _.rodata_ (read-only data) segment.
static mut __rodata_start: u8;
/// The ending byte of the _.rodata_ (read-only data) segment.
static mut __rodata_end: u8;
/// The starting byte of the _.data_ segment.
static mut __data_start: u8;
/// The ending byte of the _.data_ segment.
static mut __data_end: u8;
/// The starting byte of the thread data segment
static mut __tdata_start: u8;
/// The ending byte of the thread data segment
static mut __tdata_end: u8;
/// The starting byte of the thread BSS segment
static mut __tbss_start: u8;
/// The ending byte of the thread BSS segment
static mut __tbss_end: u8;
/// The starting byte of the _.bss_ (uninitialized data) segment.
static mut __bss_start: u8;
/// The ending byte of the _.bss_ (uninitialized data) segment.
static mut __bss_end: u8;
}
init_pat();
let mut active_table = ActivePageTable::new();
let mut temporary_page = TemporaryPage::new(Page::containing_address(VirtualAddress::new(0x8_0000_0000)));
let mut new_table = {
let frame = allocate_frame().expect("no more frames in paging::init new_table");
InactivePageTable::new(frame, &mut active_table, &mut temporary_page)
};
active_table.with(&mut new_table, &mut temporary_page, |mapper| {
{
// Map tdata and tbss
{
let size = & __tbss_end as *const _ as usize - & __tdata_start as *const _ as usize;
let start = ::KERNEL_PERCPU_OFFSET + ::KERNEL_PERCPU_SIZE * cpu_id;
let end = start + size;
let start_page = Page::containing_address(VirtualAddress::new(start));
let end_page = Page::containing_address(VirtualAddress::new(end - 1));
for page in Page::range_inclusive(start_page, end_page) {
mapper.map(page, PRESENT | GLOBAL | NO_EXECUTE | WRITABLE);
}
}
let mut remap = |start: usize, end: usize, flags: EntryFlags| {
if end > start {
let start_frame = Frame::containing_address(PhysicalAddress::new(start));
let end_frame = Frame::containing_address(PhysicalAddress::new(end - 1));
for frame in Frame::range_inclusive(start_frame, end_frame) {
let page = Page::containing_address(VirtualAddress::new(frame.start_address().get() + ::KERNEL_OFFSET));
mapper.map_to(page, frame, flags);
}
}
};
// Remap stack writable, no execute
remap(stack_start - ::KERNEL_OFFSET, stack_end - ::KERNEL_OFFSET, PRESENT | GLOBAL | NO_EXECUTE | WRITABLE);
// Remap a section with `flags`
let mut remap_section = |start: &u8, end: &u8, flags: EntryFlags| {
remap(start as *const _ as usize - ::KERNEL_OFFSET, end as *const _ as usize - ::KERNEL_OFFSET, flags);
};
// Remap text read-only
remap_section(& __text_start, & __text_end, PRESENT | GLOBAL);
// Remap rodata read-only, no execute
remap_section(& __rodata_start, & __rodata_end, PRESENT | GLOBAL | NO_EXECUTE);
// Remap data writable, no execute
remap_section(& __data_start, & __data_end, PRESENT | GLOBAL | NO_EXECUTE | WRITABLE);
// Remap tdata master writable, no execute
remap_section(& __tdata_start, & __tdata_end, PRESENT | GLOBAL | NO_EXECUTE);
// Remap bss writable, no execute
remap_section(& __bss_start, & __bss_end, PRESENT | GLOBAL | NO_EXECUTE | WRITABLE);
}
});
active_table.switch(new_table);
(active_table, init_tcb(cpu_id))
}
pub unsafe fn init_ap(cpu_id: usize, bsp_table: usize, stack_start: usize, stack_end: usize) -> usize {
extern {
/// The starting byte of the thread data segment
static mut __tdata_start: u8;
/// The ending byte of the thread data segment
static mut __tdata_end: u8;
/// The starting byte of the thread BSS segment
static mut __tbss_start: u8;
/// The ending byte of the thread BSS segment
static mut __tbss_end: u8;
}
init_pat();
let mut active_table = ActivePageTable::new();
let mut new_table = InactivePageTable::from_address(bsp_table);
let mut temporary_page = TemporaryPage::new(Page::containing_address(VirtualAddress::new(0x8_0000_0000)));
active_table.with(&mut new_table, &mut temporary_page, |mapper| {
// Map tdata and tbss
{
let size = & __tbss_end as *const _ as usize - & __tdata_start as *const _ as usize;
let start = ::KERNEL_PERCPU_OFFSET + ::KERNEL_PERCPU_SIZE * cpu_id;
let end = start + size;
let start_page = Page::containing_address(VirtualAddress::new(start));
let end_page = Page::containing_address(VirtualAddress::new(end - 1));
for page in Page::range_inclusive(start_page, end_page) {
mapper.map(page, PRESENT | GLOBAL | NO_EXECUTE | WRITABLE);
}
}
let mut remap = |start: usize, end: usize, flags: EntryFlags| {
if end > start {
let start_frame = Frame::containing_address(PhysicalAddress::new(start));
let end_frame = Frame::containing_address(PhysicalAddress::new(end - 1));
for frame in Frame::range_inclusive(start_frame, end_frame) {
let page = Page::containing_address(VirtualAddress::new(frame.start_address().get() + ::KERNEL_OFFSET));
mapper.map_to(page, frame, flags);
}
}
};
// Remap stack writable, no execute
remap(stack_start - ::KERNEL_OFFSET, stack_end - ::KERNEL_OFFSET, PRESENT | GLOBAL | NO_EXECUTE | WRITABLE);
});
active_table.switch(new_table);
init_tcb(cpu_id)
}
pub struct ActivePageTable {
mapper: Mapper,
}
impl Deref for ActivePageTable {
type Target = Mapper;
fn deref(&self) -> &Mapper {
&self.mapper
}
}
impl DerefMut for ActivePageTable {
fn deref_mut(&mut self) -> &mut Mapper {
&mut self.mapper
}
}
impl ActivePageTable {
pub unsafe fn new() -> ActivePageTable {
ActivePageTable {
mapper: Mapper::new(),
}
}
pub fn switch(&mut self, new_table: InactivePageTable) -> InactivePageTable {
use x86::controlregs;
let old_table = InactivePageTable {
p4_frame: Frame::containing_address(
PhysicalAddress::new(unsafe { controlregs::cr3() } as usize)
),
};
unsafe {
controlregs::cr3_write(new_table.p4_frame.start_address().get() as u64);
}
old_table
}
pub fn flush(&mut self, page: Page) {
unsafe { tlb::flush(page.start_address().get()); }
}
pub fn flush_all(&mut self) {
unsafe { tlb::flush_all(); }
}
pub fn with<F>(&mut self, table: &mut InactivePageTable, temporary_page: &mut temporary_page::TemporaryPage, f: F)
where F: FnOnce(&mut Mapper)
{
use x86::controlregs;
{
let backup = Frame::containing_address(PhysicalAddress::new(unsafe { controlregs::cr3() as usize }));
// map temporary_page to current p4 table
let p4_table = temporary_page.map_table_frame(backup.clone(), PRESENT | WRITABLE | NO_EXECUTE, self);
// overwrite recursive mapping
self.p4_mut()[511].set(table.p4_frame.clone(), PRESENT | WRITABLE | NO_EXECUTE);
self.flush_all();
// execute f in the new context
f(self);
// restore recursive mapping to original p4 table
p4_table[511].set(backup, PRESENT | WRITABLE | NO_EXECUTE);
self.flush_all();
}
temporary_page.unmap(self);
}
pub unsafe fn address(&self) -> usize {
use x86::controlregs;
controlregs::cr3() as usize
}
}
pub struct InactivePageTable {
p4_frame: Frame,
}
impl InactivePageTable {
pub fn new(frame: Frame, active_table: &mut ActivePageTable, temporary_page: &mut TemporaryPage) -> InactivePageTable {
{
let table = temporary_page.map_table_frame(frame.clone(), PRESENT | WRITABLE | NO_EXECUTE, active_table);
// now we are able to zero the table
table.zero();
// set up recursive mapping for the table
table[511].set(frame.clone(), PRESENT | WRITABLE | NO_EXECUTE);
}
temporary_page.unmap(active_table);
InactivePageTable { p4_frame: frame }
}
pub unsafe fn from_address(cr3: usize) -> InactivePageTable {
InactivePageTable { p4_frame: Frame::containing_address(PhysicalAddress::new(cr3)) }
}
pub unsafe fn address(&self) -> usize {
self.p4_frame.start_address().get()
}
}
/// A physical address.
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct PhysicalAddress(usize);
impl PhysicalAddress {
pub fn new(address: usize) -> Self {
PhysicalAddress(address)
}
pub fn get(&self) -> usize {
self.0
}
}
/// A virtual address.
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
pub struct VirtualAddress(usize);
impl VirtualAddress {
pub fn new(address: usize) -> Self {
VirtualAddress(address)
}
pub fn get(&self) -> usize {
self.0
}
}
/// Page
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Page {
number: usize
}
impl Page {
pub fn start_address(&self) -> VirtualAddress {
VirtualAddress::new(self.number * PAGE_SIZE)
}
pub fn p4_index(&self) -> usize {
(self.number >> 27) & 0o777
}
pub fn p3_index(&self) -> usize {
(self.number >> 18) & 0o777
}
pub fn p2_index(&self) -> usize {
(self.number >> 9) & 0o777
}
pub fn p1_index(&self) -> usize {
(self.number >> 0) & 0o777
}
pub fn containing_address(address: VirtualAddress) -> Page {
//TODO assert!(address.get() < 0x0000_8000_0000_0000 || address.get() >= 0xffff_8000_0000_0000,
// "invalid address: 0x{:x}", address.get());
Page { number: address.get() / PAGE_SIZE }
}
pub fn range_inclusive(start: Page, end: Page) -> PageIter {
PageIter {
start: start,
end: end,
}
}
}
pub struct PageIter {
start: Page,
end: Page,
}
impl Iterator for PageIter {
type Item = Page;
fn next(&mut self) -> Option<Page> {
if self.start <= self.end {
let page = self.start;
self.start.number += 1;
Some(page)
} else {
None
}
}
}

View file

@ -1,98 +0,0 @@
//! # Page table
//! Code borrowed from [Phil Opp's Blog](http://os.phil-opp.com/modifying-page-tables.html)
use core::marker::PhantomData;
use core::ops::{Index, IndexMut};
use memory::allocate_frame;
use super::entry::*;
use super::ENTRY_COUNT;
pub const P4: *mut Table<Level4> = 0xffff_ffff_ffff_f000 as *mut _;
pub trait TableLevel {}
pub enum Level4 {}
pub enum Level3 {}
pub enum Level2 {}
pub enum Level1 {}
impl TableLevel for Level4 {}
impl TableLevel for Level3 {}
impl TableLevel for Level2 {}
impl TableLevel for Level1 {}
pub trait HierarchicalLevel: TableLevel {
type NextLevel: TableLevel;
}
impl HierarchicalLevel for Level4 {
type NextLevel = Level3;
}
impl HierarchicalLevel for Level3 {
type NextLevel = Level2;
}
impl HierarchicalLevel for Level2 {
type NextLevel = Level1;
}
pub struct Table<L: TableLevel> {
entries: [Entry; ENTRY_COUNT],
level: PhantomData<L>,
}
impl<L> Table<L> where L: TableLevel {
pub fn zero(&mut self) {
for entry in self.entries.iter_mut() {
entry.set_unused();
}
}
}
impl<L> Table<L> where L: HierarchicalLevel {
pub fn next_table(&self, index: usize) -> Option<&Table<L::NextLevel>> {
self.next_table_address(index).map(|address| unsafe { &*(address as *const _) })
}
pub fn next_table_mut(&mut self, index: usize) -> Option<&mut Table<L::NextLevel>> {
self.next_table_address(index).map(|address| unsafe { &mut *(address as *mut _) })
}
pub fn next_table_create(&mut self, index: usize) -> &mut Table<L::NextLevel> {
if self.next_table(index).is_none() {
assert!(!self[index].flags().contains(HUGE_PAGE),
"mapping code does not support huge pages");
let frame = allocate_frame().expect("no frames available");
self[index].set(frame, PRESENT | WRITABLE | USER_ACCESSIBLE /* Allow users to go down the page table, implement permissions at the page level */);
self.next_table_mut(index).unwrap().zero();
}
self.next_table_mut(index).unwrap()
}
fn next_table_address(&self, index: usize) -> Option<usize> {
let entry_flags = self[index].flags();
if entry_flags.contains(PRESENT) && !entry_flags.contains(HUGE_PAGE) {
let table_address = self as *const _ as usize;
Some((table_address << 9) | (index << 12))
} else {
None
}
}
}
impl<L> Index<usize> for Table<L> where L: TableLevel {
type Output = Entry;
fn index(&self, index: usize) -> &Entry {
&self.entries[index]
}
}
impl<L> IndexMut<usize> for Table<L> where L: TableLevel {
fn index_mut(&mut self, index: usize) -> &mut Entry {
&mut self.entries[index]
}
}

View file

@ -1,43 +0,0 @@
//! Temporarily map a page
//! From [Phil Opp's Blog](http://os.phil-opp.com/remap-the-kernel.html)
use memory::Frame;
use super::{ActivePageTable, Page, VirtualAddress};
use super::entry::EntryFlags;
use super::table::{Table, Level1};
pub struct TemporaryPage {
page: Page,
}
impl TemporaryPage {
pub fn new(page: Page) -> TemporaryPage {
TemporaryPage {
page: page,
}
}
pub fn start_address (&self) -> VirtualAddress {
self.page.start_address()
}
/// Maps the temporary page to the given frame in the active table.
/// Returns the start address of the temporary page.
pub fn map(&mut self, frame: Frame, flags: EntryFlags, active_table: &mut ActivePageTable) -> VirtualAddress {
assert!(active_table.translate_page(self.page).is_none(), "temporary page is already mapped");
active_table.map_to(self.page, frame, flags);
self.page.start_address()
}
/// Maps the temporary page to the given page table frame in the active
/// table. Returns a reference to the now mapped table.
pub fn map_table_frame(&mut self, frame: Frame, flags: EntryFlags, active_table: &mut ActivePageTable) -> &mut Table<Level1> {
unsafe { &mut *(self.map(frame, flags, active_table).get() as *mut Table<Level1>) }
}
/// Unmaps the temporary page in the active table.
pub fn unmap(&mut self, active_table: &mut ActivePageTable) {
active_table.unmap(self.page)
}
}

View file

@ -1,32 +0,0 @@
//! Intrinsics for panic handling
use interrupt;
#[cfg(not(test))]
#[lang = "eh_personality"]
extern "C" fn eh_personality() {}
#[cfg(not(test))]
/// Required to handle panics
#[lang = "panic_fmt"]
extern "C" fn panic_fmt(fmt: ::core::fmt::Arguments, file: &str, line: u32) -> ! {
println!("PANIC: {}", fmt);
println!("FILE: {}", file);
println!("LINE: {}", line);
unsafe { interrupt::stack_trace(); }
println!("HALT");
loop {
unsafe { interrupt::halt(); }
}
}
#[allow(non_snake_case)]
#[no_mangle]
/// Required to handle panics
pub extern "C" fn _Unwind_Resume() -> ! {
loop {
unsafe { interrupt::halt(); }
}
}

View file

@ -1,183 +0,0 @@
/// This function is where the kernel sets up IRQ handlers
/// It is increcibly unsafe, and should be minimal in nature
/// It must create the IDT with the correct entries, those entries are
/// defined in other files inside of the `arch` module
use core::sync::atomic::{AtomicBool, ATOMIC_BOOL_INIT, AtomicUsize, ATOMIC_USIZE_INIT, Ordering};
use acpi;
use allocator;
use device;
use externs::memset;
use gdt;
use idt;
use interrupt;
use memory;
use paging::{self, entry, Page, VirtualAddress};
/// Test of zero values in BSS.
static BSS_TEST_ZERO: usize = 0;
/// Test of non-zero values in data.
static DATA_TEST_NONZERO: usize = 0xFFFFFFFFFFFFFFFF;
/// Test of zero values in thread BSS
#[thread_local]
static mut TBSS_TEST_ZERO: usize = 0;
/// Test of non-zero values in thread data.
#[thread_local]
static mut TDATA_TEST_NONZERO: usize = 0xFFFFFFFFFFFFFFFF;
pub static CPU_COUNT: AtomicUsize = ATOMIC_USIZE_INIT;
pub static AP_READY: AtomicBool = ATOMIC_BOOL_INIT;
static BSP_READY: AtomicBool = ATOMIC_BOOL_INIT;
extern {
/// Kernel main function
fn kmain(cpus: usize) -> !;
/// Kernel main for APs
fn kmain_ap(id: usize) -> !;
}
/// The entry to Rust, all things must be initialized
#[no_mangle]
pub unsafe extern fn kstart() -> ! {
{
extern {
/// The starting byte of the _.bss_ (uninitialized data) segment.
static mut __bss_start: u8;
/// The ending byte of the _.bss_ (uninitialized data) segment.
static mut __bss_end: u8;
/// The end of the kernel
static mut __end: u8;
}
// Zero BSS, this initializes statics that are set to 0
{
let start_ptr = &mut __bss_start as *mut u8;
let end_ptr = & __bss_end as *const u8 as usize;
if start_ptr as usize <= end_ptr {
let size = end_ptr - start_ptr as usize;
memset(start_ptr, 0, size);
}
assert_eq!(BSS_TEST_ZERO, 0);
assert_eq!(DATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFF);
}
// Initialize memory management
memory::init(0, &__end as *const u8 as usize - ::KERNEL_OFFSET);
// TODO: allocate a stack
let stack_start = 0x00080000 + ::KERNEL_OFFSET;
let stack_end = 0x0009F000 + ::KERNEL_OFFSET;
// Initialize paging
let (mut active_table, tcb_offset) = paging::init(0, stack_start, stack_end);
// Set up GDT
gdt::init(tcb_offset, stack_end);
// Set up IDT
idt::init();
// Test tdata and tbss
{
assert_eq!(TBSS_TEST_ZERO, 0);
TBSS_TEST_ZERO += 1;
assert_eq!(TBSS_TEST_ZERO, 1);
assert_eq!(TDATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFF);
TDATA_TEST_NONZERO -= 1;
assert_eq!(TDATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFE);
}
// Reset AP variables
CPU_COUNT.store(1, Ordering::SeqCst);
AP_READY.store(false, Ordering::SeqCst);
BSP_READY.store(false, Ordering::SeqCst);
// Setup kernel heap
{
// Map heap pages
let heap_start_page = Page::containing_address(VirtualAddress::new(::KERNEL_HEAP_OFFSET));
let heap_end_page = Page::containing_address(VirtualAddress::new(::KERNEL_HEAP_OFFSET + ::KERNEL_HEAP_SIZE-1));
for page in Page::range_inclusive(heap_start_page, heap_end_page) {
active_table.map(page, entry::PRESENT | entry::GLOBAL | entry::WRITABLE | entry::NO_EXECUTE);
}
// Init the allocator
allocator::init(::KERNEL_HEAP_OFFSET, ::KERNEL_HEAP_SIZE);
}
// Initialize devices
device::init(&mut active_table);
// Read ACPI tables, starts APs
acpi::init(&mut active_table);
BSP_READY.store(true, Ordering::SeqCst);
}
kmain(CPU_COUNT.load(Ordering::SeqCst));
}
/// Entry to rust for an AP
pub unsafe extern fn kstart_ap(cpu_id: usize, bsp_table: usize, stack_start: usize, stack_end: usize) -> ! {
{
assert_eq!(BSS_TEST_ZERO, 0);
assert_eq!(DATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFF);
// Initialize paging
let tcb_offset = paging::init_ap(cpu_id, bsp_table, stack_start, stack_end);
// Set up GDT for AP
gdt::init(tcb_offset, stack_end);
// Set up IDT for AP
idt::init();
// Test tdata and tbss
{
assert_eq!(TBSS_TEST_ZERO, 0);
TBSS_TEST_ZERO += 1;
assert_eq!(TBSS_TEST_ZERO, 1);
assert_eq!(TDATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFF);
TDATA_TEST_NONZERO -= 1;
assert_eq!(TDATA_TEST_NONZERO, 0xFFFFFFFFFFFFFFFE);
}
// Initialize devices (for AP)
device::init_ap();
AP_READY.store(true, Ordering::SeqCst);
}
while ! BSP_READY.load(Ordering::SeqCst) {
interrupt::pause();
}
kmain_ap(cpu_id);
}
pub unsafe fn usermode(ip: usize, sp: usize) -> ! {
// Go to usermode
asm!("mov ds, ax
mov es, ax
mov fs, bx
mov gs, ax
push rax
push rcx
push rdx
push rsi
push rdi
iretq"
: // No output because it never returns
: "{rax}"(gdt::GDT_USER_DATA << 3 | 3), // Data segment
"{rbx}"(gdt::GDT_USER_TLS << 3 | 3), // TLS segment
"{rcx}"(sp), // Stack pointer
"{rdx}"(3 << 12 | 1 << 9), // Flags - Set IOPL and interrupt enable flag
"{rsi}"(gdt::GDT_USER_CODE << 3 | 3), // Code segment
"{rdi}"(ip) // IP
: // No clobers because it never returns
: "intel", "volatile");
unreachable!();
}

View file

@ -1,23 +0,0 @@
use io::{Io, Pio};
#[no_mangle]
pub unsafe extern fn kstop() -> ! {
// (phony) ACPI shutdown (http://forum.osdev.org/viewtopic.php?t=16990)
// Works for qemu and bochs.
for &port in [0x604, 0xB004].iter() {
println!("Shutdown with outw(0x{:X}, 0x{:X})", port, 0x2000);
Pio::<u16>::new(port).write(0x2000);
}
// Magic shutdown code for bochs and qemu (older versions).
for c in "Shutdown".bytes() {
println!("Shutdown with outb(0x{:X}, '{}')", 0x8900, c as char);
Pio::<u8>::new(0x8900).write(c);
}
// Magic code for VMWare. Also a hard lock.
println!("Shutdown with cli hlt");
asm!("cli; hlt" : : : : "intel", "volatile");
unreachable!();
}

View file

@ -1,15 +0,0 @@
use spin::Mutex;
pub static START: Mutex<(u64, u64)> = Mutex::new((0, 0));
pub static OFFSET: Mutex<(u64, u64)> = Mutex::new((0, 0));
pub fn monotonic() -> (u64, u64) {
*OFFSET.lock()
}
pub fn realtime() -> (u64, u64) {
let offset = monotonic();
let start = *START.lock();
let sum = start.1 + offset.1;
(start.0 + offset.0 + sum / 1000000000, sum % 1000000000)
}

View file

@ -1,26 +0,0 @@
{
"llvm-target": "arm-unknown-redox",
"target-endian": "little",
"target-pointer-width": "32",
"data-layout": "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64",
"arch": "arm",
"os": "redox",
"env": "",
"vendor": "unknown",
"target-family": "redox",
"pre-link-args": ["-nostdlib", "-static"],
"features": "+soft-float",
"dynamic-linking": false,
"executables": true,
"relocation-model": "static",
"code-model": "default",
"disable-redzone": true,
"eliminate-frame-pointer": false,
"exe-suffix": "",
"has-rpath": false,
"no-compiler-rt": true,
"no-default-libraries": true,
"position-independent-executables": false,
"has-elf-tls": true,
"panic-strategy": "abort"
}

1
bootloader Submodule

@ -0,0 +1 @@
Subproject commit 59fd6fbcfe9adc7e5d0c0df58fe4e2e07df5f31b

View file

@ -1,17 +0,0 @@
interrupt_vector_table:
b . @ Reset
b .
b . @ SWI instruction
b .
b .
b .
b .
b .
.comm stack, 0x10000 @ Reserve 64k stack in the BSS
_start:
.globl _start
ldr sp, =stack+0x10000 @ Set up the stack
bl kstart @ Jump to the main function
1:
b 1b @ Halt

View file

@ -1,188 +0,0 @@
ORG 0x7C00
SECTION .text
USE16
boot: ; dl comes with disk
; initialize segment registers
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
; initialize stack
mov sp, 0x7C00
; initialize CS
push ax
push word .set_cs
retf
.set_cs:
; save disk number
mov [disk], dl
mov si, name
call print
call print_line
mov bx, (startup_start - boot) / 512
call print_num
call print_line
mov bx, startup_start
call print_num
call print_line
mov eax, (startup_start - boot) / 512
mov bx, startup_start
mov cx, (startup_end - startup_start) / 512
xor dx, dx
call load
mov si, finished
call print
call print_line
jmp startup
; load some sectors from disk to a buffer in memory
; buffer has to be below 1MiB
; IN
; ax: start sector
; bx: offset of buffer
; cx: number of sectors (512 Bytes each)
; dx: segment of buffer
; CLOBBER
; ax, bx, cx, dx, si
; TODO rewrite to (eventually) move larger parts at once
; if that is done increase buffer_size_sectors in startup-common to that (max 0x80000 - startup_end)
load:
cmp cx, 127
jbe .good_size
pusha
mov cx, 127
call load
popa
add ax, 127
add dx, 127 * 512 / 16
sub cx, 127
jmp load
.good_size:
mov [DAPACK.addr], eax
mov [DAPACK.buf], bx
mov [DAPACK.count], cx
mov [DAPACK.seg], dx
call print_dapack
mov dl, [disk]
mov si, DAPACK
mov ah, 0x42
int 0x13
jc error
ret
; store some sectors to disk from a buffer in memory
; buffer has to be below 1MiB
; IN
; ax: start sector
; bx: offset of buffer
; cx: number of sectors (512 Bytes each)
; dx: segment of buffer
; CLOBBER
; ax, bx, cx, dx, si
; TODO rewrite to (eventually) move larger parts at once
; if that is done increase buffer_size_sectors in startup-common to that (max 0x80000 - startup_end)
store:
cmp cx, 127
jbe .good_size
pusha
mov cx, 127
call store
popa
add ax, 127
add dx, 127 * 512 / 16
sub cx, 127
jmp store
.good_size:
mov [DAPACK.addr], eax
mov [DAPACK.buf], bx
mov [DAPACK.count], cx
mov [DAPACK.seg], dx
call print_dapack
mov dl, [disk]
mov si, DAPACK
mov ah, 0x43
int 0x13
jc error
ret
print_dapack:
mov bx, [DAPACK.addr + 2]
call print_num
mov bx, [DAPACK.addr]
call print_num
mov al, '#'
call print_char
mov bx, [DAPACK.count]
call print_num
mov al, ' '
call print_char
mov bx, [DAPACK.seg]
call print_num
mov al, ':'
call print_char
mov bx, [DAPACK.buf]
call print_num
jmp print_line
error:
mov bh, 0
mov bl, ah
call print_num
mov al, ' '
call print_char
mov si, errored
call print
call print_line
.halt:
cli
hlt
jmp .halt
%include "print16.asm"
name: db "Redox Loader - Stage One",0
errored: db "Could not read disk",0
finished: db "Redox Loader - Stage Two",0
disk: db 0
DAPACK:
db 0x10
db 0
.count: dw 0 ; int 13 resets this to # of blocks actually read/written
.buf: dw 0 ; memory buffer destination address (0:7c00)
.seg: dw 0 ; in memory page zero
.addr: dq 0 ; put the lba to read in this spot
times 510-($-$$) db 0
db 0x55
db 0xaa

View file

@ -1,18 +0,0 @@
SECTION .text
USE16
align 512, db 0
config:
.xres: dw 0
.yres: dw 0
times 512 - ($ - config) db 0
save_config:
mov eax, (config - boot) / 512
mov bx, config
mov cx, 1
xor dx, dx
call store
ret

View file

@ -1,46 +0,0 @@
attrib:
.present equ 1 << 7
.ring1 equ 1 << 5
.ring2 equ 1 << 6
.ring3 equ 1 << 5 | 1 << 6
.user equ 1 << 4
;user
.code equ 1 << 3
; code
.conforming equ 1 << 2
.readable equ 1 << 1
; data
.expand_down equ 1 << 2
.writable equ 1 << 1
.accessed equ 1 << 0
;system
; legacy
.tssAvailabe16 equ 0x1
.ldt equ 0x2
.tssBusy16 equ 0x3
.call16 equ 0x4
.task equ 0x5
.interrupt16 equ 0x6
.trap16 equ 0x7
.tssAvailabe32 equ 0x9
.tssBusy32 equ 0xB
.call32 equ 0xC
.interrupt32 equ 0xE
.trap32 equ 0xF
; long mode
.ldt32 equ 0x2
.tssAvailabe64 equ 0x9
.tssBusy64 equ 0xB
.call64 equ 0xC
.interrupt64 equ 0xE
.trap64 equ 0xF
flags:
.granularity equ 1 << 7
.available equ 1 << 4
;user
.default_operand_size equ 1 << 6
; code
.long_mode equ 1 << 5
; data
.reserved equ 1 << 5

View file

@ -1,8 +0,0 @@
struc GDTEntry
.limitl resw 1
.basel resw 1
.basem resb 1
.attribute resb 1
.flags__limith resb 1
.baseh resb 1
endstruc

View file

@ -1,21 +0,0 @@
%include "bootsector.asm"
startup_start:
%ifdef ARCH_i386
%include "startup-i386.asm"
%endif
%ifdef ARCH_x86_64
%include "startup-x86_64.asm"
%endif
align 512, db 0
startup_end:
kernel_file:
incbin "build/kernel/kernel"
align 512, db 0
.end:
.length equ kernel_file.end - kernel_file
.length_sectors equ .length / 512
incbin "build/filesystem.bin"

View file

@ -1,78 +0,0 @@
SECTION .text
USE16
initialize:
.fpu: ;enable fpu
mov eax, cr0
and al, 11110011b
or al, 00100010b
mov cr0, eax
mov eax, cr4
or eax, 0x200
mov cr4, eax
fninit
ret
.sse: ;enable sse
mov eax, cr4
or ax, 0000011000000000b
mov cr4, eax
ret
;PIT Frequency
;If using nanoseconds, to minimize drift, one should find a frequency as close to an integer nanosecond value in wavelength
;Divider Hz Nanoseconds Properties
;2685 444.38795779019242706393 2250286.00003631746492922946 Best For Context Switching
;5370 222.19397889509621353196 4500572.00007263492985856020
;21029 56.73981961418358774390 17624306.99991199998882825455
;23714 50.31549576902532962244 19874592.99994831745375667118
;26399 45.19798729749864262535 22124878.99998463491868476373
;29084 41.02536331545408701233 24375165.00002095238361424615
;31769 37.55804925136663623868 26625451.00005726984854313455
;34454 34.63115071302799868423 28875737.00009358731347639618
;50113 23.80982313305263437963 41999471.99993295237244784676
;52798 22.59899364874932131267 44249757.99996926983737931766
;55483 21.50535599492937776736 46500044.00000558730230583335 Lowest Drift
;58168 20.51268165772704350616 48750330.00004190476724037528
;60853 19.60760630809765610021 51000616.00007822223218031738
.pit:
;initialize the PIT
mov ax, 2685 ;this is the divider for the PIT
out 0x40, al
rol ax, 8
out 0x40, al
;DISABLED ;enable rtc interrupt
;mov al, 0xB
;out 0x70, al
;rol ax, 8
;in al, 0x71
;rol ax, 8
;out 0x70, al
;rol ax, 8
;or al, 0x40
;out 0x71, al
ret
.pic: ;sets up IRQs at int 20-2F
mov al, 0x11
out 0x20, al
out 0xA0, al
mov al, 0x20 ;IRQ0 vector
out 0x21, al
mov al, 0x28 ;IRQ8 vector
out 0xA1, al
mov al, 4
out 0x21, al
mov al, 2
out 0xA1, al
mov al, 1
out 0x21, al
out 0xA1, al
xor al, al ;no IRQ masks
out 0x21, al
out 0xA1, al
mov al, 0x20 ;reset PIC's
out 0xA0, al
out 0x20, al
ret

View file

@ -1,19 +0,0 @@
%include "bootsector.asm"
startup_start:
%ifdef ARCH_i386
%include "startup-i386.asm"
%endif
%ifdef ARCH_x86_64
%include "startup-x86_64.asm"
%endif
align 512, db 0
startup_end:
kernel_file:
incbin "build/kernel/kernel_live"
align 512, db 0
.end:
.length equ kernel_file.end - kernel_file
.length_sectors equ .length / 512

View file

@ -1,32 +0,0 @@
SECTION .text
USE16
;Generate a memory map at 0x500 to 0x5000 (available memory not used for kernel or bootloader)
memory_map:
.start equ 0x0500
.end equ 0x5000
.length equ .end - .start
xor eax, eax
mov di, .start
mov ecx, .length / 4 ; moving 4 Bytes at once
cld
rep stosd
mov di, .start
mov edx, 0x534D4150
xor ebx, ebx
.lp:
mov eax, 0xE820
mov ecx, 24
int 0x15
jc .done ; Error or finished
cmp ebx, 0
je .done ; Finished
add di, 24
cmp di, .end
jb .lp ; Still have buffer space
.done:
ret

View file

@ -1,65 +0,0 @@
SECTION .text
USE16
; provide function for printing in x86 real mode
; print a string and a newline
; IN
; si: points at zero-terminated String
; CLOBBER
; ax
print_line:
mov al, 13
call print_char
mov al, 10
jmp print_char
; print a string
; IN
; si: points at zero-terminated String
; CLOBBER
; ax
print:
cld
.loop:
lodsb
test al, al
jz .done
call print_char
jmp .loop
.done:
ret
; print a character
; IN
; al: character to print
; CLOBBER
; ah
print_char:
mov ah, 0x0e
int 0x10
ret
; print a number in hex
; IN
; bx: the number
; CLOBBER
; cx, ax
print_num:
mov cx, 4
.lp:
mov al, bh
shr al, 4
cmp al, 0xA
jb .below_0xA
add al, 'A' - 0xA - '0'
.below_0xA:
add al, '0'
call print_char
shl bx, 4
loop .lp
ret

View file

@ -1,110 +0,0 @@
SECTION .text
USE16
startup:
; enable A20-Line via IO-Port 92, might not work on all motherboards
in al, 0x92
or al, 2
out 0x92, al
; loading kernel to 1MiB
; move part of kernel to startup_end via bootsector#load and then copy it up
; repeat until all of the kernel is loaded
; buffersize in multiple of sectors (512 Bytes)
; min 1
; max (0x70000 - startup_end) / 512
buffer_size_sectors equ 127
; buffer size in Bytes
buffer_size_bytes equ buffer_size_sectors * 512
kernel_base equ 0x100000
; how often do we need to call load and move memory
mov ecx, kernel_file.length_sectors / buffer_size_sectors
mov eax, (kernel_file - boot) / 512
mov edi, kernel_base
cld
.lp:
; saving counter
push cx
; populating buffer
mov cx, buffer_size_sectors
mov bx, kernel_file
mov dx, 0x0
push edi
push eax
call load
; moving buffer
call unreal
pop eax
pop edi
mov esi, kernel_file
mov ecx, buffer_size_bytes / 4
a32 rep movsd
; preparing next iteration
add eax, buffer_size_sectors
pop cx
loop .lp
; load the part of the kernel that does not fill the buffer completely
mov cx, kernel_file.length_sectors % buffer_size_sectors
test cx, cx
jz finished_loading ; if cx = 0 => skip
mov bx, kernel_file
mov dx, 0x0
call load
; moving remnants of kernel
call unreal
mov esi, kernel_file
mov ecx, (kernel_file.length_sectors % buffer_size_bytes) / 4
a32 rep movsd
finished_loading:
call memory_map
call vesa
mov si, init_fpu_msg
call printrm
call initialize.fpu
mov si, init_sse_msg
call printrm
call initialize.sse
mov si, init_pit_msg
call printrm
call initialize.pit
mov si, init_pic_msg
call printrm
call initialize.pic
mov si, startup_arch_msg
call printrm
jmp startup_arch
%include "config.asm"
%include "descriptor_flags.inc"
%include "gdt_entry.inc"
%include "unreal.asm"
%include "memory_map.asm"
%include "vesa.asm"
%include "initialize.asm"
init_fpu_msg: db "Init FPU",13,10,0
init_sse_msg: db "Init SSE",13,10,0
init_pit_msg: db "Init PIT",13,10,0
init_pic_msg: db "Init PIC",13,10,0
startup_arch_msg: db "Startup Arch",13,10,0

View file

@ -1,148 +0,0 @@
%include "startup-common.asm"
startup_arch:
; load protected mode GDT and IDT
cli
lgdt [gdtr]
lidt [idtr]
; set protected mode bit of cr0
mov eax, cr0
or eax, 1
mov cr0, eax
; far jump to load CS with 32 bit segment
jmp gdt.kernel_code:protected_mode
USE32
protected_mode:
; load all the other segments with 32 bit data segments
mov eax, gdt.kernel_data
mov ds, eax
mov es, eax
mov fs, eax
mov gs, eax
mov ss, eax
mov esp, 0x800000 - 128
mov eax, gdt.tss
ltr ax
;rust init
mov eax, [kernel_base + 0x18]
mov [interrupts.handler], eax
mov eax, gdtr
mov ebx, idtr
mov ecx, tss
int 255
.lp:
sti
hlt
jmp .lp
gdtr:
dw gdt.end + 1 ; size
dd gdt ; offset
gdt:
.null equ $ - gdt
dq 0
.kernel_code equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db attrib.present | attrib.user | attrib.code | attrib.readable
at GDTEntry.flags__limith, db 0xFF | flags.granularity | flags.default_operand_size
at GDTEntry.baseh, db 0
iend
.kernel_data equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db attrib.present | attrib.user | attrib.writable
at GDTEntry.flags__limith, db 0xFF | flags.granularity | flags.default_operand_size
at GDTEntry.baseh, db 0
iend
.user_code equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.user | attrib.code | attrib.readable
at GDTEntry.flags__limith, db 0xFF | flags.granularity | flags.default_operand_size
at GDTEntry.baseh, db 0
iend
.user_data equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.user | attrib.writable
at GDTEntry.flags__limith, db 0xFF | flags.granularity | flags.default_operand_size
at GDTEntry.baseh, db 0
iend
.user_tls equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.user | attrib.writable
at GDTEntry.flags__limith, db 0xFF | flags.granularity | flags.default_operand_size
at GDTEntry.baseh, db 0
iend
.tss equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw (tss.end - tss) & 0xFFFF
at GDTEntry.basel, dw (tss-$$+0x7C00) & 0xFFFF
at GDTEntry.basem, db ((tss-$$+0x7C00) >> 16) & 0xFF
at GDTEntry.attribute, db attrib.present | attrib.ring3 | attrib.tssAvailabe32
at GDTEntry.flags__limith, db ((tss.end - tss) >> 16) & 0xF
at GDTEntry.baseh, db ((tss-$$+0x7C00) >> 24) & 0xFF
iend
.end equ $ - gdt
struc TSS
.prev_tss resd 1 ;The previous TSS - if we used hardware task switching this would form a linked list.
.esp0 resd 1 ;The stack pointer to load when we change to kernel mode.
.ss0 resd 1 ;The stack segment to load when we change to kernel mode.
.esp1 resd 1 ;everything below here is unused now..
.ss1 resd 1
.esp2 resd 1
.ss2 resd 1
.cr3 resd 1
.eip resd 1
.eflags resd 1
.eax resd 1
.ecx resd 1
.edx resd 1
.ebx resd 1
.esp resd 1
.ebp resd 1
.esi resd 1
.edi resd 1
.es resd 1
.cs resd 1
.ss resd 1
.ds resd 1
.fs resd 1
.gs resd 1
.ldt resd 1
.trap resw 1
.iomap_base resw 1
endstruc
tss:
istruc TSS
at TSS.esp0, dd 0x800000 - 128
at TSS.ss0, dd gdt.kernel_data
at TSS.iomap_base, dw 0xFFFF
iend
.end:

View file

@ -1,179 +0,0 @@
trampoline:
.ready: dq 0
.cpu_id: dq 0
.page_table: dq 0
.stack_start: dq 0
.stack_end: dq 0
.code: dq 0
times 512 - ($ - trampoline) db 0
startup_ap:
cli
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
; initialize stack
mov sp, 0x7C00
call initialize.fpu
call initialize.sse
;cr3 holds pointer to PML4
mov edi, 0x70000
mov cr3, edi
;enable FXSAVE/FXRSTOR, Page Global, Page Address Extension, and Page Size Extension
mov eax, cr4
or eax, 1 << 9 | 1 << 7 | 1 << 5 | 1 << 4
mov cr4, eax
; load protected mode GDT
lgdt [gdtr]
mov ecx, 0xC0000080 ; Read from the EFER MSR.
rdmsr
or eax, 1 << 11 | 1 << 8 ; Set the Long-Mode-Enable and NXE bit.
wrmsr
;enabling paging and protection simultaneously
mov ebx, cr0
or ebx, 1 << 31 | 1 << 16 | 1 ;Bit 31: Paging, Bit 16: write protect kernel, Bit 0: Protected Mode
mov cr0, ebx
; far jump to enable Long Mode and load CS with 64 bit segment
jmp gdt.kernel_code:long_mode_ap
%include "startup-common.asm"
startup_arch:
cli
; setting up Page Tables
; Identity Mapping first GB
mov ax, 0x7000
mov es, ax
xor edi, edi
xor eax, eax
mov ecx, 6 * 4096 / 4 ;PML4, PDP, 4 PD / moves 4 Bytes at once
cld
rep stosd
xor edi, edi
;Link first PML4 and second to last PML4 to PDP
mov DWORD [es:edi], 0x71000 | 1 << 1 | 1
mov DWORD [es:edi + 510*8], 0x71000 | 1 << 1 | 1
add edi, 0x1000
;Link last PML4 to PML4
mov DWORD [es:edi - 8], 0x70000 | 1 << 1 | 1
;Link first four PDP to PD
mov DWORD [es:edi], 0x72000 | 1 << 1 | 1
mov DWORD [es:edi + 8], 0x73000 | 1 << 1 | 1
mov DWORD [es:edi + 16], 0x74000 | 1 << 1 | 1
mov DWORD [es:edi + 24], 0x75000 | 1 << 1 | 1
add edi, 0x1000
;Link all PD's (512 per PDP, 2MB each)y
mov ebx, 1 << 7 | 1 << 1 | 1
mov ecx, 4*512
.setpd:
mov [es:edi], ebx
add ebx, 0x200000
add edi, 8
loop .setpd
xor ax, ax
mov es, ax
;cr3 holds pointer to PML4
mov edi, 0x70000
mov cr3, edi
;enable FXSAVE/FXRSTOR, Page Global, Page Address Extension, and Page Size Extension
mov eax, cr4
or eax, 1 << 9 | 1 << 7 | 1 << 5 | 1 << 4
mov cr4, eax
; load protected mode GDT
lgdt [gdtr]
mov ecx, 0xC0000080 ; Read from the EFER MSR.
rdmsr
or eax, 1 << 11 | 1 << 8 ; Set the Long-Mode-Enable and NXE bit.
wrmsr
;enabling paging and protection simultaneously
mov ebx, cr0
or ebx, 1 << 31 | 1 << 16 | 1 ;Bit 31: Paging, Bit 16: write protect kernel, Bit 0: Protected Mode
mov cr0, ebx
; far jump to enable Long Mode and load CS with 64 bit segment
jmp gdt.kernel_code:long_mode
USE64
long_mode:
; load all the other segments with 64 bit data segments
mov rax, gdt.kernel_data
mov ds, rax
mov es, rax
mov fs, rax
mov gs, rax
mov ss, rax
mov rsp, 0xFFFFFF000009F000
;rust init
mov rax, [kernel_base + 0x18]
jmp rax
long_mode_ap:
mov rax, gdt.kernel_data
mov ds, rax
mov es, rax
mov fs, rax
mov gs, rax
mov ss, rax
mov rdi, [trampoline.cpu_id]
mov rsi, [trampoline.page_table]
mov rdx, [trampoline.stack_start]
mov rcx, [trampoline.stack_end]
lea rsp, [rcx - 256]
mov rax, [trampoline.code]
mov qword [trampoline.ready], 1
jmp rax
gdtr:
dw gdt.end + 1 ; size
dq gdt ; offset
gdt:
.null equ $ - gdt
dq 0
.kernel_code equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
at GDTEntry.attribute, db attrib.present | attrib.user | attrib.code
at GDTEntry.flags__limith, db flags.long_mode
at GDTEntry.baseh, db 0
iend
.kernel_data equ $ - gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0
at GDTEntry.basel, dw 0
at GDTEntry.basem, db 0
; AMD System Programming Manual states that the writeable bit is ignored in long mode, but ss can not be set to this descriptor without it
at GDTEntry.attribute, db attrib.present | attrib.user | attrib.writable
at GDTEntry.flags__limith, db 0
at GDTEntry.baseh, db 0
iend
.end equ $ - gdt

View file

@ -1,54 +0,0 @@
SECTION .text
USE16
; switch to unreal mode; ds and es can address up to 4GiB
unreal:
cli
lgdt [unreal_gdtr]
push es
push ds
mov eax, cr0 ; switch to pmode by
or al,1 ; set pmode bit
mov cr0, eax
jmp $+2
; http://wiki.osdev.org/Babystep7
; When this register given a "selector", a "segment descriptor cache register"
; is filled with the descriptor values, including the size (or limit). After
; the switch back to real mode, these values are not modified, regardless of
; what value is in the 16-bit segment register. So the 64k limit is no longer
; valid and 32-bit offsets can be used with the real-mode addressing rules
mov bx, unreal_gdt.data
mov es, bx
mov ds, bx
and al,0xFE ; back to realmode
mov cr0, eax ; by toggling bit again
pop ds
pop es
sti
ret
unreal_gdtr:
dw unreal_gdt.end + 1 ; size
dd unreal_gdt ; offset
unreal_gdt:
.null equ $ - unreal_gdt
dq 0
.data equ $ - unreal_gdt
istruc GDTEntry
at GDTEntry.limitl, dw 0xFFFF
at GDTEntry.basel, dw 0x0
at GDTEntry.basem, db 0x0
at GDTEntry.attribute, db attrib.present | attrib.user | attrib.writable
at GDTEntry.flags__limith, db 0xFF | flags.granularity | flags.default_operand_size
at GDTEntry.baseh, db 0x0
iend
.end equ $ - unreal_gdt

View file

@ -1,207 +0,0 @@
%include "vesa.inc"
SECTION .text
USE16
vesa:
.getcardinfo:
mov ax, 0x4F00
mov di, VBECardInfo
int 0x10
cmp ax, 0x4F
je .findmode
mov eax, 1
ret
.resetlist:
;if needed, reset mins/maxes/stuff
xor cx, cx
mov [.minx], cx
mov [.miny], cx
mov [config.xres], cx
mov [config.yres], cx
.findmode:
mov si, [VBECardInfo.videomodeptr]
mov ax, [VBECardInfo.videomodeptr+2]
mov fs, ax
sub si, 2
.searchmodes:
add si, 2
mov cx, [fs:si]
cmp cx, 0xFFFF
jne .getmodeinfo
cmp word [.goodmode], 0
je .resetlist
jmp .findmode
.getmodeinfo:
push esi
mov [.currentmode], cx
mov ax, 0x4F01
mov di, VBEModeInfo
int 0x10
pop esi
cmp ax, 0x4F
je .foundmode
mov eax, 1
ret
.foundmode:
;check minimum values, really not minimums from an OS perspective but ugly for users
cmp byte [VBEModeInfo.bitsperpixel], 32
jb .searchmodes
.testx:
mov cx, [VBEModeInfo.xresolution]
cmp word [config.xres], 0
je .notrequiredx
cmp cx, [config.xres]
je .testy
jmp .searchmodes
.notrequiredx:
cmp cx, [.minx]
jb .searchmodes
.testy:
mov cx, [VBEModeInfo.yresolution]
cmp word [config.yres], 0
je .notrequiredy
cmp cx, [config.yres]
jne .searchmodes ;as if there weren't enough warnings, USE WITH CAUTION
cmp word [config.xres], 0
jnz .setmode
jmp .testgood
.notrequiredy:
cmp cx, [.miny]
jb .searchmodes
.testgood:
mov cx, [.currentmode]
mov [.goodmode], cx
push esi
; call decshowrm
; mov al, ':'
; call charrm
mov cx, [VBEModeInfo.xresolution]
call decshowrm
mov al, 'x'
call charrm
mov cx, [VBEModeInfo.yresolution]
call decshowrm
mov al, '@'
call charrm
xor ch, ch
mov cl, [VBEModeInfo.bitsperpixel]
call decshowrm
mov si, .modeok
call printrm
xor ax, ax
int 0x16
pop esi
cmp al, 'y'
je .setmode
cmp al, 's'
je .savemode
jmp .searchmodes
.savemode:
mov cx, [VBEModeInfo.xresolution]
mov [config.xres], cx
mov cx, [VBEModeInfo.yresolution]
mov [config.yres], cx
call save_config
.setmode:
mov bx, [.currentmode]
cmp bx, 0
je .nomode
or bx, 0x4000
mov ax, 0x4F02
int 0x10
.nomode:
cmp ax, 0x4F
je .returngood
mov eax, 1
ret
.returngood:
xor eax, eax
ret
.minx dw 640
.miny dw 480
.modeok db ": Is this OK? (s)ave/(y)es/(n)o",10,13,0
.goodmode dw 0
.currentmode dw 0
;useful functions
decshowrm:
mov si, .number
.clear:
mov al, "0"
mov [si], al
inc si
cmp si, .numberend
jb .clear
dec si
call convertrm
mov si, .number
.lp:
lodsb
cmp si, .numberend
jae .end
cmp al, "0"
jbe .lp
.end:
dec si
call printrm
ret
.number times 7 db 0
.numberend db 0
convertrm:
dec si
mov bx, si ;place to convert into must be in si, number to convert must be in cx
.cnvrt:
mov si, bx
sub si, 4
.ten4: inc si
cmp cx, 10000
jb .ten3
sub cx, 10000
inc byte [si]
jmp .cnvrt
.ten3: inc si
cmp cx, 1000
jb .ten2
sub cx, 1000
inc byte [si]
jmp .cnvrt
.ten2: inc si
cmp cx, 100
jb .ten1
sub cx, 100
inc byte [si]
jmp .cnvrt
.ten1: inc si
cmp cx, 10
jb .ten0
sub cx, 10
inc byte [si]
jmp .cnvrt
.ten0: inc si
cmp cx, 1
jb .return
sub cx, 1
inc byte [si]
jmp .cnvrt
.return:
ret
printrm:
mov al, [si]
test al, al
jz .return
call charrm
inc si
jmp printrm
.return:
ret
charrm: ;char must be in al
mov bx, 7
mov ah, 0xE
int 10h
ret

View file

@ -1,90 +0,0 @@
ABSOLUTE 0x5000
VBECardInfo:
.signature resb 4
.version resw 1
.oemstring resd 1
.capabilities resd 1
.videomodeptr resd 1
.totalmemory resw 1
.oemsoftwarerev resw 1
.oemvendornameptr resd 1
.oemproductnameptr resd 1
.oemproductrevptr resd 1
.reserved resb 222
.oemdata resb 256
ABSOLUTE 0x5200
VBEModeInfo:
.attributes resw 1
.winA resb 1
.winB resb 1
.granularity resw 1
.winsize resw 1
.segmentA resw 1
.segmentB resw 1
.winfuncptr resd 1
.bytesperscanline resw 1
.xresolution resw 1
.yresolution resw 1
.xcharsize resb 1
.ycharsize resb 1
.numberofplanes resb 1
.bitsperpixel resb 1
.numberofbanks resb 1
.memorymodel resb 1
.banksize resb 1
.numberofimagepages resb 1
.unused resb 1
.redmasksize resb 1
.redfieldposition resb 1
.greenmasksize resb 1
.greenfieldposition resb 1
.bluemasksize resb 1
.bluefieldposition resb 1
.rsvdmasksize resb 1
.rsvdfieldposition resb 1
.directcolormodeinfo resb 1
.physbaseptr resd 1
.offscreenmemoryoffset resd 1
.offscreenmemsize resw 1
.reserved resb 206
VBE.ModeAttributes:
.available equ 1 << 0
.bios equ 1 << 2
.color equ 1 << 3
.graphics equ 1 << 4
.vgacompatible equ 1 << 5
.notbankable equ 1 << 6
.linearframebuffer equ 1 << 7
ABSOLUTE 0x5400
VBEEDID:
.header resb 8
.manufacturer resw 1
.productid resw 1
.serial resd 1
.manufactureweek resb 1
.manufactureyear resb 1
.version resb 1
.revision resb 1
.input resb 1
.horizontalsize resb 1
.verticalsize resb 1
.gamma resb 1
.displaytype resb 1
.chromaticity resb 10
.timingI resb 1
.timingII resb 1
.timingreserved resb 1
.standardtiming: resw 8 ;format: db (horizontal-248)/8, aspectratio | verticalfrequency - 60
.aspect.16.10 equ 0 ;mul horizontal by 10, shr 4 to get vertical resolution
.aspect.4.3 equ 1 << 6 ;mul horizontal by 3, shr 2 to get vertical resolution
.aspect.5.4 equ 2 << 6 ;shl horizontal by 2, div by 5 to get vertical resolution
.aspect.16.9 equ 3 << 6 ;mul horizontal by 9, shr by 4 to get vertical resolution
.descriptorblock1 resb 18
.descriptorblock2 resb 18
.descriptorblock3 resb 18
.descriptorblock4 resb 18
.extensionflag resb 1
.checksum resb 1

106
bootstrap.sh Normal file → Executable file
View file

@ -4,7 +4,7 @@
# This function is simply a banner to introduce the script
##########################################################
banner()
{
{
echo "|------------------------------------------|"
echo "|----- Welcome to the redox bootstrap -----|"
echo "|------------------------------------------|"
@ -26,7 +26,7 @@ install_macos_pkg()
BIN_NAME=$PKG_NAME
fi
BIN_LOCATION=$(which $BIN_NAME)
BIN_LOCATION=$(which $BIN_NAME || true)
if [ -z "$BIN_LOCATION" ]; then
echo "$PKG_MANAGER install $PKG_NAME"
$PKG_MANAGER install "$PKG_NAME"
@ -53,7 +53,7 @@ install_brew_cask_pkg()
###############################################################################
# This function checks which of the supported package managers
# is available on the OSX Host.
# If a support package manager is found, it delegates the installing work to
# If a supported package manager is found, it delegates the installing work to
# the relevant function.
# Otherwise this function will exit this script with an error.
###############################################################################
@ -66,7 +66,7 @@ osx()
elif [ ! -z "$(which port)" ]; then
osx_macports $@
else
echo "Please install either Hombrew or MacPorts, if you wish to use this script"
echo "Please install either Homebrew or MacPorts, if you wish to use this script"
echo "Re-run this script once you installed one of those package managers"
echo "Will not install, now exiting..."
exit 1
@ -93,6 +93,8 @@ osx_macports()
install_macports_pkg "virtualbox"
fi
install_macports_pkg "coreutils"
install_macports_pkg "findutils"
install_macports_pkg "gcc49" "gcc-4.9"
install_macports_pkg "nasm"
install_macports_pkg "pkgconfig"
@ -101,7 +103,7 @@ osx_macports()
}
###############################################################################
# This function takes care of installing all dependencies using Hombrew
# This function takes care of installing all dependencies using Homebrew
# for building redox on Mac OSX
# @params: $1 the emulator to install, virtualbox or qemu
###############################################################################
@ -110,10 +112,6 @@ osx_homebrew()
echo "Homebrew detected! Now updating..."
brew update
echo "Tapping required taps..."
brew tap homebrew/versions
brew tap glendc/gcc_cross_compilers
echo "Installing missing packages..."
install_brew_pkg "git"
@ -124,13 +122,14 @@ osx_homebrew()
install_brew_pkg "virtualbox"
fi
install_brew_pkg "coreutils"
install_brew_pkg "findutils"
install_brew_pkg "gcc49" "gcc-4.9"
install_brew_pkg "nasm"
install_brew_pkg "pkg-config"
install_brew_cask_pkg "osxfuse"
install_brew_pkg "glendc/gcc_cross_compilers/x64-elf-binutils" "x86_64-elf-gcc"
install_brew_pkg "glendc/gcc_cross_compilers/x64-elf-gcc" "x86_64-elf-gcc"
install_brew_pkg "redox-os/gcc_cross_compilers/x86_64-elf-gcc"
}
###############################################################################
@ -155,7 +154,7 @@ archLinux()
fi
if [ "$1" == "qemu" ]; then
if [ -z "$(which qemu-system-i386)" ]; then
if [ -z "$(which qemu-system-x86_64)" ]; then
echo "Installing QEMU..."
sudo pacman -S qemu
else
@ -164,14 +163,14 @@ archLinux()
fi
echo "Installing fuse..."
sudo pacman -S fuse
sudo pacman -S --needed fuse
}
###############################################################################
# This function takes care of installing all dependencies for building redox on
# debian based linux
# @params: $1 the emulator to install, virtualbox or qemu
# $2 the package manager to use
# $2 the package manager to use
###############################################################################
ubuntu()
{
@ -179,9 +178,9 @@ ubuntu()
echo "Updating system..."
sudo "$2" update
echo "Installing required packages..."
sudo "$2" install build-essential libc6-dev-i386 nasm curl file git libfuse-dev
sudo "$2" install build-essential libc6-dev-i386 nasm curl file git libfuse-dev fuse pkg-config
if [ "$1" == "qemu" ]; then
if [ -z "$(which qemu-system-i386)" ]; then
if [ -z "$(which qemu-system-x86_64)" ]; then
echo "Installing QEMU..."
sudo "$2" install qemu-system-x86 qemu-kvm
else
@ -210,7 +209,7 @@ fedora()
sudo dnf install git-all
fi
if [ "$1" == "qemu" ]; then
if [ -z "$(which qemu-system-i386)" ]; then
if [ -z "$(which qemu-system-x86_64)" ]; then
echo "Installing QEMU..."
sudo dnf install qemu-system-x86 qemu-kvm
else
@ -241,13 +240,13 @@ fedora()
###############################################################################
suse()
{
echo "Detected a suse"
echo "Detected SUSE Linux"
if [ -z "$(which git)" ]; then
echo "Installing git..."
zypper install git
fi
if [ "$1" == "qemu" ]; then
if [ -z "$(which qemu-system-i386)" ]; then
if [ -z "$(which qemu-system-x86_64)" ]; then
echo "Installing QEMU..."
sudo zypper install qemu-x86 qemu-kvm
else
@ -263,7 +262,7 @@ suse()
fi
fi
echo "Installing necessary build tools..."
sudo zypper install gcc gcc-c++ glibc-devel-32bit nasm make libfuse
sudo zypper install gcc gcc-c++ glibc-devel-32bit nasm make fuse-devel
}
##############################################################################
@ -282,12 +281,14 @@ gentoo()
echo "Installing git..."
sudo emerge dev-vcs/git
fi
echo "Installing fuse..."
sudo emerge sys-fs/fuse
if [ -z "$(which fusermount)" ]; then
echo "Installing fuse..."
sudo emerge sys-fs/fuse
fi
if [ "$2" == "qemu" ]; then
if [ -z "$(which qemu-system-i386)" ]; then
if [ -z "$(which qemu-system-x86_64)" ]; then
echo "Please install QEMU and re-run this script"
echo "Step1. Add QEMU_SOFTMMU_TARGETS=\"i386\" to /etc/portage/make.conf"
echo "Step1. Add QEMU_SOFTMMU_TARGETS=\"x86_64\" to /etc/portage/make.conf"
echo "Step2. Execute \"sudo emerge app-emulation/qemu\""
else
echo "QEMU already installed!"
@ -303,18 +304,9 @@ gentoo()
solus()
{
echo "Detected SolusOS"
if [ -z "$(which nasm)" ]; then
echo "Installing nasm..."
sudo eopkg it nasm
fi
if [ -z "$(which git)" ]; then
echo "Installing git..."
sudo eopkg it git
fi
echo "Installing fuse..."
sudo eopkg it fuse-devel
if [ "$1" == "qemu" ]; then
if [ -z "$(which qemu-system-i386)" ]; then
if [ -z "$(which qemu-system-x86_64)" ]; then
sudo eopkg it qemu
else
echo "QEMU already installed!"
@ -328,6 +320,10 @@ solus()
echo "Virtualbox already installed!"
fi
fi
echo "Installing necessary build tools..."
#if guards are not necessary with eopkg since it does nothing if latest version is already installed
sudo eopkg it fuse-devel git gcc g++ libgcc-32bit libstdc++-32bit nasm make
}
######################################################################
@ -348,16 +344,16 @@ usage()
echo " -e [emulator] Install specific emulator, virtualbox or qemu"
echo " -p [package Choose an Ubuntu package manager, apt-fast or"
echo " manager] aptitude"
echo " -d Only install the dependencies, skip boot step"
echo " -d Only install the dependencies, skip boot step"
echo "EXAMPLES:"
echo
echo "./bootstrap.sh -b buddy -e qemu"
echo "./bootstrap.sh -e qemu"
exit
}
####################################################################################
# This function takes care of everything associated to rust, and the version manager
# That controls it, it can install rustup and uninstall multirust as well as making
# That controls it, it can install rustup and uninstall multirust as well as making
# sure that the correct version of rustc is selected by rustup
####################################################################################
rustInstall() {
@ -370,7 +366,7 @@ rustInstall() {
printf "Uninstall multirust (y/N):"
read multirust
if echo "$multirust" | grep -iq "^y" ;then
sudo /usr/local/lib/rustlib/uninstall.sh
sudo /usr/local/lib/rustlib/uninstall.sh
else
echo "Please manually uninstall multirust and any other versions of rust, then re-run bootstrap."
exit
@ -378,10 +374,10 @@ rustInstall() {
else
echo "Old multirust not installed, you are good to go."
fi
# If rustup is not installed we should offer to install it for them
# If rustup is not installed we should offer to install it for them
if [ -z "$(which rustup)" ]; then
echo "You do not have rustup installed."
echo "We HIGHLY reccomend using rustup."
echo "We HIGHLY recommend using rustup."
echo "Would you like to install it now?"
echo "*WARNING* this involves a 'curl | sh' style command"
printf "(y/N): "
@ -389,22 +385,22 @@ rustInstall() {
if echo "$rustup" | grep -iq "^y" ;then
#install rustup
curl https://sh.rustup.rs -sSf | sh -s -- --default-toolchain nightly
# You have to add the rustup variables to the $PATH
# You have to add the rustup variables to the $PATH
echo "export PATH=\"\$HOME/.cargo/bin:\$PATH\"" >> ~/.bashrc
# source the variables so that we can execute rustup commands in the current shell
source ~/.cargo/env
source ~/.cargo/env
rustup default nightly
else
echo "Rustup will not be installed!"
fi
fi
#
fi
#
if [ -z "$(which rustc)" ]; then
echo "Rust is not installed"
echo "Please either run the script again, accepting rustup install"
echo "or install rustc nightly manually (not reccomended) via:"
echo "or install rustc nightly manually (not recommended) via:"
echo "\#curl -sSf https://static.rust-lang.org/rustup.sh | sh -s -- --channel=nightly"
exit
exit
fi
# If the system has rustup installed then update rustc to the latest nightly
if hash 2>/dev/null rustup; then
@ -415,8 +411,8 @@ rustInstall() {
if echo "$(rustc --version)" | grep -viq "nightly" ;then
echo "It appears that you have rust installed, but it"
echo "is not the nightly version, please either install"
echo "the nightly manually (not reccomended) or run this"
echo "script again, accepting the multirust install"
echo "the nightly manually (not recommended) or run this"
echo "script again, accepting the rustup install"
echo
else
echo "Your rust install looks good!"
@ -425,7 +421,7 @@ rustInstall() {
}
####################################################################
# This function gets the current build status from travis and prints
# This function gets the current build status from travis and prints
# a message to the user
####################################################################
statusCheck() {
@ -435,14 +431,14 @@ statusCheck() {
if echo "$i" | grep -iq "0" ;then
echo
echo "********************************************"
echo "Travis reports that the last build succeded!"
echo "Travis reports that the last build succeeded!"
echo "Looks like you are good to go!"
echo "********************************************"
elif echo "$i" | grep -iq "null" ;then
echo
echo "******************************************************************"
echo "******************************************************************"
echo "The Travis build did not finish, this is an error with its config."
echo "I cannot reliably determine whether the build is succeding or not."
echo "I cannot reliably determine whether the build is succeeding or not."
echo "Consider checking for and maybe opening an issue on github"
echo "******************************************************************"
else
@ -463,7 +459,7 @@ statusCheck() {
boot()
{
echo "Cloning github repo..."
git clone https://github.com/redox-os/redox.git --origin upstream --recursive
git clone https://github.com/redox-os/redox.git --origin upstream --recursive
rustInstall
echo "Cleaning up..."
rm bootstrap.sh
@ -511,7 +507,7 @@ banner
if [ "Darwin" == "$(uname -s)" ]; then
osx "$emulator"
else
# Here we will user package managers to determine which operating system the user is using
# Here we will user package managers to determine which operating system the user is using
# Arch linux
if hash 2>/dev/null pacman; then
archLinux "$emulator"

1
cookbook Submodule

@ -0,0 +1 @@
Subproject commit 5f961f888b8cc32266f63713073ad13c1983e3e0

View file

@ -1,6 +0,0 @@
[package]
name = "dma"
version = "0.1.0"
[dependencies]
redox_syscall = { path = "../../syscall/" }

View file

@ -1,78 +0,0 @@
extern crate syscall;
use std::{mem, ptr};
use std::ops::{Deref, DerefMut};
use syscall::Result;
struct PhysBox {
address: usize,
size: usize
}
impl PhysBox {
fn new(size: usize) -> Result<PhysBox> {
let address = unsafe { syscall::physalloc(size)? };
Ok(PhysBox {
address: address,
size: size
})
}
}
impl Drop for PhysBox {
fn drop(&mut self) {
let _ = unsafe { syscall::physfree(self.address, self.size) };
}
}
pub struct Dma<T> {
phys: PhysBox,
virt: *mut T
}
impl<T> Dma<T> {
pub fn new(value: T) -> Result<Dma<T>> {
let phys = PhysBox::new(mem::size_of::<T>())?;
let virt = unsafe { syscall::physmap(phys.address, phys.size, syscall::MAP_WRITE)? } as *mut T;
unsafe { ptr::write(virt, value); }
Ok(Dma {
phys: phys,
virt: virt
})
}
pub fn zeroed() -> Result<Dma<T>> {
let phys = PhysBox::new(mem::size_of::<T>())?;
let virt = unsafe { syscall::physmap(phys.address, phys.size, syscall::MAP_WRITE)? } as *mut T;
unsafe { ptr::write_bytes(virt as *mut u8, 0, phys.size); }
Ok(Dma {
phys: phys,
virt: virt
})
}
pub fn physical(&self) -> usize {
self.phys.address
}
}
impl<T> Deref for Dma<T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.virt }
}
}
impl<T> DerefMut for Dma<T> {
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.virt }
}
}
impl<T> Drop for Dma<T> {
fn drop(&mut self) {
unsafe { drop(ptr::read(self.virt)); }
let _ = unsafe { syscall::physunmap(self.virt as usize) };
}
}

@ -1 +0,0 @@
Subproject commit 2e1bcf2ab5a03d176e7c66ae79c31ffccdcda77a

View file

@ -1,6 +0,0 @@
[package]
name = "event"
version = "0.1.0"
[dependencies]
redox_syscall = { path = "../../syscall/" }

View file

@ -1,83 +0,0 @@
extern crate syscall;
use std::collections::BTreeMap;
use std::fs::File;
use std::io::{Read, Error, Result};
use std::os::unix::io::RawFd;
pub struct EventQueue<R> {
/// The file to read events from
file: File,
/// A map of registered file descriptors to their handler callbacks
callbacks: BTreeMap<RawFd, Box<FnMut(usize) -> Result<Option<R>>>>
}
impl<R> EventQueue<R> {
/// Create a new event queue
pub fn new() -> Result<EventQueue<R>> {
Ok(EventQueue {
file: File::open("event:")?,
callbacks: BTreeMap::new()
})
}
/// Add a file to the event queue, calling a callback when an event occurs
///
/// The callback is given a mutable reference to the file and the event data
/// (typically the length of data available for read)
///
/// The callback returns Ok(None) if it wishes to continue the event loop,
/// or Ok(Some(R)) to break the event loop and return the value.
/// Err can be used to allow the callback to return an I/O error, and break the
/// event loop
pub fn add<F: FnMut(usize) -> Result<Option<R>> + 'static>(&mut self, fd: RawFd, callback: F) -> Result<()> {
syscall::fevent(fd, syscall::EVENT_READ).map_err(|x| Error::from_raw_os_error(x.errno))?;
self.callbacks.insert(fd, Box::new(callback));
Ok(())
}
/// Remove a file from the event queue, returning its callback if found
pub fn remove(&mut self, fd: RawFd) -> Result<Option<Box<FnMut(usize) -> Result<Option<R>>>>> {
if let Some(callback) = self.callbacks.remove(&fd) {
syscall::fevent(fd, 0).map_err(|x| Error::from_raw_os_error(x.errno))?;
Ok(Some(callback))
} else {
Ok(None)
}
}
/// Send an event to a descriptor callback
pub fn trigger(&mut self, fd: RawFd, count: usize) -> Result<Option<R>> {
if let Some(callback) = self.callbacks.get_mut(&fd) {
callback(count)
} else {
Ok(None)
}
}
/// Send an event to all descriptor callbacks, useful for cleaning out buffers after init
pub fn trigger_all(&mut self, count: usize) -> Result<Vec<R>> {
let mut rets = Vec::new();
for (_fd, callback) in self.callbacks.iter_mut() {
if let Some(ret) = callback(count)? {
rets.push(ret);
}
}
Ok(rets)
}
/// Process the event queue until a callback returns Some(R)
pub fn run(&mut self) -> Result<R> {
loop {
let mut event = syscall::Event::default();
if self.file.read(&mut event)? > 0 {
if let Some(ret) = self.trigger(event.id, event.data)? {
return Ok(ret);
}
}
}
}
}

View file

@ -1,8 +0,0 @@
[package]
authors = ["Philipp Oppermann <dev@phil-opp.com>"]
name = "hole_list_allocator"
version = "0.1.0"
[dependencies]
linked_list_allocator = { git = "https://github.com/phil-opp/linked-list-allocator.git" }
spin = "*"

View file

@ -1,62 +0,0 @@
#![feature(allocator)]
#![feature(const_fn)]
#![allocator]
#![no_std]
use spin::Mutex;
use linked_list_allocator::Heap;
extern crate spin;
extern crate linked_list_allocator;
static HEAP: Mutex<Option<Heap>> = Mutex::new(None);
pub unsafe fn init(offset: usize, size: usize) {
*HEAP.lock() = Some(Heap::new(offset, size));
}
#[no_mangle]
pub extern fn __rust_allocate(size: usize, align: usize) -> *mut u8 {
if let Some(ref mut heap) = *HEAP.lock() {
heap.allocate_first_fit(size, align).expect("out of memory")
} else {
panic!("__rust_allocate: heap not initialized");
}
}
#[no_mangle]
pub extern fn __rust_deallocate(ptr: *mut u8, size: usize, align: usize) {
if let Some(ref mut heap) = *HEAP.lock() {
unsafe { heap.deallocate(ptr, size, align) };
} else {
panic!("__rust_deallocate: heap not initialized");
}
}
#[no_mangle]
pub extern fn __rust_usable_size(size: usize, _align: usize) -> usize {
size
}
#[no_mangle]
pub extern fn __rust_reallocate_inplace(_ptr: *mut u8, size: usize,
_new_size: usize, _align: usize) -> usize
{
size
}
#[no_mangle]
pub extern fn __rust_reallocate(ptr: *mut u8, size: usize, new_size: usize,
align: usize) -> *mut u8 {
use core::{ptr, cmp};
// from: https://github.com/rust-lang/rust/blob/
// c66d2380a810c9a2b3dbb4f93a830b101ee49cc2/
// src/liballoc_system/lib.rs#L98-L101
let new_ptr = __rust_allocate(new_size, align);
unsafe { ptr::copy(ptr, new_ptr, cmp::min(size, new_size)) };
__rust_deallocate(ptr, size, align);
new_ptr
}

View file

@ -1,3 +0,0 @@
[package]
name = "io"
version = "0.1.0"

View file

@ -1,67 +0,0 @@
use core::cmp::PartialEq;
use core::ops::{BitAnd, BitOr, Not};
pub trait Io {
type Value: Copy + PartialEq + BitAnd<Output = Self::Value> + BitOr<Output = Self::Value> + Not<Output = Self::Value>;
fn read(&self) -> Self::Value;
fn write(&mut self, value: Self::Value);
#[inline(always)]
fn readf(&self, flags: Self::Value) -> bool {
(self.read() & flags) as Self::Value == flags
}
#[inline(always)]
fn writef(&mut self, flags: Self::Value, value: bool) {
let tmp: Self::Value = match value {
true => self.read() | flags,
false => self.read() & !flags,
};
self.write(tmp);
}
}
pub struct ReadOnly<I: Io> {
inner: I
}
impl<I: Io> ReadOnly<I> {
pub const fn new(inner: I) -> ReadOnly<I> {
ReadOnly {
inner: inner
}
}
#[inline(always)]
pub fn read(&self) -> I::Value {
self.inner.read()
}
#[inline(always)]
pub fn readf(&self, flags: I::Value) -> bool {
self.inner.readf(flags)
}
}
pub struct WriteOnly<I: Io> {
inner: I
}
impl<I: Io> WriteOnly<I> {
pub const fn new(inner: I) -> WriteOnly<I> {
WriteOnly {
inner: inner
}
}
#[inline(always)]
pub fn write(&mut self, value: I::Value) {
self.inner.write(value)
}
#[inline(always)]
pub fn writef(&mut self, flags: I::Value, value: bool) {
self.inner.writef(flags, value)
}
}

View file

@ -1,14 +0,0 @@
//! I/O functions
#![feature(asm)]
#![feature(const_fn)]
#![feature(core_intrinsics)]
#![no_std]
pub use self::io::*;
pub use self::mmio::*;
pub use self::pio::*;
mod io;
mod mmio;
mod pio;

View file

@ -1,31 +0,0 @@
use core::intrinsics::{volatile_load, volatile_store};
use core::mem::uninitialized;
use core::ops::{BitAnd, BitOr, Not};
use super::io::Io;
#[repr(packed)]
pub struct Mmio<T> {
value: T,
}
impl<T> Mmio<T> {
/// Create a new Mmio without initializing
pub fn new() -> Self {
Mmio {
value: unsafe { uninitialized() }
}
}
}
impl<T> Io for Mmio<T> where T: Copy + PartialEq + BitAnd<Output = T> + BitOr<Output = T> + Not<Output = T> {
type Value = T;
fn read(&self) -> T {
unsafe { volatile_load(&self.value) }
}
fn write(&mut self, value: T) {
unsafe { volatile_store(&mut self.value, value) };
}
}

View file

@ -1,89 +0,0 @@
use core::marker::PhantomData;
use super::io::Io;
/// Generic PIO
#[derive(Copy, Clone)]
pub struct Pio<T> {
port: u16,
value: PhantomData<T>,
}
impl<T> Pio<T> {
/// Create a PIO from a given port
pub const fn new(port: u16) -> Self {
Pio::<T> {
port: port,
value: PhantomData,
}
}
}
/// Read/Write for byte PIO
impl Io for Pio<u8> {
type Value = u8;
/// Read
#[inline(always)]
fn read(&self) -> u8 {
let value: u8;
unsafe {
asm!("in $0, $1" : "={al}"(value) : "{dx}"(self.port) : "memory" : "intel", "volatile");
}
value
}
/// Write
#[inline(always)]
fn write(&mut self, value: u8) {
unsafe {
asm!("out $1, $0" : : "{al}"(value), "{dx}"(self.port) : "memory" : "intel", "volatile");
}
}
}
/// Read/Write for word PIO
impl Io for Pio<u16> {
type Value = u16;
/// Read
#[inline(always)]
fn read(&self) -> u16 {
let value: u16;
unsafe {
asm!("in $0, $1" : "={ax}"(value) : "{dx}"(self.port) : "memory" : "intel", "volatile");
}
value
}
/// Write
#[inline(always)]
fn write(&mut self, value: u16) {
unsafe {
asm!("out $1, $0" : : "{ax}"(value), "{dx}"(self.port) : "memory" : "intel", "volatile");
}
}
}
/// Read/Write for doubleword PIO
impl Io for Pio<u32> {
type Value = u32;
/// Read
#[inline(always)]
fn read(&self) -> u32 {
let value: u32;
unsafe {
asm!("in $0, $1" : "={eax}"(value) : "{dx}"(self.port) : "memory" : "intel", "volatile");
}
value
}
/// Write
#[inline(always)]
fn write(&mut self, value: u32) {
unsafe {
asm!("out $1, $0" : : "{eax}"(value), "{dx}"(self.port) : "memory" : "intel", "volatile");
}
}
}

43
docker/Dockerfile Executable file
View file

@ -0,0 +1,43 @@
FROM ubuntu:17.04
ENV USER user
ARG LOCAL_UID=local
ARG LOCAL_GID=local
ENV BUILD_UID=${LOCAL_UID:-9001}
ENV BUILD_GID=${LOCAL_GID:-9001}
RUN apt-get update \
&& apt-get install -y dirmngr git gosu gcc fuse nasm qemu-utils pkg-config \
libfuse-dev make curl wget file sudo apt-transport-https autoconf flex \
bison texinfo \
&& apt-key adv --keyserver keyserver.ubuntu.com --recv-keys AA12E97F0881517F \
&& echo "deb https://static.redox-os.org/toolchain/apt/ /" >> /etc/apt/sources.list.d/redox.list \
&& apt-get update -o Dir::Etc::sourcelist="redox.list" \
&& apt-get install -y x86-64-unknown-redox-newlib x86-64-unknown-redox-binutils x86-64-unknown-redox-gcc \
&& groupadd -g $BUILD_GID user \
&& useradd --shell /bin/bash -u $BUILD_UID -g $BUILD_GID -o -c "" -m $USER \
&& echo "$USER ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/user-no-sudo-password
COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh
USER $USER
ENV HOME /home/$USER
ENV PATH $HOME/.cargo/bin:$PATH
ENV SRC_PATH $HOME/src
WORKDIR $HOME
RUN curl https://sh.rustup.rs > sh.rustup.rs \
&& sh sh.rustup.rs -y \
&& rustup update \
&& rustup component add rust-src \
&& rustup default nightly \
&& curl -O https://ftp.gnu.org/gnu/automake/automake-1.15.1.tar.gz \
&& tar -xvpf automake-1.15.1.tar.gz; cd automake-1.15.1; ./configure; make; sudo make install; cd .. \
&& cargo install xargo \
&& cargo install cargo-config \
&& mkdir -p $SRC_PATH
WORKDIR $SRC_PATH
USER root
ENTRYPOINT ["/usr/local/bin/entrypoint.sh"]

29
docker/README.md Normal file
View file

@ -0,0 +1,29 @@
### Building Redox using Docker images with the toolchain
*All you need is git, make, qemu, fuse and docker. The method requires a non-privileged user able to run the `docker` command, which is usually achieved by adding the user to the `docker` group.*
```shell
git clone https://github.com/redox-os/redox.git ; cd redox #1
docker build --build-arg LOCAL_UID="$(id -u)" --build-arg LOCAL_GID="$(id -g)" \
-t redox docker/ #2
git pull --rebase --recurse-submodules && git submodule sync \
&& git submodule update --recursive --init #3
docker run --cap-add MKNOD --cap-add SYS_ADMIN \
-e LOCAL_UID="$(id -u)" -e LOCAL_GID="$(id -g)" \
--device /dev/fuse -v "$(pwd):/home/user/src" --rm redox make fetch all #4
make qemu #5
```
To unpack:
1. Creates a local copy of the repository.
2. Creates a new image in the local image repository named `redox` with Redox toolchain installed. You only need to rebuild the image if you want to update the toolchain.
3. Updates all the submodules in the repository.
4. Builds Redox using the `redox` image. The arguments allow the container to use `fuse` and ensure the resulting files are owned by the current user.
5. Runs Redox.
On selinux systems, replace #4 with:
```
docker run --cap-add MKNOD --cap-add SYS_ADMIN \
-e LOCAL_UID="$(id -u)" -e LOCAL_GID="$(id -g)" \
--device /dev/fuse -v "$(pwd):/home/user/src" --security-opt label=disable \
--rm redox make fetch all
```

18
docker/entrypoint.sh Executable file
View file

@ -0,0 +1,18 @@
#!/usr/bin/env bash
# Use -e LOCAL_UID="$(id -u)" -e LOCAL_GID="$(id -g)"
# on the docker run command line if the container build user is different
# from the run user
CONT_UID=`id -u user`
CONT_GID=`id -g user`
RUN_UID=${LOCAL_UID:-$CONT_UID}
RUN_GID=${LOCAL_GID:-$CONT_GID}
if [ $RUN_UID != $CONT_UID ] || [ $RUN_GID != $CONT_GID ]; then
echo -e "\033[01;38;5;155mChanging user id:group to ${RUN_UID}:${RUN_GID}. Please wait...\033[0m"
groupmod -g $RUN_GID user
usermod -u $RUN_UID -g $RUN_GID user
fi
exec gosu user:user "$@"

View file

@ -1,10 +0,0 @@
[package]
name = "ahcid"
version = "0.1.0"
[dependencies]
bitflags = "*"
dma = { path = "../../crates/dma/" }
io = { path = "../../crates/io/" }
spin = "*"
redox_syscall = { path = "../../syscall/" }

View file

@ -1,108 +0,0 @@
use std::ptr;
use dma::Dma;
use syscall::error::Result;
use super::hba::{HbaPort, HbaCmdTable, HbaCmdHeader};
pub struct Disk {
id: usize,
port: &'static mut HbaPort,
size: u64,
clb: Dma<[HbaCmdHeader; 32]>,
ctbas: [Dma<HbaCmdTable>; 32],
fb: Dma<[u8; 256]>,
buf: Dma<[u8; 256 * 512]>
}
impl Disk {
pub fn new(id: usize, port: &'static mut HbaPort) -> Result<Self> {
let mut clb = Dma::zeroed()?;
let mut ctbas = [
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?, Dma::zeroed()?,
];
let mut fb = Dma::zeroed()?;
let buf = Dma::zeroed()?;
port.init(&mut clb, &mut ctbas, &mut fb);
let size = unsafe { port.identify(&mut clb, &mut ctbas).unwrap_or(0) };
Ok(Disk {
id: id,
port: port,
size: size,
clb: clb,
ctbas: ctbas,
fb: fb,
buf: buf
})
}
pub fn id(&self) -> usize {
self.id
}
pub fn size(&self) -> u64 {
self.size
}
pub fn read(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
let sectors = buffer.len()/512;
let mut sector: usize = 0;
while sectors - sector >= 255 {
if let Err(err) = self.port.ata_dma(block + sector as u64, 255, false, &mut self.clb, &mut self.ctbas, &mut self.buf) {
return Err(err);
}
unsafe { ptr::copy(self.buf.as_ptr(), buffer.as_mut_ptr().offset(sector as isize * 512), 255 * 512); }
sector += 255;
}
if sector < sectors {
if let Err(err) = self.port.ata_dma(block + sector as u64, sectors - sector, false, &mut self.clb, &mut self.ctbas, &mut self.buf) {
return Err(err);
}
unsafe { ptr::copy(self.buf.as_ptr(), buffer.as_mut_ptr().offset(sector as isize * 512), (sectors - sector) * 512); }
sector += sectors - sector;
}
Ok(sector * 512)
}
pub fn write(&mut self, block: u64, buffer: &[u8]) -> Result<usize> {
let sectors = buffer.len()/512;
let mut sector: usize = 0;
while sectors - sector >= 255 {
unsafe { ptr::copy(buffer.as_ptr().offset(sector as isize * 512), self.buf.as_mut_ptr(), 255 * 512); }
if let Err(err) = self.port.ata_dma(block + sector as u64, 255, true, &mut self.clb, &mut self.ctbas, &mut self.buf) {
return Err(err);
}
sector += 255;
}
if sector < sectors {
unsafe { ptr::copy(buffer.as_ptr().offset(sector as isize * 512), self.buf.as_mut_ptr(), (sectors - sector) * 512); }
if let Err(err) = self.port.ata_dma(block + sector as u64, sectors - sector, true, &mut self.clb, &mut self.ctbas, &mut self.buf) {
return Err(err);
}
sector += sectors - sector;
}
Ok(sector * 512)
}
}

View file

@ -1,155 +0,0 @@
use io::Mmio;
#[repr(u8)]
pub enum FisType {
/// Register FIS - host to device
RegH2D = 0x27,
/// Register FIS - device to host
RegD2H = 0x34,
/// DMA activate FIS - device to host
DmaAct = 0x39,
/// DMA setup FIS - bidirectional
DmaSetup = 0x41,
/// Data FIS - bidirectional
Data = 0x46,
/// BIST activate FIS - bidirectional
Bist = 0x58,
/// PIO setup FIS - device to host
PioSetup = 0x5F,
/// Set device bits FIS - device to host
DevBits = 0xA1
}
#[repr(packed)]
pub struct FisRegH2D {
// DWORD 0
pub fis_type: Mmio<u8>, // FIS_TYPE_REG_H2D
pub pm: Mmio<u8>, // Port multiplier, 1: Command, 0: Control
pub command: Mmio<u8>, // Command register
pub featurel: Mmio<u8>, // Feature register, 7:0
// DWORD 1
pub lba0: Mmio<u8>, // LBA low register, 7:0
pub lba1: Mmio<u8>, // LBA mid register, 15:8
pub lba2: Mmio<u8>, // LBA high register, 23:16
pub device: Mmio<u8>, // Device register
// DWORD 2
pub lba3: Mmio<u8>, // LBA register, 31:24
pub lba4: Mmio<u8>, // LBA register, 39:32
pub lba5: Mmio<u8>, // LBA register, 47:40
pub featureh: Mmio<u8>, // Feature register, 15:8
// DWORD 3
pub countl: Mmio<u8>, // Count register, 7:0
pub counth: Mmio<u8>, // Count register, 15:8
pub icc: Mmio<u8>, // Isochronous command completion
pub control: Mmio<u8>, // Control register
// DWORD 4
pub rsv1: [Mmio<u8>; 4], // Reserved
}
#[repr(packed)]
pub struct FisRegD2H {
// DWORD 0
pub fis_type: Mmio<u8>, // FIS_TYPE_REG_D2H
pub pm: Mmio<u8>, // Port multiplier, Interrupt bit: 2
pub status: Mmio<u8>, // Status register
pub error: Mmio<u8>, // Error register
// DWORD 1
pub lba0: Mmio<u8>, // LBA low register, 7:0
pub lba1: Mmio<u8>, // LBA mid register, 15:8
pub lba2: Mmio<u8>, // LBA high register, 23:16
pub device: Mmio<u8>, // Device register
// DWORD 2
pub lba3: Mmio<u8>, // LBA register, 31:24
pub lba4: Mmio<u8>, // LBA register, 39:32
pub lba5: Mmio<u8>, // LBA register, 47:40
pub rsv2: Mmio<u8>, // Reserved
// DWORD 3
pub countl: Mmio<u8>, // Count register, 7:0
pub counth: Mmio<u8>, // Count register, 15:8
pub rsv3: [Mmio<u8>; 2], // Reserved
// DWORD 4
pub rsv4: [Mmio<u8>; 4], // Reserved
}
#[repr(packed)]
pub struct FisData {
// DWORD 0
pub fis_type: Mmio<u8>, // FIS_TYPE_DATA
pub pm: Mmio<u8>, // Port multiplier
pub rsv1: [Mmio<u8>; 2], // Reserved
// DWORD 1 ~ N
pub data: [Mmio<u8>; 252], // Payload
}
#[repr(packed)]
pub struct FisPioSetup {
// DWORD 0
pub fis_type: Mmio<u8>, // FIS_TYPE_PIO_SETUP
pub pm: Mmio<u8>, // Port multiplier, direction: 4 - device to host, interrupt: 2
pub status: Mmio<u8>, // Status register
pub error: Mmio<u8>, // Error register
// DWORD 1
pub lba0: Mmio<u8>, // LBA low register, 7:0
pub lba1: Mmio<u8>, // LBA mid register, 15:8
pub lba2: Mmio<u8>, // LBA high register, 23:16
pub device: Mmio<u8>, // Device register
// DWORD 2
pub lba3: Mmio<u8>, // LBA register, 31:24
pub lba4: Mmio<u8>, // LBA register, 39:32
pub lba5: Mmio<u8>, // LBA register, 47:40
pub rsv2: Mmio<u8>, // Reserved
// DWORD 3
pub countl: Mmio<u8>, // Count register, 7:0
pub counth: Mmio<u8>, // Count register, 15:8
pub rsv3: Mmio<u8>, // Reserved
pub e_status: Mmio<u8>, // New value of status register
// DWORD 4
pub tc: Mmio<u16>, // Transfer count
pub rsv4: [Mmio<u8>; 2], // Reserved
}
#[repr(packed)]
pub struct FisDmaSetup {
// DWORD 0
pub fis_type: Mmio<u8>, // FIS_TYPE_DMA_SETUP
pub pm: Mmio<u8>, // Port multiplier, direction: 4 - device to host, interrupt: 2, auto-activate: 1
pub rsv1: [Mmio<u8>; 2], // Reserved
// DWORD 1&2
pub dma_buffer_id: Mmio<u64>, /* DMA Buffer Identifier. Used to Identify DMA buffer in host memory. SATA Spec says host specific and not in Spec. Trying AHCI spec might work. */
// DWORD 3
pub rsv3: Mmio<u32>, // More reserved
// DWORD 4
pub dma_buffer_offset: Mmio<u32>, // Byte offset into buffer. First 2 bits must be 0
// DWORD 5
pub transfer_count: Mmio<u32>, // Number of bytes to transfer. Bit 0 must be 0
// DWORD 6
pub rsv6: Mmio<u32>, // Reserved
}

View file

@ -1,417 +0,0 @@
use std::mem::size_of;
use std::ops::DerefMut;
use std::{ptr, u32};
use dma::Dma;
use io::{Io, Mmio};
use syscall::error::{Error, Result, EIO};
use super::fis::{FisType, FisRegH2D};
const ATA_CMD_READ_DMA_EXT: u8 = 0x25;
const ATA_CMD_WRITE_DMA_EXT: u8 = 0x35;
const ATA_CMD_IDENTIFY: u8 = 0xEC;
const ATA_DEV_BUSY: u8 = 0x80;
const ATA_DEV_DRQ: u8 = 0x08;
const HBA_PORT_CMD_CR: u32 = 1 << 15;
const HBA_PORT_CMD_FR: u32 = 1 << 14;
const HBA_PORT_CMD_FRE: u32 = 1 << 4;
const HBA_PORT_CMD_ST: u32 = 1;
const HBA_PORT_IS_ERR: u32 = 1 << 30 | 1 << 29 | 1 << 28 | 1 << 27;
const HBA_SSTS_PRESENT: u32 = 0x3;
const HBA_SIG_ATA: u32 = 0x00000101;
const HBA_SIG_ATAPI: u32 = 0xEB140101;
const HBA_SIG_PM: u32 = 0x96690101;
const HBA_SIG_SEMB: u32 = 0xC33C0101;
fn pause() {
unsafe { asm!("pause" : : : "memory" : "intel", "volatile"); }
}
#[derive(Debug)]
pub enum HbaPortType {
None,
Unknown(u32),
SATA,
SATAPI,
PM,
SEMB,
}
#[repr(packed)]
pub struct HbaPort {
pub clb: [Mmio<u32>; 2], // 0x00, command list base address, 1K-byte aligned
pub fb: [Mmio<u32>; 2], // 0x08, FIS base address, 256-byte aligned
pub is: Mmio<u32>, // 0x10, interrupt status
pub ie: Mmio<u32>, // 0x14, interrupt enable
pub cmd: Mmio<u32>, // 0x18, command and status
pub _rsv0: Mmio<u32>, // 0x1C, Reserved
pub tfd: Mmio<u32>, // 0x20, task file data
pub sig: Mmio<u32>, // 0x24, signature
pub ssts: Mmio<u32>, // 0x28, SATA status (SCR0:SStatus)
pub sctl: Mmio<u32>, // 0x2C, SATA control (SCR2:SControl)
pub serr: Mmio<u32>, // 0x30, SATA error (SCR1:SError)
pub sact: Mmio<u32>, // 0x34, SATA active (SCR3:SActive)
pub ci: Mmio<u32>, // 0x38, command issue
pub sntf: Mmio<u32>, // 0x3C, SATA notification (SCR4:SNotification)
pub fbs: Mmio<u32>, // 0x40, FIS-based switch control
pub _rsv1: [Mmio<u32>; 11], // 0x44 ~ 0x6F, Reserved
pub vendor: [Mmio<u32>; 4], // 0x70 ~ 0x7F, vendor specific
}
impl HbaPort {
pub fn probe(&self) -> HbaPortType {
if self.ssts.readf(HBA_SSTS_PRESENT) {
let sig = self.sig.read();
match sig {
HBA_SIG_ATA => HbaPortType::SATA,
HBA_SIG_ATAPI => HbaPortType::SATAPI,
HBA_SIG_PM => HbaPortType::PM,
HBA_SIG_SEMB => HbaPortType::SEMB,
_ => HbaPortType::Unknown(sig),
}
} else {
HbaPortType::None
}
}
pub fn start(&mut self) {
while self.cmd.readf(HBA_PORT_CMD_CR) {
pause();
}
self.cmd.writef(HBA_PORT_CMD_FRE | HBA_PORT_CMD_ST, true);
}
pub fn stop(&mut self) {
self.cmd.writef(HBA_PORT_CMD_ST, false);
while self.cmd.readf(HBA_PORT_CMD_FR | HBA_PORT_CMD_CR) {
pause();
}
self.cmd.writef(HBA_PORT_CMD_FRE, false);
}
pub fn slot(&self) -> Option<u32> {
let slots = self.sact.read() | self.ci.read();
for i in 0..32 {
if slots & 1 << i == 0 {
return Some(i);
}
}
None
}
pub fn init(&mut self, clb: &mut Dma<[HbaCmdHeader; 32]>, ctbas: &mut [Dma<HbaCmdTable>; 32], fb: &mut Dma<[u8; 256]>) {
self.stop();
for i in 0..32 {
let cmdheader = &mut clb[i];
cmdheader.ctba.write(ctbas[i].physical() as u64);
cmdheader.prdtl.write(0);
}
self.clb[0].write(clb.physical() as u32);
self.clb[1].write((clb.physical() >> 32) as u32);
self.fb[0].write(fb.physical() as u32);
self.fb[1].write((fb.physical() >> 32) as u32);
let is = self.is.read();
self.is.write(is);
self.ie.write(0);
let serr = self.serr.read();
self.serr.write(serr);
// Disable power management
let sctl = self.sctl.read() ;
self.sctl.write(sctl | 7 << 8);
// Power on and spin up device
self.cmd.writef(1 << 2 | 1 << 1, true);
print!("{}", format!(" - AHCI init {:X}\n", self.cmd.read()));
}
pub unsafe fn identify(&mut self, clb: &mut Dma<[HbaCmdHeader; 32]>, ctbas: &mut [Dma<HbaCmdTable>; 32]) -> Option<u64> {
self.is.write(u32::MAX);
let dest: Dma<[u16; 256]> = Dma::new([0; 256]).unwrap();
if let Some(slot) = self.slot() {
let cmdheader = &mut clb[slot as usize];
cmdheader.cfl.write(((size_of::<FisRegH2D>() / size_of::<u32>()) as u8));
cmdheader.prdtl.write(1);
{
let cmdtbl = &mut ctbas[slot as usize];
ptr::write_bytes(cmdtbl.deref_mut() as *mut HbaCmdTable as *mut u8, 0, size_of::<HbaCmdTable>());
let prdt_entry = &mut cmdtbl.prdt_entry[0];
prdt_entry.dba.write(dest.physical() as u64);
prdt_entry.dbc.write(512 | 1);
}
{
let cmdfis = &mut *(ctbas[slot as usize].cfis.as_mut_ptr() as *mut FisRegH2D);
cmdfis.fis_type.write(FisType::RegH2D as u8);
cmdfis.pm.write(1 << 7);
cmdfis.command.write(ATA_CMD_IDENTIFY);
cmdfis.device.write(0);
cmdfis.countl.write(1);
cmdfis.counth.write(0);
}
while self.tfd.readf((ATA_DEV_BUSY | ATA_DEV_DRQ) as u32) {
pause();
}
self.ci.writef(1 << slot, true);
self.start();
while (self.ci.readf(1 << slot) || self.tfd.readf(0x80)) && self.is.read() & HBA_PORT_IS_ERR == 0 {
pause();
}
self.stop();
if self.is.read() & HBA_PORT_IS_ERR != 0 {
print!("{}", format!("ERROR IS {:X} TFD {:X} SERR {:X}\n", self.is.read(), self.tfd.read(), self.serr.read()));
return None;
}
let mut serial = String::new();
for word in 10..20 {
let d = dest[word];
let a = ((d >> 8) as u8) as char;
if a != '\0' {
serial.push(a);
}
let b = (d as u8) as char;
if b != '\0' {
serial.push(b);
}
}
let mut firmware = String::new();
for word in 23..27 {
let d = dest[word];
let a = ((d >> 8) as u8) as char;
if a != '\0' {
firmware.push(a);
}
let b = (d as u8) as char;
if b != '\0' {
firmware.push(b);
}
}
let mut model = String::new();
for word in 27..47 {
let d = dest[word];
let a = ((d >> 8) as u8) as char;
if a != '\0' {
model.push(a);
}
let b = (d as u8) as char;
if b != '\0' {
model.push(b);
}
}
let mut sectors = (dest[100] as u64) |
((dest[101] as u64) << 16) |
((dest[102] as u64) << 32) |
((dest[103] as u64) << 48);
let lba_bits = if sectors == 0 {
sectors = (dest[60] as u64) | ((dest[61] as u64) << 16);
28
} else {
48
};
print!("{}", format!(" + Serial: {} Firmware: {} Model: {} {}-bit LBA Size: {} MB\n",
serial.trim(), firmware.trim(), model.trim(), lba_bits, sectors / 2048));
Some(sectors * 512)
} else {
None
}
}
pub fn ata_dma(&mut self, block: u64, sectors: usize, write: bool, clb: &mut Dma<[HbaCmdHeader; 32]>, ctbas: &mut [Dma<HbaCmdTable>; 32], buf: &mut Dma<[u8; 256 * 512]>) -> Result<usize> {
if write {
//print!("{}", format!("AHCI {:X} DMA BLOCK: {:X} SECTORS: {} WRITE: {}\n", (self as *mut HbaPort) as usize, block, sectors, write));
}
assert!(sectors > 0 && sectors < 256);
self.is.write(u32::MAX);
if let Some(slot) = self.slot() {
if write {
//print!("{}", format!("SLOT {}\n", slot));
}
let cmdheader = &mut clb[slot as usize];
cmdheader.cfl.write(((size_of::<FisRegH2D>() / size_of::<u32>()) as u8) | if write { 1 << 7 | 1 << 6 } else { 0 });
cmdheader.prdtl.write(1);
{
let cmdtbl = &mut ctbas[slot as usize];
unsafe { ptr::write_bytes(cmdtbl.deref_mut() as *mut HbaCmdTable as *mut u8, 0, size_of::<HbaCmdTable>()) };
let prdt_entry = &mut cmdtbl.prdt_entry[0];
prdt_entry.dba.write(buf.physical() as u64);
prdt_entry.dbc.write(((sectors * 512) as u32) | 1);
}
{
let cmdfis = unsafe { &mut *(ctbas[slot as usize].cfis.as_mut_ptr() as *mut FisRegH2D) };
cmdfis.fis_type.write(FisType::RegH2D as u8);
cmdfis.pm.write(1 << 7);
if write {
cmdfis.command.write(ATA_CMD_WRITE_DMA_EXT);
} else {
cmdfis.command.write(ATA_CMD_READ_DMA_EXT);
}
cmdfis.lba0.write(block as u8);
cmdfis.lba1.write((block >> 8) as u8);
cmdfis.lba2.write((block >> 16) as u8);
cmdfis.device.write(1 << 6);
cmdfis.lba3.write((block >> 24) as u8);
cmdfis.lba4.write((block >> 32) as u8);
cmdfis.lba5.write((block >> 40) as u8);
cmdfis.countl.write(sectors as u8);
cmdfis.counth.write((sectors >> 8) as u8);
}
if write {
//print!("WAIT ATA_DEV_BUSY | ATA_DEV_DRQ\n");
}
while self.tfd.readf((ATA_DEV_BUSY | ATA_DEV_DRQ) as u32) {
pause();
}
if write {
//print!("{}", format!("WRITE CI {:X} in {:X}\n", 1 << slot, self.ci.read()));
}
self.ci.writef(1 << slot, true);
self.start();
if write {
//print!("{}", format!("WAIT CI {:X} in {:X}\n", 1 << slot, self.ci.read()));
}
while (self.ci.readf(1 << slot) || self.tfd.readf(0x80)) && self.is.read() & HBA_PORT_IS_ERR == 0 {
pause();
if write {
//print!("{}", format!("WAIT CI {:X} TFD {:X} IS {:X} CMD {:X} SERR {:X}\n", self.ci.read(), self.tfd.read(), self.is.read(), self.cmd.read(), self.serr.read()));
}
}
self.stop();
if self.is.read() & HBA_PORT_IS_ERR != 0 {
print!("{}", format!("ERROR IS {:X} IE {:X} CMD {:X} TFD {:X}\nSSTS {:X} SCTL {:X} SERR {:X} SACT {:X}\nCI {:X} SNTF {:X} FBS {:X}\n",
self.is.read(), self.ie.read(), self.cmd.read(), self.tfd.read(),
self.ssts.read(), self.sctl.read(), self.serr.read(), self.sact.read(),
self.ci.read(), self.sntf.read(), self.fbs.read()));
self.is.write(u32::MAX);
return Err(Error::new(EIO));
}
if write {
//print!("{}", format!("SUCCESS {}\n", sectors));
}
Ok(sectors * 512)
} else {
print!("No Command Slots\n");
Err(Error::new(EIO))
}
}
}
#[repr(packed)]
pub struct HbaMem {
pub cap: Mmio<u32>, // 0x00, Host capability
pub ghc: Mmio<u32>, // 0x04, Global host control
pub is: Mmio<u32>, // 0x08, Interrupt status
pub pi: Mmio<u32>, // 0x0C, Port implemented
pub vs: Mmio<u32>, // 0x10, Version
pub ccc_ctl: Mmio<u32>, // 0x14, Command completion coalescing control
pub ccc_pts: Mmio<u32>, // 0x18, Command completion coalescing ports
pub em_loc: Mmio<u32>, // 0x1C, Enclosure management location
pub em_ctl: Mmio<u32>, // 0x20, Enclosure management control
pub cap2: Mmio<u32>, // 0x24, Host capabilities extended
pub bohc: Mmio<u32>, // 0x28, BIOS/OS handoff control and status
pub _rsv: [Mmio<u8>; 116], // 0x2C - 0x9F, Reserved
pub vendor: [Mmio<u8>; 96], // 0xA0 - 0xFF, Vendor specific registers
pub ports: [HbaPort; 32], // 0x100 - 0x10FF, Port control registers
}
impl HbaMem {
pub fn init(&mut self) {
/*
self.ghc.writef(1, true);
while self.ghc.readf(1) {
pause();
}
*/
self.ghc.write(1 << 31 | 1 << 1);
print!("{}", format!(" - AHCI CAP {:X} GHC {:X} IS {:X} PI {:X} VS {:X} CAP2 {:X} BOHC {:X}",
self.cap.read(), self.ghc.read(), self.is.read(), self.pi.read(),
self.vs.read(), self.cap2.read(), self.bohc.read()));
}
}
#[repr(packed)]
pub struct HbaPrdtEntry {
dba: Mmio<u64>, // Data base address
_rsv0: Mmio<u32>, // Reserved
dbc: Mmio<u32>, // Byte count, 4M max, interrupt = 1
}
#[repr(packed)]
pub struct HbaCmdTable {
// 0x00
cfis: [Mmio<u8>; 64], // Command FIS
// 0x40
acmd: [Mmio<u8>; 16], // ATAPI command, 12 or 16 bytes
// 0x50
_rsv: [Mmio<u8>; 48], // Reserved
// 0x80
prdt_entry: [HbaPrdtEntry; 65536], // Physical region descriptor table entries, 0 ~ 65535
}
#[repr(packed)]
pub struct HbaCmdHeader {
// DW0
cfl: Mmio<u8>, /* Command FIS length in DWORDS, 2 ~ 16, atapi: 4, write - host to device: 2, prefetchable: 1 */
pm: Mmio<u8>, // Reset - 0x80, bist: 0x40, clear busy on ok: 0x20, port multiplier
prdtl: Mmio<u16>, // Physical region descriptor table length in entries
// DW1
prdbc: Mmio<u32>, // Physical region descriptor byte count transferred
// DW2, 3
ctba: Mmio<u64>, // Command table descriptor base address
// DW4 - 7
_rsv1: [Mmio<u32>; 4], // Reserved
}

View file

@ -1,35 +0,0 @@
use io::Io;
use self::disk::Disk;
use self::hba::{HbaMem, HbaPortType};
pub mod disk;
pub mod fis;
pub mod hba;
pub fn disks(base: usize, name: &str) -> Vec<Disk> {
unsafe { &mut *(base as *mut HbaMem) }.init();
let pi = unsafe { &mut *(base as *mut HbaMem) }.pi.read();
let ret: Vec<Disk> = (0..32)
.filter(|&i| pi & 1 << i as i32 == 1 << i as i32)
.filter_map(|i| {
let port = &mut unsafe { &mut *(base as *mut HbaMem) }.ports[i];
let port_type = port.probe();
print!("{}", format!("{}-{}: {:?}\n", name, i, port_type));
match port_type {
HbaPortType::SATA => {
match Disk::new(i, port) {
Ok(disk) => Some(disk),
Err(err) => {
print!("{}", format!("{}: {}\n", i, err));
None
}
}
}
_ => None,
}
})
.collect();
ret
}

View file

@ -1,77 +0,0 @@
#![feature(asm)]
#[macro_use]
extern crate bitflags;
extern crate dma;
extern crate io;
extern crate spin;
extern crate syscall;
use std::{env, usize};
use std::fs::File;
use std::io::{Read, Write};
use std::os::unix::io::{AsRawFd, FromRawFd};
use syscall::{EVENT_READ, MAP_WRITE, Event, Packet, Scheme};
use scheme::DiskScheme;
pub mod ahci;
pub mod scheme;
fn main() {
let mut args = env::args().skip(1);
let mut name = args.next().expect("ahcid: no name provided");
name.push_str("_ahci");
let bar_str = args.next().expect("ahcid: no address provided");
let bar = usize::from_str_radix(&bar_str, 16).expect("ahcid: failed to parse address");
let irq_str = args.next().expect("ahcid: no irq provided");
let irq = irq_str.parse::<u8>().expect("ahcid: failed to parse irq");
print!("{}", format!(" + AHCI {} on: {:X} IRQ: {}\n", name, bar, irq));
// Daemonize
if unsafe { syscall::clone(0).unwrap() } == 0 {
let address = unsafe { syscall::physmap(bar, 4096, MAP_WRITE).expect("ahcid: failed to map address") };
{
let socket_fd = syscall::open(":disk", syscall::O_RDWR | syscall::O_CREAT | syscall::O_NONBLOCK).expect("ahcid: failed to create disk scheme");
let mut socket = unsafe { File::from_raw_fd(socket_fd) };
syscall::fevent(socket_fd, EVENT_READ).expect("ahcid: failed to fevent disk scheme");
let mut irq_file = File::open(&format!("irq:{}", irq)).expect("ahcid: failed to open irq file");
let irq_fd = irq_file.as_raw_fd();
syscall::fevent(irq_fd, EVENT_READ).expect("ahcid: failed to fevent irq file");
let mut event_file = File::open("event:").expect("ahcid: failed to open event file");
let scheme = DiskScheme::new(ahci::disks(address, &name));
loop {
let mut event = Event::default();
if event_file.read(&mut event).expect("ahcid: failed to read event file") == 0 {
break;
}
if event.id == socket_fd {
loop {
let mut packet = Packet::default();
if socket.read(&mut packet).expect("ahcid: failed to read disk scheme") == 0 {
break;
}
scheme.handle(&mut packet);
socket.write(&mut packet).expect("ahcid: failed to write disk scheme");
}
} else if event.id == irq_fd {
let mut irq = [0; 8];
if irq_file.read(&mut irq).expect("ahcid: failed to read irq file") >= irq.len() {
//TODO : Test for IRQ
//irq_file.write(&irq).expect("ahcid: failed to write irq file");
}
} else {
println!("Unknown event {}", event.id);
}
}
}
unsafe { let _ = syscall::physunmap(address); }
}
}

View file

@ -1,110 +0,0 @@
use std::collections::BTreeMap;
use std::{cmp, str};
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use spin::Mutex;
use syscall::{Error, EACCES, EBADF, EINVAL, ENOENT, Result, Scheme, Stat, MODE_FILE, SEEK_CUR, SEEK_END, SEEK_SET};
use ahci::disk::Disk;
pub struct DiskScheme {
disks: Box<[Arc<Mutex<Disk>>]>,
handles: Mutex<BTreeMap<usize, (Arc<Mutex<Disk>>, usize)>>,
next_id: AtomicUsize
}
impl DiskScheme {
pub fn new(disks: Vec<Disk>) -> DiskScheme {
let mut disk_arcs = vec![];
for disk in disks {
disk_arcs.push(Arc::new(Mutex::new(disk)));
}
DiskScheme {
disks: disk_arcs.into_boxed_slice(),
handles: Mutex::new(BTreeMap::new()),
next_id: AtomicUsize::new(0)
}
}
}
impl Scheme for DiskScheme {
fn open(&self, path: &[u8], _flags: usize, uid: u32, _gid: u32) -> Result<usize> {
if uid == 0 {
let path_str = str::from_utf8(path).or(Err(Error::new(ENOENT)))?;
let i = path_str.parse::<usize>().or(Err(Error::new(ENOENT)))?;
if let Some(disk) = self.disks.get(i) {
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
self.handles.lock().insert(id, (disk.clone(), 0));
Ok(id)
} else {
Err(Error::new(ENOENT))
}
} else {
Err(Error::new(EACCES))
}
}
fn dup(&self, id: usize, _buf: &[u8]) -> Result<usize> {
let mut handles = self.handles.lock();
let new_handle = {
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
handle.clone()
};
let new_id = self.next_id.fetch_add(1, Ordering::SeqCst);
handles.insert(new_id, new_handle);
Ok(new_id)
}
fn fstat(&self, id: usize, stat: &mut Stat) -> Result<usize> {
let handles = self.handles.lock();
let handle = handles.get(&id).ok_or(Error::new(EBADF))?;
stat.st_mode = MODE_FILE;
stat.st_size = handle.0.lock().size();
Ok(0)
}
fn read(&self, id: usize, buf: &mut [u8]) -> Result<usize> {
let mut handles = self.handles.lock();
let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
let mut disk = handle.0.lock();
let count = disk.read((handle.1 as u64)/512, buf)?;
handle.1 += count;
Ok(count)
}
fn write(&self, id: usize, buf: &[u8]) -> Result<usize> {
let mut handles = self.handles.lock();
let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
let mut disk = handle.0.lock();
let count = disk.write((handle.1 as u64)/512, buf)?;
handle.1 += count;
Ok(count)
}
fn seek(&self, id: usize, pos: usize, whence: usize) -> Result<usize> {
let mut handles = self.handles.lock();
let mut handle = handles.get_mut(&id).ok_or(Error::new(EBADF))?;
let len = handle.0.lock().size() as usize;
handle.1 = match whence {
SEEK_SET => cmp::min(len, pos),
SEEK_CUR => cmp::max(0, cmp::min(len as isize, handle.1 as isize + pos as isize)) as usize,
SEEK_END => cmp::max(0, cmp::min(len as isize, len as isize + pos as isize)) as usize,
_ => return Err(Error::new(EINVAL))
};
Ok(handle.1)
}
fn close(&self, id: usize) -> Result<usize> {
let mut handles = self.handles.lock();
handles.remove(&id).ok_or(Error::new(EBADF)).and(Ok(0))
}
}

Some files were not shown because too many files have changed in this diff Show more