Compare commits
210 Commits
disk
...
fix-string
Author | SHA1 | Date | |
---|---|---|---|
3f51413038 | |||
e3654de298 | |||
98fe63f487 | |||
15a04fb9da | |||
86113da9d3 | |||
7be0c0accc | |||
7d29b92eba | |||
2884d5d479 | |||
c862c42e43 | |||
5000c28b97 | |||
5f8965b94d | |||
e0eb027aea | |||
6eeaa57647 | |||
bec0143a40 | |||
28cd0a9f6e | |||
43f023e0b0 | |||
02cdb5239b | |||
a211e93905 | |||
1055e6a0ac | |||
c278236d81 | |||
597ffa753a | |||
aba2fbc718 | |||
c33df28307 | |||
bb1d2383bb | |||
780ed4b461 | |||
5c66577989 | |||
fe519555cc | |||
8b13cc6ef6 | |||
5734e02b30 | |||
f55189f1fe | |||
efe00ffa26 | |||
73ac8d3083 | |||
1c4c51b0ba | |||
74a0960ec3 | |||
32c1237c26 | |||
35b2949243 | |||
ec2f50f7d3 | |||
3244584da9 | |||
232617c32e | |||
f144438490 | |||
05f72af035 | |||
6e6d3424f5 | |||
6c3655bd78 | |||
c419926a4c | |||
19356a36d9 | |||
35736821c0 | |||
752b70e448 | |||
21f3a72a3d | |||
134e2bd2cc | |||
e8629b1ebf | |||
0d70751279 | |||
355071a2f1 | |||
729eba656c | |||
afce2c66c9 | |||
a36e470ea1 | |||
6c19f66d62 | |||
66d6daf0b9 | |||
9cc57e7f03 | |||
2f0b9e6592 | |||
968137cc1b | |||
01ac29160b | |||
ffd9b92f24 | |||
b804117127 | |||
cfb58fd6b3 | |||
df4fd53c96 | |||
f79b63e930 | |||
f246e84f91 | |||
91f5c6054c | |||
586c077002 | |||
8ee7470dc5 | |||
02dd1f5ccf | |||
44cfb828fb | |||
7060098809 | |||
a151bef7ef | |||
77086155b5 | |||
41611b54e8 | |||
24919a9879 | |||
7179931224 | |||
cb25b09cff | |||
81302b67a1 | |||
5a5c5a9141 | |||
2756477e67 | |||
b96808b55f | |||
3685bb6590 | |||
69f91170f6 | |||
70d7893bed | |||
8470dcc04f | |||
8b3a3bebe7 | |||
743299fcdb | |||
87dcf71857 | |||
411caac86f | |||
24be35547e | |||
0fd2815a59 | |||
a001e45c3f | |||
99b0128cfe | |||
b8cba1abd7 | |||
0c3a254296 | |||
4480212ab0 | |||
2b10da1d7d | |||
bcc16dcec0 | |||
64c8104668 | |||
f3f88e9bee | |||
e7178f4490 | |||
45647a784c | |||
63c2f62a19 | |||
d1d3ae64a6 | |||
aeb5cacb4e | |||
793bf482fc | |||
72743ded87 | |||
9f937ff81f | |||
aa6d52fae7 | |||
d4f8ba2e32 | |||
aa5ba94842 | |||
4be02a2a03 | |||
025c62b243 | |||
2eee88c1d3 | |||
ba8e7fe205 | |||
f19515f6e8 | |||
57e3ef397b | |||
fc3237c4ad | |||
655bf9eab7 | |||
aef8d219d0 | |||
025ede6080 | |||
8c844c3e5c | |||
8239079130 | |||
0a744f0f94 | |||
443556b18a | |||
35c81e5269 | |||
453de4b704 | |||
b4b7eb69c2 | |||
5c7979b746 | |||
dc33c857a6 | |||
2981925401 | |||
8e81358e51 | |||
e117ec2132 | |||
06f6137852 | |||
8629d859e4 | |||
45a2ee952c | |||
2d241e4dd5 | |||
ac1f2287a2 | |||
15f3608b63 | |||
3fa3ce0e99 | |||
d77c2448e3 | |||
72f560f3ec | |||
703c8e5448 | |||
7dff3bcdd9 | |||
cfcdce750b | |||
2e41758a52 | |||
40039eca17 | |||
e170256c9b | |||
7a32aa503c | |||
c9792d1a1a | |||
2f986da7ae | |||
08ba0154f7 | |||
7f37965ed4 | |||
2162232199 | |||
ff93fab832 | |||
288703321e | |||
ba8e36ea90 | |||
939e23883e | |||
a8bbc13142 | |||
8ba63d38a3 | |||
e77e125f96 | |||
3dfeca4c42 | |||
651e03a446 | |||
7ed53261a0 | |||
4e90d9fef7 | |||
a2d5b22774 | |||
b33c31ef38 | |||
88e1921b3c | |||
7bdde70989 | |||
35fdb1e0b0 | |||
928628c305 | |||
228d58655a | |||
03cc8e17c6 | |||
c74c99499e | |||
bee0e8ce71 | |||
cc6aab7c3f | |||
21159d3d98 | |||
43de76bd72 | |||
87d90c394f | |||
c6ea3a0cb3 | |||
a223c14f36 | |||
cdb52bc9de | |||
5963759141 | |||
6d0477153b | |||
1118f1fa0f | |||
6edb88f337 | |||
df7c0af62b | |||
21a0da8f24 | |||
caddc445b8 | |||
ca9f458a7e | |||
1b44949842 | |||
6571838263 | |||
e0e92fea3a | |||
da37e0657c | |||
9ade363ca9 | |||
9d19f0630b | |||
34a72da4b4 | |||
b104bcc6da | |||
b9c329219a | |||
d3b2d0bac6 | |||
977cb2bf96 | |||
fa64d4314d | |||
6dd0cbcc87 | |||
b22b1dea21 | |||
7aa59c2314 | |||
7de9cf55cf | |||
1906ec836c | |||
abb97d17d5 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -2,3 +2,6 @@
|
|||||||
/.idea
|
/.idea
|
||||||
*.iml
|
*.iml
|
||||||
/*.txt
|
/*.txt
|
||||||
|
/.vscode
|
||||||
|
*.a
|
||||||
|
*.o
|
||||||
|
@ -1,27 +1,38 @@
|
|||||||
default:
|
default:
|
||||||
image: rust:latest
|
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
|
||||||
|
|
||||||
stages:
|
stages:
|
||||||
- build
|
|
||||||
- test
|
- test
|
||||||
|
|
||||||
build-job:
|
|
||||||
stage: build
|
|
||||||
script:
|
|
||||||
- echo "Compiling the code..."
|
|
||||||
- cargo build
|
|
||||||
- echo "Compile complete."
|
|
||||||
|
|
||||||
unit-test-job:
|
unit-test-job:
|
||||||
stage: test
|
stage: test
|
||||||
script:
|
script:
|
||||||
|
- echo "Compiling c files"
|
||||||
|
- make
|
||||||
- echo "Running unit tests..."
|
- echo "Running unit tests..."
|
||||||
- cargo test
|
- 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:
|
lint-test-job:
|
||||||
only:
|
only:
|
||||||
- merge_requests
|
refs:
|
||||||
|
- merge_requests
|
||||||
stage: test
|
stage: test
|
||||||
script:
|
script:
|
||||||
- echo "Linting code..."
|
- echo "Linting code..."
|
||||||
- cargo clippy
|
- rustup component add clippy
|
||||||
|
- cargo clippy -- -D warnings
|
||||||
|
377
Cargo.lock
generated
377
Cargo.lock
generated
@ -2,15 +2,386 @@
|
|||||||
# It is not intended for manual editing.
|
# It is not intended for manual editing.
|
||||||
version = 3
|
version = 3
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "0.2.6"
|
||||||
|
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",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bitflags"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "burritos"
|
name = "burritos"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"cc",
|
||||||
|
"clap",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[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"
|
||||||
|
version = "1.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a855d4a1978dc52fb0536a04d384c2c0c1aa273597f08b77c8c4d3b2eec6037f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "concolor-query"
|
||||||
|
version = "0.3.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "88d11d52c3d7ca2e6d0040212be9e4dbbcd78b6447f535b6b561f449427944cf"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys 0.45.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "50d6a0976c999d473fe89ad888d5a284e55366d9dc9038b1ba2aa15128c4afa0"
|
||||||
|
dependencies = [
|
||||||
|
"errno-dragonfly",
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.45.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "errno-dragonfly"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf"
|
||||||
|
dependencies = [
|
||||||
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "heck"
|
||||||
version = "0.2.139"
|
version = "0.4.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79"
|
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hermit-abi"
|
||||||
|
version = "0.3.1"
|
||||||
|
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"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.48.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is-terminal"
|
||||||
|
version = "0.4.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "256017f749ab3117e93acb91063009e1f1bb56d03965b14c2c8df4eb02c524d8"
|
||||||
|
dependencies = [
|
||||||
|
"hermit-abi",
|
||||||
|
"io-lifetimes",
|
||||||
|
"rustix",
|
||||||
|
"windows-sys 0.45.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "libc"
|
||||||
|
version = "0.2.141"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "linux-raw-sys"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell"
|
||||||
|
version = "1.17.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.56"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.26"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix"
|
||||||
|
version = "0.37.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2aae838e49b3d63e9274e1c01833cc8139d3fec468c3b84688c628f44b1ae11d"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags",
|
||||||
|
"errno",
|
||||||
|
"io-lifetimes",
|
||||||
|
"libc",
|
||||||
|
"linux-raw-sys",
|
||||||
|
"windows-sys 0.45.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4c9da457c5285ac1f936ebd076af6dac17a61cfe7826f2076b4d015cf47bc8ec"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
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",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-targets"
|
||||||
|
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",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows_aarch64_gnullvm"
|
||||||
|
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"
|
||||||
|
@ -1,7 +1,14 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "burritos"
|
name = "burritos"
|
||||||
|
rust-version = "1.64"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
|
[registries.crates-io]
|
||||||
|
protocol = "sparse"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
cc = "1.0"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libc = { version = "0.2.139", features = ["extra_traits"] }
|
clap = { version = "4.2.1", features = ["derive"] }
|
||||||
|
31
Makefile
Normal file
31
Makefile
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
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
|
||||||
|
|
@ -1,5 +1,11 @@
|
|||||||
include $(TOPDIR)/Makefile.config
|
include $(TOPDIR)/Makefile.config
|
||||||
|
|
||||||
USERLIB = $(TOPDIR)/userlib
|
USERLIB = $(TOPDIR)/userlib
|
||||||
|
|
||||||
|
AS = $(RISCV_AS) -c
|
||||||
|
GCC = $(RISCV_GCC)
|
||||||
|
LD = $(RISCV_LD)
|
||||||
|
|
||||||
INCPATH += -I$(TOPDIR) -I$(USERLIB)
|
INCPATH += -I$(TOPDIR) -I$(USERLIB)
|
||||||
LDFLAGS = $(RISCV_LDFLAGS) -T $(USERLIB)/ldscript.lds
|
LDFLAGS = $(RISCV_LDFLAGS) -T $(USERLIB)/ldscript.lds
|
||||||
ASFLAGS = $(RISCV_ASFLAGS) $(INCPATH)
|
ASFLAGS = $(RISCV_ASFLAGS) $(INCPATH)
|
||||||
@ -7,16 +13,19 @@ CFLAGS = $(RISCV_CFLAGS) $(INCPATH)
|
|||||||
|
|
||||||
# Rules
|
# Rules
|
||||||
%.o: %.s
|
%.o: %.s
|
||||||
$(RISCV_AS) $(ASFLAGS) -c $<
|
$(AS) $(ASFLAGS) -c $<
|
||||||
|
|
||||||
%.o: %.c
|
%.o: %.c
|
||||||
$(RISCV_GCC) $(CFLAGS) -c $<
|
$(GCC) $(CFLAGS) -c $<
|
||||||
|
|
||||||
|
%.a: %.o
|
||||||
|
$(AR) $(ARFLAGS) $@ $<
|
||||||
|
|
||||||
%.dump: %.o
|
%.dump: %.o
|
||||||
$(RISCV_OBJCOPY) -j .text -O $(DUMP_FORMAT) $< $@
|
$(RISCV_OBJCOPY) -j .text -O $(DUMP_FORMAT) $< $@
|
||||||
|
|
||||||
%.guac: %.o
|
%.guac: %.o
|
||||||
$(RISCV_LD) $(LDFLAGS) $+ -o $@
|
$(LD) $(LDFLAGS) $+ -o $@
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
.%.d: %.s
|
.%.d: %.s
|
||||||
@ -31,6 +40,9 @@ CFLAGS = $(RISCV_CFLAGS) $(INCPATH)
|
|||||||
| sed '\''s/\($*\)\.o[ :]*/\1.o $@ : /g'\'' > $@; \
|
| sed '\''s/\($*\)\.o[ :]*/\1.o $@ : /g'\'' > $@; \
|
||||||
[ -s $@ ] || rm -f $@'
|
[ -s $@ ] || rm -f $@'
|
||||||
|
|
||||||
|
$(PROGRAMS):
|
||||||
|
$(LD) $(LDFLAGS) $+ -o $@
|
||||||
|
|
||||||
# Targets
|
# Targets
|
||||||
#clean:
|
#clean:
|
||||||
# rm -rf *.o 2> /dev/null
|
# rm -rf *.o 2> /dev/null
|
40
burritos.cfg
Executable file
40
burritos.cfg
Executable file
@ -0,0 +1,40 @@
|
|||||||
|
##################################################
|
||||||
|
# BurritOS configuration file
|
||||||
|
##################################################
|
||||||
|
|
||||||
|
NumPhysPages = 40000000
|
||||||
|
UserStackSize = 4096
|
||||||
|
MaxFileNameSize = 256
|
||||||
|
NumDirEntries = 30
|
||||||
|
NumPortLoc = 32009
|
||||||
|
NumPortDist = 32010
|
||||||
|
ProcessorFrequency = 100
|
||||||
|
SectorSize = 128
|
||||||
|
PageSize = 128
|
||||||
|
MaxVirtPages = 200000
|
||||||
|
|
||||||
|
# String values
|
||||||
|
###############
|
||||||
|
# WARNING: Copying can be very slow
|
||||||
|
# because the system transferts data
|
||||||
|
# by 10 byte chunks. The transfer file
|
||||||
|
# can be set by changing the transfersize
|
||||||
|
# constant in fstest.rs.
|
||||||
|
|
||||||
|
TargetMachineName = localhost
|
||||||
|
FileToCopy = test/halt /halt
|
||||||
|
FileToCopy = test/hello /hello
|
||||||
|
FileToCopy = test/sort /sort
|
||||||
|
FileToCopy = test/shell /shell
|
||||||
|
|
||||||
|
# Boolean values
|
||||||
|
################
|
||||||
|
UseACIA = None
|
||||||
|
PrintStat = 1
|
||||||
|
FormatDisk = 1
|
||||||
|
ListDir = 1
|
||||||
|
PrintFileSyst = 0
|
||||||
|
|
||||||
|
ProgramToRun = /sort
|
||||||
|
|
||||||
|
|
@ -1,143 +0,0 @@
|
|||||||
//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 */
|
|
406
src/kernel/exception.rs
Normal file
406
src/kernel/exception.rs
Normal file
@ -0,0 +1,406 @@
|
|||||||
|
use std::{cell::RefCell, rc::Rc};
|
||||||
|
|
||||||
|
use crate::{simulator::{machine::{ExceptionType, Machine}, error::{MachineOk, MachineError}}};
|
||||||
|
use crate::kernel::synch::{Lock, Semaphore};
|
||||||
|
|
||||||
|
use super::{system::System, thread::Thread};
|
||||||
|
|
||||||
|
/// The halt system call. Stops Burritos.
|
||||||
|
pub const SC_SHUTDOWN: u8 = 0;
|
||||||
|
/// The exit system call
|
||||||
|
///
|
||||||
|
/// Ends the calling thread
|
||||||
|
pub const SC_EXIT: u8 = 1;
|
||||||
|
/// The exec system call
|
||||||
|
///
|
||||||
|
/// Creates a new process (thread+address space)
|
||||||
|
pub const SC_EXEC: u8 = 2;
|
||||||
|
/// The join system call
|
||||||
|
///
|
||||||
|
/// Wait for the thread idThread to finish
|
||||||
|
pub const SC_JOIN: u8 = 3;
|
||||||
|
/// The create system call
|
||||||
|
///
|
||||||
|
/// Create a new file in nachos file system
|
||||||
|
pub const SC_CREATE: u8 = 4;
|
||||||
|
/// The open system call
|
||||||
|
///
|
||||||
|
/// Opens a file and returns an openfile identifier
|
||||||
|
pub const SC_OPEN: u8 = 5;
|
||||||
|
/// The read system call
|
||||||
|
///
|
||||||
|
/// Read in a file or the console
|
||||||
|
pub const SC_READ: u8 = 6;
|
||||||
|
/// The write system call
|
||||||
|
///
|
||||||
|
/// Write in a file or at the console
|
||||||
|
pub const SC_WRITE: u8 = 7;
|
||||||
|
/// Seek to a given position in an opened file
|
||||||
|
pub const SC_SEEK: u8 = 8;
|
||||||
|
/// The close system call
|
||||||
|
///
|
||||||
|
/// Close a file
|
||||||
|
pub const SC_CLOSE: u8 = 9;
|
||||||
|
/// The newThread system call
|
||||||
|
///
|
||||||
|
/// Create a new thread in the same address space
|
||||||
|
pub const SC_NEW_THREAD: u8 = 10;
|
||||||
|
/// The Yield System call
|
||||||
|
///
|
||||||
|
/// Relinquish the CPU if any other thread is runnable
|
||||||
|
pub const SC_YIELD: u8 = 11;
|
||||||
|
/// the PError system call
|
||||||
|
///
|
||||||
|
/// print the last error message
|
||||||
|
pub const SC_PERROR: u8 = 12;
|
||||||
|
/// carry out P() on the semaphore
|
||||||
|
pub const SC_P: u8 = 13;
|
||||||
|
/// carry out V() on the semaphore
|
||||||
|
pub const SC_V: u8 = 14;
|
||||||
|
/// create a semaphore and add it in g_objects_addrs
|
||||||
|
pub const SC_SEM_CREATE: u8 = 15;
|
||||||
|
/// destroy the semaphore corresponding to the id
|
||||||
|
pub const SC_SEM_DESTROY: u8 = 16;
|
||||||
|
/// create a lock and add it to g_object_addrs
|
||||||
|
pub const SC_LOCK_CREATE: u8 = 17;
|
||||||
|
/// destroy the lock corresponding to the id
|
||||||
|
pub const SC_LOCK_DESTROY: u8 = 18;
|
||||||
|
/// carry out acquire() on the lock
|
||||||
|
pub const SC_LOCK_ACQUIRE: u8 = 19;
|
||||||
|
/// carry out release() on the lock
|
||||||
|
pub const SC_LOCK_RELEASE: u8 = 20;
|
||||||
|
/// create a condition variable and add it to g_object_addrs
|
||||||
|
pub const SC_COND_CREATE: u8 = 21;
|
||||||
|
/// destroy the condition variable corresponding to the id
|
||||||
|
pub const SC_COND_DESTROY: u8 = 22;
|
||||||
|
/// carry out wait() on the condition
|
||||||
|
pub const SC_COND_WAIT: u8 = 23;
|
||||||
|
/// carry out signal() on the condition
|
||||||
|
pub const SC_COND_SIGNAL: u8 = 24;
|
||||||
|
/// carry out broadcast() on the condition
|
||||||
|
pub const SC_COND_BROADCAST: u8 = 25;
|
||||||
|
/// the TtySend system call
|
||||||
|
///
|
||||||
|
/// Sends some char by the serial line emulated
|
||||||
|
pub const SC_TTY_SEND: u8 = 26;
|
||||||
|
/// the TtyReceive system call
|
||||||
|
///
|
||||||
|
/// read some char on the serial line
|
||||||
|
pub const SC_TTY_RECEIVE: u8 = 27;
|
||||||
|
/// the Mkdir system call
|
||||||
|
///
|
||||||
|
/// make a new directory in the file system
|
||||||
|
pub const SC_MKDIR: u8 = 28;
|
||||||
|
/// the Rmdir system call
|
||||||
|
///
|
||||||
|
/// remove a directory from the file system
|
||||||
|
pub const SC_RMDIR: u8 = 29;
|
||||||
|
/// The Remove system call
|
||||||
|
///
|
||||||
|
/// Remove a file from the file system
|
||||||
|
pub const SC_REMOVE: u8 = 30;
|
||||||
|
/// The FSList system call
|
||||||
|
///
|
||||||
|
/// Lists all the file and directories in the filesystem
|
||||||
|
pub const SC_FSLIST: u8 = 31;
|
||||||
|
// The systime system call. Gets the system time
|
||||||
|
pub const SC_SYS_TIME: u8 = 32;
|
||||||
|
/// Map a file in memory
|
||||||
|
pub const SC_MMAP: u8 = 33;
|
||||||
|
/// Behaviour undefined and currently unused
|
||||||
|
pub const SC_DEBUG: u8 = 34;
|
||||||
|
|
||||||
|
pub const CONSOLE_OUTPUT: u8 = 1;
|
||||||
|
|
||||||
|
// todo : returns new types, not just machine errors and machine ok
|
||||||
|
pub fn call(exception: &ExceptionType, machine: &mut Machine, system: &mut System) -> Result<MachineOk, MachineError> {
|
||||||
|
|
||||||
|
match exception {
|
||||||
|
ExceptionType::NoException => Err("No Exception no yet implemented")?,
|
||||||
|
ExceptionType::SyscallException => syscall(machine, system),
|
||||||
|
ExceptionType::PagefaultException => Err("Page Fault Exception not yet implemented")?,
|
||||||
|
ExceptionType::ReadOnlyException => Err("Read Only Exception not yet implemented")?,
|
||||||
|
ExceptionType::BusErrorException => Err("Bus Error Exception not yet implemented")?,
|
||||||
|
ExceptionType::AddressErrorException => Err("AddressErrorException not yet implemented")?,
|
||||||
|
ExceptionType::OverflowException => Err("OverflowException not yet implemented")?,
|
||||||
|
ExceptionType::IllegalInstrException => Err("IllegalInstrException not yet implemented")?,
|
||||||
|
ExceptionType::NumExceptionTypes => Err("NumExceptionTypes not yet implemented")?,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn syscall(machine: &mut Machine, system: &mut System) -> Result<MachineOk, MachineError> {
|
||||||
|
let call_type = machine.read_int_register(17) as u8;
|
||||||
|
|
||||||
|
match call_type {
|
||||||
|
SC_SHUTDOWN => Ok(MachineOk::Shutdown),
|
||||||
|
SC_EXIT => {
|
||||||
|
let th = match &system.get_thread_manager().g_current_thread {
|
||||||
|
Some(th) => th.clone(),
|
||||||
|
None => Err("Current thread is None")?
|
||||||
|
};
|
||||||
|
let code = machine.read_int_register(10);
|
||||||
|
system.get_thread_manager().thread_finish(machine, th, code);
|
||||||
|
Ok(MachineOk::Ok)
|
||||||
|
},
|
||||||
|
SC_EXEC => todo!(),
|
||||||
|
SC_JOIN => sc_join(machine, system),
|
||||||
|
SC_CREATE => todo!(),
|
||||||
|
SC_OPEN => todo!(),
|
||||||
|
SC_READ => todo!(),
|
||||||
|
SC_WRITE => {
|
||||||
|
|
||||||
|
let address = machine.read_int_register(10);
|
||||||
|
let size = machine.read_int_register(11);
|
||||||
|
// openfileid or 1 (console)
|
||||||
|
let f = machine.read_int_register(12);
|
||||||
|
|
||||||
|
// load buffer
|
||||||
|
let mut buffer = String::new();
|
||||||
|
let mut val: [u8; 4] = [0; 4];
|
||||||
|
for (j, elem) in val.iter_mut().enumerate() {
|
||||||
|
*elem = machine.read_memory(1, address as usize + j) as u8;
|
||||||
|
}
|
||||||
|
for i in 0..size {
|
||||||
|
buffer.push((machine.read_memory(1, (address + i) as usize)) as u8 as char);
|
||||||
|
}
|
||||||
|
|
||||||
|
if f as u8 == CONSOLE_OUTPUT {
|
||||||
|
print!("{}", buffer); // todo replace with console driver in the future
|
||||||
|
Ok(MachineOk::Ok)
|
||||||
|
} else {
|
||||||
|
Err("SC_WRITE to file is not yet implemented")?
|
||||||
|
}
|
||||||
|
},
|
||||||
|
SC_SEEK => todo!(),
|
||||||
|
SC_CLOSE => todo!(),
|
||||||
|
SC_NEW_THREAD => sc_new_thread(machine, system),
|
||||||
|
SC_YIELD => todo!(),
|
||||||
|
SC_PERROR => todo!(),
|
||||||
|
SC_P => sc_p(machine, system),
|
||||||
|
SC_V => sc_v(machine, system),
|
||||||
|
SC_SEM_CREATE => sc_sem_create(machine, system),
|
||||||
|
SC_SEM_DESTROY => sc_sem_remove(machine, system),
|
||||||
|
SC_LOCK_CREATE => sc_lock_create(machine, system),
|
||||||
|
SC_LOCK_DESTROY => sc_lock_destroy(machine, system),
|
||||||
|
SC_LOCK_ACQUIRE => sc_lock_acquire(machine, system),
|
||||||
|
SC_LOCK_RELEASE => 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(format!("Initial_count < 0"))?,
|
||||||
|
false => {
|
||||||
|
let id = system.get_thread_manager().get_obj_addrs().add_semaphore(Semaphore::new(initial_count));
|
||||||
|
machine.write_int_register(10, id as i64);
|
||||||
|
Ok(MachineOk::Ok)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sc_sem_remove(machine: &mut Machine, system: &mut System) -> Result<MachineOk, MachineError>{
|
||||||
|
let id = machine.read_int_register(10) as i32;
|
||||||
|
system.get_thread_manager().get_obj_addrs().remove_semaphore(id);
|
||||||
|
Ok(MachineOk::Ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sc_new_thread(machine: &mut Machine, system: &mut System) -> Result<MachineOk, MachineError> {
|
||||||
|
// Get the address of the string for the name of the thread
|
||||||
|
let name_addr = machine.read_int_register(10) as usize;
|
||||||
|
// Get the pointer of the function to be executed in the new thread
|
||||||
|
let func = machine.read_int_register(11);
|
||||||
|
// Get function parameters
|
||||||
|
let args = machine.read_int_register(12);
|
||||||
|
// get string name
|
||||||
|
let name_size = get_length_param(name_addr, machine);
|
||||||
|
let thread_name: String = get_string_param(name_addr, name_size, machine).into_iter().collect();
|
||||||
|
|
||||||
|
let n_thread = Thread::new(thread_name.as_str());
|
||||||
|
let n_thread = Rc::new(RefCell::new(n_thread));
|
||||||
|
let tid = system.get_thread_manager().get_obj_addrs().add_thread(Rc::clone(&n_thread));
|
||||||
|
let current_thread = match system.get_thread_manager().get_g_current_thread() {
|
||||||
|
Some(th) => {
|
||||||
|
Rc::clone(th)
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
return Err("Current thread is none")?;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let current_thread = current_thread.borrow_mut();
|
||||||
|
if let Some(process) = current_thread.get_process_owner() {
|
||||||
|
let sp_max = system.get_thread_manager().get_sp_max() + machine.user_stack_size;
|
||||||
|
system.get_thread_manager().set_sp_max(sp_max);
|
||||||
|
system.get_thread_manager().start_thread(n_thread, Rc::clone(&process), func as u64, sp_max, args);
|
||||||
|
// TODO changé la valeur de sp quand on supportera les addresses virtuels
|
||||||
|
machine.write_int_register(10, tid as i64);
|
||||||
|
Ok(MachineOk::Ok)
|
||||||
|
} else {
|
||||||
|
return Err("Process owner of current thread is none")?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sc_join(machine: &mut Machine, system: &mut System) -> Result<MachineOk, MachineError> {
|
||||||
|
let tid = machine.read_int_register(10);
|
||||||
|
let p_thread = system.get_thread_manager().get_obj_addrs().search_thread(tid as i32);
|
||||||
|
match p_thread {
|
||||||
|
Some(waiting_for) => {
|
||||||
|
let rc_waiting_for = Rc::clone(waiting_for);
|
||||||
|
if let Some(current_thread) = system.get_thread_manager().get_g_current_thread() {
|
||||||
|
let rc_curr = Rc::clone(current_thread);
|
||||||
|
system.get_thread_manager().thread_join(machine, rc_curr, rc_waiting_for);
|
||||||
|
|
||||||
|
Ok(MachineOk::Ok)
|
||||||
|
} else {
|
||||||
|
Err("Current should not be None")?
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
// Thread already terminated (type set to INVALID_TYPE) or call on an object
|
||||||
|
// that is not a thread
|
||||||
|
// Exit with no error code since we cannot separate the two cases
|
||||||
|
Ok(MachineOk::Ok)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_length_param(addr: usize, machine: & Machine) -> usize {
|
||||||
|
let mut i = 0;
|
||||||
|
let mut c = 1;
|
||||||
|
while c != 0 {
|
||||||
|
c = machine.read_memory(1, addr + i);
|
||||||
|
i += 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
i + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_string_param(addr: usize, maxlen: usize, machine: &Machine) -> Vec<char> {
|
||||||
|
let mut dest = Vec::with_capacity(maxlen);
|
||||||
|
|
||||||
|
let mut i: usize = 0;
|
||||||
|
let mut c = 1;
|
||||||
|
|
||||||
|
while c != 0 && i < maxlen {
|
||||||
|
c = machine.read_memory(1, addr + i) as u8;
|
||||||
|
dest.push(c as char);
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::kernel::exception::{SC_SHUTDOWN, SC_WRITE};
|
||||||
|
use crate::kernel::system::System;
|
||||||
|
use crate::simulator::machine::Machine;
|
||||||
|
use crate::utility::cfg::get_debug_configuration;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_sc_shutdown() {
|
||||||
|
let mut machine = Machine::new(true, get_debug_configuration());
|
||||||
|
machine.write_int_register(17, SC_SHUTDOWN as i64); // Set type to shutdown
|
||||||
|
// let ecall = Instruction::new(0b000000000000_00000_000_00000_1110011);
|
||||||
|
let insts: [u8; 4] = 0b000000000000_00000_000_00000_1110011_u32.to_le_bytes();
|
||||||
|
machine.write_memory(1, 0, insts[0] as u64);
|
||||||
|
machine.write_memory(1, 1, insts[1] as u64);
|
||||||
|
machine.write_memory(1, 2, insts[2] as u64);
|
||||||
|
machine.write_memory(1, 3, insts[3] as u64); // ecall
|
||||||
|
// machine.write_memory(4, 0, 0b000000000000_00000_000_00000_1110011_u64.to_be()); // ecall
|
||||||
|
let insts: [u8; 4] = 0b000000001010_00000_000_00001_0010011_u32.to_le_bytes();
|
||||||
|
machine.write_memory(1, 4, insts[0] as u64);
|
||||||
|
machine.write_memory(1, 5, insts[1] as u64);
|
||||||
|
machine.write_memory(1, 6, insts[2] as u64);
|
||||||
|
machine.write_memory(1, 7, insts[3] as u64); // r1 <- 10
|
||||||
|
// machine.write_memory(4, 4, 0b000000001010_00000_000_00001_0010011_u64.to_be()); // r1 <- 10
|
||||||
|
let mut system = System::new(true);
|
||||||
|
machine.run(&mut system);
|
||||||
|
// If the machine was stopped with no error, the shutdown worked
|
||||||
|
assert_ne!(machine.read_int_register(1), 10); // Check if the next instruction was executed
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test print HELLO in the console
|
||||||
|
#[test]
|
||||||
|
#[ignore]
|
||||||
|
fn test_sc_print() {
|
||||||
|
let mut machine = Machine::new(true, get_debug_configuration());
|
||||||
|
|
||||||
|
let _address = machine.read_int_register(10);
|
||||||
|
// Write string 'HELLO' in memory
|
||||||
|
machine.write_memory(1, 4000, 72);
|
||||||
|
machine.write_memory(1, 4001, 69);
|
||||||
|
machine.write_memory(1, 4002, 76);
|
||||||
|
machine.write_memory(1, 4003, 76);
|
||||||
|
machine.write_memory(1, 4004, 79);
|
||||||
|
|
||||||
|
|
||||||
|
machine.write_int_register(10, 4000); // String address
|
||||||
|
machine.write_int_register(11, 5); // String size
|
||||||
|
machine.write_int_register(12, 1); // Console output
|
||||||
|
|
||||||
|
machine.write_memory(4, 0, 0b000000000000_00000_000_00000_1110011); // ecall
|
||||||
|
machine.write_int_register(17, SC_WRITE as i64); // Set type to write
|
||||||
|
|
||||||
|
machine.write_memory(4, 4, 0b000000000000_00000_000_10001_0010011); // r17 <- SC_SHUTDOWN
|
||||||
|
machine.write_memory(4, 8, 0b000000000000_00000_000_00000_1110011); // ecall
|
||||||
|
|
||||||
|
let mut system = System::new(true);
|
||||||
|
machine.run(&mut system);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
|
#![allow(unused, clippy::missing_docs_in_private_items)]
|
||||||
/// Error enum, use it with Result<YourSucessStruct, **ErrorCode**>
|
/// Error enum, use it with Result<YourSucessStruct, **ErrorCode**>
|
||||||
pub enum ErrorCode {
|
pub enum ErrorCode {
|
||||||
IncError,
|
IncError,
|
||||||
@ -27,5 +27,5 @@ pub enum ErrorCode {
|
|||||||
WrongFileEndianess,
|
WrongFileEndianess,
|
||||||
NoAcia,
|
NoAcia,
|
||||||
|
|
||||||
NUMMSGERROR /* Must always be last */
|
NumMsgError /* Must always be last */
|
||||||
}
|
}
|
@ -1,8 +1,7 @@
|
|||||||
mod process;
|
pub mod process;
|
||||||
pub mod thread;
|
pub mod thread;
|
||||||
pub mod scheduler;
|
|
||||||
pub mod mgerror;
|
pub mod mgerror;
|
||||||
pub mod system;
|
pub mod system;
|
||||||
mod ucontext;
|
pub mod synch;
|
||||||
mod synch;
|
mod thread_manager;
|
||||||
mod thread_manager;
|
pub mod exception;
|
@ -1,74 +0,0 @@
|
|||||||
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,21 +4,16 @@ use crate::simulator::interrupt::InterruptStatus::InterruptOff;
|
|||||||
use crate::simulator::machine::Machine;
|
use crate::simulator::machine::Machine;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
|
||||||
use super::scheduler::Scheduler;
|
|
||||||
use super::system::System;
|
|
||||||
use super::thread_manager::ThreadManager;
|
use super::thread_manager::ThreadManager;
|
||||||
|
|
||||||
/// Structure of a Semaphore used for synchronisation
|
/// Structure of a Semaphore used for synchronisation
|
||||||
|
#[derive(PartialEq)]
|
||||||
pub struct Semaphore {
|
pub struct Semaphore {
|
||||||
|
|
||||||
/// Counter of simultanous Semaphore
|
/// Counter of simultanous Semaphore
|
||||||
counter:i32,
|
pub counter:i32,
|
||||||
/// QUeue of Semaphore waiting to be exucated
|
/// QUeue of Semaphore waiting to be exucated
|
||||||
waiting_queue:List<Rc<RefCell<Thread>>>,
|
pub waiting_queue:List<Rc<RefCell<Thread>>>,
|
||||||
/// Thread manager which managing threads
|
|
||||||
thread_manager: Rc<RefCell<ThreadManager>>
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,63 +24,25 @@ impl Semaphore {
|
|||||||
/// ### Parameters
|
/// ### Parameters
|
||||||
/// - *counter* initial value of counter
|
/// - *counter* initial value of counter
|
||||||
/// - *thread_manager* Thread manager which managing threads
|
/// - *thread_manager* Thread manager which managing threads
|
||||||
pub fn new(counter: i32, thread_manager: Rc<RefCell<ThreadManager>>) -> Semaphore{
|
pub fn new(counter: i32) -> Semaphore{
|
||||||
Semaphore { counter, waiting_queue: List::new(), thread_manager}
|
Semaphore { counter, waiting_queue: List::default() }
|
||||||
}
|
|
||||||
|
|
||||||
/// 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
|
/// Lock used for synchronisation, can be interpreted has a Semaphore with a
|
||||||
/// counter of 1
|
/// counter of 1
|
||||||
/// It's used for critical parts
|
/// It's used for critical parts
|
||||||
pub struct Lock{
|
#[derive(PartialEq)]
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Lock {
|
||||||
|
|
||||||
/// Thread owning the lock
|
/// Thread owning the lock
|
||||||
owner: Option<Rc<RefCell<Thread>>>,
|
pub owner: Option<Rc<RefCell<Thread>>>,
|
||||||
/// The queue of threads waiting for execution
|
/// The queue of threads waiting for execution
|
||||||
waiting_queue:List<Rc<RefCell<Thread>>>,
|
pub 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
|
/// A boolean definig if the lock is free or not
|
||||||
free: bool
|
pub free: bool
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,8 +53,8 @@ impl Lock {
|
|||||||
///
|
///
|
||||||
/// ### Parameters
|
/// ### Parameters
|
||||||
/// - **thread_manager** Thread manager which managing threads
|
/// - **thread_manager** Thread manager which managing threads
|
||||||
pub fn new(thread_manager: Rc<RefCell<ThreadManager>>) -> Lock {
|
pub fn new() -> Lock {
|
||||||
Lock { owner: None, waiting_queue: List::new(), thread_manager, free: true }
|
Lock { owner: None, waiting_queue: List::default(), free: true }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wait until the lock become free. Checking the
|
/// Wait until the lock become free. Checking the
|
||||||
@ -111,23 +68,28 @@ impl Lock {
|
|||||||
/// ### Parameters
|
/// ### Parameters
|
||||||
/// - **current_thread** the current thread
|
/// - **current_thread** the current thread
|
||||||
/// - **machine** the machine where the threads are executed
|
/// - **machine** the machine where the threads are executed
|
||||||
pub fn acquire(&mut self, current_thread: Option<Rc<RefCell<Thread>>>, system: Rc<RefCell<System>>) {
|
pub fn acquire(&mut self, machine: &mut Machine, thread_manager: &mut ThreadManager) {
|
||||||
let old_status = system.borrow_mut().get_g_machine().borrow_mut().interrupt.set_status(InterruptOff);
|
let old_status = machine.interrupt.set_status(InterruptOff);
|
||||||
|
|
||||||
if self.free {
|
if self.free {
|
||||||
self.free = false;
|
self.free = false;
|
||||||
self.owner = current_thread;
|
self.owner = Option::Some(match thread_manager.get_g_current_thread() {
|
||||||
} else {
|
Some(th) => {
|
||||||
match current_thread {
|
Rc::clone(&th)
|
||||||
Some(x) => {
|
|
||||||
self.waiting_queue.push(Rc::clone(&x));
|
|
||||||
self.thread_manager.borrow_mut().thread_sleep(x)
|
|
||||||
},
|
},
|
||||||
None => ()
|
None => unreachable!()
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
match thread_manager.get_g_current_thread() {
|
||||||
|
Some(x) => {
|
||||||
|
let x = Rc::clone(&x);
|
||||||
|
self.waiting_queue.push(Rc::clone(&x));
|
||||||
|
thread_manager.thread_sleep(machine, Rc::clone(&x));
|
||||||
|
},
|
||||||
|
None => unreachable!("Current thread should not be None")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
system.borrow_mut().get_g_machine().borrow_mut().interrupt.set_status(old_status);
|
machine.interrupt.set_status(old_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wake up a waiter if necessary, or release it if no thread is waiting.
|
/// Wake up a waiter if necessary, or release it if no thread is waiting.
|
||||||
@ -139,43 +101,53 @@ impl Lock {
|
|||||||
/// ### Parameters
|
/// ### Parameters
|
||||||
/// - **machine** the machine where the code is executed
|
/// - **machine** the machine where the code is executed
|
||||||
/// - **scheduler** the scheduler which determine which thread to execute
|
/// - **scheduler** the scheduler which determine which thread to execute
|
||||||
pub fn release(&mut self, system: Rc<RefCell<System>>, current_thread: Rc<RefCell<Thread>>) {
|
pub fn release(&mut self, machine: &mut Machine, thread_manager: &mut ThreadManager) {
|
||||||
let old_status = system.borrow_mut().get_g_machine().borrow_mut().interrupt.set_status(InterruptOff);
|
let old_status = machine.interrupt.set_status(InterruptOff);
|
||||||
|
|
||||||
if self.held_by_current_thread(current_thread) {
|
match thread_manager.get_g_current_thread() {
|
||||||
if self.waiting_queue.peek() != None {
|
Some(_) => {
|
||||||
self.owner = Some(self.waiting_queue.pop().unwrap());
|
if self.held_by_current_thread(thread_manager) {
|
||||||
let sys = system.borrow_mut();
|
match self.waiting_queue.pop() {
|
||||||
let tm = sys.get_thread_manager();
|
Some(thread) => {
|
||||||
let scheduler = &mut tm.borrow_mut().g_scheduler;
|
self.owner = Some(thread);
|
||||||
match &self.owner {
|
match &self.owner {
|
||||||
Some(x) => scheduler.ready_to_run(Rc::clone(&x)),
|
Some(x) => thread_manager.ready_to_run(Rc::clone(&x)),
|
||||||
None => ()
|
None => ()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
self.free = true;
|
||||||
|
self.owner = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
self.free = true;
|
|
||||||
self.owner = None;
|
|
||||||
}
|
}
|
||||||
|
None => ()
|
||||||
}
|
}
|
||||||
|
|
||||||
system.borrow_mut().get_g_machine().borrow_mut().interrupt.set_status(old_status);
|
machine.interrupt.set_status(old_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn held_by_current_thread(&mut self, current_thread: Rc<RefCell<Thread>>) -> bool {
|
/// 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 {
|
||||||
match &self.owner {
|
match &self.owner {
|
||||||
Some(x) => Rc::ptr_eq(&x, ¤t_thread),
|
Some(x) =>
|
||||||
|
match thread_manager.get_g_current_thread() {
|
||||||
|
Some(thread) => Rc::ptr_eq(x, thread),
|
||||||
|
None => false
|
||||||
|
}
|
||||||
None => false
|
None => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Structure of a condition used for synchronisation
|
/// Structure of a condition used for synchronisation
|
||||||
|
#[allow(unused)] // -> No enough time to implement it
|
||||||
pub struct Condition{
|
pub struct Condition{
|
||||||
|
|
||||||
/// The queue of threads waiting for execution
|
/// The queue of threads waiting for execution
|
||||||
waiting_queue:List<Rc<RefCell<Thread>>>,
|
waiting_queue:List<Rc<RefCell<Thread>>>,
|
||||||
/// Thread manager which managing threads
|
|
||||||
thread_manager: Rc<RefCell<ThreadManager>>,
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,8 +157,9 @@ impl Condition {
|
|||||||
///
|
///
|
||||||
/// ### Parameters
|
/// ### Parameters
|
||||||
/// - *thread_manager* Thread manager which managing threads
|
/// - *thread_manager* Thread manager which managing threads
|
||||||
pub fn new(thread_manager: Rc<RefCell<ThreadManager>>) -> Condition {
|
#[allow(unused)]
|
||||||
Condition{ waiting_queue: List::new(), thread_manager }
|
pub fn new() -> Condition {
|
||||||
|
Condition{ waiting_queue: List::default()}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Block the calling thread (put it in the wait queue).
|
/// Block the calling thread (put it in the wait queue).
|
||||||
@ -195,11 +168,18 @@ impl Condition {
|
|||||||
/// ### Parameters
|
/// ### Parameters
|
||||||
/// - **current_thread** the current thread
|
/// - **current_thread** the current thread
|
||||||
/// - **machine** the machine where threads are executed
|
/// - **machine** the machine where threads are executed
|
||||||
pub fn wait(&mut self, current_thread: Rc<RefCell<Thread>>, machine: &mut Machine) {
|
#[allow(unused)]
|
||||||
|
pub fn wait(&mut self, machine: &mut Machine, thread_manager: &mut ThreadManager) {
|
||||||
let old_status = machine.interrupt.set_status(InterruptOff);
|
let old_status = machine.interrupt.set_status(InterruptOff);
|
||||||
|
match thread_manager.get_g_current_thread() {
|
||||||
self.waiting_queue.push(Rc::clone(¤t_thread));
|
Some(thread) => {
|
||||||
self.thread_manager.borrow_mut().thread_sleep(current_thread);
|
let rc1 = Rc::clone(thread);
|
||||||
|
let rc2 = Rc::clone(thread);
|
||||||
|
self.waiting_queue.push(rc1);
|
||||||
|
thread_manager.thread_sleep(machine, rc2);
|
||||||
|
},
|
||||||
|
None => unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
machine.interrupt.set_status(old_status);
|
machine.interrupt.set_status(old_status);
|
||||||
}
|
}
|
||||||
@ -210,11 +190,13 @@ impl Condition {
|
|||||||
/// ### Parameters
|
/// ### Parameters
|
||||||
/// - **machine** the machine where the code is executed
|
/// - **machine** the machine where the code is executed
|
||||||
/// - **scheduler** the scheduler which determine which thread to execute
|
/// - **scheduler** the scheduler which determine which thread to execute
|
||||||
pub fn signal(&mut self, machine: &mut Machine, scheduler: &mut Scheduler) {
|
#[allow(unused)]
|
||||||
|
pub fn signal(&mut self, machine: &mut Machine, thread_manager: &mut ThreadManager) {
|
||||||
let old_status = machine.interrupt.set_status(InterruptOff);
|
let old_status = machine.interrupt.set_status(InterruptOff);
|
||||||
|
|
||||||
if self.waiting_queue.peek() != None {
|
match self.waiting_queue.pop() {
|
||||||
scheduler.ready_to_run(self.waiting_queue.pop().unwrap());
|
Some(thread) => thread_manager.ready_to_run(thread),
|
||||||
|
None => ()
|
||||||
}
|
}
|
||||||
|
|
||||||
machine.interrupt.set_status(old_status);
|
machine.interrupt.set_status(old_status);
|
||||||
@ -227,11 +209,13 @@ impl Condition {
|
|||||||
/// ### Parameters
|
/// ### Parameters
|
||||||
/// - **machine** the machine where the code is executed
|
/// - **machine** the machine where the code is executed
|
||||||
/// - **scheduler** the scheduler which determine which thread to execute
|
/// - **scheduler** the scheduler which determine which thread to execute
|
||||||
pub fn broadcast(&mut self, machine: &mut Machine, scheduler: &mut Scheduler) {
|
#[allow(unused)]
|
||||||
|
pub fn broadcast(&mut self, machine: &mut Machine, thread_manager: &mut ThreadManager) {
|
||||||
let old_status = machine.interrupt.set_status(InterruptOff);
|
let old_status = machine.interrupt.set_status(InterruptOff);
|
||||||
|
|
||||||
while self.waiting_queue.peek() != None {
|
match self.waiting_queue.pop() {
|
||||||
scheduler.ready_to_run(self.waiting_queue.pop().unwrap());
|
Some(thread) => thread_manager.ready_to_run(thread),
|
||||||
|
None => ()
|
||||||
}
|
}
|
||||||
machine.interrupt.set_status(old_status);
|
machine.interrupt.set_status(old_status);
|
||||||
|
|
||||||
@ -243,128 +227,66 @@ impl Condition {
|
|||||||
mod test {
|
mod test {
|
||||||
use std::{rc::Rc, cell::RefCell};
|
use std::{rc::Rc, cell::RefCell};
|
||||||
|
|
||||||
use crate::{kernel::{thread::Thread, synch::{Semaphore, Lock}}, init_system, simulator::machine::Machine};
|
use crate::{kernel::{thread::Thread, synch::Lock, thread_manager::ThreadManager}, simulator::machine::Machine, utility::cfg::get_debug_configuration};
|
||||||
|
|
||||||
#[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]
|
#[test]
|
||||||
#[ignore]
|
|
||||||
fn test_lock_simple() {
|
fn test_lock_simple() {
|
||||||
let system = init_system!();
|
let mut machine = Machine::new(true, get_debug_configuration());
|
||||||
let sys = system.borrow_mut();
|
let mut tm = ThreadManager::new(true);
|
||||||
let tm = sys.get_thread_manager();
|
|
||||||
let thread = Rc::new(RefCell::new(Thread::new("test_lock")));
|
let thread = Rc::new(RefCell::new(Thread::new("test_lock")));
|
||||||
tm.borrow_mut().set_g_current_thread(Some(Rc::clone(&thread)));
|
tm.ready_to_run(Rc::clone(&thread));
|
||||||
let mut lock = Lock::new(Rc::clone(&tm));
|
tm.set_g_current_thread(Some(Rc::clone(&thread)));
|
||||||
|
let mut lock = Lock::new();
|
||||||
|
|
||||||
assert!(lock.free);
|
assert!(lock.free);
|
||||||
lock.acquire(Some(Rc::clone(&thread)), Rc::clone(&system));
|
lock.acquire(&mut machine, &mut tm);
|
||||||
assert!(lock.held_by_current_thread(Rc::clone(&thread)));
|
assert!(lock.held_by_current_thread(&mut tm));
|
||||||
|
|
||||||
assert!(!lock.free);
|
assert!(!lock.free);
|
||||||
lock.release(Rc::clone(&system), Rc::clone(&thread));
|
lock.release(&mut machine, &mut tm);
|
||||||
assert!(!lock.held_by_current_thread(thread));
|
assert!(!lock.held_by_current_thread(&mut tm));
|
||||||
assert!(lock.free);
|
assert!(lock.free);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[ignore]
|
|
||||||
fn test_lock_multiple() {
|
fn test_lock_multiple() {
|
||||||
let system = init_system!();
|
|
||||||
let thread1 = Rc::new(RefCell::new(Thread::new("test_lock1")));
|
let thread1 = Rc::new(RefCell::new(Thread::new("test_lock1")));
|
||||||
let thread2 = Rc::new(RefCell::new(Thread::new("test_lock2")));
|
let thread2 = Rc::new(RefCell::new(Thread::new("test_lock2")));
|
||||||
let thread3 = Rc::new(RefCell::new(Thread::new("test_lock3")));
|
|
||||||
|
|
||||||
let tm = system.borrow_mut().get_thread_manager();
|
let mut machine = Machine::new(true, get_debug_configuration());
|
||||||
tm.borrow_mut().set_g_current_thread(Some(Rc::clone(&thread1)));
|
let mut tm = ThreadManager::new(true);
|
||||||
let mut lock = Lock::new(Rc::clone(&tm));
|
|
||||||
|
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();
|
||||||
|
|
||||||
assert!(lock.free);
|
assert!(lock.free);
|
||||||
lock.acquire(Some(Rc::clone(&thread1)), Rc::clone(&system));
|
lock.acquire(&mut machine, &mut tm);
|
||||||
assert!(lock.held_by_current_thread(Rc::clone(&thread1)));
|
assert!(lock.held_by_current_thread(&mut tm));
|
||||||
assert!(!lock.free);
|
assert!(!lock.free);
|
||||||
|
|
||||||
tm.borrow_mut().set_g_current_thread(Some(Rc::clone(&thread2)));
|
tm.set_g_current_thread(Some(Rc::clone(&thread2)));
|
||||||
lock.acquire(Some(Rc::clone(&thread2)), Rc::clone(&system));
|
lock.acquire(&mut machine, &mut tm);
|
||||||
tm.borrow_mut().set_g_current_thread(Some(Rc::clone(&thread1)));
|
|
||||||
|
|
||||||
|
|
||||||
assert!(lock.held_by_current_thread(Rc::clone(&thread1)));
|
|
||||||
|
tm.set_g_current_thread(Some(Rc::clone(&thread1)));
|
||||||
|
assert!(lock.held_by_current_thread(&mut tm));
|
||||||
assert!(lock.waiting_queue.iter().count() == 1);
|
assert!(lock.waiting_queue.iter().count() == 1);
|
||||||
assert!(!lock.free);
|
assert!(!lock.free);
|
||||||
|
|
||||||
lock.release(Rc::clone(&system), Rc::clone(&thread1));
|
lock.release(&mut machine, &mut tm);
|
||||||
assert!(!lock.held_by_current_thread(thread1));
|
assert!(!lock.held_by_current_thread(&mut tm));
|
||||||
assert!(lock.held_by_current_thread(Rc::clone(&thread2)));
|
|
||||||
|
tm.set_g_current_thread(Some(Rc::clone(&thread2)));
|
||||||
|
assert!(lock.held_by_current_thread(&mut tm));
|
||||||
assert!(!lock.free);
|
assert!(!lock.free);
|
||||||
|
|
||||||
tm.borrow_mut().set_g_current_thread(Some(Rc::clone(&thread2)));
|
lock.release(&mut machine, &mut tm);
|
||||||
|
assert!(!lock.held_by_current_thread(&mut tm));
|
||||||
|
|
||||||
lock.release(Rc::clone(&system), Rc::clone(&thread2));
|
|
||||||
assert!(!lock.held_by_current_thread(thread2));
|
|
||||||
assert!(lock.free);
|
assert!(lock.free);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,26 +2,7 @@
|
|||||||
//!
|
//!
|
||||||
//! Module containing structs and methods pertaining to the state of the operating system
|
//! Module containing structs and methods pertaining to the state of the operating system
|
||||||
|
|
||||||
use std::{cell::RefCell, rc::Rc};
|
use super::{thread_manager::ThreadManager};
|
||||||
|
|
||||||
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
|
/// # System
|
||||||
///
|
///
|
||||||
@ -33,70 +14,20 @@ macro_rules! init_system {
|
|||||||
/// - The list of active threads
|
/// - The list of active threads
|
||||||
/// - The thread to be destroyed next
|
/// - The thread to be destroyed next
|
||||||
/// - The scheduler which acts upon these threads
|
/// - The scheduler which acts upon these threads
|
||||||
#[derive(PartialEq)]
|
|
||||||
pub struct System {
|
pub struct System {
|
||||||
g_machine: RefCell<Machine>,
|
thread_manager: ThreadManager
|
||||||
thread_manager: Rc<RefCell<ThreadManager>>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl System {
|
impl System {
|
||||||
|
|
||||||
/// System constructor
|
pub fn new(debug: bool) -> Self {
|
||||||
pub fn new(machine: Machine) -> System {
|
Self { thread_manager: ThreadManager::new(debug) }
|
||||||
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
|
// GETTERS
|
||||||
|
|
||||||
/// Returns the Machine
|
pub fn get_thread_manager(&mut self) -> &mut ThreadManager {
|
||||||
///
|
&mut self.thread_manager
|
||||||
/// 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,5 +1,7 @@
|
|||||||
use super::{process::Process, system::ObjectType, thread_manager::SIMULATORSTACKSIZE};
|
use std::{rc::Rc, cell::RefCell};
|
||||||
use crate::{simulator::machine::{NUM_INT_REGS, NUM_FP_REGS, STACK_REG}};
|
|
||||||
|
use super::{process::Process, thread_manager::ThreadRef};
|
||||||
|
use crate::{simulator::machine::{NUM_INT_REGS, NUM_FP_REGS, STACK_REG}, utility::list::List};
|
||||||
|
|
||||||
const STACK_FENCEPOST: u32 = 0xdeadbeef;
|
const STACK_FENCEPOST: u32 = 0xdeadbeef;
|
||||||
|
|
||||||
@ -16,17 +18,15 @@ macro_rules! get_new_thread {
|
|||||||
pub struct ThreadContext {
|
pub struct ThreadContext {
|
||||||
pub int_registers: [i64; NUM_INT_REGS],
|
pub int_registers: [i64; NUM_INT_REGS],
|
||||||
pub float_registers: [f32; NUM_FP_REGS],
|
pub float_registers: [f32; NUM_FP_REGS],
|
||||||
pc: i64,
|
pub pc: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug)]
|
#[derive(PartialEq, Debug)]
|
||||||
pub struct Thread {
|
pub struct Thread {
|
||||||
name: String,
|
name: String,
|
||||||
pub process: Option<Process>,
|
pub process: Option<Rc<RefCell<Process>>>,
|
||||||
// simulation_context: UContextT,
|
|
||||||
pub thread_context: ThreadContext,
|
pub thread_context: ThreadContext,
|
||||||
pub stack_pointer: i32,
|
pub join_thread: List<ThreadRef>,
|
||||||
object_type: ObjectType
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Thread {
|
impl Thread {
|
||||||
@ -40,32 +40,16 @@ impl Thread {
|
|||||||
thread_context: ThreadContext {
|
thread_context: ThreadContext {
|
||||||
int_registers: [0; NUM_INT_REGS],
|
int_registers: [0; NUM_INT_REGS],
|
||||||
float_registers: [0f32; NUM_FP_REGS],
|
float_registers: [0f32; NUM_FP_REGS],
|
||||||
pc: 0
|
pc: 0,
|
||||||
},
|
},
|
||||||
stack_pointer: 0,
|
join_thread: List::default(),
|
||||||
object_type: ObjectType::ThreadType,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_thread_context(&mut self, initial_pc_reg: i64, initial_sp: i64, arg: i64) {
|
pub fn init_thread_context(&mut self, initial_pc_reg: u64, initial_sp: u64, arg: i64) {
|
||||||
self.thread_context.pc = initial_pc_reg;
|
self.thread_context.pc = initial_pc_reg;
|
||||||
self.thread_context.int_registers[10] = arg;
|
self.thread_context.int_registers[10] = arg;
|
||||||
self.thread_context.int_registers[STACK_REG] = initial_sp;
|
self.thread_context.int_registers[STACK_REG] = initial_sp as i64;
|
||||||
}
|
|
||||||
|
|
||||||
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
|
/// Check if a thread has overflowed its stack
|
||||||
@ -78,37 +62,22 @@ 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 {
|
pub fn get_name(&self) -> String {
|
||||||
self.name.clone()
|
self.name.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
/// Return reference to an optional Process
|
||||||
|
/// can be None if Thread hasn't been initialize
|
||||||
impl Drop for Thread {
|
pub fn get_process_owner(&self) -> &Option<Rc<RefCell<Process>>> {
|
||||||
|
&self.process
|
||||||
fn drop(&mut self) {
|
|
||||||
self.object_type = ObjectType::InvalidType;
|
|
||||||
// todo!();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn start_thread_execution() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
|
||||||
use super::{Thread, ThreadContext, NUM_INT_REGS, NUM_FP_REGS, ObjectType};
|
use super::{Thread, ThreadContext, NUM_INT_REGS, NUM_FP_REGS};
|
||||||
const DEFAULT_THREAD_NAME: &str = "test_thread";
|
const DEFAULT_THREAD_NAME: &str = "test_thread";
|
||||||
|
|
||||||
/// This macro allows for getting a Thread for which we've ensured proper initial state
|
/// This macro allows for getting a Thread for which we've ensured proper initial state
|
||||||
@ -125,8 +94,6 @@ mod test {
|
|||||||
float_registers: [0f32; NUM_FP_REGS],
|
float_registers: [0f32; NUM_FP_REGS],
|
||||||
pc: 0
|
pc: 0
|
||||||
};
|
};
|
||||||
x.stack_pointer = 0;
|
|
||||||
x.object_type = ObjectType::ThreadType;
|
|
||||||
x }
|
x }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,108 @@
|
|||||||
use std::{rc::Rc, cell::{RefCell, RefMut, Ref}};
|
//! # 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 crate::{utility::list::List, simulator::{machine::{NUM_INT_REGS, NUM_FP_REGS}, interrupt::InterruptStatus}};
|
use std::{
|
||||||
|
rc::Rc,
|
||||||
|
cell::{
|
||||||
|
RefCell,
|
||||||
|
Ref
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
use super::{scheduler::Scheduler, thread::Thread, system::System, mgerror::ErrorCode, process::Process};
|
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
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
pub const SIMULATORSTACKSIZE: usize = 32 * 1024;
|
/// Using this type alias to simplify struct and method definitions
|
||||||
|
pub type ThreadRef = Rc<RefCell<Thread>>;
|
||||||
|
|
||||||
/// # Thread manager
|
/// # Thread manager
|
||||||
///
|
///
|
||||||
@ -12,166 +110,535 @@ pub const SIMULATORSTACKSIZE: usize = 32 * 1024;
|
|||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
pub struct ThreadManager {
|
pub struct ThreadManager {
|
||||||
/// Current running thread
|
/// Current running thread
|
||||||
pub g_current_thread: Option<Rc<RefCell<Thread>>>,
|
pub g_current_thread: Option<ThreadRef>,
|
||||||
/// The thread to be destroyed next
|
|
||||||
pub g_thread_to_be_destroyed: Option<Rc<RefCell<Thread>>>,
|
|
||||||
/// The list of alive threads
|
/// The list of alive threads
|
||||||
pub g_alive: List<Rc<RefCell<Thread>>>,
|
pub g_alive: List<ThreadRef>,
|
||||||
/// The thread scheduler
|
/// Thread in ready state waiting to become active
|
||||||
pub g_scheduler: Scheduler,
|
ready_list: List<ThreadRef>,
|
||||||
/// The system owning the thread manager
|
/// List of objects created by the thread manager (such as Locks and Semaphores)
|
||||||
pub system: Option<Rc<RefCell<System>>>
|
obj_addrs: ObjAddr,
|
||||||
|
/// If true, enables debug mode
|
||||||
|
debug: bool,
|
||||||
|
sp_max: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ThreadManager {
|
impl ThreadManager {
|
||||||
|
|
||||||
/// Thread manager constructor
|
/// Thread manager constructor
|
||||||
pub fn new() -> Self {
|
pub fn new(debug: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
g_current_thread: Option::None,
|
g_current_thread: Option::None,
|
||||||
g_thread_to_be_destroyed: Option::None,
|
g_alive: List::default(),
|
||||||
g_alive: List::new(),
|
ready_list: List::default(),
|
||||||
g_scheduler: Scheduler::new(),
|
obj_addrs: ObjAddr::init(),
|
||||||
system: Option::None
|
debug,
|
||||||
|
sp_max: 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn freeze(this: &Rc<RefCell<ThreadManager>>) {
|
/// Mark a thread as aready, but not necessarily running yet.
|
||||||
let copy = Rc::clone(this);
|
///
|
||||||
this.borrow_mut().g_scheduler.thread_manager = Option::Some(copy);
|
/// 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));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start a thread, attaching it to a process
|
/// Start a thread, attaching it to a process
|
||||||
pub fn start_thread(&mut self, thread: Rc<RefCell<Thread>>, owner: Process, func_pc: i64, argument: i64) -> Result<(), ErrorCode> {
|
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()));
|
||||||
|
|
||||||
let mut thread_m = thread.borrow_mut();
|
let mut thread_m = thread.borrow_mut();
|
||||||
assert_eq!(thread_m.process, Option::None);
|
assert_eq!(thread_m.process, Option::None);
|
||||||
thread_m.process = Option::Some(owner);
|
thread_m.process = Option::Some(Rc::clone(&owner));
|
||||||
let ptr = 0; // todo addrspace
|
let ptr = sp_loc; // todo addrspace
|
||||||
thread_m.init_thread_context(func_pc, ptr, argument);
|
thread_m.init_thread_context(func_pc, ptr, argument);
|
||||||
let base_stack_addr: [i8; SIMULATORSTACKSIZE] = [0; SIMULATORSTACKSIZE]; // todo AllocBoundedArray
|
owner.borrow_mut().num_thread += 1;
|
||||||
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.get_g_alive().push(Rc::clone(&thread));
|
||||||
self.g_scheduler.ready_to_run(Rc::clone(&thread));
|
self.ready_to_run(Rc::clone(&thread));
|
||||||
Result::Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Wait for another thread to finish its execution
|
/// Wait for another thread to finish its execution
|
||||||
pub fn thread_join(&mut self, id_thread: Rc<RefCell<Thread>>) {
|
pub fn thread_join(&mut self, machine: &mut Machine, waiter: ThreadRef, waiting_for: ThreadRef) {
|
||||||
while self.get_g_alive().contains(&Rc::clone(&id_thread)) {
|
let waiting_for = Rc::clone(&waiting_for);
|
||||||
self.thread_yield(Rc::clone(&id_thread));
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Relinquish the CPU if any other thread is runnable.
|
/// Relinquish the CPU if any other thread is runnable.
|
||||||
///
|
///
|
||||||
/// Cannot use yield as a function name -> reserved name in rust
|
/// Cannot use yield as a function name -> reserved name in rust
|
||||||
pub fn thread_yield(&mut self, thread: Rc<RefCell<Thread>>) {
|
///
|
||||||
if let Some(system) = &self.system {
|
/// ## Parameters
|
||||||
let sys = system.borrow_mut();
|
///
|
||||||
let mut machine = sys.get_g_machine().borrow_mut();
|
/// **is_ready** true if **thread** should be readded to ready_to_run list, false otherwise. Typically false when joining per example
|
||||||
let old_status = machine.interrupt.set_status(crate::simulator::interrupt::InterruptStatus::InterruptOff);
|
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);
|
||||||
assert_eq!(Option::Some(Rc::clone(&thread)), self.g_current_thread);
|
|
||||||
let next_thread = self.g_scheduler.find_next_to_run();
|
self.debug(format!("Yeilding thread: \"{}\"", thread.borrow().get_name()));
|
||||||
if let Some(next_thread) = next_thread {
|
debug_assert_eq!(&Option::Some(Rc::clone(&thread)), self.get_g_current_thread());
|
||||||
let scheduler = &mut self.g_scheduler;
|
let next_thread = self.find_next_to_run();
|
||||||
scheduler.ready_to_run(thread);
|
if let Some(next_thread) = next_thread {
|
||||||
scheduler.switch_to(next_thread);
|
if is_ready {
|
||||||
|
self.ready_to_run(thread);
|
||||||
}
|
}
|
||||||
machine.interrupt.set_status(old_status);
|
self.switch_to(machine, next_thread);
|
||||||
}
|
}
|
||||||
|
machine.interrupt.set_status(old_status);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Put the thread to sleep and relinquish the processor
|
/// Put the thread to sleep and relinquish the processor
|
||||||
pub fn thread_sleep(&mut self, thread: Rc<RefCell<Thread>>) {
|
pub fn thread_sleep(&mut self, machine: &mut Machine, thread: ThreadRef) {
|
||||||
|
debug_assert_eq!(Option::Some(Rc::clone(&thread)), self.g_current_thread);
|
||||||
assert_eq!(Option::Some(Rc::clone(&thread)), self.g_current_thread);
|
debug_assert_eq!(machine.interrupt.get_status(), InterruptStatus::InterruptOff);
|
||||||
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();
|
self.debug(format!("Sleeping thread {}", thread.borrow().get_name()));
|
||||||
while next_thread.is_none() {
|
let mut next_thread = self.find_next_to_run();
|
||||||
eprintln!("Nobody to run => idle");
|
while next_thread.is_none() {
|
||||||
machine.interrupt.idle();
|
eprintln!("Nobody to run => idle");
|
||||||
next_thread = self.g_scheduler.find_next_to_run();
|
machine.interrupt.idle();
|
||||||
|
if let Some(t) = self.find_next_to_run() {
|
||||||
|
next_thread = Some(t);
|
||||||
|
} else {
|
||||||
|
panic!("Couldn't find next thread to run.\nShutting down...");
|
||||||
}
|
}
|
||||||
self.g_scheduler.switch_to(Rc::clone(&next_thread.unwrap()));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
self.switch_to(machine, Rc::clone(&next_thread.unwrap()));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finish the execution of the thread and prepare its deallocation
|
/// Finish the execution of the thread and prepare its deallocation
|
||||||
pub fn thread_finish(&mut self, thread: Rc<RefCell<Thread>>) {
|
pub fn thread_finish(&mut self, machine: &mut Machine, thread: ThreadRef, exit_code: i64) {
|
||||||
if let Some(system) = &self.system {
|
let old_status = machine.interrupt.set_status(InterruptStatus::InterruptOff);
|
||||||
let sys = Rc::clone(system);
|
assert!(self.g_alive.remove(Rc::clone(&thread)));
|
||||||
let sys = sys.borrow_mut();
|
self.debug(format!("Finishing thread {} with code {}", thread.borrow().get_name(), exit_code));
|
||||||
let mut machine = sys.get_g_machine().borrow_mut();
|
// g_objets_addrs->removeObject(self.thread) // a ajouté plus tard
|
||||||
let old_status = machine.interrupt.set_status(InterruptStatus::InterruptOff);
|
for (_, el) in thread.borrow().join_thread.iter().enumerate() {
|
||||||
self.g_thread_to_be_destroyed = Option::Some(Rc::clone(&thread));
|
self.ready_to_run(Rc::clone(&el));
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn thread_save_processor_state(&mut self, thread: Rc<RefCell<Thread>>) {
|
/// Save the CPU state of a user program on a context switch.
|
||||||
if let Some(system) = &self.system {
|
pub fn thread_save_processor_state(&mut self, machine: &mut Machine, thread: ThreadRef) {
|
||||||
let mut t: RefMut<_> = thread.borrow_mut();
|
let mut t = thread.borrow_mut();
|
||||||
let system = system.borrow_mut();
|
for i in 0..NUM_INT_REGS {
|
||||||
for i in 0..NUM_INT_REGS {
|
t.thread_context.int_registers[i] = machine.read_int_register(i);
|
||||||
t.thread_context.int_registers[i] = system.get_g_machine().borrow().read_int_register(i);
|
|
||||||
}
|
|
||||||
for i in 0..NUM_FP_REGS {
|
|
||||||
t.thread_context.float_registers[i] = system.get_g_machine().borrow().read_fp_register(i);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unreachable!("System is None")
|
|
||||||
}
|
}
|
||||||
|
for i in 0..NUM_FP_REGS {
|
||||||
|
t.thread_context.float_registers[i] = machine.read_fp_register(i);
|
||||||
|
}
|
||||||
|
t.thread_context.pc = machine.pc;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn thread_restore_processor_state(&self, thread: Rc<RefCell<Thread>>) {
|
/// Restore the CPU state of a user program on a context switch.
|
||||||
if let Some(system) = &self.system {
|
pub fn thread_restore_processor_state(&self, machine: &mut Machine, thread: ThreadRef) {
|
||||||
let system = system.borrow_mut();
|
let t: Ref<_> = thread.borrow();
|
||||||
let t: Ref<_> = thread.borrow();
|
for i in 0..NUM_INT_REGS {
|
||||||
for i in 0..NUM_INT_REGS {
|
machine.write_int_register(i, t.thread_context.int_registers[i]);
|
||||||
let machine = system.get_g_machine();
|
}
|
||||||
let mut machine = machine.borrow_mut();
|
machine.pc = t.thread_context.pc;
|
||||||
machine.write_int_register(i, t.thread_context.int_registers[i]);
|
}
|
||||||
|
|
||||||
|
/// 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);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
unreachable!("System is None")
|
Err("lock_acquire error: cannot find Lock.")?
|
||||||
}
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// self.get_obj_addrs().update_lock(id, lock);
|
||||||
|
|
||||||
|
machine.interrupt.set_status(old_status);
|
||||||
|
Ok(MachineOk::Ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Currently running thread
|
/// Currently running thread
|
||||||
pub fn get_g_current_thread(&mut self) -> &mut Option<Rc<RefCell<Thread>>> {
|
pub fn get_g_current_thread(&mut self) -> &Option<ThreadRef> {
|
||||||
&mut self.g_current_thread
|
&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
|
/// List of alive threads
|
||||||
pub fn get_g_alive(&mut self) -> &mut List<Rc<RefCell<Thread>>> {
|
pub fn get_g_alive(&mut self) -> &mut List<ThreadRef> {
|
||||||
&mut self.g_alive
|
&mut self.g_alive
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set currently running thread
|
/// Set currently running thread
|
||||||
pub fn set_g_current_thread(&mut self, thread: Option<Rc<RefCell<Thread>>>) {
|
pub fn set_g_current_thread(&mut self, thread: Option<ThreadRef>) {
|
||||||
self.g_current_thread = thread
|
self.g_current_thread = thread
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set thread to be destroyed next
|
/// Returns a mutable reference to the ObjAddr field of this thread_manager
|
||||||
pub fn set_g_thread_to_be_destroyed(&mut self, thread: Option<Rc<RefCell<Thread>>>) {
|
pub fn get_obj_addrs(&mut self) -> &mut ObjAddr {
|
||||||
self.g_thread_to_be_destroyed = thread
|
&mut self.obj_addrs
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Prints debug messages if self.debug is set to true.
|
||||||
|
fn debug(&self, message: String) {
|
||||||
|
if self.debug {
|
||||||
|
println!("{}", message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_sp_max(&self) -> u64 {
|
||||||
|
self.sp_max
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_sp_max(&mut self, sp_max: u64) {
|
||||||
|
self.sp_max = sp_max;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use std::{rc::Rc, cell::RefCell};
|
||||||
|
|
||||||
|
use crate::{simulator::{machine::Machine, loader}, kernel::{system::System, thread::Thread, process::Process, thread_manager::ThreadManager, synch::Semaphore}, utility::cfg::get_debug_configuration};
|
||||||
|
use crate::kernel::synch::Lock;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_thread_context() {
|
||||||
|
let mut machine = Machine::new(true, get_debug_configuration());
|
||||||
|
|
||||||
|
let (loader, ptr) = loader::Loader::new("./target/guac/halt.guac", &mut machine, 0).expect("IO Error");
|
||||||
|
let start_pc = loader.elf_header.entrypoint;
|
||||||
|
let system = &mut System::new(true);
|
||||||
|
|
||||||
|
let thread1 = Thread::new("th1");
|
||||||
|
let thread1 = Rc::new(RefCell::new(thread1));
|
||||||
|
system.get_thread_manager().get_g_alive().push(Rc::clone(&thread1));
|
||||||
|
|
||||||
|
let owner1 = Process { num_thread: 0 };
|
||||||
|
let owner1 = Rc::new(RefCell::new(owner1));
|
||||||
|
system.get_thread_manager().start_thread(Rc::clone(&thread1), owner1, loader.elf_header.entrypoint, ptr + machine.page_size, -1);
|
||||||
|
debug_assert_eq!(thread1.borrow_mut().thread_context.pc, start_pc);
|
||||||
|
debug_assert!(system.get_thread_manager().get_g_alive().contains(&Rc::clone(&thread1)));
|
||||||
|
|
||||||
|
let to_run = system.get_thread_manager().find_next_to_run().unwrap();
|
||||||
|
debug_assert_eq!(to_run, Rc::clone(&thread1));
|
||||||
|
|
||||||
|
system.get_thread_manager().switch_to(&mut machine, Rc::clone(&to_run));
|
||||||
|
debug_assert_eq!(system.get_thread_manager().g_current_thread, Option::Some(Rc::clone(&thread1)));
|
||||||
|
debug_assert_eq!(machine.pc, loader.elf_header.entrypoint);
|
||||||
|
|
||||||
|
machine.run(system);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lock_single(){
|
||||||
|
let mut machine = Machine::new(true, get_debug_configuration());
|
||||||
|
let mut thread_manager = ThreadManager::new(true);
|
||||||
|
let lock = Lock::new();
|
||||||
|
let lock_id = thread_manager.get_obj_addrs().add_lock(lock);
|
||||||
|
let thread = Rc::new(RefCell::new(Thread::new("test_lock")));
|
||||||
|
let thread_test = thread.clone();
|
||||||
|
thread_manager.ready_to_run(Rc::clone(&thread));
|
||||||
|
thread_manager.set_g_current_thread(Some(thread));
|
||||||
|
|
||||||
|
thread_manager.lock_acquire(lock_id, &mut machine).expect("lock acquire return an error: ");
|
||||||
|
{
|
||||||
|
let lock = thread_manager.get_obj_addrs().search_lock(lock_id).unwrap();
|
||||||
|
assert_eq!(lock.owner,Some(thread_test));
|
||||||
|
assert!(!lock.free);
|
||||||
|
assert!(lock.waiting_queue.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_manager.lock_release(lock_id, &mut machine).expect("lock release return an error: ");
|
||||||
|
{
|
||||||
|
let lock = thread_manager.get_obj_addrs().search_lock(lock_id).unwrap();
|
||||||
|
assert_eq!(lock.owner, None);
|
||||||
|
assert!(lock.free);
|
||||||
|
assert!(lock.waiting_queue.is_empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lock_multiple() {
|
||||||
|
let mut machine = Machine::new(true, get_debug_configuration());
|
||||||
|
let mut thread_manager = ThreadManager::new(true);
|
||||||
|
let lock = Lock::new();
|
||||||
|
let lock_id = thread_manager.get_obj_addrs().add_lock(lock);
|
||||||
|
let thread_1 = Rc::new(RefCell::new(Thread::new("test_lock_1")));
|
||||||
|
let thread_2 = Rc::new(RefCell::new(Thread::new("test_lock_2")));
|
||||||
|
thread_manager.ready_to_run(thread_1.clone());
|
||||||
|
thread_manager.ready_to_run(thread_2.clone());
|
||||||
|
thread_manager.set_g_current_thread(Some(thread_1.clone()));
|
||||||
|
|
||||||
|
thread_manager.lock_acquire(lock_id, &mut machine).expect("lock acquire return an error at first iteration: ");
|
||||||
|
{
|
||||||
|
let lock = thread_manager.get_obj_addrs().search_lock(lock_id).unwrap();
|
||||||
|
assert_eq!(lock.owner,Some(thread_1.clone()));
|
||||||
|
assert!(!lock.free);
|
||||||
|
assert!(lock.waiting_queue.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_manager.set_g_current_thread(Some(thread_2.clone()));
|
||||||
|
thread_manager.lock_acquire(lock_id, &mut machine).expect("lock acquire return an error at second iteration: ");
|
||||||
|
{
|
||||||
|
let lock = thread_manager.get_obj_addrs().search_lock(lock_id).unwrap();
|
||||||
|
assert_eq!(lock.owner,Some(thread_1.clone()));
|
||||||
|
assert!(!lock.free);
|
||||||
|
assert_eq!(lock.waiting_queue.iter().count(),1);
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_manager.lock_release(lock_id, &mut machine).expect("lock release return an error at first iteration: ");
|
||||||
|
{
|
||||||
|
let lock = thread_manager.get_obj_addrs().search_lock(lock_id).unwrap();
|
||||||
|
assert_eq!(lock.owner, Some(thread_2.clone()));
|
||||||
|
assert!(!lock.free);
|
||||||
|
assert!(lock.waiting_queue.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_manager.set_g_current_thread(Some(thread_2.clone()));
|
||||||
|
thread_manager.lock_release(lock_id, &mut machine).expect("lock release return an error at second iteration: ");
|
||||||
|
{
|
||||||
|
let lock = thread_manager.get_obj_addrs().search_lock(lock_id).unwrap();
|
||||||
|
assert!(lock.waiting_queue.is_empty());
|
||||||
|
assert_eq!(lock.owner, None);
|
||||||
|
assert!(lock.free);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_semaphore_single() {
|
||||||
|
// Init
|
||||||
|
let mut machine = Machine::new(true, get_debug_configuration());
|
||||||
|
let mut thread_manager = ThreadManager::new(true);
|
||||||
|
let semaphore = Semaphore::new(1);
|
||||||
|
let sema_id = thread_manager.get_obj_addrs().add_semaphore(semaphore);
|
||||||
|
let thread = Rc::new(RefCell::new(Thread::new("test_semaphore")));
|
||||||
|
thread_manager.ready_to_run(Rc::clone(&thread));
|
||||||
|
thread_manager.set_g_current_thread(Some(thread));
|
||||||
|
// P
|
||||||
|
thread_manager.sem_p(sema_id, &mut machine).expect("semaphore P return an error: ");
|
||||||
|
{
|
||||||
|
let semaphore = thread_manager.get_obj_addrs().search_semaphore(sema_id).unwrap();
|
||||||
|
assert_eq!(semaphore.counter, 0);
|
||||||
|
assert!(semaphore.waiting_queue.is_empty());
|
||||||
|
}
|
||||||
|
// V
|
||||||
|
thread_manager.sem_v(sema_id, &mut machine).expect("semaphore V return an error: ");
|
||||||
|
{
|
||||||
|
let semaphore = thread_manager.get_obj_addrs().search_semaphore(sema_id).unwrap();
|
||||||
|
assert_eq!(semaphore.counter, 1);
|
||||||
|
assert!(semaphore.waiting_queue.is_empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_semaphore_multiple() {
|
||||||
|
// Init
|
||||||
|
let mut tm = ThreadManager::new(true);
|
||||||
|
let mut machine = Machine::new(true, get_debug_configuration());
|
||||||
|
let semaphore = Semaphore::new(2);
|
||||||
|
let sema_id = tm.get_obj_addrs().add_semaphore(semaphore);
|
||||||
|
let thread1 = Rc::new(RefCell::new(Thread::new("test_semaphore_1")));
|
||||||
|
let thread2 = Rc::new(RefCell::new(Thread::new("test_semaphore_2")));
|
||||||
|
let thread3 = Rc::new(RefCell::new(Thread::new("test_semaphore_3")));
|
||||||
|
|
||||||
|
// let mut borrow_tm = tm.borrow_mut();
|
||||||
|
// let scheduler = &mut tm.g_scheduler;
|
||||||
|
tm.ready_to_run(Rc::clone(&thread1));
|
||||||
|
tm.ready_to_run(Rc::clone(&thread2));
|
||||||
|
tm.ready_to_run(Rc::clone(&thread3));
|
||||||
|
// P
|
||||||
|
tm.set_g_current_thread(Some(Rc::clone(&thread1)));
|
||||||
|
tm.sem_p(sema_id, &mut machine).expect("semaphore P return an error: ");
|
||||||
|
{
|
||||||
|
let semaphore = tm.get_obj_addrs().search_semaphore(sema_id).unwrap();
|
||||||
|
assert_eq!(semaphore.counter, 1);
|
||||||
|
assert!(semaphore.waiting_queue.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
tm.set_g_current_thread(Some(Rc::clone(&thread2)));
|
||||||
|
tm.sem_p(sema_id, &mut machine).expect("semaphore P return an error: ");
|
||||||
|
{
|
||||||
|
let semaphore = tm.get_obj_addrs().search_semaphore(sema_id).unwrap();
|
||||||
|
assert_eq!(semaphore.counter, 0);
|
||||||
|
assert!(semaphore.waiting_queue.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
tm.set_g_current_thread(Some(Rc::clone(&thread3)));
|
||||||
|
tm.sem_p( sema_id, &mut machine).expect("semaphore P return an error: ");
|
||||||
|
{
|
||||||
|
let semaphore = tm.get_obj_addrs().search_semaphore(sema_id).unwrap();
|
||||||
|
assert_eq!(semaphore.counter, -1);
|
||||||
|
assert!(semaphore.waiting_queue.iter().count() == 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// V
|
||||||
|
tm.sem_v(sema_id, &mut machine).expect("semaphore V return an error: ");
|
||||||
|
{
|
||||||
|
let semaphore = tm.get_obj_addrs().search_semaphore(sema_id).unwrap();
|
||||||
|
assert_eq!(semaphore.counter, 0);
|
||||||
|
assert!(semaphore.waiting_queue.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
tm.sem_v(sema_id, &mut machine).expect("semaphore V return an error: ");
|
||||||
|
{
|
||||||
|
let semaphore = tm.get_obj_addrs().search_semaphore(sema_id).unwrap();
|
||||||
|
assert_eq!(semaphore.counter, 1);
|
||||||
|
assert!(semaphore.waiting_queue.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
tm.sem_v(sema_id, &mut machine).expect("semaphore V return an error: ");
|
||||||
|
{
|
||||||
|
let semaphore = tm.get_obj_addrs().search_semaphore(sema_id).unwrap();
|
||||||
|
assert_eq!(semaphore.counter, 2);
|
||||||
|
assert!(semaphore.waiting_queue.is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,74 +0,0 @@
|
|||||||
#[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
|
|
||||||
}
|
|
||||||
}
|
|
51
src/main.rs
51
src/main.rs
@ -15,12 +15,53 @@ pub mod utility;
|
|||||||
|
|
||||||
use std::{rc::Rc, cell::RefCell};
|
use std::{rc::Rc, cell::RefCell};
|
||||||
|
|
||||||
use kernel::system::System;
|
use kernel::{system::System, thread::Thread, process::Process};
|
||||||
use simulator::machine::Machine;
|
use simulator::{machine::Machine, loader};
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let machine = Machine::init_machine();
|
let args = Args::parse();
|
||||||
let system = Rc::new(RefCell::new(System::new(machine)));
|
|
||||||
|
|
||||||
System::freeze(system);
|
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 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));
|
||||||
|
let sp_max = ptr + machine.user_stack_size;
|
||||||
|
system.get_thread_manager().set_sp_max(sp_max);
|
||||||
|
system.get_thread_manager().start_thread(Rc::clone(&thread_exec), owner1, loader.elf_header.entrypoint, sp_max, -1);
|
||||||
|
|
||||||
|
|
||||||
|
let 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);
|
||||||
}
|
}
|
||||||
|
@ -1,95 +0,0 @@
|
|||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
60
src/simulator/error.rs
Normal file
60
src/simulator/error.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
//! # 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 }
|
||||||
|
}
|
||||||
|
}
|
532
src/simulator/instruction.rs
Normal file
532
src/simulator/instruction.rs
Normal file
@ -0,0 +1,532 @@
|
|||||||
|
//! # Instruction
|
||||||
|
//!
|
||||||
|
//! This module describes the internal representation of a RISC-V Instruction,
|
||||||
|
//! its constructor from raw data, and a debug print method.
|
||||||
|
#![allow(clippy::missing_docs_in_private_items, non_snake_case)]
|
||||||
|
|
||||||
|
use core::num::Wrapping; // Permet d'autoriser les overflow pour les opérations voulues
|
||||||
|
use super::global::*;
|
||||||
|
|
||||||
|
/// OP instruction name mapping
|
||||||
|
const NAMES_OP: [&str; 8] = ["add", "sll", "slt", "sltu", "xor", "sr", "or", "and"];
|
||||||
|
/// OPI instruction name mapping
|
||||||
|
const NAMES_OPI: [&str; 8] = ["addi", "slli", "slti", "sltiu", "xori", "slri", "ori", "andi"];
|
||||||
|
/// OPW instruction name mapping
|
||||||
|
const NAMES_OPW: [&str; 8] = ["addw", "sllw", "", "", "", "srw", "", ""];
|
||||||
|
/// OPIW instruction name mapping
|
||||||
|
const NAMES_OPIW: [&str; 8] = ["addiw", "slliw", "", "", "", "sri", "", ""];
|
||||||
|
/// MUL instruction name mapping
|
||||||
|
const NAMES_MUL: [&str; 8] = ["mul", "mulh", "mulhsu", "mulhu", "div", "divu", "rem", "remu"];
|
||||||
|
/// BR instruction name mapping
|
||||||
|
const NAMES_BR: [&str; 8] = ["beq", "bne", "", "", "blt", "bge", "bltu", "bgeu"];
|
||||||
|
/// ST instruction name mapping
|
||||||
|
const NAMES_ST: [&str; 4] = ["sb", "sh", "sw", "sd"];
|
||||||
|
/// LD instruction name mapping
|
||||||
|
const NAMES_LD: [&str; 7] = ["lb", "lh", "lw", "ld", "lbu", "lhu", "lwu"];
|
||||||
|
|
||||||
|
|
||||||
|
/// Integer register name mapping
|
||||||
|
pub const REG_X: [&str; 32] = ["zero", "ra", "sp", "gp", "tp", "t0", "t1", "t2", "s0", "s1",
|
||||||
|
"a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7",
|
||||||
|
"s2", "s3", "s4", "s5", "s6", "s7", "s8", "s9", "s10", "s11",
|
||||||
|
"t3", "t4", "t5", "t6"];
|
||||||
|
|
||||||
|
/// Floating-point register name mapping
|
||||||
|
const REG_F: [&str; 32] = ["ft0", "ft1", "ft2", "ft3", "ft4", "ft5", "ft6", "ft7", "fs0", "fs1",
|
||||||
|
"fa0", "fa1", "fa2", "fa3", "fa4", "fa5", "fa6", "fa7",
|
||||||
|
"fs2", "fs3", "fs4", "fs5", "fs6", "fs7", "fs8", "fs9", "fs10", "fs11",
|
||||||
|
"ft8", "ft9", "ft10", "ft11"];
|
||||||
|
|
||||||
|
/// RISC-V Instruction
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Instruction {
|
||||||
|
/// Original value used to construct self
|
||||||
|
pub value : u64,
|
||||||
|
|
||||||
|
pub opcode : u8,
|
||||||
|
pub rs1 : u8,
|
||||||
|
pub rs2 : u8,
|
||||||
|
pub rs3 : u8,
|
||||||
|
pub rd : u8,
|
||||||
|
pub funct7 : u8,
|
||||||
|
pub funct7_smaller : u8,
|
||||||
|
pub funct3 : u8,
|
||||||
|
pub shamt : u8, // shamt = imm[5:0] or imm[4:0] (depend of opcode)
|
||||||
|
|
||||||
|
pub imm12_I : u16,
|
||||||
|
pub imm12_S : u16,
|
||||||
|
|
||||||
|
pub imm12_I_signed : i16,
|
||||||
|
pub imm12_S_signed : i16,
|
||||||
|
pub imm13 : i16,
|
||||||
|
pub imm13_signed : i16,
|
||||||
|
|
||||||
|
pub imm31_12 : u32,
|
||||||
|
pub imm21_1 : u32,
|
||||||
|
|
||||||
|
pub imm31_12_signed : i32,
|
||||||
|
pub imm21_1_signed : i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Instruction {
|
||||||
|
|
||||||
|
/// Construct a new instruction from a big endian raw binary instruction
|
||||||
|
pub fn new(value : u64) -> Self {
|
||||||
|
|
||||||
|
let opcode = (value & 0x7f) as u8;
|
||||||
|
let rs1 = ((value >> 15) & 0x1f) as u8;
|
||||||
|
let rs2 = ((value >> 20) & 0x1f) as u8;
|
||||||
|
let rs3 = ((value >> 27) & 0x1f) as u8;
|
||||||
|
let rd = ((value >> 7) & 0x1f) as u8;
|
||||||
|
let funct7 = ((value >> 25) & 0x7f) as u8;
|
||||||
|
let funct7_smaller = funct7 & 0x3e;
|
||||||
|
|
||||||
|
let funct3 = ((value >> 12) & 0x7) as u8;
|
||||||
|
let imm12_I = ((value >> 20) & 0xfff) as u16;
|
||||||
|
let imm12_S = (((value >> 20) & 0xfe0) + ((value >> 7) & 0x1f)) as u16;
|
||||||
|
|
||||||
|
let imm12_I_signed = if imm12_I >= 2048 { (Wrapping(imm12_I) - Wrapping(4096)).0 } else { imm12_I } as i16;
|
||||||
|
let imm12_S_signed = if imm12_S >= 2048 { (Wrapping(imm12_S) - Wrapping(4096)).0 } else { imm12_S } as i16;
|
||||||
|
|
||||||
|
let imm13 = (((value >> 19) & 0x1000) + ((value >> 20) & 0x7e0) +
|
||||||
|
((value >> 7) & 0x1e) + ((value << 4) & 0x800)) as i16;
|
||||||
|
let imm13_signed = if imm13 >= 4096 { imm13 - 8192 } else { imm13 };
|
||||||
|
|
||||||
|
let imm31_12 = (value & 0xfffff000) as u32;
|
||||||
|
let imm31_12_signed = imm31_12 as i32;
|
||||||
|
|
||||||
|
let imm21_1 = ((value & 0xff000) + ((value >> 9) & 0x800) +
|
||||||
|
((value >> 20) & 0x7fe) + ((value >> 11) & 0x100000)) as u32;
|
||||||
|
let imm21_1_signed = if imm21_1 >= 1048576 { (Wrapping(imm21_1) - Wrapping(2097152)).0 } else { imm21_1 } as i32;
|
||||||
|
|
||||||
|
let shamt = ((value >> 20) & 0x3f) as u8;
|
||||||
|
|
||||||
|
Instruction {
|
||||||
|
value,
|
||||||
|
|
||||||
|
opcode,
|
||||||
|
rs1,
|
||||||
|
rs2,
|
||||||
|
rs3,
|
||||||
|
rd,
|
||||||
|
funct7,
|
||||||
|
funct7_smaller,
|
||||||
|
|
||||||
|
funct3,
|
||||||
|
imm12_I,
|
||||||
|
imm12_S,
|
||||||
|
|
||||||
|
imm12_I_signed,
|
||||||
|
imm12_S_signed,
|
||||||
|
|
||||||
|
imm13,
|
||||||
|
imm13_signed,
|
||||||
|
|
||||||
|
imm31_12,
|
||||||
|
imm31_12_signed,
|
||||||
|
|
||||||
|
imm21_1,
|
||||||
|
imm21_1_signed,
|
||||||
|
|
||||||
|
shamt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts an Instruction to a prettified debug String
|
||||||
|
///
|
||||||
|
/// ### Usage
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// let m = Machine::new();
|
||||||
|
/// let i = Instruction::new(inst);
|
||||||
|
/// println!("{}", instruction_debug(i, m.pc));
|
||||||
|
/// ```
|
||||||
|
pub fn instruction_debug(ins: &Instruction, pc: i32) -> String {
|
||||||
|
let rd = ins.rd as usize;
|
||||||
|
let rs1 = ins.rs1 as usize;
|
||||||
|
let rs2 = ins.rs2 as usize;
|
||||||
|
let rs3 = ins.rs3 as usize;
|
||||||
|
|
||||||
|
match ins.opcode {
|
||||||
|
RISCV_OP => {
|
||||||
|
let name: &str;
|
||||||
|
if ins.funct7 == 1 { // Use mul array
|
||||||
|
name = NAMES_MUL[ins.funct3 as usize]
|
||||||
|
} else if ins.funct3 == RISCV_OP_ADD {
|
||||||
|
// Add or Sub
|
||||||
|
if ins.funct7 == RISCV_OP_ADD_ADD {
|
||||||
|
name = "add";
|
||||||
|
} else {
|
||||||
|
name = "sub";
|
||||||
|
}
|
||||||
|
} else if ins.funct3 == RISCV_OP_SR {
|
||||||
|
// Srl or Sra
|
||||||
|
if ins.funct7 == RISCV_OP_SR_SRL {
|
||||||
|
name = "srl";
|
||||||
|
} else {
|
||||||
|
name = "sra";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
name = NAMES_OP[ins.funct3 as usize];
|
||||||
|
}
|
||||||
|
format!("{}\t{},{},{}", name, REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||||
|
},
|
||||||
|
RISCV_OPI => {
|
||||||
|
// SHAMT OR IMM
|
||||||
|
if ins.funct3 == RISCV_OPI_SRI {
|
||||||
|
if ins.funct7 == RISCV_OPI_SRI_SRLI {
|
||||||
|
format!("srli\t{},{},{}", REG_X[rd], REG_X[rs1], ins.shamt)
|
||||||
|
} else {
|
||||||
|
format!("srai\t{},{},{}", REG_X[rd], REG_X[rs1], ins.shamt)
|
||||||
|
}
|
||||||
|
} else if ins.funct3 == RISCV_OPI_SLLI {
|
||||||
|
format!("{}\t{},{},{}", NAMES_OPI[ins.funct3 as usize], REG_X[rd], REG_X[rs1], ins.shamt)
|
||||||
|
} else {
|
||||||
|
format!("{}\t{},{},{}", NAMES_OPI[ins.funct3 as usize], REG_X[rd], REG_X[rs1], ins.imm12_I_signed)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_LUI => {
|
||||||
|
format!("lui\t{},{:x}", REG_X[rd], ins.imm31_12)
|
||||||
|
},
|
||||||
|
RISCV_AUIPC => {
|
||||||
|
format!("auipc\t{},{:x}", REG_X[rd], ins.imm31_12)
|
||||||
|
},
|
||||||
|
RISCV_JAL => {
|
||||||
|
format!("jal\t{},{:x}", REG_X[rd], (pc + ins.imm21_1_signed))
|
||||||
|
},
|
||||||
|
RISCV_JALR => {
|
||||||
|
format!("jalr\t{},{:x}({})", REG_X[rd], ins.imm12_I_signed, REG_X[rs1])
|
||||||
|
},
|
||||||
|
RISCV_BR => {
|
||||||
|
format!("{}\t{},{},{:x}", NAMES_BR[ins.funct3 as usize], REG_X[rs1], REG_X[rs2], pc + (ins.imm13_signed as i32))
|
||||||
|
},
|
||||||
|
RISCV_LD => {
|
||||||
|
format!("{}\t{},{}({})", NAMES_LD[ins.funct3 as usize], REG_X[rd], ins.imm12_I_signed, REG_X[rs1])
|
||||||
|
},
|
||||||
|
RISCV_ST => {
|
||||||
|
format!("{}\t{},{}({})", NAMES_ST[ins.funct3 as usize], REG_X[rs2], ins.imm12_S_signed, REG_X[rs1])
|
||||||
|
},
|
||||||
|
RISCV_OPIW => {
|
||||||
|
if ins.funct3 == RISCV_OPIW_SRW {
|
||||||
|
if ins.funct7 == RISCV_OPIW_SRW_SRLIW {
|
||||||
|
format!("srliw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||||
|
} else {
|
||||||
|
format!("sraiw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
format!("{}\t{},{},0x{:x}", NAMES_OPIW[ins.funct3 as usize], REG_X[rd], REG_X[rs1], ins.imm12_I_signed)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_OPW => {
|
||||||
|
if ins.funct7 == 1 {
|
||||||
|
format!("{}w\t{},{},{}", NAMES_MUL[ins.funct3 as usize], REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||||
|
} else if ins.funct3 == RISCV_OP_ADD {
|
||||||
|
if ins.funct7 == RISCV_OPW_ADDSUBW_ADDW {
|
||||||
|
format!("addw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||||
|
} else {
|
||||||
|
format!("subw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||||
|
}
|
||||||
|
} else if ins.funct3 == RISCV_OPW_SRW {
|
||||||
|
if ins.funct7 == RISCV_OPW_SRW_SRLW {
|
||||||
|
format!("srlw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||||
|
} else {
|
||||||
|
format!("sraw\t{},{},{}", REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
format!("{}\t{},{},{}", NAMES_OPW[ins.funct3 as usize], REG_X[rd], REG_X[rs1], REG_X[rs2])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// RV32F Standard Extension
|
||||||
|
RISCV_FLW => {
|
||||||
|
format!("flw\t{},{},({})", REG_F[rd], ins.imm12_I_signed, REG_F[rs1])
|
||||||
|
},
|
||||||
|
RISCV_FSW => {
|
||||||
|
format!("fsw\t{},{},({})", REG_F[rs2], "OFFSET TODO", REG_F[rs1]) // TODO Offset in decode
|
||||||
|
},
|
||||||
|
RISCV_FMADD => {
|
||||||
|
format!("fmadd\t{}{}{}{}", REG_F[rd], REG_F[rs1], REG_F[rs2], REG_F[rs3])
|
||||||
|
},
|
||||||
|
RISCV_FMSUB => {
|
||||||
|
format!("fmsub\t{}{}{}{}", REG_F[rd], REG_F[rs1], REG_F[rs2], REG_F[rs3])
|
||||||
|
},
|
||||||
|
RISCV_FNMSUB => {
|
||||||
|
format!("fnmsub\t{}{}{}{}", REG_F[rd], REG_F[rs1], REG_F[rs2], REG_F[rs3])
|
||||||
|
},
|
||||||
|
RISCV_FNMADD => {
|
||||||
|
format!("fnmadd\t{}{}{}{}", REG_F[rd], REG_F[rs1], REG_F[rs2], REG_F[rs3])
|
||||||
|
},
|
||||||
|
RISCV_FP => {
|
||||||
|
match ins.funct7 {
|
||||||
|
RISCV_FP_ADD => {
|
||||||
|
format!("{}\t{}{}{}", "fadd", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
},
|
||||||
|
RISCV_FP_SUB => {
|
||||||
|
format!("{}\t{}{}{}", "fsub.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
},
|
||||||
|
RISCV_FP_MUL => {
|
||||||
|
format!("{}\t{}{}{}", "fmul.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
},
|
||||||
|
RISCV_FP_DIV => {
|
||||||
|
format!("{}\t{}{}{}", "fdiv.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
},
|
||||||
|
RISCV_FP_SQRT => {
|
||||||
|
format!("{}\t{}{}", "fsqrt.s", REG_F[rd], REG_F[rs1])
|
||||||
|
},
|
||||||
|
RISCV_FP_FSGN => {
|
||||||
|
match ins.funct3 {
|
||||||
|
RISCV_FP_FSGN_J => {
|
||||||
|
format!("{}\t{}{}{}", "fsgnj.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
},
|
||||||
|
RISCV_FP_FSGN_JN => {
|
||||||
|
format!("{}\t{}{}{}", "fsgnn.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
},
|
||||||
|
RISCV_FP_FSGN_JX => {
|
||||||
|
format!("{}\t{}{}{}", "fsgnx.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
},
|
||||||
|
_ => todo!("Unknown code")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_FP_MINMAX => {
|
||||||
|
if ins.funct3 == 0 {
|
||||||
|
format!("{}\t{}{}{}", "fmin.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
} else {
|
||||||
|
format!("{}\t{}{}{}", "fmax.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_FP_FCVTW => {
|
||||||
|
if rs2 == 0 {
|
||||||
|
format!("{}\t{}{}", "fcvt.w.s", REG_F[rd], REG_F[rs1])
|
||||||
|
} else {
|
||||||
|
format!("{}\t{}{}", "fcvt.wu.s", REG_F[rd], REG_F[rs1])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_FP_FMVXFCLASS => {
|
||||||
|
if ins.funct3 == 0 {
|
||||||
|
format!("{}\t{}{}", "fmv.x.w", REG_F[rd], REG_F[rs1])
|
||||||
|
} else {
|
||||||
|
format!("{}\t{}{}", "fclass.s", REG_F[rd], REG_F[rs1])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_FP_FCMP => {
|
||||||
|
if ins.funct3 == 0 {
|
||||||
|
format!("{}\t{}{}{}", "fle.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
} else if ins.funct3 == 1 {
|
||||||
|
format!("{}\t{}{}{}", "flt.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
} else {
|
||||||
|
format!("{}\t{}{}{}", "feq.s", REG_F[rd], REG_F[rs1], REG_F[rs2])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_FP_FCVTS => {
|
||||||
|
if rs2 == 0 {
|
||||||
|
format!("{}\t{}{}", "fcvt.s.w", REG_F[rd], REG_F[rs1])
|
||||||
|
} else {
|
||||||
|
format!("{}\t{}{}", "fcvt.s.wu", REG_F[rd], REG_F[rs1])
|
||||||
|
}
|
||||||
|
},
|
||||||
|
RISCV_FP_FMVW => {
|
||||||
|
format!("{}\t{}{}", "fmv.w.x", REG_F[rd], REG_F[rs1])
|
||||||
|
},
|
||||||
|
_ => todo!("Unknown code")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
RISCV_SYSTEM => {
|
||||||
|
"ecall".to_string()
|
||||||
|
},
|
||||||
|
_ => todo!("{:x} opcode non géré pc : {:x}, value : {:x}", ins.opcode, pc, ins.value) // Change todo! to panic! in the future, I put todo! because there's a lot of opcode currently not implemented
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
#![allow(clippy::unusual_byte_groupings)]
|
||||||
|
|
||||||
|
use crate::simulator::instruction::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_op() {
|
||||||
|
let sub = Instruction::new(0b0100000_10000_10001_000_11100_0110011_u64.to_le());
|
||||||
|
let add = Instruction::new(0b0000000_10000_10001_000_11100_0110011_u64.to_le());
|
||||||
|
let xor = Instruction::new(0b0000000_10000_10001_100_11100_0110011_u64.to_le());
|
||||||
|
let slr = Instruction::new(0b0000000_10000_10001_101_11100_0110011_u64.to_le());
|
||||||
|
let sra = Instruction::new(0b0100000_10000_10001_101_11100_0110011_u64.to_le());
|
||||||
|
|
||||||
|
assert_eq!("sub\tt3,a7,a6", instruction_debug(&sub, 0));
|
||||||
|
assert_eq!("xor\tt3,a7,a6", instruction_debug(&xor, 0));
|
||||||
|
assert_eq!("srl\tt3,a7,a6", instruction_debug(&slr, 0));
|
||||||
|
assert_eq!("sra\tt3,a7,a6", instruction_debug(&sra, 0));
|
||||||
|
assert_eq!("add\tt3,a7,a6", instruction_debug(&add, 0));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_opi() {
|
||||||
|
let addi = Instruction::new(0b0000000000_10001_000_11100_0010011_u64.to_le());
|
||||||
|
let slli = Instruction::new(0b0000000000_10001_001_11100_0010011_u64.to_le());
|
||||||
|
let slti = Instruction::new(0b0000000000_10001_010_11100_0010011_u64.to_le());
|
||||||
|
let sltiu = Instruction::new(0b0000000000_10001_011_11100_0010011_u64.to_le());
|
||||||
|
let xori = Instruction::new(0b_0000000000010001_100_11100_0010011_u64.to_le());
|
||||||
|
let ori = Instruction::new(0b00000000000_10001_110_11100_0010011_u64.to_le());
|
||||||
|
let andi = Instruction::new(0b000000000000_10001_111_11100_0010011_u64.to_le());
|
||||||
|
assert_eq!("andi\tt3,a7,0", instruction_debug(&andi, 0));
|
||||||
|
assert_eq!("addi\tt3,a7,0", instruction_debug(&addi, 0));
|
||||||
|
assert_eq!("slli\tt3,a7,0", instruction_debug(&slli, 0));
|
||||||
|
assert_eq!("slti\tt3,a7,0", instruction_debug(&slti, 0));
|
||||||
|
assert_eq!("sltiu\tt3,a7,0", instruction_debug(&sltiu, 0));
|
||||||
|
assert_eq!("xori\tt3,a7,0", instruction_debug(&xori, 0));
|
||||||
|
assert_eq!("ori\tt3,a7,0", instruction_debug(&ori, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_lui() {
|
||||||
|
let lui = Instruction::new(0b01110001000011111000_11100_0110111_u64.to_le());
|
||||||
|
let lui_negatif = Instruction::new(0b11110001000011111000_11100_0110111_u64.to_le());
|
||||||
|
assert_eq!("lui\tt3,710f8000", instruction_debug(&lui, 0));
|
||||||
|
assert_eq!("lui\tt3,f10f8000", instruction_debug(&lui_negatif, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ld() {
|
||||||
|
// imm rs1 f3 rd opcode
|
||||||
|
let lb = Instruction::new(0b010111110000_10001_000_11100_0000011_u64.to_le());
|
||||||
|
let lh = Instruction::new(0b010111110000_10001_001_11100_0000011_u64.to_le());
|
||||||
|
let lw = Instruction::new(0b010111110000_10001_010_11100_0000011_u64.to_le());
|
||||||
|
let lbu = Instruction::new(0b010111110000_10001_100_11100_0000011_u64.to_le());
|
||||||
|
let lhu = Instruction::new(0b010111110000_10001_101_11100_0000011_u64.to_le());
|
||||||
|
let ld = Instruction::new(0b010111110000_10001_011_11100_0000011_u64.to_le());
|
||||||
|
let lwu = Instruction::new(0b010111110000_10001_110_11100_0000011_u64.to_le());
|
||||||
|
|
||||||
|
assert_eq!("lb\tt3,1520(a7)", instruction_debug(&lb, 0));
|
||||||
|
assert_eq!("lh\tt3,1520(a7)", instruction_debug(&lh, 0));
|
||||||
|
assert_eq!("lw\tt3,1520(a7)", instruction_debug(&lw, 0));
|
||||||
|
assert_eq!("lbu\tt3,1520(a7)", instruction_debug(&lbu, 0));
|
||||||
|
assert_eq!("lhu\tt3,1520(a7)", instruction_debug(&lhu, 0));
|
||||||
|
assert_eq!("ld\tt3,1520(a7)", instruction_debug(&ld, 0));
|
||||||
|
assert_eq!("lwu\tt3,1520(a7)", instruction_debug(&lwu, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_opw() {
|
||||||
|
let addw: Instruction = Instruction::new(0b0000000_10000_10001_000_11100_0111011_u64.to_le());
|
||||||
|
let sllw: Instruction = Instruction::new(0b0000000_10000_10001_001_11100_0111011_u64.to_le());
|
||||||
|
let srlw: Instruction = Instruction::new(0b0000000_10000_10001_101_11100_0111011_u64.to_le());
|
||||||
|
let sraw: Instruction = Instruction::new(0b0100000_10000_10001_101_11100_0111011_u64.to_le());
|
||||||
|
|
||||||
|
assert_eq!("addw\tt3,a7,a6", instruction_debug(&addw, 0));
|
||||||
|
assert_eq!("sllw\tt3,a7,a6", instruction_debug(&sllw, 0));
|
||||||
|
assert_eq!("srlw\tt3,a7,a6", instruction_debug(&srlw, 0));
|
||||||
|
assert_eq!("sraw\tt3,a7,a6", instruction_debug(&sraw, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_opwi() {
|
||||||
|
let addiw: Instruction =Instruction::new(0b000000000000_10001_000_11100_0011011_u64.to_le());
|
||||||
|
let slliw: Instruction = Instruction::new(0b0000000_10000_10001_001_11100_0011011_u64.to_le());
|
||||||
|
let srai: Instruction = Instruction::new(0b010000010001_10001_101_11100_0010011_u64.to_le());
|
||||||
|
assert_eq!("addiw\tt3,a7,0x0", instruction_debug(&addiw, 0));
|
||||||
|
assert_eq!("slliw\tt3,a7,0x10", instruction_debug(&slliw, 0));
|
||||||
|
assert_eq!("srai\tt3,a7,17", instruction_debug(&srai, 0));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_br() {
|
||||||
|
let beq: Instruction = Instruction::new(0b0000000_10000_10001_000_00000_1100011_u64.to_le());
|
||||||
|
let bne: Instruction = Instruction::new(0b0000000_10000_10001_001_00000_1100011_u64.to_le());
|
||||||
|
let blt: Instruction = Instruction::new(0b0000000_10000_10001_100_00000_1100011_u64.to_le());
|
||||||
|
let bge: Instruction = Instruction::new(0b0000000_10000_10001_101_00000_1100011_u64.to_le());
|
||||||
|
let bge2: Instruction = Instruction::new(0x00f75863_u64.to_le());
|
||||||
|
let bltu: Instruction = Instruction::new(0b0000000_10000_10001_110_00000_1100011_u64.to_le());
|
||||||
|
let bgeu: Instruction = Instruction::new(0b0000000_10000_10001_111_00000_1100011_u64.to_le());
|
||||||
|
assert_eq!("blt\ta7,a6,0", instruction_debug(&blt, 0));
|
||||||
|
assert_eq!("bge\ta7,a6,0", instruction_debug(&bge, 0));
|
||||||
|
assert_eq!("bge\ta4,a5,104d4", instruction_debug(&bge2, 0x104c4));
|
||||||
|
assert_eq!("bltu\ta7,a6,0", instruction_debug(&bltu, 0));
|
||||||
|
assert_eq!("bgeu\ta7,a6,0", instruction_debug(&bgeu, 0));
|
||||||
|
assert_eq!("bne\ta7,a6,0", instruction_debug(&bne, 0));
|
||||||
|
assert_eq!("beq\ta7,a6,0", instruction_debug(&beq, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_small_program() {
|
||||||
|
/* Code for :
|
||||||
|
int a = 0;
|
||||||
|
int b = 5;
|
||||||
|
a = b;
|
||||||
|
a = a * b;
|
||||||
|
a = a + b;
|
||||||
|
b = a - b;
|
||||||
|
*/
|
||||||
|
assert_eq!("addi sp,sp,-32", instruction_debug(&Instruction::new(0xfe010113_u64.to_le()), 0));
|
||||||
|
assert_eq!("sd s0,24(sp)", instruction_debug(&Instruction::new(0x00813c23_u64.to_le()), 0));
|
||||||
|
assert_eq!("addi s0,sp,32", instruction_debug(&Instruction::new(0x02010413_u64.to_le()), 0));
|
||||||
|
assert_eq!("sw zero,-20(s0)", instruction_debug(&Instruction::new(0xfe042623_u64.to_le()), 0));
|
||||||
|
assert_eq!("addi a5,zero,5", instruction_debug(&Instruction::new(0x00500793_u64.to_le()), 0));
|
||||||
|
assert_eq!("sw a5,-24(s0)", instruction_debug(&Instruction::new(0xfef42423_u64.to_le()), 0));
|
||||||
|
assert_eq!("lw a5,-24(s0)", instruction_debug(&Instruction::new(0xfe842783_u64.to_le()), 0));
|
||||||
|
assert_eq!("sw a5,-20(s0)", instruction_debug(&Instruction::new(0xfef42623_u64.to_le()), 0));
|
||||||
|
assert_eq!("lw a5,-20(s0)", instruction_debug(&Instruction::new(0xfec42783_u64.to_le()), 0));
|
||||||
|
assert_eq!("addi a4,a5,0", instruction_debug(&Instruction::new(0x00078713_u64.to_le()), 0));
|
||||||
|
assert_eq!("lw a5,-24(s0)", instruction_debug(&Instruction::new(0xfe842783_u64.to_le()), 0));
|
||||||
|
assert_eq!("mulw a5,a4,a5", instruction_debug(&Instruction::new(0x02f707bb_u64.to_le()), 0));
|
||||||
|
assert_eq!("sw a5,-20(s0)", instruction_debug(&Instruction::new(0xfef42623_u64.to_le()), 0));
|
||||||
|
assert_eq!("lw a5,-20(s0)", instruction_debug(&Instruction::new(0xfec42783_u64.to_le()), 0));
|
||||||
|
assert_eq!("addi a4,a5,0", instruction_debug(&Instruction::new(0x00078713_u64.to_le()), 0));
|
||||||
|
assert_eq!("lw a5,-24(s0)", instruction_debug(&Instruction::new(0xfe842783_u64.to_le()), 0));
|
||||||
|
assert_eq!("addw a5,a4,a5", instruction_debug(&Instruction::new(0x00f707bb_u64.to_le()), 0));
|
||||||
|
assert_eq!("sw a5,-20(s0)", instruction_debug(&Instruction::new(0xfef42623_u64.to_le()), 0));
|
||||||
|
assert_eq!("lw a5,-20(s0)", instruction_debug(&Instruction::new(0xfec42783_u64.to_le()), 0));
|
||||||
|
assert_eq!("addi a4,a5,0", instruction_debug(&Instruction::new(0x00078713_u64.to_le()), 0));
|
||||||
|
assert_eq!("lw a5,-24(s0)", instruction_debug(&Instruction::new(0xfe842783_u64.to_le()), 0));
|
||||||
|
assert_eq!("subw a5,a4,a5", instruction_debug(&Instruction::new(0x40f707bb_u64.to_le()), 0));
|
||||||
|
assert_eq!("sw a5,-24(s0)", instruction_debug(&Instruction::new(0xfef42423_u64.to_le()), 0));
|
||||||
|
assert_eq!("addi a5,zero,0", instruction_debug(&Instruction::new(0x00000793_u64.to_le()), 0));
|
||||||
|
assert_eq!("addi a0,a5,0", instruction_debug(&Instruction::new(0x00078513_u64.to_le()), 0));
|
||||||
|
assert_eq!("ld s0,24(sp)", instruction_debug(&Instruction::new(0x01813403_u64.to_le()), 0));
|
||||||
|
assert_eq!("addi sp,sp,32", instruction_debug(&Instruction::new(0x02010113_u64.to_le()), 0));
|
||||||
|
assert_eq!("jalr zero,0(ra)", instruction_debug(&Instruction::new(0x00008067_u64.to_le()), 0));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_fibo() {
|
||||||
|
assert_eq!("jal zero,10504", instruction_debug(&Instruction::new(0x0500006f_u64.to_le()), 0x104b4));
|
||||||
|
assert_eq!("blt a4,a5,104b8", instruction_debug(&Instruction::new(0xfaf740e3_u64.to_le()), 0x10518));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mul_prog() {
|
||||||
|
assert_eq!("addi sp,sp,-32", instruction_debug(&Instruction::new(0xfe010113_u64.to_le()), 0));
|
||||||
|
assert_eq!("sd s0,24(sp)", instruction_debug(&Instruction::new(0x00813c23_u64.to_le()), 0));
|
||||||
|
assert_eq!("addi s0,sp,32", instruction_debug(&Instruction::new(0x02010413_u64.to_le()), 0));
|
||||||
|
assert_eq!("addi a5,zero,5", instruction_debug(&Instruction::new(0x00500793_u64.to_le()), 0));
|
||||||
|
assert_eq!("sw a5,-20(s0)", instruction_debug(&Instruction::new(0xfef42623_u64.to_le()), 0));
|
||||||
|
assert_eq!("lw a5,-20(s0)", instruction_debug(&Instruction::new(0xfec42783_u64.to_le()), 0));
|
||||||
|
assert_eq!("addi a4,a5,0", instruction_debug(&Instruction::new(0x00078713_u64.to_le()), 0));
|
||||||
|
assert_eq!("addi a5,a4,0", instruction_debug(&Instruction::new(0x00070793_u64.to_le()), 0));
|
||||||
|
assert_eq!("slliw a5,a5,0x2", instruction_debug(&Instruction::new(0x0027979b_u64.to_le()), 0));
|
||||||
|
assert_eq!("addw a5,a5,a4", instruction_debug(&Instruction::new(0x00e787bb_u64.to_le()), 0));
|
||||||
|
assert_eq!("sw a5,-24(s0)", instruction_debug(&Instruction::new(0xfef42423_u64.to_le()), 0));
|
||||||
|
assert_eq!("lw a5,-20(s0)", instruction_debug(&Instruction::new(0xfec42783_u64.to_le()), 0));
|
||||||
|
assert_eq!("addi a4,a5,0", instruction_debug(&Instruction::new(0x00078713_u64.to_le()), 0));
|
||||||
|
assert_eq!("lw a5,-24(s0)", instruction_debug(&Instruction::new(0xfe842783_u64.to_le()), 0));
|
||||||
|
assert_eq!("mulw a5,a4,a5", instruction_debug(&Instruction::new(0x02f707bb_u64.to_le()), 0));
|
||||||
|
assert_eq!("sw a5,-28(s0)", instruction_debug(&Instruction::new(0xfef42223_u64.to_le()), 0));
|
||||||
|
assert_eq!("lw a5,-28(s0)", instruction_debug(&Instruction::new(0xfe442783_u64.to_le()), 0));
|
||||||
|
assert_eq!("addi a4,a5,0", instruction_debug(&Instruction::new(0x00078713_u64.to_le()), 0));
|
||||||
|
assert_eq!("lw a5,-24(s0)", instruction_debug(&Instruction::new(0xfe842783_u64.to_le()), 0));
|
||||||
|
assert_eq!("divw a5,a4,a5", instruction_debug(&Instruction::new(0x02f747bb_u64.to_le()), 0));
|
||||||
|
assert_eq!("sw a5,-20(s0)", instruction_debug(&Instruction::new(0xfef42623_u64.to_le()), 0));
|
||||||
|
assert_eq!("addi a5,zero,0", instruction_debug(&Instruction::new(0x00000793_u64.to_le()), 0));
|
||||||
|
assert_eq!("addi a0,a5,0", instruction_debug(&Instruction::new(0x00078513_u64.to_le()), 0));
|
||||||
|
assert_eq!("ld s0,24(sp)", instruction_debug(&Instruction::new(0x01813403_u64.to_le()), 0));
|
||||||
|
assert_eq!("addi sp,sp,32", instruction_debug(&Instruction::new(0x02010113_u64.to_le()), 0));
|
||||||
|
assert_eq!("jalr zero,0(ra)", instruction_debug(&Instruction::new(0x00008067_u64.to_le()), 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -1,34 +1,632 @@
|
|||||||
use crate::Machine;
|
use crate::Machine;
|
||||||
|
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io;
|
use std::io::Read;
|
||||||
use std::io::BufRead;
|
|
||||||
|
|
||||||
|
/// 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])
|
||||||
|
}
|
||||||
|
|
||||||
/// Load a file into a new machine
|
/// return true if big endian, false otherwise
|
||||||
///
|
fn check_endianess(instructions: &[u8]) -> bool {
|
||||||
/// `panic!` when size is not 1, 2, 4 or 8
|
instructions.get(5) == Option::Some(&2)
|
||||||
/// `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();
|
|
||||||
|
|
||||||
for (i,line) in reader.lines().enumerate() {
|
/// return true if file is 32 bits, false if 64 bits
|
||||||
let res = u64::from_str_radix(&line.unwrap(), 16);
|
fn is_32bits(instructions: &[u8]) -> bool {
|
||||||
match res {
|
instructions.get(4) == Option::Some(&1)
|
||||||
Ok(value) => {
|
}
|
||||||
Machine::write_memory(&mut machine, instruction_size, i*instruction_size as usize, value);
|
|
||||||
},
|
/// return the version of the elf file (should be 1)
|
||||||
_ => panic!()
|
/// 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!("{:x}", Machine::read_memory(& mut machine, 4, 0));
|
|
||||||
machine
|
/// 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")?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SectionHeader {
|
||||||
|
|
||||||
|
/// return true if flag of this section contains / have `key`, false otherwise
|
||||||
|
pub fn does_flag_contains_key(&self, key: FlagValue) -> bool {
|
||||||
|
self.flags & key as u64 != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the offset to a string in .shstrtab that represents the name of this section
|
||||||
|
fn get_name_offset(instructions: &[u8], address: usize) -> Option<u32> {
|
||||||
|
get_address_point(instructions, address, true).map(|v| { v as u32 })
|
||||||
|
// set true to return a u32
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the type of header of the section
|
||||||
|
fn get_header_type(instructions: &[u8], address: usize) -> Option<u32> {
|
||||||
|
get_address_point(instructions, address + 0x4, true).map(|v| { v as u32 })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the flags of the section, can hold multiples values, see [`FlagValue`]
|
||||||
|
fn get_flags(instructions: &[u8], address: usize, is_32bits: bool) -> Option<u64> {
|
||||||
|
get_address_point(instructions, address + 0x8, is_32bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Return the virtual address of the section in memory if the sectino is loaded(see section flag), otherwise 0
|
||||||
|
fn get_virtual_address(instructions: &[u8], address: usize, is_32bits: bool) -> Option<u64> {
|
||||||
|
get_address_point(instructions, address + if is_32bits { 0x0C } else { 0x10 }, is_32bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the offset of the section in the file image (binary file)
|
||||||
|
fn get_image_offset(instructions: &[u8], address: usize, is_32bits: bool) -> Option<u64> {
|
||||||
|
get_address_point(instructions, address + if is_32bits { 0x10 } else { 0x18 }, is_32bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the size of the section in the file image (binary file), may be 0
|
||||||
|
fn get_section_size(instructions: &[u8], address: usize, is_32bits: bool) -> Option<u64> {
|
||||||
|
get_address_point(instructions, address + if is_32bits { 0x14 } else { 0x20 }, is_32bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_section_link(instructions: &[u8], address: usize, is_32bits: bool) -> Option<u32> {
|
||||||
|
get_address_point(instructions, address + if is_32bits { 0x18 } else { 0x28 }, false).map(|v| { v as u32 })
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_section_info(instructions: &[u8], address: usize, is_32bits: bool) -> Option<u32> {
|
||||||
|
get_address_point(instructions, address + if is_32bits { 0x1C } else { 0x2C }, false).map(|v| { v as u32 })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return the required alignment of the section, must be a power of 2
|
||||||
|
fn get_required_align(instructions: &[u8], address: usize, is_32bits: bool) -> Option<u64> {
|
||||||
|
get_address_point(instructions, address + if is_32bits { 0x20 } else { 0x30 }, is_32bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Contain the size of each entry for sections that contain fixed-size entries, otherwise 0
|
||||||
|
fn get_entry_size(instructions: &[u8], address: usize, is_32bits: bool) -> Option<u64> {
|
||||||
|
get_address_point(instructions, address + if is_32bits { 0x24 } else { 0x38 }, is_32bits)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<(&[u8], u64, bool)> for SectionHeader {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: (&[u8], u64, bool)) -> Result<Self, Self::Error> {
|
||||||
|
let instructions = value.0;
|
||||||
|
let address = value.1 as usize;
|
||||||
|
let is_32bits = value.2;
|
||||||
|
|
||||||
|
let name_offset = Self::get_name_offset(instructions, address).ok_or(())?;
|
||||||
|
let header_type = Self::get_header_type(instructions, address).ok_or(())?;
|
||||||
|
let attribute = Self::get_flags(instructions, address, is_32bits).ok_or(())?;
|
||||||
|
let virt_addr = Self::get_virtual_address(instructions, address, is_32bits).ok_or(())?;
|
||||||
|
let image_offset = Self::get_image_offset(instructions, address, is_32bits).ok_or(())?;
|
||||||
|
let section_size = Self::get_section_size(instructions, address, is_32bits).ok_or(())?;
|
||||||
|
let section_link = Self::get_section_link(instructions, address, is_32bits).ok_or(())?;
|
||||||
|
let section_info = Self::get_section_info(instructions, address, is_32bits).ok_or(())?;
|
||||||
|
let required_align = Self::get_required_align(instructions, address, is_32bits).ok_or(())?;
|
||||||
|
let entry_size = Self::get_entry_size(instructions, address, is_32bits).ok_or(())?;
|
||||||
|
Ok(Self { name_offset,
|
||||||
|
header_type,
|
||||||
|
flags: attribute,
|
||||||
|
virt_addr,
|
||||||
|
image_offset,
|
||||||
|
section_size,
|
||||||
|
section_link,
|
||||||
|
section_info,
|
||||||
|
required_align,
|
||||||
|
entry_size
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Error enum for [`Loader`]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum LoaderError {
|
||||||
|
/// Correspond to std IO error
|
||||||
|
IOError(std::io::Error),
|
||||||
|
/// Others errors
|
||||||
|
ParsingError(String)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Global structure of the loader, one instance per loaded files
|
||||||
|
pub struct Loader {
|
||||||
|
/// List of bytes inside the binary file
|
||||||
|
bytes: Vec<u8>,
|
||||||
|
/// Elf header, see [`ElfHeader`] for more informations
|
||||||
|
pub elf_header: ElfHeader,
|
||||||
|
/// Section header table entries, see [`SectionHeader`] for more informations
|
||||||
|
pub sections: Vec<SectionHeader>
|
||||||
|
}
|
||||||
|
impl Loader {
|
||||||
|
|
||||||
|
/// # Loader constructor
|
||||||
|
///
|
||||||
|
/// Load the binary file given in parameter, parse it and load inside the machine memory
|
||||||
|
/// return the loader instance and the location of the end of the last a allocated section in memory
|
||||||
|
///
|
||||||
|
/// ## Parameters
|
||||||
|
///
|
||||||
|
/// **path**: location of the binary file on disk
|
||||||
|
/// **machine**: well, the risc-v simulator
|
||||||
|
/// **start_index**: The position at which you want to start to allocate the program
|
||||||
|
pub fn new(path: &str, machine: &mut Machine, start_index: usize) -> Result<(Self, u64), LoaderError> {
|
||||||
|
let loader = Self::load_and_parse(path)?;
|
||||||
|
let end_alloc = loader.load_into_machine(machine, start_index)?;
|
||||||
|
Ok((loader, end_alloc))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Try to load the binary file in memory after it been parsed
|
||||||
|
///
|
||||||
|
/// Binary file is loaded according to sections order and rules, see [`SectionHeader`]
|
||||||
|
///
|
||||||
|
/// Return the location of the end of the last a allocated section in memory
|
||||||
|
fn load_into_machine(&self, machine: &mut Machine, start_index: usize) -> Result<u64, LoaderError> {
|
||||||
|
let mut end_index = 0;
|
||||||
|
for i in 0..self.sections.len() {
|
||||||
|
let section = &self.sections[i];
|
||||||
|
if section.does_flag_contains_key(FlagValue::ShfAlloc) {
|
||||||
|
end_index = section.virt_addr + section.section_size;
|
||||||
|
// Can allocate to machine memory
|
||||||
|
for j in (0..section.section_size as usize).step_by(4) {
|
||||||
|
let mut buf: [u8; 4] = [0; 4];
|
||||||
|
#[allow(clippy::needless_range_loop)]
|
||||||
|
for k in 0..buf.len() {
|
||||||
|
if section.does_flag_contains_key(FlagValue::ShfWrite) {
|
||||||
|
// flag WA, on doit allouer des données initialisés à 0
|
||||||
|
// généralement, ce signifie que le compilateur à ajouter une section .bss
|
||||||
|
buf[k] = 0;
|
||||||
|
} else {
|
||||||
|
buf[k] = self.bytes.get(section.image_offset as usize + j + k).copied().ok_or(LoaderError::ParsingError(format!("index 0x{:x} is out of bound because list have a size of 0x{:x} (image offset 0x{:x}, j 0x{:x}, k 0x{:x})", section.image_offset as usize + j + k, self.bytes.len(), section.image_offset, j, k)))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
machine.write_memory(1, start_index + section.virt_addr as usize + j, buf[0] as u64);
|
||||||
|
machine.write_memory(1, start_index + section.virt_addr as usize + j + 1, buf[1] as u64);
|
||||||
|
machine.write_memory(1, start_index + section.virt_addr as usize + j + 2, buf[2] as u64);
|
||||||
|
machine.write_memory(1, start_index + section.virt_addr as usize + j + 3, buf[3] as u64);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(start_index as u64 + end_index + 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load the binary file and store it inside an array and try to parse it,
|
||||||
|
/// useful for a lot of thing like to know which sections to allocate memory and where
|
||||||
|
fn load_and_parse(path: &str) -> Result<Self, LoaderError> {
|
||||||
|
let file = fs::File::open(path);
|
||||||
|
match file {
|
||||||
|
Ok(mut file) => {
|
||||||
|
let mut instructions: Vec<u8> = Default::default();
|
||||||
|
loop {
|
||||||
|
let mut buf: [u8; 1] = [0; 1];
|
||||||
|
let res = file.read(&mut buf);
|
||||||
|
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 })
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
Err(LoaderError::IOError(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Try to parse sections header table
|
||||||
|
///
|
||||||
|
/// Create one instance of [`SectionHeader`] for each entry and store it inside an array
|
||||||
|
///
|
||||||
|
/// ## Parameters
|
||||||
|
///
|
||||||
|
/// **instructions**: array of bytes of the binary file
|
||||||
|
/// **is_32bits**: contain whether the binary file is 32 bits or 64 bits
|
||||||
|
/// **header_location**: represent the position of the first entry of the header
|
||||||
|
/// **num_of_entries**: defines the number of section header entries
|
||||||
|
/// **entry_size**: Defines the size of an entry (each entry have the exact same size), value vary depending of if this binary file is 32 or 64 bits
|
||||||
|
fn parse_section_header(instructions: &[u8], is_32bits: bool, header_location: u64, num_of_entries: u16, entry_size: u16) -> Result<Vec<SectionHeader>, ()> {
|
||||||
|
let mut sections: Vec<SectionHeader> = Default::default();
|
||||||
|
for i in 0..num_of_entries as u64 {
|
||||||
|
sections.push(Self::parse_section_entry(instructions, is_32bits, header_location + i * entry_size as u64)?);
|
||||||
|
}
|
||||||
|
Ok(sections)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Parse one entry of the section header
|
||||||
|
///
|
||||||
|
/// ## Parameters:
|
||||||
|
///
|
||||||
|
/// **instructions**: array of bytes of the binary file
|
||||||
|
/// **is_32bits**: contain whether the binary file is 32 bits or 64 bits
|
||||||
|
/// **location**: represent the position of the entry on the file image
|
||||||
|
fn parse_section_entry(instructions: &[u8], is_32bits: bool, location: u64) -> Result<SectionHeader, ()> {
|
||||||
|
SectionHeader::try_from((instructions, location, is_32bits))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// return the memory address of something stored at address
|
||||||
|
/// Can return None if the file is smaller than adress + 3 (or 7 if 64 bits), in this case, the elf header is incorrect
|
||||||
|
fn get_address_point(instructions: &[u8], address: usize, is_32bits: bool) -> Option<u64> {
|
||||||
|
if is_32bits {
|
||||||
|
let mut bytes: [u8; 4] = [0; 4];
|
||||||
|
bytes[0] = instructions.get(address).copied()?;
|
||||||
|
bytes[1] = instructions.get(address + 1).copied()?;
|
||||||
|
bytes[2] = instructions.get(address + 2).copied()?;
|
||||||
|
bytes[3] = instructions.get(address + 3).copied()?;
|
||||||
|
Option::Some(u32::from_le_bytes(bytes) as u64)
|
||||||
|
} else {
|
||||||
|
let mut bytes: [u8; 8] = [0; 8];
|
||||||
|
bytes[0] = instructions.get(address).copied()?;
|
||||||
|
bytes[1] = instructions.get(address + 1).copied()?;
|
||||||
|
bytes[2] = instructions.get(address + 2).copied()?;
|
||||||
|
bytes[3] = instructions.get(address + 3).copied()?;
|
||||||
|
bytes[4] = instructions.get(address + 4).copied()?;
|
||||||
|
bytes[5] = instructions.get(address + 5).copied()?;
|
||||||
|
bytes[6] = instructions.get(address + 6).copied()?;
|
||||||
|
bytes[7] = instructions.get(address + 7).copied()?;
|
||||||
|
Option::Some(u64::from_le_bytes(bytes))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tests has been made for C program compiled with RISC-V GCC 12.2.0, target: riscv64-unknown-elf
|
||||||
|
///
|
||||||
|
/// It may not pass in the future if future gcc version modify order of the binary or something else
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
use crate::{simulator::{loader::{Loader, SectionHeader}, machine::Machine}, utility::cfg::get_debug_configuration};
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_elf() {
|
||||||
|
let mut machine = Machine::new(true, get_debug_configuration());
|
||||||
|
let loader = Loader::load_and_parse("./target/guac/unsigned_addition.guac").expect("IO Error");
|
||||||
|
loader.load_into_machine(&mut machine, 0).expect("Parsing error");
|
||||||
|
assert!(!loader.elf_header.is_32bits);
|
||||||
|
assert!(!loader.elf_header.endianess);
|
||||||
|
assert!(loader.elf_header.sys_v_abi);
|
||||||
|
assert!(loader.elf_header.is_riscv_target);
|
||||||
|
assert_eq!(1, loader.elf_header.version);
|
||||||
|
assert_eq!(0x4000, loader.elf_header.entrypoint);
|
||||||
|
assert_eq!(64, loader.elf_header.elf_header_size);
|
||||||
|
assert_eq!(64, loader.elf_header.program_header_location);
|
||||||
|
assert_eq!(18992, loader.elf_header.section_header_location);
|
||||||
|
assert_eq!(56, loader.elf_header.program_header_size);
|
||||||
|
assert_eq!(64, loader.elf_header.section_header_size);
|
||||||
|
assert_eq!(4, loader.elf_header.program_header_entries);
|
||||||
|
assert_eq!(9, loader.elf_header.section_header_entries);
|
||||||
|
println!("{:#x?}", loader.sections);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_parse_section() {
|
||||||
|
let mut machine = Machine::new(true, get_debug_configuration());
|
||||||
|
let loader = Loader::load_and_parse("./target/guac/unsigned_addition.guac").expect("IO Error");
|
||||||
|
loader.load_into_machine(&mut machine, 0).expect("Parsing error");
|
||||||
|
assert_eq!(9, loader.sections.len());
|
||||||
|
let n = loader.sections.iter().filter(|p| { p.does_flag_contains_key(crate::simulator::loader::FlagValue::ShfAlloc)}).collect::<Vec<&SectionHeader>>().len();
|
||||||
|
assert_eq!(3, n);
|
||||||
|
assert_eq!(loader.sections[1].virt_addr, 0x4000);
|
||||||
|
assert_eq!(loader.sections[1].image_offset, 0x1000);
|
||||||
|
assert!(loader.sections[1].does_flag_contains_key(crate::simulator::loader::FlagValue::ShfAlloc));
|
||||||
|
assert_eq!(loader.sections[2].virt_addr, 0x400_000);
|
||||||
|
assert_eq!(loader.sections[2].image_offset, 0x2000);
|
||||||
|
assert!(loader.sections[2].does_flag_contains_key(crate::simulator::loader::FlagValue::ShfAlloc));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,140 +1,139 @@
|
|||||||
use std::{ops::{Add, Sub}, io::Write};
|
//! # 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 crate::simulator::print;
|
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 super::{decode::{Instruction, decode}, interrupt::Interrupt};
|
use crate::kernel::{
|
||||||
use super::global::*;
|
exception
|
||||||
use std::fs::File;
|
};
|
||||||
|
|
||||||
|
use super::error::MachineOk;
|
||||||
|
|
||||||
/*
|
/// # Exceptions
|
||||||
* Decommenter la variant si il est utilisé quelque part
|
///
|
||||||
*/
|
/// 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)]
|
||||||
pub enum ExceptionType {
|
pub enum ExceptionType {
|
||||||
NO_EXCEPTION,//Everything ok!
|
/// Everything ok
|
||||||
//SYSCALL_EXCEPTION,//A program executed a system call.
|
NoException,
|
||||||
PAGEFAULT_EXCEPTION,//Page fault exception
|
/// A program executed a system call
|
||||||
READONLY_EXCEPTION,//Write attempted to a page marked "read-only" */
|
SyscallException,
|
||||||
BUSERROR_EXCEPTION,
|
/// Page fault exception
|
||||||
/* translation resulted
|
PagefaultException,
|
||||||
in an invalid physical
|
/// Write attempted to a page marked "read-only"
|
||||||
address (mis-aligned or
|
ReadOnlyException,
|
||||||
out-of-bounds) */
|
/// Translation resulted in an invalid physical address (mis-aligned or out-of-bounds)
|
||||||
ADDRESSERROR_EXCEPTION, /* Reference that was
|
BusErrorException,
|
||||||
not mapped in the address
|
/// Reference which was not mapped in the address space
|
||||||
space */
|
AddressErrorException,
|
||||||
//OVERFLOW_EXCEPTION, //Integer overflow in add or sub.
|
/// Integer overflow in add or sub
|
||||||
//ILLEGALINSTR_EXCEPTION, //Unimplemented or reserved instr.
|
OverflowException,
|
||||||
//NUM_EXCEPTION_TYPES
|
/// Unimplemented or reserved instruction
|
||||||
|
IllegalInstrException,
|
||||||
|
NumExceptionTypes
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// # 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;
|
pub const STACK_REG: usize = 2;
|
||||||
|
/// Number of available Integer registers
|
||||||
pub const NUM_INT_REGS: usize = 32;
|
pub const NUM_INT_REGS: usize = 32;
|
||||||
|
/// Number of available Floating Point registers
|
||||||
pub const NUM_FP_REGS: usize = 32;
|
pub const NUM_FP_REGS: usize = 32;
|
||||||
|
|
||||||
//max number of physical page
|
/// RISC-V Simulator
|
||||||
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 {
|
pub struct Machine {
|
||||||
|
/// Debug mode of the machine
|
||||||
|
debug: bool,
|
||||||
|
/// Program counter
|
||||||
pub pc : u64,
|
pub pc : u64,
|
||||||
|
/// Stack pointer
|
||||||
pub sp: usize,
|
pub sp: usize,
|
||||||
|
/// Integer register
|
||||||
pub int_reg : Register<i64>,
|
pub int_reg : Register<i64>,
|
||||||
|
/// Floating point register
|
||||||
pub fp_reg : Register<f32>,
|
pub fp_reg : Register<f32>,
|
||||||
|
/// Heap memory
|
||||||
pub main_memory : Vec<u8>,
|
pub main_memory : Vec<u8>,
|
||||||
|
/// Shiftmask
|
||||||
pub shiftmask : [u64 ; 64],
|
pub shiftmask : [u64 ; 64],
|
||||||
|
/// Debug data
|
||||||
pub registers_trace : String, // for tests
|
pub registers_trace : String, // for tests
|
||||||
pub interrupt: Interrupt
|
/// todo: document Interrupts
|
||||||
|
pub interrupt: Interrupt,
|
||||||
// futur taille à calculer int memSize = g_cfg->NumPhysPages * g_cfg->PageSize;
|
// 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
|
//creer une struct cfg(configuration) qui s'initialise avec valeur dans un fichier cfg
|
||||||
|
num_phy_page: u64,
|
||||||
|
pub page_size: u64,
|
||||||
|
pub user_stack_size: u64,
|
||||||
|
/// Current machine status
|
||||||
|
pub status: MachineStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Machine {
|
impl Machine {
|
||||||
|
|
||||||
pub fn init_machine() -> Machine {
|
/// Machine constructor
|
||||||
|
pub fn new(debug: bool, settings: Settings) -> Self {
|
||||||
let mut shiftmask : [u64 ; 64] = [0 ; 64];
|
let mut shiftmask : [u64 ; 64] = [0 ; 64];
|
||||||
let mut value : u64 = 0xffffffff;
|
let mut value : u64 = 0xffffffff;
|
||||||
|
|
||||||
value = (value << 32) + value;
|
value = (value << 32) + value;
|
||||||
for item in &mut shiftmask {
|
for item in &mut shiftmask {
|
||||||
*item = value;
|
*item = value;
|
||||||
value >>= 1;
|
value >>= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut ret = Machine {
|
let num_phy_page = *settings.get(&MachineSettingKey::NumPhysPages).unwrap();
|
||||||
|
let page_size = *settings.get(&MachineSettingKey::PageSize).unwrap();
|
||||||
|
let user_stack_size = *settings.get(&MachineSettingKey::UserStackSize).unwrap();
|
||||||
|
let mem_size = (page_size*num_phy_page) as usize;
|
||||||
|
|
||||||
|
Machine {
|
||||||
|
debug,
|
||||||
pc : 0,
|
pc : 0,
|
||||||
sp: 0,
|
sp: 0,
|
||||||
int_reg : Register::<i64>::init(),
|
int_reg : { let mut r = Register::<i64>::init(); r.set_reg(10, -1); r },
|
||||||
fp_reg : Register::<f32>::init(),
|
fp_reg : Register::<f32>::init(),
|
||||||
main_memory : vec![0_u8; MEM_SIZE],
|
main_memory : vec![0_u8; mem_size],
|
||||||
shiftmask,
|
shiftmask,
|
||||||
interrupt: Interrupt::new(),
|
interrupt: Interrupt::new(),
|
||||||
registers_trace : String::from("")
|
registers_trace : String::from(""),
|
||||||
};
|
status: MachineStatus::SystemMode,
|
||||||
|
num_phy_page,
|
||||||
ret.int_reg.set_reg(10, -1);
|
page_size,
|
||||||
ret
|
user_stack_size
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read from main memory of the machine
|
/// Read from main memory of the machine
|
||||||
@ -146,7 +145,7 @@ impl Machine {
|
|||||||
/// - **machine** which contains the main memory
|
/// - **machine** which contains the main memory
|
||||||
/// - **size** the number of bytes to read (1, 2, 4, 8)
|
/// - **size** the number of bytes to read (1, 2, 4, 8)
|
||||||
/// - **address** in the memory to read
|
/// - **address** in the memory to read
|
||||||
pub fn read_memory(machine : &mut Machine, size : i32, address : usize) -> u64 {
|
pub fn read_memory(&self, size : i32, address : usize) -> u64 {
|
||||||
if ![1, 2, 4, 8].contains(&size) {
|
if ![1, 2, 4, 8].contains(&size) {
|
||||||
panic!("ERROR read_memory : wrong size parameter {size}, must be (1, 2, 4 or 8)");
|
panic!("ERROR read_memory : wrong size parameter {size}, must be (1, 2, 4 or 8)");
|
||||||
}
|
}
|
||||||
@ -154,7 +153,7 @@ impl Machine {
|
|||||||
let mut ret: u64 = 0;
|
let mut ret: u64 = 0;
|
||||||
for i in 0..size {
|
for i in 0..size {
|
||||||
ret <<= 8;
|
ret <<= 8;
|
||||||
ret += machine.main_memory[address + i as usize] as u64;
|
ret += self.main_memory[address + i as usize] as u64;
|
||||||
}
|
}
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
@ -169,13 +168,13 @@ impl Machine {
|
|||||||
/// - **size** the number of bytes to write (1, 2, 4 or 8)
|
/// - **size** the number of bytes to write (1, 2, 4 or 8)
|
||||||
/// - **address** the address to write to
|
/// - **address** the address to write to
|
||||||
/// - **value** data to be written
|
/// - **value** data to be written
|
||||||
pub fn write_memory(machine: &mut Machine, size: i32, address: usize, value: u64) {
|
pub fn write_memory(&mut self, size: i32, address: usize, value: u64) {
|
||||||
if ![1, 2, 4, 8].contains(&size) {
|
if ![1, 2, 4, 8].contains(&size) {
|
||||||
panic!("ERROR write_memory: WRONG `size` PARAMETER ({size}), must be 1, 2, 4 or 8")
|
panic!("ERROR write_memory: WRONG `size` PARAMETER ({size}), must be 1, 2, 4 or 8")
|
||||||
}
|
}
|
||||||
for i in 0..size as usize {
|
for i in 0..size as usize {
|
||||||
let inv_i = size as usize - i - 1;
|
let inv_i = size as usize - i - 1;
|
||||||
machine.main_memory[address + i] = ((value & 0xff << (8 * inv_i)) >> (inv_i * 8)) as u8;
|
self.main_memory[address + i] = ((value & 0xff << (8 * inv_i)) >> (inv_i * 8)) as u8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,11 +184,11 @@ impl Machine {
|
|||||||
/// ### Parameters
|
/// ### Parameters
|
||||||
///
|
///
|
||||||
/// - **machine** contains the memory
|
/// - **machine** contains the memory
|
||||||
pub fn _extract_memory(machine: &mut Machine){
|
pub fn _extract_memory(&mut self){
|
||||||
let file_path = "burritos_memory.txt";
|
let file_path = "burritos_memory.txt";
|
||||||
let write_to_file = |path| -> std::io::Result<File> {
|
let write_to_file = |path| -> std::io::Result<File> {
|
||||||
let mut file = File::create(path)?;
|
let mut file = File::create(path)?;
|
||||||
file.write_all(&machine.main_memory)?;
|
file.write_all(&self.main_memory)?;
|
||||||
Ok(file)
|
Ok(file)
|
||||||
};
|
};
|
||||||
match write_to_file(file_path) {
|
match write_to_file(file_path) {
|
||||||
@ -198,556 +197,566 @@ impl Machine {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_machine_status(machine: &mut 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) {
|
||||||
println!("######### Machine status #########");
|
println!("######### Machine status #########");
|
||||||
for i in (0..32).step_by(3) {
|
for i in (0..32).step_by(3) {
|
||||||
print!(">{0: <4} : {1:<16x} ", print::REG_X[i], machine.int_reg.get_reg(i));
|
print!(">{0: <4} : {1:<16x} ", instruction::REG_X[i], self.int_reg.get_reg(i as u8));
|
||||||
print!(">{0: <4} : {1:<16x} ", print::REG_X[i+1], machine.int_reg.get_reg(i+1));
|
print!(">{0: <4} : {1:<16x} ", instruction::REG_X[i+1], self.int_reg.get_reg((i+1) as u8));
|
||||||
if i+2 < 32 {
|
if i+2 < 32 {
|
||||||
print!(">{0: <4} : {1:<16x} ", print::REG_X[i+2], machine.int_reg.get_reg(i+2));
|
print!(">{0: <4} : {1:<16x} ", instruction::REG_X[i+2], self.int_reg.get_reg((i+2) as u8));
|
||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
println!("________________SP________________");
|
println!("________________SP________________");
|
||||||
let sp_index = machine.int_reg.get_reg(2);
|
let sp = self.int_reg.get_reg(2);
|
||||||
for i in 0..5 {
|
println!("SP: {:16x}", self.read_memory(8, sp as usize));
|
||||||
println!("SP+{:<2} : {:16x}", i*8, Self::read_memory(machine, 8, (sp_index + i*8) as usize));
|
|
||||||
}
|
|
||||||
println!("##################################");
|
println!("##################################");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn string_registers(machine: &mut Machine) -> String {
|
/// Get the state of the registers as a string
|
||||||
|
///
|
||||||
|
/// ### Parameters
|
||||||
|
///
|
||||||
|
/// - **machine** the machine to read the registers from
|
||||||
|
pub fn string_registers(&self) -> String {
|
||||||
let mut s = String::from("");
|
let mut s = String::from("");
|
||||||
for i in 0..32 {
|
for i in 0..32 {
|
||||||
s.push_str(format!("{} ", machine.int_reg.get_reg(i)).as_str());
|
s.push_str(format!("{} ", self.int_reg.get_reg(i)).as_str());
|
||||||
}
|
}
|
||||||
s
|
s
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Execute the instructions table of a machine putted in param
|
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
|
||||||
///
|
///
|
||||||
/// ### Parameters
|
/// ### Parameters
|
||||||
///
|
///
|
||||||
/// - **machine** which contains a table of instructions
|
/// - **machine** which contains a table of instructions
|
||||||
pub fn run(machine : &mut Machine){
|
pub fn run(&mut self, system: &mut System) {
|
||||||
while Machine::one_instruction(machine) == 0 {}
|
loop {
|
||||||
println!("trace : \n{}", machine.registers_trace);
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// execute the current instruction
|
/// 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
|
||||||
///
|
///
|
||||||
/// ### Parameters
|
/// ### Parameters
|
||||||
///
|
///
|
||||||
/// - **machine** which contains a table of instructions and a pc to the actual instruction
|
/// - **machine** which contains a table of instructions and a pc to the actual instruction
|
||||||
pub fn one_instruction(machine :&mut Machine) -> i32 {
|
pub fn one_instruction(&mut self, system: &mut System) -> Result<MachineOk, MachineError> {
|
||||||
|
|
||||||
let unsigned_reg1 : u64;
|
if self.main_memory.len() <= self.pc as usize {
|
||||||
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");
|
panic!("ERROR : number max of instructions rushed");
|
||||||
}
|
}
|
||||||
let mut val: [u8; 4] = [0; 4];
|
let mut val: [u8; 4] = [0; 4];
|
||||||
for i in 0..4 {
|
for (i, elem) in val.iter_mut().enumerate() {
|
||||||
val[i] = machine.main_memory[machine.pc as usize + i];
|
*elem = self.main_memory[self.pc as usize + i];
|
||||||
}
|
}
|
||||||
|
|
||||||
let val = u32::from_be_bytes(val) as u64;
|
let val = u32::from_le_bytes(val) as u64;
|
||||||
let inst : Instruction = decode(val);
|
let inst : Instruction = Instruction::new(val);
|
||||||
Self::print_machine_status(machine);
|
if self.debug {
|
||||||
println!("executing instruction : {:016x} at pc {:x}", val, machine.pc);
|
self.print_status();
|
||||||
println!("{}", print::print(decode(val), machine.pc as i32));
|
println!("executing instruction : {:016x} at pc {:x}", val, self.pc);
|
||||||
let trace = Self::string_registers(machine);
|
println!("{}", instruction::instruction_debug(&inst, self.pc as i32));
|
||||||
machine.registers_trace.push_str(format!("{}\n", trace).as_str());
|
let trace = Self::string_registers(self);
|
||||||
|
self.registers_trace.push_str(format!("{}\n", trace).as_str());
|
||||||
|
}
|
||||||
|
|
||||||
machine.pc += 4;
|
self.pc += 4;
|
||||||
|
|
||||||
match inst.opcode {
|
match inst.opcode {
|
||||||
|
// Treatment for: LOAD UPPER IMMEDIATE INSTRUCTION
|
||||||
RISCV_LUI => {
|
RISCV_LUI => {
|
||||||
machine.int_reg.set_reg(inst.rd as usize, inst.imm31_12 as i64);
|
self.int_reg.set_reg(inst.rd, inst.imm31_12 as i64);
|
||||||
|
Ok(MachineOk::Ok)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Treatment for: ADD UPPER IMMEDIATE TO PC INSTRUCTION
|
||||||
RISCV_AUIPC => {
|
RISCV_AUIPC => {
|
||||||
machine.int_reg.set_reg(inst.rd as usize,machine.pc as i64 - 4 + inst.imm31_12 as i64);
|
self.int_reg.set_reg(inst.rd, self.pc as i64 - 4 + inst.imm31_12 as i64);
|
||||||
|
Ok(MachineOk::Ok)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Treatement for: JUMP AND LINK INSTRUCTIONS (direct jump)
|
||||||
RISCV_JAL => {
|
RISCV_JAL => {
|
||||||
machine.int_reg.set_reg(inst.rd as usize, machine.pc as i64);
|
self.int_reg.set_reg(inst.rd, self.pc as i64);
|
||||||
machine.pc = (machine.pc as i64 + inst.imm21_1_signed as i64 - 4) as u64;
|
self.pc = (self.pc as i64 + inst.imm21_1_signed as i64 - 4) as u64;
|
||||||
|
Ok(MachineOk::Ok)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Treatment for: JUMP AND LINK REGISTER INSTRUCTIONS (indirect jump)
|
||||||
RISCV_JALR => {
|
RISCV_JALR => {
|
||||||
let tmp = machine.pc;
|
let tmp = self.pc;
|
||||||
machine.pc = (machine.int_reg.get_reg(inst.rs1 as usize) + inst.imm12_I_signed as i64) as u64 & 0xfffffffe;
|
self.pc = (self.int_reg.get_reg(inst.rs1) + inst.imm12_I_signed as i64) as u64 & 0xfffffffe;
|
||||||
machine.int_reg.set_reg(inst.rd as usize, tmp as i64);
|
self.int_reg.set_reg(inst.rd, tmp as i64);
|
||||||
|
Ok(MachineOk::Ok)
|
||||||
},
|
},
|
||||||
|
|
||||||
//******************************************************************************************
|
|
||||||
// Treatment for: BRANCH INSTRUCTIONS
|
// Treatment for: BRANCH INSTRUCTIONS
|
||||||
RISCV_BR => {
|
RISCV_BR => self.branch_instruction(inst),
|
||||||
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
|
// Treatment for: LOAD INSTRUCTIONS
|
||||||
RISCV_LD => {
|
RISCV_LD => self.load_instruction(inst),
|
||||||
match inst.funct3 {
|
|
||||||
RISCV_LD_LB | RISCV_LD_LBU => {
|
// Treatment for: STORE INSTRUCTIONS
|
||||||
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;
|
RISCV_ST => self.store_instruction(inst),
|
||||||
machine.int_reg.set_reg(inst.rd as usize, tmp);
|
|
||||||
},
|
// Treatment for: OP INSTRUCTIONS
|
||||||
RISCV_LD_LH | RISCV_LD_LHU => {
|
RISCV_OP => self.op_instruction(inst),
|
||||||
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
|
// Treatment for: OPI INSTRUCTIONS
|
||||||
RISCV_OPI => {
|
RISCV_OPI => self.opi_instruction(inst),
|
||||||
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); }
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
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 {
|
|
||||||
machine.int_reg.set_reg(inst.rd as usize, machine.int_reg.get_reg(inst.rs1 as usize) - machine.int_reg.get_reg(inst.rs2 as usize));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
RISCV_OP_SLL => {
|
|
||||||
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
|
// Treatment for: OPW INSTRUCTIONS
|
||||||
RISCV_OPW => {
|
RISCV_OPW => self.opw_instruction(inst),
|
||||||
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;
|
|
||||||
|
|
||||||
// Match case for multiplication operations (in standard extension RV32M)
|
// Treatment for OPIW INSTRUCTIONS
|
||||||
match inst.funct3 {
|
RISCV_OPIW => self.opiw_instruction(inst),
|
||||||
RISCV_OPW_M_MULW => {
|
|
||||||
machine.int_reg.set_reg(inst.rd as usize, local_data_a * local_data_b);
|
|
||||||
},
|
|
||||||
RISCV_OPW_M_DIVW => {
|
|
||||||
machine.int_reg.set_reg(inst.rd as usize, local_data_a / local_data_b);
|
|
||||||
},
|
|
||||||
RISCV_OPW_M_DIVUW => {
|
|
||||||
machine.int_reg.set_reg(inst.rd as usize, local_data_a_unsigned / local_data_b_unsigned);
|
|
||||||
},
|
|
||||||
RISCV_OPW_M_REMW => {
|
|
||||||
machine.int_reg.set_reg(inst.rd as usize, local_data_a % local_data_b);
|
|
||||||
},
|
|
||||||
RISCV_OPW_M_REMUW => {
|
|
||||||
machine.int_reg.set_reg(inst.rd as usize, local_data_a_unsigned % local_data_b_unsigned);
|
|
||||||
},
|
|
||||||
_ => {
|
|
||||||
panic!("this instruction ({}) doesn't exists", inst.value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else { // others rv64 OPW operations
|
|
||||||
let local_dataa = 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
|
// Treatment for: FLOATING POINT INSTRUCTIONS
|
||||||
match inst.funct3 {
|
RISCV_FP => self.fp_instruction(inst),
|
||||||
RISCV_OPW_ADDSUBW => {
|
|
||||||
if inst.funct7 == RISCV_OPW_ADDSUBW_ADDW {
|
// Treatment for: SYSTEM CALLS
|
||||||
machine.int_reg.set_reg(inst.rd as usize, local_dataa + local_datab);
|
RISCV_SYSTEM => self.raise_exception(ExceptionType::SyscallException, self.pc, system),
|
||||||
} else { // SUBW
|
|
||||||
machine.int_reg.set_reg(inst.rd as usize, local_dataa - local_datab);
|
// Default case
|
||||||
}
|
_ => Err(format!("{:x}: Unknown opcode\npc: {:x}", inst.opcode, self.pc))?
|
||||||
},
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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)},
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
0
|
/// 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, shamt),
|
||||||
|
RISCV_OPI_SRI => if inst.funct7_smaller == RISCV_OPI_SRI_SRLI {
|
||||||
|
compute(&|a, b| { (a >> b) & self.shiftmask[inst.shamt as usize] as i64 }, rs1, shamt)
|
||||||
|
} else { // SRAI
|
||||||
|
compute(&core::ops::Shr::shr, rs1, shamt)
|
||||||
|
}
|
||||||
|
_ => Err(format!("Unreachable in opi_instruction match! Instruction was {:?}", inst))?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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))?
|
||||||
|
}
|
||||||
|
} 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))?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(MachineOk::Ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Exectutes simple RISC-V *iw instructions on the machine
|
||||||
|
fn opiw_instruction(&mut self, inst: Instruction) -> Result<MachineOk, MachineError> {
|
||||||
|
let local_data = self.int_reg.get_reg(inst.rs1);
|
||||||
|
let result = match inst.funct3 {
|
||||||
|
RISCV_OPIW_ADDIW => local_data + inst.imm12_I_signed as i64,
|
||||||
|
RISCV_OPIW_SLLIW => local_data << inst.rs2,
|
||||||
|
RISCV_OPIW_SRW => (local_data >> inst.rs2) & if inst.funct7 == RISCV_OPIW_SRW_SRLIW { self.shiftmask[32 + inst.rs2 as usize] as i64 } else { 1 },
|
||||||
|
_ => Err(format!("Unreachable in op_instruction match! Instruction was {:?}", inst))?
|
||||||
|
};
|
||||||
|
self.int_reg.set_reg(inst.rd, result);
|
||||||
|
Ok(MachineOk::Ok)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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) & 0xffffffff),
|
||||||
|
RISCV_OPW_M_DIVW => self.int_reg.set_reg(inst.rd, local_data_a / local_data_b),
|
||||||
|
RISCV_OPW_M_DIVUW => self.int_reg.set_reg(inst.rd, local_data_a_unsigned / local_data_b_unsigned),
|
||||||
|
RISCV_OPW_M_REMW => self.int_reg.set_reg(inst.rd, local_data_a % local_data_b),
|
||||||
|
RISCV_OPW_M_REMUW => self.int_reg.set_reg(inst.rd, local_data_a_unsigned % local_data_b_unsigned),
|
||||||
|
_ => Err(format!("Unreachable in opw_instruction match! Instruction was {:?}", inst))?
|
||||||
|
}
|
||||||
|
} 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
|
/// print memory FOR DEBUG
|
||||||
///
|
///
|
||||||
/// "@"adresse [16 bytes]
|
/// "@"adress [16 bytes]
|
||||||
pub fn _print_memory(machine : &mut Machine, from: usize, to: usize) {
|
pub fn print_memory(&self, from: usize, to: usize) {
|
||||||
for i in from..to {
|
for i in from..to {
|
||||||
if i%16 == 0 {
|
if i%16 == 0 {
|
||||||
print!("\n@{:04x} ", i);
|
print!("\n@{:04x} ", i);
|
||||||
}
|
}
|
||||||
print!("{:02x}", machine.main_memory[i]);
|
print!("{:02x}", self.main_memory[i]);
|
||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get value from int register
|
||||||
pub fn read_int_register(&self, index: usize) -> i64 {
|
pub fn read_int_register(&self, index: usize) -> i64 {
|
||||||
self.int_reg.get_reg(index)
|
self.int_reg.get_reg(index as u8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get value from float register
|
||||||
pub fn read_fp_register(&self, index: usize) -> f32 {
|
pub fn read_fp_register(&self, index: usize) -> f32 {
|
||||||
self.fp_reg.get_reg(index)
|
self.fp_reg.get_reg(index as u8)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write into int register
|
||||||
pub fn write_int_register(&mut self, index: usize, value: i64) {
|
pub fn write_int_register(&mut self, index: usize, value: i64) {
|
||||||
self.int_reg.set_reg(index, value);
|
self.int_reg.set_reg(index as u8, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write info float register
|
||||||
pub fn write_fp_register(&mut self, index: usize, value: f32) {
|
pub fn write_fp_register(&mut self, index: usize, value: f32) {
|
||||||
self.fp_reg.set_reg(index, value);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
||||||
mod test {
|
mod test {
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
use crate::simulator::{machine::Machine, mem_cmp};
|
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]
|
#[test]
|
||||||
fn test_init_machine() {
|
fn test_init_machine() {
|
||||||
let _ = Machine::init_machine();
|
let _ = Machine::new(true, get_debug_configuration());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_read_memory() {
|
fn test_read_memory() {
|
||||||
let mut m = Machine::init_machine();
|
let mut m = Machine::new(true, get_debug_configuration());
|
||||||
m.main_memory[4] = 43;
|
m.main_memory[4] = 43;
|
||||||
m.main_memory[5] = 150;
|
m.main_memory[5] = 150;
|
||||||
assert_eq!((43 << 8) + 150, Machine::read_memory(&mut m, 2, 4));
|
assert_eq!((43 << 8) + 150, m.read_memory(2, 4));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_write_memory() {
|
fn test_write_memory() {
|
||||||
let mut m = Machine::init_machine();
|
let mut m = Machine::new(true, get_debug_configuration());
|
||||||
Machine::write_memory(&mut m, 2, 6, (43 << 8) + 150);
|
m.write_memory(2, 6, (43 << 8) + 150);
|
||||||
assert_eq!(43, m.main_memory[6]);
|
assert_eq!(43, m.main_memory[6]);
|
||||||
assert_eq!(150, m.main_memory[7]);
|
assert_eq!(150, m.main_memory[7]);
|
||||||
Machine::write_memory(&mut m, 4, 8, (52 << 24) + (20 << 16) + (43 << 8) + 150);
|
m.write_memory(4, 8, (52 << 24) + (20 << 16) + (43 << 8) + 150);
|
||||||
assert_eq!(52, m.main_memory[8]);
|
assert_eq!(52, m.main_memory[8]);
|
||||||
assert_eq!(20, m.main_memory[9]);
|
assert_eq!(20, m.main_memory[9]);
|
||||||
assert_eq!(43, m.main_memory[10]);
|
assert_eq!(43, m.main_memory[10]);
|
||||||
@ -756,127 +765,46 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_comp() {
|
fn test_comp() {
|
||||||
let mut m = Machine::init_machine();
|
init_test!("Comp")
|
||||||
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]
|
#[test]
|
||||||
fn test_add() {
|
fn test_add() {
|
||||||
let mut m = Machine::init_machine();
|
init_test!("Add")
|
||||||
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]
|
#[test]
|
||||||
fn test_div() {
|
fn test_div() {
|
||||||
let mut m = Machine::init_machine();
|
init_test!("Div")
|
||||||
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]
|
#[test]
|
||||||
fn test_if() {
|
fn test_if() {
|
||||||
let mut m = Machine::init_machine();
|
init_test!("If")
|
||||||
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]
|
#[test]
|
||||||
fn test_jump() {
|
fn test_jump() {
|
||||||
let mut m = Machine::init_machine();
|
init_test!("Jump")
|
||||||
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]
|
#[test]
|
||||||
fn test_mul() {
|
fn test_mul() {
|
||||||
let mut m = Machine::init_machine();
|
init_test!("Mult")
|
||||||
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]
|
#[test]
|
||||||
fn test_ret() {
|
fn test_ret() {
|
||||||
let mut m = Machine::init_machine();
|
init_test!("Ret")
|
||||||
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]
|
#[test]
|
||||||
fn test_sub() {
|
fn test_sub() {
|
||||||
let mut m = Machine::init_machine();
|
init_test!("Sub")
|
||||||
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]
|
#[test]
|
||||||
fn test_switch() {
|
fn test_switch() {
|
||||||
let mut m = Machine::init_machine();
|
init_test!("Switch")
|
||||||
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()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,7 +14,7 @@ use std::{fs, io::{BufRead, BufReader, Lines, Error}};
|
|||||||
use crate::Machine;
|
use crate::Machine;
|
||||||
|
|
||||||
/// File section
|
/// File section
|
||||||
pub struct SectionFormat{
|
pub struct SectionFormat {
|
||||||
/// Memory address of the section
|
/// Memory address of the section
|
||||||
addr: String,
|
addr: String,
|
||||||
/// The size of data in bytes
|
/// The size of data in bytes
|
||||||
@ -26,7 +26,7 @@ pub struct SectionFormat{
|
|||||||
/// # Memory section
|
/// # Memory section
|
||||||
///
|
///
|
||||||
/// Representation of a section of memory from BurritOS or NachOS
|
/// Representation of a section of memory from BurritOS or NachOS
|
||||||
pub struct Section{
|
pub struct Section {
|
||||||
/// Memory address of the section
|
/// Memory address of the section
|
||||||
addr: usize,
|
addr: usize,
|
||||||
/// The size of data in bytes
|
/// The size of data in bytes
|
||||||
@ -36,15 +36,23 @@ pub struct Section{
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Section {
|
impl Section {
|
||||||
|
|
||||||
/// Creates a memory section from a SectionFormat
|
/// Creates a memory section from a SectionFormat
|
||||||
fn from(section: &SectionFormat) -> Section {
|
fn from(section: &mut SectionFormat) -> Section {
|
||||||
let addr = usize::from_str_radix(§ion.addr, 16).unwrap_or_default();
|
let addr = usize::from_str_radix(§ion.addr, 16).unwrap_or_default();
|
||||||
let len = usize::from_str_radix(§ion.len, 16).unwrap_or_default();
|
let len = usize::from_str_radix(§ion.len, 16).unwrap_or_default();
|
||||||
let content: Vec<u8> = section.content.as_bytes().chunks(2).map(|x| {
|
let content: Vec<u8>;
|
||||||
u8::from_str_radix(std::str::from_utf8(x).unwrap_or_default(), 16).unwrap_or_default()
|
unsafe {
|
||||||
}).collect();
|
content = section.content.as_bytes_mut()
|
||||||
Section{addr, len, content}
|
.chunks_mut(4).map(
|
||||||
|
|x| {
|
||||||
|
x.reverse();
|
||||||
|
u8::from_str_radix(
|
||||||
|
std::str::from_utf8(x).unwrap_or_default(), 16
|
||||||
|
).unwrap_or_default()
|
||||||
|
}
|
||||||
|
).collect();
|
||||||
|
}
|
||||||
|
Section { addr, len, content }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Pretty prints a memory section
|
/// Pretty prints a memory section
|
||||||
@ -68,7 +76,7 @@ pub struct MemChecker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl MemChecker{
|
impl MemChecker {
|
||||||
|
|
||||||
///Translate lines of a file in e Vector of String
|
///Translate lines of a file in e Vector of String
|
||||||
///We need this method to parse the memory we received
|
///We need this method to parse the memory we received
|
||||||
@ -126,12 +134,12 @@ impl MemChecker{
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
//lecture ligne CONTENT
|
//lecture ligne CONTENT
|
||||||
let section_f = SectionFormat{
|
let mut section_f = SectionFormat {
|
||||||
addr: tmp_addr_str.clone(),
|
addr: tmp_addr_str.clone(),
|
||||||
len: tmp_len_str.clone(),
|
len: tmp_len_str.clone(),
|
||||||
content: current_line.clone().replace(' ', ""),
|
content: current_line.clone().replace(' ', ""),
|
||||||
};
|
};
|
||||||
sections.push(Section::from(§ion_f));
|
sections.push(Section::from(&mut section_f));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -169,7 +177,7 @@ impl MemChecker{
|
|||||||
machine.pc = m_c.pc as u64;
|
machine.pc = m_c.pc as u64;
|
||||||
|
|
||||||
for section in m_c.sections.iter() {
|
for section in m_c.sections.iter() {
|
||||||
for (i,b) in section.content.iter().enumerate() {
|
for (i, b) in section.content.iter().enumerate() {
|
||||||
machine.main_memory[section.addr + i] = *b;
|
machine.main_memory[section.addr + i] = *b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -206,12 +214,14 @@ impl MemChecker{
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::utility::cfg::get_debug_configuration;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fill_memory(){
|
fn test_fill_memory(){
|
||||||
let m_c = MemChecker::from("test/machine/memoryAdd.txt").unwrap();
|
let m_c = MemChecker::from("test/machine/memoryAdd.txt").unwrap();
|
||||||
let mut machine = Machine::init_machine();
|
let mut machine = Machine::new(true, get_debug_configuration());
|
||||||
MemChecker::fill_memory_from_mem_checker(&m_c, &mut machine);
|
MemChecker::fill_memory_from_mem_checker(&m_c, &mut machine);
|
||||||
MemChecker::compare_print_m_c_machine(&m_c, &mut machine);
|
MemChecker::compare_print_m_c_machine(&m_c, &mut machine);
|
||||||
}
|
}
|
||||||
@ -233,12 +243,12 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_section_content(){
|
fn test_create_section_content(){
|
||||||
let section_format = SectionFormat{
|
let mut section_format = SectionFormat{
|
||||||
addr: "0".to_string(),
|
addr: "0".to_string(),
|
||||||
len: "0".to_string(),
|
len: "0".to_string(),
|
||||||
content: "00FF0AA0A5".to_string(),
|
content: "00FF0AA0A5".to_string(),
|
||||||
};
|
};
|
||||||
let section = Section::from(§ion_format);
|
let section = Section::from(&mut section_format);
|
||||||
let expected_vec: Vec<u8> = vec![0u8, 255u8, 10u8, 160u8, 165u8];
|
let expected_vec: Vec<u8> = vec![0u8, 255u8, 10u8, 160u8, 165u8];
|
||||||
assert_eq!(section.content, expected_vec);
|
assert_eq!(section.content, expected_vec);
|
||||||
}
|
}
|
||||||
|
@ -6,15 +6,18 @@ pub struct MMU <'a>{
|
|||||||
* Cette table est associée au processus courant
|
* Cette table est associée au processus courant
|
||||||
* Cette référence peut etre mise a jour par exemple lors d'un switchTo
|
* Cette référence peut etre mise a jour par exemple lors d'un switchTo
|
||||||
*/
|
*/
|
||||||
translationTable : Option<&'a mut TranslationTable>
|
translationTable : Option<&'a mut TranslationTable>,
|
||||||
|
numPhyPages : u64,
|
||||||
|
pageSize : u64
|
||||||
}
|
}
|
||||||
|
|
||||||
impl <'a>MMU <'_>{
|
impl <'a>MMU <'_>{
|
||||||
|
|
||||||
fn create() -> MMU <'a>{
|
fn create(numPhyPages: u64, pageSize: u64) -> MMU <'a>{
|
||||||
|
MMU {
|
||||||
MMU{
|
translationTable : None,
|
||||||
translationTable : None
|
numPhyPages,
|
||||||
|
pageSize
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,7 +32,7 @@ impl <'a>MMU <'_>{
|
|||||||
MMU::translate(mmu, virt_addr, &mut phy_addr_double_check, false);
|
MMU::translate(mmu, virt_addr, &mut phy_addr_double_check, false);
|
||||||
|
|
||||||
match exc {
|
match exc {
|
||||||
ExceptionType::NO_EXCEPTION => {
|
ExceptionType::NoException => {
|
||||||
if phy_addr != phy_addr_double_check {
|
if phy_addr != phy_addr_double_check {
|
||||||
//Besoin ici d'une impl pour gestion d'exeption
|
//Besoin ici d'une impl pour gestion d'exeption
|
||||||
//dans nachos : g-machine->RaiseException(exc, virt_addr);
|
//dans nachos : g-machine->RaiseException(exc, virt_addr);
|
||||||
@ -44,7 +47,7 @@ impl <'a>MMU <'_>{
|
|||||||
_ => {
|
_ => {
|
||||||
//Besoin ici d'une impl pour gestion d'exeption
|
//Besoin ici d'une impl pour gestion d'exeption
|
||||||
//dans nachos : g-machine->RaiseException(exc, virt_addr);
|
//dans nachos : g-machine->RaiseException(exc, virt_addr);
|
||||||
println!("Error from mmu_read_mem :: Exception different from NO_EXCEPTION");
|
println!("Error from mmu_read_mem :: Exception different from NoException");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,7 +66,7 @@ impl <'a>MMU <'_>{
|
|||||||
MMU::translate(mmu, virt_addr, &mut phy_addr_double_check, true);
|
MMU::translate(mmu, virt_addr, &mut phy_addr_double_check, true);
|
||||||
|
|
||||||
match exc {
|
match exc {
|
||||||
ExceptionType::NO_EXCEPTION => {
|
ExceptionType::NoException => {
|
||||||
if phy_addr != phy_addr_double_check {
|
if phy_addr != phy_addr_double_check {
|
||||||
//Besoin ici d'une impl pour gestion d'exeption
|
//Besoin ici d'une impl pour gestion d'exeption
|
||||||
//dans nachos : g-machine->RaiseException(exc, virt_addr);
|
//dans nachos : g-machine->RaiseException(exc, virt_addr);
|
||||||
@ -78,7 +81,7 @@ impl <'a>MMU <'_>{
|
|||||||
_ => {
|
_ => {
|
||||||
//Besoin ici d'une impl pour gestion d'exeption
|
//Besoin ici d'une impl pour gestion d'exeption
|
||||||
//dans nachos : g-machine->RaiseException(exc, virt_addr);
|
//dans nachos : g-machine->RaiseException(exc, virt_addr);
|
||||||
println!("Error from mmu_write_mem :: Exception different from NO_EXCEPTION");
|
println!("Error from mmu_write_mem :: Exception different from NoException");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -88,15 +91,15 @@ impl <'a>MMU <'_>{
|
|||||||
|
|
||||||
pub fn translate(mmu : &mut MMU, virtAddr : u64, physAddr : &mut u64, writing : bool) -> ExceptionType {
|
pub fn translate(mmu : &mut MMU, virtAddr : u64, physAddr : &mut u64, writing : bool) -> ExceptionType {
|
||||||
|
|
||||||
let vpn : u64 = virtAddr/PAGE_SIZE; //virtual page index
|
let vpn : u64 = virtAddr/(mmu.pageSize); //virtual page index
|
||||||
let offset : u64 = virtAddr%PAGE_SIZE; //adresse intra page
|
let offset : u64 = virtAddr%(mmu.pageSize); //adresse intra page
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
match &mut mmu.translationTable {
|
match &mut mmu.translationTable {
|
||||||
None => {
|
None => {
|
||||||
println!("Error from translate : MMU refers to None (No page Table)");
|
println!("Error from translate : MMU refers to None (No page Table)");
|
||||||
return ExceptionType::ADDRESSERROR_EXCEPTION;
|
return ExceptionType::AddressErrorException;
|
||||||
}
|
}
|
||||||
|
|
||||||
Some(table_ref) => {
|
Some(table_ref) => {
|
||||||
@ -104,7 +107,7 @@ impl <'a>MMU <'_>{
|
|||||||
//On verifie que notre index est valide
|
//On verifie que notre index est valide
|
||||||
if vpn >= table_ref.get_max_num_pages(){
|
if vpn >= table_ref.get_max_num_pages(){
|
||||||
println!("Error from translate :: index is out of bound");
|
println!("Error from translate :: index is out of bound");
|
||||||
return ExceptionType::ADDRESSERROR_EXCEPTION;
|
return ExceptionType::AddressErrorException;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*Doc nachos dit que ce test sert a savoir si la page est mappée
|
/*Doc nachos dit que ce test sert a savoir si la page est mappée
|
||||||
@ -113,13 +116,13 @@ impl <'a>MMU <'_>{
|
|||||||
*/
|
*/
|
||||||
if !table_ref.get_bit_read(vpn) && !table_ref.get_bit_write(vpn) {
|
if !table_ref.get_bit_read(vpn) && !table_ref.get_bit_write(vpn) {
|
||||||
println!("Error from translate :: virtual page # {} not mapped",vpn);
|
println!("Error from translate :: virtual page # {} not mapped",vpn);
|
||||||
return ExceptionType::ADDRESSERROR_EXCEPTION;
|
return ExceptionType::AddressErrorException;
|
||||||
}
|
}
|
||||||
|
|
||||||
//si on souhaite effectuer un acces lecture, on verifie que l'on dispose du droit d'acces sur cette page
|
//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) {
|
if writing && !table_ref.get_bit_write(vpn) {
|
||||||
println!("Error from translate :: write access on a read only virtual page # {}",vpn);
|
println!("Error from translate :: write access on a read only virtual page # {}",vpn);
|
||||||
return ExceptionType::READONLY_EXCEPTION;
|
return ExceptionType::AddressErrorException;
|
||||||
}
|
}
|
||||||
|
|
||||||
//if the page is not yet in main memory, run the page fault manager
|
//if the page is not yet in main memory, run the page fault manager
|
||||||
@ -129,13 +132,13 @@ impl <'a>MMU <'_>{
|
|||||||
println!("We need to update the page table by raising an exception -> not implemented");
|
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
|
//Ici il faudra reverifier le bit valid apres intervention du page fault manager
|
||||||
return ExceptionType::ADDRESSERROR_EXCEPTION;
|
return ExceptionType::AddressErrorException;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Make sure that the physical adress is correct
|
//Make sure that the physical adress is correct
|
||||||
if table_ref.get_physical_page(vpn) < 0 || table_ref.get_physical_page(vpn) >= (NUM_PHY_PAGE as i32) {
|
if table_ref.get_physical_page(vpn) < 0 || table_ref.get_physical_page(vpn) >= (mmu.numPhyPages as i32) {
|
||||||
println!("Error from translate :: no valid correspondance");
|
println!("Error from translate :: no valid correspondance");
|
||||||
return ExceptionType::BUSERROR_EXCEPTION;
|
return ExceptionType::BusErrorException;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Set U/M bits to 1
|
//Set U/M bits to 1
|
||||||
@ -147,10 +150,10 @@ impl <'a>MMU <'_>{
|
|||||||
|
|
||||||
//on se permet ici la conversion du champs physical_page de i32 vers u64
|
//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
|
//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)*PAGE_SIZE + offset;
|
*physAddr = (table_ref.get_physical_page(vpn) as u64)*(mmu.pageSize) + offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ExceptionType::NO_EXCEPTION
|
ExceptionType::NoException
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,12 +1,14 @@
|
|||||||
pub mod machine;
|
pub mod machine;
|
||||||
pub mod decode;
|
pub mod error;
|
||||||
pub mod print;
|
pub mod instruction;
|
||||||
pub mod mem_cmp;
|
pub mod mem_cmp;
|
||||||
pub mod loader;
|
pub mod loader;
|
||||||
pub mod interrupt;
|
pub mod interrupt;
|
||||||
pub mod translationtable;
|
pub mod translationtable;
|
||||||
pub mod mmu;
|
pub mod mmu;
|
||||||
|
pub mod register;
|
||||||
|
|
||||||
|
/// Definition of global constants
|
||||||
pub mod global {
|
pub mod global {
|
||||||
|
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
@ -52,15 +54,15 @@ pub mod global {
|
|||||||
///
|
///
|
||||||
/// See func3 to know the type of instruction (LD, LW, LH, LB, LWU, LHU, LBU)
|
/// See func3 to know the type of instruction (LD, LW, LH, LB, LWU, LHU, LBU)
|
||||||
pub const RISCV_LD: u8 = 0x3;
|
pub const RISCV_LD: u8 = 0x3;
|
||||||
// Store instructions
|
/// Store instructions
|
||||||
pub const RISCV_ST: u8 = 0x23;
|
pub const RISCV_ST: u8 = 0x23;
|
||||||
// immediate Arithmetic operations
|
/// immediate Arithmetic operations
|
||||||
pub const RISCV_OPI: u8 = 0x13;
|
pub const RISCV_OPI: u8 = 0x13;
|
||||||
// Arithmetic operations
|
/// Arithmetic operations
|
||||||
pub const RISCV_OP: u8 = 0x33;
|
pub const RISCV_OP: u8 = 0x33;
|
||||||
/// Immediate arithmetic operations for rv64i
|
/// Immediate arithmetic operations for rv64i
|
||||||
pub const RISCV_OPIW: u8 = 0x1b;
|
pub const RISCV_OPIW: u8 = 0x1b;
|
||||||
// Arithmetic operations for rv64i
|
/// Arithmetic operations for rv64i
|
||||||
pub const RISCV_OPW: u8 = 0x3b;
|
pub const RISCV_OPW: u8 = 0x3b;
|
||||||
|
|
||||||
/// Type: B
|
/// Type: B
|
||||||
@ -212,7 +214,7 @@ pub mod global {
|
|||||||
///
|
///
|
||||||
/// Shift left logical immediate
|
/// Shift left logical immediate
|
||||||
///
|
///
|
||||||
/// `SLLI rd, rs1, shamt` => `rd <- rs1 >> shamt`
|
/// `SLLI rd, rs1, shamt` => `rd <- rs1 << shamt`
|
||||||
pub const RISCV_OPI_SLLI: u8 = 0x1;
|
pub const RISCV_OPI_SLLI: u8 = 0x1;
|
||||||
/// Shift right immediate, may be SRAI or SRLI
|
/// Shift right immediate, may be SRAI or SRLI
|
||||||
pub const RISCV_OPI_SRI: u8 = 0x5;
|
pub const RISCV_OPI_SRI: u8 = 0x5;
|
||||||
|
@ -1,414 +0,0 @@
|
|||||||
#![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));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
61
src/simulator/register.rs
Normal file
61
src/simulator/register.rs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
//! # Register
|
||||||
|
//!
|
||||||
|
//! This mod contains the definition of the Register structs
|
||||||
|
//! for use within the Machine module.
|
||||||
|
|
||||||
|
use crate::simulator::machine::{NUM_FP_REGS, NUM_INT_REGS};
|
||||||
|
use std::ops::{Add, Sub};
|
||||||
|
|
||||||
|
/// Forcing the Register struct's generic type into having the following Traits
|
||||||
|
///
|
||||||
|
/// - Add
|
||||||
|
/// - Sub
|
||||||
|
/// - PartialEq
|
||||||
|
/// - Copy
|
||||||
|
///
|
||||||
|
/// Generally speaking, only numbers have the combinaison of these traits.
|
||||||
|
pub trait RegisterNum: Add<Output=Self> + Sub<Output=Self> + PartialEq + Copy {}
|
||||||
|
|
||||||
|
impl RegisterNum for i64 {}
|
||||||
|
|
||||||
|
impl RegisterNum for f32 {}
|
||||||
|
|
||||||
|
/// Machine register array
|
||||||
|
#[derive(PartialEq)]
|
||||||
|
pub struct Register<U: RegisterNum> {
|
||||||
|
/// 32 available registers of type U
|
||||||
|
register: [U; 32]
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<U: RegisterNum> Register<U> {
|
||||||
|
/// Returns the current value held in register *position*
|
||||||
|
pub fn get_reg(&self, position: u8) -> U {
|
||||||
|
self.register[position as usize]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set value of register *position* to *value*
|
||||||
|
///
|
||||||
|
/// Checking against trying to set a new value to the 0th register
|
||||||
|
/// as its value is NOT supposed to change
|
||||||
|
pub fn set_reg(&mut self, position: u8, value: U) {
|
||||||
|
if position != 0 { self.register[position as usize] = value; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Register<i64> {
|
||||||
|
/// i64 register constructor
|
||||||
|
pub fn init() -> Register<i64> {
|
||||||
|
Register {
|
||||||
|
register: [0i64; NUM_INT_REGS]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Register<f32> {
|
||||||
|
/// f32 register constructor
|
||||||
|
pub fn init() -> Register<f32> {
|
||||||
|
Register {
|
||||||
|
register: [0f32; NUM_FP_REGS]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
121
src/utility/cfg.rs
Normal file
121
src/utility/cfg.rs
Normal file
@ -0,0 +1,121 @@
|
|||||||
|
//! Functions for burritos.cfg configuration file parsing.
|
||||||
|
//! Needed to set-up machine and system constants without
|
||||||
|
//! recompiling.
|
||||||
|
|
||||||
|
use std::{
|
||||||
|
fs::File,
|
||||||
|
path::Path,
|
||||||
|
collections::HashMap,
|
||||||
|
io::{
|
||||||
|
BufReader,
|
||||||
|
BufRead,
|
||||||
|
Error
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Aliases the rather long HashMap<MachineSettingKey, i32> type
|
||||||
|
/// to a rather simpler to understand Settings.
|
||||||
|
pub type Settings = HashMap<MachineSettingKey, u64>;
|
||||||
|
|
||||||
|
/// Keys for the Settings HashMap, represented as enums for
|
||||||
|
/// maintainability.
|
||||||
|
#[derive(Eq, Hash, PartialEq, Debug)]
|
||||||
|
pub enum MachineSettingKey {
|
||||||
|
/// Number of physical pages.
|
||||||
|
NumPhysPages,
|
||||||
|
/// Stack size.
|
||||||
|
UserStackSize,
|
||||||
|
/// Maximum size of a file name
|
||||||
|
MaxFileNameSize,
|
||||||
|
/// Number of directory entries
|
||||||
|
NumDirEntries,
|
||||||
|
/// Processor Frequency
|
||||||
|
ProcessorFrequency,
|
||||||
|
/// Disk sector size
|
||||||
|
SectorSize,
|
||||||
|
/// Memory page size
|
||||||
|
PageSize,
|
||||||
|
/// Maximum number of Virtual Pages
|
||||||
|
MaxVirtPages,
|
||||||
|
/// In case of unknown key in configuration file.
|
||||||
|
Unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Allows for converting string slices to correspoding MachineSettingKey
|
||||||
|
/// enum value.
|
||||||
|
impl From<&str> for MachineSettingKey {
|
||||||
|
fn from(s: &str) -> Self {
|
||||||
|
match s {
|
||||||
|
"NumPhysPages" => MachineSettingKey::NumPhysPages,
|
||||||
|
"UserStackSize" => MachineSettingKey::UserStackSize,
|
||||||
|
"MaxFileNameSize" => MachineSettingKey::MaxFileNameSize,
|
||||||
|
"NumDirEntries" => MachineSettingKey::NumDirEntries,
|
||||||
|
"ProcessorFrequency" => MachineSettingKey::ProcessorFrequency,
|
||||||
|
"SectorSize" => MachineSettingKey::SectorSize,
|
||||||
|
"PageSize" => MachineSettingKey::PageSize,
|
||||||
|
"MaxVirtPages" => MachineSettingKey::MaxVirtPages,
|
||||||
|
_ => MachineSettingKey::Unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to return a HashMap containing the user defined burritos configuration
|
||||||
|
/// in the burritos.cfg file.
|
||||||
|
///
|
||||||
|
/// If the file is not found, the function will return an io error.
|
||||||
|
///
|
||||||
|
/// If the configuration is invalid, the function may return a HashMap with missing or
|
||||||
|
/// non-sensical settings.
|
||||||
|
/// It is up to the caller to determine whether or not default values should be placed
|
||||||
|
/// instead of halting the program.
|
||||||
|
pub fn read_settings() -> Result<Settings, Error> {
|
||||||
|
// Opening file
|
||||||
|
let file = {
|
||||||
|
let file_path = "./burritos.cfg";
|
||||||
|
let file_path = Path::new(file_path);
|
||||||
|
match File::open(file_path) {
|
||||||
|
Ok(opened_file) => opened_file,
|
||||||
|
Err(error_message) => Err(error_message)?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let file_reader = BufReader::new(file);
|
||||||
|
let filtered_setting_strings = filter_garbage(file_reader);
|
||||||
|
let mut settings_map = Settings::new();
|
||||||
|
// Reading settings
|
||||||
|
for line in filtered_setting_strings {
|
||||||
|
let mut split_line = line.split_whitespace();
|
||||||
|
let key = split_line.next().unwrap_or("_");
|
||||||
|
split_line.next(); // Skipping '=' character
|
||||||
|
let setting = split_line.next().unwrap_or("_");
|
||||||
|
settings_map = update_settings_map(settings_map, key, setting);
|
||||||
|
}
|
||||||
|
Ok(settings_map)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a mock configuration for Machine unit testing
|
||||||
|
///
|
||||||
|
/// FIXME: Does not cover the whole configuration yet
|
||||||
|
pub fn get_debug_configuration() -> Settings {
|
||||||
|
let mut settings_map = Settings::new();
|
||||||
|
settings_map.insert(MachineSettingKey::PageSize, 2048);
|
||||||
|
settings_map.insert(MachineSettingKey::NumPhysPages, 8192);
|
||||||
|
settings_map.insert(MachineSettingKey::UserStackSize, 4096);
|
||||||
|
settings_map
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes comments and empty lines
|
||||||
|
fn filter_garbage<R: std::io::Read>(reader: BufReader<R>) -> Vec<String> {
|
||||||
|
reader.lines()
|
||||||
|
.map(|l| l.unwrap())
|
||||||
|
.filter(|l| !l.is_empty() && !l.starts_with('#'))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Inserts user settings into setting map
|
||||||
|
fn update_settings_map(mut settings_map: Settings, key: &str, setting: &str) -> Settings {
|
||||||
|
let key = MachineSettingKey::from(key);
|
||||||
|
let setting = setting.parse::<u64>().unwrap_or(0);
|
||||||
|
settings_map.insert(key, setting);
|
||||||
|
settings_map
|
||||||
|
}
|
@ -1,62 +1,95 @@
|
|||||||
//! Data structure and definition of a genericsingle-linked LIFO list.
|
//! Data structure and definition of a generic single-linked LIFO list.
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
use std::ptr;
|
||||||
|
|
||||||
|
/// Definition of the generic single-linked FIFO list
|
||||||
|
///
|
||||||
|
/// Each elements points to a single item of the list and the following one
|
||||||
|
///
|
||||||
|
/// These methods wrap unsafe instructions because it doesn't respect borrow rules per example
|
||||||
|
/// but everything has been tested with miri to assure there's no Undefined Behaviour (use-after-free, double free, etc.)
|
||||||
|
/// or memory leak
|
||||||
|
#[derive(PartialEq, Clone, Debug)]
|
||||||
pub struct List<T: PartialEq> {
|
pub struct List<T: PartialEq> {
|
||||||
head: Link<T>,
|
head: Link<T>,
|
||||||
|
tail: Link<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Link<T> = *mut Node<T>;
|
||||||
|
|
||||||
type Link<T> = Option<Box<Node<T>>>;
|
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
struct Node<T> {
|
struct Node<T> {
|
||||||
elem: T,
|
elem: T,
|
||||||
next: Link<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> {
|
impl<T: PartialEq> List<T> {
|
||||||
|
|
||||||
/// Create an empty list
|
|
||||||
pub fn new() -> Self {
|
|
||||||
List { head: None }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Push an item at the end of the list
|
/// Push an item at the end of the list
|
||||||
pub fn push(&mut self, elem: T) {
|
pub fn push(&mut self, elem: T) {
|
||||||
let new_node = Box::new(Node {
|
unsafe {
|
||||||
elem: elem,
|
let new_tail = Box::into_raw(Box::new(Node {
|
||||||
next: self.head.take(),
|
elem,
|
||||||
});
|
next: ptr::null_mut(),
|
||||||
|
}));
|
||||||
|
|
||||||
self.head = Some(new_node);
|
if !self.tail.is_null() {
|
||||||
|
(*self.tail).next = new_tail;
|
||||||
|
} else {
|
||||||
|
self.head = new_tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tail = new_tail;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve and remove the item at the end of the list.
|
/// Retrieve and remove the item at the head of the list.
|
||||||
///
|
///
|
||||||
/// Return None if list is empty
|
/// Return None if list is empty
|
||||||
pub fn pop(&mut self) -> Option<T> {
|
pub fn pop(&mut self) -> Option<T> {
|
||||||
self.head.take().map(|node| {
|
unsafe {
|
||||||
self.head = node.next;
|
if self.head.is_null() {
|
||||||
node.elem
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve without removing the item at the end of the list
|
/// Retrieve without removing the item at the head of the list
|
||||||
///
|
///
|
||||||
/// Return None if list is empty
|
/// Return None if list is empty
|
||||||
pub fn peek(&self) -> Option<&T> {
|
pub fn peek(&self) -> Option<&T> {
|
||||||
self.head.as_ref().map(|node| {
|
unsafe {
|
||||||
&node.elem
|
self.head.as_ref().map(|node| &node.elem)
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve without removing the item at the end of the list as mutable
|
/// Retrieve without removing the item at the head of the list as mutable
|
||||||
///
|
///
|
||||||
/// Return None if lsit is empty
|
/// Return None if lsit is empty
|
||||||
pub fn peek_mut(&mut self) -> Option<&mut T> {
|
pub fn peek_mut(&mut self) -> Option<&mut T> {
|
||||||
self.head.as_mut().map(|node| {
|
unsafe {
|
||||||
&mut node.elem
|
self.head.as_mut().map(|node| &mut node.elem)
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Search for an element in the list
|
/// Search for an element in the list
|
||||||
@ -66,10 +99,12 @@ impl<T: PartialEq> List<T> {
|
|||||||
/// Worst case complexity of this function is O(n)
|
/// Worst case complexity of this function is O(n)
|
||||||
pub fn contains(&self, elem: &T) -> bool {
|
pub fn contains(&self, elem: &T) -> bool {
|
||||||
let mut iter = self.iter();
|
let mut iter = self.iter();
|
||||||
let element = iter.next();
|
let mut element = iter.next();
|
||||||
while element.is_some() {
|
while element.is_some() {
|
||||||
if element.unwrap() == elem {
|
if element.unwrap() == elem {
|
||||||
return true;
|
return true;
|
||||||
|
} else {
|
||||||
|
element = iter.next();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
@ -81,26 +116,30 @@ impl<T: PartialEq> List<T> {
|
|||||||
///
|
///
|
||||||
/// Worst-case complexity is O(n)
|
/// Worst-case complexity is O(n)
|
||||||
pub fn remove(&mut self, item: T)-> bool {
|
pub fn remove(&mut self, item: T)-> bool {
|
||||||
let mut found = false;
|
unsafe {
|
||||||
let mut tmp_list: List<T> = List::new();
|
let mut current: *mut Node<T> = self.head;
|
||||||
while !self.is_empty() {
|
let mut previous: *mut Node<T> = ptr::null_mut();
|
||||||
let current = self.pop().unwrap();
|
while !current.is_null() {
|
||||||
if current != item {
|
if (*current).elem == item {
|
||||||
tmp_list.push(current);
|
if !previous.is_null() {
|
||||||
} else {
|
(*previous).next = (*current).next;
|
||||||
found = true;
|
} else {
|
||||||
break;
|
self.head = (*current).next;
|
||||||
|
}
|
||||||
|
drop(Box::from_raw(current).elem);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
previous = current;
|
||||||
|
current = (*current).next;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while !tmp_list.is_empty() {
|
false
|
||||||
self.push(tmp_list.pop().unwrap());
|
|
||||||
}
|
|
||||||
found
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return true if the list is empty, false otherwise
|
/// Return true if the list is empty, false otherwise
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.head.is_none()
|
self.head.is_null()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turn the list into an iterator for use in a for loop per example.
|
/// Turn the list into an iterator for use in a for loop per example.
|
||||||
@ -114,27 +153,34 @@ impl<T: PartialEq> List<T> {
|
|||||||
///
|
///
|
||||||
/// When you iter using this method, elements are dereferenced
|
/// When you iter using this method, elements are dereferenced
|
||||||
pub fn iter(&self) -> Iter<'_, T> {
|
pub fn iter(&self) -> Iter<'_, T> {
|
||||||
Iter { next: self.head.as_deref() }
|
unsafe {
|
||||||
|
Iter { next: self.head.as_ref() }
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Same as iter but make the iterator mutable
|
/// Same as iter but make the iterator mutable
|
||||||
pub fn iter_mut(&mut self) -> IterMut<'_, T> {
|
pub fn iter_mut(&mut self) -> IterMut<'_, T> {
|
||||||
IterMut { next: self.head.as_deref_mut() }
|
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() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: PartialEq> Drop for List<T> {
|
impl<T: PartialEq> Drop for List<T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
let mut cur_link = self.head.take();
|
while self.pop().is_some() {} // removing every item from list (necessary as we using unsafe function)
|
||||||
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> {
|
impl<T: PartialEq> Iterator for IntoIter<T> {
|
||||||
type Item = T;
|
type Item = T;
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
@ -143,34 +189,31 @@ 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> {
|
impl<'a, T> Iterator for Iter<'a, T> {
|
||||||
type Item = &'a T;
|
type Item = &'a T;
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
|
||||||
self.next.map(|node| {
|
|
||||||
self.next = node.next.as_deref();
|
|
||||||
&node.elem
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Same as Iter structure, returned item are mutable
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
pub struct IterMut<'a, T> {
|
unsafe {
|
||||||
next: Option<&'a mut Node<T>>,
|
self.next.map(|node| {
|
||||||
|
self.next = node.next.as_ref();
|
||||||
|
&node.elem
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T> Iterator for IterMut<'a, T> {
|
impl<'a, T> Iterator for IterMut<'a, T> {
|
||||||
type Item = &'a mut T;
|
type Item = &'a mut T;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
self.next.take().map(|node| {
|
unsafe {
|
||||||
self.next = node.next.as_deref_mut();
|
self.next.take().map(|node| {
|
||||||
&mut node.elem
|
self.next = node.next.as_mut();
|
||||||
})
|
&mut node.elem
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -180,7 +223,7 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn basics() {
|
fn basics() {
|
||||||
let mut list = List::new();
|
let mut list = List::default();
|
||||||
|
|
||||||
// Check empty list behaves right
|
// Check empty list behaves right
|
||||||
assert_eq!(list.pop(), None);
|
assert_eq!(list.pop(), None);
|
||||||
@ -191,7 +234,7 @@ mod test {
|
|||||||
list.push(3);
|
list.push(3);
|
||||||
|
|
||||||
// Check normal removal
|
// Check normal removal
|
||||||
assert_eq!(list.pop(), Some(3));
|
assert_eq!(list.pop(), Some(1));
|
||||||
assert_eq!(list.pop(), Some(2));
|
assert_eq!(list.pop(), Some(2));
|
||||||
|
|
||||||
// Push some more just to make sure nothing's corrupted
|
// Push some more just to make sure nothing's corrupted
|
||||||
@ -199,63 +242,144 @@ mod test {
|
|||||||
list.push(5);
|
list.push(5);
|
||||||
|
|
||||||
// Check normal removal
|
// Check normal removal
|
||||||
assert_eq!(list.pop(), Some(5));
|
assert_eq!(list.pop(), Some(3));
|
||||||
assert_eq!(list.pop(), Some(4));
|
assert_eq!(list.pop(), Some(4));
|
||||||
|
|
||||||
// Check exhaustion
|
// Check exhaustion
|
||||||
assert_eq!(list.pop(), Some(1));
|
assert_eq!(list.pop(), Some(5));
|
||||||
assert_eq!(list.pop(), None);
|
assert_eq!(list.pop(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn peek() {
|
fn peek() {
|
||||||
let mut list = List::new();
|
let mut list = List::default();
|
||||||
assert_eq!(list.peek(), None);
|
assert_eq!(list.peek(), None);
|
||||||
assert_eq!(list.peek_mut(), 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(&3));
|
assert_eq!(list.peek(), Some(&1));
|
||||||
assert_eq!(list.peek_mut(), Some(&mut 3));
|
assert_eq!(list.peek_mut(), Some(&mut 1));
|
||||||
|
|
||||||
list.peek_mut().map(|value| {
|
|
||||||
*value = 42
|
|
||||||
});
|
|
||||||
|
|
||||||
assert_eq!(list.peek(), Some(&42));
|
|
||||||
assert_eq!(list.pop(), Some(42));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn into_iter() {
|
fn into_iter() {
|
||||||
let mut list = List::new();
|
let mut list = List::default();
|
||||||
list.push(1); list.push(2); list.push(3);
|
list.push(1);
|
||||||
|
list.push(2);
|
||||||
|
list.push(3);
|
||||||
|
|
||||||
let mut iter = list.into_iter();
|
let mut iter = list.into_iter();
|
||||||
assert_eq!(iter.next(), Some(3));
|
|
||||||
assert_eq!(iter.next(), Some(2));
|
|
||||||
assert_eq!(iter.next(), Some(1));
|
assert_eq!(iter.next(), Some(1));
|
||||||
|
assert_eq!(iter.next(), Some(2));
|
||||||
|
assert_eq!(iter.next(), Some(3));
|
||||||
assert_eq!(iter.next(), None);
|
assert_eq!(iter.next(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn iter() {
|
fn iter() {
|
||||||
let mut list = List::new();
|
let mut list = List::default();
|
||||||
list.push(1); list.push(2); list.push(3);
|
list.push(1);
|
||||||
|
list.push(2);
|
||||||
|
list.push(3);
|
||||||
|
|
||||||
let mut iter = list.iter();
|
let mut iter = list.iter();
|
||||||
assert_eq!(iter.next(), Some(&3));
|
|
||||||
assert_eq!(iter.next(), Some(&2));
|
|
||||||
assert_eq!(iter.next(), Some(&1));
|
assert_eq!(iter.next(), Some(&1));
|
||||||
|
assert_eq!(iter.next(), Some(&2));
|
||||||
|
assert_eq!(iter.next(), Some(&3));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn iter_mut() {
|
fn iter_mut() {
|
||||||
let mut list = List::new();
|
let mut list = List::default();
|
||||||
list.push(1); list.push(2); list.push(3);
|
list.push(1);
|
||||||
|
list.push(2);
|
||||||
|
list.push(3);
|
||||||
|
|
||||||
let mut iter = list.iter_mut();
|
let mut iter = list.iter_mut();
|
||||||
assert_eq!(iter.next(), Some(&mut 3));
|
|
||||||
assert_eq!(iter.next(), Some(&mut 2));
|
|
||||||
assert_eq!(iter.next(), Some(&mut 1));
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1 +1,3 @@
|
|||||||
pub mod list;
|
pub mod list;
|
||||||
|
pub mod objaddr;
|
||||||
|
pub mod cfg;
|
95
src/utility/objaddr.rs
Normal file
95
src/utility/objaddr.rs
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
//! 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 {
|
||||||
|
last_id: i32,
|
||||||
|
semaphores: HashMap<i32, Semaphore>,
|
||||||
|
locks: HashMap<i32, Lock>,
|
||||||
|
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,21 +1,3 @@
|
|||||||
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:
|
clean:
|
||||||
rm -rf $(TOPDIR)/target
|
make clean -C riscv_instructions
|
||||||
|
make clean -C syscall_tests
|
6
test/riscv_instructions/.gitignore
vendored
Normal file
6
test/riscv_instructions/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
*
|
||||||
|
!.gitignore
|
||||||
|
!*.c
|
||||||
|
!*/
|
||||||
|
!*.md
|
||||||
|
!**/Makefile
|
@ -1,9 +1,14 @@
|
|||||||
|
build:
|
||||||
|
make build -C boolean_logic/
|
||||||
|
make build -C jump_instructions/
|
||||||
|
make build -C simple_arithmetics/
|
||||||
|
|
||||||
dumps:
|
dumps:
|
||||||
make dumps -C boolean_logic/
|
make dumps -C boolean_logic/
|
||||||
make dumps -C jump_instructions/
|
make dumps -C jump_instructions/
|
||||||
make dumps -C simple_arithmetics/
|
make dumps -C simple_arithmetics/
|
||||||
|
|
||||||
tests:
|
clean:
|
||||||
make tests -C boolean_logic/
|
$(MAKE) clean -C boolean_logic/
|
||||||
make tests -C jump_instructions/
|
$(MAKE) clean -C jump_instructions/
|
||||||
make tests -C simple_arithmetics/
|
$(MAKE) clean -C simple_arithmetics/
|
@ -1,9 +1,14 @@
|
|||||||
TOPDIR = ../..
|
|
||||||
include $(TOPDIR)/Makefile.tests
|
PROGRAMS = comparisons.guac if.guac switch.guac
|
||||||
|
TOPDIR = ../../..
|
||||||
|
include $(TOPDIR)/Makefile.rules
|
||||||
|
|
||||||
|
build: $(PROGRAMS)
|
||||||
|
|
||||||
dumps: comparisons.dump if.dump switch.dump
|
dumps: comparisons.dump if.dump switch.dump
|
||||||
|
|
||||||
tests: comparisons.guac if.guac switch.guac
|
clean:
|
||||||
|
$(RM) *.o *.guac
|
||||||
|
|
||||||
# Dependances
|
# Dependances
|
||||||
$(PROGRAMS): % : $(USERLIB)/sys.o $(USERLIB)/libnachos.o %.o
|
$(PROGRAMS): %.guac : $(USERLIB)/sys.o $(USERLIB)/libnachos.o %.o
|
@ -1,15 +1,13 @@
|
|||||||
int main() {
|
int main() {
|
||||||
int x = 0;
|
int x = 0;
|
||||||
int y = 1;
|
int y = 1;
|
||||||
while (x <= y) {
|
if (x > y) {
|
||||||
if (x > y) {
|
x += 1;
|
||||||
x += 1;
|
}
|
||||||
} else if (x == y) {
|
if (x == y) {
|
||||||
x += y;
|
x += y;
|
||||||
} else if (x < y) {
|
}
|
||||||
y += 1;
|
if (x < y) {
|
||||||
} else {
|
y += 1;
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,12 @@
|
|||||||
TOPDIR = ../..
|
PROGRAMS = jump.guac ret.guac
|
||||||
include $(TOPDIR)/Makefile.tests
|
TOPDIR = ../../..
|
||||||
|
include $(TOPDIR)/Makefile.rules
|
||||||
|
|
||||||
|
build: $(PROGRAMS)
|
||||||
|
|
||||||
dumps: jump.dump ret.dump
|
dumps: jump.dump ret.dump
|
||||||
|
|
||||||
tests: jump.guac ret.guac
|
clean:
|
||||||
|
$(RM) *.o *.guac
|
||||||
|
|
||||||
|
$(PROGRAMS): %.guac : $(USERLIB)/sys.o $(USERLIB)/libnachos.o %.o
|
@ -1,6 +1,12 @@
|
|||||||
TOPDIR = ../..
|
PROGRAMS = unsigned_addition.guac unsigned_division.guac unsigned_multiplication.guac unsigned_substraction.guac
|
||||||
include $(TOPDIR)/Makefile.tests
|
TOPDIR = ../../..
|
||||||
|
include $(TOPDIR)/Makefile.rules
|
||||||
|
|
||||||
|
build: $(PROGRAMS)
|
||||||
|
|
||||||
dumps: unsigned_addition.dump unsigned_division.dump unsigned_multiplication.dump unsigned_substraction.dump
|
dumps: unsigned_addition.dump unsigned_division.dump unsigned_multiplication.dump unsigned_substraction.dump
|
||||||
|
|
||||||
tests: unsigned_addition.guac unsigned_division.guac unsigned_multiplication.guac unsigned_substraction.guac
|
clean:
|
||||||
|
$(RM) *.o *.guac
|
||||||
|
|
||||||
|
$(PROGRAMS): %.guac : $(USERLIB)/sys.o $(USERLIB)/libnachos.o %.o
|
12
test/syscall_tests/Makefile
Normal file
12
test/syscall_tests/Makefile
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
PROGRAMS = halt.guac prints.guac producteur_consommateur.guac join.guac matmult.guac
|
||||||
|
TOPDIR = ../..
|
||||||
|
include $(TOPDIR)/Makefile.rules
|
||||||
|
|
||||||
|
build: $(PROGRAMS)
|
||||||
|
|
||||||
|
dumps: halt.dump prints.dump
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) *.o *.guac
|
||||||
|
|
||||||
|
$(PROGRAMS): %.guac : $(USERLIB)/sys.o $(USERLIB)/libnachos.o %.o
|
7
test/syscall_tests/halt.c
Normal file
7
test/syscall_tests/halt.c
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
#include "userlib/syscall.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
Shutdown();
|
||||||
|
return 0;
|
||||||
|
}
|
26
test/syscall_tests/join.c
Normal file
26
test/syscall_tests/join.c
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
#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;
|
||||||
|
}
|
60
test/syscall_tests/matmult.c
Normal file
60
test/syscall_tests/matmult.c
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
/* matmult.c
|
||||||
|
* Test program to do matrix multiplication on large arrays.
|
||||||
|
*
|
||||||
|
* Intended to stress virtual memory system.
|
||||||
|
*
|
||||||
|
* Ideally, we could read the matrices off of the file system,
|
||||||
|
* and store the result back to the file system!
|
||||||
|
*
|
||||||
|
* -----------------------------------------------------
|
||||||
|
* This file is part of the Nachos-RiscV distribution
|
||||||
|
* Copyright (c) 2022 University of Rennes 1.
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, version 3.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
* General Public License for more details
|
||||||
|
* (see see <http://www.gnu.org/licenses/>).
|
||||||
|
* -----------------------------------------------------
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "userlib/syscall.h"
|
||||||
|
|
||||||
|
#define Dim 10 /* sum total of the arrays doesn't fit in
|
||||||
|
* physical memory
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* The matrices to be filled-in and multiplied */
|
||||||
|
int A[Dim][Dim];
|
||||||
|
int B[Dim][Dim];
|
||||||
|
int C[Dim][Dim];
|
||||||
|
|
||||||
|
int
|
||||||
|
main()
|
||||||
|
{
|
||||||
|
int i, j, k;
|
||||||
|
|
||||||
|
Write("Start matmult\n",14,CONSOLE_OUTPUT);
|
||||||
|
|
||||||
|
for (i = 0; i < Dim; i++) /* first initialize the matrices */
|
||||||
|
for (j = 0; j < Dim; j++) {
|
||||||
|
A[i][j] = i;
|
||||||
|
B[i][j] = j;
|
||||||
|
C[i][j] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < Dim; i++) /* then multiply them together */
|
||||||
|
for (j = 0; j < Dim; j++)
|
||||||
|
for (k = 0; k < Dim; k++)
|
||||||
|
C[i][j] += A[i][k] * B[k][j];
|
||||||
|
|
||||||
|
|
||||||
|
Exit(C[Dim-1][Dim-1]); /* and then we're done */
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
11
test/syscall_tests/prints.c
Normal file
11
test/syscall_tests/prints.c
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
#include "userlib/syscall.h"
|
||||||
|
#include "userlib/libnachos.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
n_printf("Hello World 1\n");
|
||||||
|
n_printf("Hello World 2\n");
|
||||||
|
n_printf("Hello World 3\n");
|
||||||
|
n_printf("Hello World 4\n");
|
||||||
|
Shutdown();
|
||||||
|
return 0;
|
||||||
|
}
|
48
test/syscall_tests/producteur_consommateur.c
Normal file
48
test/syscall_tests/producteur_consommateur.c
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
#include "userlib/syscall.h"
|
||||||
|
#include "userlib/libnachos.h"
|
||||||
|
|
||||||
|
|
||||||
|
const int N = 3;
|
||||||
|
int iplein = 0;
|
||||||
|
int ivide = 0;
|
||||||
|
int tab[3];
|
||||||
|
SemId svide;
|
||||||
|
SemId splein;
|
||||||
|
|
||||||
|
void producteur() {
|
||||||
|
for(int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
n_printf("batir une information\n");
|
||||||
|
P(svide);
|
||||||
|
iplein = (iplein + 1) % N;
|
||||||
|
// n_printf("communique une information : %d\n", i);
|
||||||
|
tab[iplein] = i;
|
||||||
|
V(splein);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void consommateur() {
|
||||||
|
int sum = 0;
|
||||||
|
for(int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
P(splein);
|
||||||
|
ivide = (ivide +1) % N;
|
||||||
|
n_printf("recevoir une information\n");
|
||||||
|
int info = tab[ivide];
|
||||||
|
V(svide);
|
||||||
|
sum += info;
|
||||||
|
// n_printf("exploiter l'information : %d\n", info);
|
||||||
|
}
|
||||||
|
Exit(sum);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
svide = SemCreate("producteur", N);
|
||||||
|
splein = SemCreate("consommateur", 0);
|
||||||
|
ThreadId producteurTh = threadCreate("producteur", producteur);
|
||||||
|
ThreadId consommateurTh = threadCreate("consommateur", consommateur);
|
||||||
|
Join(producteurTh);
|
||||||
|
Join(consommateurTh);
|
||||||
|
Shutdown();
|
||||||
|
return 0;
|
||||||
|
}
|
@ -1,4 +0,0 @@
|
|||||||
TOPDIR = ../
|
|
||||||
include $(TOPDIR)/Makefile.tests
|
|
||||||
|
|
||||||
default: sys.o libnachos.o
|
|
7
userlib/Makefile
Normal file
7
userlib/Makefile
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
TOPDIR = ../
|
||||||
|
include $(TOPDIR)/Makefile.rules
|
||||||
|
|
||||||
|
default: sys.o libnachos.o
|
||||||
|
|
||||||
|
clean:
|
||||||
|
$(RM) libnachos.o sys.o
|
@ -1,5 +1,5 @@
|
|||||||
/* Start.s
|
/* Start.s
|
||||||
* Assembly language assist for user programs running on top of Nachos.
|
* Assembly language assist for user programs running on top of BurritOS.
|
||||||
*
|
*
|
||||||
* Since we don't want to pull in the entire C library, we define
|
* 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
|
* what we need for a user program here, namely Start and the system
|
||||||
@ -63,9 +63,9 @@ __start:
|
|||||||
* -------------------------------------------------------------
|
* -------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
.globl Halt
|
.globl Shutdown
|
||||||
.type __Halt, @function
|
.type __Shutdown, @function
|
||||||
Halt:
|
Shutdown:
|
||||||
addi a7,zero,SC_HALT
|
addi a7,zero,SC_HALT
|
||||||
ecall
|
ecall
|
||||||
jr ra
|
jr ra
|
@ -84,7 +84,7 @@
|
|||||||
typedef int t_error;
|
typedef int t_error;
|
||||||
|
|
||||||
/* Stop Nachos, and print out performance stats */
|
/* Stop Nachos, and print out performance stats */
|
||||||
void Halt();
|
void Shutdown();
|
||||||
|
|
||||||
|
|
||||||
/* Return the time spent running Nachos */
|
/* Return the time spent running Nachos */
|
183
userlib/syscall.rs
Normal file
183
userlib/syscall.rs
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
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