Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b0f4fee4c4 | |||
| d9f7a9e808 | |||
| 022ead54fd |
+2
-2
@@ -1,2 +1,2 @@
|
|||||||
/build
|
Cargo.lock
|
||||||
/target
|
target
|
||||||
|
|||||||
+12
-30
@@ -1,35 +1,17 @@
|
|||||||
image: "redoxos/redoxer:latest"
|
image: "redoxos/redoxer"
|
||||||
|
|
||||||
before_script:
|
|
||||||
- apt-get install nasm
|
|
||||||
- rustup component add rust-src
|
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- host
|
- build
|
||||||
|
|
||||||
build:i686:
|
workflow:
|
||||||
stage: host
|
rules:
|
||||||
script:
|
- if: '$CI_COMMIT_BRANCH == "master" && $CI_PROJECT_NAMESPACE == "redox-os"'
|
||||||
- mkdir -p target/i686
|
- if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "master"'
|
||||||
- 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:
|
build:linux:
|
||||||
stage: host
|
stage: build
|
||||||
script:
|
script: cargo +nightly build
|
||||||
- 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:
|
build:redox:
|
||||||
stage: host
|
stage: build
|
||||||
script:
|
script: redoxer build
|
||||||
- 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
|
|
||||||
|
|||||||
@@ -1,2 +0,0 @@
|
|||||||
[editor]
|
|
||||||
auto-format = false
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
[[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"] }
|
|
||||||
Generated
-360
@@ -1,360 +0,0 @@
|
|||||||
# This file is automatically @generated by Cargo.
|
|
||||||
# It is not intended for manual editing.
|
|
||||||
version = 4
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "aes"
|
|
||||||
version = "0.8.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
|
||||||
dependencies = [
|
|
||||||
"cfg-if",
|
|
||||||
"cipher",
|
|
||||||
"cpufeatures",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "argon2"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "db4ce4441f99dbd377ca8a8f57b698c44d0d6e712d8329b5040da5a64aa1ce73"
|
|
||||||
dependencies = [
|
|
||||||
"base64ct",
|
|
||||||
"blake2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "autocfg"
|
|
||||||
version = "1.5.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "base64ct"
|
|
||||||
version = "1.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bit_field"
|
|
||||||
version = "0.10.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "1.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bitflags"
|
|
||||||
version = "2.9.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "blake2"
|
|
||||||
version = "0.10.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
|
||||||
dependencies = [
|
|
||||||
"digest",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "block-buffer"
|
|
||||||
version = "0.10.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
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]]
|
|
||||||
name = "cfg-if"
|
|
||||||
version = "1.0.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cipher"
|
|
||||||
version = "0.4.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
|
||||||
dependencies = [
|
|
||||||
"crypto-common",
|
|
||||||
"inout",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "cpufeatures"
|
|
||||||
version = "0.2.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
|
|
||||||
dependencies = [
|
|
||||||
"libc",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "crypto-common"
|
|
||||||
version = "0.1.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
|
||||||
dependencies = [
|
|
||||||
"generic-array",
|
|
||||||
"typenum",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "digest"
|
|
||||||
version = "0.10.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
|
||||||
dependencies = [
|
|
||||||
"block-buffer",
|
|
||||||
"crypto-common",
|
|
||||||
"subtle",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "dmidecode"
|
|
||||||
version = "0.8.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "f5a070ca68f8ba202b05487d52b9ac56eaebb5b66cdd68d1a17e63174bb11e3b"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 1.3.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "endian-num"
|
|
||||||
version = "0.1.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
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 = [
|
|
||||||
"typenum",
|
|
||||||
"version_check",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "inout"
|
|
||||||
version = "0.1.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
|
|
||||||
dependencies = [
|
|
||||||
"generic-array",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libc"
|
|
||||||
version = "0.2.176"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "linked_list_allocator"
|
|
||||||
version = "0.10.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "9afa463f5405ee81cdb9cc2baf37e08ec7e4c8209442b5d72c04cfb2cd6e6286"
|
|
||||||
dependencies = [
|
|
||||||
"spinning_top",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lock_api"
|
|
||||||
version = "0.4.13"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
|
|
||||||
dependencies = [
|
|
||||||
"autocfg",
|
|
||||||
"scopeguard",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "log"
|
|
||||||
version = "0.4.28"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "lz4_flex"
|
|
||||||
version = "0.11.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "08ab2867e3eeeca90e844d1940eab391c9dc5228783db2ed999acbc0a9ed375a"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "raw-cpuid"
|
|
||||||
version = "10.7.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 1.3.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox-path"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "436d45c2b6a5b159d43da708e62b25be3a4a3d5550d654b72216ade4c4bfd717"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_bootloader"
|
|
||||||
version = "1.0.0"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 1.3.2",
|
|
||||||
"byteorder",
|
|
||||||
"dmidecode",
|
|
||||||
"fdt",
|
|
||||||
"linked_list_allocator",
|
|
||||||
"log",
|
|
||||||
"redox_syscall",
|
|
||||||
"redox_uefi",
|
|
||||||
"redox_uefi_std",
|
|
||||||
"redoxfs",
|
|
||||||
"spin",
|
|
||||||
"x86",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_syscall"
|
|
||||||
version = "0.5.17"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
|
|
||||||
dependencies = [
|
|
||||||
"bitflags 2.9.4",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_uefi"
|
|
||||||
version = "0.1.14"
|
|
||||||
source = "git+https://gitlab.redox-os.org/redox-os/uefi.git#26a499eeaf55d42fb24206830345e26fb8f5f835"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "redox_uefi_alloc"
|
|
||||||
version = "0.1.14"
|
|
||||||
source = "git+https://gitlab.redox-os.org/redox-os/uefi.git#26a499eeaf55d42fb24206830345e26fb8f5f835"
|
|
||||||
dependencies = [
|
|
||||||
"redox_uefi",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[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.8.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "063eedabd74ddf71810e72aae1c73f3485ffc7b1e757d9466b9099046c05d7be"
|
|
||||||
dependencies = [
|
|
||||||
"aes",
|
|
||||||
"argon2",
|
|
||||||
"base64ct",
|
|
||||||
"bitflags 2.9.4",
|
|
||||||
"endian-num",
|
|
||||||
"libc",
|
|
||||||
"log",
|
|
||||||
"lz4_flex",
|
|
||||||
"redox-path",
|
|
||||||
"redox_syscall",
|
|
||||||
"seahash",
|
|
||||||
"uuid",
|
|
||||||
"xts-mode",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "scopeguard"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "seahash"
|
|
||||||
version = "4.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "spin"
|
|
||||||
version = "0.9.8"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
|
||||||
dependencies = [
|
|
||||||
"lock_api",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "spinning_top"
|
|
||||||
version = "0.2.5"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "5b9eb1a2f4c41445a3a0ff9abc5221c5fcd28e1f13cd7c0397706f9ac938ddb0"
|
|
||||||
dependencies = [
|
|
||||||
"lock_api",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "subtle"
|
|
||||||
version = "2.6.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "typenum"
|
|
||||||
version = "1.18.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "uuid"
|
|
||||||
version = "1.18.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2"
|
|
||||||
|
|
||||||
[[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 = [
|
|
||||||
"bit_field",
|
|
||||||
"bitflags 1.3.2",
|
|
||||||
"raw-cpuid",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "xts-mode"
|
|
||||||
version = "0.5.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "09cbddb7545ca0b9ffa7bdc653e8743303e1712687a6918ced25f2cdbed42520"
|
|
||||||
dependencies = [
|
|
||||||
"byteorder",
|
|
||||||
"cipher",
|
|
||||||
]
|
|
||||||
+19
-46
@@ -1,52 +1,25 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "redox_bootloader"
|
name = "redox_syscall"
|
||||||
version = "1.0.0"
|
version = "0.8.1"
|
||||||
edition = "2024"
|
description = "A Rust library to access raw Redox system calls"
|
||||||
|
license = "MIT"
|
||||||
|
authors = ["Jeremy Soller <jackpot51@gmail.com>"]
|
||||||
|
repository = "https://gitlab.redox-os.org/redox-os/syscall"
|
||||||
|
documentation = "https://docs.rs/redox_syscall"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
# UEFI uses bin target
|
|
||||||
[[bin]]
|
|
||||||
name = "bootloader"
|
|
||||||
path = "src/main.rs"
|
|
||||||
|
|
||||||
# BIOS uses lib target
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "bootloader"
|
name = "syscall"
|
||||||
path = "src/main.rs"
|
|
||||||
crate-type = ["staticlib"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
bitflags = "1.3.2"
|
|
||||||
linked_list_allocator = "0.10.5"
|
|
||||||
log = "0.4.17"
|
|
||||||
redox_syscall = "0.5"
|
|
||||||
spin = "0.9.5"
|
|
||||||
|
|
||||||
[dependencies.redoxfs]
|
|
||||||
version = "0.8"
|
|
||||||
default-features = false
|
|
||||||
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]
|
[features]
|
||||||
default = []
|
default = ["userspace"]
|
||||||
live = []
|
rustc-dep-of-std = ["core", "bitflags/rustc-dep-of-std"]
|
||||||
serial_debug = []
|
userspace = []
|
||||||
|
std = []
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bitflags = "2.4"
|
||||||
|
core = { version = "1.0.0", optional = true, package = "rustc-std-workspace-core" }
|
||||||
|
|
||||||
|
[target.'cfg(loom)'.dev-dependencies]
|
||||||
|
loom = "0.7"
|
||||||
|
|||||||
@@ -1,21 +1,22 @@
|
|||||||
|
Copyright (c) 2017 Redox OS Developers
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) 2017-2022 Redox OS
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
The above copyright notice and this permission notice shall be
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
included in all copies or substantial portions of the Software.
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
copies or substantial portions of the Software.
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
TARGET?=x86_64-unknown-uefi
|
|
||||||
SOURCE:=$(dir $(realpath $(lastword $(MAKEFILE_LIST))))
|
|
||||||
BUILD:=$(CURDIR)
|
|
||||||
export RUST_TARGET_PATH?=$(SOURCE)/targets
|
|
||||||
|
|
||||||
|
|
||||||
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,62 +1,7 @@
|
|||||||
# Bootloader
|
# syscall
|
||||||
|
|
||||||
Redox OS Bootloader
|
This crate contains the system call numbers and Rust wrappers for the inline Assembly code of system calls.
|
||||||
|
|
||||||
## Requirements
|
[](./LICENSE)
|
||||||
|
[](https://crates.io/crates/redox_syscall)
|
||||||
These software needs to be available on the PATH at build time:
|
[](https://docs.rs/redox_syscall)
|
||||||
|
|
||||||
+ [mtools](https://www.gnu.org/software/mtools/)
|
|
||||||
+ [nasm](https://nasm.us/)
|
|
||||||
+ [redoxfs-ar](https://gitlab.redox-os.org/redox-os/redoxfs)
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
```sh
|
|
||||||
make TARGET=<triplet> BUILD=build all
|
|
||||||
```
|
|
||||||
|
|
||||||
The `<triplet>` is one of:
|
|
||||||
|
|
||||||
| ARCH | Boot Mode | Triplets |
|
|
||||||
|---|---|---|
|
|
||||||
| `i686` | BIOS | `x86-unknown-none` |
|
|
||||||
| `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.
|
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
interrupt_vector_table:
|
|
||||||
b . @ Reset
|
|
||||||
b .
|
|
||||||
b . @ SWI instruction
|
|
||||||
b .
|
|
||||||
b .
|
|
||||||
b .
|
|
||||||
b .
|
|
||||||
b .
|
|
||||||
|
|
||||||
.comm stack, 0x10000 @ Reserve 64k stack in the BSS
|
|
||||||
_start:
|
|
||||||
.globl _start
|
|
||||||
ldr sp, =stack+0x10000 @ Set up the stack
|
|
||||||
bl kstart @ Jump to the main function
|
|
||||||
1:
|
|
||||||
b 1b @ Halt
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,176 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,128 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
; 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
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,222 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
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
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
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_*) }
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
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*)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
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" \
|
|
||||||
cargo rustc \
|
|
||||||
--manifest-path="$<" \
|
|
||||||
-Z build-std=core,alloc \
|
|
||||||
-Z build-std-features=compiler-builtins-mem \
|
|
||||||
--target $(TARGET) \
|
|
||||||
--bin bootloader \
|
|
||||||
--release \
|
|
||||||
-- \
|
|
||||||
--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" \
|
|
||||||
cargo rustc \
|
|
||||||
--manifest-path="$<" \
|
|
||||||
-Z build-std=core,alloc \
|
|
||||||
-Z build-std-features=compiler-builtins-mem \
|
|
||||||
--target $(TARGET) \
|
|
||||||
--bin bootloader \
|
|
||||||
--release \
|
|
||||||
--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
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
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" \
|
|
||||||
cargo rustc \
|
|
||||||
--manifest-path="$<" \
|
|
||||||
-Z build-std=core,alloc \
|
|
||||||
-Z build-std-features=compiler-builtins-mem \
|
|
||||||
--target $(TARGET) \
|
|
||||||
--lib \
|
|
||||||
--release \
|
|
||||||
-- \
|
|
||||||
--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" \
|
|
||||||
cargo rustc \
|
|
||||||
--manifest-path="$<" \
|
|
||||||
-Z build-std=core,alloc \
|
|
||||||
-Z build-std-features=compiler-builtins-mem \
|
|
||||||
--target $(TARGET) \
|
|
||||||
--lib \
|
|
||||||
--release \
|
|
||||||
--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
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
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="$<" \
|
|
||||||
-Z build-std=core,alloc \
|
|
||||||
-Z build-std-features=compiler-builtins-mem \
|
|
||||||
--target "$(TARGET)" \
|
|
||||||
--lib \
|
|
||||||
--release \
|
|
||||||
-- \
|
|
||||||
--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="$<" \
|
|
||||||
-Z build-std=core,alloc \
|
|
||||||
-Z build-std-features=compiler-builtins-mem \
|
|
||||||
--target "$(TARGET)" \
|
|
||||||
--lib \
|
|
||||||
--release \
|
|
||||||
--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
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
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="$<" \
|
|
||||||
-Z build-std=core,alloc \
|
|
||||||
-Z build-std-features=compiler-builtins-mem \
|
|
||||||
--target $(TARGET) \
|
|
||||||
--bin bootloader \
|
|
||||||
--release \
|
|
||||||
-- \
|
|
||||||
--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="$<" \
|
|
||||||
-Z build-std=core,alloc \
|
|
||||||
-Z build-std-features=compiler-builtins-mem \
|
|
||||||
--target $(TARGET) \
|
|
||||||
--bin bootloader \
|
|
||||||
--release \
|
|
||||||
--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,3 +0,0 @@
|
|||||||
[toolchain]
|
|
||||||
channel = "nightly-2025-10-03"
|
|
||||||
components = ["rust-src"]
|
|
||||||
+195
-142
@@ -1,154 +1,207 @@
|
|||||||
use crate::area_add;
|
use core::{
|
||||||
use crate::os::{Os, OsMemoryEntry, OsMemoryKind, dtb::is_in_dev_mem_region};
|
mem,
|
||||||
use core::slice;
|
ops::{Deref, DerefMut},
|
||||||
|
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
|
use super::error::{Error, Result};
|
||||||
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() {
|
pub const PAGE_SIZE: usize = 4096;
|
||||||
let l3 = paging_allocate(os)?;
|
/// Size of the metadata region used to transfer information from the kernel to the bootstrapper.
|
||||||
assert_eq!(l2[l2_i], 0);
|
pub const KERNEL_METADATA_SIZE: usize = 4 * PAGE_SIZE;
|
||||||
l2[l2_i] = l3.as_ptr() as u64 | PF_ACCESS | PF_TABLE | PF_PRESENT;
|
|
||||||
|
|
||||||
while framebuffer_mapped < framebuffer_size && l3_i < l3.len() {
|
#[cfg(feature = "userspace")]
|
||||||
let addr = framebuffer_phys + framebuffer_mapped;
|
macro_rules! syscall {
|
||||||
assert_eq!(l3[l3_i], 0);
|
($($name:ident($a:ident, $($b:ident, $($c:ident, $($d:ident, $($e:ident, $($f:ident, $($g:ident, )?)?)?)?)?)?);)+) => {
|
||||||
//TODO: is PF_RAM okay?
|
$(
|
||||||
l3[l3_i] = addr | PF_ACCESS | PF_RAM | PF_TABLE | PF_PRESENT;
|
pub unsafe fn $name($a: usize, $($b: usize, $($c: usize, $($d: usize, $($e: usize, $($f: usize, $($g: usize)?)?)?)?)?)?) -> Result<usize> {
|
||||||
framebuffer_mapped += PAGE_SIZE as u64;
|
let ret: usize;
|
||||||
l3_i += 1;
|
|
||||||
|
core::arch::asm!(
|
||||||
|
"svc 0",
|
||||||
|
in("x8") $a,
|
||||||
|
$(
|
||||||
|
in("x0") $b,
|
||||||
|
$(
|
||||||
|
in("x1") $c,
|
||||||
|
$(
|
||||||
|
in("x2") $d,
|
||||||
|
$(
|
||||||
|
in("x3") $e,
|
||||||
|
$(
|
||||||
|
in("x4") $f,
|
||||||
|
$(
|
||||||
|
in("x5") $g,
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
lateout("x0") ret,
|
||||||
|
options(nostack),
|
||||||
|
);
|
||||||
|
|
||||||
|
Error::demux(ret)
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
l2_i += 1;
|
#[cfg(feature = "userspace")]
|
||||||
l3_i = 0;
|
syscall! {
|
||||||
|
syscall0(a,);
|
||||||
|
syscall1(a, b,);
|
||||||
|
syscall2(a, b, c,);
|
||||||
|
syscall3(a, b, c, d,);
|
||||||
|
syscall4(a, b, c, d, e,);
|
||||||
|
syscall5(a, b, c, d, e, f,);
|
||||||
|
syscall6(a, b, c, d, e, f, g,);
|
||||||
}
|
}
|
||||||
|
|
||||||
l1_i += 1;
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
l2_i = 0;
|
#[repr(C)]
|
||||||
|
pub struct IntRegisters {
|
||||||
|
pub x30: usize,
|
||||||
|
pub x29: usize,
|
||||||
|
pub x28: usize,
|
||||||
|
pub x27: usize,
|
||||||
|
pub x26: usize,
|
||||||
|
pub x25: usize,
|
||||||
|
pub x24: usize,
|
||||||
|
pub x23: usize,
|
||||||
|
pub x22: usize,
|
||||||
|
pub x21: usize,
|
||||||
|
pub x20: usize,
|
||||||
|
pub x19: usize,
|
||||||
|
pub x18: usize,
|
||||||
|
pub x17: usize,
|
||||||
|
pub x16: usize,
|
||||||
|
pub x15: usize,
|
||||||
|
pub x14: usize,
|
||||||
|
pub x13: usize,
|
||||||
|
pub x12: usize,
|
||||||
|
pub x11: usize,
|
||||||
|
pub x10: usize,
|
||||||
|
pub x9: usize,
|
||||||
|
pub x8: usize,
|
||||||
|
pub x7: usize,
|
||||||
|
pub x6: usize,
|
||||||
|
pub x5: usize,
|
||||||
|
pub x4: usize,
|
||||||
|
pub x3: usize,
|
||||||
|
pub x2: usize,
|
||||||
|
pub x1: usize,
|
||||||
|
pub x0: usize,
|
||||||
}
|
}
|
||||||
assert!(framebuffer_mapped >= framebuffer_size);
|
|
||||||
|
|
||||||
Some(framebuffer_phys + PHYS_OFFSET)
|
impl Deref for IntRegisters {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const IntRegisters as *const u8,
|
||||||
|
mem::size_of::<IntRegisters>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for IntRegisters {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut IntRegisters as *mut u8,
|
||||||
|
mem::size_of::<IntRegisters>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct FloatRegisters {
|
||||||
|
pub fp_simd_regs: [u128; 32],
|
||||||
|
pub fpsr: u32,
|
||||||
|
pub fpcr: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for FloatRegisters {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const FloatRegisters as *const u8,
|
||||||
|
mem::size_of::<FloatRegisters>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for FloatRegisters {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut FloatRegisters as *mut u8,
|
||||||
|
mem::size_of::<FloatRegisters>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct EnvRegisters {
|
||||||
|
pub tpidr_el0: usize,
|
||||||
|
pub tpidrro_el0: usize,
|
||||||
|
}
|
||||||
|
impl Deref for EnvRegisters {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const EnvRegisters as *const u8,
|
||||||
|
mem::size_of::<EnvRegisters>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for EnvRegisters {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut EnvRegisters as *mut u8,
|
||||||
|
mem::size_of::<EnvRegisters>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct Exception {
|
||||||
|
pub kind: usize,
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
impl Deref for Exception {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const Exception as *const u8,
|
||||||
|
mem::size_of::<Exception>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for Exception {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut Exception as *mut u8,
|
||||||
|
mem::size_of::<Exception>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
#[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,206 @@
|
|||||||
|
use super::error::{Error, Result};
|
||||||
|
use core::arch::asm;
|
||||||
|
use core::{
|
||||||
|
mem,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
slice,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const PAGE_SIZE: usize = 4096;
|
||||||
|
/// Size of the metadata region used to transfer information from the kernel to the bootstrapper.
|
||||||
|
pub const KERNEL_METADATA_SIZE: usize = 4 * PAGE_SIZE;
|
||||||
|
|
||||||
|
#[cfg(feature = "userspace")]
|
||||||
|
macro_rules! syscall {
|
||||||
|
($($name:ident($a:ident, $($b:ident, $($c:ident, $($d:ident, $($e:ident, $($f:ident, $($g:ident, )?)?)?)?)?)?);)+) => {
|
||||||
|
$(
|
||||||
|
pub unsafe fn $name($a: usize, $($b: usize, $($c: usize, $($d: usize, $($e: usize, $($f: usize, $($g: usize)?)?)?)?)?)?) -> Result<usize> {
|
||||||
|
let ret: usize;
|
||||||
|
|
||||||
|
asm!(
|
||||||
|
"ecall",
|
||||||
|
in("a7") $a,
|
||||||
|
$(
|
||||||
|
in("a0") $b,
|
||||||
|
$(
|
||||||
|
in("a1") $c,
|
||||||
|
$(
|
||||||
|
in("a2") $d,
|
||||||
|
$(
|
||||||
|
in("a3") $e,
|
||||||
|
$(
|
||||||
|
in("a4") $f,
|
||||||
|
$(
|
||||||
|
in("a5") $g,
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
lateout("a0") ret,
|
||||||
|
options(nostack),
|
||||||
|
);
|
||||||
|
|
||||||
|
Error::demux(ret)
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "userspace")]
|
||||||
|
syscall! {
|
||||||
|
syscall0(a,);
|
||||||
|
syscall1(a, b,);
|
||||||
|
syscall2(a, b, c,);
|
||||||
|
syscall3(a, b, c, d,);
|
||||||
|
syscall4(a, b, c, d, e,);
|
||||||
|
syscall5(a, b, c, d, e, f,);
|
||||||
|
syscall6(a, b, c, d, e, f, g,);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct IntRegisters {
|
||||||
|
pub pc: usize,
|
||||||
|
pub x31: usize,
|
||||||
|
pub x30: usize,
|
||||||
|
pub x29: usize,
|
||||||
|
pub x28: usize,
|
||||||
|
pub x27: usize,
|
||||||
|
pub x26: usize,
|
||||||
|
pub x25: usize,
|
||||||
|
pub x24: usize,
|
||||||
|
pub x23: usize,
|
||||||
|
pub x22: usize,
|
||||||
|
pub x21: usize,
|
||||||
|
pub x20: usize,
|
||||||
|
pub x19: usize,
|
||||||
|
pub x18: usize,
|
||||||
|
pub x17: usize,
|
||||||
|
pub x16: usize,
|
||||||
|
pub x15: usize,
|
||||||
|
pub x14: usize,
|
||||||
|
pub x13: usize,
|
||||||
|
pub x12: usize,
|
||||||
|
pub x11: usize,
|
||||||
|
pub x10: usize,
|
||||||
|
pub x9: usize,
|
||||||
|
pub x8: usize,
|
||||||
|
pub x7: usize,
|
||||||
|
pub x6: usize,
|
||||||
|
pub x5: usize,
|
||||||
|
// x4(tp) is in env
|
||||||
|
// x3(gp) is a platform scratch register
|
||||||
|
pub x2: usize,
|
||||||
|
pub x1: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for IntRegisters {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const IntRegisters as *const u8,
|
||||||
|
mem::size_of::<IntRegisters>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for IntRegisters {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut IntRegisters as *mut u8,
|
||||||
|
mem::size_of::<IntRegisters>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct FloatRegisters {
|
||||||
|
pub fregs: [u64; 32],
|
||||||
|
pub fcsr: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for FloatRegisters {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const FloatRegisters as *const u8,
|
||||||
|
mem::size_of::<FloatRegisters>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for FloatRegisters {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut FloatRegisters as *mut u8,
|
||||||
|
mem::size_of::<FloatRegisters>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
#[repr(packed)]
|
||||||
|
pub struct EnvRegisters {
|
||||||
|
pub tp: usize,
|
||||||
|
}
|
||||||
|
impl Deref for EnvRegisters {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const EnvRegisters as *const u8,
|
||||||
|
mem::size_of::<EnvRegisters>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for EnvRegisters {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut EnvRegisters as *mut u8,
|
||||||
|
mem::size_of::<EnvRegisters>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct Exception {
|
||||||
|
pub kind: usize,
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
impl Deref for Exception {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const Exception as *const u8,
|
||||||
|
mem::size_of::<Exception>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for Exception {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut Exception as *mut u8,
|
||||||
|
mem::size_of::<Exception>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
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,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,124 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+288
@@ -0,0 +1,288 @@
|
|||||||
|
use core::{
|
||||||
|
arch::asm,
|
||||||
|
mem,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
slice,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::error::{Error, Result};
|
||||||
|
|
||||||
|
pub const PAGE_SIZE: usize = 4096;
|
||||||
|
/// Size of the metadata region used to transfer information from the kernel to the bootstrapper.
|
||||||
|
pub const KERNEL_METADATA_SIZE: usize = 4 * PAGE_SIZE;
|
||||||
|
|
||||||
|
#[cfg(feature = "userspace")]
|
||||||
|
macro_rules! syscall {
|
||||||
|
($($name:ident($a:ident, $($b:ident, $($c:ident, $($d:ident, $($e:ident, $($f:ident, )?)?)?)?)?);)+) => {
|
||||||
|
$(
|
||||||
|
pub unsafe fn $name(mut $a: usize, $($b: usize, $($c: usize, $($d: usize, $($e: usize, $($f: usize)?)?)?)?)?) -> Result<usize> {
|
||||||
|
asm!(
|
||||||
|
"int 0x80",
|
||||||
|
inout("eax") $a,
|
||||||
|
$(
|
||||||
|
in("ebx") $b,
|
||||||
|
$(
|
||||||
|
in("ecx") $c,
|
||||||
|
$(
|
||||||
|
in("edx") $d,
|
||||||
|
$(
|
||||||
|
in("esi") $e,
|
||||||
|
$(
|
||||||
|
in("edi") $f,
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
options(nostack),
|
||||||
|
);
|
||||||
|
|
||||||
|
Error::demux($a)
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "userspace")]
|
||||||
|
syscall! {
|
||||||
|
syscall0(a,);
|
||||||
|
syscall1(a, b,);
|
||||||
|
syscall2(a, b, c,);
|
||||||
|
syscall3(a, b, c, d,);
|
||||||
|
// Must be done custom because LLVM reserves ESI
|
||||||
|
//syscall4(a, b, c, d, e,);
|
||||||
|
//syscall5(a, b, c, d, e, f,);
|
||||||
|
//syscall6(a, b, c, d, e, f, g,);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "userspace")]
|
||||||
|
pub unsafe fn syscall4(mut a: usize, b: usize, c: usize, d: usize, e: usize) -> Result<usize> {
|
||||||
|
asm!(
|
||||||
|
"xchg esi, {e}
|
||||||
|
int 0x80
|
||||||
|
xchg esi, {e}",
|
||||||
|
e = in(reg) e,
|
||||||
|
inout("eax") a,
|
||||||
|
in("ebx") b,
|
||||||
|
in("ecx") c,
|
||||||
|
in("edx") d,
|
||||||
|
options(nostack),
|
||||||
|
);
|
||||||
|
|
||||||
|
Error::demux(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "userspace")]
|
||||||
|
pub unsafe fn syscall5(
|
||||||
|
mut a: usize,
|
||||||
|
b: usize,
|
||||||
|
c: usize,
|
||||||
|
d: usize,
|
||||||
|
e: usize,
|
||||||
|
f: usize,
|
||||||
|
) -> Result<usize> {
|
||||||
|
asm!(
|
||||||
|
"xchg esi, {e}
|
||||||
|
int 0x80
|
||||||
|
xchg esi, {e}",
|
||||||
|
e = in(reg) e,
|
||||||
|
inout("eax") a,
|
||||||
|
in("ebx") b,
|
||||||
|
in("ecx") c,
|
||||||
|
in("edx") d,
|
||||||
|
in("edi") f,
|
||||||
|
options(nostack),
|
||||||
|
);
|
||||||
|
|
||||||
|
Error::demux(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "userspace")]
|
||||||
|
pub unsafe fn syscall6(
|
||||||
|
mut a: usize,
|
||||||
|
b: usize,
|
||||||
|
c: usize,
|
||||||
|
d: usize,
|
||||||
|
e: usize,
|
||||||
|
f: usize,
|
||||||
|
g: usize,
|
||||||
|
) -> Result<usize> {
|
||||||
|
#[repr(C)]
|
||||||
|
struct PackedArgs {
|
||||||
|
arg4: usize,
|
||||||
|
arg6: usize,
|
||||||
|
nr: usize,
|
||||||
|
}
|
||||||
|
let args = PackedArgs {
|
||||||
|
arg4: e,
|
||||||
|
arg6: g,
|
||||||
|
nr: a,
|
||||||
|
};
|
||||||
|
let args_ptr = &args as *const PackedArgs;
|
||||||
|
asm!(
|
||||||
|
"push ebp",
|
||||||
|
"push esi",
|
||||||
|
"mov esi, [eax + 0]", // arg4 -> esi
|
||||||
|
"mov ebp, [eax + 4]", // arg6 -> ebp
|
||||||
|
"mov eax, [eax + 8]", // nr -> eax
|
||||||
|
"int 0x80",
|
||||||
|
"pop esi",
|
||||||
|
"pop ebp",
|
||||||
|
inout("eax") args_ptr => a,
|
||||||
|
in("ebx") b,
|
||||||
|
in("ecx") c,
|
||||||
|
in("edx") d,
|
||||||
|
in("edi") f,
|
||||||
|
options(nostack),
|
||||||
|
);
|
||||||
|
|
||||||
|
Error::demux(a)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct IntRegisters {
|
||||||
|
// TODO: Some of these don't get set by Redox yet. Should they?
|
||||||
|
pub ebp: usize,
|
||||||
|
pub esi: usize,
|
||||||
|
pub edi: usize,
|
||||||
|
pub ebx: usize,
|
||||||
|
pub eax: usize,
|
||||||
|
pub ecx: usize,
|
||||||
|
pub edx: usize,
|
||||||
|
// pub orig_rax: usize,
|
||||||
|
pub eip: usize,
|
||||||
|
pub cs: usize,
|
||||||
|
pub eflags: usize,
|
||||||
|
pub esp: usize,
|
||||||
|
pub ss: usize,
|
||||||
|
// pub fs_base: usize,
|
||||||
|
// pub gs_base: usize,
|
||||||
|
// pub ds: usize,
|
||||||
|
// pub es: usize,
|
||||||
|
pub fs: usize,
|
||||||
|
// pub gs: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for IntRegisters {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const IntRegisters as *const u8,
|
||||||
|
mem::size_of::<IntRegisters>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for IntRegisters {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut IntRegisters as *mut u8,
|
||||||
|
mem::size_of::<IntRegisters>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct FloatRegisters {
|
||||||
|
pub fcw: u16,
|
||||||
|
pub fsw: u16,
|
||||||
|
pub ftw: u8,
|
||||||
|
pub _reserved: u8,
|
||||||
|
pub fop: u16,
|
||||||
|
pub fip: u64,
|
||||||
|
pub fdp: u64,
|
||||||
|
pub mxcsr: u32,
|
||||||
|
pub mxcsr_mask: u32,
|
||||||
|
pub st_space: [u128; 8],
|
||||||
|
pub xmm_space: [u128; 16],
|
||||||
|
// TODO: YMM/ZMM
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for FloatRegisters {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const FloatRegisters as *const u8,
|
||||||
|
mem::size_of::<FloatRegisters>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for FloatRegisters {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut FloatRegisters as *mut u8,
|
||||||
|
mem::size_of::<FloatRegisters>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct EnvRegisters {
|
||||||
|
pub fsbase: u32,
|
||||||
|
pub gsbase: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for EnvRegisters {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const EnvRegisters as *const u8,
|
||||||
|
mem::size_of::<EnvRegisters>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for EnvRegisters {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut EnvRegisters as *mut u8,
|
||||||
|
mem::size_of::<EnvRegisters>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct Exception {
|
||||||
|
pub kind: usize,
|
||||||
|
pub code: usize,
|
||||||
|
pub address: usize,
|
||||||
|
}
|
||||||
|
impl Deref for Exception {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const Exception as *const u8,
|
||||||
|
mem::size_of::<Exception>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for Exception {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut Exception as *mut u8,
|
||||||
|
mem::size_of::<Exception>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
use core::{
|
||||||
|
mem,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
slice,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const PAGE_SIZE: usize = 4096;
|
||||||
|
/// Size of the metadata region used to transfer information from the kernel to the bootstrapper.
|
||||||
|
pub const KERNEL_METADATA_SIZE: usize = 4 * PAGE_SIZE;
|
||||||
|
|
||||||
|
#[cfg(feature = "userspace")]
|
||||||
|
macro_rules! syscall {
|
||||||
|
($($name:ident($a:ident, $($b:ident, $($c:ident, $($d:ident, $($e:ident, $($f:ident, $($g:ident, )?)?)?)?)?)?);)+) => {
|
||||||
|
$(
|
||||||
|
pub unsafe fn $name(mut $a: usize, $($b: usize, $($c: usize, $($d: usize, $($e: usize, $($f: usize, $($g: usize)?)?)?)?)?)?) -> crate::error::Result<usize> {
|
||||||
|
core::arch::asm!(
|
||||||
|
"syscall",
|
||||||
|
inout("rax") $a,
|
||||||
|
$(
|
||||||
|
in("rdi") $b,
|
||||||
|
$(
|
||||||
|
in("rsi") $c,
|
||||||
|
$(
|
||||||
|
in("rdx") $d,
|
||||||
|
$(
|
||||||
|
in("r10") $e,
|
||||||
|
$(
|
||||||
|
in("r8") $f,
|
||||||
|
$(
|
||||||
|
in("r9") $g,
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
out("rcx") _,
|
||||||
|
out("r11") _,
|
||||||
|
options(nostack),
|
||||||
|
);
|
||||||
|
|
||||||
|
crate::error::Error::demux($a)
|
||||||
|
}
|
||||||
|
)+
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "userspace")]
|
||||||
|
syscall! {
|
||||||
|
syscall0(a,);
|
||||||
|
syscall1(a, b,);
|
||||||
|
syscall2(a, b, c,);
|
||||||
|
syscall3(a, b, c, d,);
|
||||||
|
syscall4(a, b, c, d, e,);
|
||||||
|
syscall5(a, b, c, d, e, f,);
|
||||||
|
syscall6(a, b, c, d, e, f, g,);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct IntRegisters {
|
||||||
|
pub r15: usize,
|
||||||
|
pub r14: usize,
|
||||||
|
pub r13: usize,
|
||||||
|
pub r12: usize,
|
||||||
|
pub rbp: usize,
|
||||||
|
pub rbx: usize,
|
||||||
|
pub r11: usize,
|
||||||
|
pub r10: usize,
|
||||||
|
pub r9: usize,
|
||||||
|
pub r8: usize,
|
||||||
|
pub rax: usize,
|
||||||
|
pub rcx: usize,
|
||||||
|
pub rdx: usize,
|
||||||
|
pub rsi: usize,
|
||||||
|
pub rdi: usize,
|
||||||
|
pub rip: usize,
|
||||||
|
pub cs: usize,
|
||||||
|
pub rflags: usize,
|
||||||
|
pub rsp: usize,
|
||||||
|
pub ss: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for IntRegisters {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe { slice::from_raw_parts(self as *const Self as *const u8, mem::size_of::<Self>()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for IntRegisters {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe { slice::from_raw_parts_mut(self as *mut Self as *mut u8, mem::size_of::<Self>()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct FloatRegisters {
|
||||||
|
pub fcw: u16,
|
||||||
|
pub fsw: u16,
|
||||||
|
pub ftw: u8,
|
||||||
|
pub _reserved: u8,
|
||||||
|
pub fop: u16,
|
||||||
|
pub fip: u64,
|
||||||
|
pub fdp: u64,
|
||||||
|
pub mxcsr: u32,
|
||||||
|
pub mxcsr_mask: u32,
|
||||||
|
pub st_space: [u128; 8],
|
||||||
|
pub xmm_space: [u128; 16],
|
||||||
|
// TODO: YMM/ZMM
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for FloatRegisters {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const FloatRegisters as *const u8,
|
||||||
|
mem::size_of::<FloatRegisters>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for FloatRegisters {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut FloatRegisters as *mut u8,
|
||||||
|
mem::size_of::<FloatRegisters>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct EnvRegisters {
|
||||||
|
pub fsbase: u64,
|
||||||
|
pub gsbase: u64,
|
||||||
|
// TODO: PKRU?
|
||||||
|
}
|
||||||
|
impl Deref for EnvRegisters {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const EnvRegisters as *const u8,
|
||||||
|
mem::size_of::<EnvRegisters>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for EnvRegisters {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut EnvRegisters as *mut u8,
|
||||||
|
mem::size_of::<EnvRegisters>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct Exception {
|
||||||
|
pub kind: usize,
|
||||||
|
pub code: usize,
|
||||||
|
pub address: usize,
|
||||||
|
}
|
||||||
|
impl Deref for Exception {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const Exception as *const u8,
|
||||||
|
mem::size_of::<Exception>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for Exception {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut Exception as *mut u8,
|
||||||
|
mem::size_of::<Exception>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+410
@@ -0,0 +1,410 @@
|
|||||||
|
use super::{
|
||||||
|
arch::*,
|
||||||
|
data::{Map, Stat, StatVfs, StdFsCallMeta, TimeSpec},
|
||||||
|
error::Result,
|
||||||
|
flag::*,
|
||||||
|
number::*,
|
||||||
|
};
|
||||||
|
|
||||||
|
use core::mem;
|
||||||
|
|
||||||
|
/// Close a file
|
||||||
|
pub fn close(fd: usize) -> Result<usize> {
|
||||||
|
unsafe { syscall1(SYS_CLOSE, fd) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the current system time
|
||||||
|
pub fn clock_gettime(clock: usize, tp: &mut TimeSpec) -> Result<usize> {
|
||||||
|
unsafe { syscall2(SYS_CLOCK_GETTIME, clock, tp as *mut TimeSpec as usize) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copy and transform a file descriptor
|
||||||
|
pub fn dup(fd: usize, buf: &[u8]) -> Result<usize> {
|
||||||
|
unsafe { syscall3(SYS_DUP, fd, buf.as_ptr() as usize, buf.len()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copy and transform a file descriptor
|
||||||
|
pub fn dup2(fd: usize, newfd: usize, buf: &[u8]) -> Result<usize> {
|
||||||
|
unsafe { syscall4(SYS_DUP2, fd, newfd, buf.as_ptr() as usize, buf.len()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Change file permissions
|
||||||
|
pub fn fchmod(fd: usize, mode: u16) -> Result<usize> {
|
||||||
|
unsafe { syscall2(SYS_FCHMOD, fd, mode as usize) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Change file ownership
|
||||||
|
pub fn fchown(fd: usize, uid: u32, gid: u32) -> Result<usize> {
|
||||||
|
unsafe { syscall3(SYS_FCHOWN, fd, uid as usize, gid as usize) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Change file descriptor flags
|
||||||
|
pub fn fcntl(fd: usize, cmd: usize, arg: usize) -> Result<usize> {
|
||||||
|
unsafe { syscall3(SYS_FCNTL, fd, cmd, arg) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Map a file into memory, but with the ability to set the address to map into, either as a hint
|
||||||
|
/// or as a requirement of the map.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
/// `EACCES` - the file descriptor was not open for reading
|
||||||
|
/// `EBADF` - if the file descriptor was invalid
|
||||||
|
/// `ENODEV` - mmapping was not supported
|
||||||
|
/// `EINVAL` - invalid combination of flags
|
||||||
|
/// `EEXIST` - if [`MapFlags::MAP_FIXED`] was set, and the address specified was already in use.
|
||||||
|
///
|
||||||
|
pub unsafe fn fmap(fd: usize, map: &Map) -> Result<usize> {
|
||||||
|
syscall3(
|
||||||
|
SYS_FMAP,
|
||||||
|
fd,
|
||||||
|
map as *const Map as usize,
|
||||||
|
mem::size_of::<Map>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Unmap whole (or partial) continous memory-mapped files
|
||||||
|
pub unsafe fn funmap(addr: usize, len: usize) -> Result<usize> {
|
||||||
|
syscall2(SYS_FUNMAP, addr, len)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieve the canonical path of a file
|
||||||
|
pub fn fpath(fd: usize, buf: &mut [u8]) -> Result<usize> {
|
||||||
|
unsafe { syscall3(SYS_FPATH, fd, buf.as_mut_ptr() as usize, buf.len()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a link to a file
|
||||||
|
pub fn flink<T: AsRef<str>>(fd: usize, path: T) -> Result<usize> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
unsafe { syscall3(SYS_FLINK, fd, path.as_ptr() as usize, path.len()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Rename a file
|
||||||
|
pub fn frename<T: AsRef<str>>(fd: usize, path: T) -> Result<usize> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
unsafe { syscall3(SYS_FRENAME, fd, path.as_ptr() as usize, path.len()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get metadata about a file
|
||||||
|
pub fn fstat(fd: usize, stat: &mut Stat) -> Result<usize> {
|
||||||
|
unsafe {
|
||||||
|
syscall3(
|
||||||
|
SYS_FSTAT,
|
||||||
|
fd,
|
||||||
|
stat as *mut Stat as usize,
|
||||||
|
mem::size_of::<Stat>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get metadata about a filesystem
|
||||||
|
pub fn fstatvfs(fd: usize, stat: &mut StatVfs) -> Result<usize> {
|
||||||
|
unsafe {
|
||||||
|
syscall3(
|
||||||
|
SYS_FSTATVFS,
|
||||||
|
fd,
|
||||||
|
stat as *mut StatVfs as usize,
|
||||||
|
mem::size_of::<StatVfs>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sync a file descriptor to its underlying medium
|
||||||
|
pub fn fsync(fd: usize) -> Result<usize> {
|
||||||
|
unsafe { syscall1(SYS_FSYNC, fd) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Truncate or extend a file to a specified length
|
||||||
|
pub fn ftruncate(fd: usize, len: usize) -> Result<usize> {
|
||||||
|
unsafe { syscall2(SYS_FTRUNCATE, fd, len) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change modify and/or access times
|
||||||
|
pub fn futimens(fd: usize, times: &[TimeSpec]) -> Result<usize> {
|
||||||
|
unsafe {
|
||||||
|
syscall3(
|
||||||
|
SYS_FUTIMENS,
|
||||||
|
fd,
|
||||||
|
times.as_ptr() as usize,
|
||||||
|
mem::size_of_val(times),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fast userspace mutex
|
||||||
|
pub unsafe fn futex(
|
||||||
|
addr: *mut i32,
|
||||||
|
op: usize,
|
||||||
|
val: i32,
|
||||||
|
val2: usize,
|
||||||
|
addr2: *mut i32,
|
||||||
|
) -> Result<usize> {
|
||||||
|
syscall5(
|
||||||
|
SYS_FUTEX,
|
||||||
|
addr as usize,
|
||||||
|
op,
|
||||||
|
(val as isize) as usize,
|
||||||
|
val2,
|
||||||
|
addr2 as usize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Seek to `offset` bytes in a file descriptor
|
||||||
|
pub fn lseek(fd: usize, offset: isize, whence: usize) -> Result<usize> {
|
||||||
|
unsafe { syscall3(SYS_LSEEK, fd, offset as usize, whence) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make a new scheme namespace
|
||||||
|
pub fn mkns(schemes: &[[usize; 2]]) -> Result<usize> {
|
||||||
|
unsafe { syscall2(SYS_MKNS, schemes.as_ptr() as usize, schemes.len()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Change mapping flags
|
||||||
|
pub unsafe fn mprotect(addr: usize, size: usize, flags: MapFlags) -> Result<usize> {
|
||||||
|
syscall3(SYS_MPROTECT, addr, size, flags.bits())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sleep for the time specified in `req`
|
||||||
|
pub fn nanosleep(req: &TimeSpec, rem: &mut TimeSpec) -> Result<usize> {
|
||||||
|
unsafe {
|
||||||
|
syscall2(
|
||||||
|
SYS_NANOSLEEP,
|
||||||
|
req as *const TimeSpec as usize,
|
||||||
|
rem as *mut TimeSpec as usize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Open a file at a specific path
|
||||||
|
pub fn openat<T: AsRef<str>>(
|
||||||
|
fd: usize,
|
||||||
|
path: T,
|
||||||
|
flags: usize,
|
||||||
|
fcntl_flags: usize,
|
||||||
|
) -> Result<usize> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
unsafe {
|
||||||
|
syscall5(
|
||||||
|
SYS_OPENAT,
|
||||||
|
fd,
|
||||||
|
path.as_ptr() as usize,
|
||||||
|
path.len(),
|
||||||
|
flags,
|
||||||
|
fcntl_flags,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Open a file at a specific path with filter
|
||||||
|
pub fn openat_with_filter<T: AsRef<str>>(
|
||||||
|
fd: usize,
|
||||||
|
path: T,
|
||||||
|
flags: usize,
|
||||||
|
fcntl_flags: usize,
|
||||||
|
euid: u32,
|
||||||
|
egid: u32,
|
||||||
|
) -> Result<usize> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
unsafe {
|
||||||
|
syscall6(
|
||||||
|
SYS_OPENAT_WITH_FILTER,
|
||||||
|
fd,
|
||||||
|
path.as_ptr() as usize,
|
||||||
|
path.len(),
|
||||||
|
flags | fcntl_flags,
|
||||||
|
// NOTE: Short-term solution to allow namespace management.
|
||||||
|
// In the long term, we need to figure out how we should best handle
|
||||||
|
// Unix permissions using capabilities.
|
||||||
|
euid as usize,
|
||||||
|
egid as usize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a file at at specific path
|
||||||
|
pub fn unlinkat<T: AsRef<str>>(fd: usize, path: T, flags: usize) -> Result<usize> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
unsafe { syscall4(SYS_UNLINKAT, fd, path.as_ptr() as usize, path.len(), flags) }
|
||||||
|
}
|
||||||
|
/// Remove a file at at specific path with filter
|
||||||
|
pub fn unlinkat_with_filter<T: AsRef<str>>(
|
||||||
|
fd: usize,
|
||||||
|
path: T,
|
||||||
|
flags: usize,
|
||||||
|
euid: u32,
|
||||||
|
egid: u32,
|
||||||
|
) -> Result<usize> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
unsafe {
|
||||||
|
syscall6(
|
||||||
|
SYS_UNLINKAT_WITH_FILTER,
|
||||||
|
fd,
|
||||||
|
path.as_ptr() as usize,
|
||||||
|
path.len(),
|
||||||
|
flags,
|
||||||
|
// NOTE: Short-term solution to allow namespace management.
|
||||||
|
// In the long term, we need to figure out how we should best handle
|
||||||
|
// Unix permissions using capabilities.
|
||||||
|
euid as usize,
|
||||||
|
egid as usize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read from a file descriptor into a buffer
|
||||||
|
pub fn read(fd: usize, buf: &mut [u8]) -> Result<usize> {
|
||||||
|
unsafe { syscall3(SYS_READ, fd, buf.as_mut_ptr() as usize, buf.len()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write a buffer to a file descriptor
|
||||||
|
///
|
||||||
|
/// The kernel will attempt to write the bytes in `buf` to the file descriptor `fd`, returning
|
||||||
|
/// either an `Err`, explained below, or `Ok(count)` where `count` is the number of bytes which
|
||||||
|
/// were written.
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// * `EAGAIN` - the file descriptor was opened with `O_NONBLOCK` and writing would block
|
||||||
|
/// * `EBADF` - the file descriptor is not valid or is not open for writing
|
||||||
|
/// * `EFAULT` - `buf` does not point to the process's addressible memory
|
||||||
|
/// * `EIO` - an I/O error occurred
|
||||||
|
/// * `ENOSPC` - the device containing the file descriptor has no room for data
|
||||||
|
/// * `EPIPE` - the file descriptor refers to a pipe or socket whose reading end is closed
|
||||||
|
pub fn write(fd: usize, buf: &[u8]) -> Result<usize> {
|
||||||
|
unsafe { syscall3(SYS_WRITE, fd, buf.as_ptr() as usize, buf.len()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Yield the process's time slice to the kernel
|
||||||
|
///
|
||||||
|
/// This function will return Ok(0) on success
|
||||||
|
pub fn sched_yield() -> Result<usize> {
|
||||||
|
unsafe { syscall0(SYS_YIELD) }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Send a file descriptor `fd`, handled by the scheme providing `receiver_socket`. `flags` is
|
||||||
|
/// currently unused (must be zero), and `arg` is included in the scheme call.
|
||||||
|
///
|
||||||
|
/// The scheme can return an arbitrary value.
|
||||||
|
pub fn sendfd(receiver_socket: usize, fd: usize, flags: usize, arg: u64) -> Result<usize> {
|
||||||
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
unsafe {
|
||||||
|
syscall5(
|
||||||
|
SYS_SENDFD,
|
||||||
|
receiver_socket,
|
||||||
|
fd,
|
||||||
|
flags,
|
||||||
|
arg as u32 as usize,
|
||||||
|
(arg >> 32) as u32 as usize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_pointer_width = "64")]
|
||||||
|
unsafe {
|
||||||
|
syscall4(SYS_SENDFD, receiver_socket, fd, flags, arg as usize)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Call {
|
||||||
|
unsafe fn raw_call(
|
||||||
|
&self,
|
||||||
|
payload_ptr: *const u8,
|
||||||
|
len: usize,
|
||||||
|
flags: CallFlags,
|
||||||
|
metadata: &[u64],
|
||||||
|
) -> Result<usize>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Call for usize {
|
||||||
|
unsafe fn raw_call(
|
||||||
|
&self,
|
||||||
|
payload_ptr: *const u8,
|
||||||
|
len: usize,
|
||||||
|
flags: CallFlags,
|
||||||
|
metadata: &[u64],
|
||||||
|
) -> Result<usize> {
|
||||||
|
unsafe {
|
||||||
|
syscall5(
|
||||||
|
SYS_CALL,
|
||||||
|
*self,
|
||||||
|
payload_ptr as usize,
|
||||||
|
len,
|
||||||
|
metadata.len() | flags.bits(),
|
||||||
|
metadata.as_ptr() as usize,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Call for &[usize] {
|
||||||
|
unsafe fn raw_call(
|
||||||
|
&self,
|
||||||
|
payload_ptr: *const u8,
|
||||||
|
len: usize,
|
||||||
|
flags: CallFlags,
|
||||||
|
metadata: &[u64],
|
||||||
|
) -> Result<usize> {
|
||||||
|
let combined_flags = flags | CallFlags::MULTIPLE_FDS;
|
||||||
|
unsafe {
|
||||||
|
syscall6(
|
||||||
|
SYS_CALL,
|
||||||
|
self.as_ptr() as usize,
|
||||||
|
payload_ptr as usize,
|
||||||
|
len,
|
||||||
|
metadata.len() | combined_flags.bits(),
|
||||||
|
metadata.as_ptr() as usize,
|
||||||
|
self.len() * mem::size_of::<usize>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// SYS_CALL interface, read-only variant
|
||||||
|
pub fn call_ro<T: Call>(
|
||||||
|
fd: T,
|
||||||
|
payload: &mut [u8],
|
||||||
|
flags: CallFlags,
|
||||||
|
metadata: &[u64],
|
||||||
|
) -> Result<usize> {
|
||||||
|
unsafe {
|
||||||
|
fd.raw_call(
|
||||||
|
payload.as_mut_ptr(),
|
||||||
|
payload.len(),
|
||||||
|
flags | CallFlags::READ,
|
||||||
|
metadata,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// SYS_CALL interface, write-only variant
|
||||||
|
pub fn call_wo<T: Call>(
|
||||||
|
fd: T,
|
||||||
|
payload: &[u8],
|
||||||
|
flags: CallFlags,
|
||||||
|
metadata: &[u64],
|
||||||
|
) -> Result<usize> {
|
||||||
|
unsafe {
|
||||||
|
fd.raw_call(
|
||||||
|
payload.as_ptr(),
|
||||||
|
payload.len(),
|
||||||
|
flags | CallFlags::WRITE,
|
||||||
|
metadata,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// SYS_CALL interface, read-write variant
|
||||||
|
pub fn call_rw<T: Call>(
|
||||||
|
fd: T,
|
||||||
|
payload: &mut [u8],
|
||||||
|
flags: CallFlags,
|
||||||
|
metadata: &[u64],
|
||||||
|
) -> Result<usize> {
|
||||||
|
unsafe {
|
||||||
|
fd.raw_call(
|
||||||
|
payload.as_mut_ptr(),
|
||||||
|
payload.len(),
|
||||||
|
flags | CallFlags::READ | CallFlags::WRITE,
|
||||||
|
metadata,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn std_fs_call<T: Call>(fd: T, payload: &mut [u8], metadata: &StdFsCallMeta) -> Result<usize> {
|
||||||
|
call_rw(fd, payload, CallFlags::STD_FS, metadata)
|
||||||
|
}
|
||||||
+501
@@ -0,0 +1,501 @@
|
|||||||
|
use core::{
|
||||||
|
mem,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
slice,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::flag::{EventFlags, MapFlags, PtraceFlags, StdFsCallKind};
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Event {
|
||||||
|
pub id: usize,
|
||||||
|
pub flags: EventFlags,
|
||||||
|
pub data: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Event {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe { slice::from_raw_parts(self as *const Event as *const u8, mem::size_of::<Event>()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for Event {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe { slice::from_raw_parts_mut(self as *mut Event as *mut u8, mem::size_of::<Event>()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ITimerSpec {
|
||||||
|
pub it_interval: TimeSpec,
|
||||||
|
pub it_value: TimeSpec,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for ITimerSpec {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const ITimerSpec as *const u8,
|
||||||
|
mem::size_of::<ITimerSpec>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for ITimerSpec {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut ITimerSpec as *mut u8,
|
||||||
|
mem::size_of::<ITimerSpec>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct OldMap {
|
||||||
|
pub offset: usize,
|
||||||
|
pub size: usize,
|
||||||
|
pub flags: MapFlags,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for OldMap {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(self as *const OldMap as *const u8, mem::size_of::<OldMap>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for OldMap {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(self as *mut OldMap as *mut u8, mem::size_of::<OldMap>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Map {
|
||||||
|
/// The offset inside the file that is being mapped.
|
||||||
|
pub offset: usize,
|
||||||
|
|
||||||
|
/// The size of the memory map.
|
||||||
|
pub size: usize,
|
||||||
|
|
||||||
|
/// Contains both prot and map flags.
|
||||||
|
pub flags: MapFlags,
|
||||||
|
|
||||||
|
/// Functions as a hint to where in the virtual address space of the running process, to place
|
||||||
|
/// the memory map. If [`MapFlags::MAP_FIXED`] is set, then this address must be the address to
|
||||||
|
/// map to.
|
||||||
|
pub address: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Map {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe { slice::from_raw_parts(self as *const Map as *const u8, mem::size_of::<Map>()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for Map {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe { slice::from_raw_parts_mut(self as *mut Map as *mut u8, mem::size_of::<Map>()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Stat {
|
||||||
|
pub st_dev: u64,
|
||||||
|
pub st_ino: u64,
|
||||||
|
pub st_mode: u16,
|
||||||
|
pub st_nlink: u32,
|
||||||
|
pub st_uid: u32,
|
||||||
|
pub st_gid: u32,
|
||||||
|
pub st_size: u64,
|
||||||
|
pub st_blksize: u32,
|
||||||
|
pub st_blocks: u64,
|
||||||
|
pub st_mtime: u64,
|
||||||
|
pub st_mtime_nsec: u32,
|
||||||
|
pub st_atime: u64,
|
||||||
|
pub st_atime_nsec: u32,
|
||||||
|
pub st_ctime: u64,
|
||||||
|
pub st_ctime_nsec: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for Stat {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe { slice::from_raw_parts(self as *const Stat as *const u8, mem::size_of::<Stat>()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for Stat {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe { slice::from_raw_parts_mut(self as *mut Stat as *mut u8, mem::size_of::<Stat>()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct StatVfs {
|
||||||
|
pub f_bsize: u32,
|
||||||
|
pub f_blocks: u64,
|
||||||
|
pub f_bfree: u64,
|
||||||
|
pub f_bavail: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for StatVfs {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const StatVfs as *const u8,
|
||||||
|
mem::size_of::<StatVfs>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for StatVfs {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(self as *mut StatVfs as *mut u8, mem::size_of::<StatVfs>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
pub struct StdFsCallMeta {
|
||||||
|
pub kind: u8, // enum StdFsCallKind
|
||||||
|
_rsvd: [u8; 7],
|
||||||
|
pub arg1: u64,
|
||||||
|
pub arg2: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StdFsCallMeta {
|
||||||
|
pub fn new(kind: StdFsCallKind, arg1: u64, arg2: u64) -> Self {
|
||||||
|
Self {
|
||||||
|
kind: kind as u8,
|
||||||
|
_rsvd: [0; 7],
|
||||||
|
arg1,
|
||||||
|
arg2,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for StdFsCallMeta {
|
||||||
|
type Target = [u64];
|
||||||
|
fn deref(&self) -> &[u64] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const StdFsCallMeta as *const u64,
|
||||||
|
mem::size_of::<StdFsCallMeta>() / mem::size_of::<u64>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for StdFsCallMeta {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u64] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut StdFsCallMeta as *mut u64,
|
||||||
|
mem::size_of::<StdFsCallMeta>() / mem::size_of::<u64>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct TimeSpec {
|
||||||
|
pub tv_sec: i64,
|
||||||
|
pub tv_nsec: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
const NANOS_PER_SEC: u128 = 1_000_000_000;
|
||||||
|
|
||||||
|
impl TimeSpec {
|
||||||
|
pub fn from_nanos(nanos: u128) -> Self {
|
||||||
|
Self {
|
||||||
|
tv_sec: i64::try_from(nanos / NANOS_PER_SEC).unwrap_or(i64::MAX),
|
||||||
|
tv_nsec: (nanos % NANOS_PER_SEC) as i32, // guaranteed to never overflow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn to_nanos(&self) -> u128 {
|
||||||
|
self.tv_sec as u128 * NANOS_PER_SEC + self.tv_nsec as u128
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for TimeSpec {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const TimeSpec as *const u8,
|
||||||
|
mem::size_of::<TimeSpec>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for TimeSpec {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(self as *mut TimeSpec as *mut u8, mem::size_of::<TimeSpec>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct PtraceEvent {
|
||||||
|
pub cause: PtraceFlags,
|
||||||
|
pub a: usize,
|
||||||
|
pub b: usize,
|
||||||
|
pub c: usize,
|
||||||
|
pub d: usize,
|
||||||
|
pub e: usize,
|
||||||
|
pub f: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for PtraceEvent {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const PtraceEvent as *const u8,
|
||||||
|
mem::size_of::<PtraceEvent>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for PtraceEvent {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut PtraceEvent as *mut u8,
|
||||||
|
mem::size_of::<PtraceEvent>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! ptrace_event {
|
||||||
|
($cause:expr $(, $a:expr $(, $b:expr $(, $c:expr)?)?)?) => {
|
||||||
|
$crate::data::PtraceEvent {
|
||||||
|
cause: $cause,
|
||||||
|
$(a: $a,
|
||||||
|
$(b: $b,
|
||||||
|
$(c: $c,)?
|
||||||
|
)?
|
||||||
|
)?
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags::bitflags! {
|
||||||
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy, Default)]
|
||||||
|
pub struct GrantFlags: usize {
|
||||||
|
const GRANT_READ = 0x0000_0001;
|
||||||
|
const GRANT_WRITE = 0x0000_0002;
|
||||||
|
const GRANT_EXEC = 0x0000_0004;
|
||||||
|
|
||||||
|
const GRANT_SHARED = 0x0000_0008;
|
||||||
|
const GRANT_LAZY = 0x0000_0010;
|
||||||
|
const GRANT_SCHEME = 0x0000_0020;
|
||||||
|
const GRANT_PHYS = 0x0000_0040;
|
||||||
|
const GRANT_PINNED = 0x0000_0080;
|
||||||
|
const GRANT_PHYS_CONTIGUOUS = 0x0000_0100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GrantFlags {
|
||||||
|
#[deprecated = "use the safe `from_bits_retain` method instead"]
|
||||||
|
pub unsafe fn from_bits_unchecked(bits: usize) -> Self {
|
||||||
|
Self::from_bits_retain(bits)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct GrantDesc {
|
||||||
|
pub base: usize,
|
||||||
|
pub size: usize,
|
||||||
|
pub flags: GrantFlags,
|
||||||
|
pub offset: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for GrantDesc {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(
|
||||||
|
self as *const GrantDesc as *const u8,
|
||||||
|
mem::size_of::<GrantDesc>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for GrantDesc {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut GrantDesc as *mut u8,
|
||||||
|
mem::size_of::<GrantDesc>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct SetSighandlerData {
|
||||||
|
pub user_handler: usize,
|
||||||
|
pub excp_handler: usize,
|
||||||
|
pub thread_control_addr: usize,
|
||||||
|
pub proc_control_addr: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for SetSighandlerData {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe { slice::from_raw_parts(self as *const Self as *const u8, mem::size_of::<Self>()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for SetSighandlerData {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe { slice::from_raw_parts_mut(self as *mut Self as *mut u8, mem::size_of::<Self>()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub use crate::sigabi::*;
|
||||||
|
|
||||||
|
/// UNSTABLE
|
||||||
|
#[derive(Copy, Clone, Debug, Default, PartialEq)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct ProcSchemeAttrs {
|
||||||
|
pub pid: u32,
|
||||||
|
pub euid: u32,
|
||||||
|
pub egid: u32,
|
||||||
|
pub prio: u32,
|
||||||
|
pub debug_name: [u8; 32],
|
||||||
|
}
|
||||||
|
impl Deref for ProcSchemeAttrs {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe { slice::from_raw_parts(self as *const Self as *const u8, mem::size_of::<Self>()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl DerefMut for ProcSchemeAttrs {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut ProcSchemeAttrs as *mut u8,
|
||||||
|
mem::size_of::<ProcSchemeAttrs>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct CtxtStsBuf {
|
||||||
|
pub status: usize,
|
||||||
|
pub excp: crate::Exception,
|
||||||
|
}
|
||||||
|
impl Deref for CtxtStsBuf {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe { slice::from_raw_parts(self as *const Self as *const u8, mem::size_of::<Self>()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl DerefMut for CtxtStsBuf {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts_mut(
|
||||||
|
self as *mut CtxtStsBuf as *mut u8,
|
||||||
|
mem::size_of::<CtxtStsBuf>(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct NewFdParams {
|
||||||
|
pub offset: u64,
|
||||||
|
pub number: usize,
|
||||||
|
pub flags: usize,
|
||||||
|
pub internal_flags: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub enum GlobalSchemes {
|
||||||
|
Debug = 1,
|
||||||
|
Event = 2,
|
||||||
|
Memory = 3,
|
||||||
|
Pipe = 4,
|
||||||
|
Serio = 5,
|
||||||
|
Irq = 6,
|
||||||
|
Time = 7,
|
||||||
|
Sys = 8,
|
||||||
|
Proc = 9,
|
||||||
|
Acpi = 10,
|
||||||
|
Dtb = 11,
|
||||||
|
}
|
||||||
|
impl GlobalSchemes {
|
||||||
|
pub fn try_from_raw(raw: u8) -> Option<Self> {
|
||||||
|
match raw {
|
||||||
|
1 => Some(Self::Debug),
|
||||||
|
2 => Some(Self::Event),
|
||||||
|
3 => Some(Self::Memory),
|
||||||
|
4 => Some(Self::Pipe),
|
||||||
|
5 => Some(Self::Serio),
|
||||||
|
6 => Some(Self::Irq),
|
||||||
|
7 => Some(Self::Time),
|
||||||
|
8 => Some(Self::Sys),
|
||||||
|
9 => Some(Self::Proc),
|
||||||
|
10 => Some(Self::Acpi),
|
||||||
|
11 => Some(Self::Dtb),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn as_str(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::Debug => "debug",
|
||||||
|
Self::Event => "event",
|
||||||
|
Self::Memory => "memory",
|
||||||
|
Self::Pipe => "pipe",
|
||||||
|
Self::Serio => "serio",
|
||||||
|
Self::Irq => "irq",
|
||||||
|
Self::Time => "time",
|
||||||
|
Self::Sys => "sys",
|
||||||
|
Self::Proc => "kernel.proc",
|
||||||
|
Self::Acpi => "kernel.acpi",
|
||||||
|
Self::Dtb => "kernel.dtb",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
|
pub struct KernelSchemeInfo {
|
||||||
|
pub scheme_id: u8,
|
||||||
|
pub fd: usize,
|
||||||
|
}
|
||||||
+231
@@ -0,0 +1,231 @@
|
|||||||
|
use core::{
|
||||||
|
mem::size_of,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
slice,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
error::{Error, Result, EINVAL},
|
||||||
|
ENAMETOOLONG,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
#[repr(packed)]
|
||||||
|
pub struct DirentHeader {
|
||||||
|
pub inode: u64,
|
||||||
|
/// A filesystem-specific opaque value used to uniquely identify directory entries. This value,
|
||||||
|
/// in the last returned entry from a SYS_GETDENTS invocation, shall be passed to the next
|
||||||
|
/// call.
|
||||||
|
pub next_opaque_id: u64,
|
||||||
|
// This struct intentionally does not include a "next" offset field, unlike Linux, to easily
|
||||||
|
// guarantee the iterator will be reasonably deterministic, even if the scheme is adversarial.
|
||||||
|
pub record_len: u16,
|
||||||
|
/// A `DirentKind`.
|
||||||
|
///
|
||||||
|
/// May not be directly available (Unspecified), and if so needs to be looked using fstat.
|
||||||
|
pub kind: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Deref for DirentHeader {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe { slice::from_raw_parts(self as *const Self as *const u8, size_of::<Self>()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for DirentHeader {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe { slice::from_raw_parts_mut(self as *mut Self as *mut u8, size_of::<Self>()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: Must match relibc/include/bits/dirent.h
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum DirentKind {
|
||||||
|
#[default]
|
||||||
|
Unspecified = 0,
|
||||||
|
|
||||||
|
CharDev = 2,
|
||||||
|
Directory = 4,
|
||||||
|
BlockDev = 6,
|
||||||
|
Regular = 8,
|
||||||
|
Symlink = 10,
|
||||||
|
Socket = 12,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DirentKind {
|
||||||
|
// TODO: derive(FromPrimitive)
|
||||||
|
pub fn try_from_raw(raw: u8) -> Option<Self> {
|
||||||
|
Some(match raw {
|
||||||
|
0 => Self::Unspecified,
|
||||||
|
|
||||||
|
2 => Self::CharDev,
|
||||||
|
4 => Self::Directory,
|
||||||
|
6 => Self::BlockDev,
|
||||||
|
8 => Self::Regular,
|
||||||
|
10 => Self::Symlink,
|
||||||
|
12 => Self::Socket,
|
||||||
|
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DirentIter<'a>(&'a [u8]);
|
||||||
|
|
||||||
|
impl<'a> DirentIter<'a> {
|
||||||
|
pub const fn new(buffer: &'a [u8]) -> Self {
|
||||||
|
Self(buffer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Invalid;
|
||||||
|
|
||||||
|
impl<'a> Iterator for DirentIter<'a> {
|
||||||
|
type Item = Result<(&'a DirentHeader, &'a [u8]), Invalid>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
if self.0.len() < size_of::<DirentHeader>() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let header = unsafe { &*(self.0.as_ptr().cast::<DirentHeader>()) };
|
||||||
|
if self.0.len() < usize::from(header.record_len) {
|
||||||
|
return Some(Err(Invalid));
|
||||||
|
}
|
||||||
|
let (this, remaining) = self.0.split_at(usize::from(header.record_len));
|
||||||
|
self.0 = remaining;
|
||||||
|
|
||||||
|
let name_and_nul = &this[size_of::<DirentHeader>()..];
|
||||||
|
let name = &name_and_nul[..name_and_nul.len() - 1];
|
||||||
|
|
||||||
|
Some(Ok((header, name)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct DirentBuf<B> {
|
||||||
|
buffer: B,
|
||||||
|
|
||||||
|
// Exists in order to allow future extensions to the DirentHeader struct.
|
||||||
|
|
||||||
|
// TODO: Might add an upper bound to protect against cache miss DoS. The kernel currently
|
||||||
|
// forbids any other value than size_of::<DirentHeader>().
|
||||||
|
header_size: u16,
|
||||||
|
|
||||||
|
written: usize,
|
||||||
|
}
|
||||||
|
/// Abstraction between &mut [u8] and the kernel's UserSliceWo.
|
||||||
|
pub trait Buffer<'a>: Sized + 'a {
|
||||||
|
fn empty() -> Self;
|
||||||
|
fn length(&self) -> usize;
|
||||||
|
|
||||||
|
/// Split all of `self` into two disjoint contiguous subbuffers of lengths `index` and `length
|
||||||
|
/// - index` respectively.
|
||||||
|
///
|
||||||
|
/// Returns None if and only if `index > length`.
|
||||||
|
fn split_at(self, index: usize) -> Option<[Self; 2]>;
|
||||||
|
|
||||||
|
/// Copy from `src`, lengths must match exactly.
|
||||||
|
///
|
||||||
|
/// Allowed to overwrite subsequent buffer space, for performance reasons. Can be changed in
|
||||||
|
/// the future if too restrictive.
|
||||||
|
fn copy_from_slice_exact(self, src: &[u8]) -> Result<()>;
|
||||||
|
|
||||||
|
/// Write zeroes to this part of the buffer.
|
||||||
|
///
|
||||||
|
/// Allowed to overwrite subsequent buffer space, for performance reasons. Can be changed in
|
||||||
|
/// the future if too restrictive.
|
||||||
|
fn zero_out(self) -> Result<()>;
|
||||||
|
}
|
||||||
|
impl<'a> Buffer<'a> for &'a mut [u8] {
|
||||||
|
fn empty() -> Self {
|
||||||
|
&mut []
|
||||||
|
}
|
||||||
|
fn length(&self) -> usize {
|
||||||
|
self.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn split_at(self, index: usize) -> Option<[Self; 2]> {
|
||||||
|
self.split_at_mut_checked(index).map(|(a, b)| [a, b])
|
||||||
|
}
|
||||||
|
fn copy_from_slice_exact(self, src: &[u8]) -> Result<()> {
|
||||||
|
self.copy_from_slice(src);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn zero_out(self) -> Result<()> {
|
||||||
|
self.fill(0);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DirEntry<'name> {
|
||||||
|
pub inode: u64,
|
||||||
|
pub next_opaque_id: u64,
|
||||||
|
pub name: &'name str,
|
||||||
|
pub kind: DirentKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, B: Buffer<'a>> DirentBuf<B> {
|
||||||
|
pub fn new(buffer: B, header_size: u16) -> Option<Self> {
|
||||||
|
if usize::from(header_size) < size_of::<DirentHeader>() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(Self {
|
||||||
|
buffer,
|
||||||
|
header_size,
|
||||||
|
written: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn entry(&mut self, entry: DirEntry<'_>) -> Result<()> {
|
||||||
|
let name16 = u16::try_from(entry.name.len()).map_err(|_| Error::new(EINVAL))?;
|
||||||
|
let record_align = align_of::<*const DirentHeader>();
|
||||||
|
let record_len = self
|
||||||
|
.header_size
|
||||||
|
.checked_add(name16)
|
||||||
|
// XXX: NUL byte. Unfortunately this is probably the only performant way to be
|
||||||
|
// compatible with C.
|
||||||
|
.and_then(|l| l.checked_add(1))
|
||||||
|
// Align length so next header is aligned
|
||||||
|
.and_then(|l| l.checked_next_multiple_of(record_align as u16))
|
||||||
|
.ok_or(Error::new(ENAMETOOLONG))?;
|
||||||
|
|
||||||
|
let [this, remaining] = core::mem::replace(&mut self.buffer, B::empty())
|
||||||
|
.split_at(usize::from(record_len))
|
||||||
|
.ok_or(Error::new(EINVAL))?;
|
||||||
|
|
||||||
|
let [this_header_variable, this_name_and_nul] = this
|
||||||
|
.split_at(usize::from(self.header_size))
|
||||||
|
.expect("already know header_size + ... >= header_size");
|
||||||
|
|
||||||
|
let [this_name, this_name_nul] = this_name_and_nul
|
||||||
|
.split_at(usize::from(name16))
|
||||||
|
.expect("already know name.len() <= name.len() + 1");
|
||||||
|
|
||||||
|
// Every write here is currently sequential, allowing the buffer trait to do optimizations
|
||||||
|
// where subbuffer writes are out-of-bounds (but inside the total buffer).
|
||||||
|
|
||||||
|
let [this_header, this_header_extra] = this_header_variable
|
||||||
|
.split_at(size_of::<DirentHeader>())
|
||||||
|
.expect("already checked header_size <= size_of Header");
|
||||||
|
|
||||||
|
this_header.copy_from_slice_exact(&DirentHeader {
|
||||||
|
record_len,
|
||||||
|
next_opaque_id: entry.next_opaque_id,
|
||||||
|
inode: entry.inode,
|
||||||
|
kind: entry.kind as u8,
|
||||||
|
})?;
|
||||||
|
this_header_extra.zero_out()?;
|
||||||
|
this_name.copy_from_slice_exact(entry.name.as_bytes())?;
|
||||||
|
this_name_nul.zero_out()?;
|
||||||
|
|
||||||
|
self.written += usize::from(record_len);
|
||||||
|
self.buffer = remaining;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
pub fn finalize(self) -> usize {
|
||||||
|
self.written
|
||||||
|
}
|
||||||
|
}
|
||||||
-121
@@ -1,121 +0,0 @@
|
|||||||
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...");
|
|
||||||
}
|
|
||||||
+327
@@ -0,0 +1,327 @@
|
|||||||
|
use core::{fmt, result};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||||
|
pub struct Error {
|
||||||
|
pub errno: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Result<T, E = Error> = result::Result<T, E>;
|
||||||
|
|
||||||
|
impl Error {
|
||||||
|
pub fn new(errno: i32) -> Error {
|
||||||
|
Error { errno }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mux(result: Result<usize>) -> usize {
|
||||||
|
match result {
|
||||||
|
Ok(value) => value,
|
||||||
|
Err(error) => -error.errno as usize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn demux(value: usize) -> Result<usize> {
|
||||||
|
let errno = -(value as i32);
|
||||||
|
if errno >= 1 && errno < STR_ERROR.len() as i32 {
|
||||||
|
Err(Error::new(errno))
|
||||||
|
} else {
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn text(&self) -> &'static str {
|
||||||
|
STR_ERROR
|
||||||
|
.get(self.errno as usize)
|
||||||
|
.map(|&x| x)
|
||||||
|
.unwrap_or("Unknown Error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
|
||||||
|
f.write_str(self.text())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Error {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
|
||||||
|
f.write_str(self.text())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl std::error::Error for Error {}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl From<Error> for std::io::Error {
|
||||||
|
fn from(value: Error) -> Self {
|
||||||
|
std::io::Error::from_raw_os_error(value.errno)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const EPERM: i32 = 1; /* Operation not permitted */
|
||||||
|
pub const ENOENT: i32 = 2; /* No such file or directory */
|
||||||
|
pub const ESRCH: i32 = 3; /* No such process */
|
||||||
|
pub const EINTR: i32 = 4; /* Interrupted system call */
|
||||||
|
pub const EIO: i32 = 5; /* I/O error */
|
||||||
|
pub const ENXIO: i32 = 6; /* No such device or address */
|
||||||
|
pub const E2BIG: i32 = 7; /* Argument list too long */
|
||||||
|
pub const ENOEXEC: i32 = 8; /* Exec format error */
|
||||||
|
pub const EBADF: i32 = 9; /* Bad file number */
|
||||||
|
pub const ECHILD: i32 = 10; /* No child processes */
|
||||||
|
pub const EAGAIN: i32 = 11; /* Try again */
|
||||||
|
pub const ENOMEM: i32 = 12; /* Out of memory */
|
||||||
|
pub const EACCES: i32 = 13; /* Permission denied */
|
||||||
|
pub const EFAULT: i32 = 14; /* Bad address */
|
||||||
|
pub const ENOTBLK: i32 = 15; /* Block device required */
|
||||||
|
pub const EBUSY: i32 = 16; /* Device or resource busy */
|
||||||
|
pub const EEXIST: i32 = 17; /* File exists */
|
||||||
|
pub const EXDEV: i32 = 18; /* Cross-device link */
|
||||||
|
pub const ENODEV: i32 = 19; /* No such device */
|
||||||
|
pub const ENOTDIR: i32 = 20; /* Not a directory */
|
||||||
|
pub const EISDIR: i32 = 21; /* Is a directory */
|
||||||
|
pub const EINVAL: i32 = 22; /* Invalid argument */
|
||||||
|
pub const ENFILE: i32 = 23; /* File table overflow */
|
||||||
|
pub const EMFILE: i32 = 24; /* Too many open files */
|
||||||
|
pub const ENOTTY: i32 = 25; /* Not a typewriter */
|
||||||
|
pub const ETXTBSY: i32 = 26; /* Text file busy */
|
||||||
|
pub const EFBIG: i32 = 27; /* File too large */
|
||||||
|
pub const ENOSPC: i32 = 28; /* No space left on device */
|
||||||
|
pub const ESPIPE: i32 = 29; /* Illegal seek */
|
||||||
|
pub const EROFS: i32 = 30; /* Read-only file system */
|
||||||
|
pub const EMLINK: i32 = 31; /* Too many links */
|
||||||
|
pub const EPIPE: i32 = 32; /* Broken pipe */
|
||||||
|
pub const EDOM: i32 = 33; /* Math argument out of domain of func */
|
||||||
|
pub const ERANGE: i32 = 34; /* Math result not representable */
|
||||||
|
pub const EDEADLK: i32 = 35; /* Resource deadlock would occur */
|
||||||
|
pub const ENAMETOOLONG: i32 = 36; /* File name too long */
|
||||||
|
pub const ENOLCK: i32 = 37; /* No record locks available */
|
||||||
|
pub const ENOSYS: i32 = 38; /* Function not implemented */
|
||||||
|
pub const ENOTEMPTY: i32 = 39; /* Directory not empty */
|
||||||
|
pub const ELOOP: i32 = 40; /* Too many symbolic links encountered */
|
||||||
|
pub const EWOULDBLOCK: i32 = 41; /* Operation would block */
|
||||||
|
pub const ENOMSG: i32 = 42; /* No message of desired type */
|
||||||
|
pub const EIDRM: i32 = 43; /* Identifier removed */
|
||||||
|
pub const ECHRNG: i32 = 44; /* Channel number out of range */
|
||||||
|
pub const EL2NSYNC: i32 = 45; /* Level 2 not synchronized */
|
||||||
|
pub const EL3HLT: i32 = 46; /* Level 3 halted */
|
||||||
|
pub const EL3RST: i32 = 47; /* Level 3 reset */
|
||||||
|
pub const ELNRNG: i32 = 48; /* Link number out of range */
|
||||||
|
pub const EUNATCH: i32 = 49; /* Protocol driver not attached */
|
||||||
|
pub const ENOCSI: i32 = 50; /* No CSI structure available */
|
||||||
|
pub const EL2HLT: i32 = 51; /* Level 2 halted */
|
||||||
|
pub const EBADE: i32 = 52; /* Invalid exchange */
|
||||||
|
pub const EBADR: i32 = 53; /* Invalid request descriptor */
|
||||||
|
pub const EXFULL: i32 = 54; /* Exchange full */
|
||||||
|
pub const ENOANO: i32 = 55; /* No anode */
|
||||||
|
pub const EBADRQC: i32 = 56; /* Invalid request code */
|
||||||
|
pub const EBADSLT: i32 = 57; /* Invalid slot */
|
||||||
|
pub const EDEADLOCK: i32 = 58; /* Resource deadlock would occur */
|
||||||
|
pub const EBFONT: i32 = 59; /* Bad font file format */
|
||||||
|
pub const ENOSTR: i32 = 60; /* Device not a stream */
|
||||||
|
pub const ENODATA: i32 = 61; /* No data available */
|
||||||
|
pub const ETIME: i32 = 62; /* Timer expired */
|
||||||
|
pub const ENOSR: i32 = 63; /* Out of streams resources */
|
||||||
|
pub const ENONET: i32 = 64; /* Machine is not on the network */
|
||||||
|
pub const ENOPKG: i32 = 65; /* Package not installed */
|
||||||
|
pub const EREMOTE: i32 = 66; /* Object is remote */
|
||||||
|
pub const ENOLINK: i32 = 67; /* Link has been severed */
|
||||||
|
pub const EADV: i32 = 68; /* Advertise error */
|
||||||
|
pub const ESRMNT: i32 = 69; /* Srmount error */
|
||||||
|
pub const ECOMM: i32 = 70; /* Communication error on send */
|
||||||
|
pub const EPROTO: i32 = 71; /* Protocol error */
|
||||||
|
pub const EMULTIHOP: i32 = 72; /* Multihop attempted */
|
||||||
|
pub const EDOTDOT: i32 = 73; /* RFS specific error */
|
||||||
|
pub const EBADMSG: i32 = 74; /* Not a data message */
|
||||||
|
pub const EOVERFLOW: i32 = 75; /* Value too large for defined data type */
|
||||||
|
pub const ENOTUNIQ: i32 = 76; /* Name not unique on network */
|
||||||
|
pub const EBADFD: i32 = 77; /* File descriptor in bad state */
|
||||||
|
pub const EREMCHG: i32 = 78; /* Remote address changed */
|
||||||
|
pub const ELIBACC: i32 = 79; /* Can not access a needed shared library */
|
||||||
|
pub const ELIBBAD: i32 = 80; /* Accessing a corrupted shared library */
|
||||||
|
pub const ELIBSCN: i32 = 81; /* .lib section in a.out corrupted */
|
||||||
|
pub const ELIBMAX: i32 = 82; /* Attempting to link in too many shared libraries */
|
||||||
|
pub const ELIBEXEC: i32 = 83; /* Cannot exec a shared library directly */
|
||||||
|
pub const EILSEQ: i32 = 84; /* Illegal byte sequence */
|
||||||
|
pub const ERESTART: i32 = 85; /* Interrupted system call should be restarted */
|
||||||
|
pub const ESTRPIPE: i32 = 86; /* Streams pipe error */
|
||||||
|
pub const EUSERS: i32 = 87; /* Too many users */
|
||||||
|
pub const ENOTSOCK: i32 = 88; /* Socket operation on non-socket */
|
||||||
|
pub const EDESTADDRREQ: i32 = 89; /* Destination address required */
|
||||||
|
pub const EMSGSIZE: i32 = 90; /* Message too long */
|
||||||
|
pub const EPROTOTYPE: i32 = 91; /* Protocol wrong type for socket */
|
||||||
|
pub const ENOPROTOOPT: i32 = 92; /* Protocol not available */
|
||||||
|
pub const EPROTONOSUPPORT: i32 = 93; /* Protocol not supported */
|
||||||
|
pub const ESOCKTNOSUPPORT: i32 = 94; /* Socket type not supported */
|
||||||
|
pub const EOPNOTSUPP: i32 = 95; /* Operation not supported on transport endpoint */
|
||||||
|
pub const EPFNOSUPPORT: i32 = 96; /* Protocol family not supported */
|
||||||
|
pub const EAFNOSUPPORT: i32 = 97; /* Address family not supported by protocol */
|
||||||
|
pub const EADDRINUSE: i32 = 98; /* Address already in use */
|
||||||
|
pub const EADDRNOTAVAIL: i32 = 99; /* Cannot assign requested address */
|
||||||
|
pub const ENETDOWN: i32 = 100; /* Network is down */
|
||||||
|
pub const ENETUNREACH: i32 = 101; /* Network is unreachable */
|
||||||
|
pub const ENETRESET: i32 = 102; /* Network dropped connection because of reset */
|
||||||
|
pub const ECONNABORTED: i32 = 103; /* Software caused connection abort */
|
||||||
|
pub const ECONNRESET: i32 = 104; /* Connection reset by peer */
|
||||||
|
pub const ENOBUFS: i32 = 105; /* No buffer space available */
|
||||||
|
pub const EISCONN: i32 = 106; /* Transport endpoint is already connected */
|
||||||
|
pub const ENOTCONN: i32 = 107; /* Transport endpoint is not connected */
|
||||||
|
pub const ESHUTDOWN: i32 = 108; /* Cannot send after transport endpoint shutdown */
|
||||||
|
pub const ETOOMANYREFS: i32 = 109; /* Too many references: cannot splice */
|
||||||
|
pub const ETIMEDOUT: i32 = 110; /* Connection timed out */
|
||||||
|
pub const ECONNREFUSED: i32 = 111; /* Connection refused */
|
||||||
|
pub const EHOSTDOWN: i32 = 112; /* Host is down */
|
||||||
|
pub const EHOSTUNREACH: i32 = 113; /* No route to host */
|
||||||
|
pub const EALREADY: i32 = 114; /* Operation already in progress */
|
||||||
|
pub const EINPROGRESS: i32 = 115; /* Operation now in progress */
|
||||||
|
pub const ESTALE: i32 = 116; /* Stale NFS file handle */
|
||||||
|
pub const EUCLEAN: i32 = 117; /* Structure needs cleaning */
|
||||||
|
pub const ENOTNAM: i32 = 118; /* Not a XENIX named type file */
|
||||||
|
pub const ENAVAIL: i32 = 119; /* No XENIX semaphores available */
|
||||||
|
pub const EISNAM: i32 = 120; /* Is a named type file */
|
||||||
|
pub const EREMOTEIO: i32 = 121; /* Remote I/O error */
|
||||||
|
pub const EDQUOT: i32 = 122; /* Quota exceeded */
|
||||||
|
pub const ENOMEDIUM: i32 = 123; /* No medium found */
|
||||||
|
pub const EMEDIUMTYPE: i32 = 124; /* Wrong medium type */
|
||||||
|
pub const ECANCELED: i32 = 125; /* Operation Canceled */
|
||||||
|
pub const ENOKEY: i32 = 126; /* Required key not available */
|
||||||
|
pub const EKEYEXPIRED: i32 = 127; /* Key has expired */
|
||||||
|
pub const EKEYREVOKED: i32 = 128; /* Key has been revoked */
|
||||||
|
pub const EKEYREJECTED: i32 = 129; /* Key was rejected by service */
|
||||||
|
pub const EOWNERDEAD: i32 = 130; /* Owner died */
|
||||||
|
pub const ENOTRECOVERABLE: i32 = 131; /* State not recoverable */
|
||||||
|
pub const ERSVD: i32 = 132; /* Reserved (formerly "scheme-kernel message code") */
|
||||||
|
|
||||||
|
pub static STR_ERROR: [&'static str; 133] = [
|
||||||
|
"Success",
|
||||||
|
"Operation not permitted",
|
||||||
|
"No such file or directory",
|
||||||
|
"No such process",
|
||||||
|
"Interrupted system call",
|
||||||
|
"I/O error",
|
||||||
|
"No such device or address",
|
||||||
|
"Argument list too long",
|
||||||
|
"Exec format error",
|
||||||
|
"Bad file number",
|
||||||
|
"No child processes",
|
||||||
|
"Try again",
|
||||||
|
"Out of memory",
|
||||||
|
"Permission denied",
|
||||||
|
"Bad address",
|
||||||
|
"Block device required",
|
||||||
|
"Device or resource busy",
|
||||||
|
"File exists",
|
||||||
|
"Cross-device link",
|
||||||
|
"No such device",
|
||||||
|
"Not a directory",
|
||||||
|
"Is a directory",
|
||||||
|
"Invalid argument",
|
||||||
|
"File table overflow",
|
||||||
|
"Too many open files",
|
||||||
|
"Not a typewriter",
|
||||||
|
"Text file busy",
|
||||||
|
"File too large",
|
||||||
|
"No space left on device",
|
||||||
|
"Illegal seek",
|
||||||
|
"Read-only file system",
|
||||||
|
"Too many links",
|
||||||
|
"Broken pipe",
|
||||||
|
"Math argument out of domain of func",
|
||||||
|
"Math result not representable",
|
||||||
|
"Resource deadlock would occur",
|
||||||
|
"File name too long",
|
||||||
|
"No record locks available",
|
||||||
|
"Function not implemented",
|
||||||
|
"Directory not empty",
|
||||||
|
"Too many symbolic links encountered",
|
||||||
|
"Operation would block",
|
||||||
|
"No message of desired type",
|
||||||
|
"Identifier removed",
|
||||||
|
"Channel number out of range",
|
||||||
|
"Level 2 not synchronized",
|
||||||
|
"Level 3 halted",
|
||||||
|
"Level 3 reset",
|
||||||
|
"Link number out of range",
|
||||||
|
"Protocol driver not attached",
|
||||||
|
"No CSI structure available",
|
||||||
|
"Level 2 halted",
|
||||||
|
"Invalid exchange",
|
||||||
|
"Invalid request descriptor",
|
||||||
|
"Exchange full",
|
||||||
|
"No anode",
|
||||||
|
"Invalid request code",
|
||||||
|
"Invalid slot",
|
||||||
|
"Resource deadlock would occur",
|
||||||
|
"Bad font file format",
|
||||||
|
"Device not a stream",
|
||||||
|
"No data available",
|
||||||
|
"Timer expired",
|
||||||
|
"Out of streams resources",
|
||||||
|
"Machine is not on the network",
|
||||||
|
"Package not installed",
|
||||||
|
"Object is remote",
|
||||||
|
"Link has been severed",
|
||||||
|
"Advertise error",
|
||||||
|
"Srmount error",
|
||||||
|
"Communication error on send",
|
||||||
|
"Protocol error",
|
||||||
|
"Multihop attempted",
|
||||||
|
"RFS specific error",
|
||||||
|
"Not a data message",
|
||||||
|
"Value too large for defined data type",
|
||||||
|
"Name not unique on network",
|
||||||
|
"File descriptor in bad state",
|
||||||
|
"Remote address changed",
|
||||||
|
"Can not access a needed shared library",
|
||||||
|
"Accessing a corrupted shared library",
|
||||||
|
".lib section in a.out corrupted",
|
||||||
|
"Attempting to link in too many shared libraries",
|
||||||
|
"Cannot exec a shared library directly",
|
||||||
|
"Illegal byte sequence",
|
||||||
|
"Interrupted system call should be restarted",
|
||||||
|
"Streams pipe error",
|
||||||
|
"Too many users",
|
||||||
|
"Socket operation on non-socket",
|
||||||
|
"Destination address required",
|
||||||
|
"Message too long",
|
||||||
|
"Protocol wrong type for socket",
|
||||||
|
"Protocol not available",
|
||||||
|
"Protocol not supported",
|
||||||
|
"Socket type not supported",
|
||||||
|
"Operation not supported on transport endpoint",
|
||||||
|
"Protocol family not supported",
|
||||||
|
"Address family not supported by protocol",
|
||||||
|
"Address already in use",
|
||||||
|
"Cannot assign requested address",
|
||||||
|
"Network is down",
|
||||||
|
"Network is unreachable",
|
||||||
|
"Network dropped connection because of reset",
|
||||||
|
"Software caused connection abort",
|
||||||
|
"Connection reset by peer",
|
||||||
|
"No buffer space available",
|
||||||
|
"Transport endpoint is already connected",
|
||||||
|
"Transport endpoint is not connected",
|
||||||
|
"Cannot send after transport endpoint shutdown",
|
||||||
|
"Too many references: cannot splice",
|
||||||
|
"Connection timed out",
|
||||||
|
"Connection refused",
|
||||||
|
"Host is down",
|
||||||
|
"No route to host",
|
||||||
|
"Operation already in progress",
|
||||||
|
"Operation now in progress",
|
||||||
|
"Stale NFS file handle",
|
||||||
|
"Structure needs cleaning",
|
||||||
|
"Not a XENIX named type file",
|
||||||
|
"No XENIX semaphores available",
|
||||||
|
"Is a named type file",
|
||||||
|
"Remote I/O error",
|
||||||
|
"Quota exceeded",
|
||||||
|
"No medium found",
|
||||||
|
"Wrong medium type",
|
||||||
|
"Operation Canceled",
|
||||||
|
"Required key not available",
|
||||||
|
"Key has expired",
|
||||||
|
"Key has been revoked",
|
||||||
|
"Key was rejected by service",
|
||||||
|
"Owner died",
|
||||||
|
"State not recoverable",
|
||||||
|
"Reserved (formerly scheme-kernel message code)",
|
||||||
|
];
|
||||||
+567
@@ -0,0 +1,567 @@
|
|||||||
|
use bitflags::bitflags as inner_bitflags;
|
||||||
|
use core::{mem, ops::Deref, slice};
|
||||||
|
|
||||||
|
macro_rules! bitflags {
|
||||||
|
(
|
||||||
|
$(#[$outer:meta])*
|
||||||
|
pub struct $BitFlags:ident: $T:ty {
|
||||||
|
$(
|
||||||
|
$(#[$inner:ident $($args:tt)*])*
|
||||||
|
const $Flag:ident = $value:expr;
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
// First, use the inner bitflags
|
||||||
|
inner_bitflags! {
|
||||||
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy, Default)]
|
||||||
|
$(#[$outer])*
|
||||||
|
pub struct $BitFlags: $T {
|
||||||
|
$(
|
||||||
|
$(#[$inner $($args)*])*
|
||||||
|
const $Flag = $value;
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl $BitFlags {
|
||||||
|
#[deprecated = "use the safe `from_bits_retain` method instead"]
|
||||||
|
pub unsafe fn from_bits_unchecked(bits: $T) -> Self {
|
||||||
|
Self::from_bits_retain(bits)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Secondly, re-export all inner constants
|
||||||
|
// (`pub use self::Struct::*` doesn't work)
|
||||||
|
$(
|
||||||
|
$(#[$inner $($args)*])*
|
||||||
|
pub const $Flag: $BitFlags = $BitFlags::$Flag;
|
||||||
|
)+
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const CLOCK_REALTIME: usize = 1;
|
||||||
|
pub const CLOCK_MONOTONIC: usize = 4;
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct EventFlags: usize {
|
||||||
|
const EVENT_NONE = 0;
|
||||||
|
const EVENT_READ = 1;
|
||||||
|
const EVENT_WRITE = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const F_DUPFD: usize = 0;
|
||||||
|
pub const F_GETFD: usize = 1;
|
||||||
|
pub const F_SETFD: usize = 2;
|
||||||
|
pub const F_GETFL: usize = 3;
|
||||||
|
pub const F_SETFL: usize = 4;
|
||||||
|
pub const F_DUPFD_CLOEXEC: usize = 1030;
|
||||||
|
|
||||||
|
pub const FUTEX_WAIT: usize = 0;
|
||||||
|
pub const FUTEX_WAKE: usize = 1;
|
||||||
|
pub const FUTEX_REQUEUE: usize = 2;
|
||||||
|
pub const FUTEX_WAIT64: usize = 3;
|
||||||
|
|
||||||
|
// TODO: Split SendFdFlags into caller flags and flags that the scheme receives?
|
||||||
|
bitflags::bitflags! {
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct SendFdFlags: usize {
|
||||||
|
/// If set, the kernel will enforce that the file descriptors are exclusively owned.
|
||||||
|
///
|
||||||
|
/// That is, there will no longer exist any other reference to those FDs when removed from
|
||||||
|
/// the file table (sendfd always removes the FDs from the file table, but without this
|
||||||
|
/// flag, it can be retained by SYS_DUPing them first).
|
||||||
|
const EXCLUSIVE = 1;
|
||||||
|
|
||||||
|
/// If set, the file descriptors will be cloned and *not* removed from the sender's file table.
|
||||||
|
/// By default, `SYS_SENDFD` moves the file descriptors, removing them from the sender.
|
||||||
|
const CLONE = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bitflags::bitflags! {
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct FobtainFdFlags: usize {
|
||||||
|
/// If set, the SYS_CALL payload specifies the destination file descriptor slots, otherwise the lowest
|
||||||
|
/// available slots will be selected, and placed in the usize pointed to by SYS_CALL
|
||||||
|
/// payload.
|
||||||
|
const MANUAL_FD = 1;
|
||||||
|
|
||||||
|
/// If set, the file descriptors received are guaranteed to be exclusively owned (by the file
|
||||||
|
/// table the obtainer is running in).
|
||||||
|
const EXCLUSIVE = 2;
|
||||||
|
|
||||||
|
/// If set, the file descriptors received will be placed into the *upper* file table.
|
||||||
|
const UPPER_TBL = 4;
|
||||||
|
|
||||||
|
/// If set, the received file descriptors are marked as close-on-exec.
|
||||||
|
const CLOEXEC = 8;
|
||||||
|
|
||||||
|
// No, cloexec won't be stored in the kernel in the future, when the stable ABI is moved to
|
||||||
|
// relibc, so no flag for that!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bitflags::bitflags! {
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct RecvFdFlags: usize {
|
||||||
|
/// If set, the SYS_CALL payload specifies the destination file descriptor slots, otherwise the lowest
|
||||||
|
/// available slots will be selected, and placed in the usize pointed to by SYS_CALL
|
||||||
|
/// payload.
|
||||||
|
const MANUAL_FD = 1;
|
||||||
|
|
||||||
|
/// If set, the file descriptors received will be placed into the *upper* file table.
|
||||||
|
const UPPER_TBL = 2;
|
||||||
|
|
||||||
|
/// If set, the received file descriptors are marked as close-on-exec.
|
||||||
|
const CLOEXEC = 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bitflags::bitflags! {
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct FmoveFdFlags: usize {
|
||||||
|
/// If set, the kernel will enforce that the file descriptors are exclusively owned.
|
||||||
|
///
|
||||||
|
/// That is, there will no longer exist any other reference to those FDs when removed from
|
||||||
|
/// the file table (SYS_CALL always removes the FDs from the file table, but without this
|
||||||
|
/// flag, it can be retained by SYS_DUPing them first).
|
||||||
|
const EXCLUSIVE = 1;
|
||||||
|
|
||||||
|
/// If set, the file descriptors will be cloned and *not* removed from the sender's file table.
|
||||||
|
/// By default, sendfd moves the file descriptors, removing them from the sender.
|
||||||
|
const CLONE = 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct MapFlags: usize {
|
||||||
|
// TODO: Downgrade PROT_NONE to global constant? (bitflags specifically states zero flags
|
||||||
|
// can cause buggy behavior).
|
||||||
|
const PROT_NONE = 0x0000_0000;
|
||||||
|
|
||||||
|
const PROT_EXEC = 0x0001_0000;
|
||||||
|
const PROT_WRITE = 0x0002_0000;
|
||||||
|
const PROT_READ = 0x0004_0000;
|
||||||
|
|
||||||
|
const MAP_SHARED = 0x0001;
|
||||||
|
const MAP_PRIVATE = 0x0002;
|
||||||
|
|
||||||
|
const MAP_FIXED = 0x0004;
|
||||||
|
const MAP_FIXED_NOREPLACE = 0x000C;
|
||||||
|
|
||||||
|
/// For *userspace-backed mmaps*, return from the mmap call before all pages have been
|
||||||
|
/// provided by the scheme. This requires the scheme to be trusted, as the current context
|
||||||
|
/// can block indefinitely, if the scheme does not respond to the page fault handler's
|
||||||
|
/// request, as it tries to map the page by requesting it from the scheme.
|
||||||
|
///
|
||||||
|
/// In some cases however, such as the program loader, the data needs to be trusted as much
|
||||||
|
/// with or without MAP_LAZY, and if so, mapping lazily will not cause insecureness by
|
||||||
|
/// itself.
|
||||||
|
///
|
||||||
|
/// For kernel-backed mmaps, this flag has no effect at all. It is unspecified whether
|
||||||
|
/// kernel mmaps are lazy or not.
|
||||||
|
const MAP_LAZY = 0x0010;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bitflags! {
|
||||||
|
pub struct MunmapFlags: usize {
|
||||||
|
/// Indicates whether the funmap call must implicitly do an msync, for the changes to
|
||||||
|
/// become visible later.
|
||||||
|
///
|
||||||
|
/// This flag will currently be set if and only if MAP_SHARED | PROT_WRITE are set.
|
||||||
|
const NEEDS_SYNC = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const MODE_TYPE: u16 = 0xF000;
|
||||||
|
pub const MODE_DIR: u16 = 0x4000;
|
||||||
|
pub const MODE_FILE: u16 = 0x8000;
|
||||||
|
pub const MODE_SYMLINK: u16 = 0xA000;
|
||||||
|
pub const MODE_FIFO: u16 = 0x1000;
|
||||||
|
pub const MODE_CHR: u16 = 0x2000;
|
||||||
|
pub const MODE_SOCK: u16 = 0xC000;
|
||||||
|
|
||||||
|
pub const MODE_PERM: u16 = 0x0FFF;
|
||||||
|
pub const MODE_SETUID: u16 = 0o4000;
|
||||||
|
pub const MODE_SETGID: u16 = 0o2000;
|
||||||
|
|
||||||
|
pub const O_RDONLY: usize = 0x0001_0000;
|
||||||
|
pub const O_WRONLY: usize = 0x0002_0000;
|
||||||
|
pub const O_RDWR: usize = 0x0003_0000;
|
||||||
|
pub const O_NONBLOCK: usize = 0x0004_0000;
|
||||||
|
pub const O_APPEND: usize = 0x0008_0000;
|
||||||
|
pub const O_SHLOCK: usize = 0x0010_0000;
|
||||||
|
pub const O_EXLOCK: usize = 0x0020_0000;
|
||||||
|
pub const O_ASYNC: usize = 0x0040_0000;
|
||||||
|
pub const O_FSYNC: usize = 0x0080_0000;
|
||||||
|
pub const O_CLOEXEC: usize = 0x0100_0000;
|
||||||
|
pub const O_CREAT: usize = 0x0200_0000;
|
||||||
|
pub const O_TRUNC: usize = 0x0400_0000;
|
||||||
|
pub const O_EXCL: usize = 0x0800_0000;
|
||||||
|
pub const O_DIRECTORY: usize = 0x1000_0000;
|
||||||
|
pub const O_STAT: usize = 0x2000_0000;
|
||||||
|
pub const O_SYMLINK: usize = 0x4000_0000;
|
||||||
|
pub const O_NOFOLLOW: usize = 0x8000_0000;
|
||||||
|
pub const O_ACCMODE: usize = O_RDONLY | O_WRONLY | O_RDWR;
|
||||||
|
pub const O_FCNTL_MASK: usize = O_NONBLOCK | O_APPEND | O_ASYNC | O_FSYNC;
|
||||||
|
|
||||||
|
/// Remove directory instead of unlinking file.
|
||||||
|
pub const AT_REMOVEDIR: usize = 0x200;
|
||||||
|
|
||||||
|
// The top 48 bits of PTRACE_* are reserved, for now
|
||||||
|
|
||||||
|
// NOT ABI STABLE!
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||||
|
#[repr(usize)]
|
||||||
|
pub enum ContextStatus {
|
||||||
|
Runnable,
|
||||||
|
Blocked,
|
||||||
|
NotYetStarted,
|
||||||
|
Dead,
|
||||||
|
ForceKilled,
|
||||||
|
Stopped,
|
||||||
|
UnhandledExcp,
|
||||||
|
#[default]
|
||||||
|
Other, // reserved
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
#[repr(usize)]
|
||||||
|
pub enum ContextVerb {
|
||||||
|
Stop = 1,
|
||||||
|
Unstop = 2,
|
||||||
|
Interrupt = 3,
|
||||||
|
ForceKill = usize::MAX,
|
||||||
|
}
|
||||||
|
impl ContextVerb {
|
||||||
|
pub fn try_from_raw(raw: usize) -> Option<Self> {
|
||||||
|
Some(match raw {
|
||||||
|
1 => Self::Stop,
|
||||||
|
2 => Self::Unstop,
|
||||||
|
3 => Self::Interrupt,
|
||||||
|
usize::MAX => Self::ForceKill,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOT ABI STABLE!
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum AddrSpaceVerb {
|
||||||
|
MmapMin = 255,
|
||||||
|
}
|
||||||
|
impl AddrSpaceVerb {
|
||||||
|
pub fn try_from_raw(verb: u8) -> Option<Self> {
|
||||||
|
Some(match verb {
|
||||||
|
255 => Self::MmapMin,
|
||||||
|
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOT ABI STABLE!
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
#[repr(u8)]
|
||||||
|
pub enum ProcSchemeVerb {
|
||||||
|
RegsInt = 250,
|
||||||
|
RegsFloat = 251,
|
||||||
|
RegsEnv = 252,
|
||||||
|
SchedAffinity = 253,
|
||||||
|
Start = 254,
|
||||||
|
Iopl = 255,
|
||||||
|
}
|
||||||
|
impl ProcSchemeVerb {
|
||||||
|
pub fn try_from_raw(verb: u8) -> Option<Self> {
|
||||||
|
Some(match verb {
|
||||||
|
250 => Self::RegsInt,
|
||||||
|
251 => Self::RegsFloat,
|
||||||
|
252 => Self::RegsEnv,
|
||||||
|
253 => Self::SchedAffinity,
|
||||||
|
254 => Self::Start,
|
||||||
|
255 => Self::Iopl,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum FileTableVerb {
|
||||||
|
Close = 1,
|
||||||
|
Dup2 = 2,
|
||||||
|
CloseCloExec = 3,
|
||||||
|
}
|
||||||
|
impl FileTableVerb {
|
||||||
|
pub fn try_from_raw(value: u8) -> Option<Self> {
|
||||||
|
Some(match value {
|
||||||
|
1 => Self::Close,
|
||||||
|
2 => Self::Dup2,
|
||||||
|
3 => Self::CloseCloExec,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOT ABI-STABLE!
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
#[repr(u64)]
|
||||||
|
pub enum AcpiVerb {
|
||||||
|
// copies the rsdt/xsdt to the payload buffer (the number of bytes that fit), and returns the
|
||||||
|
// rsdt/xsdt length regardless
|
||||||
|
ReadRxsdt = 1,
|
||||||
|
// no payload, just returns 0 or 1
|
||||||
|
CheckShutdown = 2,
|
||||||
|
/// Red Bear OS extension (Phase I): acpid requests the kernel
|
||||||
|
/// enter s2idle (Modern Standby / S0ix). The kernel sets
|
||||||
|
/// `S2IDLE_REQUESTED`; the idle path calls `mwait_loop()`. Read
|
||||||
|
/// payload (1 byte) returns the *previous* value of the flag.
|
||||||
|
/// Write payload is opaque (ignored by current kernel).
|
||||||
|
/// Mirrors Linux 7.1 `s2idle_enter()` in
|
||||||
|
/// `kernel/power/suspend.c:91`. Hardware-agnostic — works on
|
||||||
|
/// any platform with Modern Standby firmware (Dell, HP, Lenovo,
|
||||||
|
/// LG Gram, etc.), not just LG Gram.
|
||||||
|
EnterS2Idle = 3,
|
||||||
|
/// Red Bear OS extension (Phase I): acpid signals s2idle
|
||||||
|
/// exit. Kernel clears `S2IDLE_REQUESTED`. Read payload (1
|
||||||
|
/// byte) always returns 0. Mirrors Linux 7.1 `s2idle_wake()` in
|
||||||
|
/// `kernel/power/suspend.c:133`. Hardware-agnostic.
|
||||||
|
ExitS2Idle = 4,
|
||||||
|
/// Red Bear OS extension (Phase II.X.W): acpid writes the
|
||||||
|
/// 64-bit kernel S3 resume trampoline address to
|
||||||
|
/// FACS.xfirmware_waking_vector. The kernel's
|
||||||
|
/// `arch/x86_shared/s3_resume.rs` trampoline is at this
|
||||||
|
/// address; the platform firmware jumps to it on S3 wake.
|
||||||
|
/// Write payload: 8-byte little-endian u64 (the trampoline
|
||||||
|
/// address). Read payload: 0 (no error condition; this verb
|
||||||
|
/// is one-way). Mirrors Linux 7.1's
|
||||||
|
/// `acpi_set_firmware_waking_vector` in ACPICA.
|
||||||
|
/// Hardware-agnostic: works on any x86_64 system with
|
||||||
|
/// standard ACPI S3 support (Dell, HP, Lenovo, LG Gram 14).
|
||||||
|
SetS3WakingVector = 5,
|
||||||
|
/// Red Bear OS extension (Phase II.X.W): acpid requests
|
||||||
|
/// the kernel to enter S3. The kernel's kstop handler
|
||||||
|
/// dispatches on the "s3" string arg, dispatches on the
|
||||||
|
/// SLP_TYP byte, and does the PM1 register write. The
|
||||||
|
/// acpid has already done the AML prep (`_TTS(3)`, `_PTS(3)`,
|
||||||
|
/// `_SST(3)`) and written the trampoline address to FACS
|
||||||
|
/// via `SetS3WakingVector`. No payload needed.
|
||||||
|
/// Mirrors Linux 7.1's `enter_sleep_state` /
|
||||||
|
/// `acpi_hw_legacy_sleep` for S3.
|
||||||
|
EnterS3 = 6,
|
||||||
|
}
|
||||||
|
impl AcpiVerb {
|
||||||
|
pub const fn try_from_raw(value: u64) -> Option<Self> {
|
||||||
|
Some(match value {
|
||||||
|
1 => Self::ReadRxsdt,
|
||||||
|
2 => Self::CheckShutdown,
|
||||||
|
3 => Self::EnterS2Idle,
|
||||||
|
4 => Self::ExitS2Idle,
|
||||||
|
5 => Self::SetS3WakingVector,
|
||||||
|
6 => Self::EnterS3,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
#[repr(usize)]
|
||||||
|
pub enum SchemeSocketCall {
|
||||||
|
ObtainFd = 0,
|
||||||
|
MoveFd = 1,
|
||||||
|
}
|
||||||
|
impl SchemeSocketCall {
|
||||||
|
pub fn try_from_raw(raw: usize) -> Option<Self> {
|
||||||
|
Some(match raw {
|
||||||
|
0 => Self::ObtainFd,
|
||||||
|
1 => Self::MoveFd,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
#[repr(usize)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
pub enum FsCall {
|
||||||
|
Connect = 0,
|
||||||
|
}
|
||||||
|
impl FsCall {
|
||||||
|
pub fn try_from_raw(raw: usize) -> Option<Self> {
|
||||||
|
Some(match raw {
|
||||||
|
0 => Self::Connect,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct PtraceFlags: u64 {
|
||||||
|
/// Stop before a syscall is handled. Send PTRACE_FLAG_IGNORE to not
|
||||||
|
/// handle the syscall.
|
||||||
|
const PTRACE_STOP_PRE_SYSCALL = 0x0000_0000_0000_0001;
|
||||||
|
/// Stop after a syscall is handled.
|
||||||
|
const PTRACE_STOP_POST_SYSCALL = 0x0000_0000_0000_0002;
|
||||||
|
/// Stop after exactly one instruction. TODO: This may not handle
|
||||||
|
/// fexec/signal boundaries. Should it?
|
||||||
|
const PTRACE_STOP_SINGLESTEP = 0x0000_0000_0000_0004;
|
||||||
|
/// Stop before a signal is handled. Send PTRACE_FLAG_IGNORE to not
|
||||||
|
/// handle signal.
|
||||||
|
const PTRACE_STOP_SIGNAL = 0x0000_0000_0000_0008;
|
||||||
|
/// Stop on a software breakpoint, such as the int3 instruction for
|
||||||
|
/// x86_64.
|
||||||
|
const PTRACE_STOP_BREAKPOINT = 0x0000_0000_0000_0010;
|
||||||
|
/// Stop just before exiting for good.
|
||||||
|
const PTRACE_STOP_EXIT = 0x0000_0000_0000_0020;
|
||||||
|
|
||||||
|
const PTRACE_STOP_MASK = 0x0000_0000_0000_00FF;
|
||||||
|
|
||||||
|
|
||||||
|
/// Sent when a child is cloned, giving you the opportunity to trace it.
|
||||||
|
/// If you don't catch this, the child is started as normal.
|
||||||
|
const PTRACE_EVENT_CLONE = 0x0000_0000_0000_0100;
|
||||||
|
|
||||||
|
/// Sent when current-addrspace is changed, allowing the tracer to reopen the memory file.
|
||||||
|
const PTRACE_EVENT_ADDRSPACE_SWITCH = 0x0000_0000_0000_0200;
|
||||||
|
|
||||||
|
const PTRACE_EVENT_MASK = 0x0000_0000_0000_0F00;
|
||||||
|
|
||||||
|
/// Special meaning, depending on the event. Usually, when fired before
|
||||||
|
/// an action, it will skip performing that action.
|
||||||
|
const PTRACE_FLAG_IGNORE = 0x0000_0000_0000_1000;
|
||||||
|
|
||||||
|
const PTRACE_FLAG_MASK = 0x0000_0000_0000_F000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Deref for PtraceFlags {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
// Same as to_ne_bytes but in-place
|
||||||
|
unsafe {
|
||||||
|
slice::from_raw_parts(&self.bits() as *const _ as *const u8, mem::size_of::<u64>())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const SEEK_SET: usize = 0;
|
||||||
|
pub const SEEK_CUR: usize = 1;
|
||||||
|
pub const SEEK_END: usize = 2;
|
||||||
|
|
||||||
|
pub const SIGCHLD: usize = 17;
|
||||||
|
pub const SIGTSTP: usize = 20;
|
||||||
|
pub const SIGTTIN: usize = 21;
|
||||||
|
pub const SIGTTOU: usize = 22;
|
||||||
|
|
||||||
|
pub const ADDRSPACE_OP_MMAP: usize = 0;
|
||||||
|
pub const ADDRSPACE_OP_MUNMAP: usize = 1;
|
||||||
|
pub const ADDRSPACE_OP_MPROTECT: usize = 2;
|
||||||
|
pub const ADDRSPACE_OP_TRANSFER: usize = 3;
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
pub struct MremapFlags: usize {
|
||||||
|
const FIXED = 1;
|
||||||
|
const FIXED_REPLACE = 3;
|
||||||
|
/// Alias's memory region at `old_address` to `new_address` such that both regions share
|
||||||
|
/// the same frames.
|
||||||
|
const KEEP_OLD = 1 << 2;
|
||||||
|
// TODO: MAYMOVE, DONTUNMAP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bitflags! {
|
||||||
|
pub struct RwFlags: u32 {
|
||||||
|
const NONBLOCK = 1;
|
||||||
|
const APPEND = 2;
|
||||||
|
// TODO: sync/dsync
|
||||||
|
// TODO: O_DIRECT?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bitflags! {
|
||||||
|
pub struct SigcontrolFlags: usize {
|
||||||
|
/// Prevents the kernel from jumping the context to the signal trampoline, but otherwise
|
||||||
|
/// has absolutely no effect on which signals are blocked etc. Meant to be used for
|
||||||
|
/// short-lived critical sections inside libc.
|
||||||
|
const INHIBIT_DELIVERY = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bitflags! {
|
||||||
|
pub struct CallFlags: usize {
|
||||||
|
// reserved
|
||||||
|
const RSVD0 = 1 << 0;
|
||||||
|
const RSVD1 = 1 << 1;
|
||||||
|
const RSVD2 = 1 << 2;
|
||||||
|
const RSVD3 = 1 << 3;
|
||||||
|
const RSVD4 = 1 << 4;
|
||||||
|
const RSVD5 = 1 << 5;
|
||||||
|
const RSVD6 = 1 << 6;
|
||||||
|
const RSVD7 = 1 << 7;
|
||||||
|
|
||||||
|
/// Remove the fd from the caller's file table before sending the message.
|
||||||
|
const CONSUME = 1 << 8;
|
||||||
|
|
||||||
|
const WRITE = 1 << 9;
|
||||||
|
const READ = 1 << 10;
|
||||||
|
|
||||||
|
/// Indicates the request is a bulk fd passing request.
|
||||||
|
const FD = 1 << 11;
|
||||||
|
/// Flags for the fd passing request.
|
||||||
|
const FD_EXCLUSIVE = 1 << 12;
|
||||||
|
const FD_CLONE = 1 << 13;
|
||||||
|
const FD_UPPER = 1 << 14;
|
||||||
|
const FD_CLOEXEC = 1 << 15;
|
||||||
|
|
||||||
|
/// Call is a standard fs call, with metadata defined in `StdFsCallMeta`
|
||||||
|
const STD_FS = 1 << 16;
|
||||||
|
|
||||||
|
/// Call is taking multiple fds as an argument
|
||||||
|
const MULTIPLE_FDS = 1 << 17;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
pub enum StdFsCallKind {
|
||||||
|
// TODO: remove old syscalls
|
||||||
|
Fchmod = 1,
|
||||||
|
Fchown = 2,
|
||||||
|
Getdents = 3,
|
||||||
|
Fstat = 4,
|
||||||
|
Fstatvfs = 5,
|
||||||
|
Fsync = 6,
|
||||||
|
Ftruncate = 7,
|
||||||
|
Futimens = 8,
|
||||||
|
// 9 reserved in fscall RFC
|
||||||
|
// Unlinkat = 10,
|
||||||
|
Relpathat = 11,
|
||||||
|
Lock = 12,
|
||||||
|
Unlock = 13,
|
||||||
|
GetLock = 14,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StdFsCallKind {
|
||||||
|
pub fn try_from_raw(raw: u8) -> Option<Self> {
|
||||||
|
use StdFsCallKind::*;
|
||||||
|
|
||||||
|
// TODO: Use a library where this match can be automated.
|
||||||
|
Some(match raw {
|
||||||
|
1 => Fchmod,
|
||||||
|
2 => Fchown,
|
||||||
|
3 => Getdents,
|
||||||
|
4 => Fstat,
|
||||||
|
5 => Fstatvfs,
|
||||||
|
6 => Fsync,
|
||||||
|
7 => Ftruncate,
|
||||||
|
8 => Futimens,
|
||||||
|
// 9 reserved in fscall RFC
|
||||||
|
// 10 => Unlinkat,
|
||||||
|
11 => Relpathat,
|
||||||
|
12 => Lock,
|
||||||
|
13 => Unlock,
|
||||||
|
14 => GetLock,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The tag for the fd number in the upper file descriptor table.
|
||||||
|
pub const UPPER_FDTBL_TAG: usize = 1 << (usize::BITS - 2);
|
||||||
|
|
||||||
|
/// The identifier for registering event timeout
|
||||||
|
pub const EVENT_TIMEOUT_ID: usize = usize::MAX - 2;
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
use core::{
|
||||||
|
cmp::PartialEq,
|
||||||
|
ops::{BitAnd, BitOr, Not},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub trait Io {
|
||||||
|
type Value: Copy
|
||||||
|
+ PartialEq
|
||||||
|
+ BitAnd<Output = Self::Value>
|
||||||
|
+ BitOr<Output = Self::Value>
|
||||||
|
+ Not<Output = Self::Value>;
|
||||||
|
|
||||||
|
fn read(&self) -> Self::Value;
|
||||||
|
fn write(&mut self, value: Self::Value);
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn readf(&self, flags: Self::Value) -> bool {
|
||||||
|
(self.read() & flags) as Self::Value == flags
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn writef(&mut self, flags: Self::Value, value: bool) {
|
||||||
|
let tmp: Self::Value = match value {
|
||||||
|
true => self.read() | flags,
|
||||||
|
false => self.read() & !flags,
|
||||||
|
};
|
||||||
|
self.write(tmp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ReadOnly<I> {
|
||||||
|
inner: I,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> ReadOnly<I> {
|
||||||
|
pub const fn new(inner: I) -> ReadOnly<I> {
|
||||||
|
ReadOnly { inner: inner }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Io> ReadOnly<I> {
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn read(&self) -> I::Value {
|
||||||
|
self.inner.read()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn readf(&self, flags: I::Value) -> bool {
|
||||||
|
self.inner.readf(flags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct WriteOnly<I> {
|
||||||
|
inner: I,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I> WriteOnly<I> {
|
||||||
|
pub const fn new(inner: I) -> WriteOnly<I> {
|
||||||
|
WriteOnly { inner: inner }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Io> WriteOnly<I> {
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn write(&mut self, value: I::Value) {
|
||||||
|
self.inner.write(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn writef(&mut self, flags: I::Value, value: bool) {
|
||||||
|
self.inner.writef(flags, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
+165
@@ -0,0 +1,165 @@
|
|||||||
|
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
||||||
|
use core::ops::{BitAnd, BitOr, Not};
|
||||||
|
use core::{mem::MaybeUninit, ptr};
|
||||||
|
|
||||||
|
use super::io::Io;
|
||||||
|
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct Mmio<T> {
|
||||||
|
value: MaybeUninit<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Mmio<T> {
|
||||||
|
pub unsafe fn zeroed() -> Self {
|
||||||
|
Self {
|
||||||
|
value: MaybeUninit::zeroed(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub unsafe fn uninit() -> Self {
|
||||||
|
Self {
|
||||||
|
value: MaybeUninit::uninit(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub const fn from(value: T) -> Self {
|
||||||
|
Self {
|
||||||
|
value: MaybeUninit::new(value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic implementation (WARNING: requires aligned pointers!)
|
||||||
|
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
||||||
|
impl<T> Io for Mmio<T>
|
||||||
|
where
|
||||||
|
T: Copy + PartialEq + BitAnd<Output = T> + BitOr<Output = T> + Not<Output = T>,
|
||||||
|
{
|
||||||
|
type Value = T;
|
||||||
|
|
||||||
|
fn read(&self) -> T {
|
||||||
|
unsafe { ptr::read_volatile(ptr::addr_of!(self.value).cast::<T>()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, value: T) {
|
||||||
|
unsafe { ptr::write_volatile(ptr::addr_of_mut!(self.value).cast::<T>(), value) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// x86 u8 implementation
|
||||||
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||||
|
impl Io for Mmio<u8> {
|
||||||
|
type Value = u8;
|
||||||
|
|
||||||
|
fn read(&self) -> Self::Value {
|
||||||
|
unsafe {
|
||||||
|
let value: Self::Value;
|
||||||
|
let ptr: *const Self::Value = ptr::addr_of!(self.value).cast::<Self::Value>();
|
||||||
|
core::arch::asm!(
|
||||||
|
"mov {}, [{}]",
|
||||||
|
out(reg_byte) value,
|
||||||
|
in(reg) ptr
|
||||||
|
);
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, value: Self::Value) {
|
||||||
|
unsafe {
|
||||||
|
let ptr: *mut Self::Value = ptr::addr_of_mut!(self.value).cast::<Self::Value>();
|
||||||
|
core::arch::asm!(
|
||||||
|
"mov [{}], {}",
|
||||||
|
in(reg) ptr,
|
||||||
|
in(reg_byte) value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// x86 u16 implementation
|
||||||
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||||
|
impl Io for Mmio<u16> {
|
||||||
|
type Value = u16;
|
||||||
|
|
||||||
|
fn read(&self) -> Self::Value {
|
||||||
|
unsafe {
|
||||||
|
let value: Self::Value;
|
||||||
|
let ptr: *const Self::Value = ptr::addr_of!(self.value).cast::<Self::Value>();
|
||||||
|
core::arch::asm!(
|
||||||
|
"mov {:x}, [{}]",
|
||||||
|
out(reg) value,
|
||||||
|
in(reg) ptr
|
||||||
|
);
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, value: Self::Value) {
|
||||||
|
unsafe {
|
||||||
|
let ptr: *mut Self::Value = ptr::addr_of_mut!(self.value).cast::<Self::Value>();
|
||||||
|
core::arch::asm!(
|
||||||
|
"mov [{}], {:x}",
|
||||||
|
in(reg) ptr,
|
||||||
|
in(reg) value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// x86 u32 implementation
|
||||||
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||||
|
impl Io for Mmio<u32> {
|
||||||
|
type Value = u32;
|
||||||
|
|
||||||
|
fn read(&self) -> Self::Value {
|
||||||
|
unsafe {
|
||||||
|
let value: Self::Value;
|
||||||
|
let ptr: *const Self::Value = ptr::addr_of!(self.value).cast::<Self::Value>();
|
||||||
|
core::arch::asm!(
|
||||||
|
"mov {:e}, [{}]",
|
||||||
|
out(reg) value,
|
||||||
|
in(reg) ptr
|
||||||
|
);
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, value: Self::Value) {
|
||||||
|
unsafe {
|
||||||
|
let ptr: *mut Self::Value = ptr::addr_of_mut!(self.value).cast::<Self::Value>();
|
||||||
|
core::arch::asm!(
|
||||||
|
"mov [{}], {:e}",
|
||||||
|
in(reg) ptr,
|
||||||
|
in(reg) value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// x86 u64 implementation (x86_64 only)
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
impl Io for Mmio<u64> {
|
||||||
|
type Value = u64;
|
||||||
|
|
||||||
|
fn read(&self) -> Self::Value {
|
||||||
|
unsafe {
|
||||||
|
let value: Self::Value;
|
||||||
|
let ptr: *const Self::Value = ptr::addr_of!(self.value).cast::<Self::Value>();
|
||||||
|
core::arch::asm!(
|
||||||
|
"mov {:r}, [{}]",
|
||||||
|
out(reg) value,
|
||||||
|
in(reg) ptr
|
||||||
|
);
|
||||||
|
value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write(&mut self, value: Self::Value) {
|
||||||
|
unsafe {
|
||||||
|
let ptr: *mut Self::Value = ptr::addr_of_mut!(self.value).cast::<Self::Value>();
|
||||||
|
core::arch::asm!(
|
||||||
|
"mov [{}], {:r}",
|
||||||
|
in(reg) ptr,
|
||||||
|
in(reg) value,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
//! I/O functions
|
||||||
|
|
||||||
|
pub use self::{io::*, mmio::*};
|
||||||
|
|
||||||
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||||
|
pub use self::pio::*;
|
||||||
|
|
||||||
|
mod io;
|
||||||
|
mod mmio;
|
||||||
|
|
||||||
|
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||||
|
mod pio;
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
use core::{arch::asm, marker::PhantomData};
|
||||||
|
|
||||||
|
use super::io::Io;
|
||||||
|
|
||||||
|
/// Generic PIO
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Pio<T> {
|
||||||
|
port: u16,
|
||||||
|
value: PhantomData<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> Pio<T> {
|
||||||
|
/// Create a PIO from a given port
|
||||||
|
pub const fn new(port: u16) -> Self {
|
||||||
|
Pio::<T> {
|
||||||
|
port,
|
||||||
|
value: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read/Write for byte PIO
|
||||||
|
impl Io for Pio<u8> {
|
||||||
|
type Value = u8;
|
||||||
|
|
||||||
|
/// Read
|
||||||
|
#[inline(always)]
|
||||||
|
fn read(&self) -> u8 {
|
||||||
|
let value: u8;
|
||||||
|
unsafe {
|
||||||
|
asm!("in al, dx", in("dx") self.port, out("al") value, options(nostack, nomem, preserves_flags));
|
||||||
|
}
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write
|
||||||
|
#[inline(always)]
|
||||||
|
fn write(&mut self, value: u8) {
|
||||||
|
unsafe {
|
||||||
|
asm!("out dx, al", in("dx") self.port, in("al") value, options(nostack, nomem, preserves_flags));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read/Write for word PIO
|
||||||
|
impl Io for Pio<u16> {
|
||||||
|
type Value = u16;
|
||||||
|
|
||||||
|
/// Read
|
||||||
|
#[inline(always)]
|
||||||
|
fn read(&self) -> u16 {
|
||||||
|
let value: u16;
|
||||||
|
unsafe {
|
||||||
|
asm!("in ax, dx", in("dx") self.port, out("ax") value, options(nostack, nomem, preserves_flags));
|
||||||
|
}
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write
|
||||||
|
#[inline(always)]
|
||||||
|
fn write(&mut self, value: u16) {
|
||||||
|
unsafe {
|
||||||
|
asm!("out dx, ax", in("dx") self.port, in("ax") value, options(nostack, nomem, preserves_flags));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read/Write for doubleword PIO
|
||||||
|
impl Io for Pio<u32> {
|
||||||
|
type Value = u32;
|
||||||
|
|
||||||
|
/// Read
|
||||||
|
#[inline(always)]
|
||||||
|
fn read(&self) -> u32 {
|
||||||
|
let value: u32;
|
||||||
|
unsafe {
|
||||||
|
asm!("in eax, dx", in("dx") self.port, out("eax") value, options(nostack, nomem, preserves_flags));
|
||||||
|
}
|
||||||
|
value
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write
|
||||||
|
#[inline(always)]
|
||||||
|
fn write(&mut self, value: u32) {
|
||||||
|
unsafe {
|
||||||
|
asm!("out dx, eax", in("dx") self.port, in("eax") value, options(nostack, nomem, preserves_flags));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+53
@@ -0,0 +1,53 @@
|
|||||||
|
#![cfg_attr(not(any(feature = "std", test)), no_std)]
|
||||||
|
#![allow(unexpected_cfgs)] // why does this even exist?
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
extern crate core;
|
||||||
|
|
||||||
|
pub use self::{arch::*, data::*, error::*, flag::*, io::*, number::*};
|
||||||
|
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
#[path = "arch/aarch64.rs"]
|
||||||
|
mod arch;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "riscv64")]
|
||||||
|
#[path = "arch/riscv64.rs"]
|
||||||
|
mod arch;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86")]
|
||||||
|
#[path = "arch/x86.rs"]
|
||||||
|
mod arch;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
#[path = "arch/x86_64.rs"]
|
||||||
|
mod arch;
|
||||||
|
|
||||||
|
/// Function definitions
|
||||||
|
#[cfg(feature = "userspace")]
|
||||||
|
pub mod call;
|
||||||
|
|
||||||
|
#[cfg(feature = "userspace")]
|
||||||
|
pub use call::*;
|
||||||
|
|
||||||
|
/// Complex structures that are used for some system calls
|
||||||
|
pub mod data;
|
||||||
|
|
||||||
|
pub mod dirent;
|
||||||
|
|
||||||
|
/// All errors that can be generated by a system call
|
||||||
|
pub mod error;
|
||||||
|
|
||||||
|
/// Flags used as an argument to many system calls
|
||||||
|
pub mod flag;
|
||||||
|
|
||||||
|
/// Functions for low level hardware control
|
||||||
|
pub mod io;
|
||||||
|
|
||||||
|
/// Call numbers used by each system call
|
||||||
|
pub mod number;
|
||||||
|
|
||||||
|
/// ABI for shared memory based signals
|
||||||
|
pub mod sigabi;
|
||||||
|
|
||||||
|
/// V2 scheme format
|
||||||
|
pub mod schemev2;
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
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) {}
|
|
||||||
}
|
|
||||||
-675
@@ -1,675 +0,0 @@
|
|||||||
#![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,
|
|
||||||
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> {
|
|
||||||
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 best resolution
|
|
||||||
print!("Output {}", output_i);
|
|
||||||
let mut selected = modes.first().map_or(0, |x| x.0.id);
|
|
||||||
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!();
|
|
||||||
print!(" ");
|
|
||||||
|
|
||||||
let (off_x, off_y) = os.get_text_position();
|
|
||||||
let rows = 12;
|
|
||||||
let mut mode_opt = None;
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read keypress
|
|
||||||
match os.get_key() {
|
|
||||||
OsKey::Left => {
|
|
||||||
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 => {
|
|
||||||
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 => {
|
|
||||||
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 => {
|
|
||||||
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 => {
|
|
||||||
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') => {
|
|
||||||
*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') => {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 ptr = os.alloc_zeroed_page_aligned(size as usize);
|
|
||||||
if ptr.is_null() {
|
|
||||||
panic!("Failed to allocate memory for live");
|
|
||||||
}
|
|
||||||
|
|
||||||
let live = unsafe { slice::from_raw_parts_mut(ptr, size as usize) };
|
|
||||||
|
|
||||||
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, size as usize)));
|
|
||||||
}
|
|
||||||
|
|
||||||
area_add(OsMemoryEntry {
|
|
||||||
base: live.as_ptr() as u64,
|
|
||||||
size: live.len() as u64,
|
|
||||||
kind: OsMemoryKind::Reserved,
|
|
||||||
});
|
|
||||||
|
|
||||||
Some(live)
|
|
||||||
} 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,58 @@
|
|||||||
|
pub const SYS_CLASS: usize = 0xF000_0000;
|
||||||
|
pub const SYS_CLASS_PATH: usize = 0x1000_0000;
|
||||||
|
pub const SYS_CLASS_FILE: usize = 0x2000_0000;
|
||||||
|
|
||||||
|
pub const SYS_ARG: usize = 0x0F00_0000;
|
||||||
|
pub const SYS_ARG_SLICE: usize = 0x0100_0000;
|
||||||
|
pub const SYS_ARG_MSLICE: usize = 0x0200_0000;
|
||||||
|
pub const SYS_ARG_PATH: usize = 0x0300_0000;
|
||||||
|
|
||||||
|
pub const SYS_RET: usize = 0x00F0_0000;
|
||||||
|
pub const SYS_RET_FILE: usize = 0x0010_0000;
|
||||||
|
|
||||||
|
pub const SYS_OPENAT: usize = SYS_CLASS_PATH | SYS_RET_FILE | 7;
|
||||||
|
pub const SYS_OPENAT_WITH_FILTER: usize = SYS_CLASS_PATH | SYS_RET_FILE | 985;
|
||||||
|
pub const SYS_UNLINKAT: usize = SYS_CLASS_PATH | 263;
|
||||||
|
pub const SYS_UNLINKAT_WITH_FILTER: usize = SYS_CLASS_PATH | 986;
|
||||||
|
|
||||||
|
pub const SYS_CLOSE: usize = SYS_CLASS_FILE | 6;
|
||||||
|
pub const SYS_DUP: usize = SYS_CLASS_FILE | SYS_RET_FILE | 41;
|
||||||
|
pub const SYS_DUP2: usize = SYS_CLASS_FILE | SYS_RET_FILE | 63;
|
||||||
|
pub const SYS_READ: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 3;
|
||||||
|
pub const SYS_READ2: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 35;
|
||||||
|
pub const SYS_WRITE: usize = SYS_CLASS_FILE | SYS_ARG_SLICE | 4;
|
||||||
|
pub const SYS_WRITE2: usize = SYS_CLASS_FILE | SYS_ARG_SLICE | 45;
|
||||||
|
pub const SYS_LSEEK: usize = SYS_CLASS_FILE | 19;
|
||||||
|
pub const SYS_FCHMOD: usize = SYS_CLASS_FILE | 94;
|
||||||
|
pub const SYS_FCHOWN: usize = SYS_CLASS_FILE | 207;
|
||||||
|
pub const SYS_FCNTL: usize = SYS_CLASS_FILE | 55;
|
||||||
|
pub const SYS_FEVENT: usize = SYS_CLASS_FILE | 927;
|
||||||
|
|
||||||
|
// SYS_CALL, fd, inout buf ptr, inout buf len, flags, metadata buf ptr, metadata buf len
|
||||||
|
// TODO: new number for SYS_CALL where flags are sent as 6th argument (using syscall6)
|
||||||
|
pub const SYS_CALL: usize = SYS_CLASS_FILE | SYS_ARG_SLICE | SYS_ARG_MSLICE | 0xCA11;
|
||||||
|
|
||||||
|
pub const SYS_SENDFD: usize = SYS_CLASS_FILE | 34;
|
||||||
|
pub const SYS_GETDENTS: usize = SYS_CLASS_FILE | 43;
|
||||||
|
|
||||||
|
// TODO: Rename FMAP/FUNMAP to MMAP/MUNMAP
|
||||||
|
pub const SYS_FMAP: usize = SYS_CLASS_FILE | SYS_ARG_SLICE | 900;
|
||||||
|
// TODO: SYS_FUNMAP should be SYS_CLASS_FILE
|
||||||
|
pub const SYS_FUNMAP: usize = SYS_CLASS_FILE | 92;
|
||||||
|
pub const SYS_MREMAP: usize = 155;
|
||||||
|
|
||||||
|
pub const SYS_FLINK: usize = SYS_CLASS_FILE | SYS_ARG_PATH | 9;
|
||||||
|
pub const SYS_FPATH: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 928;
|
||||||
|
pub const SYS_FRENAME: usize = SYS_CLASS_FILE | SYS_ARG_PATH | 38;
|
||||||
|
pub const SYS_FSTAT: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 28;
|
||||||
|
pub const SYS_FSTATVFS: usize = SYS_CLASS_FILE | SYS_ARG_MSLICE | 100;
|
||||||
|
pub const SYS_FSYNC: usize = SYS_CLASS_FILE | 118;
|
||||||
|
pub const SYS_FTRUNCATE: usize = SYS_CLASS_FILE | 93;
|
||||||
|
pub const SYS_FUTIMENS: usize = SYS_CLASS_FILE | SYS_ARG_SLICE | 320;
|
||||||
|
|
||||||
|
pub const SYS_CLOCK_GETTIME: usize = 265;
|
||||||
|
pub const SYS_FUTEX: usize = 240;
|
||||||
|
pub const SYS_MPROTECT: usize = 125;
|
||||||
|
pub const SYS_MKNS: usize = 984;
|
||||||
|
pub const SYS_NANOSLEEP: usize = 162;
|
||||||
|
pub const SYS_YIELD: usize = 158;
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
use core::{mem, ptr};
|
|
||||||
use redoxfs::{BLOCK_SIZE, Disk};
|
|
||||||
use syscall::error::{EIO, Error, Result};
|
|
||||||
|
|
||||||
use super::{DISK_ADDRESS_PACKET_ADDR, DISK_BIOS_ADDR, ThunkData};
|
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
/// 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)*));
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
use core::{cmp, mem, ptr};
|
|
||||||
|
|
||||||
use crate::area_add;
|
|
||||||
use crate::os::{OsMemoryEntry, OsMemoryKind};
|
|
||||||
|
|
||||||
use super::{MEMORY_MAP_ADDR, thunk::ThunkData};
|
|
||||||
|
|
||||||
#[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
|
|
||||||
}
|
|
||||||
@@ -1,318 +0,0 @@
|
|||||||
use alloc::alloc::{Layout, alloc_zeroed};
|
|
||||||
use core::{convert::TryFrom, mem, ptr, slice};
|
|
||||||
use linked_list_allocator::LockedHeap;
|
|
||||||
use spin::Mutex;
|
|
||||||
|
|
||||||
use crate::KernelArgs;
|
|
||||||
use crate::logger::LOGGER;
|
|
||||||
use crate::os::{Os, OsHwDesc, OsKey, OsVideoMode};
|
|
||||||
|
|
||||||
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 ptr =
|
|
||||||
unsafe { alloc_zeroed(Layout::from_size_align(pages * page_size, page_size).unwrap()) };
|
|
||||||
|
|
||||||
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 },
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
//! 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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
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));
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,151 +0,0 @@
|
|||||||
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
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
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(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,95 +0,0 @@
|
|||||||
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),
|
|
||||||
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 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);
|
|
||||||
}
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,236 +0,0 @@
|
|||||||
use core::{arch::asm, fmt::Write, mem, slice};
|
|
||||||
use uefi::status::Result;
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
KernelArgs,
|
|
||||||
arch::{ENTRY_ADDRESS_MASK, PAGE_ENTRIES, PF_PRESENT, PF_TABLE, PHYS_OFFSET},
|
|
||||||
logger::LOGGER,
|
|
||||||
};
|
|
||||||
|
|
||||||
use super::super::{OsEfi, memory_map::memory_map};
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
#[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::*;
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
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()
|
|
||||||
}
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
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
|
|
||||||
"#
|
|
||||||
);
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
use core::{arch::asm, mem};
|
|
||||||
use uefi::status::Result;
|
|
||||||
use x86::{
|
|
||||||
controlregs::{self, Cr0, Cr4},
|
|
||||||
msr,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::{KernelArgs, logger::LOGGER};
|
|
||||||
|
|
||||||
use super::super::{OsEfi, memory_map::memory_map};
|
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,509 +0,0 @@
|
|||||||
use alloc::{string::String, vec, vec::Vec};
|
|
||||||
use core::{fmt::Write, mem, ptr, slice};
|
|
||||||
use uefi::{
|
|
||||||
Handle,
|
|
||||||
device::{
|
|
||||||
DevicePath, DevicePathAcpiType, DevicePathBbsType, DevicePathEndType,
|
|
||||||
DevicePathHardwareType, DevicePathMediaType, DevicePathMessagingType, DevicePathType,
|
|
||||||
},
|
|
||||||
guid::Guid,
|
|
||||||
status::Status,
|
|
||||||
};
|
|
||||||
use uefi_std::{fs::FileSystem, loaded_image::LoadedImage, proto::Protocol};
|
|
||||||
|
|
||||||
use super::disk::{DiskEfi, DiskOrFileEfi};
|
|
||||||
|
|
||||||
#[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") {
|
|
||||||
// First try to get a live image from redox-live.iso. This is required to support netbooting.
|
|
||||||
if let Some(buffer) = esp_live_image(esp_handle, esp_device_path.0) {
|
|
||||||
return vec![DiskDevice {
|
|
||||||
handle: esp_handle,
|
|
||||||
// Support both a copy of livedisk.iso and a standalone redoxfs partition
|
|
||||||
partition_offset: if &buffer[512..520] == b"EFI PART" {
|
|
||||||
//TODO: get block from partition table
|
|
||||||
2 * crate::MIBI as u64
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
},
|
|
||||||
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 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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
devices.push(DiskDevice {
|
|
||||||
handle,
|
|
||||||
partition_offset: if disk.0.Media.LogicalPartition {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
//TODO: get block from partition table
|
|
||||||
2 * crate::MIBI as u64
|
|
||||||
},
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
use alloc::vec::Vec;
|
|
||||||
use core::slice;
|
|
||||||
use redoxfs::{BLOCK_SIZE, Disk, RECORD_SIZE};
|
|
||||||
use std::proto::Protocol;
|
|
||||||
use syscall::{EINVAL, EIO, Error, Result};
|
|
||||||
use uefi::block_io::BlockIo as UefiBlockIo;
|
|
||||||
use uefi::guid::{BLOCK_IO_GUID, 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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
use std::proto::Protocol;
|
|
||||||
use uefi::graphics::GraphicsOutput;
|
|
||||||
use uefi::guid::{GRAPHICS_OUTPUT_PROTOCOL_GUID, 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
use crate::Os;
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
use byteorder::BE;
|
|
||||||
use byteorder::ByteOrder;
|
|
||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
@@ -1,396 +0,0 @@
|
|||||||
use alloc::vec::Vec;
|
|
||||||
use core::{cell::RefCell, mem, ptr, slice};
|
|
||||||
use std::proto::Protocol;
|
|
||||||
use uefi::{
|
|
||||||
Handle,
|
|
||||||
boot::LocateSearchType,
|
|
||||||
memory::MemoryType,
|
|
||||||
reset::ResetType,
|
|
||||||
status::{Result, Status},
|
|
||||||
system::SystemTable,
|
|
||||||
text::TextInputKey,
|
|
||||||
};
|
|
||||||
|
|
||||||
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();
|
|
||||||
ptr as *mut u8
|
|
||||||
};
|
|
||||||
|
|
||||||
assert!(!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>> {
|
|
||||||
// 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 => (),
|
|
||||||
// Print any other errors
|
|
||||||
_ => {
|
|
||||||
log::warn!("BlockIo error: {:?}", err);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 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());
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
use core::ptr;
|
|
||||||
use log::error;
|
|
||||||
use uefi::status::Status;
|
|
||||||
|
|
||||||
use crate::os::OsVideoMode;
|
|
||||||
use crate::os::uefi::display::Output;
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+213
@@ -0,0 +1,213 @@
|
|||||||
|
use core::{
|
||||||
|
mem,
|
||||||
|
ops::{Deref, DerefMut},
|
||||||
|
slice,
|
||||||
|
};
|
||||||
|
|
||||||
|
use bitflags::bitflags;
|
||||||
|
|
||||||
|
pub struct CallerCtx {
|
||||||
|
pub pid: usize,
|
||||||
|
pub uid: u32,
|
||||||
|
pub gid: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum OpenResult {
|
||||||
|
ThisScheme { number: usize },
|
||||||
|
OtherScheme { fd: usize },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct Sqe {
|
||||||
|
pub opcode: u8,
|
||||||
|
pub sqe_flags: SqeFlags,
|
||||||
|
pub _rsvd: u16, // TODO: priority
|
||||||
|
pub tag: u32,
|
||||||
|
pub args: [u64; 6],
|
||||||
|
pub caller: u64,
|
||||||
|
}
|
||||||
|
impl Deref for Sqe {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe { slice::from_raw_parts(self as *const Sqe as *const u8, mem::size_of::<Sqe>()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for Sqe {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe { slice::from_raw_parts_mut(self as *mut Sqe as *mut u8, mem::size_of::<Sqe>()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct SqeFlags: u8 {
|
||||||
|
// If zero, the message is bidirectional, and the scheme is expected to pass the Ksmsg's
|
||||||
|
// tag field to the Skmsg. Some opcodes require this flag to be set.
|
||||||
|
const ONEWAY = 1;
|
||||||
|
|
||||||
|
// If this flag is set, index 0 of Sqe's args stores the IDs buffer address,
|
||||||
|
// and index 1 stores the IDs buffer length.
|
||||||
|
const MULTIPLE_IDS = 1 << 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy, Debug, Default)]
|
||||||
|
pub struct Cqe {
|
||||||
|
pub flags: u8, // bits 2:0 are CqeOpcode
|
||||||
|
pub extra_raw: [u8; 3],
|
||||||
|
pub tag: u32,
|
||||||
|
pub result: u64,
|
||||||
|
}
|
||||||
|
impl Deref for Cqe {
|
||||||
|
type Target = [u8];
|
||||||
|
fn deref(&self) -> &[u8] {
|
||||||
|
unsafe { slice::from_raw_parts(self as *const Cqe as *const u8, mem::size_of::<Cqe>()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DerefMut for Cqe {
|
||||||
|
fn deref_mut(&mut self) -> &mut [u8] {
|
||||||
|
unsafe { slice::from_raw_parts_mut(self as *mut Cqe as *mut u8, mem::size_of::<Cqe>()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bitflags! {
|
||||||
|
#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
|
||||||
|
pub struct NewFdFlags: u8 {
|
||||||
|
const POSITIONED = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Cqe {
|
||||||
|
pub fn extra(&self) -> u32 {
|
||||||
|
u32::from_ne_bytes([self.extra_raw[0], self.extra_raw[1], self.extra_raw[2], 0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||||
|
pub enum CqeOpcode {
|
||||||
|
RespondRegular,
|
||||||
|
RespondWithFd,
|
||||||
|
SendFevent, // no tag
|
||||||
|
ObtainFd,
|
||||||
|
RespondWithMultipleFds,
|
||||||
|
/// [`SchemeAsync::on_close`] and [`SchemeSync::on_close`] are only called when the last file
|
||||||
|
/// descriptor referring to the file description is closed. To implement traditional POSIX
|
||||||
|
/// advisory file locking, [`CqeOpcode::RespondAndNotifyOnDetach`] is used to notify the scheme
|
||||||
|
/// by sending a [`RequestKind::OnDetach`] request the next time the file description is
|
||||||
|
/// "detached" from a file descriptor. Not done by default to avoid unnecessary IPC.
|
||||||
|
RespondAndNotifyOnDetach,
|
||||||
|
// TODO: ProvideMmap
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CqeOpcode {
|
||||||
|
pub fn try_from_raw(raw: u8) -> Option<Self> {
|
||||||
|
// TODO: Use a library where this match can be automated.
|
||||||
|
Some(match raw {
|
||||||
|
0 => Self::RespondRegular,
|
||||||
|
1 => Self::RespondWithFd,
|
||||||
|
2 => Self::SendFevent,
|
||||||
|
3 => Self::ObtainFd,
|
||||||
|
4 => Self::RespondWithMultipleFds,
|
||||||
|
5 => Self::RespondAndNotifyOnDetach,
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// SqeOpcode
|
||||||
|
#[repr(u8)]
|
||||||
|
#[non_exhaustive]
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum Opcode {
|
||||||
|
Close = 3, // fd
|
||||||
|
Dup = 4, // old fd, buf_ptr, buf_len
|
||||||
|
Read = 5, // fd, buf_ptr, buf_len, TODO offset, TODO flags, _
|
||||||
|
Write = 6, // fd, buf_ptr, buf_len, TODO offset, TODO flags)
|
||||||
|
Fsize = 7, // fd
|
||||||
|
Fchmod = 8, // fd, new mode
|
||||||
|
Fchown = 9, // fd, new uid, new gid
|
||||||
|
Fcntl = 10, // fd, cmd, arg
|
||||||
|
Fevent = 11, // fd, requested mask
|
||||||
|
Sendfd = 12,
|
||||||
|
Fpath = 13, // fd, buf_ptr, buf_len
|
||||||
|
Frename = 14,
|
||||||
|
Fstat = 15, // fd, buf_ptr, buf_len
|
||||||
|
Fstatvfs = 16, // fd, buf_ptr, buf_len
|
||||||
|
Fsync = 17, // fd
|
||||||
|
Ftruncate = 18, // fd, new len
|
||||||
|
Futimens = 19, // fd, times_buf, times_len
|
||||||
|
|
||||||
|
MmapPrep = 20,
|
||||||
|
RequestMmap = 21,
|
||||||
|
Mremap = 22,
|
||||||
|
Munmap = 23,
|
||||||
|
Msync = 24, // TODO
|
||||||
|
|
||||||
|
Cancel = 25, // @tag
|
||||||
|
|
||||||
|
Getdents = 26,
|
||||||
|
CloseMsg = 27,
|
||||||
|
Call = 28,
|
||||||
|
|
||||||
|
OpenAt = 29, // fd, buf_ptr, buf_len, flags
|
||||||
|
Flink = 30,
|
||||||
|
Recvfd = 31,
|
||||||
|
|
||||||
|
UnlinkAt = 32, // fd, path_ptr, path_len (utf8), flags
|
||||||
|
StdFsCall = 33,
|
||||||
|
|
||||||
|
Detach = 34,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Opcode {
|
||||||
|
pub fn try_from_raw(raw: u8) -> Option<Self> {
|
||||||
|
use Opcode::*;
|
||||||
|
|
||||||
|
// TODO: Use a library where this match can be automated.
|
||||||
|
Some(match raw {
|
||||||
|
3 => Close,
|
||||||
|
4 => Dup,
|
||||||
|
5 => Read,
|
||||||
|
6 => Write,
|
||||||
|
7 => Fsize,
|
||||||
|
8 => Fchmod,
|
||||||
|
9 => Fchown,
|
||||||
|
10 => Fcntl,
|
||||||
|
11 => Fevent,
|
||||||
|
12 => Sendfd,
|
||||||
|
13 => Fpath,
|
||||||
|
14 => Frename,
|
||||||
|
15 => Fstat,
|
||||||
|
16 => Fstatvfs,
|
||||||
|
17 => Fsync,
|
||||||
|
18 => Ftruncate,
|
||||||
|
19 => Futimens,
|
||||||
|
|
||||||
|
20 => MmapPrep,
|
||||||
|
21 => RequestMmap,
|
||||||
|
22 => Mremap,
|
||||||
|
23 => Munmap,
|
||||||
|
24 => Msync,
|
||||||
|
|
||||||
|
25 => Cancel,
|
||||||
|
26 => Getdents,
|
||||||
|
27 => CloseMsg,
|
||||||
|
28 => Call,
|
||||||
|
|
||||||
|
29 => OpenAt,
|
||||||
|
30 => Flink,
|
||||||
|
31 => Recvfd,
|
||||||
|
|
||||||
|
32 => UnlinkAt,
|
||||||
|
33 => StdFsCall,
|
||||||
|
34 => Detach,
|
||||||
|
|
||||||
|
_ => return None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
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(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+340
@@ -0,0 +1,340 @@
|
|||||||
|
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
|
/// Signal runtime struct for the entire process
|
||||||
|
#[derive(Debug)]
|
||||||
|
#[repr(C, align(4096))]
|
||||||
|
pub struct SigProcControl {
|
||||||
|
pub pending: AtomicU64,
|
||||||
|
pub actions: [RawAction; 64],
|
||||||
|
pub sender_infos: [AtomicU64; 32],
|
||||||
|
//pub queue: [RealtimeSig; 32], TODO
|
||||||
|
// qhead, qtail TODO
|
||||||
|
}
|
||||||
|
/*#[derive(Debug)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct RealtimeSig {
|
||||||
|
pub arg: NonatomicUsize,
|
||||||
|
}*/
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
#[repr(C, align(16))]
|
||||||
|
pub struct RawAction {
|
||||||
|
/// Only two MSBs are interesting for the kernel. If bit 63 is set, signal is ignored. If bit
|
||||||
|
/// 62 is set and the signal is SIGTSTP/SIGTTIN/SIGTTOU, it's equivalent to the action of
|
||||||
|
/// Stop.
|
||||||
|
pub first: AtomicU64,
|
||||||
|
/// Completely ignored by the kernel, but exists so userspace can (when 16-byte atomics exist)
|
||||||
|
/// atomically set both the handler, sigaction flags, and sigaction mask.
|
||||||
|
pub user_data: AtomicU64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Signal runtime struct for a thread
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Sigcontrol {
|
||||||
|
// composed of [lo "pending" | lo "unmasked", hi "pending" | hi "unmasked"]
|
||||||
|
pub word: [AtomicU64; 2],
|
||||||
|
|
||||||
|
// lo = sender pid, hi = sender ruid
|
||||||
|
pub sender_infos: [AtomicU64; 32],
|
||||||
|
|
||||||
|
pub control_flags: SigatomicUsize,
|
||||||
|
|
||||||
|
pub saved_ip: NonatomicUsize, // rip/eip/pc
|
||||||
|
pub saved_archdep_reg: NonatomicUsize, // rflags(x64)/eflags(x86)/x0(aarch64)/t0(riscv64)
|
||||||
|
}
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct SenderInfo {
|
||||||
|
pub pid: u32,
|
||||||
|
pub ruid: u32,
|
||||||
|
}
|
||||||
|
impl SenderInfo {
|
||||||
|
#[inline]
|
||||||
|
pub fn raw(self) -> u64 {
|
||||||
|
u64::from(self.pid) | (u64::from(self.ruid) << 32)
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub const fn from_raw(raw: u64) -> Self {
|
||||||
|
Self {
|
||||||
|
pid: raw as u32,
|
||||||
|
ruid: (raw >> 32) as u32,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Sigcontrol {
|
||||||
|
pub fn currently_pending_unblocked(&self, proc: &SigProcControl) -> u64 {
|
||||||
|
let proc_pending = proc.pending.load(Ordering::Relaxed);
|
||||||
|
let [w0, w1] = core::array::from_fn(|i| {
|
||||||
|
let w = self.word[i].load(Ordering::Relaxed);
|
||||||
|
((w | (proc_pending >> (i * 32))) & 0xffff_ffff) & (w >> 32)
|
||||||
|
});
|
||||||
|
//core::sync::atomic::fence(Ordering::Acquire);
|
||||||
|
w0 | (w1 << 32)
|
||||||
|
}
|
||||||
|
pub fn set_allowset(&self, new_allowset: u64) -> u64 {
|
||||||
|
//core::sync::atomic::fence(Ordering::Release);
|
||||||
|
let [w0, w1] = self.word.each_ref().map(|w| w.load(Ordering::Relaxed));
|
||||||
|
let old_a0 = w0 & 0xffff_ffff_0000_0000;
|
||||||
|
let old_a1 = w1 & 0xffff_ffff_0000_0000;
|
||||||
|
let new_a0 = (new_allowset & 0xffff_ffff) << 32;
|
||||||
|
let new_a1 = new_allowset & 0xffff_ffff_0000_0000;
|
||||||
|
|
||||||
|
let prev_w0 = self.word[0].fetch_add(new_a0.wrapping_sub(old_a0), Ordering::Relaxed);
|
||||||
|
let prev_w1 = self.word[0].fetch_add(new_a1.wrapping_sub(old_a1), Ordering::Relaxed);
|
||||||
|
//core::sync::atomic::fence(Ordering::Acquire);
|
||||||
|
let up0 = prev_w0 & (prev_w0 >> 32);
|
||||||
|
let up1 = prev_w1 & (prev_w1 >> 32);
|
||||||
|
|
||||||
|
up0 | (up1 << 32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct SigatomicUsize(AtomicUsize);
|
||||||
|
|
||||||
|
impl SigatomicUsize {
|
||||||
|
#[inline]
|
||||||
|
pub fn load(&self, ordering: Ordering) -> usize {
|
||||||
|
let value = self.0.load(Ordering::Relaxed);
|
||||||
|
if ordering != Ordering::Relaxed {
|
||||||
|
core::sync::atomic::compiler_fence(ordering);
|
||||||
|
}
|
||||||
|
value
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn store(&self, value: usize, ordering: Ordering) {
|
||||||
|
if ordering != Ordering::Relaxed {
|
||||||
|
core::sync::atomic::compiler_fence(ordering);
|
||||||
|
}
|
||||||
|
self.0.store(value, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
pub struct NonatomicUsize(AtomicUsize);
|
||||||
|
|
||||||
|
impl NonatomicUsize {
|
||||||
|
#[inline]
|
||||||
|
pub const fn new(a: usize) -> Self {
|
||||||
|
Self(AtomicUsize::new(a))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
pub fn get(&self) -> usize {
|
||||||
|
self.0.load(Ordering::Relaxed)
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
pub fn set(&self, value: usize) {
|
||||||
|
self.0.store(value, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn sig_bit(sig: usize) -> u64 {
|
||||||
|
1 << (sig - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Move to redox_rt?
|
||||||
|
impl SigProcControl {
|
||||||
|
/// Checks if `sig` should be ignored based on the current action flags.
|
||||||
|
///
|
||||||
|
/// * `sig` - The signal to check (e.g. `SIGCHLD`).
|
||||||
|
///
|
||||||
|
/// * `stop_or_continue` - Whether the signal is generated because a child
|
||||||
|
/// process stopped (`SIGSTOP`, `SIGTSTP`) or continued (`SIGCONT`). If
|
||||||
|
/// `true` and `sig` is `SIGCHLD`, the signal shall not be delivered if the
|
||||||
|
/// `SA_NOCLDSTOP` flag is set for `SIGCHLD`.
|
||||||
|
pub fn signal_will_ign(&self, sig: usize, stop_or_continue: bool) -> bool {
|
||||||
|
let flags = self.actions[sig - 1].first.load(Ordering::Relaxed);
|
||||||
|
let will_ign = flags & (1 << 63) != 0;
|
||||||
|
let sig_specific = flags & (1 << 62) != 0; // SA_NOCLDSTOP if sig == SIGCHLD
|
||||||
|
|
||||||
|
will_ign || (sig == SIGCHLD && stop_or_continue && sig_specific)
|
||||||
|
}
|
||||||
|
// TODO: Move to redox_rt?
|
||||||
|
pub fn signal_will_stop(&self, sig: usize) -> bool {
|
||||||
|
use crate::flag::*;
|
||||||
|
matches!(sig, SIGTSTP | SIGTTIN | SIGTTOU)
|
||||||
|
&& self.actions[sig - 1].first.load(Ordering::Relaxed) & (1 << 62) != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "x86"))]
|
||||||
|
pub use core::sync::atomic::AtomicU64;
|
||||||
|
|
||||||
|
use crate::SIGCHLD;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86")]
|
||||||
|
pub use self::atomic::AtomicU64;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86")]
|
||||||
|
mod atomic {
|
||||||
|
use core::{cell::UnsafeCell, sync::atomic::Ordering};
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct AtomicU64(UnsafeCell<u64>);
|
||||||
|
|
||||||
|
unsafe impl Send for AtomicU64 {}
|
||||||
|
unsafe impl Sync for AtomicU64 {}
|
||||||
|
|
||||||
|
impl AtomicU64 {
|
||||||
|
pub const fn new(inner: u64) -> Self {
|
||||||
|
Self(UnsafeCell::new(inner))
|
||||||
|
}
|
||||||
|
pub fn compare_exchange(
|
||||||
|
&self,
|
||||||
|
old: u64,
|
||||||
|
new: u64,
|
||||||
|
_success: Ordering,
|
||||||
|
_failure: Ordering,
|
||||||
|
) -> Result<u64, u64> {
|
||||||
|
let old_hi = (old >> 32) as u32;
|
||||||
|
let old_lo = old as u32;
|
||||||
|
let new_hi = (new >> 32) as u32;
|
||||||
|
let new_lo = new as u32;
|
||||||
|
let mut out_hi;
|
||||||
|
let mut out_lo;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
core::arch::asm!("lock cmpxchg8b [{}]", in(reg) self.0.get(), inout("edx") old_hi => out_hi, inout("eax") old_lo => out_lo, in("ecx") new_hi, in("ebx") new_lo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if old_hi == out_hi && old_lo == out_lo {
|
||||||
|
Ok(old)
|
||||||
|
} else {
|
||||||
|
Err(u64::from(out_lo) | (u64::from(out_hi) << 32))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn load(&self, ordering: Ordering) -> u64 {
|
||||||
|
match self.compare_exchange(0, 0, ordering, ordering) {
|
||||||
|
Ok(new) => new,
|
||||||
|
Err(new) => new,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn store(&self, new: u64, ordering: Ordering) {
|
||||||
|
let mut old = 0;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match self.compare_exchange(old, new, ordering, Ordering::Relaxed) {
|
||||||
|
Ok(_) => break,
|
||||||
|
Err(new) => {
|
||||||
|
old = new;
|
||||||
|
core::hint::spin_loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn fetch_update(
|
||||||
|
&self,
|
||||||
|
set_order: Ordering,
|
||||||
|
fetch_order: Ordering,
|
||||||
|
mut f: impl FnMut(u64) -> Option<u64>,
|
||||||
|
) -> Result<u64, u64> {
|
||||||
|
let mut old = self.load(fetch_order);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let new = f(old).ok_or(old)?;
|
||||||
|
match self.compare_exchange(old, new, set_order, Ordering::Relaxed) {
|
||||||
|
Ok(_) => return Ok(new),
|
||||||
|
Err(changed) => {
|
||||||
|
old = changed;
|
||||||
|
core::hint::spin_loop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn fetch_or(&self, bits: u64, order: Ordering) -> u64 {
|
||||||
|
self.fetch_update(order, Ordering::Relaxed, |b| Some(b | bits))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
pub fn fetch_and(&self, bits: u64, order: Ordering) -> u64 {
|
||||||
|
self.fetch_update(order, Ordering::Relaxed, |b| Some(b & bits))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
pub fn fetch_add(&self, term: u64, order: Ordering) -> u64 {
|
||||||
|
self.fetch_update(order, Ordering::Relaxed, |b| Some(b.wrapping_add(term)))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::sync::{
|
||||||
|
atomic::{AtomicU64, Ordering},
|
||||||
|
Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(not(loom))]
|
||||||
|
use std::{sync::Mutex, thread};
|
||||||
|
#[cfg(not(loom))]
|
||||||
|
fn model(f: impl FnOnce()) {
|
||||||
|
f()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(loom)]
|
||||||
|
use loom::{model, sync::Mutex, thread};
|
||||||
|
|
||||||
|
use crate::{RawAction, SigProcControl, Sigcontrol};
|
||||||
|
|
||||||
|
struct FakeThread {
|
||||||
|
ctl: Sigcontrol,
|
||||||
|
pctl: SigProcControl,
|
||||||
|
ctxt: Mutex<()>,
|
||||||
|
}
|
||||||
|
impl Default for FakeThread {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
ctl: Sigcontrol::default(),
|
||||||
|
pctl: SigProcControl {
|
||||||
|
pending: AtomicU64::new(0),
|
||||||
|
actions: core::array::from_fn(|_| RawAction::default()),
|
||||||
|
sender_infos: Default::default(),
|
||||||
|
},
|
||||||
|
ctxt: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn singlethread_mask() {
|
||||||
|
model(|| {
|
||||||
|
let fake_thread = Arc::new(FakeThread::default());
|
||||||
|
|
||||||
|
let thread = {
|
||||||
|
let fake_thread = Arc::clone(&fake_thread);
|
||||||
|
|
||||||
|
thread::spawn(move || {
|
||||||
|
fake_thread.ctl.set_allowset(!0);
|
||||||
|
{
|
||||||
|
let _g = fake_thread.ctxt.lock();
|
||||||
|
if fake_thread
|
||||||
|
.ctl
|
||||||
|
.currently_pending_unblocked(&fake_thread.pctl)
|
||||||
|
== 0
|
||||||
|
{
|
||||||
|
drop(_g);
|
||||||
|
thread::park();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
for sig in 1..=64 {
|
||||||
|
let _g = fake_thread.ctxt.lock();
|
||||||
|
|
||||||
|
let idx = sig - 1;
|
||||||
|
let bit = 1 << (idx % 32);
|
||||||
|
|
||||||
|
fake_thread.ctl.word[idx / 32].fetch_or(bit, Ordering::Relaxed);
|
||||||
|
let w = fake_thread.ctl.word[idx / 32].load(Ordering::Relaxed);
|
||||||
|
|
||||||
|
if w & (w >> 32) != 0 {
|
||||||
|
thread.thread().unpark();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thread.join().unwrap();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"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
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"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