mirror of
https://github.com/phil-opp/blog_os.git
synced 2025-12-16 14:27:49 +00:00
Compare commits
409 Commits
rust-wip
...
catching_e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
df3917dfbf | ||
|
|
9d4a761be7 | ||
|
|
43ef84d68a | ||
|
|
b3ddd18e7a | ||
|
|
d905671f45 | ||
|
|
a46b3a13c4 | ||
|
|
7c8f62d312 | ||
|
|
fdec350e85 | ||
|
|
d0bb3f847a | ||
|
|
041aa2bfe6 | ||
|
|
02b8078e1d | ||
|
|
351ae60205 | ||
|
|
b050b475f7 | ||
|
|
29fd8983c5 | ||
|
|
40bc3e9059 | ||
|
|
30a75ec760 | ||
|
|
19f0b5443a | ||
|
|
689bd2e3e2 | ||
|
|
f5af9e25e4 | ||
|
|
0a7843abd9 | ||
|
|
dcab71016c | ||
|
|
3f4f2b8ac2 | ||
|
|
428f97d049 | ||
|
|
69b22d1fc0 | ||
|
|
20f7e5472b | ||
|
|
534d23c80f | ||
|
|
8c8f9dbb68 | ||
|
|
77f3ca3483 | ||
|
|
f58a6fe185 | ||
|
|
c1df1d3354 | ||
|
|
2d15a7bdb1 | ||
|
|
c65b16f42d | ||
|
|
b84c5822df | ||
|
|
245125cb4a | ||
|
|
e2b45d3971 | ||
|
|
865e74bfad | ||
|
|
745b1f6a0b | ||
|
|
3b71e9e5a0 | ||
|
|
e4b42b106f | ||
|
|
08540eb602 | ||
|
|
4633f84af6 | ||
|
|
5054e48c9a | ||
|
|
13e94de7b4 | ||
|
|
8540d3844b | ||
|
|
968ae00de7 | ||
|
|
cbe034f3a5 | ||
|
|
c961fdc32b | ||
|
|
59382699d0 | ||
|
|
74f33c0f44 | ||
|
|
78655d6bef | ||
|
|
431bb39fdb | ||
|
|
7c565abba8 | ||
|
|
a59f51766a | ||
|
|
e383a9235a | ||
|
|
1703827da2 | ||
|
|
a39c59801b | ||
|
|
7170c658a7 | ||
|
|
07d534e4b4 | ||
|
|
a9f41d7303 | ||
|
|
301859a4b9 | ||
|
|
088e87b23a | ||
|
|
0d210e554f | ||
|
|
622c4f0797 | ||
|
|
b633b29b6c | ||
|
|
f1c68fd6dc | ||
|
|
d056a41154 | ||
|
|
2cecc5abeb | ||
|
|
e95710c66e | ||
|
|
1e69a0c627 | ||
|
|
40170d0039 | ||
|
|
192780e219 | ||
|
|
568e95605b | ||
|
|
0fc63576e7 | ||
|
|
fe9b742978 | ||
|
|
aeb3100ee4 | ||
|
|
ed1b23a386 | ||
|
|
bbb77e2f9b | ||
|
|
7b2d40d3f0 | ||
|
|
7b2bdf5c3c | ||
|
|
cdc0b0748b | ||
|
|
814fc5c3c0 | ||
|
|
8651fafb2c | ||
|
|
0d539a6dae | ||
|
|
9e74969732 | ||
|
|
a365d19c46 | ||
|
|
43e73ff050 | ||
|
|
6839321233 | ||
|
|
f2f2568e26 | ||
|
|
2b85f38b41 | ||
|
|
6a45b29560 | ||
|
|
eac60162ad | ||
|
|
fbc1c60164 | ||
|
|
cea73b3ab9 | ||
|
|
9e46fb21c7 | ||
|
|
f25cb599c2 | ||
|
|
c6981c2f24 | ||
|
|
5f84061469 | ||
|
|
05f76f525c | ||
|
|
1d8a36a14b | ||
|
|
03ed3ce9a0 | ||
|
|
23df363136 | ||
|
|
4fcb4b3424 | ||
|
|
c145f9f1d1 | ||
|
|
941b1fab1c | ||
|
|
3b7638bb1d | ||
|
|
6734417e0f | ||
|
|
0aa9b27f9b | ||
|
|
5ee21d43f7 | ||
|
|
635f7d3f9d | ||
|
|
4142cff3e6 | ||
|
|
75aa669cdb | ||
|
|
8b2ec752b7 | ||
|
|
aea5054cf3 | ||
|
|
ac9044bd96 | ||
|
|
d321982755 | ||
|
|
2795b33fd6 | ||
|
|
75bd3c8785 | ||
|
|
d421bac590 | ||
|
|
b092e79495 | ||
|
|
aaffb32bbd | ||
|
|
937224a2c7 | ||
|
|
de2305038a | ||
|
|
bb4d64dc99 | ||
|
|
d6efe62c80 | ||
|
|
c2c863c7f3 | ||
|
|
b9c94baaaa | ||
|
|
cbf4534eca | ||
|
|
7b16d80d98 | ||
|
|
1a886fed5d | ||
|
|
df72565933 | ||
|
|
cb8c438820 | ||
|
|
954ec78a44 | ||
|
|
865dd9cd72 | ||
|
|
23ee000f70 | ||
|
|
49b78f59e2 | ||
|
|
4beecbe138 | ||
|
|
86d27933ff | ||
|
|
1bd054d718 | ||
|
|
5bc31e7c46 | ||
|
|
a6632af0b6 | ||
|
|
1284ea3657 | ||
|
|
db9e6c5739 | ||
|
|
0c45cf1b97 | ||
|
|
59d9a08aea | ||
|
|
16eef8e945 | ||
|
|
9ed358c54c | ||
|
|
e320e4d1d9 | ||
|
|
cd9441fe13 | ||
|
|
555096c953 | ||
|
|
519bb2b385 | ||
|
|
376ab13f1a | ||
|
|
73673f759d | ||
|
|
43a86e7b86 | ||
|
|
3828ba8830 | ||
|
|
a338bf5d98 | ||
|
|
be251361fa | ||
|
|
d57657ac29 | ||
|
|
157b3b208d | ||
|
|
980c94202f | ||
|
|
cfe03f612d | ||
|
|
ac6c1af84f | ||
|
|
1c2e6fca64 | ||
|
|
db928db0b5 | ||
|
|
e2b99ac8c1 | ||
|
|
76d1c41e96 | ||
|
|
b64f980c34 | ||
|
|
051596d8f9 | ||
|
|
33322ec49b | ||
|
|
8e65034643 | ||
|
|
dd51bde9f9 | ||
|
|
fe8c81a531 | ||
|
|
c16b3059cf | ||
|
|
a100d5f63f | ||
|
|
716e3f7359 | ||
|
|
7ce86b3a80 | ||
|
|
a7000e537c | ||
|
|
2198d09695 | ||
|
|
f378e6aed3 | ||
|
|
295ae7ba89 | ||
|
|
9bad9020f6 | ||
|
|
b11ed7bd46 | ||
|
|
5c6630fa97 | ||
|
|
b58bca6fe9 | ||
|
|
350069fd44 | ||
|
|
a9e5b7fd28 | ||
|
|
543f202412 | ||
|
|
6f3daa8a2a | ||
|
|
36cf22895a | ||
|
|
97d381198b | ||
|
|
049fc4d74d | ||
|
|
65d2f7e013 | ||
|
|
b39bb643dd | ||
|
|
d28548fa10 | ||
|
|
d9eff3e50a | ||
|
|
6470e2f56d | ||
|
|
ad8e9da766 | ||
|
|
4aec3b3d78 | ||
|
|
c19d176aa0 | ||
|
|
337799599a | ||
|
|
74e84c7ea4 | ||
|
|
14166b3a35 | ||
|
|
dced423588 | ||
|
|
60eb7ed4a7 | ||
|
|
165e6ebff8 | ||
|
|
52f239e5b4 | ||
|
|
f00366d09f | ||
|
|
de50b37443 | ||
|
|
bb0bd73584 | ||
|
|
6e60552e87 | ||
|
|
3c6057dd76 | ||
|
|
7e83931746 | ||
|
|
7675a08ebe | ||
|
|
22014eb38d | ||
|
|
87b8fc6a29 | ||
|
|
ce5c4b970a | ||
|
|
807e8110be | ||
|
|
c0439cf194 | ||
|
|
26fc76fbda | ||
|
|
953704f103 | ||
|
|
e272cc9257 | ||
|
|
617c92d017 | ||
|
|
5cc6f9ec4b | ||
|
|
3a82ca32cd | ||
|
|
9366db272c | ||
|
|
b3cb09ba8d | ||
|
|
329a64c9a1 | ||
|
|
75264e7cac | ||
|
|
8bb09f47f8 | ||
|
|
cf5ea7664e | ||
|
|
5b4e457439 | ||
|
|
786e1d5cab | ||
|
|
d8c6b6f5b7 | ||
|
|
4fd8891aff | ||
|
|
d52c79b106 | ||
|
|
86d8e99271 | ||
|
|
fb7d2d22b6 | ||
|
|
46b93e0650 | ||
|
|
e84344f59a | ||
|
|
d267ac1c98 | ||
|
|
e5a4114262 | ||
|
|
e071c24680 | ||
|
|
96b0dc0c66 | ||
|
|
14384fb27f | ||
|
|
d827f51bb6 | ||
|
|
dabef43db9 | ||
|
|
9019309d40 | ||
|
|
9f509aec80 | ||
|
|
fd42d634ad | ||
|
|
2b8838459c | ||
|
|
bf6bcdcc82 | ||
|
|
fac58a003a | ||
|
|
57fbd2efc5 | ||
|
|
bd00921dac | ||
|
|
67b30314c4 | ||
|
|
e331a6610b | ||
|
|
15427b363a | ||
|
|
b314d4827f | ||
|
|
6ed376112d | ||
|
|
a279609c26 | ||
|
|
557738e705 | ||
|
|
92194d8354 | ||
|
|
05873d9dae | ||
|
|
c5fac2647e | ||
|
|
5113fce8f7 | ||
|
|
f912dfa506 | ||
|
|
4aeda180a1 | ||
|
|
cdaad86a06 | ||
|
|
c52f58cc4c | ||
|
|
bd05aa7715 | ||
|
|
b1a2c8caad | ||
|
|
7005cd7819 | ||
|
|
e88de41914 | ||
|
|
4a54a24145 | ||
|
|
799067f8b1 | ||
|
|
51f83717d6 | ||
|
|
7e5da6c897 | ||
|
|
2d9b619587 | ||
|
|
6535aa017d | ||
|
|
6c5b932fbc | ||
|
|
44fd5f682d | ||
|
|
a8df7b2e4d | ||
|
|
562221d725 | ||
|
|
b7debed3b7 | ||
|
|
204a9d9c9d | ||
|
|
c7fe1348f0 | ||
|
|
ace53fad91 | ||
|
|
ae49ab5072 | ||
|
|
540c398ec3 | ||
|
|
907f51bb5b | ||
|
|
882af6f14e | ||
|
|
cf5e2715ae | ||
|
|
08a9743caa | ||
|
|
90dba32898 | ||
|
|
6462a3263c | ||
|
|
6b72dd8223 | ||
|
|
f917bd67a1 | ||
|
|
0e574426c5 | ||
|
|
38849516a1 | ||
|
|
f1f1e66dce | ||
|
|
0cfb38ee6e | ||
|
|
2d069d636f | ||
|
|
0a6a174e54 | ||
|
|
9b6bb7f895 | ||
|
|
e52d8bcfd2 | ||
|
|
43924afbbf | ||
|
|
5137ad103a | ||
|
|
84813d9b08 | ||
|
|
cefe95dc88 | ||
|
|
a8600a00b6 | ||
|
|
c71981947b | ||
|
|
4e0071cf3d | ||
|
|
88eb0636f3 | ||
|
|
f4c61f7634 | ||
|
|
0e31d4a221 | ||
|
|
25122bc05c | ||
|
|
fc856a55a6 | ||
|
|
fc55d6dc06 | ||
|
|
eb93c94d6d | ||
|
|
a1d744cf5a | ||
|
|
f08b2cb340 | ||
|
|
ced6edbdd1 | ||
|
|
e380129fde | ||
|
|
f997f1f0f0 | ||
|
|
96cee9ab2b | ||
|
|
5de4255fca | ||
|
|
78c2fd6acc | ||
|
|
28c56f2c06 | ||
|
|
18d397b48d | ||
|
|
a8580ad3a2 | ||
|
|
6ce34f5e2b | ||
|
|
d0cf5e7c61 | ||
|
|
ca07527ede | ||
|
|
cf835e19ae | ||
|
|
939d33efce | ||
|
|
16078431db | ||
|
|
8304439c82 | ||
|
|
75988f1324 | ||
|
|
a2047bc70a | ||
|
|
6f8a21eba6 | ||
|
|
ccaa2ed645 | ||
|
|
718db100cd | ||
|
|
f2b91d3d61 | ||
|
|
fc389c9e9e | ||
|
|
2d1c801c72 | ||
|
|
895d40cd13 | ||
|
|
33fdbce530 | ||
|
|
73aa41e25e | ||
|
|
d07530d660 | ||
|
|
a54b2d8f47 | ||
|
|
c064de5190 | ||
|
|
1f074264c0 | ||
|
|
4d59d7350c | ||
|
|
b8d1dfd84d | ||
|
|
65b4b4f027 | ||
|
|
340e1ff670 | ||
|
|
d082f802f4 | ||
|
|
2a83b3440e | ||
|
|
79bd6873fb | ||
|
|
86d1ff145d | ||
|
|
6d05d4513c | ||
|
|
b9a35036e3 | ||
|
|
e8b6947555 | ||
|
|
6646326fb0 | ||
|
|
8dfc8531e4 | ||
|
|
91f7c70194 | ||
|
|
2691f2b5d7 | ||
|
|
5978234f15 | ||
|
|
753c8d4aea | ||
|
|
5a49d761d9 | ||
|
|
1b2ff0f66e | ||
|
|
7e5dad2277 | ||
|
|
6e93a7d528 | ||
|
|
e759a6826c | ||
|
|
885168bea4 | ||
|
|
fa78bd82a8 | ||
|
|
9986ee22d4 | ||
|
|
2b075af068 | ||
|
|
77373d59b2 | ||
|
|
fa96486353 | ||
|
|
e582b37f19 | ||
|
|
8789fa8531 | ||
|
|
1e7236caf5 | ||
|
|
2a35ec19b6 | ||
|
|
dfabc0ff32 | ||
|
|
e439e9eb5d | ||
|
|
c36a3d19d2 | ||
|
|
54b47aaac1 | ||
|
|
7b0428b07a | ||
|
|
8640091bc6 | ||
|
|
5e5374cd7d | ||
|
|
37b576eb3c | ||
|
|
9ecaf0fe0c | ||
|
|
1e90a1e78d | ||
|
|
34dd42a6fc | ||
|
|
68c0ab0d59 | ||
|
|
f670e330f5 | ||
|
|
a39a611629 | ||
|
|
9cd9ec25db | ||
|
|
3d94304bbb | ||
|
|
95028c0f5d | ||
|
|
5b1f2361f2 | ||
|
|
4d29482952 | ||
|
|
e4a8c4c9fa | ||
|
|
c547b128e5 | ||
|
|
4668acb09e | ||
|
|
ab464b107d | ||
|
|
49fd8926a5 | ||
|
|
3e7fd1b433 | ||
|
|
5bcd6d2c2d |
34
Cargo.toml
34
Cargo.toml
@@ -1,21 +1,29 @@
|
||||
[package]
|
||||
authors = ["Philipp Oppermann <dev@phil-opp.com>"]
|
||||
name = "blog_os"
|
||||
version = "0.1.0"
|
||||
authors = ["Philipp Oppermann <dev@phil-opp.com>"]
|
||||
|
||||
[dependencies]
|
||||
bit_field = "0.7.0"
|
||||
bitflags = "0.7.0"
|
||||
multiboot2 = "0.1.0"
|
||||
once = "0.2.1"
|
||||
rlibc = "0.1.4"
|
||||
spin = "0.3.4"
|
||||
volatile = "0.1.0"
|
||||
|
||||
[dependencies.hole_list_allocator]
|
||||
path = "libs/hole_list_allocator"
|
||||
|
||||
[dependencies.x86]
|
||||
default-features = false
|
||||
version = "0.8.0"
|
||||
|
||||
[lib]
|
||||
crate-type = ["staticlib"]
|
||||
|
||||
[dependencies]
|
||||
rlibc = "*"
|
||||
spin = "*"
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
|
||||
[dependencies.bitflags]
|
||||
git = "https://github.com/phil-opp/bitflags.git"
|
||||
branch = "no_std"
|
||||
|
||||
[dependencies.multiboot2]
|
||||
git = "https://github.com/phil-opp/multiboot2-elf64"
|
||||
|
||||
[dependencies.allocator]
|
||||
path = "src/memory/alloc/allocator"
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
|
||||
9
LICENSE-MIT
Normal file
9
LICENSE-MIT
Normal file
@@ -0,0 +1,9 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Philipp Oppermann
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
38
Makefile
38
Makefile
@@ -1,29 +1,25 @@
|
||||
# Copyright 2015 Philipp Oppermann
|
||||
# Copyright 2016 Philipp Oppermann. See the README.md
|
||||
# file at the top-level directory of this distribution.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
# option. This file may not be copied, modified, or distributed
|
||||
# except according to those terms.
|
||||
|
||||
arch ?= x86_64
|
||||
target ?= $(arch)-unknown-linux-gnu
|
||||
kernel := build/kernel-$(arch).bin
|
||||
iso := build/os-$(arch).iso
|
||||
|
||||
rust_os := target/debug/libblog_os.a
|
||||
rust_os := target/$(target)/debug/libblog_os.a
|
||||
linker_script := src/arch/$(arch)/linker.ld
|
||||
grub_cfg := src/arch/$(arch)/grub.cfg
|
||||
assembly_source_files := $(wildcard src/arch/$(arch)/*.asm)
|
||||
assembly_object_files := $(patsubst src/arch/$(arch)/%.asm, \
|
||||
build/arch/$(arch)/%.o, $(assembly_source_files))
|
||||
|
||||
.PHONY: all clean run iso cargo
|
||||
.PHONY: all clean run debug iso cargo gdb
|
||||
|
||||
all: $(kernel)
|
||||
|
||||
@@ -32,13 +28,19 @@ clean:
|
||||
@rm -rf build
|
||||
|
||||
run: $(iso)
|
||||
@qemu-system-x86_64 -s -hda $(iso)
|
||||
@qemu-system-x86_64 -cdrom $(iso) -s
|
||||
|
||||
debug: $(iso)
|
||||
@qemu-system-x86_64 -cdrom $(iso) -s -S
|
||||
|
||||
gdb:
|
||||
@rust-os-gdb/bin/rust-gdb "build/kernel-x86_64.bin" -ex "target remote :1234"
|
||||
|
||||
iso: $(iso)
|
||||
|
||||
$(iso): $(kernel)
|
||||
$(iso): $(kernel) $(grub_cfg)
|
||||
@mkdir -p build/isofiles/boot/grub
|
||||
@cp $(kernel) build/isofiles/boot/
|
||||
@cp $(kernel) build/isofiles/boot/kernel.bin
|
||||
@cp $(grub_cfg) build/isofiles/boot/grub
|
||||
@grub-mkrescue -o $(iso) build/isofiles 2> /dev/null
|
||||
@rm -r build/isofiles
|
||||
@@ -47,7 +49,7 @@ $(kernel): cargo $(rust_os) $(assembly_object_files) $(linker_script)
|
||||
@ld -n --gc-sections -T $(linker_script) -o $(kernel) $(assembly_object_files) $(rust_os)
|
||||
|
||||
cargo:
|
||||
@cargo rustc -- -Z no-landing-pads
|
||||
@cargo build --target $(target)
|
||||
|
||||
# compile assembly files
|
||||
build/arch/$(arch)/%.o: src/arch/$(arch)/%.asm
|
||||
|
||||
15
README.md
15
README.md
@@ -1 +1,14 @@
|
||||
# blogOS
|
||||
# Blog OS (Catching Exceptions)
|
||||
[](https://travis-ci.org/phil-opp/blog_os/branches)
|
||||
|
||||
This repository contains the source code for the [Catching Exceptions](http://os.phil-opp.com/catching-exceptions.html) post of the [Writing an OS in Rust](http://os.phil-opp.com) series.
|
||||
|
||||
**Check out the [master branch](https://github.com/phil-opp/blog_os) for more information.**
|
||||
|
||||
## Building
|
||||
You need to have `nasm`, `grub-mkrescue`, `xorriso`, `qemu`, and a nightly Rust compiler installed. Then you can run it using `make run`.
|
||||
|
||||
Please file an issue if you have any problems.
|
||||
|
||||
## License
|
||||
The source code is dual-licensed under MIT or the Apache License (Version 2.0).
|
||||
|
||||
2
libs/bump_allocator/.gitignore
vendored
Normal file
2
libs/bump_allocator/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Generated by Cargo
|
||||
/target/
|
||||
@@ -1,4 +1,7 @@
|
||||
[package]
|
||||
name = "allocator"
|
||||
version = "0.1.0"
|
||||
authors = ["Philipp Oppermann <dev@phil-opp.com>"]
|
||||
name = "bump_allocator"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
spin = "0.3.5"
|
||||
114
libs/bump_allocator/src/lib.rs
Normal file
114
libs/bump_allocator/src/lib.rs
Normal file
@@ -0,0 +1,114 @@
|
||||
// Copyright 2016 Philipp Oppermann. See the README.md
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(const_fn)]
|
||||
#![feature(allocator)]
|
||||
|
||||
#![allocator]
|
||||
#![no_std]
|
||||
|
||||
use spin::Mutex;
|
||||
|
||||
extern crate spin;
|
||||
|
||||
pub const HEAP_START: usize = 0o_000_001_000_000_0000;
|
||||
pub const HEAP_SIZE: usize = 100 * 1024; // 100 KiB
|
||||
|
||||
static BUMP_ALLOCATOR: Mutex<BumpAllocator> = Mutex::new(
|
||||
BumpAllocator::new(HEAP_START, HEAP_SIZE));
|
||||
|
||||
#[derive(Debug)]
|
||||
struct BumpAllocator {
|
||||
heap_start: usize,
|
||||
heap_size: usize,
|
||||
next: usize,
|
||||
}
|
||||
|
||||
impl BumpAllocator {
|
||||
/// Create a new allocator, which uses the memory in the
|
||||
/// range [heap_start, heap_start + heap_size).
|
||||
const fn new(heap_start: usize, heap_size: usize) -> BumpAllocator {
|
||||
BumpAllocator {
|
||||
heap_start: heap_start,
|
||||
heap_size: heap_size,
|
||||
next: heap_start,
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates a block of memory with the given size and alignment.
|
||||
fn allocate(&mut self, size: usize, align: usize) -> Option<*mut u8> {
|
||||
let alloc_start = align_up(self.next, align);
|
||||
let alloc_end = alloc_start.saturating_add(size);
|
||||
|
||||
if alloc_end <= self.heap_start + self.heap_size {
|
||||
self.next = alloc_end;
|
||||
Some(alloc_start as *mut u8)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Align downwards. Returns the greatest x with alignment `align`
|
||||
/// so that x <= addr. The alignment must be a power of 2.
|
||||
pub fn align_down(addr: usize, align: usize) -> usize {
|
||||
if align.is_power_of_two() {
|
||||
addr & !(align - 1)
|
||||
} else if align == 0 {
|
||||
addr
|
||||
} else {
|
||||
panic!("`align` must be a power of 2");
|
||||
}
|
||||
}
|
||||
|
||||
/// Align upwards. Returns the smallest x with alignment `align`
|
||||
/// so that x >= addr. The alignment must be a power of 2.
|
||||
pub fn align_up(addr: usize, align: usize) -> usize {
|
||||
align_down(addr + align - 1, align)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn __rust_allocate(size: usize, align: usize) -> *mut u8 {
|
||||
BUMP_ALLOCATOR.lock().allocate(size, align).expect("out of memory")
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn __rust_deallocate(_ptr: *mut u8, _size: usize,
|
||||
_align: usize)
|
||||
{
|
||||
// just leak it
|
||||
}
|
||||
|
||||
#[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
|
||||
}
|
||||
|
||||
2
libs/hole_list_allocator/.gitignore
vendored
Normal file
2
libs/hole_list_allocator/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
# Generated by Cargo
|
||||
/target/
|
||||
12
libs/hole_list_allocator/Cargo.toml
Normal file
12
libs/hole_list_allocator/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
authors = ["Philipp Oppermann <dev@phil-opp.com>"]
|
||||
name = "hole_list_allocator"
|
||||
version = "0.1.0"
|
||||
|
||||
[dependencies]
|
||||
linked_list_allocator = "0.2.0"
|
||||
spin = "0.3.5"
|
||||
|
||||
[dependencies.lazy_static]
|
||||
version = "0.2.1"
|
||||
features = ["spin_no_std"]
|
||||
68
libs/hole_list_allocator/src/lib.rs
Normal file
68
libs/hole_list_allocator/src/lib.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright 2016 Philipp Oppermann. See the README.md
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(allocator)]
|
||||
#![feature(const_fn)]
|
||||
|
||||
#![allocator]
|
||||
#![no_std]
|
||||
|
||||
use spin::Mutex;
|
||||
use linked_list_allocator::Heap;
|
||||
|
||||
extern crate spin;
|
||||
extern crate linked_list_allocator;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
|
||||
pub const HEAP_START: usize = 0o_000_001_000_000_0000;
|
||||
pub const HEAP_SIZE: usize = 100 * 1024; // 100 KiB
|
||||
|
||||
lazy_static! {
|
||||
static ref HEAP: Mutex<Heap> = Mutex::new(unsafe {
|
||||
Heap::new(HEAP_START, HEAP_SIZE)
|
||||
});
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn __rust_allocate(size: usize, align: usize) -> *mut u8 {
|
||||
HEAP.lock().allocate_first_fit(size, align).expect("out of memory")
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn __rust_deallocate(ptr: *mut u8, size: usize, align: usize) {
|
||||
unsafe { HEAP.lock().deallocate(ptr, size, align) };
|
||||
}
|
||||
|
||||
#[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,16 +1,11 @@
|
||||
; Copyright 2015 Philipp Oppermann
|
||||
; Copyright 2016 Philipp Oppermann. See the README.md
|
||||
; file at the top-level directory of this distribution.
|
||||
;
|
||||
; Licensed under the Apache License, Version 2.0 (the "License");
|
||||
; you may not use this file except in compliance with the License.
|
||||
; You may obtain a copy of the License at
|
||||
;
|
||||
; http://www.apache.org/licenses/LICENSE-2.0
|
||||
;
|
||||
; Unless required by applicable law or agreed to in writing, software
|
||||
; distributed under the License is distributed on an "AS IS" BASIS,
|
||||
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
; See the License for the specific language governing permissions and
|
||||
; limitations under the License.
|
||||
; Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
; http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
; <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
; option. This file may not be copied, modified, or distributed
|
||||
; except according to those terms.
|
||||
|
||||
global start
|
||||
extern long_mode_start
|
||||
@@ -19,39 +14,51 @@ section .text
|
||||
bits 32
|
||||
start:
|
||||
mov esp, stack_top
|
||||
; Move Multiboot info pointer to edi to pass it to the kernel. We must not
|
||||
; modify the `edi` register until the kernel it called.
|
||||
mov edi, ebx
|
||||
|
||||
call check_multiboot
|
||||
call check_cpuid
|
||||
call check_long_mode
|
||||
|
||||
call setup_page_tables
|
||||
call set_up_page_tables
|
||||
call enable_paging
|
||||
call set_up_SSE
|
||||
|
||||
; load the 64-bit GDT
|
||||
lgdt [gdt64.pointer]
|
||||
|
||||
; update selectors
|
||||
mov ax, gdt64.data
|
||||
mov ss, ax
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
|
||||
jmp gdt64.code:long_mode_start
|
||||
|
||||
setup_page_tables:
|
||||
set_up_page_tables:
|
||||
; recursive map P4
|
||||
mov eax, p4_table
|
||||
or eax, 0b11 ; present + writable
|
||||
mov [p4_table + 511 * 8], eax
|
||||
|
||||
; map first P4 entry to P3 table
|
||||
mov eax, p3_table
|
||||
or eax, 0b11 ; present + writable
|
||||
mov [p4_table], eax
|
||||
|
||||
; map first P3 entry to a huge page that starts at address 0
|
||||
mov dword [p3_table], 0b10000011 ; present + writable + huge
|
||||
|
||||
; recursive map P4
|
||||
mov eax, p4_table
|
||||
; map first P3 entry to P2 table
|
||||
mov eax, p2_table
|
||||
or eax, 0b11 ; present + writable
|
||||
mov [p4_table + 511 * 8], eax
|
||||
mov [p3_table], eax
|
||||
|
||||
; map each P2 entry to a huge 2MiB page
|
||||
mov ecx, 0 ; counter variable
|
||||
.map_p2_table:
|
||||
; map ecx-th P2 entry to a huge page that starts at address (2MiB * ecx)
|
||||
mov eax, 0x200000 ; 2MiB
|
||||
mul ecx ; start address of ecx-th page
|
||||
or eax, 0b10000011 ; present + writable + huge
|
||||
mov [p2_table + ecx * 8], eax ; map ecx-th entry
|
||||
|
||||
inc ecx ; increase counter
|
||||
cmp ecx, 512 ; if counter == 512, the whole P2 table is mapped
|
||||
jne .map_p2_table ; else map the next entry
|
||||
|
||||
ret
|
||||
|
||||
@@ -68,14 +75,12 @@ enable_paging:
|
||||
; set the long mode bit in the EFER MSR (model specific register)
|
||||
mov ecx, 0xC0000080
|
||||
rdmsr
|
||||
or eax, 1 << 8 ; enable long mode
|
||||
or eax, 1 << 11 ; enable no-execute bit in page tables
|
||||
or eax, 1 << 8
|
||||
wrmsr
|
||||
|
||||
; enable paging in the cr0 register
|
||||
mov eax, cr0
|
||||
or eax, 1 << 31
|
||||
or eax, 1 << 16
|
||||
mov cr0, eax
|
||||
|
||||
ret
|
||||
@@ -100,57 +105,98 @@ check_multiboot:
|
||||
|
||||
; Throw error 1 if the CPU doesn't support the CPUID command.
|
||||
check_cpuid:
|
||||
pushfd ; Store the FLAGS-register.
|
||||
pop eax ; Restore the A-register.
|
||||
mov ecx, eax ; Set the C-register to the A-register.
|
||||
xor eax, 1 << 21 ; Flip the ID-bit, which is bit 21.
|
||||
push eax ; Store the A-register.
|
||||
popfd ; Restore the FLAGS-register.
|
||||
pushfd ; Store the FLAGS-register.
|
||||
pop eax ; Restore the A-register.
|
||||
push ecx ; Store the C-register.
|
||||
popfd ; Restore the FLAGS-register.
|
||||
xor eax, ecx ; Do a XOR-operation on the A-register and the C-register.
|
||||
jz .no_cpuid ; The zero flag is set, no CPUID.
|
||||
ret ; CPUID is available for use.
|
||||
; Check if CPUID is supported by attempting to flip the ID bit (bit 21) in
|
||||
; the FLAGS register. If we can flip it, CPUID is available.
|
||||
|
||||
; Copy FLAGS in to EAX via stack
|
||||
pushfd
|
||||
pop eax
|
||||
|
||||
; Copy to ECX as well for comparing later on
|
||||
mov ecx, eax
|
||||
|
||||
; Flip the ID bit
|
||||
xor eax, 1 << 21
|
||||
|
||||
; Copy EAX to FLAGS via the stack
|
||||
push eax
|
||||
popfd
|
||||
|
||||
; Copy FLAGS back to EAX (with the flipped bit if CPUID is supported)
|
||||
pushfd
|
||||
pop eax
|
||||
|
||||
; Restore FLAGS from the old version stored in ECX (i.e. flipping the ID bit
|
||||
; back if it was ever flipped).
|
||||
push ecx
|
||||
popfd
|
||||
|
||||
; Compare EAX and ECX. If they are equal then that means the bit wasn't
|
||||
; flipped, and CPUID isn't supported.
|
||||
cmp eax, ecx
|
||||
je .no_cpuid
|
||||
ret
|
||||
.no_cpuid:
|
||||
mov al, "1"
|
||||
jmp error
|
||||
|
||||
; Throw error 2 if the CPU doesn't support Long Mode.
|
||||
check_long_mode:
|
||||
mov eax, 0x80000000 ; Set the A-register to 0x80000000.
|
||||
cpuid ; CPU identification.
|
||||
cmp eax, 0x80000001 ; Compare the A-register with 0x80000001.
|
||||
jb .no_long_mode ; It is less, there is no long mode.
|
||||
mov eax, 0x80000000 ; Set the A-register to 0x80000000.
|
||||
cpuid ; CPU identification.
|
||||
cmp eax, 0x80000001 ; Compare the A-register with 0x80000001.
|
||||
jb .no_long_mode ; It is less, there is no long mode.
|
||||
; test if extended processor info in available
|
||||
mov eax, 0x80000000 ; implicit argument for cpuid
|
||||
cpuid ; get highest supported argument
|
||||
cmp eax, 0x80000001 ; it needs to be at least 0x80000001
|
||||
jb .no_long_mode ; if it's less, the CPU is too old for long mode
|
||||
|
||||
; use extended info to test if long mode is available
|
||||
mov eax, 0x80000001 ; argument for extended processor info
|
||||
cpuid ; returns various feature bits in ecx and edx
|
||||
test edx, 1 << 29 ; test if the LM-bit is set in the D-register
|
||||
jz .no_long_mode ; If it's not set, there is no long mode
|
||||
ret
|
||||
.no_long_mode:
|
||||
mov al, "2"
|
||||
jmp error
|
||||
|
||||
; Check for SSE and enable it. If it's not supported throw error "a".
|
||||
set_up_SSE:
|
||||
; check for SSE
|
||||
mov eax, 0x1
|
||||
cpuid
|
||||
test edx, 1<<25
|
||||
jz .no_SSE
|
||||
|
||||
; enable SSE
|
||||
mov eax, cr0
|
||||
and ax, 0xFFFB ; clear coprocessor emulation CR0.EM
|
||||
or ax, 0x2 ; set coprocessor monitoring CR0.MP
|
||||
mov cr0, eax
|
||||
mov eax, cr4
|
||||
or ax, 3 << 9 ; set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
|
||||
mov cr4, eax
|
||||
|
||||
ret
|
||||
.no_SSE:
|
||||
mov al, "a"
|
||||
jmp error
|
||||
|
||||
section .bss
|
||||
align 4096
|
||||
p4_table:
|
||||
resb 4096
|
||||
p3_table:
|
||||
resb 4096
|
||||
stack_bottom:
|
||||
; TODO a >= 80 byte stack is enough. Theoretically we could use the memory
|
||||
; of the p3 table as a hack (it won't override the important first entry)
|
||||
p2_table:
|
||||
resb 4096
|
||||
stack_bottom:
|
||||
resb 4096 * 2
|
||||
stack_top:
|
||||
|
||||
section .rodata
|
||||
gdt64:
|
||||
dq 0 ; zero entry
|
||||
.code: equ $ - gdt64 ; new
|
||||
dq (1<<44) | (1<<47) | (1<<41) | (1<<43) | (1<<53) ; code segment
|
||||
.data: equ $ - gdt64 ; new
|
||||
dq (1<<44) | (1<<47) | (1<<41) ; data segment
|
||||
dq (1<<44) | (1<<47) | (1<<43) | (1<<53) ; code segment
|
||||
.pointer:
|
||||
dw $ - gdt64 - 1
|
||||
dq gdt64
|
||||
|
||||
@@ -1,21 +1,16 @@
|
||||
# Copyright 2015 Philipp Oppermann
|
||||
# Copyright 2016 Philipp Oppermann. See the README.md
|
||||
# file at the top-level directory of this distribution.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License")
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
# Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
# http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
# <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
# option. This file may not be copied, modified, or distributed
|
||||
# except according to those terms.
|
||||
|
||||
set timeout=0
|
||||
set default=0
|
||||
|
||||
menuentry "my os" {
|
||||
multiboot2 /boot/kernel-x86_64.bin
|
||||
multiboot2 /boot/kernel.bin
|
||||
boot
|
||||
}
|
||||
|
||||
@@ -1,17 +1,12 @@
|
||||
/*
|
||||
Copyright 2015 Philipp Oppermann
|
||||
Copyright 2016 Philipp Oppermann. See the README.md
|
||||
file at the top-level directory of this distribution.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License")
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
<LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
option. This file may not be copied, modified, or distributed
|
||||
except according to those terms.
|
||||
*/
|
||||
|
||||
ENTRY(start)
|
||||
@@ -19,26 +14,51 @@ ENTRY(start)
|
||||
SECTIONS {
|
||||
. = 1M;
|
||||
|
||||
.text BLOCK(4k) : {
|
||||
.rodata :
|
||||
{
|
||||
/* ensure that the multiboot header is at the beginning */
|
||||
KEEP(*(.multiboot))
|
||||
*(.text .text.*)
|
||||
}
|
||||
|
||||
.rodata BLOCK(4k) : {
|
||||
KEEP(*(.multiboot_header))
|
||||
*(.rodata .rodata.*)
|
||||
. = ALIGN(4K);
|
||||
}
|
||||
|
||||
.data BLOCK(4k) : {
|
||||
.text :
|
||||
{
|
||||
*(.text .text.*)
|
||||
. = ALIGN(4K);
|
||||
}
|
||||
|
||||
.data :
|
||||
{
|
||||
*(.data .data.*)
|
||||
. = ALIGN(4K);
|
||||
}
|
||||
|
||||
.data.rel.ro BLOCK(4k) : {
|
||||
.bss :
|
||||
{
|
||||
*(.bss .bss.*)
|
||||
. = ALIGN(4K);
|
||||
}
|
||||
|
||||
.got :
|
||||
{
|
||||
*(.got)
|
||||
. = ALIGN(4K);
|
||||
}
|
||||
|
||||
.got.plt :
|
||||
{
|
||||
*(.got.plt)
|
||||
. = ALIGN(4K);
|
||||
}
|
||||
|
||||
.data.rel.ro : ALIGN(4K) {
|
||||
*(.data.rel.ro.local*) *(.data.rel.ro .data.rel.ro.*)
|
||||
. = ALIGN(4K);
|
||||
}
|
||||
|
||||
.gcc_except_table BLOCK(4k) : {
|
||||
*(.gcc_except_table .gcc_except_table.*)
|
||||
.gcc_except_table : ALIGN(4K) {
|
||||
*(.gcc_except_table)
|
||||
. = ALIGN(4K);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
; Copyright 2015 Philipp Oppermann
|
||||
; Copyright 2016 Philipp Oppermann. See the README.md
|
||||
; file at the top-level directory of this distribution.
|
||||
;
|
||||
; Licensed under the Apache License, Version 2.0 (the "License");
|
||||
; you may not use this file except in compliance with the License.
|
||||
; You may obtain a copy of the License at
|
||||
;
|
||||
; http://www.apache.org/licenses/LICENSE-2.0
|
||||
;
|
||||
; Unless required by applicable law or agreed to in writing, software
|
||||
; distributed under the License is distributed on an "AS IS" BASIS,
|
||||
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
; See the License for the specific language governing permissions and
|
||||
; limitations under the License.
|
||||
; Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
; http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
; <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
; option. This file may not be copied, modified, or distributed
|
||||
; except according to those terms.
|
||||
|
||||
global long_mode_start
|
||||
extern rust_main
|
||||
@@ -18,7 +13,13 @@ extern rust_main
|
||||
section .text
|
||||
bits 64
|
||||
long_mode_start:
|
||||
call setup_SSE
|
||||
; load 0 into all data segment registers
|
||||
mov ax, 0
|
||||
mov ss, ax
|
||||
mov ds, ax
|
||||
mov es, ax
|
||||
mov fs, ax
|
||||
mov gs, ax
|
||||
|
||||
; call rust main (with multiboot pointer in rdi)
|
||||
call rust_main
|
||||
@@ -31,36 +32,3 @@ long_mode_start:
|
||||
mov rax, 0x4f214f644f654f6e
|
||||
mov [0xb8010], rax
|
||||
hlt
|
||||
|
||||
; Check for SSE and enable it. If it's not supported throw error "a".
|
||||
setup_SSE:
|
||||
; check for SSE
|
||||
mov rax, 0x1
|
||||
cpuid
|
||||
test edx, 1<<25
|
||||
jz .no_SSE
|
||||
|
||||
; enable SSE
|
||||
mov rax, cr0
|
||||
and ax, 0xFFFB ; clear coprocessor emulation CR0.EM
|
||||
or ax, 0x2 ; set coprocessor monitoring CR0.MP
|
||||
mov cr0, rax
|
||||
mov rax, cr4
|
||||
or ax, 3 << 9 ; set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time
|
||||
mov cr4, rax
|
||||
|
||||
ret
|
||||
.no_SSE:
|
||||
mov al, "a"
|
||||
jmp error
|
||||
|
||||
; Prints `ERROR: ` and the given error code to screen and hangs.
|
||||
; parameter: error code (in ascii) in al
|
||||
error:
|
||||
mov rbx, 0x4f4f4f524f524f45
|
||||
mov [0xb8000], rbx
|
||||
mov rbx, 0x4f204f204f3a4f52
|
||||
mov [0xb8008], rbx
|
||||
mov byte [0xb800e], al
|
||||
hlt
|
||||
jmp error
|
||||
|
||||
@@ -1,18 +1,13 @@
|
||||
; Copyright 2015 Philipp Oppermann
|
||||
; Copyright 2016 Philipp Oppermann. See the README.md
|
||||
; file at the top-level directory of this distribution.
|
||||
;
|
||||
; Licensed under the Apache License, Version 2.0 (the "License");
|
||||
; you may not use this file except in compliance with the License.
|
||||
; You may obtain a copy of the License at
|
||||
;
|
||||
; http://www.apache.org/licenses/LICENSE-2.0
|
||||
;
|
||||
; Unless required by applicable law or agreed to in writing, software
|
||||
; distributed under the License is distributed on an "AS IS" BASIS,
|
||||
; WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
; See the License for the specific language governing permissions and
|
||||
; limitations under the License.
|
||||
; Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
; http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
; <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
; option. This file may not be copied, modified, or distributed
|
||||
; except according to those terms.
|
||||
|
||||
section .multiboot
|
||||
section .multiboot_header
|
||||
header_start:
|
||||
dd 0xe85250d6 ; magic number (multiboot 2)
|
||||
dd 0 ; architecture 0 (protected mode i386)
|
||||
|
||||
113
src/interrupts/idt.rs
Normal file
113
src/interrupts/idt.rs
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright 2016 Philipp Oppermann. See the README.md
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use x86::shared::segmentation::{self, SegmentSelector};
|
||||
use x86::shared::PrivilegeLevel;
|
||||
|
||||
pub struct Idt([Entry; 16]);
|
||||
|
||||
impl Idt {
|
||||
pub fn new() -> Idt {
|
||||
Idt([Entry::missing(); 16])
|
||||
}
|
||||
|
||||
pub fn set_handler(&mut self, entry: u8, handler: HandlerFunc) -> &mut EntryOptions {
|
||||
self.0[entry as usize] = Entry::new(segmentation::cs(), handler);
|
||||
&mut self.0[entry as usize].options
|
||||
}
|
||||
|
||||
pub fn load(&'static self) {
|
||||
use x86::shared::dtables::{DescriptorTablePointer, lidt};
|
||||
use core::mem::size_of;
|
||||
|
||||
let ptr = DescriptorTablePointer {
|
||||
base: self as *const _ as *const ::x86::bits64::irq::IdtEntry,
|
||||
limit: (size_of::<Self>() - 1) as u16,
|
||||
};
|
||||
|
||||
unsafe { lidt(&ptr) };
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(C, packed)]
|
||||
pub struct Entry {
|
||||
pointer_low: u16,
|
||||
gdt_selector: SegmentSelector,
|
||||
options: EntryOptions,
|
||||
pointer_middle: u16,
|
||||
pointer_high: u32,
|
||||
reserved: u32,
|
||||
}
|
||||
|
||||
pub type HandlerFunc = extern "C" fn() -> !;
|
||||
|
||||
impl Entry {
|
||||
fn new(gdt_selector: SegmentSelector, handler: HandlerFunc) -> Self {
|
||||
let pointer = handler as u64;
|
||||
Entry {
|
||||
gdt_selector: gdt_selector,
|
||||
pointer_low: pointer as u16,
|
||||
pointer_middle: (pointer >> 16) as u16,
|
||||
pointer_high: (pointer >> 32) as u32,
|
||||
options: EntryOptions::new(),
|
||||
reserved: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn missing() -> Self {
|
||||
Entry {
|
||||
gdt_selector: SegmentSelector::new(0, PrivilegeLevel::Ring0),
|
||||
pointer_low: 0,
|
||||
pointer_middle: 0,
|
||||
pointer_high: 0,
|
||||
options: EntryOptions::minimal(),
|
||||
reserved: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
use bit_field::BitField;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct EntryOptions(u16);
|
||||
|
||||
impl EntryOptions {
|
||||
fn minimal() -> Self {
|
||||
let mut options = 0;
|
||||
options.set_bits(9..12, 0b111); // 'must-be-one' bits
|
||||
EntryOptions(options)
|
||||
}
|
||||
|
||||
fn new() -> Self {
|
||||
let mut options = Self::minimal();
|
||||
options.set_present(true).disable_interrupts(true);
|
||||
options
|
||||
}
|
||||
|
||||
pub fn set_present(&mut self, present: bool) -> &mut Self {
|
||||
self.0.set_bit(15, present);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn disable_interrupts(&mut self, disable: bool) -> &mut Self {
|
||||
self.0.set_bit(8, !disable);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_privilege_level(&mut self, dpl: u16) -> &mut Self {
|
||||
self.0.set_bits(13..15, dpl);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn set_stack_index(&mut self, index: u16) -> &mut Self {
|
||||
self.0.set_bits(0..3, index);
|
||||
self
|
||||
}
|
||||
}
|
||||
29
src/interrupts/mod.rs
Normal file
29
src/interrupts/mod.rs
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2016 Philipp Oppermann. See the README.md
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
mod idt;
|
||||
|
||||
lazy_static! {
|
||||
static ref IDT: idt::Idt = {
|
||||
let mut idt = idt::Idt::new();
|
||||
|
||||
idt.set_handler(0, divide_by_zero_handler);
|
||||
|
||||
idt
|
||||
};
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
IDT.load();
|
||||
}
|
||||
|
||||
extern "C" fn divide_by_zero_handler() -> ! {
|
||||
println!("EXCEPTION: DIVIDE BY ZERO");
|
||||
loop {}
|
||||
}
|
||||
104
src/lib.rs
104
src/lib.rs
@@ -1,70 +1,98 @@
|
||||
// Copyright 2015 Philipp Oppermann
|
||||
// Copyright 2016 Philipp Oppermann. See the README.md
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
#![feature(no_std, lang_items, asm)]
|
||||
#![feature(core_str_ext, const_fn, range_inclusive)]
|
||||
#![feature(unique, core_intrinsics, alloc)]
|
||||
#![feature(box_syntax)]
|
||||
#![feature(lang_items)]
|
||||
#![feature(const_fn, unique)]
|
||||
#![feature(alloc, collections)]
|
||||
#![feature(asm)]
|
||||
#![no_std]
|
||||
|
||||
extern crate rlibc;
|
||||
extern crate alloc;
|
||||
extern crate allocator;
|
||||
extern crate volatile;
|
||||
extern crate spin;
|
||||
extern crate multiboot2;
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
extern crate spin;
|
||||
extern crate x86;
|
||||
#[macro_use]
|
||||
extern crate once;
|
||||
extern crate bit_field;
|
||||
|
||||
use core::fmt::Write;
|
||||
extern crate hole_list_allocator;
|
||||
extern crate alloc;
|
||||
#[macro_use]
|
||||
extern crate collections;
|
||||
|
||||
#[macro_use]
|
||||
mod vga_buffer;
|
||||
|
||||
mod memory;
|
||||
|
||||
mod interrupts;
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn rust_main(multiboot_address: usize) {
|
||||
pub extern "C" fn rust_main(multiboot_information_address: usize) {
|
||||
// ATTENTION: we have a very small stack and no guard page
|
||||
use vga_buffer::{Writer, Color};
|
||||
use alloc::boxed::Box;
|
||||
|
||||
vga_buffer::clear_screen();
|
||||
let multiboot = unsafe{multiboot2::load(multiboot_address)};
|
||||
memory::init(multiboot);
|
||||
println!("Hello World{}", "!");
|
||||
|
||||
let mut writer = Writer::new(Color::Blue, Color::LightGreen);
|
||||
writer.write_byte(b'H');
|
||||
let _ = writer.write_str("ello! ");
|
||||
let _ = write!(writer, "The numbers are {} and {}", 42, 1.0/3.0);
|
||||
println!("");
|
||||
println!("{} {}", "line", 1);
|
||||
print!("line {}", 2);
|
||||
let boot_info = unsafe { multiboot2::load(multiboot_information_address) };
|
||||
enable_nxe_bit();
|
||||
enable_write_protect_bit();
|
||||
|
||||
Box::new(42);
|
||||
box [42; 25000000];
|
||||
// set up guard page and map the heap pages
|
||||
memory::init(boot_info);
|
||||
|
||||
// initialize our IDT
|
||||
interrupts::init();
|
||||
|
||||
fn divide_by_zero() {
|
||||
unsafe { asm!("mov dx, 0; div dx" ::: "ax", "dx" : "volatile", "intel") }
|
||||
}
|
||||
|
||||
// provoke a divide by zero fault inside println
|
||||
println!("{:?}", divide_by_zero());
|
||||
|
||||
println!("It did not crash!");
|
||||
loop {}
|
||||
}
|
||||
|
||||
fn enable_nxe_bit() {
|
||||
use x86::shared::msr::{IA32_EFER, rdmsr, wrmsr};
|
||||
|
||||
let nxe_bit = 1 << 11;
|
||||
unsafe {
|
||||
let efer = rdmsr(IA32_EFER);
|
||||
wrmsr(IA32_EFER, efer | nxe_bit);
|
||||
}
|
||||
}
|
||||
|
||||
fn enable_write_protect_bit() {
|
||||
use x86::shared::control_regs::{cr0, cr0_write, CR0_WRITE_PROTECT};
|
||||
|
||||
unsafe { cr0_write(cr0() | CR0_WRITE_PROTECT) };
|
||||
}
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[lang = "eh_personality"]
|
||||
extern fn eh_personality() {}
|
||||
extern "C" fn eh_personality() {}
|
||||
|
||||
#[cfg(not(test))]
|
||||
#[lang = "panic_fmt"]
|
||||
extern fn panic_fmt(fmt: core::fmt::Arguments, file: &str, line: u32) -> ! {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn panic_fmt(fmt: core::fmt::Arguments, file: &'static str, line: u32) -> ! {
|
||||
println!("\n\nPANIC in {} at line {}:", file, line);
|
||||
println!(" {}", fmt);
|
||||
loop {}
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
#[no_mangle]
|
||||
pub extern "C" fn _Unwind_Resume() -> ! {
|
||||
loop {}
|
||||
}
|
||||
|
||||
4
src/memory/alloc/allocator/Cargo.lock
generated
4
src/memory/alloc/allocator/Cargo.lock
generated
@@ -1,4 +0,0 @@
|
||||
[root]
|
||||
name = "allocator"
|
||||
version = "0.1.0"
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
#![feature(no_std, allocator)]
|
||||
#![no_std]
|
||||
#![allocator]
|
||||
#![allow(improper_ctypes)]
|
||||
|
||||
extern {
|
||||
fn __rust_allocate(size: usize, align: usize) -> *mut u8;
|
||||
fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize);
|
||||
fn __rust_reallocate(ptr: *mut u8, old_size: usize, size: usize,
|
||||
align: usize) -> *mut u8;
|
||||
fn __rust_reallocate_inplace(ptr: *mut u8, old_size: usize, size: usize,
|
||||
align: usize) -> usize;
|
||||
fn __rust_usable_size(size: usize, align: usize) -> usize;
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
use memory::paging::{self, Page, Mapper};
|
||||
use memory::frame_allocator::{FrameAllocator, DynamicFrameStack};
|
||||
use core::iter::range_inclusive;
|
||||
use rlibc::memcpy;
|
||||
use spin::Mutex;
|
||||
|
||||
static ALLOCATOR: Mutex<Option<Allocator>> = Mutex::new(None);
|
||||
|
||||
const HEAD_BOTTOM: usize = 0o_001_000_000_000_0000;
|
||||
|
||||
struct Allocator {
|
||||
heap_top: usize,
|
||||
last_mapped_page: Page,
|
||||
lock: paging::Lock,
|
||||
frame_stack: DynamicFrameStack,
|
||||
}
|
||||
|
||||
impl Allocator {
|
||||
pub fn allocate(&mut self, size: usize, align: usize) -> *mut u8 {
|
||||
println!("allocate {} bytes (align {})", size, align); //loop{}
|
||||
let start_address = align_up(self.heap_top, align);
|
||||
let end_address = start_address + size;
|
||||
let end_page = Page::containing_address(end_address - 1).number;
|
||||
let last_mapped_page = self.last_mapped_page.number;
|
||||
|
||||
if end_page > last_mapped_page {
|
||||
for page in range_inclusive(last_mapped_page + 1, end_page).map(|n| Page{number: n}) {
|
||||
let mut mapper = self.lock.mapper(&mut self.frame_stack);
|
||||
mapper.map(page, true, false)
|
||||
}
|
||||
self.last_mapped_page.number = end_page;
|
||||
}
|
||||
self.heap_top = end_address;
|
||||
start_address as *mut u8
|
||||
}
|
||||
|
||||
pub fn reallocate(&mut self, ptr: *mut u8, old_size: usize, size: usize,
|
||||
align: usize) -> *mut u8
|
||||
{
|
||||
let new_ptr = self.allocate(size, align);
|
||||
unsafe{ memcpy(new_ptr, ptr, old_size) };
|
||||
new_ptr
|
||||
}
|
||||
|
||||
pub fn deallocate(&mut self, ptr: *mut u8, old_size: usize, align: usize) {
|
||||
//TODO
|
||||
}
|
||||
}
|
||||
|
||||
fn align_up(addr: usize, align: usize) -> usize {
|
||||
if addr % align == 0 {
|
||||
addr
|
||||
} else {
|
||||
addr + align - (addr % align)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(mut lock: paging::Lock, mut frame_stack: DynamicFrameStack) {
|
||||
let last_mapped_page = Page::containing_address(HEAD_BOTTOM);
|
||||
{
|
||||
let mut mapper = lock.mapper(&mut frame_stack);
|
||||
mapper.map(last_mapped_page, true, false);
|
||||
}
|
||||
|
||||
*ALLOCATOR.lock() = Some(Allocator {
|
||||
heap_top: HEAD_BOTTOM,
|
||||
last_mapped_page: last_mapped_page,
|
||||
lock: lock,
|
||||
frame_stack: frame_stack,
|
||||
})
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn __rust_allocate(size: usize, align: usize) -> *mut u8 {
|
||||
ALLOCATOR.lock().as_mut().expect("no allocator").allocate(size, align)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern fn __rust_deallocate(ptr: *mut u8, old_size: usize, align: usize) {
|
||||
ALLOCATOR.lock().as_mut().expect("no allocator").deallocate(ptr, old_size, align)
|
||||
}
|
||||
104
src/memory/area_frame_allocator.rs
Normal file
104
src/memory/area_frame_allocator.rs
Normal file
@@ -0,0 +1,104 @@
|
||||
// Copyright 2016 Philipp Oppermann. See the README.md
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use memory::{Frame, FrameAllocator};
|
||||
use multiboot2::{MemoryAreaIter, MemoryArea};
|
||||
|
||||
/// A frame allocator that uses the memory areas from the multiboot information structure as
|
||||
/// source. The {kernel, multiboot}_{start, end} fields are used to avoid returning memory that is
|
||||
/// already in use.
|
||||
///
|
||||
/// `kernel_end` and `multiboot_end` are _inclusive_ bounds.
|
||||
pub struct AreaFrameAllocator {
|
||||
next_free_frame: Frame,
|
||||
current_area: Option<&'static MemoryArea>,
|
||||
areas: MemoryAreaIter,
|
||||
kernel_start: Frame,
|
||||
kernel_end: Frame,
|
||||
multiboot_start: Frame,
|
||||
multiboot_end: Frame,
|
||||
}
|
||||
|
||||
impl AreaFrameAllocator {
|
||||
pub fn new(kernel_start: usize,
|
||||
kernel_end: usize,
|
||||
multiboot_start: usize,
|
||||
multiboot_end: usize,
|
||||
memory_areas: MemoryAreaIter)
|
||||
-> AreaFrameAllocator {
|
||||
let mut allocator = AreaFrameAllocator {
|
||||
next_free_frame: Frame::containing_address(0),
|
||||
current_area: None,
|
||||
areas: memory_areas,
|
||||
kernel_start: Frame::containing_address(kernel_start),
|
||||
kernel_end: Frame::containing_address(kernel_end),
|
||||
multiboot_start: Frame::containing_address(multiboot_start),
|
||||
multiboot_end: Frame::containing_address(multiboot_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(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(area.base_addr as usize);
|
||||
if self.next_free_frame < start_frame {
|
||||
self.next_free_frame = start_frame;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FrameAllocator for AreaFrameAllocator {
|
||||
fn allocate_frame(&mut self) -> Option<Frame> {
|
||||
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 frame = Frame { number: self.next_free_frame.number };
|
||||
|
||||
// the last frame of the current area
|
||||
let current_area_last_frame = {
|
||||
let address = area.base_addr + area.length - 1;
|
||||
Frame::containing_address(address as usize)
|
||||
};
|
||||
|
||||
if frame > current_area_last_frame {
|
||||
// all frames of current area are used, switch to next area
|
||||
self.choose_next_area();
|
||||
} else if frame >= self.kernel_start && frame <= self.kernel_end {
|
||||
// `frame` is used by the kernel
|
||||
self.next_free_frame = Frame { number: self.kernel_end.number + 1 };
|
||||
} else if frame >= self.multiboot_start && frame <= self.multiboot_end {
|
||||
// `frame` is used by the multiboot information structure
|
||||
self.next_free_frame = Frame { number: self.multiboot_end.number + 1 };
|
||||
} else {
|
||||
// frame is unused, increment `next_free_frame` and return it
|
||||
self.next_free_frame.number += 1;
|
||||
return Some(frame);
|
||||
}
|
||||
// `frame` was not valid, try it again with the updated `next_free_frame`
|
||||
self.allocate_frame()
|
||||
} else {
|
||||
None // no free frames left
|
||||
}
|
||||
}
|
||||
|
||||
fn deallocate_frame(&mut self, _frame: Frame) {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
use core::ptr::Unique;
|
||||
use core::mem;
|
||||
use memory::paging;
|
||||
|
||||
pub type Frame = super::Frame;
|
||||
pub type Page = super::paging::Page;
|
||||
|
||||
pub trait FrameAllocator {
|
||||
fn allocate_frame(&mut self, lock: &mut paging::Lock) -> Option<Frame>;
|
||||
fn deallocate_frame(&mut self, lock: &mut paging::Lock, frame: Frame);
|
||||
}
|
||||
|
||||
pub struct DynamicFrameStack {
|
||||
head: Unique<Frame>, // TODO invariant
|
||||
length: usize,
|
||||
capacity: usize,
|
||||
}
|
||||
|
||||
impl DynamicFrameStack {
|
||||
pub fn new(at: Page) -> DynamicFrameStack {
|
||||
DynamicFrameStack {
|
||||
head: unsafe{ Unique::new(at.pointer() as *mut () as *mut _) },
|
||||
length: 0,
|
||||
capacity: Self::capacity_per_frame(),
|
||||
}
|
||||
}
|
||||
|
||||
fn capacity_per_frame() -> usize {
|
||||
(super::PAGE_SIZE as usize) / mem::size_of::<Frame>()
|
||||
}
|
||||
}
|
||||
|
||||
impl FrameAllocator for DynamicFrameStack {
|
||||
fn allocate_frame(&mut self, lock: &mut paging::Lock) -> Option<Frame> {
|
||||
use core::intrinsics::offset;
|
||||
|
||||
if self.length == 0 {
|
||||
// no frames left but maybe we can decrease the capacity and use that frame (but keep
|
||||
// at least 1 frame because the paging logic might need some frames to map a page)
|
||||
if self.capacity <= Self::capacity_per_frame() {
|
||||
None
|
||||
} else {
|
||||
// decrease capacity and thus free a frame used as backing store
|
||||
self.capacity -= Self::capacity_per_frame();
|
||||
let page_address = unsafe{ offset(*self.head, self.capacity as isize) } as usize;
|
||||
lock.mapper(self).unmap(Page::containing_address(page_address));
|
||||
self.allocate_frame(lock)
|
||||
}
|
||||
} else {
|
||||
// pop the last frame from the stack
|
||||
self.length -= 1;
|
||||
unsafe {
|
||||
let frame = offset(*self.head, self.length as isize) as *mut _;
|
||||
Some(mem::replace(&mut *frame, mem::zeroed()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn deallocate_frame(&mut self, lock: &mut paging::Lock, frame: Frame) {
|
||||
use core::intrinsics::offset;
|
||||
|
||||
if self.length < self.capacity {
|
||||
// add frame to frame stack
|
||||
unsafe {
|
||||
let new_frame = offset(*self.head, self.length as isize) as *mut _;
|
||||
mem::forget(mem::replace(&mut *new_frame, frame));
|
||||
}
|
||||
self.length += 1;
|
||||
} else {
|
||||
// frame stack is full, use passed frame to expand it
|
||||
let page_address = unsafe{ offset(*self.head, self.capacity as isize) } as usize;
|
||||
unsafe {
|
||||
lock.mapper(self).map_to(Page::containing_address(page_address), frame,
|
||||
true, false);
|
||||
}
|
||||
self.capacity += Self::capacity_per_frame();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,150 +1,111 @@
|
||||
use multiboot2::Multiboot;
|
||||
use self::paging::Page;
|
||||
use self::frame_allocator::{FrameAllocator, DynamicFrameStack};
|
||||
// Copyright 2016 Philipp Oppermann. See the README.md
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
mod alloc;
|
||||
pub use self::area_frame_allocator::AreaFrameAllocator;
|
||||
pub use self::paging::remap_the_kernel;
|
||||
use self::paging::PhysicalAddress;
|
||||
use multiboot2::BootInformation;
|
||||
|
||||
mod area_frame_allocator;
|
||||
mod paging;
|
||||
mod frame_allocator;
|
||||
mod tlb;
|
||||
|
||||
pub const PAGE_SIZE: u64 = 4096;
|
||||
pub const PAGE_SIZE: usize = 4096;
|
||||
|
||||
pub fn init(multiboot: &Multiboot) {
|
||||
// ATTENTION: we have a very small stack and no guard page
|
||||
use core::cmp::max;
|
||||
use self::frame_allocator::FrameAllocator;
|
||||
pub fn init(boot_info: &BootInformation) {
|
||||
assert_has_not_been_called!("memory::init must be called only once");
|
||||
|
||||
let kernel_end = multiboot.elf_sections_tag().unwrap().sections().map(|s| s.addr + s.size).max()
|
||||
.unwrap() as usize;
|
||||
let multiboot_end = multiboot as *const _ as usize + multiboot.total_size as usize;
|
||||
let mut bump_pointer = BumpPointer::new(max(kernel_end, multiboot_end));
|
||||
let memory_map_tag = boot_info.memory_map_tag().expect("Memory map tag required");
|
||||
let elf_sections_tag = boot_info.elf_sections_tag().expect("Elf sections tag required");
|
||||
|
||||
let mut lock = unsafe{ paging::Lock::new() };
|
||||
let new_p4_frame = bump_pointer.allocate_frame(&mut lock).expect("failed allocating
|
||||
new_p4_frame");
|
||||
let kernel_start = elf_sections_tag.sections()
|
||||
.filter(|s| s.is_allocated())
|
||||
.map(|s| s.addr)
|
||||
.min()
|
||||
.unwrap();
|
||||
let kernel_end = elf_sections_tag.sections()
|
||||
.filter(|s| s.is_allocated())
|
||||
.map(|s| s.addr + s.size)
|
||||
.max()
|
||||
.unwrap();
|
||||
|
||||
unsafe{lock.begin_new_table_on_identity_mapped_frame(new_p4_frame)};
|
||||
identity_map_kernel_sections(multiboot, lock.mapper(&mut bump_pointer));
|
||||
lock.activate_current_table();
|
||||
println!("kernel start: {:#x}, kernel end: {:#x}",
|
||||
kernel_start,
|
||||
kernel_end);
|
||||
println!("multiboot start: {:#x}, multiboot end: {:#x}",
|
||||
boot_info.start_address(),
|
||||
boot_info.end_address());
|
||||
|
||||
let frame_stack = init_core_map(multiboot, &mut lock, bump_pointer);
|
||||
let mut frame_allocator = AreaFrameAllocator::new(kernel_start as usize,
|
||||
kernel_end as usize,
|
||||
boot_info.start_address(),
|
||||
boot_info.end_address(),
|
||||
memory_map_tag.memory_areas());
|
||||
|
||||
let maximal_memory = multiboot.memory_area_tag().unwrap().memory_areas().map(
|
||||
|area| area.base_addr + area.length).max().unwrap();
|
||||
println!("maximal_memory: 0x{:x}", maximal_memory);
|
||||
alloc::init(lock, frame_stack);
|
||||
}
|
||||
let mut active_table = paging::remap_the_kernel(&mut frame_allocator, boot_info);
|
||||
|
||||
use self::paging::Page;
|
||||
use hole_list_allocator::{HEAP_START, HEAP_SIZE};
|
||||
|
||||
fn identity_map_kernel_sections<T>(multiboot: &Multiboot, mut mapper: paging::Mapper<T>)
|
||||
where T: frame_allocator::FrameAllocator,
|
||||
{
|
||||
use core::iter::range_inclusive;
|
||||
let heap_start_page = Page::containing_address(HEAP_START);
|
||||
let heap_end_page = Page::containing_address(HEAP_START + HEAP_SIZE - 1);
|
||||
|
||||
for section in multiboot.elf_sections_tag().expect("no section tag").sections() {
|
||||
let in_memory = section.flags & 0x2 != 0;
|
||||
let writable = section.flags & 0x1 != 0;
|
||||
let executable = section.flags & 0x4 != 0;
|
||||
if !in_memory {
|
||||
continue;
|
||||
}
|
||||
println!("section at 0x{:x}, allocated: {}, writable: {}, executable: {}", section.addr,
|
||||
in_memory, writable, executable);
|
||||
let start_page = Page::containing_address(section.addr as usize);
|
||||
let end_page = Page::containing_address((section.addr + section.size) as usize);
|
||||
for page in range_inclusive(start_page.number, end_page.number)
|
||||
.map(|n| Page{number: n})
|
||||
{
|
||||
unsafe{ mapper.identity_map(page, writable, executable) };
|
||||
for page in Page::range_inclusive(heap_start_page, heap_end_page) {
|
||||
active_table.map(page, paging::WRITABLE, &mut frame_allocator);
|
||||
}
|
||||
}
|
||||
|
||||
// identity map VGA text buffer
|
||||
unsafe {
|
||||
mapper.identity_map(Page::containing_address(0xb8000), true, false);
|
||||
}
|
||||
|
||||
// identity map Multiboot structure
|
||||
let multiboot_address = multiboot as *const _ as usize;
|
||||
let start_page = Page::containing_address(multiboot_address);
|
||||
let end_page = Page::containing_address(multiboot_address + multiboot.total_size as usize);
|
||||
for page in range_inclusive(start_page.number, end_page.number).map(|n| Page{number: n}) {
|
||||
unsafe{ mapper.identity_map(page, false, false) };
|
||||
}
|
||||
}
|
||||
|
||||
fn init_core_map(multiboot: &Multiboot, lock: &mut paging::Lock,
|
||||
mut bump_pointer: BumpPointer) -> DynamicFrameStack
|
||||
{
|
||||
use core::iter::range_inclusive;
|
||||
|
||||
|
||||
const CORE_MAP_PAGE: Page = Page{number: 0o_001_000_000};
|
||||
|
||||
lock.mapper(&mut bump_pointer).map(CORE_MAP_PAGE, true, false);
|
||||
let mut frame_stack = DynamicFrameStack::new(CORE_MAP_PAGE);
|
||||
|
||||
for area in multiboot.memory_area_tag().expect("no memory tag").memory_areas() {
|
||||
println!("area start {:x} length {:x}", area.base_addr, area.length);
|
||||
let start_frame = Frame::containing_address(area.base_addr as usize);
|
||||
let end_frame = Frame::containing_address((area.base_addr + area.length) as usize);
|
||||
for frame in range_inclusive(start_frame.number, end_frame.number)
|
||||
.map(|n| Frame{number:n})
|
||||
{
|
||||
let page = Page{number: frame.number};
|
||||
if page.is_unused() && !bump_pointer.has_allocated(frame) {
|
||||
frame_stack.deallocate_frame(lock, frame)
|
||||
}
|
||||
}
|
||||
}
|
||||
frame_stack
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct BumpPointer {
|
||||
first_free_frame: usize,
|
||||
next_free_frame: usize,
|
||||
}
|
||||
|
||||
impl frame_allocator::FrameAllocator for BumpPointer {
|
||||
fn allocate_frame(&mut self, _: &mut paging::Lock) -> Option<Frame> {
|
||||
self.allocate_frames(1)
|
||||
}
|
||||
fn deallocate_frame(&mut self, _: &mut paging::Lock, _: Frame) {}
|
||||
}
|
||||
|
||||
impl BumpPointer {
|
||||
fn new(kernel_end: usize) -> BumpPointer {
|
||||
assert!(kernel_end > 0x100000);
|
||||
let frame = ((kernel_end - 1) >> 12) + 1;
|
||||
BumpPointer {
|
||||
first_free_frame: frame,
|
||||
next_free_frame: frame,
|
||||
}
|
||||
}
|
||||
|
||||
fn allocate_frames(&mut self, number: usize) -> Option<Frame> {
|
||||
let page_number = self.next_free_frame;
|
||||
self.next_free_frame += number;
|
||||
Some(Frame {
|
||||
number: page_number
|
||||
})
|
||||
}
|
||||
|
||||
fn has_allocated(&self, frame: Frame) -> bool {
|
||||
frame.number >= self.first_free_frame && frame.number < self.next_free_frame
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone, Copy)]
|
||||
struct Frame {
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Frame {
|
||||
number: usize,
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
fn containing_address(address: usize) -> Frame {
|
||||
Frame {
|
||||
number: address >> 12,
|
||||
Frame { number: address / PAGE_SIZE }
|
||||
}
|
||||
|
||||
fn start_address(&self) -> PhysicalAddress {
|
||||
self.number * PAGE_SIZE
|
||||
}
|
||||
|
||||
fn clone(&self) -> Frame {
|
||||
Frame { number: self.number }
|
||||
}
|
||||
|
||||
fn range_inclusive(start: Frame, end: Frame) -> FrameIter {
|
||||
FrameIter {
|
||||
start: start,
|
||||
end: end,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 allocate_frame(&mut self) -> Option<Frame>;
|
||||
fn deallocate_frame(&mut self, frame: Frame);
|
||||
}
|
||||
|
||||
76
src/memory/paging/entry.rs
Normal file
76
src/memory/paging/entry.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright 2016 Philipp Oppermann. See the README.md
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use memory::Frame;
|
||||
use multiboot2::ElfSection;
|
||||
|
||||
pub struct Entry(u64);
|
||||
|
||||
impl Entry {
|
||||
pub fn is_unused(&self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
|
||||
pub fn set_unused(&mut self) {
|
||||
self.0 = 0;
|
||||
}
|
||||
|
||||
pub fn flags(&self) -> EntryFlags {
|
||||
EntryFlags::from_bits_truncate(self.0)
|
||||
}
|
||||
|
||||
pub fn pointed_frame(&self) -> Option<Frame> {
|
||||
if self.flags().contains(PRESENT) {
|
||||
Some(Frame::containing_address(self.0 as usize & 0x000fffff_fffff000))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(&mut self, frame: Frame, flags: EntryFlags) {
|
||||
assert!(frame.start_address() & !0x000fffff_fffff000 == 0);
|
||||
self.0 = (frame.start_address() as u64) | flags.bits();
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
impl EntryFlags {
|
||||
pub fn from_elf_section_flags(section: &ElfSection) -> EntryFlags {
|
||||
use multiboot2::{ELF_SECTION_ALLOCATED, ELF_SECTION_WRITABLE, ELF_SECTION_EXECUTABLE};
|
||||
|
||||
let mut flags = EntryFlags::empty();
|
||||
|
||||
if section.flags().contains(ELF_SECTION_ALLOCATED) {
|
||||
// section is loaded to memory
|
||||
flags = flags | PRESENT;
|
||||
}
|
||||
if section.flags().contains(ELF_SECTION_WRITABLE) {
|
||||
flags = flags | WRITABLE;
|
||||
}
|
||||
if !section.flags().contains(ELF_SECTION_EXECUTABLE) {
|
||||
flags = flags | NO_EXECUTE;
|
||||
}
|
||||
|
||||
flags
|
||||
}
|
||||
}
|
||||
118
src/memory/paging/mapper.rs
Normal file
118
src/memory/paging/mapper.rs
Normal file
@@ -0,0 +1,118 @@
|
||||
// Copyright 2016 Philipp Oppermann. See the README.md
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::{VirtualAddress, PhysicalAddress, Page, ENTRY_COUNT};
|
||||
use super::entry::*;
|
||||
use super::table::{self, Table, Level4};
|
||||
use memory::{PAGE_SIZE, Frame, FrameAllocator};
|
||||
use core::ptr::Unique;
|
||||
|
||||
pub struct Mapper {
|
||||
p4: Unique<Table<Level4>>,
|
||||
}
|
||||
|
||||
impl Mapper {
|
||||
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() }
|
||||
}
|
||||
|
||||
pub fn translate(&self, virtual_address: VirtualAddress) -> Option<PhysicalAddress> {
|
||||
let offset = virtual_address % PAGE_SIZE;
|
||||
self.translate_page(Page::containing_address(virtual_address))
|
||||
.map(|frame| frame.number * PAGE_SIZE + offset)
|
||||
}
|
||||
|
||||
pub fn translate_page(&self, page: Page) -> Option<Frame> {
|
||||
let p3 = self.p4().next_table(page.p4_index());
|
||||
|
||||
let huge_page = || {
|
||||
p3.and_then(|p3| {
|
||||
let p3_entry = &p3[page.p3_index()];
|
||||
// 1GiB page?
|
||||
if let Some(start_frame) = p3_entry.pointed_frame() {
|
||||
if p3_entry.flags().contains(HUGE_PAGE) {
|
||||
// address must be 1GiB aligned
|
||||
assert!(start_frame.number % (ENTRY_COUNT * ENTRY_COUNT) == 0);
|
||||
return Some(Frame {
|
||||
number: start_frame.number + page.p2_index() * ENTRY_COUNT +
|
||||
page.p1_index(),
|
||||
});
|
||||
}
|
||||
}
|
||||
if let Some(p2) = p3.next_table(page.p3_index()) {
|
||||
let p2_entry = &p2[page.p2_index()];
|
||||
// 2MiB page?
|
||||
if let Some(start_frame) = p2_entry.pointed_frame() {
|
||||
if p2_entry.flags().contains(HUGE_PAGE) {
|
||||
// address must be 2MiB aligned
|
||||
assert!(start_frame.number % ENTRY_COUNT == 0);
|
||||
return Some(Frame { number: start_frame.number + page.p1_index() });
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
})
|
||||
};
|
||||
|
||||
p3.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())
|
||||
.or_else(huge_page)
|
||||
}
|
||||
|
||||
pub fn map_to<A>(&mut self, page: Page, frame: Frame, flags: EntryFlags, allocator: &mut A)
|
||||
where A: FrameAllocator
|
||||
{
|
||||
let mut p3 = self.p4_mut().next_table_create(page.p4_index(), allocator);
|
||||
let mut p2 = p3.next_table_create(page.p3_index(), allocator);
|
||||
let mut p1 = p2.next_table_create(page.p2_index(), allocator);
|
||||
|
||||
assert!(p1[page.p1_index()].is_unused());
|
||||
p1[page.p1_index()].set(frame, flags | PRESENT);
|
||||
}
|
||||
|
||||
pub fn map<A>(&mut self, page: Page, flags: EntryFlags, allocator: &mut A)
|
||||
where A: FrameAllocator
|
||||
{
|
||||
let frame = allocator.allocate_frame().expect("out of memory");
|
||||
self.map_to(page, frame, flags, allocator)
|
||||
}
|
||||
|
||||
pub fn identity_map<A>(&mut self, frame: Frame, flags: EntryFlags, allocator: &mut A)
|
||||
where A: FrameAllocator
|
||||
{
|
||||
let page = Page::containing_address(frame.start_address());
|
||||
self.map_to(page, frame, flags, allocator)
|
||||
}
|
||||
|
||||
pub fn unmap<A>(&mut self, page: Page, allocator: &mut A)
|
||||
where A: FrameAllocator
|
||||
{
|
||||
assert!(self.translate(page.start_address()).is_some());
|
||||
|
||||
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();
|
||||
unsafe { ::x86::shared::tlb::flush(page.start_address()) };
|
||||
// TODO free p(1,2,3) table if empty
|
||||
// allocator.deallocate_frame(frame);
|
||||
}
|
||||
}
|
||||
@@ -1,75 +1,227 @@
|
||||
pub use self::table::Page;
|
||||
// Copyright 2016 Philipp Oppermann. See the README.md
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use self::table::{map_to, unmap};
|
||||
use memory::frame_allocator::{Frame, FrameAllocator};
|
||||
|
||||
pub const PAGE_SIZE: usize = 4096;
|
||||
pub use self::entry::*;
|
||||
use memory::{PAGE_SIZE, Frame, FrameAllocator};
|
||||
use self::temporary_page::TemporaryPage;
|
||||
pub use self::mapper::Mapper;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use multiboot2::BootInformation;
|
||||
|
||||
mod entry;
|
||||
mod table;
|
||||
mod temporary_page;
|
||||
mod mapper;
|
||||
|
||||
/// The paging lock must be unique. It is required for all page table operations and thus
|
||||
/// guarantees exclusive page table access.
|
||||
pub struct Lock {
|
||||
_private: (),
|
||||
const ENTRY_COUNT: usize = 512;
|
||||
|
||||
pub type PhysicalAddress = usize;
|
||||
pub type VirtualAddress = usize;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Page {
|
||||
number: usize,
|
||||
}
|
||||
|
||||
impl Lock {
|
||||
/// Creates a new paging lock. It's unsafe because only one lock can exist at a
|
||||
/// time.
|
||||
pub unsafe fn new() -> Lock {
|
||||
Lock {
|
||||
_private: (),
|
||||
impl Page {
|
||||
pub fn containing_address(address: VirtualAddress) -> Page {
|
||||
assert!(address < 0x0000_8000_0000_0000 || address >= 0xffff_8000_0000_0000,
|
||||
"invalid address: 0x{:x}",
|
||||
address);
|
||||
Page { number: address / PAGE_SIZE }
|
||||
}
|
||||
|
||||
fn start_address(&self) -> usize {
|
||||
self.number * PAGE_SIZE
|
||||
}
|
||||
|
||||
fn p4_index(&self) -> usize {
|
||||
(self.number >> 27) & 0o777
|
||||
}
|
||||
fn p3_index(&self) -> usize {
|
||||
(self.number >> 18) & 0o777
|
||||
}
|
||||
fn p2_index(&self) -> usize {
|
||||
(self.number >> 9) & 0o777
|
||||
}
|
||||
fn p1_index(&self) -> usize {
|
||||
(self.number >> 0) & 0o777
|
||||
}
|
||||
|
||||
pub fn range_inclusive(start: Page, end: Page) -> PageIter {
|
||||
PageIter {
|
||||
start: start,
|
||||
end: end,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Uses the passed frame to create a new page table that becomes the _current table_.
|
||||
/// All subsequent page table operations will modify it (the _current_ table) and leave the
|
||||
/// _active_ table unchanged. To activate the current table and make it the active table, use
|
||||
/// the `activate_new_table` method.
|
||||
/// This method assumes that the passed frame is identity mapped and is thus unsafe.
|
||||
pub unsafe fn begin_new_table_on_identity_mapped_frame(&mut self, frame: Frame)
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
unsafe fn new() -> ActivePageTable {
|
||||
ActivePageTable { mapper: Mapper::new() }
|
||||
}
|
||||
|
||||
pub fn with<F>(&mut self,
|
||||
table: &mut InactivePageTable,
|
||||
temporary_page: &mut temporary_page::TemporaryPage, // new
|
||||
f: F)
|
||||
where F: FnOnce(&mut Mapper)
|
||||
{
|
||||
table::begin_new_on_identity_mapped_frame(self, frame)
|
||||
}
|
||||
use x86::shared::{control_regs, tlb};
|
||||
let flush_tlb = || unsafe { tlb::flush_all() };
|
||||
|
||||
/// Activates the _current_ table. If the current table is equal to the active table, nothing
|
||||
/// changes. However, if _current_ and _active_ table are different, a new table becomes active /// and becomes the table used by the CPU.
|
||||
pub fn activate_current_table(&mut self) {
|
||||
table::activate_current()
|
||||
}
|
||||
|
||||
pub fn mapper<'a, A>(&'a mut self, allocator: &'a mut A) -> Mapper<'a, A>
|
||||
where A: FrameAllocator,
|
||||
{
|
||||
Mapper {
|
||||
lock: self,
|
||||
allocator: allocator,
|
||||
let backup = Frame::containing_address(unsafe { control_regs::cr3() } as usize);
|
||||
|
||||
// map temporary_page to current p4 table
|
||||
let p4_table = temporary_page.map_table_frame(backup.clone(), self);
|
||||
|
||||
// overwrite recursive mapping
|
||||
self.p4_mut()[511].set(table.p4_frame.clone(), PRESENT | WRITABLE);
|
||||
flush_tlb();
|
||||
|
||||
// execute f in the new context
|
||||
f(self);
|
||||
|
||||
// restore recursive mapping to original p4 table
|
||||
p4_table[511].set(backup, PRESENT | WRITABLE);
|
||||
flush_tlb();
|
||||
}
|
||||
|
||||
temporary_page.unmap(self);
|
||||
}
|
||||
|
||||
pub fn switch(&mut self, new_table: InactivePageTable) -> InactivePageTable {
|
||||
use x86::shared::control_regs;
|
||||
|
||||
let old_table = InactivePageTable {
|
||||
p4_frame: Frame::containing_address(unsafe { control_regs::cr3() } as usize),
|
||||
};
|
||||
unsafe {
|
||||
control_regs::cr3_write(new_table.p4_frame.start_address());
|
||||
}
|
||||
old_table
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Mapper<'a, A> where A: 'a {
|
||||
lock: &'a mut Lock,
|
||||
allocator: &'a mut A,
|
||||
pub struct InactivePageTable {
|
||||
p4_frame: Frame,
|
||||
}
|
||||
|
||||
impl<'a, A> Mapper<'a, A> where A: FrameAllocator {
|
||||
pub fn map(&mut self, page: Page, writable: bool, executable: bool) {
|
||||
let frame = self.allocator.allocate_frame(&mut self.lock)
|
||||
.expect("no more frames available");
|
||||
unsafe{ self.map_to(page, frame, writable, executable) }
|
||||
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(), active_table);
|
||||
table.zero();
|
||||
table[511].set(frame.clone(), PRESENT | WRITABLE);
|
||||
}
|
||||
temporary_page.unmap(active_table);
|
||||
|
||||
InactivePageTable { p4_frame: frame }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unmap(&mut self, page: Page) {
|
||||
unmap(self.lock, page, self.allocator)
|
||||
pub fn remap_the_kernel<A>(allocator: &mut A, boot_info: &BootInformation) -> ActivePageTable
|
||||
where A: FrameAllocator
|
||||
{
|
||||
let mut temporary_page = TemporaryPage::new(Page { number: 0xcafebabe }, allocator);
|
||||
|
||||
let mut active_table = unsafe { ActivePageTable::new() };
|
||||
let mut new_table = {
|
||||
let frame = allocator.allocate_frame().expect("no more frames");
|
||||
InactivePageTable::new(frame, &mut active_table, &mut temporary_page)
|
||||
};
|
||||
|
||||
active_table.with(&mut new_table, &mut temporary_page, |mapper| {
|
||||
let elf_sections_tag = boot_info.elf_sections_tag()
|
||||
.expect("Memory map tag required");
|
||||
|
||||
// identity map the allocated kernel sections
|
||||
for section in elf_sections_tag.sections() {
|
||||
if !section.is_allocated() {
|
||||
// section is not loaded to memory
|
||||
continue;
|
||||
}
|
||||
|
||||
pub unsafe fn map_to(&mut self, page: Page, frame: Frame, writable: bool, executable: bool) {
|
||||
map_to(self.lock, page, frame, writable, executable, self.allocator)
|
||||
assert!(section.addr as usize % PAGE_SIZE == 0,
|
||||
"sections need to be page aligned");
|
||||
println!("mapping section at addr: {:#x}, size: {:#x}",
|
||||
section.addr,
|
||||
section.size);
|
||||
|
||||
let flags = EntryFlags::from_elf_section_flags(section);
|
||||
|
||||
let start_frame = Frame::containing_address(section.start_address());
|
||||
let end_frame = Frame::containing_address(section.end_address() - 1);
|
||||
for frame in Frame::range_inclusive(start_frame, end_frame) {
|
||||
mapper.identity_map(frame, flags, allocator);
|
||||
}
|
||||
}
|
||||
|
||||
pub unsafe fn identity_map(&mut self, page: Page, writable: bool, executable: bool) {
|
||||
let frame = Frame {number: page.number};
|
||||
self.map_to(page, frame, writable, executable)
|
||||
// identity map the VGA text buffer
|
||||
let vga_buffer_frame = Frame::containing_address(0xb8000);
|
||||
mapper.identity_map(vga_buffer_frame, WRITABLE, allocator);
|
||||
|
||||
// identity map the multiboot info structure
|
||||
let multiboot_start = Frame::containing_address(boot_info.start_address());
|
||||
let multiboot_end = Frame::containing_address(boot_info.end_address() - 1);
|
||||
for frame in Frame::range_inclusive(multiboot_start, multiboot_end) {
|
||||
mapper.identity_map(frame, PRESENT, allocator);
|
||||
}
|
||||
});
|
||||
|
||||
let old_table = active_table.switch(new_table);
|
||||
println!("NEW TABLE!!!");
|
||||
|
||||
let old_p4_page = Page::containing_address(old_table.p4_frame.start_address());
|
||||
active_table.unmap(old_p4_page, allocator);
|
||||
println!("guard page at {:#x}", old_p4_page.start_address());
|
||||
|
||||
active_table
|
||||
}
|
||||
|
||||
@@ -1,182 +1,119 @@
|
||||
use memory::frame_allocator::FrameAllocator;
|
||||
use memory::tlb;
|
||||
use super::{PAGE_SIZE, Lock};
|
||||
use memory::frame_allocator::Frame;
|
||||
use core::intrinsics::offset;
|
||||
use core::mem::size_of;
|
||||
// Copyright 2016 Philipp Oppermann. See the README.md
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
const P4: Table = Table( Page{ number: 0o_777_777_777_777} );
|
||||
use memory::paging::entry::*;
|
||||
use memory::paging::ENTRY_COUNT;
|
||||
use memory::FrameAllocator;
|
||||
use core::ops::{Index, IndexMut};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
pub unsafe fn begin_new_on_identity_mapped_frame(_lock: &mut Lock, new_p4_frame: Frame) {
|
||||
let new_p4 = &mut Table(Page{ number: new_p4_frame.number });
|
||||
new_p4.zero();
|
||||
new_p4.field(511).set(new_p4_frame, PRESENT | WRITABLE);
|
||||
pub const P4: *mut Table<Level4> = 0xffffffff_fffff000 as *mut _;
|
||||
|
||||
P4.field(511).set(new_p4_frame, PRESENT | WRITABLE);
|
||||
|
||||
tlb::flush();
|
||||
pub struct Table<L: TableLevel> {
|
||||
entries: [Entry; ENTRY_COUNT],
|
||||
level: PhantomData<L>,
|
||||
}
|
||||
|
||||
pub fn activate_current() {
|
||||
unsafe {
|
||||
let p4_address: u64 = {
|
||||
let field = *(0xfffffffffffffff8 as *const u64);
|
||||
field & !0xfff
|
||||
};
|
||||
|
||||
asm!("mov cr3, $0" :: "r"(p4_address) :: "intel")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_to<A>(lock: &mut Lock, page: Page, frame: Frame, writable: bool,
|
||||
executable: bool, allocator: &mut A) where A: FrameAllocator
|
||||
impl<L> Table<L>
|
||||
where L: TableLevel
|
||||
{
|
||||
let mut flags = PRESENT;
|
||||
if writable {
|
||||
flags = flags | WRITABLE;
|
||||
pub fn zero(&mut self) {
|
||||
for entry in self.entries.iter_mut() {
|
||||
entry.set_unused();
|
||||
}
|
||||
if !executable {
|
||||
flags = flags | NO_EXECUTE;
|
||||
}
|
||||
|
||||
let p4_field = page.p4_page().field(page.p4_index());
|
||||
if p4_field.is_unused() {
|
||||
p4_field.set(allocator.allocate_frame(lock).expect("no more frames"), PRESENT | WRITABLE);
|
||||
unsafe{page.p3_page().zero()};
|
||||
}
|
||||
let p3_field = page.p3_page().field(page.p3_index());
|
||||
if p3_field.is_unused() {
|
||||
p3_field.set(allocator.allocate_frame(lock).expect("no more frames"), PRESENT | WRITABLE);
|
||||
unsafe{page.p2_page().zero()};
|
||||
}
|
||||
let p2_field = page.p2_page().field(page.p2_index());
|
||||
if p2_field.is_unused() {
|
||||
p2_field.set(allocator.allocate_frame(lock).expect("no more frames"), PRESENT | WRITABLE);
|
||||
unsafe{page.p1_page().zero()};
|
||||
}
|
||||
let p1_field = page.p1_page().field(page.p1_index());
|
||||
assert!(p1_field.is_unused());
|
||||
p1_field.set(frame, flags);
|
||||
}
|
||||
|
||||
pub fn unmap<A>(lock: &mut Lock, page: Page, allocator: &mut A) where A: FrameAllocator {
|
||||
assert!(!page.is_unused());
|
||||
let p1_field = page.p1_page().field(page.p1_index());
|
||||
let frame = p1_field.pointed_frame();
|
||||
p1_field.set_unused();
|
||||
// TODO free p(1,2,3) table if empty
|
||||
allocator.deallocate_frame(lock, frame);
|
||||
}
|
||||
|
||||
|
||||
/// A mapped or unmapped page
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Page {
|
||||
pub number: usize, // TOOD make private
|
||||
}
|
||||
|
||||
impl Page {
|
||||
pub fn containing_address(address: usize) -> Page {
|
||||
Page {
|
||||
number: (address >> 12) & 0o_777_777_777_777,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pointer(&self) -> *const () {
|
||||
if self.number >= 0o400_000_000_000 {
|
||||
//sign extension
|
||||
((self.number << 12) | 0o177777_000_000_000_000_0000) as *const ()
|
||||
impl<L> Table<L>
|
||||
where L: HierarchicalLevel
|
||||
{
|
||||
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 {
|
||||
(self.number << 12) as *const ()
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_unused(&self) -> bool {
|
||||
self.p4_page().field(self.p4_index()).is_unused() ||
|
||||
self.p3_page().field(self.p3_index()).is_unused() ||
|
||||
self.p2_page().field(self.p2_index()).is_unused() ||
|
||||
self.p1_page().field(self.p1_index()).is_unused()
|
||||
pub fn next_table(&self, index: usize) -> Option<&Table<L::NextLevel>> {
|
||||
self.next_table_address(index)
|
||||
.map(|address| unsafe { &*(address as *const _) })
|
||||
}
|
||||
|
||||
fn p4_index(&self) -> usize {(self.number >> 27) & 0o777}
|
||||
fn p3_index(&self) -> usize {(self.number >> 18) & 0o777}
|
||||
fn p2_index(&self) -> usize {(self.number >> 9) & 0o777}
|
||||
fn p1_index(&self) -> usize {(self.number >> 0) & 0o777}
|
||||
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 _) })
|
||||
}
|
||||
|
||||
fn p4_page(&self) -> Table {
|
||||
P4
|
||||
pub fn next_table_create<A>(&mut self,
|
||||
index: usize,
|
||||
allocator: &mut A)
|
||||
-> &mut Table<L::NextLevel>
|
||||
where A: FrameAllocator
|
||||
{
|
||||
if self.next_table(index).is_none() {
|
||||
assert!(!self.entries[index].flags().contains(HUGE_PAGE),
|
||||
"mapping code does not support huge pages");
|
||||
let frame = allocator.allocate_frame().expect("no frames available");
|
||||
self.entries[index].set(frame, PRESENT | WRITABLE);
|
||||
self.next_table_mut(index).unwrap().zero();
|
||||
}
|
||||
fn p3_page(&self) -> Table {
|
||||
Table(Page {
|
||||
number: 0o_777_777_777_000 | self.p4_index(),
|
||||
})
|
||||
}
|
||||
fn p2_page(&self) -> Table {
|
||||
Table(Page {
|
||||
number: 0o_777_777_000_000 | (self.p4_index() << 9) | self.p3_index(),
|
||||
})
|
||||
}
|
||||
fn p1_page(&self) -> Table {
|
||||
Table(Page {
|
||||
number: 0o_777_000_000_000 | (self.p4_index() << 18) | (self.p3_index() << 9)
|
||||
| self.p2_index(),
|
||||
})
|
||||
self.next_table_mut(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// A page table on a _mapped_ page.
|
||||
struct Table(Page);
|
||||
impl<L> Index<usize> for Table<L>
|
||||
where L: TableLevel
|
||||
{
|
||||
type Output = Entry;
|
||||
|
||||
impl Table {
|
||||
unsafe fn zero(&mut self) {
|
||||
const ENTRIES: usize = PAGE_SIZE / 8;
|
||||
let page = self.0.pointer() as *mut () as *mut [u64; ENTRIES];
|
||||
*page = [0; ENTRIES];
|
||||
}
|
||||
|
||||
fn field(&self, index: usize) -> &'static mut TableField {
|
||||
assert!(index < PAGE_SIZE / size_of::<u64>());
|
||||
unsafe {
|
||||
let field = offset(self.0.pointer() as *const u64, index as isize);
|
||||
&mut *(field as *const _ as *mut _)
|
||||
}
|
||||
fn index(&self, index: usize) -> &Entry {
|
||||
&self.entries[index]
|
||||
}
|
||||
}
|
||||
|
||||
struct TableField(u64);
|
||||
|
||||
impl TableField {
|
||||
fn is_unused(&self) -> bool {
|
||||
self.0 == 0
|
||||
}
|
||||
|
||||
fn set_unused(&mut self) {
|
||||
self.0 = 0
|
||||
}
|
||||
|
||||
fn set(&mut self, frame: Frame, flags: TableFieldFlags) {
|
||||
self.0 = (((frame.number as u64) << 12) & 0x000fffff_fffff000) | flags.bits();
|
||||
}
|
||||
|
||||
fn pointed_frame(&self) -> Frame {
|
||||
Frame {
|
||||
number: ((self.0 & 0x000fffff_fffff000) >> 12) as usize,
|
||||
impl<L> IndexMut<usize> for Table<L>
|
||||
where L: TableLevel
|
||||
{
|
||||
fn index_mut(&mut self, index: usize) -> &mut Entry {
|
||||
&mut self.entries[index]
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TableLevel {}
|
||||
|
||||
pub enum Level4 {}
|
||||
#[allow(dead_code)]
|
||||
pub enum Level3 {}
|
||||
#[allow(dead_code)]
|
||||
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;
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
flags TableFieldFlags: 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 OTHER1 = 1 << 9,
|
||||
const OTHER2 = 1 << 10,
|
||||
const NO_EXECUTE = 1 << 63,
|
||||
impl HierarchicalLevel for Level4 {
|
||||
type NextLevel = Level3;
|
||||
}
|
||||
|
||||
impl HierarchicalLevel for Level3 {
|
||||
type NextLevel = Level2;
|
||||
}
|
||||
|
||||
impl HierarchicalLevel for Level2 {
|
||||
type NextLevel = Level1;
|
||||
}
|
||||
|
||||
86
src/memory/paging/temporary_page.rs
Normal file
86
src/memory/paging/temporary_page.rs
Normal file
@@ -0,0 +1,86 @@
|
||||
// Copyright 2016 Philipp Oppermann. See the README.md
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::{Page, ActivePageTable, VirtualAddress};
|
||||
use super::table::{Table, Level1};
|
||||
use memory::{Frame, FrameAllocator};
|
||||
|
||||
pub struct TemporaryPage {
|
||||
page: Page,
|
||||
allocator: TinyAllocator,
|
||||
}
|
||||
|
||||
impl TemporaryPage {
|
||||
pub fn new<A>(page: Page, allocator: &mut A) -> TemporaryPage
|
||||
where A: FrameAllocator
|
||||
{
|
||||
TemporaryPage {
|
||||
page: page,
|
||||
allocator: TinyAllocator::new(allocator),
|
||||
}
|
||||
}
|
||||
|
||||
/// 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, active_table: &mut ActivePageTable) -> VirtualAddress {
|
||||
use super::entry::WRITABLE;
|
||||
|
||||
assert!(active_table.translate_page(self.page).is_none(),
|
||||
"temporary page is already mapped");
|
||||
active_table.map_to(self.page, frame, WRITABLE, &mut self.allocator);
|
||||
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,
|
||||
active_table: &mut ActivePageTable)
|
||||
-> &mut Table<Level1> {
|
||||
unsafe { &mut *(self.map(frame, active_table) 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, &mut self.allocator)
|
||||
}
|
||||
}
|
||||
|
||||
struct TinyAllocator([Option<Frame>; 3]);
|
||||
|
||||
impl TinyAllocator {
|
||||
fn new<A>(allocator: &mut A) -> TinyAllocator
|
||||
where A: FrameAllocator
|
||||
{
|
||||
let mut f = || allocator.allocate_frame();
|
||||
let frames = [f(), f(), f()];
|
||||
TinyAllocator(frames)
|
||||
}
|
||||
}
|
||||
|
||||
impl FrameAllocator for TinyAllocator {
|
||||
fn allocate_frame(&mut self) -> Option<Frame> {
|
||||
for frame_option in &mut self.0 {
|
||||
if frame_option.is_some() {
|
||||
return frame_option.take();
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn deallocate_frame(&mut self, frame: Frame) {
|
||||
for frame_option in &mut self.0 {
|
||||
if frame_option.is_none() {
|
||||
*frame_option = Some(frame);
|
||||
return;
|
||||
}
|
||||
}
|
||||
panic!("Tiny allocator can hold only 3 frames.");
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
pub fn flush() {
|
||||
unsafe{asm!("mov rax, cr3
|
||||
mov cr3, rax" ::: "{rax}" : "intel")}
|
||||
}
|
||||
@@ -1,97 +1,50 @@
|
||||
// Copyright 2016 Philipp Oppermann. See the README.md
|
||||
// file at the top-level directory of this distribution.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use core::ptr::Unique;
|
||||
use core::fmt;
|
||||
use spin::Mutex;
|
||||
use volatile::Volatile;
|
||||
|
||||
const BUFFER_HEIGHT: usize = 25;
|
||||
const BUFFER_WIDTH: usize = 80;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => ($crate::vga_buffer::_print(format_args!($($arg)*)));
|
||||
}
|
||||
pub static WRITER: Mutex<Writer> = Mutex::new(Writer {
|
||||
column_position: 0,
|
||||
color_code: ColorCode::new(Color::LightGreen, Color::Black),
|
||||
buffer: unsafe { Unique::new(0xb8000 as *mut _) },
|
||||
});
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! println {
|
||||
($fmt:expr) => (print!(concat!($fmt, "\n")));
|
||||
($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
|
||||
}
|
||||
|
||||
macro_rules! print {
|
||||
($($arg:tt)*) => ({
|
||||
$crate::vga_buffer::print(format_args!($($arg)*));
|
||||
});
|
||||
}
|
||||
|
||||
pub fn print(args: fmt::Arguments) {
|
||||
use core::fmt::Write;
|
||||
WRITER.lock().write_fmt(args).unwrap();
|
||||
}
|
||||
|
||||
pub fn clear_screen() {
|
||||
for _ in 0..BUFFER_HEIGHT {
|
||||
println!("");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn _print(fmt: ::core::fmt::Arguments) {
|
||||
use core::fmt::Write;
|
||||
static mut WRITER: Writer = Writer::new(Color::LightGreen, Color::Black);
|
||||
let _ = unsafe{WRITER.write_fmt(fmt)};
|
||||
}
|
||||
|
||||
pub struct Writer {
|
||||
column_position: usize,
|
||||
color_code: ColorCode,
|
||||
}
|
||||
|
||||
impl Writer {
|
||||
pub const fn new(foreground: Color, background: Color) -> Writer {
|
||||
Writer {
|
||||
column_position: 0,
|
||||
color_code: ColorCode::new(foreground, background),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_byte(&mut self, byte: u8) {
|
||||
const NEWLINE: u8 = b'\n';
|
||||
|
||||
match byte {
|
||||
NEWLINE => self.new_line(),
|
||||
byte => {
|
||||
if self.column_position >= BUFFER_WIDTH {
|
||||
self.new_line()
|
||||
}
|
||||
let row = BUFFER_HEIGHT - 1;
|
||||
let col = self.column_position;
|
||||
|
||||
Self::buffer().chars[row][col] = ScreenChar {
|
||||
ascii_character: byte,
|
||||
color_code: self.color_code,
|
||||
};
|
||||
self.column_position += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn buffer() -> &'static mut Buffer {
|
||||
const BUFFER: *mut Buffer = 0xb8000 as *mut _;
|
||||
unsafe{&mut *BUFFER}
|
||||
}
|
||||
|
||||
fn new_line(&mut self) {
|
||||
let buffer = Self::buffer();
|
||||
for row in 0..(BUFFER_HEIGHT-1) {
|
||||
buffer.chars[row] = buffer.chars[row + 1]
|
||||
}
|
||||
self.clear_row(BUFFER_HEIGHT-1);
|
||||
self.column_position = 0;
|
||||
}
|
||||
|
||||
fn clear_row(&mut self, row: usize) {
|
||||
let blank = ScreenChar {
|
||||
ascii_character: ' ' as u8,
|
||||
color_code: self.color_code,
|
||||
};
|
||||
Self::buffer().chars[row] = [blank; BUFFER_WIDTH];
|
||||
}
|
||||
}
|
||||
|
||||
impl ::core::fmt::Write for Writer {
|
||||
fn write_str(&mut self, s: &str) -> ::core::fmt::Result {
|
||||
for byte in s.bytes() {
|
||||
self.write_byte(byte)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(u8)]
|
||||
pub enum Color {
|
||||
Black = 0,
|
||||
@@ -112,7 +65,71 @@ pub enum Color {
|
||||
White = 15,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct Writer {
|
||||
column_position: usize,
|
||||
color_code: ColorCode,
|
||||
buffer: Unique<Buffer>,
|
||||
}
|
||||
|
||||
impl Writer {
|
||||
pub fn write_byte(&mut self, byte: u8) {
|
||||
match byte {
|
||||
b'\n' => self.new_line(),
|
||||
byte => {
|
||||
if self.column_position >= BUFFER_WIDTH {
|
||||
self.new_line();
|
||||
}
|
||||
let row = BUFFER_HEIGHT - 1;
|
||||
let col = self.column_position;
|
||||
|
||||
let color_code = self.color_code;
|
||||
|
||||
self.buffer().chars[row][col].write(ScreenChar {
|
||||
ascii_character: byte,
|
||||
color_code: color_code,
|
||||
});
|
||||
self.column_position += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn buffer(&mut self) -> &mut Buffer {
|
||||
unsafe { self.buffer.get_mut() }
|
||||
}
|
||||
|
||||
fn new_line(&mut self) {
|
||||
for row in 1..BUFFER_HEIGHT {
|
||||
for col in 0..BUFFER_WIDTH {
|
||||
let buffer = self.buffer();
|
||||
let character = buffer.chars[row][col].read();
|
||||
buffer.chars[row - 1][col].write(character);
|
||||
}
|
||||
}
|
||||
self.clear_row(BUFFER_HEIGHT - 1);
|
||||
self.column_position = 0;
|
||||
}
|
||||
|
||||
fn clear_row(&mut self, row: usize) {
|
||||
let blank = ScreenChar {
|
||||
ascii_character: b' ',
|
||||
color_code: self.color_code,
|
||||
};
|
||||
for col in 0..BUFFER_WIDTH {
|
||||
self.buffer().chars[row][col].write(blank);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Write for Writer {
|
||||
fn write_str(&mut self, s: &str) -> ::core::fmt::Result {
|
||||
for byte in s.bytes() {
|
||||
self.write_byte(byte)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct ColorCode(u8);
|
||||
|
||||
impl ColorCode {
|
||||
@@ -121,13 +138,13 @@ impl ColorCode {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[repr(packed)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
struct ScreenChar {
|
||||
ascii_character: u8,
|
||||
color_code: ColorCode,
|
||||
}
|
||||
|
||||
struct Buffer {
|
||||
chars: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT],
|
||||
chars: [[Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user