Compare commits
47 Commits
Author | SHA1 | Date | |
---|---|---|---|
364723984e | |||
88d8d9cd58 | |||
d6eef6fe08 | |||
8b19189689 | |||
153c3d6618 | |||
d6ac7671dc | |||
9a23b1bb37 | |||
347cdd9cf3 | |||
69a8a0d4ea | |||
85e999a7b0 | |||
dae609c6ed | |||
551c9079b9 | |||
d6ed2f1d75 | |||
4d2f27b3b2 | |||
1a884f2e98 | |||
00b4ed0066 | |||
6ad3b36201 | |||
3f3c79ec0c | |||
25140bda17 | |||
61b3643945 | |||
2549103636 | |||
856cbdfe01 | |||
391359a54e | |||
f4420d9306 | |||
4633c512bd | |||
d620822d97 | |||
58e866cb2f | |||
60410efd1a | |||
1da8b8465f | |||
6ac67efe2d | |||
1c0f0765c0 | |||
c1f436bcfb | |||
99a69c7998 | |||
52929cef24 | |||
79bfcf6b57 | |||
021aac6e6a | |||
3be14e7f6a | |||
b07c675986 | |||
12c28f7681 | |||
2413d4dec6 | |||
bc970a9603 | |||
274a8d2c0e | |||
c5c82ac567 | |||
edf593cbf8 | |||
4c81f0591a | |||
66eaa8a64f | |||
38004e0bc4 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -2,6 +2,3 @@
|
||||
/.idea
|
||||
*.iml
|
||||
/*.txt
|
||||
/.vscode
|
||||
*.a
|
||||
*.o
|
||||
|
@ -1,38 +1,27 @@
|
||||
default:
|
||||
image: rust:1.68-bookworm
|
||||
before_script:
|
||||
- wget -q https://cloud.cuwott.fr/s/9fyrejDxMdNRQNn/download/riscv64-cross-compiler-multilib.tar.gz
|
||||
- mkdir /opt/riscv
|
||||
- tar xzf riscv64-cross-compiler-multilib.tar.gz -C /opt/riscv
|
||||
image: rust:latest
|
||||
|
||||
stages:
|
||||
- build
|
||||
- test
|
||||
|
||||
build-job:
|
||||
stage: build
|
||||
script:
|
||||
- echo "Compiling the code..."
|
||||
- cargo build
|
||||
- echo "Compile complete."
|
||||
|
||||
unit-test-job:
|
||||
stage: test
|
||||
script:
|
||||
- echo "Compiling c files"
|
||||
- make
|
||||
- echo "Running unit tests..."
|
||||
- cargo test
|
||||
|
||||
unsafe-test-job:
|
||||
stage: test
|
||||
script:
|
||||
- echo "Checking if List is still safe"
|
||||
- rustup +nightly component add miri
|
||||
- export MIRIFLAGS="-Zmiri-disable-isolation"
|
||||
- cargo +nightly miri test utility::list::test
|
||||
only:
|
||||
changes:
|
||||
- "src/utility/list.rs"
|
||||
|
||||
lint-test-job:
|
||||
only:
|
||||
refs:
|
||||
- merge_requests
|
||||
stage: test
|
||||
script:
|
||||
- echo "Linting code..."
|
||||
- rustup component add clippy
|
||||
- cargo clippy -- -D warnings
|
||||
- cargo clippy
|
426
Cargo.lock
generated
426
Cargo.lock
generated
@ -3,44 +3,10 @@
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "anstream"
|
||||
version = "0.2.6"
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "342258dd14006105c2b75ab1bd7543a03bdf0cfc94383303ac212a04939dff6f"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"anstyle-parse",
|
||||
"anstyle-wincon",
|
||||
"concolor-override",
|
||||
"concolor-query",
|
||||
"is-terminal",
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "23ea9e81bd02e310c216d080f6223c179012256e5151c41db88d12c88a1684d2"
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-parse"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7d1bb534e9efed14f3e5f44e7dd1a4f709384023a4165199a4241e18dff0116"
|
||||
dependencies = [
|
||||
"utf8parse",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anstyle-wincon"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3127af6145b149f3287bb9a0d10ad9c5692dba8c53ad48285e5bec4063834fa"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
@ -52,140 +18,148 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
name = "burritos"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"clap",
|
||||
"libc",
|
||||
"serial_test",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "046ae530c528f252094e4a77886ee1374437744b2bff1497aa898bbddbbb29b3"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "223163f58c9a40c3b0a43e1c4b50a9ce09f007ea2cb1ec258a687945b4b7929f"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
"bitflags",
|
||||
"clap_lex",
|
||||
"strsim",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f9644cd56d6b87dbe899ef8b053e331c0637664e9e21a33dfcdc36093f5c5c4"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1"
|
||||
|
||||
[[package]]
|
||||
name = "concolor-override"
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a855d4a1978dc52fb0536a04d384c2c0c1aa273597f08b77c8c4d3b2eec6037f"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "concolor-query"
|
||||
version = "0.3.3"
|
||||
name = "dashmap"
|
||||
version = "5.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf"
|
||||
checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"
|
||||
dependencies = [
|
||||
"windows-sys 0.45.0",
|
||||
"cfg-if",
|
||||
"hashbrown",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.0"
|
||||
name = "futures"
|
||||
version = "0.3.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0"
|
||||
checksum = "531ac96c6ff5fd7c62263c5e3c67a603af4fcaee2e1a0ae5565ba3a11e69e549"
|
||||
dependencies = [
|
||||
"errno-dragonfly",
|
||||
"libc",
|
||||
"windows-sys 0.45.0",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "errno-dragonfly"
|
||||
version = "0.1.2"
|
||||
name = "futures-channel"
|
||||
version = "0.3.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||
checksum = "164713a5a0dcc3e7b4b1ed7d3b433cabc18025386f9339346e8daf15963cf7ac"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
name = "futures-core"
|
||||
version = "0.3.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
checksum = "86d7a0c1aa76363dac491de0ee99faf6941128376f1cf96f07db7603b7de69dd"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.1"
|
||||
name = "futures-executor"
|
||||
version = "0.3.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286"
|
||||
|
||||
[[package]]
|
||||
name = "io-lifetimes"
|
||||
version = "1.0.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220"
|
||||
checksum = "1997dd9df74cdac935c76252744c1ed5794fac083242ea4fe77ef3ed60ba0f83"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
"windows-sys 0.48.0",
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is-terminal"
|
||||
version = "0.4.6"
|
||||
name = "futures-io"
|
||||
version = "0.3.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "256017f749ab3117e93acb91063009e1f1bb56d03965b14c2c8df4eb02c524d8"
|
||||
checksum = "89d422fa3cbe3b40dca574ab087abb5bc98258ea57eea3fd6f1fa7162c778b91"
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec93083a4aecafb2a80a885c9de1f0ccae9dbd32c2bb54b0c3a65690e0b8d2f2"
|
||||
|
||||
[[package]]
|
||||
name = "futures-task"
|
||||
version = "0.3.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd65540d33b37b16542a0438c12e6aeead10d4ac5d05bd3f805b8f35ab592879"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ef6b17e481503ec85211fed8f39d1970f128935ca1f814cd32ac4a6842e84ab"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"io-lifetimes",
|
||||
"rustix",
|
||||
"windows-sys 0.45.0",
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.12.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.141"
|
||||
version = "0.2.139"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
|
||||
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.3.1"
|
||||
name = "lock_api"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f"
|
||||
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
@ -194,10 +168,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.56"
|
||||
name = "parking_lot"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
|
||||
checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking_lot_core"
|
||||
version = "0.9.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"redox_syscall",
|
||||
"smallvec",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pin-project-lite"
|
||||
version = "0.2.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
|
||||
|
||||
[[package]]
|
||||
name = "pin-utils"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@ -212,30 +221,65 @@ dependencies = [
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "0.37.7"
|
||||
name = "redox_syscall"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2aae838e49b3d63e9274e1c01833cc8139d3fec468c3b84688c628f44b1ae11d"
|
||||
checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
"io-lifetimes",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.10.0"
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "serial_test"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "538c30747ae860d6fb88330addbbd3e0ddbe46d662d032855596d8a8ca260611"
|
||||
dependencies = [
|
||||
"dashmap",
|
||||
"futures",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"parking_lot",
|
||||
"serial_test_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serial_test_derive"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "079a83df15f85d89a68d64ae1238f142f172b1fa915d0d76b26a7cba1b659a69"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6528351c9bc8ab22353f9d776db39a20288e8d6c37ef8cfe3317cf875eecfc2d"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.13"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -248,28 +292,13 @@ version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.45.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
|
||||
dependencies = [
|
||||
"windows-targets 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.0",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -278,28 +307,13 @@ version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.42.2",
|
||||
"windows_aarch64_msvc 0.42.2",
|
||||
"windows_i686_gnu 0.42.2",
|
||||
"windows_i686_msvc 0.42.2",
|
||||
"windows_x86_64_gnu 0.42.2",
|
||||
"windows_x86_64_gnullvm 0.42.2",
|
||||
"windows_x86_64_msvc 0.42.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.48.0",
|
||||
"windows_aarch64_msvc 0.48.0",
|
||||
"windows_i686_gnu 0.48.0",
|
||||
"windows_i686_msvc 0.48.0",
|
||||
"windows_x86_64_gnu 0.48.0",
|
||||
"windows_x86_64_gnullvm 0.48.0",
|
||||
"windows_x86_64_msvc 0.48.0",
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
"windows_x86_64_msvc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -308,80 +322,38 @@ version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.42.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
|
||||
|
10
Cargo.toml
10
Cargo.toml
@ -1,14 +1,8 @@
|
||||
[package]
|
||||
name = "burritos"
|
||||
rust-version = "1.64"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[registries.crates-io]
|
||||
protocol = "sparse"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.2.1", features = ["derive"] }
|
||||
libc = { version = "0.2.139", features = ["extra_traits"] }
|
||||
serial_test = "*"
|
55
Makefile
55
Makefile
@ -1,55 +0,0 @@
|
||||
TOPDIR=.
|
||||
include $(TOPDIR)/Makefile.config
|
||||
|
||||
#
|
||||
# Demo vars
|
||||
#
|
||||
FLAGS=--offline -r --
|
||||
CARGO=RUSTFLAGS=-Awarnings cargo run ${FLAGS}
|
||||
|
||||
|
||||
|
||||
all: dumps user_lib instruction_tests syscall
|
||||
#
|
||||
# Main targets
|
||||
#
|
||||
|
||||
instruction_tests:
|
||||
$(MAKE) build -C test/riscv_instructions/
|
||||
|
||||
dumps:
|
||||
$(MAKE) dumps -C test/riscv_instructions/
|
||||
mkdir -p ${TOPDIR}/target/dumps/
|
||||
find . -path ${TOPDIR}/target -prune -o -name '*.dump' -exec mv {} ${TOPDIR}/target/dumps/ \;
|
||||
|
||||
user_lib:
|
||||
$(MAKE) -C userlib/
|
||||
|
||||
syscall: user_lib
|
||||
$(MAKE) build -C test/syscall_tests/
|
||||
$(RM) test/syscall_tests/*.o
|
||||
mkdir -p ${TOPDIR}/target/guac/
|
||||
find . -name '*.guac' -exec mv {} ${TOPDIR}/target/guac/ 2> /dev/null \;
|
||||
|
||||
clean:
|
||||
$(MAKE) clean -C userlib/
|
||||
$(MAKE) clean -C test/
|
||||
$(RM) -rf $(TOPDIR)/target
|
||||
|
||||
#
|
||||
# Demo targets
|
||||
#
|
||||
halt: syscall
|
||||
${CARGO} -x ./target/guac/halt.guac -d3
|
||||
|
||||
pc: syscall
|
||||
${CARGO} -x ./target/guac/producteur_consommateur.guac -d2
|
||||
|
||||
matmult: syscall
|
||||
${CARGO} -x ./target/guac/matmult.guac -d2
|
||||
|
||||
lock: syscall
|
||||
${CARGO} -x ./target/guac/lock.guac -d2
|
||||
|
||||
prints: syscall
|
||||
${CARGO} -x ./target/guac/prints.guac -d2
|
70
README.md
70
README.md
@ -6,76 +6,6 @@ BurritOS (BurritOS Using Rust Really Improves The Operating System) is an educat
|
||||
|
||||
Based on [NachOS](https://homes.cs.washington.edu/~tom/nachos/) (Copyright (c) 1992-1993 The Regents of the University of California. All rights reserved.)
|
||||
|
||||
## Progress overview
|
||||
|
||||

|
||||
|
||||
Currently, the BurritOS project simulator contains a RISC-V processing unit supporting all 47 base instructions plus multiplication and 32bit floating point operations. RAM and the interrupt controller are also integrated. Both the memory management unit and the Disk are written but not tested nor integrated yet.
|
||||
|
||||
On the kernel side, synchronization primitives and scheduling logic are all implemented except for Conditions.
|
||||
|
||||
## Build instructions
|
||||
|
||||
To build in release mode:
|
||||
|
||||
```
|
||||
$ cargo build -r
|
||||
```
|
||||
|
||||
To build in development mode:
|
||||
|
||||
```
|
||||
$ cargo build
|
||||
```
|
||||
|
||||
The generated executable can then be found in the `./target` directory.
|
||||
|
||||
## Running BurritOS
|
||||
|
||||
*In the following examples, BurritOS is started by directly invoking its executable. However, replacing this direct invocation by `cargo run -- <PARAMETERS>` would garner the same result.*
|
||||
|
||||
As it stands, BurritOS does not include a virtual console nor a shell for dynamic user interaction. Thus, programs need to be manually loaded into the system memory. To perform this operation:
|
||||
|
||||
```
|
||||
$ ./burritos --executable <PATH>
|
||||
```
|
||||
|
||||
## Help
|
||||
|
||||
BurritOS provides a succinct manual. To display this manual:
|
||||
|
||||
```
|
||||
$ ./burritos --help
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Documentation for all components of the BurritOS project can be generated using the following command:
|
||||
|
||||
```
|
||||
$ cargo doc
|
||||
```
|
||||
|
||||
The generated web documentation can be found in the `./target/doc` directory.
|
||||
|
||||
## Tests
|
||||
|
||||
BurritOS is unit tested using the cargo provided testing framework. However, some tests, most notably the Machine tests, require access to a few files **which need to be generated first**.
|
||||
|
||||
To generate test files:
|
||||
|
||||
```
|
||||
$ make all
|
||||
```
|
||||
|
||||
Afterwards, tests can be run with:
|
||||
|
||||
```
|
||||
$ cargo test
|
||||
```
|
||||
|
||||
All make artifacts can be found in the `./target` directory.
|
||||
|
||||
## Authors
|
||||
|
||||
Amaury Brodu, Abdelmajid El Bahri, François Autin, Quentin Legot, Baptiste Meauze, Gabriel Moysan, Rémi Rativel, Samy Solhi
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 100 KiB |
12
build.rs
12
build.rs
@ -1,12 +0,0 @@
|
||||
//! Build script for BurritOS.
|
||||
//!
|
||||
//! Moves files from the assets folder to the target directory
|
||||
//! and runs `make all`.
|
||||
|
||||
use std::process::Command;
|
||||
|
||||
fn main() {
|
||||
let mut make_all = Command::new("make");
|
||||
make_all.arg("all");
|
||||
println!("{:?}", make_all.output().unwrap());
|
||||
}
|
40
burritos.cfg
40
burritos.cfg
@ -1,40 +0,0 @@
|
||||
##################################################
|
||||
# BurritOS configuration file
|
||||
##################################################
|
||||
|
||||
NumPhysPages = 40000000
|
||||
UserStackSize = 4096
|
||||
MaxFileNameSize = 256
|
||||
NumDirEntries = 30
|
||||
NumPortLoc = 32009
|
||||
NumPortDist = 32010
|
||||
ProcessorFrequency = 100
|
||||
SectorSize = 128
|
||||
PageSize = 128
|
||||
MaxVirtPages = 200000
|
||||
|
||||
# String values
|
||||
###############
|
||||
# WARNING: Copying can be very slow
|
||||
# because the system transferts data
|
||||
# by 10 byte chunks. The transfer file
|
||||
# can be set by changing the transfersize
|
||||
# constant in fstest.rs.
|
||||
|
||||
TargetMachineName = localhost
|
||||
FileToCopy = test/halt /halt
|
||||
FileToCopy = test/hello /hello
|
||||
FileToCopy = test/sort /sort
|
||||
FileToCopy = test/shell /shell
|
||||
|
||||
# Boolean values
|
||||
################
|
||||
UseACIA = None
|
||||
PrintStat = 1
|
||||
FormatDisk = 1
|
||||
ListDir = 1
|
||||
PrintFileSyst = 0
|
||||
|
||||
ProgramToRun = /sort
|
||||
|
||||
|
113
src/drivers/drv_disk.rs
Normal file
113
src/drivers/drv_disk.rs
Normal file
@ -0,0 +1,113 @@
|
||||
use crate::Disk;
|
||||
|
||||
/// driver disk
|
||||
pub struct DrvDisk {
|
||||
disk: Disk,
|
||||
}
|
||||
|
||||
impl DrvDisk {
|
||||
///initialize the disk ok the current driver
|
||||
pub fn init_drv_disk(disk: Disk) -> DrvDisk {
|
||||
DrvDisk { disk: disk }
|
||||
}
|
||||
|
||||
/// read inside the disk
|
||||
///
|
||||
/// ### Parameters
|
||||
///
|
||||
/// - **self** driver disk
|
||||
/// - **sector_number** sector where to read the data
|
||||
/// - **data** where the readed data will be stored
|
||||
pub fn read_sector(&mut self, sector_number: i32, data: &mut [u8]) {
|
||||
match Disk::read_request(&mut self.disk, sector_number, data) {
|
||||
Err(e) => println!("{:?}", e),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
/// write inside the disk
|
||||
///
|
||||
/// ### Parameters
|
||||
///
|
||||
/// - **self** driver disk
|
||||
/// - **sector_number** sector where to write the data
|
||||
/// - **data** where the data to write is stored
|
||||
pub fn write_sector(&mut self, sector_number: i32, data: &[u8]) {
|
||||
match Disk::write_request(&mut self.disk, sector_number, data) {
|
||||
Err(e) => println!("{:?}", e),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
/// read inside the disk
|
||||
///
|
||||
/// ### Parameters
|
||||
///
|
||||
/// - **self** driver disk
|
||||
/// - **sector_number** sector where to read the data
|
||||
/// - **data** where the readed data will be stored
|
||||
pub fn read_multiple_sector(&mut self, sector_number: i32, data: &mut [u8]) {
|
||||
match Disk::read_multiple_request(&mut self.disk, sector_number, data) {
|
||||
Err(e) => println!("{:?}", e),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
/// write inside the disk
|
||||
///
|
||||
/// ### Parameters
|
||||
///
|
||||
/// - **self** driver disk
|
||||
/// - **sector_number** sector where to write the data
|
||||
/// - **data** where the data to write is stored
|
||||
pub fn write_multiple_sector(&mut self, sector_number: i32, data: &[u8]) {
|
||||
match Disk::write_multiple_request(&mut self.disk, sector_number, data) {
|
||||
Err(e) => println!("{:?}", e),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use serial_test::serial;
|
||||
|
||||
use super::DrvDisk;
|
||||
use crate::Disk;
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_init_driver() {
|
||||
let disk = Disk::init_disk();
|
||||
let _ = DrvDisk::init_drv_disk(disk);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_read_write_drv_disk() {
|
||||
let disk = Disk::init_disk();
|
||||
let mut drv_disk = DrvDisk::init_drv_disk(disk);
|
||||
|
||||
let mut data = Vec::new();
|
||||
data.push(0 as u8);
|
||||
data.push(0 as u8);
|
||||
data.push(0 as u8);
|
||||
data.push(0 as u8);
|
||||
|
||||
let mut data1 = Vec::new();
|
||||
data1.push(1 as u8);
|
||||
data1.push(1 as u8);
|
||||
data1.push(1 as u8);
|
||||
data1.push(1 as u8);
|
||||
|
||||
let mut data2: Vec<u8> = Vec::new();
|
||||
|
||||
drv_disk.write_sector(0, &mut data);
|
||||
drv_disk.write_sector(1, &mut data1);
|
||||
drv_disk.read_sector(1, &mut data2);
|
||||
|
||||
assert_eq!(data1, data2);
|
||||
assert_ne!(data, data1);
|
||||
assert_ne!(data, data2);
|
||||
}
|
||||
}
|
1
src/drivers/mod.rs
Normal file
1
src/drivers/mod.rs
Normal file
@ -0,0 +1 @@
|
||||
pub mod drv_disk;
|
178
src/filesys/directory.rs
Normal file
178
src/filesys/directory.rs
Normal file
@ -0,0 +1,178 @@
|
||||
const FILE_NAME_MAX_LEN: i32 = 80;
|
||||
const NUM_DIR_ENTRIES: i32 = 30;
|
||||
|
||||
use crate::drivers::drv_disk::DrvDisk;
|
||||
use crate::kernel::mgerror::ErrorCode;
|
||||
use std::mem;
|
||||
|
||||
use super::openfile::OpenFile;
|
||||
pub struct DirectoryEntry {
|
||||
in_use: bool,
|
||||
sector: i32,
|
||||
name: [char; FILE_NAME_MAX_LEN as usize],
|
||||
}
|
||||
|
||||
impl DirectoryEntry {
|
||||
fn to_bytes(&self) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
bytes.push(self.in_use as u8);
|
||||
bytes.extend_from_slice(&self.sector.to_le_bytes());
|
||||
for ch in &self.name {
|
||||
bytes.extend_from_slice(&ch.encode_utf8(&mut [0; 4]).as_bytes());
|
||||
}
|
||||
bytes
|
||||
}
|
||||
}
|
||||
|
||||
fn entries_to_bytes(entries: &Vec<DirectoryEntry>) -> Vec<u8> {
|
||||
let mut bytes = Vec::new();
|
||||
for entry in entries {
|
||||
bytes.extend_from_slice(&entry.to_bytes());
|
||||
}
|
||||
bytes
|
||||
}
|
||||
|
||||
pub struct Directory {
|
||||
table_size: i32,
|
||||
table: Vec<DirectoryEntry>,
|
||||
}
|
||||
|
||||
impl Directory {
|
||||
/// Initialize a directory; initially, the directory is completely
|
||||
/// empty. If the disk is being formatted, an empty directory
|
||||
/// is all we need, but otherwise, we need to call FetchFrom in order
|
||||
/// to initialize it from disk.
|
||||
///
|
||||
/// ### Parameters
|
||||
/// - **size** is the number of entries in the directory
|
||||
pub fn init_directory(size: i32) -> Directory {
|
||||
let mut tmp:Vec<DirectoryEntry> = Vec::new();
|
||||
for i in 0..size {
|
||||
tmp[i as usize].in_use = false;
|
||||
}
|
||||
Directory {
|
||||
table_size: size,
|
||||
table: tmp,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fetch_from(&self,mut file: OpenFile, mut drv_disk: DrvDisk) {
|
||||
OpenFile::read_at(&mut drv_disk, &mut file, &mut entries_to_bytes(&self.table), self.table_size * mem::size_of::<DirectoryEntry>() as i32, 0);
|
||||
}
|
||||
|
||||
pub fn write_back(&self,mut file: OpenFile, mut drv_disk: DrvDisk) {
|
||||
OpenFile::write_at(&mut drv_disk,&mut file,&mut entries_to_bytes(&self.table),self.table_size * mem::size_of::<DirectoryEntry>() as i32,0);
|
||||
}
|
||||
|
||||
/// Look up file name in directory.
|
||||
///
|
||||
/// return its location in the table of directory entries,
|
||||
/// ERROR if the name isn't in the directory.
|
||||
///
|
||||
/// ### Parameters
|
||||
/// - **name** the file name to look up
|
||||
pub fn find_index(&self, name: String) -> i32 {
|
||||
for i in 0..self.table_size {
|
||||
if self.table[i as usize].in_use && self.table[i as usize].name.starts_with(&self.table[i as usize].name) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/// Look up file name in directory, and return the disk sector number
|
||||
/// where the file's header is stored. Return ERROR if the name isn't
|
||||
/// in the directory.
|
||||
///
|
||||
/// return the disk sector number where the file's header is stored
|
||||
/// or ERROR if the name isn't in the directory.
|
||||
///
|
||||
/// ### Parameters
|
||||
///
|
||||
/// - **name** the file name to look up
|
||||
pub fn find(&self, name: String) -> i32 {
|
||||
let i = self.find_index(name);
|
||||
if i != -1 {
|
||||
return self.table[i as usize].sector;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
///Add a file into the directory.
|
||||
///
|
||||
/// ### Parameters
|
||||
/// - **name** the name of the file being added
|
||||
/// - **new_sector** the disk sector containing the added file's header
|
||||
///
|
||||
/// return NO_ERROR, ALREADY_IN_DIRECTORY or NOSPACE_IN_DIRECTORY.
|
||||
pub fn add(&mut self, name: String, new_sector: i32) -> Result<(), ErrorCode> {
|
||||
if self.find_index(name) != -1 {
|
||||
return Err(ErrorCode::AlreadyInDirectory);
|
||||
}
|
||||
for i in 0..self.table_size {
|
||||
if !self.table[i as usize].in_use {
|
||||
self.table[i as usize].in_use = true;
|
||||
self.table[i as usize].name.starts_with(&self.table[i as usize].name);
|
||||
self.table[i as usize].sector = new_sector;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
return Err(ErrorCode::NospaceInDirectory);
|
||||
|
||||
}
|
||||
|
||||
/// Remove a file name from the directory.
|
||||
///
|
||||
/// ### Parameters
|
||||
/// - **name** the file name to be removed
|
||||
/// return NO_ERROR, or INEXIST_DIRECTORY_ERROR
|
||||
|
||||
pub fn remove(&mut self, name: String) -> Result<(), ErrorCode> {
|
||||
let i = self.find_index(name);
|
||||
if i == -1 {
|
||||
return Err(ErrorCode::InexistDirectoryError);
|
||||
}
|
||||
self.table[i as usize].in_use = false;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// List all the file names in the directory.(recursive function)
|
||||
///
|
||||
/// ### Parameters
|
||||
/// - **name** the name of the Dir to print
|
||||
/// - **depth** the depth in the recursion (to print a nice hierarchy with spaces)
|
||||
pub fn list(&self, name: char, depth: i32) {
|
||||
let dir = Directory::init_directory(NUM_DIR_ENTRIES);
|
||||
|
||||
for i in 0..self.table_size {
|
||||
if self.table[i as usize].in_use {
|
||||
for j in 0..depth {
|
||||
if j<depth-3 {
|
||||
print!(" ");
|
||||
}else if j==depth-3 {
|
||||
print!("+");
|
||||
}else if j>depth-3 {
|
||||
print!("-");
|
||||
}
|
||||
}
|
||||
print!("{:#?}",self.table[i as usize].name);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Return true if the directory is empty
|
||||
pub fn is_empty(&mut self) -> bool {
|
||||
let mut empty = true;
|
||||
for i in 0..self.table_size {
|
||||
if self.table[i as usize].in_use {
|
||||
empty = false;
|
||||
}
|
||||
}
|
||||
empty
|
||||
}
|
||||
|
||||
|
||||
}
|
262
src/filesys/filehdr.rs
Normal file
262
src/filesys/filehdr.rs
Normal file
@ -0,0 +1,262 @@
|
||||
use crate::drivers::drv_disk;
|
||||
use crate::filesys::openfile;
|
||||
use crate::{simulator::disk::SECTOR_SIZE, utility::bitmap::BitMap};
|
||||
pub const MAX_HEADER_SECTORS: i32 = 32;
|
||||
pub const DATAS_IN_FIRST_SECTOR: i32 = (SECTOR_SIZE - 5 * 8) /8 ;
|
||||
pub const DATAS_IN_SECTOR: i32 = (SECTOR_SIZE - 1 * 8) /8;
|
||||
pub const MAX_DATA_SECTORS: i32 = (MAX_HEADER_SECTORS-1) * DATAS_IN_SECTOR + DATAS_IN_FIRST_SECTOR;
|
||||
pub const MAX_FILE_LENGTH: i32 = (MAX_DATA_SECTORS) * SECTOR_SIZE;
|
||||
|
||||
use crate::DrvDisk;
|
||||
use crate::Disk;
|
||||
|
||||
use std::mem;
|
||||
|
||||
use super::openfile::OpenFile;
|
||||
|
||||
pub struct FileHdr {
|
||||
is_dir: i32,
|
||||
num_bytes: i32,
|
||||
num_sectors: i32,
|
||||
data_sectors: Vec<i32>,
|
||||
num_header_sectors: i32,
|
||||
header_sectors: [i32; MAX_HEADER_SECTORS as usize],
|
||||
}
|
||||
|
||||
impl FileHdr {
|
||||
|
||||
|
||||
pub fn init_file_hdr() -> FileHdr {
|
||||
FileHdr {
|
||||
is_dir: 0,
|
||||
num_bytes: 0,
|
||||
num_sectors: 0,
|
||||
data_sectors: Vec::new(),
|
||||
num_header_sectors: 0,
|
||||
header_sectors: [0;MAX_HEADER_SECTORS as usize],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn allocate(&mut self, mut free_map: BitMap, file_size: i32) -> bool {
|
||||
self.num_bytes = file_size;
|
||||
if file_size > MAX_FILE_LENGTH {
|
||||
panic!("file size is too long");
|
||||
}
|
||||
|
||||
self.num_sectors = openfile::div_round_up(file_size, SECTOR_SIZE);
|
||||
self.num_header_sectors = openfile::div_round_up(self.num_sectors-DATAS_IN_FIRST_SECTOR, DATAS_IN_SECTOR);
|
||||
|
||||
// Check if there is enough free sectors for both of them
|
||||
if free_map.num_clear() < (self.num_sectors + self.num_header_sectors) as i32 {
|
||||
return false;
|
||||
}
|
||||
|
||||
for i in 0..self.num_header_sectors {
|
||||
self.header_sectors[i as usize] = free_map.find();
|
||||
}
|
||||
|
||||
self.data_sectors = vec![0; MAX_DATA_SECTORS as usize];
|
||||
for i in 0..self.num_sectors {
|
||||
self.data_sectors[i as usize] = free_map.find();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn re_allocate(&mut self, free_map: &mut BitMap, old_file_size: i32, new_file_size: i32) -> bool {
|
||||
|
||||
let mut new_num_sectors = openfile::div_round_up(new_file_size, SECTOR_SIZE) - self.num_sectors;
|
||||
self.num_bytes = new_file_size;
|
||||
|
||||
let mut new_num_header_sectors = openfile::div_round_up(self.num_sectors-DATAS_IN_FIRST_SECTOR, DATAS_IN_SECTOR) - self.num_header_sectors;
|
||||
|
||||
assert!(new_file_size <= MAX_FILE_LENGTH);
|
||||
|
||||
if free_map.num_clear() < (new_num_sectors + new_num_header_sectors) as i32{
|
||||
return false;
|
||||
}
|
||||
|
||||
for i in 0..new_num_header_sectors {
|
||||
self.header_sectors[(i + self.num_header_sectors)as usize] = free_map.find();
|
||||
}
|
||||
|
||||
for i in 0..new_num_sectors {
|
||||
self.data_sectors[(i + self.num_sectors) as usize] = free_map.find();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
pub fn deallocate(&mut self, mut free_map: BitMap) {
|
||||
for i in 0..self.num_sectors {
|
||||
assert!(free_map.test(self.data_sectors[i as usize] as usize));
|
||||
free_map.clear(self.data_sectors[i as usize]as usize);
|
||||
}
|
||||
|
||||
for i in 0..self.num_header_sectors {
|
||||
assert!(free_map.test(self.header_sectors[i as usize] as usize));
|
||||
free_map.clear(self.header_sectors[i as usize]as usize);
|
||||
}
|
||||
}
|
||||
|
||||
///Fetch contents of file header from disk.
|
||||
pub fn fetch_from(&mut self, sector: i32, drv_disk: &mut DrvDisk) {
|
||||
//temporary buffer
|
||||
let mut sector_img = vec![0; SECTOR_SIZE as usize / std::mem::size_of::<i32>() ];
|
||||
let mut data_sectors = vec![0; MAX_DATA_SECTORS as usize];
|
||||
|
||||
// Fills the temporary buffer with zeros
|
||||
sector_img.fill(0);
|
||||
|
||||
// Read the header from the disk
|
||||
// and put it in the temporary buffer
|
||||
DrvDisk::read_sector(&mut drv_disk, sector, &mut sector_img);
|
||||
|
||||
// Set up the memory image of the file header
|
||||
// from the newly read buffer
|
||||
self.is_dir = sector_img[0] as i32;
|
||||
self.num_bytes = sector_img[1] as i32;
|
||||
self.num_sectors = sector_img[2] as i32;
|
||||
self.num_header_sectors = sector_img[3] as i32;
|
||||
|
||||
// Get the first header sector
|
||||
self.header_sectors[0] = *FileHdr::next_header_sector(sector_img);
|
||||
|
||||
// Get the number of the data sectors stored into
|
||||
// the header sector in disk
|
||||
for i in 4..DATAS_IN_SECTOR as usize {
|
||||
data_sectors[i - 4] = sector_img[i];
|
||||
}
|
||||
|
||||
// Get the other numbers of header sectors and data sectors
|
||||
for i in 0..self.num_header_sectors as usize {
|
||||
// Fill the temporary buffer with zeroes
|
||||
sector_img.fill(0);
|
||||
|
||||
DrvDisk::read_sector(&mut drv_disk, self.header_sectors[i], &mut sector_img);
|
||||
|
||||
for j in 0..DATAS_IN_SECTOR as usize {
|
||||
data_sectors[DATAS_IN_FIRST_SECTOR as usize + i * DATAS_IN_SECTOR as usize + j] = sector_img[j];
|
||||
}
|
||||
|
||||
// Make sure we don't go out of bounds
|
||||
if i + 1 < self.num_header_sectors as usize {
|
||||
self.header_sectors[i + 1] = *FileHdr::next_header_sector(&mut sector_img);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pub fn write_back(&self, sector: i32, drv_disk: &mut DrvDisk) {
|
||||
let mut sector_img = vec![0; DATAS_IN_SECTOR as usize];
|
||||
|
||||
// Fills the temporary buffer with zeroes
|
||||
sector_img.fill(0);
|
||||
|
||||
// Fills the header of the first header sector
|
||||
sector_img[0] = self.is_dir;
|
||||
sector_img[1] = self.num_bytes;
|
||||
sector_img[2] = self.num_sectors;
|
||||
sector_img[3] = self.num_header_sectors;
|
||||
|
||||
// Fills the number of the data sectors and the first header
|
||||
// sector in the temporary buffer
|
||||
for i in 4..DATAS_IN_SECTOR as usize {
|
||||
sector_img[i] = self.data_sectors[(i - 4)];
|
||||
}
|
||||
|
||||
// Write the first header sector into disk
|
||||
*FileHdr::next_header_sector(&mut sector_img) = self.header_sectors[0];
|
||||
let mut vec_u8: Vec<u8> = sector_img.iter().map(|&x| x as u8).collect();
|
||||
DrvDisk::write_sector(&mut drv_disk, sector, &mut vec_u8);
|
||||
|
||||
// Write the following header sectors into disk
|
||||
for i in 0..self.num_header_sectors {
|
||||
sector_img.fill(0);
|
||||
|
||||
for j in 0..DATAS_IN_SECTOR {
|
||||
sector_img[j as usize] = self.data_sectors[(j + DATAS_IN_FIRST_SECTOR + i * DATAS_IN_SECTOR) as usize];
|
||||
}
|
||||
|
||||
if i + 1 < self.num_header_sectors {
|
||||
*FileHdr::next_header_sector(&mut sector_img) = self.header_sectors[(i + 1) as usize];
|
||||
} else {
|
||||
*FileHdr::next_header_sector(&mut sector_img) = 0;
|
||||
}
|
||||
let mut vec_u8: Vec<u8> = sector_img.iter().map(|&x| x as u8).collect();
|
||||
DrvDisk::write_sector(&mut drv_disk, self.header_sectors[i as usize], &mut vec_u8);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_header_sector(hdr_sector: &mut [i32]) -> &mut i32 {
|
||||
let sector_size = SECTOR_SIZE as usize / std::mem::size_of::<i32>();
|
||||
&mut hdr_sector[sector_size - 1]
|
||||
}
|
||||
|
||||
pub fn byte_to_sector(&self,offset: usize) -> i32 {
|
||||
return self.data_sectors[ offset / SECTOR_SIZE as usize];
|
||||
}
|
||||
|
||||
pub fn file_length(&self) -> i32 {
|
||||
return self.num_bytes;
|
||||
}
|
||||
|
||||
pub fn change_file_length(&mut self, new_size: i32) {
|
||||
self.num_bytes = new_size;
|
||||
assert!(new_size <= MAX_FILE_LENGTH);
|
||||
|
||||
}
|
||||
|
||||
pub fn max_file_length(&self) -> i32 {
|
||||
return self.num_sectors * SECTOR_SIZE;
|
||||
}
|
||||
|
||||
pub fn print(&self, drv_disk: &mut DrvDisk) {
|
||||
let mut data = Vec::new();
|
||||
println!("FileHeader contents. File size: {}. File blocks:", self.num_bytes);
|
||||
for i in 0..self.num_sectors {
|
||||
print!("{} ", self.data_sectors[i as usize]);
|
||||
}
|
||||
println!("\nFile contents:");
|
||||
let mut k = 0;
|
||||
for i in 0..self.num_sectors {
|
||||
drv_disk.read_sector(self.data_sectors[i as usize], &mut data);
|
||||
for j in 0..SECTOR_SIZE.min(self.num_bytes - k) {
|
||||
let c = data[j as usize];
|
||||
if c >= 0x20 && c <= 0x7E {
|
||||
print!("{}", c as char);
|
||||
} else {
|
||||
print!("\\{:x}", c);
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
println!("");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_dir(&self) -> bool{
|
||||
return self.is_dir == 1;
|
||||
}
|
||||
|
||||
pub fn set_file(&mut self) {
|
||||
self.is_dir = 0;
|
||||
}
|
||||
|
||||
pub fn set_dir(&mut self) {
|
||||
self.is_dir = 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
#[test]
|
||||
fn test_allocate() {
|
||||
|
||||
}
|
||||
}
|
508
src/filesys/filesys.rs
Normal file
508
src/filesys/filesys.rs
Normal file
@ -0,0 +1,508 @@
|
||||
use crate::{kernel::mgerror::ErrorCode, simulator::disk, utility::bitmap::BitMap, drivers::drv_disk::DrvDisk, filesys::openfile};
|
||||
|
||||
use super::{filehdr::FileHdr, openfile::OpenFile, directory::Directory};
|
||||
|
||||
pub const ERROR: i32 = -1;
|
||||
pub const FREE_MAP_SECTOR: i32 = 0;
|
||||
pub const DIRECTORY_SECTOR: i32 = 1;
|
||||
pub const NUM_DIR_ENTRIES: i32 = 10;
|
||||
pub const DIRECTORY_FILE_SIZE: i32 = 100; //std::mem::size_of<Directory>() * NUM_DIR_ENTRIES;
|
||||
pub const FREE_MAP_FILE_PATH : &str = "free_map_file";
|
||||
pub const DIRECTORY_FILE_PATH : &str = "directory_file";
|
||||
|
||||
|
||||
/// decompose the name of the first directory
|
||||
/// of the path given in argument, as well as the remainder of the path.
|
||||
/// if we call decompname("/bin/new/halt", head, tail), we get "bin" in head and "/new/halt" in tail
|
||||
/// returns true if everything goes ok, otherwise, return false.
|
||||
/// head and tail MUST be large enough to contain the resulting strings
|
||||
///
|
||||
/// ### parameters
|
||||
///
|
||||
/// - **orig_path** the pathname we want to decompose
|
||||
/// - **head** the first element in the path (for instance "bin")
|
||||
/// - **tail** the remainder of the string (for instance "/new/halt")
|
||||
pub fn decomp_name(origin_path: String, head: &mut String, tail: &mut String) -> bool {
|
||||
let mut working_path = origin_path;
|
||||
|
||||
// remove the leading "/" if any
|
||||
if working_path.get(0..1).unwrap_or_else(|| "").eq("/") {
|
||||
working_path = (&working_path[1..]).to_string();
|
||||
}
|
||||
|
||||
if working_path.contains("/") {
|
||||
let index = working_path.find("/").expect("shouldn't happen");
|
||||
*head = working_path[..index].to_string();
|
||||
*tail = working_path[index..].to_string();
|
||||
return true;
|
||||
} else {
|
||||
*head = "".to_string();
|
||||
*tail = origin_path;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// this function takes a complete pathname and returns
|
||||
/// the disc sector of the fileheader of the directory.
|
||||
/// return -1 in case of error
|
||||
///
|
||||
/// IMPORTANT WARNING : name is modified : after the execution, name
|
||||
/// contains only the name of the file
|
||||
/// (e.g. if we called find_dir("/bin/halt") then name is "halt" after the execution,
|
||||
/// the function returns the sector number of the directory "/bin").
|
||||
///
|
||||
/// Actually, this function does not support complete pathnames
|
||||
/// This is one of the objectives of the file system assignment
|
||||
///
|
||||
/// ### parameters
|
||||
///
|
||||
/// - **name** is the complete name (relatively to the root directory).
|
||||
/// its content will be modified!
|
||||
pub fn find_dir(name: &mut String, file_sys : Filesys, drv_disk : DrvDisk) -> i32 {
|
||||
let directory = Directory::init_directory(NUM_DIR_ENTRIES);
|
||||
directory.fetch_from(file_sys.get_dir_file(), drv_disk);
|
||||
|
||||
let mut sector = DIRECTORY_SECTOR;
|
||||
let mut dirname = String::from("");
|
||||
let mut reminder = String::from("");
|
||||
|
||||
while decomp_name(name.to_string(), &mut dirname, &mut reminder) {
|
||||
*name = reminder;
|
||||
|
||||
// Get the sector of the file/directory corresponding to 'name'
|
||||
sector = directory.find(dirname);
|
||||
if sector < 0 {
|
||||
return -1; // This file/directory does not exist
|
||||
}
|
||||
|
||||
let file = OpenFile::open_file(sector);
|
||||
if file.hdr.is_dir() {
|
||||
directory.fetch_from(file, drv_disk);
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
*name = reminder;
|
||||
|
||||
sector
|
||||
}
|
||||
|
||||
pub struct Filesys {
|
||||
pub free_map_file: OpenFile, //Bit map of free disk blocks, represented as a file
|
||||
pub directory_file: OpenFile, //"Root" directory -- list of file names, represented as a file
|
||||
}
|
||||
|
||||
impl Filesys {
|
||||
/// Initialize the file system. If format = true, the disk has
|
||||
/// nothing on it, and we need to initialize the disk to contain
|
||||
/// an empty directory, and a bitmap of free sectors (with almost but
|
||||
/// not all of the sectors marked as free).
|
||||
///
|
||||
/// If format = false, we just have to open the files
|
||||
/// representing the bitmap and the directory.
|
||||
///
|
||||
/// ### parameters
|
||||
///
|
||||
/// - **format** should we initialize the disk?
|
||||
pub fn init_filesys(format: bool, mut drv_disk : DrvDisk) -> Filesys {
|
||||
if format {
|
||||
let mut free_map = BitMap::init_bitmap(disk::NUM_SECTORS as usize);
|
||||
let mut directory = Directory::init_directory(NUM_DIR_ENTRIES);
|
||||
|
||||
free_map.mark(FREE_MAP_SECTOR as usize);
|
||||
free_map.mark(DIRECTORY_SECTOR as usize);
|
||||
|
||||
let mut map_header: FileHdr;
|
||||
let mut dir_header: FileHdr;
|
||||
|
||||
map_header.allocate(free_map, FREE_MAP_SECTOR);
|
||||
dir_header.allocate(free_map, DIRECTORY_FILE_SIZE);
|
||||
|
||||
dir_header.set_dir();
|
||||
|
||||
map_header.write_back(FREE_MAP_SECTOR, &mut drv_disk);
|
||||
dir_header.write_back(DIRECTORY_SECTOR, &mut drv_disk);
|
||||
|
||||
let mut free_map_file = OpenFile::open_file(FREE_MAP_SECTOR);
|
||||
let directory_file = OpenFile::open_file(DIRECTORY_SECTOR);
|
||||
|
||||
free_map.write_back(&mut free_map_file, &mut drv_disk);
|
||||
directory.write_back(directory_file, drv_disk);
|
||||
|
||||
Filesys {
|
||||
free_map_file,
|
||||
directory_file,
|
||||
}
|
||||
} else {
|
||||
Filesys {
|
||||
free_map_file: OpenFile::open_file(FREE_MAP_SECTOR),
|
||||
directory_file: OpenFile::open_file(DIRECTORY_SECTOR),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a file in the BurritOS file system (similar to UNIX create).
|
||||
/// Since we can't increase the size of files dynamically, we have
|
||||
/// to give Create the initial size of the file.
|
||||
//
|
||||
/// The steps to create a file are:
|
||||
/// Make sure the file doesn't already exist
|
||||
/// Allocate a sector for the file header
|
||||
/// Allocate space on disk for the data blocks for the file
|
||||
/// Add the name to the directory
|
||||
/// Store the new file header on disk
|
||||
/// Flush the changes to the bitmap and the directory back to disk
|
||||
///
|
||||
/// Create fails if:
|
||||
/// file is already in directory
|
||||
/// no free space for file header
|
||||
/// no free entry for file in directory
|
||||
/// no free space for data blocks for the file
|
||||
///
|
||||
/// Note that this implementation assumes there is no concurrent access
|
||||
/// to the file system!
|
||||
///
|
||||
/// ### parameters
|
||||
///
|
||||
/// - **name** is the name of file to be created (NOT MODIFIED)
|
||||
/// - **initialSize** is the size of file to be created
|
||||
pub fn create(&mut self, name: String, initial_size: i32, mut drv_disk : DrvDisk) -> Result<(), ErrorCode> {
|
||||
//lock.acquire();
|
||||
|
||||
let mut dir_name = name.clone();
|
||||
|
||||
let dir_sector = find_dir(&mut dir_name, *self, drv_disk);
|
||||
if dir_sector == ERROR {
|
||||
//lock.release();
|
||||
return Err(ErrorCode::InexistFileError);
|
||||
}
|
||||
|
||||
let dir_file = OpenFile::open_file(dir_sector);
|
||||
let mut directory = Directory::init_directory(NUM_DIR_ENTRIES);
|
||||
directory.fetch_from(dir_file, drv_disk);
|
||||
|
||||
if directory.find(dir_name) != ERROR {
|
||||
//lock.release();
|
||||
return Err(ErrorCode::AlreadyInDirectory);
|
||||
}
|
||||
|
||||
// Get the freemap from the disk
|
||||
let mut free_map = BitMap::init_bitmap(disk::NUM_SECTORS as usize);
|
||||
free_map.fetch_from(&mut self.free_map_file, &mut drv_disk);
|
||||
|
||||
// Find a sector to hold the file header
|
||||
let sector = free_map.find();
|
||||
if sector == ERROR {
|
||||
//lock.release();
|
||||
return Err(ErrorCode::OutOfDisk);
|
||||
}
|
||||
|
||||
// Add the file in the directory
|
||||
let add_result = directory.add(dir_name, sector);
|
||||
match add_result {
|
||||
Err(e) => {
|
||||
//lock.release();
|
||||
return Err(e);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Indicate that this is a file, not a directory
|
||||
let mut hdr = FileHdr::init_file_hdr();
|
||||
hdr.set_file();
|
||||
|
||||
if !hdr.allocate(free_map, initial_size) {
|
||||
//lock.release();
|
||||
return Err(ErrorCode::OutOfDisk);
|
||||
}
|
||||
|
||||
// everthing worked, flush all changes back to disk
|
||||
hdr.write_back(sector, &mut drv_disk);
|
||||
directory.write_back(dir_file, drv_disk);
|
||||
free_map.write_back(&mut self.free_map_file, &mut drv_disk);
|
||||
|
||||
//lock.release();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Open a file for reading and writing.
|
||||
/// To open a file:
|
||||
/// Find the location of the file's header, using the directory
|
||||
/// Bring the header into memory
|
||||
///
|
||||
/// ### parameters
|
||||
///
|
||||
/// - **name** the text name of the file to be opened (NOT MODIFIED)
|
||||
pub fn open(self, name: String, file_sys : Filesys, drv_disk : DrvDisk) -> Option<OpenFile> {
|
||||
let mut open_file: OpenFile;
|
||||
let mut dir_name = name.clone();
|
||||
|
||||
// Find the directory containing the file
|
||||
let dir_sector = find_dir(&mut dir_name, file_sys, drv_disk);
|
||||
if dir_sector == ERROR {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Read the directory from disk
|
||||
let dir_file = OpenFile::open_file(dir_sector);
|
||||
let directory = Directory::init_directory(NUM_DIR_ENTRIES);
|
||||
directory.fetch_from(dir_file, drv_disk);
|
||||
|
||||
// Find the file in the directory
|
||||
let sector = directory.find(dir_name);
|
||||
if sector >= 0 {
|
||||
open_file = OpenFile::open_file(sector);
|
||||
open_file.name = name;
|
||||
if open_file.hdr.is_dir() {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
Some(open_file)
|
||||
}
|
||||
|
||||
/// Delete a file from the file system.
|
||||
/// This requires:
|
||||
/// Remove it from the directory
|
||||
/// Delete the space for its header
|
||||
/// Delete the space for its data blocks
|
||||
/// Write changes to directory, bitmap back to disk
|
||||
///
|
||||
/// ### parameters
|
||||
///
|
||||
/// - **name** the text name of the file to be removed (NOT MODIFIED)
|
||||
pub fn remove(&mut self, name: String, file_sys : Filesys, mut drv_disk : DrvDisk) -> Result<(), ErrorCode> {
|
||||
let mut dir_name = name.clone();
|
||||
|
||||
// Get the sector number of the parent directory
|
||||
let dir_sector = find_dir(&mut dir_name, file_sys, drv_disk);
|
||||
|
||||
// Check if the path is correct
|
||||
if dir_sector == ERROR {
|
||||
return Err(ErrorCode::InexistDirectoryError);
|
||||
}
|
||||
|
||||
// Fetch the directory from the disk
|
||||
let dir_file = OpenFile::open_file(dir_sector);
|
||||
let mut directory = Directory::init_directory(NUM_DIR_ENTRIES);
|
||||
directory.fetch_from(dir_file, drv_disk);
|
||||
|
||||
// Look for the file in the directory
|
||||
let sector = directory.find(dir_name);
|
||||
|
||||
// Look if we find the file in the directory
|
||||
if sector as i32 == ERROR {
|
||||
return Err(ErrorCode::InexistFileError);
|
||||
}
|
||||
|
||||
// Fetch the file header from disk
|
||||
let mut file_hdr: FileHdr;
|
||||
file_hdr.fetch_from(sector, &mut drv_disk);
|
||||
|
||||
// Do nothing if it's a directory
|
||||
if file_hdr.is_dir() {
|
||||
return Err(ErrorCode::NotAFile);
|
||||
}
|
||||
|
||||
// Get the freemap file from the disk
|
||||
let mut free_map = BitMap::init_bitmap(disk::NUM_SECTORS as usize);
|
||||
free_map.fetch_from(&mut self.free_map_file, &mut drv_disk);
|
||||
|
||||
// Indicate that sectors are deallocated in the freemap
|
||||
file_hdr.deallocate(free_map);
|
||||
free_map.clear(sector as usize);
|
||||
|
||||
// Remove the file from the directory
|
||||
directory.remove(dir_name);
|
||||
|
||||
// Flush everything to disk
|
||||
free_map.write_back(&mut self.free_map_file, &mut drv_disk);
|
||||
directory.write_back(dir_file, drv_disk);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// List all the files in the file system directory.
|
||||
pub fn list() {
|
||||
//TODO
|
||||
}
|
||||
|
||||
/// Print everything about the file system:
|
||||
/// the contents of the bitmap
|
||||
/// the contents of the directory
|
||||
/// for each file in the directory,
|
||||
/// the contents of the file header
|
||||
/// the data in the file
|
||||
pub fn print() {
|
||||
//TODO
|
||||
}
|
||||
|
||||
/*
|
||||
Temporairement, et pour des raison de test,
|
||||
Nous allons utiliser un fichier unix dans lequels
|
||||
Donc pour le moment ces fonctions renvoient le nom fichier contenant ces infos
|
||||
*/
|
||||
/// return the free map file (used by the open file table).
|
||||
///
|
||||
pub fn get_free_map_file() -> String {
|
||||
//return self.free_map_file.get_name();
|
||||
return String::from(FREE_MAP_FILE_PATH);
|
||||
}
|
||||
|
||||
/// return the base directory file (used by the open file table).
|
||||
pub fn get_dir_file() -> String {
|
||||
//return self.directory_file.get_name();
|
||||
return String::from(DIRECTORY_FILE_PATH);
|
||||
}
|
||||
|
||||
/// Create a directory in the Nachos file system (similar to UNIX create).
|
||||
/// A directory is a file containing a table of directory entries.
|
||||
///
|
||||
/// The steps to create a dirctory are:
|
||||
/// Make sure the name is valid and does not exist
|
||||
/// Create a file containing the directory entries
|
||||
///
|
||||
/// mkdir fails if:
|
||||
/// dir already exists
|
||||
/// no free space for file header
|
||||
/// no free entry for dir in directory
|
||||
/// no free space for data blocks for the file
|
||||
///
|
||||
/// Note that this implementation assumes there is no concurrent access
|
||||
/// to the file system!
|
||||
///
|
||||
/// ### parameters
|
||||
///
|
||||
/// - **name** is the name of directory to be created (NOT MODIFIED)
|
||||
pub fn mkdir(&mut self, name: String, file_sys : Filesys, mut drv_disk : DrvDisk) -> Result<(), ErrorCode> {
|
||||
let mut dir_name = name.clone();
|
||||
|
||||
// Lokk for the sector number of the parent directory
|
||||
let parent_sector = find_dir(&mut dir_name, file_sys, drv_disk);
|
||||
if parent_sector < 0 {
|
||||
return Err(ErrorCode::InexistDirectoryError);
|
||||
}
|
||||
|
||||
// Fetch it from disk
|
||||
let parent_dir_file = OpenFile::open_file(parent_sector);
|
||||
let mut parent_dir = Directory::init_directory(NUM_DIR_ENTRIES);
|
||||
parent_dir.fetch_from(parent_dir_file, drv_disk);
|
||||
|
||||
// Check that the directory does not exit yet
|
||||
if parent_dir.find(dir_name) >= 0 {
|
||||
return Err(ErrorCode::AlreadyInDirectory);
|
||||
}
|
||||
|
||||
// Get the freemap
|
||||
let mut free_map = BitMap::init_bitmap(disk::NUM_SECTORS as usize);
|
||||
free_map.fetch_from(&mut self.free_map_file, &mut drv_disk);
|
||||
|
||||
// Get a free sector for the file header
|
||||
let hdr_sector = free_map.find();
|
||||
if hdr_sector < 0 {
|
||||
return Err(ErrorCode::OutOfDisk);
|
||||
}
|
||||
|
||||
// Allocate free sectors for the directory contents
|
||||
let mut hdr: FileHdr;
|
||||
if !hdr.allocate(free_map, DIRECTORY_FILE_SIZE) {
|
||||
return Err(ErrorCode::OutOfDisk);
|
||||
}
|
||||
|
||||
// Add the directory in the parent directory
|
||||
let add_result = parent_dir.add(dir_name, hdr_sector);
|
||||
match add_result {
|
||||
Err(e) => {
|
||||
return Err(e);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// File header
|
||||
hdr.set_dir();
|
||||
hdr.write_back(hdr_sector, &mut drv_disk);
|
||||
|
||||
// New directory (initially empty)
|
||||
let new_dir_file = OpenFile::open_file(hdr_sector);
|
||||
let new_dir = Directory::init_directory(NUM_DIR_ENTRIES);
|
||||
new_dir.write_back(new_dir_file, drv_disk);
|
||||
|
||||
// Parent directory
|
||||
parent_dir.write_back(parent_dir_file, drv_disk);
|
||||
free_map.write_back(&mut self.free_map_file, &mut drv_disk);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Delete a directory from the file system.
|
||||
/// This requires:
|
||||
/// check the name is valid
|
||||
/// check the directory is empty
|
||||
/// Remove it from its directory
|
||||
/// Delete the space for its header
|
||||
/// Delete the space for its data blocks
|
||||
/// Write changes to directory, bitmap back to disk
|
||||
///
|
||||
/// ### parameters
|
||||
///
|
||||
/// - **name** the text name of the directory to be removed (NOT MODIFIED)
|
||||
pub fn rmdir(&mut self, name: String, file_sys : Filesys, mut drv_disk : DrvDisk) -> Result<(), ErrorCode> {
|
||||
let mut dir_name = name.clone();
|
||||
|
||||
// Get the sector number of the parent directory
|
||||
let parent_sector = find_dir(&mut dir_name, file_sys, drv_disk);
|
||||
if parent_sector < 0 {
|
||||
return Err(ErrorCode::InexistDirectoryError);
|
||||
}
|
||||
|
||||
// Fetch it from disk
|
||||
let parent_dir_file = OpenFile::open_file(parent_sector);
|
||||
let mut parent_dir = Directory::init_directory(NUM_DIR_ENTRIES);
|
||||
parent_dir.fetch_from(parent_dir_file, drv_disk);
|
||||
|
||||
// Check that the directory to be removed exist
|
||||
let the_dir_sector = parent_dir.find(dir_name);
|
||||
if the_dir_sector < 0 {
|
||||
return Err(ErrorCode::InexistDirectoryError);
|
||||
}
|
||||
|
||||
// Get its header
|
||||
let mut the_dir_header: FileHdr;
|
||||
the_dir_header.fetch_from(the_dir_sector, &mut drv_disk);
|
||||
|
||||
// Check that is is a directory
|
||||
if !the_dir_header.is_dir() {
|
||||
return Err(ErrorCode::NotADirectory);
|
||||
}
|
||||
|
||||
// Fetch its contents from the disk
|
||||
let the_dir_file = OpenFile::open_file(the_dir_sector);
|
||||
let mut the_dir = Directory::init_directory(NUM_DIR_ENTRIES);
|
||||
the_dir.fetch_from(the_dir_file, drv_disk);
|
||||
|
||||
// Check that is is empty
|
||||
if !the_dir.is_empty() {
|
||||
return Err(ErrorCode::DirectoryNotEmpty);
|
||||
}
|
||||
|
||||
// Get the freemap from disk
|
||||
let mut free_map = BitMap::init_bitmap(disk::NUM_SECTORS as usize);
|
||||
free_map.fetch_from(&mut self.free_map_file, &mut drv_disk);
|
||||
|
||||
// Deallocate the data sectors of the directory
|
||||
the_dir_header.deallocate(free_map);
|
||||
|
||||
// Deallocate the sector containing the directory header
|
||||
free_map.clear(the_dir_sector as usize);
|
||||
|
||||
// We remove the directory from its parent directory
|
||||
parent_dir.remove(dir_name);
|
||||
|
||||
// Flush everything to disk
|
||||
free_map.write_back(&mut self.free_map_file, &mut drv_disk);
|
||||
parent_dir.write_back(parent_dir_file, drv_disk);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
90
src/filesys/fsmisc.rs
Normal file
90
src/filesys/fsmisc.rs
Normal file
@ -0,0 +1,90 @@
|
||||
use std::{
|
||||
fs::File,
|
||||
io::{Read, Seek, SeekFrom},
|
||||
};
|
||||
|
||||
use crate::drivers::drv_disk::DrvDisk;
|
||||
|
||||
use super::{filesys::Filesys, openfile::OpenFile};
|
||||
|
||||
pub const TRANSFER_SIZE: usize = 10;
|
||||
|
||||
/// copy the contents of the UNIX file "from" to the BurritOS file "to"
|
||||
///
|
||||
/// `panic!` when the file from doesn't exist
|
||||
///
|
||||
/// ### parameters
|
||||
///
|
||||
/// - **from** file UNIX
|
||||
/// - **to** BurritOS file
|
||||
pub fn copy(from: String, to: String, file_sys : Filesys, mut drv_disk : DrvDisk) {
|
||||
let file_from_opt = File::options().read(true).open(from);
|
||||
let mut file_from: File;
|
||||
match file_from_opt {
|
||||
Err(e) => {
|
||||
panic!("Copy: couldn't open Unix file {}", from);
|
||||
}
|
||||
|
||||
Ok(f) => {
|
||||
file_from = f;
|
||||
}
|
||||
}
|
||||
|
||||
let mut file_length_buf = [0; 1];
|
||||
file_from.seek(SeekFrom::End(2));
|
||||
file_from.read(&mut file_length_buf);
|
||||
file_from.seek(SeekFrom::Start(0));
|
||||
let file_length = file_length_buf[0];
|
||||
|
||||
file_sys.create(to, file_length as i32, file_sys, drv_disk);
|
||||
let mut open_file_opt = file_sys.open(to, file_sys, drv_disk);
|
||||
let open_file : &mut OpenFile;
|
||||
match open_file_opt {
|
||||
Some(f) => {
|
||||
open_file = &mut f;
|
||||
},
|
||||
None => return
|
||||
}
|
||||
|
||||
let mut buffer = Vec::new();
|
||||
loop {
|
||||
let amount_read = file_from
|
||||
.read(&mut buffer)
|
||||
.expect("copy : couldn't read the UNIX file");
|
||||
OpenFile::write(&mut drv_disk, open_file, &mut buffer, TRANSFER_SIZE as i32);
|
||||
|
||||
if amount_read != TRANSFER_SIZE {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Print the contents of the BurritOS file "name".
|
||||
///
|
||||
/// `panic!` when the file name doesn't exist
|
||||
///
|
||||
/// ### parameters
|
||||
///
|
||||
/// - **name** of the BurritOS file
|
||||
pub fn print(name: String, file_sys : Filesys, mut drv_disk : DrvDisk) {
|
||||
let mut open_file_opt = file_sys.open(name, file_sys, drv_disk);
|
||||
let open_file : &mut OpenFile;
|
||||
match open_file_opt {
|
||||
Some(mut f) => {
|
||||
open_file = &mut f;
|
||||
},
|
||||
None => return
|
||||
}
|
||||
|
||||
let mut buffer = Vec::new();
|
||||
loop {
|
||||
let amount_read = OpenFile::read(&mut drv_disk, open_file, &mut buffer, TRANSFER_SIZE as i32);
|
||||
for i in 0..amount_read {
|
||||
print!("{:1x} ", buffer[i as usize]);
|
||||
}
|
||||
|
||||
if amount_read != TRANSFER_SIZE as i32 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
6
src/filesys/mod.rs
Normal file
6
src/filesys/mod.rs
Normal file
@ -0,0 +1,6 @@
|
||||
pub mod directory;
|
||||
pub mod filehdr;
|
||||
pub mod filesys;
|
||||
pub mod fsmisc;
|
||||
pub mod oftable;
|
||||
pub mod openfile;
|
153
src/filesys/oftable.rs
Normal file
153
src/filesys/oftable.rs
Normal file
@ -0,0 +1,153 @@
|
||||
use super::openfile::OpenFile;
|
||||
|
||||
|
||||
pub const MAX_OF_TABLE_ENTRY : i32 = 500;
|
||||
|
||||
pub struct OpenFileTableEntry {
|
||||
name : String,
|
||||
pub file : Option<OpenFile>,
|
||||
pub num_thread : i32,//number of threads havng that file open
|
||||
//lock : Lock,
|
||||
pub to_be_deleted : bool,//si mis a true, quand ce fichier n'est plus ouvert par aucun thread, il doit etre supprimé
|
||||
pub sector : i32,//secteur disque ou se situe le header de ce fichier
|
||||
}
|
||||
|
||||
pub struct OpenFileTable{
|
||||
table : Vec<Option<OpenFileTableEntry>>,
|
||||
nb_entry : i32//numero du prochain fichier ouvert/prochaine entrée
|
||||
}
|
||||
|
||||
impl OpenFileTableEntry {
|
||||
|
||||
pub fn new() -> OpenFileTableEntry {
|
||||
|
||||
OpenFileTableEntry {
|
||||
name : String::new(),
|
||||
file : None,
|
||||
num_thread : 1,
|
||||
to_be_deleted : false,
|
||||
sector : -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl OpenFileTable{
|
||||
|
||||
pub fn open(oft : &mut OpenFileTable, name : String)-> Option<OpenFile>{
|
||||
|
||||
let mut num : i32;
|
||||
let mut sector : i32;
|
||||
let mut dirsector : i32;
|
||||
let mut file_name = String::new();
|
||||
|
||||
//Impl le trait copy sur open file
|
||||
num = OpenFileTable::findl(oft, &name);
|
||||
|
||||
//no error
|
||||
if num != -1 {
|
||||
//the file is opened by another thread
|
||||
match oft.table[num as usize] {
|
||||
|
||||
Some(e) => {
|
||||
|
||||
if !e.to_be_deleted{
|
||||
e.num_thread += 1;
|
||||
|
||||
let new_file : OpenFile = OpenFile::open_file(e.sector);
|
||||
new_file.name = name.clone();
|
||||
|
||||
return Some(new_file);
|
||||
}
|
||||
else {
|
||||
return None;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
None => {
|
||||
println!("OpenFileTale :: open :: Error opening {}", name);
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//file is not opened yet
|
||||
else {
|
||||
if(oft.nb_entry != -1){
|
||||
//il reste de la place
|
||||
let mut new_entry : OpenFileTableEntry = OpenFileTableEntry::new();
|
||||
let mut open_file : OpenFile;
|
||||
let mut new_file : OpenFile;
|
||||
//on va creer, si fichier present sur disque, deux objet open_file,
|
||||
//open_file ira dans la table
|
||||
//new_file sera renvoyé
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
return None;
|
||||
|
||||
}
|
||||
|
||||
pub fn close(name : String){
|
||||
|
||||
}
|
||||
|
||||
pub fn file_lock(name : String){
|
||||
|
||||
}
|
||||
|
||||
pub fn file_release(name : String){
|
||||
|
||||
}
|
||||
|
||||
pub fn remove(name : String)-> i32{
|
||||
0
|
||||
}
|
||||
|
||||
pub fn next_entry(oft : &mut OpenFileTable)-> i32{
|
||||
let mut i = 0;
|
||||
|
||||
for entry in oft.table.iter(){
|
||||
match entry {
|
||||
None => {
|
||||
return i;
|
||||
}
|
||||
|
||||
_ => {
|
||||
i+=1;
|
||||
}
|
||||
}
|
||||
}
|
||||
0
|
||||
}
|
||||
|
||||
pub fn findl(oft : &mut OpenFileTable, name : &String)-> i32{
|
||||
let mut i = 0;
|
||||
|
||||
for entry in oft.table.iter(){
|
||||
|
||||
match entry {
|
||||
|
||||
Some(e) => {
|
||||
if e.name.eq(name){
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
i+=1
|
||||
}
|
||||
|
||||
-1
|
||||
}
|
||||
|
||||
}
|
156
src/filesys/openfile.rs
Normal file
156
src/filesys/openfile.rs
Normal file
@ -0,0 +1,156 @@
|
||||
|
||||
use crate::{simulator::{self, disk}, drivers::drv_disk::{DrvDisk, self}, utility::bitmap::BitMap};
|
||||
|
||||
use super::{filehdr::FileHdr, filesys::{Filesys, self}};
|
||||
|
||||
|
||||
pub const MAX_FILE_NAME_SIZE : i32 = 500;
|
||||
|
||||
pub struct OpenFile {
|
||||
pub name : String, // the file's
|
||||
pub hdr : FileHdr, // Header for this file
|
||||
pub seekPosition : i32, // Current position in the file (in byte)
|
||||
pub fSector : i32 // this file first sector on the disk (fichiers stockés de maniere contigue)
|
||||
}
|
||||
|
||||
//division arrondie vers le bas
|
||||
pub fn div_round_down(a : i32, b : i32) -> i32{
|
||||
a/b
|
||||
}
|
||||
|
||||
//division arrondie vers le haut
|
||||
pub fn div_round_up(a : i32, b : i32) -> i32{
|
||||
let mut result = a/b;
|
||||
|
||||
if a%b > 0 {
|
||||
result + 1
|
||||
}
|
||||
else {
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl OpenFile {
|
||||
|
||||
/*
|
||||
Creer un "fichier ouvert", charge le file_hdr depuis disk
|
||||
*/
|
||||
pub fn open_file(sector : i32) -> OpenFile{
|
||||
let file_hdr : FileHdr = FileHdr::init_file_hdr();
|
||||
let name : String = String::from("");
|
||||
|
||||
//ici appel a fetchFrom(sector) sur file_hdr
|
||||
|
||||
OpenFile { name: name,
|
||||
hdr: file_hdr,
|
||||
seekPosition: 0,
|
||||
fSector: sector }
|
||||
}
|
||||
|
||||
pub fn seek(file : &mut OpenFile, position : i32){
|
||||
file.seekPosition = position;
|
||||
}
|
||||
|
||||
/* params :
|
||||
into : buffer de reception
|
||||
num_bytes : nombre d'octets à lire
|
||||
position : position du premier octet à lire dans le fichier (header compté??)
|
||||
*/
|
||||
pub fn read_at(driver_disk : &mut DrvDisk, file : &mut OpenFile, into : &mut Vec<u8>, num_bytes : i32, position : i32) -> i32 {
|
||||
|
||||
let mut nbr_octet_lu = num_bytes;
|
||||
let file_length = file.hdr.file_length();
|
||||
//on transforme position (position en octets) en un numéro de secteur
|
||||
let sector_position = position/disk::SECTOR_SIZE;
|
||||
|
||||
if ( num_bytes <= 0)||( position < 0)||( position >= file_length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (position + num_bytes > file_length) {
|
||||
nbr_octet_lu = file_length - position;
|
||||
}
|
||||
|
||||
DrvDisk::read_multiple_sector(driver_disk, sector_position, &mut into[..]);
|
||||
|
||||
nbr_octet_lu
|
||||
}
|
||||
|
||||
|
||||
/* params :
|
||||
from : données à écrire
|
||||
num_bytes : nombre d'octets à écrire depuis from
|
||||
position : position du premier octet à écrire dans le fichier(header compté??)
|
||||
*/
|
||||
pub fn write_at(driver_disk : &mut DrvDisk, file : &mut OpenFile, from : &Vec<u8>, num_bytes_to_write : i32, position : i32) -> i32 {
|
||||
|
||||
let mut file_length = file.hdr.file_length();
|
||||
let mut max_file_length = file.hdr.max_file_length();
|
||||
//on transforme position (position en octets) en un numéro de secteur
|
||||
let sector_position = position/disk::SECTOR_SIZE;
|
||||
let mut num_bytes = num_bytes_to_write; //le nombre d'octets que l'on va réellement écrire, on ne pourra pas forcément ecrire la taille demandée en paramètre
|
||||
|
||||
if (num_bytes <= 0)||(position < 0) ||(position > file_length){
|
||||
return 0;
|
||||
}
|
||||
|
||||
if num_bytes + file_length > max_file_length {
|
||||
|
||||
//bit_map represente les secteur du disque, pour savoir ou trouver de la place
|
||||
let mut bit_map = BitMap::init_bitmap(disk::NUM_SECTORS as usize);
|
||||
bit_map.fetch_from(filesys::FREE_MAP_FILE_PATH);
|
||||
|
||||
if !file.hdr.re_allocate(&mut bit_map, file_length, position + num_bytes){
|
||||
//plus de place sur le disquen on se contente d'écrire ce que l'on peut
|
||||
num_bytes = file_length - position;
|
||||
}
|
||||
else {
|
||||
//write_back the header and freemap to disk
|
||||
file.hdr.write_back(file.fSector, driver_disk);
|
||||
bit_map.write_back(filesys::FREE_MAP_FILE_PATH);
|
||||
}
|
||||
}
|
||||
|
||||
else if position + num_bytes > file_length{
|
||||
file.hdr.change_file_length(position + num_bytes);
|
||||
}
|
||||
|
||||
DrvDisk::write_multiple_sector(driver_disk, sector_position, &from[..]);
|
||||
|
||||
num_bytes
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub fn read(driver_disk : &mut DrvDisk, file : &mut OpenFile, into : &mut Vec<u8>, num_bytes : i32) -> i32 {
|
||||
|
||||
let result = OpenFile::read_at(driver_disk, file, into, num_bytes, file.seekPosition);
|
||||
file.seekPosition += result;
|
||||
result
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub fn write(driver_disk : &mut DrvDisk, file : &mut OpenFile, from : & Vec<u8>, num_bytes : i32) -> i32 {
|
||||
|
||||
let result = OpenFile::write_at(driver_disk, file, from, num_bytes, file.seekPosition);
|
||||
file.seekPosition += result;
|
||||
result
|
||||
}
|
||||
|
||||
|
||||
|
||||
pub fn get_name(&self) -> String {
|
||||
return self.name.clone();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
|
||||
}
|
143
src/kernel/elf.rs
Normal file
143
src/kernel/elf.rs
Normal file
@ -0,0 +1,143 @@
|
||||
//Declaration des alias
|
||||
|
||||
/*
|
||||
Def ELF :
|
||||
|
||||
The header file <elf.h> defines the format of ELF executable binary
|
||||
files. Amongst these files are normal executable files, relocatable
|
||||
object files, core files and shared libraries.
|
||||
|
||||
An executable file using the ELF file format consists of an ELF header,
|
||||
followed by a program header table or a section header table, or both.
|
||||
The ELF header is always at offset zero of the file. The program
|
||||
header table and the section header table's offset in the file are
|
||||
defined in the ELF header. The two tables describe the rest of the
|
||||
particularities of the file
|
||||
*/
|
||||
|
||||
|
||||
/* Type for a 16-bit quantity. */
|
||||
|
||||
type Elf32_Half = u16;
|
||||
|
||||
type Elf64_Half = u16;
|
||||
|
||||
/* Types for signed and unsigned 32-bit quantities. */
|
||||
|
||||
type Elf32_Word = u32;
|
||||
|
||||
type Elf32_Sword = i32;
|
||||
|
||||
type Elf64_Word = u32;
|
||||
|
||||
type Elf64_Sword = i32;
|
||||
|
||||
/* Types for signed and unsigned 64-bit quantities. */
|
||||
|
||||
type Elf32_Xword = u64;
|
||||
|
||||
type Elf32_Sxword = i64;
|
||||
|
||||
type Elf64_Xword = u64;
|
||||
|
||||
type Elf64_Sxword = i64;
|
||||
|
||||
/* Type of addresses. */
|
||||
|
||||
type Elf32_Addr = u32;
|
||||
|
||||
type Elf64_Addr = u64;
|
||||
|
||||
/* Type of file offsets. */
|
||||
|
||||
type Elf32_Off = u32;
|
||||
|
||||
type Elf64_Off = u64;
|
||||
|
||||
//role de ce truc ?
|
||||
const EI_NIDENT : u8 = 16;
|
||||
|
||||
//ELF file header 32 bits
|
||||
struct Elf32Ehdr{
|
||||
e_ident : [u8;EI_NIDENT],//16 octects décrivant comment le fichier doit etre parsé
|
||||
//e_ident must starts with magice number : 0x 7f 45 4c 46
|
||||
e_type : Elf32_Half,//type of the file
|
||||
e_machine : Elf32_Half,//type architecture machine
|
||||
e_version : Elf32_Word,//always 1
|
||||
e_entry : Elf32_Addr,//entry point @ for executable
|
||||
e_phoff : Elf32_Off,//Offset of the program header table
|
||||
e_shoff : Elf32_Off,//Offset of the section header table
|
||||
e_flags : Elf32_Word,//des flags ?
|
||||
e_ehsize : Elf32_Half,//size of this (the header), redundant
|
||||
e_phentsize : Elf32_Half,//size per program header
|
||||
e_phnum : Elf32_Half,//number of program header
|
||||
e_shentsize : Elf32_Half,//size per section header
|
||||
e_shnum : Elf32_Half,//number of section header
|
||||
e_shstrndx : Elf32_Half//section header string table index
|
||||
}
|
||||
|
||||
|
||||
//ELF file header 64 bits
|
||||
//les champs ont le meme rôle que dans le header 32 bits
|
||||
struct Elf64Ehdr{
|
||||
e_ident : [u8;EI_NIDENT],
|
||||
e_type : Elf64_Half,
|
||||
e_machine : Elf64_Half,
|
||||
e_version : Elf64_Word,
|
||||
e_entry : Elf64_Addr,
|
||||
e_phoff : Elf64_Off,
|
||||
e_shoff : Elf64_Off,
|
||||
e_flags : Elf64_Word,
|
||||
e_ehsize : Elf64_Half,
|
||||
e_phentsize : Elf64_Half,
|
||||
e_phnum : Elf64_Half,
|
||||
e_shentsize : Elf64_Half,
|
||||
e_shnum : Elf64_Half,
|
||||
e_shstrndx : Elf64_Half
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* e_ident offsets */
|
||||
const EI_MAG0 : u32 = 0;
|
||||
const EI_MAG1 : u32 = 1;
|
||||
const EI_MAG2 : u32 = 2;
|
||||
const EI_MAG3 : u32 = 3;
|
||||
const EI_CLASS : u32 = 4;
|
||||
const EI_DATA : u32 = 5;
|
||||
const EI_VERSION : u32 = 6;
|
||||
const EI_PAD : u32 = 7;
|
||||
|
||||
/* e_ident[EI_CLASS] */
|
||||
const ELFCLASSNONE : u32 = 0;
|
||||
const ELFCLASS32 : u32 = 1;
|
||||
const ELFCLASS64 : u32 = 2;
|
||||
|
||||
/* e_ident[EI_DATA] */
|
||||
const ELFDATANONE : u32 = 0;
|
||||
const ELFDATA2LSB : u32 = 1;
|
||||
const ELFDATA2MSB : u32 = 2;
|
||||
|
||||
/* e_type */
|
||||
const ET_NONE : u32 = 0; /* No file type */
|
||||
const ET_REL : u32 = 1; /* Relocatable file */
|
||||
const ET_EXEC : u32 = 2; /* Executable file */
|
||||
const ET_DYN : u32 = 3; /* Shared object file */
|
||||
const ET_CORE : u32 = 4; /* Core file */
|
||||
const ET_LOPROC : u32 = 0xff00; /* Processor-specific */
|
||||
const ET_HIPROC : u32 = 0xffff; /* Processor-specific */
|
||||
|
||||
/* e_machine */
|
||||
const EM_NONE : u32 = 0; /* No machine */
|
||||
const EM_M32 : u32 = 1; /* AT&T WE 32100 */
|
||||
const EM_SPARC : u32 = 2; /* SPARC */
|
||||
const EM_386 : u32 = 3; /* Intel 80386 */
|
||||
const EM_68K : u32 = 4; /* Motorola 68000 */
|
||||
const EM_88K : u32 = 5; /* Motorola 88000 */
|
||||
const EM_860 : u32 = 7; /* Intel 80860 */
|
||||
const EM_MIPS : u32 = 8; /* MIPS R3000 */
|
||||
const EM_RISC : u32 = 243; /* RISCV */
|
||||
|
||||
/* e_version */
|
||||
const EV_NONE : u32 = 0; /* invalid version */
|
||||
const EV_CURRENT : u32 = 1; /* current version */
|
@ -1,412 +0,0 @@
|
||||
//! # Exceprions
|
||||
//!
|
||||
//! This module Enum the constant values of the exceptions.
|
||||
//! They are used to stop the system to execute some opperation
|
||||
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use crate::{simulator::{machine::{ExceptionType, Machine}, error::{MachineOk, MachineError}}};
|
||||
use crate::kernel::synch::{Lock, Semaphore};
|
||||
|
||||
use super::{system::System, thread::Thread};
|
||||
|
||||
/// The halt system call. Stops Burritos.
|
||||
pub const SC_SHUTDOWN: u8 = 0;
|
||||
/// The exit system call
|
||||
///
|
||||
/// Ends the calling thread
|
||||
pub const SC_EXIT: u8 = 1;
|
||||
/// The exec system call
|
||||
///
|
||||
/// Creates a new process (thread+address space)
|
||||
pub const SC_EXEC: u8 = 2;
|
||||
/// The join system call
|
||||
///
|
||||
/// Wait for the thread idThread to finish
|
||||
pub const SC_JOIN: u8 = 3;
|
||||
/// The create system call
|
||||
///
|
||||
/// Create a new file in nachos file system
|
||||
pub const SC_CREATE: u8 = 4;
|
||||
/// The open system call
|
||||
///
|
||||
/// Opens a file and returns an openfile identifier
|
||||
pub const SC_OPEN: u8 = 5;
|
||||
/// The read system call
|
||||
///
|
||||
/// Read in a file or the console
|
||||
pub const SC_READ: u8 = 6;
|
||||
/// The write system call
|
||||
///
|
||||
/// Write in a file or at the console
|
||||
pub const SC_WRITE: u8 = 7;
|
||||
/// Seek to a given position in an opened file
|
||||
pub const SC_SEEK: u8 = 8;
|
||||
/// The close system call
|
||||
///
|
||||
/// Close a file
|
||||
pub const SC_CLOSE: u8 = 9;
|
||||
/// The newThread system call
|
||||
///
|
||||
/// Create a new thread in the same address space
|
||||
pub const SC_NEW_THREAD: u8 = 10;
|
||||
/// The Yield System call
|
||||
///
|
||||
/// Relinquish the CPU if any other thread is runnable
|
||||
pub const SC_YIELD: u8 = 11;
|
||||
/// the PError system call
|
||||
///
|
||||
/// print the last error message
|
||||
pub const SC_PERROR: u8 = 12;
|
||||
/// carry out P() on the semaphore
|
||||
pub const SC_P: u8 = 13;
|
||||
/// carry out V() on the semaphore
|
||||
pub const SC_V: u8 = 14;
|
||||
/// create a semaphore and add it in g_objects_addrs
|
||||
pub const SC_SEM_CREATE: u8 = 15;
|
||||
/// destroy the semaphore corresponding to the id
|
||||
pub const SC_SEM_DESTROY: u8 = 16;
|
||||
/// create a lock and add it to g_object_addrs
|
||||
pub const SC_LOCK_CREATE: u8 = 17;
|
||||
/// destroy the lock corresponding to the id
|
||||
pub const SC_LOCK_DESTROY: u8 = 18;
|
||||
/// carry out acquire() on the lock
|
||||
pub const SC_LOCK_ACQUIRE: u8 = 19;
|
||||
/// carry out release() on the lock
|
||||
pub const SC_LOCK_RELEASE: u8 = 20;
|
||||
/// create a condition variable and add it to g_object_addrs
|
||||
pub const SC_COND_CREATE: u8 = 21;
|
||||
/// destroy the condition variable corresponding to the id
|
||||
pub const SC_COND_DESTROY: u8 = 22;
|
||||
/// carry out wait() on the condition
|
||||
pub const SC_COND_WAIT: u8 = 23;
|
||||
/// carry out signal() on the condition
|
||||
pub const SC_COND_SIGNAL: u8 = 24;
|
||||
/// carry out broadcast() on the condition
|
||||
pub const SC_COND_BROADCAST: u8 = 25;
|
||||
/// the TtySend system call
|
||||
///
|
||||
/// Sends some char by the serial line emulated
|
||||
pub const SC_TTY_SEND: u8 = 26;
|
||||
/// the TtyReceive system call
|
||||
///
|
||||
/// read some char on the serial line
|
||||
pub const SC_TTY_RECEIVE: u8 = 27;
|
||||
/// the Mkdir system call
|
||||
///
|
||||
/// make a new directory in the file system
|
||||
pub const SC_MKDIR: u8 = 28;
|
||||
/// the Rmdir system call
|
||||
///
|
||||
/// remove a directory from the file system
|
||||
pub const SC_RMDIR: u8 = 29;
|
||||
/// The Remove system call
|
||||
///
|
||||
/// Remove a file from the file system
|
||||
pub const SC_REMOVE: u8 = 30;
|
||||
/// The FSList system call
|
||||
///
|
||||
/// Lists all the file and directories in the filesystem
|
||||
pub const SC_FSLIST: u8 = 31;
|
||||
// The systime system call. Gets the system time
|
||||
pub const SC_SYS_TIME: u8 = 32;
|
||||
/// Map a file in memory
|
||||
pub const SC_MMAP: u8 = 33;
|
||||
/// Behaviour undefined and currently unused
|
||||
pub const SC_DEBUG: u8 = 34;
|
||||
|
||||
pub const CONSOLE_OUTPUT: u8 = 1;
|
||||
|
||||
// todo : returns new types, not just machine errors and machine ok
|
||||
pub fn call(exception: &ExceptionType, machine: &mut Machine, system: &mut System) -> Result<MachineOk, MachineError> {
|
||||
|
||||
match exception {
|
||||
ExceptionType::NoException => Err("No Exception no yet implemented")?,
|
||||
ExceptionType::SyscallException => syscall(machine, system),
|
||||
ExceptionType::PagefaultException => Err("Page Fault Exception not yet implemented")?,
|
||||
ExceptionType::ReadOnlyException => Err("Read Only Exception not yet implemented")?,
|
||||
ExceptionType::BusErrorException => Err("Bus Error Exception not yet implemented")?,
|
||||
ExceptionType::AddressErrorException => Err("AddressErrorException not yet implemented")?,
|
||||
ExceptionType::OverflowException => Err("OverflowException not yet implemented")?,
|
||||
ExceptionType::IllegalInstrException => Err("IllegalInstrException not yet implemented")?,
|
||||
ExceptionType::NumExceptionTypes => Err("NumExceptionTypes not yet implemented")?,
|
||||
}
|
||||
}
|
||||
|
||||
fn syscall(machine: &mut Machine, system: &mut System) -> Result<MachineOk, MachineError> {
|
||||
let call_type = machine.read_int_register(17) as u8;
|
||||
|
||||
match call_type {
|
||||
SC_SHUTDOWN => Ok(MachineOk::Shutdown),
|
||||
SC_EXIT => {
|
||||
let th = match &system.get_thread_manager().g_current_thread {
|
||||
Some(th) => th.clone(),
|
||||
None => Err("Current thread is None")?
|
||||
};
|
||||
let code = machine.read_int_register(10);
|
||||
system.get_thread_manager().thread_finish(machine, th, code);
|
||||
Ok(MachineOk::Ok)
|
||||
},
|
||||
SC_EXEC => todo!(),
|
||||
SC_JOIN => sc_join(machine, system),
|
||||
SC_CREATE => todo!(),
|
||||
SC_OPEN => todo!(),
|
||||
SC_READ => todo!(),
|
||||
SC_WRITE => {
|
||||
|
||||
let address = machine.read_int_register(10);
|
||||
let size = machine.read_int_register(11);
|
||||
// openfileid or 1 (console)
|
||||
let f = machine.read_int_register(12);
|
||||
|
||||
// load buffer
|
||||
let mut buffer = String::new();
|
||||
let mut val: [u8; 4] = [0; 4];
|
||||
for (j, elem) in val.iter_mut().enumerate() {
|
||||
*elem = machine.read_memory(1, address as usize + j) as u8;
|
||||
}
|
||||
for i in 0..size {
|
||||
buffer.push((machine.read_memory(1, (address + i) as usize)) as u8 as char);
|
||||
}
|
||||
|
||||
if f as u8 == CONSOLE_OUTPUT {
|
||||
print!("{}", buffer); // todo replace with console driver in the future
|
||||
Ok(MachineOk::Ok)
|
||||
} else {
|
||||
Err("SC_WRITE to file is not yet implemented")?
|
||||
}
|
||||
},
|
||||
SC_SEEK => todo!(),
|
||||
SC_CLOSE => todo!(),
|
||||
SC_NEW_THREAD => sc_new_thread(machine, system),
|
||||
SC_YIELD => todo!(),
|
||||
SC_PERROR => todo!(),
|
||||
SC_P => sc_p(machine, system),
|
||||
SC_V => sc_v(machine, system),
|
||||
SC_SEM_CREATE => sc_sem_create(machine, system),
|
||||
SC_SEM_DESTROY => sc_sem_remove(machine, system),
|
||||
SC_LOCK_CREATE => sc_lock_create(machine, system),
|
||||
SC_LOCK_DESTROY => sc_lock_destroy(machine, system),
|
||||
SC_LOCK_ACQUIRE => sc_lock_acquire(machine, system),
|
||||
SC_LOCK_RELEASE => sc_lock_release(machine, system),
|
||||
SC_COND_CREATE => todo!(),
|
||||
SC_COND_DESTROY => todo!(),
|
||||
SC_COND_WAIT => todo!(),
|
||||
SC_COND_SIGNAL => todo!(),
|
||||
SC_COND_BROADCAST => todo!(),
|
||||
SC_TTY_SEND => todo!(),
|
||||
SC_TTY_RECEIVE => todo!(),
|
||||
SC_MKDIR => todo!(),
|
||||
SC_RMDIR => todo!(),
|
||||
SC_REMOVE => todo!(),
|
||||
SC_FSLIST => todo!(),
|
||||
SC_SYS_TIME => todo!(),
|
||||
SC_MMAP => todo!(),
|
||||
SC_DEBUG => todo!(),
|
||||
_ => todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn sc_lock_release(machine: &mut Machine, system: &mut System) -> Result<MachineOk, MachineError>{
|
||||
let id = machine.read_int_register(10) as i32;
|
||||
system.get_thread_manager().lock_release(id, machine)
|
||||
}
|
||||
|
||||
fn sc_lock_acquire(machine: &mut Machine, system: &mut System) -> Result<MachineOk, MachineError> {
|
||||
let id = machine.read_int_register(10) as i32;
|
||||
system.get_thread_manager().lock_acquire(id, machine)
|
||||
}
|
||||
|
||||
fn sc_lock_create(machine: &mut Machine, system: &mut System) -> Result<MachineOk, MachineError> {
|
||||
let addr_name = machine.read_int_register(10) as usize;
|
||||
let size = get_length_param(addr_name, machine);
|
||||
let _name = get_string_param(addr_name, size, machine);
|
||||
let lock = Lock::new();
|
||||
let id = system.get_thread_manager().get_obj_addrs().add_lock(lock);
|
||||
machine.write_int_register(10, id as i64);
|
||||
Ok(MachineOk::Ok)
|
||||
}
|
||||
|
||||
fn sc_lock_destroy(machine: &mut Machine, system: &mut System) -> Result<MachineOk, MachineError> {
|
||||
let id = machine.read_int_register(10) as i32;
|
||||
system.get_thread_manager().get_obj_addrs().remove_lock(id);
|
||||
Ok(MachineOk::Ok)
|
||||
}
|
||||
|
||||
fn sc_p(machine: &mut Machine, system: &mut System) -> Result<MachineOk, MachineError> {
|
||||
let id_sema = machine.int_reg.get_reg(10);
|
||||
system.get_thread_manager().sem_p(id_sema as i32, machine)
|
||||
}
|
||||
|
||||
fn sc_v(machine: &mut Machine, system: &mut System) -> Result<MachineOk, MachineError> {
|
||||
let id_sema = machine.int_reg.get_reg(10);
|
||||
system.get_thread_manager().sem_v(id_sema as i32, machine)
|
||||
}
|
||||
|
||||
fn sc_sem_create(machine: &mut Machine, system: &mut System) -> Result<MachineOk, MachineError> {
|
||||
let addr_name = machine.read_int_register(10) as usize;
|
||||
let initial_count = machine.read_int_register(11) as i32;
|
||||
let size = get_length_param(addr_name, machine);
|
||||
let _name = get_string_param(addr_name, size, machine);
|
||||
match initial_count < 0 {
|
||||
true => Err(format!("Initial_count < 0"))?,
|
||||
false => {
|
||||
let id = system.get_thread_manager().get_obj_addrs().add_semaphore(Semaphore::new(initial_count));
|
||||
machine.write_int_register(10, id as i64);
|
||||
Ok(MachineOk::Ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn sc_sem_remove(machine: &mut Machine, system: &mut System) -> Result<MachineOk, MachineError>{
|
||||
let id = machine.read_int_register(10) as i32;
|
||||
system.get_thread_manager().get_obj_addrs().remove_semaphore(id);
|
||||
Ok(MachineOk::Ok)
|
||||
}
|
||||
|
||||
fn sc_new_thread(machine: &mut Machine, system: &mut System) -> Result<MachineOk, MachineError> {
|
||||
// Get the address of the string for the name of the thread
|
||||
let name_addr = machine.read_int_register(10) as usize;
|
||||
// Get the pointer of the function to be executed in the new thread
|
||||
let func = machine.read_int_register(11);
|
||||
// Get function parameters
|
||||
let args = machine.read_int_register(12);
|
||||
// get string name
|
||||
let name_size = get_length_param(name_addr, machine);
|
||||
let thread_name: String = get_string_param(name_addr, name_size, machine).into_iter().collect();
|
||||
|
||||
let n_thread = Thread::new(thread_name.as_str());
|
||||
let n_thread = Rc::new(RefCell::new(n_thread));
|
||||
let tid = system.get_thread_manager().get_obj_addrs().add_thread(Rc::clone(&n_thread));
|
||||
let current_thread = match system.get_thread_manager().get_g_current_thread() {
|
||||
Some(th) => {
|
||||
Rc::clone(th)
|
||||
},
|
||||
None => {
|
||||
return Err("Current thread is none")?;
|
||||
}
|
||||
};
|
||||
let current_thread = current_thread.borrow_mut();
|
||||
if let Some(process) = current_thread.get_process_owner() {
|
||||
let sp_max = system.get_thread_manager().get_sp_max() + machine.user_stack_size;
|
||||
system.get_thread_manager().set_sp_max(sp_max);
|
||||
system.get_thread_manager().start_thread(n_thread, Rc::clone(&process), func as u64, sp_max, args);
|
||||
// TODO changé la valeur de sp quand on supportera les addresses virtuels
|
||||
machine.write_int_register(10, tid as i64);
|
||||
Ok(MachineOk::Ok)
|
||||
} else {
|
||||
return Err("Process owner of current thread is none")?;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn sc_join(machine: &mut Machine, system: &mut System) -> Result<MachineOk, MachineError> {
|
||||
let tid = machine.read_int_register(10);
|
||||
let p_thread = system.get_thread_manager().get_obj_addrs().search_thread(tid as i32);
|
||||
match p_thread {
|
||||
Some(waiting_for) => {
|
||||
let rc_waiting_for = Rc::clone(waiting_for);
|
||||
if let Some(current_thread) = system.get_thread_manager().get_g_current_thread() {
|
||||
let rc_curr = Rc::clone(current_thread);
|
||||
system.get_thread_manager().thread_join(machine, rc_curr, rc_waiting_for);
|
||||
|
||||
Ok(MachineOk::Ok)
|
||||
} else {
|
||||
Err("Current should not be None")?
|
||||
}
|
||||
},
|
||||
None => {
|
||||
// Thread already terminated (type set to INVALID_TYPE) or call on an object
|
||||
// that is not a thread
|
||||
// Exit with no error code since we cannot separate the two cases
|
||||
Ok(MachineOk::Ok)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_length_param(addr: usize, machine: & Machine) -> usize {
|
||||
let mut i = 0;
|
||||
let mut c = 1;
|
||||
while c != 0 {
|
||||
c = machine.read_memory(1, addr + i);
|
||||
i += 1;
|
||||
|
||||
}
|
||||
i + 1
|
||||
}
|
||||
|
||||
fn get_string_param(addr: usize, maxlen: usize, machine: &Machine) -> Vec<char> {
|
||||
let mut dest = Vec::with_capacity(maxlen);
|
||||
|
||||
let mut i: usize = 0;
|
||||
let mut c = 1;
|
||||
|
||||
while c != 0 && i < maxlen {
|
||||
c = machine.read_memory(1, addr + i) as u8;
|
||||
dest.push(c as char);
|
||||
i += 1;
|
||||
}
|
||||
|
||||
dest
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::kernel::exception::{SC_SHUTDOWN, SC_WRITE};
|
||||
use crate::kernel::system::System;
|
||||
use crate::simulator::machine::Machine;
|
||||
use crate::utility::cfg::get_debug_configuration;
|
||||
|
||||
#[test]
|
||||
fn test_sc_shutdown() {
|
||||
let mut machine = Machine::new(true, get_debug_configuration());
|
||||
machine.write_int_register(17, SC_SHUTDOWN as i64); // Set type to shutdown
|
||||
// let ecall = Instruction::new(0b000000000000_00000_000_00000_1110011);
|
||||
let insts: [u8; 4] = 0b000000000000_00000_000_00000_1110011_u32.to_le_bytes();
|
||||
machine.write_memory(1, 0, insts[0] as u64);
|
||||
machine.write_memory(1, 1, insts[1] as u64);
|
||||
machine.write_memory(1, 2, insts[2] as u64);
|
||||
machine.write_memory(1, 3, insts[3] as u64); // ecall
|
||||
// machine.write_memory(4, 0, 0b000000000000_00000_000_00000_1110011_u64.to_be()); // ecall
|
||||
let insts: [u8; 4] = 0b000000001010_00000_000_00001_0010011_u32.to_le_bytes();
|
||||
machine.write_memory(1, 4, insts[0] as u64);
|
||||
machine.write_memory(1, 5, insts[1] as u64);
|
||||
machine.write_memory(1, 6, insts[2] as u64);
|
||||
machine.write_memory(1, 7, insts[3] as u64); // r1 <- 10
|
||||
// machine.write_memory(4, 4, 0b000000001010_00000_000_00001_0010011_u64.to_be()); // r1 <- 10
|
||||
let mut system = System::new(true);
|
||||
machine.run(&mut system);
|
||||
// If the machine was stopped with no error, the shutdown worked
|
||||
assert_ne!(machine.read_int_register(1), 10); // Check if the next instruction was executed
|
||||
}
|
||||
|
||||
// This test print HELLO in the console
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_sc_print() {
|
||||
let mut machine = Machine::new(true, get_debug_configuration());
|
||||
|
||||
let _address = machine.read_int_register(10);
|
||||
// Write string 'HELLO' in memory
|
||||
machine.write_memory(1, 4000, 72);
|
||||
machine.write_memory(1, 4001, 69);
|
||||
machine.write_memory(1, 4002, 76);
|
||||
machine.write_memory(1, 4003, 76);
|
||||
machine.write_memory(1, 4004, 79);
|
||||
|
||||
|
||||
machine.write_int_register(10, 4000); // String address
|
||||
machine.write_int_register(11, 5); // String size
|
||||
machine.write_int_register(12, 1); // Console output
|
||||
|
||||
machine.write_memory(4, 0, 0b000000000000_00000_000_00000_1110011); // ecall
|
||||
machine.write_int_register(17, SC_WRITE as i64); // Set type to write
|
||||
|
||||
machine.write_memory(4, 4, 0b000000000000_00000_000_10001_0010011); // r17 <- SC_SHUTDOWN
|
||||
machine.write_memory(4, 8, 0b000000000000_00000_000_00000_1110011); // ecall
|
||||
|
||||
let mut system = System::new(true);
|
||||
machine.run(&mut system);
|
||||
}
|
||||
|
||||
}
|
@ -1,12 +1,4 @@
|
||||
//! # Error Code
|
||||
//!
|
||||
//! This module enumerate the possibles error code who could get in a function
|
||||
//!
|
||||
//! **Basic Usage:*
|
||||
//!
|
||||
//! Result<YourSuccessStruct, **ErrorCode**
|
||||
|
||||
#![allow(unused, clippy::missing_docs_in_private_items)]
|
||||
/// Error enum, use it with Result<YourSucessStruct, **ErrorCode**>
|
||||
pub enum ErrorCode {
|
||||
IncError,
|
||||
@ -35,5 +27,5 @@ pub enum ErrorCode {
|
||||
WrongFileEndianess,
|
||||
NoAcia,
|
||||
|
||||
NumMsgError /* Must always be last */
|
||||
NUMMSGERROR /* Must always be last */
|
||||
}
|
@ -1,13 +1,8 @@
|
||||
//! # Kernel
|
||||
//!
|
||||
//! This module contains all the tool required for the kernel to work.
|
||||
//!
|
||||
//! Currently it contains the scheduling and synchroisation tools, but it will contains the tools
|
||||
//! required Memory gestion, Files gestion and peripheral pilots.
|
||||
pub mod process;
|
||||
mod process;
|
||||
pub mod thread;
|
||||
pub mod scheduler;
|
||||
pub mod mgerror;
|
||||
pub mod system;
|
||||
pub mod synch;
|
||||
mod ucontext;
|
||||
mod synch;
|
||||
mod thread_manager;
|
||||
pub mod exception;
|
74
src/kernel/scheduler.rs
Normal file
74
src/kernel/scheduler.rs
Normal file
@ -0,0 +1,74 @@
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::utility::list::List;
|
||||
use crate::kernel::thread::Thread;
|
||||
use super::thread_manager::ThreadManager;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub struct Scheduler {
|
||||
ready_list: List<Rc<RefCell<Thread>>>,
|
||||
pub thread_manager: Option<Rc<RefCell<ThreadManager>>>
|
||||
}
|
||||
|
||||
impl Scheduler {
|
||||
|
||||
/// Constructor
|
||||
///
|
||||
/// Initilize the list of ready thread
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
ready_list: List::new(),
|
||||
thread_manager: Option::None
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark a thread as aready, but not necessarily running yet.
|
||||
///
|
||||
/// Put it in the ready list, for later scheduling onto the CPU.
|
||||
///
|
||||
/// ## Pamameter
|
||||
///
|
||||
/// **thread** is the thread to be put on the read list
|
||||
pub fn ready_to_run(&mut self, thread: Rc<RefCell<Thread>>) {
|
||||
self.ready_list.push(thread);
|
||||
}
|
||||
|
||||
/// Return the next thread to be scheduled onto the CPU.
|
||||
/// If there are no ready threads, return Option::None
|
||||
///
|
||||
/// Thread is removed from the ready list.
|
||||
///
|
||||
/// **return** Thread thread to be scheduled
|
||||
pub fn find_next_to_run(&mut self) -> Option<Rc<RefCell<Thread>>> {
|
||||
self.ready_list.pop()
|
||||
}
|
||||
|
||||
/// Dispatch the CPU to next_thread. Save the state of the old thread
|
||||
/// and load the state of the new thread.
|
||||
///
|
||||
/// We assume the state of the previously running thread has already been changed from running to blocked or ready.
|
||||
///
|
||||
/// Global variable g_current_thread become next_thread
|
||||
///
|
||||
/// ## Parameter
|
||||
///
|
||||
/// **next_thread** thread to dispatch to the CPU
|
||||
pub fn switch_to(&mut self, next_thread: Rc<RefCell<Thread>>) {
|
||||
if let Some(tm) = &self.thread_manager {
|
||||
let rc = Rc::clone(&tm);
|
||||
if let Some(old_thread) = tm.borrow_mut().get_g_current_thread() {
|
||||
rc.borrow_mut().thread_save_processor_state(Rc::clone(&old_thread));
|
||||
// old_thread.save_simulator_state();
|
||||
|
||||
if old_thread != &next_thread {
|
||||
rc.borrow_mut().thread_restore_processor_state(Rc::clone(&next_thread));
|
||||
// next_thread.restore_simulator_state();
|
||||
rc.borrow_mut().set_g_current_thread(Option::Some(next_thread));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
panic!("thread manager shouldn't be none");
|
||||
}
|
||||
}
|
||||
}
|
@ -1,28 +1,24 @@
|
||||
//! # Synchronisation
|
||||
//!
|
||||
//! This module contains some scheduling and synchronisation utilities:
|
||||
//! - **Semaphore**
|
||||
//! - **Lock**
|
||||
//!
|
||||
//! Conditions aren't implemented currently
|
||||
|
||||
use crate::utility::list::List;
|
||||
use crate::kernel::thread::Thread;
|
||||
use crate::simulator::interrupt::InterruptStatus::InterruptOff;
|
||||
use crate::simulator::machine::Machine;
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
|
||||
|
||||
use super::scheduler::Scheduler;
|
||||
use super::system::System;
|
||||
use super::thread_manager::ThreadManager;
|
||||
|
||||
/// Structure of a Semaphore used for synchronisation.
|
||||
/// It use a counter to determine the number of thread that can be executed simultaneously.
|
||||
#[derive(PartialEq)]
|
||||
/// Structure of a Semaphore used for synchronisation
|
||||
pub struct Semaphore {
|
||||
|
||||
/// Counter of simultaneous Semaphore
|
||||
pub counter:i32,
|
||||
/// QUeue of Semaphore waiting to be executed
|
||||
pub waiting_queue:List<Rc<RefCell<Thread>>>,
|
||||
/// Counter of simultanous Semaphore
|
||||
counter:i32,
|
||||
/// QUeue of Semaphore waiting to be exucated
|
||||
waiting_queue:List<Rc<RefCell<Thread>>>,
|
||||
/// Thread manager which managing threads
|
||||
thread_manager: Rc<RefCell<ThreadManager>>
|
||||
|
||||
}
|
||||
|
||||
@ -33,37 +29,75 @@ impl Semaphore {
|
||||
/// ### Parameters
|
||||
/// - *counter* initial value of counter
|
||||
/// - *thread_manager* Thread manager which managing threads
|
||||
pub fn new(counter: i32) -> Semaphore{
|
||||
Semaphore { counter, waiting_queue: List::default() }
|
||||
pub fn new(counter: i32, thread_manager: Rc<RefCell<ThreadManager>>) -> Semaphore{
|
||||
Semaphore { counter, waiting_queue: List::new(), thread_manager}
|
||||
}
|
||||
|
||||
/// Decrement the value, and wait if it becomes < 0. Checking the
|
||||
/// value and decrementing must be done atomically, so we
|
||||
/// need to disable interrupts before checking the value.
|
||||
///
|
||||
/// Note that thread_manager::thread_sleep assumes that interrupts are disabled
|
||||
/// when it is called.
|
||||
///
|
||||
/// ### Parameters
|
||||
/// - *current_thread* the current thread
|
||||
/// - *machine* the machine where the threads are executed
|
||||
pub fn p(&mut self, current_thread: Rc<RefCell<Thread>>, system: Rc<RefCell<System>>) {
|
||||
let old_status = system.borrow_mut().get_g_machine().borrow_mut().interrupt.set_status(InterruptOff);
|
||||
self.counter -= 1;
|
||||
if self.counter < 0 {
|
||||
self.waiting_queue.push(Rc::clone(¤t_thread));
|
||||
self.thread_manager.borrow_mut().thread_sleep(current_thread);
|
||||
}
|
||||
system.borrow_mut().get_g_machine().borrow_mut().interrupt.set_status(old_status);
|
||||
}
|
||||
|
||||
/// Increment semaphore value, waking up a waiting thread if any.
|
||||
/// As with P(), this operation must be atomic, so we need to disable
|
||||
/// interrupts.
|
||||
///
|
||||
/// scheduler::ready_to_run() assumes that interrupts
|
||||
/// are disabled when it is called.
|
||||
///
|
||||
/// ### Parameters
|
||||
/// - **machine** the machine where the threads are executed
|
||||
/// - **scheduler** the scheduler which determine which thread to execute
|
||||
pub fn v(&mut self, system: Rc<RefCell<System>>){
|
||||
let old_status = system.borrow_mut().get_g_machine().borrow_mut().interrupt.set_status(InterruptOff);
|
||||
self.counter += 1;
|
||||
if self.waiting_queue.peek() != None {
|
||||
system.borrow_mut().get_thread_manager().borrow_mut().g_scheduler.ready_to_run(self.waiting_queue.pop().unwrap());
|
||||
}
|
||||
system.borrow_mut().get_g_machine().borrow_mut().interrupt.set_status(old_status);
|
||||
}
|
||||
}
|
||||
|
||||
/// Lock used for synchronisation, can be interpreted has a Semaphore with a
|
||||
/// counter of 1
|
||||
/// It's used for critical parts
|
||||
#[derive(PartialEq)]
|
||||
#[derive(Clone)]
|
||||
pub struct Lock {
|
||||
pub struct Lock{
|
||||
|
||||
/// Thread owning the lock
|
||||
pub owner: Option<Rc<RefCell<Thread>>>,
|
||||
owner: Option<Rc<RefCell<Thread>>>,
|
||||
/// The queue of threads waiting for execution
|
||||
pub waiting_queue:List<Rc<RefCell<Thread>>>,
|
||||
waiting_queue:List<Rc<RefCell<Thread>>>,
|
||||
/// Thread manager which managing threads
|
||||
thread_manager: Rc<RefCell<ThreadManager>>,
|
||||
/// A boolean definig if the lock is free or not
|
||||
pub free: bool
|
||||
free: bool
|
||||
|
||||
}
|
||||
|
||||
impl Lock {
|
||||
|
||||
/// Initialize a Lock, so that it can be used for synchronization.
|
||||
/// The lock is initially free
|
||||
/// The lock is initialy free
|
||||
///
|
||||
/// ### Parameters
|
||||
/// - **thread_manager** Thread manager which managing threads
|
||||
pub fn new() -> Lock {
|
||||
Lock { owner: None, waiting_queue: List::default(), free: true }
|
||||
pub fn new(thread_manager: Rc<RefCell<ThreadManager>>) -> Lock {
|
||||
Lock { owner: None, waiting_queue: List::new(), thread_manager, free: true }
|
||||
}
|
||||
|
||||
/// Wait until the lock become free. Checking the
|
||||
@ -77,28 +111,23 @@ impl Lock {
|
||||
/// ### Parameters
|
||||
/// - **current_thread** the current thread
|
||||
/// - **machine** the machine where the threads are executed
|
||||
pub fn acquire(&mut self, machine: &mut Machine, thread_manager: &mut ThreadManager) {
|
||||
let old_status = machine.interrupt.set_status(InterruptOff);
|
||||
pub fn acquire(&mut self, current_thread: Option<Rc<RefCell<Thread>>>, system: Rc<RefCell<System>>) {
|
||||
let old_status = system.borrow_mut().get_g_machine().borrow_mut().interrupt.set_status(InterruptOff);
|
||||
|
||||
if self.free {
|
||||
self.free = false;
|
||||
self.owner = Some(match thread_manager.get_g_current_thread() {
|
||||
Some(th) => {
|
||||
Rc::clone(&th)
|
||||
},
|
||||
None => unreachable!()
|
||||
});
|
||||
self.owner = current_thread;
|
||||
} else {
|
||||
match thread_manager.get_g_current_thread() {
|
||||
match current_thread {
|
||||
Some(x) => {
|
||||
let x = Rc::clone(&x);
|
||||
self.waiting_queue.push(Rc::clone(&x));
|
||||
thread_manager.thread_sleep(machine, Rc::clone(&x));
|
||||
self.thread_manager.borrow_mut().thread_sleep(x)
|
||||
},
|
||||
None => unreachable!("Current thread should not be None")
|
||||
None => ()
|
||||
}
|
||||
}
|
||||
|
||||
machine.interrupt.set_status(old_status);
|
||||
system.borrow_mut().get_g_machine().borrow_mut().interrupt.set_status(old_status);
|
||||
}
|
||||
|
||||
/// Wake up a waiter if necessary, or release it if no thread is waiting.
|
||||
@ -110,59 +139,43 @@ impl Lock {
|
||||
/// ### Parameters
|
||||
/// - **machine** the machine where the code is executed
|
||||
/// - **scheduler** the scheduler which determine which thread to execute
|
||||
pub fn release(&mut self, machine: &mut Machine, thread_manager: &mut ThreadManager) {
|
||||
let old_status = machine.interrupt.set_status(InterruptOff);
|
||||
pub fn release(&mut self, system: Rc<RefCell<System>>, current_thread: Rc<RefCell<Thread>>) {
|
||||
let old_status = system.borrow_mut().get_g_machine().borrow_mut().interrupt.set_status(InterruptOff);
|
||||
|
||||
match thread_manager.get_g_current_thread() {
|
||||
Some(_) => {
|
||||
if self.held_by_current_thread(thread_manager) {
|
||||
match self.waiting_queue.pop() {
|
||||
Some(thread) => {
|
||||
self.owner = Some(thread);
|
||||
if self.held_by_current_thread(current_thread) {
|
||||
if self.waiting_queue.peek() != None {
|
||||
self.owner = Some(self.waiting_queue.pop().unwrap());
|
||||
let sys = system.borrow_mut();
|
||||
let tm = sys.get_thread_manager();
|
||||
let scheduler = &mut tm.borrow_mut().g_scheduler;
|
||||
match &self.owner {
|
||||
Some(x) => thread_manager.ready_to_run(Rc::clone(&x)),
|
||||
Some(x) => scheduler.ready_to_run(Rc::clone(&x)),
|
||||
None => ()
|
||||
}
|
||||
},
|
||||
None => {
|
||||
} else {
|
||||
self.free = true;
|
||||
self.owner = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None => ()
|
||||
|
||||
system.borrow_mut().get_g_machine().borrow_mut().interrupt.set_status(old_status);
|
||||
}
|
||||
|
||||
machine.interrupt.set_status(old_status);
|
||||
}
|
||||
|
||||
/// Say if the lock is held by the current thread
|
||||
/// Useful for checking in Release, and in Condition operations below.
|
||||
/// ### Parameters
|
||||
/// - **self** The current lock
|
||||
/// - **thread-manager** The thread manager present in the system
|
||||
/// ### Return
|
||||
/// True if the current thread holds this lock.
|
||||
|
||||
pub fn held_by_current_thread(&mut self, thread_manager: &mut ThreadManager) -> bool {
|
||||
pub fn held_by_current_thread(&mut self, current_thread: Rc<RefCell<Thread>>) -> bool {
|
||||
match &self.owner {
|
||||
Some(x) =>
|
||||
match thread_manager.get_g_current_thread() {
|
||||
Some(thread) => Rc::ptr_eq(x, thread),
|
||||
None => false
|
||||
}
|
||||
Some(x) => Rc::ptr_eq(&x, ¤t_thread),
|
||||
None => false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Structure of a condition used for synchronisation
|
||||
#[allow(unused)] // -> No enough time to implement it
|
||||
pub struct Condition{
|
||||
|
||||
/// The queue of threads waiting for execution
|
||||
waiting_queue:List<Rc<RefCell<Thread>>>,
|
||||
/// Thread manager which managing threads
|
||||
thread_manager: Rc<RefCell<ThreadManager>>,
|
||||
|
||||
}
|
||||
|
||||
@ -172,9 +185,8 @@ impl Condition {
|
||||
///
|
||||
/// ### Parameters
|
||||
/// - *thread_manager* Thread manager which managing threads
|
||||
#[allow(unused)]
|
||||
pub fn new() -> Condition {
|
||||
Condition{ waiting_queue: List::default()}
|
||||
pub fn new(thread_manager: Rc<RefCell<ThreadManager>>) -> Condition {
|
||||
Condition{ waiting_queue: List::new(), thread_manager }
|
||||
}
|
||||
|
||||
/// Block the calling thread (put it in the wait queue).
|
||||
@ -183,18 +195,11 @@ impl Condition {
|
||||
/// ### Parameters
|
||||
/// - **current_thread** the current thread
|
||||
/// - **machine** the machine where threads are executed
|
||||
#[allow(unused)]
|
||||
pub fn wait(&mut self, machine: &mut Machine, thread_manager: &mut ThreadManager) {
|
||||
pub fn wait(&mut self, current_thread: Rc<RefCell<Thread>>, machine: &mut Machine) {
|
||||
let old_status = machine.interrupt.set_status(InterruptOff);
|
||||
match thread_manager.get_g_current_thread() {
|
||||
Some(thread) => {
|
||||
let rc1 = Rc::clone(thread);
|
||||
let rc2 = Rc::clone(thread);
|
||||
self.waiting_queue.push(rc1);
|
||||
thread_manager.thread_sleep(machine, rc2);
|
||||
},
|
||||
None => unreachable!()
|
||||
}
|
||||
|
||||
self.waiting_queue.push(Rc::clone(¤t_thread));
|
||||
self.thread_manager.borrow_mut().thread_sleep(current_thread);
|
||||
|
||||
machine.interrupt.set_status(old_status);
|
||||
}
|
||||
@ -205,13 +210,11 @@ impl Condition {
|
||||
/// ### Parameters
|
||||
/// - **machine** the machine where the code is executed
|
||||
/// - **scheduler** the scheduler which determine which thread to execute
|
||||
#[allow(unused)]
|
||||
pub fn signal(&mut self, machine: &mut Machine, thread_manager: &mut ThreadManager) {
|
||||
pub fn signal(&mut self, machine: &mut Machine, scheduler: &mut Scheduler) {
|
||||
let old_status = machine.interrupt.set_status(InterruptOff);
|
||||
|
||||
match self.waiting_queue.pop() {
|
||||
Some(thread) => thread_manager.ready_to_run(thread),
|
||||
None => ()
|
||||
if self.waiting_queue.peek() != None {
|
||||
scheduler.ready_to_run(self.waiting_queue.pop().unwrap());
|
||||
}
|
||||
|
||||
machine.interrupt.set_status(old_status);
|
||||
@ -224,13 +227,11 @@ impl Condition {
|
||||
/// ### Parameters
|
||||
/// - **machine** the machine where the code is executed
|
||||
/// - **scheduler** the scheduler which determine which thread to execute
|
||||
#[allow(unused)]
|
||||
pub fn broadcast(&mut self, machine: &mut Machine, thread_manager: &mut ThreadManager) {
|
||||
pub fn broadcast(&mut self, machine: &mut Machine, scheduler: &mut Scheduler) {
|
||||
let old_status = machine.interrupt.set_status(InterruptOff);
|
||||
|
||||
match self.waiting_queue.pop() {
|
||||
Some(thread) => thread_manager.ready_to_run(thread),
|
||||
None => ()
|
||||
while self.waiting_queue.peek() != None {
|
||||
scheduler.ready_to_run(self.waiting_queue.pop().unwrap());
|
||||
}
|
||||
machine.interrupt.set_status(old_status);
|
||||
|
||||
@ -242,66 +243,128 @@ impl Condition {
|
||||
mod test {
|
||||
use std::{rc::Rc, cell::RefCell};
|
||||
|
||||
use crate::{kernel::{thread::Thread, synch::Lock, thread_manager::ThreadManager}, simulator::machine::Machine, utility::cfg::get_debug_configuration};
|
||||
use crate::{kernel::{thread::Thread, synch::{Semaphore, Lock}}, init_system, simulator::machine::Machine};
|
||||
|
||||
#[test]
|
||||
fn test_semaphore_single() {
|
||||
// Init
|
||||
let system = init_system!();
|
||||
let mut semaphore = Semaphore::new(1, Rc::clone(&system.borrow_mut().get_thread_manager()));
|
||||
let thread = Rc::new(RefCell::new(Thread::new("test_semaphore")));
|
||||
// P
|
||||
semaphore.p(thread, Rc::clone(&system));
|
||||
assert_eq!(semaphore.counter, 0);
|
||||
assert!(semaphore.waiting_queue.is_empty());
|
||||
// V
|
||||
semaphore.v(Rc::clone(&system));
|
||||
assert_eq!(semaphore.counter, 1);
|
||||
assert!(semaphore.waiting_queue.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_semaphore_multiple() {
|
||||
// Init
|
||||
let system = init_system!();
|
||||
let tm = system.borrow_mut().get_thread_manager();
|
||||
let mut semaphore = Semaphore::new(2, Rc::clone(&tm));
|
||||
let thread1 = Rc::new(RefCell::new(Thread::new("test_semaphore_1")));
|
||||
let thread2 = Rc::new(RefCell::new(Thread::new("test_semaphore_2")));
|
||||
let thread3 = Rc::new(RefCell::new(Thread::new("test_semaphore_3")));
|
||||
|
||||
let mut borrow_tm = tm.borrow_mut();
|
||||
let scheduler = &mut borrow_tm.g_scheduler;
|
||||
scheduler.ready_to_run(Rc::clone(&thread1));
|
||||
scheduler.ready_to_run(Rc::clone(&thread2));
|
||||
scheduler.ready_to_run(Rc::clone(&thread3));
|
||||
// P
|
||||
borrow_tm.set_g_current_thread(Some(Rc::clone(&thread1)));
|
||||
semaphore.p(thread1, Rc::clone(&system));
|
||||
assert_eq!(semaphore.counter, 1);
|
||||
assert!(semaphore.waiting_queue.is_empty());
|
||||
|
||||
borrow_tm.set_g_current_thread(Some(Rc::clone(&thread2)));
|
||||
semaphore.p(thread2, Rc::clone(&system));
|
||||
assert_eq!(semaphore.counter, 0);
|
||||
assert!(semaphore.waiting_queue.is_empty());
|
||||
|
||||
borrow_tm.set_g_current_thread(Some(Rc::clone(&thread3)));
|
||||
semaphore.p(thread3, Rc::clone(&system));
|
||||
assert_eq!(semaphore.counter, -1);
|
||||
assert!(semaphore.waiting_queue.iter().count() == 1);
|
||||
|
||||
// V
|
||||
semaphore.v(Rc::clone(&system));
|
||||
assert_eq!(semaphore.counter, 0);
|
||||
assert!(semaphore.waiting_queue.is_empty());
|
||||
|
||||
semaphore.v(Rc::clone(&system));
|
||||
assert_eq!(semaphore.counter, 1);
|
||||
assert!(semaphore.waiting_queue.is_empty());
|
||||
|
||||
semaphore.v(Rc::clone(&system));
|
||||
assert_eq!(semaphore.counter, 2);
|
||||
assert!(semaphore.waiting_queue.is_empty());
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_lock_simple() {
|
||||
let mut machine = Machine::new(true, get_debug_configuration());
|
||||
let mut tm = ThreadManager::new(true);
|
||||
let system = init_system!();
|
||||
let sys = system.borrow_mut();
|
||||
let tm = sys.get_thread_manager();
|
||||
let thread = Rc::new(RefCell::new(Thread::new("test_lock")));
|
||||
tm.ready_to_run(Rc::clone(&thread));
|
||||
tm.set_g_current_thread(Some(Rc::clone(&thread)));
|
||||
let mut lock = Lock::new();
|
||||
tm.borrow_mut().set_g_current_thread(Some(Rc::clone(&thread)));
|
||||
let mut lock = Lock::new(Rc::clone(&tm));
|
||||
|
||||
assert!(lock.free);
|
||||
lock.acquire(&mut machine, &mut tm);
|
||||
assert!(lock.held_by_current_thread(&mut tm));
|
||||
lock.acquire(Some(Rc::clone(&thread)), Rc::clone(&system));
|
||||
assert!(lock.held_by_current_thread(Rc::clone(&thread)));
|
||||
|
||||
assert!(!lock.free);
|
||||
lock.release(&mut machine, &mut tm);
|
||||
assert!(!lock.held_by_current_thread(&mut tm));
|
||||
lock.release(Rc::clone(&system), Rc::clone(&thread));
|
||||
assert!(!lock.held_by_current_thread(thread));
|
||||
assert!(lock.free);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_lock_multiple() {
|
||||
let system = init_system!();
|
||||
let thread1 = Rc::new(RefCell::new(Thread::new("test_lock1")));
|
||||
let thread2 = Rc::new(RefCell::new(Thread::new("test_lock2")));
|
||||
let thread3 = Rc::new(RefCell::new(Thread::new("test_lock3")));
|
||||
|
||||
let mut machine = Machine::new(true, get_debug_configuration());
|
||||
let mut tm = ThreadManager::new(true);
|
||||
|
||||
tm.ready_to_run(Rc::clone(&thread1));
|
||||
tm.ready_to_run(Rc::clone(&thread2));
|
||||
|
||||
tm.set_g_current_thread(Some(Rc::clone(&thread1)));
|
||||
let mut lock = Lock::new();
|
||||
let tm = system.borrow_mut().get_thread_manager();
|
||||
tm.borrow_mut().set_g_current_thread(Some(Rc::clone(&thread1)));
|
||||
let mut lock = Lock::new(Rc::clone(&tm));
|
||||
|
||||
assert!(lock.free);
|
||||
lock.acquire(&mut machine, &mut tm);
|
||||
assert!(lock.held_by_current_thread(&mut tm));
|
||||
lock.acquire(Some(Rc::clone(&thread1)), Rc::clone(&system));
|
||||
assert!(lock.held_by_current_thread(Rc::clone(&thread1)));
|
||||
assert!(!lock.free);
|
||||
|
||||
tm.set_g_current_thread(Some(Rc::clone(&thread2)));
|
||||
lock.acquire(&mut machine, &mut tm);
|
||||
tm.borrow_mut().set_g_current_thread(Some(Rc::clone(&thread2)));
|
||||
lock.acquire(Some(Rc::clone(&thread2)), Rc::clone(&system));
|
||||
tm.borrow_mut().set_g_current_thread(Some(Rc::clone(&thread1)));
|
||||
|
||||
|
||||
tm.set_g_current_thread(Some(Rc::clone(&thread1)));
|
||||
assert!(lock.held_by_current_thread(&mut tm));
|
||||
assert!(lock.held_by_current_thread(Rc::clone(&thread1)));
|
||||
assert!(lock.waiting_queue.iter().count() == 1);
|
||||
assert!(!lock.free);
|
||||
|
||||
lock.release(&mut machine, &mut tm);
|
||||
assert!(!lock.held_by_current_thread(&mut tm));
|
||||
|
||||
tm.set_g_current_thread(Some(Rc::clone(&thread2)));
|
||||
assert!(lock.held_by_current_thread(&mut tm));
|
||||
lock.release(Rc::clone(&system), Rc::clone(&thread1));
|
||||
assert!(!lock.held_by_current_thread(thread1));
|
||||
assert!(lock.held_by_current_thread(Rc::clone(&thread2)));
|
||||
assert!(!lock.free);
|
||||
|
||||
lock.release(&mut machine, &mut tm);
|
||||
assert!(!lock.held_by_current_thread(&mut tm));
|
||||
tm.borrow_mut().set_g_current_thread(Some(Rc::clone(&thread2)));
|
||||
|
||||
|
||||
lock.release(Rc::clone(&system), Rc::clone(&thread2));
|
||||
assert!(!lock.held_by_current_thread(thread2));
|
||||
assert!(lock.free);
|
||||
}
|
||||
}
|
@ -2,7 +2,26 @@
|
||||
//!
|
||||
//! Module containing structs and methods pertaining to the state of the operating system
|
||||
|
||||
use super::{thread_manager::ThreadManager};
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use crate::simulator::machine::Machine;
|
||||
|
||||
use super::thread_manager::ThreadManager;
|
||||
|
||||
/// This macro properly initializes the system
|
||||
#[macro_export]
|
||||
macro_rules! init_system {
|
||||
() => {{
|
||||
let m = Machine::init_machine();
|
||||
init_system!(m)
|
||||
}};
|
||||
($a:expr) => {{
|
||||
let sys = std::rc::Rc::new(std::cell::RefCell::new(crate::System::new($a)));
|
||||
crate::System::freeze(std::rc::Rc::clone(&sys));
|
||||
sys
|
||||
|
||||
}};
|
||||
}
|
||||
|
||||
/// # System
|
||||
///
|
||||
@ -14,20 +33,70 @@ use super::{thread_manager::ThreadManager};
|
||||
/// - The list of active threads
|
||||
/// - The thread to be destroyed next
|
||||
/// - The scheduler which acts upon these threads
|
||||
#[derive(PartialEq)]
|
||||
pub struct System {
|
||||
thread_manager: ThreadManager
|
||||
g_machine: RefCell<Machine>,
|
||||
thread_manager: Rc<RefCell<ThreadManager>>
|
||||
}
|
||||
|
||||
impl System {
|
||||
|
||||
pub fn new(debug: bool) -> Self {
|
||||
Self { thread_manager: ThreadManager::new(debug) }
|
||||
/// System constructor
|
||||
pub fn new(machine: Machine) -> System {
|
||||
Self {
|
||||
g_machine: RefCell::new(machine),
|
||||
thread_manager: Rc::new(RefCell::new(ThreadManager::new()))
|
||||
}
|
||||
}
|
||||
|
||||
/// use thread_manager setter to send it system instance
|
||||
pub fn freeze(this: Rc<RefCell<System>>) {
|
||||
let copy = Rc::clone(&this);
|
||||
let tm = &this.borrow_mut().thread_manager;
|
||||
tm.borrow_mut().system = Option::Some(copy);
|
||||
ThreadManager::freeze(tm);
|
||||
}
|
||||
|
||||
// GETTERS
|
||||
|
||||
pub fn get_thread_manager(&mut self) -> &mut ThreadManager {
|
||||
&mut self.thread_manager
|
||||
/// Returns the Machine
|
||||
///
|
||||
/// Useful to access RAM, devices, ...
|
||||
pub fn get_g_machine(&self) -> &RefCell<Machine> {
|
||||
&self.g_machine
|
||||
}
|
||||
|
||||
pub fn get_thread_manager(&self) -> Rc<RefCell<ThreadManager>> {
|
||||
Rc::clone(&self.thread_manager)
|
||||
}
|
||||
|
||||
// Setters
|
||||
|
||||
/// Assign a machine to the system
|
||||
pub fn set_g_machine(&mut self, machine: RefCell<Machine>) {
|
||||
self.g_machine = machine
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub enum ObjectType {
|
||||
SemaphoreType,
|
||||
LockType,
|
||||
ConditionType,
|
||||
FileType,
|
||||
ThreadType,
|
||||
InvalidType
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use crate::{System, Machine};
|
||||
|
||||
#[test]
|
||||
fn test_init_system() {
|
||||
init_system!();
|
||||
}
|
||||
|
||||
}
|
@ -1,10 +1,5 @@
|
||||
//! # Thread
|
||||
//!
|
||||
//!
|
||||
use std::{rc::Rc, cell::RefCell};
|
||||
|
||||
use super::{process::Process, thread_manager::ThreadRef};
|
||||
use crate::{simulator::machine::{NUM_INT_REGS, NUM_FP_REGS, STACK_REG}, utility::list::List};
|
||||
use super::{process::Process, system::ObjectType, thread_manager::SIMULATORSTACKSIZE};
|
||||
use crate::{simulator::machine::{NUM_INT_REGS, NUM_FP_REGS, STACK_REG}};
|
||||
|
||||
const STACK_FENCEPOST: u32 = 0xdeadbeef;
|
||||
|
||||
@ -21,15 +16,17 @@ macro_rules! get_new_thread {
|
||||
pub struct ThreadContext {
|
||||
pub int_registers: [i64; NUM_INT_REGS],
|
||||
pub float_registers: [f32; NUM_FP_REGS],
|
||||
pub pc: u64,
|
||||
pc: i64,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct Thread {
|
||||
name: String,
|
||||
pub process: Option<Rc<RefCell<Process>>>,
|
||||
pub process: Option<Process>,
|
||||
// simulation_context: UContextT,
|
||||
pub thread_context: ThreadContext,
|
||||
pub join_thread: List<ThreadRef>,
|
||||
pub stack_pointer: i32,
|
||||
object_type: ObjectType
|
||||
}
|
||||
|
||||
impl Thread {
|
||||
@ -43,16 +40,32 @@ impl Thread {
|
||||
thread_context: ThreadContext {
|
||||
int_registers: [0; NUM_INT_REGS],
|
||||
float_registers: [0f32; NUM_FP_REGS],
|
||||
pc: 0,
|
||||
pc: 0
|
||||
},
|
||||
join_thread: List::default(),
|
||||
stack_pointer: 0,
|
||||
object_type: ObjectType::ThreadType,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_thread_context(&mut self, initial_pc_reg: u64, initial_sp: u64, arg: i64) {
|
||||
pub fn init_thread_context(&mut self, initial_pc_reg: i64, initial_sp: i64, arg: i64) {
|
||||
self.thread_context.pc = initial_pc_reg;
|
||||
self.thread_context.int_registers[10] = arg;
|
||||
self.thread_context.int_registers[STACK_REG] = initial_sp as i64;
|
||||
self.thread_context.int_registers[STACK_REG] = initial_sp;
|
||||
}
|
||||
|
||||
pub fn init_simulator_context(&self, base_stack_addr: [i8; SIMULATORSTACKSIZE]) {
|
||||
// let res = self.simulation_context.get_context();
|
||||
// if res != 0 {
|
||||
// panic!("getcontext returns non-zero value {}", res);
|
||||
// }
|
||||
// self.simulation_context.buf.uc_stack.ss_sp = base_stack_addr;
|
||||
// self.simulation_context.buf.uc_stack.ss_size = base_stack_addr.len();
|
||||
// self.simulation_context.buf.uc_stack.ss_flags = 0;
|
||||
// self.simulation_context.buf.uc_link = UContextT::new().buf;
|
||||
// self.simulation_context.make_context(start_thread_execution, 0);
|
||||
|
||||
// self.simulation_context.stackBottom = base_stack_addr.to_vec();
|
||||
// self.simulation_context.stackBottom[0] = STACK_FENCEPOST;
|
||||
}
|
||||
|
||||
/// Check if a thread has overflowed its stack
|
||||
@ -65,22 +78,37 @@ impl Thread {
|
||||
// }
|
||||
}
|
||||
|
||||
pub fn save_simulator_state(&self) {
|
||||
// todo!(); // simulator state will maybe be removed so panic call is remove. See ucontext.rs
|
||||
}
|
||||
|
||||
pub fn restore_simulator_state(&self) {
|
||||
// todo!(); // simulator state will maybe be removed so panic call is remove. See ucontext.rs
|
||||
}
|
||||
|
||||
pub fn get_name(&self) -> String {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
/// Return reference to an optional Process
|
||||
/// can be None if Thread hasn't been initialize
|
||||
pub fn get_process_owner(&self) -> &Option<Rc<RefCell<Process>>> {
|
||||
&self.process
|
||||
}
|
||||
|
||||
impl Drop for Thread {
|
||||
|
||||
fn drop(&mut self) {
|
||||
self.object_type = ObjectType::InvalidType;
|
||||
// todo!();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fn start_thread_execution() {
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use super::{Thread, ThreadContext, NUM_INT_REGS, NUM_FP_REGS};
|
||||
use super::{Thread, ThreadContext, NUM_INT_REGS, NUM_FP_REGS, ObjectType};
|
||||
const DEFAULT_THREAD_NAME: &str = "test_thread";
|
||||
|
||||
/// This macro allows for getting a Thread for which we've ensured proper initial state
|
||||
@ -97,6 +125,8 @@ mod test {
|
||||
float_registers: [0f32; NUM_FP_REGS],
|
||||
pc: 0
|
||||
};
|
||||
x.stack_pointer = 0;
|
||||
x.object_type = ObjectType::ThreadType;
|
||||
x }
|
||||
};
|
||||
}
|
||||
|
@ -1,108 +1,10 @@
|
||||
//! # Thread manager
|
||||
//!
|
||||
//! This module describes the data structure and the methods used for thread scheduling
|
||||
//! in the BurritOS operating system. A struct named [`ThreadManager`] holds the list of
|
||||
//! all existing [`Thread`] instances and synchronization objects, such as
|
||||
//! [`Lock`](crate::kernel::synch::Lock),
|
||||
//! [`Semaphore`](crate::kernel::synch::Semaphore) and
|
||||
//! [`Condition`](crate::kernel::synch::Condition).
|
||||
//!
|
||||
//! ## Purpose
|
||||
//!
|
||||
//! [`ThreadManager`] holds the state of the system processes using the following subcomponents:
|
||||
//!
|
||||
//! ### Two lists of threads
|
||||
//!
|
||||
//! - **ready_list**: The list of threads ready to be executed
|
||||
//! - **g_alive**: The list of currently executing threads
|
||||
//!
|
||||
//! The difference between the two above lists lies in the state of the threads in question.
|
||||
//! Ready threads have just been enqueued. They are not being executed yet. The second list is
|
||||
//! needed because many threads may be executing at a given time. However, only a single thread
|
||||
//! can be handled by the machine at a time. The system thus needs to keep in memory the alive
|
||||
//! threads in case the currently running thread finishes or gets rescheduled.
|
||||
//!
|
||||
//! ### A list of synchronization objects
|
||||
//!
|
||||
//! Locks, Semaphores and Conditions allow resource sharing among running threads. Since resources
|
||||
//! can only be accessed by a single thread at a time, we need data structures to signal other
|
||||
//! threads that a resource may be busy or unavailable; say for example that:
|
||||
//!
|
||||
//! - Thread **A** wants to write to a file while **B** is currently reading said file.
|
||||
//! - Thread **A** mutating the state of the file could cause issues for **B**.
|
||||
//! - Therefore **B** needs to lock the file in question to avoid such issues.
|
||||
//! - Thread **A** will have to wait for **B** to finish reading the file.
|
||||
//!
|
||||
//! These synchronization objects are held in an instance of the ObjAddr structure held by
|
||||
//! ThreadManager. Their state is mutated depending on the actions of the currently running thread
|
||||
//! through methods such as [`ThreadManager::sem_p`].
|
||||
//!
|
||||
//! ## Usage
|
||||
//!
|
||||
//! [`ThreadManager`] is thought as a subcomponent of the [`System`](crate::kernel::system::System) struct.
|
||||
//! Instanciating [`System`](crate::kernel::system::System) will automatically instanciate a [`ThreadManager`]
|
||||
//!
|
||||
//! Manually loading a [`Thread`] into [`ThreadManager`] to execute a program with BurritOS could look like
|
||||
//! this:
|
||||
//!
|
||||
//! ```
|
||||
//! fn load_thread_manually(args: ...) {
|
||||
//! let mut system = System::new(args.debug);
|
||||
//!
|
||||
//! let thread_exec = Thread::new(args.executable.as_str());
|
||||
//! let thread_exec = Rc::new(RefCell::new(thread_exec));
|
||||
//! system.get_thread_manager().get_g_alive().push(Rc::clone(&thread_exec));
|
||||
//!
|
||||
//! let owner1 = Process { num_thread: 0 };
|
||||
//! let owner1 = Rc::new(RefCell::new(owner1));
|
||||
//! system.get_thread_manager().start_thread(Rc::clone(&thread_exec), owner1, loader.elf_header.entrypoint, ptr, -1);
|
||||
//!
|
||||
//! let to_run = system.get_thread_manager().find_next_to_run().unwrap();
|
||||
//! system.get_thread_manager().switch_to(&mut machine, Rc::clone(&to_run));
|
||||
//!
|
||||
//! machine.run(&mut system);
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Imports
|
||||
//!
|
||||
//! The [`List`] and [`ObjAddr`] submodules used in this module are defined in the utility
|
||||
//! module. The source code of [`ObjAddr`] has been decoupled from thread_manager in an effort
|
||||
//! to keep this module concise.
|
||||
use std::{rc::Rc, cell::{RefCell, RefMut, Ref}};
|
||||
|
||||
use std::{
|
||||
rc::Rc,
|
||||
cell::{
|
||||
RefCell,
|
||||
Ref
|
||||
}
|
||||
};
|
||||
use crate::{utility::list::List, simulator::{machine::{NUM_INT_REGS, NUM_FP_REGS}, interrupt::InterruptStatus}};
|
||||
|
||||
use crate::{
|
||||
utility::{
|
||||
list::List,
|
||||
objaddr::ObjAddr
|
||||
},
|
||||
simulator::{
|
||||
machine::{
|
||||
NUM_INT_REGS,
|
||||
NUM_FP_REGS,
|
||||
Machine
|
||||
},
|
||||
interrupt::InterruptStatus,
|
||||
error::{
|
||||
MachineOk,
|
||||
MachineError
|
||||
}
|
||||
},
|
||||
kernel::{
|
||||
thread::Thread,
|
||||
process::Process
|
||||
}
|
||||
};
|
||||
use super::{scheduler::Scheduler, thread::Thread, system::System, mgerror::ErrorCode, process::Process};
|
||||
|
||||
/// Using this type alias to simplify struct and method definitions
|
||||
pub type ThreadRef = Rc<RefCell<Thread>>;
|
||||
pub const SIMULATORSTACKSIZE: usize = 32 * 1024;
|
||||
|
||||
/// # Thread manager
|
||||
///
|
||||
@ -110,581 +12,166 @@ pub type ThreadRef = Rc<RefCell<Thread>>;
|
||||
#[derive(PartialEq)]
|
||||
pub struct ThreadManager {
|
||||
/// Current running thread
|
||||
pub g_current_thread: Option<ThreadRef>,
|
||||
pub g_current_thread: Option<Rc<RefCell<Thread>>>,
|
||||
/// The thread to be destroyed next
|
||||
pub g_thread_to_be_destroyed: Option<Rc<RefCell<Thread>>>,
|
||||
/// The list of alive threads
|
||||
pub g_alive: List<ThreadRef>,
|
||||
/// Thread in ready state waiting to become active
|
||||
ready_list: List<ThreadRef>,
|
||||
/// List of objects created by the thread manager (such as Locks and Semaphores)
|
||||
obj_addrs: ObjAddr,
|
||||
/// If true, enables debug mode
|
||||
debug: bool,
|
||||
/// Temporary field, to be removed when virtual memory will be available to use.
|
||||
///
|
||||
/// A value to know where the next starting thread should have its stack pointer
|
||||
sp_max: u64,
|
||||
pub g_alive: List<Rc<RefCell<Thread>>>,
|
||||
/// The thread scheduler
|
||||
pub g_scheduler: Scheduler,
|
||||
/// The system owning the thread manager
|
||||
pub system: Option<Rc<RefCell<System>>>
|
||||
}
|
||||
|
||||
impl ThreadManager {
|
||||
|
||||
/// Thread manager constructor
|
||||
pub fn new(debug: bool) -> Self {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
g_current_thread: Option::None,
|
||||
g_alive: List::default(),
|
||||
ready_list: List::default(),
|
||||
obj_addrs: ObjAddr::init(),
|
||||
debug,
|
||||
sp_max: 0
|
||||
g_thread_to_be_destroyed: Option::None,
|
||||
g_alive: List::new(),
|
||||
g_scheduler: Scheduler::new(),
|
||||
system: Option::None
|
||||
}
|
||||
}
|
||||
|
||||
/// Mark `thread` as ready, but not necessarily running yet.
|
||||
///
|
||||
/// Put it in the ready list, for later scheduling onto the CPU.
|
||||
///
|
||||
/// ## Pamameter
|
||||
///
|
||||
/// **thread** is the thread to be put on the ready list
|
||||
pub fn ready_to_run(&mut self, thread: ThreadRef) {
|
||||
self.ready_list.push(thread);
|
||||
}
|
||||
|
||||
/// Return the next thread to be scheduled onto the CPU.
|
||||
/// If there are no ready threads, return `Option::None`
|
||||
///
|
||||
/// Thread is removed from the ready list.
|
||||
///
|
||||
/// **return** Thread thread to be scheduled
|
||||
pub fn find_next_to_run(&mut self) -> Option<ThreadRef> {
|
||||
self.ready_list.pop()
|
||||
}
|
||||
|
||||
/// Dispatch the CPU to `next_thread`. Save the state of the old thread
|
||||
/// and load the state of the new thread.
|
||||
///
|
||||
/// We assume the state of the previously running thread has already been changed from running to blocked or ready.
|
||||
///
|
||||
/// Variable `g_current_thread` become next_thread
|
||||
///
|
||||
/// ## Parameter
|
||||
///
|
||||
/// **next_thread** thread to dispatch to the CPU
|
||||
pub fn switch_to(&mut self, machine: &mut Machine, next_thread: ThreadRef) {
|
||||
if let Some(old_thread) = self.get_g_current_thread() {
|
||||
let old_thread = old_thread.clone();
|
||||
self.thread_save_processor_state(machine, old_thread.clone());
|
||||
// old_thread.save_simulator_state();
|
||||
if old_thread != next_thread {
|
||||
self.debug(format!("switching from \"{}\" to \"{}\"", old_thread.borrow().get_name(), next_thread.borrow().get_name()));
|
||||
self.thread_restore_processor_state(machine, Rc::clone(&next_thread));
|
||||
// next_thread.restore_simulator_state();
|
||||
debug_assert!(!self.ready_list.contains(&next_thread));
|
||||
self.set_g_current_thread(Some(next_thread));
|
||||
}
|
||||
} else {
|
||||
self.thread_restore_processor_state(machine, Rc::clone(&next_thread));
|
||||
// next_thread.restore_simulator_state();
|
||||
self.set_g_current_thread(Some(next_thread));
|
||||
}
|
||||
pub fn freeze(this: &Rc<RefCell<ThreadManager>>) {
|
||||
let copy = Rc::clone(this);
|
||||
this.borrow_mut().g_scheduler.thread_manager = Option::Some(copy);
|
||||
}
|
||||
|
||||
/// Start a thread, attaching it to a process
|
||||
///
|
||||
/// ## Parameter
|
||||
///
|
||||
/// **thread** thread to start
|
||||
/// **owner** process owner of thread (after the execution of this method)
|
||||
/// **func_pc** pc the thread
|
||||
/// **sp_loc** stack pointer of the thread, to remove (or move) when mmu will be completed
|
||||
/// **argument** value to be place on register[10]
|
||||
pub fn start_thread(&mut self, thread: ThreadRef, owner: Rc<RefCell<Process>>, func_pc: u64, sp_loc: u64, argument: i64) {
|
||||
self.debug(format!("starting thread \"{}\"", thread.borrow().get_name()));
|
||||
|
||||
pub fn start_thread(&mut self, thread: Rc<RefCell<Thread>>, owner: Process, func_pc: i64, argument: i64) -> Result<(), ErrorCode> {
|
||||
let mut thread_m = thread.borrow_mut();
|
||||
assert_eq!(thread_m.process, Option::None);
|
||||
thread_m.process = Option::Some(Rc::clone(&owner));
|
||||
let ptr = sp_loc; // todo addrspace
|
||||
thread_m.process = Option::Some(owner);
|
||||
let ptr = 0; // todo addrspace
|
||||
thread_m.init_thread_context(func_pc, ptr, argument);
|
||||
owner.borrow_mut().num_thread += 1;
|
||||
let base_stack_addr: [i8; SIMULATORSTACKSIZE] = [0; SIMULATORSTACKSIZE]; // todo AllocBoundedArray
|
||||
thread_m.init_simulator_context(base_stack_addr);
|
||||
thread_m.process.as_mut().unwrap().num_thread += 1;
|
||||
self.get_g_alive().push(Rc::clone(&thread));
|
||||
self.ready_to_run(Rc::clone(&thread));
|
||||
self.g_scheduler.ready_to_run(Rc::clone(&thread));
|
||||
Result::Ok(())
|
||||
}
|
||||
|
||||
/// Wait for another thread to finish its execution
|
||||
///
|
||||
/// If the thread you want to wait doesn't exist (isn't alive), execution will resume.
|
||||
/// Otherwise, CPU is dispatch to next alive thread if any.
|
||||
///
|
||||
/// When the thread you want to join finish, it place the waiting thread (self) in ready list
|
||||
pub fn thread_join(&mut self, machine: &mut Machine, waiter: ThreadRef, waiting_for: ThreadRef) {
|
||||
let waiting_for = Rc::clone(&waiting_for);
|
||||
if self.get_g_alive().contains(&waiting_for) {
|
||||
waiting_for.borrow_mut().join_thread.push(Rc::clone(&waiter));
|
||||
self.thread_yield(machine, Rc::clone(&waiter), false);
|
||||
pub fn thread_join(&mut self, id_thread: Rc<RefCell<Thread>>) {
|
||||
while self.get_g_alive().contains(&Rc::clone(&id_thread)) {
|
||||
self.thread_yield(Rc::clone(&id_thread));
|
||||
}
|
||||
}
|
||||
|
||||
/// Relinquish the CPU if any other thread is runnable.
|
||||
///
|
||||
/// If so, put the current thread at the end of the ready list, so it'll be re-scheduled in the future.
|
||||
///
|
||||
/// **Returns** immediately if there's no other thread ready or return when the current thread has been switched.
|
||||
///
|
||||
/// Interruptions are disabled during the process, so all the process of looking for a next thread and switching to it is atomic,
|
||||
/// and is place at its old status at the end of the method.
|
||||
///
|
||||
/// Cannot use `yield` as a function name -> reserved name in rust
|
||||
///
|
||||
/// ## Parameters
|
||||
///
|
||||
/// **machine** RISC-V simulator
|
||||
/// **thread** current thread to be relinquish
|
||||
/// **is_ready** true if **thread** should be readded to ready_to_run list, false otherwise. Typically false when joining per example
|
||||
pub fn thread_yield(&mut self, machine: &mut Machine, thread: ThreadRef, is_ready: bool) {
|
||||
/// Cannot use yield as a function name -> reserved name in rust
|
||||
pub fn thread_yield(&mut self, thread: Rc<RefCell<Thread>>) {
|
||||
if let Some(system) = &self.system {
|
||||
let sys = system.borrow_mut();
|
||||
let mut machine = sys.get_g_machine().borrow_mut();
|
||||
let old_status = machine.interrupt.set_status(crate::simulator::interrupt::InterruptStatus::InterruptOff);
|
||||
|
||||
self.debug(format!("Yeilding thread: \"{}\"", thread.borrow().get_name()));
|
||||
debug_assert_eq!(&Option::Some(Rc::clone(&thread)), self.get_g_current_thread());
|
||||
let next_thread = self.find_next_to_run();
|
||||
assert_eq!(Option::Some(Rc::clone(&thread)), self.g_current_thread);
|
||||
let next_thread = self.g_scheduler.find_next_to_run();
|
||||
if let Some(next_thread) = next_thread {
|
||||
if is_ready {
|
||||
self.ready_to_run(thread);
|
||||
}
|
||||
self.switch_to(machine, next_thread);
|
||||
let scheduler = &mut self.g_scheduler;
|
||||
scheduler.ready_to_run(thread);
|
||||
scheduler.switch_to(next_thread);
|
||||
}
|
||||
machine.interrupt.set_status(old_status);
|
||||
}
|
||||
}
|
||||
|
||||
/// Put the thread to sleep and relinquish the processor because the current thread is blocked (Semaphore, Lock, Condition) or because it finished its execution
|
||||
///
|
||||
/// Another thread will eventually wake it up and put it back to ready list after it has been unblocked.
|
||||
///
|
||||
/// Behavior now: At the moment, disk isn't fully develop and not integrated to burritos, so if there's no ready thread, then we stop the OS.
|
||||
///
|
||||
/// Behaviour in the future: If there are no threads on the ready list, that means there is no thread to run,
|
||||
/// we assume this is because at least one thread is waiting for I/O [`interrupt`](crate::simulator::interrupt::Interrupt) (the only reason a new thread can become ready at this point).
|
||||
///
|
||||
/// We also assume interruption are already disabled, becuase it's called from a synchronization routine for interrupt should be disabled.
|
||||
pub fn thread_sleep(&mut self, machine: &mut Machine, thread: ThreadRef) {
|
||||
debug_assert_eq!(Option::Some(Rc::clone(&thread)), self.g_current_thread);
|
||||
debug_assert_eq!(machine.interrupt.get_status(), InterruptStatus::InterruptOff);
|
||||
/// Put the thread to sleep and relinquish the processor
|
||||
pub fn thread_sleep(&mut self, thread: Rc<RefCell<Thread>>) {
|
||||
|
||||
self.debug(format!("Sleeping thread {}", thread.borrow().get_name()));
|
||||
let mut next_thread = self.find_next_to_run();
|
||||
assert_eq!(Option::Some(Rc::clone(&thread)), self.g_current_thread);
|
||||
if let Some(system) = &self.system {
|
||||
let sys = system.borrow_mut();
|
||||
let machine = sys.get_g_machine().borrow_mut();
|
||||
assert_eq!(machine.interrupt.get_status(), InterruptStatus::InterruptOff);
|
||||
|
||||
let mut next_thread = self.g_scheduler.find_next_to_run();
|
||||
while next_thread.is_none() {
|
||||
eprintln!("Nobody to run => idle");
|
||||
machine.interrupt.idle();
|
||||
if let Some(t) = self.find_next_to_run() {
|
||||
next_thread = Some(t);
|
||||
} else {
|
||||
panic!("Couldn't find next thread to run.\nShutting down...");
|
||||
next_thread = self.g_scheduler.find_next_to_run();
|
||||
}
|
||||
}
|
||||
self.switch_to(machine, Rc::clone(&next_thread.unwrap()));
|
||||
self.g_scheduler.switch_to(Rc::clone(&next_thread.unwrap()));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// Finish the execution of the thread and prepare its deallocation
|
||||
///
|
||||
/// Called by the thread itself when it finish its execution ([`Exit`](super::exception::SC_EXIT) exception).
|
||||
///
|
||||
/// We remove the thread from the alive list, and rustc deallocate the thread itself(behaviour different than Nachos)
|
||||
///
|
||||
/// Interruption are disabled to assume atomicity.
|
||||
pub fn thread_finish(&mut self, machine: &mut Machine, thread: ThreadRef, exit_code: i64) {
|
||||
pub fn thread_finish(&mut self, thread: Rc<RefCell<Thread>>) {
|
||||
if let Some(system) = &self.system {
|
||||
let sys = Rc::clone(system);
|
||||
let sys = sys.borrow_mut();
|
||||
let mut machine = sys.get_g_machine().borrow_mut();
|
||||
let old_status = machine.interrupt.set_status(InterruptStatus::InterruptOff);
|
||||
assert!(self.g_alive.remove(Rc::clone(&thread)));
|
||||
self.debug(format!("Finishing thread {} with code {}", thread.borrow().get_name(), exit_code));
|
||||
self.g_thread_to_be_destroyed = Option::Some(Rc::clone(&thread));
|
||||
self.g_alive.remove(Rc::clone(&thread));
|
||||
// g_objets_addrs->removeObject(self.thread) // a ajouté plus tard
|
||||
for (_, el) in thread.borrow().join_thread.iter().enumerate() {
|
||||
self.ready_to_run(Rc::clone(&el));
|
||||
}
|
||||
self.thread_sleep(machine, Rc::clone(&thread));
|
||||
self.thread_sleep(Rc::clone(&thread));
|
||||
machine.interrupt.set_status(old_status);
|
||||
}
|
||||
}
|
||||
|
||||
/// Save the CPU state of a user program on a context switch.
|
||||
///
|
||||
/// Save PC and registers
|
||||
pub fn thread_save_processor_state(&mut self, machine: &mut Machine, thread: ThreadRef) {
|
||||
let mut t = thread.borrow_mut();
|
||||
pub fn thread_save_processor_state(&mut self, thread: Rc<RefCell<Thread>>) {
|
||||
if let Some(system) = &self.system {
|
||||
let mut t: RefMut<_> = thread.borrow_mut();
|
||||
let system = system.borrow_mut();
|
||||
for i in 0..NUM_INT_REGS {
|
||||
t.thread_context.int_registers[i] = machine.read_int_register(i);
|
||||
t.thread_context.int_registers[i] = system.get_g_machine().borrow().read_int_register(i);
|
||||
}
|
||||
for i in 0..NUM_FP_REGS {
|
||||
t.thread_context.float_registers[i] = machine.read_fp_register(i);
|
||||
t.thread_context.float_registers[i] = system.get_g_machine().borrow().read_fp_register(i);
|
||||
}
|
||||
} else {
|
||||
unreachable!("System is None")
|
||||
}
|
||||
t.thread_context.pc = machine.pc;
|
||||
}
|
||||
|
||||
/// Restore the CPU state of a user program on a context switch.
|
||||
///
|
||||
/// Restore PC and registers
|
||||
pub fn thread_restore_processor_state(&self, machine: &mut Machine, thread: ThreadRef) {
|
||||
pub fn thread_restore_processor_state(&self, thread: Rc<RefCell<Thread>>) {
|
||||
if let Some(system) = &self.system {
|
||||
let system = system.borrow_mut();
|
||||
let t: Ref<_> = thread.borrow();
|
||||
for i in 0..NUM_INT_REGS {
|
||||
let machine = system.get_g_machine();
|
||||
let mut machine = machine.borrow_mut();
|
||||
machine.write_int_register(i, t.thread_context.int_registers[i]);
|
||||
}
|
||||
machine.pc = t.thread_context.pc;
|
||||
}
|
||||
|
||||
/// Decrement the value, and wait if it becomes < 0. Checking the
|
||||
/// value and decrementing must be done atomically, so we
|
||||
/// need to disable interrupts before checking the value.
|
||||
///
|
||||
/// Note that thread_manager::thread_sleep assumes that interrupts are disabled
|
||||
/// when it is called.
|
||||
///
|
||||
/// ### Parameters
|
||||
/// - *id_sema* id of the semaphore, stored in [`ObjAddr`], id given by user program thought exceptions
|
||||
/// - *machine* Current state of the machine
|
||||
pub fn sem_p(&mut self, id_sema: i32, machine: &mut Machine) -> Result<MachineOk, MachineError> {
|
||||
let old_status = machine.interrupt.set_status(InterruptStatus::InterruptOff);
|
||||
let thread = match self.get_g_current_thread() {
|
||||
Some(thread) => Rc::clone(thread),
|
||||
None => Err("sem_p error: current thread should not be None")?
|
||||
};
|
||||
let sema = match self.get_obj_addrs().search_semaphore(id_sema) {
|
||||
Some(sema) => sema,
|
||||
None => Err("sem_p error: cannot find semaphore")?
|
||||
};
|
||||
sema.counter -= 1;
|
||||
if sema.counter < 0 {
|
||||
sema.waiting_queue.push(thread.clone());
|
||||
self.thread_sleep(machine, thread);
|
||||
}
|
||||
machine.interrupt.set_status(old_status);
|
||||
Ok(MachineOk::Ok)
|
||||
}
|
||||
|
||||
/// Increment semaphore value, waking up a waiting thread if any.
|
||||
/// As with P(), this operation must be atomic, so we need to disable
|
||||
/// interrupts.
|
||||
///
|
||||
/// scheduler::ready_to_run() assumes that interrupts
|
||||
/// are disabled when it is called.
|
||||
///
|
||||
/// ### Parameters
|
||||
/// - *id_sema* id of the semaphore, stored in [`ObjAddr`], id given by user program thought exceptions
|
||||
/// - **machine** the machine where the threads are executed
|
||||
pub fn sem_v(&mut self, id_sema: i32, machine: &mut Machine) -> Result<MachineOk, MachineError> {
|
||||
let sema = match self.get_obj_addrs().search_semaphore(id_sema) {
|
||||
Some(sema) => sema,
|
||||
None => Err("sem_v error: cannot find semaphore")?
|
||||
};
|
||||
let old_status = machine.interrupt.set_status(InterruptStatus::InterruptOff);
|
||||
sema.counter += 1;
|
||||
if let Some(thread) = sema.waiting_queue.pop() {
|
||||
self.ready_to_run(thread)
|
||||
}
|
||||
machine.interrupt.set_status(old_status);
|
||||
Ok(MachineOk::Ok)
|
||||
}
|
||||
|
||||
|
||||
/// Wait until the lock become free. Checking the
|
||||
/// state of the lock (free or busy) and modify it must be done
|
||||
/// atomically, so we need to disable interrupts before checking
|
||||
/// the value of free.
|
||||
///
|
||||
/// Note that thread_manager::thread_seep assumes that interrupts are disabled
|
||||
/// when it is called.
|
||||
///
|
||||
/// ### Parameters
|
||||
/// - **id** id of the lock, stored in [`ObjAddr`], id given by user program thought exceptions
|
||||
/// - **machine** the machine where the threads are executed
|
||||
pub fn lock_acquire(&mut self, id: i32, machine: &mut Machine) -> Result<MachineOk, MachineError> {
|
||||
let current_thread = match self.get_g_current_thread() {
|
||||
Some(thread) => Rc::clone(thread),
|
||||
None => Err("lock_acquire error: current_thread should not be None.")?
|
||||
};
|
||||
let old_status = machine.interrupt.set_status(InterruptStatus::InterruptOff);
|
||||
if let Some(lock) = self.get_obj_addrs().search_lock(id) {
|
||||
if lock.free {
|
||||
lock.free = false;
|
||||
lock.owner = Some(current_thread)
|
||||
} else {
|
||||
lock.waiting_queue.push(current_thread.clone());
|
||||
self.thread_sleep(machine, current_thread);
|
||||
unreachable!("System is None")
|
||||
}
|
||||
} else {
|
||||
Err("lock_acquire error: cannot find Lock.")?
|
||||
}
|
||||
machine.interrupt.set_status(old_status);
|
||||
Ok(MachineOk::Ok)
|
||||
}
|
||||
|
||||
/// Release lock hold by current thread and wake up a waiter if necessary, placing it on ready list, this thread now hold the lock.
|
||||
/// Currently running thread
|
||||
pub fn get_g_current_thread(&mut self) -> &mut Option<Rc<RefCell<Thread>>> {
|
||||
&mut self.g_current_thread
|
||||
}
|
||||
|
||||
/// Thread to be destroyed by [...]
|
||||
///
|
||||
/// If no thread is waiting for the lock, the lock is released
|
||||
pub fn lock_release(&mut self, id: i32, machine: &mut Machine) -> Result<MachineOk, MachineError> {
|
||||
let old_status = machine.interrupt.set_status(InterruptStatus::InterruptOff);
|
||||
let current_thread = match self.get_g_current_thread() {
|
||||
Some(thread) => Rc::clone(thread),
|
||||
None => Err(String::from("lock_release error: current_thread should not be None."))?
|
||||
};
|
||||
let mut lock = match self.get_obj_addrs().search_lock(id) {
|
||||
Some(lock) => lock,
|
||||
None => Err(String::from("lock_release error: cannot find lock."))?
|
||||
};
|
||||
if let Some(lock_owner) = &lock.owner {
|
||||
if current_thread.eq(lock_owner) { // is_held_by_current_thread
|
||||
match lock.waiting_queue.pop() {
|
||||
Some(th) => {
|
||||
lock.owner = Some(Rc::clone(&th));
|
||||
self.ready_to_run(Rc::clone(&th));
|
||||
},
|
||||
None => {
|
||||
lock.free = true;
|
||||
lock.owner = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
// self.get_obj_addrs().update_lock(id, lock);
|
||||
|
||||
machine.interrupt.set_status(old_status);
|
||||
Ok(MachineOk::Ok)
|
||||
/// TODO: Finish the comment with the relevant value
|
||||
pub fn get_g_thread_to_be_destroyed(&mut self) -> &mut Option<Rc<RefCell<Thread>>> {
|
||||
&mut self.g_thread_to_be_destroyed
|
||||
}
|
||||
|
||||
/// Return currently running thread
|
||||
pub fn get_g_current_thread(&mut self) -> &Option<ThreadRef> {
|
||||
&self.g_current_thread
|
||||
}
|
||||
|
||||
/// Return list of alive threads
|
||||
pub fn get_g_alive(&mut self) -> &mut List<ThreadRef> {
|
||||
/// List of alive threads
|
||||
pub fn get_g_alive(&mut self) -> &mut List<Rc<RefCell<Thread>>> {
|
||||
&mut self.g_alive
|
||||
}
|
||||
|
||||
/// Set currently running thread
|
||||
pub fn set_g_current_thread(&mut self, thread: Option<ThreadRef>) {
|
||||
pub fn set_g_current_thread(&mut self, thread: Option<Rc<RefCell<Thread>>>) {
|
||||
self.g_current_thread = thread
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the ObjAddr field of this thread_manager
|
||||
pub fn get_obj_addrs(&mut self) -> &mut ObjAddr {
|
||||
&mut self.obj_addrs
|
||||
}
|
||||
|
||||
/// Prints debug messages if self.debug is set to true.
|
||||
fn debug(&self, message: String) {
|
||||
if self.debug {
|
||||
println!("{}", message);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_sp_max(&self) -> u64 {
|
||||
self.sp_max
|
||||
}
|
||||
|
||||
pub fn set_sp_max(&mut self, sp_max: u64) {
|
||||
self.sp_max = sp_max;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use std::{rc::Rc, cell::RefCell};
|
||||
|
||||
use crate::{simulator::{machine::Machine, loader}, kernel::{system::System, thread::Thread, process::Process, thread_manager::ThreadManager, synch::Semaphore}, utility::cfg::get_debug_configuration};
|
||||
use crate::kernel::synch::Lock;
|
||||
|
||||
#[test]
|
||||
fn test_thread_context() {
|
||||
let mut machine = Machine::new(true, get_debug_configuration());
|
||||
|
||||
let (loader, ptr) = loader::Loader::new("./target/guac/halt.guac", &mut machine, 0).expect("IO Error");
|
||||
let start_pc = loader.elf_header.entrypoint;
|
||||
let system = &mut System::new(true);
|
||||
|
||||
let thread1 = Thread::new("th1");
|
||||
let thread1 = Rc::new(RefCell::new(thread1));
|
||||
system.get_thread_manager().get_g_alive().push(Rc::clone(&thread1));
|
||||
|
||||
let owner1 = Process { num_thread: 0 };
|
||||
let owner1 = Rc::new(RefCell::new(owner1));
|
||||
system.get_thread_manager().start_thread(Rc::clone(&thread1), owner1, loader.elf_header.entrypoint, ptr + machine.page_size, -1);
|
||||
debug_assert_eq!(thread1.borrow_mut().thread_context.pc, start_pc);
|
||||
debug_assert!(system.get_thread_manager().get_g_alive().contains(&Rc::clone(&thread1)));
|
||||
|
||||
let to_run = system.get_thread_manager().find_next_to_run().unwrap();
|
||||
debug_assert_eq!(to_run, Rc::clone(&thread1));
|
||||
|
||||
system.get_thread_manager().switch_to(&mut machine, Rc::clone(&to_run));
|
||||
debug_assert_eq!(system.get_thread_manager().g_current_thread, Option::Some(Rc::clone(&thread1)));
|
||||
debug_assert_eq!(machine.pc, loader.elf_header.entrypoint);
|
||||
|
||||
machine.run(system);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lock_single(){
|
||||
let mut machine = Machine::new(true, get_debug_configuration());
|
||||
let mut thread_manager = ThreadManager::new(true);
|
||||
let lock = Lock::new();
|
||||
let lock_id = thread_manager.get_obj_addrs().add_lock(lock);
|
||||
let thread = Rc::new(RefCell::new(Thread::new("test_lock")));
|
||||
let thread_test = thread.clone();
|
||||
thread_manager.ready_to_run(Rc::clone(&thread));
|
||||
thread_manager.set_g_current_thread(Some(thread));
|
||||
|
||||
thread_manager.lock_acquire(lock_id, &mut machine).expect("lock acquire return an error: ");
|
||||
{
|
||||
let lock = thread_manager.get_obj_addrs().search_lock(lock_id).unwrap();
|
||||
assert_eq!(lock.owner,Some(thread_test));
|
||||
assert!(!lock.free);
|
||||
assert!(lock.waiting_queue.is_empty());
|
||||
}
|
||||
|
||||
thread_manager.lock_release(lock_id, &mut machine).expect("lock release return an error: ");
|
||||
{
|
||||
let lock = thread_manager.get_obj_addrs().search_lock(lock_id).unwrap();
|
||||
assert_eq!(lock.owner, None);
|
||||
assert!(lock.free);
|
||||
assert!(lock.waiting_queue.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lock_multiple() {
|
||||
let mut machine = Machine::new(true, get_debug_configuration());
|
||||
let mut thread_manager = ThreadManager::new(true);
|
||||
let lock = Lock::new();
|
||||
let lock_id = thread_manager.get_obj_addrs().add_lock(lock);
|
||||
let thread_1 = Rc::new(RefCell::new(Thread::new("test_lock_1")));
|
||||
let thread_2 = Rc::new(RefCell::new(Thread::new("test_lock_2")));
|
||||
thread_manager.ready_to_run(thread_1.clone());
|
||||
thread_manager.ready_to_run(thread_2.clone());
|
||||
thread_manager.set_g_current_thread(Some(thread_1.clone()));
|
||||
|
||||
thread_manager.lock_acquire(lock_id, &mut machine).expect("lock acquire return an error at first iteration: ");
|
||||
{
|
||||
let lock = thread_manager.get_obj_addrs().search_lock(lock_id).unwrap();
|
||||
assert_eq!(lock.owner,Some(thread_1.clone()));
|
||||
assert!(!lock.free);
|
||||
assert!(lock.waiting_queue.is_empty());
|
||||
}
|
||||
|
||||
thread_manager.set_g_current_thread(Some(thread_2.clone()));
|
||||
thread_manager.lock_acquire(lock_id, &mut machine).expect("lock acquire return an error at second iteration: ");
|
||||
{
|
||||
let lock = thread_manager.get_obj_addrs().search_lock(lock_id).unwrap();
|
||||
assert_eq!(lock.owner,Some(thread_1.clone()));
|
||||
assert!(!lock.free);
|
||||
assert_eq!(lock.waiting_queue.iter().count(),1);
|
||||
}
|
||||
|
||||
thread_manager.lock_release(lock_id, &mut machine).expect("lock release return an error at first iteration: ");
|
||||
{
|
||||
let lock = thread_manager.get_obj_addrs().search_lock(lock_id).unwrap();
|
||||
assert_eq!(lock.owner, Some(thread_2.clone()));
|
||||
assert!(!lock.free);
|
||||
assert!(lock.waiting_queue.is_empty());
|
||||
}
|
||||
|
||||
thread_manager.set_g_current_thread(Some(thread_2.clone()));
|
||||
thread_manager.lock_release(lock_id, &mut machine).expect("lock release return an error at second iteration: ");
|
||||
{
|
||||
let lock = thread_manager.get_obj_addrs().search_lock(lock_id).unwrap();
|
||||
assert!(lock.waiting_queue.is_empty());
|
||||
assert_eq!(lock.owner, None);
|
||||
assert!(lock.free);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_semaphore_single() {
|
||||
// Init
|
||||
let mut machine = Machine::new(true, get_debug_configuration());
|
||||
let mut thread_manager = ThreadManager::new(true);
|
||||
let semaphore = Semaphore::new(1);
|
||||
let sema_id = thread_manager.get_obj_addrs().add_semaphore(semaphore);
|
||||
let thread = Rc::new(RefCell::new(Thread::new("test_semaphore")));
|
||||
thread_manager.ready_to_run(Rc::clone(&thread));
|
||||
thread_manager.set_g_current_thread(Some(thread));
|
||||
// P
|
||||
thread_manager.sem_p(sema_id, &mut machine).expect("semaphore P return an error: ");
|
||||
{
|
||||
let semaphore = thread_manager.get_obj_addrs().search_semaphore(sema_id).unwrap();
|
||||
assert_eq!(semaphore.counter, 0);
|
||||
assert!(semaphore.waiting_queue.is_empty());
|
||||
}
|
||||
// V
|
||||
thread_manager.sem_v(sema_id, &mut machine).expect("semaphore V return an error: ");
|
||||
{
|
||||
let semaphore = thread_manager.get_obj_addrs().search_semaphore(sema_id).unwrap();
|
||||
assert_eq!(semaphore.counter, 1);
|
||||
assert!(semaphore.waiting_queue.is_empty());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_semaphore_multiple() {
|
||||
// Init
|
||||
let mut tm = ThreadManager::new(true);
|
||||
let mut machine = Machine::new(true, get_debug_configuration());
|
||||
let semaphore = Semaphore::new(2);
|
||||
let sema_id = tm.get_obj_addrs().add_semaphore(semaphore);
|
||||
let thread1 = Rc::new(RefCell::new(Thread::new("test_semaphore_1")));
|
||||
let thread2 = Rc::new(RefCell::new(Thread::new("test_semaphore_2")));
|
||||
let thread3 = Rc::new(RefCell::new(Thread::new("test_semaphore_3")));
|
||||
|
||||
// let mut borrow_tm = tm.borrow_mut();
|
||||
// let scheduler = &mut tm.g_scheduler;
|
||||
tm.ready_to_run(Rc::clone(&thread1));
|
||||
tm.ready_to_run(Rc::clone(&thread2));
|
||||
tm.ready_to_run(Rc::clone(&thread3));
|
||||
// P
|
||||
tm.set_g_current_thread(Some(Rc::clone(&thread1)));
|
||||
tm.sem_p(sema_id, &mut machine).expect("semaphore P return an error: ");
|
||||
{
|
||||
let semaphore = tm.get_obj_addrs().search_semaphore(sema_id).unwrap();
|
||||
assert_eq!(semaphore.counter, 1);
|
||||
assert!(semaphore.waiting_queue.is_empty());
|
||||
}
|
||||
|
||||
tm.set_g_current_thread(Some(Rc::clone(&thread2)));
|
||||
tm.sem_p(sema_id, &mut machine).expect("semaphore P return an error: ");
|
||||
{
|
||||
let semaphore = tm.get_obj_addrs().search_semaphore(sema_id).unwrap();
|
||||
assert_eq!(semaphore.counter, 0);
|
||||
assert!(semaphore.waiting_queue.is_empty());
|
||||
}
|
||||
|
||||
tm.set_g_current_thread(Some(Rc::clone(&thread3)));
|
||||
tm.sem_p( sema_id, &mut machine).expect("semaphore P return an error: ");
|
||||
{
|
||||
let semaphore = tm.get_obj_addrs().search_semaphore(sema_id).unwrap();
|
||||
assert_eq!(semaphore.counter, -1);
|
||||
assert!(semaphore.waiting_queue.iter().count() == 1);
|
||||
}
|
||||
|
||||
// V
|
||||
tm.sem_v(sema_id, &mut machine).expect("semaphore V return an error: ");
|
||||
{
|
||||
let semaphore = tm.get_obj_addrs().search_semaphore(sema_id).unwrap();
|
||||
assert_eq!(semaphore.counter, 0);
|
||||
assert!(semaphore.waiting_queue.is_empty());
|
||||
}
|
||||
|
||||
tm.sem_v(sema_id, &mut machine).expect("semaphore V return an error: ");
|
||||
{
|
||||
let semaphore = tm.get_obj_addrs().search_semaphore(sema_id).unwrap();
|
||||
assert_eq!(semaphore.counter, 1);
|
||||
assert!(semaphore.waiting_queue.is_empty());
|
||||
}
|
||||
|
||||
tm.sem_v(sema_id, &mut machine).expect("semaphore V return an error: ");
|
||||
{
|
||||
let semaphore = tm.get_obj_addrs().search_semaphore(sema_id).unwrap();
|
||||
assert_eq!(semaphore.counter, 2);
|
||||
assert!(semaphore.waiting_queue.is_empty());
|
||||
}
|
||||
|
||||
/// Set thread to be destroyed next
|
||||
pub fn set_g_thread_to_be_destroyed(&mut self, thread: Option<Rc<RefCell<Thread>>>) {
|
||||
self.g_thread_to_be_destroyed = thread
|
||||
}
|
||||
|
||||
}
|
74
src/kernel/ucontext.rs
Normal file
74
src/kernel/ucontext.rs
Normal file
@ -0,0 +1,74 @@
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
use std::mem::MaybeUninit;
|
||||
|
||||
/// Safe wrapper for ucontext_t struct of linux-gnu libc
|
||||
///
|
||||
/// setcontext and getcontext are unsafe function, this wrap unsafe libc functions
|
||||
///
|
||||
/// This struct doesn't work on windows, because this struct is unavailable
|
||||
///
|
||||
/// todo ucontext_t is not thread-safe (doesn't implements Send and Sync trait), and cannot be use in Threads as rust require var in Mutex (see system.rs) to have everything inside to implements thread-safe traits
|
||||
#[derive(PartialEq)]
|
||||
pub struct UContextT {
|
||||
#[cfg(not(target_os = "windows"))] // struct non disponible sur la libc sur windows
|
||||
pub buf: libc::ucontext_t,
|
||||
pub stack_bottom: Vec<i8>
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
#[allow(unused)] // Temporary as we currently doesn't use this structure (this structure may disapear in a near future)
|
||||
impl UContextT {
|
||||
|
||||
pub fn new() -> Self {
|
||||
let mut context = MaybeUninit::<libc::ucontext_t>::uninit();
|
||||
unsafe { libc::getcontext(context.as_mut_ptr()) };
|
||||
Self {
|
||||
buf: unsafe { context.assume_init() },
|
||||
stack_bottom: Vec::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get user context and store it in variable pointed to by UCP.
|
||||
///
|
||||
/// Use `man getcontext` for more informations
|
||||
pub fn get_context(&mut self) -> i32 {
|
||||
unsafe {
|
||||
libc::getcontext(&mut self.buf)
|
||||
}
|
||||
}
|
||||
|
||||
/// Set user context from information of variable pointed to by UCP.
|
||||
///
|
||||
/// Use `man setcontext` for more informations
|
||||
pub fn set_context(&mut self) -> i32 {
|
||||
unsafe {
|
||||
libc::setcontext(&self.buf)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_context(&mut self, func: extern "C" fn(), args: i32) {
|
||||
unsafe {
|
||||
libc::makecontext(&mut self.buf, func, args)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[cfg(target_os = "windows")]
|
||||
#[allow(unused)]
|
||||
impl UContextT {
|
||||
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
stack_bottom: Vec::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_context(&mut self) {
|
||||
// no op
|
||||
}
|
||||
|
||||
pub fn set_context(&mut self) {
|
||||
// no op
|
||||
}
|
||||
}
|
75
src/main.rs
75
src/main.rs
@ -1,7 +1,4 @@
|
||||
#![doc(
|
||||
html_logo_url = "https://gitlab.istic.univ-rennes1.fr/simpleos/burritos/-/raw/main/assets/logo/logo.svg",
|
||||
html_favicon_url = "https://gitlab.istic.univ-rennes1.fr/simpleos/burritos/-/raw/main/assets/logo/logo.svg")
|
||||
]
|
||||
|
||||
#![warn(missing_docs)]
|
||||
#![warn(clippy::missing_docs_in_private_items)]
|
||||
|
||||
@ -10,58 +7,50 @@
|
||||
//! Burritos is an educational operating system written in Rust
|
||||
//! running on RISC-V emulator.
|
||||
|
||||
/// Contain hardware simulated part of the machine
|
||||
mod simulator;
|
||||
mod kernel;
|
||||
/// module containing useful tools which can be use in most part of the OS to ease the development of the OS
|
||||
pub mod utility;
|
||||
/// module containing drivers
|
||||
pub mod drivers;
|
||||
mod filesys;
|
||||
|
||||
use std::{rc::Rc, cell::RefCell};
|
||||
|
||||
use kernel::{system::System, thread::Thread, process::Process};
|
||||
use simulator::{machine::Machine, loader};
|
||||
use kernel::system::System;
|
||||
use simulator::{machine::Machine, print::print};
|
||||
|
||||
|
||||
use clap::Parser;
|
||||
use utility::cfg::read_settings;
|
||||
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(name = "BurritOS", author, version, about = "Burritos (BurritOS Using Rust Really Improves The Operating System)
|
||||
|
||||
Burritos is an educational operating system written in Rust
|
||||
running on RISC-V emulator.", long_about = None)]
|
||||
/// Launch argument parser
|
||||
struct Args {
|
||||
/// Enable debug mode.
|
||||
/// 0 to disable debug,
|
||||
/// 1 to enable machine debug,
|
||||
/// 2 to enable system debug,
|
||||
/// 3 to enable all debug
|
||||
#[arg(short, long, value_parser = clap::value_parser!(u8).range(0..=3), default_value_t = 0)]
|
||||
debug: u8,
|
||||
/// Path to the executable binary file to execute
|
||||
#[arg(short = 'x', long, value_name = "PATH")]
|
||||
executable: String
|
||||
}
|
||||
use drivers::drv_disk::DrvDisk;
|
||||
use simulator::disk::Disk;
|
||||
use filesys::fsmisc;
|
||||
|
||||
fn main() {
|
||||
let args = Args::parse();
|
||||
fsmisc::copy("ici", "là");
|
||||
/*
|
||||
let machine = Machine::init_machine();
|
||||
let system = Rc::new(RefCell::new(System::new(machine)));
|
||||
|
||||
let mut machine = Machine::new(args.debug & 1 != 0, read_settings().unwrap());
|
||||
let (loader, ptr) = loader::Loader::new(args.executable.as_str(), &mut machine, 0).expect("An error occured while parsing the program");
|
||||
let disk = Disk::init_disk();
|
||||
let mut drv_disk = DrvDisk::init_drv_disk(disk);
|
||||
|
||||
let mut system = System::new(args.debug & 2 != 0);
|
||||
let mut data = Vec::new();
|
||||
data.push(0 as u8); data.push(0 as u8); data.push(0 as u8); data.push(0 as u8);
|
||||
|
||||
let thread_exec = Thread::new(args.executable.as_str());
|
||||
let thread_exec = Rc::new(RefCell::new(thread_exec));
|
||||
system.get_thread_manager().get_g_alive().push(Rc::clone(&thread_exec));
|
||||
let mut data1 = Vec::new();
|
||||
data1.push(1 as u8); data1.push(1 as u8); data1.push(1 as u8); data1.push(1 as u8);
|
||||
|
||||
let owner1 = Process { num_thread: 0 };
|
||||
let owner1 = Rc::new(RefCell::new(owner1));
|
||||
let sp_max = ptr + machine.user_stack_size;
|
||||
system.get_thread_manager().set_sp_max(sp_max);
|
||||
system.get_thread_manager().start_thread(Rc::clone(&thread_exec), owner1, loader.elf_header.entrypoint, sp_max, -1);
|
||||
let mut data2: Vec<u8> = Vec::new();
|
||||
|
||||
let to_run = system.get_thread_manager().find_next_to_run().unwrap();
|
||||
system.get_thread_manager().switch_to(&mut machine, Rc::clone(&to_run));
|
||||
drv_disk.write_sector(0, &mut data);
|
||||
drv_disk.write_sector(1, &mut data1);
|
||||
|
||||
machine.run(&mut system);
|
||||
drv_disk.read_sector(1, &mut data2);
|
||||
|
||||
for value in data2 {
|
||||
println!("BYTE: {}", value);
|
||||
}
|
||||
|
||||
System::freeze(system);
|
||||
*/
|
||||
}
|
||||
|
95
src/simulator/decode.rs
Normal file
95
src/simulator/decode.rs
Normal file
@ -0,0 +1,95 @@
|
||||
use core::num::Wrapping; // Permet d'autoriser les overflow pour les opérations voulues
|
||||
|
||||
#[allow(non_snake_case)] // supprimer le warning snake case (quand les noms de variables ont des majuscules)
|
||||
#[derive(Debug)]
|
||||
pub struct Instruction {
|
||||
pub value : u64,
|
||||
|
||||
pub opcode : u8,
|
||||
pub rs1 : u8,
|
||||
pub rs2 : u8,
|
||||
pub rs3 : u8,
|
||||
pub rd : u8,
|
||||
pub funct7 : u8,
|
||||
pub funct7_smaller : u8,
|
||||
pub funct3 : u8,
|
||||
pub shamt : u8, // shamt = imm[5:0] or imm[4:0] (depend of opcode)
|
||||
|
||||
pub imm12_I : u16,
|
||||
pub imm12_S : u16,
|
||||
|
||||
pub imm12_I_signed : i16,
|
||||
pub imm12_S_signed : i16,
|
||||
pub imm13 : i16,
|
||||
pub imm13_signed : i16,
|
||||
|
||||
pub imm31_12 : u32,
|
||||
pub imm21_1 : u32,
|
||||
|
||||
pub imm31_12_signed : i32,
|
||||
pub imm21_1_signed : i32,
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
pub fn decode(val : u64) -> Instruction {
|
||||
|
||||
let value = val;
|
||||
|
||||
let opcode = (val & 0x7f) as u8;
|
||||
let rs1 = ((val >> 15) & 0x1f) as u8;
|
||||
let rs2 = ((val >> 20) & 0x1f) as u8;
|
||||
let rs3 = ((val >> 27) & 0x1f) as u8;
|
||||
let rd = ((val >> 7) & 0x1f) as u8;
|
||||
let funct7 = ((val >> 25) & 0x7f) as u8;
|
||||
let funct7_smaller = funct7 & 0x3e;
|
||||
|
||||
let funct3 = ((val >> 12) & 0x7) as u8;
|
||||
let imm12_I = ((val >> 20) & 0xfff) as u16;
|
||||
let imm12_S = (((val >> 20) & 0xfe0) + ((val >> 7) & 0x1f)) as u16;
|
||||
|
||||
let imm12_I_signed = if imm12_I >= 2048 { (Wrapping(imm12_I) - Wrapping(4096)).0 } else { imm12_I } as i16;
|
||||
let imm12_S_signed = if imm12_S >= 2048 { (Wrapping(imm12_S) - Wrapping(4096)).0 } else { imm12_S } as i16;
|
||||
|
||||
let imm13 = (((val >> 19) & 0x1000) + ((val >> 20) & 0x7e0) +
|
||||
((val >> 7) & 0x1e) + ((val << 4) & 0x800)) as i16;
|
||||
let imm13_signed = if imm13 >= 4096 { imm13 - 8192 } else { imm13 };
|
||||
|
||||
let imm31_12 = (val & 0xfffff000) as u32;
|
||||
let imm31_12_signed = imm31_12 as i32;
|
||||
|
||||
let imm21_1 = ((val & 0xff000) + ((val >> 9) & 0x800) +
|
||||
((val >> 20) & 0x7fe) + ((val >> 11) & 0x100000)) as u32;
|
||||
let imm21_1_signed = if imm21_1 >= 1048576 { (Wrapping(imm21_1) - Wrapping(2097152)).0 } else { imm21_1 } as i32;
|
||||
|
||||
let shamt = ((val >> 20) & 0x3f) as u8;
|
||||
|
||||
Instruction {
|
||||
value,
|
||||
|
||||
opcode,
|
||||
rs1,
|
||||
rs2,
|
||||
rs3,
|
||||
rd,
|
||||
funct7,
|
||||
funct7_smaller,
|
||||
|
||||
funct3,
|
||||
imm12_I,
|
||||
imm12_S,
|
||||
|
||||
imm12_I_signed,
|
||||
imm12_S_signed,
|
||||
|
||||
imm13,
|
||||
imm13_signed,
|
||||
|
||||
imm31_12,
|
||||
imm31_12_signed,
|
||||
|
||||
imm21_1,
|
||||
imm21_1_signed,
|
||||
|
||||
shamt
|
||||
}
|
||||
}
|
283
src/simulator/disk.rs
Normal file
283
src/simulator/disk.rs
Normal file
@ -0,0 +1,283 @@
|
||||
use core::panic;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::prelude::*;
|
||||
use std::io::SeekFrom;
|
||||
use std::os::unix::prelude::FileExt;
|
||||
|
||||
pub const SECTORS_PER_TRACK: i32 = 32;
|
||||
pub const NUM_TRACKS: i32 = 64;
|
||||
pub const NUM_SECTORS: i32 = SECTORS_PER_TRACK * NUM_TRACKS;
|
||||
pub const SECTOR_SIZE: i32 = 4; //4 octects ?
|
||||
pub const DISK_PATH: &str = "disk_file";
|
||||
|
||||
pub const MAGIC_NUMBER: u32 = 0xABBACDDC;
|
||||
//taille en octets, a confirmer
|
||||
pub const DISK_SIZE: i32 = 4 + NUM_SECTORS * SECTOR_SIZE; //4 <=> sizeof(u32)
|
||||
|
||||
pub struct Disk {
|
||||
pub last_sector: i32, //Secteur accédé lors de la précédente requête de lecture/ecriture
|
||||
pub active: bool, //C'est notre lock -> Une seule requête de lecture/ecriture à la fois
|
||||
pub disk_file: File, //On simule le disque sur un fichier
|
||||
//Manque une ref a une fonction type handler, fournie via constructeur dans Nachos
|
||||
}
|
||||
|
||||
impl Disk {
|
||||
pub fn init_disk() -> Disk {
|
||||
//see Rust File impl FileExt for File For UNIX mode
|
||||
let mut magic_buff: [u8; 4] = [0u8; 4];
|
||||
let mut readed_magic_number: u32;
|
||||
let disk_file: File;
|
||||
|
||||
let result_open = File::options().read(true).write(true).open(DISK_PATH);
|
||||
|
||||
let magic_to_write: [u8; 4] = [0xAB, 0xBA, 0xCD, 0xDC];
|
||||
|
||||
match result_open {
|
||||
Err(e) => {
|
||||
//On creer le fichier et on y écrit le magic number
|
||||
println!("Error opening file : {}", e);
|
||||
|
||||
disk_file = File::options()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(DISK_PATH)
|
||||
.unwrap();
|
||||
|
||||
disk_file.set_len(DISK_SIZE as u64);
|
||||
disk_file.write_at(&magic_to_write[..], 0);
|
||||
}
|
||||
|
||||
Ok(f) => {
|
||||
disk_file = f;
|
||||
disk_file.set_len(DISK_SIZE as u64);
|
||||
disk_file.write_at(&magic_to_write[..], 0);
|
||||
}
|
||||
}
|
||||
|
||||
match disk_file.read_at(&mut magic_buff[..], 0) {
|
||||
Ok(t) => {
|
||||
println!("init_disk :: on a lu {} octets", t);
|
||||
readed_magic_number = ((magic_buff[0] as u32) << 24)
|
||||
+ ((magic_buff[1] as u32) << 16)
|
||||
+ ((magic_buff[2] as u32) << 8)
|
||||
+ ((magic_buff[3] as u32) << 0);
|
||||
|
||||
if readed_magic_number != MAGIC_NUMBER {
|
||||
panic!(
|
||||
"init_disk :: Did not recognize magic number at the beginning of disk_file,
|
||||
On a lu {:#08x}, Panic",
|
||||
readed_magic_number
|
||||
);
|
||||
} else {
|
||||
println!(
|
||||
"init_disk :: on a lu le magic_number {:#08x}",
|
||||
readed_magic_number
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Err(e) => {
|
||||
println!("init_disk :: Error reading file : {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
Disk {
|
||||
last_sector: 0,
|
||||
active: false,
|
||||
disk_file: disk_file,
|
||||
}
|
||||
}
|
||||
|
||||
/// read data from a disk, at a certain sector number
|
||||
/// on lit un et un seul secteur
|
||||
/// `panic!` when the disk is already active or the sector number is impossible
|
||||
///
|
||||
/// ### parameters
|
||||
///
|
||||
/// - **disk** to read from
|
||||
/// - **sector_number** sector where to read the data
|
||||
/// - **data** where the readed data will be stored
|
||||
pub fn read_request(disk: &mut Disk, sector_number: i32, data: &mut [u8]) -> io::Result<()> {
|
||||
if disk.active {
|
||||
panic!("Only one request at time");
|
||||
}
|
||||
if sector_number < 0 || sector_number >= NUM_SECTORS {
|
||||
panic!("sector_number isn't right");
|
||||
}
|
||||
if data.len() < (SECTOR_SIZE as usize){
|
||||
panic!("disk::read_request, param slice too small, must be at least {} bytes", SECTOR_SIZE);
|
||||
}
|
||||
|
||||
disk.active = true;
|
||||
disk.disk_file.seek(SeekFrom::Start((sector_number * SECTOR_SIZE) as u64));
|
||||
|
||||
//let mut buffer = [0; SECTOR_SIZE as usize];
|
||||
disk.disk_file.read(&mut data[0..(SECTOR_SIZE as usize)]);
|
||||
/*for byte in buffer {
|
||||
data.push(byte);
|
||||
}*/
|
||||
disk.active = false;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// write data into a disk, at a certain sector number
|
||||
/// on écrit sur le disque un secteur, ni plus, ni moins
|
||||
/// `panic!` when the disk is already active or the sector number is impossible
|
||||
///
|
||||
/// ### parameters
|
||||
///
|
||||
/// - **disk** to write data into
|
||||
/// - **sector_number** sector where to write the data
|
||||
/// - **data** where the data to write is stored
|
||||
///
|
||||
pub fn write_request(disk: &mut Disk,sector_number: i32,data: &[u8]) -> io::Result<()> {
|
||||
if disk.active {
|
||||
panic!("Only one request at time");
|
||||
}
|
||||
if sector_number < 0 || sector_number >= NUM_SECTORS {
|
||||
panic!("sector_number isn't right");
|
||||
}
|
||||
if data.len() < (SECTOR_SIZE as usize) {
|
||||
panic!("disk::write_request, param slice must contain at least enought to write a full sector")
|
||||
}
|
||||
|
||||
disk.active = true;
|
||||
disk.disk_file
|
||||
.seek(SeekFrom::Start((sector_number * SECTOR_SIZE) as u64))?;
|
||||
/*
|
||||
let mut i = 0;
|
||||
let mut buff = Vec::new();
|
||||
for value in data {
|
||||
buff.push(*value);
|
||||
i = i + 1;
|
||||
if i >= SECTOR_SIZE {
|
||||
break;
|
||||
}
|
||||
}
|
||||
*/
|
||||
let res = disk.disk_file.write(&data[0..(SECTOR_SIZE as usize)]);
|
||||
match res {
|
||||
Ok(_) => println!("Data written successfully"),
|
||||
Err(e) => println!("{:?}", e),
|
||||
}
|
||||
disk.active = false;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// read data from a disk, at a certain sector number
|
||||
/// on lit de un à plusieurs secteurs
|
||||
/// `panic!` when the disk is already active or the sector number is impossible
|
||||
///
|
||||
/// ### parameters
|
||||
///
|
||||
/// - **disk** to read from
|
||||
/// - **sector_number** sector where to read the data
|
||||
/// - **data** where the readed data will be stored
|
||||
pub fn read_multiple_request(disk: &mut Disk, sector_number: i32, data: &mut [u8]) -> io::Result<()> {
|
||||
|
||||
if data.len() < (SECTOR_SIZE as usize) {
|
||||
panic!("disk::read_multiple_request, param slice smaller than one sector")
|
||||
}
|
||||
|
||||
//ex SECTOR_SIZE = 4, data.len = 27, number_sector_to_read = 27 - (27%4) = 27 - 3 = 24 = 6*4, soit 6 secteurs
|
||||
let number_sector_to_read = data.len() - (data.len()%(SECTOR_SIZE as usize));
|
||||
|
||||
//ex on lit 4 secteurs ce qui donne data[0..4],data[4..8],data[8..12],data[12..16]
|
||||
|
||||
for i in 0..number_sector_to_read {
|
||||
Disk::read_request(disk, sector_number + (i as i32), &mut data[(i*(SECTOR_SIZE as usize))..((i+1)*(SECTOR_SIZE as usize))]);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// write data into a disk, at a certain sector number
|
||||
/// on écrit de un à plusieurs secteurs
|
||||
/// `panic!` when the disk is already active or the sector number is impossible
|
||||
///
|
||||
/// ### parameters
|
||||
///
|
||||
/// - **disk** to write data into
|
||||
/// - **sector_number** sector where to write the data
|
||||
/// - **data** where the data to write is stored
|
||||
///
|
||||
pub fn write_multiple_request(disk: &mut Disk,sector_number: i32,data: &[u8]) -> io::Result<()> {
|
||||
|
||||
if data.len() < (SECTOR_SIZE as usize) {
|
||||
panic!("disk::read_multiple_request, param slice smaller than one sector")
|
||||
}
|
||||
|
||||
let number_sector_to_write = data.len() - (data.len()%(SECTOR_SIZE as usize));
|
||||
|
||||
//ex on ecrit 4 secteurs ce qui donne data[0..4],data[4..8],data[8..12],data[12..16]
|
||||
|
||||
for i in 0..number_sector_to_write {
|
||||
Disk::write_request(disk, sector_number + (i as i32), &data[(i*(SECTOR_SIZE as usize))..((i+1)*(SECTOR_SIZE as usize))]);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stupid_fct(buf: &[u8]) {
|
||||
println!("Size of unknown sized parameter buffer : {}", buf.len());
|
||||
for i in 0..buf.len() {
|
||||
println!("buf[{}] = {}", i, buf[i]);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
|
||||
use serial_test::serial;
|
||||
|
||||
use crate::Disk;
|
||||
|
||||
use super::stupid_fct;
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_init_stupid_fct() {
|
||||
let buffy: [u8; 6] = [1, 2, 3, 4, 5, 6];
|
||||
stupid_fct(&buffy);
|
||||
|
||||
//EN passant par une ref , on s'abstrait du besoin de connaitre la taille au moment de la compilation
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_init_disk() {
|
||||
let disk = Disk::init_disk();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[serial]
|
||||
fn test_read_write_disk() {
|
||||
let mut disk = Disk::init_disk();
|
||||
|
||||
let mut data = Vec::new();
|
||||
data.push(0 as u8);
|
||||
data.push(0 as u8);
|
||||
data.push(0 as u8);
|
||||
data.push(0 as u8);
|
||||
|
||||
let mut data1 = Vec::new();
|
||||
data1.push(1 as u8);
|
||||
data1.push(1 as u8);
|
||||
data1.push(1 as u8);
|
||||
data1.push(1 as u8);
|
||||
|
||||
let mut data2: Vec<u8> = Vec::new();
|
||||
|
||||
Disk::write_request(&mut disk, 0, &mut data);
|
||||
Disk::write_request(&mut disk, 1, &mut data1);
|
||||
Disk::read_request(&mut disk, 1, &mut data2);
|
||||
|
||||
assert_eq!(data1, data2);
|
||||
assert_ne!(data, data1);
|
||||
assert_ne!(data, data2);
|
||||
}
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
//! # Error
|
||||
//!
|
||||
//! This module contains the definition of the MachineError struct,
|
||||
//! for error management in the Machine module.
|
||||
//!
|
||||
//! Basic usage:
|
||||
//!
|
||||
//! ```
|
||||
//! fn example(x: bool) -> Result<(), MachineError> {
|
||||
//! match x {
|
||||
//! true => Ok(()),
|
||||
//! _ => Err(MachineError::new("Machine failed because of ..."));
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use std::fmt;
|
||||
|
||||
/// Machine Error
|
||||
/// This error serves as a specific exception handler for the Machine struct
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MachineError {
|
||||
/// The error message
|
||||
message: String
|
||||
}
|
||||
|
||||
pub enum MachineOk {
|
||||
Ok,
|
||||
Shutdown
|
||||
}
|
||||
|
||||
/// This impl allows this MachineError to be formatted into an empty format.
|
||||
///
|
||||
/// ```
|
||||
/// // Result of printing a MachineError
|
||||
/// let m = MachineError::new("Lorem Ipsum");
|
||||
/// println!("Example: {}", m);
|
||||
/// ```
|
||||
///
|
||||
/// Console output:Error}
|
||||
/// ```
|
||||
/// example Lorem Ipsum
|
||||
/// ```
|
||||
impl fmt::Display for MachineError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "Machine error: {}", &self.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for MachineError {
|
||||
fn from(value: &str) -> Self {
|
||||
MachineError { message: value.to_string() }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for MachineError {
|
||||
fn from(value: String) -> Self {
|
||||
MachineError { message: value }
|
||||
}
|
||||
}
|
@ -1,532 +0,0 @@
|
||||
//! # Instruction
|
||||
//!
|
||||
//! This module describes the internal representation of a RISC-V Instruction,
|
||||
//! its constructor from raw data, and a debug print method.
|
||||
#![allow(clippy::missing_docs_in_private_items, non_snake_case)]
|
||||
|
||||
use core::num::Wrapping; // Permet d'autoriser les overflow pour les opérations voulues
|
||||
use super::global::*;
|
||||
|
||||
/// OP instruction name mapping
|
||||
const NAMES_OP: [&str; 8] = ["add", "sll", "slt", "sltu", "xor", "sr", "or", "and"];
|
||||
/// OPI instruction name mapping
|
||||
const NAMES_OPI: [&str; 8] = ["addi", "slli", "slti", "sltiu", "xori", "slri", "ori", "andi"];
|
||||
/// OPW instruction name mapping
|
||||
const NAMES_OPW: [&str; 8] = ["addw", "sllw", "", "", "", "srw", "", ""];
|
||||
/// OPIW instruction name mapping
|
||||
const NAMES_OPIW: [&str; 8] = ["addiw", "slliw", "", "", "", "sri", "", ""];
|
||||
/// MUL instruction name mapping
|
||||
const NAMES_MUL: [&str; 8] = ["mul", "mulh", "mulhsu", "mulhu", "div", "divu", "rem", "remu"];
|
||||
/// BR instruction name mapping
|
||||
const NAMES_BR: [&str; 8] = ["beq", "bne", "", "", "blt", "bge", "bltu", "bgeu"];
|
||||
/// ST instruction name mapping
|
||||
const NAMES_ST: [&str; 4] = ["sb", "sh", "sw", "sd"];
|
||||
/// LD instruction name mapping
|
||||
const NAMES_LD: [&str; 7] = ["lb", "lh", "lw", "ld", "lbu", "lhu", "lwu"];
|
||||
|
||||
|
||||
/// Integer register name mapping
|
||||
pub const REG_X: [&str; 32] = ["zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "s1",
|
||||
"a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
|
||||
"s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11",
|
||||
"t3", "t4", "t5", "t6"];
|
||||
|
||||
/// Floating-point register name mapping
|
||||
const REG_F: [&str; 32] = ["ft0", "ft1", "ft2", "ft3", "ft4", "ft5", "ft6", "ft7", "fs0", "fs1",
|
||||
"fa0", "fa1", "fa2", "fa3", "fa4", "fa5", "fa6", "fa7",
|
||||
"fs2", "fs3", "fs4", "fs5", "fs6", "fs7", "fs8", "fs9", "fs10", "fs11",
|
||||
"ft8", "ft9", "ft10", "ft11"];
|
||||
|
||||
/// RISC-V Instruction
|
||||
#[derive(Debug)]
|
||||
pub struct Instruction {
|
||||
/// Original value used to construct self
|
||||
pub value : u64,
|
||||
|
||||
pub opcode : u8,
|
||||
pub rs1 : u8,
|
||||
pub rs2 : u8,
|
||||
pub rs3 : u8,
|
||||
pub rd : u8,
|
||||
pub funct7 : u8,
|
||||
pub funct7_smaller : u8,
|
||||
pub funct3 : u8,
|
||||
pub shamt : u8, // shamt = imm[5:0] or imm[4:0] (depend of opcode)
|
||||
|
||||
pub imm12_I : u16,
|
||||
pub imm12_S : u16,
|
||||
|
||||
pub imm12_I_signed : i16,
|
||||
pub imm12_S_signed : i16,
|
||||
pub imm13 : i16,
|
||||
pub imm13_signed : i16,
|
||||
|
||||
pub imm31_12 : u32,
|
||||
pub imm21_1 : u32,
|
||||
|
||||
pub imm31_12_signed : i32,
|
||||
pub imm21_1_signed : i32,
|
||||
}
|
||||
|
||||
impl Instruction {
|
||||
|
||||
/// Construct a new instruction from a big endian raw binary instruction
|
||||
pub fn new(value : u64) -> Self {
|
||||
|
||||
let opcode = (value & 0x7f) as u8;
|
||||
let rs1 = ((value >> 15) & 0x1f) as u8;
|
||||
let rs2 = ((value >> 20) & 0x1f) as u8;
|
||||
let rs3 = ((value >> 27) & 0x1f) as u8;
|
||||
let rd = ((value >> 7) & 0x1f) as u8;
|
||||
let funct7 = ((value >> 25) & 0x7f) as u8;
|
||||
let funct7_smaller = funct7 & 0x3e;
|
||||
|
||||
let funct3 = ((value >> 12) & 0x7) as u8;
|
||||
let imm12_I = ((value >> 20) & 0xfff) as u16;
|
||||
let imm12_S = (((value >> 20) & 0xfe0) + ((value >> 7) & 0x1f)) as u16;
|
||||
|
||||
let imm12_I_signed = if imm12_I >= 2048 { (Wrapping(imm12_I) - Wrapping(4096)).0 } else { imm12_I } as i16;
|
||||
let imm12_S_signed = if imm12_S >= 2048 { (Wrapping(imm12_S) - Wrapping(4096)).0 } else { imm12_S } as i16;
|
||||
|
||||
let imm13 = (((value >> 19) & 0x1000) + ((value >> 20) & 0x7e0) +
|
||||
((value >> 7) & 0x1e) + ((value << 4) & 0x800)) as i16;
|
||||
let imm13_signed = if imm13 >= 4096 { imm13 - 8192 } else { imm13 };
|
||||
|
||||
let imm31_12 = (value & 0xfffff000) as u32;
|
||||
let imm31_12_signed = imm31_12 as i32;
|
||||
|
||||
let imm21_1 = ((value & 0xff000) + ((value >> 9) & 0x800) +
|
||||
((value >> 20) & 0x7fe) + ((value >> 11) & 0x100000)) as u32;
|
||||
let imm21_1_signed = if imm21_1 >= 1048576 { (Wrapping(imm21_1) - Wrapping(2097152)).0 } else { imm21_1 } as i32;
|
||||
|
||||
let shamt = ((value >> 20) & 0x3f) as u8;
|
||||
|
||||
Instruction {
|
||||
value,
|
||||
|
||||
opcode,
|
||||
rs1,
|
||||
rs2,
|
||||
rs3,
|
||||
rd,
|
||||
funct7,
|
||||
funct7_smaller,
|
||||
|
||||
funct3,
|
||||
imm12_I,
|
||||
imm12_S,
|
||||
|
||||
imm12_I_signed,
|
||||
imm12_S_signed,
|
||||
|
||||
imm13,
|
||||
imm13_signed,
|
||||
|
||||
imm31_12,
|
||||
imm31_12_signed,
|
||||
|
||||
imm21_1,
|
||||
imm21_1_signed,
|
||||
|
||||
shamt
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// Converts an Instruction to a prettified debug String
|
||||
///
|
||||
/// ### Usage
|
||||
///
|
||||
/// ```
|
||||
/// let m = Machine::new();
|
||||
/// let i = Instruction::new(inst);
|
||||
/// println!("{}", instruction_debug(i, m.pc));
|
||||
/// ```
|
||||
pub fn instruction_debug(ins: &Instruction, pc: i32) -> String {
|
||||
let rd = ins.rd as usize;
|
||||
let rs1 = ins.rs1 as usize;
|
||||
let rs2 = ins.rs2 as usize;
|
||||
let rs3 = ins.rs3 as usize;
|
||||
|
||||
match ins.opcode {
|
||||
RISCV_OP => {
|
||||
let name: &str;
|
||||
if ins.funct7 == 1 { // Use mul array
|
||||
name = NAMES_MUL[ins.funct3 as usize]
|
||||
} else if ins.funct3 == RISCV_OP_ADD {
|
||||
// Add or Sub
|
||||
if ins.funct7 == RISCV_OP_ADD_ADD {
|
||||
name = "add";
|
||||
} else {
|
||||
name = "sub";
|
||||
}
|
||||
} else if ins.funct3 == RISCV_OP_SR {
|
||||
// Srl or Sra
|
||||
if ins.funct7 == RISCV_OP_SR_SRL {
|
||||
name = "srl";
|
||||
} else {
|
||||
name = "sra";
|
||||
}
|
||||
} else {
|
||||
name = NAMES_OP[ins.funct3 as usize];
|
||||
}
|
||||
format!("{}\t{},{},{}", name, REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||
},
|
||||
RISCV_OPI => {
|
||||
// SHAMT OR IMM
|
||||
if ins.funct3 == RISCV_OPI_SRI {
|
||||
if ins.funct7 == RISCV_OPI_SRI_SRLI {
|
||||
format!("srli\t{},{},{}", REG_X[rd], REG_X[rs1], ins.shamt)
|
||||
} else {
|
||||
format!("srai\t{},{},{}", REG_X[rd], REG_X[rs1], ins.shamt)
|
||||
}
|
||||
} else if ins.funct3 == RISCV_OPI_SLLI {
|
||||
format!("{}\t{},{},{}", NAMES_OPI[ins.funct3 as usize], REG_X[rd], REG_X[rs1], ins.shamt)
|
||||
} else {
|
||||
format!("{}\t{},{},{}", NAMES_OPI[ins.funct3 as usize], REG_X[rd], REG_X[rs1], ins.imm12_I_signed)
|
||||
}
|
||||
},
|
||||
RISCV_LUI => {
|
||||
format!("lui\t{},{:x}", REG_X[rd], ins.imm31_12)
|
||||
},
|
||||
RISCV_AUIPC => {
|
||||
format!("auipc\t{},{:x}", REG_X[rd], ins.imm31_12)
|
||||
},
|
||||
RISCV_JAL => {
|
||||
format!("jal\t{},{:x}", REG_X[rd], (pc + ins.imm21_1_signed))
|
||||
},
|
||||
RISCV_JALR => {
|
||||
format!("jalr\t{},{:x}({})", REG_X[rd], ins.imm12_I_signed, REG_X[rs1])
|
||||
},
|
||||
RISCV_BR => {
|
||||
format!("{}\t{},{},{:x}", NAMES_BR[ins.funct3 as usize], REG_X[rs1], REG_X[rs2], pc + (ins.imm13_signed as i32))
|
||||
},
|
||||
RISCV_LD => {
|
||||
format!("{}\t{},{}({})", NAMES_LD[ins.funct3 as usize], REG_X[rd], ins.imm12_I_signed, REG_X[rs1])
|
||||
},
|
||||
RISCV_ST => {
|
||||
format!("{}\t{},{}({})", NAMES_ST[ins.funct3 as usize], REG_X[rs2], ins.imm12_S_signed, REG_X[rs1])
|
||||
},
|
||||
RISCV_OPIW => {
|
||||
if ins.funct3 == RISCV_OPIW_SRW {
|
||||
if ins.funct7 == RISCV_OPIW_SRW_SRLIW {
|
||||
format!("srliw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||
} else {
|
||||
format!("sraiw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||
}
|
||||
} else {
|
||||
format!("{}\t{},{},0x{:x}", NAMES_OPIW[ins.funct3 as usize], REG_X[rd], REG_X[rs1], ins.imm12_I_signed)
|
||||
}
|
||||
},
|
||||
RISCV_OPW => {
|
||||
if ins.funct7 == 1 {
|
||||
format!("{}w\t{},{},{}", NAMES_MUL[ins.funct3 as usize], REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||
} else if ins.funct3 == RISCV_OP_ADD {
|
||||
if ins.funct7 == RISCV_OPW_ADDSUBW_ADDW {
|
||||
format!("addw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||
} else {
|
||||
format!("subw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||
}
|
||||
} else if ins.funct3 == RISCV_OPW_SRW {
|
||||
if ins.funct7 == RISCV_OPW_SRW_SRLW {
|
||||
format!("srlw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||
} else {
|
||||
format!("sraw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||
}
|
||||
} else {
|
||||
format!("{}\t{},{},{}", NAMES_OPW[ins.funct3 as usize], REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||
}
|
||||
},
|
||||
// RV32F Standard Extension
|
||||
RISCV_FLW => {
|
||||
format!("flw\t{},{},({})", REG_F[rd], ins.imm12_I_signed, REG_F[rs1])
|
||||
},
|
||||
RISCV_FSW => {
|
||||
format!("fsw\t{},{},({})", REG_F[rs2], "OFFSET TODO", REG_F[rs1]) // TODO Offset in decode
|
||||
},
|
||||
RISCV_FMADD => {
|
||||
format!("fmadd\t{}{}{}{}", REG_F[rd], REG_F[rs1], REG_F[rs2], REG_F[rs3])
|
||||
},
|
||||
RISCV_FMSUB => {
|
||||
format!("fmsub\t{}{}{}{}", REG_F[rd], REG_F[rs1], REG_F[rs2], REG_F[rs3])
|
||||
},
|
||||
RISCV_FNMSUB => {
|
||||
format!("fnmsub\t{}{}{}{}", REG_F[rd], REG_F[rs1], REG_F[rs2], REG_F[rs3])
|
||||
},
|
||||
RISCV_FNMADD => {
|
||||
format!("fnmadd\t{}{}{}{}", REG_F[rd], REG_F[rs1], REG_F[rs2], REG_F[rs3])
|
||||
},
|
||||
RISCV_FP => {
|
||||
match ins.funct7 {
|
||||
RISCV_FP_ADD => {
|
||||
format!("{}\t{}{}{}", "fadd", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
},
|
||||
RISCV_FP_SUB => {
|
||||
format!("{}\t{}{}{}", "fsub.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
},
|
||||
RISCV_FP_MUL => {
|
||||
format!("{}\t{}{}{}", "fmul.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
},
|
||||
RISCV_FP_DIV => {
|
||||
format!("{}\t{}{}{}", "fdiv.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
},
|
||||
RISCV_FP_SQRT => {
|
||||
format!("{}\t{}{}", "fsqrt.s", REG_F[rd], REG_F[rs1])
|
||||
},
|
||||
RISCV_FP_FSGN => {
|
||||
match ins.funct3 {
|
||||
RISCV_FP_FSGN_J => {
|
||||
format!("{}\t{}{}{}", "fsgnj.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
},
|
||||
RISCV_FP_FSGN_JN => {
|
||||
format!("{}\t{}{}{}", "fsgnn.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
},
|
||||
RISCV_FP_FSGN_JX => {
|
||||
format!("{}\t{}{}{}", "fsgnx.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
},
|
||||
_ => todo!("Unknown code")
|
||||
}
|
||||
},
|
||||
RISCV_FP_MINMAX => {
|
||||
if ins.funct3 == 0 {
|
||||
format!("{}\t{}{}{}", "fmin.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
} else {
|
||||
format!("{}\t{}{}{}", "fmax.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
}
|
||||
},
|
||||
RISCV_FP_FCVTW => {
|
||||
if rs2 == 0 {
|
||||
format!("{}\t{}{}", "fcvt.w.s", REG_F[rd], REG_F[rs1])
|
||||
} else {
|
||||
format!("{}\t{}{}", "fcvt.wu.s", REG_F[rd], REG_F[rs1])
|
||||
}
|
||||
},
|
||||
RISCV_FP_FMVXFCLASS => {
|
||||
if ins.funct3 == 0 {
|
||||
format!("{}\t{}{}", "fmv.x.w", REG_F[rd], REG_F[rs1])
|
||||
} else {
|
||||
format!("{}\t{}{}", "fclass.s", REG_F[rd], REG_F[rs1])
|
||||
}
|
||||
},
|
||||
RISCV_FP_FCMP => {
|
||||
if ins.funct3 == 0 {
|
||||
format!("{}\t{}{}{}", "fle.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
} else if ins.funct3 == 1 {
|
||||
format!("{}\t{}{}{}", "flt.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
} else {
|
||||
format!("{}\t{}{}{}", "feq.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
}
|
||||
},
|
||||
RISCV_FP_FCVTS => {
|
||||
if rs2 == 0 {
|
||||
format!("{}\t{}{}", "fcvt.s.w", REG_F[rd], REG_F[rs1])
|
||||
} else {
|
||||
format!("{}\t{}{}", "fcvt.s.wu", REG_F[rd], REG_F[rs1])
|
||||
}
|
||||
},
|
||||
RISCV_FP_FMVW => {
|
||||
format!("{}\t{}{}", "fmv.w.x", REG_F[rd], REG_F[rs1])
|
||||
},
|
||||
_ => todo!("Unknown code")
|
||||
}
|
||||
},
|
||||
|
||||
RISCV_SYSTEM => {
|
||||
"ecall".to_string()
|
||||
},
|
||||
_ => todo!("{:x} opcode non géré pc : {:x}, value : {:x}", ins.opcode, pc, ins.value) // Change todo! to panic! in the future, I put todo! because there's a lot of opcode currently not implemented
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#![allow(clippy::unusual_byte_groupings)]
|
||||
|
||||
use crate::simulator::instruction::*;
|
||||
|
||||
#[test]
|
||||
fn test_op() {
|
||||
let sub = Instruction::new(0b0100000_10000_10001_000_11100_0110011_u64.to_le());
|
||||
let add = Instruction::new(0b0000000_10000_10001_000_11100_0110011_u64.to_le());
|
||||
let xor = Instruction::new(0b0000000_10000_10001_100_11100_0110011_u64.to_le());
|
||||
let slr = Instruction::new(0b0000000_10000_10001_101_11100_0110011_u64.to_le());
|
||||
let sra = Instruction::new(0b0100000_10000_10001_101_11100_0110011_u64.to_le());
|
||||
|
||||
assert_eq!("sub\tt3,a7,a6", instruction_debug(&sub, 0));
|
||||
assert_eq!("xor\tt3,a7,a6", instruction_debug(&xor, 0));
|
||||
assert_eq!("srl\tt3,a7,a6", instruction_debug(&slr, 0));
|
||||
assert_eq!("sra\tt3,a7,a6", instruction_debug(&sra, 0));
|
||||
assert_eq!("add\tt3,a7,a6", instruction_debug(&add, 0));
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opi() {
|
||||
let addi = Instruction::new(0b0000000000_10001_000_11100_0010011_u64.to_le());
|
||||
let slli = Instruction::new(0b0000000000_10001_001_11100_0010011_u64.to_le());
|
||||
let slti = Instruction::new(0b0000000000_10001_010_11100_0010011_u64.to_le());
|
||||
let sltiu = Instruction::new(0b0000000000_10001_011_11100_0010011_u64.to_le());
|
||||
let xori = Instruction::new(0b_0000000000010001_100_11100_0010011_u64.to_le());
|
||||
let ori = Instruction::new(0b00000000000_10001_110_11100_0010011_u64.to_le());
|
||||
let andi = Instruction::new(0b000000000000_10001_111_11100_0010011_u64.to_le());
|
||||
assert_eq!("andi\tt3,a7,0", instruction_debug(&andi, 0));
|
||||
assert_eq!("addi\tt3,a7,0", instruction_debug(&addi, 0));
|
||||
assert_eq!("slli\tt3,a7,0", instruction_debug(&slli, 0));
|
||||
assert_eq!("slti\tt3,a7,0", instruction_debug(&slti, 0));
|
||||
assert_eq!("sltiu\tt3,a7,0", instruction_debug(&sltiu, 0));
|
||||
assert_eq!("xori\tt3,a7,0", instruction_debug(&xori, 0));
|
||||
assert_eq!("ori\tt3,a7,0", instruction_debug(&ori, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lui() {
|
||||
let lui = Instruction::new(0b01110001000011111000_11100_0110111_u64.to_le());
|
||||
let lui_negatif = Instruction::new(0b11110001000011111000_11100_0110111_u64.to_le());
|
||||
assert_eq!("lui\tt3,710f8000", instruction_debug(&lui, 0));
|
||||
assert_eq!("lui\tt3,f10f8000", instruction_debug(&lui_negatif, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ld() {
|
||||
// imm rs1 f3 rd opcode
|
||||
let lb = Instruction::new(0b010111110000_10001_000_11100_0000011_u64.to_le());
|
||||
let lh = Instruction::new(0b010111110000_10001_001_11100_0000011_u64.to_le());
|
||||
let lw = Instruction::new(0b010111110000_10001_010_11100_0000011_u64.to_le());
|
||||
let lbu = Instruction::new(0b010111110000_10001_100_11100_0000011_u64.to_le());
|
||||
let lhu = Instruction::new(0b010111110000_10001_101_11100_0000011_u64.to_le());
|
||||
let ld = Instruction::new(0b010111110000_10001_011_11100_0000011_u64.to_le());
|
||||
let lwu = Instruction::new(0b010111110000_10001_110_11100_0000011_u64.to_le());
|
||||
|
||||
assert_eq!("lb\tt3,1520(a7)", instruction_debug(&lb, 0));
|
||||
assert_eq!("lh\tt3,1520(a7)", instruction_debug(&lh, 0));
|
||||
assert_eq!("lw\tt3,1520(a7)", instruction_debug(&lw, 0));
|
||||
assert_eq!("lbu\tt3,1520(a7)", instruction_debug(&lbu, 0));
|
||||
assert_eq!("lhu\tt3,1520(a7)", instruction_debug(&lhu, 0));
|
||||
assert_eq!("ld\tt3,1520(a7)", instruction_debug(&ld, 0));
|
||||
assert_eq!("lwu\tt3,1520(a7)", instruction_debug(&lwu, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opw() {
|
||||
let addw: Instruction = Instruction::new(0b0000000_10000_10001_000_11100_0111011_u64.to_le());
|
||||
let sllw: Instruction = Instruction::new(0b0000000_10000_10001_001_11100_0111011_u64.to_le());
|
||||
let srlw: Instruction = Instruction::new(0b0000000_10000_10001_101_11100_0111011_u64.to_le());
|
||||
let sraw: Instruction = Instruction::new(0b0100000_10000_10001_101_11100_0111011_u64.to_le());
|
||||
|
||||
assert_eq!("addw\tt3,a7,a6", instruction_debug(&addw, 0));
|
||||
assert_eq!("sllw\tt3,a7,a6", instruction_debug(&sllw, 0));
|
||||
assert_eq!("srlw\tt3,a7,a6", instruction_debug(&srlw, 0));
|
||||
assert_eq!("sraw\tt3,a7,a6", instruction_debug(&sraw, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opwi() {
|
||||
let addiw: Instruction =Instruction::new(0b000000000000_10001_000_11100_0011011_u64.to_le());
|
||||
let slliw: Instruction = Instruction::new(0b0000000_10000_10001_001_11100_0011011_u64.to_le());
|
||||
let srai: Instruction = Instruction::new(0b010000010001_10001_101_11100_0010011_u64.to_le());
|
||||
assert_eq!("addiw\tt3,a7,0x0", instruction_debug(&addiw, 0));
|
||||
assert_eq!("slliw\tt3,a7,0x10", instruction_debug(&slliw, 0));
|
||||
assert_eq!("srai\tt3,a7,17", instruction_debug(&srai, 0));
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_br() {
|
||||
let beq: Instruction = Instruction::new(0b0000000_10000_10001_000_00000_1100011_u64.to_le());
|
||||
let bne: Instruction = Instruction::new(0b0000000_10000_10001_001_00000_1100011_u64.to_le());
|
||||
let blt: Instruction = Instruction::new(0b0000000_10000_10001_100_00000_1100011_u64.to_le());
|
||||
let bge: Instruction = Instruction::new(0b0000000_10000_10001_101_00000_1100011_u64.to_le());
|
||||
let bge2: Instruction = Instruction::new(0x00f75863_u64.to_le());
|
||||
let bltu: Instruction = Instruction::new(0b0000000_10000_10001_110_00000_1100011_u64.to_le());
|
||||
let bgeu: Instruction = Instruction::new(0b0000000_10000_10001_111_00000_1100011_u64.to_le());
|
||||
assert_eq!("blt\ta7,a6,0", instruction_debug(&blt, 0));
|
||||
assert_eq!("bge\ta7,a6,0", instruction_debug(&bge, 0));
|
||||
assert_eq!("bge\ta4,a5,104d4", instruction_debug(&bge2, 0x104c4));
|
||||
assert_eq!("bltu\ta7,a6,0", instruction_debug(&bltu, 0));
|
||||
assert_eq!("bgeu\ta7,a6,0", instruction_debug(&bgeu, 0));
|
||||
assert_eq!("bne\ta7,a6,0", instruction_debug(&bne, 0));
|
||||
assert_eq!("beq\ta7,a6,0", instruction_debug(&beq, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_small_program() {
|
||||
/* Code for :
|
||||
int a = 0;
|
||||
int b = 5;
|
||||
a = b;
|
||||
a = a * b;
|
||||
a = a + b;
|
||||
b = a - b;
|
||||
*/
|
||||
assert_eq!("addi sp,sp,-32", instruction_debug(&Instruction::new(0xfe010113_u64.to_le()), 0));
|
||||
assert_eq!("sd s0,24(sp)", instruction_debug(&Instruction::new(0x00813c23_u64.to_le()), 0));
|
||||
assert_eq!("addi s0,sp,32", instruction_debug(&Instruction::new(0x02010413_u64.to_le()), 0));
|
||||
assert_eq!("sw zero,-20(s0)", instruction_debug(&Instruction::new(0xfe042623_u64.to_le()), 0));
|
||||
assert_eq!("addi a5,zero,5", instruction_debug(&Instruction::new(0x00500793_u64.to_le()), 0));
|
||||
assert_eq!("sw a5,-24(s0)", instruction_debug(&Instruction::new(0xfef42423_u64.to_le()), 0));
|
||||
assert_eq!("lw a5,-24(s0)", instruction_debug(&Instruction::new(0xfe842783_u64.to_le()), 0));
|
||||
assert_eq!("sw a5,-20(s0)", instruction_debug(&Instruction::new(0xfef42623_u64.to_le()), 0));
|
||||
assert_eq!("lw a5,-20(s0)", instruction_debug(&Instruction::new(0xfec42783_u64.to_le()), 0));
|
||||
assert_eq!("addi a4,a5,0", instruction_debug(&Instruction::new(0x00078713_u64.to_le()), 0));
|
||||
assert_eq!("lw a5,-24(s0)", instruction_debug(&Instruction::new(0xfe842783_u64.to_le()), 0));
|
||||
assert_eq!("mulw a5,a4,a5", instruction_debug(&Instruction::new(0x02f707bb_u64.to_le()), 0));
|
||||
assert_eq!("sw a5,-20(s0)", instruction_debug(&Instruction::new(0xfef42623_u64.to_le()), 0));
|
||||
assert_eq!("lw a5,-20(s0)", instruction_debug(&Instruction::new(0xfec42783_u64.to_le()), 0));
|
||||
assert_eq!("addi a4,a5,0", instruction_debug(&Instruction::new(0x00078713_u64.to_le()), 0));
|
||||
assert_eq!("lw a5,-24(s0)", instruction_debug(&Instruction::new(0xfe842783_u64.to_le()), 0));
|
||||
assert_eq!("addw a5,a4,a5", instruction_debug(&Instruction::new(0x00f707bb_u64.to_le()), 0));
|
||||
assert_eq!("sw a5,-20(s0)", instruction_debug(&Instruction::new(0xfef42623_u64.to_le()), 0));
|
||||
assert_eq!("lw a5,-20(s0)", instruction_debug(&Instruction::new(0xfec42783_u64.to_le()), 0));
|
||||
assert_eq!("addi a4,a5,0", instruction_debug(&Instruction::new(0x00078713_u64.to_le()), 0));
|
||||
assert_eq!("lw a5,-24(s0)", instruction_debug(&Instruction::new(0xfe842783_u64.to_le()), 0));
|
||||
assert_eq!("subw a5,a4,a5", instruction_debug(&Instruction::new(0x40f707bb_u64.to_le()), 0));
|
||||
assert_eq!("sw a5,-24(s0)", instruction_debug(&Instruction::new(0xfef42423_u64.to_le()), 0));
|
||||
assert_eq!("addi a5,zero,0", instruction_debug(&Instruction::new(0x00000793_u64.to_le()), 0));
|
||||
assert_eq!("addi a0,a5,0", instruction_debug(&Instruction::new(0x00078513_u64.to_le()), 0));
|
||||
assert_eq!("ld s0,24(sp)", instruction_debug(&Instruction::new(0x01813403_u64.to_le()), 0));
|
||||
assert_eq!("addi sp,sp,32", instruction_debug(&Instruction::new(0x02010113_u64.to_le()), 0));
|
||||
assert_eq!("jalr zero,0(ra)", instruction_debug(&Instruction::new(0x00008067_u64.to_le()), 0));
|
||||
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_fibo() {
|
||||
assert_eq!("jal zero,10504", instruction_debug(&Instruction::new(0x0500006f_u64.to_le()), 0x104b4));
|
||||
assert_eq!("blt a4,a5,104b8", instruction_debug(&Instruction::new(0xfaf740e3_u64.to_le()), 0x10518));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul_prog() {
|
||||
assert_eq!("addi sp,sp,-32", instruction_debug(&Instruction::new(0xfe010113_u64.to_le()), 0));
|
||||
assert_eq!("sd s0,24(sp)", instruction_debug(&Instruction::new(0x00813c23_u64.to_le()), 0));
|
||||
assert_eq!("addi s0,sp,32", instruction_debug(&Instruction::new(0x02010413_u64.to_le()), 0));
|
||||
assert_eq!("addi a5,zero,5", instruction_debug(&Instruction::new(0x00500793_u64.to_le()), 0));
|
||||
assert_eq!("sw a5,-20(s0)", instruction_debug(&Instruction::new(0xfef42623_u64.to_le()), 0));
|
||||
assert_eq!("lw a5,-20(s0)", instruction_debug(&Instruction::new(0xfec42783_u64.to_le()), 0));
|
||||
assert_eq!("addi a4,a5,0", instruction_debug(&Instruction::new(0x00078713_u64.to_le()), 0));
|
||||
assert_eq!("addi a5,a4,0", instruction_debug(&Instruction::new(0x00070793_u64.to_le()), 0));
|
||||
assert_eq!("slliw a5,a5,0x2", instruction_debug(&Instruction::new(0x0027979b_u64.to_le()), 0));
|
||||
assert_eq!("addw a5,a5,a4", instruction_debug(&Instruction::new(0x00e787bb_u64.to_le()), 0));
|
||||
assert_eq!("sw a5,-24(s0)", instruction_debug(&Instruction::new(0xfef42423_u64.to_le()), 0));
|
||||
assert_eq!("lw a5,-20(s0)", instruction_debug(&Instruction::new(0xfec42783_u64.to_le()), 0));
|
||||
assert_eq!("addi a4,a5,0", instruction_debug(&Instruction::new(0x00078713_u64.to_le()), 0));
|
||||
assert_eq!("lw a5,-24(s0)", instruction_debug(&Instruction::new(0xfe842783_u64.to_le()), 0));
|
||||
assert_eq!("mulw a5,a4,a5", instruction_debug(&Instruction::new(0x02f707bb_u64.to_le()), 0));
|
||||
assert_eq!("sw a5,-28(s0)", instruction_debug(&Instruction::new(0xfef42223_u64.to_le()), 0));
|
||||
assert_eq!("lw a5,-28(s0)", instruction_debug(&Instruction::new(0xfe442783_u64.to_le()), 0));
|
||||
assert_eq!("addi a4,a5,0", instruction_debug(&Instruction::new(0x00078713_u64.to_le()), 0));
|
||||
assert_eq!("lw a5,-24(s0)", instruction_debug(&Instruction::new(0xfe842783_u64.to_le()), 0));
|
||||
assert_eq!("divw a5,a4,a5", instruction_debug(&Instruction::new(0x02f747bb_u64.to_le()), 0));
|
||||
assert_eq!("sw a5,-20(s0)", instruction_debug(&Instruction::new(0xfef42623_u64.to_le()), 0));
|
||||
assert_eq!("addi a5,zero,0", instruction_debug(&Instruction::new(0x00000793_u64.to_le()), 0));
|
||||
assert_eq!("addi a0,a5,0", instruction_debug(&Instruction::new(0x00078513_u64.to_le()), 0));
|
||||
assert_eq!("ld s0,24(sp)", instruction_debug(&Instruction::new(0x01813403_u64.to_le()), 0));
|
||||
assert_eq!("addi sp,sp,32", instruction_debug(&Instruction::new(0x02010113_u64.to_le()), 0));
|
||||
assert_eq!("jalr zero,0(ra)", instruction_debug(&Instruction::new(0x00008067_u64.to_le()), 0));
|
||||
}
|
||||
|
||||
}
|
@ -1,38 +1,17 @@
|
||||
//! # Interrupt
|
||||
//!
|
||||
//! This module contains an interrupt Handler.
|
||||
//! The methodes one_trick and idle aren't implemented for now
|
||||
|
||||
/// # Interrupt
|
||||
///
|
||||
/// Interrupt Handler
|
||||
#[derive(PartialEq)]
|
||||
pub struct Interrupt {
|
||||
/// Current Status
|
||||
level: InterruptStatus
|
||||
}
|
||||
|
||||
impl Interrupt {
|
||||
|
||||
/// Interrupt constructor
|
||||
///
|
||||
/// ### Return
|
||||
/// Interrupt with status Off
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
level: InterruptStatus::InterruptOff
|
||||
}
|
||||
}
|
||||
|
||||
/// Interrupt setter
|
||||
/// Change the value of the Interrupt
|
||||
///
|
||||
/// ### Parameters
|
||||
/// - **self** the interupt handler
|
||||
/// - **new_status** the new status value
|
||||
///
|
||||
/// ### return
|
||||
/// The previus status
|
||||
pub fn set_status(&mut self, new_status: InterruptStatus) -> InterruptStatus {
|
||||
let old = self.level;
|
||||
self.level = new_status;
|
||||
@ -46,7 +25,6 @@ impl Interrupt {
|
||||
todo!();
|
||||
}
|
||||
|
||||
/// Interupt getter
|
||||
pub fn get_status(&self) -> InterruptStatus {
|
||||
self.level
|
||||
}
|
||||
|
@ -1,648 +1,34 @@
|
||||
//! # Loader
|
||||
//!
|
||||
//! This module contains a loader for file section.
|
||||
//! Following the common standard file format for executable files
|
||||
//! [ELF (Executable and Linkable Format)](https://en.wikipedia.org/wiki/Executable_and_Linkable_Forma)
|
||||
//!
|
||||
//! It's used to charge a programme into the machine from a binary file (.guac files)
|
||||
//!
|
||||
//! Basic usage:
|
||||
//!
|
||||
//! ```
|
||||
//! let args = Args::parse();
|
||||
//! let mut machine = Machine::new(args.debug & 1 != 0, read_settings().unwrap());
|
||||
//! let (loader, ptr) = loader::Loader::new(args.executable.as_str(), &mut machine, 0).expect("An error occured while parsing the program");
|
||||
//! ```
|
||||
|
||||
use crate::Machine;
|
||||
|
||||
use std::fs;
|
||||
use std::io::Read;
|
||||
|
||||
/// The elf header defines principes aspects of the binary files, it's place at the start of the file
|
||||
/// see [ELF file Header](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#File_header) for more informations
|
||||
pub struct ElfHeader {
|
||||
/// Defines whether the file is big or little endian
|
||||
/// true correspond to big endian, false otherwise
|
||||
///
|
||||
/// Offset: 0x05, size: 1 byte
|
||||
pub endianess: bool,
|
||||
/// Defines whether the file is 32 bits or 64 bits
|
||||
///
|
||||
/// Offset: 0x04, size: 1 byte
|
||||
pub is_32bits: bool,
|
||||
/// Version of the elf file, current version is 1
|
||||
///
|
||||
/// Offset: 0x06, size: 1 byte
|
||||
pub version: u8,
|
||||
/// Identifies the target ABI.
|
||||
///
|
||||
/// In this implementation: Defines if the target abi is system V compliant
|
||||
///
|
||||
/// Offset: 0x07, size: 1 byte
|
||||
pub sys_v_abi: bool,
|
||||
/// Identifies target ISA, 0xF3 correspond to RISC-V
|
||||
///
|
||||
/// In this implementatio, true if target isa is RISC-V, false otherwise
|
||||
///
|
||||
/// Offset: 0x12, size: 2 bytes
|
||||
pub is_riscv_target: bool,
|
||||
/// Memory address of the entry point from w<here the process starts its execution.
|
||||
/// If the program doesn't have an entrypoint (i.e. not an executable), the value is 0
|
||||
///
|
||||
/// Offset: 0x18, size: 4 (32 bits) or 8 (64 bits)
|
||||
pub entrypoint: u64,
|
||||
/// Size of the elf header, 64 bytes for 64 bits and 52 for 32 bits
|
||||
///
|
||||
/// Offset: 0x28(32 bits) or 0x34 (64 bits), size: 2 bytes
|
||||
pub elf_header_size: u16,
|
||||
/// Position of the first program header entry
|
||||
///
|
||||
/// Offset: 0x1C (32 bits) or 0x20 (64 bits), size: 4 (32 bits) or 8 (64 bits) bytes
|
||||
pub program_header_location: u64,
|
||||
/// Number of entries in the progream header table
|
||||
///
|
||||
/// Offset: 0x2C (32 bits) or 0x38 (64 bits), size: 2 bytes
|
||||
pub program_header_entries: u16,
|
||||
/// Size of a program header entry
|
||||
///
|
||||
/// Offset: 0x2A (32 bits) or 0x36 (64 bits), size: 2 bytes
|
||||
pub program_header_size: u16,
|
||||
/// Position of the first section header entry
|
||||
///
|
||||
/// Offset: 0x20 (32 bits) or 0x28 (64 bits), size: 4 (32 bits) or 8 (64 bits) bytes
|
||||
pub section_header_location: u64,
|
||||
/// Number of entries in the section header table
|
||||
///
|
||||
/// Offset: 0x30 (32 bits) or 0x3C (64 bits), size: 2 bytes
|
||||
pub section_header_entries: u16,
|
||||
/// Size of a section header entry
|
||||
///
|
||||
/// Offset: 0x2E (32 bits) or 0x36 (64 bits), size: 2 bytes
|
||||
pub section_header_size: u16,
|
||||
}
|
||||
|
||||
impl ElfHeader {
|
||||
|
||||
/// return true if the 4 first bytes constitude the elf magic number
|
||||
fn is_elf(instructions: &[u8]) -> bool {
|
||||
instructions.get(0..4) == Option::Some(&[0x7f, 0x45, 0x4c, 0x46])
|
||||
}
|
||||
|
||||
/// return true if big endian, false otherwise
|
||||
fn check_endianess(instructions: &[u8]) -> bool {
|
||||
instructions.get(5) == Option::Some(&2)
|
||||
}
|
||||
|
||||
/// return true if file is 32 bits, false if 64 bits
|
||||
fn is_32bits(instructions: &[u8]) -> bool {
|
||||
instructions.get(4) == Option::Some(&1)
|
||||
}
|
||||
|
||||
/// return the version of the elf file (should be 1)
|
||||
/// Can be None if the file is smaller than 7 bytes -> the file is invalid
|
||||
fn get_version(instructions: &[u8]) -> Option<u8> {
|
||||
instructions.get(6).copied() // work as primitives implements Copy
|
||||
}
|
||||
|
||||
/// return true if target abi of the binary file is System V, false otherwise
|
||||
fn is_system_v_elf(instructions: &[u8]) -> bool {
|
||||
instructions.get(7) == Option::Some(&0)
|
||||
}
|
||||
|
||||
/// return true if specified target instruction set architecture is RISCV
|
||||
fn is_riscv_isa(instructions: &[u8]) -> bool {
|
||||
Self::get_u16_value(instructions, 0x12) == Option::Some(0xf3)
|
||||
}
|
||||
|
||||
/// memory address of the entry point from where the process starts its execution
|
||||
///
|
||||
/// ## Paramters:
|
||||
///
|
||||
/// **instructions** List of bytes of the loaded binary file
|
||||
/// **is_32bits** defines whether the binary file is 32 bits or 64 bits
|
||||
fn get_entrypoint(instructions: &[u8], is_32bits: bool) -> Option<u64> {
|
||||
if is_32bits {
|
||||
get_address_point(instructions, 0x18, true)
|
||||
} else {
|
||||
get_address_point(instructions, 0x18, false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Memory address of the start of the program header table
|
||||
///
|
||||
/// ## Paramters:
|
||||
///
|
||||
/// **instructions** List of bytes of the loaded binary file
|
||||
/// **is_32bits** defines whether the binary file is 32 bits or 64 bits
|
||||
fn get_program_header_table_location(instructions: &[u8], is_32bits: bool) -> Option<u64> {
|
||||
if is_32bits {
|
||||
get_address_point(instructions, 0x1c, true)
|
||||
} else {
|
||||
get_address_point(instructions, 0x20, false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Memory address of the start of the section header table
|
||||
///
|
||||
/// ## Paramters:
|
||||
///
|
||||
/// **instructions** List of bytes of the loaded binary file
|
||||
/// **is_32bits** defines whether the binary file is 32 bits or 64 bits
|
||||
fn get_section_header_table_location(instructions: &[u8], is_32bits: bool) -> Option<u64> {
|
||||
if is_32bits {
|
||||
get_address_point(instructions, 0x20, true)
|
||||
} else {
|
||||
get_address_point(instructions, 0x28, false)
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the size of the header, normally, 0x40 for 64 bits bin and 0x34 for 32 bits
|
||||
///
|
||||
/// ## Paramters:
|
||||
///
|
||||
/// **instructions** List of bytes of the loaded binary file
|
||||
/// **is_32bits** defines whether the binary file is 32 bits or 64 bits
|
||||
fn get_elf_header_size(instructions: &[u8], is_32bits: bool) -> Option<u16> {
|
||||
let address = if is_32bits { 0x28 } else { 0x34 };
|
||||
Self::get_u16_value(instructions, address)
|
||||
}
|
||||
|
||||
/// return the size of a program header table entry
|
||||
///
|
||||
/// ## Paramters:
|
||||
///
|
||||
/// **instructions** List of bytes of the loaded binary file
|
||||
/// **is_32bits** defines whether the binary file is 32 bits or 64 bits
|
||||
fn get_program_header_size(instructions: &[u8], is_32bits: bool) -> Option<u16> {
|
||||
let address = if is_32bits { 0x2a } else { 0x36 };
|
||||
Self::get_u16_value(instructions, address)
|
||||
}
|
||||
|
||||
/// return the number of entries in the program header
|
||||
///
|
||||
/// ## Paramters:
|
||||
///
|
||||
/// **instructions** List of bytes of the loaded binary file
|
||||
/// **is_32bits** defines whether the binary file is 32 bits or 64 bits
|
||||
fn get_number_entries_program_header(instructions: &[u8], is_32bits: bool) -> Option<u16> {
|
||||
let address = if is_32bits { 0x2c } else { 0x38 };
|
||||
Self::get_u16_value(instructions, address)
|
||||
}
|
||||
|
||||
/// Return the size of a section header table entry
|
||||
///
|
||||
/// ## Paramters:
|
||||
///
|
||||
/// **instructions** List of bytes of the loaded binary file
|
||||
/// **is_32bits** defines whether the binary file is 32 bits or 64 bits
|
||||
fn get_section_header_size(instructions: &[u8], is_32bits: bool) -> Option<u16> {
|
||||
let address = if is_32bits { 0x2e } else { 0x3a };
|
||||
Self::get_u16_value(instructions, address)
|
||||
}
|
||||
|
||||
/// Return the number of entries in the section header
|
||||
///
|
||||
/// ## Paramters:
|
||||
///
|
||||
/// **instructions** List of bytes of the loaded binary file
|
||||
/// **is_32bits** defines whether the binary file is 32 bits or 64 bits
|
||||
fn get_section_header_num_entries(instructions: &[u8], is_32bits: bool) -> Option<u16> {
|
||||
let address = if is_32bits { 0x30 } else { 0x3c };
|
||||
Self::get_u16_value(instructions, address)
|
||||
}
|
||||
|
||||
/// Return a u16 value, usually for the size or the number of entries inside a header
|
||||
///
|
||||
/// This method retrieve 2 bytes and concatenate them assuming the file is little endian
|
||||
///
|
||||
/// ## Paramters:
|
||||
///
|
||||
/// **instructions** List of bytes of the loaded binary file
|
||||
/// **address** Position of the first byte
|
||||
fn get_u16_value(instructions: &[u8], address: usize) -> Option<u16> {
|
||||
let mut bytes: [u8; 2] = [0; 2];
|
||||
bytes[0] = instructions.get(address).copied()?;
|
||||
bytes[1] = instructions.get(address + 1).copied()?;
|
||||
Option::Some(u16::from_le_bytes(bytes))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl TryFrom<&Vec<u8>> for ElfHeader {
|
||||
type Error = String;
|
||||
|
||||
fn try_from(instructions: &Vec<u8>) -> Result<Self, Self::Error> {
|
||||
if Self::is_elf(instructions) {
|
||||
let format = Self::is_32bits(instructions);
|
||||
let endianess = Self::check_endianess(instructions);
|
||||
let version = Self::get_version(instructions).ok_or("Cannot retrieve version")?;
|
||||
let is_sys_v_abi = Self::is_system_v_elf(instructions);
|
||||
let is_rv_target = Self::is_riscv_isa(instructions);
|
||||
let entrypoint = Self::get_entrypoint(instructions, format).ok_or("Cannot get entrypoint")?;
|
||||
let elf_header_size = Self::get_elf_header_size(instructions, format).ok_or("Cannot get elf header size")?;
|
||||
let program_header_location = Self::get_program_header_table_location(instructions, format).ok_or("Cannot get program header table location")?;
|
||||
let program_header_entries = Self::get_number_entries_program_header(instructions, format).ok_or("Cannot get number of entries in program header table")? ;
|
||||
let program_header_size = Self::get_program_header_size(instructions, format).ok_or("Cannot get program header entry size")?;
|
||||
let section_header_location = Self::get_section_header_table_location(instructions, format).ok_or("Cannot get section header table location")?;
|
||||
let section_header_entries = Self::get_section_header_num_entries(instructions, format).ok_or("Cannot get number of entries of section header")?;
|
||||
let section_header_size = Self::get_section_header_size(instructions, format).ok_or("Cannot get size of section header entry")?;
|
||||
Ok(ElfHeader {
|
||||
endianess,
|
||||
is_32bits: format,
|
||||
version,
|
||||
sys_v_abi: is_sys_v_abi,
|
||||
is_riscv_target: is_rv_target,
|
||||
entrypoint,
|
||||
elf_header_size,
|
||||
program_header_location,
|
||||
program_header_entries,
|
||||
program_header_size,
|
||||
section_header_location,
|
||||
section_header_entries,
|
||||
section_header_size
|
||||
})
|
||||
} else {
|
||||
Err("File doesn't have elf magic number")?
|
||||
}
|
||||
}
|
||||
}
|
||||
use std::io;
|
||||
use std::io::BufRead;
|
||||
|
||||
|
||||
/// Flag of a section, a section can have multiples flags by adding the values
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
#[allow(dead_code)]
|
||||
pub enum FlagValue {
|
||||
/// The section is writable
|
||||
ShfWrite = 0x1,
|
||||
/// The section need to be allocate/occupe memory during execution
|
||||
ShfAlloc = 0x2,
|
||||
/// The section need to be executable
|
||||
ShfExecinstr = 0x4,
|
||||
/// Section might ber merged
|
||||
ShfMerge = 0x10,
|
||||
/// Contain null-terminated (\0) strings
|
||||
ShfStrings = 0x20,
|
||||
// There is others but are unrelevant (I think)
|
||||
}
|
||||
|
||||
/// Section header entry, contains useful informations for each sections of the binary file
|
||||
///
|
||||
/// see <https://en.wikipedia.org/wiki/Executable_and_Linkable_Format#Section_header>
|
||||
#[derive(Debug)]
|
||||
pub struct SectionHeader {
|
||||
/// Offset to a string in .shstrtab section that represent the name of this section
|
||||
/// Load a file into a new machine
|
||||
///
|
||||
/// Offset: 0x0, size: 4 bytes
|
||||
pub name_offset: u32,
|
||||
/// Identify the type of this header
|
||||
/// `panic!` when size is not 1, 2, 4 or 8
|
||||
/// `panic!` when the text does not represents instructions in hexadecimal
|
||||
///
|
||||
/// Offset: 0x4, size: 4 bytes
|
||||
pub header_type: u32,
|
||||
/// Identify the atributes of this section
|
||||
/// ### Parameters
|
||||
///
|
||||
/// see `Self::does_flag_contains_key(self, FlagValue)`
|
||||
///
|
||||
/// Offset: 0x8, size: 4 (32 bits) or 8 (64 bits) bytes
|
||||
pub flags: u64,
|
||||
/// Virtual address of the section in memory if section is loaded, 0x0 otherwise
|
||||
///
|
||||
/// Offset: 0x0C (32 bits) or 0x10 (64 bits), size: 4 (32 bits) or 8 (64 bits) bytes
|
||||
pub virt_addr: u64,
|
||||
/// Offset of the section in the file image (binary file)
|
||||
///
|
||||
/// Offset: 0x10 (32 bits) or 0x18 (64 bits), size: 4 (32 bits) or 8 (64 bits) bytes
|
||||
pub image_offset: u64,
|
||||
/// Size of the section in the file image, may be 0
|
||||
///
|
||||
/// Offset: 0x14 (32 bits) or 0x20 (64 bits), size: 4 (32 bits) or 8 (64 bits) bytes
|
||||
pub section_size: u64,
|
||||
pub section_link: u32,
|
||||
pub section_info: u32,
|
||||
/// Contain the required alignment of the section, must be a power of 2
|
||||
///
|
||||
/// Offset: 0x20 (32 bits) or 0x30 (64 bits), size: 4 (32 bits) or 8 (64 bits) bytes
|
||||
pub required_align: u64,
|
||||
/// Contain the size of each entry, for sections that contain fixed size entries, otherwise 0
|
||||
///
|
||||
/// Offset: 0x24 (32 bits) or 0x38 (64 bits), size: 4 (32 bits) or 8 (64 bits) bytes
|
||||
pub entry_size: u64
|
||||
}
|
||||
/// - **path** the path of the file to load
|
||||
/// - **size** the number of bytes to write (1, 2, 4 or 8)
|
||||
pub fn _load(path : &str, instruction_size: i32) -> Machine {
|
||||
let file = fs::File::open(path).expect("Wrong filename");
|
||||
let reader = io::BufReader::new(file);
|
||||
let mut machine = Machine::init_machine();
|
||||
|
||||
impl SectionHeader {
|
||||
|
||||
/// return true if flag of this section contains / have `key`, false otherwise
|
||||
pub fn does_flag_contains_key(&self, key: FlagValue) -> bool {
|
||||
self.flags & key as u64 != 0
|
||||
}
|
||||
|
||||
/// Return the offset to a string in .shstrtab that represents the name of this section
|
||||
fn get_name_offset(instructions: &[u8], address: usize) -> Option<u32> {
|
||||
get_address_point(instructions, address, true).map(|v| { v as u32 })
|
||||
// set true to return a u32
|
||||
}
|
||||
|
||||
/// Return the type of header of the section
|
||||
fn get_header_type(instructions: &[u8], address: usize) -> Option<u32> {
|
||||
get_address_point(instructions, address + 0x4, true).map(|v| { v as u32 })
|
||||
}
|
||||
|
||||
/// Return the flags of the section, can hold multiples values, see [`FlagValue`]
|
||||
fn get_flags(instructions: &[u8], address: usize, is_32bits: bool) -> Option<u64> {
|
||||
get_address_point(instructions, address + 0x8, is_32bits)
|
||||
}
|
||||
|
||||
|
||||
/// Return the virtual address of the section in memory if the sectino is loaded(see section flag), otherwise 0
|
||||
fn get_virtual_address(instructions: &[u8], address: usize, is_32bits: bool) -> Option<u64> {
|
||||
get_address_point(instructions, address + if is_32bits { 0x0C } else { 0x10 }, is_32bits)
|
||||
}
|
||||
|
||||
/// Return the offset of the section in the file image (binary file)
|
||||
fn get_image_offset(instructions: &[u8], address: usize, is_32bits: bool) -> Option<u64> {
|
||||
get_address_point(instructions, address + if is_32bits { 0x10 } else { 0x18 }, is_32bits)
|
||||
}
|
||||
|
||||
/// Return the size of the section in the file image (binary file), may be 0
|
||||
fn get_section_size(instructions: &[u8], address: usize, is_32bits: bool) -> Option<u64> {
|
||||
get_address_point(instructions, address + if is_32bits { 0x14 } else { 0x20 }, is_32bits)
|
||||
}
|
||||
|
||||
fn get_section_link(instructions: &[u8], address: usize, is_32bits: bool) -> Option<u32> {
|
||||
get_address_point(instructions, address + if is_32bits { 0x18 } else { 0x28 }, false).map(|v| { v as u32 })
|
||||
}
|
||||
|
||||
fn get_section_info(instructions: &[u8], address: usize, is_32bits: bool) -> Option<u32> {
|
||||
get_address_point(instructions, address + if is_32bits { 0x1C } else { 0x2C }, false).map(|v| { v as u32 })
|
||||
}
|
||||
|
||||
/// Return the required alignment of the section, must be a power of 2
|
||||
fn get_required_align(instructions: &[u8], address: usize, is_32bits: bool) -> Option<u64> {
|
||||
get_address_point(instructions, address + if is_32bits { 0x20 } else { 0x30 }, is_32bits)
|
||||
}
|
||||
|
||||
/// Contain the size of each entry for sections that contain fixed-size entries, otherwise 0
|
||||
fn get_entry_size(instructions: &[u8], address: usize, is_32bits: bool) -> Option<u64> {
|
||||
get_address_point(instructions, address + if is_32bits { 0x24 } else { 0x38 }, is_32bits)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl TryFrom<(&[u8], u64, bool)> for SectionHeader {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: (&[u8], u64, bool)) -> Result<Self, Self::Error> {
|
||||
let instructions = value.0;
|
||||
let address = value.1 as usize;
|
||||
let is_32bits = value.2;
|
||||
|
||||
let name_offset = Self::get_name_offset(instructions, address).ok_or(())?;
|
||||
let header_type = Self::get_header_type(instructions, address).ok_or(())?;
|
||||
let attribute = Self::get_flags(instructions, address, is_32bits).ok_or(())?;
|
||||
let virt_addr = Self::get_virtual_address(instructions, address, is_32bits).ok_or(())?;
|
||||
let image_offset = Self::get_image_offset(instructions, address, is_32bits).ok_or(())?;
|
||||
let section_size = Self::get_section_size(instructions, address, is_32bits).ok_or(())?;
|
||||
let section_link = Self::get_section_link(instructions, address, is_32bits).ok_or(())?;
|
||||
let section_info = Self::get_section_info(instructions, address, is_32bits).ok_or(())?;
|
||||
let required_align = Self::get_required_align(instructions, address, is_32bits).ok_or(())?;
|
||||
let entry_size = Self::get_entry_size(instructions, address, is_32bits).ok_or(())?;
|
||||
Ok(Self { name_offset,
|
||||
header_type,
|
||||
flags: attribute,
|
||||
virt_addr,
|
||||
image_offset,
|
||||
section_size,
|
||||
section_link,
|
||||
section_info,
|
||||
required_align,
|
||||
entry_size
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Error enum for [`Loader`]
|
||||
#[derive(Debug)]
|
||||
pub enum LoaderError {
|
||||
/// Correspond to std IO error
|
||||
IOError(std::io::Error),
|
||||
/// Others errors
|
||||
ParsingError(String)
|
||||
}
|
||||
|
||||
/// Global structure of the loader, one instance per loaded files
|
||||
pub struct Loader {
|
||||
/// List of bytes inside the binary file
|
||||
bytes: Vec<u8>,
|
||||
/// Elf header, see [`ElfHeader`] for more informations
|
||||
pub elf_header: ElfHeader,
|
||||
/// Section header table entries, see [`SectionHeader`] for more informations
|
||||
pub sections: Vec<SectionHeader>
|
||||
}
|
||||
impl Loader {
|
||||
|
||||
/// # Loader constructor
|
||||
///
|
||||
/// Load the binary file given in parameter, parse it and load inside the machine memory
|
||||
/// return the loader instance and the location of the end of the last a allocated section in memory
|
||||
///
|
||||
/// ## Parameters
|
||||
///
|
||||
/// **path**: location of the binary file on disk
|
||||
/// **machine**: well, the risc-v simulator
|
||||
/// **start_index**: The position at which you want to start to allocate the program
|
||||
pub fn new(path: &str, machine: &mut Machine, start_index: usize) -> Result<(Self, u64), LoaderError> {
|
||||
let loader = Self::load_and_parse(path)?;
|
||||
let end_alloc = loader.load_into_machine(machine, start_index)?;
|
||||
Ok((loader, end_alloc))
|
||||
}
|
||||
|
||||
/// Try to load the binary file in memory after it been parsed
|
||||
///
|
||||
/// Binary file is loaded according to sections order and rules, see [`SectionHeader`]
|
||||
///
|
||||
/// Return the location of the end of the last a allocated section in memory
|
||||
fn load_into_machine(&self, machine: &mut Machine, start_index: usize) -> Result<u64, LoaderError> {
|
||||
let mut end_index = 0;
|
||||
for i in 0..self.sections.len() {
|
||||
let section = &self.sections[i];
|
||||
if section.does_flag_contains_key(FlagValue::ShfAlloc) {
|
||||
end_index = section.virt_addr + section.section_size;
|
||||
// Can allocate to machine memory
|
||||
for j in (0..section.section_size as usize).step_by(4) {
|
||||
let mut buf: [u8; 4] = [0; 4];
|
||||
#[allow(clippy::needless_range_loop)]
|
||||
for k in 0..buf.len() {
|
||||
if section.does_flag_contains_key(FlagValue::ShfWrite) {
|
||||
// flag WA, on doit allouer des données initialisés à 0
|
||||
// généralement, ce signifie que le compilateur à ajouter une section .bss
|
||||
buf[k] = 0;
|
||||
} else {
|
||||
buf[k] = self.bytes.get(section.image_offset as usize + j + k).copied().ok_or(LoaderError::ParsingError(format!("index 0x{:x} is out of bound because list have a size of 0x{:x} (image offset 0x{:x}, j 0x{:x}, k 0x{:x})", section.image_offset as usize + j + k, self.bytes.len(), section.image_offset, j, k)))?;
|
||||
}
|
||||
}
|
||||
|
||||
machine.write_memory(1, start_index + section.virt_addr as usize + j, buf[0] as u64);
|
||||
machine.write_memory(1, start_index + section.virt_addr as usize + j + 1, buf[1] as u64);
|
||||
machine.write_memory(1, start_index + section.virt_addr as usize + j + 2, buf[2] as u64);
|
||||
machine.write_memory(1, start_index + section.virt_addr as usize + j + 3, buf[3] as u64);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(start_index as u64 + end_index + 4)
|
||||
}
|
||||
|
||||
/// Load the binary file and store it inside an array and try to parse it,
|
||||
/// useful for a lot of thing like to know which sections to allocate memory and where
|
||||
fn load_and_parse(path: &str) -> Result<Self, LoaderError> {
|
||||
let file = fs::File::open(path);
|
||||
match file {
|
||||
Ok(mut file) => {
|
||||
let mut instructions: Vec<u8> = Default::default();
|
||||
loop {
|
||||
let mut buf: [u8; 1] = [0; 1];
|
||||
let res = file.read(&mut buf);
|
||||
for (i,line) in reader.lines().enumerate() {
|
||||
let res = u64::from_str_radix(&line.unwrap(), 16);
|
||||
match res {
|
||||
Ok(res) => {
|
||||
if res == 0 {
|
||||
break; // eof
|
||||
} else {
|
||||
instructions.push(buf[0]);
|
||||
}
|
||||
Ok(value) => {
|
||||
Machine::write_memory(&mut machine, instruction_size, i*instruction_size as usize, value);
|
||||
},
|
||||
Err(err) => {
|
||||
return Err(LoaderError::IOError(err))
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
let elf_header = match ElfHeader::try_from(&instructions) {
|
||||
Ok(header) => {
|
||||
header
|
||||
},
|
||||
Err(err) => {
|
||||
return Err(LoaderError::ParsingError(format!("Cannot parse elf header : {}", err)));
|
||||
}
|
||||
};
|
||||
let section_header = match Self::parse_section_header(&instructions, elf_header.is_32bits, elf_header.section_header_location, elf_header.section_header_entries, elf_header.section_header_size) {
|
||||
Ok(header) => {
|
||||
header
|
||||
},
|
||||
Err(_) => {
|
||||
return Err(LoaderError::ParsingError("Cannot parse section header".to_string()));
|
||||
}
|
||||
};
|
||||
// #[cfg(debug_assertions)]
|
||||
// println!("{:04x?}", instructions); // only print loaded program in debug build
|
||||
Ok(Self { bytes: instructions, elf_header, sections: section_header })
|
||||
},
|
||||
Err(err) => {
|
||||
Err(LoaderError::IOError(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Try to parse sections header table
|
||||
///
|
||||
/// Create one instance of [`SectionHeader`] for each entry and store it inside an array
|
||||
///
|
||||
/// ## Parameters
|
||||
///
|
||||
/// **instructions**: array of bytes of the binary file
|
||||
/// **is_32bits**: contain whether the binary file is 32 bits or 64 bits
|
||||
/// **header_location**: represent the position of the first entry of the header
|
||||
/// **num_of_entries**: defines the number of section header entries
|
||||
/// **entry_size**: Defines the size of an entry (each entry have the exact same size), value vary depending of if this binary file is 32 or 64 bits
|
||||
fn parse_section_header(instructions: &[u8], is_32bits: bool, header_location: u64, num_of_entries: u16, entry_size: u16) -> Result<Vec<SectionHeader>, ()> {
|
||||
let mut sections: Vec<SectionHeader> = Default::default();
|
||||
for i in 0..num_of_entries as u64 {
|
||||
sections.push(Self::parse_section_entry(instructions, is_32bits, header_location + i * entry_size as u64)?);
|
||||
}
|
||||
Ok(sections)
|
||||
}
|
||||
|
||||
|
||||
/// Parse one entry of the section header
|
||||
///
|
||||
/// ## Parameters:
|
||||
///
|
||||
/// **instructions**: array of bytes of the binary file
|
||||
/// **is_32bits**: contain whether the binary file is 32 bits or 64 bits
|
||||
/// **location**: represent the position of the entry on the file image
|
||||
fn parse_section_entry(instructions: &[u8], is_32bits: bool, location: u64) -> Result<SectionHeader, ()> {
|
||||
SectionHeader::try_from((instructions, location, is_32bits))
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// return the memory address of something stored at address
|
||||
/// Can return None if the file is smaller than adress + 3 (or 7 if 64 bits), in this case, the elf header is incorrect
|
||||
fn get_address_point(instructions: &[u8], address: usize, is_32bits: bool) -> Option<u64> {
|
||||
if is_32bits {
|
||||
let mut bytes: [u8; 4] = [0; 4];
|
||||
bytes[0] = instructions.get(address).copied()?;
|
||||
bytes[1] = instructions.get(address + 1).copied()?;
|
||||
bytes[2] = instructions.get(address + 2).copied()?;
|
||||
bytes[3] = instructions.get(address + 3).copied()?;
|
||||
Option::Some(u32::from_le_bytes(bytes) as u64)
|
||||
} else {
|
||||
let mut bytes: [u8; 8] = [0; 8];
|
||||
bytes[0] = instructions.get(address).copied()?;
|
||||
bytes[1] = instructions.get(address + 1).copied()?;
|
||||
bytes[2] = instructions.get(address + 2).copied()?;
|
||||
bytes[3] = instructions.get(address + 3).copied()?;
|
||||
bytes[4] = instructions.get(address + 4).copied()?;
|
||||
bytes[5] = instructions.get(address + 5).copied()?;
|
||||
bytes[6] = instructions.get(address + 6).copied()?;
|
||||
bytes[7] = instructions.get(address + 7).copied()?;
|
||||
Option::Some(u64::from_le_bytes(bytes))
|
||||
}
|
||||
}
|
||||
|
||||
/// Tests has been made for C program compiled with RISC-V GCC 12.2.0, target: riscv64-unknown-elf
|
||||
///
|
||||
/// It may not pass in the future if future gcc version modify order of the binary or something else
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{simulator::{loader::{Loader, SectionHeader}, machine::Machine}, utility::cfg::get_debug_configuration};
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_parse_elf() {
|
||||
let mut machine = Machine::new(true, get_debug_configuration());
|
||||
let loader = Loader::load_and_parse("./target/guac/unsigned_addition.guac").expect("IO Error");
|
||||
loader.load_into_machine(&mut machine, 0).expect("Parsing error");
|
||||
assert!(!loader.elf_header.is_32bits);
|
||||
assert!(!loader.elf_header.endianess);
|
||||
assert!(loader.elf_header.sys_v_abi);
|
||||
assert!(loader.elf_header.is_riscv_target);
|
||||
assert_eq!(1, loader.elf_header.version);
|
||||
assert_eq!(0x4000, loader.elf_header.entrypoint);
|
||||
assert_eq!(64, loader.elf_header.elf_header_size);
|
||||
assert_eq!(64, loader.elf_header.program_header_location);
|
||||
assert_eq!(18992, loader.elf_header.section_header_location);
|
||||
assert_eq!(56, loader.elf_header.program_header_size);
|
||||
assert_eq!(64, loader.elf_header.section_header_size);
|
||||
assert_eq!(4, loader.elf_header.program_header_entries);
|
||||
assert_eq!(9, loader.elf_header.section_header_entries);
|
||||
println!("{:#x?}", loader.sections);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_section() {
|
||||
let mut machine = Machine::new(true, get_debug_configuration());
|
||||
let loader = Loader::load_and_parse("./target/guac/unsigned_addition.guac").expect("IO Error");
|
||||
loader.load_into_machine(&mut machine, 0).expect("Parsing error");
|
||||
assert_eq!(9, loader.sections.len());
|
||||
let n = loader.sections.iter().filter(|p| { p.does_flag_contains_key(crate::simulator::loader::FlagValue::ShfAlloc)}).collect::<Vec<&SectionHeader>>().len();
|
||||
assert_eq!(3, n);
|
||||
assert_eq!(loader.sections[1].virt_addr, 0x4000);
|
||||
assert_eq!(loader.sections[1].image_offset, 0x1000);
|
||||
assert!(loader.sections[1].does_flag_contains_key(crate::simulator::loader::FlagValue::ShfAlloc));
|
||||
assert_eq!(loader.sections[2].virt_addr, 0x400_000);
|
||||
assert_eq!(loader.sections[2].image_offset, 0x2000);
|
||||
assert!(loader.sections[2].does_flag_contains_key(crate::simulator::loader::FlagValue::ShfAlloc));
|
||||
}
|
||||
|
||||
println!("{:x}", Machine::read_memory(& mut machine, 4, 0));
|
||||
machine
|
||||
}
|
@ -1,139 +1,140 @@
|
||||
//! # Machine
|
||||
//!
|
||||
//! This module contains a RISC-V simulator.
|
||||
//! It supports the base instruction set along
|
||||
//! with 32bit floating point operations.
|
||||
//!
|
||||
//! Basic usage:
|
||||
//!
|
||||
//! ```
|
||||
//! let mut machine = Machine::init_machine();
|
||||
//! machine.run();
|
||||
//! ```
|
||||
use std::{ops::{Add, Sub}, io::Write};
|
||||
|
||||
use std::{
|
||||
io::Write,
|
||||
fs::File
|
||||
};
|
||||
use crate::{simulator::{
|
||||
error::MachineError,
|
||||
instruction::{*, self},
|
||||
interrupt::Interrupt,
|
||||
global::*,
|
||||
register::*
|
||||
}, kernel::system::System, utility::cfg::{Settings, MachineSettingKey}};
|
||||
use crate::simulator::print;
|
||||
|
||||
use crate::kernel::{
|
||||
exception
|
||||
};
|
||||
use super::{decode::{Instruction, decode}, interrupt::Interrupt};
|
||||
use super::global::*;
|
||||
use std::fs::File;
|
||||
|
||||
use super::error::MachineOk;
|
||||
|
||||
/// # Exceptions
|
||||
///
|
||||
/// Textual names of the exceptions that can be generated by user program
|
||||
/// execution, for debugging purpose.
|
||||
/// todo: is this really supposed to stand in machine.rs?
|
||||
#[derive(Debug)]
|
||||
/*
|
||||
* Decommenter la variant si il est utilisé quelque part
|
||||
*/
|
||||
pub enum ExceptionType {
|
||||
/// Everything ok
|
||||
NoException,
|
||||
/// A program executed a system call
|
||||
SyscallException,
|
||||
/// Page fault exception
|
||||
PagefaultException,
|
||||
/// Write attempted to a page marked "read-only"
|
||||
ReadOnlyException,
|
||||
/// Translation resulted in an invalid physical address (mis-aligned or out-of-bounds)
|
||||
BusErrorException,
|
||||
/// Reference which was not mapped in the address space
|
||||
AddressErrorException,
|
||||
/// Integer overflow in add or sub
|
||||
OverflowException,
|
||||
/// Unimplemented or reserved instruction
|
||||
IllegalInstrException,
|
||||
NumExceptionTypes
|
||||
NO_EXCEPTION,//Everything ok!
|
||||
//SYSCALL_EXCEPTION,//A program executed a system call.
|
||||
PAGEFAULT_EXCEPTION,//Page fault exception
|
||||
READONLY_EXCEPTION,//Write attempted to a page marked "read-only" */
|
||||
BUSERROR_EXCEPTION,
|
||||
/* translation resulted
|
||||
in an invalid physical
|
||||
address (mis-aligned or
|
||||
out-of-bounds) */
|
||||
ADDRESSERROR_EXCEPTION, /* Reference that was
|
||||
not mapped in the address
|
||||
space */
|
||||
//OVERFLOW_EXCEPTION, //Integer overflow in add or sub.
|
||||
//ILLEGALINSTR_EXCEPTION, //Unimplemented or reserved instr.
|
||||
//NUM_EXCEPTION_TYPES
|
||||
}
|
||||
|
||||
/// # Machine Status
|
||||
///
|
||||
/// The machine can be running kernel code (SystemMode), user code (UserMode),
|
||||
/// or there can be no running thread if the ready list is empty (IdleMode).
|
||||
pub enum MachineStatus {
|
||||
IdleMode,
|
||||
SystemMode,
|
||||
UserMode
|
||||
}
|
||||
|
||||
/// ID of the stack register
|
||||
pub const STACK_REG: usize = 2;
|
||||
/// Number of available Integer registers
|
||||
|
||||
pub const NUM_INT_REGS: usize = 32;
|
||||
/// Number of available Floating Point registers
|
||||
pub const NUM_FP_REGS: usize = 32;
|
||||
|
||||
/// RISC-V Simulator
|
||||
//max number of physical page
|
||||
pub const NUM_PHY_PAGE : u64 = 400;
|
||||
//doit etre une puissance de deux
|
||||
pub const PAGE_SIZE : u64 = 128;
|
||||
//doit etre un multiple de PAGE_SIZE
|
||||
pub const MEM_SIZE : usize = (PAGE_SIZE*NUM_PHY_PAGE*100) as usize;
|
||||
|
||||
|
||||
pub trait RegisterNum: Add<Output=Self> + Sub<Output=Self> + PartialEq + Copy {}
|
||||
|
||||
impl RegisterNum for i64 {}
|
||||
|
||||
impl RegisterNum for f32 {}
|
||||
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub struct Register<U: RegisterNum> {
|
||||
register: [U; 32]
|
||||
}
|
||||
|
||||
impl<U: RegisterNum> Register<U> {
|
||||
|
||||
pub fn get_reg(&self, position: usize) -> U {
|
||||
self.register[position]
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Register<i64> {
|
||||
|
||||
pub fn init() -> Register<i64> {
|
||||
Register {
|
||||
register: [0i64; 32]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_reg(&mut self, position: usize, value: i64) {
|
||||
if position != 0 {
|
||||
self.register[position] = value;
|
||||
} else {
|
||||
// Panic ou rien ? (dans le doute pour le moment panic)
|
||||
// unreachable!("You can't write to zero register")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Register<f32> {
|
||||
|
||||
pub fn init() -> Register<f32> {
|
||||
Register {
|
||||
register: [0f32; 32]
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_reg(&mut self, position: usize, value: f32) {
|
||||
self.register[position] = value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
pub struct Machine {
|
||||
/// Debug mode of the machine
|
||||
debug: bool,
|
||||
/// Program counter
|
||||
pub pc : u64,
|
||||
/// Stack pointer
|
||||
pub sp: usize,
|
||||
/// Integer register
|
||||
pub int_reg : Register<i64>,
|
||||
/// Floating point register
|
||||
pub fp_reg : Register<f32>,
|
||||
/// Heap memory
|
||||
pub main_memory : Vec<u8>,
|
||||
/// Shiftmask
|
||||
pub shiftmask : [u64 ; 64],
|
||||
/// Debug data
|
||||
pub registers_trace : String, // for tests
|
||||
/// todo: document Interrupts
|
||||
pub interrupt: Interrupt,
|
||||
pub interrupt: Interrupt
|
||||
// futur taille à calculer int memSize = g_cfg->NumPhysPages * g_cfg->PageSize;
|
||||
//creer une struct cfg(configuration) qui s'initialise avec valeur dans un fichier cfg
|
||||
num_phy_page: u64,
|
||||
pub page_size: u64,
|
||||
pub user_stack_size: u64,
|
||||
/// Current machine status
|
||||
pub status: MachineStatus
|
||||
}
|
||||
|
||||
|
||||
impl Machine {
|
||||
|
||||
/// Machine constructor
|
||||
pub fn new(debug: bool, settings: Settings) -> Self {
|
||||
pub fn init_machine() -> Machine {
|
||||
let mut shiftmask : [u64 ; 64] = [0 ; 64];
|
||||
let mut value : u64 = 0xffffffff;
|
||||
|
||||
value = (value << 32) + value;
|
||||
for item in &mut shiftmask {
|
||||
*item = value;
|
||||
value >>= 1;
|
||||
}
|
||||
|
||||
let num_phy_page = *settings.get(&MachineSettingKey::NumPhysPages).unwrap();
|
||||
let page_size = *settings.get(&MachineSettingKey::PageSize).unwrap();
|
||||
let user_stack_size = *settings.get(&MachineSettingKey::UserStackSize).unwrap();
|
||||
let mem_size = (page_size*num_phy_page) as usize;
|
||||
|
||||
Machine {
|
||||
debug,
|
||||
let mut ret = Machine {
|
||||
pc : 0,
|
||||
sp: 0,
|
||||
int_reg : { let mut r = Register::<i64>::init(); r.set_reg(10, -1); r },
|
||||
int_reg : Register::<i64>::init(),
|
||||
fp_reg : Register::<f32>::init(),
|
||||
main_memory : vec![0_u8; mem_size],
|
||||
main_memory : vec![0_u8; MEM_SIZE],
|
||||
shiftmask,
|
||||
interrupt: Interrupt::new(),
|
||||
registers_trace : String::from(""),
|
||||
status: MachineStatus::SystemMode,
|
||||
num_phy_page,
|
||||
page_size,
|
||||
user_stack_size
|
||||
}
|
||||
registers_trace : String::from("")
|
||||
};
|
||||
|
||||
ret.int_reg.set_reg(10, -1);
|
||||
ret
|
||||
}
|
||||
|
||||
/// Read from main memory of the machine
|
||||
@ -145,7 +146,7 @@ impl Machine {
|
||||
/// - **machine** which contains the main memory
|
||||
/// - **size** the number of bytes to read (1, 2, 4, 8)
|
||||
/// - **address** in the memory to read
|
||||
pub fn read_memory(&self, size : i32, address : usize) -> u64 {
|
||||
pub fn read_memory(machine : &mut Machine, size : i32, address : usize) -> u64 {
|
||||
if ![1, 2, 4, 8].contains(&size) {
|
||||
panic!("ERROR read_memory : wrong size parameter {size}, must be (1, 2, 4 or 8)");
|
||||
}
|
||||
@ -153,7 +154,7 @@ impl Machine {
|
||||
let mut ret: u64 = 0;
|
||||
for i in 0..size {
|
||||
ret <<= 8;
|
||||
ret += self.main_memory[address + i as usize] as u64;
|
||||
ret += machine.main_memory[address + i as usize] as u64;
|
||||
}
|
||||
ret
|
||||
}
|
||||
@ -168,13 +169,13 @@ impl Machine {
|
||||
/// - **size** the number of bytes to write (1, 2, 4 or 8)
|
||||
/// - **address** the address to write to
|
||||
/// - **value** data to be written
|
||||
pub fn write_memory(&mut self, size: i32, address: usize, value: u64) {
|
||||
pub fn write_memory(machine: &mut Machine, size: i32, address: usize, value: u64) {
|
||||
if ![1, 2, 4, 8].contains(&size) {
|
||||
panic!("ERROR write_memory: WRONG `size` PARAMETER ({size}), must be 1, 2, 4 or 8")
|
||||
}
|
||||
for i in 0..size as usize {
|
||||
let inv_i = size as usize - i - 1;
|
||||
self.main_memory[address + i] = ((value & 0xff << (8 * inv_i)) >> (inv_i * 8)) as u8;
|
||||
machine.main_memory[address + i] = ((value & 0xff << (8 * inv_i)) >> (inv_i * 8)) as u8;
|
||||
}
|
||||
}
|
||||
|
||||
@ -184,11 +185,11 @@ impl Machine {
|
||||
/// ### Parameters
|
||||
///
|
||||
/// - **machine** contains the memory
|
||||
pub fn _extract_memory(&mut self){
|
||||
pub fn _extract_memory(machine: &mut Machine){
|
||||
let file_path = "burritos_memory.txt";
|
||||
let write_to_file = |path| -> std::io::Result<File> {
|
||||
let mut file = File::create(path)?;
|
||||
file.write_all(&self.main_memory)?;
|
||||
file.write_all(&machine.main_memory)?;
|
||||
Ok(file)
|
||||
};
|
||||
match write_to_file(file_path) {
|
||||
@ -197,566 +198,556 @@ impl Machine {
|
||||
};
|
||||
}
|
||||
|
||||
/// Print the status of the machine to the standard output
|
||||
///
|
||||
/// ### Parameters
|
||||
///
|
||||
/// - **machine** the machine to get the status from
|
||||
pub fn print_status(&self) {
|
||||
pub fn print_machine_status(machine: &mut Machine) {
|
||||
println!("######### Machine status #########");
|
||||
for i in (0..32).step_by(3) {
|
||||
print!(">{0: <4} : {1:<16x} ", instruction::REG_X[i], self.int_reg.get_reg(i as u8));
|
||||
print!(">{0: <4} : {1:<16x} ", instruction::REG_X[i+1], self.int_reg.get_reg((i+1) as u8));
|
||||
print!(">{0: <4} : {1:<16x} ", print::REG_X[i], machine.int_reg.get_reg(i));
|
||||
print!(">{0: <4} : {1:<16x} ", print::REG_X[i+1], machine.int_reg.get_reg(i+1));
|
||||
if i+2 < 32 {
|
||||
print!(">{0: <4} : {1:<16x} ", instruction::REG_X[i+2], self.int_reg.get_reg((i+2) as u8));
|
||||
print!(">{0: <4} : {1:<16x} ", print::REG_X[i+2], machine.int_reg.get_reg(i+2));
|
||||
}
|
||||
println!();
|
||||
}
|
||||
println!("________________SP________________");
|
||||
let sp = self.int_reg.get_reg(2);
|
||||
println!("SP: {:16x}", self.read_memory(8, sp as usize));
|
||||
let sp_index = machine.int_reg.get_reg(2);
|
||||
for i in 0..5 {
|
||||
println!("SP+{:<2} : {:16x}", i*8, Self::read_memory(machine, 8, (sp_index + i*8) as usize));
|
||||
}
|
||||
println!("##################################");
|
||||
}
|
||||
|
||||
/// Get the state of the registers as a string
|
||||
///
|
||||
/// ### Parameters
|
||||
///
|
||||
/// - **machine** the machine to read the registers from
|
||||
pub fn string_registers(&self) -> String {
|
||||
pub fn string_registers(machine: &mut Machine) -> String {
|
||||
let mut s = String::from("");
|
||||
for i in 0..32 {
|
||||
s.push_str(format!("{} ", self.int_reg.get_reg(i)).as_str());
|
||||
s.push_str(format!("{} ", machine.int_reg.get_reg(i)).as_str());
|
||||
}
|
||||
s
|
||||
}
|
||||
|
||||
pub fn raise_exception(&mut self, exception: ExceptionType, address : u64, system: &mut System) -> Result<MachineOk, MachineError>{
|
||||
self.set_status(MachineStatus::SystemMode);
|
||||
// Handle the interruption
|
||||
match exception::call(&exception, self, system) {
|
||||
Ok(r) => {
|
||||
self.set_status(MachineStatus::UserMode);
|
||||
Ok(r)
|
||||
},
|
||||
Err(e) => Err(format!("Syscall {:?} invalid or not implemented", e))?
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute the instructions table of a machine put in param
|
||||
/// Execute the instructions table of a machine putted in param
|
||||
///
|
||||
/// ### Parameters
|
||||
///
|
||||
/// - **machine** which contains a table of instructions
|
||||
pub fn run(&mut self, system: &mut System) {
|
||||
loop {
|
||||
match self.one_instruction(system) {
|
||||
Ok(MachineOk::Ok) => {},
|
||||
Ok(MachineOk::Shutdown) => break,
|
||||
Err(e) => panic!("FATAL at pc {} -> {}", self.pc, e)
|
||||
}
|
||||
self.write_int_register(0, 0); // In case an instruction write on register 0
|
||||
}
|
||||
pub fn run(machine : &mut Machine){
|
||||
while Machine::one_instruction(machine) == 0 {}
|
||||
println!("trace : \n{}", machine.registers_trace);
|
||||
}
|
||||
|
||||
/// Execute the instructions table of a machine put in param
|
||||
/// **WITHOUT INTERPRETING SYSCALLS**
|
||||
///
|
||||
/// For debug purposes
|
||||
pub fn _run_debug(&mut self, system: &mut System) {
|
||||
loop {
|
||||
match self.one_instruction(system) {
|
||||
Ok(_) => (),
|
||||
_ => break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute the current instruction
|
||||
/// execute the current instruction
|
||||
///
|
||||
/// ### Parameters
|
||||
///
|
||||
/// - **machine** which contains a table of instructions and a pc to the actual instruction
|
||||
pub fn one_instruction(&mut self, system: &mut System) -> Result<MachineOk, MachineError> {
|
||||
pub fn one_instruction(machine :&mut Machine) -> i32 {
|
||||
|
||||
if self.main_memory.len() <= self.pc as usize {
|
||||
let unsigned_reg1 : u64;
|
||||
let unsigned_reg2 : u64;
|
||||
let long_result : i128;
|
||||
|
||||
/*__int128 longResult;
|
||||
int32_t local_data_a, local_data_b;
|
||||
int64_t localLongResult;
|
||||
uint32_t local_data_aUnsigned, local_data_bUnsigned;
|
||||
int32_t localResult;
|
||||
float localFloat;
|
||||
uint64_t value;*/
|
||||
|
||||
if machine.main_memory.len() <= machine.pc as usize {
|
||||
panic!("ERROR : number max of instructions rushed");
|
||||
}
|
||||
let mut val: [u8; 4] = [0; 4];
|
||||
for (i, elem) in val.iter_mut().enumerate() {
|
||||
*elem = self.main_memory[self.pc as usize + i];
|
||||
for i in 0..4 {
|
||||
val[i] = machine.main_memory[machine.pc as usize + i];
|
||||
}
|
||||
|
||||
let val = u32::from_le_bytes(val) as u64;
|
||||
let inst : Instruction = Instruction::new(val);
|
||||
if self.debug {
|
||||
self.print_status();
|
||||
println!("executing instruction : {:016x} at pc {:x}", val, self.pc);
|
||||
println!("{}", instruction::instruction_debug(&inst, self.pc as i32));
|
||||
let trace = Self::string_registers(self);
|
||||
self.registers_trace.push_str(format!("{}\n", trace).as_str());
|
||||
}
|
||||
let val = u32::from_be_bytes(val) as u64;
|
||||
let inst : Instruction = decode(val);
|
||||
Self::print_machine_status(machine);
|
||||
println!("executing instruction : {:016x} at pc {:x}", val, machine.pc);
|
||||
println!("{}", print::print(decode(val), machine.pc as i32));
|
||||
let trace = Self::string_registers(machine);
|
||||
machine.registers_trace.push_str(format!("{}\n", trace).as_str());
|
||||
|
||||
self.pc += 4;
|
||||
machine.pc += 4;
|
||||
|
||||
match inst.opcode {
|
||||
// Treatment for: LOAD UPPER IMMEDIATE INSTRUCTION
|
||||
RISCV_LUI => {
|
||||
self.int_reg.set_reg(inst.rd, inst.imm31_12 as i64);
|
||||
Ok(MachineOk::Ok)
|
||||
machine.int_reg.set_reg(inst.rd as usize, inst.imm31_12 as i64);
|
||||
},
|
||||
|
||||
// Treatment for: ADD UPPER IMMEDIATE TO PC INSTRUCTION
|
||||
RISCV_AUIPC => {
|
||||
self.int_reg.set_reg(inst.rd, self.pc as i64 - 4 + inst.imm31_12 as i64);
|
||||
Ok(MachineOk::Ok)
|
||||
machine.int_reg.set_reg(inst.rd as usize,machine.pc as i64 - 4 + inst.imm31_12 as i64);
|
||||
},
|
||||
|
||||
// Treatement for: JUMP AND LINK INSTRUCTIONS (direct jump)
|
||||
RISCV_JAL => {
|
||||
self.int_reg.set_reg(inst.rd, self.pc as i64);
|
||||
self.pc = (self.pc as i64 + inst.imm21_1_signed as i64 - 4) as u64;
|
||||
Ok(MachineOk::Ok)
|
||||
machine.int_reg.set_reg(inst.rd as usize, machine.pc as i64);
|
||||
machine.pc = (machine.pc as i64 + inst.imm21_1_signed as i64 - 4) as u64;
|
||||
},
|
||||
|
||||
// Treatment for: JUMP AND LINK REGISTER INSTRUCTIONS (indirect jump)
|
||||
RISCV_JALR => {
|
||||
let tmp = self.pc;
|
||||
self.pc = (self.int_reg.get_reg(inst.rs1) + inst.imm12_I_signed as i64) as u64 & 0xfffffffe;
|
||||
self.int_reg.set_reg(inst.rd, tmp as i64);
|
||||
Ok(MachineOk::Ok)
|
||||
let tmp = machine.pc;
|
||||
machine.pc = (machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_I_signed as i64) as u64 & 0xfffffffe;
|
||||
machine.int_reg.set_reg(inst.rd as usize, tmp as i64);
|
||||
},
|
||||
|
||||
//******************************************************************************************
|
||||
// Treatment for: BRANCH INSTRUCTIONS
|
||||
RISCV_BR => self.branch_instruction(inst),
|
||||
RISCV_BR => {
|
||||
match inst.funct3 {
|
||||
RISCV_BR_BEQ => {
|
||||
if machine.int_reg.get_reg(inst.rs1 as usize) == machine.int_reg.get_reg(inst.rs2 as usize) {
|
||||
machine.pc = (machine.pc as i64 + inst.imm13_signed as i64 - 4) as u64;
|
||||
}
|
||||
},
|
||||
RISCV_BR_BNE => {
|
||||
if machine.int_reg.get_reg(inst.rs1 as usize) != machine.int_reg.get_reg(inst.rs2 as usize) {
|
||||
machine.pc = (machine.pc as i64 + inst.imm13_signed as i64 - 4) as u64;
|
||||
}
|
||||
},
|
||||
RISCV_BR_BLT => {
|
||||
if machine.int_reg.get_reg(inst.rs1 as usize) < machine.int_reg.get_reg(inst.rs2 as usize) {
|
||||
machine.pc = (machine.pc as i64 + inst.imm13_signed as i64 - 4) as u64;
|
||||
}
|
||||
},
|
||||
RISCV_BR_BGE => {
|
||||
if machine.int_reg.get_reg(inst.rs1 as usize) >= machine.int_reg.get_reg(inst.rs2 as usize) {
|
||||
machine.pc = (machine.pc as i64 + inst.imm13_signed as i64 - 4) as u64;
|
||||
}
|
||||
},
|
||||
RISCV_BR_BLTU => {
|
||||
if machine.int_reg.get_reg(inst.rs1 as usize) < machine.int_reg.get_reg(inst.rs2 as usize) {
|
||||
machine.pc = (machine.pc as i64 + inst.imm13_signed as i64 - 4) as u64;
|
||||
}
|
||||
},
|
||||
RISCV_BR_BGEU => {
|
||||
if machine.int_reg.get_reg(inst.rs1 as usize) >= machine.int_reg.get_reg(inst.rs2 as usize) {
|
||||
machine.pc = (machine.pc as i64 + inst.imm13_signed as i64 - 4) as u64;
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
panic!("In BR switch case, this should never happen... Instr was {}", inst.value);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
//******************************************************************************************
|
||||
// Treatment for: LOAD INSTRUCTIONS
|
||||
RISCV_LD => self.load_instruction(inst),
|
||||
|
||||
// Treatment for: STORE INSTRUCTIONS
|
||||
RISCV_ST => self.store_instruction(inst),
|
||||
|
||||
// Treatment for: OP INSTRUCTIONS
|
||||
RISCV_OP => self.op_instruction(inst),
|
||||
|
||||
RISCV_LD => {
|
||||
match inst.funct3 {
|
||||
RISCV_LD_LB | RISCV_LD_LBU => {
|
||||
let tmp = Self::read_memory(machine, 1, (machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_I_signed as i64) as usize) as i64;
|
||||
machine.int_reg.set_reg(inst.rd as usize, tmp);
|
||||
},
|
||||
RISCV_LD_LH | RISCV_LD_LHU => {
|
||||
let tmp = Self::read_memory(machine, 2, (machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_I_signed as i64) as usize) as i64;
|
||||
machine.int_reg.set_reg(inst.rd as usize, tmp);
|
||||
},
|
||||
RISCV_LD_LW | RISCV_LD_LWU => {
|
||||
let tmp = Self::read_memory(machine, 4, (machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_I_signed as i64) as usize) as i64;
|
||||
machine.int_reg.set_reg(inst.rd as usize, tmp);
|
||||
},
|
||||
RISCV_LD_LD => {
|
||||
let tmp = Self::read_memory(machine, 8, (machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_I_signed as i64) as usize) as i64;
|
||||
machine.int_reg.set_reg(inst.rd as usize, tmp);
|
||||
},
|
||||
_ => {
|
||||
panic!("In LD switch case, this should never happen... Instr was {}", inst.value);
|
||||
}
|
||||
}
|
||||
},
|
||||
// store instructions
|
||||
RISCV_ST => {
|
||||
match inst.funct3 {
|
||||
RISCV_ST_STB => {
|
||||
Self::write_memory(machine, 1, (machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_S_signed as i64) as usize, machine.int_reg.get_reg(inst.rs2 as usize) as u64);
|
||||
},
|
||||
RISCV_ST_STH => {
|
||||
Self::write_memory(machine, 2, (machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_S_signed as i64) as usize, machine.int_reg.get_reg(inst.rs2 as usize) as u64);
|
||||
},
|
||||
RISCV_ST_STW => {
|
||||
Self::write_memory(machine, 4, (machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_S_signed as i64) as usize, machine.int_reg.get_reg(inst.rs2 as usize) as u64);
|
||||
},
|
||||
RISCV_ST_STD => {
|
||||
Self::write_memory(machine, 8, (machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_S_signed as i64) as usize, machine.int_reg.get_reg(inst.rs2 as usize) as u64);
|
||||
},
|
||||
_ => {
|
||||
panic!("In ST switch case, this should never happen... Instr was {}", inst.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
//******************************************************************************************
|
||||
// Treatment for: OPI INSTRUCTIONS
|
||||
RISCV_OPI => self.opi_instruction(inst),
|
||||
|
||||
// Treatment for: OPW INSTRUCTIONS
|
||||
RISCV_OPW => self.opw_instruction(inst),
|
||||
|
||||
// Treatment for OPIW INSTRUCTIONS
|
||||
RISCV_OPIW => self.opiw_instruction(inst),
|
||||
|
||||
// Treatment for: FLOATING POINT INSTRUCTIONS
|
||||
RISCV_FP => self.fp_instruction(inst),
|
||||
|
||||
// Treatment for: SYSTEM CALLS
|
||||
RISCV_SYSTEM => self.raise_exception(ExceptionType::SyscallException, self.pc, system),
|
||||
|
||||
// Default case
|
||||
_ => Err(format!("{:x}: Unknown opcode\npc: {:x}", inst.opcode, self.pc))?
|
||||
}
|
||||
}
|
||||
|
||||
/// Treatement for Branch instructions
|
||||
fn branch_instruction(&mut self, inst: Instruction) -> Result<MachineOk, MachineError> {
|
||||
let op = match inst.funct3 {
|
||||
RISCV_BR_BEQ => |a, b| a == b,
|
||||
RISCV_BR_BNE => |a, b| a != b,
|
||||
RISCV_BR_BLT => |a, b| a < b,
|
||||
RISCV_BR_BGE => |a, b| a >= b,
|
||||
RISCV_BR_BLTU => |a, b| a < b,
|
||||
RISCV_BR_BGEU => |a, b| a >= b,
|
||||
_ => Err(format!("Unreachable in branch_instruction match! Instruction was {:?}", inst))?
|
||||
};
|
||||
let rs1 = self.int_reg.get_reg(inst.rs1);
|
||||
let rs2 = self.int_reg.get_reg(inst.rs2);
|
||||
if op(rs1, rs2) {
|
||||
self.pc = (self.pc as i64 + inst.imm13_signed as i64 - 4) as u64;
|
||||
}
|
||||
Ok(MachineOk::Ok)
|
||||
}
|
||||
|
||||
/// Executes RISC-V Load Instructions on the machine
|
||||
fn load_instruction(&mut self, inst: Instruction) -> Result<MachineOk, MachineError> {
|
||||
let mut set_reg = |rd, size| {
|
||||
let val = self.read_memory(size, (self.int_reg.get_reg(inst.rs1) + inst.imm12_I_signed as i64) as usize) as i64;
|
||||
self.int_reg.set_reg(rd, val);
|
||||
Ok(MachineOk::Ok)
|
||||
};
|
||||
RISCV_OPI => {
|
||||
match inst.funct3 {
|
||||
RISCV_LD_LB | RISCV_LD_LBU => set_reg(inst.rd, 1),
|
||||
RISCV_LD_LH | RISCV_LD_LHU => set_reg(inst.rd, 2),
|
||||
RISCV_LD_LW | RISCV_LD_LWU => set_reg(inst.rd, 4),
|
||||
RISCV_LD_LD => set_reg(inst.rd, 8),
|
||||
_ => Err(format!("Unreachable in load_instruction match! Instruction was {:?}", inst))?
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes RISC-V Store Instructions on the machine
|
||||
fn store_instruction(&mut self, inst: Instruction) -> Result<MachineOk, MachineError> {
|
||||
let mut store = |size| {
|
||||
self.write_memory(
|
||||
size,
|
||||
(self.int_reg.get_reg(inst.rs1) + inst.imm12_S_signed as i64) as usize,
|
||||
self.int_reg.get_reg(inst.rs2) as u64
|
||||
);
|
||||
Ok(MachineOk::Ok)
|
||||
};
|
||||
match inst.funct3 {
|
||||
RISCV_ST_STB => store(1),
|
||||
RISCV_ST_STH => store(2),
|
||||
RISCV_ST_STW => store(4),
|
||||
RISCV_ST_STD => store(8),
|
||||
_ => Err(format!("Unreachable in store_instruction match! Instruction was {:?}", inst))?
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes RISC-V Integer Register-Immediate Instructions on the machine
|
||||
fn opi_instruction(&mut self, inst: Instruction) -> Result<MachineOk, MachineError> {
|
||||
let rs1 = self.int_reg.get_reg(inst.rs1);
|
||||
let imm12 = inst.imm12_I_signed as i64;
|
||||
let shamt = inst.shamt as i64;
|
||||
let mut compute = |operation: &dyn Fn (i64, i64) -> i64, a, b| {
|
||||
self.int_reg.set_reg(inst.rd, operation(a, b));
|
||||
Ok(MachineOk::Ok)
|
||||
};
|
||||
match inst.funct3 {
|
||||
RISCV_OPI_ADDI => compute(&std::ops::Add::add, rs1, imm12),
|
||||
RISCV_OPI_SLTI => compute(&|a, b| (a < b) as i64, rs1, imm12),
|
||||
RISCV_OPI_XORI => compute(&core::ops::BitXor::bitxor, rs1, imm12),
|
||||
RISCV_OPI_ORI => compute(&core::ops::BitOr::bitor, rs1, imm12),
|
||||
RISCV_OPI_ANDI => compute(&core::ops::BitAnd::bitand, rs1, imm12),
|
||||
RISCV_OPI_SLLI => compute(&core::ops::Shl::shl, rs1, shamt),
|
||||
RISCV_OPI_SRI => if inst.funct7_smaller == RISCV_OPI_SRI_SRLI {
|
||||
compute(&|a, b| { (a >> b) & self.shiftmask[inst.shamt as usize] as i64 }, rs1, shamt)
|
||||
RISCV_OPI_ADDI => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_I_signed as i64);
|
||||
},
|
||||
RISCV_OPI_SLTI => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, (machine.int_reg.get_reg(inst.rs1 as usize) < inst.imm12_I_signed as i64) as i64);
|
||||
},
|
||||
RISCV_OPI_XORI => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) ^ inst.imm12_I_signed as i64);
|
||||
},
|
||||
RISCV_OPI_ORI => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) | inst.imm12_I_signed as i64);
|
||||
},
|
||||
RISCV_OPI_ANDI => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) & inst.imm12_I_signed as i64);
|
||||
},
|
||||
RISCV_OPI_SLLI => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) << inst.shamt);
|
||||
},
|
||||
RISCV_OPI_SRI => {
|
||||
if inst.funct7_smaller == RISCV_OPI_SRI_SRLI {
|
||||
machine.int_reg.set_reg(inst.rd as usize, (machine.int_reg.get_reg(inst.rs1 as usize) >> inst.shamt) & machine.shiftmask[inst.shamt as usize] as i64);
|
||||
} else { // SRAI
|
||||
compute(&core::ops::Shr::shr, rs1, shamt)
|
||||
}
|
||||
_ => Err(format!("Unreachable in opi_instruction match! Instruction was {:?}", inst))?
|
||||
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) >> inst.shamt);
|
||||
}
|
||||
}
|
||||
_ => { panic!("In OPI switch case, this should never happen... Instr was %x\n {}", inst.value); }
|
||||
}
|
||||
},
|
||||
|
||||
/// Executes simple RISC-V mathematical operations on the machine
|
||||
fn op_instruction(&mut self, inst: Instruction) -> Result<MachineOk, MachineError> {
|
||||
let long_result: i128;
|
||||
let unsigned_reg1: u64;
|
||||
let unsigned_reg2: u64;
|
||||
RISCV_OP => {
|
||||
if inst.funct7 == 1 {
|
||||
match inst.funct3 {
|
||||
RISCV_OP_M_MUL => {
|
||||
long_result = (self.int_reg.get_reg(inst.rs1) * self.int_reg.get_reg(inst.rs2)) as i128;
|
||||
self.int_reg.set_reg(inst.rd, (long_result & 0xffffffffffffffff) as i64)
|
||||
long_result = (machine.int_reg.get_reg(inst.rs1 as usize) * machine.int_reg.get_reg(inst.rs2 as usize)) as i128;
|
||||
machine.int_reg.set_reg(inst.rd as usize, (long_result & 0xffffffffffffffff) as i64);
|
||||
},
|
||||
RISCV_OP_M_MULH => {
|
||||
long_result = (self.int_reg.get_reg(inst.rs1) * self.int_reg.get_reg(inst.rs2)) as i128;
|
||||
self.int_reg.set_reg(inst.rd, ((long_result >> 64) & 0xffffffffffffffff) as i64)
|
||||
long_result = (machine.int_reg.get_reg(inst.rs1 as usize) * machine.int_reg.get_reg(inst.rs2 as usize)) as i128;
|
||||
machine.int_reg.set_reg(inst.rd as usize, ((long_result >> 64) & 0xffffffffffffffff) as i64);
|
||||
},
|
||||
RISCV_OP_M_MULHSU => {
|
||||
unsigned_reg2 = self.int_reg.get_reg(inst.rs2) as u64;
|
||||
long_result = (self.int_reg.get_reg(inst.rs1) as u64 * unsigned_reg2) as i128;
|
||||
self.int_reg.set_reg(inst.rd, ((long_result >> 64) & 0xffffffffffffffff) as i64)
|
||||
unsigned_reg2 = machine.int_reg.get_reg(inst.rs2 as usize) as u64;
|
||||
long_result = (machine.int_reg.get_reg(inst.rs1 as usize) as u64 * unsigned_reg2) as i128;
|
||||
machine.int_reg.set_reg(inst.rd as usize, ((long_result >> 64) & 0xffffffffffffffff) as i64);
|
||||
},
|
||||
// VOIR CE QUE FAIT EXACTEMENT CE TRUC , PK on converve
|
||||
/*
|
||||
* VOIR SI LES CAST machine.int_reg[....] = i128*u64 as u32 FAUSSE RESULTAT (suit pas la logique du code c++)
|
||||
* WHAT DA HECK
|
||||
*/
|
||||
RISCV_OP_M_MULHU => {
|
||||
unsigned_reg1 = self.int_reg.get_reg(inst.rs1) as u64;
|
||||
unsigned_reg2 = self.int_reg.get_reg(inst.rs2) as u64;
|
||||
unsigned_reg1 = machine.int_reg.get_reg(inst.rs1 as usize) as u64;
|
||||
unsigned_reg2 = machine.int_reg.get_reg(inst.rs2 as usize) as u64;
|
||||
long_result = (unsigned_reg1 * unsigned_reg2) as i128;
|
||||
self.int_reg.set_reg(inst.rd, ((long_result >> 64) & 0xffffffffffffffff) as i64);
|
||||
machine.int_reg.set_reg(inst.rd as usize, ((long_result >> 64) & 0xffffffffffffffff) as i64);
|
||||
},
|
||||
RISCV_OP_M_DIV => self.int_reg.set_reg(inst.rd, self.int_reg.get_reg(inst.rs1) / self.int_reg.get_reg(inst.rs2)),
|
||||
_ => Err(format!("Unreachable in op_instruction match! Instruction was {:?}", inst))?
|
||||
RISCV_OP_M_DIV => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) / machine.int_reg.get_reg(inst.rs2 as usize));
|
||||
}
|
||||
_ => {
|
||||
panic!("RISCV_OP : funct7 = 1 (Multiplication) :: Error\n");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match inst.funct3 {
|
||||
RISCV_OP_ADD => if inst.funct7 == RISCV_OP_ADD_ADD {
|
||||
self.int_reg.set_reg(inst.rd, self.int_reg.get_reg(inst.rs1) + self.int_reg.get_reg(inst.rs2))
|
||||
RISCV_OP_ADD => {
|
||||
if inst.funct7 == RISCV_OP_ADD_ADD {
|
||||
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) + machine.int_reg.get_reg(inst.rs2 as usize));
|
||||
} else {
|
||||
self.int_reg.set_reg(inst.rd, self.int_reg.get_reg(inst.rs1) - self.int_reg.get_reg(inst.rs2))
|
||||
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) - machine.int_reg.get_reg(inst.rs2 as usize));
|
||||
}
|
||||
},
|
||||
RISCV_OP_SLL => self.int_reg.set_reg(inst.rd, self.int_reg.get_reg(inst.rs1) << (self.int_reg.get_reg(inst.rs2) & 0x3f)),
|
||||
RISCV_OP_SLT => if self.int_reg.get_reg(inst.rs1) < self.int_reg.get_reg(inst.rs2) {
|
||||
self.int_reg.set_reg(inst.rd, 1)
|
||||
RISCV_OP_SLL => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) << (machine.int_reg.get_reg(inst.rs2 as usize) & 0x3f));
|
||||
},
|
||||
RISCV_OP_SLT => {
|
||||
if machine.int_reg.get_reg(inst.rs1 as usize) < machine.int_reg.get_reg(inst.rs2 as usize) {
|
||||
machine.int_reg.set_reg(inst.rd as usize, 1);
|
||||
} else {
|
||||
self.int_reg.set_reg(inst.rd, 0)
|
||||
machine.int_reg.set_reg(inst.rd as usize, 0);
|
||||
}
|
||||
},
|
||||
RISCV_OP_SLTU => {
|
||||
unsigned_reg1 = self.int_reg.get_reg(inst.rs1) as u64;
|
||||
unsigned_reg2 = self.int_reg.get_reg(inst.rs2) as u64;
|
||||
unsigned_reg1 = machine.int_reg.get_reg(inst.rs1 as usize) as u64;
|
||||
unsigned_reg2 = machine.int_reg.get_reg(inst.rs2 as usize) as u64;
|
||||
if unsigned_reg1 < unsigned_reg2 {
|
||||
self.int_reg.set_reg(inst.rd, 1)
|
||||
machine.int_reg.set_reg(inst.rd as usize, 1);
|
||||
} else {
|
||||
self.int_reg.set_reg(inst.rd, 0)
|
||||
machine.int_reg.set_reg(inst.rd as usize, 0);
|
||||
}
|
||||
},
|
||||
RISCV_OP_XOR => self.int_reg.set_reg(inst.rd, self.int_reg.get_reg(inst.rs1) ^ self.int_reg.get_reg(inst.rs2)),
|
||||
RISCV_OP_SR => self.int_reg.set_reg(inst.rd, self.int_reg.get_reg(inst.rs1) >> self.int_reg.get_reg(inst.rs2)), // RISCV_OP_SR_SRL inaccessible
|
||||
RISCV_OP_OR => self.int_reg.set_reg(inst.rd, self.int_reg.get_reg(inst.rs1) | self.int_reg.get_reg(inst.rs2)),
|
||||
RISCV_OP_AND => self.int_reg.set_reg(inst.rd, self.int_reg.get_reg(inst.rs1) & self.int_reg.get_reg(inst.rs2)),
|
||||
_ => Err(format!("Unreachable in op_instruction match! Instruction was {:?}", inst))?
|
||||
RISCV_OP_XOR => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) ^ machine.int_reg.get_reg(inst.rs2 as usize));
|
||||
},
|
||||
RISCV_OP_SR => {
|
||||
// RISCV_OP_SR_SRL inaccessible
|
||||
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) >> machine.int_reg.get_reg(inst.rs2 as usize));
|
||||
},
|
||||
RISCV_OP_OR => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) | machine.int_reg.get_reg(inst.rs2 as usize));
|
||||
},
|
||||
RISCV_OP_AND => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) & machine.int_reg.get_reg(inst.rs2 as usize));
|
||||
},
|
||||
_ => {
|
||||
panic!("RISCV_OP undefined case\n");
|
||||
}
|
||||
}//LA
|
||||
}
|
||||
Ok(MachineOk::Ok)
|
||||
}
|
||||
|
||||
/// Exectutes simple RISC-V *iw instructions on the machine
|
||||
fn opiw_instruction(&mut self, inst: Instruction) -> Result<MachineOk, MachineError> {
|
||||
let local_data = self.int_reg.get_reg(inst.rs1);
|
||||
let result = match inst.funct3 {
|
||||
RISCV_OPIW_ADDIW => local_data + inst.imm12_I_signed as i64,
|
||||
RISCV_OPIW_SLLIW => local_data << inst.rs2,
|
||||
RISCV_OPIW_SRW => (local_data >> inst.rs2) & if inst.funct7 == RISCV_OPIW_SRW_SRLIW { self.shiftmask[32 + inst.rs2 as usize] as i64 } else { 1 },
|
||||
_ => Err(format!("Unreachable in op_instruction match! Instruction was {:?}", inst))?
|
||||
},
|
||||
//******************************************************************************************
|
||||
// Treatment for OPIW INSTRUCTIONS
|
||||
RISCV_OPIW => {
|
||||
let local_data = machine.int_reg.get_reg(inst.rs1 as usize);
|
||||
match inst.funct3 {
|
||||
RISCV_OPIW_ADDIW => {
|
||||
let result = local_data + inst.imm12_I_signed as i64;
|
||||
machine.int_reg.set_reg(inst.rd as usize, result);
|
||||
},
|
||||
RISCV_OPIW_SLLIW => {
|
||||
let result = local_data << inst.shamt;
|
||||
machine.int_reg.set_reg(inst.rd as usize, result);
|
||||
},
|
||||
RISCV_OPIW_SRW => {
|
||||
let result = if inst.funct7 == RISCV_OPIW_SRW_SRLIW {
|
||||
(local_data >> inst.shamt) & machine.shiftmask[32 + inst.shamt as usize] as i64
|
||||
} else { // SRAIW
|
||||
local_data >> inst.shamt
|
||||
};
|
||||
self.int_reg.set_reg(inst.rd, result);
|
||||
Ok(MachineOk::Ok)
|
||||
machine.int_reg.set_reg(inst.rd as usize, result);
|
||||
},
|
||||
_ => {
|
||||
panic!("In OPI switch case, this should never happen... Instr was {}\n", inst.value);
|
||||
}
|
||||
|
||||
/// Executes simple RISC-V *w instructions on the machine
|
||||
fn opw_instruction(&mut self, inst: Instruction) -> Result<MachineOk, MachineError> {
|
||||
}
|
||||
},
|
||||
//******************************************************************************************
|
||||
// Treatment for: OPW INSTRUCTIONS
|
||||
RISCV_OPW => {
|
||||
if inst.funct7 == 1 { // rv64m
|
||||
let local_data_a = self.int_reg.get_reg(inst.rs1) & 0xffffffff;
|
||||
let local_data_b = self.int_reg.get_reg(inst.rs2) & 0xffffffff;
|
||||
let local_data_a_unsigned = self.int_reg.get_reg(inst.rs1) & 0xffffffff;
|
||||
let local_data_b_unsigned = self.int_reg.get_reg(inst.rs2) & 0xffffffff;
|
||||
let local_data_a = machine.int_reg.get_reg(inst.rs1 as usize) & 0xffffffff;
|
||||
let local_data_b = machine.int_reg.get_reg(inst.rs2 as usize) & 0xffffffff;
|
||||
let local_data_a_unsigned = machine.int_reg.get_reg(inst.rs1 as usize) & 0xffffffff;
|
||||
let local_data_b_unsigned = machine.int_reg.get_reg(inst.rs2 as usize) & 0xffffffff;
|
||||
|
||||
// Match case for multiplication operations (in standard extension RV32M)
|
||||
match inst.funct3 {
|
||||
RISCV_OPW_M_MULW => self.int_reg.set_reg(inst.rd, (local_data_a * local_data_b) & 0xffffffff),
|
||||
RISCV_OPW_M_DIVW => self.int_reg.set_reg(inst.rd, local_data_a / local_data_b),
|
||||
RISCV_OPW_M_DIVUW => self.int_reg.set_reg(inst.rd, local_data_a_unsigned / local_data_b_unsigned),
|
||||
RISCV_OPW_M_REMW => self.int_reg.set_reg(inst.rd, local_data_a % local_data_b),
|
||||
RISCV_OPW_M_REMUW => self.int_reg.set_reg(inst.rd, local_data_a_unsigned % local_data_b_unsigned),
|
||||
_ => Err(format!("Unreachable in opw_instruction match! Instruction was {:?}", inst))?
|
||||
RISCV_OPW_M_MULW => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, local_data_a * local_data_b);
|
||||
},
|
||||
RISCV_OPW_M_DIVW => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, local_data_a / local_data_b);
|
||||
},
|
||||
RISCV_OPW_M_DIVUW => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, local_data_a_unsigned / local_data_b_unsigned);
|
||||
},
|
||||
RISCV_OPW_M_REMW => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, local_data_a % local_data_b);
|
||||
},
|
||||
RISCV_OPW_M_REMUW => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, local_data_a_unsigned % local_data_b_unsigned);
|
||||
},
|
||||
_ => {
|
||||
panic!("this instruction ({}) doesn't exists", inst.value);
|
||||
}
|
||||
}
|
||||
} else { // others rv64 OPW operations
|
||||
let local_dataa = self.int_reg.get_reg(inst.rs1) & 0xffffffff;
|
||||
let local_datab = self.int_reg.get_reg(inst.rs2) & 0xffffffff;
|
||||
let local_dataa = machine.int_reg.get_reg(inst.rs1 as usize) & 0xffffffff;
|
||||
let local_datab = machine.int_reg.get_reg(inst.rs2 as usize) & 0xffffffff;
|
||||
|
||||
// Match case for base OP operation
|
||||
match inst.funct3 {
|
||||
RISCV_OPW_ADDSUBW => if inst.funct7 == RISCV_OPW_ADDSUBW_ADDW {
|
||||
self.int_reg.set_reg(inst.rd, local_dataa + local_datab);
|
||||
RISCV_OPW_ADDSUBW => {
|
||||
if inst.funct7 == RISCV_OPW_ADDSUBW_ADDW {
|
||||
machine.int_reg.set_reg(inst.rd as usize, local_dataa + local_datab);
|
||||
} else { // SUBW
|
||||
self.int_reg.set_reg(inst.rd, local_dataa - local_datab);
|
||||
machine.int_reg.set_reg(inst.rd as usize, local_dataa - local_datab);
|
||||
}
|
||||
},
|
||||
RISCV_OPW_SLLW => self.int_reg.set_reg(inst.rd, local_dataa << (local_datab & 0x1f)),
|
||||
RISCV_OPW_SRW => if inst.funct7 == RISCV_OPW_SRW_SRLW {
|
||||
self.int_reg.set_reg(inst.rd, local_dataa >> (local_datab /* & 0x1f */) & self.shiftmask[32 + local_datab as usize] as i64)
|
||||
RISCV_OPW_SLLW => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, local_dataa << (local_datab & 0x1f));
|
||||
},
|
||||
RISCV_OPW_SRW => {
|
||||
if inst.funct7 == RISCV_OPW_SRW_SRLW {
|
||||
machine.int_reg.set_reg(inst.rd as usize, local_dataa >> (local_datab & 0x1f) & machine.shiftmask[32 + local_datab as usize] as i64);
|
||||
} else { // SRAW
|
||||
self.int_reg.set_reg(inst.rd, local_dataa >> (local_datab /* & 0x1f */))
|
||||
machine.int_reg.set_reg(inst.rd as usize, local_dataa >> (local_datab & 0x1f));
|
||||
}
|
||||
},
|
||||
_ => Err(format!("Unreachable in opw_instruction match! Instruction was {:?}", inst))?
|
||||
_ => {
|
||||
panic!("this instruction ({}) doesn't exists", inst.value);
|
||||
}
|
||||
}
|
||||
Ok(MachineOk::Ok)
|
||||
}
|
||||
|
||||
/// Executes simple RISC-V floating point instructions on the machine.
|
||||
///
|
||||
/// See Risc-V Spec v2.2 Chapter 8: “F” Standard Extension for Single-Precision Floating-Point, Version 2.0.
|
||||
fn fp_instruction(&mut self, inst: Instruction) -> Result<MachineOk, MachineError> {
|
||||
let mut set_reg = |operation: &dyn Fn (f32, f32) -> f32| {
|
||||
let a = self.fp_reg.get_reg(inst.rs1);
|
||||
let b = self.fp_reg.get_reg(inst.rs2);
|
||||
self.fp_reg.set_reg(inst.rd, operation(a, b));
|
||||
Ok(MachineOk::Ok)
|
||||
};
|
||||
},
|
||||
//******************************************************************************************
|
||||
// Treatment for: Simple floating point extension
|
||||
RISCV_FP => {
|
||||
match inst.funct7 {
|
||||
RISCV_FP_ADD => set_reg(&core::ops::Add::add),
|
||||
RISCV_FP_SUB => set_reg(&core::ops::Sub::sub),
|
||||
RISCV_FP_MUL => set_reg(&core::ops::Mul::mul),
|
||||
RISCV_FP_DIV => set_reg(&core::ops::Div::div),
|
||||
RISCV_FP_SQRT => { self.fp_reg.set_reg(inst.rd, self.fp_reg.get_reg(inst.rs1).sqrt()); Ok(MachineOk::Ok) },
|
||||
RISCV_FP_FSGN => self.fp_fsgn_instruction(inst),
|
||||
RISCV_FP_MINMAX => self.fp_minmax_instruction(inst),
|
||||
RISCV_FP_FCVTW => self.fp_fcvtw_instruction(inst),
|
||||
RISCV_FP_FCVTS => self.fp_fcvts_instruction(inst),
|
||||
RISCV_FP_FMVW => self.fp_fmvw_instruction(inst),
|
||||
RISCV_FP_FMVXFCLASS => self.fp_fmvxfclass_instruction(inst),
|
||||
RISCV_FP_FCMP => self.fp_fcmp_instruction(inst),
|
||||
_ => Err(format!("Unreachable in fp_instruction match! Instruction was {:?}", inst))?
|
||||
}
|
||||
}
|
||||
|
||||
/// Executes RISC-V sign-injection instruction on floating point values on the machine.
|
||||
fn fp_fsgn_instruction(&mut self, inst: Instruction) -> Result<MachineOk, MachineError> {
|
||||
let local_float = self.fp_reg.get_reg(inst.rs1);
|
||||
RISCV_FP_ADD => {
|
||||
machine.fp_reg.set_reg(inst.rd as usize, machine.fp_reg.get_reg(inst.rs1 as usize) + machine.fp_reg.get_reg(inst.rs2 as usize));
|
||||
},
|
||||
RISCV_FP_SUB => {
|
||||
machine.fp_reg.set_reg(inst.rd as usize, machine.fp_reg.get_reg(inst.rs1 as usize) - machine.fp_reg.get_reg(inst.rs2 as usize));
|
||||
},
|
||||
RISCV_FP_MUL => {
|
||||
machine.fp_reg.set_reg(inst.rd as usize, machine.fp_reg.get_reg(inst.rs1 as usize) * machine.fp_reg.get_reg(inst.rs2 as usize));
|
||||
},
|
||||
RISCV_FP_DIV => {
|
||||
machine.fp_reg.set_reg(inst.rd as usize, machine.fp_reg.get_reg(inst.rs1 as usize) / machine.fp_reg.get_reg(inst.rs2 as usize));
|
||||
},
|
||||
RISCV_FP_SQRT => {
|
||||
machine.fp_reg.set_reg(inst.rd as usize, machine.fp_reg.get_reg(inst.rs1 as usize).sqrt());
|
||||
},
|
||||
RISCV_FP_FSGN => {
|
||||
let local_float = machine.fp_reg.get_reg(inst.rs1 as usize);
|
||||
match inst.funct3 {
|
||||
RISCV_FP_FSGN_J => if self.fp_reg.get_reg(inst.rs2) < 0f32 {
|
||||
self.fp_reg.set_reg(inst.rd, -local_float);
|
||||
RISCV_FP_FSGN_J => {
|
||||
if machine.fp_reg.get_reg(inst.rs2 as usize) < 0f32 {
|
||||
machine.fp_reg.set_reg(inst.rd as usize, -local_float);
|
||||
} else {
|
||||
self.fp_reg.set_reg(inst.rd, local_float);
|
||||
},
|
||||
RISCV_FP_FSGN_JN => if self.fp_reg.get_reg(inst.rs2) < 0f32 {
|
||||
self.fp_reg.set_reg(inst.rd, local_float);
|
||||
} else {
|
||||
self.fp_reg.set_reg(inst.rd, -local_float);
|
||||
},
|
||||
RISCV_FP_FSGN_JX => if (self.fp_reg.get_reg(inst.rs2) < 0.0 && self.fp_reg.get_reg(inst.rs1) >= 0.0) ||
|
||||
(self.fp_reg.get_reg(inst.rs2) >= 0.0 && self.fp_reg.get_reg(inst.rs1) < 0.0) {
|
||||
self.fp_reg.set_reg(inst.rd, -local_float);
|
||||
} else {
|
||||
self.fp_reg.set_reg(inst.rd, local_float);
|
||||
},
|
||||
_ => Err(format!("Unreachable in fp_fsgn_instruction! Instruction was {:?}", inst))?
|
||||
machine.fp_reg.set_reg(inst.rd as usize, local_float);
|
||||
}
|
||||
Ok(MachineOk::Ok)
|
||||
}
|
||||
|
||||
/// Executes RISC-V min / max instruction on floating point values on the machine.
|
||||
fn fp_minmax_instruction(&mut self, inst: Instruction) -> Result<MachineOk, MachineError> {
|
||||
let r1 = self.fp_reg.get_reg(inst.rs1);
|
||||
let r2 = self.fp_reg.get_reg(inst.rs2);
|
||||
RISCV_FP_FSGN_JN => {
|
||||
if machine.fp_reg.get_reg(inst.rs2 as usize) < 0f32 {
|
||||
machine.fp_reg.set_reg(inst.rd as usize, local_float);
|
||||
} else {
|
||||
machine.fp_reg.set_reg(inst.rd as usize, -local_float);
|
||||
}
|
||||
}
|
||||
RISCV_FP_FSGN_JX => {
|
||||
if (machine.fp_reg.get_reg(inst.rs2 as usize) < 0.0 && machine.fp_reg.get_reg(inst.rs1 as usize) >= 0.0) || (machine.fp_reg.get_reg(inst.rs2 as usize) >= 0.0 && machine.fp_reg.get_reg(inst.rs1 as usize) < 0.0) {
|
||||
machine.fp_reg.set_reg(inst.rd as usize, -local_float);
|
||||
} else {
|
||||
machine.fp_reg.set_reg(inst.rd as usize, local_float);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
panic!("this instruction ({}) doesn't exists", inst.value);
|
||||
}
|
||||
}
|
||||
},
|
||||
RISCV_FP_MINMAX => {
|
||||
let r1 = machine.fp_reg.get_reg(inst.rs1 as usize);
|
||||
let r2 = machine.fp_reg.get_reg(inst.rs2 as usize);
|
||||
match inst.funct3 {
|
||||
RISCV_FP_MINMAX_MIN => self.fp_reg.set_reg(inst.rd, if r1 < r2 {r1} else {r2}),
|
||||
RISCV_FP_MINMAX_MAX => self.fp_reg.set_reg(inst.rd, if r1 > r2 {r1} else {r2}),
|
||||
_ => Err(format!("Unreachable in fp_minmax_instruction! Instruction was {:?}", inst))?
|
||||
};
|
||||
Ok(MachineOk::Ok)
|
||||
RISCV_FP_MINMAX_MIN => {
|
||||
machine.fp_reg.set_reg(inst.rd as usize, if r1 < r2 {r1} else {r2});
|
||||
},
|
||||
RISCV_FP_MINMAX_MAX => {
|
||||
machine.fp_reg.set_reg(inst.rd as usize, if r1 > r2 {r1} else {r2});
|
||||
},
|
||||
_ => {
|
||||
panic!("this instruction ({}) doesn't exists", inst.value);
|
||||
}
|
||||
|
||||
/// Executes RISC-V floating-point to integer conversion instruction on the machine.
|
||||
fn fp_fcvtw_instruction(&mut self, inst: Instruction) -> Result<MachineOk, MachineError> {
|
||||
}
|
||||
},
|
||||
RISCV_FP_FCVTW => {
|
||||
if inst.rs2 == RISCV_FP_FCVTW_W {
|
||||
self.int_reg.set_reg(inst.rd, self.fp_reg.get_reg(inst.rs1) as i64)
|
||||
machine.int_reg.set_reg(inst.rd as usize, machine.fp_reg.get_reg(inst.rs1 as usize) as i64);
|
||||
} else {
|
||||
self.int_reg.set_reg(inst.rd, (self.fp_reg.get_reg(inst.rs1) as u64) as i64)
|
||||
machine.int_reg.set_reg(inst.rd as usize, (machine.fp_reg.get_reg(inst.rs1 as usize) as u64) as i64);
|
||||
}
|
||||
Ok(MachineOk::Ok)
|
||||
}
|
||||
|
||||
/// Executes RISC-V integer to floating-point conversion instruction on the machine.
|
||||
fn fp_fcvts_instruction(&mut self, inst: Instruction) -> Result<MachineOk, MachineError> {
|
||||
},
|
||||
RISCV_FP_FCVTS => {
|
||||
if inst.rs2 == RISCV_FP_FCVTS_W {
|
||||
self.fp_reg.set_reg(inst.rd, self.int_reg.get_reg(inst.rs1) as f32);
|
||||
machine.fp_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) as f32);
|
||||
} else {
|
||||
self.fp_reg.set_reg(inst.rd, (self.int_reg.get_reg(inst.rs1) as u32) as f32);
|
||||
machine.fp_reg.set_reg(inst.rd as usize, (machine.int_reg.get_reg(inst.rs1 as usize) as u32) as f32);
|
||||
}
|
||||
Ok(MachineOk::Ok)
|
||||
}
|
||||
|
||||
/// Executes RISC-V move from int_reg to fp_reg instruction on the machine.
|
||||
fn fp_fmvw_instruction(&mut self, inst: Instruction) -> Result<MachineOk, MachineError> {
|
||||
self.fp_reg.set_reg(inst.rd, self.int_reg.get_reg(inst.rs1) as f32);
|
||||
Ok(MachineOk::Ok)
|
||||
}
|
||||
|
||||
/// Executes RISC-V move from fp_reg to int_reg instruction on the machine.
|
||||
fn fp_fmvxfclass_instruction(&mut self, inst: Instruction) -> Result<MachineOk, MachineError> {
|
||||
},
|
||||
RISCV_FP_FMVW => {
|
||||
machine.fp_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) as f32);
|
||||
},
|
||||
RISCV_FP_FMVXFCLASS => {
|
||||
if inst.funct3 == RISCV_FP_FMVXFCLASS_FMVX {
|
||||
self.int_reg.set_reg(inst.rd, self.fp_reg.get_reg(inst.rs1) as i64);
|
||||
Ok(MachineOk::Ok)
|
||||
machine.int_reg.set_reg(inst.rd as usize, machine.fp_reg.get_reg(inst.rs1 as usize) as i64);
|
||||
} else {
|
||||
Err(format!("Unreachable in fp_fmvxfclass_instruction! Instruction was {:?}", inst))?
|
||||
panic!("Fclass instruction is not handled in riscv simulator");
|
||||
}
|
||||
},
|
||||
RISCV_FP_FCMP => {
|
||||
match inst.funct3 {
|
||||
RISCV_FP_FCMP_FEQ => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, (machine.fp_reg.get_reg(inst.rs1 as usize) == machine.fp_reg.get_reg(inst.rs2 as usize)) as i64);
|
||||
},
|
||||
RISCV_FP_FCMP_FLT => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, (machine.fp_reg.get_reg(inst.rs1 as usize) < machine.fp_reg.get_reg(inst.rs2 as usize)) as i64);
|
||||
},
|
||||
RISCV_FP_FCMP_FLE => {
|
||||
machine.int_reg.set_reg(inst.rd as usize, (machine.fp_reg.get_reg(inst.rs1 as usize) <= machine.fp_reg.get_reg(inst.rs2 as usize)) as i64);
|
||||
},
|
||||
_ => {
|
||||
panic!("this instruction ({}) doesn't exists", inst.value);
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
panic!("this instruction ({}) doesn't exists", inst.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
RISCV_SYSTEM => {
|
||||
// temporary return value to stop the loop of run
|
||||
// before we can use system call
|
||||
return 1;
|
||||
}
|
||||
_ => { panic!("{:x} opcode non géré pc : {:x}", inst.opcode, machine.pc)},
|
||||
}
|
||||
|
||||
/// Executes RISC-V floating point values comparaison instructions on the machine.
|
||||
fn fp_fcmp_instruction(&mut self, inst: Instruction) -> Result<MachineOk, MachineError> {
|
||||
match inst.funct3 {
|
||||
RISCV_FP_FCMP_FEQ => self.int_reg.set_reg(inst.rd, (self.fp_reg.get_reg(inst.rs1) == self.fp_reg.get_reg(inst.rs2)) as i64),
|
||||
RISCV_FP_FCMP_FLT => self.int_reg.set_reg(inst.rd, (self.fp_reg.get_reg(inst.rs1) < self.fp_reg.get_reg(inst.rs2)) as i64),
|
||||
RISCV_FP_FCMP_FLE => self.int_reg.set_reg(inst.rd, (self.fp_reg.get_reg(inst.rs1) <= self.fp_reg.get_reg(inst.rs2)) as i64),
|
||||
_ => Err(format!("Unreachable in fp_fcmp_instruction match! Instruction was {:?}", inst))?
|
||||
}
|
||||
Ok(MachineOk::Ok)
|
||||
0
|
||||
|
||||
}
|
||||
|
||||
/// print memory FOR DEBUG
|
||||
///
|
||||
/// "@"adress [16 bytes]
|
||||
pub fn print_memory(&self, from: usize, to: usize) {
|
||||
/// "@"adresse [16 bytes]
|
||||
pub fn _print_memory(machine : &mut Machine, from: usize, to: usize) {
|
||||
for i in from..to {
|
||||
if i%16 == 0 {
|
||||
print!("\n@{:04x} ", i);
|
||||
}
|
||||
print!("{:02x}", self.main_memory[i]);
|
||||
print!("{:02x}", machine.main_memory[i]);
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
/// Get value from int register
|
||||
pub fn read_int_register(&self, index: usize) -> i64 {
|
||||
self.int_reg.get_reg(index as u8)
|
||||
self.int_reg.get_reg(index)
|
||||
}
|
||||
|
||||
/// Get value from float register
|
||||
pub fn read_fp_register(&self, index: usize) -> f32 {
|
||||
self.fp_reg.get_reg(index as u8)
|
||||
self.fp_reg.get_reg(index)
|
||||
}
|
||||
|
||||
/// Write into int register
|
||||
pub fn write_int_register(&mut self, index: usize, value: i64) {
|
||||
self.int_reg.set_reg(index as u8, value);
|
||||
self.int_reg.set_reg(index, value);
|
||||
}
|
||||
|
||||
/// Write info float register
|
||||
pub fn write_fp_register(&mut self, index: usize, value: f32) {
|
||||
self.fp_reg.set_reg(index as u8, value);
|
||||
}
|
||||
|
||||
pub fn get_status(&self) -> MachineStatus {
|
||||
todo!()
|
||||
}
|
||||
|
||||
pub fn set_status(&mut self, new_status: MachineStatus) {
|
||||
self.status = new_status;
|
||||
self.fp_reg.set_reg(index, value);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
mod test {
|
||||
use std::fs;
|
||||
|
||||
use crate::simulator::{machine::Machine, mem_cmp};
|
||||
use crate::utility::cfg::get_debug_configuration;
|
||||
|
||||
macro_rules! get_full_path {
|
||||
($prefix: expr, $test_name:expr) => {{
|
||||
let mut s = String::from("test/machine/");
|
||||
s.push_str($prefix);
|
||||
s.push_str($test_name);
|
||||
s.push_str(".txt");
|
||||
&s.to_owned()
|
||||
}}
|
||||
}
|
||||
|
||||
macro_rules! init_test {
|
||||
($a:expr) => {{
|
||||
let mut m = Machine::new(true, get_debug_configuration());
|
||||
let end_file_name = { let mut s = String::from($a); s.push_str("End"); s };
|
||||
let memory_before = mem_cmp::MemChecker::from(get_full_path!("memory", $a)).unwrap();
|
||||
let memory_after = mem_cmp::MemChecker::from(get_full_path!("memory", &end_file_name)).unwrap();
|
||||
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||
let mut system = crate::kernel::system::System::new(true);
|
||||
m._run_debug(&mut system);
|
||||
let expected_trace = fs::read_to_string(get_full_path!("reg_trace", $a)).unwrap();
|
||||
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||
}};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_init_machine() {
|
||||
let _ = Machine::new(true, get_debug_configuration());
|
||||
let _ = Machine::init_machine();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_memory() {
|
||||
let mut m = Machine::new(true, get_debug_configuration());
|
||||
let mut m = Machine::init_machine();
|
||||
m.main_memory[4] = 43;
|
||||
m.main_memory[5] = 150;
|
||||
assert_eq!((43 << 8) + 150, m.read_memory(2, 4));
|
||||
assert_eq!((43 << 8) + 150, Machine::read_memory(&mut m, 2, 4));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_write_memory() {
|
||||
let mut m = Machine::new(true, get_debug_configuration());
|
||||
m.write_memory(2, 6, (43 << 8) + 150);
|
||||
let mut m = Machine::init_machine();
|
||||
Machine::write_memory(&mut m, 2, 6, (43 << 8) + 150);
|
||||
assert_eq!(43, m.main_memory[6]);
|
||||
assert_eq!(150, m.main_memory[7]);
|
||||
m.write_memory(4, 8, (52 << 24) + (20 << 16) + (43 << 8) + 150);
|
||||
Machine::write_memory(&mut m, 4, 8, (52 << 24) + (20 << 16) + (43 << 8) + 150);
|
||||
assert_eq!(52, m.main_memory[8]);
|
||||
assert_eq!(20, m.main_memory[9]);
|
||||
assert_eq!(43, m.main_memory[10]);
|
||||
@ -765,46 +756,127 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn test_comp() {
|
||||
init_test!("Comp")
|
||||
let mut m = Machine::init_machine();
|
||||
let memory_before = mem_cmp::MemChecker::from("test/machine/memoryComp.txt").unwrap();
|
||||
let memory_after = mem_cmp::MemChecker::from("test/machine/memoryCompEnd.txt").unwrap();
|
||||
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||
Machine::run(&mut m);
|
||||
|
||||
let expected_trace = fs::read_to_string("test/machine/reg_traceComp.txt").unwrap();
|
||||
|
||||
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add() {
|
||||
init_test!("Add")
|
||||
let mut m = Machine::init_machine();
|
||||
let memory_before = mem_cmp::MemChecker::from("test/machine/memoryAdd.txt").unwrap();
|
||||
let memory_after = mem_cmp::MemChecker::from("test/machine/memoryAddEnd.txt").unwrap();
|
||||
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||
Machine::run(&mut m);
|
||||
|
||||
let expected_trace = fs::read_to_string("test/machine/reg_traceAdd.txt").unwrap();
|
||||
|
||||
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_div() {
|
||||
init_test!("Div")
|
||||
let mut m = Machine::init_machine();
|
||||
let memory_before = mem_cmp::MemChecker::from("test/machine/memoryDiv.txt").unwrap();
|
||||
let memory_after = mem_cmp::MemChecker::from("test/machine/memoryDivEnd.txt").unwrap();
|
||||
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||
Machine::run(&mut m);
|
||||
|
||||
let expected_trace = fs::read_to_string("test/machine/reg_traceDiv.txt").unwrap();
|
||||
|
||||
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_if() {
|
||||
init_test!("If")
|
||||
let mut m = Machine::init_machine();
|
||||
let memory_before = mem_cmp::MemChecker::from("test/machine/memoryIf.txt").unwrap();
|
||||
let memory_after = mem_cmp::MemChecker::from("test/machine/memoryIfEnd.txt").unwrap();
|
||||
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||
Machine::run(&mut m);
|
||||
|
||||
let expected_trace = fs::read_to_string("test/machine/reg_traceIf.txt").unwrap();
|
||||
|
||||
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_jump() {
|
||||
init_test!("Jump")
|
||||
let mut m = Machine::init_machine();
|
||||
let memory_before = mem_cmp::MemChecker::from("test/machine/memoryJump.txt").unwrap();
|
||||
let memory_after = mem_cmp::MemChecker::from("test/machine/memoryJumpEnd.txt").unwrap();
|
||||
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||
Machine::run(&mut m);
|
||||
|
||||
let expected_trace = fs::read_to_string("test/machine/reg_traceJump.txt").unwrap();
|
||||
|
||||
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul() {
|
||||
init_test!("Mult")
|
||||
let mut m = Machine::init_machine();
|
||||
let memory_before = mem_cmp::MemChecker::from("test/machine/memoryMult.txt").unwrap();
|
||||
let memory_after = mem_cmp::MemChecker::from("test/machine/memoryMultEnd.txt").unwrap();
|
||||
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||
Machine::run(&mut m);
|
||||
|
||||
let expected_trace = fs::read_to_string("test/machine/reg_traceMult.txt").unwrap();
|
||||
|
||||
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ret() {
|
||||
init_test!("Ret")
|
||||
let mut m = Machine::init_machine();
|
||||
let memory_before = mem_cmp::MemChecker::from("test/machine/memoryRet.txt").unwrap();
|
||||
let memory_after = mem_cmp::MemChecker::from("test/machine/memoryRetEnd.txt").unwrap();
|
||||
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||
Machine::run(&mut m);
|
||||
|
||||
let expected_trace = fs::read_to_string("test/machine/reg_traceRet.txt").unwrap();
|
||||
|
||||
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sub() {
|
||||
init_test!("Sub")
|
||||
let mut m = Machine::init_machine();
|
||||
let memory_before = mem_cmp::MemChecker::from("test/machine/memorySub.txt").unwrap();
|
||||
let memory_after = mem_cmp::MemChecker::from("test/machine/memorySubEnd.txt").unwrap();
|
||||
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||
Machine::run(&mut m);
|
||||
|
||||
let expected_trace = fs::read_to_string("test/machine/reg_traceSub.txt").unwrap();
|
||||
|
||||
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_switch() {
|
||||
init_test!("Switch")
|
||||
let mut m = Machine::init_machine();
|
||||
let memory_before = mem_cmp::MemChecker::from("test/machine/memorySwitch.txt").unwrap();
|
||||
let memory_after = mem_cmp::MemChecker::from("test/machine/memorySwitchEnd.txt").unwrap();
|
||||
mem_cmp::MemChecker::fill_memory_from_mem_checker(&memory_before, &mut m);
|
||||
Machine::run(&mut m);
|
||||
|
||||
let expected_trace = fs::read_to_string("test/machine/reg_traceSwitch.txt").unwrap();
|
||||
|
||||
assert!(mem_cmp::MemChecker::compare_machine_memory(&memory_after, &m));
|
||||
assert!(expected_trace.contains(m.registers_trace.as_str()));
|
||||
}
|
||||
}
|
||||
|
@ -1,37 +1,20 @@
|
||||
//! # Memory Comparator
|
||||
//!
|
||||
//! This module contains a MemChecker.
|
||||
//!
|
||||
//! It's used to compare state memory obtained after a dump memory from NachOS and BurritOS.
|
||||
//!
|
||||
//! This module is used exclusively for testing the instruction simulator.
|
||||
//!
|
||||
//! Basic usage:
|
||||
//!
|
||||
//! ```
|
||||
//! let mut m = Machine::new(true, get_debug_configuration());
|
||||
//! let mut MemChecker = mem_cmp::MemChecker::from(get_full_path!("memory", expr));
|
||||
//! mem_cmp::MemChecker::fill_memory_from_mem_checker(&MemChecker, &mut m);
|
||||
//! ```
|
||||
//!
|
||||
//!
|
||||
//! ! FILE.TXT FORMAT Representing machine memory memory
|
||||
//! - PC
|
||||
//! - SP
|
||||
//! - Section_1
|
||||
//! - Section_2
|
||||
//! - ...
|
||||
//! - Section_n
|
||||
//!
|
||||
//! Each section is divided in 3 parts, on two lines of text
|
||||
//! addr SPACE len
|
||||
//! content
|
||||
///! FILE.TXT FORMAT Representing machine memory memory
|
||||
/// - PC
|
||||
/// - SP
|
||||
/// - Section_1
|
||||
/// - Section_2
|
||||
/// - ...
|
||||
/// - Section_n
|
||||
///
|
||||
/// Each section is divided in 3 parts, on two lines of text
|
||||
/// addr SPACE len
|
||||
/// content
|
||||
|
||||
use std::{fs, io::{BufRead, BufReader, Lines, Error}};
|
||||
use crate::Machine;
|
||||
|
||||
/// File section
|
||||
pub struct SectionFormat {
|
||||
pub struct SectionFormat{
|
||||
/// Memory address of the section
|
||||
addr: String,
|
||||
/// The size of data in bytes
|
||||
@ -43,7 +26,7 @@ pub struct SectionFormat {
|
||||
/// # Memory section
|
||||
///
|
||||
/// Representation of a section of memory from BurritOS or NachOS
|
||||
pub struct Section {
|
||||
pub struct Section{
|
||||
/// Memory address of the section
|
||||
addr: usize,
|
||||
/// The size of data in bytes
|
||||
@ -53,23 +36,15 @@ pub struct Section {
|
||||
}
|
||||
|
||||
impl Section {
|
||||
|
||||
/// Creates a memory section from a SectionFormat
|
||||
fn from(section: &mut SectionFormat) -> Section {
|
||||
fn from(section: &SectionFormat) -> Section {
|
||||
let addr = usize::from_str_radix(§ion.addr, 16).unwrap_or_default();
|
||||
let len = usize::from_str_radix(§ion.len, 16).unwrap_or_default();
|
||||
let content: Vec<u8>;
|
||||
unsafe {
|
||||
content = section.content.as_bytes_mut()
|
||||
.chunks_mut(4).map(
|
||||
|x| {
|
||||
x.reverse();
|
||||
u8::from_str_radix(
|
||||
std::str::from_utf8(x).unwrap_or_default(), 16
|
||||
).unwrap_or_default()
|
||||
}
|
||||
).collect();
|
||||
}
|
||||
Section { addr, len, content }
|
||||
let content: Vec<u8> = section.content.as_bytes().chunks(2).map(|x| {
|
||||
u8::from_str_radix(std::str::from_utf8(x).unwrap_or_default(), 16).unwrap_or_default()
|
||||
}).collect();
|
||||
Section{addr, len, content}
|
||||
}
|
||||
|
||||
/// Pretty prints a memory section
|
||||
@ -93,7 +68,7 @@ pub struct MemChecker {
|
||||
}
|
||||
|
||||
|
||||
impl MemChecker {
|
||||
impl MemChecker{
|
||||
|
||||
///Translate lines of a file in e Vector of String
|
||||
///We need this method to parse the memory we received
|
||||
@ -119,7 +94,7 @@ impl MemChecker {
|
||||
/// Extract the values of pc, sp and sections
|
||||
///
|
||||
/// ### Parameter
|
||||
/// - **path** addr to the file
|
||||
/// -**path** addr to the file
|
||||
///
|
||||
/// ### Return
|
||||
/// Mem-checker filled
|
||||
@ -151,12 +126,12 @@ impl MemChecker {
|
||||
}
|
||||
else {
|
||||
//lecture ligne CONTENT
|
||||
let mut section_f = SectionFormat {
|
||||
let section_f = SectionFormat{
|
||||
addr: tmp_addr_str.clone(),
|
||||
len: tmp_len_str.clone(),
|
||||
content: current_line.clone().replace(' ', ""),
|
||||
};
|
||||
sections.push(Section::from(&mut section_f));
|
||||
sections.push(Section::from(§ion_f));
|
||||
}
|
||||
|
||||
}
|
||||
@ -194,7 +169,7 @@ impl MemChecker {
|
||||
machine.pc = m_c.pc as u64;
|
||||
|
||||
for section in m_c.sections.iter() {
|
||||
for (i, b) in section.content.iter().enumerate() {
|
||||
for (i,b) in section.content.iter().enumerate() {
|
||||
machine.main_memory[section.addr + i] = *b;
|
||||
}
|
||||
}
|
||||
@ -231,14 +206,12 @@ impl MemChecker {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::utility::cfg::get_debug_configuration;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_fill_memory(){
|
||||
let m_c = MemChecker::from("test/machine/memoryAdd.txt").unwrap();
|
||||
let mut machine = Machine::new(true, get_debug_configuration());
|
||||
let mut machine = Machine::init_machine();
|
||||
MemChecker::fill_memory_from_mem_checker(&m_c, &mut machine);
|
||||
MemChecker::compare_print_m_c_machine(&m_c, &mut machine);
|
||||
}
|
||||
@ -260,12 +233,12 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn test_create_section_content(){
|
||||
let mut section_format = SectionFormat{
|
||||
let section_format = SectionFormat{
|
||||
addr: "0".to_string(),
|
||||
len: "0".to_string(),
|
||||
content: "00FF0AA0A5".to_string(),
|
||||
};
|
||||
let section = Section::from(&mut section_format);
|
||||
let section = Section::from(§ion_format);
|
||||
let expected_vec: Vec<u8> = vec![0u8, 255u8, 10u8, 160u8, 165u8];
|
||||
assert_eq!(section.content, expected_vec);
|
||||
}
|
||||
|
@ -1,43 +1,20 @@
|
||||
//! # MMU
|
||||
//!
|
||||
//! This module contains a MMU implementation
|
||||
//!
|
||||
//! This part isn't tested nor integrated to BurritOS because of the lack of pagination implementation
|
||||
//!
|
||||
//!
|
||||
|
||||
use crate::simulator::translationtable::*;
|
||||
use crate::simulator::machine::*;
|
||||
|
||||
/// # Memory Management Unit
|
||||
/// An MMU possesses a single reference to a translation table
|
||||
/// This table is associated to the current process
|
||||
pub struct MMU <'a>{
|
||||
/// Reference to a page table
|
||||
translationTable : Option<&'a mut TranslationTable>,
|
||||
/// The number of physique pages
|
||||
numPhyPages : u64,
|
||||
/// Size of each page
|
||||
pageSize : u64
|
||||
/* Un MMU possède une seule référence vers une table des pages à un instant donné
|
||||
* Cette table est associée au processus courant
|
||||
* Cette référence peut etre mise a jour par exemple lors d'un switchTo
|
||||
*/
|
||||
translationTable : Option<&'a mut TranslationTable>
|
||||
}
|
||||
|
||||
impl <'a>MMU <'_>{
|
||||
|
||||
/// Create a MMU with a None reference for the translation table
|
||||
///
|
||||
/// ### Parameters
|
||||
///
|
||||
/// - **numPhyPages** the number of physique pages
|
||||
/// - **pageSize** the size of a page
|
||||
///
|
||||
/// ### Return
|
||||
///
|
||||
/// MMU with None reference and the value for the number of physical pages and pae size associated
|
||||
fn create(numPhyPages: u64, pageSize: u64) -> MMU <'a>{
|
||||
MMU {
|
||||
translationTable : None,
|
||||
numPhyPages,
|
||||
pageSize
|
||||
fn create() -> MMU <'a>{
|
||||
|
||||
MMU{
|
||||
translationTable : None
|
||||
}
|
||||
}
|
||||
|
||||
@ -52,7 +29,7 @@ impl <'a>MMU <'_>{
|
||||
MMU::translate(mmu, virt_addr, &mut phy_addr_double_check, false);
|
||||
|
||||
match exc {
|
||||
ExceptionType::NoException => {
|
||||
ExceptionType::NO_EXCEPTION => {
|
||||
if phy_addr != phy_addr_double_check {
|
||||
//Besoin ici d'une impl pour gestion d'exeption
|
||||
//dans nachos : g-machine->RaiseException(exc, virt_addr);
|
||||
@ -67,7 +44,7 @@ impl <'a>MMU <'_>{
|
||||
_ => {
|
||||
//Besoin ici d'une impl pour gestion d'exeption
|
||||
//dans nachos : g-machine->RaiseException(exc, virt_addr);
|
||||
println!("Error from mmu_read_mem :: Exception different from NoException");
|
||||
println!("Error from mmu_read_mem :: Exception different from NO_EXCEPTION");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -86,7 +63,7 @@ impl <'a>MMU <'_>{
|
||||
MMU::translate(mmu, virt_addr, &mut phy_addr_double_check, true);
|
||||
|
||||
match exc {
|
||||
ExceptionType::NoException => {
|
||||
ExceptionType::NO_EXCEPTION => {
|
||||
if phy_addr != phy_addr_double_check {
|
||||
//Besoin ici d'une impl pour gestion d'exeption
|
||||
//dans nachos : g-machine->RaiseException(exc, virt_addr);
|
||||
@ -101,7 +78,7 @@ impl <'a>MMU <'_>{
|
||||
_ => {
|
||||
//Besoin ici d'une impl pour gestion d'exeption
|
||||
//dans nachos : g-machine->RaiseException(exc, virt_addr);
|
||||
println!("Error from mmu_write_mem :: Exception different from NoException");
|
||||
println!("Error from mmu_write_mem :: Exception different from NO_EXCEPTION");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -111,15 +88,13 @@ impl <'a>MMU <'_>{
|
||||
|
||||
pub fn translate(mmu : &mut MMU, virtAddr : u64, physAddr : &mut u64, writing : bool) -> ExceptionType {
|
||||
|
||||
let vpn : u64 = virtAddr/(mmu.pageSize); //virtual page index
|
||||
let offset : u64 = virtAddr%(mmu.pageSize); //adresse intra page
|
||||
|
||||
|
||||
let vpn : u64 = virtAddr/PAGE_SIZE; //virtual page index
|
||||
let offset : u64 = virtAddr%PAGE_SIZE; //adresse intra page
|
||||
|
||||
match &mut mmu.translationTable {
|
||||
None => {
|
||||
println!("Error from translate : MMU refers to None (No page Table)");
|
||||
return ExceptionType::AddressErrorException;
|
||||
return ExceptionType::ADDRESSERROR_EXCEPTION;
|
||||
}
|
||||
|
||||
Some(table_ref) => {
|
||||
@ -127,7 +102,7 @@ impl <'a>MMU <'_>{
|
||||
//On verifie que notre index est valide
|
||||
if vpn >= table_ref.get_max_num_pages(){
|
||||
println!("Error from translate :: index is out of bound");
|
||||
return ExceptionType::AddressErrorException;
|
||||
return ExceptionType::ADDRESSERROR_EXCEPTION;
|
||||
}
|
||||
|
||||
/*Doc nachos dit que ce test sert a savoir si la page est mappée
|
||||
@ -136,13 +111,13 @@ impl <'a>MMU <'_>{
|
||||
*/
|
||||
if !table_ref.get_bit_read(vpn) && !table_ref.get_bit_write(vpn) {
|
||||
println!("Error from translate :: virtual page # {} not mapped",vpn);
|
||||
return ExceptionType::AddressErrorException;
|
||||
return ExceptionType::ADDRESSERROR_EXCEPTION;
|
||||
}
|
||||
|
||||
//si on souhaite effectuer un acces lecture, on verifie que l'on dispose du droit d'acces sur cette page
|
||||
if writing && !table_ref.get_bit_write(vpn) {
|
||||
println!("Error from translate :: write access on a read only virtual page # {}",vpn);
|
||||
return ExceptionType::AddressErrorException;
|
||||
return ExceptionType::READONLY_EXCEPTION;
|
||||
}
|
||||
|
||||
//if the page is not yet in main memory, run the page fault manager
|
||||
@ -152,13 +127,13 @@ impl <'a>MMU <'_>{
|
||||
println!("We need to update the page table by raising an exception -> not implemented");
|
||||
|
||||
//Ici il faudra reverifier le bit valid apres intervention du page fault manager
|
||||
return ExceptionType::AddressErrorException;
|
||||
return ExceptionType::ADDRESSERROR_EXCEPTION;
|
||||
}
|
||||
|
||||
//Make sure that the physical adress is correct
|
||||
if table_ref.get_physical_page(vpn) < 0 || table_ref.get_physical_page(vpn) >= (mmu.numPhyPages as i32) {
|
||||
if table_ref.get_physical_page(vpn) < 0 || table_ref.get_physical_page(vpn) >= (NUM_PHY_PAGE as i32) {
|
||||
println!("Error from translate :: no valid correspondance");
|
||||
return ExceptionType::BusErrorException;
|
||||
return ExceptionType::BUSERROR_EXCEPTION;
|
||||
}
|
||||
|
||||
//Set U/M bits to 1
|
||||
@ -170,10 +145,10 @@ impl <'a>MMU <'_>{
|
||||
|
||||
//on se permet ici la conversion du champs physical_page de i32 vers u64
|
||||
//si cette valeur avait été signée, cela aurait été detecté juste au dessus, renvoyant une BUSERROR_EXCEPTION
|
||||
*physAddr = (table_ref.get_physical_page(vpn) as u64)*(mmu.pageSize) + offset;
|
||||
*physAddr = (table_ref.get_physical_page(vpn) as u64)*PAGE_SIZE + offset;
|
||||
}
|
||||
}
|
||||
|
||||
ExceptionType::NoException
|
||||
ExceptionType::NO_EXCEPTION
|
||||
}
|
||||
}
|
@ -1,23 +1,13 @@
|
||||
//! This module implement an Instruction simulator
|
||||
//! with all the simulated hardware requested to run the Machine :
|
||||
//! - **MMU**
|
||||
//! - **Processor**
|
||||
//! - **RAM**
|
||||
//! - **Interruption Controler**
|
||||
//!
|
||||
//! The disk, the console and the serial coupler aren't implmented for now
|
||||
//!
|
||||
pub mod machine;
|
||||
pub mod error;
|
||||
pub mod instruction;
|
||||
pub mod decode;
|
||||
pub mod print;
|
||||
pub mod mem_cmp;
|
||||
pub mod loader;
|
||||
pub mod interrupt;
|
||||
pub mod translationtable;
|
||||
pub mod mmu;
|
||||
pub mod register;
|
||||
pub mod disk;
|
||||
|
||||
/// Definition of global constants
|
||||
pub mod global {
|
||||
|
||||
#![allow(dead_code)]
|
||||
@ -63,15 +53,15 @@ pub mod global {
|
||||
///
|
||||
/// See func3 to know the type of instruction (LD, LW, LH, LB, LWU, LHU, LBU)
|
||||
pub const RISCV_LD: u8 = 0x3;
|
||||
/// Store instructions
|
||||
// Store instructions
|
||||
pub const RISCV_ST: u8 = 0x23;
|
||||
/// immediate Arithmetic operations
|
||||
// immediate Arithmetic operations
|
||||
pub const RISCV_OPI: u8 = 0x13;
|
||||
/// Arithmetic operations
|
||||
// Arithmetic operations
|
||||
pub const RISCV_OP: u8 = 0x33;
|
||||
/// Immediate arithmetic operations for rv64i
|
||||
pub const RISCV_OPIW: u8 = 0x1b;
|
||||
/// Arithmetic operations for rv64i
|
||||
// Arithmetic operations for rv64i
|
||||
pub const RISCV_OPW: u8 = 0x3b;
|
||||
|
||||
/// Type: B
|
||||
@ -223,7 +213,7 @@ pub mod global {
|
||||
///
|
||||
/// Shift left logical immediate
|
||||
///
|
||||
/// `SLLI rd, rs1, shamt` => `rd <- rs1 << shamt`
|
||||
/// `SLLI rd, rs1, shamt` => `rd <- rs1 >> shamt`
|
||||
pub const RISCV_OPI_SLLI: u8 = 0x1;
|
||||
/// Shift right immediate, may be SRAI or SRLI
|
||||
pub const RISCV_OPI_SRI: u8 = 0x5;
|
||||
|
414
src/simulator/print.rs
Normal file
414
src/simulator/print.rs
Normal file
@ -0,0 +1,414 @@
|
||||
#![allow(dead_code)]
|
||||
use super::decode::{Instruction};
|
||||
use super::global::*;
|
||||
|
||||
const NAMES_OP: [&str; 8] = ["add", "sll", "slt", "sltu", "xor", "sr", "or", "and"];
|
||||
const NAMES_OPI: [&str; 8] = ["addi", "slli", "slti", "sltiu", "xori", "slri", "ori", "andi"];
|
||||
const NAMES_MUL: [&str; 8] = ["mul", "mulh", "mulhsu", "mulhu", "div", "divu", "rem", "remu"];
|
||||
const NAMES_BR: [&str; 8] = ["beq", "bne", "", "", "blt", "bge", "bltu", "bgeu"];
|
||||
const NAMES_ST: [&str; 4] = ["sb", "sh", "sw", "sd"];
|
||||
const NAMES_LD: [&str; 7] = ["lb", "lh", "lw", "ld", "lbu", "lhu", "lwu"];
|
||||
const NAMES_OPW: [&str; 8] = ["addw", "sllw", "", "", "", "srw", "", ""];
|
||||
const NAMES_OPIW: [&str; 8] = ["addiw", "slliw", "", "", "", "sri", "", ""];
|
||||
|
||||
|
||||
// Register name mapping
|
||||
pub const REG_X: [&str; 32] = ["zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "s1",
|
||||
"a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
|
||||
"s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11",
|
||||
"t3", "t4", "t5", "t6"];
|
||||
|
||||
const REG_F: [&str; 32] = ["ft0", "ft1", "ft2", "ft3", "ft4", "ft5", "ft6", "ft7", "fs0", "fs1",
|
||||
"fa0", "fa1", "fa2", "fa3", "fa4", "fa5", "fa6", "fa7",
|
||||
"fs2", "fs3", "fs4", "fs5", "fs6", "fs7", "fs8", "fs9", "fs10", "fs11",
|
||||
"ft8", "ft9", "ft10", "ft11"];
|
||||
|
||||
|
||||
pub fn print(ins: Instruction, pc: i32) -> String { //TODO pc should be u64
|
||||
let rd = ins.rd as usize;
|
||||
let rs1 = ins.rs1 as usize;
|
||||
let rs2 = ins.rs2 as usize;
|
||||
let rs3 = ins.rs3 as usize;
|
||||
|
||||
match ins.opcode {
|
||||
RISCV_OP => {
|
||||
let name: &str;
|
||||
if ins.funct7 == 1 { // Use mul array
|
||||
name = NAMES_MUL[ins.funct3 as usize]
|
||||
} else if ins.funct3 == RISCV_OP_ADD {
|
||||
// Add or Sub
|
||||
if ins.funct7 == RISCV_OP_ADD_ADD {
|
||||
name = "add";
|
||||
} else {
|
||||
name = "sub";
|
||||
}
|
||||
} else if ins.funct3 == RISCV_OP_SR {
|
||||
// Srl or Sra
|
||||
if ins.funct7 == RISCV_OP_SR_SRL {
|
||||
name = "srl";
|
||||
} else {
|
||||
name = "sra";
|
||||
}
|
||||
} else {
|
||||
name = NAMES_OP[ins.funct3 as usize];
|
||||
}
|
||||
format!("{}\t{},{},{}", name, REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||
},
|
||||
RISCV_OPI => {
|
||||
// SHAMT OR IMM
|
||||
if ins.funct3 == RISCV_OPI_SRI {
|
||||
if ins.funct7 == RISCV_OPI_SRI_SRLI {
|
||||
format!("srli\t{},{},{}", REG_X[rd], REG_X[rs1], ins.shamt)
|
||||
} else {
|
||||
format!("srai\t{},{},{}", REG_X[rd], REG_X[rs1], ins.shamt)
|
||||
}
|
||||
} else if ins.funct3 == RISCV_OPI_SLLI {
|
||||
format!("{}\t{},{},{}", NAMES_OPI[ins.funct3 as usize], REG_X[rd], REG_X[rs1], ins.shamt)
|
||||
} else {
|
||||
format!("{}\t{},{},{}", NAMES_OPI[ins.funct3 as usize], REG_X[rd], REG_X[rs1], ins.imm12_I_signed)
|
||||
}
|
||||
},
|
||||
RISCV_LUI => {
|
||||
format!("lui\t{},{:x}", REG_X[rd], ins.imm31_12)
|
||||
},
|
||||
RISCV_AUIPC => {
|
||||
format!("auipc\t{},{:x}", REG_X[rd], ins.imm31_12)
|
||||
},
|
||||
RISCV_JAL => {
|
||||
format!("jal\t{},{:x}", REG_X[rd], (pc + ins.imm21_1_signed))
|
||||
},
|
||||
RISCV_JALR => {
|
||||
format!("jalr\t{},{:x}({})", REG_X[rd], ins.imm12_I_signed, REG_X[rs1])
|
||||
},
|
||||
RISCV_BR => {
|
||||
format!("{}\t{},{},{:x}", NAMES_BR[ins.funct3 as usize], REG_X[rs1], REG_X[rs2], pc + (ins.imm13_signed as i32))
|
||||
},
|
||||
RISCV_LD => {
|
||||
format!("{}\t{},{}({})", NAMES_LD[ins.funct3 as usize], REG_X[rd], ins.imm12_I_signed, REG_X[rs1])
|
||||
},
|
||||
RISCV_ST => {
|
||||
format!("{}\t{},{}({})", NAMES_ST[ins.funct3 as usize], REG_X[rs2], ins.imm12_S_signed, REG_X[rs1])
|
||||
},
|
||||
RISCV_OPIW => {
|
||||
if ins.funct3 == RISCV_OPIW_SRW {
|
||||
if ins.funct7 == RISCV_OPIW_SRW_SRLIW {
|
||||
format!("srliw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||
} else {
|
||||
format!("sraiw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||
}
|
||||
} else {
|
||||
format!("{}\t{},{},0x{:x}", NAMES_OPIW[ins.funct3 as usize], REG_X[rd], REG_X[rs1], ins.imm12_I_signed)
|
||||
}
|
||||
},
|
||||
RISCV_OPW => {
|
||||
if ins.funct7 == 1 {
|
||||
format!("{}w\t{},{},{}", NAMES_MUL[ins.funct3 as usize], REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||
} else if ins.funct3 == RISCV_OP_ADD {
|
||||
if ins.funct7 == RISCV_OPW_ADDSUBW_ADDW {
|
||||
format!("addw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||
} else {
|
||||
format!("subw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||
}
|
||||
} else if ins.funct3 == RISCV_OPW_SRW {
|
||||
if ins.funct7 == RISCV_OPW_SRW_SRLW {
|
||||
format!("srlw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||
} else {
|
||||
format!("sraw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||
}
|
||||
} else {
|
||||
format!("{}\t{},{},{}", NAMES_OPW[ins.funct3 as usize], REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||
}
|
||||
},
|
||||
// RV32F Standard Extension
|
||||
RISCV_FLW => {
|
||||
format!("flw\t{},{},({})", REG_F[rd], ins.imm12_I_signed, REG_F[rs1])
|
||||
},
|
||||
RISCV_FSW => {
|
||||
format!("fsw\t{},{},({})", REG_F[rs2], "OFFSET TODO", REG_F[rs1]) // TODO Offset in decode
|
||||
},
|
||||
RISCV_FMADD => {
|
||||
format!("fmadd\t{}{}{}{}", REG_F[rd], REG_F[rs1], REG_F[rs2], REG_F[rs3])
|
||||
},
|
||||
RISCV_FMSUB => {
|
||||
format!("fmsub\t{}{}{}{}", REG_F[rd], REG_F[rs1], REG_F[rs2], REG_F[rs3])
|
||||
},
|
||||
RISCV_FNMSUB => {
|
||||
format!("fnmsub\t{}{}{}{}", REG_F[rd], REG_F[rs1], REG_F[rs2], REG_F[rs3])
|
||||
},
|
||||
RISCV_FNMADD => {
|
||||
format!("fnmadd\t{}{}{}{}", REG_F[rd], REG_F[rs1], REG_F[rs2], REG_F[rs3])
|
||||
},
|
||||
RISCV_FP => {
|
||||
match ins.funct7 {
|
||||
RISCV_FP_ADD => {
|
||||
format!("{}\t{}{}{}", "fadd", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
},
|
||||
RISCV_FP_SUB => {
|
||||
format!("{}\t{}{}{}", "fsub.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
},
|
||||
RISCV_FP_MUL => {
|
||||
format!("{}\t{}{}{}", "fmul.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
},
|
||||
RISCV_FP_DIV => {
|
||||
format!("{}\t{}{}{}", "fdiv.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
},
|
||||
RISCV_FP_SQRT => {
|
||||
format!("{}\t{}{}", "fsqrt.s", REG_F[rd], REG_F[rs1])
|
||||
},
|
||||
RISCV_FP_FSGN => {
|
||||
match ins.funct3 {
|
||||
RISCV_FP_FSGN_J => {
|
||||
format!("{}\t{}{}{}", "fsgnj.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
},
|
||||
RISCV_FP_FSGN_JN => {
|
||||
format!("{}\t{}{}{}", "fsgnn.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
},
|
||||
RISCV_FP_FSGN_JX => {
|
||||
format!("{}\t{}{}{}", "fsgnx.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
},
|
||||
_ => todo!("Unknown code")
|
||||
}
|
||||
},
|
||||
RISCV_FP_MINMAX => {
|
||||
if ins.funct3 == 0 {
|
||||
format!("{}\t{}{}{}", "fmin.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
} else {
|
||||
format!("{}\t{}{}{}", "fmax.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
}
|
||||
},
|
||||
RISCV_FP_FCVTW => {
|
||||
if rs2 == 0 {
|
||||
format!("{}\t{}{}", "fcvt.w.s", REG_F[rd], REG_F[rs1])
|
||||
} else {
|
||||
format!("{}\t{}{}", "fcvt.wu.s", REG_F[rd], REG_F[rs1])
|
||||
}
|
||||
},
|
||||
RISCV_FP_FMVXFCLASS => {
|
||||
if ins.funct3 == 0 {
|
||||
format!("{}\t{}{}", "fmv.x.w", REG_F[rd], REG_F[rs1])
|
||||
} else {
|
||||
format!("{}\t{}{}", "fclass.s", REG_F[rd], REG_F[rs1])
|
||||
}
|
||||
},
|
||||
RISCV_FP_FCMP => {
|
||||
if ins.funct3 == 0 {
|
||||
format!("{}\t{}{}{}", "fle.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
} else if ins.funct3 == 1 {
|
||||
format!("{}\t{}{}{}", "flt.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
} else {
|
||||
format!("{}\t{}{}{}", "feq.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||
}
|
||||
},
|
||||
RISCV_FP_FCVTS => {
|
||||
if rs2 == 0 {
|
||||
format!("{}\t{}{}", "fcvt.s.w", REG_F[rd], REG_F[rs1])
|
||||
} else {
|
||||
format!("{}\t{}{}", "fcvt.s.wu", REG_F[rd], REG_F[rs1])
|
||||
}
|
||||
},
|
||||
RISCV_FP_FMVW => {
|
||||
format!("{}\t{}{}", "fmv.w.x", REG_F[rd], REG_F[rs1])
|
||||
},
|
||||
_ => todo!("Unknown code")
|
||||
}
|
||||
},
|
||||
|
||||
RISCV_SYSTEM => {
|
||||
"ecall".to_string()
|
||||
},
|
||||
_ => todo!("{:x} opcode non géré pc : {:x}, value : {:x}", ins.opcode, pc, ins.value) // Change todo! to panic! in the future, I put todo! because there's a lot of opcode currently not implemented
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
#![allow(clippy::unusual_byte_groupings)]
|
||||
|
||||
use crate::simulator::{decode, print};
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_op() {
|
||||
let sub = decode::decode(0b0100000_10000_10001_000_11100_0110011);
|
||||
let add = decode::decode(0b0000000_10000_10001_000_11100_0110011);
|
||||
let xor = decode::decode(0b0000000_10000_10001_100_11100_0110011);
|
||||
let slr = decode::decode(0b0000000_10000_10001_101_11100_0110011);
|
||||
let sra = decode::decode(0b0100000_10000_10001_101_11100_0110011);
|
||||
|
||||
assert_eq!("sub\tt3,a7,a6", print::print(sub, 0));
|
||||
assert_eq!("xor\tt3,a7,a6", print::print(xor, 0));
|
||||
assert_eq!("srl\tt3,a7,a6", print::print(slr, 0));
|
||||
assert_eq!("sra\tt3,a7,a6", print::print(sra, 0));
|
||||
assert_eq!("add\tt3,a7,a6", print::print(add, 0));
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opi() {
|
||||
let addi = decode::decode(0b0000000000_10001_000_11100_0010011);
|
||||
let slli = decode::decode(0b0000000000_10001_001_11100_0010011);
|
||||
let slti = decode::decode(0b0000000000_10001_010_11100_0010011);
|
||||
let sltiu = decode::decode(0b0000000000_10001_011_11100_0010011);
|
||||
let xori = decode::decode(0b_0000000000010001_100_11100_0010011);
|
||||
let ori = decode::decode(0b00000000000_10001_110_11100_0010011);
|
||||
let andi = decode::decode(0b000000000000_10001_111_11100_0010011);
|
||||
assert_eq!("andi\tt3,a7,0", print::print(andi, 0));
|
||||
assert_eq!("addi\tt3,a7,0", print::print(addi, 0));
|
||||
assert_eq!("slli\tt3,a7,0", print::print(slli, 0));
|
||||
assert_eq!("slti\tt3,a7,0", print::print(slti, 0));
|
||||
assert_eq!("sltiu\tt3,a7,0", print::print(sltiu, 0));
|
||||
assert_eq!("xori\tt3,a7,0", print::print(xori, 0));
|
||||
assert_eq!("ori\tt3,a7,0", print::print(ori, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_lui() {
|
||||
let lui = decode::decode(0b01110001000011111000_11100_0110111);
|
||||
let lui_negatif = decode::decode(0b11110001000011111000_11100_0110111);
|
||||
assert_eq!("lui\tt3,710f8000", print::print(lui, 0));
|
||||
assert_eq!("lui\tt3,f10f8000", print::print(lui_negatif, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ld() {
|
||||
// imm rs1 f3 rd opcode
|
||||
let lb = decode::decode(0b010111110000_10001_000_11100_0000011);
|
||||
let lh = decode::decode(0b010111110000_10001_001_11100_0000011);
|
||||
let lw = decode::decode(0b010111110000_10001_010_11100_0000011);
|
||||
let lbu = decode::decode(0b010111110000_10001_100_11100_0000011);
|
||||
let lhu = decode::decode(0b010111110000_10001_101_11100_0000011);
|
||||
let ld = decode::decode(0b010111110000_10001_011_11100_0000011);
|
||||
let lwu = decode::decode(0b010111110000_10001_110_11100_0000011);
|
||||
|
||||
assert_eq!("lb\tt3,1520(a7)", print::print(lb, 0));
|
||||
assert_eq!("lh\tt3,1520(a7)", print::print(lh, 0));
|
||||
assert_eq!("lw\tt3,1520(a7)", print::print(lw, 0));
|
||||
assert_eq!("lbu\tt3,1520(a7)", print::print(lbu, 0));
|
||||
assert_eq!("lhu\tt3,1520(a7)", print::print(lhu, 0));
|
||||
assert_eq!("ld\tt3,1520(a7)", print::print(ld, 0));
|
||||
assert_eq!("lwu\tt3,1520(a7)", print::print(lwu, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opw() {
|
||||
let addw: decode::Instruction = decode::decode(0b0000000_10000_10001_000_11100_0111011);
|
||||
let sllw: decode::Instruction = decode::decode(0b0000000_10000_10001_001_11100_0111011);
|
||||
let srlw: decode::Instruction = decode::decode(0b0000000_10000_10001_101_11100_0111011);
|
||||
let sraw: decode::Instruction = decode::decode(0b0100000_10000_10001_101_11100_0111011);
|
||||
|
||||
assert_eq!("addw\tt3,a7,a6", print::print(addw, 0));
|
||||
assert_eq!("sllw\tt3,a7,a6", print::print(sllw, 0));
|
||||
assert_eq!("srlw\tt3,a7,a6", print::print(srlw, 0));
|
||||
assert_eq!("sraw\tt3,a7,a6", print::print(sraw, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_opwi() {
|
||||
let addiw: decode::Instruction =decode::decode(0b000000000000_10001_000_11100_0011011);
|
||||
let slliw: decode::Instruction = decode::decode(0b0000000_10000_10001_001_11100_0011011);
|
||||
let srai: decode::Instruction = decode::decode(0b010000010001_10001_101_11100_0010011);
|
||||
assert_eq!("addiw\tt3,a7,0x0", print::print(addiw, 0));
|
||||
assert_eq!("slliw\tt3,a7,0x10", print::print(slliw, 0));
|
||||
assert_eq!("srai\tt3,a7,17", print::print(srai, 0));
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_br() {
|
||||
let beq: decode::Instruction = decode::decode(0b0000000_10000_10001_000_00000_1100011);
|
||||
let bne: decode::Instruction = decode::decode(0b0000000_10000_10001_001_00000_1100011);
|
||||
let blt: decode::Instruction = decode::decode(0b0000000_10000_10001_100_00000_1100011);
|
||||
let bge: decode::Instruction = decode::decode(0b0000000_10000_10001_101_00000_1100011);
|
||||
let bge2: decode::Instruction = decode::decode(0x00f75863);
|
||||
let bltu: decode::Instruction = decode::decode(0b0000000_10000_10001_110_00000_1100011);
|
||||
let bgeu: decode::Instruction = decode::decode(0b0000000_10000_10001_111_00000_1100011);
|
||||
assert_eq!("blt\ta7,a6,0", print::print(blt, 0));
|
||||
assert_eq!("bge\ta7,a6,0", print::print(bge, 0));
|
||||
assert_eq!("bge\ta4,a5,104d4", print::print(bge2, 0x104c4));
|
||||
assert_eq!("bltu\ta7,a6,0", print::print(bltu, 0));
|
||||
assert_eq!("bgeu\ta7,a6,0", print::print(bgeu, 0));
|
||||
assert_eq!("bne\ta7,a6,0", print::print(bne, 0));
|
||||
assert_eq!("beq\ta7,a6,0", print::print(beq, 0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_small_program() {
|
||||
/* Code for :
|
||||
int a = 0;
|
||||
int b = 5;
|
||||
a = b;
|
||||
a = a * b;
|
||||
a = a + b;
|
||||
b = a - b;
|
||||
*/
|
||||
assert_eq!("addi sp,sp,-32", print::print(decode::decode(0xfe010113), 0));
|
||||
assert_eq!("sd s0,24(sp)", print::print(decode::decode(0x00813c23), 0));
|
||||
assert_eq!("addi s0,sp,32", print::print(decode::decode(0x02010413), 0));
|
||||
assert_eq!("sw zero,-20(s0)", print::print(decode::decode(0xfe042623), 0));
|
||||
assert_eq!("addi a5,zero,5", print::print(decode::decode(0x00500793), 0));
|
||||
assert_eq!("sw a5,-24(s0)", print::print(decode::decode(0xfef42423), 0));
|
||||
assert_eq!("lw a5,-24(s0)", print::print(decode::decode(0xfe842783), 0));
|
||||
assert_eq!("sw a5,-20(s0)", print::print(decode::decode(0xfef42623), 0));
|
||||
assert_eq!("lw a5,-20(s0)", print::print(decode::decode(0xfec42783), 0));
|
||||
assert_eq!("addi a4,a5,0", print::print(decode::decode(0x00078713), 0));
|
||||
assert_eq!("lw a5,-24(s0)", print::print(decode::decode(0xfe842783), 0));
|
||||
assert_eq!("mulw a5,a4,a5", print::print(decode::decode(0x02f707bb), 0));
|
||||
assert_eq!("sw a5,-20(s0)", print::print(decode::decode(0xfef42623), 0));
|
||||
assert_eq!("lw a5,-20(s0)", print::print(decode::decode(0xfec42783), 0));
|
||||
assert_eq!("addi a4,a5,0", print::print(decode::decode(0x00078713), 0));
|
||||
assert_eq!("lw a5,-24(s0)", print::print(decode::decode(0xfe842783), 0));
|
||||
assert_eq!("addw a5,a4,a5", print::print(decode::decode(0x00f707bb), 0));
|
||||
assert_eq!("sw a5,-20(s0)", print::print(decode::decode(0xfef42623), 0));
|
||||
assert_eq!("lw a5,-20(s0)", print::print(decode::decode(0xfec42783), 0));
|
||||
assert_eq!("addi a4,a5,0", print::print(decode::decode(0x00078713), 0));
|
||||
assert_eq!("lw a5,-24(s0)", print::print(decode::decode(0xfe842783), 0));
|
||||
assert_eq!("subw a5,a4,a5", print::print(decode::decode(0x40f707bb), 0));
|
||||
assert_eq!("sw a5,-24(s0)", print::print(decode::decode(0xfef42423), 0));
|
||||
assert_eq!("addi a5,zero,0", print::print(decode::decode(0x00000793), 0));
|
||||
assert_eq!("addi a0,a5,0", print::print(decode::decode(0x00078513), 0));
|
||||
assert_eq!("ld s0,24(sp)", print::print(decode::decode(0x01813403), 0));
|
||||
assert_eq!("addi sp,sp,32", print::print(decode::decode(0x02010113), 0));
|
||||
assert_eq!("jalr zero,0(ra)", print::print(decode::decode(0x00008067), 0));
|
||||
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_fibo() {
|
||||
assert_eq!("jal zero,10504", print::print(decode::decode(0x0500006f), 0x104b4));
|
||||
assert_eq!("blt a4,a5,104b8", print::print(decode::decode(0xfaf740e3), 0x10518));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul_prog() {
|
||||
assert_eq!("addi sp,sp,-32", print::print(decode::decode(0xfe010113), 0));
|
||||
assert_eq!("sd s0,24(sp)", print::print(decode::decode(0x00813c23), 0));
|
||||
assert_eq!("addi s0,sp,32", print::print(decode::decode(0x02010413), 0));
|
||||
assert_eq!("addi a5,zero,5", print::print(decode::decode(0x00500793), 0));
|
||||
assert_eq!("sw a5,-20(s0)", print::print(decode::decode(0xfef42623), 0));
|
||||
assert_eq!("lw a5,-20(s0)", print::print(decode::decode(0xfec42783), 0));
|
||||
assert_eq!("addi a4,a5,0", print::print(decode::decode(0x00078713), 0));
|
||||
assert_eq!("addi a5,a4,0", print::print(decode::decode(0x00070793), 0));
|
||||
assert_eq!("slliw a5,a5,0x2", print::print(decode::decode(0x0027979b), 0));
|
||||
assert_eq!("addw a5,a5,a4", print::print(decode::decode(0x00e787bb), 0));
|
||||
assert_eq!("sw a5,-24(s0)", print::print(decode::decode(0xfef42423), 0));
|
||||
assert_eq!("lw a5,-20(s0)", print::print(decode::decode(0xfec42783), 0));
|
||||
assert_eq!("addi a4,a5,0", print::print(decode::decode(0x00078713), 0));
|
||||
assert_eq!("lw a5,-24(s0)", print::print(decode::decode(0xfe842783), 0));
|
||||
assert_eq!("mulw a5,a4,a5", print::print(decode::decode(0x02f707bb), 0));
|
||||
assert_eq!("sw a5,-28(s0)", print::print(decode::decode(0xfef42223), 0));
|
||||
assert_eq!("lw a5,-28(s0)", print::print(decode::decode(0xfe442783), 0));
|
||||
assert_eq!("addi a4,a5,0", print::print(decode::decode(0x00078713), 0));
|
||||
assert_eq!("lw a5,-24(s0)", print::print(decode::decode(0xfe842783), 0));
|
||||
assert_eq!("divw a5,a4,a5", print::print(decode::decode(0x02f747bb), 0));
|
||||
assert_eq!("sw a5,-20(s0)", print::print(decode::decode(0xfef42623), 0));
|
||||
assert_eq!("addi a5,zero,0", print::print(decode::decode(0x00000793), 0));
|
||||
assert_eq!("addi a0,a5,0", print::print(decode::decode(0x00078513), 0));
|
||||
assert_eq!("ld s0,24(sp)", print::print(decode::decode(0x01813403), 0));
|
||||
assert_eq!("addi sp,sp,32", print::print(decode::decode(0x02010113), 0));
|
||||
assert_eq!("jalr zero,0(ra)", print::print(decode::decode(0x00008067), 0));
|
||||
}
|
||||
|
||||
}
|
@ -1,61 +0,0 @@
|
||||
//! # Register
|
||||
//!
|
||||
//! This mod contains the definition of the Register structs
|
||||
//! for use within the Machine module.
|
||||
|
||||
use crate::simulator::machine::{NUM_FP_REGS, NUM_INT_REGS};
|
||||
use std::ops::{Add, Sub};
|
||||
|
||||
/// Forcing the Register struct's generic type into having the following Traits
|
||||
///
|
||||
/// - Add
|
||||
/// - Sub
|
||||
/// - PartialEq
|
||||
/// - Copy
|
||||
///
|
||||
/// Generally speaking, only numbers have the combinaison of these traits.
|
||||
pub trait RegisterNum: Add<Output=Self> + Sub<Output=Self> + PartialEq + Copy {}
|
||||
|
||||
impl RegisterNum for i64 {}
|
||||
|
||||
impl RegisterNum for f32 {}
|
||||
|
||||
/// Machine register array
|
||||
#[derive(PartialEq)]
|
||||
pub struct Register<U: RegisterNum> {
|
||||
/// 32 available registers of type U
|
||||
register: [U; 32]
|
||||
}
|
||||
|
||||
impl<U: RegisterNum> Register<U> {
|
||||
/// Returns the current value held in register *position*
|
||||
pub fn get_reg(&self, position: u8) -> U {
|
||||
self.register[position as usize]
|
||||
}
|
||||
|
||||
/// Set value of register *position* to *value*
|
||||
///
|
||||
/// Checking against trying to set a new value to the 0th register
|
||||
/// as its value is NOT supposed to change
|
||||
pub fn set_reg(&mut self, position: u8, value: U) {
|
||||
if position != 0 { self.register[position as usize] = value; }
|
||||
}
|
||||
}
|
||||
|
||||
impl Register<i64> {
|
||||
/// i64 register constructor
|
||||
pub fn init() -> Register<i64> {
|
||||
Register {
|
||||
register: [0i64; NUM_INT_REGS]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Register<f32> {
|
||||
/// f32 register constructor
|
||||
pub fn init() -> Register<f32> {
|
||||
Register {
|
||||
register: [0f32; NUM_FP_REGS]
|
||||
}
|
||||
}
|
||||
}
|
@ -1,35 +1,23 @@
|
||||
//! # Translation Table
|
||||
//!
|
||||
//! This module implement a trnslation table used for fot the MMU Emulator
|
||||
//!
|
||||
//! This part isn't tested nor integrated to BurritOS,
|
||||
//! but will be useful in the futur when the pagination will be implemented.
|
||||
//!
|
||||
//! It contains:
|
||||
//! - all the setters and getters for translation table
|
||||
//! - modificaters of table values
|
||||
|
||||
|
||||
/// Maximum number in a Page Table
|
||||
/// For a futur evolution of program, this value should be load from a configuration file
|
||||
//Nombre maximum de correspondances dans une table des pages
|
||||
//Cette donnée devra a terme etre recupérée depuis un fichier de configuration
|
||||
const MaxVirtPages : u64 = 200000;
|
||||
|
||||
/// Translation Table corresponding to a process
|
||||
/// An iteration of type TranslationTable should be possesses by an oject of type Process
|
||||
|
||||
/* Une table de correspondance propre à un processus
|
||||
* Une variable de type TranslationTable devra etre possédée par un objet de type Process
|
||||
*/
|
||||
pub struct TranslationTable{
|
||||
/// Table size <=> nb of possible translation
|
||||
//capacité de cette table <=> nombre de correspondances possibles
|
||||
//A voir si cette donnée doit etre immuable
|
||||
pub maxNumPages : u64,
|
||||
|
||||
///The table *Vec impemente Index Trait*
|
||||
//la table en question
|
||||
//Vec implemente le trait Index, donc un bon choix
|
||||
pub pageTable : Vec<PageTableEntry>
|
||||
}
|
||||
|
||||
impl TranslationTable {
|
||||
|
||||
/// TranslationTable constructor
|
||||
///
|
||||
/// ### Return
|
||||
/// TranslationTable with an empty Vector
|
||||
pub fn create() -> TranslationTable {
|
||||
|
||||
let mut tmp_vec : Vec<PageTableEntry> = Vec::new();
|
||||
|
465
src/utility/bitmap.rs
Normal file
465
src/utility/bitmap.rs
Normal file
@ -0,0 +1,465 @@
|
||||
pub const BITS_IN_BYTE: usize = 8;
|
||||
pub const BITS_IN_WORD: usize = 32;
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{Cursor, Write, Read};
|
||||
|
||||
use crate::simulator::disk::SECTOR_SIZE;
|
||||
|
||||
pub struct BitMap {
|
||||
pub num_bits: usize,
|
||||
pub num_words: usize,
|
||||
pub map: Vec<u32>,
|
||||
}
|
||||
|
||||
impl BitMap {
|
||||
|
||||
pub fn compute_map_size(n_item : usize, dst_n_item : &mut usize, dst_num_word : &mut usize){
|
||||
//on effectue un calcul sur n_item pour avoir un nombre de bits multiple de 32
|
||||
//On fait le choix d'arrondir vers le haut pour que la map puisse acceuillir la taille souhaitée
|
||||
*dst_n_item = n_item - (n_item%32);
|
||||
|
||||
if n_item%32 > 0 {
|
||||
*dst_n_item += 32;
|
||||
}
|
||||
|
||||
*dst_num_word = *dst_n_item/32;
|
||||
}
|
||||
|
||||
/// Initialize a bitmap with "nitems" bits, so that every bit is clear.
|
||||
/// it can be added somewhere on a list.
|
||||
///
|
||||
/// ### Parameters
|
||||
/// - **nitems** is the number of bits in the bitmap.
|
||||
///----------------------------------------------------------------------
|
||||
pub fn init_bitmap(n_items: usize) -> BitMap {
|
||||
|
||||
let mut rounded_num_bits = 0;
|
||||
let mut rounded_num_words = 0;
|
||||
|
||||
BitMap::compute_map_size(n_items, &mut rounded_num_bits, &mut rounded_num_words);
|
||||
|
||||
BitMap{
|
||||
num_bits: rounded_num_bits,
|
||||
num_words: rounded_num_words,//(n_items + SECTOR_SIZE as usize -1) / SECTOR_SIZE as usize,
|
||||
map: vec![0u32 ; rounded_num_words],
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//values is not borrowed
|
||||
pub fn init_bitmap_from(values: &mut Vec<u32>)-> BitMap {
|
||||
let mut tmp: Vec<u32> = Vec::new();
|
||||
|
||||
for value in values.iter(){
|
||||
tmp.push(*value)
|
||||
}
|
||||
|
||||
BitMap{
|
||||
num_bits: values.len()*std::mem::size_of::<u32>()*8,
|
||||
num_words: values.len(),
|
||||
map: tmp
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the "nth" bit in a bitmap.
|
||||
/// ### Parameters
|
||||
/// - **which** is the number of the bit to be set.
|
||||
pub fn mark(&mut self, which: usize) {
|
||||
assert!(which >= 0 && which < self.num_bits);
|
||||
//self.map[which / BITS_IN_WORD] |= 1 << (which % BITS_IN_WORD);
|
||||
let position_modulo = which%BITS_IN_WORD;
|
||||
let left_shift = BITS_IN_WORD-position_modulo-1;
|
||||
|
||||
self.map[which / BITS_IN_WORD] |= 1u32 << left_shift;
|
||||
}
|
||||
|
||||
/// return true if the "nth" bit is set.
|
||||
///
|
||||
/// ### Paramenters
|
||||
/// - **which** is the number of the bit to be tested.
|
||||
pub fn test(&self, which: usize) -> bool {
|
||||
assert!(which < self.num_bits);
|
||||
//(self.map[which / BITS_IN_WORD] & (1 << (which % BITS_IN_WORD))) != 0
|
||||
let position_modulo = which%BITS_IN_WORD;
|
||||
let left_shift = BITS_IN_WORD-position_modulo-1;
|
||||
|
||||
(self.map[which / BITS_IN_WORD] & (1u32 << left_shift)) != 0
|
||||
}
|
||||
|
||||
/// Return the number of the first bit which is clear.
|
||||
/// As a side effect, set the bit (mark it as in use).
|
||||
/// (In other words, find and allocate a bit.)
|
||||
/// If no bits are clear, return ERROR
|
||||
pub fn find(&mut self) -> i32 {
|
||||
for i in 0..self.num_bits {
|
||||
if !self.test(i) {
|
||||
self.mark(i);
|
||||
return i as i32;
|
||||
}
|
||||
}
|
||||
-1
|
||||
}
|
||||
|
||||
/// Clear the "nth" bit in a bitmap.
|
||||
/// ### Parameters
|
||||
/// - **which** is the number of the bit to be cleared.
|
||||
pub fn clear(&mut self, which: usize) {
|
||||
assert!(which < self.num_bits, "index out of range");
|
||||
|
||||
let position_modulo = which%BITS_IN_WORD;
|
||||
let left_shift = BITS_IN_WORD-position_modulo-1;
|
||||
let mask : u32 = 0xFFFFFFFF^(1u32 << left_shift);
|
||||
|
||||
self.map[which / BITS_IN_WORD] &= mask;
|
||||
}
|
||||
|
||||
/// Return the number of clear bits in the bitmap.
|
||||
/// (In other words, how many bits are unallocated?)
|
||||
pub fn num_clear(&self) -> i32 {
|
||||
let mut count = 0;
|
||||
for i in 0..self.num_bits {
|
||||
if !self.test(i) {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
count
|
||||
}
|
||||
|
||||
/// Print the contents of the bitmap, for debugging.
|
||||
/// Could be done in a number of ways, but we just print the #'s of
|
||||
/// all the bits that are set in the bitmap.
|
||||
pub fn print(&self) {
|
||||
println!("Bitmap set:");
|
||||
for i in 0..self.num_bits {
|
||||
if self.test(i) {
|
||||
print!("{}, ", i);
|
||||
}
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
|
||||
///Initialize the contents of a bitmap from a Nachos file.
|
||||
///
|
||||
/// ### Parameters
|
||||
/// - **file** is the place to read the bitmap from
|
||||
pub fn fetch_from(&mut self, file_path: &str){
|
||||
// Ouvre le fichier en mode lecture seule
|
||||
let mut file = File::open(file_path).expect("BitMap::fetch_from , unable to open file in read only");
|
||||
|
||||
let mut buf_to_convert : Vec<u8> = Vec::new();
|
||||
|
||||
file.read_to_end(&mut buf_to_convert).expect("BitMap::fetch_from , unable to read from file");
|
||||
|
||||
let borne_max_exclue;
|
||||
|
||||
//si le fichier contient plus de mots que la map ne peut en acceuillir, on lit de quoi remplir la map
|
||||
if buf_to_convert.len() > self.num_bits/8 {
|
||||
borne_max_exclue = self.num_words*4;
|
||||
}
|
||||
//sinon on lit ce que le fichier contient, en respectant l'alignement sur 4 octects !
|
||||
else {
|
||||
borne_max_exclue = buf_to_convert.len() - (buf_to_convert.len()%4);
|
||||
}
|
||||
|
||||
let mut i = 0;
|
||||
|
||||
// !! borne_max_exclue doit etre multiple de 4 !!
|
||||
while i < borne_max_exclue {
|
||||
|
||||
let a : u32 = buf_to_convert[i] as u32;
|
||||
let b : u32 = buf_to_convert[i+1] as u32;
|
||||
let c : u32 = buf_to_convert[i+2] as u32;
|
||||
let d : u32 = buf_to_convert[i+3] as u32;
|
||||
|
||||
self.map[i/4] = (a<<24) | (b<<16) | (c<<8) | (d);
|
||||
|
||||
i += 4;//on passe aux 4 octets suivants
|
||||
}
|
||||
}
|
||||
|
||||
/// Store the contents of a bitmap to a Nachos file.
|
||||
///
|
||||
/// ### Paramenters
|
||||
/// - **file** is the place to write the bitmap to
|
||||
pub fn write_back(&mut self, file_path: &str) -> std::io::Result<()> {
|
||||
|
||||
let mut buf : Vec<u8> = Vec::new();
|
||||
|
||||
for val in self.map.iter(){
|
||||
let a : u32 = (*val>>24)&0x000000ff;
|
||||
let b : u32 = (*val>>16)&0x000000ff;
|
||||
let c : u32 = (*val>>8)&0x000000ff;
|
||||
let d : u32 = *val&0x000000ff;
|
||||
|
||||
buf.push(a as u8);
|
||||
buf.push(b as u8);
|
||||
buf.push(c as u8);
|
||||
buf.push(d as u8);
|
||||
}
|
||||
|
||||
// Ouvre le fichier en mode écriture, si absent on le creer
|
||||
let mut file = File::options()
|
||||
.read(false)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open(file_path)
|
||||
.expect("bitmap::write_back, Unable to create or open File");
|
||||
|
||||
// Écrit les données dans le fichier
|
||||
file.write_all(&buf);
|
||||
file.flush();//histoire d'etre sur
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
pub fn add_word(&mut self, word : u32){
|
||||
self.map.push(word);
|
||||
self.num_words += 1;
|
||||
self.num_bits += std::mem::size_of::<u32>()*8;
|
||||
}
|
||||
|
||||
pub fn add_words(&mut self, words : Vec<u32>){
|
||||
for word in words.iter(){
|
||||
self.add_word(*word);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::io::{Seek, SeekFrom};
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_num_clear() {
|
||||
|
||||
let mut bit_map : BitMap = BitMap::init_bitmap(128);
|
||||
let index_to_set : [usize ; 16] = [0,5,10,15,18,25,28,31,45,70,74,88,99,101,102,127];
|
||||
|
||||
for val in index_to_set.iter(){
|
||||
bit_map.mark(*val);
|
||||
}
|
||||
|
||||
assert!(bit_map.num_bits == 128);
|
||||
assert!(bit_map.num_words == 4);
|
||||
assert!((bit_map.num_clear() as usize) == bit_map.num_bits - index_to_set.len());
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_fetch_from_file_smaller_than_map() {
|
||||
//On ecrit la séquence 0x AB CD EF 10 20 2E 3E 4F
|
||||
let values : [u8 ; 8] = [0xAB, 0xCD, 0xEF, 0x10, 0x20, 0x2E, 0x3E, 0x4F];
|
||||
let values_for_test : [u32 ; 2] = [0xABCDEF10, 0x202E3E4F];
|
||||
|
||||
let mut f = File::options()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open("test_fetch_bitmap")
|
||||
.expect("Unable to create File");
|
||||
|
||||
f.write_all(&values[..]).expect("Unable to write data");
|
||||
|
||||
//creating and loading the bitmap
|
||||
|
||||
let mut bit_map : BitMap = BitMap::init_bitmap(100);
|
||||
|
||||
bit_map.fetch_from("test_fetch_bitmap");
|
||||
|
||||
assert!(bit_map.num_bits == 128);
|
||||
assert!(bit_map.num_words == 4);
|
||||
|
||||
for j in 0..bit_map.map.len(){
|
||||
if j<2 {
|
||||
assert!(values_for_test[j] == bit_map.map[j]);
|
||||
}
|
||||
else {
|
||||
assert!(bit_map.map[j] == 0u32);
|
||||
}
|
||||
}
|
||||
//print for debug
|
||||
/*for val in bit_map.map.iter(){
|
||||
println!("{:08X?}", *val)
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_fetch_from_map_smaller_than_file() {
|
||||
//println!("\n\n TEST FETCH FROM\n");
|
||||
//On ecrit la séquence 0x AB CD EF 10 20 2E 3E 4F
|
||||
let mut values : [u8 ; 8] = [0xFE, 0x59, 0xEF, 0x10, 0x20, 0x2E, 0x3E, 0x4F];
|
||||
|
||||
let mut f = File::options()
|
||||
.read(true)
|
||||
.write(true)
|
||||
.create(true)
|
||||
.open("test_fetch_bitmap_2")
|
||||
.expect("Unable to create File");
|
||||
|
||||
f.write_all(&values[..]).expect("Unable to write data");
|
||||
|
||||
//creating and loading the bitmap
|
||||
//1 bit donnera lieu via init_bit_map a une map de 32 bit soit 1 mot
|
||||
let mut bit_map : BitMap = BitMap::init_bitmap(1);
|
||||
|
||||
bit_map.fetch_from("test_fetch_bitmap_2");
|
||||
|
||||
assert!(bit_map.num_bits == 32);
|
||||
assert!(bit_map.num_words == 1);
|
||||
assert!(bit_map.map[0] == 0xFE59EF10);
|
||||
/*println!("\n\n data loaded into map \n");
|
||||
|
||||
//print for debug
|
||||
for val in bit_map.map.iter(){
|
||||
println!("{:08X?}", *val)
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_mark() {
|
||||
|
||||
let mut bit_map : BitMap = BitMap::init_bitmap(128);
|
||||
let index_to_set : [usize ; 16] = [0,5,10,15,18,25,28,31,45,70,74,88,99,101,102,127];
|
||||
|
||||
for val in index_to_set.iter(){
|
||||
bit_map.mark(*val);
|
||||
}
|
||||
|
||||
assert!(bit_map.num_bits == 128);
|
||||
assert!(bit_map.num_words == 4);
|
||||
|
||||
/*for i in bit_map.map.iter(){
|
||||
println!("\n{:#032b}", *i);
|
||||
}*/
|
||||
|
||||
for i in 0..bit_map.num_bits {
|
||||
|
||||
let word_index = i/32;
|
||||
let mask_to_shift : u32 = 0x80000000;//un 1 au debut puis full 0 <=> 0x100000000000000000000000000000000
|
||||
let shift_value = i%32;
|
||||
|
||||
if index_to_set.contains(&i) {
|
||||
assert!( (bit_map.map[word_index] & (mask_to_shift>>shift_value)) != 0);
|
||||
}
|
||||
else{
|
||||
assert!( (bit_map.map[word_index] & (mask_to_shift>>shift_value)) == 0) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_test() {
|
||||
|
||||
let mut bit_map : BitMap = BitMap::init_bitmap(128);
|
||||
let index_to_set : [usize ; 16] = [0,5,10,15,18,25,28,31,45,70,74,88,99,101,102,127];
|
||||
|
||||
for val in index_to_set.iter(){
|
||||
bit_map.mark(*val);
|
||||
}
|
||||
|
||||
assert!(bit_map.num_bits == 128);
|
||||
assert!(bit_map.num_words == 4);
|
||||
|
||||
for i in 0..bit_map.num_bits {
|
||||
|
||||
if index_to_set.contains(&i) {
|
||||
assert!( bit_map.test(i) == true);
|
||||
}
|
||||
else{
|
||||
assert!( bit_map.test(i) == false) ;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_clear() {
|
||||
let mut bit_map : BitMap = BitMap::init_bitmap(128);
|
||||
let index_to_set : [usize ; 16] = [0,5,10,15,18,25,28,31,45,70,74,88,99,101,102,127];
|
||||
|
||||
//mise a 1
|
||||
for val in index_to_set.iter(){
|
||||
bit_map.mark(*val);
|
||||
}
|
||||
|
||||
assert!(bit_map.num_bits == 128);
|
||||
assert!(bit_map.num_words == 4);
|
||||
|
||||
for i in 0..bit_map.num_bits {
|
||||
|
||||
if index_to_set.contains(&i) {
|
||||
assert!( bit_map.test(i) == true);
|
||||
}
|
||||
else{
|
||||
assert!( bit_map.test(i) == false) ;
|
||||
}
|
||||
}
|
||||
|
||||
//on met a 0 partout dans bit_map pour tester que le clear ne transforme pas 0 en 1
|
||||
for i in 0..bit_map.num_bits {
|
||||
bit_map.clear(i);
|
||||
assert!( bit_map.test(i) == false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_find() {
|
||||
let mut bit_map : BitMap = BitMap::init_bitmap(128);
|
||||
|
||||
assert!(bit_map.num_bits == 128);
|
||||
assert!(bit_map.num_words == 4);
|
||||
|
||||
for i in 0..bit_map.num_bits {
|
||||
let j = bit_map.find();
|
||||
assert!(i == (j as usize));
|
||||
}
|
||||
|
||||
for j in 0..bit_map.num_bits{
|
||||
assert!(bit_map.test(j) == true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_write_back() {
|
||||
|
||||
let mut bit_map : BitMap = BitMap::init_bitmap(128);
|
||||
let mut bit_map_from_file : BitMap = BitMap::init_bitmap(128);
|
||||
let index_to_set : [usize ; 16] = [1,5,10,15,18,25,28,31,45,70,74,88,99,101,102,127];
|
||||
|
||||
for val in index_to_set.iter(){
|
||||
bit_map.mark(*val);
|
||||
}
|
||||
|
||||
assert!(bit_map.num_bits == 128);
|
||||
assert!(bit_map.num_words == 4);
|
||||
assert!(bit_map_from_file.num_bits == 128);
|
||||
assert!(bit_map_from_file.num_words == 4);
|
||||
|
||||
bit_map.write_back("test_bit_map_write_back");
|
||||
|
||||
bit_map_from_file.fetch_from("test_bit_map_write_back");
|
||||
|
||||
for i in 0..(bit_map.num_words){
|
||||
assert!(bit_map_from_file.map[i] == bit_map.map[i]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
//! Functions for burritos.cfg configuration file parsing.
|
||||
//! Needed to set-up machine and system constants without
|
||||
//! recompiling.
|
||||
|
||||
use std::{
|
||||
fs::File,
|
||||
path::Path,
|
||||
collections::HashMap,
|
||||
io::{
|
||||
BufReader,
|
||||
BufRead,
|
||||
Error
|
||||
}
|
||||
};
|
||||
|
||||
/// Aliases the rather long HashMap<MachineSettingKey, i32> type
|
||||
/// to a rather simpler to understand Settings.
|
||||
pub type Settings = HashMap<MachineSettingKey, u64>;
|
||||
|
||||
/// Keys for the Settings HashMap, represented as enums for
|
||||
/// maintainability.
|
||||
#[derive(Eq, Hash, PartialEq, Debug)]
|
||||
pub enum MachineSettingKey {
|
||||
/// Number of physical pages.
|
||||
NumPhysPages,
|
||||
/// Stack size.
|
||||
UserStackSize,
|
||||
/// Maximum size of a file name
|
||||
MaxFileNameSize,
|
||||
/// Number of directory entries
|
||||
NumDirEntries,
|
||||
/// Processor Frequency
|
||||
ProcessorFrequency,
|
||||
/// Disk sector size
|
||||
SectorSize,
|
||||
/// Memory page size
|
||||
PageSize,
|
||||
/// Maximum number of Virtual Pages
|
||||
MaxVirtPages,
|
||||
/// In case of unknown key in configuration file.
|
||||
Unknown
|
||||
}
|
||||
|
||||
/// Allows for converting string slices to correspoding MachineSettingKey
|
||||
/// enum value.
|
||||
impl From<&str> for MachineSettingKey {
|
||||
fn from(s: &str) -> Self {
|
||||
match s {
|
||||
"NumPhysPages" => MachineSettingKey::NumPhysPages,
|
||||
"UserStackSize" => MachineSettingKey::UserStackSize,
|
||||
"MaxFileNameSize" => MachineSettingKey::MaxFileNameSize,
|
||||
"NumDirEntries" => MachineSettingKey::NumDirEntries,
|
||||
"ProcessorFrequency" => MachineSettingKey::ProcessorFrequency,
|
||||
"SectorSize" => MachineSettingKey::SectorSize,
|
||||
"PageSize" => MachineSettingKey::PageSize,
|
||||
"MaxVirtPages" => MachineSettingKey::MaxVirtPages,
|
||||
_ => MachineSettingKey::Unknown
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Tries to return a HashMap containing the user defined burritos configuration
|
||||
/// in the burritos.cfg file.
|
||||
///
|
||||
/// If the file is not found, the function will return an io error.
|
||||
///
|
||||
/// If the configuration is invalid, the function may return a HashMap with missing or
|
||||
/// non-sensical settings.
|
||||
/// It is up to the caller to determine whether or not default values should be placed
|
||||
/// instead of halting the program.
|
||||
pub fn read_settings() -> Result<Settings, Error> {
|
||||
// Opening file
|
||||
let file = {
|
||||
let file_path = "./burritos.cfg";
|
||||
let file_path = Path::new(file_path);
|
||||
match File::open(file_path) {
|
||||
Ok(opened_file) => opened_file,
|
||||
Err(error_message) => Err(error_message)?
|
||||
}
|
||||
};
|
||||
let file_reader = BufReader::new(file);
|
||||
let filtered_setting_strings = filter_garbage(file_reader);
|
||||
let mut settings_map = Settings::new();
|
||||
// Reading settings
|
||||
for line in filtered_setting_strings {
|
||||
let mut split_line = line.split_whitespace();
|
||||
let key = split_line.next().unwrap_or("_");
|
||||
split_line.next(); // Skipping '=' character
|
||||
let setting = split_line.next().unwrap_or("_");
|
||||
settings_map = update_settings_map(settings_map, key, setting);
|
||||
}
|
||||
Ok(settings_map)
|
||||
}
|
||||
|
||||
/// Returns a mock configuration for Machine unit testing
|
||||
///
|
||||
/// FIXME: Does not cover the whole configuration yet
|
||||
pub fn get_debug_configuration() -> Settings {
|
||||
let mut settings_map = Settings::new();
|
||||
settings_map.insert(MachineSettingKey::PageSize, 2048);
|
||||
settings_map.insert(MachineSettingKey::NumPhysPages, 8192);
|
||||
settings_map.insert(MachineSettingKey::UserStackSize, 4096);
|
||||
settings_map
|
||||
}
|
||||
|
||||
/// Removes comments and empty lines
|
||||
/// Filters out empty lines and comments from the reader `BufReader`.
|
||||
///
|
||||
/// Returns a [`Vec<String>`], each entry containing a valid
|
||||
/// line from the input file.
|
||||
fn filter_garbage<R: std::io::Read>(reader: BufReader<R>) -> Vec<String> {
|
||||
reader.lines()
|
||||
.map(|l| l.unwrap())
|
||||
.filter(|l| !l.is_empty() && !l.starts_with('#'))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Inserts user settings into setting map
|
||||
/// Adds a <K, V> pair to a [`Settings`] map.
|
||||
///
|
||||
/// Returns the updated [`Settings`].
|
||||
fn update_settings_map(mut settings_map: Settings, key: &str, setting: &str) -> Settings {
|
||||
let key = MachineSettingKey::from(key);
|
||||
let setting = str::parse::<u64>(setting).unwrap_or(0);
|
||||
settings_map.insert(key, setting);
|
||||
settings_map
|
||||
}
|
@ -1,95 +1,62 @@
|
||||
//! Data structure and definition of a generic single-linked LIFO list.
|
||||
//! Data structure and definition of a genericsingle-linked LIFO list.
|
||||
|
||||
use std::ptr;
|
||||
|
||||
/// Definition of the generic single-linked FIFO list
|
||||
///
|
||||
/// Each elements points to a single item of the list and the following one
|
||||
///
|
||||
/// These methods wrap unsafe instructions because it doesn't respect borrow rules per example
|
||||
/// but everything has been tested with miri to assure there's no Undefined Behaviour (use-after-free, double free, etc.)
|
||||
/// or memory leak
|
||||
#[derive(PartialEq, Clone, Debug)]
|
||||
#[derive(PartialEq)]
|
||||
pub struct List<T: PartialEq> {
|
||||
head: Link<T>,
|
||||
tail: Link<T>,
|
||||
}
|
||||
|
||||
type Link<T> = *mut Node<T>;
|
||||
|
||||
type Link<T> = Option<Box<Node<T>>>;
|
||||
|
||||
#[derive(PartialEq)]
|
||||
struct Node<T> {
|
||||
elem: T,
|
||||
next: Link<T>,
|
||||
}
|
||||
/// Iterator structure for use in a for loop, pop elements before returning it
|
||||
pub struct IntoIter<T: PartialEq>(List<T>);
|
||||
|
||||
/// Iterator structure for use in a for loop, dereference before returning it
|
||||
pub struct Iter<'a, T> {
|
||||
next: Option<&'a Node<T>>,
|
||||
}
|
||||
|
||||
/// Same as Iter structure, returned item are mutable
|
||||
pub struct IterMut<'a, T> {
|
||||
next: Option<&'a mut Node<T>>,
|
||||
}
|
||||
|
||||
impl<T: PartialEq> List<T> {
|
||||
|
||||
/// Create an empty list
|
||||
pub fn new() -> Self {
|
||||
List { head: None }
|
||||
}
|
||||
|
||||
/// Push an item at the end of the list
|
||||
pub fn push(&mut self, elem: T) {
|
||||
unsafe {
|
||||
let new_tail = Box::into_raw(Box::new(Node {
|
||||
elem,
|
||||
next: ptr::null_mut(),
|
||||
}));
|
||||
let new_node = Box::new(Node {
|
||||
elem: elem,
|
||||
next: self.head.take(),
|
||||
});
|
||||
|
||||
if !self.tail.is_null() {
|
||||
(*self.tail).next = new_tail;
|
||||
} else {
|
||||
self.head = new_tail;
|
||||
self.head = Some(new_node);
|
||||
}
|
||||
|
||||
self.tail = new_tail;
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve and remove the item at the head of the list.
|
||||
/// Retrieve and remove the item at the end of the list.
|
||||
///
|
||||
/// Return None if list is empty
|
||||
pub fn pop(&mut self) -> Option<T> {
|
||||
unsafe {
|
||||
if self.head.is_null() {
|
||||
None
|
||||
} else {
|
||||
let head = Box::from_raw(self.head);
|
||||
self.head = head.next;
|
||||
if self.head.is_null() {
|
||||
self.tail = ptr::null_mut();
|
||||
}
|
||||
Some(head.elem)
|
||||
}
|
||||
}
|
||||
self.head.take().map(|node| {
|
||||
self.head = node.next;
|
||||
node.elem
|
||||
})
|
||||
}
|
||||
|
||||
/// Retrieve without removing the item at the head of the list
|
||||
/// Retrieve without removing the item at the end of the list
|
||||
///
|
||||
/// Return None if list is empty
|
||||
pub fn peek(&self) -> Option<&T> {
|
||||
unsafe {
|
||||
self.head.as_ref().map(|node| &node.elem)
|
||||
}
|
||||
self.head.as_ref().map(|node| {
|
||||
&node.elem
|
||||
})
|
||||
}
|
||||
|
||||
/// Retrieve without removing the item at the head of the list as mutable
|
||||
/// Retrieve without removing the item at the end of the list as mutable
|
||||
///
|
||||
/// Return None if lsit is empty
|
||||
pub fn peek_mut(&mut self) -> Option<&mut T> {
|
||||
unsafe {
|
||||
self.head.as_mut().map(|node| &mut node.elem)
|
||||
}
|
||||
self.head.as_mut().map(|node| {
|
||||
&mut node.elem
|
||||
})
|
||||
}
|
||||
|
||||
/// Search for an element in the list
|
||||
@ -99,12 +66,10 @@ impl<T: PartialEq> List<T> {
|
||||
/// Worst case complexity of this function is O(n)
|
||||
pub fn contains(&self, elem: &T) -> bool {
|
||||
let mut iter = self.iter();
|
||||
let mut element = iter.next();
|
||||
let element = iter.next();
|
||||
while element.is_some() {
|
||||
if element.unwrap() == elem {
|
||||
return true;
|
||||
} else {
|
||||
element = iter.next();
|
||||
}
|
||||
}
|
||||
false
|
||||
@ -116,30 +81,26 @@ impl<T: PartialEq> List<T> {
|
||||
///
|
||||
/// Worst-case complexity is O(n)
|
||||
pub fn remove(&mut self, item: T)-> bool {
|
||||
unsafe {
|
||||
let mut current: *mut Node<T> = self.head;
|
||||
let mut previous: *mut Node<T> = ptr::null_mut();
|
||||
while !current.is_null() {
|
||||
if (*current).elem == item {
|
||||
if !previous.is_null() {
|
||||
(*previous).next = (*current).next;
|
||||
let mut found = false;
|
||||
let mut tmp_list: List<T> = List::new();
|
||||
while !self.is_empty() {
|
||||
let current = self.pop().unwrap();
|
||||
if current != item {
|
||||
tmp_list.push(current);
|
||||
} else {
|
||||
self.head = (*current).next;
|
||||
}
|
||||
drop(Box::from_raw(current).elem);
|
||||
return true;
|
||||
} else {
|
||||
previous = current;
|
||||
current = (*current).next;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while !tmp_list.is_empty() {
|
||||
self.push(tmp_list.pop().unwrap());
|
||||
}
|
||||
false
|
||||
found
|
||||
}
|
||||
|
||||
/// Return true if the list is empty, false otherwise
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.head.is_null()
|
||||
self.head.is_none()
|
||||
}
|
||||
|
||||
/// Turn the list into an iterator for use in a for loop per example.
|
||||
@ -153,34 +114,27 @@ impl<T: PartialEq> List<T> {
|
||||
///
|
||||
/// When you iter using this method, elements are dereferenced
|
||||
pub fn iter(&self) -> Iter<'_, T> {
|
||||
unsafe {
|
||||
Iter { next: self.head.as_ref() }
|
||||
}
|
||||
|
||||
Iter { next: self.head.as_deref() }
|
||||
}
|
||||
|
||||
/// Same as iter but make the iterator mutable
|
||||
pub fn iter_mut(&mut self) -> IterMut<'_, T> {
|
||||
unsafe {
|
||||
IterMut { next: self.head.as_mut() }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq> Default for List<T> {
|
||||
/// Create an empty list
|
||||
fn default() -> Self {
|
||||
Self { head: ptr::null_mut(), tail: ptr::null_mut() }
|
||||
IterMut { next: self.head.as_deref_mut() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialEq> Drop for List<T> {
|
||||
fn drop(&mut self) {
|
||||
while self.pop().is_some() {} // removing every item from list (necessary as we using unsafe function)
|
||||
let mut cur_link = self.head.take();
|
||||
while let Some(mut boxed_node) = cur_link {
|
||||
cur_link = boxed_node.next.take();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator structure for use in a for loop, pop elements before returning it
|
||||
pub struct IntoIter<T: PartialEq>(List<T>);
|
||||
|
||||
impl<T: PartialEq> Iterator for IntoIter<T> {
|
||||
type Item = T;
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
@ -189,32 +143,35 @@ impl<T: PartialEq> Iterator for IntoIter<T> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterator structure for use in a for loop, dereference before returning it
|
||||
pub struct Iter<'a, T> {
|
||||
next: Option<&'a Node<T>>,
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for Iter<'a, T> {
|
||||
type Item = &'a T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
unsafe {
|
||||
self.next.map(|node| {
|
||||
self.next = node.next.as_ref();
|
||||
self.next = node.next.as_deref();
|
||||
&node.elem
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Same as Iter structure, returned item are mutable
|
||||
pub struct IterMut<'a, T> {
|
||||
next: Option<&'a mut Node<T>>,
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for IterMut<'a, T> {
|
||||
type Item = &'a mut T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
unsafe {
|
||||
self.next.take().map(|node| {
|
||||
self.next = node.next.as_mut();
|
||||
self.next = node.next.as_deref_mut();
|
||||
&mut node.elem
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -223,7 +180,7 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn basics() {
|
||||
let mut list = List::default();
|
||||
let mut list = List::new();
|
||||
|
||||
// Check empty list behaves right
|
||||
assert_eq!(list.pop(), None);
|
||||
@ -234,7 +191,7 @@ mod test {
|
||||
list.push(3);
|
||||
|
||||
// Check normal removal
|
||||
assert_eq!(list.pop(), Some(1));
|
||||
assert_eq!(list.pop(), Some(3));
|
||||
assert_eq!(list.pop(), Some(2));
|
||||
|
||||
// Push some more just to make sure nothing's corrupted
|
||||
@ -242,144 +199,63 @@ mod test {
|
||||
list.push(5);
|
||||
|
||||
// Check normal removal
|
||||
assert_eq!(list.pop(), Some(3));
|
||||
assert_eq!(list.pop(), Some(5));
|
||||
assert_eq!(list.pop(), Some(4));
|
||||
|
||||
// Check exhaustion
|
||||
assert_eq!(list.pop(), Some(5));
|
||||
assert_eq!(list.pop(), Some(1));
|
||||
assert_eq!(list.pop(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn peek() {
|
||||
let mut list = List::default();
|
||||
let mut list = List::new();
|
||||
assert_eq!(list.peek(), None);
|
||||
assert_eq!(list.peek_mut(), None);
|
||||
list.push(1);
|
||||
list.push(2);
|
||||
list.push(3);
|
||||
list.push(1); list.push(2); list.push(3);
|
||||
|
||||
assert_eq!(list.peek(), Some(&1));
|
||||
assert_eq!(list.peek_mut(), Some(&mut 1));
|
||||
assert_eq!(list.peek(), Some(&3));
|
||||
assert_eq!(list.peek_mut(), Some(&mut 3));
|
||||
|
||||
list.peek_mut().map(|value| {
|
||||
*value = 42
|
||||
});
|
||||
|
||||
assert_eq!(list.peek(), Some(&42));
|
||||
assert_eq!(list.pop(), Some(42));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn into_iter() {
|
||||
let mut list = List::default();
|
||||
list.push(1);
|
||||
list.push(2);
|
||||
list.push(3);
|
||||
let mut list = List::new();
|
||||
list.push(1); list.push(2); list.push(3);
|
||||
|
||||
let mut iter = list.into_iter();
|
||||
assert_eq!(iter.next(), Some(1));
|
||||
assert_eq!(iter.next(), Some(2));
|
||||
assert_eq!(iter.next(), Some(3));
|
||||
assert_eq!(iter.next(), Some(2));
|
||||
assert_eq!(iter.next(), Some(1));
|
||||
assert_eq!(iter.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iter() {
|
||||
let mut list = List::default();
|
||||
list.push(1);
|
||||
list.push(2);
|
||||
list.push(3);
|
||||
let mut list = List::new();
|
||||
list.push(1); list.push(2); list.push(3);
|
||||
|
||||
let mut iter = list.iter();
|
||||
assert_eq!(iter.next(), Some(&1));
|
||||
assert_eq!(iter.next(), Some(&2));
|
||||
assert_eq!(iter.next(), Some(&3));
|
||||
assert_eq!(iter.next(), Some(&2));
|
||||
assert_eq!(iter.next(), Some(&1));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iter_mut() {
|
||||
let mut list = List::default();
|
||||
list.push(1);
|
||||
list.push(2);
|
||||
list.push(3);
|
||||
let mut list = List::new();
|
||||
list.push(1); list.push(2); list.push(3);
|
||||
|
||||
let mut iter = list.iter_mut();
|
||||
assert_eq!(iter.next(), Some(&mut 1));
|
||||
assert_eq!(iter.next(), Some(&mut 2));
|
||||
assert_eq!(iter.next(), Some(&mut 3));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn contains_test() {
|
||||
let mut list = List::default();
|
||||
assert_eq!(list.peek(), None);
|
||||
list.push(1);
|
||||
list.push(2);
|
||||
list.push(3);
|
||||
|
||||
assert_eq!(list.contains(&1), true);
|
||||
assert_eq!(list.contains(&4), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_test() {
|
||||
let mut list = List::default();
|
||||
assert_eq!(list.peek(), None);
|
||||
list.push(1);
|
||||
list.push(2);
|
||||
list.push(3);
|
||||
|
||||
assert_eq!(list.contains(&2), true);
|
||||
list.remove(2);
|
||||
assert_eq!(list.contains(&2), false);
|
||||
assert_eq!(list.pop(), Option::Some(1));
|
||||
assert_eq!(list.pop(), Option::Some(3));
|
||||
assert_eq!(list.peek(), Option::None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn remove_test2() {
|
||||
let mut list = List::default();
|
||||
assert_eq!(list.peek(), None);
|
||||
list.push(1);
|
||||
list.push(2);
|
||||
list.push(3);
|
||||
|
||||
assert_eq!(list.contains(&1), true);
|
||||
list.remove(1);
|
||||
assert_eq!(list.contains(&1), false);
|
||||
assert_eq!(list.pop(), Option::Some(2));
|
||||
assert_eq!(list.pop(), Option::Some(3));
|
||||
assert_eq!(list.peek(), Option::None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn miri_test() {
|
||||
let mut list = List::default();
|
||||
|
||||
list.push(1);
|
||||
list.push(2);
|
||||
list.push(3);
|
||||
|
||||
assert!(list.pop() == Some(1));
|
||||
list.push(4);
|
||||
assert!(list.pop() == Some(2));
|
||||
list.push(5);
|
||||
|
||||
assert!(list.peek() == Some(&3));
|
||||
list.push(6);
|
||||
list.peek_mut().map(|x| *x *= 10);
|
||||
assert!(list.peek() == Some(&30));
|
||||
assert!(list.pop() == Some(30));
|
||||
|
||||
for elem in list.iter_mut() {
|
||||
*elem *= 100;
|
||||
}
|
||||
|
||||
let mut iter = list.iter();
|
||||
assert_eq!(iter.next(), Some(&400));
|
||||
assert_eq!(iter.next(), Some(&500));
|
||||
assert_eq!(iter.next(), Some(&600));
|
||||
assert_eq!(iter.next(), None);
|
||||
assert_eq!(iter.next(), None);
|
||||
|
||||
assert!(list.pop() == Some(400));
|
||||
list.peek_mut().map(|x| *x *= 10);
|
||||
assert!(list.peek() == Some(&5000));
|
||||
list.push(7);
|
||||
assert_eq!(iter.next(), Some(&mut 2));
|
||||
assert_eq!(iter.next(), Some(&mut 1));
|
||||
}
|
||||
}
|
@ -1,6 +1,2 @@
|
||||
//! This module contains data type definitions used in other parts the BurritOS
|
||||
//! They are separated from the rest of the operating system so as to promote
|
||||
//! reusability and to separate data constructs proper from state and actions.
|
||||
pub mod list;
|
||||
pub mod objaddr;
|
||||
pub mod cfg;
|
||||
pub mod bitmap;
|
@ -1,99 +0,0 @@
|
||||
//! Burritos stores a data structure associating object ids with
|
||||
//! their references. The ObjAddr struct
|
||||
//! allows to maintain this data structure.
|
||||
|
||||
use std::{collections::HashMap, cell::RefCell, rc::Rc};
|
||||
|
||||
use crate::kernel::{synch::{ Semaphore, Lock }, thread::Thread};
|
||||
|
||||
/// Brief Definition of object identifiers:
|
||||
///
|
||||
/// The struct stores the list of created objects (Semaphore, Lock, ...) and for each of them
|
||||
/// associates an object identifier than can be passed to subsequent
|
||||
/// system calls on the object.
|
||||
///
|
||||
/// A method allows to detect of an object corresponding to a given
|
||||
/// identifier exists; this is used to check the parameters of system
|
||||
/// calls.
|
||||
#[derive(PartialEq)]
|
||||
pub struct ObjAddr {
|
||||
/// Id of the last added object
|
||||
last_id: i32,
|
||||
/// List of [Semaphore] added in this struct. Each is keyed with a unique i32 id.
|
||||
semaphores: HashMap<i32, Semaphore>,
|
||||
/// List of [Lock] added in this struct. Each is keyed with a unique i32 id.
|
||||
locks: HashMap<i32, Lock>,
|
||||
/// List of threads known by this instance of ObjAddr (useful for managing lock ownership)
|
||||
threads: HashMap<i32, Rc<RefCell<Thread>>>,
|
||||
}
|
||||
|
||||
impl ObjAddr {
|
||||
|
||||
/// Initializes and returns a ObjAddr struct
|
||||
pub fn init() -> Self {
|
||||
Self {
|
||||
last_id: 3,
|
||||
semaphores: HashMap::<i32, Semaphore>::new(),
|
||||
locks: HashMap::<i32, Lock>::new(),
|
||||
threads: HashMap::<i32, Rc<RefCell<Thread>>>::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds the **obj** Semaphore to self
|
||||
pub fn add_semaphore(&mut self, obj: Semaphore) -> i32 {
|
||||
self.last_id += 1;
|
||||
self.semaphores.insert(self.last_id, obj);
|
||||
self.last_id
|
||||
}
|
||||
|
||||
/// Adds the **obj** Lock to self
|
||||
pub fn add_lock(&mut self, obj: Lock) -> i32 {
|
||||
self.last_id += 1;
|
||||
self.locks.insert(self.last_id, obj);
|
||||
self.last_id
|
||||
}
|
||||
|
||||
/// Adds the **obj** Lock to self
|
||||
pub fn add_thread(&mut self, obj: Rc<RefCell<Thread>>) -> i32 {
|
||||
self.last_id +=1;
|
||||
self.threads.insert(self.last_id, obj);
|
||||
self.last_id
|
||||
}
|
||||
|
||||
/// Searches for a semaphore of id **id** in self
|
||||
pub fn search_semaphore(&mut self, id: i32) -> Option<&mut Semaphore> {
|
||||
self.semaphores.get_mut(&id)
|
||||
}
|
||||
|
||||
/// Searches for a lock of id **id** in self
|
||||
pub fn search_lock(&mut self, id:i32) -> Option<&mut Lock> {
|
||||
self.locks.get_mut(&id)
|
||||
}
|
||||
|
||||
/// Update lock at given id
|
||||
pub fn update_lock(&mut self, id: i32, lock: Lock) {
|
||||
self.locks.insert(id, lock);
|
||||
}
|
||||
|
||||
/// Searches for a lock of id **id** in self
|
||||
pub fn search_thread(&mut self, id: i32) -> Option<&Rc<RefCell<Thread>>> {
|
||||
self.threads.get(&id)
|
||||
}
|
||||
|
||||
/// Removes the object of id **id** from self if it exists
|
||||
pub fn remove_semaphore(&mut self, id: i32) -> Option<Semaphore> {
|
||||
self.semaphores.remove(&id)
|
||||
}
|
||||
|
||||
/// Remove the object of id **id** from self if it exists
|
||||
pub fn remove_lock(&mut self, id:i32) -> Option<Lock> {
|
||||
self.locks.remove(&id)
|
||||
}
|
||||
|
||||
/// Remove the object of id **id** from self if it exists
|
||||
pub fn remove_thread(&mut self, id: i32) -> Option<Rc<RefCell<Thread>>> {
|
||||
self.threads.remove(&id)
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,3 +1,21 @@
|
||||
TOPDIR=.
|
||||
include $(TOPDIR)/Makefile.config
|
||||
|
||||
#
|
||||
# Main targets
|
||||
#
|
||||
dumps:
|
||||
$(MAKE) dumps -C riscv_instructions/
|
||||
mkdir -p ${TOPDIR}/target/dumps/
|
||||
find . -name '*.dump' -exec mv {} ${TOPDIR}/target/dumps/ \;
|
||||
|
||||
user_lib:
|
||||
$(MAKE) -C userlib/
|
||||
|
||||
tests: user_lib
|
||||
$(MAKE) tests -C riscv_instructions/
|
||||
mkdir -p ${TOPDIR}/target/guac/
|
||||
find . -name '*.guac' -exec mv {} ${TOPDIR}/target/guac/ \;
|
||||
|
||||
clean:
|
||||
make clean -C riscv_instructions
|
||||
make clean -C syscall_tests
|
||||
rm -rf $(TOPDIR)/target
|
@ -1,11 +1,5 @@
|
||||
include $(TOPDIR)/Makefile.config
|
||||
|
||||
USERLIB = $(TOPDIR)/userlib
|
||||
|
||||
AS = $(RISCV_AS) -c
|
||||
GCC = $(RISCV_GCC)
|
||||
LD = $(RISCV_LD)
|
||||
|
||||
INCPATH += -I$(TOPDIR) -I$(USERLIB)
|
||||
LDFLAGS = $(RISCV_LDFLAGS) -T $(USERLIB)/ldscript.lds
|
||||
ASFLAGS = $(RISCV_ASFLAGS) $(INCPATH)
|
||||
@ -13,19 +7,16 @@ CFLAGS = $(RISCV_CFLAGS) $(INCPATH)
|
||||
|
||||
# Rules
|
||||
%.o: %.s
|
||||
$(AS) $(ASFLAGS) -c $<
|
||||
$(RISCV_AS) $(ASFLAGS) -c $<
|
||||
|
||||
%.o: %.c
|
||||
$(GCC) $(CFLAGS) -c $<
|
||||
|
||||
%.a: %.o
|
||||
$(AR) $(ARFLAGS) $@ $<
|
||||
$(RISCV_GCC) $(CFLAGS) -c $<
|
||||
|
||||
%.dump: %.o
|
||||
$(RISCV_OBJCOPY) -j .text -O $(DUMP_FORMAT) $< $@
|
||||
|
||||
%.guac: %.o
|
||||
$(LD) $(LDFLAGS) $+ -o $@
|
||||
$(RISCV_LD) $(LDFLAGS) $+ -o $@
|
||||
|
||||
# Dependencies
|
||||
.%.d: %.s
|
||||
@ -40,9 +31,6 @@ CFLAGS = $(RISCV_CFLAGS) $(INCPATH)
|
||||
| sed '\''s/\($*\)\.o[ :]*/\1.o $@ : /g'\'' > $@; \
|
||||
[ -s $@ ] || rm -f $@'
|
||||
|
||||
$(PROGRAMS):
|
||||
$(LD) $(LDFLAGS) $+ -o $@
|
||||
|
||||
# Targets
|
||||
#clean:
|
||||
# rm -rf *.o 2> /dev/null
|
BIN
test/disk.txt
Normal file
BIN
test/disk.txt
Normal file
Binary file not shown.
6
test/riscv_instructions/.gitignore
vendored
6
test/riscv_instructions/.gitignore
vendored
@ -1,6 +0,0 @@
|
||||
*
|
||||
!.gitignore
|
||||
!*.c
|
||||
!*/
|
||||
!*.md
|
||||
!**/Makefile
|
@ -1,14 +1,9 @@
|
||||
build:
|
||||
make build -C boolean_logic/
|
||||
make build -C jump_instructions/
|
||||
make build -C simple_arithmetics/
|
||||
|
||||
dumps:
|
||||
make dumps -C boolean_logic/
|
||||
make dumps -C jump_instructions/
|
||||
make dumps -C simple_arithmetics/
|
||||
|
||||
clean:
|
||||
$(MAKE) clean -C boolean_logic/
|
||||
$(MAKE) clean -C jump_instructions/
|
||||
$(MAKE) clean -C simple_arithmetics/
|
||||
tests:
|
||||
make tests -C boolean_logic/
|
||||
make tests -C jump_instructions/
|
||||
make tests -C simple_arithmetics/
|
@ -1,14 +1,9 @@
|
||||
|
||||
PROGRAMS = comparisons.guac if.guac switch.guac
|
||||
TOPDIR = ../../..
|
||||
include $(TOPDIR)/Makefile.rules
|
||||
|
||||
build: $(PROGRAMS)
|
||||
TOPDIR = ../..
|
||||
include $(TOPDIR)/Makefile.tests
|
||||
|
||||
dumps: comparisons.dump if.dump switch.dump
|
||||
|
||||
clean:
|
||||
$(RM) *.o *.guac
|
||||
tests: comparisons.guac if.guac switch.guac
|
||||
|
||||
# Dependances
|
||||
$(PROGRAMS): %.guac : $(USERLIB)/sys.o $(USERLIB)/libnachos.o %.o
|
||||
$(PROGRAMS): % : $(USERLIB)/sys.o $(USERLIB)/libnachos.o %.o
|
@ -1,13 +1,15 @@
|
||||
int main() {
|
||||
int x = 0;
|
||||
int y = 1;
|
||||
while (x <= y) {
|
||||
if (x > y) {
|
||||
x += 1;
|
||||
}
|
||||
if (x == y) {
|
||||
} else if (x == y) {
|
||||
x += y;
|
||||
}
|
||||
if (x < y) {
|
||||
} else if (x < y) {
|
||||
y += 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,12 +1,6 @@
|
||||
PROGRAMS = jump.guac ret.guac
|
||||
TOPDIR = ../../..
|
||||
include $(TOPDIR)/Makefile.rules
|
||||
|
||||
build: $(PROGRAMS)
|
||||
TOPDIR = ../..
|
||||
include $(TOPDIR)/Makefile.tests
|
||||
|
||||
dumps: jump.dump ret.dump
|
||||
|
||||
clean:
|
||||
$(RM) *.o *.guac
|
||||
|
||||
$(PROGRAMS): %.guac : $(USERLIB)/sys.o $(USERLIB)/libnachos.o %.o
|
||||
tests: jump.guac ret.guac
|
@ -1,12 +1,6 @@
|
||||
PROGRAMS = unsigned_addition.guac unsigned_division.guac unsigned_multiplication.guac unsigned_substraction.guac
|
||||
TOPDIR = ../../..
|
||||
include $(TOPDIR)/Makefile.rules
|
||||
|
||||
build: $(PROGRAMS)
|
||||
TOPDIR = ../..
|
||||
include $(TOPDIR)/Makefile.tests
|
||||
|
||||
dumps: unsigned_addition.dump unsigned_division.dump unsigned_multiplication.dump unsigned_substraction.dump
|
||||
|
||||
clean:
|
||||
$(RM) *.o *.guac
|
||||
|
||||
$(PROGRAMS): %.guac : $(USERLIB)/sys.o $(USERLIB)/libnachos.o %.o
|
||||
tests: unsigned_addition.guac unsigned_division.guac unsigned_multiplication.guac unsigned_substraction.guac
|
@ -1,12 +0,0 @@
|
||||
PROGRAMS = halt.guac prints.guac producteur_consommateur.guac lock.guac join.guac matmult.guac
|
||||
TOPDIR = ../..
|
||||
include $(TOPDIR)/Makefile.rules
|
||||
|
||||
build: $(PROGRAMS)
|
||||
|
||||
dumps: halt.dump prints.dump
|
||||
|
||||
clean:
|
||||
$(RM) *.o *.guac
|
||||
|
||||
$(PROGRAMS): %.guac : $(USERLIB)/sys.o $(USERLIB)/libnachos.o %.o
|
@ -1,7 +0,0 @@
|
||||
|
||||
#include "userlib/syscall.h"
|
||||
|
||||
int main() {
|
||||
Shutdown();
|
||||
return 0;
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
#include "userlib/syscall.h"
|
||||
#include "userlib/libnachos.h"
|
||||
|
||||
void thread1() {
|
||||
for(int i = 0; i < 10; i++)
|
||||
{
|
||||
n_printf("Hello from th1\n");
|
||||
}
|
||||
}
|
||||
|
||||
void thread2() {
|
||||
for(int i = 0; i < 10; i++)
|
||||
{
|
||||
n_printf("Hello from th2\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
ThreadId th1 = threadCreate("thread 1", thread1);
|
||||
ThreadId th2 = threadCreate("thread 2", thread2);
|
||||
Join(th1);
|
||||
Join(th2);
|
||||
Shutdown();
|
||||
return 0;
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
#include "userlib/syscall.h"
|
||||
#include "userlib/libnachos.h"
|
||||
|
||||
LockId mutex;
|
||||
int glob_int = 0;
|
||||
|
||||
void increment(void){
|
||||
LockAcquire(mutex);
|
||||
glob_int++;
|
||||
LockRelease(mutex);
|
||||
}
|
||||
|
||||
void counter(int n){
|
||||
for (int i = 0; i < 50; i++){
|
||||
increment();
|
||||
}
|
||||
}
|
||||
|
||||
int main(void){
|
||||
mutex = LockCreate("Lock_debug");
|
||||
|
||||
ThreadId th1 = threadCreate("Thread1", (VoidNoArgFunctionPtr) counter);
|
||||
ThreadId th2 = threadCreate("Thread2",(VoidNoArgFunctionPtr) counter);
|
||||
|
||||
Join(th1);
|
||||
Join(th2);
|
||||
Exit(glob_int);
|
||||
return 0;
|
||||
}
|
@ -1,60 +0,0 @@
|
||||
/* matmult.c
|
||||
* Test program to do matrix multiplication on large arrays.
|
||||
*
|
||||
* Intended to stress virtual memory system.
|
||||
*
|
||||
* Ideally, we could read the matrices off of the file system,
|
||||
* and store the result back to the file system!
|
||||
*
|
||||
* -----------------------------------------------------
|
||||
* This file is part of the Nachos-RiscV distribution
|
||||
* Copyright (c) 2022 University of Rennes 1.
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details
|
||||
* (see see <http://www.gnu.org/licenses/>).
|
||||
* -----------------------------------------------------
|
||||
|
||||
*/
|
||||
|
||||
#include "userlib/syscall.h"
|
||||
|
||||
#define Dim 10 /* sum total of the arrays doesn't fit in
|
||||
* physical memory
|
||||
*/
|
||||
|
||||
/* The matrices to be filled-in and multiplied */
|
||||
int A[Dim][Dim];
|
||||
int B[Dim][Dim];
|
||||
int C[Dim][Dim];
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
int i, j, k;
|
||||
|
||||
Write("Start matmult\n",14,CONSOLE_OUTPUT);
|
||||
|
||||
for (i = 0; i < Dim; i++) /* first initialize the matrices */
|
||||
for (j = 0; j < Dim; j++) {
|
||||
A[i][j] = i;
|
||||
B[i][j] = j;
|
||||
C[i][j] = 0;
|
||||
}
|
||||
|
||||
for (i = 0; i < Dim; i++) /* then multiply them together */
|
||||
for (j = 0; j < Dim; j++)
|
||||
for (k = 0; k < Dim; k++)
|
||||
C[i][j] += A[i][k] * B[k][j];
|
||||
|
||||
|
||||
Exit(C[Dim-1][Dim-1]); /* and then we're done */
|
||||
|
||||
return 0;
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
#include "userlib/syscall.h"
|
||||
#include "userlib/libnachos.h"
|
||||
|
||||
int main() {
|
||||
n_printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&##BBGGGPPGGGBB##&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&#G5J?!~^::............::^~!?J5G#&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@&B5?!^:....:^^~!!7777??7777!!~~^::...:^!JPB&@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@@@@@@@@@@@@@@@@@@B57^....^~!?JJYYY5555555555555555Y5PGP57~::..:^75B@@@@@@@@@@@@@@@@@@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@@@@@@@@@@@@@@&G?~. .:~7?JY55555YYYYYYYYYYYYYYYYY5PB##BPY???7!~^:..:~JG&@@@@@@@@@@@@@@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@@@@@@@@@@@@BJ~. .^!?Y5555YYYYYYYYYYYYYYYYYYYY5PB##B5J?77???????7!~^:.:~JB@@@@@@@@@@@@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@@@@@@@@@&P!. .^!JY55YYYYYYYYYYYYYYYYYYYYYY5PB##B5J?77??????????????7~^:.:!P&@@@@@@@@@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@@@@@@@&Y^..:~?Y55YYYYYYYYYYYYYYYYYYYYYY5PB##B5J?77????????????????????7~:.:~5&@@@@@@@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@@@@@&5^..:!J55YYYYYYYYYYYYYYYYYYYYYY5PB##B5J?77?????????????????????????7~^::~5@@@@@@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@@@@G~..:!Y55YYYYYYYYYYYYYYYYYYYYY5PB##B5J?77??????????????????????????????7~:::!B@@@@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@@&J...~J55YYYYYYYYYYYYYYYYYYYY5PB##B5J?77??????????????????????????????777?Y57:::J&@@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@#!..:7Y5YYYYYYYYYYYYYYYYYYY5PB##B5J?77??????????????????????????????777?YPB##G?^::7#@@@@@@@@\n");
|
||||
n_printf("@@@@@@@G^..^J5YYYYYYYYYYYYYYYYYY5PB##B5J?77??????????????????????????????777?YPB##GP5Y5J~::~B@@@@@@@\n");
|
||||
n_printf("@@@@@@G^..^Y5YYYYYYYYYYYYYYYY5PB##B5J?77??????????????????????????????777?YPB##GP55Y5555Y~::^B@@@@@@\n");
|
||||
n_printf("@@@@@B^..^Y5YYYYYYYYYYYYYY5PB##B5J?77??????????????????????????????777?YPB##GP5YYYY555555Y~::~B@@@@@\n");
|
||||
n_printf("@@@@&!..^J5YYYYYYYYYYYY5PB##B5J?77??????????????????????????????777?YPB#BGPYYYYYY555555555Y~::7&@@@@\n");
|
||||
n_printf("@@@@J..:?5YYYYYYYYYY5PB##B5J?77??????????????????????????????777?YPB#BG5YYYYYYY55YY55555555J^::Y@@@@\n");
|
||||
n_printf("@@@B:..~5YYYYYYYY5PB##G5J?77??????????????????????????????777?YPB#BG5YYYYYYYY5YYY55555555555!::^#@@@\n");
|
||||
n_printf("@@@?..:JYYYYYY5PB##G5J?77??????????????????????????????777?YPB#BG5YYYYYYYYYYYYY55YY555555555Y^::J@@@\n");
|
||||
n_printf("@@&^..~YYYY5PB##G5J?77??????????????????????????????777?YPB#BG5YYYYYYYYYYYYYYYYYY5555Y5555555!::~&@@\n");
|
||||
n_printf("@@G:..7Y5PB##G5J?77??????????????????????????????777?YPB#BG5YJYYYYYYYYYYYYYYYYY55YYY555555555?::^B@@\n");
|
||||
n_printf("@@5:..YB##G5J?77??????????????????????????????777?YPB#BG5JJJYYYJYYYYYYYYYYYYYY5YY5555Y5555555J:^^P@@\n");
|
||||
n_printf("@@5:..PB5J?77??????????????????????????????777?YGB#BG5JJJYYJYYYYYYYYYYYYYYYYYYY55Y55555555555J:^^5@@\n");
|
||||
n_printf("@@5:..7?77??????????????????????????????777?YPB#BG5JJJJJJJJYYYYYYYYYYYYYYYYYYY5YY5Y5555555555J:^^P@@\n");
|
||||
n_printf("@@G:..~??????????????????????????????777?YGB#BPYJJJJJJJJJJJYYYYYYYYYYYYYYYYYYYY5YY55555555555?:^^B@@\n");
|
||||
n_printf("@@&^..^???????????????????????????777?YGB#BPYJ?JJJJJJJJJJJYYJYYYYYYYYYYYYYYYYY5YY5Y5555555555!^:~@@@\n");
|
||||
n_printf("@@@?.::7???????????????????????777?YGB#BPYJ???JJJJJJJJJJJJJYYYYYYYYYYYYYYYYYYYYYYY5555555555Y^^:J@@@\n");
|
||||
n_printf("@@@#:..^????????????????????777?YG##BPYJ????JJJJJJJJJJJJJJYJJYYYYYYYYYYYYYYYYY5YYYY555555555!^^^#@@@\n");
|
||||
n_printf("@@@@J.::!????????????????777?YG##BPY???????JJJJJJJJJJJJJJJJYYYYYYYYYYYYYYYYYY5YYY5555555555J^^:Y@@@@\n");
|
||||
n_printf("@@@@&!..:7????????????777?YGB#BPJ????????????JJJJJJJJJJJJYYJJYYYYYYYYYYYYYYYYYY55YY5555555Y~::7&@@@@\n");
|
||||
n_printf("@@@@@B^.:^7????????777?YGB#BPJ?7???????????JJJ?JJJJJJJJJJJJYYYYJYYYYYYYYYYYYYYYYY5555Y555Y~^:~B@@@@@\n");
|
||||
n_printf("@@@@@@G^.:^7????777?YGB#BPJ?7????????????????JJJJJJJJJJJJYYJJYYYYYYYYYYYYYYYYYY55YYY5555Y~::~B@@@@@@\n");
|
||||
n_printf("@@@@@@@B^.:^7?77?YGB#BPJ?77??7?????????????JJJ?JJJJJJJJJJJJYYYYJYYYYYYYYYYYYYYYYY5555Y5J~::~B@@@@@@@\n");
|
||||
n_printf("@@@@@@@@#!..:!YG##BPJ?77?????????????????????JJJJJJJJJJJJYYJYYYYYYYYYYYYYYYYYYY55YYY55?^::7#@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@@&J:.:7PPY?77???7????7??????????????JJ?JJJJJJJJJJJJYYYYJYYYYYYYYYYYYYY5YY555J!^:^Y&@@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@@@@G!..:~7?????????????????????????JJJJJJJJJJJJJJYYJYYYYYYYYYYYYYYYYYYY555Y7^::7G@@@@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@@@@@@5~..:~7????????????????????????JJJJJJJJJJJJJJJYJYYYYYYYYYYYYYYYYY5YY7^::~5@@@@@@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@@@@@@@&Y~..:~7?????????????????????JJJJJJJJJJJJJJJJYYYYYYYYYYYYYYYYY5YJ!^::!5&@@@@@@@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@@@@@@@@@&P!:.:^~7???????????????????JJJJJJJJJJJJJJJJJYYYYYYYYYYYYYYJ7~::^7P&@@@@@@@@@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@@@@@@@@@@@@BJ~:.:^~77??????????????JJ?JJJJJJJJJJJJJYYYYYYYYYYYYYJ7~^::!YB@@@@@@@@@@@@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@@@@@@@@@@@@@@&GJ~:.::~!!7????????????JJJJJJJJJJJJJYJJYYYYYYJ?7!^::^!JG&@@@@@@@@@@@@@@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@@@@@@@@@@@@@@@@@@B5?~::::^~~!7????JJJJJJJJJJJJYYJJJJJJ?77!~^::^~?5B@@@@@@@@@@@@@@@@@@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@&BPJ7~^:::::^~~~!!77777777!!!~~^^::::^~7JPB&@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&#G5Y?7!~^^::::::::::::^~~!7JYPG#&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@&&##BGGGGGGGBB##&&@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
|
||||
n_printf("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n");
|
||||
Shutdown();
|
||||
return 0;
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
#include "userlib/syscall.h"
|
||||
#include "userlib/libnachos.h"
|
||||
|
||||
|
||||
const int N = 3;
|
||||
int iplein = 0;
|
||||
int ivide = 0;
|
||||
int tab[3];
|
||||
SemId svide;
|
||||
SemId splein;
|
||||
|
||||
void producteur() {
|
||||
for(int i = 0; i < 10; i++)
|
||||
{
|
||||
n_printf("batir une information\n");
|
||||
P(svide);
|
||||
iplein = (iplein + 1) % N;
|
||||
// n_printf("communique une information : %d\n", i);
|
||||
tab[iplein] = i;
|
||||
V(splein);
|
||||
}
|
||||
}
|
||||
|
||||
void consommateur() {
|
||||
int sum = 0;
|
||||
for(int i = 0; i < 10; i++)
|
||||
{
|
||||
P(splein);
|
||||
ivide = (ivide +1) % N;
|
||||
n_printf("recevoir une information\n");
|
||||
int info = tab[ivide];
|
||||
V(svide);
|
||||
sum += info;
|
||||
// n_printf("exploiter l'information : %d\n", info);
|
||||
}
|
||||
Exit(sum);
|
||||
}
|
||||
|
||||
int main() {
|
||||
svide = SemCreate("producteur", N);
|
||||
splein = SemCreate("consommateur", 0);
|
||||
ThreadId producteurTh = threadCreate("producteur", producteur);
|
||||
ThreadId consommateurTh = threadCreate("consommateur", consommateur);
|
||||
Join(producteurTh);
|
||||
Join(consommateurTh);
|
||||
Shutdown();
|
||||
return 0;
|
||||
}
|
4
test/userlib/Makefile
Normal file
4
test/userlib/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
TOPDIR = ../
|
||||
include $(TOPDIR)/Makefile.tests
|
||||
|
||||
default: sys.o libnachos.o
|
@ -1,5 +1,5 @@
|
||||
/* Start.s
|
||||
* Assembly language assist for user programs running on top of BurritOS.
|
||||
* Assembly language assist for user programs running on top of Nachos.
|
||||
*
|
||||
* Since we don't want to pull in the entire C library, we define
|
||||
* what we need for a user program here, namely Start and the system
|
||||
@ -63,9 +63,9 @@ __start:
|
||||
* -------------------------------------------------------------
|
||||
*/
|
||||
|
||||
.globl Shutdown
|
||||
.type __Shutdown, @function
|
||||
Shutdown:
|
||||
.globl Halt
|
||||
.type __Halt, @function
|
||||
Halt:
|
||||
addi a7,zero,SC_HALT
|
||||
ecall
|
||||
jr ra
|
@ -84,7 +84,7 @@
|
||||
typedef int t_error;
|
||||
|
||||
/* Stop Nachos, and print out performance stats */
|
||||
void Shutdown();
|
||||
void Halt();
|
||||
|
||||
|
||||
/* Return the time spent running Nachos */
|
@ -1,7 +0,0 @@
|
||||
TOPDIR = ../
|
||||
include $(TOPDIR)/Makefile.rules
|
||||
|
||||
default: sys.o libnachos.o
|
||||
|
||||
clean:
|
||||
$(RM) libnachos.o sys.o
|
@ -1,183 +0,0 @@
|
||||
use std::str::Chars;
|
||||
|
||||
/// Define the BurritOS running time basic unit
|
||||
pub struct BurritosTime {
|
||||
seconds: i64,
|
||||
nanos: i64
|
||||
}
|
||||
|
||||
/// A unique identifier for a thread executed within a user program
|
||||
pub struct ThreadId{
|
||||
id: u64
|
||||
}
|
||||
|
||||
/// The system call interface. These are the operations the BurritOS
|
||||
/// kernel needs to support, to be able to run user programs.
|
||||
pub struct TError {
|
||||
t: i32
|
||||
}
|
||||
|
||||
/// A unique identifier for an open BurritOS file.
|
||||
pub struct OpenFiledId{
|
||||
id: u64
|
||||
}
|
||||
|
||||
/// System calls concerning semaphores management
|
||||
pub struct SemId{
|
||||
id: u64
|
||||
}
|
||||
|
||||
/// System calls concerning locks management
|
||||
pub struct LockId{
|
||||
id: u64
|
||||
}
|
||||
|
||||
/// System calls concerning conditions variables.
|
||||
pub struct CondId{
|
||||
id: u64
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
///Stop BurritOS, and print out performance stats
|
||||
fn Shutdown() -> ();
|
||||
|
||||
/// Return the time spent running BurritOS
|
||||
/// ## Param
|
||||
/// - **t** a struct to define the time unit
|
||||
fn SysTime(t: BurritosTime) -> ();
|
||||
|
||||
/// This user program is done
|
||||
/// ## Param
|
||||
/// - **status** status at the end of execution *(status = 0 means exited normally)*.
|
||||
fn Exit(status: i32) -> ();
|
||||
|
||||
/// Run the executable, stored in the BurritOS file "name", and return the
|
||||
/// master thread identifier
|
||||
fn Exec(name: *const char) -> ThreadId;
|
||||
|
||||
/// Create a new thread in the current process
|
||||
/// Return thread identifier
|
||||
fn newThread(debug_name: *const char, func: i32, arg: i32) -> ThreadId;
|
||||
|
||||
/// Only return once the the thread "id" has finished.
|
||||
fn Join (id: ThreadId) -> TError;
|
||||
|
||||
/// Yield the CPU to another runnable thread, whether in this address space
|
||||
/// or not.
|
||||
fn Yield() -> ();
|
||||
|
||||
/// Print the last error message with the personalized one "mess"
|
||||
fn PError(mess: *const char) -> ();
|
||||
|
||||
/// Create a BurritOS file, with "name"
|
||||
fn Create(name: *const char, size: i32) -> TError;
|
||||
|
||||
/// Open the Nachos file "name", and return an "OpenFileId" that can
|
||||
/// be used to read and write to the file.
|
||||
fn Open(name: *const char) -> OpenFiledId;
|
||||
|
||||
/// Write "size" bytes from "buffer" to the open file.
|
||||
fn Write(buffer: *const char, size: i32, id: OpenFiledId) -> TError;
|
||||
|
||||
/// Read "size" bytes from the open file into "buffer".
|
||||
/// Return the number of bytes actually read -- if the open file isn't
|
||||
/// long enough, or if it is an I/O device, and there aren't enough
|
||||
/// characters to read, return whatever is available (for I/O devices,
|
||||
/// you should always wait until you can return at least one character).
|
||||
fn Read(buffer: *const char, size: i32, id:OpenFiledId) -> TError;
|
||||
|
||||
/// Seek to a specified offset into an opened file
|
||||
fn Seek(offset: i32, id: OpenFiledId) -> TError;
|
||||
|
||||
/// Close the file, we're done reading and writing to it.
|
||||
fn Close(id: OpenFiledId) -> TError;
|
||||
|
||||
/// Remove the file
|
||||
fn Remove(name: *const char) -> TError;
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
/// system calls concerning directory management ///
|
||||
////////////////////////////////////////////////////
|
||||
|
||||
/// Create a new repertory
|
||||
/// Return a negative number if an error ocurred.
|
||||
fn mkdir(name: *const char) -> t_length;
|
||||
|
||||
/// Destroy a repertory, which must be empty.
|
||||
/// Return a negative number if an error ocurred.
|
||||
fn Rmdir(name: *const char) -> TError;
|
||||
|
||||
/// List the content of BurritOS FileSystem
|
||||
fn FSList() -> TError;
|
||||
|
||||
/// Create a semaphore, initialising it at count.
|
||||
/// Return a Semid, which will enable to do operations on this
|
||||
/// semaphore
|
||||
fn SemCreate(debug_name: *const char, count: i32) -> SemId;
|
||||
|
||||
/// Destroy a semaphore identified by sema.
|
||||
/// Return a negative number if an error occured during the destruction
|
||||
fn SemDestroy(sema: SemId) -> TError;
|
||||
|
||||
/// Do the operation P() on the semaphore sema
|
||||
fn P(sema: SemId) -> TError;
|
||||
|
||||
/// Do the operation V() on the semaphore sema
|
||||
fn V(sema: SemId) -> TError;
|
||||
|
||||
/// Create a lock.
|
||||
/// Return an identifier
|
||||
fn LockCreate(debug_name: *const char) -> LockId;
|
||||
|
||||
/// Destroy a lock.
|
||||
/// Return a negative number if an error ocurred
|
||||
/// during the destruction.
|
||||
fn LockDestroy(id: LockId) -> TError;
|
||||
|
||||
/// Do the operation Acquire on the lock id.
|
||||
/// Return a negative number if an error ocurred.
|
||||
fn LockAcquire(id: LockId) -> TError;
|
||||
|
||||
/// Do the operation Release on the lock id.
|
||||
/// Return a negative number if an error ocurred.
|
||||
fn LockRelease(id: LockId) -> TError;
|
||||
|
||||
/// Create a new condition variable
|
||||
fn CondCreate(debug_name: *const char) -> CondId;
|
||||
|
||||
/// Destroy a condition variable.
|
||||
/// Return a negative number if an error ocurred.
|
||||
fn CondDestroy(id: CondId) -> TError;
|
||||
|
||||
/// Do the operation Wait on a condition variable.
|
||||
/// Returns a negative number if an error ocurred.
|
||||
fn CondWait(id: CondId) -> TError;
|
||||
|
||||
/// Do the operation Signal on a condition variable (wake up only one thread).
|
||||
/// Return a negative number if an error ocurred.
|
||||
fn CondSignal(id: CondId) -> TError;
|
||||
|
||||
/// Do the operation Signal on a condition variable (wake up all threads).
|
||||
/// Return a negative number if an error ocurred.
|
||||
fn CondBroadcast(id: CondId) -> TError;
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
/// # System calls concerning serial port and console
|
||||
///////////////////////////////////////////////////////
|
||||
|
||||
///Send the message on the serial communication link.
|
||||
/// Returns the number of bytes successfully sent.
|
||||
fn TtySend(mess: *const char) -> i32;
|
||||
|
||||
/// Wait for a message comming from the serial communication link.
|
||||
/// The length of the buffer where the bytes will be copied is given as a parameter.
|
||||
/// Returns the number of characters actually received.
|
||||
fn TtyReceive(mess: *const char, length: i32) -> i32;
|
||||
|
||||
/// Map an opened file in memory. Size is the size to be mapped in bytes.
|
||||
fn Mmap(id: OpenFiledId, size: i32) -> *mut ();
|
||||
|
||||
/// For debug purpose
|
||||
fn Debug(param: i32)-> ();
|
||||
}
|
Reference in New Issue
Block a user