Compare commits
47 Commits
clippy_fix
...
disk
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
|
||||
- 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 = "*"
|
31
Makefile
31
Makefile
@ -1,31 +0,0 @@
|
||||
TOPDIR=.
|
||||
include $(TOPDIR)/Makefile.config
|
||||
|
||||
|
||||
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/ \;
|
||||
|
||||
clean:
|
||||
$(MAKE) clean -C userlib/
|
||||
$(MAKE) clean -C test/
|
||||
$(RM) -rf $(TOPDIR)/target
|
||||
|
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 = 400
|
||||
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,390 +0,0 @@
|
||||
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")?
|
||||
};
|
||||
system.get_thread_manager().thread_finish(machine, th);
|
||||
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 = "".to_string();
|
||||
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 {
|
||||
println!("{}", 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 => todo!(),
|
||||
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("Initial_count < 0".to_string())?,
|
||||
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() {
|
||||
system.get_thread_manager().start_thread(n_thread, Rc::clone(process), func as u64, current_thread.thread_context.int_registers[2] as u64 + machine.page_size, 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 {
|
||||
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);
|
||||
|
||||
machine.write_memory(4, 0, 0b0000_0000_0000_0000_0000_0000_0111_0011); // ecall
|
||||
machine.write_memory(4, 4, 0b0000_0000_1010_0000_0000_0000_1001_0011); // 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, 0b0000_0000_0000_0000_0000_0000_0111_0011); // ecall
|
||||
machine.write_int_register(17, SC_WRITE as i64); // Set type to write
|
||||
|
||||
machine.write_memory(4, 4, 0b0000_0000_0000_0000_0000_1000_1001_0011); // r17 <- SC_SHUTDOWN
|
||||
machine.write_memory(4, 8, 0b0000_0000_0000_0000_0000_0000_0111_0011); // ecall
|
||||
|
||||
let mut system = System::new(true);
|
||||
machine.run(&mut system);
|
||||
}
|
||||
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
#![allow(unused, clippy::missing_docs_in_private_items)]
|
||||
|
||||
/// Error enum, use it with Result<YourSucessStruct, **ErrorCode**>
|
||||
pub enum ErrorCode {
|
||||
IncError,
|
||||
@ -27,5 +27,5 @@ pub enum ErrorCode {
|
||||
WrongFileEndianess,
|
||||
NoAcia,
|
||||
|
||||
NumMsgError /* Must always be last */
|
||||
NUMMSGERROR /* Must always be last */
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
pub mod process;
|
||||
mod process;
|
||||
pub mod thread;
|
||||
pub mod scheduler;
|
||||
pub mod mgerror;
|
||||
pub mod system;
|
||||
pub mod synch;
|
||||
mod thread_manager;
|
||||
pub mod exception;
|
||||
mod ucontext;
|
||||
mod synch;
|
||||
mod thread_manager;
|
@ -1,5 +1,5 @@
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
#[derive(PartialEq, Debug)]
|
||||
pub struct Process {
|
||||
pub num_thread: usize,
|
||||
}
|
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");
|
||||
}
|
||||
}
|
||||
}
|
@ -4,16 +4,21 @@ 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
|
||||
#[derive(PartialEq)]
|
||||
pub struct Semaphore {
|
||||
|
||||
/// Counter of simultanous Semaphore
|
||||
pub counter:i32,
|
||||
counter:i32,
|
||||
/// QUeue of Semaphore waiting to be exucated
|
||||
pub waiting_queue:List<Rc<RefCell<Thread>>>,
|
||||
waiting_queue:List<Rc<RefCell<Thread>>>,
|
||||
/// Thread manager which managing threads
|
||||
thread_manager: Rc<RefCell<ThreadManager>>
|
||||
|
||||
}
|
||||
|
||||
@ -24,25 +29,63 @@ 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
|
||||
|
||||
}
|
||||
|
||||
@ -53,8 +96,8 @@ impl Lock {
|
||||
///
|
||||
/// ### 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
|
||||
@ -68,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 = Option::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.
|
||||
@ -101,53 +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);
|
||||
match &self.owner {
|
||||
Some(x) => thread_manager.ready_to_run(Rc::clone(x)),
|
||||
None => ()
|
||||
}
|
||||
},
|
||||
None => {
|
||||
self.free = true;
|
||||
self.owner = None;
|
||||
}
|
||||
}
|
||||
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) => scheduler.ready_to_run(Rc::clone(&x)),
|
||||
None => ()
|
||||
}
|
||||
} else {
|
||||
self.free = true;
|
||||
self.owner = None;
|
||||
}
|
||||
None => ()
|
||||
}
|
||||
|
||||
machine.interrupt.set_status(old_status);
|
||||
system.borrow_mut().get_g_machine().borrow_mut().interrupt.set_status(old_status);
|
||||
}
|
||||
|
||||
/// True if the current thread holds this lock.
|
||||
/// Useful for checking in Release, and in Condition operations below.
|
||||
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>>,
|
||||
|
||||
}
|
||||
|
||||
@ -157,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).
|
||||
@ -168,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);
|
||||
}
|
||||
@ -190,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);
|
||||
@ -209,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);
|
||||
|
||||
@ -227,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,7 +1,5 @@
|
||||
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;
|
||||
|
||||
@ -18,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 {
|
||||
@ -40,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
|
||||
@ -62,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
|
||||
@ -94,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,521 +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
|
||||
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
|
||||
g_current_thread: Option::None,
|
||||
g_thread_to_be_destroyed: Option::None,
|
||||
g_alive: List::new(),
|
||||
g_scheduler: Scheduler::new(),
|
||||
system: 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: 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.
|
||||
///
|
||||
/// Global 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
|
||||
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
|
||||
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.
|
||||
///
|
||||
/// Cannot use yield as a function name -> reserved name in rust
|
||||
///
|
||||
/// ## Parameters
|
||||
///
|
||||
/// **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) {
|
||||
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();
|
||||
if let Some(next_thread) = next_thread {
|
||||
if is_ready {
|
||||
self.ready_to_run(thread);
|
||||
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);
|
||||
|
||||
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 {
|
||||
let scheduler = &mut self.g_scheduler;
|
||||
scheduler.ready_to_run(thread);
|
||||
scheduler.switch_to(next_thread);
|
||||
}
|
||||
self.switch_to(machine, next_thread);
|
||||
machine.interrupt.set_status(old_status);
|
||||
}
|
||||
machine.interrupt.set_status(old_status);
|
||||
}
|
||||
|
||||
/// Put the thread to sleep and relinquish the processor
|
||||
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);
|
||||
|
||||
self.debug(format!("Sleeping thread {}", thread.borrow().get_name()));
|
||||
let mut next_thread = self.find_next_to_run();
|
||||
while next_thread.is_none() {
|
||||
eprintln!("Nobody to run => idle");
|
||||
machine.interrupt.idle();
|
||||
next_thread = self.find_next_to_run();
|
||||
}
|
||||
self.switch_to(machine, Rc::clone(&next_thread.unwrap()));
|
||||
pub fn thread_sleep(&mut self, thread: Rc<RefCell<Thread>>) {
|
||||
|
||||
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();
|
||||
next_thread = self.g_scheduler.find_next_to_run();
|
||||
}
|
||||
self.g_scheduler.switch_to(Rc::clone(&next_thread.unwrap()));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// Finish the execution of the thread and prepare its deallocation
|
||||
pub fn thread_finish(&mut self, machine: &mut Machine, thread: ThreadRef) {
|
||||
let old_status = machine.interrupt.set_status(InterruptStatus::InterruptOff);
|
||||
assert!(self.g_alive.remove(Rc::clone(&thread)));
|
||||
self.debug(format!("Finishing thread {}", thread.borrow().get_name()));
|
||||
// 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));
|
||||
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);
|
||||
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
|
||||
self.thread_sleep(Rc::clone(&thread));
|
||||
machine.interrupt.set_status(old_status);
|
||||
}
|
||||
self.thread_sleep(machine, Rc::clone(&thread));
|
||||
machine.interrupt.set_status(old_status);
|
||||
}
|
||||
|
||||
/// Save the CPU state of a user program on a context switch.
|
||||
pub fn thread_save_processor_state(&mut self, machine: &mut Machine, thread: ThreadRef) {
|
||||
let mut t = thread.borrow_mut();
|
||||
for i in 0..NUM_INT_REGS {
|
||||
t.thread_context.int_registers[i] = machine.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.pc = machine.pc;
|
||||
}
|
||||
|
||||
/// Restore the CPU state of a user program on a context switch.
|
||||
pub fn thread_restore_processor_state(&self, machine: &mut Machine, thread: ThreadRef) {
|
||||
let t: Ref<_> = thread.borrow();
|
||||
for i in 0..NUM_INT_REGS {
|
||||
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);
|
||||
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] = system.get_g_machine().borrow().read_int_register(i);
|
||||
}
|
||||
for i in 0..NUM_FP_REGS {
|
||||
t.thread_context.float_registers[i] = system.get_g_machine().borrow().read_fp_register(i);
|
||||
}
|
||||
} else {
|
||||
Err("lock_acquire error: cannot find Lock.")?
|
||||
unreachable!("System is None")
|
||||
}
|
||||
machine.interrupt.set_status(old_status);
|
||||
Ok(MachineOk::Ok)
|
||||
}
|
||||
|
||||
/// Wake up a waiter if necessary, or release it if no thread is waiting.
|
||||
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;
|
||||
}
|
||||
}
|
||||
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]);
|
||||
}
|
||||
};
|
||||
// self.get_obj_addrs().update_lock(id, lock);
|
||||
|
||||
machine.interrupt.set_status(old_status);
|
||||
Ok(MachineOk::Ok)
|
||||
} else {
|
||||
unreachable!("System is None")
|
||||
}
|
||||
}
|
||||
|
||||
/// Currently running thread
|
||||
pub fn get_g_current_thread(&mut self) -> &Option<ThreadRef> {
|
||||
&self.g_current_thread
|
||||
pub fn get_g_current_thread(&mut self) -> &mut Option<Rc<RefCell<Thread>>> {
|
||||
&mut self.g_current_thread
|
||||
}
|
||||
|
||||
/// Thread to be destroyed by [...]
|
||||
///
|
||||
/// 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
|
||||
}
|
||||
|
||||
/// List of alive threads
|
||||
pub fn get_g_alive(&mut self) -> &mut List<ThreadRef> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[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));
|
||||
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));
|
||||
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
|
||||
}
|
||||
}
|
77
src/main.rs
77
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,56 +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 mut system = System::new(args.debug & 2 != 0);
|
||||
let disk = Disk::init_disk();
|
||||
let mut drv_disk = DrvDisk::init_drv_disk(disk);
|
||||
|
||||
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 data = Vec::new();
|
||||
data.push(0 as u8); data.push(0 as u8); data.push(0 as u8); data.push(0 as u8);
|
||||
|
||||
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 + machine.page_size, -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));
|
||||
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);
|
||||
|
||||
machine.run(&mut system);
|
||||
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);
|
||||
|
||||
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);
|
||||
let add = Instruction::new(0b0000000_10000_10001_000_11100_0110011);
|
||||
let xor = Instruction::new(0b0000000_10000_10001_100_11100_0110011);
|
||||
let slr = Instruction::new(0b0000000_10000_10001_101_11100_0110011);
|
||||
let sra = Instruction::new(0b0100000_10000_10001_101_11100_0110011);
|
||||
|
||||
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);
|
||||
let slli = Instruction::new(0b0000000000_10001_001_11100_0010011);
|
||||
let slti = Instruction::new(0b0000000000_10001_010_11100_0010011);
|
||||
let sltiu = Instruction::new(0b0000000000_10001_011_11100_0010011);
|
||||
let xori = Instruction::new(0b_0000000000010001_100_11100_0010011);
|
||||
let ori = Instruction::new(0b00000000000_10001_110_11100_0010011);
|
||||
let andi = Instruction::new(0b000000000000_10001_111_11100_0010011);
|
||||
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);
|
||||
let lui_negatif = Instruction::new(0b11110001000011111000_11100_0110111);
|
||||
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);
|
||||
let lh = Instruction::new(0b010111110000_10001_001_11100_0000011);
|
||||
let lw = Instruction::new(0b010111110000_10001_010_11100_0000011);
|
||||
let lbu = Instruction::new(0b010111110000_10001_100_11100_0000011);
|
||||
let lhu = Instruction::new(0b010111110000_10001_101_11100_0000011);
|
||||
let ld = Instruction::new(0b010111110000_10001_011_11100_0000011);
|
||||
let lwu = Instruction::new(0b010111110000_10001_110_11100_0000011);
|
||||
|
||||
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);
|
||||
let sllw: Instruction = Instruction::new(0b0000000_10000_10001_001_11100_0111011);
|
||||
let srlw: Instruction = Instruction::new(0b0000000_10000_10001_101_11100_0111011);
|
||||
let sraw: Instruction = Instruction::new(0b0100000_10000_10001_101_11100_0111011);
|
||||
|
||||
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);
|
||||
let slliw: Instruction = Instruction::new(0b0000000_10000_10001_001_11100_0011011);
|
||||
let srai: Instruction = Instruction::new(0b010000010001_10001_101_11100_0010011);
|
||||
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);
|
||||
let bne: Instruction = Instruction::new(0b0000000_10000_10001_001_00000_1100011);
|
||||
let blt: Instruction = Instruction::new(0b0000000_10000_10001_100_00000_1100011);
|
||||
let bge: Instruction = Instruction::new(0b0000000_10000_10001_101_00000_1100011);
|
||||
let bge2: Instruction = Instruction::new(0x00f75863);
|
||||
let bltu: Instruction = Instruction::new(0b0000000_10000_10001_110_00000_1100011);
|
||||
let bgeu: Instruction = Instruction::new(0b0000000_10000_10001_111_00000_1100011);
|
||||
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), 0));
|
||||
assert_eq!("sd s0,24(sp)", instruction_debug(&Instruction::new(0x00813c23), 0));
|
||||
assert_eq!("addi s0,sp,32", instruction_debug(&Instruction::new(0x02010413), 0));
|
||||
assert_eq!("sw zero,-20(s0)", instruction_debug(&Instruction::new(0xfe042623), 0));
|
||||
assert_eq!("addi a5,zero,5", instruction_debug(&Instruction::new(0x00500793), 0));
|
||||
assert_eq!("sw a5,-24(s0)", instruction_debug(&Instruction::new(0xfef42423), 0));
|
||||
assert_eq!("lw a5,-24(s0)", instruction_debug(&Instruction::new(0xfe842783), 0));
|
||||
assert_eq!("sw a5,-20(s0)", instruction_debug(&Instruction::new(0xfef42623), 0));
|
||||
assert_eq!("lw a5,-20(s0)", instruction_debug(&Instruction::new(0xfec42783), 0));
|
||||
assert_eq!("addi a4,a5,0", instruction_debug(&Instruction::new(0x00078713), 0));
|
||||
assert_eq!("lw a5,-24(s0)", instruction_debug(&Instruction::new(0xfe842783), 0));
|
||||
assert_eq!("mulw a5,a4,a5", instruction_debug(&Instruction::new(0x02f707bb), 0));
|
||||
assert_eq!("sw a5,-20(s0)", instruction_debug(&Instruction::new(0xfef42623), 0));
|
||||
assert_eq!("lw a5,-20(s0)", instruction_debug(&Instruction::new(0xfec42783), 0));
|
||||
assert_eq!("addi a4,a5,0", instruction_debug(&Instruction::new(0x00078713), 0));
|
||||
assert_eq!("lw a5,-24(s0)", instruction_debug(&Instruction::new(0xfe842783), 0));
|
||||
assert_eq!("addw a5,a4,a5", instruction_debug(&Instruction::new(0x00f707bb), 0));
|
||||
assert_eq!("sw a5,-20(s0)", instruction_debug(&Instruction::new(0xfef42623), 0));
|
||||
assert_eq!("lw a5,-20(s0)", instruction_debug(&Instruction::new(0xfec42783), 0));
|
||||
assert_eq!("addi a4,a5,0", instruction_debug(&Instruction::new(0x00078713), 0));
|
||||
assert_eq!("lw a5,-24(s0)", instruction_debug(&Instruction::new(0xfe842783), 0));
|
||||
assert_eq!("subw a5,a4,a5", instruction_debug(&Instruction::new(0x40f707bb), 0));
|
||||
assert_eq!("sw a5,-24(s0)", instruction_debug(&Instruction::new(0xfef42423), 0));
|
||||
assert_eq!("addi a5,zero,0", instruction_debug(&Instruction::new(0x00000793), 0));
|
||||
assert_eq!("addi a0,a5,0", instruction_debug(&Instruction::new(0x00078513), 0));
|
||||
assert_eq!("ld s0,24(sp)", instruction_debug(&Instruction::new(0x01813403), 0));
|
||||
assert_eq!("addi sp,sp,32", instruction_debug(&Instruction::new(0x02010113), 0));
|
||||
assert_eq!("jalr zero,0(ra)", instruction_debug(&Instruction::new(0x00008067), 0));
|
||||
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_fibo() {
|
||||
assert_eq!("jal zero,10504", instruction_debug(&Instruction::new(0x0500006f), 0x104b4));
|
||||
assert_eq!("blt a4,a5,104b8", instruction_debug(&Instruction::new(0xfaf740e3), 0x10518));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mul_prog() {
|
||||
assert_eq!("addi sp,sp,-32", instruction_debug(&Instruction::new(0xfe010113), 0));
|
||||
assert_eq!("sd s0,24(sp)", instruction_debug(&Instruction::new(0x00813c23), 0));
|
||||
assert_eq!("addi s0,sp,32", instruction_debug(&Instruction::new(0x02010413), 0));
|
||||
assert_eq!("addi a5,zero,5", instruction_debug(&Instruction::new(0x00500793), 0));
|
||||
assert_eq!("sw a5,-20(s0)", instruction_debug(&Instruction::new(0xfef42623), 0));
|
||||
assert_eq!("lw a5,-20(s0)", instruction_debug(&Instruction::new(0xfec42783), 0));
|
||||
assert_eq!("addi a4,a5,0", instruction_debug(&Instruction::new(0x00078713), 0));
|
||||
assert_eq!("addi a5,a4,0", instruction_debug(&Instruction::new(0x00070793), 0));
|
||||
assert_eq!("slliw a5,a5,0x2", instruction_debug(&Instruction::new(0x0027979b), 0));
|
||||
assert_eq!("addw a5,a5,a4", instruction_debug(&Instruction::new(0x00e787bb), 0));
|
||||
assert_eq!("sw a5,-24(s0)", instruction_debug(&Instruction::new(0xfef42423), 0));
|
||||
assert_eq!("lw a5,-20(s0)", instruction_debug(&Instruction::new(0xfec42783), 0));
|
||||
assert_eq!("addi a4,a5,0", instruction_debug(&Instruction::new(0x00078713), 0));
|
||||
assert_eq!("lw a5,-24(s0)", instruction_debug(&Instruction::new(0xfe842783), 0));
|
||||
assert_eq!("mulw a5,a4,a5", instruction_debug(&Instruction::new(0x02f707bb), 0));
|
||||
assert_eq!("sw a5,-28(s0)", instruction_debug(&Instruction::new(0xfef42223), 0));
|
||||
assert_eq!("lw a5,-28(s0)", instruction_debug(&Instruction::new(0xfe442783), 0));
|
||||
assert_eq!("addi a4,a5,0", instruction_debug(&Instruction::new(0x00078713), 0));
|
||||
assert_eq!("lw a5,-24(s0)", instruction_debug(&Instruction::new(0xfe842783), 0));
|
||||
assert_eq!("divw a5,a4,a5", instruction_debug(&Instruction::new(0x02f747bb), 0));
|
||||
assert_eq!("sw a5,-20(s0)", instruction_debug(&Instruction::new(0xfef42623), 0));
|
||||
assert_eq!("addi a5,zero,0", instruction_debug(&Instruction::new(0x00000793), 0));
|
||||
assert_eq!("addi a0,a5,0", instruction_debug(&Instruction::new(0x00078513), 0));
|
||||
assert_eq!("ld s0,24(sp)", instruction_debug(&Instruction::new(0x01813403), 0));
|
||||
assert_eq!("addi sp,sp,32", instruction_debug(&Instruction::new(0x02010113), 0));
|
||||
assert_eq!("jalr zero,0(ra)", instruction_debug(&Instruction::new(0x00008067), 0));
|
||||
}
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
#[derive(PartialEq)]
|
||||
pub struct Interrupt {
|
||||
level: InterruptStatus
|
||||
}
|
||||
@ -21,7 +21,7 @@ impl Interrupt {
|
||||
old
|
||||
}
|
||||
|
||||
fn one_tick(&self, _nb_cycle: i32) {
|
||||
fn one_tick(&self, nb_cycle: i32) {
|
||||
todo!();
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ impl Interrupt {
|
||||
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
||||
#[derive(PartialEq, Clone, Copy, Debug)]
|
||||
pub enum InterruptStatus {
|
||||
InterruptOff,
|
||||
InterruptOn
|
||||
|
@ -1,627 +1,34 @@
|
||||
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 <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
|
||||
///
|
||||
/// Offset: 0x0, size: 4 bytes
|
||||
pub name_offset: u32,
|
||||
/// Identify the type of this header
|
||||
///
|
||||
/// Offset: 0x4, size: 4 bytes
|
||||
pub header_type: u32,
|
||||
/// Identify the atributes of this section
|
||||
///
|
||||
/// 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
|
||||
}
|
||||
/// Load a file into a new machine
|
||||
///
|
||||
/// `panic!` when size is not 1, 2, 4 or 8
|
||||
/// `panic!` when the text does not represents instructions in hexadecimal
|
||||
///
|
||||
/// ### Parameters
|
||||
///
|
||||
/// - **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(4, start_index + section.virt_addr as usize + j, u32::from_le_bytes(buf) 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);
|
||||
match res {
|
||||
Ok(res) => {
|
||||
if res == 0 {
|
||||
break; // eof
|
||||
} else {
|
||||
instructions.push(buf[0]);
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
return Err(LoaderError::IOError(err))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
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 })
|
||||
for (i,line) in reader.lines().enumerate() {
|
||||
let res = u64::from_str_radix(&line.unwrap(), 16);
|
||||
match res {
|
||||
Ok(value) => {
|
||||
Machine::write_memory(&mut machine, instruction_size, i*instruction_size as usize, value);
|
||||
},
|
||||
Err(err) => {
|
||||
Err(LoaderError::IOError(err))
|
||||
}
|
||||
_ => panic!()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 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, 0x0040_0000);
|
||||
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,136 +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,
|
||||
/// 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 mem_size = (page_size*num_phy_page*100_000) 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
|
||||
}
|
||||
registers_trace : String::from("")
|
||||
};
|
||||
|
||||
ret.int_reg.set_reg(10, -1);
|
||||
ret
|
||||
}
|
||||
|
||||
/// Read from main memory of the machine
|
||||
@ -142,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)");
|
||||
}
|
||||
@ -150,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
|
||||
}
|
||||
@ -165,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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,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) {
|
||||
@ -194,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_be_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 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),
|
||||
RISCV_OPI => {
|
||||
match inst.funct3 {
|
||||
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
|
||||
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); }
|
||||
}
|
||||
},
|
||||
|
||||
// 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)
|
||||
};
|
||||
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, imm12),
|
||||
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_OP => {
|
||||
if inst.funct7 == 1 {
|
||||
match inst.funct3 {
|
||||
RISCV_OP_M_MUL => {
|
||||
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 = (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 = 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 = 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;
|
||||
machine.int_reg.set_reg(inst.rd as usize, ((long_result >> 64) & 0xffffffffffffffff) as i64);
|
||||
},
|
||||
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 {
|
||||
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 {
|
||||
compute(&core::ops::Shr::shr, rs1, shamt)
|
||||
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));
|
||||
}
|
||||
_ => Err(format!("Unreachable in opi_instruction match! Instruction was {:?}", inst))?
|
||||
}
|
||||
}
|
||||
},
|
||||
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 {
|
||||
machine.int_reg.set_reg(inst.rd as usize, 0);
|
||||
}
|
||||
},
|
||||
RISCV_OP_SLTU => {
|
||||
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 {
|
||||
machine.int_reg.set_reg(inst.rd as usize, 1);
|
||||
} else {
|
||||
machine.int_reg.set_reg(inst.rd as usize, 0);
|
||||
}
|
||||
},
|
||||
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
|
||||
}
|
||||
},
|
||||
//******************************************************************************************
|
||||
// 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
|
||||
};
|
||||
machine.int_reg.set_reg(inst.rd as usize, result);
|
||||
},
|
||||
_ => {
|
||||
panic!("In OPI switch case, this should never happen... Instr was {}\n", inst.value);
|
||||
}
|
||||
}
|
||||
},
|
||||
//******************************************************************************************
|
||||
// Treatment for: OPW INSTRUCTIONS
|
||||
RISCV_OPW => {
|
||||
if inst.funct7 == 1 { // rv64m
|
||||
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;
|
||||
|
||||
/// 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;
|
||||
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)
|
||||
},
|
||||
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)
|
||||
},
|
||||
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)
|
||||
},
|
||||
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;
|
||||
long_result = (unsigned_reg1 * unsigned_reg2) as i128;
|
||||
self.int_reg.set_reg(inst.rd, ((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))?
|
||||
// Match case for multiplication operations (in standard extension RV32M)
|
||||
match inst.funct3 {
|
||||
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 = 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 {
|
||||
machine.int_reg.set_reg(inst.rd as usize, local_dataa + local_datab);
|
||||
} else { // SUBW
|
||||
machine.int_reg.set_reg(inst.rd as usize, local_dataa - local_datab);
|
||||
}
|
||||
},
|
||||
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
|
||||
machine.int_reg.set_reg(inst.rd as usize, local_dataa >> (local_datab & 0x1f));
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
panic!("this instruction ({}) doesn't exists", inst.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
//******************************************************************************************
|
||||
// Treatment for: Simple floating point extension
|
||||
RISCV_FP => {
|
||||
match inst.funct7 {
|
||||
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 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_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 => {
|
||||
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);
|
||||
}
|
||||
}
|
||||
},
|
||||
RISCV_FP_FCVTW => {
|
||||
if inst.rs2 == RISCV_FP_FCVTW_W {
|
||||
machine.int_reg.set_reg(inst.rd as usize, machine.fp_reg.get_reg(inst.rs1 as usize) as i64);
|
||||
} else {
|
||||
machine.int_reg.set_reg(inst.rd as usize, (machine.fp_reg.get_reg(inst.rs1 as usize) as u64) as i64);
|
||||
}
|
||||
},
|
||||
RISCV_FP_FCVTS => {
|
||||
if inst.rs2 == RISCV_FP_FCVTS_W {
|
||||
machine.fp_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) as f32);
|
||||
} else {
|
||||
machine.fp_reg.set_reg(inst.rd as usize, (machine.int_reg.get_reg(inst.rs1 as usize) as u32) as f32);
|
||||
}
|
||||
},
|
||||
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 {
|
||||
machine.int_reg.set_reg(inst.rd as usize, machine.fp_reg.get_reg(inst.rs1 as usize) as i64);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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))
|
||||
} else {
|
||||
self.int_reg.set_reg(inst.rd, self.int_reg.get_reg(inst.rs1) - self.int_reg.get_reg(inst.rs2))
|
||||
},
|
||||
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)
|
||||
} else {
|
||||
self.int_reg.set_reg(inst.rd, 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;
|
||||
if unsigned_reg1 < unsigned_reg2 {
|
||||
self.int_reg.set_reg(inst.rd, 1)
|
||||
} else {
|
||||
self.int_reg.set_reg(inst.rd, 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_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)},
|
||||
}
|
||||
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.shamt,
|
||||
RISCV_OPIW_SRW => (local_data >> inst.shamt) & if inst.funct7 == RISCV_OPIW_SRW_SRLIW { self.shiftmask[32 + inst.shamt as usize] as i64 } else { 1 },
|
||||
_ => Err(format!("Unreachable in op_instruction match! Instruction was {:?}", inst))?
|
||||
};
|
||||
self.int_reg.set_reg(inst.rd, result);
|
||||
Ok(MachineOk::Ok)
|
||||
}
|
||||
0
|
||||
|
||||
/// Executes simple RISC-V *w instructions on the machine
|
||||
fn opw_instruction(&mut self, inst: Instruction) -> Result<MachineOk, MachineError> {
|
||||
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;
|
||||
|
||||
// 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),
|
||||
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))?
|
||||
}
|
||||
} 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;
|
||||
// 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);
|
||||
} else { // SUBW
|
||||
self.int_reg.set_reg(inst.rd, 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)
|
||||
} else { // SRAW
|
||||
self.int_reg.set_reg(inst.rd, local_dataa >> (local_datab & 0x1f))
|
||||
},
|
||||
_ => Err(format!("Unreachable in opw_instruction match! Instruction was {:?}", inst))?
|
||||
}
|
||||
}
|
||||
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)
|
||||
};
|
||||
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);
|
||||
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);
|
||||
} 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))?
|
||||
}
|
||||
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);
|
||||
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)
|
||||
}
|
||||
|
||||
/// Executes RISC-V floating-point to integer conversion instruction on the machine.
|
||||
fn fp_fcvtw_instruction(&mut self, inst: Instruction) -> Result<MachineOk, MachineError> {
|
||||
if inst.rs2 == RISCV_FP_FCVTW_W {
|
||||
self.int_reg.set_reg(inst.rd, self.fp_reg.get_reg(inst.rs1) as i64)
|
||||
} else {
|
||||
self.int_reg.set_reg(inst.rd, (self.fp_reg.get_reg(inst.rs1) 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> {
|
||||
if inst.rs2 == RISCV_FP_FCVTS_W {
|
||||
self.fp_reg.set_reg(inst.rd, self.int_reg.get_reg(inst.rs1) as f32);
|
||||
} else {
|
||||
self.fp_reg.set_reg(inst.rd, (self.int_reg.get_reg(inst.rs1) 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> {
|
||||
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)
|
||||
} else {
|
||||
Err(format!("Unreachable in fp_fmvxfclass_instruction! Instruction was {:?}", inst))?
|
||||
}
|
||||
}
|
||||
|
||||
/// 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)
|
||||
}
|
||||
|
||||
/// 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]);
|
||||
@ -762,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()));
|
||||
}
|
||||
}
|
||||
|
@ -206,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);
|
||||
}
|
||||
|
@ -6,18 +6,15 @@ pub struct MMU <'a>{
|
||||
* 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>,
|
||||
numPhyPages : u64,
|
||||
pageSize : u64
|
||||
translationTable : Option<&'a mut TranslationTable>
|
||||
}
|
||||
|
||||
impl <'a>MMU <'_>{
|
||||
|
||||
fn create(numPhyPages: u64, pageSize: u64) -> MMU <'a>{
|
||||
MMU {
|
||||
translationTable : None,
|
||||
numPhyPages,
|
||||
pageSize
|
||||
fn create() -> MMU <'a>{
|
||||
|
||||
MMU{
|
||||
translationTable : None
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,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);
|
||||
@ -47,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;
|
||||
}
|
||||
}
|
||||
@ -66,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);
|
||||
@ -81,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;
|
||||
}
|
||||
}
|
||||
@ -91,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 {
|
||||
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) => {
|
||||
@ -107,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
|
||||
@ -116,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
|
||||
@ -132,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
|
||||
@ -150,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,14 +1,13 @@
|
||||
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)]
|
||||
@ -54,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
|
||||
|
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, Eq)]
|
||||
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]
|
||||
}
|
||||
}
|
||||
}
|
@ -22,7 +22,7 @@ impl TranslationTable {
|
||||
|
||||
let mut tmp_vec : Vec<PageTableEntry> = Vec::new();
|
||||
|
||||
for _i in 0..MaxVirtPages {
|
||||
for i in 0..MaxVirtPages {
|
||||
tmp_vec.push(PageTableEntry::create());
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ impl TranslationTable {
|
||||
//Assert a mettre dans chacune des fonctions suivantes
|
||||
|
||||
pub fn get_max_num_pages(&self) -> u64{
|
||||
self.maxNumPages
|
||||
return self.maxNumPages;
|
||||
}
|
||||
|
||||
pub fn set_physical_page(&mut self, vpn : u64, physical_page : i32){
|
||||
|
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,122 +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
|
||||
pub fn get_debug_configuration() -> Settings {
|
||||
let mut settings_map = Settings::new();
|
||||
settings_map.insert(MachineSettingKey::PageSize, 128);
|
||||
settings_map.insert(MachineSettingKey::NumPhysPages, 400);
|
||||
settings_map
|
||||
}
|
||||
|
||||
/// 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()
|
||||
}
|
||||
|
||||
/// 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, Eq, 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> {
|
||||
|
||||
/// 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(),
|
||||
}));
|
||||
|
||||
if !self.tail.is_null() {
|
||||
(*self.tail).next = new_tail;
|
||||
} else {
|
||||
self.head = new_tail;
|
||||
}
|
||||
|
||||
self.tail = new_tail;
|
||||
}
|
||||
/// Create an empty list
|
||||
pub fn new() -> Self {
|
||||
List { head: None }
|
||||
}
|
||||
|
||||
/// Retrieve and remove the item at the head of the list.
|
||||
/// Push an item at the end of the list
|
||||
pub fn push(&mut self, elem: T) {
|
||||
let new_node = Box::new(Node {
|
||||
elem: elem,
|
||||
next: self.head.take(),
|
||||
});
|
||||
|
||||
self.head = Some(new_node);
|
||||
}
|
||||
|
||||
/// 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;
|
||||
} else {
|
||||
self.head = (*current).next;
|
||||
}
|
||||
drop(Box::from_raw(current).elem);
|
||||
return true;
|
||||
} else {
|
||||
previous = current;
|
||||
current = (*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 {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
false
|
||||
while !tmp_list.is_empty() {
|
||||
self.push(tmp_list.pop().unwrap());
|
||||
}
|
||||
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,31 +143,34 @@ 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();
|
||||
&node.elem
|
||||
})
|
||||
}
|
||||
self.next.map(|node| {
|
||||
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();
|
||||
&mut node.elem
|
||||
})
|
||||
}
|
||||
|
||||
self.next.take().map(|node| {
|
||||
self.next = node.next.as_deref_mut();
|
||||
&mut node.elem
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -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,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 join.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,10 +0,0 @@
|
||||
#include "userlib/syscall.h"
|
||||
#include "userlib/libnachos.h"
|
||||
|
||||
int main() {
|
||||
n_printf("Hello World 1");
|
||||
n_printf("Hello World 2");
|
||||
n_printf("Hello World 3");
|
||||
n_printf("Hello World 4");
|
||||
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();
|
||||
|
||||
void consommateur();
|
||||
|
||||
int main() {
|
||||
svide = SemCreate("producteur", N);
|
||||
splein = SemCreate("consommateur", 0);
|
||||
ThreadId producteurTh = threadCreate("producteur", producteur);
|
||||
ThreadId consommateurTh = threadCreate("consommateur", consommateur);
|
||||
Join(producteurTh);
|
||||
Join(consommateurTh);
|
||||
return 0;
|
||||
}
|
||||
|
||||
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() {
|
||||
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);
|
||||
n_printf("exploiter l'information : %d\n", info);
|
||||
}
|
||||
}
|
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