mirror of
https://github.com/phil-opp/blog_os.git
synced 2025-12-16 22:37:49 +00:00
Compare commits
19 Commits
post-12-te
...
post-12-wi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3fc7bf6aa1 | ||
|
|
b337f65abb | ||
|
|
22c6bd5aa7 | ||
|
|
87719f2260 | ||
|
|
0caf5c351e | ||
|
|
cb7bb0ddef | ||
|
|
7ad30651fb | ||
|
|
49923acb3f | ||
|
|
f2b1f3a593 | ||
|
|
5e2e0b629e | ||
|
|
35379c90e6 | ||
|
|
e5d10fcaec | ||
|
|
e1242a867f | ||
|
|
cd138a3a1b | ||
|
|
11a0eb679c | ||
|
|
241c1ab2c9 | ||
|
|
b75406b37e | ||
|
|
c3450b6df7 | ||
|
|
ce1fdcf768 |
201
Cargo.lock
generated
201
Cargo.lock
generated
@@ -1,10 +1,13 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
# This file is automatically @generated by Cargo.
|
||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "array-init"
|
||||||
version = "1.0.0"
|
version = "0.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d"
|
checksum = "23589ecb866b460d3a0f1278834750268c607e8e28a1b982c907219f3178cd72"
|
||||||
|
dependencies = [
|
||||||
|
"nodrop",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bit_field"
|
name = "bit_field"
|
||||||
@@ -23,9 +26,6 @@ name = "blog_os"
|
|||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bootloader",
|
"bootloader",
|
||||||
"conquer-once",
|
|
||||||
"crossbeam-queue",
|
|
||||||
"futures-util",
|
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"linked_list_allocator",
|
"linked_list_allocator",
|
||||||
"pc-keyboard",
|
"pc-keyboard",
|
||||||
@@ -33,84 +33,29 @@ dependencies = [
|
|||||||
"spin",
|
"spin",
|
||||||
"uart_16550",
|
"uart_16550",
|
||||||
"volatile",
|
"volatile",
|
||||||
"x86_64 0.11.1",
|
"x86_64 0.8.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bootloader"
|
name = "bootloader"
|
||||||
version = "0.9.3"
|
version = "0.8.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "44ac0bdf4930c3c4d7f0d04eb6f15d7dcb9d5972b1ff9cd2bee0128112260fc7"
|
checksum = "d596849a47f28abdea62d7a6a25c4f6e69c3d9b09b0a2877db6e9cda004ca993"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cast"
|
||||||
version = "0.1.10"
|
version = "0.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
checksum = "4b9434b9a5aa1450faa3f9cb14ea0e8c53bb5d2b3c1bfd1ab4fc03e9f33fbfb0"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "conquer-once"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6f7644600a548ecad74e4a918392af1798f7dd045be610be3203b9e129b4f98f"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"conquer-util",
|
"rustc_version",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "conquer-util"
|
|
||||||
version = "0.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "654fb2472cc369d311c547103a1fa81d467bef370ae7a0680f65939895b1182a"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cpuio"
|
name = "cpuio"
|
||||||
version = "0.3.0"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d531514efb06912141fa65967447de805691b685a7565c87d1765afe34a98aa7"
|
checksum = "22b8e308ccfc5acf3b82f79c0eac444cf6114cb2ac67a230ca6c177210068daa"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-queue"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-utils"
|
|
||||||
version = "0.7.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"cfg-if",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-core"
|
|
||||||
version = "0.3.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f25592f769825e89b92358db00d26f965761e094951ac44d3663ef25b7ac464a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-task"
|
|
||||||
version = "0.3.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7b0a34e53cf6cdcd0178aa573aed466b646eb3db769570841fda0c7ede375a27"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures-util"
|
|
||||||
version = "0.3.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "22766cf25d64306bedf0384da004d05c9974ab104fcc4528f1236181c18004c5"
|
|
||||||
dependencies = [
|
|
||||||
"futures-core",
|
|
||||||
"futures-task",
|
|
||||||
"pin-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
@@ -123,66 +68,57 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "linked_list_allocator"
|
name = "linked_list_allocator"
|
||||||
version = "0.8.0"
|
version = "0.6.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "18c618c431dfe4419afbe22852f6aceffbc17bd82ba0a18b982def291000824c"
|
checksum = "47314ec1d29aa869ee7cb5a5be57be9b1055c56567d59c3fb6689926743e0bea"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"spinning_top",
|
"spin",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lock_api"
|
name = "nodrop"
|
||||||
version = "0.3.3"
|
version = "0.1.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "79b2de95ecb4691949fea4716ca53cdbcfccb2c612e19644a8bad05edcf9f47b"
|
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
|
||||||
dependencies = [
|
|
||||||
"scopeguard",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pc-keyboard"
|
name = "pc-keyboard"
|
||||||
version = "0.5.0"
|
version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c48392db76c4e9a69e0b3be356c5f97ebb7b14413c5e4fd0af4755dbf86e2fce"
|
checksum = "fff50ab09ba31bcebc0669f4e64c0952fae1acdca9e6e0587e68e4e8443808ac"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pic8259_simple"
|
name = "pic8259_simple"
|
||||||
version = "0.2.0"
|
version = "0.1.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "af2a5497fb8e59bf8015f67b7dff238d75ef445e03f23edac24ac3a8f09be952"
|
checksum = "dc64b2fd10828da8521b6cdabe0679385d7d2a3a6d4c336b819d1fa31ba35c72"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cpuio",
|
"cpuio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pin-utils"
|
name = "rustc_version"
|
||||||
version = "0.1.0-alpha.4"
|
version = "0.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5894c618ce612a3fa23881b152b608bafb8c56cfc22f434a3ba3120b40f7b587"
|
checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro2"
|
|
||||||
version = "1.0.18"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "beae6331a816b1f65d04c45b078fd8e6c93e8071771f41b8163255bbd8d7c8fa"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"unicode-xid",
|
"semver",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "semver"
|
||||||
version = "1.0.7"
|
version = "0.9.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
|
checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"semver-parser",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "scopeguard"
|
name = "semver-parser"
|
||||||
version = "1.1.0"
|
version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "spin"
|
name = "spin"
|
||||||
@@ -190,41 +126,21 @@ version = "0.5.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "spinning_top"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "32d801a3a53bcf5071f85fef8d5cab9e5f638fc5580a37e6eb7aba4b37438d24"
|
|
||||||
dependencies = [
|
|
||||||
"lock_api",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "1.0.33"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e8d5d96e8cbb005d6959f119f773bfaebb5684296108fb32600c00cde305b2cd"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-xid",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "uart_16550"
|
name = "uart_16550"
|
||||||
version = "0.2.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e58fc40dc1712664fc9b0a7bd8ca2f21ab49960924fb245a80a05e1e92f3dfe9"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags",
|
|
||||||
"x86_64 0.11.0",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-xid"
|
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"
|
checksum = "803ea8cb602dbb32c1a657a866d2dd79fe7dbeab0fb2ac667cb4dcc7de12a58b"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"x86_64 0.7.7",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ux"
|
||||||
|
version = "0.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "88dfeb711b61ce620c0cb6fd9f8e3e678622f0c971da2a63c4b3e25e88ed012f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "volatile"
|
name = "volatile"
|
||||||
@@ -234,28 +150,25 @@ checksum = "6af0edf5b4faacc31fc51159244d78d65ec580f021afcef7bd53c04aeabc7f29"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "x86_64"
|
name = "x86_64"
|
||||||
version = "0.11.0"
|
version = "0.7.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "365de37eb7c6da582cbb510dd0f3f1235d24ff6309a8a96e8a9909cc9bfd608f"
|
checksum = "1f27d9168654aee1b0c1b73746caeb4aa33248f8b8c8f6e100e697fcc2a794b2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"array-init",
|
||||||
"bit_field",
|
"bit_field",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
|
"cast",
|
||||||
|
"ux",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "x86_64"
|
name = "x86_64"
|
||||||
version = "0.11.1"
|
version = "0.8.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f21672dcbed52bc09eea030d189600c0189c66c97bc5b31779eb780e064a201f"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"array-init",
|
||||||
"bit_field",
|
"bit_field",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"x86_64-idt-general-handler",
|
"cast",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "x86_64-idt-general-handler"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
]
|
||||||
|
|||||||
26
Cargo.toml
26
Cargo.toml
@@ -13,32 +13,19 @@ name = "stack_overflow"
|
|||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bootloader = { version = "0.9.3", features = ["map_physical_memory"]}
|
bootloader = { version = "0.8.0", features = ["map_physical_memory"]}
|
||||||
volatile = "0.2.6"
|
volatile = "0.2.6"
|
||||||
spin = "0.5.2"
|
spin = "0.5.2"
|
||||||
x86_64 = { path = "../../x86_64" }
|
x86_64 = "0.8.1"
|
||||||
uart_16550 = "0.2.0"
|
uart_16550 = "0.2.0"
|
||||||
pic8259_simple = "0.2.0"
|
pic8259_simple = "0.1.1"
|
||||||
pc-keyboard = "0.5.0"
|
pc-keyboard = "0.3.1"
|
||||||
linked_list_allocator = "0.8.0"
|
linked_list_allocator = "0.6.4"
|
||||||
|
|
||||||
[dependencies.lazy_static]
|
[dependencies.lazy_static]
|
||||||
version = "1.0"
|
version = "1.0"
|
||||||
features = ["spin_no_std"]
|
features = ["spin_no_std"]
|
||||||
|
|
||||||
[dependencies.crossbeam-queue]
|
|
||||||
version = "0.2.1"
|
|
||||||
default-features = false
|
|
||||||
features = ["alloc"]
|
|
||||||
|
|
||||||
[dependencies.conquer-once]
|
|
||||||
version = "0.2.0"
|
|
||||||
default-features = false
|
|
||||||
|
|
||||||
[dependencies.futures-util]
|
|
||||||
version = "0.3.4"
|
|
||||||
default-features = false
|
|
||||||
features = ["alloc"]
|
|
||||||
|
|
||||||
[package.metadata.bootimage]
|
[package.metadata.bootimage]
|
||||||
test-args = [
|
test-args = [
|
||||||
@@ -46,3 +33,6 @@ test-args = [
|
|||||||
"-display", "none"
|
"-display", "none"
|
||||||
]
|
]
|
||||||
test-success-exit-code = 33 # (0x10 << 1) | 1
|
test-success-exit-code = 33 # (0x10 << 1) | 1
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
# Blog OS (Async/Await)
|
# Blog OS (Heap Allocation)
|
||||||
|
|
||||||
[](https://github.com/phil-opp/blog_os/actions?query=workflow%3A%22Build+Code%22+branch%3Apost-12)
|
[](https://github.com/phil-opp/blog_os/actions?query=workflow%3A%22Build+Code%22+branch%3Apost-10)
|
||||||
|
|
||||||
This repository contains the source code for the [Async/Await][post] post of the [Writing an OS in Rust](https://os.phil-opp.com) series.
|
This repository contains the source code for the [Heap Allocation][post] post of the [Writing an OS in Rust](https://os.phil-opp.com) series.
|
||||||
|
|
||||||
[post]: https://os.phil-opp.com/async-await/
|
[post]: https://os.phil-opp.com/heap-allocation/
|
||||||
|
|
||||||
**Check out the [master branch](https://github.com/phil-opp/blog_os) for more information.**
|
**Check out the [master branch](https://github.com/phil-opp/blog_os) for more information.**
|
||||||
|
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ impl BumpAllocator {
|
|||||||
/// memory range is unused. Also, this method must be called only once.
|
/// memory range is unused. Also, this method must be called only once.
|
||||||
pub unsafe fn init(&mut self, heap_start: usize, heap_size: usize) {
|
pub unsafe fn init(&mut self, heap_start: usize, heap_size: usize) {
|
||||||
self.heap_start = heap_start;
|
self.heap_start = heap_start;
|
||||||
self.heap_end = heap_start.saturating_add(heap_size);
|
self.heap_end = heap_start + heap_size;
|
||||||
self.next = heap_start;
|
self.next = heap_start;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -36,10 +36,7 @@ unsafe impl GlobalAlloc for Locked<BumpAllocator> {
|
|||||||
let mut bump = self.lock(); // get a mutable reference
|
let mut bump = self.lock(); // get a mutable reference
|
||||||
|
|
||||||
let alloc_start = align_up(bump.next, layout.align());
|
let alloc_start = align_up(bump.next, layout.align());
|
||||||
let alloc_end = match alloc_start.checked_add(layout.size()) {
|
let alloc_end = alloc_start + layout.size();
|
||||||
Some(end) => end,
|
|
||||||
None => return ptr::null_mut(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if alloc_end > bump.heap_end {
|
if alloc_end > bump.heap_end {
|
||||||
ptr::null_mut() // out of memory
|
ptr::null_mut() // out of memory
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ impl LinkedListAllocator {
|
|||||||
/// Adds the given memory region to the front of the list.
|
/// Adds the given memory region to the front of the list.
|
||||||
unsafe fn add_free_region(&mut self, addr: usize, size: usize) {
|
unsafe fn add_free_region(&mut self, addr: usize, size: usize) {
|
||||||
// ensure that the freed region is capable of holding ListNode
|
// ensure that the freed region is capable of holding ListNode
|
||||||
assert_eq!(align_up(addr, mem::align_of::<ListNode>()), addr);
|
assert!(align_up(addr, mem::align_of::<ListNode>()) == addr);
|
||||||
assert!(size >= mem::size_of::<ListNode>());
|
assert!(size >= mem::size_of::<ListNode>());
|
||||||
|
|
||||||
// create a new list node and append it at the start of the list
|
// create a new list node and append it at the start of the list
|
||||||
@@ -86,7 +86,7 @@ impl LinkedListAllocator {
|
|||||||
/// Returns the allocation start address on success.
|
/// Returns the allocation start address on success.
|
||||||
fn alloc_from_region(region: &ListNode, size: usize, align: usize) -> Result<usize, ()> {
|
fn alloc_from_region(region: &ListNode, size: usize, align: usize) -> Result<usize, ()> {
|
||||||
let alloc_start = align_up(region.start_addr(), align);
|
let alloc_start = align_up(region.start_addr(), align);
|
||||||
let alloc_end = alloc_start.checked_add(size).ok_or(())?;
|
let alloc_end = alloc_start + size;
|
||||||
|
|
||||||
if alloc_end > region.end_addr() {
|
if alloc_end > region.end_addr() {
|
||||||
// region too small
|
// region too small
|
||||||
@@ -122,10 +122,10 @@ unsafe impl GlobalAlloc for Locked<LinkedListAllocator> {
|
|||||||
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
|
||||||
// perform layout adjustments
|
// perform layout adjustments
|
||||||
let (size, align) = LinkedListAllocator::size_align(layout);
|
let (size, align) = LinkedListAllocator::size_align(layout);
|
||||||
let mut allocator = self.lock();
|
let mut allocator = self.inner.lock();
|
||||||
|
|
||||||
if let Some((region, alloc_start)) = allocator.find_region(size, align) {
|
if let Some((region, alloc_start)) = allocator.find_region(size, align) {
|
||||||
let alloc_end = alloc_start.checked_add(size).expect("overflow");
|
let alloc_end = alloc_start + size;
|
||||||
let excess_size = region.end_addr() - alloc_end;
|
let excess_size = region.end_addr() - alloc_end;
|
||||||
if excess_size > 0 {
|
if excess_size > 0 {
|
||||||
allocator.add_free_region(alloc_end, excess_size);
|
allocator.add_free_region(alloc_end, excess_size);
|
||||||
@@ -140,6 +140,6 @@ unsafe impl GlobalAlloc for Locked<LinkedListAllocator> {
|
|||||||
// perform layout adjustments
|
// perform layout adjustments
|
||||||
let (size, _) = LinkedListAllocator::size_align(layout);
|
let (size, _) = LinkedListAllocator::size_align(layout);
|
||||||
|
|
||||||
self.lock().add_free_region(ptr as usize, size)
|
self.inner.lock().add_free_region(ptr as usize, size)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ static ALLOCATOR: Locked<FixedSizeBlockAllocator> = Locked::new(FixedSizeBlockAl
|
|||||||
pub fn init_heap(
|
pub fn init_heap(
|
||||||
mapper: &mut impl Mapper<Size4KiB>,
|
mapper: &mut impl Mapper<Size4KiB>,
|
||||||
frame_allocator: &mut impl FrameAllocator<Size4KiB>,
|
frame_allocator: &mut impl FrameAllocator<Size4KiB>,
|
||||||
) -> Result<(), MapToError<Size4KiB>> {
|
) -> Result<(), MapToError> {
|
||||||
let page_range = {
|
let page_range = {
|
||||||
let heap_start = VirtAddr::new(HEAP_START as u64);
|
let heap_start = VirtAddr::new(HEAP_START as u64);
|
||||||
let heap_end = heap_start + HEAP_SIZE - 1u64;
|
let heap_end = heap_start + HEAP_SIZE - 1u64;
|
||||||
@@ -35,7 +35,7 @@ pub fn init_heap(
|
|||||||
.allocate_frame()
|
.allocate_frame()
|
||||||
.ok_or(MapToError::FrameAllocationFailed)?;
|
.ok_or(MapToError::FrameAllocationFailed)?;
|
||||||
let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE;
|
let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE;
|
||||||
unsafe { mapper.map_to(page, frame, flags, frame_allocator)?.flush() };
|
mapper.map_to(page, frame, flags, frame_allocator)?.flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -74,9 +74,11 @@ impl<A> Locked<A> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Align the given address `addr` upwards to alignment `align`.
|
|
||||||
///
|
|
||||||
/// Requires that `align` is a power of two.
|
|
||||||
fn align_up(addr: usize, align: usize) -> usize {
|
fn align_up(addr: usize, align: usize) -> usize {
|
||||||
(addr + align - 1) & !(align - 1)
|
let remainder = addr % align;
|
||||||
|
if remainder == 0 {
|
||||||
|
addr // addr already aligned
|
||||||
|
} else {
|
||||||
|
addr - remainder + align
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,9 +2,7 @@ use crate::{gdt, hlt_loop, print, println};
|
|||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use pic8259_simple::ChainedPics;
|
use pic8259_simple::ChainedPics;
|
||||||
use spin;
|
use spin;
|
||||||
use x86_64::structures::idt::{
|
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode};
|
||||||
self, InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const PIC_1_OFFSET: u8 = 32;
|
pub const PIC_1_OFFSET: u8 = 32;
|
||||||
pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8;
|
pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8;
|
||||||
@@ -32,8 +30,6 @@ pub static PICS: spin::Mutex<ChainedPics> =
|
|||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref IDT: InterruptDescriptorTable = {
|
static ref IDT: InterruptDescriptorTable = {
|
||||||
let mut idt = InterruptDescriptorTable::new();
|
let mut idt = InterruptDescriptorTable::new();
|
||||||
|
|
||||||
idt::set_general_handler!(&mut idt, default_handler);
|
|
||||||
idt.breakpoint.set_handler_fn(breakpoint_handler);
|
idt.breakpoint.set_handler_fn(breakpoint_handler);
|
||||||
idt.page_fault.set_handler_fn(page_fault_handler);
|
idt.page_fault.set_handler_fn(page_fault_handler);
|
||||||
unsafe {
|
unsafe {
|
||||||
@@ -41,6 +37,8 @@ lazy_static! {
|
|||||||
.set_handler_fn(double_fault_handler)
|
.set_handler_fn(double_fault_handler)
|
||||||
.set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX);
|
.set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX);
|
||||||
}
|
}
|
||||||
|
idt[InterruptIndex::Timer.as_usize()].set_handler_fn(timer_interrupt_handler);
|
||||||
|
idt[InterruptIndex::Keyboard.as_usize()].set_handler_fn(keyboard_interrupt_handler);
|
||||||
idt
|
idt
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -49,18 +47,6 @@ pub fn init_idt() {
|
|||||||
IDT.load();
|
IDT.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_handler(stack_frame: &mut InterruptStackFrame, index: u8, error_code: Option<u64>) {
|
|
||||||
if index == 32 {
|
|
||||||
print!("{} ", index);
|
|
||||||
} else {
|
|
||||||
println!("INTERRUPT {}: \n{:#?}", index, stack_frame);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
PICS.lock().notify_end_of_interrupt(index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "x86-interrupt" fn breakpoint_handler(stack_frame: &mut InterruptStackFrame) {
|
extern "x86-interrupt" fn breakpoint_handler(stack_frame: &mut InterruptStackFrame) {
|
||||||
println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
|
println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
|
||||||
}
|
}
|
||||||
@@ -91,14 +77,31 @@ extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: &mut InterruptSt
|
|||||||
PICS.lock()
|
PICS.lock()
|
||||||
.notify_end_of_interrupt(InterruptIndex::Timer.as_u8());
|
.notify_end_of_interrupt(InterruptIndex::Timer.as_u8());
|
||||||
}
|
}
|
||||||
|
crate::multitasking::invoke_scheduler();
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: &mut InterruptStackFrame) {
|
extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: &mut InterruptStackFrame) {
|
||||||
|
use pc_keyboard::{layouts, DecodedKey, Keyboard, ScancodeSet1};
|
||||||
|
use spin::Mutex;
|
||||||
use x86_64::instructions::port::Port;
|
use x86_64::instructions::port::Port;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref KEYBOARD: Mutex<Keyboard<layouts::Us104Key, ScancodeSet1>> =
|
||||||
|
Mutex::new(Keyboard::new(layouts::Us104Key, ScancodeSet1));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut keyboard = KEYBOARD.lock();
|
||||||
let mut port = Port::new(0x60);
|
let mut port = Port::new(0x60);
|
||||||
|
|
||||||
let scancode: u8 = unsafe { port.read() };
|
let scancode: u8 = unsafe { port.read() };
|
||||||
crate::task::keyboard::add_scancode(scancode);
|
if let Ok(Some(key_event)) = keyboard.add_byte(scancode) {
|
||||||
|
if let Some(key) = keyboard.process_keyevent(key_event) {
|
||||||
|
match key {
|
||||||
|
DecodedKey::Unicode(character) => print!("{}", character),
|
||||||
|
DecodedKey::RawKey(key) => print!("{:?}", key),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
PICS.lock()
|
PICS.lock()
|
||||||
@@ -106,8 +109,13 @@ extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: &mut Interrup
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::{serial_print, serial_println};
|
||||||
|
|
||||||
#[test_case]
|
#[test_case]
|
||||||
fn test_breakpoint_exception() {
|
fn test_breakpoint_exception() {
|
||||||
|
serial_print!("test_breakpoint_exception...");
|
||||||
// invoke a breakpoint exception
|
// invoke a breakpoint exception
|
||||||
x86_64::instructions::interrupts::int3();
|
x86_64::instructions::interrupts::int3();
|
||||||
|
serial_println!("[ok]");
|
||||||
}
|
}
|
||||||
|
|||||||
28
src/lib.rs
28
src/lib.rs
@@ -4,8 +4,14 @@
|
|||||||
#![feature(abi_x86_interrupt)]
|
#![feature(abi_x86_interrupt)]
|
||||||
#![feature(alloc_error_handler)]
|
#![feature(alloc_error_handler)]
|
||||||
#![feature(const_fn)]
|
#![feature(const_fn)]
|
||||||
|
#![feature(alloc_layout_extra)]
|
||||||
#![feature(const_in_array_repeat_expressions)]
|
#![feature(const_in_array_repeat_expressions)]
|
||||||
#![feature(wake_trait)]
|
#![feature(global_asm)]
|
||||||
|
#![feature(asm)]
|
||||||
|
#![feature(raw)]
|
||||||
|
#![feature(never_type)]
|
||||||
|
#![feature(naked_functions)]
|
||||||
|
#![feature(option_expect_none)]
|
||||||
#![test_runner(crate::test_runner)]
|
#![test_runner(crate::test_runner)]
|
||||||
#![reexport_test_harness_main = "test_main"]
|
#![reexport_test_harness_main = "test_main"]
|
||||||
|
|
||||||
@@ -17,8 +23,8 @@ pub mod allocator;
|
|||||||
pub mod gdt;
|
pub mod gdt;
|
||||||
pub mod interrupts;
|
pub mod interrupts;
|
||||||
pub mod memory;
|
pub mod memory;
|
||||||
|
pub mod multitasking;
|
||||||
pub mod serial;
|
pub mod serial;
|
||||||
pub mod task;
|
|
||||||
pub mod vga_buffer;
|
pub mod vga_buffer;
|
||||||
|
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
@@ -27,25 +33,11 @@ pub fn init() {
|
|||||||
unsafe { interrupts::PICS.lock().initialize() };
|
unsafe { interrupts::PICS.lock().initialize() };
|
||||||
x86_64::instructions::interrupts::enable();
|
x86_64::instructions::interrupts::enable();
|
||||||
}
|
}
|
||||||
pub trait Testable {
|
|
||||||
fn run(&self) -> ();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Testable for T
|
pub fn test_runner(tests: &[&dyn Fn()]) {
|
||||||
where
|
|
||||||
T: Fn(),
|
|
||||||
{
|
|
||||||
fn run(&self) {
|
|
||||||
serial_print!("{}...\t", core::any::type_name::<T>());
|
|
||||||
self();
|
|
||||||
serial_println!("[ok]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn test_runner(tests: &[&dyn Testable]) {
|
|
||||||
serial_println!("Running {} tests", tests.len());
|
serial_println!("Running {} tests", tests.len());
|
||||||
for test in tests {
|
for test in tests {
|
||||||
test.run();
|
test();
|
||||||
}
|
}
|
||||||
exit_qemu(QemuExitCode::Success);
|
exit_qemu(QemuExitCode::Success);
|
||||||
}
|
}
|
||||||
|
|||||||
80
src/main.rs
80
src/main.rs
@@ -6,8 +6,9 @@
|
|||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
use blog_os::println;
|
use alloc::{boxed::Box, rc::Rc, vec, vec::Vec};
|
||||||
use blog_os::task::{executor::Executor, keyboard, Task};
|
use blog_os::multitasking::{self, thread::Thread, with_scheduler};
|
||||||
|
use blog_os::{print, println};
|
||||||
use bootloader::{entry_point, BootInfo};
|
use bootloader::{entry_point, BootInfo};
|
||||||
use core::panic::PanicInfo;
|
use core::panic::PanicInfo;
|
||||||
|
|
||||||
@@ -27,19 +28,72 @@ fn kernel_main(boot_info: &'static BootInfo) -> ! {
|
|||||||
|
|
||||||
allocator::init_heap(&mut mapper, &mut frame_allocator).expect("heap initialization failed");
|
allocator::init_heap(&mut mapper, &mut frame_allocator).expect("heap initialization failed");
|
||||||
|
|
||||||
|
// allocate a number on the heap
|
||||||
|
let heap_value = Box::new(41);
|
||||||
|
println!("heap_value at {:p}", heap_value);
|
||||||
|
|
||||||
|
// create a dynamically sized vector
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
for i in 0..500 {
|
||||||
|
vec.push(i);
|
||||||
|
}
|
||||||
|
println!("vec at {:p}", vec.as_slice());
|
||||||
|
|
||||||
|
// create a reference counted vector -> will be freed when count reaches 0
|
||||||
|
let reference_counted = Rc::new(vec![1, 2, 3]);
|
||||||
|
let cloned_reference = reference_counted.clone();
|
||||||
|
println!(
|
||||||
|
"current reference count is {}",
|
||||||
|
Rc::strong_count(&cloned_reference)
|
||||||
|
);
|
||||||
|
core::mem::drop(reference_counted);
|
||||||
|
println!(
|
||||||
|
"reference count is {} now",
|
||||||
|
Rc::strong_count(&cloned_reference)
|
||||||
|
);
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
test_main();
|
test_main();
|
||||||
|
|
||||||
let mut executor = Executor::new();
|
let idle_thread = Thread::create(idle_thread, 2, &mut mapper, &mut frame_allocator).unwrap();
|
||||||
executor.spawn(Task::new(example_task()));
|
with_scheduler(|s| s.set_idle_thread(idle_thread));
|
||||||
executor.spawn(Task::new(keyboard::print_keypresses()));
|
|
||||||
executor.run();
|
for _ in 0..10 {
|
||||||
|
let thread = Thread::create(thread_entry, 2, &mut mapper, &mut frame_allocator).unwrap();
|
||||||
|
with_scheduler(|s| s.add_new_thread(thread));
|
||||||
|
}
|
||||||
|
let thread =
|
||||||
|
Thread::create_from_closure(|| thread_entry(), 2, &mut mapper, &mut frame_allocator)
|
||||||
|
.unwrap();
|
||||||
|
with_scheduler(|s| s.add_new_thread(thread));
|
||||||
|
|
||||||
|
println!("It did not crash!");
|
||||||
|
thread_entry();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn idle_thread() -> ! {
|
||||||
|
loop {
|
||||||
|
x86_64::instructions::hlt();
|
||||||
|
multitasking::yield_now();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn thread_entry() -> ! {
|
||||||
|
let thread_id = with_scheduler(|s| s.current_thread_id()).as_u64();
|
||||||
|
for _ in 0..=thread_id {
|
||||||
|
print!("{}", thread_id);
|
||||||
|
x86_64::instructions::hlt();
|
||||||
|
}
|
||||||
|
multitasking::exit_thread();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function is called on panic.
|
/// This function is called on panic.
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(info: &PanicInfo) -> ! {
|
fn panic(info: &PanicInfo) -> ! {
|
||||||
|
unsafe {
|
||||||
|
blog_os::vga_buffer::WRITER.force_unlock();
|
||||||
|
}
|
||||||
println!("{}", info);
|
println!("{}", info);
|
||||||
blog_os::hlt_loop();
|
blog_os::hlt_loop();
|
||||||
}
|
}
|
||||||
@@ -49,17 +103,3 @@ fn panic(info: &PanicInfo) -> ! {
|
|||||||
fn panic(info: &PanicInfo) -> ! {
|
fn panic(info: &PanicInfo) -> ! {
|
||||||
blog_os::test_panic_handler(info)
|
blog_os::test_panic_handler(info)
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn async_number() -> u32 {
|
|
||||||
42
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn example_task() {
|
|
||||||
let number = async_number().await;
|
|
||||||
println!("async number: {}", number);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test_case]
|
|
||||||
fn trivial_assertion() {
|
|
||||||
assert_eq!(1, 1);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
use bootloader::bootinfo::{MemoryMap, MemoryRegionType};
|
use bootloader::bootinfo::{MemoryMap, MemoryRegionType};
|
||||||
use x86_64::{
|
use x86_64::{
|
||||||
structures::paging::{
|
structures::paging::{
|
||||||
FrameAllocator, Mapper, OffsetPageTable, Page, PageTable, PhysFrame, Size4KiB,
|
mapper, FrameAllocator, Mapper, OffsetPageTable, Page, PageTable, PhysFrame, Size4KiB,
|
||||||
|
UnusedPhysFrame,
|
||||||
},
|
},
|
||||||
PhysAddr, VirtAddr,
|
PhysAddr, VirtAddr,
|
||||||
};
|
};
|
||||||
@@ -35,6 +36,54 @@ unsafe fn active_level_4_table(physical_memory_offset: VirtAddr) -> &'static mut
|
|||||||
&mut *page_table_ptr // unsafe
|
&mut *page_table_ptr // unsafe
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub struct StackBounds {
|
||||||
|
start: VirtAddr,
|
||||||
|
end: VirtAddr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StackBounds {
|
||||||
|
pub fn start(&self) -> VirtAddr {
|
||||||
|
self.start
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn end(&self) -> VirtAddr {
|
||||||
|
self.end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn alloc_stack(
|
||||||
|
size_in_pages: u64,
|
||||||
|
mapper: &mut impl Mapper<Size4KiB>,
|
||||||
|
frame_allocator: &mut impl FrameAllocator<Size4KiB>,
|
||||||
|
) -> Result<StackBounds, mapper::MapToError> {
|
||||||
|
use core::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
use x86_64::structures::paging::PageTableFlags as Flags;
|
||||||
|
|
||||||
|
static STACK_ALLOC_NEXT: AtomicU64 = AtomicU64::new(0x_5555_5555_0000);
|
||||||
|
|
||||||
|
let guard_page_start = STACK_ALLOC_NEXT.fetch_add(
|
||||||
|
(size_in_pages + 1) * Page::<Size4KiB>::SIZE,
|
||||||
|
Ordering::SeqCst,
|
||||||
|
);
|
||||||
|
let guard_page = Page::from_start_address(VirtAddr::new(guard_page_start))
|
||||||
|
.expect("`STACK_ALLOC_NEXT` not page aligned");
|
||||||
|
|
||||||
|
let stack_start = guard_page + 1;
|
||||||
|
let stack_end = stack_start + size_in_pages;
|
||||||
|
let flags = Flags::PRESENT | Flags::WRITABLE;
|
||||||
|
for page in Page::range(stack_start, stack_end) {
|
||||||
|
let frame = frame_allocator
|
||||||
|
.allocate_frame()
|
||||||
|
.ok_or(mapper::MapToError::FrameAllocationFailed)?;
|
||||||
|
mapper.map_to(page, frame, flags, frame_allocator)?.flush();
|
||||||
|
}
|
||||||
|
Ok(StackBounds {
|
||||||
|
start: stack_start.start_address(),
|
||||||
|
end: stack_end.start_address(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Creates an example mapping for the given page to frame `0xb8000`.
|
/// Creates an example mapping for the given page to frame `0xb8000`.
|
||||||
pub fn create_example_mapping(
|
pub fn create_example_mapping(
|
||||||
page: Page,
|
page: Page,
|
||||||
@@ -44,12 +93,11 @@ pub fn create_example_mapping(
|
|||||||
use x86_64::structures::paging::PageTableFlags as Flags;
|
use x86_64::structures::paging::PageTableFlags as Flags;
|
||||||
|
|
||||||
let frame = PhysFrame::containing_address(PhysAddr::new(0xb8000));
|
let frame = PhysFrame::containing_address(PhysAddr::new(0xb8000));
|
||||||
|
// FIXME: ONLY FOR TEMPORARY TESTING
|
||||||
|
let unused_frame = unsafe { UnusedPhysFrame::new(frame) };
|
||||||
let flags = Flags::PRESENT | Flags::WRITABLE;
|
let flags = Flags::PRESENT | Flags::WRITABLE;
|
||||||
|
|
||||||
let map_to_result = unsafe {
|
let map_to_result = mapper.map_to(page, unused_frame, flags, frame_allocator);
|
||||||
// FIXME: this is not safe, we do it only for testing
|
|
||||||
mapper.map_to(page, frame, flags, frame_allocator)
|
|
||||||
};
|
|
||||||
map_to_result.expect("map_to failed").flush();
|
map_to_result.expect("map_to failed").flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,7 +105,7 @@ pub fn create_example_mapping(
|
|||||||
pub struct EmptyFrameAllocator;
|
pub struct EmptyFrameAllocator;
|
||||||
|
|
||||||
unsafe impl FrameAllocator<Size4KiB> for EmptyFrameAllocator {
|
unsafe impl FrameAllocator<Size4KiB> for EmptyFrameAllocator {
|
||||||
fn allocate_frame(&mut self) -> Option<PhysFrame> {
|
fn allocate_frame(&mut self) -> Option<UnusedPhysFrame> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -82,7 +130,7 @@ impl BootInfoFrameAllocator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns an iterator over the usable frames specified in the memory map.
|
/// Returns an iterator over the usable frames specified in the memory map.
|
||||||
fn usable_frames(&self) -> impl Iterator<Item = PhysFrame> {
|
fn usable_frames(&self) -> impl Iterator<Item = UnusedPhysFrame> {
|
||||||
// get usable regions from memory map
|
// get usable regions from memory map
|
||||||
let regions = self.memory_map.iter();
|
let regions = self.memory_map.iter();
|
||||||
let usable_regions = regions.filter(|r| r.region_type == MemoryRegionType::Usable);
|
let usable_regions = regions.filter(|r| r.region_type == MemoryRegionType::Usable);
|
||||||
@@ -91,12 +139,14 @@ impl BootInfoFrameAllocator {
|
|||||||
// transform to an iterator of frame start addresses
|
// transform to an iterator of frame start addresses
|
||||||
let frame_addresses = addr_ranges.flat_map(|r| r.step_by(4096));
|
let frame_addresses = addr_ranges.flat_map(|r| r.step_by(4096));
|
||||||
// create `PhysFrame` types from the start addresses
|
// create `PhysFrame` types from the start addresses
|
||||||
frame_addresses.map(|addr| PhysFrame::containing_address(PhysAddr::new(addr)))
|
let frames = frame_addresses.map(|addr| PhysFrame::containing_address(PhysAddr::new(addr)));
|
||||||
|
// we know that the frames are really unused
|
||||||
|
frames.map(|f| unsafe { UnusedPhysFrame::new(f) })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl FrameAllocator<Size4KiB> for BootInfoFrameAllocator {
|
unsafe impl FrameAllocator<Size4KiB> for BootInfoFrameAllocator {
|
||||||
fn allocate_frame(&mut self) -> Option<PhysFrame> {
|
fn allocate_frame(&mut self) -> Option<UnusedPhysFrame> {
|
||||||
let frame = self.usable_frames().nth(self.next);
|
let frame = self.usable_frames().nth(self.next);
|
||||||
self.next += 1;
|
self.next += 1;
|
||||||
frame
|
frame
|
||||||
|
|||||||
105
src/multitasking/context_switch.rs
Normal file
105
src/multitasking/context_switch.rs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
use super::{with_scheduler, SwitchReason};
|
||||||
|
use crate::multitasking::thread::ThreadId;
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
use core::mem;
|
||||||
|
use core::raw::TraitObject;
|
||||||
|
use x86_64::VirtAddr;
|
||||||
|
|
||||||
|
pub struct Stack {
|
||||||
|
pointer: VirtAddr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stack {
|
||||||
|
pub unsafe fn new(stack_pointer: VirtAddr) -> Self {
|
||||||
|
Stack {
|
||||||
|
pointer: stack_pointer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_stack_pointer(self) -> VirtAddr {
|
||||||
|
self.pointer
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_up_for_closure(&mut self, closure: Box<dyn FnOnce() -> !>) {
|
||||||
|
let trait_object: TraitObject = unsafe { mem::transmute(closure) };
|
||||||
|
unsafe { self.push(trait_object.data) };
|
||||||
|
unsafe { self.push(trait_object.vtable) };
|
||||||
|
|
||||||
|
self.set_up_for_entry_point(call_closure_entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_up_for_entry_point(&mut self, entry_point: fn() -> !) {
|
||||||
|
unsafe { self.push(entry_point) };
|
||||||
|
let rflags: u64 = 0x200;
|
||||||
|
unsafe { self.push(rflags) };
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn push<T>(&mut self, value: T) {
|
||||||
|
self.pointer -= core::mem::size_of::<T>();
|
||||||
|
let ptr: *mut T = self.pointer.as_mut_ptr();
|
||||||
|
ptr.write(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn context_switch_to(
|
||||||
|
new_stack_pointer: VirtAddr,
|
||||||
|
prev_thread_id: ThreadId,
|
||||||
|
switch_reason: SwitchReason,
|
||||||
|
) {
|
||||||
|
asm!(
|
||||||
|
"call asm_context_switch"
|
||||||
|
:
|
||||||
|
: "{rdi}"(new_stack_pointer), "{rsi}"(prev_thread_id), "{rdx}"(switch_reason as u64)
|
||||||
|
: "rax", "rbx", "rcx", "rdx", "rsi", "rdi", "rbp", "r8", "r9", "r10",
|
||||||
|
"r11", "r12", "r13", "r14", "r15", "rflags", "memory"
|
||||||
|
: "intel", "volatile"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
global_asm!(
|
||||||
|
"
|
||||||
|
.intel_syntax noprefix
|
||||||
|
|
||||||
|
// asm_context_switch(stack_pointer: u64, thread_id: u64)
|
||||||
|
asm_context_switch:
|
||||||
|
pushfq
|
||||||
|
|
||||||
|
mov rax, rsp
|
||||||
|
mov rsp, rdi
|
||||||
|
|
||||||
|
mov rdi, rax
|
||||||
|
call add_paused_thread
|
||||||
|
|
||||||
|
popfq
|
||||||
|
ret
|
||||||
|
"
|
||||||
|
);
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn add_paused_thread(
|
||||||
|
paused_stack_pointer: VirtAddr,
|
||||||
|
paused_thread_id: ThreadId,
|
||||||
|
switch_reason: SwitchReason,
|
||||||
|
) {
|
||||||
|
with_scheduler(|s| s.add_paused_thread(paused_stack_pointer, paused_thread_id, switch_reason));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[naked]
|
||||||
|
fn call_closure_entry() -> ! {
|
||||||
|
unsafe {
|
||||||
|
asm!("
|
||||||
|
pop rsi
|
||||||
|
pop rdi
|
||||||
|
call call_closure
|
||||||
|
" ::: "mem" : "intel", "volatile")
|
||||||
|
};
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
|
||||||
|
// no_mangle required because of https://github.com/rust-lang/rust/issues/68136
|
||||||
|
#[no_mangle]
|
||||||
|
extern "C" fn call_closure(data: *mut (), vtable: *mut ()) -> ! {
|
||||||
|
let trait_object = TraitObject { data, vtable };
|
||||||
|
let f: Box<dyn FnOnce() -> !> = unsafe { mem::transmute(trait_object) };
|
||||||
|
f()
|
||||||
|
}
|
||||||
57
src/multitasking/mod.rs
Normal file
57
src/multitasking/mod.rs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
use scheduler::Scheduler;
|
||||||
|
|
||||||
|
pub mod context_switch;
|
||||||
|
pub mod scheduler;
|
||||||
|
pub mod thread;
|
||||||
|
|
||||||
|
static SCHEDULER: spin::Mutex<Option<Scheduler>> = spin::Mutex::new(None);
|
||||||
|
|
||||||
|
#[repr(u64)]
|
||||||
|
pub enum SwitchReason {
|
||||||
|
Paused,
|
||||||
|
Yield,
|
||||||
|
Blocked,
|
||||||
|
Exit,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn invoke_scheduler() {
|
||||||
|
let next = SCHEDULER
|
||||||
|
.try_lock()
|
||||||
|
.and_then(|mut scheduler| scheduler.as_mut().and_then(|s| s.schedule()));
|
||||||
|
if let Some((next_stack_pointer, prev_thread_id)) = next {
|
||||||
|
unsafe {
|
||||||
|
context_switch::context_switch_to(
|
||||||
|
next_stack_pointer,
|
||||||
|
prev_thread_id,
|
||||||
|
SwitchReason::Paused,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exit_thread() -> ! {
|
||||||
|
synchronous_context_switch(SwitchReason::Exit).expect("can't exit last thread");
|
||||||
|
unreachable!("finished thread continued");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn yield_now() {
|
||||||
|
let _ = synchronous_context_switch(SwitchReason::Yield);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn synchronous_context_switch(reason: SwitchReason) -> Result<(), ()> {
|
||||||
|
let next = with_scheduler(|s| s.schedule());
|
||||||
|
match next {
|
||||||
|
Some((next_stack_pointer, prev_thread_id)) => unsafe {
|
||||||
|
context_switch::context_switch_to(next_stack_pointer, prev_thread_id, reason);
|
||||||
|
Ok(())
|
||||||
|
},
|
||||||
|
None => Err(()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn with_scheduler<F, T>(f: F) -> T
|
||||||
|
where
|
||||||
|
F: FnOnce(&mut Scheduler) -> T,
|
||||||
|
{
|
||||||
|
f(SCHEDULER.lock().get_or_insert_with(Scheduler::new))
|
||||||
|
}
|
||||||
122
src/multitasking/scheduler.rs
Normal file
122
src/multitasking/scheduler.rs
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
use super::SwitchReason;
|
||||||
|
use crate::multitasking::thread::{Thread, ThreadId};
|
||||||
|
use alloc::collections::{BTreeMap, BTreeSet, VecDeque};
|
||||||
|
use core::mem;
|
||||||
|
use x86_64::VirtAddr;
|
||||||
|
|
||||||
|
pub struct Scheduler {
|
||||||
|
threads: BTreeMap<ThreadId, Thread>,
|
||||||
|
idle_thread_id: Option<ThreadId>,
|
||||||
|
current_thread_id: ThreadId,
|
||||||
|
paused_threads: VecDeque<ThreadId>,
|
||||||
|
blocked_threads: BTreeSet<ThreadId>,
|
||||||
|
wakeups: BTreeSet<ThreadId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Scheduler {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let root_thread = Thread::create_root_thread();
|
||||||
|
let root_id = root_thread.id();
|
||||||
|
let mut threads = BTreeMap::new();
|
||||||
|
threads
|
||||||
|
.insert(root_id, root_thread)
|
||||||
|
.expect_none("map is not empty after creation");
|
||||||
|
Scheduler {
|
||||||
|
threads,
|
||||||
|
current_thread_id: root_id,
|
||||||
|
paused_threads: VecDeque::new(),
|
||||||
|
blocked_threads: BTreeSet::new(),
|
||||||
|
wakeups: BTreeSet::new(),
|
||||||
|
idle_thread_id: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn next_thread(&mut self) -> Option<ThreadId> {
|
||||||
|
self.paused_threads.pop_front()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn schedule(&mut self) -> Option<(VirtAddr, ThreadId)> {
|
||||||
|
let mut next_thread_id = self.next_thread();
|
||||||
|
if next_thread_id.is_none() && Some(self.current_thread_id) != self.idle_thread_id {
|
||||||
|
next_thread_id = self.idle_thread_id
|
||||||
|
}
|
||||||
|
if let Some(next_id) = next_thread_id {
|
||||||
|
let next_thread = self
|
||||||
|
.threads
|
||||||
|
.get_mut(&next_id)
|
||||||
|
.expect("next thread does not exist");
|
||||||
|
let next_stack_pointer = next_thread
|
||||||
|
.stack_pointer()
|
||||||
|
.take()
|
||||||
|
.expect("paused thread has no stack pointer");
|
||||||
|
let prev_thread_id = mem::replace(&mut self.current_thread_id, next_thread.id());
|
||||||
|
Some((next_stack_pointer, prev_thread_id))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn add_paused_thread(
|
||||||
|
&mut self,
|
||||||
|
paused_stack_pointer: VirtAddr,
|
||||||
|
paused_thread_id: ThreadId,
|
||||||
|
switch_reason: SwitchReason,
|
||||||
|
) {
|
||||||
|
let paused_thread = self
|
||||||
|
.threads
|
||||||
|
.get_mut(&paused_thread_id)
|
||||||
|
.expect("paused thread does not exist");
|
||||||
|
paused_thread
|
||||||
|
.stack_pointer()
|
||||||
|
.replace(paused_stack_pointer)
|
||||||
|
.expect_none("running thread should have stack pointer set to None");
|
||||||
|
if Some(paused_thread_id) == self.idle_thread_id {
|
||||||
|
return; // do nothing
|
||||||
|
}
|
||||||
|
match switch_reason {
|
||||||
|
SwitchReason::Paused | SwitchReason::Yield => {
|
||||||
|
self.paused_threads.push_back(paused_thread_id)
|
||||||
|
}
|
||||||
|
SwitchReason::Blocked => {
|
||||||
|
self.blocked_threads.insert(paused_thread_id);
|
||||||
|
self.check_for_wakeup(paused_thread_id);
|
||||||
|
}
|
||||||
|
SwitchReason::Exit => {
|
||||||
|
let thread = self
|
||||||
|
.threads
|
||||||
|
.remove(&paused_thread_id)
|
||||||
|
.expect("thread not found");
|
||||||
|
// TODO: free stack memory again
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_new_thread(&mut self, thread: Thread) {
|
||||||
|
let thread_id = thread.id();
|
||||||
|
self.threads
|
||||||
|
.insert(thread_id, thread)
|
||||||
|
.expect_none("thread already exists");
|
||||||
|
self.paused_threads.push_back(thread_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_idle_thread(&mut self, thread: Thread) {
|
||||||
|
let thread_id = thread.id();
|
||||||
|
self.threads
|
||||||
|
.insert(thread_id, thread)
|
||||||
|
.expect_none("thread already exists");
|
||||||
|
self.idle_thread_id
|
||||||
|
.replace(thread_id)
|
||||||
|
.expect_none("idle thread should be set only once");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn current_thread_id(&self) -> ThreadId {
|
||||||
|
self.current_thread_id
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_for_wakeup(&mut self, thread_id: ThreadId) {
|
||||||
|
if self.wakeups.remove(&thread_id) {
|
||||||
|
assert!(self.blocked_threads.remove(&thread_id));
|
||||||
|
self.paused_threads.push_back(thread_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
82
src/multitasking/thread.rs
Normal file
82
src/multitasking/thread.rs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
use crate::memory::{alloc_stack, StackBounds};
|
||||||
|
use crate::multitasking::context_switch::Stack;
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
use x86_64::{
|
||||||
|
structures::paging::{mapper, FrameAllocator, Mapper, Size4KiB},
|
||||||
|
VirtAddr,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub struct ThreadId(u64);
|
||||||
|
|
||||||
|
impl ThreadId {
|
||||||
|
pub fn as_u64(&self) -> u64 {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new() -> Self {
|
||||||
|
use core::sync::atomic::{AtomicU64, Ordering};
|
||||||
|
static NEXT_THREAD_ID: AtomicU64 = AtomicU64::new(1);
|
||||||
|
ThreadId(NEXT_THREAD_ID.fetch_add(1, Ordering::SeqCst))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Thread {
|
||||||
|
id: ThreadId,
|
||||||
|
stack_pointer: Option<VirtAddr>,
|
||||||
|
stack_bounds: Option<StackBounds>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Thread {
|
||||||
|
pub fn create(
|
||||||
|
entry_point: fn() -> !,
|
||||||
|
stack_size: u64,
|
||||||
|
mapper: &mut impl Mapper<Size4KiB>,
|
||||||
|
frame_allocator: &mut impl FrameAllocator<Size4KiB>,
|
||||||
|
) -> Result<Self, mapper::MapToError> {
|
||||||
|
let stack_bounds = alloc_stack(stack_size, mapper, frame_allocator)?;
|
||||||
|
let mut stack = unsafe { Stack::new(stack_bounds.end()) };
|
||||||
|
stack.set_up_for_entry_point(entry_point);
|
||||||
|
Ok(Self::new(stack.get_stack_pointer(), stack_bounds))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn create_from_closure<F>(
|
||||||
|
closure: F,
|
||||||
|
stack_size: u64,
|
||||||
|
mapper: &mut impl Mapper<Size4KiB>,
|
||||||
|
frame_allocator: &mut impl FrameAllocator<Size4KiB>,
|
||||||
|
) -> Result<Self, mapper::MapToError>
|
||||||
|
where
|
||||||
|
F: FnOnce() -> ! + 'static + Send + Sync,
|
||||||
|
{
|
||||||
|
let stack_bounds = alloc_stack(stack_size, mapper, frame_allocator)?;
|
||||||
|
let mut stack = unsafe { Stack::new(stack_bounds.end()) };
|
||||||
|
stack.set_up_for_closure(Box::new(closure));
|
||||||
|
Ok(Self::new(stack.get_stack_pointer(), stack_bounds))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(stack_pointer: VirtAddr, stack_bounds: StackBounds) -> Self {
|
||||||
|
Thread {
|
||||||
|
id: ThreadId::new(),
|
||||||
|
stack_pointer: Some(stack_pointer),
|
||||||
|
stack_bounds: Some(stack_bounds),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn create_root_thread() -> Self {
|
||||||
|
Thread {
|
||||||
|
id: ThreadId(0),
|
||||||
|
stack_pointer: None,
|
||||||
|
stack_bounds: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> ThreadId {
|
||||||
|
self.id
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn stack_pointer(&mut self) -> &mut Option<VirtAddr> {
|
||||||
|
&mut self.stack_pointer
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
use super::{Task, TaskId};
|
|
||||||
use alloc::{collections::BTreeMap, sync::Arc, task::Wake};
|
|
||||||
use core::task::{Context, Poll, Waker};
|
|
||||||
use crossbeam_queue::ArrayQueue;
|
|
||||||
|
|
||||||
pub struct Executor {
|
|
||||||
tasks: BTreeMap<TaskId, Task>,
|
|
||||||
task_queue: Arc<ArrayQueue<TaskId>>,
|
|
||||||
waker_cache: BTreeMap<TaskId, Waker>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Executor {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Executor {
|
|
||||||
tasks: BTreeMap::new(),
|
|
||||||
task_queue: Arc::new(ArrayQueue::new(100)),
|
|
||||||
waker_cache: BTreeMap::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn spawn(&mut self, task: Task) {
|
|
||||||
let task_id = task.id;
|
|
||||||
if self.tasks.insert(task.id, task).is_some() {
|
|
||||||
panic!("task with same ID already in tasks");
|
|
||||||
}
|
|
||||||
self.task_queue.push(task_id).expect("queue full");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run(&mut self) -> ! {
|
|
||||||
loop {
|
|
||||||
self.run_ready_tasks();
|
|
||||||
self.sleep_if_idle();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_ready_tasks(&mut self) {
|
|
||||||
// destructure `self` to avoid borrow checker errors
|
|
||||||
let Self {
|
|
||||||
tasks,
|
|
||||||
task_queue,
|
|
||||||
waker_cache,
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
while let Ok(task_id) = task_queue.pop() {
|
|
||||||
let task = match tasks.get_mut(&task_id) {
|
|
||||||
Some(task) => task,
|
|
||||||
None => continue, // task no longer exists
|
|
||||||
};
|
|
||||||
let waker = waker_cache
|
|
||||||
.entry(task_id)
|
|
||||||
.or_insert_with(|| TaskWaker::new(task_id, task_queue.clone()));
|
|
||||||
let mut context = Context::from_waker(waker);
|
|
||||||
match task.poll(&mut context) {
|
|
||||||
Poll::Ready(()) => {
|
|
||||||
// task done -> remove it and its cached waker
|
|
||||||
tasks.remove(&task_id);
|
|
||||||
waker_cache.remove(&task_id);
|
|
||||||
}
|
|
||||||
Poll::Pending => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn sleep_if_idle(&self) {
|
|
||||||
use x86_64::instructions::interrupts::{self, enable_interrupts_and_hlt};
|
|
||||||
|
|
||||||
interrupts::disable();
|
|
||||||
if self.task_queue.is_empty() {
|
|
||||||
enable_interrupts_and_hlt();
|
|
||||||
} else {
|
|
||||||
interrupts::enable();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TaskWaker {
|
|
||||||
task_id: TaskId,
|
|
||||||
task_queue: Arc<ArrayQueue<TaskId>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TaskWaker {
|
|
||||||
fn new(task_id: TaskId, task_queue: Arc<ArrayQueue<TaskId>>) -> Waker {
|
|
||||||
Waker::from(Arc::new(TaskWaker {
|
|
||||||
task_id,
|
|
||||||
task_queue,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wake_task(&self) {
|
|
||||||
self.task_queue.push(self.task_id).expect("task_queue full");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Wake for TaskWaker {
|
|
||||||
fn wake(self: Arc<Self>) {
|
|
||||||
self.wake_task();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wake_by_ref(self: &Arc<Self>) {
|
|
||||||
self.wake_task();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
use crate::{print, println};
|
|
||||||
use conquer_once::spin::OnceCell;
|
|
||||||
use core::{
|
|
||||||
pin::Pin,
|
|
||||||
task::{Context, Poll},
|
|
||||||
};
|
|
||||||
use crossbeam_queue::ArrayQueue;
|
|
||||||
use futures_util::{
|
|
||||||
stream::{Stream, StreamExt},
|
|
||||||
task::AtomicWaker,
|
|
||||||
};
|
|
||||||
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
|
|
||||||
|
|
||||||
static SCANCODE_QUEUE: OnceCell<ArrayQueue<u8>> = OnceCell::uninit();
|
|
||||||
static WAKER: AtomicWaker = AtomicWaker::new();
|
|
||||||
|
|
||||||
/// Called by the keyboard interrupt handler
|
|
||||||
///
|
|
||||||
/// Must not block or allocate.
|
|
||||||
pub(crate) fn add_scancode(scancode: u8) {
|
|
||||||
if let Ok(queue) = SCANCODE_QUEUE.try_get() {
|
|
||||||
if let Err(_) = queue.push(scancode) {
|
|
||||||
println!("WARNING: scancode queue full; dropping keyboard input");
|
|
||||||
} else {
|
|
||||||
WAKER.wake();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
println!("WARNING: scancode queue uninitialized");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ScancodeStream {
|
|
||||||
_private: (),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ScancodeStream {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
SCANCODE_QUEUE
|
|
||||||
.try_init_once(|| ArrayQueue::new(100))
|
|
||||||
.expect("ScancodeStream::new should only be called once");
|
|
||||||
ScancodeStream { _private: () }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Stream for ScancodeStream {
|
|
||||||
type Item = u8;
|
|
||||||
|
|
||||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<u8>> {
|
|
||||||
let queue = SCANCODE_QUEUE
|
|
||||||
.try_get()
|
|
||||||
.expect("scancode queue not initialized");
|
|
||||||
|
|
||||||
// fast path
|
|
||||||
if let Ok(scancode) = queue.pop() {
|
|
||||||
return Poll::Ready(Some(scancode));
|
|
||||||
}
|
|
||||||
|
|
||||||
WAKER.register(&cx.waker());
|
|
||||||
match queue.pop() {
|
|
||||||
Ok(scancode) => {
|
|
||||||
WAKER.take();
|
|
||||||
Poll::Ready(Some(scancode))
|
|
||||||
}
|
|
||||||
Err(crossbeam_queue::PopError) => Poll::Pending,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn print_keypresses() {
|
|
||||||
let mut scancodes = ScancodeStream::new();
|
|
||||||
let mut keyboard = Keyboard::new(layouts::Us104Key, ScancodeSet1, HandleControl::Ignore);
|
|
||||||
|
|
||||||
while let Some(scancode) = scancodes.next().await {
|
|
||||||
if let Ok(Some(key_event)) = keyboard.add_byte(scancode) {
|
|
||||||
if let Some(key) = keyboard.process_keyevent(key_event) {
|
|
||||||
match key {
|
|
||||||
DecodedKey::Unicode(character) => print!("{}", character),
|
|
||||||
DecodedKey::RawKey(key) => print!("{:?}", key),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
use alloc::boxed::Box;
|
|
||||||
use core::{
|
|
||||||
future::Future,
|
|
||||||
pin::Pin,
|
|
||||||
sync::atomic::{AtomicU64, Ordering},
|
|
||||||
task::{Context, Poll},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub mod executor;
|
|
||||||
pub mod keyboard;
|
|
||||||
pub mod simple_executor;
|
|
||||||
|
|
||||||
pub struct Task {
|
|
||||||
id: TaskId,
|
|
||||||
future: Pin<Box<dyn Future<Output = ()>>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Task {
|
|
||||||
pub fn new(future: impl Future<Output = ()> + 'static) -> Task {
|
|
||||||
Task {
|
|
||||||
id: TaskId::new(),
|
|
||||||
future: Box::pin(future),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn poll(&mut self, context: &mut Context) -> Poll<()> {
|
|
||||||
self.future.as_mut().poll(context)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
|
||||||
struct TaskId(u64);
|
|
||||||
|
|
||||||
impl TaskId {
|
|
||||||
fn new() -> Self {
|
|
||||||
static NEXT_ID: AtomicU64 = AtomicU64::new(0);
|
|
||||||
TaskId(NEXT_ID.fetch_add(1, Ordering::Relaxed))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
use super::Task;
|
|
||||||
use alloc::collections::VecDeque;
|
|
||||||
use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
|
|
||||||
|
|
||||||
pub struct SimpleExecutor {
|
|
||||||
task_queue: VecDeque<Task>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SimpleExecutor {
|
|
||||||
pub fn new() -> SimpleExecutor {
|
|
||||||
SimpleExecutor {
|
|
||||||
task_queue: VecDeque::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn spawn(&mut self, task: Task) {
|
|
||||||
self.task_queue.push_back(task)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run(&mut self) {
|
|
||||||
while let Some(mut task) = self.task_queue.pop_front() {
|
|
||||||
let waker = dummy_waker();
|
|
||||||
let mut context = Context::from_waker(&waker);
|
|
||||||
match task.poll(&mut context) {
|
|
||||||
Poll::Ready(()) => {} // task done
|
|
||||||
Poll::Pending => self.task_queue.push_back(task),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dummy_raw_waker() -> RawWaker {
|
|
||||||
fn no_op(_: *const ()) {}
|
|
||||||
fn clone(_: *const ()) -> RawWaker {
|
|
||||||
dummy_raw_waker()
|
|
||||||
}
|
|
||||||
|
|
||||||
let vtable = &RawWakerVTable::new(clone, no_op, no_op, no_op);
|
|
||||||
RawWaker::new(0 as *const (), vtable)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dummy_waker() -> Waker {
|
|
||||||
unsafe { Waker::from_raw(dummy_raw_waker()) }
|
|
||||||
}
|
|
||||||
@@ -3,6 +3,9 @@ use lazy_static::lazy_static;
|
|||||||
use spin::Mutex;
|
use spin::Mutex;
|
||||||
use volatile::Volatile;
|
use volatile::Volatile;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
use crate::{serial_print, serial_println};
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
/// A global `Writer` instance that can be used for printing to the VGA text buffer.
|
/// A global `Writer` instance that can be used for printing to the VGA text buffer.
|
||||||
///
|
///
|
||||||
@@ -177,14 +180,18 @@ pub fn _print(args: fmt::Arguments) {
|
|||||||
|
|
||||||
#[test_case]
|
#[test_case]
|
||||||
fn test_println_simple() {
|
fn test_println_simple() {
|
||||||
|
serial_print!("test_println... ");
|
||||||
println!("test_println_simple output");
|
println!("test_println_simple output");
|
||||||
|
serial_println!("[ok]");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case]
|
#[test_case]
|
||||||
fn test_println_many() {
|
fn test_println_many() {
|
||||||
|
serial_print!("test_println_many... ");
|
||||||
for _ in 0..200 {
|
for _ in 0..200 {
|
||||||
println!("test_println_many output");
|
println!("test_println_many output");
|
||||||
}
|
}
|
||||||
|
serial_println!("[ok]");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case]
|
#[test_case]
|
||||||
@@ -192,6 +199,8 @@ fn test_println_output() {
|
|||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
use x86_64::instructions::interrupts;
|
use x86_64::instructions::interrupts;
|
||||||
|
|
||||||
|
serial_print!("test_println_output... ");
|
||||||
|
|
||||||
let s = "Some test string that fits on a single line";
|
let s = "Some test string that fits on a single line";
|
||||||
interrupts::without_interrupts(|| {
|
interrupts::without_interrupts(|| {
|
||||||
let mut writer = WRITER.lock();
|
let mut writer = WRITER.lock();
|
||||||
@@ -201,4 +210,6 @@ fn test_println_output() {
|
|||||||
assert_eq!(char::from(screen_char.ascii_character), c);
|
assert_eq!(char::from(screen_char.ascii_character), c);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
serial_println!("[ok]");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
#![test_runner(blog_os::test_runner)]
|
#![test_runner(blog_os::test_runner)]
|
||||||
#![reexport_test_harness_main = "test_main"]
|
#![reexport_test_harness_main = "test_main"]
|
||||||
|
|
||||||
use blog_os::println;
|
use blog_os::{println, serial_print, serial_println};
|
||||||
use core::panic::PanicInfo;
|
use core::panic::PanicInfo;
|
||||||
|
|
||||||
#[no_mangle] // don't mangle the name of this function
|
#[no_mangle] // don't mangle the name of this function
|
||||||
@@ -21,5 +21,7 @@ fn panic(info: &PanicInfo) -> ! {
|
|||||||
|
|
||||||
#[test_case]
|
#[test_case]
|
||||||
fn test_println() {
|
fn test_println() {
|
||||||
|
serial_print!("test_println... ");
|
||||||
println!("test_println output");
|
println!("test_println output");
|
||||||
|
serial_println!("[ok]");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
use alloc::{boxed::Box, vec::Vec};
|
use alloc::{boxed::Box, vec::Vec};
|
||||||
use blog_os::allocator::HEAP_SIZE;
|
use blog_os::{allocator::HEAP_SIZE, serial_print, serial_println};
|
||||||
use bootloader::{entry_point, BootInfo};
|
use bootloader::{entry_point, BootInfo};
|
||||||
use core::panic::PanicInfo;
|
use core::panic::PanicInfo;
|
||||||
|
|
||||||
@@ -30,38 +30,44 @@ fn main(boot_info: &'static BootInfo) -> ! {
|
|||||||
|
|
||||||
#[test_case]
|
#[test_case]
|
||||||
fn simple_allocation() {
|
fn simple_allocation() {
|
||||||
let heap_value_1 = Box::new(41);
|
serial_print!("simple_allocation... ");
|
||||||
let heap_value_2 = Box::new(13);
|
let heap_value = Box::new(41);
|
||||||
assert_eq!(*heap_value_1, 41);
|
assert_eq!(*heap_value, 41);
|
||||||
assert_eq!(*heap_value_2, 13);
|
serial_println!("[ok]");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case]
|
#[test_case]
|
||||||
fn large_vec() {
|
fn large_vec() {
|
||||||
|
serial_print!("large_vec... ");
|
||||||
let n = 1000;
|
let n = 1000;
|
||||||
let mut vec = Vec::new();
|
let mut vec = Vec::new();
|
||||||
for i in 0..n {
|
for i in 0..n {
|
||||||
vec.push(i);
|
vec.push(i);
|
||||||
}
|
}
|
||||||
assert_eq!(vec.iter().sum::<u64>(), (n - 1) * n / 2);
|
assert_eq!(vec.iter().sum::<u64>(), (n - 1) * n / 2);
|
||||||
|
serial_println!("[ok]");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case]
|
#[test_case]
|
||||||
fn many_boxes() {
|
fn many_boxes() {
|
||||||
|
serial_print!("many_boxes... ");
|
||||||
for i in 0..HEAP_SIZE {
|
for i in 0..HEAP_SIZE {
|
||||||
let x = Box::new(i);
|
let x = Box::new(i);
|
||||||
assert_eq!(*x, i);
|
assert_eq!(*x, i);
|
||||||
}
|
}
|
||||||
|
serial_println!("[ok]");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test_case]
|
#[test_case]
|
||||||
fn many_boxes_long_lived() {
|
fn many_boxes_long_lived() {
|
||||||
|
serial_print!("many_boxes_long_lived... ");
|
||||||
let long_lived = Box::new(1); // new
|
let long_lived = Box::new(1); // new
|
||||||
for i in 0..HEAP_SIZE {
|
for i in 0..HEAP_SIZE {
|
||||||
let x = Box::new(i);
|
let x = Box::new(i);
|
||||||
assert_eq!(*x, i);
|
assert_eq!(*x, i);
|
||||||
}
|
}
|
||||||
assert_eq!(*long_lived, 1); // new
|
assert_eq!(*long_lived, 1); // new
|
||||||
|
serial_println!("[ok]");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ pub extern "C" fn _start() -> ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn should_fail() {
|
fn should_fail() {
|
||||||
serial_print!("should_panic::should_fail...\t");
|
serial_print!("should_fail... ");
|
||||||
assert_eq!(0, 1);
|
assert_eq!(0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
|
|||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub extern "C" fn _start() -> ! {
|
pub extern "C" fn _start() -> ! {
|
||||||
serial_print!("stack_overflow::stack_overflow...\t");
|
serial_print!("stack_overflow... ");
|
||||||
|
|
||||||
blog_os::gdt::init();
|
blog_os::gdt::init();
|
||||||
init_test_idt();
|
init_test_idt();
|
||||||
@@ -23,7 +23,6 @@ pub extern "C" fn _start() -> ! {
|
|||||||
#[allow(unconditional_recursion)]
|
#[allow(unconditional_recursion)]
|
||||||
fn stack_overflow() {
|
fn stack_overflow() {
|
||||||
stack_overflow(); // for each recursion, the return address is pushed
|
stack_overflow(); // for each recursion, the return address is pushed
|
||||||
volatile::Volatile::new(0).read(); // prevent tail recursion optimizations
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
|||||||
Reference in New Issue
Block a user