Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6b43b7f9c5 | |||
| 62fa9f9a1b | |||
| ea5b2418e6 | |||
| 6c3c312819 | |||
| e7bc6a56dc | |||
| db006c0b72 | |||
| 83a34a1515 | |||
| 3cc7ed909c | |||
| 89c68d0738 |
+2
-1
@@ -1 +1,2 @@
|
|||||||
/target/
|
/build
|
||||||
|
/target
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
image: "redoxos/redoxer:latest"
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- apt-get install nasm
|
||||||
|
- rustup component add rust-src
|
||||||
|
|
||||||
|
stages:
|
||||||
|
- host
|
||||||
|
|
||||||
|
build:i686:
|
||||||
|
stage: host
|
||||||
|
script:
|
||||||
|
- mkdir -p target/i686
|
||||||
|
- cd target/i686
|
||||||
|
- TARGET=x86-unknown-none make -f ${CI_PROJECT_DIR}/Makefile -C `pwd` `pwd`/bootloader.bin `pwd`/bootloader-live.bin
|
||||||
|
|
||||||
|
build:x86_64:
|
||||||
|
stage: host
|
||||||
|
script:
|
||||||
|
- mkdir -p target/x86_64
|
||||||
|
- cd target/x86_64
|
||||||
|
- TARGET=x86_64-unknown-uefi make -f ${CI_PROJECT_DIR}/Makefile -C `pwd` `pwd`/bootloader.efi `pwd`/bootloader-live.efi
|
||||||
|
|
||||||
|
build:aarch64:
|
||||||
|
stage: host
|
||||||
|
script:
|
||||||
|
- mkdir -p target/aarch64
|
||||||
|
- cd target/aarch64
|
||||||
|
- TARGET=aarch64-unknown-uefi make -f ${CI_PROJECT_DIR}/Makefile -C `pwd` `pwd`/bootloader.efi `pwd`/bootloader-live.efi
|
||||||
|
|
||||||
|
fmt:
|
||||||
|
stage: host
|
||||||
|
script:
|
||||||
|
- rustup component add rustfmt-preview
|
||||||
|
- cargo fmt -- --check
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
[editor]
|
||||||
|
auto-format = false
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
[[language]]
|
||||||
|
name = "rust"
|
||||||
|
# TODO: Add more targets (BIOS, x86_32)
|
||||||
|
# Uncomment this line and set cargo.target to your target to get accurate completions
|
||||||
|
# config = { cargo.target = "aarch64-unknown-uefi", check.targets = ["x86_64-unknown-uefi", "aarch64-unknown-uefi"] }
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
language: rust
|
|
||||||
rust:
|
|
||||||
- nightly
|
|
||||||
sudo: false
|
|
||||||
notifications:
|
|
||||||
email: false
|
|
||||||
Generated
+191
-498
@@ -3,42 +3,37 @@
|
|||||||
version = 4
|
version = 4
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ansi_term"
|
name = "aes"
|
||||||
version = "0.12.1"
|
version = "0.8.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
|
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winapi",
|
"cfg-if",
|
||||||
|
"cipher",
|
||||||
|
"cpufeatures",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "arrayref"
|
name = "argon2"
|
||||||
version = "0.3.9"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
|
checksum = "db4ce4441f99dbd377ca8a8f57b698c44d0d6e712d8329b5040da5a64aa1ce73"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "arrayvec"
|
|
||||||
version = "0.5.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "atty"
|
|
||||||
version = "0.2.14"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hermit-abi",
|
"base64ct",
|
||||||
"libc",
|
"blake2",
|
||||||
"winapi",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64ct"
|
||||||
version = "0.13.1"
|
version = "1.8.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8"
|
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bit_field"
|
||||||
|
version = "0.10.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
@@ -53,21 +48,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
|
checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blake2b_simd"
|
name = "blake2"
|
||||||
version = "0.5.11"
|
version = "0.10.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587"
|
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"arrayref",
|
"digest",
|
||||||
"arrayvec",
|
|
||||||
"constant_time_eq",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytecount"
|
name = "block-buffer"
|
||||||
version = "0.6.9"
|
version = "0.10.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e"
|
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cfg-if"
|
name = "cfg-if"
|
||||||
@@ -76,125 +78,84 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clap"
|
name = "cipher"
|
||||||
version = "2.34.0"
|
version = "0.4.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
|
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ansi_term",
|
"crypto-common",
|
||||||
"atty",
|
"inout",
|
||||||
"bitflags 1.3.2",
|
|
||||||
"strsim",
|
|
||||||
"textwrap",
|
|
||||||
"unicode-width",
|
|
||||||
"vec_map",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "constant_time_eq"
|
name = "cpufeatures"
|
||||||
version = "0.1.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crossbeam-utils"
|
|
||||||
version = "0.8.22"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "either"
|
|
||||||
version = "1.16.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "equivalent"
|
|
||||||
version = "1.0.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "extra"
|
|
||||||
version = "0.1.0"
|
|
||||||
source = "git+https://gitlab.redox-os.org/redox-os/libextra.git#cf213969493db8667052a591e32a1e26d43c4234"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "generic-rt"
|
|
||||||
version = "0.1.0"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "getrandom"
|
|
||||||
version = "0.2.17"
|
version = "0.2.17"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
|
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
"libc",
|
||||||
"wasi",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "goblin"
|
name = "crypto-common"
|
||||||
|
version = "0.1.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array",
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "17582616a7718cca54cec18e534a76c7c4aec11a8b9a85695712f262fd15a4c8"
|
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"log",
|
"block-buffer",
|
||||||
"plain",
|
"crypto-common",
|
||||||
"scroll",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "dmidecode"
|
||||||
version = "0.17.1"
|
version = "0.8.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
checksum = "f5a070ca68f8ba202b05487d52b9ac56eaebb5b66cdd68d1a17e63174bb11e3b"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hermit-abi"
|
|
||||||
version = "0.1.19"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"bitflags 1.3.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "endian-num"
|
||||||
version = "2.14.0"
|
version = "0.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
|
checksum = "f8f59926911ef34d1efb9ea1ee8ca78385df62ce700ccf2bcb149011bd226888"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fdt"
|
||||||
|
version = "0.2.0-alpha1"
|
||||||
|
source = "git+https://github.com/repnop/fdt.git?rev=2fb1409edd1877c714a0aa36b6a7c5351004be54#2fb1409edd1877c714a0aa36b6a7c5351004be54"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"typenum",
|
||||||
"hashbrown",
|
"version_check",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ioslice"
|
name = "inout"
|
||||||
version = "0.6.0"
|
version = "0.1.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e571352c8a3b89074d12e3ee5173ffe162159105352aaaf1fc5764da747e31b"
|
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"generic-array",
|
||||||
"winapi",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itertools"
|
|
||||||
version = "0.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lazy_static"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.186"
|
version = "0.2.186"
|
||||||
@@ -202,14 +163,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libredox"
|
name = "linked_list_allocator"
|
||||||
version = "0.1.18+rb0.3.0"
|
version = "0.10.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b23ac50abb8261cb38c6e2a7192d3302e0836dac1628f6a93b82b4fad185897"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"spinning_top",
|
||||||
"ioslice",
|
]
|
||||||
"libc",
|
|
||||||
"plain",
|
[[package]]
|
||||||
"redox_syscall",
|
name = "lock_api"
|
||||||
|
version = "0.4.14"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||||
|
dependencies = [
|
||||||
|
"scopeguard",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -219,50 +187,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
|
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "memchr"
|
name = "lz4_flex"
|
||||||
version = "2.8.2"
|
version = "0.11.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4"
|
checksum = "373f5eceeeab7925e0c1098212f2fbc4d416adec9d35051a6ab251e824c1854a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "numtoa"
|
name = "raw-cpuid"
|
||||||
version = "0.2.4"
|
version = "10.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6aa2c4e539b869820a2b82e1aef6ff40aa85e65decdd5185e83fb4b1249cd00f"
|
checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "orbclient"
|
|
||||||
version = "0.3.55"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5df339f526ea9a60e371768d50efc2f2508c7203290731565d1f7a6f71d21747"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"bitflags 1.3.2",
|
||||||
"libredox",
|
|
||||||
"sdl2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "plain"
|
|
||||||
version = "0.2.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "proc-macro2"
|
|
||||||
version = "1.0.106"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "quote"
|
|
||||||
version = "1.0.46"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -272,48 +208,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717"
|
checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox-rt"
|
name = "redox_bootloader"
|
||||||
version = "0.1.0"
|
version = "1.0.0+rb0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 2.13.0",
|
"bitflags 1.3.2",
|
||||||
"generic-rt",
|
"byteorder",
|
||||||
"goblin",
|
"dmidecode",
|
||||||
"ioslice",
|
"fdt",
|
||||||
"libredox",
|
"linked_list_allocator",
|
||||||
"plain",
|
"log",
|
||||||
"redox-path",
|
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
]
|
"redox_uefi",
|
||||||
|
"redox_uefi_std",
|
||||||
[[package]]
|
"redoxfs",
|
||||||
name = "redox-scheme"
|
"spin",
|
||||||
version = "0.11.2+rb0.3.0"
|
"x86",
|
||||||
dependencies = [
|
|
||||||
"libredox",
|
|
||||||
"redox_syscall",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_event"
|
|
||||||
version = "0.4.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c5018d583d6d2f5499352aea8d177e9067d1eb03ab17c78169d5ba7a30001b15"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.13.0",
|
|
||||||
"libredox",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_liner"
|
|
||||||
version = "0.5.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ee4596948a78a2ac29268d4ee45f788789ac8c032c77cf255378e5c96ca0c779"
|
|
||||||
dependencies = [
|
|
||||||
"bytecount",
|
|
||||||
"itertools",
|
|
||||||
"strip-ansi-escapes",
|
|
||||||
"termion",
|
|
||||||
"unicode-width",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -324,333 +233,117 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_termios"
|
name = "redox_uefi"
|
||||||
version = "0.1.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "20145670ba436b55d91fc92d25e71160fbfbdd57831631c8d7d36377a476f1cb"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_users"
|
|
||||||
version = "0.4.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
|
||||||
dependencies = [
|
|
||||||
"getrandom",
|
|
||||||
"libredox",
|
|
||||||
"rust-argon2",
|
|
||||||
"thiserror",
|
|
||||||
"zeroize",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rust-argon2"
|
|
||||||
version = "0.8.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb"
|
|
||||||
dependencies = [
|
|
||||||
"base64",
|
|
||||||
"blake2b_simd",
|
|
||||||
"constant_time_eq",
|
|
||||||
"crossbeam-utils",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "scroll"
|
|
||||||
version = "0.13.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c1257cd4248b4132760d6524d6dda4e053bc648c9070b960929bf50cfb1e7add"
|
|
||||||
dependencies = [
|
|
||||||
"scroll_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "scroll_derive"
|
|
||||||
version = "0.13.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ed76efe62313ab6610570951494bdaa81568026e0318eaa55f167de70eeea67d"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sdl2"
|
|
||||||
version = "0.38.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2d42407afc6a8ab67e36f92e80b8ba34cbdc55aaeed05249efe9a2e8d0e9feef"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 1.3.2",
|
|
||||||
"lazy_static",
|
|
||||||
"libc",
|
|
||||||
"sdl2-sys",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sdl2-sys"
|
|
||||||
version = "0.38.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3ff61407fc75d4b0bbc93dc7e4d6c196439965fbef8e4a4f003a36095823eac0"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"libc",
|
|
||||||
"version-compare",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde"
|
|
||||||
version = "1.0.228"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
|
||||||
dependencies = [
|
|
||||||
"serde_core",
|
|
||||||
"serde_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_core"
|
|
||||||
version = "1.0.228"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
|
||||||
dependencies = [
|
|
||||||
"serde_derive",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_derive"
|
|
||||||
version = "1.0.228"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "serde_spanned"
|
|
||||||
version = "0.6.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "strip-ansi-escapes"
|
|
||||||
version = "0.2.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2a8f8038e7e7969abb3f1b7c2a811225e9296da208539e0f79c5251d6cac0025"
|
|
||||||
dependencies = [
|
|
||||||
"vte",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "strsim"
|
|
||||||
version = "0.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "syn"
|
|
||||||
version = "2.0.118"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"unicode-ident",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "termion"
|
|
||||||
version = "4.0.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f44138a9ae08f0f502f24104d82517ef4da7330c35acd638f1f29d3cd5475ecb"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
"numtoa",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "textwrap"
|
|
||||||
version = "0.11.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
|
|
||||||
dependencies = [
|
|
||||||
"unicode-width",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "thiserror"
|
|
||||||
version = "1.0.69"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
|
||||||
dependencies = [
|
|
||||||
"thiserror-impl",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "thiserror-impl"
|
|
||||||
version = "1.0.69"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "toml"
|
|
||||||
version = "0.8.23"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
"serde_spanned",
|
|
||||||
"toml_datetime",
|
|
||||||
"toml_edit",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "toml_datetime"
|
|
||||||
version = "0.6.11"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
|
|
||||||
dependencies = [
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "toml_edit"
|
|
||||||
version = "0.22.27"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
|
||||||
dependencies = [
|
|
||||||
"indexmap",
|
|
||||||
"serde",
|
|
||||||
"serde_spanned",
|
|
||||||
"toml_datetime",
|
|
||||||
"toml_write",
|
|
||||||
"winnow",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "toml_write"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-ident"
|
|
||||||
version = "1.0.24"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "unicode-width"
|
|
||||||
version = "0.1.14"
|
version = "0.1.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "git+https://gitlab.redox-os.org/redox-os/uefi.git#26a499eeaf55d42fb24206830345e26fb8f5f835"
|
||||||
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "userutils"
|
name = "redox_uefi_alloc"
|
||||||
version = "0.1.0+rb0.3.0"
|
version = "0.1.14"
|
||||||
|
source = "git+https://gitlab.redox-os.org/redox-os/uefi.git#26a499eeaf55d42fb24206830345e26fb8f5f835"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"redox_uefi",
|
||||||
"extra",
|
]
|
||||||
"ioslice",
|
|
||||||
|
[[package]]
|
||||||
|
name = "redox_uefi_std"
|
||||||
|
version = "0.1.14"
|
||||||
|
source = "git+https://gitlab.redox-os.org/redox-os/uefi.git#26a499eeaf55d42fb24206830345e26fb8f5f835"
|
||||||
|
dependencies = [
|
||||||
|
"redox_uefi",
|
||||||
|
"redox_uefi_alloc",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "redoxfs"
|
||||||
|
version = "0.9.1+rb0.3.0"
|
||||||
|
dependencies = [
|
||||||
|
"aes",
|
||||||
|
"argon2",
|
||||||
|
"base64ct",
|
||||||
|
"bitflags 2.13.0",
|
||||||
|
"endian-num",
|
||||||
"libc",
|
"libc",
|
||||||
"libredox",
|
"log",
|
||||||
"orbclient",
|
"lz4_flex",
|
||||||
"plain",
|
"redox-path",
|
||||||
"redox-rt",
|
|
||||||
"redox-scheme",
|
|
||||||
"redox_event",
|
|
||||||
"redox_liner",
|
|
||||||
"redox_syscall",
|
"redox_syscall",
|
||||||
"redox_termios",
|
"seahash",
|
||||||
"redox_users",
|
"uuid",
|
||||||
"serde",
|
"xts-mode",
|
||||||
"termion",
|
|
||||||
"toml",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vec_map"
|
name = "scopeguard"
|
||||||
version = "0.8.2"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version-compare"
|
name = "seahash"
|
||||||
version = "0.1.1"
|
version = "4.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "579a42fc0b8e0c63b76519a339be31bed574929511fa53c1a3acae26eb258f29"
|
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vte"
|
name = "spin"
|
||||||
version = "0.14.1"
|
version = "0.9.8"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "231fdcd7ef3037e8330d8e17e61011a2c244126acc0a982f4040ac3f9f0bc077"
|
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"lock_api",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "wasi"
|
name = "spinning_top"
|
||||||
version = "0.11.1+wasi-snapshot-preview1"
|
version = "0.2.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
|
checksum = "5b9eb1a2f4c41445a3a0ff9abc5221c5fcd28e1f13cd7c0397706f9ac938ddb0"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "winapi"
|
|
||||||
version = "0.3.9"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"winapi-i686-pc-windows-gnu",
|
"lock_api",
|
||||||
"winapi-x86_64-pc-windows-gnu",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-i686-pc-windows-gnu"
|
name = "subtle"
|
||||||
version = "0.4.0"
|
version = "2.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-x86_64-pc-windows-gnu"
|
name = "typenum"
|
||||||
version = "0.4.0"
|
version = "1.20.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winnow"
|
name = "uuid"
|
||||||
version = "0.7.15"
|
version = "1.23.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945"
|
checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.9.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "x86"
|
||||||
|
version = "0.52.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2781db97787217ad2a2845c396a5efe286f87467a5810836db6d74926e94a385"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"memchr",
|
"bit_field",
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"raw-cpuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zeroize"
|
name = "xts-mode"
|
||||||
version = "1.9.0"
|
version = "0.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
|
checksum = "09cbddb7545ca0b9ffa7bdc653e8743303e1712687a6918ced25f2cdbed42520"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"zeroize_derive",
|
"byteorder",
|
||||||
]
|
"cipher",
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "zeroize_derive"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328"
|
|
||||||
dependencies = [
|
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn",
|
|
||||||
]
|
]
|
||||||
|
|||||||
+44
-69
@@ -1,82 +1,57 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "userutils"
|
name = "redox_bootloader"
|
||||||
version = "0.1.0+rb0.3.0"
|
version = "1.0.0+rb0.3.0"
|
||||||
authors = ["Jeremy Soller <jackpot51@gmail.com>", "vasilito <adminpupkin@gmail.com>"]
|
authors = ["Jeremy Soller <jackpot51@gmail.com>", "vasilito <adminpupkin@gmail.com>"]
|
||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
|
# UEFI uses bin target
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "id"
|
name = "bootloader"
|
||||||
path = "src/bin/id.rs"
|
path = "src/main.rs"
|
||||||
|
|
||||||
[[bin]]
|
# BIOS uses lib target
|
||||||
name = "getty"
|
[lib]
|
||||||
path = "src/bin/getty.rs"
|
name = "bootloader"
|
||||||
|
path = "src/main.rs"
|
||||||
[[bin]]
|
crate-type = ["staticlib"]
|
||||||
name = "groupadd"
|
|
||||||
path = "src/bin/groupadd.rs"
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "groupdel"
|
|
||||||
path = "src/bin/groupdel.rs"
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "groupmod"
|
|
||||||
path = "src/bin/groupmod.rs"
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "login"
|
|
||||||
path = "src/bin/login.rs"
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "passwd"
|
|
||||||
path = "src/bin/passwd.rs"
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "su"
|
|
||||||
path = "src/bin/su.rs"
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "sudo"
|
|
||||||
path = "src/bin/sudo.rs"
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "useradd"
|
|
||||||
path = "src/bin/useradd.rs"
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "userdel"
|
|
||||||
path = "src/bin/userdel.rs"
|
|
||||||
|
|
||||||
[[bin]]
|
|
||||||
name = "usermod"
|
|
||||||
path = "src/bin/usermod.rs"
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.33.0"
|
bitflags = "1.3.2"
|
||||||
extra = { git = "https://gitlab.redox-os.org/redox-os/libextra.git" }
|
linked_list_allocator = "0.10.5"
|
||||||
orbclient = "0.3.47"
|
log = "0.4.17"
|
||||||
plain = "0.2.3"
|
|
||||||
redox_liner = "0.5.2"
|
|
||||||
libredox = { path = "../libredox", features = ["mkns"] }
|
|
||||||
redox_termios = "0.1.3"
|
|
||||||
redox_event = "0.4.3"
|
|
||||||
redox-scheme = { path = "../redox-scheme" }
|
|
||||||
redox_syscall = { path = "../syscall" }
|
redox_syscall = { path = "../syscall" }
|
||||||
redox_users = "0.4.6"
|
spin = "0.9.5"
|
||||||
termion = "4"
|
|
||||||
libc = "0.2"
|
[dependencies.redoxfs]
|
||||||
serde = { version = "1.0.203", features = ["derive"] }
|
path = "../redoxfs"
|
||||||
toml = "0.8.11"
|
default-features = false
|
||||||
ioslice = "0.6"
|
features = ["log"]
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "uefi")'.dependencies]
|
||||||
|
redox_uefi = { git = "https://gitlab.redox-os.org/redox-os/uefi.git" }
|
||||||
|
redox_uefi_std = { git = "https://gitlab.redox-os.org/redox-os/uefi.git" }
|
||||||
|
|
||||||
|
#TODO: riscv cannot use target_os = "uefi" at this time
|
||||||
|
[target.'cfg(target_arch = "riscv64")'.dependencies]
|
||||||
|
redox_uefi = { git = "https://gitlab.redox-os.org/redox-os/uefi.git" }
|
||||||
|
redox_uefi_std = { git = "https://gitlab.redox-os.org/redox-os/uefi.git" }
|
||||||
|
|
||||||
|
[target."aarch64-unknown-uefi".dependencies]
|
||||||
|
dmidecode = "0.8.0"
|
||||||
|
|
||||||
|
[target."x86_64-unknown-uefi".dependencies]
|
||||||
|
x86 = "0.52.0"
|
||||||
|
|
||||||
|
[target.'cfg(any(target_arch = "aarch64", target_arch = "riscv64"))'.dependencies]
|
||||||
|
byteorder = { version = "1", default-features = false }
|
||||||
|
fdt = { git = "https://github.com/repnop/fdt.git", rev = "2fb1409edd1877c714a0aa36b6a7c5351004be54" }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
live = []
|
||||||
|
serial_debug = []
|
||||||
|
|
||||||
[target.'cfg(target_os = "redox")'.dependencies]
|
|
||||||
redox-rt = { path = "../relibc/redox-rt", default-features = false }
|
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
redox_syscall = { path = "../syscall" }
|
redox_syscall = { path = "../syscall" }
|
||||||
libredox = { path = "../libredox" }
|
redoxfs = { path = "../redoxfs" }
|
||||||
redox-scheme = { path = "../redox-scheme" }
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
lto = true
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
The MIT License (MIT)
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2016 The Redox developers
|
Copyright (c) 2017-2022 Redox OS
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
TARGET?=x86_64-unknown-uefi
|
||||||
|
SOURCE:=$(dir $(realpath $(lastword $(MAKEFILE_LIST))))
|
||||||
|
BUILD:=$(CURDIR)
|
||||||
|
export RUST_TARGET_PATH?=$(SOURCE)/targets
|
||||||
|
CARGO_ARGS=--release --locked --target $(TARGET) \
|
||||||
|
-Z build-std=core,alloc \
|
||||||
|
-Z build-std-features=compiler-builtins-mem
|
||||||
|
|
||||||
|
include $(SOURCE)/mk/$(TARGET).mk
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf build target
|
||||||
|
|
||||||
|
$(BUILD)/filesystem:
|
||||||
|
mkdir -p $(BUILD)
|
||||||
|
rm -f $@.partial
|
||||||
|
mkdir $@.partial
|
||||||
|
fallocate -l 1MiB $@.partial/kernel
|
||||||
|
mv $@.partial $@
|
||||||
|
|
||||||
|
$(BUILD)/filesystem.bin: $(BUILD)/filesystem
|
||||||
|
mkdir -p $(BUILD)
|
||||||
|
rm -f $@.partial
|
||||||
|
fallocate -l 254MiB $@.partial
|
||||||
|
redoxfs-ar $@.partial $<
|
||||||
|
mv $@.partial $@
|
||||||
@@ -1,24 +1,62 @@
|
|||||||
# Redox OS user and group utilities.
|
# Bootloader
|
||||||
|
|
||||||
The `userutils` crate contains the utilities for dealing with users and groups in Redox OS.
|
Redox OS Bootloader
|
||||||
They are heavily influenced by UNIX and are, when needed, tailored to specific Redox use cases.
|
|
||||||
|
|
||||||
These implementations strive to be as simple as possible drawing particular
|
## Requirements
|
||||||
inspiration by BSD systems. They are indeed small, by choice.
|
|
||||||
|
|
||||||
[](https://travis-ci.org/redox-os/userutils)
|
These software needs to be available on the PATH at build time:
|
||||||
|
|
||||||
**Currently included:**
|
+ [mtools](https://www.gnu.org/software/mtools/)
|
||||||
|
+ [nasm](https://nasm.us/)
|
||||||
|
+ [redoxfs-ar](https://gitlab.redox-os.org/redox-os/redoxfs)
|
||||||
|
|
||||||
- `getty`: Used by `init(8)` to open and initialize the TTY line, read a login name and invoke `login(1)`.
|
## Building
|
||||||
- `id`: Displays user identity.
|
|
||||||
- `login`: Allows users to login into the system
|
```sh
|
||||||
- `passwd`: Allows users to modify their passwords.
|
make TARGET=<triplet> BUILD=build all
|
||||||
- `su`: Allows users to substitute identity.
|
```
|
||||||
- `sudo`: Enables users to execute a command as another user.
|
|
||||||
- `useradd`: Add a user
|
The `<triplet>` is one of:
|
||||||
- `usermod`: Modify user information
|
|
||||||
- `userdel`: Delete a user
|
| ARCH | Boot Mode | Triplets |
|
||||||
- `groupadd`: Add a user group
|
|---|---|---|
|
||||||
- `groupmod`: Modify group information
|
| `i686` | BIOS | `x86-unknown-none` |
|
||||||
- `groupdel`: Remove a user group
|
| `x86_64` | BIOS | `x86-unknown-none` |
|
||||||
|
| `x86_64` | UEFI | `x86_64-unknown-uefi` |
|
||||||
|
| `aarch64` | UEFI | `aarch64-unknown-uefi` |
|
||||||
|
| `riscv64gc` | UEFI | `riscv64gc-unknown-uefi` |
|
||||||
|
|
||||||
|
See [mk directory](./mk) for more information of how the build is working.
|
||||||
|
|
||||||
|
## Entry points
|
||||||
|
|
||||||
|
Please read [Boot Process](https://doc.redox-os.org/book/boot-process.html) in the Redox OS Book for an introductory guide.
|
||||||
|
|
||||||
|
In this source code, some interesting files for entry points are:
|
||||||
|
|
||||||
|
+ BIOS boot stages: [asm/x86-unknown-none/bootloader.asm](./asm/x86-unknown-none/bootloader.asm)
|
||||||
|
+ BIOS boot entry: `fn start` at [src/os/bios/mod.rs](./src/os/bios/mod.rs)
|
||||||
|
+ UEFI boot entry: `fn main` at [src/os/uefi/mod.rs](src/os/uefi/mod.rs)
|
||||||
|
+ Common boot process: `fn main` at [src/main.rs](src/main.rs)
|
||||||
|
+ UEFI kernel entry: `fn kernel_entry` in each arch:
|
||||||
|
- `x86_64`: [src/os/uefi/arch/x86_64.rs](src/os/uefi/arch/x86_64.rs)
|
||||||
|
- `aarch64`: [src/os/uefi/arch/aarch64.rs](src/os/uefi/arch/aarch64.rs)
|
||||||
|
- `riscv64gc`: [src/os/uefi/arch/riscv64/mod.rs](src/os/uefi/arch/riscv64/mod.rs)
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
### QEMU
|
||||||
|
|
||||||
|
```sh
|
||||||
|
make TARGET=<triplet> BUILD=build qemu
|
||||||
|
```
|
||||||
|
|
||||||
|
## How To Contribute
|
||||||
|
|
||||||
|
To learn how to contribute to this system component you need to read the following document:
|
||||||
|
|
||||||
|
- [CONTRIBUTING.md](https://gitlab.redox-os.org/redox-os/redox/-/blob/master/CONTRIBUTING.md)
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
To learn how to do development with this system component inside the Redox build system you need to read the [Build System](https://doc.redox-os.org/book/build-system-reference.html) and [Coding and Building](https://doc.redox-os.org/book/coding-and-building.html) pages.
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
interrupt_vector_table:
|
||||||
|
b . @ Reset
|
||||||
|
b .
|
||||||
|
b . @ SWI instruction
|
||||||
|
b .
|
||||||
|
b .
|
||||||
|
b .
|
||||||
|
b .
|
||||||
|
b .
|
||||||
|
|
||||||
|
.comm stack, 0x10000 @ Reserve 64k stack in the BSS
|
||||||
|
_start:
|
||||||
|
.globl _start
|
||||||
|
ldr sp, =stack+0x10000 @ Set up the stack
|
||||||
|
bl kstart @ Jump to the main function
|
||||||
|
1:
|
||||||
|
b 1b @ Halt
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
sectalign off
|
||||||
|
|
||||||
|
; stage 1 is sector 0, loaded at 0x7C00
|
||||||
|
%include "stage1.asm"
|
||||||
|
|
||||||
|
; GPT area from sector 1 to 33, loaded at 0x7E00
|
||||||
|
times (33*512) db 0
|
||||||
|
|
||||||
|
; stage 2, loaded at 0xC000
|
||||||
|
stage2:
|
||||||
|
%include "stage2.asm"
|
||||||
|
align 512, db 0
|
||||||
|
stage2.end:
|
||||||
|
|
||||||
|
; the maximum size of stage2 is 4 KiB
|
||||||
|
times (4*1024)-($-stage2) db 0
|
||||||
|
|
||||||
|
; ISO compatibility, uses up space until 0x12400
|
||||||
|
%include "iso.asm"
|
||||||
|
|
||||||
|
times 3072 db 0 ; Pad to 0x13000
|
||||||
|
|
||||||
|
; stage3, loaded at 0x13000
|
||||||
|
stage3:
|
||||||
|
%defstr STAGE3_STR %[STAGE3]
|
||||||
|
incbin STAGE3_STR
|
||||||
|
align 512, db 0
|
||||||
|
.end:
|
||||||
|
|
||||||
|
; the maximum size of the boot loader portion is 384 KiB
|
||||||
|
times (384*1024)-($-$$) db 0
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
SECTION .text
|
||||||
|
USE16
|
||||||
|
|
||||||
|
cpuid_required_features:
|
||||||
|
.edx equ cpuid_edx.fpu | cpuid_edx.pse | cpuid_edx.pge | cpuid_edx.fxsr
|
||||||
|
.ecx equ 0
|
||||||
|
|
||||||
|
cpuid_check:
|
||||||
|
; If bit 21 of EFLAGS can be changed, then CPUID is supported
|
||||||
|
pushfd ;Save EFLAGS
|
||||||
|
pushfd ;Store EFLAGS
|
||||||
|
xor dword [esp],0x00200000 ;Invert the ID bit in stored EFLAGS
|
||||||
|
popfd ;Load stored EFLAGS (with ID bit inverted)
|
||||||
|
pushfd ;Store EFLAGS again (ID bit may or may not be inverted)
|
||||||
|
pop eax ;eax = modified EFLAGS (ID bit may or may not be inverted)
|
||||||
|
xor eax,[esp] ;eax = whichever bits were changed
|
||||||
|
popfd ;Restore original EFLAGS
|
||||||
|
test eax,0x00200000 ;eax = zero if ID bit can't be changed, else non-zero
|
||||||
|
jz .no_cpuid
|
||||||
|
|
||||||
|
mov eax, 1
|
||||||
|
cpuid
|
||||||
|
|
||||||
|
and edx, cpuid_required_features.edx
|
||||||
|
cmp edx, cpuid_required_features.edx
|
||||||
|
jne .error
|
||||||
|
|
||||||
|
and ecx, cpuid_required_features.ecx
|
||||||
|
cmp ecx, cpuid_required_features.ecx
|
||||||
|
jne .error
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
.no_cpuid:
|
||||||
|
mov si, .msg_cpuid
|
||||||
|
call print
|
||||||
|
|
||||||
|
mov si, .msg_line
|
||||||
|
call print
|
||||||
|
|
||||||
|
jmp .halt
|
||||||
|
|
||||||
|
.error:
|
||||||
|
push ecx
|
||||||
|
push edx
|
||||||
|
|
||||||
|
mov si, .msg_features
|
||||||
|
call print
|
||||||
|
|
||||||
|
mov si, .msg_line
|
||||||
|
call print
|
||||||
|
|
||||||
|
mov si, .msg_edx
|
||||||
|
call print
|
||||||
|
|
||||||
|
pop ebx
|
||||||
|
push ebx
|
||||||
|
shr ebx, 16
|
||||||
|
call print_hex
|
||||||
|
|
||||||
|
pop ebx
|
||||||
|
call print_hex
|
||||||
|
|
||||||
|
mov si, .msg_must_contain
|
||||||
|
call print
|
||||||
|
|
||||||
|
mov ebx, cpuid_required_features.edx
|
||||||
|
shr ebx, 16
|
||||||
|
call print_hex
|
||||||
|
|
||||||
|
mov ebx, cpuid_required_features.edx
|
||||||
|
call print_hex
|
||||||
|
|
||||||
|
mov si, .msg_line
|
||||||
|
call print
|
||||||
|
|
||||||
|
mov si, .msg_ecx
|
||||||
|
call print
|
||||||
|
|
||||||
|
pop ebx
|
||||||
|
push ebx
|
||||||
|
shr ebx, 16
|
||||||
|
call print_hex
|
||||||
|
|
||||||
|
pop ebx
|
||||||
|
call print_hex
|
||||||
|
|
||||||
|
mov si, .msg_must_contain
|
||||||
|
call print
|
||||||
|
|
||||||
|
mov ebx, cpuid_required_features.ecx
|
||||||
|
shr ebx, 16
|
||||||
|
call print_hex
|
||||||
|
|
||||||
|
mov ebx, cpuid_required_features.ecx
|
||||||
|
call print_hex
|
||||||
|
|
||||||
|
mov si, .msg_line
|
||||||
|
call print
|
||||||
|
|
||||||
|
.halt:
|
||||||
|
cli
|
||||||
|
hlt
|
||||||
|
jmp .halt
|
||||||
|
|
||||||
|
.msg_cpuid: db "CPUID not supported",0
|
||||||
|
.msg_features: db "Required CPU features are not present",0
|
||||||
|
.msg_line: db 13,10,0
|
||||||
|
.msg_edx: db "EDX ",0
|
||||||
|
.msg_ecx: db "ECX ",0
|
||||||
|
.msg_must_contain: db " must contain ",0
|
||||||
|
|
||||||
|
cpuid_edx:
|
||||||
|
.fpu equ 1 << 0
|
||||||
|
.vme equ 1 << 1
|
||||||
|
.de equ 1 << 2
|
||||||
|
.pse equ 1 << 3
|
||||||
|
.tsc equ 1 << 4
|
||||||
|
.msr equ 1 << 5
|
||||||
|
.pae equ 1 << 6
|
||||||
|
.mce equ 1 << 7
|
||||||
|
.cx8 equ 1 << 8
|
||||||
|
.apic equ 1 << 9
|
||||||
|
.sep equ 1 << 11
|
||||||
|
.mtrr equ 1 << 12
|
||||||
|
.pge equ 1 << 13
|
||||||
|
.mca equ 1 << 14
|
||||||
|
.cmov equ 1 << 15
|
||||||
|
.pat equ 1 << 16
|
||||||
|
.pse_36 equ 1 << 17
|
||||||
|
.psn equ 1 << 18
|
||||||
|
.clfsh equ 1 << 19
|
||||||
|
.ds equ 1 << 21
|
||||||
|
.acpi equ 1 << 22
|
||||||
|
.mmx equ 1 << 23
|
||||||
|
.fxsr equ 1 << 24
|
||||||
|
.sse equ 1 << 25
|
||||||
|
.sse2 equ 1 << 26
|
||||||
|
.ss equ 1 << 27
|
||||||
|
.htt equ 1 << 28
|
||||||
|
.tm equ 1 << 29
|
||||||
|
.ia64 equ 1 << 30
|
||||||
|
.pbe equ 1 << 31
|
||||||
|
|
||||||
|
cpuid_ecx:
|
||||||
|
.sse3 equ 1 << 0
|
||||||
|
.pclmulqdq equ 1 << 1
|
||||||
|
.dtes64 equ 1 << 2
|
||||||
|
.monitor equ 1 << 3
|
||||||
|
.ds_cpl equ 1 << 4
|
||||||
|
.vmx equ 1 << 5
|
||||||
|
.smx equ 1 << 6
|
||||||
|
.est equ 1 << 7
|
||||||
|
.tm2 equ 1 << 8
|
||||||
|
.ssse3 equ 1 << 9
|
||||||
|
.cnxt_id equ 1 << 10
|
||||||
|
.sdbg equ 1 << 11
|
||||||
|
.fma equ 1 << 12
|
||||||
|
.cmpxchg16b equ 1 << 13
|
||||||
|
.xtpr equ 1 << 14
|
||||||
|
.pdcm equ 1 << 15
|
||||||
|
.pcid equ 1 << 17
|
||||||
|
.dca equ 1 << 18
|
||||||
|
.sse4_1 equ 1 << 19
|
||||||
|
.sse4_2 equ 1 << 20
|
||||||
|
.x2apic equ 1 << 21
|
||||||
|
.movbe equ 1 << 22
|
||||||
|
.popcnt equ 1 << 23
|
||||||
|
.tsc_deadline equ 1 << 24
|
||||||
|
.aes equ 1 << 25
|
||||||
|
.xsave equ 1 << 26
|
||||||
|
.osxsave equ 1 << 27
|
||||||
|
.avx equ 1 << 28
|
||||||
|
.f16c equ 1 << 29
|
||||||
|
.rdrand equ 1 << 30
|
||||||
|
.hypervisor equ 1 << 31
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
SECTION .text ; cannot use .data
|
||||||
|
|
||||||
|
struc GDTEntry
|
||||||
|
.limitl resw 1
|
||||||
|
.basel resw 1
|
||||||
|
.basem resb 1
|
||||||
|
.attribute resb 1
|
||||||
|
.flags__limith resb 1
|
||||||
|
.baseh resb 1
|
||||||
|
endstruc
|
||||||
|
|
||||||
|
gdt_attr:
|
||||||
|
.present equ 1 << 7
|
||||||
|
.ring1 equ 1 << 5
|
||||||
|
.ring2 equ 1 << 6
|
||||||
|
.ring3 equ 1 << 5 | 1 << 6
|
||||||
|
.user equ 1 << 4
|
||||||
|
;user
|
||||||
|
.code equ 1 << 3
|
||||||
|
; code
|
||||||
|
.conforming equ 1 << 2
|
||||||
|
.readable equ 1 << 1
|
||||||
|
; data
|
||||||
|
.expand_down equ 1 << 2
|
||||||
|
.writable equ 1 << 1
|
||||||
|
.accessed equ 1 << 0
|
||||||
|
;system
|
||||||
|
; legacy
|
||||||
|
.tssAvailabe16 equ 0x1
|
||||||
|
.ldt equ 0x2
|
||||||
|
.tssBusy16 equ 0x3
|
||||||
|
.call16 equ 0x4
|
||||||
|
.task equ 0x5
|
||||||
|
.interrupt16 equ 0x6
|
||||||
|
.trap16 equ 0x7
|
||||||
|
.tssAvailabe32 equ 0x9
|
||||||
|
.tssBusy32 equ 0xB
|
||||||
|
.call32 equ 0xC
|
||||||
|
.interrupt32 equ 0xE
|
||||||
|
.trap32 equ 0xF
|
||||||
|
; long mode
|
||||||
|
.ldt32 equ 0x2
|
||||||
|
.tssAvailabe64 equ 0x9
|
||||||
|
.tssBusy64 equ 0xB
|
||||||
|
.call64 equ 0xC
|
||||||
|
.interrupt64 equ 0xE
|
||||||
|
.trap64 equ 0xF
|
||||||
|
|
||||||
|
gdt_flag:
|
||||||
|
.granularity equ 1 << 7
|
||||||
|
.available equ 1 << 4
|
||||||
|
;user
|
||||||
|
.default_operand_size equ 1 << 6
|
||||||
|
; code
|
||||||
|
.long_mode equ 1 << 5
|
||||||
|
; data
|
||||||
|
.reserved equ 1 << 5
|
||||||
|
|
||||||
|
gdtr:
|
||||||
|
dw gdt.end + 1 ; size
|
||||||
|
dq gdt ; offset
|
||||||
|
|
||||||
|
gdt:
|
||||||
|
.null equ $ - gdt
|
||||||
|
dq 0
|
||||||
|
|
||||||
|
.lm64_code equ $ - gdt
|
||||||
|
istruc GDTEntry
|
||||||
|
at GDTEntry.limitl, dw 0
|
||||||
|
at GDTEntry.basel, dw 0
|
||||||
|
at GDTEntry.basem, db 0
|
||||||
|
at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.code
|
||||||
|
at GDTEntry.flags__limith, db gdt_flag.long_mode
|
||||||
|
at GDTEntry.baseh, db 0
|
||||||
|
iend
|
||||||
|
|
||||||
|
.lm64_data equ $ - gdt
|
||||||
|
istruc GDTEntry
|
||||||
|
at GDTEntry.limitl, dw 0
|
||||||
|
at GDTEntry.basel, dw 0
|
||||||
|
at GDTEntry.basem, db 0
|
||||||
|
; AMD System Programming Manual states that the writeable bit is ignored in long mode, but ss can not be set to this descriptor without it
|
||||||
|
at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.writable
|
||||||
|
at GDTEntry.flags__limith, db 0
|
||||||
|
at GDTEntry.baseh, db 0
|
||||||
|
iend
|
||||||
|
|
||||||
|
.pm32_code equ $ - gdt
|
||||||
|
istruc GDTEntry
|
||||||
|
at GDTEntry.limitl, dw 0xFFFF
|
||||||
|
at GDTEntry.basel, dw 0
|
||||||
|
at GDTEntry.basem, db 0
|
||||||
|
at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.code | gdt_attr.readable
|
||||||
|
at GDTEntry.flags__limith, db 0xF | gdt_flag.granularity | gdt_flag.default_operand_size
|
||||||
|
at GDTEntry.baseh, db 0
|
||||||
|
iend
|
||||||
|
|
||||||
|
.pm32_data equ $ - gdt
|
||||||
|
istruc GDTEntry
|
||||||
|
at GDTEntry.limitl, dw 0xFFFF
|
||||||
|
at GDTEntry.basel, dw 0
|
||||||
|
at GDTEntry.basem, db 0
|
||||||
|
at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.writable
|
||||||
|
at GDTEntry.flags__limith, db 0xF | gdt_flag.granularity | gdt_flag.default_operand_size
|
||||||
|
at GDTEntry.baseh, db 0
|
||||||
|
iend
|
||||||
|
|
||||||
|
.pm16_code equ $ - gdt
|
||||||
|
istruc GDTEntry
|
||||||
|
at GDTEntry.limitl, dw 0xFFFF
|
||||||
|
at GDTEntry.basel, dw 0
|
||||||
|
at GDTEntry.basem, db 0
|
||||||
|
at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.code | gdt_attr.readable
|
||||||
|
at GDTEntry.flags__limith, db 0xF
|
||||||
|
at GDTEntry.baseh, db 0
|
||||||
|
iend
|
||||||
|
|
||||||
|
.pm16_data equ $ - gdt
|
||||||
|
istruc GDTEntry
|
||||||
|
at GDTEntry.limitl, dw 0xFFFF
|
||||||
|
at GDTEntry.basel, dw 0
|
||||||
|
at GDTEntry.basem, db 0
|
||||||
|
at GDTEntry.attribute, db gdt_attr.present | gdt_attr.user | gdt_attr.writable
|
||||||
|
at GDTEntry.flags__limith, db 0xF
|
||||||
|
at GDTEntry.baseh, db 0
|
||||||
|
iend
|
||||||
|
|
||||||
|
.end equ $ - gdt
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
; Simple ISO emulation with el torito
|
||||||
|
|
||||||
|
; Fill until CD sector 0x10
|
||||||
|
times (0x10*2048)-($-$$) db 0
|
||||||
|
|
||||||
|
; Volume record
|
||||||
|
;TODO: fill in more fields
|
||||||
|
iso_volume_record:
|
||||||
|
db 1 ; Type volume record
|
||||||
|
db "CD001" ; Identifier
|
||||||
|
db 1 ; Version
|
||||||
|
db 0 ; Unused
|
||||||
|
times 32 db ' ' ; System identifier
|
||||||
|
.volume_id: ; Volume identifier
|
||||||
|
db 'Redox OS'
|
||||||
|
times 32-($-.volume_id) db ' '
|
||||||
|
times 8 db 0 ; Unused
|
||||||
|
db 0x15, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15 ; Volume space size (0x15)
|
||||||
|
times 32 db 0 ; Unused
|
||||||
|
db 0x01, 0x00, 0x00, 0x01 ; Volume set size
|
||||||
|
db 0x01, 0x00, 0x00, 0x01 ; Volume sequence number
|
||||||
|
db 0x00, 0x08, 0x08, 0x00 ; Logical block size in little and big endian
|
||||||
|
|
||||||
|
times 156-($-iso_volume_record) db 0
|
||||||
|
|
||||||
|
; Root directory entry
|
||||||
|
.root_directory:
|
||||||
|
db 0x22 ; Length of entry
|
||||||
|
db 0x00 ; Length of extended attributes
|
||||||
|
db 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14 ; Location of extent (0x14)
|
||||||
|
db 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 ; Size of extent
|
||||||
|
db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Recording time
|
||||||
|
db 0x02 ; File flags
|
||||||
|
db 0x00 ; Interleaved file unit size
|
||||||
|
db 0x00 ; Interleaved gap size
|
||||||
|
db 0x01, 0x00, 0x00, 0x01 ; Volume sequence number
|
||||||
|
db 0x01 ; Length of file identifier
|
||||||
|
db 0x00 ; File identifier
|
||||||
|
|
||||||
|
times 128 db ' ' ; Volume set identifier
|
||||||
|
times 128 db ' ' ; Publisher identifier
|
||||||
|
times 128 db ' ' ; Data preparer identifier
|
||||||
|
times 128 db ' ' ; Application identifier
|
||||||
|
times 37 db ' ' ; Copyright file ID
|
||||||
|
times 37 db ' ' ; Abstract file ID
|
||||||
|
times 37 db ' ' ; Bibliographic file ID
|
||||||
|
|
||||||
|
times 881-($-iso_volume_record) db 0
|
||||||
|
|
||||||
|
db 1 ; File structure version
|
||||||
|
|
||||||
|
; Fill until CD sector 0x11
|
||||||
|
times (0x11*2048)-($-$$) db 0
|
||||||
|
|
||||||
|
; Boot record
|
||||||
|
iso_boot_record:
|
||||||
|
db 0 ; Type boot record
|
||||||
|
db "CD001" ; Identifier
|
||||||
|
db 1 ; Version
|
||||||
|
db "EL TORITO SPECIFICATION" ; Boot system identifier
|
||||||
|
times 0x47-($ - iso_boot_record) db 0 ; Padding
|
||||||
|
dd 0x13 ; Sector of boot catalog
|
||||||
|
|
||||||
|
; Fill until CD sector 0x12
|
||||||
|
times (0x12*2048)-($-$$) db 0
|
||||||
|
|
||||||
|
; Terminator
|
||||||
|
iso_terminator:
|
||||||
|
db 0xFF ; Type terminator
|
||||||
|
db "CD001" ; Identifier
|
||||||
|
db 1 ; Version
|
||||||
|
|
||||||
|
; Fill until CD sector 0x13
|
||||||
|
times (0x13*2048)-($-$$) db 0
|
||||||
|
|
||||||
|
; Boot catalog
|
||||||
|
iso_boot_catalog:
|
||||||
|
|
||||||
|
; Validation entry
|
||||||
|
.validation:
|
||||||
|
db 1 ; Header ID
|
||||||
|
db 0 ; Platform ID (x86)
|
||||||
|
dw 0 ; Reserved
|
||||||
|
times 24 db 0 ; ID string
|
||||||
|
dw 0x55aa ; Checksum
|
||||||
|
dw 0xaa55 ; Key
|
||||||
|
|
||||||
|
; Default entry
|
||||||
|
.default:
|
||||||
|
db 0x88 ; Bootable
|
||||||
|
db 4 ; Hard drive emulation
|
||||||
|
dw 0 ; Load segment (0 is platform default)
|
||||||
|
db 0xEE ; Partition type (0xEE is protective MBR)
|
||||||
|
db 0 ; Unused
|
||||||
|
dw 1 ; Sector count
|
||||||
|
dd 0 ; Start address for virtual disk
|
||||||
|
times 20 db 0 ; Padding
|
||||||
|
|
||||||
|
; EFI section header entry
|
||||||
|
.efi_section_header:
|
||||||
|
db 0x91 ; Final header
|
||||||
|
db 0xEF ; Platform ID (EFI)
|
||||||
|
dw 1 ; Number of section header entries
|
||||||
|
times 28 db 0 ; ID string
|
||||||
|
|
||||||
|
; EFI section entry
|
||||||
|
.efi_section_entry:
|
||||||
|
db 0x88 ; Bootable
|
||||||
|
db 0 ; No emulation
|
||||||
|
dw 0 ; Load segment (0 is platform default)
|
||||||
|
db 0 ; Partition type (not used)
|
||||||
|
db 0 ; Unused
|
||||||
|
dw 512 ; Sector count (1 MiB = 512 CD sectors)
|
||||||
|
dd 512 ; Start address for virtual disk (1 MiB = 512 CD sectors)
|
||||||
|
times 20 db 0 ; Padding
|
||||||
|
|
||||||
|
; Fill until CD sector 0x14
|
||||||
|
times (0x14*2048)-($-$$) db 0
|
||||||
|
|
||||||
|
iso_root_directory:
|
||||||
|
.self:
|
||||||
|
db 0x22 ; Length of entry
|
||||||
|
db 0x00 ; Length of extended attributes
|
||||||
|
db 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14 ; Location of extent (0x14)
|
||||||
|
db 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 ; Size of extent
|
||||||
|
db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Recording time
|
||||||
|
db 0x02 ; File flags
|
||||||
|
db 0x00 ; Interleaved file unit size
|
||||||
|
db 0x00 ; Interleaved gap size
|
||||||
|
db 0x01, 0x00, 0x00, 0x01 ; Volume sequence number
|
||||||
|
db 0x01 ; Length of file identifier
|
||||||
|
db 0x00 ; File identifier
|
||||||
|
|
||||||
|
.parent:
|
||||||
|
db 0x22 ; Length of entry
|
||||||
|
db 0x00 ; Length of extended attributes
|
||||||
|
db 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14 ; Location of extent (0x14)
|
||||||
|
db 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 ; Size of extent
|
||||||
|
db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Recording time
|
||||||
|
db 0x02 ; File flags
|
||||||
|
db 0x00 ; Interleaved file unit size
|
||||||
|
db 0x00 ; Interleaved gap size
|
||||||
|
db 0x01, 0x00, 0x00, 0x01 ; Volume sequence number
|
||||||
|
db 0x01 ; Length of file identifier
|
||||||
|
db 0x01 ; File identifier
|
||||||
|
|
||||||
|
.boot_cat:
|
||||||
|
db 0x2C ; Length of entry
|
||||||
|
db 0x00 ; Length of extended attributes
|
||||||
|
db 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13 ; Location of extent (0x13)
|
||||||
|
db 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00 ; Size of extent
|
||||||
|
db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ; Recording time
|
||||||
|
db 0x00 ; File flags
|
||||||
|
db 0x00 ; Interleaved file unit size
|
||||||
|
db 0x00 ; Interleaved gap size
|
||||||
|
db 0x01, 0x00, 0x00, 0x01 ; Volume sequence number
|
||||||
|
db 0x0A ; Length of file identifier
|
||||||
|
db "BOOT.CAT;1",0 ; File identifier
|
||||||
|
|
||||||
|
; Fill until CD sector 0x15
|
||||||
|
times (0x15*2048)-($-$$) db 0
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
SECTION .text
|
||||||
|
USE32
|
||||||
|
|
||||||
|
long_mode:
|
||||||
|
.func: dq 0
|
||||||
|
.page_table: dd 0
|
||||||
|
|
||||||
|
.entry:
|
||||||
|
; disable interrupts
|
||||||
|
cli
|
||||||
|
|
||||||
|
; disable paging
|
||||||
|
mov eax, cr0
|
||||||
|
and eax, 0x7FFFFFFF
|
||||||
|
mov cr0, eax
|
||||||
|
|
||||||
|
; enable FXSAVE/FXRSTOR, Page Global, Page Address Extension, and Page Size Extension
|
||||||
|
mov eax, cr4
|
||||||
|
or eax, 1 << 9 | 1 << 7 | 1 << 5 | 1 << 4
|
||||||
|
mov cr4, eax
|
||||||
|
|
||||||
|
; load long mode GDT
|
||||||
|
lgdt [gdtr]
|
||||||
|
|
||||||
|
; enable long mode
|
||||||
|
mov ecx, 0xC0000080 ; Read from the EFER MSR.
|
||||||
|
rdmsr
|
||||||
|
or eax, 1 << 11 | 1 << 8 ; Set the Long-Mode-Enable and NXE bit.
|
||||||
|
wrmsr
|
||||||
|
|
||||||
|
; set page table
|
||||||
|
mov eax, [.page_table]
|
||||||
|
mov cr3, eax
|
||||||
|
|
||||||
|
; enabling paging and protection simultaneously
|
||||||
|
mov eax, cr0
|
||||||
|
or eax, 1 << 31 | 1 << 16 | 1 ;Bit 31: Paging, Bit 16: write protect kernel, Bit 0: Protected Mode
|
||||||
|
mov cr0, eax
|
||||||
|
|
||||||
|
; far jump to enable Long Mode and load CS with 64 bit segment
|
||||||
|
jmp gdt.lm64_code:.inner
|
||||||
|
|
||||||
|
USE64
|
||||||
|
|
||||||
|
.inner:
|
||||||
|
; load all the other segments with 64 bit data segments
|
||||||
|
mov rax, gdt.lm64_data
|
||||||
|
mov ds, rax
|
||||||
|
mov es, rax
|
||||||
|
mov fs, rax
|
||||||
|
mov gs, rax
|
||||||
|
mov ss, rax
|
||||||
|
|
||||||
|
; jump to specified function
|
||||||
|
mov rax, [.func]
|
||||||
|
jmp rax
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
SECTION .text
|
||||||
|
USE16
|
||||||
|
|
||||||
|
; provide function for printing in x86 real mode
|
||||||
|
|
||||||
|
; print a string and a newline
|
||||||
|
; CLOBBER
|
||||||
|
; ax
|
||||||
|
print_line:
|
||||||
|
mov al, 13
|
||||||
|
call print_char
|
||||||
|
mov al, 10
|
||||||
|
jmp print_char
|
||||||
|
|
||||||
|
; print a string
|
||||||
|
; IN
|
||||||
|
; si: points at zero-terminated String
|
||||||
|
; CLOBBER
|
||||||
|
; si, ax
|
||||||
|
print:
|
||||||
|
pushf
|
||||||
|
cld
|
||||||
|
.loop:
|
||||||
|
lodsb
|
||||||
|
test al, al
|
||||||
|
jz .done
|
||||||
|
call print_char
|
||||||
|
jmp .loop
|
||||||
|
.done:
|
||||||
|
popf
|
||||||
|
ret
|
||||||
|
|
||||||
|
; print a character
|
||||||
|
; IN
|
||||||
|
; al: character to print
|
||||||
|
print_char:
|
||||||
|
pusha
|
||||||
|
mov bx, 7
|
||||||
|
mov ah, 0x0e
|
||||||
|
int 0x10
|
||||||
|
popa
|
||||||
|
ret
|
||||||
|
|
||||||
|
; print a number in hex
|
||||||
|
; IN
|
||||||
|
; bx: the number
|
||||||
|
; CLOBBER
|
||||||
|
; al, cx
|
||||||
|
print_hex:
|
||||||
|
mov cx, 4
|
||||||
|
.lp:
|
||||||
|
mov al, bh
|
||||||
|
shr al, 4
|
||||||
|
|
||||||
|
cmp al, 0xA
|
||||||
|
jb .below_0xA
|
||||||
|
|
||||||
|
add al, 'A' - 0xA - '0'
|
||||||
|
.below_0xA:
|
||||||
|
add al, '0'
|
||||||
|
|
||||||
|
call print_char
|
||||||
|
|
||||||
|
shl bx, 4
|
||||||
|
loop .lp
|
||||||
|
|
||||||
|
ret
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
SECTION .text
|
||||||
|
USE16
|
||||||
|
|
||||||
|
protected_mode:
|
||||||
|
|
||||||
|
.func: dd 0
|
||||||
|
|
||||||
|
.entry:
|
||||||
|
; disable interrupts
|
||||||
|
cli
|
||||||
|
|
||||||
|
; load protected mode GDT
|
||||||
|
lgdt [gdtr]
|
||||||
|
|
||||||
|
; set protected mode bit of cr0
|
||||||
|
mov eax, cr0
|
||||||
|
or eax, 1
|
||||||
|
mov cr0, eax
|
||||||
|
|
||||||
|
; far jump to load CS with 32 bit segment
|
||||||
|
jmp gdt.pm32_code:.inner
|
||||||
|
|
||||||
|
USE32
|
||||||
|
|
||||||
|
.inner:
|
||||||
|
; load all the other segments with 32 bit data segments
|
||||||
|
mov eax, gdt.pm32_data
|
||||||
|
mov ds, eax
|
||||||
|
mov es, eax
|
||||||
|
mov fs, eax
|
||||||
|
mov gs, eax
|
||||||
|
mov ss, eax
|
||||||
|
|
||||||
|
; jump to specified function
|
||||||
|
mov eax, [.func]
|
||||||
|
jmp eax
|
||||||
@@ -0,0 +1,222 @@
|
|||||||
|
ORG 0x7C00
|
||||||
|
SECTION .text
|
||||||
|
USE16
|
||||||
|
|
||||||
|
stage1: ; dl comes with disk
|
||||||
|
; initialize segment registers
|
||||||
|
xor ax, ax
|
||||||
|
mov ds, ax
|
||||||
|
mov es, ax
|
||||||
|
mov ss, ax
|
||||||
|
|
||||||
|
; initialize stack
|
||||||
|
mov sp, 0x7C00
|
||||||
|
|
||||||
|
; initialize CS
|
||||||
|
push ax
|
||||||
|
push word .set_cs
|
||||||
|
retf
|
||||||
|
|
||||||
|
.set_cs:
|
||||||
|
|
||||||
|
; save disk number
|
||||||
|
mov [disk], dl
|
||||||
|
|
||||||
|
mov si, stage_msg
|
||||||
|
call print
|
||||||
|
mov al, '1'
|
||||||
|
call print_char
|
||||||
|
call print_line
|
||||||
|
|
||||||
|
; read CHS gemotry
|
||||||
|
; CL (bits 0-5) = maximum sector number
|
||||||
|
; CL (bits 6-7) = high bits of max cylinder number
|
||||||
|
; CH = low bits of maximum cylinder number
|
||||||
|
; DH = maximum head number
|
||||||
|
mov ah, 0x08
|
||||||
|
mov dl, [disk]
|
||||||
|
xor di, di
|
||||||
|
int 0x13
|
||||||
|
jc error ; carry flag set on error
|
||||||
|
mov bl, ch
|
||||||
|
mov bh, cl
|
||||||
|
shr bh, 6
|
||||||
|
mov [chs.c], bx
|
||||||
|
shr dx, 8
|
||||||
|
inc dx ; returns heads - 1
|
||||||
|
mov [chs.h], dx
|
||||||
|
and cl, 0x3f
|
||||||
|
mov [chs.s], cl
|
||||||
|
|
||||||
|
mov eax, (stage2 - stage1) / 512
|
||||||
|
mov bx, stage2
|
||||||
|
mov cx, (stage3.end - stage2) / 512
|
||||||
|
mov dx, 0
|
||||||
|
call load
|
||||||
|
|
||||||
|
mov si, stage_msg
|
||||||
|
call print
|
||||||
|
mov al, '2'
|
||||||
|
call print_char
|
||||||
|
call print_line
|
||||||
|
|
||||||
|
jmp stage2.entry
|
||||||
|
|
||||||
|
; load some sectors from disk to a buffer in memory
|
||||||
|
; buffer has to be below 1MiB
|
||||||
|
; IN
|
||||||
|
; ax: start sector
|
||||||
|
; bx: offset of buffer
|
||||||
|
; cx: number of sectors (512 Bytes each)
|
||||||
|
; dx: segment of buffer
|
||||||
|
; CLOBBER
|
||||||
|
; ax, bx, cx, dx, si
|
||||||
|
; TODO rewrite to (eventually) move larger parts at once
|
||||||
|
; if that is done increase buffer_size_sectors in startup-common to that (max 0x80000 - startup_end)
|
||||||
|
load:
|
||||||
|
cmp cx, 127
|
||||||
|
jbe .good_size
|
||||||
|
|
||||||
|
pusha
|
||||||
|
mov cx, 127
|
||||||
|
call load
|
||||||
|
popa
|
||||||
|
add eax, 127
|
||||||
|
add dx, 127 * 512 / 16
|
||||||
|
sub cx, 127
|
||||||
|
|
||||||
|
jmp load
|
||||||
|
.good_size:
|
||||||
|
mov [DAPACK.addr], eax
|
||||||
|
mov [DAPACK.buf], bx
|
||||||
|
mov [DAPACK.count], cx
|
||||||
|
mov [DAPACK.seg], dx
|
||||||
|
|
||||||
|
call print_dapack
|
||||||
|
|
||||||
|
cmp byte [chs.s], 0
|
||||||
|
jne .chs
|
||||||
|
;INT 0x13 extended read does not work on CDROM!
|
||||||
|
mov dl, [disk]
|
||||||
|
mov si, DAPACK
|
||||||
|
mov ah, 0x42
|
||||||
|
int 0x13
|
||||||
|
jc error ; carry flag set on error
|
||||||
|
ret
|
||||||
|
|
||||||
|
.chs:
|
||||||
|
; calculate CHS
|
||||||
|
xor edx, edx
|
||||||
|
mov eax, [DAPACK.addr]
|
||||||
|
div dword [chs.s] ; divide by sectors
|
||||||
|
mov ecx, edx ; move sector remainder to ecx
|
||||||
|
xor edx, edx
|
||||||
|
div dword [chs.h] ; divide by heads
|
||||||
|
; eax has cylinders, edx has heads, ecx has sectors
|
||||||
|
|
||||||
|
; Sector cannot be greater than 63
|
||||||
|
inc ecx ; Sector is base 1
|
||||||
|
cmp ecx, 63
|
||||||
|
ja error_chs
|
||||||
|
|
||||||
|
; Head cannot be greater than 255
|
||||||
|
cmp edx, 255
|
||||||
|
ja error_chs
|
||||||
|
|
||||||
|
; Cylinder cannot be greater than 1023
|
||||||
|
cmp eax, 1023
|
||||||
|
ja error_chs
|
||||||
|
|
||||||
|
; Move CHS values to parameters
|
||||||
|
mov ch, al
|
||||||
|
shl ah, 6
|
||||||
|
and cl, 0x3f
|
||||||
|
or cl, ah
|
||||||
|
shl dx, 8
|
||||||
|
|
||||||
|
; read from disk using CHS
|
||||||
|
mov al, [DAPACK.count]
|
||||||
|
mov ah, 0x02 ; disk read (CHS)
|
||||||
|
mov bx, [DAPACK.buf]
|
||||||
|
mov dl, [disk]
|
||||||
|
push es ; save ES
|
||||||
|
mov es, [DAPACK.seg]
|
||||||
|
int 0x13
|
||||||
|
pop es ; restore EC
|
||||||
|
jc error ; carry flag set on error
|
||||||
|
ret
|
||||||
|
|
||||||
|
print_dapack:
|
||||||
|
mov bx, [DAPACK.addr + 2]
|
||||||
|
call print_hex
|
||||||
|
|
||||||
|
mov bx, [DAPACK.addr]
|
||||||
|
call print_hex
|
||||||
|
|
||||||
|
mov al, '#'
|
||||||
|
call print_char
|
||||||
|
|
||||||
|
mov bx, [DAPACK.count]
|
||||||
|
call print_hex
|
||||||
|
|
||||||
|
mov al, ' '
|
||||||
|
call print_char
|
||||||
|
|
||||||
|
mov bx, [DAPACK.seg]
|
||||||
|
call print_hex
|
||||||
|
|
||||||
|
mov al, ':'
|
||||||
|
call print_char
|
||||||
|
|
||||||
|
mov bx, [DAPACK.buf]
|
||||||
|
call print_hex
|
||||||
|
|
||||||
|
call print_line
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
error_chs:
|
||||||
|
mov ah, 0
|
||||||
|
|
||||||
|
error:
|
||||||
|
call print_line
|
||||||
|
|
||||||
|
mov bh, 0
|
||||||
|
mov bl, ah
|
||||||
|
call print_hex
|
||||||
|
|
||||||
|
mov al, ' '
|
||||||
|
call print_char
|
||||||
|
|
||||||
|
mov si, error_msg
|
||||||
|
call print
|
||||||
|
call print_line
|
||||||
|
.halt:
|
||||||
|
cli
|
||||||
|
hlt
|
||||||
|
jmp .halt
|
||||||
|
|
||||||
|
%include "print.asm"
|
||||||
|
|
||||||
|
stage_msg: db "Stage ",0
|
||||||
|
error_msg: db "ERROR",0
|
||||||
|
|
||||||
|
disk: db 0
|
||||||
|
|
||||||
|
chs:
|
||||||
|
.c: dd 0
|
||||||
|
.h: dd 0
|
||||||
|
.s: dd 0
|
||||||
|
|
||||||
|
DAPACK:
|
||||||
|
db 0x10
|
||||||
|
db 0
|
||||||
|
.count: dw 0 ; int 13 resets this to # of blocks actually read/written
|
||||||
|
.buf: dw 0 ; memory buffer destination address (0:7c00)
|
||||||
|
.seg: dw 0 ; in memory page zero
|
||||||
|
.addr: dq 0 ; put the lba to read in this spot
|
||||||
|
|
||||||
|
times 446-($-$$) db 0
|
||||||
|
partitions: times 4 * 16 db 0
|
||||||
|
db 0x55
|
||||||
|
db 0xaa
|
||||||
@@ -0,0 +1,134 @@
|
|||||||
|
SECTION .text
|
||||||
|
USE16
|
||||||
|
|
||||||
|
stage2.entry:
|
||||||
|
; check for required features
|
||||||
|
call cpuid_check
|
||||||
|
|
||||||
|
; enable A20-Line via IO-Port 92, might not work on all motherboards
|
||||||
|
in al, 0x92
|
||||||
|
or al, 2
|
||||||
|
out 0x92, al
|
||||||
|
|
||||||
|
mov dword [protected_mode.func], stage3.entry
|
||||||
|
jmp protected_mode.entry
|
||||||
|
|
||||||
|
%include "cpuid.asm"
|
||||||
|
%include "gdt.asm"
|
||||||
|
%include "long_mode.asm"
|
||||||
|
%include "protected_mode.asm"
|
||||||
|
%include "thunk.asm"
|
||||||
|
|
||||||
|
USE32
|
||||||
|
|
||||||
|
stage3.entry:
|
||||||
|
; stage3 stack at 448 KiB (512KiB minus 64KiB disk buffer)
|
||||||
|
mov esp, 0x70000
|
||||||
|
|
||||||
|
; push arguments
|
||||||
|
mov eax, thunk.int16
|
||||||
|
push eax
|
||||||
|
mov eax, thunk.int15
|
||||||
|
push eax
|
||||||
|
mov eax, thunk.int13
|
||||||
|
push eax
|
||||||
|
mov eax, thunk.int10
|
||||||
|
push eax
|
||||||
|
xor eax, eax
|
||||||
|
mov al, [disk]
|
||||||
|
push eax
|
||||||
|
mov eax, kernel.entry
|
||||||
|
push eax
|
||||||
|
mov eax, [stage3 + 0x18]
|
||||||
|
call eax
|
||||||
|
.halt:
|
||||||
|
cli
|
||||||
|
hlt
|
||||||
|
jmp .halt
|
||||||
|
|
||||||
|
kernel:
|
||||||
|
.stack: dq 0
|
||||||
|
.func: dq 0
|
||||||
|
.args: dq 0
|
||||||
|
|
||||||
|
.entry:
|
||||||
|
; page_table: usize
|
||||||
|
mov eax, [esp + 4]
|
||||||
|
mov [long_mode.page_table], eax
|
||||||
|
|
||||||
|
; stack: u64
|
||||||
|
mov eax, [esp + 8]
|
||||||
|
mov [.stack], eax
|
||||||
|
mov eax, [esp + 12]
|
||||||
|
mov [.stack + 4], eax
|
||||||
|
|
||||||
|
; func: u64
|
||||||
|
mov eax, [esp + 16]
|
||||||
|
mov [.func], eax
|
||||||
|
mov eax, [esp + 20]
|
||||||
|
mov [.func + 4], eax
|
||||||
|
|
||||||
|
; args: *const KernelArgs
|
||||||
|
mov eax, [esp + 24]
|
||||||
|
mov [.args], eax
|
||||||
|
|
||||||
|
; long_mode: usize
|
||||||
|
mov eax, [esp + 28]
|
||||||
|
test eax, eax
|
||||||
|
jz .inner32
|
||||||
|
|
||||||
|
mov eax, .inner64
|
||||||
|
mov [long_mode.func], eax
|
||||||
|
jmp long_mode.entry
|
||||||
|
|
||||||
|
.inner32:
|
||||||
|
; disable paging
|
||||||
|
mov eax, cr0
|
||||||
|
and eax, 0x7FFFFFFF
|
||||||
|
mov cr0, eax
|
||||||
|
|
||||||
|
;TODO: PAE (1 << 5)
|
||||||
|
; enable FXSAVE/FXRSTOR, Page Global, and Page Size Extension
|
||||||
|
mov eax, cr4
|
||||||
|
or eax, 1 << 9 | 1 << 7 | 1 << 4
|
||||||
|
mov cr4, eax
|
||||||
|
|
||||||
|
; set page table
|
||||||
|
mov eax, [long_mode.page_table]
|
||||||
|
mov cr3, eax
|
||||||
|
|
||||||
|
; enabling paging and protection simultaneously
|
||||||
|
mov eax, cr0
|
||||||
|
; Bit 31: Paging, Bit 16: write protect kernel, Bit 0: Protected Mode
|
||||||
|
or eax, 1 << 31 | 1 << 16 | 1
|
||||||
|
mov cr0, eax
|
||||||
|
|
||||||
|
; enable FPU
|
||||||
|
;TODO: move to Rust
|
||||||
|
mov eax, cr0
|
||||||
|
and al, 11110011b ; Clear task switched (3) and emulation (2)
|
||||||
|
or al, 00100010b ; Set numeric error (5) monitor co-processor (1)
|
||||||
|
mov cr0, eax
|
||||||
|
fninit
|
||||||
|
|
||||||
|
mov esp, [.stack]
|
||||||
|
mov eax, [.args]
|
||||||
|
push eax
|
||||||
|
mov eax, [.func]
|
||||||
|
call eax
|
||||||
|
.halt32:
|
||||||
|
cli
|
||||||
|
hlt
|
||||||
|
jmp .halt32
|
||||||
|
|
||||||
|
USE64
|
||||||
|
|
||||||
|
.inner64:
|
||||||
|
mov rsp, [.stack]
|
||||||
|
mov rax, [.func]
|
||||||
|
mov rdi, [.args]
|
||||||
|
call rax
|
||||||
|
.halt64:
|
||||||
|
cli
|
||||||
|
hlt
|
||||||
|
jmp .halt64
|
||||||
@@ -0,0 +1,149 @@
|
|||||||
|
SECTION .text
|
||||||
|
USE32
|
||||||
|
|
||||||
|
thunk:
|
||||||
|
.int10:
|
||||||
|
mov dword [.func], .int10_real
|
||||||
|
jmp .enter
|
||||||
|
|
||||||
|
.int13:
|
||||||
|
mov dword [.func], .int13_real
|
||||||
|
jmp .enter
|
||||||
|
|
||||||
|
.int15:
|
||||||
|
mov dword [.func], .int15_real
|
||||||
|
jmp .enter
|
||||||
|
|
||||||
|
.int16:
|
||||||
|
mov dword [.func], .int16_real
|
||||||
|
jmp .enter
|
||||||
|
|
||||||
|
.func: dd 0
|
||||||
|
.esp: dd 0
|
||||||
|
.cr0: dd 0
|
||||||
|
|
||||||
|
.enter:
|
||||||
|
; save flags
|
||||||
|
pushfd
|
||||||
|
|
||||||
|
; save registers
|
||||||
|
pushad
|
||||||
|
|
||||||
|
; save esp
|
||||||
|
mov [.esp], esp
|
||||||
|
|
||||||
|
; load gdt
|
||||||
|
lgdt [gdtr]
|
||||||
|
|
||||||
|
; far jump to protected mode 16-bit
|
||||||
|
jmp gdt.pm16_code:.pm16
|
||||||
|
|
||||||
|
.exit:
|
||||||
|
; set segment selectors to 32-bit protected mode
|
||||||
|
mov eax, gdt.pm32_data
|
||||||
|
mov ds, eax
|
||||||
|
mov es, eax
|
||||||
|
mov fs, eax
|
||||||
|
mov gs, eax
|
||||||
|
mov ss, eax
|
||||||
|
|
||||||
|
; restore esp
|
||||||
|
mov esp, [.esp]
|
||||||
|
|
||||||
|
; restore registers
|
||||||
|
popad
|
||||||
|
|
||||||
|
; restore flags
|
||||||
|
popfd
|
||||||
|
|
||||||
|
; return
|
||||||
|
ret
|
||||||
|
|
||||||
|
USE16
|
||||||
|
|
||||||
|
.int10_real:
|
||||||
|
int 0x10
|
||||||
|
ret
|
||||||
|
|
||||||
|
.int13_real:
|
||||||
|
int 0x13
|
||||||
|
ret
|
||||||
|
|
||||||
|
.int15_real:
|
||||||
|
int 0x15
|
||||||
|
ret
|
||||||
|
|
||||||
|
.int16_real:
|
||||||
|
int 0x16
|
||||||
|
ret
|
||||||
|
|
||||||
|
.pm16:
|
||||||
|
; set segment selectors to protected mode 16-bit
|
||||||
|
mov eax, gdt.pm16_data
|
||||||
|
mov ds, eax
|
||||||
|
mov es, eax
|
||||||
|
mov fs, eax
|
||||||
|
mov gs, eax
|
||||||
|
mov ss, eax
|
||||||
|
|
||||||
|
; save cr0
|
||||||
|
mov eax, cr0
|
||||||
|
mov [.cr0], eax
|
||||||
|
|
||||||
|
; disable paging and protected mode
|
||||||
|
and eax, 0x7FFFFFFE
|
||||||
|
mov cr0, eax
|
||||||
|
|
||||||
|
; far jump to real mode
|
||||||
|
jmp 0:.real
|
||||||
|
|
||||||
|
.real:
|
||||||
|
; set segment selectors to real mode
|
||||||
|
mov eax, 0
|
||||||
|
mov ds, eax
|
||||||
|
mov es, eax
|
||||||
|
mov fs, eax
|
||||||
|
mov gs, eax
|
||||||
|
mov ss, eax
|
||||||
|
|
||||||
|
; set stack
|
||||||
|
mov esp, 0x7C00 - 64
|
||||||
|
|
||||||
|
; load registers and ES
|
||||||
|
pop es
|
||||||
|
pop edi
|
||||||
|
pop esi
|
||||||
|
pop ebp
|
||||||
|
pop ebx
|
||||||
|
pop edx
|
||||||
|
pop ecx
|
||||||
|
pop eax
|
||||||
|
|
||||||
|
; enable interrupts
|
||||||
|
sti
|
||||||
|
|
||||||
|
; call real mode function
|
||||||
|
call [.func]
|
||||||
|
|
||||||
|
; disable interrupts
|
||||||
|
cli
|
||||||
|
|
||||||
|
; save registers and ES
|
||||||
|
push eax
|
||||||
|
push ecx
|
||||||
|
push edx
|
||||||
|
push ebx
|
||||||
|
push ebp
|
||||||
|
push esi
|
||||||
|
push edi
|
||||||
|
push es
|
||||||
|
|
||||||
|
; load gdt (BIOS sometimes overwrites this)
|
||||||
|
lgdt [gdtr]
|
||||||
|
|
||||||
|
; restore cr0, will enable protected mode
|
||||||
|
mov eax, [.cr0]
|
||||||
|
mov cr0, eax
|
||||||
|
|
||||||
|
; far jump to protected mode 32-bit
|
||||||
|
jmp gdt.pm32_code:.exit
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
OUTPUT_FORMAT("elf64-littleriscv", "elf64-littleriscv", "elf64-littleriscv")
|
||||||
|
OUTPUT_ARCH(riscv)
|
||||||
|
ENTRY(coff_start)
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
PROVIDE(ImageBase = .);
|
||||||
|
. = SEGMENT_START("text-segment", 0) + SIZEOF_HEADERS;
|
||||||
|
.note.gnu.build-id : { *(.note.gnu.build-id) }
|
||||||
|
.hash : { *(.hash) *(.gnu.hash) }
|
||||||
|
|
||||||
|
. = ALIGN(4096);
|
||||||
|
.text :
|
||||||
|
{
|
||||||
|
PROVIDE(_text = .);
|
||||||
|
*(.text.unlikely .text.*_unlikely .text.unlikely.*)
|
||||||
|
*(.text.exit .text.exit.*)
|
||||||
|
*(.text.startup .text.startup.*)
|
||||||
|
*(.text.hot .text.hot.*)
|
||||||
|
*(SORT(.text.sorted.*))
|
||||||
|
*(.text .stub .text.* .gnu.linkonce.t.*)
|
||||||
|
/* .gnu.warning sections are handled specially by elf.em. */
|
||||||
|
*(.gnu.warning)
|
||||||
|
}
|
||||||
|
PROVIDE (__etext = .);
|
||||||
|
PROVIDE (_etext = .);
|
||||||
|
PROVIDE (etext = .);
|
||||||
|
. = ALIGN(4096);
|
||||||
|
|
||||||
|
.rdata :
|
||||||
|
{
|
||||||
|
*(.rodata .rodata.* .gnu.linkonce.r.*)
|
||||||
|
*(.rodata1)
|
||||||
|
KEEP (*(.eh_frame))
|
||||||
|
*(.eh_frame.*)
|
||||||
|
*(.dynamic)
|
||||||
|
}
|
||||||
|
. = ALIGN(4096);
|
||||||
|
.data :
|
||||||
|
{
|
||||||
|
*(.got) *(.igot)
|
||||||
|
*(.got.plt) *(.igot.plt)
|
||||||
|
*(.data .data.* .gnu.linkonce.d.*)
|
||||||
|
*(.data1)
|
||||||
|
*(.srodata.cst16) *(.srodata.cst8) *(.srodata.cst4) *(.srodata.cst2) *(.srodata .srodata.*)
|
||||||
|
*(.sdata2 .sdata2.* .gnu.linkonce.s2.*)
|
||||||
|
*(.sdata .sdata.* .gnu.linkonce.s.*)
|
||||||
|
PROVIDE (_edata = .); PROVIDE (edata = .);
|
||||||
|
. = ALIGN(4096);
|
||||||
|
PROVIDE (__bss_start = .);
|
||||||
|
*(.sbss2 .sbss2.* .gnu.linkonce.sb2.*)
|
||||||
|
*(.dynsbss)
|
||||||
|
*(.sbss .sbss.* .gnu.linkonce.sb.*)
|
||||||
|
*(.scommon)
|
||||||
|
*(.dynbss)
|
||||||
|
*(.bss .bss.* .gnu.linkonce.b.*)
|
||||||
|
*(COMMON)
|
||||||
|
. = ALIGN(4096);
|
||||||
|
}
|
||||||
|
.reloc :
|
||||||
|
{
|
||||||
|
KEEP(*(.reloc*))
|
||||||
|
}
|
||||||
|
.rela :
|
||||||
|
{
|
||||||
|
*(.rela.*)
|
||||||
|
}
|
||||||
|
.data.rel.ro :
|
||||||
|
{
|
||||||
|
*(.data.rel.ro.local* .gnu.linkonce.d.rel.ro.local.*)
|
||||||
|
*(.data.rel.ro .data.rel.ro.* .gnu.linkonce.d.rel.ro.*)
|
||||||
|
}
|
||||||
|
. = ALIGN(4096);
|
||||||
|
|
||||||
|
.dynsym : { *(.dynsym) }
|
||||||
|
.dynstr : { *(.dynstr) }
|
||||||
|
|
||||||
|
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
ENTRY(start)
|
||||||
|
OUTPUT_FORMAT(elf32-i386)
|
||||||
|
|
||||||
|
SECTIONS {
|
||||||
|
/* The start address must match bootloader.asm */
|
||||||
|
. = 0x13000;
|
||||||
|
|
||||||
|
. += SIZEOF_HEADERS;
|
||||||
|
. = ALIGN(4096);
|
||||||
|
|
||||||
|
.text : {
|
||||||
|
__text_start = .;
|
||||||
|
*(.text*)
|
||||||
|
. = ALIGN(4096);
|
||||||
|
__text_end = .;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rodata : {
|
||||||
|
__rodata_start = .;
|
||||||
|
*(.rodata*)
|
||||||
|
. = ALIGN(4096);
|
||||||
|
__rodata_end = .;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data : {
|
||||||
|
__data_start = .;
|
||||||
|
*(.data*)
|
||||||
|
. = ALIGN(4096);
|
||||||
|
__data_end = .;
|
||||||
|
__bss_start = .;
|
||||||
|
*(.bss*)
|
||||||
|
. = ALIGN(4096);
|
||||||
|
__bss_end = .;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tdata : {
|
||||||
|
__tdata_start = .;
|
||||||
|
*(.tdata*)
|
||||||
|
. = ALIGN(4096);
|
||||||
|
__tdata_end = .;
|
||||||
|
__tbss_start = .;
|
||||||
|
*(.tbss*)
|
||||||
|
. += 8;
|
||||||
|
. = ALIGN(4096);
|
||||||
|
__tbss_end = .;
|
||||||
|
}
|
||||||
|
|
||||||
|
__end = .;
|
||||||
|
|
||||||
|
/DISCARD/ : {
|
||||||
|
*(.comment*)
|
||||||
|
*(.eh_frame*)
|
||||||
|
*(.gcc_except_table*)
|
||||||
|
*(.note*)
|
||||||
|
*(.rel.eh_frame*)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
export PARTED?=parted
|
||||||
|
export QEMU?=qemu-system-aarch64
|
||||||
|
|
||||||
|
all: $(BUILD)/bootloader.efi
|
||||||
|
|
||||||
|
$(BUILD)/bootloader.efi: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell find $(SOURCE)/src -type f)
|
||||||
|
mkdir -p "$(BUILD)"
|
||||||
|
env RUSTFLAGS="--cfg aes_force_soft -Zunstable-options" \
|
||||||
|
cargo rustc \
|
||||||
|
--manifest-path="$<" \
|
||||||
|
$(CARGO_ARGS) \
|
||||||
|
--bin bootloader \
|
||||||
|
-- \
|
||||||
|
--emit link="$@"
|
||||||
|
|
||||||
|
$(BUILD)/bootloader-live.efi: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell find $(SOURCE)/src -type f)
|
||||||
|
mkdir -p "$(BUILD)"
|
||||||
|
env RUSTFLAGS="--cfg aes_force_soft -Zunstable-options" \
|
||||||
|
cargo rustc \
|
||||||
|
--manifest-path="$<" \
|
||||||
|
$(CARGO_ARGS) \
|
||||||
|
--bin bootloader \
|
||||||
|
--features live \
|
||||||
|
-- \
|
||||||
|
--emit link="$@"
|
||||||
|
|
||||||
|
$(BUILD)/esp.bin: $(BUILD)/bootloader.efi
|
||||||
|
rm -f "$@.partial"
|
||||||
|
fallocate -l 64MiB "$@.partial"
|
||||||
|
mkfs.vfat -F 32 "$@.partial"
|
||||||
|
mmd -i "$@.partial" efi
|
||||||
|
mmd -i "$@.partial" efi/boot
|
||||||
|
mcopy -i "$@.partial" "$<" ::efi/boot/bootaa64.efi
|
||||||
|
mv "$@.partial" "$@"
|
||||||
|
|
||||||
|
$(BUILD)/harddrive.bin: $(BUILD)/esp.bin $(BUILD)/filesystem.bin
|
||||||
|
rm -f "$@.partial"
|
||||||
|
fallocate -l 320MiB "$@.partial"
|
||||||
|
$(PARTED) -s -a minimal "$@.partial" mklabel gpt
|
||||||
|
$(PARTED) -s -a minimal "$@.partial" mkpart ESP FAT32 1MiB 65MiB
|
||||||
|
$(PARTED) -s -a minimal "$@.partial" mkpart REDOXFS 65MiB 100%
|
||||||
|
$(PARTED) -s -a minimal "$@.partial" toggle 1 boot
|
||||||
|
dd if="$(BUILD)/esp.bin" of="$@.partial" bs=1MiB seek=1 conv=notrunc
|
||||||
|
dd if="$(BUILD)/filesystem.bin" of="$@.partial" bs=1MiB seek=65 conv=notrunc
|
||||||
|
mv "$@.partial" "$@"
|
||||||
|
|
||||||
|
$(BUILD)/firmware.rom: /usr/share/AAVMF/AAVMF_CODE.fd
|
||||||
|
cp "$<" "$@"
|
||||||
|
|
||||||
|
qemu: $(BUILD)/harddrive.bin $(BUILD)/firmware.rom
|
||||||
|
$(QEMU) \
|
||||||
|
-d cpu_reset \
|
||||||
|
-no-reboot \
|
||||||
|
-smp 4 -m 2048 \
|
||||||
|
-chardev stdio,id=debug,signal=off,mux=on \
|
||||||
|
-serial chardev:debug \
|
||||||
|
-mon chardev=debug \
|
||||||
|
-device virtio-gpu-pci \
|
||||||
|
-machine virt \
|
||||||
|
-net none \
|
||||||
|
-cpu max \
|
||||||
|
-bios "$(BUILD)/firmware.rom" \
|
||||||
|
-drive file="$<",format=raw
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
LD=riscv64-unknown-redox-ld
|
||||||
|
OBJCOPY=riscv64-unknown-redox-objcopy
|
||||||
|
SCRIPT=$(SOURCE)/linkers/riscv64-unknown-uefi.ld
|
||||||
|
PARTED?=parted
|
||||||
|
QEMU?=qemu-system-riscv64
|
||||||
|
|
||||||
|
all: $(BUILD)/bootloader.efi
|
||||||
|
|
||||||
|
$(BUILD)/%.efi: $(BUILD)/%.efi.elf $(BUILD)/%.efi.sym
|
||||||
|
$(OBJCOPY) -j .text -j .data -j .rdata -j .rela -j .reloc --target pei-riscv64-little \
|
||||||
|
--file-alignment 512 --section-alignment 4096 --subsystem 10 "$<" "$@"
|
||||||
|
|
||||||
|
.PRECIOUS: $(BUILD)/%.efi.sym
|
||||||
|
$(BUILD)/%.efi.sym: $(BUILD)/%.efi.elf
|
||||||
|
$(OBJCOPY) --only-keep-debug "$<" "$@"
|
||||||
|
|
||||||
|
$(BUILD)/%.efi.elf: $(BUILD)/%.a $(SCRIPT)
|
||||||
|
$(LD) --gc-sections -z max-page-size=0x1000 --warn-common --no-undefined -z nocombreloc -shared \
|
||||||
|
--fatal-warnings -Bsymbolic --entry coff_start -T "$(SCRIPT)" -o "$@" "$<"
|
||||||
|
|
||||||
|
$(BUILD)/bootloader.a: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell find $(SOURCE)/src -type f)
|
||||||
|
mkdir -p "$(BUILD)"
|
||||||
|
env RUSTFLAGS="--cfg aes_force_soft -Zunstable-options" \
|
||||||
|
cargo rustc \
|
||||||
|
--manifest-path="$<" \
|
||||||
|
$(CARGO_ARGS) \
|
||||||
|
--lib \
|
||||||
|
-- \
|
||||||
|
--emit link=$@
|
||||||
|
|
||||||
|
$(BUILD)/bootloader-live.a: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell find $(SOURCE)/src -type f)
|
||||||
|
mkdir -p "$(BUILD)"
|
||||||
|
env RUSTFLAGS="--cfg aes_force_soft -Zunstable-options" \
|
||||||
|
cargo rustc \
|
||||||
|
--manifest-path="$<" \
|
||||||
|
$(CARGO_ARGS) \
|
||||||
|
--lib \
|
||||||
|
--features live \
|
||||||
|
-- \
|
||||||
|
--emit link=$@
|
||||||
|
|
||||||
|
|
||||||
|
$(BUILD)/esp.bin: $(BUILD)/bootloader.efi
|
||||||
|
rm -f $@.partial
|
||||||
|
fallocate -l 64MiB $@.partial
|
||||||
|
mkfs.vfat -F 32 $@.partial
|
||||||
|
mmd -i $@.partial EFI
|
||||||
|
mmd -i $@.partial EFI/BOOT
|
||||||
|
mcopy -i $@.partial $< ::EFI/BOOT/BOOTRISCV64.EFI
|
||||||
|
mv $@.partial $@
|
||||||
|
|
||||||
|
$(BUILD)/harddrive.bin: $(BUILD)/esp.bin $(BUILD)/filesystem.bin
|
||||||
|
rm -f $@.partial
|
||||||
|
fallocate -l 320MiB $@.partial
|
||||||
|
$(PARTED) -s -a minimal $@.partial mklabel gpt
|
||||||
|
$(PARTED) -s -a minimal $@.partial mkpart ESP FAT32 1MiB 65MiB
|
||||||
|
$(PARTED) -s -a minimal $@.partial mkpart REDOXFS 65MiB 100%
|
||||||
|
$(PARTED) -s -a minimal $@.partial toggle 1 boot
|
||||||
|
dd if=$(BUILD)/esp.bin of=$@.partial bs=1MiB seek=1 conv=notrunc
|
||||||
|
dd if=$(BUILD)/filesystem.bin of=$@.partial bs=1MiB seek=65 conv=notrunc
|
||||||
|
mv $@.partial $@
|
||||||
|
|
||||||
|
$(BUILD)/fw_vars.img: /usr/share/qemu-efi-riscv64/RISCV_VIRT_VARS.fd
|
||||||
|
cp "$<" "$@"
|
||||||
|
|
||||||
|
$(BUILD)/firmware.rom: /usr/share/qemu-efi-riscv64/RISCV_VIRT_CODE.fd
|
||||||
|
cp "$<" "$@"
|
||||||
|
|
||||||
|
qemu-acpi: $(BUILD)/harddrive.bin $(BUILD)/firmware.rom $(BUILD)/fw_vars.img
|
||||||
|
$(QEMU) \
|
||||||
|
-M virt \
|
||||||
|
-d cpu_reset \
|
||||||
|
-no-reboot \
|
||||||
|
-smp 4 -m 2048 \
|
||||||
|
-chardev stdio,id=debug,signal=off,mux=on \
|
||||||
|
-serial chardev:debug \
|
||||||
|
-mon chardev=debug \
|
||||||
|
-device virtio-gpu-pci \
|
||||||
|
-machine virt \
|
||||||
|
-net none \
|
||||||
|
-cpu max \
|
||||||
|
-drive if=pflash,format=raw,unit=0,file=$(BUILD)/firmware.rom,readonly=on \
|
||||||
|
-drive if=pflash,format=raw,unit=1,file=$(BUILD)/fw_vars.img \
|
||||||
|
-drive file=$(BUILD)/harddrive.bin,format=raw,if=virtio
|
||||||
|
|
||||||
|
|
||||||
|
qemu-dtb: $(BUILD)/harddrive.bin $(BUILD)/firmware.rom $(BUILD)/fw_vars.img
|
||||||
|
$(QEMU) \
|
||||||
|
-M virt,acpi=off \
|
||||||
|
-d cpu_reset \
|
||||||
|
-no-reboot \
|
||||||
|
-smp 4 -m 2048 \
|
||||||
|
-chardev stdio,id=debug,signal=off,mux=on \
|
||||||
|
-serial chardev:debug \
|
||||||
|
-mon chardev=debug \
|
||||||
|
-device virtio-gpu-pci \
|
||||||
|
-machine virt \
|
||||||
|
-net none \
|
||||||
|
-cpu max \
|
||||||
|
-drive if=pflash,format=raw,unit=0,file=$(BUILD)/firmware.rom,readonly=on \
|
||||||
|
-drive if=pflash,format=raw,unit=1,file=$(BUILD)/fw_vars.img \
|
||||||
|
-drive file=$(BUILD)/harddrive.bin,format=raw,if=virtio -s
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
export LD?=ld
|
||||||
|
export OBJCOPY?=objcopy
|
||||||
|
export PARTED?=parted
|
||||||
|
export QEMU?=qemu-system-x86_64
|
||||||
|
|
||||||
|
all: $(BUILD)/bootloader.bin
|
||||||
|
|
||||||
|
$(BUILD)/libbootloader.a: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell find $(SOURCE)/src -type f)
|
||||||
|
mkdir -p "$(BUILD)"
|
||||||
|
env RUSTFLAGS="--cfg aes_force_soft -Zunstable-options" \
|
||||||
|
cargo rustc \
|
||||||
|
--manifest-path="$<" \
|
||||||
|
$(CARGO_ARGS) \
|
||||||
|
--lib \
|
||||||
|
-- \
|
||||||
|
--emit link="$@"
|
||||||
|
|
||||||
|
$(BUILD)/libbootloader-live.a: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell find $(SOURCE)/src -type f)
|
||||||
|
mkdir -p "$(BUILD)"
|
||||||
|
env RUSTFLAGS="--cfg aes_force_soft -Zunstable-options" \
|
||||||
|
cargo rustc \
|
||||||
|
--manifest-path="$<" \
|
||||||
|
$(CARGO_ARGS) \
|
||||||
|
--lib \
|
||||||
|
--features live \
|
||||||
|
-- \
|
||||||
|
--emit link="$@"
|
||||||
|
|
||||||
|
$(BUILD)/%.elf: $(BUILD)/lib%.a $(SOURCE)/linkers/$(TARGET).ld
|
||||||
|
$(LD) -m elf_i386 --gc-sections -z max-page-size=0x1000 -T "$(SOURCE)/linkers/$(TARGET).ld" -o "$@" "$<"
|
||||||
|
$(OBJCOPY) --only-keep-debug "$@" "$@.sym"
|
||||||
|
$(OBJCOPY) --strip-debug "$@"
|
||||||
|
|
||||||
|
$(BUILD)/%.bin: $(BUILD)/%.elf $(shell find $(SOURCE)/asm/$(TARGET) -type f)
|
||||||
|
nasm -f bin -o "$@" -l "$@.lst" -D STAGE3="$<" -i"$(SOURCE)/asm/$(TARGET)/" "$(SOURCE)/asm/$(TARGET)/bootloader.asm"
|
||||||
|
|
||||||
|
$(BUILD)/harddrive.bin: $(BUILD)/bootloader.bin $(BUILD)/filesystem.bin
|
||||||
|
rm -f "$@.partial"
|
||||||
|
fallocate -l 256MiB "$@.partial"
|
||||||
|
$(PARTED) -s -a minimal "$@.partial" mklabel msdos
|
||||||
|
$(PARTED) -s -a minimal "$@.partial" mkpart primary 2MiB 100%
|
||||||
|
dd if="$<" of="$@.partial" bs=1 count=446 conv=notrunc
|
||||||
|
dd if="$<" of="$@.partial" bs=512 skip=1 seek=1 conv=notrunc
|
||||||
|
dd if="$(BUILD)/filesystem.bin" of="$@.partial" bs=1MiB seek=2 conv=notrunc
|
||||||
|
mv "$@.partial" "$@"
|
||||||
|
|
||||||
|
qemu: $(BUILD)/harddrive.bin
|
||||||
|
$(QEMU) \
|
||||||
|
-d cpu_reset \
|
||||||
|
-no-reboot \
|
||||||
|
-smp 4 -m 2048 \
|
||||||
|
-chardev stdio,id=debug,signal=off,mux=on \
|
||||||
|
-serial chardev:debug \
|
||||||
|
-mon chardev=debug \
|
||||||
|
-machine q35 \
|
||||||
|
-net none \
|
||||||
|
-enable-kvm \
|
||||||
|
-cpu host \
|
||||||
|
-drive file="$<",format=raw
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
export PARTED?=parted
|
||||||
|
export QEMU?=qemu-system-x86_64
|
||||||
|
|
||||||
|
all: $(BUILD)/bootloader.efi
|
||||||
|
|
||||||
|
$(BUILD)/bootloader.efi: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell find $(SOURCE)/src -type f)
|
||||||
|
mkdir -p "$(BUILD)"
|
||||||
|
env RUSTFLAGS="--cfg aes_force_soft" \
|
||||||
|
cargo rustc \
|
||||||
|
--manifest-path="$<" \
|
||||||
|
$(CARGO_ARGS) \
|
||||||
|
--bin bootloader \
|
||||||
|
-- \
|
||||||
|
--emit link="$@"
|
||||||
|
|
||||||
|
$(BUILD)/bootloader-live.efi: $(SOURCE)/Cargo.toml $(SOURCE)/Cargo.lock $(shell find $(SOURCE)/src -type f)
|
||||||
|
mkdir -p $(BUILD)
|
||||||
|
cd "$(SOURCE)"
|
||||||
|
env RUSTFLAGS="--cfg aes_force_soft" \
|
||||||
|
cargo rustc \
|
||||||
|
--manifest-path="$<" \
|
||||||
|
$(CARGO_ARGS) \
|
||||||
|
--bin bootloader \
|
||||||
|
--features live \
|
||||||
|
-- \
|
||||||
|
--emit link="$@"
|
||||||
|
|
||||||
|
$(BUILD)/esp.bin: $(BUILD)/bootloader.efi
|
||||||
|
rm -f "$@.partial"
|
||||||
|
fallocate -l 1MiB $@.partial
|
||||||
|
mkfs.vfat "$@.partial"
|
||||||
|
mmd -i "$@.partial" efi
|
||||||
|
mmd -i "$@.partial" efi/boot
|
||||||
|
mcopy -i "$@.partial" "$<" ::efi/boot/bootx64.efi
|
||||||
|
mv "$@.partial" "$@"
|
||||||
|
|
||||||
|
$(BUILD)/harddrive.bin: $(BUILD)/esp.bin $(BUILD)/filesystem.bin
|
||||||
|
rm -f "$@.partial"
|
||||||
|
fallocate -l 320MiB "$@.partial"
|
||||||
|
$(PARTED) -s -a minimal "$@.partial" mklabel gpt
|
||||||
|
$(PARTED) -s -a minimal "$@.partial" mkpart ESP FAT32 1MiB 2MiB
|
||||||
|
$(PARTED) -s -a minimal "$@.partial" mkpart REDOXFS 2MiB 100%
|
||||||
|
$(PARTED) -s -a minimal "$@.partial" toggle 1 boot
|
||||||
|
dd if="$(BUILD)/esp.bin" of="$@.partial" bs=1MiB seek=1 conv=notrunc
|
||||||
|
dd if="$(BUILD)/filesystem.bin" of="$@.partial" bs=1MiB seek=2 conv=notrunc
|
||||||
|
mv "$@.partial" "$@"
|
||||||
|
|
||||||
|
$(BUILD)/firmware.rom: /usr/share/OVMF/OVMF_CODE.fd
|
||||||
|
cp "$<" "$@"
|
||||||
|
|
||||||
|
qemu: $(BUILD)/harddrive.bin $(BUILD)/firmware.rom
|
||||||
|
$(QEMU) \
|
||||||
|
-d cpu_reset \
|
||||||
|
-no-reboot \
|
||||||
|
-smp 4 -m 2048 \
|
||||||
|
-chardev stdio,id=debug,signal=off,mux=on \
|
||||||
|
-serial chardev:debug \
|
||||||
|
-mon chardev=debug \
|
||||||
|
-machine q35 \
|
||||||
|
-net none \
|
||||||
|
-enable-kvm \
|
||||||
|
-cpu host \
|
||||||
|
-bios "$(BUILD)/firmware.rom" \
|
||||||
|
-drive file="$<",format=raw
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
########## Red Bear OS #########
|
|
||||||
# Login with the following: #
|
|
||||||
# `user` #
|
|
||||||
# `root`:`password` #
|
|
||||||
################################
|
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
########## Redox OS ##########
|
|
||||||
# Login with the following: #
|
|
||||||
# `user` #
|
|
||||||
# `root`:`password` #
|
|
||||||
##############################
|
|
||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "nightly-2026-05-24"
|
||||||
|
components = ["rust-src"]
|
||||||
@@ -0,0 +1,154 @@
|
|||||||
|
use crate::area_add;
|
||||||
|
use crate::os::{dtb::is_in_dev_mem_region, Os, OsMemoryEntry, OsMemoryKind};
|
||||||
|
use core::slice;
|
||||||
|
|
||||||
|
pub(crate) const PF_PRESENT: u64 = 1 << 0;
|
||||||
|
pub(crate) const PF_TABLE: u64 = 1 << 1;
|
||||||
|
pub(crate) const PF_OUTER_SHAREABLE: u64 = 0b01 << 8;
|
||||||
|
pub(crate) const PF_INNER_SHAREABLE: u64 = 0b11 << 8;
|
||||||
|
pub(crate) const PF_ACCESS: u64 = 1 << 10;
|
||||||
|
|
||||||
|
pub(crate) const PF_DEV: u64 = PF_OUTER_SHAREABLE | 2 << 2;
|
||||||
|
pub(crate) const PF_RAM: u64 = PF_INNER_SHAREABLE;
|
||||||
|
|
||||||
|
pub(crate) const ENTRY_ADDRESS_MASK: u64 = 0x000F_FFFF_FFFF_F000;
|
||||||
|
pub(crate) const PAGE_ENTRIES: usize = 512;
|
||||||
|
const PAGE_SIZE: usize = 4096;
|
||||||
|
pub(crate) const PHYS_OFFSET: u64 = 0xFFFF_8000_0000_0000;
|
||||||
|
|
||||||
|
unsafe fn paging_allocate(os: &impl Os) -> Option<&'static mut [u64]> {
|
||||||
|
unsafe {
|
||||||
|
let ptr = os.alloc_zeroed_page_aligned(PAGE_SIZE);
|
||||||
|
if !ptr.is_null() {
|
||||||
|
area_add(OsMemoryEntry {
|
||||||
|
base: ptr as u64,
|
||||||
|
size: PAGE_SIZE as u64,
|
||||||
|
kind: OsMemoryKind::Reclaim,
|
||||||
|
});
|
||||||
|
Some(slice::from_raw_parts_mut(ptr as *mut u64, PAGE_ENTRIES))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) -> Option<usize> {
|
||||||
|
unsafe {
|
||||||
|
// Create L0
|
||||||
|
let l0 = paging_allocate(os)?;
|
||||||
|
|
||||||
|
{
|
||||||
|
// Create L1 for identity mapping
|
||||||
|
let l1 = paging_allocate(os)?;
|
||||||
|
|
||||||
|
// Link first user and first kernel L0 entry to L1
|
||||||
|
l0[0] = l1.as_ptr() as u64 | PF_ACCESS | PF_TABLE | PF_PRESENT;
|
||||||
|
l0[256] = l1.as_ptr() as u64 | PF_ACCESS | PF_TABLE | PF_PRESENT;
|
||||||
|
|
||||||
|
// Identity map 8 GiB using 1 GiB pages
|
||||||
|
for l1_i in 0..8 {
|
||||||
|
let addr = l1_i as u64 * 0x4000_0000;
|
||||||
|
//TODO: is PF_RAM okay?
|
||||||
|
l1[l1_i] = addr | PF_ACCESS | PF_DEV | PF_PRESENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Create L1 for kernel mapping
|
||||||
|
let l1 = paging_allocate(os)?;
|
||||||
|
|
||||||
|
// Link second to last L0 entry to L1
|
||||||
|
l0[510] = l1.as_ptr() as u64 | PF_ACCESS | PF_TABLE | PF_PRESENT;
|
||||||
|
|
||||||
|
// Map kernel_size at kernel offset
|
||||||
|
let mut kernel_mapped = 0;
|
||||||
|
let mut l1_i = 0;
|
||||||
|
while kernel_mapped < kernel_size && l1_i < l1.len() {
|
||||||
|
let l2 = paging_allocate(os)?;
|
||||||
|
l1[l1_i] = l2.as_ptr() as u64 | PF_ACCESS | PF_TABLE | PF_PRESENT;
|
||||||
|
l1_i += 1;
|
||||||
|
|
||||||
|
let mut l2_i = 0;
|
||||||
|
while kernel_mapped < kernel_size && l2_i < l2.len() {
|
||||||
|
let l3 = paging_allocate(os)?;
|
||||||
|
l2[l2_i] = l3.as_ptr() as u64 | PF_ACCESS | PF_TABLE | PF_PRESENT;
|
||||||
|
l2_i += 1;
|
||||||
|
|
||||||
|
let mut l3_i = 0;
|
||||||
|
while kernel_mapped < kernel_size && l3_i < l3.len() {
|
||||||
|
let addr = kernel_phys + kernel_mapped;
|
||||||
|
l3[l3_i] = addr | PF_ACCESS | PF_RAM | PF_TABLE | PF_PRESENT;
|
||||||
|
l3_i += 1;
|
||||||
|
kernel_mapped += PAGE_SIZE as u64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(kernel_mapped >= kernel_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(l0.as_ptr() as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn paging_framebuffer(
|
||||||
|
os: &impl Os,
|
||||||
|
page_phys: usize,
|
||||||
|
framebuffer_phys: u64,
|
||||||
|
framebuffer_size: u64,
|
||||||
|
) -> Option<u64> {
|
||||||
|
unsafe {
|
||||||
|
//TODO: smarter test for framebuffer already mapped
|
||||||
|
if framebuffer_phys + framebuffer_size <= 0x2_0000_0000 {
|
||||||
|
return Some(framebuffer_phys + PHYS_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
let l0_i = ((framebuffer_phys / 0x80_0000_0000) + 256) as usize;
|
||||||
|
let mut l1_i = ((framebuffer_phys % 0x80_0000_0000) / 0x4000_0000) as usize;
|
||||||
|
let mut l2_i = ((framebuffer_phys % 0x4000_0000) / 0x20_0000) as usize;
|
||||||
|
let mut l3_i = ((framebuffer_phys % 0x20_0000) / (PAGE_SIZE as u64)) as usize;
|
||||||
|
assert_eq!(framebuffer_phys % (PAGE_SIZE as u64), 0);
|
||||||
|
|
||||||
|
let l0 = slice::from_raw_parts_mut(page_phys as *mut u64, PAGE_ENTRIES);
|
||||||
|
|
||||||
|
// Create l1 for framebuffer mapping
|
||||||
|
let l1 = if l0[l0_i] == 0 {
|
||||||
|
let l1 = paging_allocate(os)?;
|
||||||
|
l0[l0_i] = l1.as_ptr() as u64 | PF_ACCESS | PF_TABLE | PF_PRESENT;
|
||||||
|
l1
|
||||||
|
} else {
|
||||||
|
slice::from_raw_parts_mut((l0[l0_i] & ENTRY_ADDRESS_MASK) as *mut u64, PAGE_ENTRIES)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Map framebuffer_size at framebuffer offset
|
||||||
|
let mut framebuffer_mapped = 0;
|
||||||
|
while framebuffer_mapped < framebuffer_size && l1_i < l1.len() {
|
||||||
|
let l2 = paging_allocate(os)?;
|
||||||
|
assert_eq!(l1[l1_i], 0);
|
||||||
|
l1[l1_i] = l2.as_ptr() as u64 | PF_ACCESS | PF_TABLE | PF_PRESENT;
|
||||||
|
|
||||||
|
while framebuffer_mapped < framebuffer_size && l2_i < l2.len() {
|
||||||
|
let l3 = paging_allocate(os)?;
|
||||||
|
assert_eq!(l2[l2_i], 0);
|
||||||
|
l2[l2_i] = l3.as_ptr() as u64 | PF_ACCESS | PF_TABLE | PF_PRESENT;
|
||||||
|
|
||||||
|
while framebuffer_mapped < framebuffer_size && l3_i < l3.len() {
|
||||||
|
let addr = framebuffer_phys + framebuffer_mapped;
|
||||||
|
assert_eq!(l3[l3_i], 0);
|
||||||
|
//TODO: is PF_RAM okay?
|
||||||
|
l3[l3_i] = addr | PF_ACCESS | PF_RAM | PF_TABLE | PF_PRESENT;
|
||||||
|
framebuffer_mapped += PAGE_SIZE as u64;
|
||||||
|
l3_i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
l2_i += 1;
|
||||||
|
l3_i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
l1_i += 1;
|
||||||
|
l2_i = 0;
|
||||||
|
}
|
||||||
|
assert!(framebuffer_mapped >= framebuffer_size);
|
||||||
|
|
||||||
|
Some(framebuffer_phys + PHYS_OFFSET)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
pub use self::aarch64::*;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
mod aarch64;
|
||||||
|
|
||||||
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||||
|
pub use self::x86::*;
|
||||||
|
|
||||||
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||||
|
mod x86;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "riscv64")]
|
||||||
|
pub use self::riscv64::*;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "riscv64")]
|
||||||
|
mod riscv64;
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
use core::slice;
|
||||||
|
|
||||||
|
use crate::area_add;
|
||||||
|
use crate::os::{Os, OsMemoryEntry, OsMemoryKind};
|
||||||
|
|
||||||
|
pub(crate) mod sv39;
|
||||||
|
pub(crate) mod sv48;
|
||||||
|
pub(crate) mod sv57;
|
||||||
|
|
||||||
|
// Common constants
|
||||||
|
const PAGE_SHIFT: usize = 12;
|
||||||
|
const TABLE_SHIFT: usize = 9;
|
||||||
|
const TABLE_MASK: usize = (1 << TABLE_SHIFT) - 1;
|
||||||
|
const PAGE_ENTRIES: usize = 512;
|
||||||
|
const PAGE_SIZE: usize = 4096;
|
||||||
|
const PHYS_MASK: usize = (1usize << 44) - 1;
|
||||||
|
|
||||||
|
const VALID: u64 = 1;
|
||||||
|
const RWX: u64 = 7 << 1;
|
||||||
|
const ACCESSED: u64 = 1 << 6;
|
||||||
|
const DIRTY: u64 = 1 << 7;
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
pub(crate) use sv39::PHYS_OFFSET;
|
||||||
|
pub(crate) use sv39::SATP_BITS;
|
||||||
|
pub(crate) use sv39::paging_create;
|
||||||
|
pub(crate) use sv39::paging_physmem as paging_framebuffer;
|
||||||
|
|
||||||
|
unsafe fn paging_allocate(os: &impl Os) -> Option<&'static mut [u64]> {
|
||||||
|
unsafe {
|
||||||
|
let ptr = os.alloc_zeroed_page_aligned(PAGE_SIZE);
|
||||||
|
if !ptr.is_null() {
|
||||||
|
area_add(OsMemoryEntry {
|
||||||
|
base: ptr as u64,
|
||||||
|
size: PAGE_SIZE as u64,
|
||||||
|
kind: OsMemoryKind::Reclaim,
|
||||||
|
});
|
||||||
|
Some(slice::from_raw_parts_mut(ptr as *mut u64, PAGE_ENTRIES))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn get_table(os: &impl Os, parent: &mut [u64], index: usize) -> Option<&'static mut [u64]> {
|
||||||
|
unsafe {
|
||||||
|
if parent[index] == 0 {
|
||||||
|
let table = paging_allocate(os)?;
|
||||||
|
parent[index] = table.as_ptr() as u64 >> 2 | VALID;
|
||||||
|
Some(table)
|
||||||
|
} else {
|
||||||
|
Some(slice::from_raw_parts_mut(
|
||||||
|
(((parent[index] >> 10) & PHYS_MASK as u64) << 12) as *mut u64,
|
||||||
|
PAGE_ENTRIES,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
use core::slice;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::os::Os;
|
||||||
|
|
||||||
|
// Sv39 scheme
|
||||||
|
|
||||||
|
pub(crate) const PHYS_OFFSET: u64 = 0xFFFF_FFC0_0000_0000;
|
||||||
|
pub(crate) const SATP_BITS: usize = 8;
|
||||||
|
|
||||||
|
pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) -> Option<usize> {
|
||||||
|
unsafe {
|
||||||
|
// Create L2
|
||||||
|
let l2 = paging_allocate(os)?;
|
||||||
|
|
||||||
|
{
|
||||||
|
// Create L1 for identity mapping
|
||||||
|
for l2_i in 0..8 {
|
||||||
|
let addr = l2_i as u64 * 0x4000_0000;
|
||||||
|
// Identity map 8 GiB using 1GB pages
|
||||||
|
l2[l2_i] = addr >> 2 | RWX | VALID | ACCESSED | DIRTY;
|
||||||
|
// map phys into kernel VAS
|
||||||
|
l2[(PAGE_ENTRIES / 2) + l2_i] = addr >> 2 | RWX | VALID | ACCESSED | DIRTY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Create L1 for kernel mapping
|
||||||
|
let l1 = paging_allocate(os)?;
|
||||||
|
|
||||||
|
// Link second to last L0 entry to L1
|
||||||
|
l2[510] = l1.as_ptr() as u64 >> 2 | VALID;
|
||||||
|
|
||||||
|
// Map kernel_size at kernel offset
|
||||||
|
let mut kernel_mapped = 0;
|
||||||
|
let mut l1_i = 0;
|
||||||
|
while kernel_mapped < kernel_size && l1_i < l1.len() {
|
||||||
|
let l0 = paging_allocate(os)?;
|
||||||
|
l1[l1_i] = l0.as_ptr() as u64 >> 2 | VALID;
|
||||||
|
l1_i += 1;
|
||||||
|
|
||||||
|
let mut l0_i = 0;
|
||||||
|
while kernel_mapped < kernel_size && l0_i < l2.len() {
|
||||||
|
let addr = kernel_phys + kernel_mapped;
|
||||||
|
l0[l0_i] = addr >> 2 | RWX | VALID | ACCESSED | DIRTY;
|
||||||
|
l0_i += 1;
|
||||||
|
kernel_mapped += PAGE_SIZE as u64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(kernel_mapped >= kernel_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(l2.as_ptr() as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn paging_physmem(os: &impl Os, page_phys: usize, phys: u64, size: u64) -> Option<u64> {
|
||||||
|
unsafe {
|
||||||
|
if phys + size <= 0x2_0000_0000 {
|
||||||
|
return Some(phys + PHYS_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut l1_i = (phys as usize >> (PAGE_SHIFT + 2 * TABLE_SHIFT)) + PAGE_ENTRIES / 2;
|
||||||
|
let mut l0_i = (phys as usize >> (PAGE_SHIFT + TABLE_SHIFT)) & TABLE_MASK;
|
||||||
|
assert_eq!(phys & ((1 << (PAGE_SHIFT + TABLE_SHIFT)) - 1), 0);
|
||||||
|
|
||||||
|
let l1 = slice::from_raw_parts_mut(page_phys as *mut u64, PAGE_ENTRIES);
|
||||||
|
|
||||||
|
// Map framebuffer_size at framebuffer offset
|
||||||
|
let mut mapped = 0;
|
||||||
|
while mapped < size && l1_i < l1.len() {
|
||||||
|
let l0 = get_table(os, l1, l1_i)?;
|
||||||
|
|
||||||
|
while mapped < size && l0_i < l0.len() {
|
||||||
|
let addr = phys + mapped;
|
||||||
|
assert_eq!(l0[l0_i], 0);
|
||||||
|
l0[l0_i] = (addr >> 2) | RWX | VALID | ACCESSED | DIRTY;
|
||||||
|
mapped += 1 << (PAGE_SHIFT + TABLE_SHIFT); // Map with 2mb mega-pages
|
||||||
|
l0_i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
l1_i += 1;
|
||||||
|
l0_i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(mapped >= size);
|
||||||
|
|
||||||
|
Some(phys + PHYS_OFFSET)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
use core::slice;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::os::Os;
|
||||||
|
|
||||||
|
// Sv48 scheme
|
||||||
|
|
||||||
|
pub(crate) const PHYS_OFFSET: u64 = 0xFFFF_8000_0000_0000;
|
||||||
|
pub(crate) const SATP_BITS: usize = 9;
|
||||||
|
|
||||||
|
pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) -> Option<usize> {
|
||||||
|
unsafe {
|
||||||
|
// Create L3
|
||||||
|
let l3 = paging_allocate(os)?;
|
||||||
|
|
||||||
|
{
|
||||||
|
// Create L2 for identity mapping
|
||||||
|
let l2 = paging_allocate(os)?;
|
||||||
|
|
||||||
|
// Map L2 into beginning of userspace and kernelspace
|
||||||
|
l3[0] = (l2.as_ptr() as u64 >> 2) | VALID;
|
||||||
|
l3[PAGE_ENTRIES / 2] = (l2.as_ptr() as u64 >> 2) | VALID;
|
||||||
|
|
||||||
|
// Identity map 8 GiB using 1GB pages
|
||||||
|
for l2_i in 0..8 {
|
||||||
|
let addr = l2_i as u64 * 0x4000_0000;
|
||||||
|
l2[l2_i] = addr >> 2 | RWX | VALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Create L2 for kernel mapping
|
||||||
|
let l2 = paging_allocate(os)?;
|
||||||
|
|
||||||
|
// Link last L3 entry to L2
|
||||||
|
l3[511] = l2.as_ptr() as u64 >> 2 | VALID;
|
||||||
|
|
||||||
|
// Create L1 for kernel mapping
|
||||||
|
let l1 = paging_allocate(os)?;
|
||||||
|
|
||||||
|
// Link last L1 entry to L2
|
||||||
|
l2[510] = l1.as_ptr() as u64 >> 2 | VALID;
|
||||||
|
|
||||||
|
// Map kernel_size at kernel offset
|
||||||
|
let mut kernel_mapped = 0;
|
||||||
|
let mut l1_i = 0;
|
||||||
|
while kernel_mapped < kernel_size && l1_i < l1.len() {
|
||||||
|
let l0 = paging_allocate(os)?;
|
||||||
|
l1[l1_i] = l0.as_ptr() as u64 >> 2 | VALID;
|
||||||
|
l1_i += 1;
|
||||||
|
|
||||||
|
let mut l0_i = 0;
|
||||||
|
while kernel_mapped < kernel_size && l0_i < l2.len() {
|
||||||
|
let addr = kernel_phys + kernel_mapped;
|
||||||
|
l0[l0_i] = addr >> 2 | RWX | VALID | ACCESSED | DIRTY;
|
||||||
|
l0_i += 1;
|
||||||
|
kernel_mapped += PAGE_SIZE as u64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(kernel_mapped >= kernel_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(l3.as_ptr() as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn paging_physmem(os: &impl Os, page_phys: usize, phys: u64, size: u64) -> Option<u64> {
|
||||||
|
unsafe {
|
||||||
|
if phys + size <= 0x2_0000_0000 {
|
||||||
|
return Some(phys + PHYS_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut l2_i = (phys as usize >> (PAGE_SHIFT + 3 * TABLE_SHIFT)) + PAGE_ENTRIES / 2;
|
||||||
|
let mut l1_i = (phys as usize >> (PAGE_SHIFT + 2 * TABLE_SHIFT)) & TABLE_MASK;
|
||||||
|
let mut l0_i = (phys as usize >> (PAGE_SHIFT + TABLE_SHIFT)) & TABLE_MASK;
|
||||||
|
assert_eq!(phys & ((1 << (PAGE_SHIFT + TABLE_SHIFT)) - 1), 0);
|
||||||
|
|
||||||
|
let l2 = slice::from_raw_parts_mut(page_phys as *mut u64, PAGE_ENTRIES);
|
||||||
|
|
||||||
|
// Map framebuffer_size at framebuffer offset
|
||||||
|
let mut mapped = 0;
|
||||||
|
while mapped < size && l2_i < l2.len() {
|
||||||
|
let l1 = get_table(os, l2, l2_i)?;
|
||||||
|
|
||||||
|
while mapped < size && l1_i < l1.len() {
|
||||||
|
let l0 = get_table(os, l1, l1_i)?;
|
||||||
|
|
||||||
|
while mapped < size && l0_i < l0.len() {
|
||||||
|
let addr = phys + mapped;
|
||||||
|
assert_eq!(l0[l0_i], 0);
|
||||||
|
l0[l0_i] = (addr >> 2) | RWX | VALID;
|
||||||
|
mapped += 1 << (PAGE_SHIFT + TABLE_SHIFT);
|
||||||
|
l0_i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
l1_i += 1;
|
||||||
|
l0_i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
l2_i += 1;
|
||||||
|
l1_i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(mapped >= size);
|
||||||
|
|
||||||
|
Some(phys + PHYS_OFFSET)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
use core::slice;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
use crate::os::Os;
|
||||||
|
|
||||||
|
// Sv57 scheme
|
||||||
|
|
||||||
|
pub(crate) const PHYS_OFFSET: u64 = 0xFF00_0000_0000_0000;
|
||||||
|
pub(crate) const SATP_BIT: usize = 10;
|
||||||
|
|
||||||
|
pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) -> Option<usize> {
|
||||||
|
unsafe {
|
||||||
|
// Create L4
|
||||||
|
let l4 = paging_allocate(os)?;
|
||||||
|
|
||||||
|
{
|
||||||
|
// Create L3
|
||||||
|
let l3 = paging_allocate(os)?;
|
||||||
|
|
||||||
|
// Map L3 into beginning of userspace and kernelspace
|
||||||
|
l4[0] = (l3.as_ptr() as u64 >> 2) | VALID;
|
||||||
|
l4[PAGE_ENTRIES / 2] = (l3.as_ptr() as u64 >> 2) | VALID;
|
||||||
|
|
||||||
|
// Create L2 for identity mapping
|
||||||
|
let l2 = paging_allocate(os)?;
|
||||||
|
|
||||||
|
// Identity map 8 GiB using 1GB pages
|
||||||
|
for l2_i in 0..8 {
|
||||||
|
let addr = l2_i as u64 * 0x4000_0000;
|
||||||
|
l2[l2_i] = addr >> 2 | RWX | VALID;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Create L3
|
||||||
|
let l3 = paging_allocate(os)?;
|
||||||
|
|
||||||
|
// Link last L4 entry to L3
|
||||||
|
l4[511] = l3.as_ptr() as u64 >> 2 | VALID;
|
||||||
|
|
||||||
|
// Create L2 for kernel mapping
|
||||||
|
let l2 = paging_allocate(os)?;
|
||||||
|
|
||||||
|
// Link last L3 entry to L2
|
||||||
|
l3[511] = l2.as_ptr() as u64 >> 2 | VALID;
|
||||||
|
|
||||||
|
// Create L1 for kernel mapping
|
||||||
|
let l1 = paging_allocate(os)?;
|
||||||
|
|
||||||
|
// Link last L1 entry to L2
|
||||||
|
l2[510] = l1.as_ptr() as u64 >> 2 | VALID;
|
||||||
|
|
||||||
|
// Map kernel_size at kernel offset
|
||||||
|
let mut kernel_mapped = 0;
|
||||||
|
let mut l1_i = 0;
|
||||||
|
while kernel_mapped < kernel_size && l1_i < l1.len() {
|
||||||
|
let l0 = paging_allocate(os)?;
|
||||||
|
l1[l1_i] = l0.as_ptr() as u64 >> 2 | VALID;
|
||||||
|
l1_i += 1;
|
||||||
|
|
||||||
|
let mut l0_i = 0;
|
||||||
|
while kernel_mapped < kernel_size && l0_i < l2.len() {
|
||||||
|
let addr = kernel_phys + kernel_mapped;
|
||||||
|
l0[l0_i] = addr >> 2 | RWX | VALID | ACCESSED | DIRTY;
|
||||||
|
l0_i += 1;
|
||||||
|
kernel_mapped += PAGE_SIZE as u64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(kernel_mapped >= kernel_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(l4.as_ptr() as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn paging_physmem(os: &impl Os, page_phys: usize, phys: u64, size: u64) -> Option<u64> {
|
||||||
|
unsafe {
|
||||||
|
if phys + size <= 0x2_0000_0000 {
|
||||||
|
return Some(phys + PHYS_OFFSET);
|
||||||
|
}
|
||||||
|
let mut l3_i = (phys as usize >> (PAGE_SHIFT + 4 * TABLE_SHIFT)) + PAGE_ENTRIES / 2;
|
||||||
|
let mut l2_i = (phys as usize >> (PAGE_SHIFT + 3 * TABLE_SHIFT)) & TABLE_MASK;
|
||||||
|
let mut l1_i = (phys as usize >> (PAGE_SHIFT + 2 * TABLE_SHIFT)) & TABLE_MASK;
|
||||||
|
let mut l0_i = (phys as usize >> (PAGE_SHIFT + TABLE_SHIFT)) & TABLE_MASK;
|
||||||
|
assert_eq!(phys & ((1 << (PAGE_SHIFT + TABLE_SHIFT)) - 1), 0);
|
||||||
|
|
||||||
|
let l3 = slice::from_raw_parts_mut(page_phys as *mut u64, PAGE_ENTRIES);
|
||||||
|
|
||||||
|
// Map framebuffer_size at framebuffer offset
|
||||||
|
let mut mapped = 0;
|
||||||
|
|
||||||
|
while mapped < size && l3_i < l3.len() {
|
||||||
|
let l2 = get_table(os, l3, l3_i)?;
|
||||||
|
|
||||||
|
while mapped < size && l2_i < l2.len() {
|
||||||
|
let l1 = get_table(os, l2, l2_i)?;
|
||||||
|
|
||||||
|
while mapped < size && l1_i < l1.len() {
|
||||||
|
let l0 = get_table(os, l1, l1_i)?;
|
||||||
|
|
||||||
|
while mapped < size && l0_i < l0.len() {
|
||||||
|
let addr = phys + mapped;
|
||||||
|
assert_eq!(l0[l0_i], 0);
|
||||||
|
l0[l0_i] = (addr >> 2) | RWX | VALID;
|
||||||
|
mapped += 1 << (PAGE_SHIFT + TABLE_SHIFT);
|
||||||
|
l0_i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
l1_i += 1;
|
||||||
|
l0_i = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
l2_i += 1;
|
||||||
|
l1_i = 0;
|
||||||
|
}
|
||||||
|
l3_i += 1;
|
||||||
|
l2_i += 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert!(mapped >= size);
|
||||||
|
|
||||||
|
Some(phys + PHYS_OFFSET)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
use crate::os::Os;
|
||||||
|
|
||||||
|
pub(crate) mod x32;
|
||||||
|
pub(crate) mod x64;
|
||||||
|
|
||||||
|
pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) -> Option<usize> {
|
||||||
|
unsafe {
|
||||||
|
if crate::KERNEL_64BIT {
|
||||||
|
x64::paging_create(os, kernel_phys, kernel_size)
|
||||||
|
} else {
|
||||||
|
x32::paging_create(os, kernel_phys, kernel_size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn paging_framebuffer(
|
||||||
|
os: &impl Os,
|
||||||
|
page_phys: usize,
|
||||||
|
framebuffer_phys: u64,
|
||||||
|
framebuffer_size: u64,
|
||||||
|
) -> Option<u64> {
|
||||||
|
unsafe {
|
||||||
|
if crate::KERNEL_64BIT {
|
||||||
|
x64::paging_framebuffer(os, page_phys, framebuffer_phys, framebuffer_size)
|
||||||
|
} else {
|
||||||
|
x32::paging_framebuffer(os, page_phys, framebuffer_phys, framebuffer_size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,88 @@
|
|||||||
|
use crate::area_add;
|
||||||
|
use crate::os::{Os, OsMemoryEntry, OsMemoryKind};
|
||||||
|
use core::slice;
|
||||||
|
|
||||||
|
const PAGE_ENTRIES: usize = 1024;
|
||||||
|
const PAGE_SIZE: usize = 4096;
|
||||||
|
pub(crate) const PHYS_OFFSET: u32 = 0x8000_0000;
|
||||||
|
|
||||||
|
unsafe fn paging_allocate(os: &impl Os) -> Option<&'static mut [u32]> {
|
||||||
|
unsafe {
|
||||||
|
let ptr = os.alloc_zeroed_page_aligned(PAGE_SIZE);
|
||||||
|
if !ptr.is_null() {
|
||||||
|
area_add(OsMemoryEntry {
|
||||||
|
base: ptr as u64,
|
||||||
|
size: PAGE_SIZE as u64,
|
||||||
|
kind: OsMemoryKind::Reclaim,
|
||||||
|
});
|
||||||
|
Some(slice::from_raw_parts_mut(ptr as *mut u32, PAGE_ENTRIES))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) -> Option<usize> {
|
||||||
|
unsafe {
|
||||||
|
let pd = paging_allocate(os)?;
|
||||||
|
//Identity map 1 GiB using 4 MiB pages, also map at PHYS_OFFSET
|
||||||
|
for pd_i in 0..256 {
|
||||||
|
let addr = pd_i as u32 * 0x40_0000;
|
||||||
|
pd[pd_i] = addr | 1 << 7 | 1 << 1 | 1;
|
||||||
|
pd[pd_i + 512] = addr | 1 << 7 | 1 << 1 | 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map kernel_size at kernel offset
|
||||||
|
let mut kernel_mapped = 0;
|
||||||
|
let mut pd_i = 0xC000_0000 / 0x40_0000;
|
||||||
|
while kernel_mapped < kernel_size && pd_i < pd.len() {
|
||||||
|
let pt = paging_allocate(os)?;
|
||||||
|
pd[pd_i] = pt.as_ptr() as u32 | 1 << 1 | 1;
|
||||||
|
pd_i += 1;
|
||||||
|
|
||||||
|
let mut pt_i = 0;
|
||||||
|
while kernel_mapped < kernel_size && pt_i < pt.len() {
|
||||||
|
let addr = kernel_phys + kernel_mapped;
|
||||||
|
pt[pt_i] = addr as u32 | 1 << 1 | 1;
|
||||||
|
pt_i += 1;
|
||||||
|
kernel_mapped += PAGE_SIZE as u64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(kernel_mapped >= kernel_size);
|
||||||
|
|
||||||
|
Some(pd.as_ptr() as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn paging_framebuffer(
|
||||||
|
os: &impl Os,
|
||||||
|
page_phys: usize,
|
||||||
|
framebuffer_phys: u64,
|
||||||
|
framebuffer_size: u64,
|
||||||
|
) -> Option<u64> {
|
||||||
|
unsafe {
|
||||||
|
let framebuffer_virt = 0xD000_0000; // 256 MiB after kernel mapping, but before heap mapping
|
||||||
|
|
||||||
|
let pd = slice::from_raw_parts_mut(page_phys as *mut u32, PAGE_ENTRIES);
|
||||||
|
|
||||||
|
// Map framebuffer_size at framebuffer offset
|
||||||
|
let mut framebuffer_mapped = 0;
|
||||||
|
let mut pd_i = framebuffer_virt / 0x40_0000;
|
||||||
|
while framebuffer_mapped < framebuffer_size && pd_i < pd.len() {
|
||||||
|
let pt = paging_allocate(os)?;
|
||||||
|
pd[pd_i] = pt.as_ptr() as u32 | 1 << 1 | 1;
|
||||||
|
pd_i += 1;
|
||||||
|
|
||||||
|
let mut pt_i = 0;
|
||||||
|
while framebuffer_mapped < framebuffer_size && pt_i < pt.len() {
|
||||||
|
let addr = framebuffer_phys + framebuffer_mapped;
|
||||||
|
pt[pt_i] = addr as u32 | 1 << 1 | 1;
|
||||||
|
pt_i += 1;
|
||||||
|
framebuffer_mapped += PAGE_SIZE as u64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(framebuffer_mapped >= framebuffer_size);
|
||||||
|
|
||||||
|
Some(framebuffer_virt as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
use core::slice;
|
||||||
|
|
||||||
|
use crate::area_add;
|
||||||
|
use crate::os::{Os, OsMemoryEntry, OsMemoryKind};
|
||||||
|
|
||||||
|
const ENTRY_ADDRESS_MASK: u64 = 0x000F_FFFF_FFFF_F000;
|
||||||
|
const PAGE_ENTRIES: usize = 512;
|
||||||
|
const PAGE_SIZE: usize = 4096;
|
||||||
|
pub(crate) const PHYS_OFFSET: u64 = 0xFFFF_8000_0000_0000;
|
||||||
|
|
||||||
|
unsafe fn paging_allocate(os: &impl Os) -> Option<&'static mut [u64]> {
|
||||||
|
unsafe {
|
||||||
|
let ptr = os.alloc_zeroed_page_aligned(PAGE_SIZE);
|
||||||
|
if !ptr.is_null() {
|
||||||
|
area_add(OsMemoryEntry {
|
||||||
|
base: ptr as u64,
|
||||||
|
size: PAGE_SIZE as u64,
|
||||||
|
kind: OsMemoryKind::Reclaim,
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(slice::from_raw_parts_mut(ptr as *mut u64, PAGE_ENTRIES))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const PRESENT: u64 = 1;
|
||||||
|
const WRITABLE: u64 = 1 << 1;
|
||||||
|
const LARGE: u64 = 1 << 7;
|
||||||
|
|
||||||
|
pub unsafe fn paging_create(os: &impl Os, kernel_phys: u64, kernel_size: u64) -> Option<usize> {
|
||||||
|
unsafe {
|
||||||
|
// Create PML4
|
||||||
|
let pml4 = paging_allocate(os)?;
|
||||||
|
|
||||||
|
{
|
||||||
|
// Create PDP for identity mapping
|
||||||
|
let pdp = paging_allocate(os)?;
|
||||||
|
|
||||||
|
// Link first user and first kernel PML4 entry to PDP
|
||||||
|
pml4[0] = pdp.as_ptr() as u64 | WRITABLE | PRESENT;
|
||||||
|
pml4[256] = pdp.as_ptr() as u64 | WRITABLE | PRESENT;
|
||||||
|
|
||||||
|
// Identity map 8 GiB using 2 MiB pages
|
||||||
|
for pdp_i in 0..8 {
|
||||||
|
let pd = paging_allocate(os)?;
|
||||||
|
pdp[pdp_i] = pd.as_ptr() as u64 | WRITABLE | PRESENT;
|
||||||
|
for pd_i in 0..pd.len() {
|
||||||
|
let addr = pdp_i as u64 * 0x4000_0000 + pd_i as u64 * 0x20_0000;
|
||||||
|
pd[pd_i] = addr | LARGE | WRITABLE | PRESENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Create PDP (spanning 512 GiB) for kernel mapping
|
||||||
|
let pdp = paging_allocate(os)?;
|
||||||
|
|
||||||
|
// Link last PML4 entry to PDP
|
||||||
|
pml4[511] = pdp.as_ptr() as u64 | WRITABLE | PRESENT;
|
||||||
|
|
||||||
|
// Create PD (spanning 1 GiB) for kernel mapping.
|
||||||
|
let pd = paging_allocate(os)?;
|
||||||
|
|
||||||
|
// The kernel is mapped at -2^31, i.e. 0xFFFF_FFFF_8000_0000. Since a PD is 1 GiB, link
|
||||||
|
// the second last PDP entry to PD.
|
||||||
|
pdp[510] = pd.as_ptr() as u64 | WRITABLE | PRESENT;
|
||||||
|
|
||||||
|
// Map kernel_size bytes to kernel offset, i.e. to the start of the PD.
|
||||||
|
|
||||||
|
let mut kernel_mapped = 0;
|
||||||
|
|
||||||
|
let mut pd_idx = 0;
|
||||||
|
while kernel_mapped < kernel_size && pd_idx < pd.len() {
|
||||||
|
let pt = paging_allocate(os)?;
|
||||||
|
pd[pd_idx] = pt.as_ptr() as u64 | WRITABLE | PRESENT;
|
||||||
|
pd_idx += 1;
|
||||||
|
|
||||||
|
let mut pt_idx = 0;
|
||||||
|
while kernel_mapped < kernel_size && pt_idx < pt.len() {
|
||||||
|
let addr = kernel_phys + kernel_mapped;
|
||||||
|
pt[pt_idx] = addr | WRITABLE | PRESENT;
|
||||||
|
pt_idx += 1;
|
||||||
|
kernel_mapped += PAGE_SIZE as u64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert!(kernel_mapped >= kernel_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(pml4.as_ptr() as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn paging_framebuffer(
|
||||||
|
os: &impl Os,
|
||||||
|
page_phys: usize,
|
||||||
|
framebuffer_phys: u64,
|
||||||
|
framebuffer_size: u64,
|
||||||
|
) -> Option<u64> {
|
||||||
|
unsafe {
|
||||||
|
//TODO: smarter test for framebuffer already mapped
|
||||||
|
if framebuffer_phys + framebuffer_size <= 0x2_0000_0000 {
|
||||||
|
return Some(framebuffer_phys + PHYS_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
|
let pml4_i = ((framebuffer_phys / 0x80_0000_0000) + 256) as usize;
|
||||||
|
let mut pdp_i = ((framebuffer_phys % 0x80_0000_0000) / 0x4000_0000) as usize;
|
||||||
|
let mut pd_i = ((framebuffer_phys % 0x4000_0000) / 0x20_0000) as usize;
|
||||||
|
assert_eq!(framebuffer_phys % 0x20_0000, 0);
|
||||||
|
|
||||||
|
let pml4 = slice::from_raw_parts_mut(page_phys as *mut u64, PAGE_ENTRIES);
|
||||||
|
|
||||||
|
// Create PDP for framebuffer mapping
|
||||||
|
let pdp = if pml4[pml4_i] == 0 {
|
||||||
|
let pdp = paging_allocate(os)?;
|
||||||
|
pml4[pml4_i] = pdp.as_ptr() as u64 | 1 << 1 | 1;
|
||||||
|
pdp
|
||||||
|
} else {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
(pml4[pml4_i] & ENTRY_ADDRESS_MASK) as *mut u64,
|
||||||
|
PAGE_ENTRIES,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Map framebuffer_size at framebuffer offset
|
||||||
|
let mut framebuffer_mapped = 0;
|
||||||
|
while framebuffer_mapped < framebuffer_size && pdp_i < pdp.len() {
|
||||||
|
let pd = paging_allocate(os)?;
|
||||||
|
assert_eq!(pdp[pdp_i], 0);
|
||||||
|
pdp[pdp_i] = pd.as_ptr() as u64 | 1 << 1 | 1;
|
||||||
|
|
||||||
|
while framebuffer_mapped < framebuffer_size && pd_i < pd.len() {
|
||||||
|
let addr = framebuffer_phys + framebuffer_mapped;
|
||||||
|
assert_eq!(pd[pd_i], 0);
|
||||||
|
pd[pd_i] = addr | 1 << 7 | 1 << 1 | 1;
|
||||||
|
framebuffer_mapped += 0x20_0000;
|
||||||
|
pd_i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pdp_i += 1;
|
||||||
|
pd_i = 0;
|
||||||
|
}
|
||||||
|
assert!(framebuffer_mapped >= framebuffer_size);
|
||||||
|
|
||||||
|
Some(framebuffer_phys + PHYS_OFFSET)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,302 +0,0 @@
|
|||||||
#[macro_use]
|
|
||||||
extern crate clap;
|
|
||||||
|
|
||||||
use std::error::Error;
|
|
||||||
use std::fs::{File, OpenOptions};
|
|
||||||
use std::io::{self, ErrorKind, Read, Stderr, Write};
|
|
||||||
|
|
||||||
fn dlog(msg: &str) {
|
|
||||||
let _ = OpenOptions::new()
|
|
||||||
.write(true)
|
|
||||||
.open("/scheme/debug/no-preserve")
|
|
||||||
.and_then(|mut f| writeln!(f, "GETTY {}", msg));
|
|
||||||
}
|
|
||||||
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
|
||||||
use std::process::{Child, Command, Stdio};
|
|
||||||
use std::str;
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
use event::{EventFlags, RawEventQueue};
|
|
||||||
use extra::io::fail;
|
|
||||||
use libredox::call as redox;
|
|
||||||
use libredox::errno::EAGAIN;
|
|
||||||
use libredox::flag;
|
|
||||||
|
|
||||||
const _MAN_PAGE: &'static str = /* @MANSTART{getty} */
|
|
||||||
r#"
|
|
||||||
NAME
|
|
||||||
getty - set terminal mode
|
|
||||||
|
|
||||||
SYNOPSIS
|
|
||||||
getty [-J | --noclear | -C | --contain ] tty
|
|
||||||
getty [ -h | --help ]
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
The getty utility is called by init(8) to open and initialize the tty line,
|
|
||||||
read a login name, and invoke login(1).
|
|
||||||
|
|
||||||
OPTIONS
|
|
||||||
|
|
||||||
-h, --help
|
|
||||||
Display this help and exit.
|
|
||||||
|
|
||||||
-J, --noclear
|
|
||||||
Do not clear the screen before forking login(1).
|
|
||||||
|
|
||||||
-C, --contain
|
|
||||||
Run contain_login instead of login
|
|
||||||
|
|
||||||
AUTHOR
|
|
||||||
Written by Jeremy Soller.
|
|
||||||
"#; /* @MANEND */
|
|
||||||
|
|
||||||
const DEFAULT_COLS: u16 = 80;
|
|
||||||
const DEFAULT_LINES: u16 = 30;
|
|
||||||
|
|
||||||
pub fn handle(
|
|
||||||
event_queue: &mut RawEventQueue,
|
|
||||||
tty_fd: RawFd,
|
|
||||||
master_fd: RawFd,
|
|
||||||
process: &mut Child,
|
|
||||||
) {
|
|
||||||
// tty_fd => Display
|
|
||||||
// master_fd => PTY
|
|
||||||
|
|
||||||
let handle_event = |event_id: usize| {
|
|
||||||
if event_id as RawFd == tty_fd {
|
|
||||||
let mut packet = [0; 4096];
|
|
||||||
loop {
|
|
||||||
let count = match redox::read(tty_fd as usize, &mut packet) {
|
|
||||||
Ok(0) => return,
|
|
||||||
Ok(count) => count,
|
|
||||||
Err(ref err) if err.errno() == EAGAIN => break,
|
|
||||||
Err(_) => panic!("getty: failed to read from TTY"),
|
|
||||||
};
|
|
||||||
redox::write(master_fd as usize, &packet[..count])
|
|
||||||
.expect("getty: failed to write master PTY");
|
|
||||||
}
|
|
||||||
} else if event_id as RawFd == master_fd {
|
|
||||||
let mut packet = [0; 4096];
|
|
||||||
loop {
|
|
||||||
let count = match redox::read(master_fd as usize, &mut packet) {
|
|
||||||
Ok(0) => return,
|
|
||||||
Ok(count) => count,
|
|
||||||
Err(ref err) if err.errno() == EAGAIN => break,
|
|
||||||
Err(_) => panic!("getty: failed to read from master TTY"),
|
|
||||||
};
|
|
||||||
redox::write(tty_fd as usize, &packet[1..count])
|
|
||||||
.expect("getty: failed to write to TTY");
|
|
||||||
if packet[0] & 1 == 1 {
|
|
||||||
let _ = redox::fsync(tty_fd as usize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handle_event(tty_fd as usize);
|
|
||||||
handle_event(master_fd as usize);
|
|
||||||
|
|
||||||
'events: loop {
|
|
||||||
let sys_event = event_queue
|
|
||||||
.next()
|
|
||||||
.expect("getty: event queue stopped")
|
|
||||||
.expect("getty: failed to read event file");
|
|
||||||
handle_event(sys_event.fd);
|
|
||||||
|
|
||||||
match process.try_wait() {
|
|
||||||
Ok(status) => match status {
|
|
||||||
Some(_code) => break 'events,
|
|
||||||
None => (),
|
|
||||||
},
|
|
||||||
Err(err) => match err.kind() {
|
|
||||||
ErrorKind::WouldBlock => (),
|
|
||||||
_ => panic!("getty: failed to wait on child: {:?}", err),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = process.kill();
|
|
||||||
process.wait().expect("getty: failed to wait on login");
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn getpty(columns: u16, lines: u16) -> (RawFd, String) {
|
|
||||||
let master = redox::open(
|
|
||||||
"/scheme/pty",
|
|
||||||
flag::O_CLOEXEC | flag::O_RDWR | flag::O_CREAT | flag::O_NONBLOCK,
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
.expect("getty: failed to create PTY");
|
|
||||||
|
|
||||||
if let Ok(winsize_fd) = redox::dup(master, b"winsize") {
|
|
||||||
let _ = redox::write(
|
|
||||||
winsize_fd,
|
|
||||||
&redox_termios::Winsize {
|
|
||||||
ws_row: lines,
|
|
||||||
ws_col: columns,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
let _ = redox::close(winsize_fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut buf: [u8; 4096] = [0; 4096];
|
|
||||||
let count = redox::fpath(master, &mut buf).unwrap();
|
|
||||||
(master as RawFd, unsafe {
|
|
||||||
String::from_utf8_unchecked(Vec::from(&buf[..count]))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// termion cursor_pos prone to error and does not work on nonblocking files
|
|
||||||
fn tty_cursor_pos(tty: &mut File) -> Result<(u16, u16), Box<dyn Error>> {
|
|
||||||
write!(tty, "\x1B[6n")?;
|
|
||||||
tty.flush()?;
|
|
||||||
|
|
||||||
let timeout = Duration::from_millis(500);
|
|
||||||
let instant = Instant::now();
|
|
||||||
let mut data = String::new();
|
|
||||||
while instant.elapsed() < timeout {
|
|
||||||
let mut bytes = [0];
|
|
||||||
match tty.read(&mut bytes) {
|
|
||||||
Ok(count) => if count == 1 {
|
|
||||||
let c = bytes[0] as char;
|
|
||||||
if c == 'R' {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
data.push(c);
|
|
||||||
},
|
|
||||||
Err(err) => if err.kind() != ErrorKind::WouldBlock {
|
|
||||||
return Err(err.into());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if data.is_empty() {
|
|
||||||
return Err("cursor position timed out".into());
|
|
||||||
}
|
|
||||||
|
|
||||||
let beg = data.rfind('[').ok_or("failed to find [")?;
|
|
||||||
let coords: String = data.chars().skip(beg + 1).collect();
|
|
||||||
let mut nums = coords.split(';');
|
|
||||||
|
|
||||||
let row = nums.next().ok_or("failed to find row")?.parse::<u16>()?;
|
|
||||||
let col = nums.next().ok_or("failed to find col")?.parse::<u16>()?;
|
|
||||||
|
|
||||||
Ok((col, row))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn tty_columns_lines(tty: &mut File) -> Result<(u16, u16), Box<dyn Error>> {
|
|
||||||
write!(tty, "{}", termion::cursor::Save)?;
|
|
||||||
tty.flush()?;
|
|
||||||
|
|
||||||
write!(tty, "{}", termion::cursor::Goto(999, 999))?;
|
|
||||||
tty.flush()?;
|
|
||||||
|
|
||||||
let res = tty_cursor_pos(tty);
|
|
||||||
|
|
||||||
write!(tty, "{}", termion::cursor::Restore)?;
|
|
||||||
tty.flush()?;
|
|
||||||
|
|
||||||
res
|
|
||||||
}
|
|
||||||
|
|
||||||
fn daemon(tty: &mut File, clear: bool, contain: bool, stderr: &mut Stderr) {
|
|
||||||
let (columns, lines) = tty_columns_lines(tty).unwrap_or((DEFAULT_COLS, DEFAULT_LINES));
|
|
||||||
let tty_fd = tty.as_raw_fd();
|
|
||||||
|
|
||||||
let (master_fd, pty) = getpty(columns, lines);
|
|
||||||
|
|
||||||
let mut event_queue = event::RawEventQueue::new().expect("getty: failed to open event queue");
|
|
||||||
|
|
||||||
event_queue
|
|
||||||
.subscribe(tty_fd as usize, 0, EventFlags::READ)
|
|
||||||
.expect("getty: failed to fevent TTY");
|
|
||||||
|
|
||||||
event_queue
|
|
||||||
.subscribe(master_fd as usize, 0, EventFlags::READ)
|
|
||||||
.expect("getty: failed to fevent master PTY");
|
|
||||||
|
|
||||||
loop {
|
|
||||||
dlog("daemon loop");
|
|
||||||
if clear {
|
|
||||||
let _ = redox::write(tty_fd as usize, b"\x1Bc");
|
|
||||||
}
|
|
||||||
let _ = redox::fsync(tty_fd as usize);
|
|
||||||
|
|
||||||
let slave_stdin = redox::open(&pty, flag::O_CLOEXEC | flag::O_RDONLY, 0)
|
|
||||||
.expect("getty: failed to open slave stdin");
|
|
||||||
let slave_stdout = redox::open(&pty, flag::O_CLOEXEC | flag::O_WRONLY, 0)
|
|
||||||
.expect("getty: failed to open slave stdout");
|
|
||||||
let slave_stderr = redox::open(&pty, flag::O_CLOEXEC | flag::O_WRONLY, 0)
|
|
||||||
.expect("getty: failed to open slave stderr");
|
|
||||||
|
|
||||||
let mut command = if contain {
|
|
||||||
Command::new("contain_login")
|
|
||||||
} else {
|
|
||||||
Command::new("login")
|
|
||||||
};
|
|
||||||
unsafe {
|
|
||||||
command
|
|
||||||
.stdin(Stdio::from_raw_fd(slave_stdin as RawFd))
|
|
||||||
.stdout(Stdio::from_raw_fd(slave_stdout as RawFd))
|
|
||||||
.stderr(Stdio::from_raw_fd(slave_stderr as RawFd))
|
|
||||||
.env("TERM", "xterm-256color")
|
|
||||||
.env("TTY", &pty);
|
|
||||||
}
|
|
||||||
|
|
||||||
match command.spawn() {
|
|
||||||
Ok(mut process) => {
|
|
||||||
dlog("login spawned");
|
|
||||||
handle(&mut event_queue, tty_fd, master_fd, &mut process);
|
|
||||||
dlog("handle returned");
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
dlog(&format!("login spawn failed: {}", err));
|
|
||||||
fail(&format!("getty: failed to execute login: {}", err), stderr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main() {
|
|
||||||
dlog("main started");
|
|
||||||
let mut stderr = io::stderr();
|
|
||||||
|
|
||||||
let args = clap_app!(getty =>
|
|
||||||
(author: "Jeremy Soller")
|
|
||||||
(about: "Set terminal mode")
|
|
||||||
(@arg TTY: +required "")
|
|
||||||
(@arg NO_CLEAR: -J --("no-clear") "Do not clear the screen before forking")
|
|
||||||
(@arg CONTAIN: -C --("contain") "Run contain_login instead of login")
|
|
||||||
)
|
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
let clear = !args.is_present("NO_CLEAR");
|
|
||||||
|
|
||||||
let contain = args.is_present("CONTAIN");
|
|
||||||
|
|
||||||
let vt = args.value_of("TTY").unwrap();
|
|
||||||
|
|
||||||
let buf: String;
|
|
||||||
let vt_path = if vt.parse::<usize>().is_ok() {
|
|
||||||
buf = format!("/scheme/fbcon/{vt}");
|
|
||||||
&*buf
|
|
||||||
} else {
|
|
||||||
vt
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut tty = match redox::open(
|
|
||||||
&vt_path,
|
|
||||||
flag::O_CLOEXEC | flag::O_RDWR | flag::O_NONBLOCK,
|
|
||||||
0,
|
|
||||||
) {
|
|
||||||
Ok(fd) => {
|
|
||||||
dlog(&format!("opened TTY {}", vt_path));
|
|
||||||
unsafe { File::from_raw_fd(fd as RawFd) }
|
|
||||||
}
|
|
||||||
Err(err) => fail(
|
|
||||||
&format!("getty: failed to open TTY {}: {}", vt_path, err),
|
|
||||||
&mut stderr,
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
daemon(&mut tty, clear, contain, &mut stderr);
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
#[macro_use]
|
|
||||||
extern crate clap;
|
|
||||||
|
|
||||||
use extra::option::OptionalExt;
|
|
||||||
|
|
||||||
use std::process::exit;
|
|
||||||
|
|
||||||
use redox_users::{All, AllGroups, Config, Error, GroupBuilder};
|
|
||||||
|
|
||||||
const _MAN_PAGE: &'static str = /* @MANSTART{groupadd} */
|
|
||||||
r#"
|
|
||||||
NAME
|
|
||||||
groupadd - add a user group
|
|
||||||
|
|
||||||
SYNOPSIS
|
|
||||||
groupadd [ -f | --force ] GROUP
|
|
||||||
groupadd [ -h | --help ]
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
The groupadd utility adds a new user group using values
|
|
||||||
passed on the command line and system defaults.
|
|
||||||
|
|
||||||
OPTIONS
|
|
||||||
-f, --force
|
|
||||||
Simply forces the exit status of the program to 0
|
|
||||||
even if the group already exists. A message is still
|
|
||||||
printed to stdout.
|
|
||||||
|
|
||||||
-g, --gid GID
|
|
||||||
The group id to use. This value must not be used and must
|
|
||||||
be non-negative. The default is to pick the smallest available
|
|
||||||
group id (between values defined in redox_users).
|
|
||||||
|
|
||||||
-h, --help
|
|
||||||
Display this help and exit.
|
|
||||||
|
|
||||||
AUTHOR
|
|
||||||
Written by Wesley Hershberger.
|
|
||||||
"#; /* @MANEND */
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let args = clap_app!(groupadd =>
|
|
||||||
(author: "Wesley Hershberger")
|
|
||||||
(about: "Add groups based on the system's redox_users backend")
|
|
||||||
(@arg GROUP: +required "Add group GROUP")
|
|
||||||
(@arg FORCE: -f --force "Force the status of the program to be 0 even if the group exists")
|
|
||||||
(@arg GID: -g --gid +takes_value "Group id. Positive integer and must not be in use")
|
|
||||||
)
|
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
let mut sys_groups = AllGroups::new(Config::default().writeable(true)).unwrap_or_exit(1);
|
|
||||||
|
|
||||||
let groupname = args.value_of("GROUP").unwrap();
|
|
||||||
|
|
||||||
let gid = match args.value_of("GID") {
|
|
||||||
Some(gid) => {
|
|
||||||
let id = gid.parse::<usize>().unwrap_or_exit(1);
|
|
||||||
if let Some(_group) = sys_groups.get_by_id(id) {
|
|
||||||
eprintln!("groupadd: group already exists");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
id
|
|
||||||
}
|
|
||||||
None => sys_groups.get_unique_id().unwrap_or_else(|| {
|
|
||||||
eprintln!("groupadd: no available gid");
|
|
||||||
exit(1);
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
let group = GroupBuilder::new(groupname).gid(gid);
|
|
||||||
match sys_groups.add_group(group) {
|
|
||||||
Ok(_) => (),
|
|
||||||
Err(Error::GroupAlreadyExists) if args.is_present("FORCE") => {
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
eprintln!("groupadd: {}", err);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sys_groups.save().unwrap_or_exit(1);
|
|
||||||
}
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
#[macro_use]
|
|
||||||
extern crate clap;
|
|
||||||
|
|
||||||
use extra::option::OptionalExt;
|
|
||||||
use redox_users::{All, AllGroups, Config};
|
|
||||||
|
|
||||||
const _MAN_PAGE: &'static str = /* @MANSTART{groupdel} */
|
|
||||||
r#"
|
|
||||||
NAME
|
|
||||||
groupdel - modify system files to delete groups
|
|
||||||
|
|
||||||
SYNOPSYS
|
|
||||||
groupdel [ options ] GROUP
|
|
||||||
groupdel [ -h | --help ]
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
groupdel removes groups from whatever backend is employed by
|
|
||||||
the system's redox_users.
|
|
||||||
|
|
||||||
Note that you should not remove a primary user group before
|
|
||||||
removing the user. It is also generally wise not to remove
|
|
||||||
groups that still own files on the system.
|
|
||||||
|
|
||||||
OPTIONS
|
|
||||||
-h, --help
|
|
||||||
Print this help page and exit.
|
|
||||||
|
|
||||||
AUTHORS
|
|
||||||
Wesley Hershberger.
|
|
||||||
"#; /* @MANEND */
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let matches = clap_app!(groupdel =>
|
|
||||||
(author: "Wesley Hershberger")
|
|
||||||
(about: "Removes a group from the system using redox_users")
|
|
||||||
(@arg GROUP: +required "Removes group GROUP")
|
|
||||||
)
|
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
let group = matches.value_of("GROUP").unwrap();
|
|
||||||
|
|
||||||
let mut sys_groups = AllGroups::new(Config::default().writeable(true)).unwrap_or_exit(1);
|
|
||||||
|
|
||||||
sys_groups.remove_by_name(group.to_string());
|
|
||||||
|
|
||||||
sys_groups.save().unwrap_or_exit(1);
|
|
||||||
}
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
#[macro_use]
|
|
||||||
extern crate clap;
|
|
||||||
|
|
||||||
use std::process::exit;
|
|
||||||
|
|
||||||
use extra::option::OptionalExt;
|
|
||||||
use redox_users::{All, AllGroups, AllUsers, Config};
|
|
||||||
|
|
||||||
const _MAN_PAGE: &'static str = /* @MANSTART{groupmod} */
|
|
||||||
r#"
|
|
||||||
NAME
|
|
||||||
groupmod - modify group information
|
|
||||||
|
|
||||||
SYNOPSYS
|
|
||||||
groupmod [ options ] GROUP
|
|
||||||
groupmod [ -h | --help ]
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
groupmod modifies a user group GROUP in the system's
|
|
||||||
redox_users backend.
|
|
||||||
|
|
||||||
OPTIONS
|
|
||||||
-h, --help
|
|
||||||
Print this help page and exit.
|
|
||||||
|
|
||||||
-g, --gid GID
|
|
||||||
Change GROUP's group id. GID must be a non-negative
|
|
||||||
decimal integer.
|
|
||||||
|
|
||||||
Files with GROUP's old gid will not be updated.
|
|
||||||
|
|
||||||
User's who use the old gid as their primary gid will
|
|
||||||
be updated.
|
|
||||||
|
|
||||||
-n, --name NAME
|
|
||||||
The name of the group will be set to NAME
|
|
||||||
|
|
||||||
AUTHORS
|
|
||||||
Wesley Hershberger.
|
|
||||||
"#; /* @MANEND */
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let args = clap_app!(groupmod =>
|
|
||||||
(author: "Wesley Hershberger")
|
|
||||||
(about: "Modify users according to the system's redox_users backend")
|
|
||||||
(@arg GROUP: +required "Modify GROUP")
|
|
||||||
(@arg GID: -g --gid +takes_value "Change GROUP's group id. See man page for details")
|
|
||||||
(@arg NAME: -n --name +takes_value "Change GROUP's name")
|
|
||||||
)
|
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
let groupname = args.value_of("GROUP").unwrap();
|
|
||||||
|
|
||||||
let mut sys_groups = AllGroups::new(Config::default().writeable(true)).unwrap_or_exit(1);
|
|
||||||
{
|
|
||||||
let group = sys_groups.get_mut_by_name(groupname).unwrap_or_else(|| {
|
|
||||||
eprintln!("groupmod: group not found: {}", groupname);
|
|
||||||
exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(gid) = args.value_of("GID") {
|
|
||||||
let gid = gid.parse::<usize>().unwrap_or_exit(1);
|
|
||||||
// Update users
|
|
||||||
let mut sys_users =
|
|
||||||
AllUsers::authenticator(Config::default().writeable(true)).unwrap_or_exit(1);
|
|
||||||
for user in sys_users.iter_mut() {
|
|
||||||
if user.gid == group.gid {
|
|
||||||
user.gid = gid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sys_users.save().unwrap_or_exit(1);
|
|
||||||
group.gid = gid;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(name) = args.value_of("NAME") {
|
|
||||||
group.group = name.to_string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sys_groups.save().unwrap_or_exit(1);
|
|
||||||
}
|
|
||||||
-163
@@ -1,163 +0,0 @@
|
|||||||
#[macro_use]
|
|
||||||
extern crate clap;
|
|
||||||
|
|
||||||
use std::env::args;
|
|
||||||
use std::process::exit;
|
|
||||||
|
|
||||||
use extra::option::OptionalExt;
|
|
||||||
use redox_users::{All, AllGroups, AllUsers, Config, get_egid, get_euid, get_gid, get_uid};
|
|
||||||
|
|
||||||
const _MAN_PAGE: &'static str = /* @MANSTART{id} */
|
|
||||||
r#"
|
|
||||||
NAME
|
|
||||||
id - display user identity
|
|
||||||
|
|
||||||
SYNOPSIS
|
|
||||||
id
|
|
||||||
id -g [-nr]
|
|
||||||
id -u [-nr]
|
|
||||||
id [ -h | --help ]
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
The id utility displays the user and group names and numeric IDs, of
|
|
||||||
the calling process, to the standard output.
|
|
||||||
|
|
||||||
OPTIONS
|
|
||||||
-G, --groups
|
|
||||||
Display the different group IDs (effective and real) as white-space
|
|
||||||
separated numbers, in no particular order.
|
|
||||||
|
|
||||||
-g, --group
|
|
||||||
Display the effective group ID as a number.
|
|
||||||
|
|
||||||
-n, --name
|
|
||||||
Display the name of the user or group ID for the -g and -u options
|
|
||||||
instead of the number.
|
|
||||||
|
|
||||||
-u, --user
|
|
||||||
Display the effective user ID as a number.
|
|
||||||
|
|
||||||
-a
|
|
||||||
Ignored for compatibility with other id implementations.
|
|
||||||
|
|
||||||
-r, --real
|
|
||||||
Display the real ID for the -g and -u options instead of the effective ID.
|
|
||||||
|
|
||||||
-h, --help
|
|
||||||
Display help and exit.
|
|
||||||
|
|
||||||
AUTHOR
|
|
||||||
Written by Jose Narvaez.
|
|
||||||
"#; /* @MANEND */
|
|
||||||
|
|
||||||
pub fn main() {
|
|
||||||
let app = clap_app!(id =>
|
|
||||||
(author: "Jose Narvaez")
|
|
||||||
(about: "Get user and group information about the current user")
|
|
||||||
(@arg IGNORE: -a "Ignored for compatibility with other impls of id")
|
|
||||||
(@arg GROUPS: -G --groups conflicts_with[selector modifier] "Display current user's real and effective group id's")
|
|
||||||
(@group selector =>
|
|
||||||
(@arg GROUP: -g --group "Display current user's effective group id")
|
|
||||||
(@arg USER: -u --user "Display the effective userid")
|
|
||||||
)
|
|
||||||
(@group modifier =>
|
|
||||||
(@arg NAME: -n --name requires[selector] "Display names of groups/users instead of ids (use with -g or -u)")
|
|
||||||
(@arg REAL: -r --real requires[selector] "Display real id's instead of effective ids (use with -g and -u)")
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
let args = match &*args().nth(0).unwrap_or(String::new()) {
|
|
||||||
"whoami" => app.get_matches_from(["id", "-un"].iter()),
|
|
||||||
_ => app.get_matches(),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Display the different group IDs (effective and real)
|
|
||||||
// as white-space separated numbers, in no particular order.
|
|
||||||
if args.is_present("GROUPS") {
|
|
||||||
let egid = get_egid().unwrap_or_exit(1);
|
|
||||||
|
|
||||||
let gid = get_gid().unwrap_or_exit(1);
|
|
||||||
|
|
||||||
println!("{} {}", egid, gid);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display effective/real process user ID UNIX user name
|
|
||||||
if args.is_present("USER") && args.is_present("NAME") {
|
|
||||||
// Did they pass -r? If so, we show the real
|
|
||||||
let uid = if args.is_present("REAL") {
|
|
||||||
get_uid()
|
|
||||||
} else {
|
|
||||||
get_euid()
|
|
||||||
}
|
|
||||||
.unwrap_or_exit(1);
|
|
||||||
|
|
||||||
let users = AllUsers::basic(Config::default()).unwrap_or_exit(1);
|
|
||||||
let user = users.get_by_id(uid).unwrap_or_exit(1);
|
|
||||||
|
|
||||||
println!("{}", user.user);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display real user ID
|
|
||||||
if args.is_present("USER") && args.is_present("REAL") {
|
|
||||||
let uid = get_uid().unwrap_or_exit(1);
|
|
||||||
|
|
||||||
println!("{}", uid);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display effective user ID
|
|
||||||
if args.is_present("USER") {
|
|
||||||
let euid = get_euid().unwrap_or_exit(1);
|
|
||||||
|
|
||||||
println!("{}", euid);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display effective/real process group ID UNIX group name
|
|
||||||
if args.is_present("GROUP") && args.is_present("NAME") {
|
|
||||||
// Did they pass -r? If so we show the real one
|
|
||||||
let gid = if args.is_present("REAL") {
|
|
||||||
get_gid()
|
|
||||||
} else {
|
|
||||||
get_egid()
|
|
||||||
}
|
|
||||||
.unwrap_or_exit(1);
|
|
||||||
|
|
||||||
let groups = AllGroups::new(Config::default()).unwrap_or_exit(1);
|
|
||||||
let group = groups.get_by_id(gid).unwrap_or_exit(1);
|
|
||||||
|
|
||||||
println!("{}", group.group);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display the real group ID
|
|
||||||
if args.is_present("GROUP") && args.is_present("REAL") {
|
|
||||||
let gid = get_gid().unwrap_or_exit(1);
|
|
||||||
|
|
||||||
println!("{}", gid);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display effective group ID
|
|
||||||
if args.is_present("GROUP") {
|
|
||||||
let egid = get_egid().unwrap_or_exit(1);
|
|
||||||
|
|
||||||
println!("{}", egid);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We get everything we can and show
|
|
||||||
let euid = get_euid().unwrap_or_exit(1);
|
|
||||||
let egid = get_egid().unwrap_or_exit(1);
|
|
||||||
|
|
||||||
let users = AllUsers::basic(Config::default()).unwrap_or_exit(1);
|
|
||||||
let groups = AllGroups::new(Config::default()).unwrap_or_exit(1);
|
|
||||||
|
|
||||||
let user = users.get_by_id(euid).unwrap_or_exit(1);
|
|
||||||
let group = groups.get_by_id(egid).unwrap_or_exit(1);
|
|
||||||
|
|
||||||
println!("uid={}({}) gid={}({})", euid, user.user, egid, group.group);
|
|
||||||
exit(0);
|
|
||||||
}
|
|
||||||
@@ -1,221 +0,0 @@
|
|||||||
#[macro_use]
|
|
||||||
extern crate clap;
|
|
||||||
|
|
||||||
use libredox::error::Result;
|
|
||||||
use std::fs::{File, OpenOptions};
|
|
||||||
use std::io::{self, Write};
|
|
||||||
use std::str;
|
|
||||||
|
|
||||||
fn dlog(msg: &str) {
|
|
||||||
let _ = OpenOptions::new()
|
|
||||||
.write(true)
|
|
||||||
.open("/scheme/debug/no-preserve")
|
|
||||||
.and_then(|mut f| writeln!(f, "LOGIN {}", msg));
|
|
||||||
}
|
|
||||||
|
|
||||||
use extra::option::OptionalExt;
|
|
||||||
use redox_users::{All, AllUsers, Config, User};
|
|
||||||
use termion::input::TermRead;
|
|
||||||
use userutils::spawn_shell;
|
|
||||||
|
|
||||||
const _MAN_PAGE: &'static str = /* @MANSTART{login} */
|
|
||||||
r#"
|
|
||||||
NAME
|
|
||||||
login - log into the computer
|
|
||||||
|
|
||||||
SYNOPSIS
|
|
||||||
login
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
The login utility logs users (and pseudo-users) into the computer system.
|
|
||||||
|
|
||||||
OPTIONS
|
|
||||||
|
|
||||||
-h --help
|
|
||||||
Display help info and exit.
|
|
||||||
|
|
||||||
AUTHOR
|
|
||||||
Written by Jeremy Soller, Jose Narvaez.
|
|
||||||
"#; /* @MANEND */
|
|
||||||
|
|
||||||
const ISSUE_FILE: &'static str = "/etc/issue";
|
|
||||||
const MOTD_FILE: &'static str = "/etc/motd";
|
|
||||||
|
|
||||||
// TODO: Move to redox_users once the definition solidifies.
|
|
||||||
const DEFAULT_SCHEMES: [&'static str; 26] = [
|
|
||||||
// Kernel schemes
|
|
||||||
"debug",
|
|
||||||
"event",
|
|
||||||
"memory",
|
|
||||||
"pipe",
|
|
||||||
"serio",
|
|
||||||
"irq",
|
|
||||||
"time",
|
|
||||||
"sys",
|
|
||||||
// Base schemes
|
|
||||||
"rand",
|
|
||||||
"null",
|
|
||||||
"zero",
|
|
||||||
"log",
|
|
||||||
// Network schemes
|
|
||||||
"ip",
|
|
||||||
"icmp",
|
|
||||||
"tcp",
|
|
||||||
"udp",
|
|
||||||
// IPC schemes
|
|
||||||
"shm",
|
|
||||||
"chan",
|
|
||||||
"uds_stream",
|
|
||||||
"uds_dgram",
|
|
||||||
// File schemes
|
|
||||||
"file",
|
|
||||||
// Display schemes
|
|
||||||
"display.vesa",
|
|
||||||
"display*",
|
|
||||||
// Other schemes
|
|
||||||
"pty",
|
|
||||||
"sudo",
|
|
||||||
"audio",
|
|
||||||
];
|
|
||||||
pub fn apply_login_schemes(
|
|
||||||
user: &User<redox_users::auth::Full>,
|
|
||||||
default_schemes: &[&str],
|
|
||||||
) -> Result<libredox::Fd> {
|
|
||||||
let schemes = match load_config_schemes(user) {
|
|
||||||
Some(s) => s,
|
|
||||||
_ => default_schemes.iter().map(|s| s.to_string()).collect(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut names: Vec<ioslice::IoSlice> = Vec::with_capacity(schemes.len());
|
|
||||||
for scheme in schemes.iter() {
|
|
||||||
names.push(ioslice::IoSlice::new(scheme.as_bytes()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let ns_fd = libredox::call::mkns(&names)?;
|
|
||||||
let before_ns_fd = libredox::Fd::new(libredox::call::setns(ns_fd)?);
|
|
||||||
|
|
||||||
Ok(before_ns_fd)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn load_config_schemes(user: &User<redox_users::auth::Full>) -> Option<Vec<String>> {
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use std::collections::BTreeMap;
|
|
||||||
use std::fs;
|
|
||||||
|
|
||||||
const LOGIN_SCHEMES_FILE: &'static str = "/etc/login_schemes.toml";
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
struct UserSchemeConfig {
|
|
||||||
pub schemes: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
||||||
struct LoginConfig {
|
|
||||||
#[serde(rename = "user_schemes")]
|
|
||||||
pub user_schemes: BTreeMap<String, UserSchemeConfig>,
|
|
||||||
}
|
|
||||||
|
|
||||||
let config_str = fs::read_to_string(LOGIN_SCHEMES_FILE).ok()?;
|
|
||||||
let config: LoginConfig = toml::from_str(&config_str).ok()?;
|
|
||||||
|
|
||||||
config
|
|
||||||
.user_schemes
|
|
||||||
.get(&user.user)
|
|
||||||
.map(|cfg| cfg.schemes.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn main() {
|
|
||||||
dlog("main started");
|
|
||||||
let mut stdout = io::stdout();
|
|
||||||
let mut stderr = io::stderr();
|
|
||||||
|
|
||||||
let _args = clap_app!(login =>
|
|
||||||
(author: "Jeremy Soller, Jose Narvaez")
|
|
||||||
(about: "Login as a user")
|
|
||||||
)
|
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
dlog("before issue");
|
|
||||||
if let Ok(mut issue) = File::open(ISSUE_FILE) {
|
|
||||||
io::copy(&mut issue, &mut stdout).r#try(&mut stderr);
|
|
||||||
stdout.flush().r#try(&mut stderr);
|
|
||||||
dlog("issue printed");
|
|
||||||
} else {
|
|
||||||
dlog("no issue file");
|
|
||||||
}
|
|
||||||
|
|
||||||
loop {
|
|
||||||
dlog("before liner read_line");
|
|
||||||
let _ = stdout.write_all(b"DIRECT_PROMPT_TEST");
|
|
||||||
let _ = stdout.flush();
|
|
||||||
dlog("direct prompt write done");
|
|
||||||
let user = liner::Context::new()
|
|
||||||
.read_line(
|
|
||||||
liner::Prompt::from("\x1B[1mRed Bear login:\x1B[0m "),
|
|
||||||
None,
|
|
||||||
&mut liner::BasicCompleter::new(Vec::<String>::new()),
|
|
||||||
)
|
|
||||||
.r#try(&mut stderr);
|
|
||||||
dlog("after liner read_line");
|
|
||||||
|
|
||||||
if !user.is_empty() {
|
|
||||||
let stdin = io::stdin();
|
|
||||||
let mut stdin = stdin.lock();
|
|
||||||
let sys_users = AllUsers::authenticator(Config::default()).unwrap_or_exit(1);
|
|
||||||
|
|
||||||
match sys_users.get_by_name(user) {
|
|
||||||
None => {
|
|
||||||
stdout.write(b"\nLogin incorrect\n").r#try(&mut stderr);
|
|
||||||
stdout.write(b"\n").r#try(&mut stderr);
|
|
||||||
stdout.flush().r#try(&mut stderr);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
Some(user) => {
|
|
||||||
if user.is_passwd_blank() {
|
|
||||||
if let Ok(mut motd) = File::open(MOTD_FILE) {
|
|
||||||
io::copy(&mut motd, &mut stdout).r#try(&mut stderr);
|
|
||||||
stdout.flush().r#try(&mut stderr);
|
|
||||||
}
|
|
||||||
|
|
||||||
let before_ns_fd =
|
|
||||||
apply_login_schemes(user, &DEFAULT_SCHEMES).unwrap_or_exit(1);
|
|
||||||
|
|
||||||
let _ = syscall::fcntl(
|
|
||||||
before_ns_fd.raw(),
|
|
||||||
syscall::F_SETFD,
|
|
||||||
syscall::O_CLOEXEC,
|
|
||||||
);
|
|
||||||
spawn_shell(user).unwrap_or_exit(1);
|
|
||||||
let _ = syscall::fcntl(before_ns_fd.raw(), syscall::F_SETFD, 0);
|
|
||||||
let _ = libredox::call::close(
|
|
||||||
libredox::call::setns(before_ns_fd.into_raw()).unwrap_or_exit(1),
|
|
||||||
);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
stdout
|
|
||||||
.write_all(b"\x1B[1mpassword:\x1B[0m ")
|
|
||||||
.r#try(&mut stderr);
|
|
||||||
stdout.flush().r#try(&mut stderr);
|
|
||||||
if let Some(password) = stdin.read_passwd(&mut stdout).r#try(&mut stderr) {
|
|
||||||
stdout.write(b"\n").r#try(&mut stderr);
|
|
||||||
stdout.flush().r#try(&mut stderr);
|
|
||||||
|
|
||||||
if user.verify_passwd(&password) {
|
|
||||||
if let Ok(mut motd) = File::open(MOTD_FILE) {
|
|
||||||
io::copy(&mut motd, &mut stdout).r#try(&mut stderr);
|
|
||||||
stdout.flush().r#try(&mut stderr);
|
|
||||||
}
|
|
||||||
|
|
||||||
spawn_shell(user).unwrap_or_exit(1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
stdout.write(b"\n").r#try(&mut stderr);
|
|
||||||
stdout.flush().r#try(&mut stderr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,197 +0,0 @@
|
|||||||
#[macro_use]
|
|
||||||
extern crate clap;
|
|
||||||
|
|
||||||
use std::io;
|
|
||||||
use std::io::Write;
|
|
||||||
use std::process::exit;
|
|
||||||
|
|
||||||
use extra::option::OptionalExt;
|
|
||||||
use libredox::flag::O_CLOEXEC;
|
|
||||||
use libredox::errno::EPERM;
|
|
||||||
use redox_users::{All, AllUsers, Config, get_uid};
|
|
||||||
use termion::input::TermRead;
|
|
||||||
|
|
||||||
const _MAN_PAGE: &'static str = /* @MANSTART{passwd} */
|
|
||||||
r#"
|
|
||||||
NAME
|
|
||||||
passwd - modify a user's password
|
|
||||||
|
|
||||||
SYNOPSIS
|
|
||||||
passwd [ LOGIN ]
|
|
||||||
passwd [ -h | --help ]
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
The passwd utility changes the user's local password. If the user is not
|
|
||||||
the super-user, passwd first prompts for the current password and will
|
|
||||||
not continue unless the correct password is entered.
|
|
||||||
|
|
||||||
OPTIONS
|
|
||||||
|
|
||||||
-h, --help
|
|
||||||
Display this help and exit.
|
|
||||||
|
|
||||||
-l, --lock
|
|
||||||
Lock the password of the named account. This changes the stored password
|
|
||||||
hash so that it matches no encrypted value ("!")
|
|
||||||
|
|
||||||
Users with locked passwords are not allowed to change their password.
|
|
||||||
|
|
||||||
AUTHOR
|
|
||||||
Written by Jeremy Soller, Jose Narvaez.
|
|
||||||
"#; /* @MANEND */
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let mut stdin = io::stdin().lock();
|
|
||||||
let mut stdout = io::stdout().lock();
|
|
||||||
let mut stderr = io::stderr();
|
|
||||||
|
|
||||||
let args = clap_app!(passwd =>
|
|
||||||
(author: "Jeremy Soller, Jose Narvaez")
|
|
||||||
(about: "Set user passwords")
|
|
||||||
(@arg LOGIN: "Apply to login. Sets password for current user if not supplied")
|
|
||||||
(@arg LOCK: -l --lock "Lock the password for an account (no login)")
|
|
||||||
)
|
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
if args.is_present("LOCK") {
|
|
||||||
if get_uid().unwrap_or_exit(1) != 0 {
|
|
||||||
eprintln!("passwd: only root is allowed to lock accounts");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut users =
|
|
||||||
AllUsers::authenticator(Config::default().writeable(true)).unwrap_or_exit(1);
|
|
||||||
|
|
||||||
let Some(login) = args.value_of("LOGIN") else {
|
|
||||||
eprintln!("passwd: no account specified to lock");
|
|
||||||
exit(1);
|
|
||||||
};
|
|
||||||
|
|
||||||
let user = users.get_mut_by_name(login).unwrap_or_else(|| {
|
|
||||||
eprintln!("passwd: user does not exist: {}", login);
|
|
||||||
exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
user.unset_passwd();
|
|
||||||
users.save().unwrap_or_exit(1);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let uid = get_uid().unwrap_or_exit(1);
|
|
||||||
|
|
||||||
if uid == 0 {
|
|
||||||
let mut users =
|
|
||||||
AllUsers::authenticator(Config::default().writeable(true)).unwrap_or_exit(1);
|
|
||||||
|
|
||||||
let user = find_user(&args, &mut users);
|
|
||||||
|
|
||||||
let msg = format!("changing password for '{}' \n", user.user);
|
|
||||||
stdout.write_all(&msg.as_bytes()).r#try(&mut stderr);
|
|
||||||
stdout.flush().r#try(&mut stderr);
|
|
||||||
|
|
||||||
let new_password = ask_new_password(stdin, stdout, stderr);
|
|
||||||
|
|
||||||
user.set_passwd(&new_password).unwrap_or_exit(1);
|
|
||||||
users.save().unwrap_or_exit(1);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut users = AllUsers::basic(Config::default()).unwrap_or_exit(1);
|
|
||||||
|
|
||||||
let user = find_user(&args, &mut users);
|
|
||||||
|
|
||||||
if user.uid != uid {
|
|
||||||
eprintln!(
|
|
||||||
"passwd: you do not have permission to set the password of '{}'",
|
|
||||||
user.user
|
|
||||||
);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let msg = format!("changing password for '{}' \n", user.user);
|
|
||||||
stdout.write_all(&msg.as_bytes()).r#try(&mut stderr);
|
|
||||||
stdout.flush().r#try(&mut stderr);
|
|
||||||
|
|
||||||
drop(users); // Unlock /etc/passwd
|
|
||||||
|
|
||||||
stdout.write_all(b"current password: ").r#try(&mut stderr);
|
|
||||||
stdout.flush().r#try(&mut stderr);
|
|
||||||
|
|
||||||
let file = libredox::call::open("/scheme/sudo/passwd", O_CLOEXEC, 0).unwrap();
|
|
||||||
|
|
||||||
if let Some(password) = stdin.read_passwd(&mut stdout).r#try(&mut stderr) {
|
|
||||||
stdout.write(b"\n").r#try(&mut stderr);
|
|
||||||
stdout.flush().r#try(&mut stderr);
|
|
||||||
|
|
||||||
match libredox::call::write(file, password.as_bytes()) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(err) if err.errno() == EPERM => {
|
|
||||||
eprintln!("passwd: incorrect current password");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
Err(err) => panic!("{err}"),
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
eprintln!("passwd: incorrect current password");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_password = ask_new_password(stdin, stdout, stderr);
|
|
||||||
|
|
||||||
match libredox::call::write(file, new_password.as_bytes()) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(err) if err.errno() == EPERM => {
|
|
||||||
eprintln!("passwd: invalid new password");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
Err(err) => panic!("{err}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn find_user<'a, T: Default>(
|
|
||||||
args: &clap::ArgMatches<'_>,
|
|
||||||
users: &'a mut AllUsers<T>,
|
|
||||||
) -> &'a mut redox_users::User<T> {
|
|
||||||
let uid = get_uid().unwrap_or_exit(1);
|
|
||||||
match args.value_of("LOGIN") {
|
|
||||||
Some(login) => users.get_mut_by_name(login).unwrap_or_else(|| {
|
|
||||||
eprintln!("passwd: user does not exist: {}", login);
|
|
||||||
exit(1);
|
|
||||||
}),
|
|
||||||
None => users.get_mut_by_id(uid).unwrap_or_else(|| {
|
|
||||||
eprintln!("passwd: you do not exist");
|
|
||||||
exit(1);
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ask_new_password(
|
|
||||||
mut stdin: io::StdinLock<'_>,
|
|
||||||
mut stdout: io::StdoutLock<'_>,
|
|
||||||
mut stderr: io::Stderr,
|
|
||||||
) -> String {
|
|
||||||
stdout.write_all(b"new password: ").r#try(&mut stderr);
|
|
||||||
stdout.flush().r#try(&mut stderr);
|
|
||||||
let Some(new_password) = stdin.read_passwd(&mut stdout).r#try(&mut stderr) else {
|
|
||||||
eprintln!("passwd: no new password provided");
|
|
||||||
exit(1);
|
|
||||||
};
|
|
||||||
|
|
||||||
stdout.write(b"\nconfirm password: ").r#try(&mut stderr);
|
|
||||||
stdout.flush().r#try(&mut stderr);
|
|
||||||
let Some(confirm_password) = stdin.read_passwd(&mut stdout).r#try(&mut stderr) else {
|
|
||||||
eprintln!("\npasswd: no confirm password provided");
|
|
||||||
exit(1);
|
|
||||||
};
|
|
||||||
|
|
||||||
stdout.write(b"\n").r#try(&mut stderr);
|
|
||||||
stdout.flush().r#try(&mut stderr);
|
|
||||||
|
|
||||||
if new_password != confirm_password {
|
|
||||||
eprintln!("passwd: new password does not match confirm password");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
new_password
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
#[macro_use]
|
|
||||||
extern crate clap;
|
|
||||||
|
|
||||||
use std::io::{self, Write};
|
|
||||||
use std::process::exit;
|
|
||||||
use std::str;
|
|
||||||
|
|
||||||
use extra::option::OptionalExt;
|
|
||||||
use libredox::flag::O_CLOEXEC;
|
|
||||||
use redox_users::{All, AllUsers, Config, get_uid};
|
|
||||||
use syscall::EPERM;
|
|
||||||
use termion::input::TermRead;
|
|
||||||
use userutils::spawn_shell;
|
|
||||||
|
|
||||||
const _MAN_PAGE: &'static str = /* @MANSTART{su} */
|
|
||||||
r#"
|
|
||||||
NAME
|
|
||||||
su - substitute user identity
|
|
||||||
|
|
||||||
SYNOPSIS
|
|
||||||
su [ user ]
|
|
||||||
su [ -h | --help ]
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
The su utility requests appropriate user credentials via PAM and switches to
|
|
||||||
that user ID (the default user is the superuser). A shell is then executed.
|
|
||||||
|
|
||||||
OPTIONS
|
|
||||||
|
|
||||||
-h, --help
|
|
||||||
Display this help and exit.
|
|
||||||
|
|
||||||
AUTHOR
|
|
||||||
Written by Jeremy Soller, Jose Narvaez.
|
|
||||||
"#; /* @MANEND */
|
|
||||||
|
|
||||||
pub fn main() {
|
|
||||||
let stdin = io::stdin();
|
|
||||||
let mut stdin = stdin.lock();
|
|
||||||
let stdout = io::stdout();
|
|
||||||
let mut stdout = stdout.lock();
|
|
||||||
let mut stderr = io::stderr();
|
|
||||||
|
|
||||||
let args = clap_app!(su =>
|
|
||||||
(author: "Jeremy Soller, Jose Narvaez")
|
|
||||||
(about: "substitue user identity")
|
|
||||||
(@arg LOGIN: "Login as LOGIN. Default is \'root\'")
|
|
||||||
)
|
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
let target_user = args.value_of("LOGIN").unwrap_or("root");
|
|
||||||
|
|
||||||
let uid = get_uid().unwrap_or_exit(1);
|
|
||||||
|
|
||||||
let users = AllUsers::basic(Config::default()).unwrap_or_exit(1);
|
|
||||||
let user = users.get_by_name(&target_user).unwrap_or_exit(1);
|
|
||||||
|
|
||||||
// If the user executing su is root, then they can do anything without a password.
|
|
||||||
// Same if the user we're being asked to login as doesn't have a password.
|
|
||||||
if uid == 0 {
|
|
||||||
writeln!(stdout).unwrap_or_exit(1);
|
|
||||||
exit(spawn_shell(user).unwrap_or_exit(1));
|
|
||||||
} else {
|
|
||||||
let file = libredox::call::open("/scheme/sudo/su", O_CLOEXEC, 0).unwrap();
|
|
||||||
|
|
||||||
write!(stdout, "password: ").unwrap_or_exit(1);
|
|
||||||
stdout.flush().unwrap_or_exit(1);
|
|
||||||
|
|
||||||
// Read the password, reading an empty string if CTRL-d is specified
|
|
||||||
let password = stdin
|
|
||||||
.read_passwd(&mut stdout)
|
|
||||||
.r#try(&mut stderr)
|
|
||||||
.unwrap_or(String::new());
|
|
||||||
|
|
||||||
match libredox::call::write(file, password.as_bytes()) {
|
|
||||||
Ok(_) => exit(spawn_shell(user).unwrap_or_exit(1)),
|
|
||||||
Err(err) if err.errno() == EPERM => {
|
|
||||||
writeln!(stderr, "su: authentication failed").unwrap_or_exit(1);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
Err(err) => panic!("{err}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-402
@@ -1,402 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
use std::env;
|
|
||||||
use std::io::{self, Write};
|
|
||||||
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd};
|
|
||||||
use std::os::unix::process::CommandExt;
|
|
||||||
use std::process::{Command, exit};
|
|
||||||
|
|
||||||
use extra::option::OptionalExt;
|
|
||||||
use libredox::flag::O_CLOEXEC;
|
|
||||||
use libredox::protocol::ProcCall;
|
|
||||||
use redox_rt::sys::proc_call;
|
|
||||||
use redox_scheme::scheme::{SchemeSync, register_sync_scheme};
|
|
||||||
use redox_scheme::{
|
|
||||||
CallerCtx, OpenResult, RequestKind, Response, SendFdRequest, SignalBehavior, Socket,
|
|
||||||
};
|
|
||||||
use redox_users::{All, AllGroups, AllUsers, Config, get_uid};
|
|
||||||
use syscall::error::*;
|
|
||||||
use syscall::flag::*;
|
|
||||||
use syscall::schemev2::NewFdFlags;
|
|
||||||
use termion::input::TermRead;
|
|
||||||
|
|
||||||
const MAX_ATTEMPTS: u16 = 3;
|
|
||||||
const _MAN_PAGE: &'static str = /* @MANSTART{sudo} */
|
|
||||||
r#"
|
|
||||||
NAME
|
|
||||||
sudo - execute a command as another user
|
|
||||||
|
|
||||||
SYNOPSIS
|
|
||||||
sudo command
|
|
||||||
sudo [ -h | --help ]
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
The sudo utility allows a permitted user to execute a command as the
|
|
||||||
superuser or another user, as specified by the security policy.
|
|
||||||
|
|
||||||
EXIT STATUS
|
|
||||||
Upon successful execution of a command, the exit status from sudo will
|
|
||||||
be the exit status of the program that was executed. In case of error
|
|
||||||
the exit status will be >0.
|
|
||||||
|
|
||||||
AUTHOR
|
|
||||||
Written by Jeremy Soller, Jose Narvaez, bjorn3.
|
|
||||||
"#; /* @MANEND */
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
if env::args().nth(1).as_deref() == Some("--daemon") {
|
|
||||||
daemon_main();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut args = env::args().skip(1);
|
|
||||||
let cmd = args.next().unwrap_or_else(|| {
|
|
||||||
eprintln!("sudo: no command provided");
|
|
||||||
exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
let users = AllUsers::basic(Config::default()).unwrap_or_exit(1);
|
|
||||||
let uid = get_uid().unwrap_or_exit(1);
|
|
||||||
let user = users.get_by_id(uid).unwrap_or_exit(1);
|
|
||||||
|
|
||||||
if uid == 0 {
|
|
||||||
// We are root already. No need to elevate privileges
|
|
||||||
run_command_as_root(&cmd, &args.collect());
|
|
||||||
}
|
|
||||||
|
|
||||||
let file = libredox::call::open("/scheme/sudo", O_CLOEXEC, 0).unwrap();
|
|
||||||
|
|
||||||
let mut attempts = 0;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
print!("[sudo] password for {}: ", user.user);
|
|
||||||
let _ = io::stdout().flush();
|
|
||||||
|
|
||||||
match io::stdin().read_passwd(&mut io::stdout()).unwrap() {
|
|
||||||
Some(password) => {
|
|
||||||
println!();
|
|
||||||
|
|
||||||
match libredox::call::write(file, password.as_bytes()) {
|
|
||||||
Ok(_) => break,
|
|
||||||
Err(err) if err.errno() == EPERM => {
|
|
||||||
attempts += 1;
|
|
||||||
eprintln!(
|
|
||||||
"sudo: incorrect password or not in sudo group ({}/{})",
|
|
||||||
attempts, MAX_ATTEMPTS,
|
|
||||||
);
|
|
||||||
if attempts >= MAX_ATTEMPTS {
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(err) => panic!("{err}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
println!();
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME move to libredox
|
|
||||||
unsafe extern "C" {
|
|
||||||
safe fn redox_cur_procfd_v0() -> usize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Elevate privileges of our own process with help from the sudo daemon
|
|
||||||
syscall::sendfd(
|
|
||||||
file,
|
|
||||||
syscall::dup(redox_cur_procfd_v0(), &[]).unwrap(),
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// FIXME perhaps keep the original namespace available in a subdirectory of the namespace we switch to?
|
|
||||||
let ns = syscall::openat(file, "ns", 0, syscall::O_CLOEXEC).unwrap();
|
|
||||||
libredox::call::setns(ns).unwrap();
|
|
||||||
|
|
||||||
run_command_as_root(&cmd, &args.collect());
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Policy {
|
|
||||||
Deny,
|
|
||||||
Authenticate,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn policy_for_user(uid: u32) -> Policy {
|
|
||||||
let users = AllUsers::authenticator(Config::default()).unwrap_or_exit(1);
|
|
||||||
let groups = AllGroups::new(Config::default()).unwrap_or_exit(1);
|
|
||||||
|
|
||||||
let user = users.get_by_id(uid as usize).unwrap_or_exit(1);
|
|
||||||
|
|
||||||
let sudo_group = groups.get_by_name("sudo").unwrap_or_exit(1);
|
|
||||||
if !sudo_group.users.iter().any(|name| name == &user.user) {
|
|
||||||
return Policy::Deny;
|
|
||||||
}
|
|
||||||
|
|
||||||
Policy::Authenticate
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_command_as_root(cmd: &str, args: &Vec<String>) -> ! {
|
|
||||||
let mut command = Command::new(&cmd);
|
|
||||||
for arg in args {
|
|
||||||
command.arg(&arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
command.uid(0);
|
|
||||||
command.gid(0);
|
|
||||||
command.env("USER", "root");
|
|
||||||
command.env("UID", "0");
|
|
||||||
command.env("GROUPS", "0");
|
|
||||||
|
|
||||||
let err = command.exec();
|
|
||||||
|
|
||||||
eprintln!("sudo: failed to execute {}: {}", cmd, err);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Scheme {
|
|
||||||
next_fd: usize,
|
|
||||||
handles: HashMap<usize, Handle>,
|
|
||||||
}
|
|
||||||
enum Handle {
|
|
||||||
AwaitingPassword { uid: u32 },
|
|
||||||
AwaitingRootPassword,
|
|
||||||
AwaitingContextFd,
|
|
||||||
AwaitingNamespaceFetch { ns: libredox::Fd },
|
|
||||||
|
|
||||||
AwaitingPasswordForPasswd { uid: u32 },
|
|
||||||
AwaitingNewPassword { uid: u32 },
|
|
||||||
|
|
||||||
Placeholder,
|
|
||||||
|
|
||||||
SchemeRoot,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl SchemeSync for Scheme {
|
|
||||||
fn scheme_root(&mut self) -> Result<usize> {
|
|
||||||
let fd = self.next_fd;
|
|
||||||
self.next_fd = self.next_fd.checked_add(1).ok_or(Error::new(EMFILE))?;
|
|
||||||
self.handles.insert(fd, Handle::SchemeRoot);
|
|
||||||
Ok(fd)
|
|
||||||
}
|
|
||||||
fn openat(
|
|
||||||
&mut self,
|
|
||||||
dirfd: usize,
|
|
||||||
path: &str,
|
|
||||||
_flags: usize,
|
|
||||||
_fcntl_flags: u32,
|
|
||||||
ctx: &CallerCtx,
|
|
||||||
) -> Result<OpenResult> {
|
|
||||||
let handle = match self.handles.get_mut(&dirfd).ok_or(Error::new(EBADF))? {
|
|
||||||
Handle::SchemeRoot => match path {
|
|
||||||
"" => Handle::AwaitingPassword { uid: ctx.uid },
|
|
||||||
"su" => Handle::AwaitingRootPassword,
|
|
||||||
"passwd" => Handle::AwaitingPasswordForPasswd { uid: ctx.uid },
|
|
||||||
_ => return Err(Error::new(ENOENT)),
|
|
||||||
},
|
|
||||||
Handle::AwaitingNamespaceFetch { .. } => {
|
|
||||||
if path != "ns" {
|
|
||||||
return Err(Error::new(ENOENT));
|
|
||||||
}
|
|
||||||
let ns = match self.handles.insert(dirfd, Handle::Placeholder).unwrap() {
|
|
||||||
Handle::AwaitingNamespaceFetch { ns } => ns,
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
return Ok(OpenResult::OtherScheme { fd: ns.into_raw() });
|
|
||||||
}
|
|
||||||
_ => return Err(Error::new(EINVAL)),
|
|
||||||
};
|
|
||||||
|
|
||||||
let fd = self.next_fd;
|
|
||||||
self.next_fd = self.next_fd.checked_add(1).ok_or(Error::new(EMFILE))?;
|
|
||||||
self.handles.insert(fd, handle);
|
|
||||||
|
|
||||||
Ok(OpenResult::ThisScheme {
|
|
||||||
number: fd,
|
|
||||||
flags: NewFdFlags::empty(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write(
|
|
||||||
&mut self,
|
|
||||||
id: usize,
|
|
||||||
buf: &[u8],
|
|
||||||
_off: u64,
|
|
||||||
_flags: u32,
|
|
||||||
_ctx: &CallerCtx,
|
|
||||||
) -> Result<usize> {
|
|
||||||
let handle = self.handles.get_mut(&id).ok_or(Error::new(EBADF))?;
|
|
||||||
|
|
||||||
let validate_utf8 = |buf| std::str::from_utf8(buf).map_err(|_| Error::new(EINVAL));
|
|
||||||
|
|
||||||
match std::mem::replace(handle, Handle::Placeholder) {
|
|
||||||
Handle::AwaitingPassword { uid } => {
|
|
||||||
let users = AllUsers::authenticator(Config::default()).unwrap_or_exit(1);
|
|
||||||
let user = users.get_by_id(uid as usize).unwrap_or_exit(1);
|
|
||||||
|
|
||||||
match policy_for_user(uid) {
|
|
||||||
Policy::Deny => {
|
|
||||||
*handle = Handle::AwaitingPassword { uid };
|
|
||||||
return Err(Error::new(EPERM));
|
|
||||||
}
|
|
||||||
Policy::Authenticate => {
|
|
||||||
let password = validate_utf8(buf)?;
|
|
||||||
if user.verify_passwd(&password) {
|
|
||||||
*handle = Handle::AwaitingContextFd
|
|
||||||
} else {
|
|
||||||
*handle = Handle::AwaitingPassword { uid };
|
|
||||||
return Err(Error::new(EPERM));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Handle::AwaitingRootPassword => {
|
|
||||||
let users = AllUsers::authenticator(Config::default()).unwrap_or_exit(1);
|
|
||||||
let user = users.get_by_id(0).unwrap_or_exit(1);
|
|
||||||
|
|
||||||
let password = validate_utf8(buf)?;
|
|
||||||
if user.verify_passwd(&password) {
|
|
||||||
*handle = Handle::AwaitingContextFd
|
|
||||||
} else {
|
|
||||||
*handle = Handle::AwaitingRootPassword;
|
|
||||||
return Err(Error::new(EPERM));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Handle::AwaitingContextFd => {
|
|
||||||
*handle = Handle::AwaitingContextFd;
|
|
||||||
return Err(Error::new(EINVAL));
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle::AwaitingPasswordForPasswd { uid } => {
|
|
||||||
let users =
|
|
||||||
AllUsers::authenticator(Config::default()).map_err(|_| Error::new(ENOLCK))?;
|
|
||||||
let user = users.get_by_id(uid as usize).ok_or(Error::new(EEXIST))?;
|
|
||||||
|
|
||||||
let password = validate_utf8(buf)?;
|
|
||||||
if user.verify_passwd(&password) {
|
|
||||||
*handle = Handle::AwaitingNewPassword { uid }
|
|
||||||
} else {
|
|
||||||
*handle = Handle::AwaitingPasswordForPasswd { uid };
|
|
||||||
return Err(Error::new(EPERM));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Handle::AwaitingNewPassword { uid } => {
|
|
||||||
let mut users = AllUsers::authenticator(Config::default().writeable(true))
|
|
||||||
.map_err(|_| Error::new(ENOLCK))?;
|
|
||||||
let user = users
|
|
||||||
.get_mut_by_id(uid as usize)
|
|
||||||
.ok_or(Error::new(EEXIST))?;
|
|
||||||
|
|
||||||
let new_password = validate_utf8(buf)?;
|
|
||||||
if user.set_passwd(&new_password).is_ok() {
|
|
||||||
users.save().map_err(|_| Error::new(ENOLCK))?;
|
|
||||||
*handle = Handle::Placeholder
|
|
||||||
} else {
|
|
||||||
*handle = Handle::AwaitingNewPassword { uid };
|
|
||||||
return Err(Error::new(EPERM));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle::AwaitingNamespaceFetch { .. } => {
|
|
||||||
eprintln!("sudo: found namespace fetch handle with ID {id}");
|
|
||||||
return Err(Error::new(EBADFD));
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle::Placeholder => {
|
|
||||||
eprintln!("sudo: found placeholder handle with ID {id}");
|
|
||||||
return Err(Error::new(EBADFD));
|
|
||||||
}
|
|
||||||
|
|
||||||
Handle::SchemeRoot => {
|
|
||||||
eprintln!("sudo: found Scheme root handle with ID {id}");
|
|
||||||
return Err(Error::new(EBADFD));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(buf.len())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl Scheme {
|
|
||||||
fn on_close(&mut self, id: usize) {
|
|
||||||
self.handles.remove(&id);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn on_sendfd(&mut self, socket: &Socket, req: &SendFdRequest) -> Result<usize> {
|
|
||||||
let handle = self.handles.get_mut(&req.id()).ok_or(Error::new(EBADF))?;
|
|
||||||
match std::mem::replace(handle, Handle::Placeholder) {
|
|
||||||
Handle::AwaitingContextFd => {
|
|
||||||
let mut proc_fd = usize::MAX;
|
|
||||||
req.obtain_fd(
|
|
||||||
socket,
|
|
||||||
FobtainFdFlags::empty(),
|
|
||||||
std::slice::from_mut(&mut proc_fd),
|
|
||||||
)?;
|
|
||||||
let proc_fd = unsafe { OwnedFd::from_raw_fd(proc_fd as RawFd) };
|
|
||||||
|
|
||||||
let [ruid, euid, suid] = [0, 0, 0];
|
|
||||||
let [rgid, egid, sgid] = [0, 0, 0];
|
|
||||||
let mut payload = [0; size_of::<u32>() * 6];
|
|
||||||
plain::slice_from_mut_bytes(&mut payload)
|
|
||||||
.unwrap()
|
|
||||||
.copy_from_slice(&[ruid, euid, suid, rgid, egid, sgid]);
|
|
||||||
|
|
||||||
if let Err(err) = proc_call(
|
|
||||||
proc_fd.as_raw_fd() as usize,
|
|
||||||
&mut payload,
|
|
||||||
CallFlags::empty(),
|
|
||||||
&[ProcCall::SetResugid as u64],
|
|
||||||
) {
|
|
||||||
eprintln!("failed to setresugid: {err}");
|
|
||||||
}
|
|
||||||
|
|
||||||
*handle = Handle::AwaitingNamespaceFetch {
|
|
||||||
ns: libredox::Fd::new(
|
|
||||||
syscall::dup(libredox::call::getns().unwrap(), b"").unwrap(),
|
|
||||||
),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
old => {
|
|
||||||
*handle = old;
|
|
||||||
return Err(Error::new(EBADF));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn daemon_main() -> ! {
|
|
||||||
// TODO: Linux kernel audit-like logging?
|
|
||||||
let socket = Socket::create().expect("failed to open scheme socket");
|
|
||||||
|
|
||||||
let mut scheme = Scheme {
|
|
||||||
next_fd: 1,
|
|
||||||
handles: HashMap::new(),
|
|
||||||
};
|
|
||||||
let mut scheme_state = redox_scheme::scheme::SchemeState::new();
|
|
||||||
|
|
||||||
register_sync_scheme(&socket, "sudo", &mut scheme)
|
|
||||||
.expect("failed to register sudo scheme to namespace");
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let Some(req) = socket
|
|
||||||
.next_request(SignalBehavior::Restart)
|
|
||||||
.expect("failed to get request")
|
|
||||||
else {
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
|
|
||||||
let response = match req.kind() {
|
|
||||||
RequestKind::Call(call) => call.handle_sync(&mut scheme, &mut scheme_state),
|
|
||||||
RequestKind::SendFd(req) => Response::new(scheme.on_sendfd(&socket, &req), req),
|
|
||||||
RequestKind::OnClose { id } => {
|
|
||||||
scheme.on_close(id);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
_ => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
socket
|
|
||||||
.write_response(response, SignalBehavior::Restart)
|
|
||||||
.expect("sudo: scheme write failed");
|
|
||||||
}
|
|
||||||
std::process::exit(0)
|
|
||||||
}
|
|
||||||
@@ -1,205 +0,0 @@
|
|||||||
#[macro_use]
|
|
||||||
extern crate clap;
|
|
||||||
|
|
||||||
use std::process::exit;
|
|
||||||
|
|
||||||
use extra::option::OptionalExt;
|
|
||||||
use redox_users::{All, AllGroups, AllUsers, Config, GroupBuilder, UserBuilder};
|
|
||||||
use userutils::create_user_dir;
|
|
||||||
|
|
||||||
const _MAN_PAGE: &'static str = /* @MANSTART{useradd} */
|
|
||||||
r#"
|
|
||||||
NAME
|
|
||||||
useradd - add a new user
|
|
||||||
|
|
||||||
SYNOPSYS
|
|
||||||
useradd [ options ] LOGIN
|
|
||||||
useradd [ -h | --help ]
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
The useradd utility creates a new user based on
|
|
||||||
system defaults and values passed on the command line.
|
|
||||||
|
|
||||||
Useradd creates a new group for the user by default and
|
|
||||||
can also be instructed to create the user's home directory.
|
|
||||||
|
|
||||||
Note that useradd creates a new user with the password
|
|
||||||
unset (no login). This is better documented with the
|
|
||||||
redox_users crate.
|
|
||||||
|
|
||||||
OPTIONS
|
|
||||||
-h, --help
|
|
||||||
Display this help and exit.
|
|
||||||
|
|
||||||
-c, --comment
|
|
||||||
Any text string, usually used as the user's full name.
|
|
||||||
Historically known as the GECOS field
|
|
||||||
|
|
||||||
-d, --home-dir HOME_DIR
|
|
||||||
The new user will be created with HOME_DIR as their home
|
|
||||||
directory. The default value is LOGIN prepended with "/home/".
|
|
||||||
This flag DOES NOT create the home directory. See --create-home
|
|
||||||
|
|
||||||
-g, --gid GID
|
|
||||||
The group id to use when creating the default login group. This value
|
|
||||||
must not be in use and must be non-negative. The default is to pick the
|
|
||||||
smallest available group id between values defined in redox_users.
|
|
||||||
|
|
||||||
-m, --create-home
|
|
||||||
Creates the user's home directory if it does not already exist.
|
|
||||||
|
|
||||||
This option is not enabled by default. This option must be specified
|
|
||||||
for a home directory to be created. If not set, the user's home dir is
|
|
||||||
set to "/"
|
|
||||||
|
|
||||||
-N, --no-user-group
|
|
||||||
Do not attempt to create the user's user group. Instead, the groupid
|
|
||||||
is set to 99 ("nobody"). -N and -g are mutually exclusive.
|
|
||||||
|
|
||||||
-s, --shell SHELL
|
|
||||||
The path to the user's default login shell. If not specified, the
|
|
||||||
default shell is set as "/bin/ion"
|
|
||||||
|
|
||||||
-u, --uid UID
|
|
||||||
The user id to use. This value must not be in use and must be
|
|
||||||
non-negative. The default is to pick the smallest available
|
|
||||||
user id between the defaults defined in redox_users
|
|
||||||
|
|
||||||
AUTHORS
|
|
||||||
Written by Wesley Hershberger.
|
|
||||||
"#; /* @MANEND */
|
|
||||||
const DEFAULT_SHELL: &'static str = "/bin/ion";
|
|
||||||
const DEFAULT_HOME: &'static str = "/home";
|
|
||||||
const DEFAULT_NO_GROUP: &'static str = "nobody";
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let args = clap_app!(useradd =>
|
|
||||||
(author: "Wesley Hershberger")
|
|
||||||
(about: "Add users based on the system's redox_users backend")
|
|
||||||
(@arg LOGIN:
|
|
||||||
+required
|
|
||||||
"Add user LOGIN")
|
|
||||||
(@arg COMMENT:
|
|
||||||
-c --comment
|
|
||||||
+takes_value
|
|
||||||
"Set user description (GECOS field)")
|
|
||||||
(@arg HOME_DIR:
|
|
||||||
-d --("home-dir")
|
|
||||||
+takes_value
|
|
||||||
"Set LOGIN's home dir to HOME_DIR (does not create directory)")
|
|
||||||
(@arg CREATE_HOME:
|
|
||||||
-m --("create-home")
|
|
||||||
"Create the user's home directory")
|
|
||||||
(@arg SHELL:
|
|
||||||
-s --shell
|
|
||||||
+takes_value
|
|
||||||
"Set user's default login shell")
|
|
||||||
(@arg GID:
|
|
||||||
-g --gid
|
|
||||||
+takes_value
|
|
||||||
"Set LOGIN's primary group id. Positive integer and must not be in use.")
|
|
||||||
(@arg NO_USER_GROUP:
|
|
||||||
-N --("no-user-group")
|
|
||||||
conflicts_with[GID]
|
|
||||||
"Do not create primary user group (set gid to 99, \"nobody\")")
|
|
||||||
(@arg UID:
|
|
||||||
-u --uid
|
|
||||||
+takes_value
|
|
||||||
"Set LOGIN's user id. Positive ineger and must not be in use.")
|
|
||||||
)
|
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
// unwrap is safe because of "+required". clap-rs is cool...
|
|
||||||
let login = args.value_of("LOGIN").unwrap();
|
|
||||||
|
|
||||||
let mut sys_users =
|
|
||||||
AllUsers::authenticator(Config::default().writeable(true)).unwrap_or_exit(1);
|
|
||||||
let mut sys_groups = AllGroups::new(Config::default().writeable(true)).unwrap_or_exit(1);
|
|
||||||
|
|
||||||
let uid = match args.value_of("UID") {
|
|
||||||
Some(uid) => {
|
|
||||||
let id = uid.parse::<usize>().unwrap_or_exit(1);
|
|
||||||
if let Some(_user) = sys_users.get_by_id(id) {
|
|
||||||
eprintln!("useradd: userid already in use: {}", id);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
id
|
|
||||||
}
|
|
||||||
None => sys_users.get_unique_id().unwrap_or_else(|| {
|
|
||||||
eprintln!("useradd: no available uid");
|
|
||||||
exit(1);
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
let gid = if args.is_present("NO_USER_GROUP") {
|
|
||||||
let nobody = sys_groups
|
|
||||||
.get_mut_by_name(DEFAULT_NO_GROUP)
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
eprintln!("useradd: group not found: {}", DEFAULT_NO_GROUP);
|
|
||||||
exit(1)
|
|
||||||
});
|
|
||||||
nobody.users.push(login.to_string());
|
|
||||||
99
|
|
||||||
} else {
|
|
||||||
let id = match args.value_of("GID") {
|
|
||||||
Some(id) => {
|
|
||||||
let id = id.parse::<usize>().unwrap_or_exit(1);
|
|
||||||
if let Some(_group) = sys_groups.get_by_id(id) {
|
|
||||||
eprintln!("useradd: group already exists with gid: {}", id);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
id
|
|
||||||
}
|
|
||||||
None => sys_groups.get_unique_id().unwrap_or_else(|| {
|
|
||||||
eprintln!("useradd: no available gid");
|
|
||||||
exit(1);
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
sys_groups
|
|
||||||
.add_group(GroupBuilder::new(login).gid(id).user(login))
|
|
||||||
.unwrap_or_else(|err| {
|
|
||||||
eprintln!("useradd: {}: {}", err, login);
|
|
||||||
exit(1);
|
|
||||||
});
|
|
||||||
id
|
|
||||||
};
|
|
||||||
|
|
||||||
let gecos = args.value_of("COMMENT").unwrap_or(login);
|
|
||||||
|
|
||||||
//Ugly way to satisfy the borrow checker...
|
|
||||||
let mut sys_homes = String::from(DEFAULT_HOME);
|
|
||||||
let userhome = args.value_of("HOME_DIR").unwrap_or_else(|| {
|
|
||||||
if args.is_present("CREATE_HOME") {
|
|
||||||
sys_homes.push_str("/");
|
|
||||||
sys_homes.push_str(&login);
|
|
||||||
sys_homes.as_str()
|
|
||||||
} else {
|
|
||||||
"/"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
let shell = args.value_of("SHELL").unwrap_or(DEFAULT_SHELL);
|
|
||||||
|
|
||||||
let user = UserBuilder::new(login)
|
|
||||||
.uid(uid)
|
|
||||||
.gid(gid)
|
|
||||||
.name(gecos)
|
|
||||||
.home(userhome)
|
|
||||||
.shell(shell);
|
|
||||||
sys_users.add_user(user).unwrap_or_else(|err| {
|
|
||||||
eprintln!("useradd: {}: {}", err, login);
|
|
||||||
exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Make sure to try and create the user/groups before we create
|
|
||||||
// their home, that way we get a permissions error that makes
|
|
||||||
// more sense
|
|
||||||
sys_groups.save().unwrap_or_exit(1);
|
|
||||||
sys_users.save().unwrap_or_exit(1);
|
|
||||||
|
|
||||||
if args.is_present("CREATE_HOME") {
|
|
||||||
//Shouldn't ever error...
|
|
||||||
let user = sys_users.get_by_id(uid).unwrap_or_exit(1);
|
|
||||||
create_user_dir(user, userhome).unwrap_or_exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
#[macro_use]
|
|
||||||
extern crate clap;
|
|
||||||
|
|
||||||
use std::fs::remove_dir;
|
|
||||||
use std::process::exit;
|
|
||||||
|
|
||||||
use extra::option::OptionalExt;
|
|
||||||
use redox_users::{All, AllGroups, AllUsers, Config};
|
|
||||||
use userutils::AllGroupsExt;
|
|
||||||
|
|
||||||
const _MAN_PAGE: &'static str = /* @MANSTART{userdel} */
|
|
||||||
r#"
|
|
||||||
NAME
|
|
||||||
userdel - modify system files to delete users
|
|
||||||
|
|
||||||
SYNOPSYS
|
|
||||||
userdel [ options ] LOGIN
|
|
||||||
userdel [ -h | --help ]
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
userdel removes users from whatever backend is employed by
|
|
||||||
the system's redox_users. The utility removes the user from
|
|
||||||
all groups of which they are a member.
|
|
||||||
|
|
||||||
It can also be used to manage removal of home directories.
|
|
||||||
|
|
||||||
OPTIONS
|
|
||||||
-h, --help
|
|
||||||
Print this help page and exit.
|
|
||||||
|
|
||||||
-r, --remove
|
|
||||||
The user's home directory and all files inside will be
|
|
||||||
removed.
|
|
||||||
|
|
||||||
AUTHORS
|
|
||||||
Wesley Hershberger.
|
|
||||||
"#; /* @MANEND */
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let args = clap_app!(userdel =>
|
|
||||||
(author: "Wesley Hershberger")
|
|
||||||
(about: "Removes system users using redox_users")
|
|
||||||
(@arg LOGIN: +required "Remove user LOGIN")
|
|
||||||
(@arg REMOVE: -r --remove "Remove the user's home and all files and directories inside")
|
|
||||||
)
|
|
||||||
.get_matches();
|
|
||||||
|
|
||||||
let login = args.value_of("LOGIN").unwrap();
|
|
||||||
|
|
||||||
let mut sys_users =
|
|
||||||
AllUsers::authenticator(Config::default().writeable(true)).unwrap_or_exit(1);
|
|
||||||
let mut sys_groups = AllGroups::new(Config::default().writeable(true)).unwrap_or_exit(1);
|
|
||||||
{
|
|
||||||
sys_groups.remove_user_from_all_groups(login);
|
|
||||||
|
|
||||||
if args.is_present("REMOVE") {
|
|
||||||
let user = sys_users.get_by_name(login).unwrap_or_else(|| {
|
|
||||||
eprintln!("userdel: user does not exist: {}", login);
|
|
||||||
exit(1);
|
|
||||||
});
|
|
||||||
remove_dir(&user.home).unwrap_or_exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sys_users.remove_by_name(login.to_string());
|
|
||||||
|
|
||||||
sys_groups.save().unwrap_or_exit(1);
|
|
||||||
sys_users.save().unwrap_or_exit(1);
|
|
||||||
}
|
|
||||||
@@ -1,196 +0,0 @@
|
|||||||
#[macro_use]
|
|
||||||
extern crate clap;
|
|
||||||
|
|
||||||
use std::fs::{remove_dir, rename};
|
|
||||||
use std::process::exit;
|
|
||||||
|
|
||||||
use extra::option::OptionalExt;
|
|
||||||
use redox_users::{All, AllGroups, AllUsers, Config};
|
|
||||||
use userutils::{AllGroupsExt, create_user_dir};
|
|
||||||
|
|
||||||
const _MAN_PAGE: &'static str = /* @MANSTART{usermod} */
|
|
||||||
r#"
|
|
||||||
NAME
|
|
||||||
usermod - modify user information
|
|
||||||
|
|
||||||
SYNOPSYS
|
|
||||||
usermod [ options ] LOGIN
|
|
||||||
usermod [ -h | --help ]
|
|
||||||
|
|
||||||
DESCRIPTION
|
|
||||||
The usermod utility can be used to modify user information.
|
|
||||||
|
|
||||||
This utility uses the redox_users API, so the backend is whatever
|
|
||||||
backend in use on the system for that API at the time.
|
|
||||||
|
|
||||||
See passwd for setting user passwords.
|
|
||||||
|
|
||||||
OPTIONS
|
|
||||||
-h, --help
|
|
||||||
Display this help and exit.
|
|
||||||
|
|
||||||
-c, --comment COMMENT
|
|
||||||
The comment field (or GECOS, historically) for the user. This
|
|
||||||
is typically the full name of the user, although sometimes it
|
|
||||||
includes an e-mail.
|
|
||||||
|
|
||||||
-d, --home-dir HOME_DIR
|
|
||||||
Sets the home directory to HOME_DIR and creates the directory.
|
|
||||||
See -m for move
|
|
||||||
|
|
||||||
-m, --move-home
|
|
||||||
Moves the the user's old home directory into the home directory
|
|
||||||
specified by --home-dir. Has no effect if passed without --home-dir
|
|
||||||
|
|
||||||
-G, --append-groups GROUP[,GROUP, ...]
|
|
||||||
Add this user to GROUP groups. This does not remove the user from
|
|
||||||
any group of which they are already a member.
|
|
||||||
|
|
||||||
-S, --set-groups GROUP[,GROUP, ...]
|
|
||||||
Remove the user from all groups of which they are a part and add
|
|
||||||
them to GROUP groups.
|
|
||||||
|
|
||||||
-g, --gid GID
|
|
||||||
Set the user's primary group id. If the group does not exist,
|
|
||||||
a warning is issued and no changes are applied.
|
|
||||||
|
|
||||||
-l, --login NEW_LOGIN
|
|
||||||
Set the new login name for the user. Must not be in use.
|
|
||||||
|
|
||||||
-s, --shell SHELL
|
|
||||||
Set the user's login shell as SHELL. This must be a full path.
|
|
||||||
|
|
||||||
-u, --uid UID
|
|
||||||
Set the user's user id. If another user's userid is the same as
|
|
||||||
UID, a warning is issued and no changes are applied. Note that
|
|
||||||
changing the value of the user's userid may have unexpected consequences.
|
|
||||||
|
|
||||||
AUTHORS
|
|
||||||
Written by Wesley Hershberger.
|
|
||||||
"#; /* @MANEND */
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let args = clap_app!(usermod =>
|
|
||||||
(author: "Wesley Hershberger")
|
|
||||||
(about: "Modify users according to the system's redox_users backend")
|
|
||||||
(@arg LOGIN:
|
|
||||||
+required
|
|
||||||
"Apply modifications to LOGIN")
|
|
||||||
(@arg COMMENT:
|
|
||||||
-c --comment
|
|
||||||
+takes_value
|
|
||||||
"Set LOGIN's description (GECOS field)")
|
|
||||||
(@arg HOME_DIR:
|
|
||||||
-d --("home-dir")
|
|
||||||
+takes_value
|
|
||||||
"Create and set LOGIN's home directory")
|
|
||||||
(@arg MOVE_HOME:
|
|
||||||
-m --("move-home")
|
|
||||||
requires[HOME_DIR]
|
|
||||||
"Move LOGIN's old home to HOME_DIR (see --home-dir) instead of creating it. Requires -d")
|
|
||||||
(@arg APPEND_GROUPS:
|
|
||||||
-G --("append-groups")
|
|
||||||
+takes_value conflicts_with[SET_GROUPS]
|
|
||||||
"Add user to groups specified (comma separated list, see man page)")
|
|
||||||
(@arg SET_GROUPS:
|
|
||||||
-S --("set-groups")
|
|
||||||
+takes_value conflicts_with[APPEND_GROUPS]
|
|
||||||
"Set LOGIN's groups as specified (truncates existing, see man page)")
|
|
||||||
(@arg GID:
|
|
||||||
-g --gid
|
|
||||||
+takes_value
|
|
||||||
"Set LOGIN's primary group id. Group must exist")
|
|
||||||
(@arg NEW_LOGIN:
|
|
||||||
-l --login
|
|
||||||
+takes_value
|
|
||||||
"Set LOGIN's name to NEW_LOGIN")
|
|
||||||
(@arg SHELL:
|
|
||||||
-s --shell
|
|
||||||
+takes_value
|
|
||||||
"Set LOGIN's default login shell")
|
|
||||||
(@arg UID:
|
|
||||||
-u --uid
|
|
||||||
+takes_value
|
|
||||||
"Set LOGIN's user id. See man page for details")
|
|
||||||
).get_matches();
|
|
||||||
|
|
||||||
let login = args.value_of("LOGIN").unwrap();
|
|
||||||
|
|
||||||
//TODO: Does not always need shadowfile access
|
|
||||||
let mut sys_users =
|
|
||||||
AllUsers::authenticator(Config::default().writeable(true)).unwrap_or_exit(1);
|
|
||||||
let mut sys_groups;
|
|
||||||
|
|
||||||
if let Some(new_groups) = args.value_of("SET_GROUPS") {
|
|
||||||
sys_groups = AllGroups::new(Config::default().writeable(true)).unwrap_or_exit(1);
|
|
||||||
sys_groups.remove_user_from_all_groups(login);
|
|
||||||
sys_groups
|
|
||||||
.add_user_to_groups(login, new_groups.split(',').collect())
|
|
||||||
.unwrap_or_exit(1);
|
|
||||||
sys_groups.save().unwrap_or_exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(new_groups) = args.value_of("APPEND_GROUPS") {
|
|
||||||
sys_groups = AllGroups::new(Config::default().writeable(true)).unwrap_or_exit(1);
|
|
||||||
sys_groups
|
|
||||||
.add_user_to_groups(login, new_groups.split(',').collect())
|
|
||||||
.unwrap_or_exit(1);
|
|
||||||
sys_groups.save().unwrap_or_exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
let uid = args.value_of("UID").map(|uid| {
|
|
||||||
let uid = uid.parse::<usize>().unwrap_or_exit(1);
|
|
||||||
if let Some(_user) = sys_users.get_by_id(uid) {
|
|
||||||
eprintln!("usermod: userid already in use: {}", uid);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
uid
|
|
||||||
});
|
|
||||||
|
|
||||||
{
|
|
||||||
let user = sys_users.get_mut_by_name(&login).unwrap_or_else(|| {
|
|
||||||
eprintln!("usermod: user \"{}\" not found", login);
|
|
||||||
exit(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(gecos) = args.value_of("COMMENT") {
|
|
||||||
user.name = gecos.to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(new_login) = args.value_of("NEW_LOGIN") {
|
|
||||||
user.user = new_login.to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(shell) = args.value_of("SHELL") {
|
|
||||||
user.shell = shell.to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(home) = args.value_of("HOME_DIR") {
|
|
||||||
if args.is_present("MOVE_HOME") {
|
|
||||||
rename(&user.home, &home).unwrap_or_exit(1);
|
|
||||||
} else {
|
|
||||||
create_user_dir(user, &home).unwrap_or_exit(1);
|
|
||||||
remove_dir(&user.home).unwrap_or_exit(1);
|
|
||||||
}
|
|
||||||
user.home = home.to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(uid) = uid {
|
|
||||||
user.uid = uid;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(gid) = args.value_of("GID") {
|
|
||||||
sys_groups = AllGroups::new(Config::default()).unwrap_or_exit(1);
|
|
||||||
let gid = gid.parse::<usize>().unwrap_or_exit(1);
|
|
||||||
|
|
||||||
if let Some(_group) = sys_groups.get_by_id(gid) {
|
|
||||||
user.gid = gid;
|
|
||||||
} else {
|
|
||||||
eprintln!("usermod: no group found for id: {}", gid);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sys_users.save().unwrap_or_exit(1);
|
|
||||||
}
|
|
||||||
+121
@@ -0,0 +1,121 @@
|
|||||||
|
use crate::os::{Os, OsKey};
|
||||||
|
|
||||||
|
fn edit_banner(os: &impl Os) {
|
||||||
|
os.clear_text();
|
||||||
|
println!("--- Redox Bootloader Environment Editor ---");
|
||||||
|
println!("ENTER twice to boot. UP/DOWN to edit lines.");
|
||||||
|
println!("-------------------------------------------");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn edit_env(os: &impl Os, env_ptr: *mut u8, env_size: &mut usize, max_size: usize) {
|
||||||
|
edit_banner(os);
|
||||||
|
|
||||||
|
let env_slice = unsafe { core::slice::from_raw_parts_mut(env_ptr, max_size) };
|
||||||
|
// counting at line index
|
||||||
|
let mut cursor = 0xFFF;
|
||||||
|
// position at current line, not including LF
|
||||||
|
let mut cursor_start = 0;
|
||||||
|
let mut cursor_end = 0;
|
||||||
|
let original_size = *env_size;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
os.set_text_position(0, 4);
|
||||||
|
|
||||||
|
let mut iline = 0;
|
||||||
|
for i in 0..*env_size {
|
||||||
|
let c = env_slice[i] as char;
|
||||||
|
if c == '\n' {
|
||||||
|
os.set_text_highlight(iline == cursor);
|
||||||
|
print!(" ");
|
||||||
|
os.set_text_highlight(false);
|
||||||
|
print!("\n");
|
||||||
|
iline += 1;
|
||||||
|
if iline == cursor {
|
||||||
|
cursor_start = i + 1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print!("{}", c);
|
||||||
|
}
|
||||||
|
if iline == cursor {
|
||||||
|
cursor_end = i + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if cursor > iline {
|
||||||
|
cursor = iline;
|
||||||
|
// update cursors, should never hang
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
os.set_text_highlight(iline == cursor);
|
||||||
|
print!(" ");
|
||||||
|
os.set_text_highlight(false);
|
||||||
|
|
||||||
|
match os.get_key() {
|
||||||
|
OsKey::Enter => {
|
||||||
|
if cursor_start == cursor_end {
|
||||||
|
// blank line to boot
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if *env_size < max_size - 1 {
|
||||||
|
if *env_size == max_size {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for i in (cursor_end..*env_size).rev() {
|
||||||
|
env_slice[i + 1] = env_slice[i];
|
||||||
|
}
|
||||||
|
env_slice[cursor_end] = b'\n';
|
||||||
|
*env_size += 1;
|
||||||
|
cursor += 1;
|
||||||
|
edit_banner(os);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OsKey::Backspace => {
|
||||||
|
if cursor_end == 0 || *env_size == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if cursor_start == cursor_end && iline > 0 {
|
||||||
|
iline -= 1;
|
||||||
|
}
|
||||||
|
for i in cursor_end..*env_size {
|
||||||
|
env_slice[i - 1] = env_slice[i];
|
||||||
|
}
|
||||||
|
*env_size -= 1;
|
||||||
|
edit_banner(os);
|
||||||
|
}
|
||||||
|
OsKey::Up => {
|
||||||
|
if cursor > 0 {
|
||||||
|
cursor -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OsKey::Down => {
|
||||||
|
cursor += 1;
|
||||||
|
}
|
||||||
|
OsKey::Char(c) => {
|
||||||
|
if *env_size == max_size {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for i in (cursor_end..*env_size).rev() {
|
||||||
|
env_slice[i + 1] = env_slice[i];
|
||||||
|
}
|
||||||
|
env_slice[cursor_end] = c as u8;
|
||||||
|
*env_size += 1;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if *env_size == 0 || env_slice[*env_size - 1] != b'\n' {
|
||||||
|
if *env_size < max_size {
|
||||||
|
env_slice[*env_size] = b'\n';
|
||||||
|
*env_size += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if *env_size < original_size {
|
||||||
|
for i in (*env_size..original_size).rev() {
|
||||||
|
env_slice[i] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
println!("\nBooting...");
|
||||||
|
}
|
||||||
-92
@@ -1,92 +0,0 @@
|
|||||||
//! Redox OS user and group utilities.
|
|
||||||
//!
|
|
||||||
//! The `userutils` crate contains the utilities for dealing with users and groups in Redox OS.
|
|
||||||
//! They are heavily influenced by UNIX and are, when needed, tailored to specific Redox use cases.
|
|
||||||
//!
|
|
||||||
//! These implementations strive to be as simple as possible drawing particular
|
|
||||||
//! inspiration by BSD systems. They are indeed small, by choice.
|
|
||||||
//!
|
|
||||||
//! The included utilities are:
|
|
||||||
//!
|
|
||||||
//! - `getty`: Used by `init(8)` to open and initialize the TTY line, read a login name and invoke `login(1)`.
|
|
||||||
//! - `id`: Displays user identity.
|
|
||||||
//! - `login`: Allows users to into the system.
|
|
||||||
//! - `passwd`: Allows users to modify their passwords.
|
|
||||||
//! - `su`: Allows users to substitute identity.
|
|
||||||
//! - `sudo`: Enables users to execute a command as another user.
|
|
||||||
//! - `whoami`: Display effective user ID.
|
|
||||||
|
|
||||||
use std::io::Result as IoResult;
|
|
||||||
|
|
||||||
use libredox::call::{fchown, open};
|
|
||||||
use libredox::error::Result as SysResult;
|
|
||||||
use libredox::flag::{O_CLOEXEC, O_CREAT, O_DIRECTORY};
|
|
||||||
use redox_users::{All, AllGroups, Error, Result, User, auth};
|
|
||||||
|
|
||||||
const DEFAULT_MODE: u16 = 0o700;
|
|
||||||
|
|
||||||
// Not the prettiest thing in the world, but some functionality here makes
|
|
||||||
// some of the utils much less gross
|
|
||||||
pub trait AllGroupsExt {
|
|
||||||
fn add_user_to_groups(&mut self, login: &str, groups: Vec<&str>) -> Result<()>;
|
|
||||||
fn remove_user_from_all_groups(&mut self, login: &str);
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AllGroupsExt for AllGroups {
|
|
||||||
// new_groups is a comma separated list of groupnames
|
|
||||||
fn add_user_to_groups(&mut self, login: &str, new_groups: Vec<&str>) -> Result<()> {
|
|
||||||
for groupname in new_groups {
|
|
||||||
let group = match self.get_mut_by_name(groupname) {
|
|
||||||
Some(group) => group,
|
|
||||||
None => return Err(Error::UserNotFound),
|
|
||||||
};
|
|
||||||
group.users.push(login.to_string());
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove a user from all groups of which they are a member
|
|
||||||
fn remove_user_from_all_groups(&mut self, login: &str) {
|
|
||||||
for group in self.iter_mut() {
|
|
||||||
let op_pos = group.users.iter().position(|username| username == login);
|
|
||||||
if let Some(indx) = op_pos {
|
|
||||||
group.users.remove(indx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Spawns a shell for the given `User`.
|
|
||||||
///
|
|
||||||
/// This function wraps the shell_cmd function of the User struct
|
|
||||||
/// from redox_users and manages the child process. It is a blocking
|
|
||||||
/// operation.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use redox_users::AllUsers;
|
|
||||||
///
|
|
||||||
/// let sys_users = AllUsers::new().unwrap();
|
|
||||||
/// let user = sys_users.get_by_name("goyox86");
|
|
||||||
/// spawn_shell(user).unwrap();
|
|
||||||
/// ```
|
|
||||||
pub fn spawn_shell<T: Default>(user: &User<T>) -> IoResult<i32> {
|
|
||||||
let mut command = user.shell_cmd();
|
|
||||||
|
|
||||||
let mut child = command.spawn()?;
|
|
||||||
match child.wait()?.code() {
|
|
||||||
Some(code) => Ok(code),
|
|
||||||
None => Ok(1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a directory with 700 user:user permissions
|
|
||||||
pub fn create_user_dir<T>(user: &User<auth::Full>, dir: T) -> SysResult<()>
|
|
||||||
where
|
|
||||||
T: AsRef<str> + std::convert::AsRef<[u8]>,
|
|
||||||
{
|
|
||||||
let fd = open(dir, O_CREAT | O_DIRECTORY | O_CLOEXEC, DEFAULT_MODE)?;
|
|
||||||
fchown(fd, user.uid as u32, user.gid as u32)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
use log::{LevelFilter, Log, Metadata, Record};
|
||||||
|
|
||||||
|
pub static LOGGER: Logger = Logger;
|
||||||
|
|
||||||
|
pub struct Logger;
|
||||||
|
|
||||||
|
impl Logger {
|
||||||
|
pub fn init(&'static self) {
|
||||||
|
log::set_logger(self).unwrap();
|
||||||
|
log::set_max_level(LevelFilter::Info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Log for Logger {
|
||||||
|
fn enabled(&self, _metadata: &Metadata) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn log(&self, record: &Record) {
|
||||||
|
if self.enabled(record.metadata()) {
|
||||||
|
println!("{} - {}", record.level(), record.args());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&self) {}
|
||||||
|
}
|
||||||
+761
@@ -0,0 +1,761 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![cfg_attr(any(target_arch = "riscv64", target_os = "uefi"), no_main)]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
#[cfg(any(target_arch = "riscv64", target_os = "uefi"))]
|
||||||
|
#[macro_use]
|
||||||
|
extern crate uefi_std as std;
|
||||||
|
|
||||||
|
use alloc::{format, string::String, vec::Vec};
|
||||||
|
use core::{
|
||||||
|
cmp,
|
||||||
|
convert::TryFrom,
|
||||||
|
fmt::{self, Write},
|
||||||
|
mem, ptr, slice, str,
|
||||||
|
};
|
||||||
|
use redoxfs::{Disk, Node, TreeData};
|
||||||
|
|
||||||
|
use self::arch::{paging_create, paging_framebuffer};
|
||||||
|
use self::os::{Os, OsHwDesc, OsKey, OsMemoryEntry, OsMemoryKind, OsVideoMode};
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod os;
|
||||||
|
|
||||||
|
mod arch;
|
||||||
|
mod editor;
|
||||||
|
mod logger;
|
||||||
|
mod serial_16550;
|
||||||
|
|
||||||
|
const KIBI: usize = 1024;
|
||||||
|
const MIBI: usize = KIBI * KIBI;
|
||||||
|
|
||||||
|
//TODO: allocate this in a more reasonable manner
|
||||||
|
static mut AREAS: [OsMemoryEntry; 1024] = [OsMemoryEntry {
|
||||||
|
base: 0,
|
||||||
|
size: 0,
|
||||||
|
kind: OsMemoryKind::Null,
|
||||||
|
}; 1024];
|
||||||
|
static mut AREAS_LEN: usize = 0;
|
||||||
|
|
||||||
|
pub fn area_add(area: OsMemoryEntry) {
|
||||||
|
#[allow(static_mut_refs)]
|
||||||
|
unsafe {
|
||||||
|
for existing_area in &mut AREAS[0..AREAS_LEN] {
|
||||||
|
if existing_area.kind == area.kind {
|
||||||
|
if existing_area.base.unchecked_add(existing_area.size) == area.base {
|
||||||
|
existing_area.size += area.size;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if area.base.unchecked_add(area.size) == existing_area.base {
|
||||||
|
existing_area.size += area.size;
|
||||||
|
existing_area.base = area.base;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*AREAS.get_mut(AREAS_LEN).expect("AREAS overflowed!") = area;
|
||||||
|
AREAS_LEN += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub static mut KERNEL_64BIT: bool = false;
|
||||||
|
|
||||||
|
pub static mut LIVE_OPT: Option<(u64, &'static [u8])> = None;
|
||||||
|
|
||||||
|
struct SliceWriter<'a> {
|
||||||
|
slice: &'a mut [u8],
|
||||||
|
i: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Write for SliceWriter<'a> {
|
||||||
|
fn write_str(&mut self, s: &str) -> fmt::Result {
|
||||||
|
for b in s.bytes() {
|
||||||
|
if let Some(slice_b) = self.slice.get_mut(self.i) {
|
||||||
|
*slice_b = b;
|
||||||
|
self.i += 1;
|
||||||
|
} else {
|
||||||
|
return Err(fmt::Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[repr(C, packed(8))]
|
||||||
|
pub struct KernelArgs {
|
||||||
|
kernel_base: u64,
|
||||||
|
kernel_size: u64,
|
||||||
|
stack_base: u64,
|
||||||
|
stack_size: u64,
|
||||||
|
env_base: u64,
|
||||||
|
env_size: u64,
|
||||||
|
|
||||||
|
/// The base pointer to the saved RSDP.
|
||||||
|
///
|
||||||
|
/// This field can be NULL, and if so, the system has not booted with UEFI or in some other way
|
||||||
|
/// retrieved the RSDPs. The kernel or a userspace driver will thus try searching the BIOS
|
||||||
|
/// memory instead. On UEFI systems, searching is not guaranteed to actually work though.
|
||||||
|
acpi_rsdp_base: u64,
|
||||||
|
/// The size of the RSDP region.
|
||||||
|
acpi_rsdp_size: u64,
|
||||||
|
|
||||||
|
areas_base: u64,
|
||||||
|
areas_size: u64,
|
||||||
|
|
||||||
|
bootstrap_base: u64,
|
||||||
|
bootstrap_size: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn select_mode(
|
||||||
|
os: &impl Os,
|
||||||
|
output_i: usize,
|
||||||
|
live: &mut bool,
|
||||||
|
edit_env: &mut bool,
|
||||||
|
) -> Option<OsVideoMode> {
|
||||||
|
const DEFAULT_WIDTH: u32 = 1280;
|
||||||
|
const DEFAULT_HEIGHT: u32 = 720;
|
||||||
|
const AUTOBOOT_SECONDS: usize = 5;
|
||||||
|
|
||||||
|
let mut modes = Vec::new();
|
||||||
|
for mode in os.video_modes(output_i) {
|
||||||
|
let mut aspect_w = mode.width;
|
||||||
|
let mut aspect_h = mode.height;
|
||||||
|
for i in 2..cmp::min(aspect_w / 2, aspect_h / 2) {
|
||||||
|
while aspect_w % i == 0 && aspect_h % i == 0 {
|
||||||
|
aspect_w /= i;
|
||||||
|
aspect_h /= i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modes.push((
|
||||||
|
mode,
|
||||||
|
format!(
|
||||||
|
"{:>4}x{:<4} {:>3}:{:<3}",
|
||||||
|
mode.width, mode.height, aspect_w, aspect_h
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
if modes.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort modes by pixel area, reversed
|
||||||
|
modes.sort_by(|a, b| (b.0.width * b.0.height).cmp(&(a.0.width * a.0.height)));
|
||||||
|
|
||||||
|
// Set selected based on Red Bear default resolution first, then best resolution fallback
|
||||||
|
print!("Output {}", output_i);
|
||||||
|
let mut selected = modes.first().map_or(0, |x| x.0.id);
|
||||||
|
let mut selected_from_default = false;
|
||||||
|
for (mode, _text) in modes.iter() {
|
||||||
|
if mode.width == DEFAULT_WIDTH && mode.height == DEFAULT_HEIGHT {
|
||||||
|
selected = mode.id;
|
||||||
|
selected_from_default = true;
|
||||||
|
print!(", default resolution: {}x{}", DEFAULT_WIDTH, DEFAULT_HEIGHT);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !selected_from_default {
|
||||||
|
if let Some((best_width, best_height)) = os.best_resolution(output_i) {
|
||||||
|
print!(", best resolution: {}x{}", best_width, best_height);
|
||||||
|
for (mode, _text) in modes.iter() {
|
||||||
|
if mode.width == best_width && mode.height == best_height {
|
||||||
|
selected = mode.id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!();
|
||||||
|
|
||||||
|
println!("Arrow keys and enter select mode");
|
||||||
|
let live_mode = os.get_text_position();
|
||||||
|
if *live {
|
||||||
|
println!("Press l to disable live mode");
|
||||||
|
} else {
|
||||||
|
println!("Press l to enable live mode");
|
||||||
|
}
|
||||||
|
println!("Press e to edit boot environment");
|
||||||
|
println!(
|
||||||
|
"Autobooting default mode in {} seconds (press any key to cancel countdown)",
|
||||||
|
AUTOBOOT_SECONDS
|
||||||
|
);
|
||||||
|
println!();
|
||||||
|
print!(" ");
|
||||||
|
|
||||||
|
let (off_x, off_y) = os.get_text_position();
|
||||||
|
let rows = 12;
|
||||||
|
let mut mode_opt = None;
|
||||||
|
let countdown_y = off_y.saturating_sub(2);
|
||||||
|
let mut countdown = AUTOBOOT_SECONDS;
|
||||||
|
let mut countdown_active = true;
|
||||||
|
while !modes.is_empty() {
|
||||||
|
let mut row = 0;
|
||||||
|
let mut col = 0;
|
||||||
|
for (mode, text) in modes.iter() {
|
||||||
|
if row >= rows {
|
||||||
|
col += 1;
|
||||||
|
row = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
os.set_text_position(off_x + col * 20, off_y + row);
|
||||||
|
os.set_text_highlight(mode.id == selected);
|
||||||
|
|
||||||
|
print!("{}", text);
|
||||||
|
|
||||||
|
row += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
os.set_text_position(0, countdown_y);
|
||||||
|
os.set_text_highlight(false);
|
||||||
|
if countdown_active {
|
||||||
|
println!(
|
||||||
|
"Autobooting default mode in {} seconds (press any key to cancel countdown)",
|
||||||
|
countdown
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
println!("Manual mode selection active. Press Enter to boot selected mode. ");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read keypress
|
||||||
|
match if countdown_active {
|
||||||
|
os.get_key_timeout(1000)
|
||||||
|
} else {
|
||||||
|
os.get_key()
|
||||||
|
} {
|
||||||
|
OsKey::Timeout => {
|
||||||
|
if countdown_active {
|
||||||
|
if countdown == 0 {
|
||||||
|
if let Some(mode_i) = modes.iter().position(|x| x.0.id == selected) {
|
||||||
|
if let Some((mode, _text)) = modes.get(mode_i) {
|
||||||
|
mode_opt = Some(*mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
countdown = countdown.saturating_sub(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OsKey::Left => {
|
||||||
|
countdown_active = false;
|
||||||
|
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) {
|
||||||
|
if mode_i < rows {
|
||||||
|
while mode_i < modes.len() {
|
||||||
|
mode_i += rows;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mode_i -= rows;
|
||||||
|
if let Some(new) = modes.get(mode_i) {
|
||||||
|
selected = new.0.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OsKey::Right => {
|
||||||
|
countdown_active = false;
|
||||||
|
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) {
|
||||||
|
mode_i += rows;
|
||||||
|
if mode_i >= modes.len() {
|
||||||
|
mode_i %= rows;
|
||||||
|
}
|
||||||
|
if let Some(new) = modes.get(mode_i) {
|
||||||
|
selected = new.0.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OsKey::Up => {
|
||||||
|
countdown_active = false;
|
||||||
|
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) {
|
||||||
|
if mode_i % rows == 0 {
|
||||||
|
mode_i += rows;
|
||||||
|
if mode_i > modes.len() {
|
||||||
|
mode_i = modes.len();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mode_i -= 1;
|
||||||
|
if let Some(new) = modes.get(mode_i) {
|
||||||
|
selected = new.0.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OsKey::Down => {
|
||||||
|
countdown_active = false;
|
||||||
|
if let Some(mut mode_i) = modes.iter().position(|x| x.0.id == selected) {
|
||||||
|
mode_i += 1;
|
||||||
|
if mode_i % rows == 0 {
|
||||||
|
mode_i -= rows;
|
||||||
|
}
|
||||||
|
if mode_i >= modes.len() {
|
||||||
|
mode_i = mode_i - mode_i % rows;
|
||||||
|
}
|
||||||
|
if let Some(new) = modes.get(mode_i) {
|
||||||
|
selected = new.0.id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OsKey::Enter => {
|
||||||
|
countdown_active = false;
|
||||||
|
if let Some(mode_i) = modes.iter().position(|x| x.0.id == selected) {
|
||||||
|
if let Some((mode, _text)) = modes.get(mode_i) {
|
||||||
|
mode_opt = Some(*mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
OsKey::Char('l') => {
|
||||||
|
countdown_active = false;
|
||||||
|
*live = !*live;
|
||||||
|
os.set_text_position(live_mode.0, live_mode.1);
|
||||||
|
if *live {
|
||||||
|
println!("Press l to disable live mode");
|
||||||
|
} else {
|
||||||
|
println!("Press l to enable live mode");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OsKey::Char('e') => {
|
||||||
|
countdown_active = false;
|
||||||
|
if let Some(mode_i) = modes.iter().position(|x| x.0.id == selected) {
|
||||||
|
if let Some((mode, _text)) = modes.get(mode_i) {
|
||||||
|
*edit_env = true;
|
||||||
|
mode_opt = Some(*mode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
OsKey::Other | OsKey::Backspace | OsKey::Delete | OsKey::Char(_) => {
|
||||||
|
countdown_active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
os.set_text_position(0, off_y + rows);
|
||||||
|
os.set_text_highlight(false);
|
||||||
|
println!();
|
||||||
|
|
||||||
|
mode_opt
|
||||||
|
}
|
||||||
|
|
||||||
|
fn redoxfs<O: Os>(os: &O) -> (redoxfs::FileSystem<O::D>, Option<&'static [u8]>) {
|
||||||
|
let attempts = 10;
|
||||||
|
for attempt in 0..=attempts {
|
||||||
|
let mut password_opt = None;
|
||||||
|
if attempt > 0 {
|
||||||
|
print!("\rRedoxFS password ({}/{}): ", attempt, attempts);
|
||||||
|
|
||||||
|
let mut password = String::new();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match os.get_key() {
|
||||||
|
OsKey::Backspace | OsKey::Delete => {
|
||||||
|
if !password.is_empty() {
|
||||||
|
print!("\x08 \x08");
|
||||||
|
password.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OsKey::Char(c) => {
|
||||||
|
print!("*");
|
||||||
|
password.push(c)
|
||||||
|
}
|
||||||
|
OsKey::Enter => break,
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Erase password information
|
||||||
|
while os.get_text_position().0 > 0 {
|
||||||
|
print!("\x08 \x08");
|
||||||
|
}
|
||||||
|
|
||||||
|
if !password.is_empty() {
|
||||||
|
password_opt = Some(password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match os.filesystem(password_opt.as_ref().map(|x| x.as_bytes())) {
|
||||||
|
Ok(fs) => {
|
||||||
|
return (
|
||||||
|
fs,
|
||||||
|
password_opt.map(|password| {
|
||||||
|
// Copy password to page aligned memory
|
||||||
|
let password_size = password.len();
|
||||||
|
let password_base = os.alloc_zeroed_page_aligned(password_size);
|
||||||
|
|
||||||
|
area_add(OsMemoryEntry {
|
||||||
|
base: password_base as u64,
|
||||||
|
size: password_size as u64,
|
||||||
|
kind: OsMemoryKind::Reserved,
|
||||||
|
});
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
ptr::copy(password.as_ptr(), password_base, password_size);
|
||||||
|
slice::from_raw_parts(password_base, password_size)
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Err(err) => match err.errno {
|
||||||
|
// Incorrect password, try again
|
||||||
|
syscall::ENOKEY => (),
|
||||||
|
_ => {
|
||||||
|
panic!("Failed to open RedoxFS: {}", err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic!("RedoxFS out of unlock attempts");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
enum Filetype {
|
||||||
|
Elf,
|
||||||
|
Initfs,
|
||||||
|
}
|
||||||
|
fn load_to_memory<O: Os>(
|
||||||
|
os: &O,
|
||||||
|
fs: &mut redoxfs::FileSystem<O::D>,
|
||||||
|
path: &str,
|
||||||
|
filetype: Filetype,
|
||||||
|
) -> &'static mut [u8] {
|
||||||
|
fs.tx(|tx| {
|
||||||
|
let mut node = None;
|
||||||
|
for component in path.split('/') {
|
||||||
|
node = Some(
|
||||||
|
tx.find_node(
|
||||||
|
node.map_or(redoxfs::TreePtr::root(), |node: TreeData<Node>| node.ptr()),
|
||||||
|
component,
|
||||||
|
)
|
||||||
|
.unwrap_or_else(|err| panic!("Failed to find {component}: {err}")),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let node = node.unwrap();
|
||||||
|
|
||||||
|
let size = node.data().size();
|
||||||
|
|
||||||
|
print!("{}: 0/{} MiB", path, size / MIBI as u64);
|
||||||
|
|
||||||
|
let ptr = os.alloc_zeroed_page_aligned(size as usize);
|
||||||
|
if ptr.is_null() {
|
||||||
|
panic!("Failed to allocate memory for {}", path);
|
||||||
|
}
|
||||||
|
|
||||||
|
let slice = unsafe { slice::from_raw_parts_mut(ptr, size as usize) };
|
||||||
|
|
||||||
|
let mut i = 0;
|
||||||
|
for chunk in slice.chunks_mut(MIBI) {
|
||||||
|
print!("\r{}: {}/{} MiB", path, i / MIBI as u64, size / MIBI as u64);
|
||||||
|
i += tx
|
||||||
|
.read_node_inner(&node, i, chunk)
|
||||||
|
.unwrap_or_else(|err| panic!("Failed to read `{}` file: {}", path, err))
|
||||||
|
as u64;
|
||||||
|
}
|
||||||
|
println!("\r{}: {}/{} MiB", path, i / MIBI as u64, size / MIBI as u64);
|
||||||
|
|
||||||
|
if filetype == Filetype::Elf {
|
||||||
|
let magic = &slice[..4];
|
||||||
|
if magic != b"\x7FELF" {
|
||||||
|
panic!("{} has invalid magic number {:#X?}", path, magic);
|
||||||
|
}
|
||||||
|
} else if filetype == Filetype::Initfs {
|
||||||
|
let magic = &slice[..8];
|
||||||
|
if magic != b"RedoxFtw" {
|
||||||
|
panic!("{} has invalid magic number {:#X?}", path, magic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(slice)
|
||||||
|
})
|
||||||
|
.unwrap_or_else(|err| {
|
||||||
|
panic!(
|
||||||
|
"RedoxFS transaction failed while loading `{}`: {}",
|
||||||
|
path, err
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn elf_entry(data: &[u8]) -> (u64, bool) {
|
||||||
|
match (data[4], data[5]) {
|
||||||
|
// 32-bit, little endian
|
||||||
|
(1, 1) => (
|
||||||
|
u32::from_le_bytes(
|
||||||
|
<[u8; 4]>::try_from(&data[0x18..0x18 + 4]).expect("conversion cannot fail"),
|
||||||
|
) as u64,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
// 32-bit, big endian
|
||||||
|
(1, 2) => (
|
||||||
|
u32::from_be_bytes(
|
||||||
|
<[u8; 4]>::try_from(&data[0x18..0x18 + 4]).expect("conversion cannot fail"),
|
||||||
|
) as u64,
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
// 64-bit, little endian
|
||||||
|
(2, 1) => (
|
||||||
|
u64::from_le_bytes(
|
||||||
|
<[u8; 8]>::try_from(&data[0x18..0x18 + 8]).expect("conversion cannot fail"),
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
// 64-bit, big endian
|
||||||
|
(2, 2) => (
|
||||||
|
u64::from_be_bytes(
|
||||||
|
<[u8; 8]>::try_from(&data[0x18..0x18 + 8]).expect("conversion cannot fail"),
|
||||||
|
),
|
||||||
|
true,
|
||||||
|
),
|
||||||
|
(ei_class, ei_data) => {
|
||||||
|
panic!("Unsupported ELF EI_CLASS {} EI_DATA {}", ei_class, ei_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main(os: &impl Os) -> (usize, u64, KernelArgs) {
|
||||||
|
println!(
|
||||||
|
"Redox OS Bootloader {} on {}",
|
||||||
|
env!("CARGO_PKG_VERSION"),
|
||||||
|
os.name()
|
||||||
|
);
|
||||||
|
|
||||||
|
let hwdesc = os.hwdesc();
|
||||||
|
println!("Hardware descriptor: {:x?}", hwdesc);
|
||||||
|
let (acpi_rsdp_base, acpi_rsdp_size) = match hwdesc {
|
||||||
|
OsHwDesc::Acpi(base, size) => (base, size),
|
||||||
|
OsHwDesc::DeviceTree(base, size) => (base, size),
|
||||||
|
OsHwDesc::NotFound => (0, 0),
|
||||||
|
};
|
||||||
|
|
||||||
|
let (mut fs, password_opt) = redoxfs(os);
|
||||||
|
|
||||||
|
print!("RedoxFS ");
|
||||||
|
for i in 0..fs.header.uuid().len() {
|
||||||
|
if i == 4 || i == 6 || i == 8 || i == 10 {
|
||||||
|
print!("-");
|
||||||
|
}
|
||||||
|
|
||||||
|
print!("{:>02x}", fs.header.uuid()[i]);
|
||||||
|
}
|
||||||
|
println!(": {} MiB", fs.header.size() / MIBI as u64);
|
||||||
|
println!();
|
||||||
|
|
||||||
|
let mut mode_opts = Vec::new();
|
||||||
|
let mut live = cfg!(feature = "live");
|
||||||
|
let mut edit_env = false;
|
||||||
|
for output_i in 0..os.video_outputs() {
|
||||||
|
if output_i > 0 {
|
||||||
|
os.clear_text();
|
||||||
|
}
|
||||||
|
mode_opts.push(select_mode(os, output_i, &mut live, &mut edit_env));
|
||||||
|
}
|
||||||
|
|
||||||
|
let stack_size = 128 * KIBI;
|
||||||
|
let stack_base = os.alloc_zeroed_page_aligned(stack_size);
|
||||||
|
if stack_base.is_null() {
|
||||||
|
panic!("Failed to allocate memory for stack");
|
||||||
|
}
|
||||||
|
|
||||||
|
let live_opt = if live {
|
||||||
|
let size = fs.header.size();
|
||||||
|
|
||||||
|
print!("live: 0/{} MiB", size / MIBI as u64);
|
||||||
|
|
||||||
|
let live_size = match usize::try_from(size) {
|
||||||
|
Ok(live_size) => live_size,
|
||||||
|
Err(_) => {
|
||||||
|
println!("\rlive: disabled (image too large for bootloader address space)");
|
||||||
|
live = false;
|
||||||
|
0
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let ptr = if live {
|
||||||
|
os.alloc_zeroed_page_aligned(live_size)
|
||||||
|
} else {
|
||||||
|
ptr::null_mut()
|
||||||
|
};
|
||||||
|
if live && ptr.is_null() {
|
||||||
|
println!(
|
||||||
|
"\rlive: disabled (unable to allocate {} MiB upfront)",
|
||||||
|
size / MIBI as u64
|
||||||
|
);
|
||||||
|
live = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let live = if live {
|
||||||
|
Some(unsafe { slice::from_raw_parts_mut(ptr, live_size) })
|
||||||
|
} else {
|
||||||
|
println!("Continuing without live preload");
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(live) = live {
|
||||||
|
let mut i = 0;
|
||||||
|
for chunk in live.chunks_mut(MIBI) {
|
||||||
|
print!("\rlive: {}/{} MiB", i / MIBI as u64, size / MIBI as u64);
|
||||||
|
i += unsafe {
|
||||||
|
fs.disk
|
||||||
|
.read_at(fs.block + i / redoxfs::BLOCK_SIZE, chunk)
|
||||||
|
.expect("Failed to read live disk") as u64
|
||||||
|
};
|
||||||
|
}
|
||||||
|
println!("\rlive: {}/{} MiB", i / MIBI as u64, size / MIBI as u64);
|
||||||
|
|
||||||
|
println!("Switching to live disk");
|
||||||
|
unsafe {
|
||||||
|
LIVE_OPT = Some((fs.block, slice::from_raw_parts_mut(ptr, live_size)));
|
||||||
|
}
|
||||||
|
|
||||||
|
area_add(OsMemoryEntry {
|
||||||
|
base: live.as_ptr() as u64,
|
||||||
|
size: live.len() as u64,
|
||||||
|
kind: OsMemoryKind::Reserved,
|
||||||
|
});
|
||||||
|
|
||||||
|
Some(live)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
let (kernel, kernel_entry) = {
|
||||||
|
let kernel = load_to_memory(os, &mut fs, "usr/lib/boot/kernel", Filetype::Elf);
|
||||||
|
let (kernel_entry, kernel_64bit) = elf_entry(kernel);
|
||||||
|
unsafe {
|
||||||
|
KERNEL_64BIT = kernel_64bit;
|
||||||
|
}
|
||||||
|
(kernel, kernel_entry)
|
||||||
|
};
|
||||||
|
|
||||||
|
let (bootstrap_size, bootstrap_base) = {
|
||||||
|
let initfs_slice = load_to_memory(os, &mut fs, "usr/lib/boot/initfs", Filetype::Initfs);
|
||||||
|
|
||||||
|
let memory = unsafe {
|
||||||
|
let total_size = initfs_slice.len().next_multiple_of(4096);
|
||||||
|
let ptr = os.alloc_zeroed_page_aligned(total_size);
|
||||||
|
assert!(!ptr.is_null(), "failed to allocate bootstrap+initfs memory");
|
||||||
|
core::slice::from_raw_parts_mut(ptr, total_size)
|
||||||
|
};
|
||||||
|
memory[..initfs_slice.len()].copy_from_slice(initfs_slice);
|
||||||
|
|
||||||
|
(memory.len() as u64, memory.as_mut_ptr() as u64)
|
||||||
|
};
|
||||||
|
|
||||||
|
let page_phys = unsafe { paging_create(os, kernel.as_ptr() as u64, kernel.len() as u64) }
|
||||||
|
.expect("Failed to set up paging");
|
||||||
|
|
||||||
|
let max_env_size = 64 * KIBI;
|
||||||
|
let mut env_size = max_env_size;
|
||||||
|
let env_base = os.alloc_zeroed_page_aligned(env_size);
|
||||||
|
if env_base.is_null() {
|
||||||
|
panic!("Failed to allocate memory for stack");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut w = SliceWriter {
|
||||||
|
slice: unsafe { slice::from_raw_parts_mut(env_base, max_env_size) },
|
||||||
|
i: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
match hwdesc {
|
||||||
|
OsHwDesc::Acpi(addr, size) => {
|
||||||
|
writeln!(w, "RSDP_ADDR={addr:016x}").unwrap();
|
||||||
|
writeln!(w, "RSDP_SIZE={size:016x}").unwrap();
|
||||||
|
}
|
||||||
|
OsHwDesc::DeviceTree(addr, size) => {
|
||||||
|
writeln!(w, "DTB_ADDR={addr:016x}").unwrap();
|
||||||
|
writeln!(w, "DTB_SIZE={size:016x}").unwrap();
|
||||||
|
}
|
||||||
|
OsHwDesc::NotFound => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(live) = live_opt {
|
||||||
|
writeln!(w, "DISK_LIVE_ADDR={:016x}", live.as_ptr() as usize).unwrap();
|
||||||
|
writeln!(w, "DISK_LIVE_SIZE={:016x}", live.len()).unwrap();
|
||||||
|
writeln!(w, "REDOXFS_BLOCK={:016x}", 0).unwrap();
|
||||||
|
} else {
|
||||||
|
writeln!(w, "REDOXFS_BLOCK={:016x}", fs.block).unwrap();
|
||||||
|
}
|
||||||
|
write!(w, "REDOXFS_UUID=").unwrap();
|
||||||
|
for i in 0..fs.header.uuid().len() {
|
||||||
|
if i == 4 || i == 6 || i == 8 || i == 10 {
|
||||||
|
write!(w, "-").unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
write!(w, "{:>02x}", fs.header.uuid()[i]).unwrap();
|
||||||
|
}
|
||||||
|
writeln!(w).unwrap();
|
||||||
|
if let Some(password) = password_opt {
|
||||||
|
writeln!(
|
||||||
|
w,
|
||||||
|
"REDOXFS_PASSWORD_ADDR={:016x}",
|
||||||
|
password.as_ptr() as usize
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
writeln!(w, "REDOXFS_PASSWORD_SIZE={:016x}", password.len()).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "riscv64")]
|
||||||
|
{
|
||||||
|
let boot_hartid = os::efi_get_boot_hartid()
|
||||||
|
.expect("Could not retrieve boot hart id from EFI implementation!");
|
||||||
|
writeln!(w, "BOOT_HART_ID={:016x}", boot_hartid).unwrap();
|
||||||
|
}
|
||||||
|
if edit_env {
|
||||||
|
editor::edit_env(os, env_base, &mut w.i, max_env_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
for output_i in 0..os.video_outputs() {
|
||||||
|
if let Some(mut mode) = mode_opts[output_i] {
|
||||||
|
// Set mode to get updated values
|
||||||
|
os.set_video_mode(output_i, &mut mode);
|
||||||
|
|
||||||
|
if output_i == 0 {
|
||||||
|
let virt = unsafe {
|
||||||
|
paging_framebuffer(
|
||||||
|
os,
|
||||||
|
page_phys,
|
||||||
|
mode.base,
|
||||||
|
(mode.stride * mode.height * 4) as u64,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.expect("Failed to map framebuffer");
|
||||||
|
|
||||||
|
writeln!(w, "FRAMEBUFFER_ADDR={:016x}", mode.base).unwrap();
|
||||||
|
writeln!(w, "FRAMEBUFFER_VIRT={virt:016x}").unwrap();
|
||||||
|
writeln!(w, "FRAMEBUFFER_WIDTH={:016x}", mode.width).unwrap();
|
||||||
|
writeln!(w, "FRAMEBUFFER_HEIGHT={:016x}", mode.height).unwrap();
|
||||||
|
writeln!(w, "FRAMEBUFFER_STRIDE={:016x}", mode.stride).unwrap();
|
||||||
|
} else {
|
||||||
|
writeln!(
|
||||||
|
w,
|
||||||
|
"FRAMEBUFFER{}={:#x},{},{},{}",
|
||||||
|
output_i, mode.base, mode.width, mode.height, mode.stride,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
env_size = w.i;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(static_mut_refs)]
|
||||||
|
(
|
||||||
|
page_phys,
|
||||||
|
kernel_entry,
|
||||||
|
KernelArgs {
|
||||||
|
kernel_base: kernel.as_ptr() as u64,
|
||||||
|
kernel_size: kernel.len() as u64,
|
||||||
|
stack_base: stack_base as u64,
|
||||||
|
stack_size: stack_size as u64,
|
||||||
|
env_base: env_base as u64,
|
||||||
|
env_size: env_size as u64,
|
||||||
|
acpi_rsdp_base,
|
||||||
|
acpi_rsdp_size,
|
||||||
|
areas_base: unsafe { AREAS.as_ptr() as u64 },
|
||||||
|
areas_size: unsafe { (AREAS.len() * mem::size_of::<OsMemoryEntry>()) as u64 },
|
||||||
|
bootstrap_base,
|
||||||
|
bootstrap_size,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,177 @@
|
|||||||
|
use core::{mem, ptr};
|
||||||
|
use redoxfs::{Disk, BLOCK_SIZE};
|
||||||
|
use syscall::error::{Error, Result, EIO};
|
||||||
|
|
||||||
|
use super::{ThunkData, DISK_ADDRESS_PACKET_ADDR, DISK_BIOS_ADDR};
|
||||||
|
|
||||||
|
const SECTOR_SIZE: u64 = 512;
|
||||||
|
const BLOCKS_PER_SECTOR: u64 = BLOCK_SIZE / SECTOR_SIZE;
|
||||||
|
// 128 sectors is the amount allocated for DISK_BIOS_ADDR
|
||||||
|
// 127 sectors is the maximum for many BIOSes
|
||||||
|
const MAX_SECTORS: u64 = 127;
|
||||||
|
const MAX_BLOCKS: u64 = MAX_SECTORS * SECTOR_SIZE / BLOCK_SIZE;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct DiskAddressPacket {
|
||||||
|
size: u8,
|
||||||
|
reserved: u8,
|
||||||
|
sectors: u16,
|
||||||
|
buffer: u16,
|
||||||
|
segment: u16,
|
||||||
|
address: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiskAddressPacket {
|
||||||
|
pub fn from_block(block: u64, count: u64) -> DiskAddressPacket {
|
||||||
|
let address = block * BLOCKS_PER_SECTOR;
|
||||||
|
let sectors = count * BLOCKS_PER_SECTOR;
|
||||||
|
assert!(sectors <= MAX_SECTORS);
|
||||||
|
DiskAddressPacket {
|
||||||
|
size: mem::size_of::<DiskAddressPacket>() as u8,
|
||||||
|
reserved: 0,
|
||||||
|
sectors: sectors as u16,
|
||||||
|
buffer: (DISK_BIOS_ADDR & 0xF) as u16,
|
||||||
|
segment: (DISK_BIOS_ADDR >> 4) as u16,
|
||||||
|
address,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DiskBios {
|
||||||
|
boot_disk: u8,
|
||||||
|
thunk13: extern "C" fn(),
|
||||||
|
chs_opt: Option<(u32, u32, u32)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DiskBios {
|
||||||
|
pub fn new(boot_disk: u8, thunk13: extern "C" fn()) -> Self {
|
||||||
|
let chs_opt = unsafe {
|
||||||
|
let mut data = ThunkData::new();
|
||||||
|
data.eax = 0x4100;
|
||||||
|
data.ebx = 0x55AA;
|
||||||
|
data.edx = boot_disk as u32;
|
||||||
|
|
||||||
|
data.with(thunk13);
|
||||||
|
|
||||||
|
if (data.ebx & 0xFFFF) == 0xAA55 {
|
||||||
|
// Extensions are installed, do not use CHS
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
// Extensions are not installed, get CHS geometry
|
||||||
|
data = ThunkData::new();
|
||||||
|
data.eax = 0x0800;
|
||||||
|
data.edx = boot_disk as u32;
|
||||||
|
data.edi = 0;
|
||||||
|
|
||||||
|
data.with(thunk13);
|
||||||
|
|
||||||
|
//TODO: return result on error
|
||||||
|
let ah = ({ data.eax } >> 8) & 0xFF;
|
||||||
|
assert_eq!(ah, 0);
|
||||||
|
|
||||||
|
let c = (data.ecx >> 8) & 0xFF | ((data.ecx >> 6) & 0x3) << 8;
|
||||||
|
let h = ((data.edx >> 8) & 0xFF) + 1;
|
||||||
|
let s = data.ecx & 0x3F;
|
||||||
|
|
||||||
|
Some((c, h, s))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
boot_disk,
|
||||||
|
thunk13,
|
||||||
|
chs_opt,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Disk for DiskBios {
|
||||||
|
unsafe fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
|
||||||
|
unsafe {
|
||||||
|
// Optimization for live disks
|
||||||
|
if let Some(live) = crate::LIVE_OPT {
|
||||||
|
if block >= live.0 {
|
||||||
|
let start = ((block - live.0) * BLOCK_SIZE) as usize;
|
||||||
|
let end = start + buffer.len();
|
||||||
|
if end <= live.1.len() {
|
||||||
|
buffer.copy_from_slice(&live.1[start..end]);
|
||||||
|
return Ok(buffer.len());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i, chunk) in buffer
|
||||||
|
.chunks_mut((MAX_BLOCKS * BLOCK_SIZE) as usize)
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
let dap = DiskAddressPacket::from_block(
|
||||||
|
block + i as u64 * MAX_BLOCKS,
|
||||||
|
chunk.len() as u64 / BLOCK_SIZE,
|
||||||
|
);
|
||||||
|
|
||||||
|
if let Some((_, h_max, s_max)) = self.chs_opt {
|
||||||
|
let s = (dap.address % s_max as u64) + 1;
|
||||||
|
assert!(s <= 63, "invalid sector {s}");
|
||||||
|
|
||||||
|
let tmp = dap.address / s_max as u64;
|
||||||
|
let h = tmp % h_max as u64;
|
||||||
|
assert!(h <= 255, "invalid head {h}");
|
||||||
|
|
||||||
|
let c = tmp / h_max as u64;
|
||||||
|
assert!(c <= 1023, "invalid cylinder {c}");
|
||||||
|
|
||||||
|
let mut data = ThunkData::new();
|
||||||
|
data.eax = 0x0200 | (dap.sectors as u32);
|
||||||
|
data.ebx = dap.buffer as u32;
|
||||||
|
data.ecx =
|
||||||
|
(s as u32) | (((c as u32) & 0xFF) << 8) | ((((c as u32) >> 8) & 0x3) << 6);
|
||||||
|
data.edx = (self.boot_disk as u32) | ((h as u32) << 8);
|
||||||
|
data.es = dap.segment;
|
||||||
|
|
||||||
|
data.with(self.thunk13);
|
||||||
|
|
||||||
|
//TODO: return result on error
|
||||||
|
let ah = ({ data.eax } >> 8) & 0xFF;
|
||||||
|
assert_eq!(ah, 0);
|
||||||
|
} else {
|
||||||
|
ptr::write(DISK_ADDRESS_PACKET_ADDR as *mut DiskAddressPacket, dap);
|
||||||
|
|
||||||
|
let mut data = ThunkData::new();
|
||||||
|
data.eax = 0x4200;
|
||||||
|
data.edx = self.boot_disk as u32;
|
||||||
|
data.esi = DISK_ADDRESS_PACKET_ADDR as u32;
|
||||||
|
|
||||||
|
data.with(self.thunk13);
|
||||||
|
|
||||||
|
//TODO: return result on error
|
||||||
|
let ah = ({ data.eax } >> 8) & 0xFF;
|
||||||
|
assert_eq!(ah, 0);
|
||||||
|
|
||||||
|
//TODO: check blocks transferred
|
||||||
|
// dap = ptr::read(DISK_ADDRESS_PACKET_ADDR as *mut DiskAddressPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr::copy(DISK_BIOS_ADDR as *const u8, chunk.as_mut_ptr(), chunk.len());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(buffer.len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn write_at(&mut self, block: u64, buffer: &[u8]) -> Result<usize> {
|
||||||
|
log::error!(
|
||||||
|
"DiskBios::write_at(0x{:X}, 0x{:X}:0x{:X}) not allowed",
|
||||||
|
block,
|
||||||
|
buffer.as_ptr() as usize,
|
||||||
|
buffer.len()
|
||||||
|
);
|
||||||
|
Err(Error::new(EIO))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&mut self) -> Result<u64> {
|
||||||
|
log::error!("DiskBios::size not implemented");
|
||||||
|
Err(Error::new(EIO))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/// Print to console
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! print {
|
||||||
|
($($arg:tt)*) => ({
|
||||||
|
use core::fmt::Write;
|
||||||
|
#[cfg(feature = "serial_debug")]
|
||||||
|
{
|
||||||
|
let _ = write!($crate::os::serial::COM1.lock(), $($arg)*);
|
||||||
|
}
|
||||||
|
let _ = write!($crate::os::VGA.lock(), $($arg)*);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Print with new line to console
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! println {
|
||||||
|
() => (print!("\n"));
|
||||||
|
($fmt:expr_2021) => (print!(concat!($fmt, "\n")));
|
||||||
|
($fmt:expr_2021, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
use core::{cmp, mem, ptr};
|
||||||
|
|
||||||
|
use crate::area_add;
|
||||||
|
use crate::os::{OsMemoryEntry, OsMemoryKind};
|
||||||
|
|
||||||
|
use super::{thunk::ThunkData, MEMORY_MAP_ADDR};
|
||||||
|
|
||||||
|
#[repr(C, packed)]
|
||||||
|
struct MemoryMapEntry {
|
||||||
|
pub base: u64,
|
||||||
|
pub size: u64,
|
||||||
|
pub kind: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MemoryMapIter {
|
||||||
|
thunk15: extern "C" fn(),
|
||||||
|
data: ThunkData,
|
||||||
|
first: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemoryMapIter {
|
||||||
|
pub fn new(thunk15: extern "C" fn()) -> Self {
|
||||||
|
Self {
|
||||||
|
thunk15,
|
||||||
|
data: ThunkData::new(),
|
||||||
|
first: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for MemoryMapIter {
|
||||||
|
type Item = OsMemoryEntry;
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.first {
|
||||||
|
self.first = false;
|
||||||
|
} else if self.data.ebx == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.data.eax = 0xE820;
|
||||||
|
self.data.ecx = mem::size_of::<MemoryMapEntry>() as u32;
|
||||||
|
self.data.edx = 0x534D4150;
|
||||||
|
self.data.edi = MEMORY_MAP_ADDR as u32;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
self.data.with(self.thunk15);
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: return error?
|
||||||
|
assert_eq!({ self.data.eax }, 0x534D4150);
|
||||||
|
assert_eq!({ self.data.ecx }, mem::size_of::<MemoryMapEntry>() as u32);
|
||||||
|
|
||||||
|
let entry = unsafe { ptr::read(MEMORY_MAP_ADDR as *const MemoryMapEntry) };
|
||||||
|
Some(Self::Item {
|
||||||
|
base: entry.base,
|
||||||
|
size: entry.size,
|
||||||
|
kind: match entry.kind {
|
||||||
|
0 => OsMemoryKind::Null,
|
||||||
|
1 => OsMemoryKind::Free,
|
||||||
|
3 => OsMemoryKind::Reclaim,
|
||||||
|
_ => OsMemoryKind::Reserved,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn memory_map(thunk15: extern "C" fn()) -> Option<(usize, usize)> {
|
||||||
|
let mut heap_limits = None;
|
||||||
|
for entry in MemoryMapIter::new(thunk15) {
|
||||||
|
let heap_start = 1024 * 1024;
|
||||||
|
if { entry.kind } == OsMemoryKind::Free
|
||||||
|
&& entry.base <= heap_start as u64
|
||||||
|
&& (entry.base + entry.size) >= heap_start as u64
|
||||||
|
{
|
||||||
|
let heap_end = cmp::min(entry.base + entry.size, usize::MAX as u64) as usize;
|
||||||
|
if heap_end >= heap_start {
|
||||||
|
heap_limits = Some((heap_start, heap_end - heap_start));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
area_add(entry);
|
||||||
|
}
|
||||||
|
heap_limits
|
||||||
|
}
|
||||||
@@ -0,0 +1,323 @@
|
|||||||
|
use alloc::alloc::{alloc_zeroed, Layout};
|
||||||
|
use core::{convert::TryFrom, mem, ptr, slice};
|
||||||
|
use linked_list_allocator::LockedHeap;
|
||||||
|
use spin::Mutex;
|
||||||
|
|
||||||
|
use crate::logger::LOGGER;
|
||||||
|
use crate::os::{Os, OsHwDesc, OsKey, OsVideoMode};
|
||||||
|
use crate::KernelArgs;
|
||||||
|
|
||||||
|
use self::disk::DiskBios;
|
||||||
|
use self::memory_map::memory_map;
|
||||||
|
use self::thunk::ThunkData;
|
||||||
|
use self::vbe::VideoModeIter;
|
||||||
|
use self::vga::{Vga, VgaTextColor};
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
mod macros;
|
||||||
|
|
||||||
|
mod disk;
|
||||||
|
mod memory_map;
|
||||||
|
mod panic;
|
||||||
|
pub(crate) mod serial;
|
||||||
|
mod thunk;
|
||||||
|
mod vbe;
|
||||||
|
mod vga;
|
||||||
|
|
||||||
|
// Real mode memory allocation, for use with thunk
|
||||||
|
// 0x500 to 0x7BFF is free
|
||||||
|
const DISK_BIOS_ADDR: usize = 0x70000; // 64 KiB at 448 KiB, ends at 512 KiB
|
||||||
|
const VBE_CARD_INFO_ADDR: usize = 0x1000; // 512 bytes, ends at 0x11FF
|
||||||
|
const VBE_MODE_INFO_ADDR: usize = 0x1200; // 256 bytes, ends at 0x12FF
|
||||||
|
const VBE_EDID_ADDR: usize = 0x1300; // 128 bytes, ends at 0x137F
|
||||||
|
const MEMORY_MAP_ADDR: usize = 0x1380; // 24 bytes, ends at 0x1397
|
||||||
|
const DISK_ADDRESS_PACKET_ADDR: usize = 0x1398; // 16 bytes, ends at 0x13A7
|
||||||
|
const THUNK_STACK_ADDR: usize = 0x7C00; // Grows downwards
|
||||||
|
const VGA_ADDR: usize = 0xB8000;
|
||||||
|
|
||||||
|
#[global_allocator]
|
||||||
|
static ALLOCATOR: LockedHeap = LockedHeap::empty();
|
||||||
|
|
||||||
|
pub(crate) static VGA: Mutex<Vga> = Mutex::new(unsafe { Vga::new(VGA_ADDR, 80, 25) });
|
||||||
|
|
||||||
|
pub struct OsBios {
|
||||||
|
boot_disk: usize,
|
||||||
|
thunk10: extern "C" fn(),
|
||||||
|
thunk13: extern "C" fn(),
|
||||||
|
thunk15: extern "C" fn(),
|
||||||
|
thunk16: extern "C" fn(),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct Rsdp {
|
||||||
|
signature: [u8; 8],
|
||||||
|
checksum: u8,
|
||||||
|
oemid: [u8; 6],
|
||||||
|
revision: u8,
|
||||||
|
rsdt_address: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct Xsdp {
|
||||||
|
rsdp: Rsdp,
|
||||||
|
|
||||||
|
length: u32,
|
||||||
|
xsdt_address: u64,
|
||||||
|
extended_checksum: u8,
|
||||||
|
reserved: [u8; 3],
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn search_rsdp(start: usize, end: usize) -> Option<(u64, u64)> {
|
||||||
|
unsafe {
|
||||||
|
// Align start up to 16 bytes
|
||||||
|
let mut addr = start.div_ceil(16) * 16;
|
||||||
|
// Search until reading the end of the Rsdp would be past the end of the memory area
|
||||||
|
while addr + mem::size_of::<Rsdp>() <= end {
|
||||||
|
let rsdp = ptr::read(addr as *const Rsdp);
|
||||||
|
if &rsdp.signature == b"RSD PTR " {
|
||||||
|
//TODO: check checksum?
|
||||||
|
if rsdp.revision == 0 {
|
||||||
|
return Some((addr as u64, mem::size_of::<Rsdp>() as u64));
|
||||||
|
} else if rsdp.revision == 2 {
|
||||||
|
let xsdp = ptr::read(addr as *const Xsdp);
|
||||||
|
//TODO: check extended checksum?
|
||||||
|
return Some((addr as u64, xsdp.length as u64));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rsdp is always aligned to 16 bytes
|
||||||
|
addr += 16;
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Os for OsBios {
|
||||||
|
type D = DiskBios;
|
||||||
|
type V = VideoModeIter;
|
||||||
|
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"x86/BIOS"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_zeroed_page_aligned(&self, size: usize) -> *mut u8 {
|
||||||
|
assert!(size != 0);
|
||||||
|
|
||||||
|
let page_size = self.page_size();
|
||||||
|
let pages = size.div_ceil(page_size);
|
||||||
|
let Some(total_size) = pages.checked_mul(page_size) else {
|
||||||
|
return ptr::null_mut();
|
||||||
|
};
|
||||||
|
let Ok(layout) = Layout::from_size_align(total_size, page_size) else {
|
||||||
|
return ptr::null_mut();
|
||||||
|
};
|
||||||
|
|
||||||
|
let ptr = unsafe { alloc_zeroed(layout) };
|
||||||
|
|
||||||
|
assert!(!ptr.is_null());
|
||||||
|
ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
fn page_size(&self) -> usize {
|
||||||
|
4096
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filesystem(
|
||||||
|
&self,
|
||||||
|
password_opt: Option<&[u8]>,
|
||||||
|
) -> syscall::Result<redoxfs::FileSystem<DiskBios>> {
|
||||||
|
let disk = DiskBios::new(u8::try_from(self.boot_disk).unwrap(), self.thunk13);
|
||||||
|
|
||||||
|
//TODO: get block from partition table
|
||||||
|
let block = 2 * crate::MIBI as u64 / redoxfs::BLOCK_SIZE;
|
||||||
|
redoxfs::FileSystem::open(disk, password_opt, Some(block), false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hwdesc(&self) -> OsHwDesc {
|
||||||
|
// See ACPI specification - Finding the RSDP on IA-PC Systems
|
||||||
|
unsafe {
|
||||||
|
let ebda_segment = ptr::read(0x40E as *const u16);
|
||||||
|
let ebda_addr = (ebda_segment as usize) << 4;
|
||||||
|
if let Some((addr, size)) =
|
||||||
|
search_rsdp(ebda_addr, ebda_addr + 1024).or(search_rsdp(0xE0000, 0xFFFFF))
|
||||||
|
{
|
||||||
|
// Copy to a page
|
||||||
|
let page_aligned = self.alloc_zeroed_page_aligned(size as usize);
|
||||||
|
ptr::copy(addr as *const u8, page_aligned, size as usize);
|
||||||
|
return OsHwDesc::Acpi(page_aligned as u64, size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
OsHwDesc::NotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
fn video_outputs(&self) -> usize {
|
||||||
|
//TODO: return 1 only if vbe supported?
|
||||||
|
1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn video_modes(&self, _output_i: usize) -> VideoModeIter {
|
||||||
|
VideoModeIter::new(self.thunk10)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_video_mode(&self, _output_i: usize, mode: &mut OsVideoMode) {
|
||||||
|
// Set video mode
|
||||||
|
let mut data = ThunkData::new();
|
||||||
|
data.eax = 0x4F02;
|
||||||
|
data.ebx = mode.id;
|
||||||
|
unsafe {
|
||||||
|
data.with(self.thunk10);
|
||||||
|
}
|
||||||
|
//TODO: check result
|
||||||
|
}
|
||||||
|
|
||||||
|
fn best_resolution(&self, _output_i: usize) -> Option<(u32, u32)> {
|
||||||
|
let mut data = ThunkData::new();
|
||||||
|
data.eax = 0x4F15;
|
||||||
|
data.ebx = 0x01;
|
||||||
|
data.ecx = 0;
|
||||||
|
data.edx = 0;
|
||||||
|
data.edi = VBE_EDID_ADDR as u32;
|
||||||
|
unsafe {
|
||||||
|
data.with(self.thunk10);
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.eax == 0x4F {
|
||||||
|
let edid = unsafe { slice::from_raw_parts(VBE_EDID_ADDR as *const u8, 128) };
|
||||||
|
|
||||||
|
Some((
|
||||||
|
(edid[0x38] as u32) | (((edid[0x3A] as u32) & 0xF0) << 4),
|
||||||
|
(edid[0x3B] as u32) | (((edid[0x3D] as u32) & 0xF0) << 4),
|
||||||
|
))
|
||||||
|
} else {
|
||||||
|
log::warn!("Failed to get VBE EDID: 0x{:X}", { data.eax });
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_key(&self) -> OsKey {
|
||||||
|
// Read keypress
|
||||||
|
let mut data = ThunkData::new();
|
||||||
|
unsafe {
|
||||||
|
data.with(self.thunk16);
|
||||||
|
}
|
||||||
|
match (data.eax >> 8) as u8 {
|
||||||
|
0x4B => OsKey::Left,
|
||||||
|
0x4D => OsKey::Right,
|
||||||
|
0x48 => OsKey::Up,
|
||||||
|
0x50 => OsKey::Down,
|
||||||
|
0x0E => OsKey::Backspace,
|
||||||
|
0x53 => OsKey::Delete,
|
||||||
|
0x1C => OsKey::Enter,
|
||||||
|
_ => match data.eax as u8 {
|
||||||
|
0 => OsKey::Other,
|
||||||
|
b => OsKey::Char(b as char),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_text(&self) {
|
||||||
|
let mut vga = VGA.lock();
|
||||||
|
vga.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_text_position(&self) -> (usize, usize) {
|
||||||
|
let vga = VGA.lock();
|
||||||
|
(vga.x, vga.y)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_text_position(&self, x: usize, y: usize) {
|
||||||
|
//TODO: ensure this is inside bounds!
|
||||||
|
let mut vga = VGA.lock();
|
||||||
|
vga.x = x;
|
||||||
|
vga.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_text_highlight(&self, highlight: bool) {
|
||||||
|
let mut vga = VGA.lock();
|
||||||
|
if highlight {
|
||||||
|
vga.bg = VgaTextColor::Gray;
|
||||||
|
vga.fg = VgaTextColor::Black;
|
||||||
|
} else {
|
||||||
|
vga.bg = VgaTextColor::Black;
|
||||||
|
vga.fg = VgaTextColor::Gray;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub unsafe extern "C" fn start(
|
||||||
|
kernel_entry: extern "C" fn(
|
||||||
|
page_table: usize,
|
||||||
|
stack: u64,
|
||||||
|
func: u64,
|
||||||
|
args: *const KernelArgs,
|
||||||
|
long_mode: usize,
|
||||||
|
) -> !,
|
||||||
|
boot_disk: usize,
|
||||||
|
thunk10: extern "C" fn(),
|
||||||
|
thunk13: extern "C" fn(),
|
||||||
|
thunk15: extern "C" fn(),
|
||||||
|
thunk16: extern "C" fn(),
|
||||||
|
) -> ! {
|
||||||
|
unsafe {
|
||||||
|
#[cfg(feature = "serial_debug")]
|
||||||
|
{
|
||||||
|
let mut com1 = serial::COM1.lock();
|
||||||
|
com1.init();
|
||||||
|
com1.write(b"SERIAL\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Make sure we are in mode 3 (80x25 text mode)
|
||||||
|
let mut data = ThunkData::new();
|
||||||
|
data.eax = 0x03;
|
||||||
|
data.with(thunk10);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// Disable cursor
|
||||||
|
let mut data = ThunkData::new();
|
||||||
|
data.eax = 0x0100;
|
||||||
|
data.ecx = 0x3F00;
|
||||||
|
data.with(thunk10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear screen
|
||||||
|
VGA.lock().clear();
|
||||||
|
|
||||||
|
// Set logger
|
||||||
|
LOGGER.init();
|
||||||
|
|
||||||
|
let mut os = OsBios {
|
||||||
|
boot_disk,
|
||||||
|
thunk10,
|
||||||
|
thunk13,
|
||||||
|
thunk15,
|
||||||
|
thunk16,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (heap_start, heap_size) = memory_map(os.thunk15).expect("No memory for heap");
|
||||||
|
|
||||||
|
ALLOCATOR.lock().init(heap_start as *mut u8, heap_size);
|
||||||
|
|
||||||
|
let (page_phys, func, args) = crate::main(&mut os);
|
||||||
|
|
||||||
|
kernel_entry(
|
||||||
|
page_phys,
|
||||||
|
args.stack_base
|
||||||
|
+ args.stack_size
|
||||||
|
+ if crate::KERNEL_64BIT {
|
||||||
|
crate::arch::x64::PHYS_OFFSET
|
||||||
|
} else {
|
||||||
|
crate::arch::x32::PHYS_OFFSET as u64
|
||||||
|
},
|
||||||
|
func,
|
||||||
|
&args,
|
||||||
|
if crate::KERNEL_64BIT { 1 } else { 0 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
//! Intrinsics for panic handling
|
||||||
|
|
||||||
|
use core::alloc::Layout;
|
||||||
|
use core::arch::asm;
|
||||||
|
use core::panic::PanicInfo;
|
||||||
|
|
||||||
|
/// Required to handle panics
|
||||||
|
#[panic_handler]
|
||||||
|
pub fn rust_begin_unwind(info: &PanicInfo) -> ! {
|
||||||
|
unsafe {
|
||||||
|
println!("BOOTLOADER PANIC:\n{}", info);
|
||||||
|
loop {
|
||||||
|
asm!("hlt");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
use spin::Mutex;
|
||||||
|
use syscall::Pio;
|
||||||
|
|
||||||
|
use crate::serial_16550::SerialPort;
|
||||||
|
|
||||||
|
pub static COM1: Mutex<SerialPort<Pio<u8>>> = Mutex::new(SerialPort::<Pio<u8>>::new(0x3F8));
|
||||||
|
pub static COM2: Mutex<SerialPort<Pio<u8>>> = Mutex::new(SerialPort::<Pio<u8>>::new(0x2F8));
|
||||||
|
pub static COM3: Mutex<SerialPort<Pio<u8>>> = Mutex::new(SerialPort::<Pio<u8>>::new(0x3E8));
|
||||||
|
pub static COM4: Mutex<SerialPort<Pio<u8>>> = Mutex::new(SerialPort::<Pio<u8>>::new(0x2E8));
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
use core::ptr;
|
||||||
|
|
||||||
|
use super::THUNK_STACK_ADDR;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct ThunkData {
|
||||||
|
pub es: u16,
|
||||||
|
pub edi: u32,
|
||||||
|
pub esi: u32,
|
||||||
|
pub ebp: u32,
|
||||||
|
pub ebx: u32,
|
||||||
|
pub edx: u32,
|
||||||
|
pub ecx: u32,
|
||||||
|
pub eax: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ThunkData {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
es: 0,
|
||||||
|
edi: 0,
|
||||||
|
esi: 0,
|
||||||
|
ebp: 0,
|
||||||
|
ebx: 0,
|
||||||
|
edx: 0,
|
||||||
|
ecx: 0,
|
||||||
|
eax: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn save(&self) {
|
||||||
|
unsafe {
|
||||||
|
ptr::write((THUNK_STACK_ADDR - 64) as *mut ThunkData, *self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn load(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
*self = ptr::read((THUNK_STACK_ADDR - 64) as *const ThunkData);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn with(&mut self, f: extern "C" fn()) {
|
||||||
|
unsafe {
|
||||||
|
self.save();
|
||||||
|
f();
|
||||||
|
self.load();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,151 @@
|
|||||||
|
use core::ptr;
|
||||||
|
use log::error;
|
||||||
|
|
||||||
|
use crate::os::OsVideoMode;
|
||||||
|
|
||||||
|
use super::{ThunkData, VBE_CARD_INFO_ADDR, VBE_MODE_INFO_ADDR};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct VbeFarPtr {
|
||||||
|
pub offset: u16,
|
||||||
|
pub segment: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VbeFarPtr {
|
||||||
|
pub unsafe fn as_ptr<T>(&self) -> *const T {
|
||||||
|
(((self.segment as usize) << 4) + (self.offset as usize)) as *const T
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct VbeCardInfo {
|
||||||
|
pub signature: [u8; 4],
|
||||||
|
pub version: u16,
|
||||||
|
pub oemstring: VbeFarPtr,
|
||||||
|
pub capabilities: [u8; 4],
|
||||||
|
pub videomodeptr: VbeFarPtr,
|
||||||
|
pub totalmemory: u16,
|
||||||
|
pub oemsoftwarerev: u16,
|
||||||
|
pub oemvendornameptr: VbeFarPtr,
|
||||||
|
pub oemproductnameptr: VbeFarPtr,
|
||||||
|
pub oemproductrevptr: VbeFarPtr,
|
||||||
|
pub reserved: [u8; 222],
|
||||||
|
pub oemdata: [u8; 256],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct VbeModeInfo {
|
||||||
|
pub attributes: u16,
|
||||||
|
pub win_a: u8,
|
||||||
|
pub win_b: u8,
|
||||||
|
pub granularity: u16,
|
||||||
|
pub winsize: u16,
|
||||||
|
pub segment_a: u16,
|
||||||
|
pub segment_b: u16,
|
||||||
|
pub winfuncptr: u32,
|
||||||
|
pub bytesperscanline: u16,
|
||||||
|
pub xresolution: u16,
|
||||||
|
pub yresolution: u16,
|
||||||
|
pub xcharsize: u8,
|
||||||
|
pub ycharsize: u8,
|
||||||
|
pub numberofplanes: u8,
|
||||||
|
pub bitsperpixel: u8,
|
||||||
|
pub numberofbanks: u8,
|
||||||
|
pub memorymodel: u8,
|
||||||
|
pub banksize: u8,
|
||||||
|
pub numberofimagepages: u8,
|
||||||
|
pub unused: u8,
|
||||||
|
pub redmasksize: u8,
|
||||||
|
pub redfieldposition: u8,
|
||||||
|
pub greenmasksize: u8,
|
||||||
|
pub greenfieldposition: u8,
|
||||||
|
pub bluemasksize: u8,
|
||||||
|
pub bluefieldposition: u8,
|
||||||
|
pub rsvdmasksize: u8,
|
||||||
|
pub rsvdfieldposition: u8,
|
||||||
|
pub directcolormodeinfo: u8,
|
||||||
|
pub physbaseptr: u32,
|
||||||
|
pub offscreenmemoryoffset: u32,
|
||||||
|
pub offscreenmemsize: u16,
|
||||||
|
pub reserved: [u8; 206],
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct VideoModeIter {
|
||||||
|
thunk10: extern "C" fn(),
|
||||||
|
mode_ptr: *const u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VideoModeIter {
|
||||||
|
pub fn new(thunk10: extern "C" fn()) -> Self {
|
||||||
|
// Get card info
|
||||||
|
let mut data = ThunkData::new();
|
||||||
|
data.eax = 0x4F00;
|
||||||
|
data.edi = VBE_CARD_INFO_ADDR as u32;
|
||||||
|
unsafe {
|
||||||
|
data.with(thunk10);
|
||||||
|
}
|
||||||
|
let mode_ptr = if data.eax == 0x004F {
|
||||||
|
let card_info = unsafe { ptr::read(VBE_CARD_INFO_ADDR as *const VbeCardInfo) };
|
||||||
|
unsafe { card_info.videomodeptr.as_ptr::<u16>() }
|
||||||
|
} else {
|
||||||
|
error!("Failed to read VBE card info: 0x{:04X}", { data.eax });
|
||||||
|
ptr::null()
|
||||||
|
};
|
||||||
|
Self { thunk10, mode_ptr }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for VideoModeIter {
|
||||||
|
type Item = OsVideoMode;
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.mode_ptr.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
loop {
|
||||||
|
// Set bit 14 to get linear frame buffer
|
||||||
|
let mode = unsafe { *self.mode_ptr } | (1 << 14);
|
||||||
|
if mode == 0xFFFF {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
self.mode_ptr = unsafe { self.mode_ptr.add(1) };
|
||||||
|
|
||||||
|
// Get mode info
|
||||||
|
let mut data = ThunkData::new();
|
||||||
|
data.eax = 0x4F01;
|
||||||
|
data.ecx = mode as u32;
|
||||||
|
data.edi = VBE_MODE_INFO_ADDR as u32;
|
||||||
|
unsafe {
|
||||||
|
data.with(self.thunk10);
|
||||||
|
}
|
||||||
|
if data.eax == 0x004F {
|
||||||
|
let mode_info = unsafe { ptr::read(VBE_MODE_INFO_ADDR as *const VbeModeInfo) };
|
||||||
|
|
||||||
|
// We only support 32-bits per pixel modes
|
||||||
|
if mode_info.bitsperpixel != 32 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let width = mode_info.xresolution as u32;
|
||||||
|
let height = mode_info.yresolution as u32;
|
||||||
|
//TODO: support stride that is not a multiple of 4
|
||||||
|
let stride = mode_info.bytesperscanline as u32 / 4;
|
||||||
|
|
||||||
|
return Some(OsVideoMode {
|
||||||
|
id: mode as u32,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
stride,
|
||||||
|
base: mode_info.physbaseptr as u64,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
error!("Failed to read VBE mode 0x{:04X} info: 0x{:04X}", mode, {
|
||||||
|
data.eax
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
use core::{fmt, slice};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct VgaTextBlock {
|
||||||
|
pub char: u8,
|
||||||
|
pub color: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum VgaTextColor {
|
||||||
|
Black = 0,
|
||||||
|
Blue = 1,
|
||||||
|
Green = 2,
|
||||||
|
Cyan = 3,
|
||||||
|
Red = 4,
|
||||||
|
Purple = 5,
|
||||||
|
Brown = 6,
|
||||||
|
Gray = 7,
|
||||||
|
DarkGray = 8,
|
||||||
|
LightBlue = 9,
|
||||||
|
LightGreen = 10,
|
||||||
|
LightCyan = 11,
|
||||||
|
LightRed = 12,
|
||||||
|
LightPurple = 13,
|
||||||
|
Yellow = 14,
|
||||||
|
White = 15,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Vga {
|
||||||
|
pub base: usize,
|
||||||
|
pub width: usize,
|
||||||
|
pub height: usize,
|
||||||
|
pub x: usize,
|
||||||
|
pub y: usize,
|
||||||
|
pub bg: VgaTextColor,
|
||||||
|
pub fg: VgaTextColor,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Vga {
|
||||||
|
pub const unsafe fn new(base: usize, width: usize, height: usize) -> Self {
|
||||||
|
Self {
|
||||||
|
base,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
bg: VgaTextColor::Black,
|
||||||
|
fg: VgaTextColor::Gray,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn blocks(&mut self) -> &'static mut [VgaTextBlock] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(self.base as *mut VgaTextBlock, self.width * self.height)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.x = 0;
|
||||||
|
self.y = 0;
|
||||||
|
let blocks = unsafe { self.blocks() };
|
||||||
|
for i in 0..blocks.len() {
|
||||||
|
blocks[i] = VgaTextBlock {
|
||||||
|
char: 0,
|
||||||
|
color: ((self.bg as u8) << 4) | (self.fg as u8),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Write for Vga {
|
||||||
|
fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
|
||||||
|
let blocks = unsafe { self.blocks() };
|
||||||
|
for c in s.chars() {
|
||||||
|
if self.x >= self.width {
|
||||||
|
self.x = 0;
|
||||||
|
self.y += 1;
|
||||||
|
}
|
||||||
|
while self.y >= self.height {
|
||||||
|
for y in 1..self.height {
|
||||||
|
for x in 0..self.width {
|
||||||
|
let i = y * self.width + x;
|
||||||
|
let j = i - self.width;
|
||||||
|
blocks[j] = blocks[i];
|
||||||
|
if y + 1 == self.height {
|
||||||
|
blocks[i].char = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.y -= 1;
|
||||||
|
}
|
||||||
|
match c {
|
||||||
|
'\x08' => {
|
||||||
|
if self.x > 0 {
|
||||||
|
self.x -= 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'\r' => {
|
||||||
|
self.x = 0;
|
||||||
|
}
|
||||||
|
'\n' => {
|
||||||
|
self.x = 0;
|
||||||
|
self.y += 1;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let i = self.y * self.width + self.x;
|
||||||
|
if let Some(block) = blocks.get_mut(i) {
|
||||||
|
block.char = c as u8;
|
||||||
|
block.color = ((self.bg as u8) << 4) | (self.fg as u8);
|
||||||
|
}
|
||||||
|
self.x += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
use redoxfs::Disk;
|
||||||
|
|
||||||
|
#[cfg(all(target_arch = "x86", target_os = "none"))]
|
||||||
|
pub use self::bios::*;
|
||||||
|
|
||||||
|
#[cfg(all(target_arch = "x86", target_os = "none"))]
|
||||||
|
#[macro_use]
|
||||||
|
mod bios;
|
||||||
|
|
||||||
|
#[cfg(any(target_arch = "riscv64", target_os = "uefi"))]
|
||||||
|
#[allow(unused_imports)]
|
||||||
|
pub use self::uefi::*;
|
||||||
|
|
||||||
|
#[cfg(any(target_arch = "riscv64", target_os = "uefi"))]
|
||||||
|
#[macro_use]
|
||||||
|
mod uefi;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum OsHwDesc {
|
||||||
|
Acpi(u64, u64),
|
||||||
|
DeviceTree(u64, u64),
|
||||||
|
NotFound,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum OsKey {
|
||||||
|
Left,
|
||||||
|
Right,
|
||||||
|
Up,
|
||||||
|
Down,
|
||||||
|
Backspace,
|
||||||
|
Delete,
|
||||||
|
Enter,
|
||||||
|
Char(char),
|
||||||
|
Timeout,
|
||||||
|
Other,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep synced with BootloaderMemoryKind in kernel
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
#[repr(u64)]
|
||||||
|
pub enum OsMemoryKind {
|
||||||
|
Null = 0,
|
||||||
|
Free = 1,
|
||||||
|
Reclaim = 2,
|
||||||
|
Reserved = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep synced with BootloaderMemoryEntry in kernel
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
#[repr(C, packed(8))]
|
||||||
|
pub struct OsMemoryEntry {
|
||||||
|
pub base: u64,
|
||||||
|
pub size: u64,
|
||||||
|
pub kind: OsMemoryKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct OsVideoMode {
|
||||||
|
pub id: u32,
|
||||||
|
pub width: u32,
|
||||||
|
pub height: u32,
|
||||||
|
pub stride: u32,
|
||||||
|
pub base: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Os {
|
||||||
|
type D: Disk;
|
||||||
|
type V: Iterator<Item = OsVideoMode>;
|
||||||
|
|
||||||
|
fn name(&self) -> &str;
|
||||||
|
|
||||||
|
fn alloc_zeroed_page_aligned(&self, size: usize) -> *mut u8;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn page_size(&self) -> usize;
|
||||||
|
|
||||||
|
fn filesystem(
|
||||||
|
&self,
|
||||||
|
password_opt: Option<&[u8]>,
|
||||||
|
) -> syscall::Result<redoxfs::FileSystem<Self::D>>;
|
||||||
|
|
||||||
|
fn hwdesc(&self) -> OsHwDesc;
|
||||||
|
|
||||||
|
fn video_outputs(&self) -> usize;
|
||||||
|
fn video_modes(&self, output_i: usize) -> Self::V;
|
||||||
|
fn set_video_mode(&self, output_i: usize, mode: &mut OsVideoMode);
|
||||||
|
fn best_resolution(&self, output_i: usize) -> Option<(u32, u32)>;
|
||||||
|
|
||||||
|
fn get_key(&self) -> OsKey;
|
||||||
|
fn get_key_timeout(&self, _milliseconds: usize) -> OsKey {
|
||||||
|
self.get_key()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_text(&self);
|
||||||
|
fn get_text_position(&self) -> (usize, usize);
|
||||||
|
fn set_text_position(&self, x: usize, y: usize);
|
||||||
|
fn set_text_highlight(&self, highlight: bool);
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
use core::slice;
|
||||||
|
use uefi::guid::{ACPI_20_TABLE_GUID, ACPI_TABLE_GUID};
|
||||||
|
|
||||||
|
use crate::Os;
|
||||||
|
|
||||||
|
struct Invalid;
|
||||||
|
|
||||||
|
fn validate_rsdp(address: usize, _v2: bool) -> core::result::Result<usize, Invalid> {
|
||||||
|
#[repr(C, packed)]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
struct Rsdp {
|
||||||
|
signature: [u8; 8], // b"RSD PTR "
|
||||||
|
chksum: u8,
|
||||||
|
oem_id: [u8; 6],
|
||||||
|
revision: u8,
|
||||||
|
rsdt_addr: u32,
|
||||||
|
// the following fields are only available for ACPI 2.0, and are reserved otherwise
|
||||||
|
length: u32,
|
||||||
|
xsdt_addr: u64,
|
||||||
|
extended_chksum: u8,
|
||||||
|
_rsvd: [u8; 3],
|
||||||
|
}
|
||||||
|
// paging is not enabled at this stage; we can just read the physical address here.
|
||||||
|
let rsdp_bytes =
|
||||||
|
unsafe { core::slice::from_raw_parts(address as *const u8, core::mem::size_of::<Rsdp>()) };
|
||||||
|
let rsdp = unsafe {
|
||||||
|
(rsdp_bytes.as_ptr() as *const Rsdp)
|
||||||
|
.as_ref::<'static>()
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
log::debug!("RSDP: {:?}", rsdp);
|
||||||
|
|
||||||
|
if rsdp.signature != *b"RSD PTR " {
|
||||||
|
return Err(Invalid);
|
||||||
|
}
|
||||||
|
let mut base_sum = 0u8;
|
||||||
|
for base_byte in &rsdp_bytes[..20] {
|
||||||
|
base_sum = base_sum.wrapping_add(*base_byte);
|
||||||
|
}
|
||||||
|
if base_sum != 0 {
|
||||||
|
return Err(Invalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if rsdp.revision == 2 {
|
||||||
|
let mut extended_sum = 0u8;
|
||||||
|
for byte in rsdp_bytes {
|
||||||
|
extended_sum = extended_sum.wrapping_add(*byte);
|
||||||
|
}
|
||||||
|
|
||||||
|
if extended_sum != 0 {
|
||||||
|
return Err(Invalid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let length = if rsdp.revision == 2 {
|
||||||
|
rsdp.length as usize
|
||||||
|
} else {
|
||||||
|
core::mem::size_of::<Rsdp>()
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(length)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn find_acpi_table_pointers(os: &impl Os) -> Option<(u64, u64)> {
|
||||||
|
let cfg_tables = std::system_table().config_tables();
|
||||||
|
let mut acpi = None;
|
||||||
|
let mut acpi2 = None;
|
||||||
|
for cfg_table in cfg_tables.iter() {
|
||||||
|
if cfg_table.VendorGuid == ACPI_TABLE_GUID {
|
||||||
|
match validate_rsdp(cfg_table.VendorTable, false) {
|
||||||
|
Ok(length) => {
|
||||||
|
acpi = Some(unsafe {
|
||||||
|
core::slice::from_raw_parts(cfg_table.VendorTable as *const u8, length)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Err(_) => log::warn!(
|
||||||
|
"Found RSDP that was not valid at {:p}",
|
||||||
|
cfg_table.VendorTable as *const u8
|
||||||
|
),
|
||||||
|
}
|
||||||
|
} else if cfg_table.VendorGuid == ACPI_20_TABLE_GUID {
|
||||||
|
match validate_rsdp(cfg_table.VendorTable, true) {
|
||||||
|
Ok(length) => {
|
||||||
|
acpi2 = Some(unsafe {
|
||||||
|
core::slice::from_raw_parts(cfg_table.VendorTable as *const u8, length)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Err(_) => log::warn!(
|
||||||
|
"Found RSDP that was not valid at {:p}",
|
||||||
|
cfg_table.VendorTable as *const u8
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let rsdp_area = acpi2.or(acpi).unwrap_or(&[]);
|
||||||
|
|
||||||
|
if !rsdp_area.is_empty() {
|
||||||
|
unsafe {
|
||||||
|
// Copy to page aligned area
|
||||||
|
let size = rsdp_area.len();
|
||||||
|
let base = os.alloc_zeroed_page_aligned(size);
|
||||||
|
slice::from_raw_parts_mut(base, size).copy_from_slice(rsdp_area);
|
||||||
|
Some((base as u64, size as u64))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,236 @@
|
|||||||
|
use core::{arch::asm, fmt::Write, mem, slice};
|
||||||
|
use uefi::status::Result;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
arch::{ENTRY_ADDRESS_MASK, PAGE_ENTRIES, PF_PRESENT, PF_TABLE, PHYS_OFFSET},
|
||||||
|
logger::LOGGER,
|
||||||
|
KernelArgs,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::super::{memory_map::memory_map, OsEfi};
|
||||||
|
|
||||||
|
unsafe fn dump_page_tables(table_phys: u64, table_virt: u64, table_level: u64) {
|
||||||
|
unsafe {
|
||||||
|
let entries = slice::from_raw_parts(table_phys as *const u64, PAGE_ENTRIES);
|
||||||
|
for (i, entry) in entries.iter().enumerate() {
|
||||||
|
let phys = entry & ENTRY_ADDRESS_MASK;
|
||||||
|
let flags = entry & !ENTRY_ADDRESS_MASK;
|
||||||
|
if flags & PF_PRESENT == 0 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let mut shift = 39u64;
|
||||||
|
for _ in 0..table_level {
|
||||||
|
shift -= 9;
|
||||||
|
print!("\t");
|
||||||
|
}
|
||||||
|
let virt = table_virt + (i as u64) << shift;
|
||||||
|
println!(
|
||||||
|
"index {} virt {:#x}: phys {:#x} flags {:#x}",
|
||||||
|
i, virt, phys, flags
|
||||||
|
);
|
||||||
|
if table_level < 3 && flags & PF_TABLE == PF_TABLE {
|
||||||
|
dump_page_tables(phys, virt, table_level + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe extern "C" fn kernel_entry(
|
||||||
|
page_phys: usize,
|
||||||
|
stack: u64,
|
||||||
|
func: u64,
|
||||||
|
args: *const KernelArgs,
|
||||||
|
) -> ! {
|
||||||
|
unsafe {
|
||||||
|
// Read memory map and exit boot services
|
||||||
|
memory_map().exit_boot_services();
|
||||||
|
|
||||||
|
let currentel: u64;
|
||||||
|
asm!(
|
||||||
|
"mrs {0}, currentel", // Read current exception level
|
||||||
|
out(reg) currentel,
|
||||||
|
);
|
||||||
|
if currentel == (2 << 2) {
|
||||||
|
// Need to drop from EL2 to EL1
|
||||||
|
|
||||||
|
// Allow access to timers
|
||||||
|
asm!(
|
||||||
|
"mrs {0}, cnthctl_el2",
|
||||||
|
"orr {0}, {0}, #0x3",
|
||||||
|
"msr cnthctl_el2, {0}",
|
||||||
|
"msr cntvoff_el2, xzr",
|
||||||
|
out(reg) _
|
||||||
|
);
|
||||||
|
|
||||||
|
// Initialize ID registers
|
||||||
|
asm!(
|
||||||
|
"mrs {0}, midr_el1",
|
||||||
|
"msr vpidr_el2, {0}",
|
||||||
|
"mrs {0}, mpidr_el1",
|
||||||
|
"msr vmpidr_el2, {0}",
|
||||||
|
out(reg) _
|
||||||
|
);
|
||||||
|
|
||||||
|
// Disable traps
|
||||||
|
asm!(
|
||||||
|
"msr cptr_el2, {0}",
|
||||||
|
"msr hstr_el2, xzr",
|
||||||
|
in(reg) 0x33FF as u64
|
||||||
|
);
|
||||||
|
|
||||||
|
// Enable floating point
|
||||||
|
asm!(
|
||||||
|
"msr cpacr_el1, {0}",
|
||||||
|
in(reg) (3 << 20) as u64
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set EL1 system control register
|
||||||
|
asm!(
|
||||||
|
"msr sctlr_el1, {0}",
|
||||||
|
in(reg) 0x30d00800 as u64
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set EL1 stack and VBAR
|
||||||
|
asm!(
|
||||||
|
"mov {0}, sp",
|
||||||
|
"msr sp_el1, {0}",
|
||||||
|
"mrs {0}, vbar_el2",
|
||||||
|
"msr vbar_el1, {0}",
|
||||||
|
out(reg) _
|
||||||
|
);
|
||||||
|
|
||||||
|
// Configure execution state of EL1 as aarch64 and disable hypervisor call.
|
||||||
|
asm!(
|
||||||
|
"msr hcr_el2, {0}",
|
||||||
|
in(reg) ((1u64 << 31) | (1u64 << 29)),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set saved program status register
|
||||||
|
asm!(
|
||||||
|
"msr spsr_el2, {0}",
|
||||||
|
in(reg) 0x3C5 as u64
|
||||||
|
);
|
||||||
|
|
||||||
|
// Switch to EL1
|
||||||
|
asm!(
|
||||||
|
"adr {0}, 1f",
|
||||||
|
"msr elr_el2, {0}",
|
||||||
|
"eret",
|
||||||
|
"1:",
|
||||||
|
out(reg) _
|
||||||
|
);
|
||||||
|
} else if currentel == (1 << 2) {
|
||||||
|
// Already in EL1
|
||||||
|
} else {
|
||||||
|
//TODO: what to do if not EL2 or already EL1?
|
||||||
|
loop {
|
||||||
|
asm!("wfi");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable MMU
|
||||||
|
asm!(
|
||||||
|
"mrs {0}, sctlr_el1", // Read system control register
|
||||||
|
"bic {0}, {0}, 1", // Clear MMU enable bit
|
||||||
|
"msr sctlr_el1, {0}", // Write system control register
|
||||||
|
"isb", // Instruction sync barrier
|
||||||
|
out(reg) _,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set MAIR
|
||||||
|
// You can think about MAIRs as of an array with 8 elements each of 8 bits long.
|
||||||
|
// You can store inside MAIRs up to 8 attributes sets and reffer them by the index 0..7 stored in INDX (AttrIndx) field of the table descriptor.
|
||||||
|
// https://lowenware.com/blog/aarch64-mmu-programming/
|
||||||
|
// https://developer.arm.com/documentation/102376/0200/Describing-memory-in-AArch64
|
||||||
|
// https://developer.arm.com/documentation/ddi0595/2021-06/AArch64-Registers/MAIR-EL1--Memory-Attribute-Indirection-Register--EL1-
|
||||||
|
// Attribute 0 (0xFF) - normal memory, caches are enabled
|
||||||
|
// Attribute 1 (0x44) - normal memory, caches are disabled. Atomics wouldn't work here if memory doesn't support exclusive access (most real hardware don't)
|
||||||
|
// Attribute 2 (0x00) - nGnRnE device memory, caches are disabled, gathering, re-ordering, and early write acknowledgement aren't allowed.
|
||||||
|
asm!(
|
||||||
|
"msr mair_el1, {0}",
|
||||||
|
in(reg) 0x00000000000044FF as u64, // MAIR: Arrange for Device, Normal Non-Cache, Normal Write-Back access types
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set TCR
|
||||||
|
asm!(
|
||||||
|
"mrs {1}, id_aa64mmfr0_el1", // Read memory model feature register
|
||||||
|
"bfi {0}, {1}, #32, #3",
|
||||||
|
"msr tcr_el1, {0}", // Write translation control register
|
||||||
|
"isb", // Instruction sync barrier
|
||||||
|
in(reg) 0x1085100510u64, // TCR: (TxSZ, ASID_16, TG1_4K, Cache Attrs, SMP Attrs)
|
||||||
|
out(reg) _,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set page tables
|
||||||
|
asm!(
|
||||||
|
"dsb sy", // Data sync barrier
|
||||||
|
"msr ttbr1_el1, {0}", // Set higher half page table
|
||||||
|
"msr ttbr0_el1, {0}", // Set lower half page table
|
||||||
|
"isb", // Instruction sync barrier
|
||||||
|
"dsb ishst", // Data sync barrier, only for stores, and only for inner shareable domain
|
||||||
|
"tlbi vmalle1is", // Invalidate TLB
|
||||||
|
"dsb ish", // Dta sync bariar, only for inner shareable domain
|
||||||
|
"isb", // Instruction sync barrier
|
||||||
|
in(reg) page_phys,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Enable MMU
|
||||||
|
asm!(
|
||||||
|
"mrs {2}, sctlr_el1", // Read system control register
|
||||||
|
"bic {2}, {2}, {0}", // Clear bits
|
||||||
|
"orr {2}, {2}, {1}", // Set bits
|
||||||
|
"msr sctlr_el1, {2}", // Write system control register
|
||||||
|
"isb", // Instruction sync barrier
|
||||||
|
in(reg) 0x32802c2u64, // Clear SCTLR bits: (EE, EOE, IESB, WXN, UMA, ITD, THEE, A)
|
||||||
|
in(reg) 0x3485d13du64, // Set SCTLR bits: (LSMAOE, nTLSMD, UCI, SPAN, nTWW, nTWI, UCT, DZE, I, SED, SA0, SA, C, M, CP15BEN)
|
||||||
|
out(reg) _,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set stack
|
||||||
|
asm!("mov sp, {}", in(reg) stack);
|
||||||
|
|
||||||
|
// Call kernel entry
|
||||||
|
let entry_fn: extern "C" fn(*const KernelArgs) -> ! = mem::transmute(func);
|
||||||
|
entry_fn(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() -> Result<()> {
|
||||||
|
LOGGER.init();
|
||||||
|
|
||||||
|
let mut os = OsEfi::new();
|
||||||
|
|
||||||
|
// Disable cursor
|
||||||
|
let _ = (os.st.ConsoleOut.EnableCursor)(os.st.ConsoleOut, false);
|
||||||
|
|
||||||
|
let currentel: u64;
|
||||||
|
unsafe {
|
||||||
|
asm!(
|
||||||
|
"mrs {0}, currentel", // Read current exception level
|
||||||
|
out(reg) currentel,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
log::info!("Currently in EL{}", (currentel >> 2) & 3);
|
||||||
|
|
||||||
|
let (page_phys, func, args) = crate::main(&mut os);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let stack = args.stack_base + args.stack_size + PHYS_OFFSET;
|
||||||
|
|
||||||
|
// dump_page_tables(page_phys as _, 0, 0);
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"kernel_entry({:#x}, {:#x}, {:#x}, {:p})",
|
||||||
|
page_phys, stack, func, &args
|
||||||
|
);
|
||||||
|
println!("{:#x?}", args);
|
||||||
|
|
||||||
|
kernel_entry(page_phys, stack, func, &args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable_interrupts() {
|
||||||
|
unsafe {
|
||||||
|
asm!("msr daifset, #2");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
mod aarch64;
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
pub use self::aarch64::*;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
mod x86_64;
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
pub use self::x86_64::*;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "riscv64")]
|
||||||
|
mod riscv64;
|
||||||
|
#[cfg(target_arch = "riscv64")]
|
||||||
|
pub use self::riscv64::*;
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
use std::proto::Protocol;
|
||||||
|
use uefi::guid::Guid;
|
||||||
|
use uefi::status::{Result, Status};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct RiscVEfiBootProtocol {
|
||||||
|
pub revision: u64,
|
||||||
|
pub efi_get_boot_hartid:
|
||||||
|
unsafe extern "efiapi" fn(this: *mut Self, phartid: *mut usize) -> Status,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RiscVEfiBootProtocol {
|
||||||
|
pub const GUID: Guid = Guid::parse_str("ccd15fec-6f73-4eec-8395-3e69e4b940bf");
|
||||||
|
// pub const REVISION: u64 = 0x00010000;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct RiscVEfiBoot(pub &'static mut RiscVEfiBootProtocol);
|
||||||
|
|
||||||
|
impl Protocol<RiscVEfiBootProtocol> for RiscVEfiBoot {
|
||||||
|
fn guid() -> Guid {
|
||||||
|
RiscVEfiBootProtocol::GUID
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(inner: &'static mut RiscVEfiBootProtocol) -> Self {
|
||||||
|
Self(inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RiscVEfiBoot {
|
||||||
|
pub fn efi_get_boot_hartid(&mut self) -> Result<usize> {
|
||||||
|
let mut boot_hartid: usize = 0;
|
||||||
|
match unsafe { (self.0.efi_get_boot_hartid)(self.0, &mut boot_hartid) } {
|
||||||
|
ok if ok.is_success() => Ok(boot_hartid),
|
||||||
|
err => Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn efi_get_boot_hartid() -> Result<usize> {
|
||||||
|
let handles = RiscVEfiBoot::locate_handle()?;
|
||||||
|
let handle = handles.first().ok_or(Status::NOT_FOUND)?;
|
||||||
|
let mut proto = RiscVEfiBoot::handle_protocol(*handle)?;
|
||||||
|
proto.efi_get_boot_hartid()
|
||||||
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
use core::arch::{global_asm, naked_asm};
|
||||||
|
|
||||||
|
/// Unfortunately this can't be written in Rust because it might use some not-yet
|
||||||
|
/// relocated data such as jump tables
|
||||||
|
#[unsafe(naked)]
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
extern "C" fn coff_relocate(dynentry: *const u8, base: usize) -> usize {
|
||||||
|
unsafe {
|
||||||
|
naked_asm!(
|
||||||
|
"
|
||||||
|
mv t4, zero // RELA
|
||||||
|
li t5, -1 // RELASZ
|
||||||
|
li t6, -1 // RELAENT
|
||||||
|
|
||||||
|
5:
|
||||||
|
ld t0, 0(a0)
|
||||||
|
beqz t0, 6f
|
||||||
|
addi a0, a0, 16
|
||||||
|
addi t0, t0, -4
|
||||||
|
bltz t0, 3f // fail on DT_NEEDED=1, DT_PLTRELSZ=2, DT_PLTGOT=3
|
||||||
|
addi t0, t0, -3
|
||||||
|
bltz t0, 5b // skip DT_HASH=4, DT_STRTAB=5, DT_SYMTAB=6
|
||||||
|
bnez t0, 2f
|
||||||
|
ld t4, -8(a0) // DT_RELA=7
|
||||||
|
j 5b
|
||||||
|
2: addi t0, t0, -1 // DT_RELASZ=8
|
||||||
|
bnez t0, 2f
|
||||||
|
ld t5, -8(a0)
|
||||||
|
j 5b
|
||||||
|
2: addi t0, t0, -1 // DT_RELAENT=9
|
||||||
|
bnez t0, 2f
|
||||||
|
ld t6, -8(a0)
|
||||||
|
j 5b
|
||||||
|
2: addi t0, t0, -3
|
||||||
|
bltz t0, 5b // skip DT_STRSZ=10, DT_SYMENT=11
|
||||||
|
addi t0, t0, -2
|
||||||
|
bltz t0, 3f // fail on DT_INIT=12, DT_FINI=13
|
||||||
|
beqz t0, 5b // skip DT_SONAME=14
|
||||||
|
2: addi t0, t0, -2
|
||||||
|
bltz t0, 3f // fail on DT_RPATH
|
||||||
|
beqz t0, 5b // skip SYMBOLIC=16
|
||||||
|
li t1, 0x6ffffef5-16
|
||||||
|
sub t0, t0, t1
|
||||||
|
beqz t0, 5b // skip DT_GNU_HASH=0x6ffffef5
|
||||||
|
nop
|
||||||
|
3: // error
|
||||||
|
mv a0, zero
|
||||||
|
ret
|
||||||
|
|
||||||
|
6:
|
||||||
|
bnez t4, 2f
|
||||||
|
4: // success
|
||||||
|
li a0, 1
|
||||||
|
ret
|
||||||
|
2: bltz t5, 3b
|
||||||
|
blez t6, 3b
|
||||||
|
|
||||||
|
add t4, t4, a1
|
||||||
|
add t5, t5, t4
|
||||||
|
7:
|
||||||
|
bge t4, t5, 4b
|
||||||
|
ld t0, 0(t4) // r_offset
|
||||||
|
add t0, t0, a1
|
||||||
|
lwu t1, 8(t4) // r_type
|
||||||
|
ld t2, 16(t4) // r_addend
|
||||||
|
add t4, t4, t6
|
||||||
|
addi t1, t1, -3 // R_RISCV_RELATIVE=3
|
||||||
|
bnez t1, 3b
|
||||||
|
add t2, t2, a1 // RELATIVE: *value = base + addend
|
||||||
|
sd t2, 0(t0)
|
||||||
|
j 7b
|
||||||
|
"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
global_asm!(
|
||||||
|
r#"
|
||||||
|
.global coff_start
|
||||||
|
coff_start:
|
||||||
|
.option norelax
|
||||||
|
addi sp, sp, -24
|
||||||
|
sd a0, 0(sp)
|
||||||
|
sd a1, 8(sp)
|
||||||
|
sd ra, 16(sp)
|
||||||
|
lla a0, _DYNAMIC
|
||||||
|
lla a1, ImageBase // actual loaded image base to relocate to
|
||||||
|
jal coff_relocate
|
||||||
|
.option relax
|
||||||
|
mv t0, a0
|
||||||
|
ld a0, 0(sp)
|
||||||
|
ld a1, 8(sp)
|
||||||
|
ld ra, 16(sp)
|
||||||
|
addi sp, sp, 24
|
||||||
|
beqz t0, 2f
|
||||||
|
j efi_main
|
||||||
|
2: ret
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
|
||||||
|
// GNU-EFI .reloc trick to make objcopy say we are relocatable
|
||||||
|
global_asm!(
|
||||||
|
r#"
|
||||||
|
.section .data
|
||||||
|
DUMMY_RELOCATION: .4byte 0
|
||||||
|
.section .reloc, "a"
|
||||||
|
|
||||||
|
2:
|
||||||
|
.4byte DUMMY_RELOCATION - ImageBase
|
||||||
|
.4byte 12
|
||||||
|
.4byte 0
|
||||||
|
"#
|
||||||
|
);
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
use crate::KernelArgs;
|
||||||
|
use crate::arch::PHYS_OFFSET;
|
||||||
|
use crate::arch::SATP_BITS;
|
||||||
|
use crate::logger::LOGGER;
|
||||||
|
use crate::os::OsEfi;
|
||||||
|
use crate::os::uefi::memory_map::memory_map;
|
||||||
|
use core::arch::asm;
|
||||||
|
use core::mem;
|
||||||
|
use uefi::status::Result;
|
||||||
|
|
||||||
|
mod boot_protocol;
|
||||||
|
mod coff_helper;
|
||||||
|
|
||||||
|
pub use boot_protocol::*;
|
||||||
|
|
||||||
|
unsafe extern "C" fn kernel_entry(
|
||||||
|
page_phys: usize,
|
||||||
|
stack: u64,
|
||||||
|
func: u64,
|
||||||
|
args: *const KernelArgs,
|
||||||
|
) -> ! {
|
||||||
|
unsafe {
|
||||||
|
// Set page tables
|
||||||
|
asm!(
|
||||||
|
"csrw satp, {0}",
|
||||||
|
"sfence.vma",
|
||||||
|
in(reg) (page_phys >> 12 | SATP_BITS << 60)
|
||||||
|
);
|
||||||
|
|
||||||
|
let entry_fn: extern "C" fn(*const KernelArgs) -> ! = mem::transmute(func);
|
||||||
|
|
||||||
|
// Set stack and go to kernel
|
||||||
|
asm!("mv sp, {0}",
|
||||||
|
"mv a0, {1}",
|
||||||
|
"jalr {2}",
|
||||||
|
in(reg) stack,
|
||||||
|
in(reg) args,
|
||||||
|
in(reg) entry_fn
|
||||||
|
);
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() -> Result<()> {
|
||||||
|
LOGGER.init();
|
||||||
|
|
||||||
|
let mut os = OsEfi::new();
|
||||||
|
|
||||||
|
// Disable cursor
|
||||||
|
let _ = (os.st.ConsoleOut.EnableCursor)(os.st.ConsoleOut, false);
|
||||||
|
|
||||||
|
let (page_phys, func, args) = crate::main(&mut os);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
memory_map().exit_boot_services();
|
||||||
|
|
||||||
|
kernel_entry(
|
||||||
|
page_phys,
|
||||||
|
args.stack_base + args.stack_size + PHYS_OFFSET,
|
||||||
|
func,
|
||||||
|
&args,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable_interrupts() {
|
||||||
|
unsafe {
|
||||||
|
asm!("csrci sstatus, 2");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
use core::{arch::asm, mem};
|
||||||
|
use uefi::status::Result;
|
||||||
|
use x86::{
|
||||||
|
controlregs::{self, Cr0, Cr4},
|
||||||
|
msr,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{logger::LOGGER, KernelArgs};
|
||||||
|
|
||||||
|
use super::super::{memory_map::memory_map, OsEfi};
|
||||||
|
|
||||||
|
unsafe extern "C" fn kernel_entry(
|
||||||
|
page_phys: usize,
|
||||||
|
stack: u64,
|
||||||
|
func: u64,
|
||||||
|
args: *const KernelArgs,
|
||||||
|
) -> ! {
|
||||||
|
unsafe {
|
||||||
|
// Read memory map and exit boot services
|
||||||
|
memory_map().exit_boot_services();
|
||||||
|
|
||||||
|
// Enable FXSAVE/FXRSTOR, Page Global, Page Address Extension, and Page Size Extension
|
||||||
|
let mut cr4 = controlregs::cr4();
|
||||||
|
cr4 |= Cr4::CR4_ENABLE_SSE
|
||||||
|
| Cr4::CR4_ENABLE_GLOBAL_PAGES
|
||||||
|
| Cr4::CR4_ENABLE_PAE
|
||||||
|
| Cr4::CR4_ENABLE_PSE;
|
||||||
|
controlregs::cr4_write(cr4);
|
||||||
|
|
||||||
|
// Enable Long mode and NX bit
|
||||||
|
let mut efer = msr::rdmsr(msr::IA32_EFER);
|
||||||
|
efer |= 1 << 11 | 1 << 8;
|
||||||
|
msr::wrmsr(msr::IA32_EFER, efer);
|
||||||
|
|
||||||
|
// Set new page map
|
||||||
|
controlregs::cr3_write(page_phys as u64);
|
||||||
|
|
||||||
|
// Enable paging, write protect kernel, protected mode
|
||||||
|
let mut cr0 = controlregs::cr0();
|
||||||
|
cr0 |= Cr0::CR0_ENABLE_PAGING | Cr0::CR0_WRITE_PROTECT | Cr0::CR0_PROTECTED_MODE;
|
||||||
|
controlregs::cr0_write(cr0);
|
||||||
|
|
||||||
|
// Set stack
|
||||||
|
asm!("mov rsp, {}", in(reg) stack);
|
||||||
|
|
||||||
|
// Call kernel entry
|
||||||
|
let entry_fn: extern "sysv64" fn(*const KernelArgs) -> ! = mem::transmute(func);
|
||||||
|
entry_fn(args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() -> Result<()> {
|
||||||
|
LOGGER.init();
|
||||||
|
|
||||||
|
let mut os = OsEfi::new();
|
||||||
|
|
||||||
|
// Disable cursor
|
||||||
|
let _ = (os.st.ConsoleOut.EnableCursor)(os.st.ConsoleOut, false);
|
||||||
|
|
||||||
|
let (page_phys, func, args) = crate::main(&mut os);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
kernel_entry(
|
||||||
|
page_phys,
|
||||||
|
args.stack_base
|
||||||
|
+ args.stack_size
|
||||||
|
+ if crate::KERNEL_64BIT {
|
||||||
|
crate::arch::x64::PHYS_OFFSET
|
||||||
|
} else {
|
||||||
|
crate::arch::x32::PHYS_OFFSET as u64
|
||||||
|
},
|
||||||
|
func,
|
||||||
|
&args,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disable_interrupts() {
|
||||||
|
unsafe {
|
||||||
|
asm!("cli");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,647 @@
|
|||||||
|
use alloc::{string::String, vec, vec::Vec};
|
||||||
|
use core::{fmt::Write, mem, ptr, slice};
|
||||||
|
use uefi::{
|
||||||
|
device::{
|
||||||
|
DevicePath, DevicePathAcpiType, DevicePathBbsType, DevicePathEndType,
|
||||||
|
DevicePathHardwareType, DevicePathMediaType, DevicePathMessagingType, DevicePathType,
|
||||||
|
},
|
||||||
|
guid::Guid,
|
||||||
|
status::Status,
|
||||||
|
Handle,
|
||||||
|
};
|
||||||
|
use uefi_std::{fs::FileSystem, loaded_image::LoadedImage, proto::Protocol};
|
||||||
|
|
||||||
|
use super::disk::{DiskEfi, DiskOrFileEfi};
|
||||||
|
use redoxfs::Disk;
|
||||||
|
|
||||||
|
const LINUX_FS_GUID: [u8; 16] = [
|
||||||
|
0xAF, 0x3D, 0xC6, 0x0F, 0x83, 0x84, 0x72, 0x47,
|
||||||
|
0x8E, 0x79, 0x3D, 0x69, 0xD8, 0x47, 0x7D, 0xE4,
|
||||||
|
];
|
||||||
|
|
||||||
|
fn gpt_find_redoxfs_offset(disk: &mut DiskEfi) -> u64 {
|
||||||
|
let block_size = disk.0.Media.BlockSize as u64;
|
||||||
|
if block_size == 0 {
|
||||||
|
return 2 * crate::MIBI as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut header_buf = [0u8; 512];
|
||||||
|
let lba1_block = block_size / redoxfs::BLOCK_SIZE;
|
||||||
|
let header_read_len = if 512 <= redoxfs::BLOCK_SIZE as usize {
|
||||||
|
redoxfs::BLOCK_SIZE as usize
|
||||||
|
} else {
|
||||||
|
512
|
||||||
|
};
|
||||||
|
let mut read_buf = vec![0u8; header_read_len];
|
||||||
|
if unsafe { disk.read_at(lba1_block, &mut read_buf) }.is_err() {
|
||||||
|
log::warn!("GPT: failed to read LBA 1");
|
||||||
|
return 2 * crate::MIBI as u64;
|
||||||
|
}
|
||||||
|
let copy_len = 512.min(read_buf.len());
|
||||||
|
header_buf[..copy_len].copy_from_slice(&read_buf[..copy_len]);
|
||||||
|
|
||||||
|
if &header_buf[0..8] != b"EFI PART" {
|
||||||
|
log::warn!("GPT: no valid signature at LBA 1");
|
||||||
|
return 2 * crate::MIBI as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
let read_le_u32 = |off: usize| -> u32 {
|
||||||
|
u32::from_le_bytes(header_buf[off..off + 4].try_into().unwrap_or([0; 4]))
|
||||||
|
};
|
||||||
|
let read_le_u64 = |off: usize| -> u64 {
|
||||||
|
u64::from_le_bytes(header_buf[off..off + 8].try_into().unwrap_or([0; 8]))
|
||||||
|
};
|
||||||
|
|
||||||
|
let _revision = read_le_u32(8);
|
||||||
|
let _header_size = read_le_u32(12);
|
||||||
|
let partition_entry_start_lba = read_le_u64(72);
|
||||||
|
let num_entries = read_le_u32(80);
|
||||||
|
let entry_size = read_le_u32(84);
|
||||||
|
|
||||||
|
if num_entries == 0 || entry_size == 0 || num_entries > 128 {
|
||||||
|
log::warn!("GPT: invalid entry count {} or size {}", num_entries, entry_size);
|
||||||
|
return 2 * crate::MIBI as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
let entries_byte_offset = partition_entry_start_lba * block_size;
|
||||||
|
let entries_start_block = entries_byte_offset / redoxfs::BLOCK_SIZE;
|
||||||
|
|
||||||
|
let total_entries_bytes = num_entries as usize * entry_size as usize;
|
||||||
|
let read_size = total_entries_bytes.min(4096);
|
||||||
|
let mut entries_buf = vec![0u8; read_size + redoxfs::BLOCK_SIZE as usize];
|
||||||
|
let entries_read_blocks = (read_size as u64).div_ceil(redoxfs::BLOCK_SIZE) as usize;
|
||||||
|
if unsafe { disk.read_at(entries_start_block, &mut entries_buf[..entries_read_blocks * redoxfs::BLOCK_SIZE as usize]) }.is_err() {
|
||||||
|
log::warn!("GPT: failed to read partition entries");
|
||||||
|
return 2 * crate::MIBI as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
let entries_per_chunk = read_size / entry_size as usize;
|
||||||
|
for i in 0..entries_per_chunk.min(num_entries as usize) {
|
||||||
|
let off = i * entry_size as usize;
|
||||||
|
if off + entry_size as usize > entries_buf.len() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let entry = &entries_buf[off..off + entry_size as usize];
|
||||||
|
|
||||||
|
let type_guid = &entry[0..16];
|
||||||
|
if type_guid == [0u8; 16] {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if type_guid == LINUX_FS_GUID {
|
||||||
|
let first_lba = u64::from_le_bytes(
|
||||||
|
entry[32..40].try_into().unwrap_or([0; 8]),
|
||||||
|
);
|
||||||
|
let offset_bytes = first_lba * block_size;
|
||||||
|
log::debug!("GPT: found REDOXFS/Linux partition at LBA {} (offset {} bytes)", first_lba, offset_bytes);
|
||||||
|
return offset_bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log::warn!("GPT: no Linux filesystem partition found, falling back to 2 MiB");
|
||||||
|
2 * crate::MIBI as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
fn gpt_find_redoxfs_offset_from_slice(data: &[u8]) -> u64 {
|
||||||
|
if data.len() < 520 {
|
||||||
|
return 2 * crate::MIBI as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
let header = &data[512..];
|
||||||
|
if &header[0..8] != b"EFI PART" {
|
||||||
|
return 2 * crate::MIBI as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
let read_le_u32 = |off: usize| -> u32 {
|
||||||
|
u32::from_le_bytes(header[off..off + 4].try_into().unwrap_or([0; 4]))
|
||||||
|
};
|
||||||
|
let read_le_u64 = |off: usize| -> u64 {
|
||||||
|
u64::from_le_bytes(header[off..off + 8].try_into().unwrap_or([0; 8]))
|
||||||
|
};
|
||||||
|
|
||||||
|
let partition_entry_start_lba = read_le_u64(72);
|
||||||
|
let num_entries = read_le_u32(80);
|
||||||
|
let entry_size = read_le_u32(84);
|
||||||
|
|
||||||
|
if num_entries == 0 || entry_size == 0 || num_entries > 128 {
|
||||||
|
return 2 * crate::MIBI as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
let gpt_lba_size: u64 = 512;
|
||||||
|
let entries_byte_offset = partition_entry_start_lba * gpt_lba_size;
|
||||||
|
|
||||||
|
for i in 0..num_entries as usize {
|
||||||
|
let off = entries_byte_offset as usize + i * entry_size as usize;
|
||||||
|
if off + entry_size as usize > data.len() {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let entry = &data[off..off + entry_size as usize];
|
||||||
|
|
||||||
|
let type_guid = &entry[0..16];
|
||||||
|
if type_guid == [0u8; 16] {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if type_guid == LINUX_FS_GUID {
|
||||||
|
let first_lba = u64::from_le_bytes(
|
||||||
|
entry[32..40].try_into().unwrap_or([0; 8]),
|
||||||
|
);
|
||||||
|
return first_lba * gpt_lba_size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
2 * crate::MIBI as u64
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
enum DevicePathRelation {
|
||||||
|
This,
|
||||||
|
Parent(usize),
|
||||||
|
Child(usize),
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn device_path_relation(a_path: &DevicePath, b_path: &DevicePath) -> DevicePathRelation {
|
||||||
|
let mut a_iter = DevicePathIter::new(a_path);
|
||||||
|
let mut b_iter = DevicePathIter::new(b_path);
|
||||||
|
loop {
|
||||||
|
match (a_iter.next(), b_iter.next()) {
|
||||||
|
(None, None) => return DevicePathRelation::This,
|
||||||
|
(None, Some(_)) => return DevicePathRelation::Parent(b_iter.count()),
|
||||||
|
(Some(_), None) => return DevicePathRelation::Child(a_iter.count()),
|
||||||
|
(Some((a_node, a_data)), Some((b_node, b_data))) => {
|
||||||
|
if a_node.Type != b_node.Type {
|
||||||
|
return DevicePathRelation::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if a_node.SubType != b_node.SubType {
|
||||||
|
return DevicePathRelation::None;
|
||||||
|
}
|
||||||
|
|
||||||
|
if a_data != b_data {
|
||||||
|
return DevicePathRelation::None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn esp_live_image(esp_handle: Handle, esp_device_path: &DevicePath) -> Option<Vec<u8>> {
|
||||||
|
let mut esp_fs = match FileSystem::handle_protocol(esp_handle) {
|
||||||
|
Ok(esp_fs) => esp_fs,
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("Failed to find SimpleFileSystem protocol: {:?}", err);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut root = match esp_fs.root() {
|
||||||
|
Ok(root) => root,
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("Failed to open ESP filesystem: {:?}", err);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const fn as_utf16_str<const N: usize>(s: [u8; N]) -> [u16; N] {
|
||||||
|
let mut ret = [0; N];
|
||||||
|
let mut i = 0;
|
||||||
|
while i < N {
|
||||||
|
ret[i] = s[i] as u16;
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
|
||||||
|
let filename = const { &as_utf16_str(*b"redox-live.iso\0") };
|
||||||
|
let mut live_image = match root.open(filename) {
|
||||||
|
Ok(live_image) => live_image,
|
||||||
|
Err(Status::NOT_FOUND) => return None,
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!(
|
||||||
|
"Failed to open {}\\redox-live.iso: {:?}",
|
||||||
|
device_path_to_string(esp_device_path),
|
||||||
|
err
|
||||||
|
);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut buffer = Vec::new();
|
||||||
|
|
||||||
|
live_image.read_to_end(&mut buffer).unwrap();
|
||||||
|
|
||||||
|
Some(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DiskDevice {
|
||||||
|
pub handle: Handle,
|
||||||
|
pub disk: DiskOrFileEfi,
|
||||||
|
pub partition_offset: u64,
|
||||||
|
pub device_path: DevicePathProtocol,
|
||||||
|
pub file_path: Option<&'static str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn disk_device_priority() -> Vec<DiskDevice> {
|
||||||
|
// Get the handle of the partition this program was loaded from, which should be the ESP
|
||||||
|
let esp_handle = match LoadedImage::handle_protocol(std::handle()) {
|
||||||
|
Ok(loaded_image) => loaded_image.0.DeviceHandle,
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("Failed to find LoadedImage protocol: {:?}", err);
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the device path of the ESP
|
||||||
|
let esp_device_path = match DevicePathProtocol::handle_protocol(esp_handle) {
|
||||||
|
Ok(ok) => ok,
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!(
|
||||||
|
"Failed to find device path protocol on {:?}: {:?}",
|
||||||
|
esp_handle,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
return Vec::new();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if cfg!(feature = "live") {
|
||||||
|
if let Some(buffer) = esp_live_image(esp_handle, esp_device_path.0) {
|
||||||
|
let partition_offset = if buffer.len() > 520 && &buffer[512..520] == b"EFI PART" {
|
||||||
|
gpt_find_redoxfs_offset_from_slice(&buffer)
|
||||||
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
return vec![DiskDevice {
|
||||||
|
handle: esp_handle,
|
||||||
|
partition_offset,
|
||||||
|
disk: DiskOrFileEfi::File(buffer),
|
||||||
|
device_path: esp_device_path,
|
||||||
|
file_path: Some("redox-live.iso"),
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all block I/O handles along with their block I/O implementations and device paths
|
||||||
|
let handles = match DiskEfi::locate_handle() {
|
||||||
|
Ok(ok) => ok,
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("Failed to find block I/O handles: {:?}", err);
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut devices = Vec::with_capacity(handles.len());
|
||||||
|
for handle in handles {
|
||||||
|
let mut disk = match DiskEfi::handle_protocol(handle) {
|
||||||
|
Ok(ok) => ok,
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!(
|
||||||
|
"Failed to find block I/O protocol on {:?}: {:?}",
|
||||||
|
handle,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !disk.0.Media.MediaPresent {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let device_path = match DevicePathProtocol::handle_protocol(handle) {
|
||||||
|
Ok(ok) => ok,
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!(
|
||||||
|
"Failed to find device path protocol on {:?}: {:?}",
|
||||||
|
handle,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let partition_offset = if disk.0.Media.LogicalPartition {
|
||||||
|
0
|
||||||
|
} else {
|
||||||
|
gpt_find_redoxfs_offset(&mut disk)
|
||||||
|
};
|
||||||
|
devices.push(DiskDevice {
|
||||||
|
handle,
|
||||||
|
partition_offset,
|
||||||
|
disk: DiskOrFileEfi::Disk(disk),
|
||||||
|
device_path,
|
||||||
|
file_path: None,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find possible boot disks
|
||||||
|
let mut boot_disks = Vec::with_capacity(1);
|
||||||
|
{
|
||||||
|
let mut i = 0;
|
||||||
|
while i < devices.len() {
|
||||||
|
if let DevicePathRelation::Parent(0) =
|
||||||
|
device_path_relation(devices[i].device_path.0, esp_device_path.0)
|
||||||
|
{
|
||||||
|
boot_disks.push(devices.remove(i));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find all children of possible boot devices
|
||||||
|
let mut priority = Vec::with_capacity(devices.capacity());
|
||||||
|
for boot_disk in boot_disks {
|
||||||
|
let mut i = 0;
|
||||||
|
while i < devices.len() {
|
||||||
|
// Only prioritize non-ESP devices
|
||||||
|
if devices[i].handle != esp_handle {
|
||||||
|
if let DevicePathRelation::Child(0) =
|
||||||
|
device_path_relation(devices[i].device_path.0, boot_disk.device_path.0)
|
||||||
|
{
|
||||||
|
priority.push(devices.remove(i));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
priority.push(boot_disk);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add any remaining devices
|
||||||
|
priority.extend(devices);
|
||||||
|
|
||||||
|
priority
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C, packed)]
|
||||||
|
#[allow(dead_code)]
|
||||||
|
struct DevicePathHarddrive {
|
||||||
|
partition_number: u32,
|
||||||
|
partition_start: u64,
|
||||||
|
partition_size: u64,
|
||||||
|
partition_signature: [u8; 16],
|
||||||
|
partition_format: u8,
|
||||||
|
signature_type: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn device_path_to_string(device_path: &DevicePath) -> String {
|
||||||
|
let mut s = String::new();
|
||||||
|
for (node, node_data) in DevicePathIter::new(device_path) {
|
||||||
|
let read_u16 = |i: usize| -> u16 { (node_data[i] as u16) | (node_data[i + 1] as u16) << 8 };
|
||||||
|
|
||||||
|
let read_u32 = |i: usize| -> u32 {
|
||||||
|
(node_data[i] as u32)
|
||||||
|
| (node_data[i + 1] as u32) << 8
|
||||||
|
| (node_data[i + 2] as u32) << 16
|
||||||
|
| (node_data[i + 3] as u32) << 24
|
||||||
|
};
|
||||||
|
|
||||||
|
if !s.is_empty() {
|
||||||
|
s.push('/');
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = match DevicePathType::try_from(node.Type) {
|
||||||
|
Ok(path_type) => match path_type {
|
||||||
|
DevicePathType::Hardware => match DevicePathHardwareType::try_from(node.SubType) {
|
||||||
|
Ok(sub_type) => match sub_type {
|
||||||
|
DevicePathHardwareType::Pci if node_data.len() == 2 => {
|
||||||
|
let func = node_data[0];
|
||||||
|
let dev = node_data[1];
|
||||||
|
write!(s, "Pci(0x{dev:X},0x{func:X})")
|
||||||
|
}
|
||||||
|
_ => write!(s, "{path_type:?} {sub_type:?} {node_data:X?}"),
|
||||||
|
},
|
||||||
|
Err(()) => write!(s, "{:?} 0x{:02X} {:X?}", path_type, node.SubType, node_data),
|
||||||
|
},
|
||||||
|
DevicePathType::Acpi => match DevicePathAcpiType::try_from(node.SubType) {
|
||||||
|
Ok(sub_type) => match sub_type {
|
||||||
|
DevicePathAcpiType::Acpi if node_data.len() == 8 => {
|
||||||
|
let hid = read_u32(0);
|
||||||
|
let uid = read_u32(4);
|
||||||
|
if hid & 0xFFFF == 0x41D0 {
|
||||||
|
write!(s, "Acpi(PNP{:04X},0x{:X})", hid >> 16, uid)
|
||||||
|
} else {
|
||||||
|
write!(s, "Acpi(0x{hid:08X},0x{uid:X})")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => write!(s, "{path_type:?} {sub_type:?} {node_data:X?}"),
|
||||||
|
},
|
||||||
|
Err(()) => write!(s, "{:?} 0x{:02X} {:X?}", path_type, node.SubType, node_data),
|
||||||
|
},
|
||||||
|
DevicePathType::Messaging => {
|
||||||
|
match DevicePathMessagingType::try_from(node.SubType) {
|
||||||
|
Ok(sub_type) => match sub_type {
|
||||||
|
DevicePathMessagingType::Sata if node_data.len() == 6 => {
|
||||||
|
let hba_port = read_u16(0);
|
||||||
|
let multiplier_port = read_u16(2);
|
||||||
|
let logical_unit = read_u16(4);
|
||||||
|
if multiplier_port & (1 << 15) != 0 {
|
||||||
|
write!(s, "Sata(0x{hba_port:X},0x{logical_unit:X})")
|
||||||
|
} else {
|
||||||
|
write!(
|
||||||
|
s,
|
||||||
|
"Sata(0x{hba_port:X},0x{multiplier_port:X},0x{logical_unit:X})"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DevicePathMessagingType::Usb if node_data.len() == 2 => {
|
||||||
|
let port = node_data[0];
|
||||||
|
let iface = node_data[1];
|
||||||
|
write!(s, "Usb(0x{port:X},0x{iface:X})")
|
||||||
|
}
|
||||||
|
DevicePathMessagingType::Nvme if node_data.len() == 12 => {
|
||||||
|
let nsid = read_u32(0);
|
||||||
|
let eui = &node_data[4..];
|
||||||
|
if eui == [0, 0, 0, 0, 0, 0, 0, 0] {
|
||||||
|
write!(s, "NVMe(0x{nsid:X})")
|
||||||
|
} else {
|
||||||
|
write!(
|
||||||
|
s,
|
||||||
|
"NVMe(0x{:X},{:02X}-{:02X}-{:02X}-{:02X}-{:02X}-{:02X}-{:02X}-{:02X})",
|
||||||
|
nsid,
|
||||||
|
eui[0],
|
||||||
|
eui[1],
|
||||||
|
eui[2],
|
||||||
|
eui[3],
|
||||||
|
eui[4],
|
||||||
|
eui[5],
|
||||||
|
eui[6],
|
||||||
|
eui[7],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DevicePathMessagingType::Mac
|
||||||
|
if node_data.len() == 33 && node_data[32] == 0
|
||||||
|
|| node_data[32] == 1 =>
|
||||||
|
{
|
||||||
|
write!(
|
||||||
|
s,
|
||||||
|
"Mac({:02x}{:02x}{:02x}{:02x}{:02x}{:02x},{:#02x})",
|
||||||
|
node_data[0],
|
||||||
|
node_data[1],
|
||||||
|
node_data[2],
|
||||||
|
node_data[3],
|
||||||
|
node_data[4],
|
||||||
|
node_data[5],
|
||||||
|
node_data[32],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => write!(s, "{path_type:?} {sub_type:?} {node_data:X?}"),
|
||||||
|
},
|
||||||
|
Err(()) => {
|
||||||
|
write!(s, "{:?} 0x{:02X} {:X?}", path_type, node.SubType, node_data)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DevicePathType::Media => match DevicePathMediaType::try_from(node.SubType) {
|
||||||
|
Ok(sub_type) => {
|
||||||
|
match sub_type {
|
||||||
|
DevicePathMediaType::Harddrive
|
||||||
|
if node_data.len() == mem::size_of::<DevicePathHarddrive>() =>
|
||||||
|
{
|
||||||
|
let harddrive = unsafe {
|
||||||
|
ptr::read(node_data.as_ptr() as *const DevicePathHarddrive)
|
||||||
|
};
|
||||||
|
let partition_number = unsafe {
|
||||||
|
ptr::read_unaligned(ptr::addr_of!(harddrive.partition_number))
|
||||||
|
};
|
||||||
|
match harddrive.signature_type {
|
||||||
|
1 => {
|
||||||
|
let id = unsafe {
|
||||||
|
ptr::read(harddrive.partition_signature.as_ptr()
|
||||||
|
as *const u32)
|
||||||
|
};
|
||||||
|
write!(s, "HD(0x{partition_number:X},MBR,0x{id:X})")
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
let guid = unsafe {
|
||||||
|
ptr::read(harddrive.partition_signature.as_ptr()
|
||||||
|
as *const Guid)
|
||||||
|
};
|
||||||
|
write!(
|
||||||
|
s,
|
||||||
|
"HD(0x{:X},GPT,{:08X}-{:04X}-{:04X}-{:02X}{:02X}-{:02X}{:02X}{:02X}{:02X}{:02X}{:02X})",
|
||||||
|
partition_number,
|
||||||
|
guid.0,
|
||||||
|
guid.1,
|
||||||
|
guid.2,
|
||||||
|
guid.3[0],
|
||||||
|
guid.3[1],
|
||||||
|
guid.3[2],
|
||||||
|
guid.3[3],
|
||||||
|
guid.3[4],
|
||||||
|
guid.3[5],
|
||||||
|
guid.3[6],
|
||||||
|
guid.3[7],
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
write!(
|
||||||
|
s,
|
||||||
|
"HD(0x{:X},0x{:X},{:X?})",
|
||||||
|
partition_number,
|
||||||
|
harddrive.signature_type,
|
||||||
|
harddrive.partition_signature
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DevicePathMediaType::Filepath => {
|
||||||
|
for chunk in node_data.chunks_exact(2) {
|
||||||
|
let data = (chunk[0] as u16) | (chunk[1] as u16) << 8;
|
||||||
|
match unsafe { char::from_u32_unchecked(data as u32) } {
|
||||||
|
'\\' => s.push('/'),
|
||||||
|
c => s.push(c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => write!(s, "{path_type:?} {sub_type:?} {node_data:X?}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(()) => write!(s, "{:?} 0x{:02X} {:X?}", path_type, node.SubType, node_data),
|
||||||
|
},
|
||||||
|
DevicePathType::Bbs => match DevicePathBbsType::try_from(node.SubType) {
|
||||||
|
Ok(sub_type) => write!(s, "{path_type:?} {sub_type:?} {node_data:X?}"),
|
||||||
|
Err(()) => write!(s, "{:?} 0x{:02X} {:X?}", path_type, node.SubType, node_data),
|
||||||
|
},
|
||||||
|
DevicePathType::End => match DevicePathEndType::try_from(node.SubType) {
|
||||||
|
Ok(sub_type) => write!(s, "{path_type:?} {sub_type:?} {node_data:X?}"),
|
||||||
|
Err(()) => write!(s, "{:?} 0x{:02X} {:X?}", path_type, node.SubType, node_data),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Err(()) => {
|
||||||
|
write!(
|
||||||
|
s,
|
||||||
|
"0x{:02X} 0x{:02X} {:X?}",
|
||||||
|
node.Type, node.SubType, node_data
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
s
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DevicePathProtocol(pub &'static mut DevicePath);
|
||||||
|
|
||||||
|
impl Protocol<DevicePath> for DevicePathProtocol {
|
||||||
|
fn guid() -> Guid {
|
||||||
|
uefi::guid::DEVICE_PATH_GUID
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(inner: &'static mut DevicePath) -> Self {
|
||||||
|
Self(inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LoadedImageDevicePathProtocol(pub &'static mut DevicePath);
|
||||||
|
|
||||||
|
impl Protocol<DevicePath> for LoadedImageDevicePathProtocol {
|
||||||
|
fn guid() -> Guid {
|
||||||
|
uefi::guid::LOADED_IMAGE_DEVICE_PATH_GUID
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(inner: &'static mut DevicePath) -> Self {
|
||||||
|
Self(inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DevicePathIter<'a> {
|
||||||
|
device_path: &'a DevicePath,
|
||||||
|
node_ptr: *const DevicePath,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> DevicePathIter<'a> {
|
||||||
|
pub fn new(device_path: &'a DevicePath) -> Self {
|
||||||
|
Self {
|
||||||
|
device_path,
|
||||||
|
node_ptr: device_path as *const DevicePath,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Iterator for DevicePathIter<'a> {
|
||||||
|
type Item = (&'a DevicePath, &'a [u8]);
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let node = unsafe { &*self.node_ptr };
|
||||||
|
|
||||||
|
if node.Type == DevicePathType::End as u8 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let node_data = unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self.node_ptr.add(1) as *mut u8,
|
||||||
|
node.Length.saturating_sub(4) as usize,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
self.node_ptr = (self.node_ptr as usize + node.Length as usize) as *const DevicePath;
|
||||||
|
|
||||||
|
Some((node, node_data))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
use alloc::vec::Vec;
|
||||||
|
use core::slice;
|
||||||
|
use redoxfs::{Disk, BLOCK_SIZE, RECORD_SIZE};
|
||||||
|
use std::proto::Protocol;
|
||||||
|
use syscall::{Error, Result, EINVAL, EIO};
|
||||||
|
use uefi::block_io::BlockIo as UefiBlockIo;
|
||||||
|
use uefi::guid::{Guid, BLOCK_IO_GUID};
|
||||||
|
|
||||||
|
pub enum DiskOrFileEfi {
|
||||||
|
Disk(DiskEfi),
|
||||||
|
File(Vec<u8>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl redoxfs::Disk for DiskOrFileEfi {
|
||||||
|
unsafe fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> syscall::Result<usize> {
|
||||||
|
unsafe {
|
||||||
|
match self {
|
||||||
|
DiskOrFileEfi::Disk(disk_efi) => disk_efi.read_at(block, buffer),
|
||||||
|
DiskOrFileEfi::File(data) => {
|
||||||
|
buffer.copy_from_slice(
|
||||||
|
&data[(block * redoxfs::BLOCK_SIZE) as usize
|
||||||
|
..(block * redoxfs::BLOCK_SIZE) as usize + buffer.len()],
|
||||||
|
);
|
||||||
|
Ok(buffer.len())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn write_at(&mut self, _block: u64, _buffer: &[u8]) -> syscall::Result<usize> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&mut self) -> syscall::Result<u64> {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DiskEfi(pub &'static mut UefiBlockIo, &'static mut [u8]);
|
||||||
|
|
||||||
|
impl Protocol<UefiBlockIo> for DiskEfi {
|
||||||
|
fn guid() -> Guid {
|
||||||
|
BLOCK_IO_GUID
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(inner: &'static mut UefiBlockIo) -> Self {
|
||||||
|
// Hack to get aligned buffer
|
||||||
|
let block = unsafe {
|
||||||
|
let ptr = super::alloc_zeroed_page_aligned(RECORD_SIZE as usize);
|
||||||
|
slice::from_raw_parts_mut(ptr, RECORD_SIZE as usize)
|
||||||
|
};
|
||||||
|
|
||||||
|
Self(inner, block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Disk for DiskEfi {
|
||||||
|
unsafe fn read_at(&mut self, block: u64, buffer: &mut [u8]) -> Result<usize> {
|
||||||
|
unsafe {
|
||||||
|
// Optimization for live disks
|
||||||
|
if let Some(live) = crate::LIVE_OPT {
|
||||||
|
if block >= live.0 {
|
||||||
|
let start = ((block - live.0) * BLOCK_SIZE) as usize;
|
||||||
|
let end = start + buffer.len();
|
||||||
|
if end <= live.1.len() {
|
||||||
|
buffer.copy_from_slice(&live.1[start..end]);
|
||||||
|
return Ok(buffer.len());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use aligned buffer if necessary
|
||||||
|
let mut ptr = buffer.as_mut_ptr();
|
||||||
|
if self.0.Media.IoAlign != 0 {
|
||||||
|
if (ptr as usize) % (self.0.Media.IoAlign as usize) != 0 {
|
||||||
|
if buffer.len() <= self.1.len() {
|
||||||
|
ptr = self.1.as_mut_ptr();
|
||||||
|
} else {
|
||||||
|
println!(
|
||||||
|
"DiskEfi::read_at 0x{:X} requires alignment, ptr = 0x{:p}, len = 0x{:x}",
|
||||||
|
block,
|
||||||
|
ptr,
|
||||||
|
buffer.len()
|
||||||
|
);
|
||||||
|
return Err(Error::new(EINVAL));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let block_size = self.0.Media.BlockSize as u64;
|
||||||
|
let lba = block * BLOCK_SIZE / block_size;
|
||||||
|
|
||||||
|
match (self.0.ReadBlocks)(self.0, self.0.Media.MediaId, lba, buffer.len(), ptr) {
|
||||||
|
status if status.is_success() => {
|
||||||
|
// Copy to original buffer if using aligned buffer
|
||||||
|
if ptr != buffer.as_mut_ptr() {
|
||||||
|
let (left, _) = self.1.split_at(buffer.len());
|
||||||
|
buffer.copy_from_slice(left);
|
||||||
|
}
|
||||||
|
Ok(buffer.len())
|
||||||
|
}
|
||||||
|
err => {
|
||||||
|
println!("DiskEfi::read_at 0x{:X} failed: {:?}", block, err);
|
||||||
|
Err(Error::new(EIO))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn write_at(&mut self, block: u64, _buffer: &[u8]) -> Result<usize> {
|
||||||
|
println!("DiskEfi::write_at 0x{:X} not implemented", block);
|
||||||
|
Err(Error::new(EIO))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn size(&mut self) -> Result<u64> {
|
||||||
|
println!("DiskEfi::size not implemented");
|
||||||
|
Err(Error::new(EIO))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
use std::proto::Protocol;
|
||||||
|
use uefi::graphics::GraphicsOutput;
|
||||||
|
use uefi::guid::{Guid, GRAPHICS_OUTPUT_PROTOCOL_GUID};
|
||||||
|
|
||||||
|
pub struct Output(pub &'static mut GraphicsOutput);
|
||||||
|
|
||||||
|
impl Protocol<GraphicsOutput> for Output {
|
||||||
|
fn guid() -> Guid {
|
||||||
|
GRAPHICS_OUTPUT_PROTOCOL_GUID
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(inner: &'static mut GraphicsOutput) -> Self {
|
||||||
|
Output(inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const EDID_ACTIVE_PROTOCOL_GUID: Guid = Guid(
|
||||||
|
0xbd8c1056,
|
||||||
|
0x9f36,
|
||||||
|
0x44ec,
|
||||||
|
[0x92, 0xa8, 0xa6, 0x33, 0x7f, 0x81, 0x79, 0x86],
|
||||||
|
);
|
||||||
|
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct EdidActiveProtocol {
|
||||||
|
pub SizeOfEdid: u32,
|
||||||
|
pub Edid: *const u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct EdidActive(pub &'static mut EdidActiveProtocol);
|
||||||
|
|
||||||
|
impl Protocol<EdidActiveProtocol> for EdidActive {
|
||||||
|
fn guid() -> Guid {
|
||||||
|
EDID_ACTIVE_PROTOCOL_GUID
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new(inner: &'static mut EdidActiveProtocol) -> Self {
|
||||||
|
EdidActive(inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
use crate::Os;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use byteorder::ByteOrder;
|
||||||
|
use byteorder::BE;
|
||||||
|
use core::slice;
|
||||||
|
use fdt::Fdt;
|
||||||
|
use uefi::guid::DEVICE_TREE_GUID;
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
use uefi::{
|
||||||
|
guid::SMBIOS3_TABLE_GUID,
|
||||||
|
status::{Result, Status},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub static mut DEV_MEM_AREA: Vec<(usize, usize)> = Vec::new();
|
||||||
|
|
||||||
|
pub unsafe fn is_in_dev_mem_region(addr: usize) -> bool {
|
||||||
|
#[allow(static_mut_refs)]
|
||||||
|
unsafe {
|
||||||
|
if DEV_MEM_AREA.is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for item in DEV_MEM_AREA.iter() {
|
||||||
|
if (addr >= item.0) && (addr < item.0 + item.1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn get_dev_mem_region(fdt: &Fdt) {
|
||||||
|
unsafe {
|
||||||
|
let Some(soc) = fdt.find_node("/soc") else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let Some(ranges) = soc.ranges() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let cell_sizes = soc.cell_sizes();
|
||||||
|
for chunk in ranges {
|
||||||
|
let child_bus_addr = chunk.child_bus_address;
|
||||||
|
let parent_bus_addr = chunk.parent_bus_address;
|
||||||
|
let addr_size = chunk.size;
|
||||||
|
println!(
|
||||||
|
"dev mem 0x{:08x} 0x{:08x} 0x{:08x}",
|
||||||
|
child_bus_addr, parent_bus_addr, addr_size
|
||||||
|
);
|
||||||
|
#[allow(static_mut_refs)]
|
||||||
|
DEV_MEM_AREA.push((parent_bus_addr as usize, addr_size as usize));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_dtb(os: &impl Os, address: *const u8) -> Option<(u64, u64)> {
|
||||||
|
unsafe {
|
||||||
|
if let Ok(fdt) = fdt::Fdt::from_ptr(address) {
|
||||||
|
let mut rsdps_area = Vec::new();
|
||||||
|
//println!("DTB model = {}", fdt.root().model());
|
||||||
|
get_dev_mem_region(&fdt);
|
||||||
|
let length = fdt.total_size();
|
||||||
|
let align = 8;
|
||||||
|
rsdps_area.extend(core::slice::from_raw_parts(address, length));
|
||||||
|
rsdps_area.resize(((rsdps_area.len() + (align - 1)) / align) * align, 0u8);
|
||||||
|
let size = rsdps_area.len();
|
||||||
|
let base = os.alloc_zeroed_page_aligned(size);
|
||||||
|
slice::from_raw_parts_mut(base, size).copy_from_slice(&rsdps_area);
|
||||||
|
Some((base as u64, size as u64))
|
||||||
|
} else {
|
||||||
|
println!("Failed to parse DTB");
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
fn find_smbios3_system(address: *const u8) -> Result<dmidecode::System<'static>> {
|
||||||
|
unsafe {
|
||||||
|
let smb = core::slice::from_raw_parts(address, 24);
|
||||||
|
if let Ok(smbios) = dmidecode::EntryPoint::search(smb) {
|
||||||
|
let smb_structure_data = core::slice::from_raw_parts(
|
||||||
|
smbios.smbios_address() as *const u8,
|
||||||
|
smbios.smbios_len() as usize,
|
||||||
|
);
|
||||||
|
for structure in smbios.structures(smb_structure_data) {
|
||||||
|
if let Ok(sval) = structure {
|
||||||
|
//println!("SMBIOS: {:#?}", sval);
|
||||||
|
if let dmidecode::Structure::System(buf) = sval {
|
||||||
|
return Ok(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(Status::NOT_FOUND)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn find_dtb(os: &impl Os) -> Option<(u64, u64)> {
|
||||||
|
let cfg_tables = std::system_table().config_tables();
|
||||||
|
for cfg_table in cfg_tables.iter() {
|
||||||
|
if cfg_table.VendorGuid == DEVICE_TREE_GUID {
|
||||||
|
let addr = cfg_table.VendorTable;
|
||||||
|
return parse_dtb(os, addr as *const u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* This hack is no longer needed, but can be re-enabled for testing
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
for cfg_table in cfg_tables.iter() {
|
||||||
|
if cfg_table.VendorGuid == SMBIOS3_TABLE_GUID {
|
||||||
|
let addr = cfg_table.VendorTable;
|
||||||
|
if let Ok(sys) = find_smbios3_system(addr as *const u8) {
|
||||||
|
let get_dtb_addr = match (sys.manufacturer, sys.version) {
|
||||||
|
("QEMU", version) if version.starts_with("virt") => Some(0x4000_0000 as usize),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
if let Some(dtb_addr) = get_dtb_addr {
|
||||||
|
return parse_dtb(os, dtb_addr as *const u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
@@ -0,0 +1,141 @@
|
|||||||
|
use alloc::vec;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use core::{mem, ptr};
|
||||||
|
use uefi::memory::{MemoryDescriptor, MemoryType};
|
||||||
|
|
||||||
|
use crate::area_add;
|
||||||
|
use crate::os::{OsMemoryEntry, OsMemoryKind};
|
||||||
|
|
||||||
|
use super::status_to_result;
|
||||||
|
|
||||||
|
pub struct MemoryMapIter {
|
||||||
|
map: Vec<u8>,
|
||||||
|
map_key: usize,
|
||||||
|
descriptor_size: usize,
|
||||||
|
descriptor_version: u32,
|
||||||
|
i: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MemoryMapIter {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let uefi = std::system_table();
|
||||||
|
|
||||||
|
let mut map = vec![0; 65536];
|
||||||
|
let mut map_size = map.len();
|
||||||
|
let mut map_key = 0;
|
||||||
|
let mut descriptor_size = 0;
|
||||||
|
let mut descriptor_version = 0;
|
||||||
|
status_to_result((uefi.BootServices.GetMemoryMap)(
|
||||||
|
&mut map_size,
|
||||||
|
map.as_mut_ptr() as *mut MemoryDescriptor,
|
||||||
|
&mut map_key,
|
||||||
|
&mut descriptor_size,
|
||||||
|
&mut descriptor_version,
|
||||||
|
))
|
||||||
|
.expect("Failed to get UEFI memory map");
|
||||||
|
|
||||||
|
// Ensure descriptor size is usable
|
||||||
|
assert!(descriptor_size >= mem::size_of::<MemoryDescriptor>());
|
||||||
|
|
||||||
|
// Ensure descriptor version is supported
|
||||||
|
assert_eq!(descriptor_version, 1);
|
||||||
|
|
||||||
|
// Reduce map size to returned value
|
||||||
|
map.truncate(map_size);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
map,
|
||||||
|
map_key,
|
||||||
|
descriptor_size,
|
||||||
|
descriptor_version,
|
||||||
|
i: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exit_boot_services(mut self) {
|
||||||
|
let handle = std::handle();
|
||||||
|
let uefi = std::system_table();
|
||||||
|
|
||||||
|
// We are writing to the memory map that will be passed to
|
||||||
|
// SetVirtualAddressMap before ExitBootServices as on some firmware
|
||||||
|
// EfiLoaderData memory regions like this one are marked as read-only
|
||||||
|
// after ExitBootServices
|
||||||
|
for i in 0..self.map.len() / self.descriptor_size {
|
||||||
|
let descriptor_ptr = unsafe { self.map.as_mut_ptr().add(i * self.descriptor_size) };
|
||||||
|
let descriptor = unsafe { &mut *(descriptor_ptr as *mut MemoryDescriptor) };
|
||||||
|
|
||||||
|
// Map all memory regions even when not marked as EFI_MEMORY_RUNTIME
|
||||||
|
// as some firmware uses memory regions not marked as
|
||||||
|
// EFI_MEMORY_RUNTIME in runtime services. Linux has a list of
|
||||||
|
// exactly which memory regions need to be mapped, but for simplicity
|
||||||
|
// we are mapping all regions here.
|
||||||
|
|
||||||
|
// Identity map all memory regions as some firmware fails to update
|
||||||
|
// all pointers in SetVirtualAddressMap.
|
||||||
|
|
||||||
|
descriptor.VirtualStart.0 = descriptor.PhysicalStart.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
status_to_result((uefi.BootServices.ExitBootServices)(handle, self.map_key))
|
||||||
|
.expect("Failed to exit UEFI boot services");
|
||||||
|
|
||||||
|
// Runtime services must be called with interrupts disabled
|
||||||
|
super::arch::disable_interrupts();
|
||||||
|
|
||||||
|
status_to_result((uefi.RuntimeServices.SetVirtualAddressMap)(
|
||||||
|
self.map.len(),
|
||||||
|
self.descriptor_size,
|
||||||
|
self.descriptor_version,
|
||||||
|
self.map.as_ptr() as *const MemoryDescriptor,
|
||||||
|
))
|
||||||
|
.expect("Failed to set UEFI runtime services virtual address map");
|
||||||
|
|
||||||
|
// After ExitBootServices, GlobalAlloc::dealloc() is not allowed anymore
|
||||||
|
// as it uses boot services.
|
||||||
|
mem::forget(self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for MemoryMapIter {
|
||||||
|
type Item = OsMemoryEntry;
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.i < self.map.len() / self.descriptor_size {
|
||||||
|
let descriptor_ptr = unsafe { self.map.as_ptr().add(self.i * self.descriptor_size) };
|
||||||
|
self.i += 1;
|
||||||
|
|
||||||
|
let descriptor = unsafe { ptr::read(descriptor_ptr as *const MemoryDescriptor) };
|
||||||
|
let descriptor_type: MemoryType = unsafe { mem::transmute(descriptor.Type) };
|
||||||
|
|
||||||
|
Some(OsMemoryEntry {
|
||||||
|
base: descriptor.PhysicalStart.0,
|
||||||
|
//TODO: do not hard code page size
|
||||||
|
size: descriptor.NumberOfPages * 4096,
|
||||||
|
kind: match descriptor_type {
|
||||||
|
MemoryType::EfiLoaderCode
|
||||||
|
| MemoryType::EfiLoaderData
|
||||||
|
| MemoryType::EfiBootServicesCode
|
||||||
|
| MemoryType::EfiBootServicesData
|
||||||
|
| MemoryType::EfiConventionalMemory => OsMemoryKind::Free,
|
||||||
|
//TODO: mark ACPI memory as reclaim
|
||||||
|
_ => OsMemoryKind::Reserved,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub unsafe fn memory_map() -> MemoryMapIter {
|
||||||
|
let mut iter = MemoryMapIter::new();
|
||||||
|
|
||||||
|
// Using next to avoid consuming iterator
|
||||||
|
while let Some(entry) = iter.next() {
|
||||||
|
area_add(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rewind iterator
|
||||||
|
iter.i = 0;
|
||||||
|
|
||||||
|
iter
|
||||||
|
}
|
||||||
@@ -0,0 +1,443 @@
|
|||||||
|
use alloc::vec::Vec;
|
||||||
|
use core::{cell::RefCell, mem, ptr, slice};
|
||||||
|
use std::proto::Protocol;
|
||||||
|
use uefi::{
|
||||||
|
boot::LocateSearchType,
|
||||||
|
memory::MemoryType,
|
||||||
|
reset::ResetType,
|
||||||
|
status::{Result, Status},
|
||||||
|
system::SystemTable,
|
||||||
|
text::TextInputKey,
|
||||||
|
Handle,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::os::{Os, OsHwDesc, OsKey, OsVideoMode};
|
||||||
|
|
||||||
|
use self::{
|
||||||
|
device::{device_path_to_string, disk_device_priority},
|
||||||
|
disk::DiskOrFileEfi,
|
||||||
|
display::{EdidActive, Output},
|
||||||
|
video_mode::VideoModeIter,
|
||||||
|
};
|
||||||
|
|
||||||
|
mod acpi;
|
||||||
|
mod arch;
|
||||||
|
mod device;
|
||||||
|
mod disk;
|
||||||
|
mod display;
|
||||||
|
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
|
||||||
|
pub mod dtb;
|
||||||
|
mod memory_map;
|
||||||
|
mod video_mode;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "riscv64")]
|
||||||
|
pub use arch::efi_get_boot_hartid;
|
||||||
|
|
||||||
|
pub(crate) fn page_size() -> usize {
|
||||||
|
// EDK2 always uses 4096 as the page size
|
||||||
|
4096
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn alloc_zeroed_page_aligned(size: usize) -> *mut u8 {
|
||||||
|
assert!(size != 0);
|
||||||
|
|
||||||
|
let page_size = page_size();
|
||||||
|
let pages = size.div_ceil(page_size);
|
||||||
|
|
||||||
|
let ptr = {
|
||||||
|
// Max address mapped by src/arch paging code (8 GiB)
|
||||||
|
let mut ptr = 0x2_0000_0000;
|
||||||
|
status_to_result((std::system_table().BootServices.AllocatePages)(
|
||||||
|
1, // AllocateMaxAddress
|
||||||
|
MemoryType::EfiRuntimeServicesData, // Keeps this memory out of free space list
|
||||||
|
pages,
|
||||||
|
&mut ptr,
|
||||||
|
))
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
ptr = 0;
|
||||||
|
0
|
||||||
|
});
|
||||||
|
if ptr == 0 { ptr::null_mut() } else { ptr as *mut u8 }
|
||||||
|
};
|
||||||
|
|
||||||
|
if !ptr.is_null() {
|
||||||
|
unsafe { ptr::write_bytes(ptr, 0, pages * page_size) };
|
||||||
|
}
|
||||||
|
ptr
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct OsEfi {
|
||||||
|
st: &'static SystemTable,
|
||||||
|
outputs: RefCell<Vec<(Output, Option<EdidActive>)>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OsEfi {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let st = std::system_table();
|
||||||
|
let mut outputs = Vec::<(Output, Option<EdidActive>)>::new();
|
||||||
|
{
|
||||||
|
let guid = Output::guid();
|
||||||
|
let mut handles = Vec::with_capacity(256);
|
||||||
|
let mut len = handles.capacity() * mem::size_of::<Handle>();
|
||||||
|
match status_to_result((st.BootServices.LocateHandle)(
|
||||||
|
LocateSearchType::ByProtocol,
|
||||||
|
&guid,
|
||||||
|
ptr::null(),
|
||||||
|
&mut len,
|
||||||
|
handles.as_mut_ptr(),
|
||||||
|
)) {
|
||||||
|
Ok(_) => {
|
||||||
|
unsafe {
|
||||||
|
handles.set_len(len / mem::size_of::<Handle>());
|
||||||
|
}
|
||||||
|
'handles: for handle in handles {
|
||||||
|
//TODO: do we have to query all modes to get good edid?
|
||||||
|
match Output::handle_protocol(handle) {
|
||||||
|
Ok(output) => {
|
||||||
|
log::debug!(
|
||||||
|
"Output {:?} at {:x}",
|
||||||
|
handle,
|
||||||
|
output.0.Mode.FrameBufferBase
|
||||||
|
);
|
||||||
|
|
||||||
|
if output.0.Mode.FrameBufferBase == 0 {
|
||||||
|
log::debug!("Skipping output with frame buffer base of 0");
|
||||||
|
continue 'handles;
|
||||||
|
}
|
||||||
|
|
||||||
|
for other_output in outputs.iter() {
|
||||||
|
if output.0.Mode.FrameBufferBase
|
||||||
|
== other_output.0 .0.Mode.FrameBufferBase
|
||||||
|
{
|
||||||
|
log::debug!(
|
||||||
|
"Skipping output with frame buffer base matching another output"
|
||||||
|
);
|
||||||
|
continue 'handles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outputs.push((
|
||||||
|
output,
|
||||||
|
match EdidActive::handle_protocol(handle) {
|
||||||
|
Ok(efi_edid) => Some(efi_edid),
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!(
|
||||||
|
"Failed to get EFI EDID from handle {:?}: {:?}",
|
||||||
|
handle,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
None
|
||||||
|
}
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!(
|
||||||
|
"Failed to get Output from handle {:?}: {:?}",
|
||||||
|
handle,
|
||||||
|
err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
log::warn!("Failed to locate Outputs: {:?}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
st,
|
||||||
|
outputs: RefCell::new(outputs),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Os for OsEfi {
|
||||||
|
type D = DiskOrFileEfi;
|
||||||
|
type V = VideoModeIter;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"aarch64/UEFI"
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"x86_64/UEFI"
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "riscv64")]
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"riscv64/UEFI"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_zeroed_page_aligned(&self, size: usize) -> *mut u8 {
|
||||||
|
alloc_zeroed_page_aligned(size)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn page_size(&self) -> usize {
|
||||||
|
page_size()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn filesystem(
|
||||||
|
&self,
|
||||||
|
password_opt: Option<&[u8]>,
|
||||||
|
) -> syscall::Result<redoxfs::FileSystem<DiskOrFileEfi>> {
|
||||||
|
let mut seen_enokey = false;
|
||||||
|
|
||||||
|
// Search for RedoxFS on disks in prioritized order
|
||||||
|
println!("Looking for RedoxFS:");
|
||||||
|
for device in disk_device_priority() {
|
||||||
|
if let Some(file_path) = device.file_path {
|
||||||
|
log::debug!(
|
||||||
|
" - {}\\{}",
|
||||||
|
device_path_to_string(device.device_path.0),
|
||||||
|
file_path
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
log::debug!(" - {}", device_path_to_string(device.device_path.0));
|
||||||
|
}
|
||||||
|
|
||||||
|
let block = device.partition_offset / redoxfs::BLOCK_SIZE;
|
||||||
|
|
||||||
|
match redoxfs::FileSystem::open(device.disk, password_opt, Some(block), false) {
|
||||||
|
Ok(ok) => return Ok(ok),
|
||||||
|
Err(err) => match err.errno {
|
||||||
|
// Ignore header not found error
|
||||||
|
syscall::ENOENT => (),
|
||||||
|
// Bubble ENOKEY up later if we can't find another partition to return
|
||||||
|
syscall::ENOKEY => seen_enokey = true,
|
||||||
|
// Print any other errors
|
||||||
|
_ => {
|
||||||
|
log::warn!("BlockIo error: {:?}", err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let the caller prompt for a password
|
||||||
|
if seen_enokey {
|
||||||
|
return Err(syscall::Error::new(syscall::ENOKEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
log::warn!("No RedoxFS partitions found");
|
||||||
|
Err(syscall::Error::new(syscall::ENOENT))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hwdesc(&self) -> OsHwDesc {
|
||||||
|
//TODO: if both DTB and ACPI are found, we should probably let the OS choose what to use?
|
||||||
|
|
||||||
|
// For now we will prefer DTB on platforms that have it
|
||||||
|
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
|
||||||
|
if let Some((addr, size)) = dtb::find_dtb(self) {
|
||||||
|
return OsHwDesc::DeviceTree(addr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((addr, size)) = acpi::find_acpi_table_pointers(self) {
|
||||||
|
return OsHwDesc::Acpi(addr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
OsHwDesc::NotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
fn video_outputs(&self) -> usize {
|
||||||
|
self.outputs.borrow().len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn video_modes(&self, output_i: usize) -> VideoModeIter {
|
||||||
|
let output_opt = match self.outputs.borrow_mut().get_mut(output_i) {
|
||||||
|
Some(output) => unsafe {
|
||||||
|
// Hack to enable clone
|
||||||
|
let ptr = output.0 .0 as *mut _;
|
||||||
|
Some(Output::new(&mut *ptr))
|
||||||
|
},
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
VideoModeIter::new(output_opt)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_video_mode(&self, output_i: usize, mode: &mut OsVideoMode) {
|
||||||
|
//TODO: return error?
|
||||||
|
let mut outputs = self.outputs.borrow_mut();
|
||||||
|
let (output, _efi_edid_opt) = &mut outputs[output_i];
|
||||||
|
status_to_result((output.0.SetMode)(output.0, mode.id)).unwrap();
|
||||||
|
|
||||||
|
// Update with actual mode information
|
||||||
|
mode.width = output.0.Mode.Info.HorizontalResolution;
|
||||||
|
mode.height = output.0.Mode.Info.VerticalResolution;
|
||||||
|
mode.base = output.0.Mode.FrameBufferBase as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn best_resolution(&self, output_i: usize) -> Option<(u32, u32)> {
|
||||||
|
let mut outputs = self.outputs.borrow_mut();
|
||||||
|
let (output, efi_edid_opt) = outputs.get_mut(output_i)?;
|
||||||
|
|
||||||
|
if let Some(efi_edid) = efi_edid_opt {
|
||||||
|
let edid =
|
||||||
|
unsafe { slice::from_raw_parts(efi_edid.0.Edid, efi_edid.0.SizeOfEdid as usize) };
|
||||||
|
|
||||||
|
if edid.len() > 0x3D {
|
||||||
|
return Some((
|
||||||
|
(edid[0x38] as u32) | (((edid[0x3A] as u32) & 0xF0) << 4),
|
||||||
|
(edid[0x3B] as u32) | (((edid[0x3D] as u32) & 0xF0) << 4),
|
||||||
|
));
|
||||||
|
} else {
|
||||||
|
log::warn!("EFI EDID too small: {}", edid.len());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to the current output resolution
|
||||||
|
Some((
|
||||||
|
output.0.Mode.Info.HorizontalResolution,
|
||||||
|
output.0.Mode.Info.VerticalResolution,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_key(&self) -> OsKey {
|
||||||
|
//TODO: do not unwrap
|
||||||
|
|
||||||
|
let mut index = 0;
|
||||||
|
status_to_result((self.st.BootServices.WaitForEvent)(
|
||||||
|
1,
|
||||||
|
&self.st.ConsoleIn.WaitForKey,
|
||||||
|
&mut index,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut key = TextInputKey {
|
||||||
|
ScanCode: 0,
|
||||||
|
UnicodeChar: 0,
|
||||||
|
};
|
||||||
|
status_to_result((self.st.ConsoleIn.ReadKeyStroke)(
|
||||||
|
self.st.ConsoleIn,
|
||||||
|
&mut key,
|
||||||
|
))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
match key.ScanCode {
|
||||||
|
0 => match key.UnicodeChar {
|
||||||
|
8 => OsKey::Backspace,
|
||||||
|
13 => OsKey::Enter,
|
||||||
|
w => match char::from_u32(w as u32) {
|
||||||
|
Some(c) => OsKey::Char(c),
|
||||||
|
None => OsKey::Other,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
1 => OsKey::Up,
|
||||||
|
2 => OsKey::Down,
|
||||||
|
3 => OsKey::Right,
|
||||||
|
4 => OsKey::Left,
|
||||||
|
8 => OsKey::Delete,
|
||||||
|
_ => OsKey::Other,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_key_timeout(&self, milliseconds: usize) -> OsKey {
|
||||||
|
let slices = milliseconds.div_ceil(100);
|
||||||
|
for _ in 0..slices {
|
||||||
|
let mut key = TextInputKey {
|
||||||
|
ScanCode: 0,
|
||||||
|
UnicodeChar: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let status = (self.st.ConsoleIn.ReadKeyStroke)(self.st.ConsoleIn, &mut key);
|
||||||
|
if status.is_success() {
|
||||||
|
return match key.ScanCode {
|
||||||
|
0 => match key.UnicodeChar {
|
||||||
|
8 => OsKey::Backspace,
|
||||||
|
13 => OsKey::Enter,
|
||||||
|
w => match char::from_u32(w as u32) {
|
||||||
|
Some(c) => OsKey::Char(c),
|
||||||
|
None => OsKey::Other,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
1 => OsKey::Up,
|
||||||
|
2 => OsKey::Down,
|
||||||
|
3 => OsKey::Right,
|
||||||
|
4 => OsKey::Left,
|
||||||
|
8 => OsKey::Delete,
|
||||||
|
_ => OsKey::Other,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let _ = status_to_result((self.st.BootServices.Stall)(100_000));
|
||||||
|
}
|
||||||
|
|
||||||
|
OsKey::Timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_text(&self) {
|
||||||
|
//TODO: why does this sometimes return InvalidParameter, but otherwise appear to work?
|
||||||
|
let _ = status_to_result((self.st.ConsoleOut.ClearScreen)(self.st.ConsoleOut));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_text_position(&self) -> (usize, usize) {
|
||||||
|
(
|
||||||
|
self.st.ConsoleOut.Mode.CursorColumn as usize,
|
||||||
|
self.st.ConsoleOut.Mode.CursorRow as usize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_text_position(&self, x: usize, y: usize) {
|
||||||
|
// Ignore error because Tow-Boot appears to not implement this
|
||||||
|
let _ = status_to_result((self.st.ConsoleOut.SetCursorPosition)(
|
||||||
|
self.st.ConsoleOut,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_text_highlight(&self, highlight: bool) {
|
||||||
|
let attr = if highlight { 0x70 } else { 0x07 };
|
||||||
|
status_to_result((self.st.ConsoleOut.SetAttribute)(self.st.ConsoleOut, attr)).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn status_to_result(status: Status) -> Result<usize> {
|
||||||
|
match status {
|
||||||
|
Status(ok) if status.is_success() => Ok(ok),
|
||||||
|
err => Err(err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_max_mode(output: &uefi::text::TextOutput) -> Result<()> {
|
||||||
|
let mut max_i = None;
|
||||||
|
let mut max_w = 0;
|
||||||
|
let mut max_h = 0;
|
||||||
|
|
||||||
|
for i in 0..output.Mode.MaxMode as usize {
|
||||||
|
let mut w = 0;
|
||||||
|
let mut h = 0;
|
||||||
|
if (output.QueryMode)(output, i, &mut w, &mut h).is_success() {
|
||||||
|
if w >= max_w && h >= max_h {
|
||||||
|
max_i = Some(i);
|
||||||
|
max_w = w;
|
||||||
|
max_h = h;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(i) = max_i {
|
||||||
|
status_to_result((output.SetMode)(output, i))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
pub extern "C" fn main() -> Status {
|
||||||
|
let uefi = std::system_table();
|
||||||
|
|
||||||
|
let _ = (uefi.BootServices.SetWatchdogTimer)(0, 0, 0, ptr::null());
|
||||||
|
|
||||||
|
if let Err(err) = set_max_mode(uefi.ConsoleOut) {
|
||||||
|
println!("Failed to set max mode: {:?}", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(err) = arch::main() {
|
||||||
|
panic!("App error: {:?}", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
(uefi.RuntimeServices.ResetSystem)(ResetType::Cold, Status(0), 0, ptr::null());
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
use core::ptr;
|
||||||
|
use log::error;
|
||||||
|
use uefi::status::Status;
|
||||||
|
|
||||||
|
use crate::os::uefi::display::Output;
|
||||||
|
use crate::os::OsVideoMode;
|
||||||
|
|
||||||
|
pub struct VideoModeIter {
|
||||||
|
output_opt: Option<Output>,
|
||||||
|
i: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VideoModeIter {
|
||||||
|
pub fn new(output_opt: Option<Output>) -> Self {
|
||||||
|
Self { output_opt, i: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for VideoModeIter {
|
||||||
|
type Item = OsVideoMode;
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if let Some(ref mut output) = self.output_opt {
|
||||||
|
while self.i < output.0.Mode.MaxMode {
|
||||||
|
let id = self.i;
|
||||||
|
self.i += 1;
|
||||||
|
|
||||||
|
let mut mode_ptr = ::core::ptr::null_mut();
|
||||||
|
let mut mode_size = 0;
|
||||||
|
match (output.0.QueryMode)(output.0, id, &mut mode_size, &mut mode_ptr) {
|
||||||
|
Status::SUCCESS => (),
|
||||||
|
err => {
|
||||||
|
error!("Failed to read mode {}: {:?}", id, err);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//TODO: ensure mode_size is set correctly
|
||||||
|
let mode = unsafe { ptr::read(mode_ptr) };
|
||||||
|
|
||||||
|
let width = mode.HorizontalResolution;
|
||||||
|
let height = mode.VerticalResolution;
|
||||||
|
let stride = mode.PixelsPerScanLine;
|
||||||
|
|
||||||
|
return Some(OsVideoMode {
|
||||||
|
id,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
stride,
|
||||||
|
// Base is retrieved later by setting the mode
|
||||||
|
base: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
use bitflags::bitflags;
|
||||||
|
use core::convert::TryInto;
|
||||||
|
use core::fmt;
|
||||||
|
use core::ptr::{addr_of, addr_of_mut};
|
||||||
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||||
|
use syscall::io::Pio;
|
||||||
|
use syscall::io::{Io, Mmio, ReadOnly};
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
/// Interrupt enable flags
|
||||||
|
struct IntEnFlags: u8 {
|
||||||
|
const RECEIVED = 1;
|
||||||
|
const SENT = 1 << 1;
|
||||||
|
const ERRORED = 1 << 2;
|
||||||
|
const STATUS_CHANGE = 1 << 3;
|
||||||
|
// 4 to 7 are unused
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
/// Line status flags
|
||||||
|
struct LineStsFlags: u8 {
|
||||||
|
const INPUT_FULL = 1;
|
||||||
|
// 1 to 4 unknown
|
||||||
|
const OUTPUT_EMPTY = 1 << 5;
|
||||||
|
// 6 and 7 unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct SerialPort<T: Io> {
|
||||||
|
/// Data register, read to receive, write to send
|
||||||
|
data: T,
|
||||||
|
/// Interrupt enable
|
||||||
|
int_en: T,
|
||||||
|
/// FIFO control
|
||||||
|
fifo_ctrl: T,
|
||||||
|
/// Line control
|
||||||
|
line_ctrl: T,
|
||||||
|
/// Modem control
|
||||||
|
modem_ctrl: T,
|
||||||
|
/// Line status
|
||||||
|
line_sts: ReadOnly<T>,
|
||||||
|
/// Modem status
|
||||||
|
modem_sts: ReadOnly<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||||
|
impl SerialPort<Pio<u8>> {
|
||||||
|
pub const fn new(base: u16) -> SerialPort<Pio<u8>> {
|
||||||
|
SerialPort {
|
||||||
|
data: Pio::new(base),
|
||||||
|
int_en: Pio::new(base + 1),
|
||||||
|
fifo_ctrl: Pio::new(base + 2),
|
||||||
|
line_ctrl: Pio::new(base + 3),
|
||||||
|
modem_ctrl: Pio::new(base + 4),
|
||||||
|
line_sts: ReadOnly::new(Pio::new(base + 5)),
|
||||||
|
modem_sts: ReadOnly::new(Pio::new(base + 6)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SerialPort<Mmio<u32>> {
|
||||||
|
pub unsafe fn new(base: usize) -> &'static mut SerialPort<Mmio<u32>> {
|
||||||
|
unsafe { &mut *(base as *mut Self) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Io> SerialPort<T>
|
||||||
|
where
|
||||||
|
T::Value: From<u8> + TryInto<u8>,
|
||||||
|
{
|
||||||
|
pub fn init(&mut self) {
|
||||||
|
unsafe {
|
||||||
|
//TODO: Cleanup
|
||||||
|
// FIXME: Fix UB if unaligned
|
||||||
|
(*addr_of_mut!(self.int_en)).write(0x00.into());
|
||||||
|
(*addr_of_mut!(self.line_ctrl)).write(0x80.into());
|
||||||
|
(*addr_of_mut!(self.data)).write(0x01.into());
|
||||||
|
(*addr_of_mut!(self.int_en)).write(0x00.into());
|
||||||
|
(*addr_of_mut!(self.line_ctrl)).write(0x03.into());
|
||||||
|
(*addr_of_mut!(self.fifo_ctrl)).write(0xC7.into());
|
||||||
|
(*addr_of_mut!(self.modem_ctrl)).write(0x0B.into());
|
||||||
|
(*addr_of_mut!(self.int_en)).write(0x01.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn line_sts(&self) -> LineStsFlags {
|
||||||
|
LineStsFlags::from_bits_truncate(
|
||||||
|
(unsafe { &*addr_of!(self.line_sts) }.read() & 0xFF.into())
|
||||||
|
.try_into()
|
||||||
|
.unwrap_or(0),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn receive(&mut self) -> Option<u8> {
|
||||||
|
if self.line_sts().contains(LineStsFlags::INPUT_FULL) {
|
||||||
|
Some(
|
||||||
|
(unsafe { &*addr_of!(self.data) }.read() & 0xFF.into())
|
||||||
|
.try_into()
|
||||||
|
.unwrap_or(0),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn send(&mut self, data: u8) {
|
||||||
|
while !self.line_sts().contains(LineStsFlags::OUTPUT_EMPTY) {}
|
||||||
|
unsafe { &mut *addr_of_mut!(self.data) }.write(data.into())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&mut self, buf: &[u8]) {
|
||||||
|
for &b in buf {
|
||||||
|
match b {
|
||||||
|
8 | 0x7F => {
|
||||||
|
self.send(8);
|
||||||
|
self.send(b' ');
|
||||||
|
self.send(8);
|
||||||
|
}
|
||||||
|
b'\n' => {
|
||||||
|
self.send(b'\r');
|
||||||
|
self.send(b'\n');
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.send(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Io> fmt::Write for SerialPort<T>
|
||||||
|
where
|
||||||
|
T::Value: From<u8> + TryInto<u8>,
|
||||||
|
{
|
||||||
|
fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
|
||||||
|
self.write(s.as_bytes());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"arch": "aarch64",
|
||||||
|
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
|
||||||
|
"default-hidden-visibility": true,
|
||||||
|
"emit-debug-gdb-scripts": false,
|
||||||
|
"exe-suffix": ".efi",
|
||||||
|
"executables": true,
|
||||||
|
"is-like-windows": true,
|
||||||
|
"linker": "rust-lld",
|
||||||
|
"linker-flavor": "lld-link",
|
||||||
|
"llvm-target": "aarch64-pc-windows-msvc",
|
||||||
|
"os": "uefi",
|
||||||
|
"panic-strategy": "abort",
|
||||||
|
"pre-link-args": {
|
||||||
|
"lld-link": [
|
||||||
|
"/subsystem:EFI_Application",
|
||||||
|
"/entry:efi_main",
|
||||||
|
"/machine:arm64"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"stack_probes": true,
|
||||||
|
"target-c-int-width": 32,
|
||||||
|
"target-endian": "little",
|
||||||
|
"target-pointer-width": 64
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"arch": "riscv64",
|
||||||
|
"code-model": "medium",
|
||||||
|
"cpu": "generic-rv64",
|
||||||
|
"data-layout": "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128",
|
||||||
|
"emit-debug-gdb-scripts": false,
|
||||||
|
"exe-suffix": ".elf",
|
||||||
|
"executables": true,
|
||||||
|
"linker-flavor": "gnu-cc",
|
||||||
|
"linker": "riscv64-unknown-redox-gcc",
|
||||||
|
"llvm-abiname": "lp64d",
|
||||||
|
"features": "+m,+a,+f,+d,+c",
|
||||||
|
"llvm-target": "riscv64-unknown-none-elf",
|
||||||
|
"os": "none",
|
||||||
|
"metadata": {
|
||||||
|
"description": null,
|
||||||
|
"host_tools": null,
|
||||||
|
"std": null,
|
||||||
|
"tier": null
|
||||||
|
},
|
||||||
|
"panic-strategy": "abort",
|
||||||
|
"target-c-int-width": 32,
|
||||||
|
"target-endian": "little",
|
||||||
|
"target-pointer-width": 64
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"llvm-target": "i686-unknown-none",
|
||||||
|
"target-endian": "little",
|
||||||
|
"target-pointer-width": 32,
|
||||||
|
"target-c-int-width": 32,
|
||||||
|
"data-layout": "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128",
|
||||||
|
"arch": "x86",
|
||||||
|
"os": "none",
|
||||||
|
"env": "",
|
||||||
|
"vendor": "unknown",
|
||||||
|
"linker-flavor": "gcc",
|
||||||
|
"panic-strategy": "abort",
|
||||||
|
"pre-link-args": {
|
||||||
|
"gcc": ["-m32", "-nostdlib", "-static"]
|
||||||
|
},
|
||||||
|
"features": "-mmx,-sse,-sse2,-sse3,-ssse3,-sse4.1,-sse4.2,-avx,-avx2,+soft-float",
|
||||||
|
"rustc-abi": "x86-softfloat",
|
||||||
|
"dynamic-linking": false,
|
||||||
|
"executables": false,
|
||||||
|
"relocation-model": "static",
|
||||||
|
"code-model": "large",
|
||||||
|
"disable-redzone": true,
|
||||||
|
"frame-pointer": "always",
|
||||||
|
"exe-suffix": "",
|
||||||
|
"has-rpath": false,
|
||||||
|
"no-default-libraries": true,
|
||||||
|
"position-independent-executables": false,
|
||||||
|
"tls-model": "global-dynamic"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user