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 |
25
Cargo.toml
25
Cargo.toml
@@ -1,10 +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 = "*"
|
||||
[profile.dev]
|
||||
panic = "abort"
|
||||
|
||||
[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 -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/
|
||||
7
libs/bump_allocator/Cargo.toml
Normal file
7
libs/bump_allocator/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
||||
[package]
|
||||
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,33 +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
|
||||
; map first P3 entry to P2 table
|
||||
mov eax, p2_table
|
||||
or eax, 0b11 ; present + writable
|
||||
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,7 +81,6 @@ enable_paging:
|
||||
; enable paging in the cr0 register
|
||||
mov eax, cr0
|
||||
or eax, 1 << 31
|
||||
or eax, 1 << 16
|
||||
mov cr0, eax
|
||||
|
||||
ret
|
||||
@@ -93,55 +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
|
||||
p2_table:
|
||||
resb 4096
|
||||
stack_bottom:
|
||||
resb 64
|
||||
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,14 +14,51 @@ ENTRY(start)
|
||||
SECTIONS {
|
||||
. = 1M;
|
||||
|
||||
.boot :
|
||||
.rodata :
|
||||
{
|
||||
/* ensure that the multiboot header is at the beginning */
|
||||
KEEP(*(.multiboot))
|
||||
KEEP(*(.multiboot_header))
|
||||
*(.rodata .rodata.*)
|
||||
. = ALIGN(4K);
|
||||
}
|
||||
|
||||
.text :
|
||||
{
|
||||
*(.text)
|
||||
*(.text .text.*)
|
||||
. = ALIGN(4K);
|
||||
}
|
||||
|
||||
.data :
|
||||
{
|
||||
*(.data .data.*)
|
||||
. = ALIGN(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 : 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,9 +13,15 @@ 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
|
||||
; call rust main (with multiboot pointer in rdi)
|
||||
call rust_main
|
||||
.os_returned:
|
||||
; rust main returned, print `OS returned!`
|
||||
@@ -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 {}
|
||||
}
|
||||
106
src/lib.rs
106
src/lib.rs
@@ -1,46 +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)]
|
||||
#![feature(core_slice_ext, core_str_ext, core_intrinsics)]
|
||||
#![feature(lang_items)]
|
||||
#![feature(const_fn, unique)]
|
||||
#![feature(alloc, collections)]
|
||||
#![feature(asm)]
|
||||
#![no_std]
|
||||
|
||||
extern crate rlibc;
|
||||
extern crate volatile;
|
||||
extern crate spin;
|
||||
extern crate multiboot2;
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
extern crate x86;
|
||||
#[macro_use]
|
||||
extern crate once;
|
||||
extern crate bit_field;
|
||||
|
||||
use core::intrinsics::offset;
|
||||
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() {
|
||||
pub extern "C" fn rust_main(multiboot_information_address: usize) {
|
||||
// ATTENTION: we have a very small stack and no guard page
|
||||
let x = ["Hello", " ", "World", "!"];
|
||||
let screen_pointer = 0xb8000 as *const u16;
|
||||
vga_buffer::clear_screen();
|
||||
println!("Hello World{}", "!");
|
||||
|
||||
for (byte, i) in x.iter().flat_map(|s| s.bytes()).zip(0..) {
|
||||
let c = 0x1f00 | (byte as u16);
|
||||
unsafe {
|
||||
let screen_char = offset(screen_pointer, i) as *mut u16;
|
||||
*screen_char = c
|
||||
}
|
||||
let boot_info = unsafe { multiboot2::load(multiboot_information_address) };
|
||||
enable_nxe_bit();
|
||||
enable_write_protect_bit();
|
||||
|
||||
// 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") }
|
||||
}
|
||||
|
||||
loop{}
|
||||
// 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() -> ! {loop{}}
|
||||
#[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 {}
|
||||
}
|
||||
|
||||
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!()
|
||||
}
|
||||
}
|
||||
111
src/memory/mod.rs
Normal file
111
src/memory/mod.rs
Normal file
@@ -0,0 +1,111 @@
|
||||
// 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.
|
||||
|
||||
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;
|
||||
|
||||
pub const PAGE_SIZE: usize = 4096;
|
||||
|
||||
pub fn init(boot_info: &BootInformation) {
|
||||
assert_has_not_been_called!("memory::init must be called only once");
|
||||
|
||||
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 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();
|
||||
|
||||
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 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 mut active_table = paging::remap_the_kernel(&mut frame_allocator, boot_info);
|
||||
|
||||
use self::paging::Page;
|
||||
use hole_list_allocator::{HEAP_START, HEAP_SIZE};
|
||||
|
||||
let heap_start_page = Page::containing_address(HEAP_START);
|
||||
let heap_end_page = Page::containing_address(HEAP_START + HEAP_SIZE - 1);
|
||||
|
||||
for page in Page::range_inclusive(heap_start_page, heap_end_page) {
|
||||
active_table.map(page, paging::WRITABLE, &mut frame_allocator);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct Frame {
|
||||
number: usize,
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
fn containing_address(address: usize) -> Frame {
|
||||
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);
|
||||
}
|
||||
}
|
||||
227
src/memory/paging/mod.rs
Normal file
227
src/memory/paging/mod.rs
Normal file
@@ -0,0 +1,227 @@
|
||||
// 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.
|
||||
|
||||
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;
|
||||
|
||||
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 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
use x86::shared::{control_regs, tlb};
|
||||
let flush_tlb = || unsafe { tlb::flush_all() };
|
||||
|
||||
{
|
||||
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 InactivePageTable {
|
||||
p4_frame: Frame,
|
||||
}
|
||||
|
||||
impl InactivePageTable {
|
||||
pub fn new(frame: Frame,
|
||||
active_table: &mut ActivePageTable,
|
||||
temporary_page: &mut TemporaryPage)
|
||||
-> InactivePageTable {
|
||||
{
|
||||
let table = temporary_page.map_table_frame(frame.clone(), active_table);
|
||||
table.zero();
|
||||
table[511].set(frame.clone(), PRESENT | WRITABLE);
|
||||
}
|
||||
temporary_page.unmap(active_table);
|
||||
|
||||
InactivePageTable { p4_frame: frame }
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
119
src/memory/paging/table.rs
Normal file
119
src/memory/paging/table.rs
Normal file
@@ -0,0 +1,119 @@
|
||||
// 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::paging::entry::*;
|
||||
use memory::paging::ENTRY_COUNT;
|
||||
use memory::FrameAllocator;
|
||||
use core::ops::{Index, IndexMut};
|
||||
use core::marker::PhantomData;
|
||||
|
||||
pub const P4: *mut Table<Level4> = 0xffffffff_fffff000 as *mut _;
|
||||
|
||||
pub struct Table<L: TableLevel> {
|
||||
entries: [Entry; ENTRY_COUNT],
|
||||
level: PhantomData<L>,
|
||||
}
|
||||
|
||||
impl<L> Table<L>
|
||||
where L: TableLevel
|
||||
{
|
||||
pub fn zero(&mut self) {
|
||||
for entry in self.entries.iter_mut() {
|
||||
entry.set_unused();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<L> Table<L>
|
||||
where L: HierarchicalLevel
|
||||
{
|
||||
fn next_table_address(&self, index: usize) -> Option<usize> {
|
||||
let entry_flags = self[index].flags();
|
||||
if entry_flags.contains(PRESENT) && !entry_flags.contains(HUGE_PAGE) {
|
||||
let table_address = self as *const _ as usize;
|
||||
Some((table_address << 9) | (index << 12))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_table(&self, index: usize) -> Option<&Table<L::NextLevel>> {
|
||||
self.next_table_address(index)
|
||||
.map(|address| unsafe { &*(address as *const _) })
|
||||
}
|
||||
|
||||
pub fn next_table_mut(&mut self, index: usize) -> Option<&mut Table<L::NextLevel>> {
|
||||
self.next_table_address(index)
|
||||
.map(|address| unsafe { &mut *(address as *mut _) })
|
||||
}
|
||||
|
||||
pub fn next_table_create<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();
|
||||
}
|
||||
self.next_table_mut(index).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<L> Index<usize> for Table<L>
|
||||
where L: TableLevel
|
||||
{
|
||||
type Output = Entry;
|
||||
|
||||
fn index(&self, index: usize) -> &Entry {
|
||||
&self.entries[index]
|
||||
}
|
||||
}
|
||||
|
||||
impl<L> IndexMut<usize> for Table<L>
|
||||
where L: TableLevel
|
||||
{
|
||||
fn index_mut(&mut self, index: usize) -> &mut Entry {
|
||||
&mut self.entries[index]
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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.");
|
||||
}
|
||||
}
|
||||
150
src/vga_buffer.rs
Normal file
150
src/vga_buffer.rs
Normal file
@@ -0,0 +1,150 @@
|
||||
// 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;
|
||||
|
||||
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_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!("");
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(u8)]
|
||||
pub enum Color {
|
||||
Black = 0,
|
||||
Blue = 1,
|
||||
Green = 2,
|
||||
Cyan = 3,
|
||||
Red = 4,
|
||||
Magenta = 5,
|
||||
Brown = 6,
|
||||
LightGray = 7,
|
||||
DarkGray = 8,
|
||||
LightBlue = 9,
|
||||
LightGreen = 10,
|
||||
LightCyan = 11,
|
||||
LightRed = 12,
|
||||
Pink = 13,
|
||||
Yellow = 14,
|
||||
White = 15,
|
||||
}
|
||||
|
||||
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 {
|
||||
const fn new(foreground: Color, background: Color) -> ColorCode {
|
||||
ColorCode((background as u8) << 4 | (foreground as u8))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[repr(C)]
|
||||
struct ScreenChar {
|
||||
ascii_character: u8,
|
||||
color_code: ColorCode,
|
||||
}
|
||||
|
||||
struct Buffer {
|
||||
chars: [[Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
|
||||
}
|
||||
Reference in New Issue
Block a user