commit
1595cb9ccb
3
.github/ISSUE_TEMPLATE
vendored
3
.github/ISSUE_TEMPLATE
vendored
|
@ -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
6
.gitignore
vendored
|
@ -1,7 +1 @@
|
|||
Cargo.lock
|
||||
build
|
||||
target
|
||||
initfs/bin
|
||||
filesystem/bin
|
||||
filesystem/ref
|
||||
filesystem/sbin
|
||||
|
|
75
.gitmodules
vendored
75
.gitmodules
vendored
|
@ -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
|
||||
|
|
32
.travis.yml
32
.travis.yml
|
@ -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
|
||||
|
|
120
CONTRIBUTING.md
120
CONTRIBUTING.md
|
@ -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,44 +35,52 @@ 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
|
||||
|
@ -78,25 +88,24 @@ It's completely okay to just submit a small pull request without first making an
|
|||
* 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!)
|
||||
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,25 +114,25 @@ 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>
|
||||
|
||||
|
@ -134,7 +143,7 @@ It's completely okay to just submit a small pull request without first making an
|
|||
### <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.
|
||||
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.
|
||||
|
|
37
Cargo.toml
37
Cargo.toml
|
@ -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
646
Makefile
|
@ -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
|
||||
|
|
90
README.md
90
README.md
|
@ -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)).
|
||||
|
||||
[](https://travis-ci.org/redox-os/redox)
|
||||
[](https://github.com/redox-os/redox/releases)
|
||||
[](./LICENSE.md)
|
||||
-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.
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
[package]
|
||||
name = "arch_arm"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
bitflags = "*"
|
||||
hole_list_allocator = { path = "../../crates/hole_list_allocator"}
|
||||
spin = "*"
|
|
@ -1,9 +0,0 @@
|
|||
#[derive(Debug)]
|
||||
pub struct Context;
|
||||
|
||||
impl Context {
|
||||
pub fn new() -> Self {
|
||||
Context
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
|
@ -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() {
|
||||
}
|
|
@ -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;
|
|
@ -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*)
|
||||
}
|
||||
}
|
|
@ -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 {}
|
||||
}
|
|
@ -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 {}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
[package]
|
||||
name = "arch_test"
|
||||
version = "0.1.0"
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
|
@ -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();
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
pub fn monotonic() -> (u64, u64) {
|
||||
(0, 0)
|
||||
}
|
||||
|
||||
pub fn realtime() -> (u64, u64) {
|
||||
(0, 0)
|
||||
}
|
|
@ -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
|
|
@ -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,
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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");
|
||||
}
|
|
@ -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(())
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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(())
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
});
|
|
@ -1,5 +0,0 @@
|
|||
use device::local_apic::LOCAL_APIC;
|
||||
|
||||
interrupt!(ipi, {
|
||||
LOCAL_APIC.eoi();
|
||||
});
|
|
@ -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();
|
||||
});
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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;
|
|
@ -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*)
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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]
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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(); }
|
||||
}
|
||||
}
|
|
@ -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!();
|
||||
}
|
|
@ -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!();
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
1
bootloader
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 59fd6fbcfe9adc7e5d0c0df58fe4e2e07df5f31b
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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"
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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:
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
76
bootstrap.sh
Normal file → Executable file
76
bootstrap.sh
Normal file → Executable file
|
@ -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,7 +163,7 @@ archLinux()
|
|||
fi
|
||||
|
||||
echo "Installing fuse..."
|
||||
sudo pacman -S fuse
|
||||
sudo pacman -S --needed fuse
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
######################################################################
|
||||
|
@ -351,7 +347,7 @@ usage()
|
|||
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
|
||||
}
|
||||
|
||||
|
@ -381,7 +377,7 @@ rustInstall() {
|
|||
# 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): "
|
||||
|
@ -402,7 +398,7 @@ rustInstall() {
|
|||
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
|
||||
fi
|
||||
|
@ -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!"
|
||||
|
@ -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 "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
|
||||
|
|
1
cookbook
Submodule
1
cookbook
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 5f961f888b8cc32266f63713073ad13c1983e3e0
|
|
@ -1,6 +0,0 @@
|
|||
[package]
|
||||
name = "dma"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
redox_syscall = { path = "../../syscall/" }
|
|
@ -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
|
|
@ -1,6 +0,0 @@
|
|||
[package]
|
||||
name = "event"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
redox_syscall = { path = "../../syscall/" }
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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 = "*"
|
|
@ -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
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
[package]
|
||||
name = "io"
|
||||
version = "0.1.0"
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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;
|
|
@ -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) };
|
||||
}
|
||||
}
|
|
@ -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
43
docker/Dockerfile
Executable 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
29
docker/README.md
Normal 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
18
docker/entrypoint.sh
Executable 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 "$@"
|
|
@ -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/" }
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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); }
|
||||
}
|
||||
}
|
|
@ -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
Loading…
Reference in a new issue