├── .editorconfig ├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── qa.yml │ └── rust.yml ├── .gitignore ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── screenshot.png ├── src ├── addr_width.rs ├── cli.rs ├── main.rs ├── page_table_index.rs ├── paging_info.rs └── print.rs ├── test_cli.sh └── test_res ├── 0xdeadbeef_x86.stdout.txt ├── 0xdeadbeef_x86_64.stdout.txt ├── 0xdeadbeef_x86_64_5level.stdout.txt └── 0xdeadbeef_x86_pae.stdout.txt /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | # Unix-style newlines with a newline ending every file 5 | [*] 6 | charset = utf-8 7 | end_of_line = lf 8 | insert_final_newline = true 9 | indent_style = space 10 | indent_size = 4 11 | trim_trailing_whitespace = true 12 | max_line_length = 80 13 | 14 | [Makefile] 15 | indent_style = tab 16 | 17 | [*.yml] 18 | indent_size = 2 19 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: phip1611 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: monthly 7 | open-pull-requests-limit: 10 8 | ignore: 9 | - dependency-name: "*" 10 | update-types: [ "version-update:semver-patch" ] 11 | - package-ecosystem: github-actions 12 | directory: "/" 13 | schedule: 14 | interval: monthly 15 | open-pull-requests-limit: 10 16 | -------------------------------------------------------------------------------- /.github/workflows/qa.yml: -------------------------------------------------------------------------------- 1 | name: QA 2 | on: [ merge_group, push, pull_request ] 3 | jobs: 4 | spellcheck: 5 | name: Spellcheck 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v4 9 | # Executes "typos ." 10 | - uses: crate-ci/typos@v1.32.0 11 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | # Triggers the workflow on push or pull request events (for any branch in a repository) 4 | on: [ push, pull_request, merge_group ] 5 | 6 | env: 7 | CARGO_TERM_COLOR: always 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | matrix: 13 | runs-on: [ macos-latest, ubuntu-latest, windows-latest ] 14 | # MSRV 15 | rust: [ 1.75.0, stable, nightly ] 16 | name: Build + Unit Tests 17 | runs-on: ${{ matrix.runs-on }} 18 | steps: 19 | - uses: actions/checkout@v4 20 | - name: Setup Rust toolchain 21 | uses: dtolnay/rust-toolchain@stable 22 | with: 23 | toolchain: ${{ matrix.rust }} 24 | - uses: Swatinem/rust-cache@v2 25 | - name: Check 26 | run: cargo check --all-targets --verbose 27 | - name: Build 28 | run: cargo build --all-targets --verbose 29 | - name: Run tests 30 | run: cargo test --verbose 31 | 32 | cli: 33 | runs-on: ubuntu-latest 34 | steps: 35 | - uses: actions/checkout@v4 36 | - name: Test CLI Output 37 | run: ./test_cli.sh 38 | 39 | style_checks: 40 | runs-on: ubuntu-latest 41 | strategy: 42 | matrix: 43 | rust: 44 | - 1.75.0 # MSVR 45 | steps: 46 | - uses: actions/checkout@v4 47 | - name: Setup Rust toolchain 48 | uses: dtolnay/rust-toolchain@stable 49 | with: 50 | toolchain: ${{ matrix.rust }} 51 | components: clippy, rustfmt 52 | - uses: Swatinem/rust-cache@v2 53 | - name: Rustfmt 54 | run: cargo fmt -- --check 55 | - name: Clippy 56 | run: cargo clippy --all-targets 57 | - name: Rustdoc 58 | run: cargo doc --document-private-items --no-deps 59 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog for Paging Address Calculator / `paging-calculator` 2 | 3 | ## v0.4.0 4 | - **BREAKING** The MSRV is `1.75.0` stable. 5 | - Updated dependencies. 6 | - The binary is now much smaller (637 KiB on Linux instead of 883 KiB) 7 | 8 | ## v0.3.0 (2023-09-22) 9 | - **BREAKING** The MSRV is `1.70.0` stable. 10 | - updated dependencies 11 | 12 | ## v0.2.0 (2023-03-05) 13 | - more functionality, such as `$ paging-calculator 0xdeadbeef x86 --pae` to get 14 | page table indices for x86 with the physical address extension (PAE). Type 15 | `$ paging-calculator help` for more instructions. 16 | - MSRV is `1.64.0` 17 | 18 | ## v0.1.2 (2022-12-01) 19 | - minor internal fixes 20 | 21 | ## v0.1.1 (2022-12-01) 22 | - output both, entry index and entry offset 23 | - print 32-bit addresses only with 32-bits and not 64-bits 24 | 25 | ## v0.1.0 (2022-11-30) 26 | - initial release 27 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "anstream" 7 | version = "0.6.15" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526" 10 | dependencies = [ 11 | "anstyle", 12 | "anstyle-parse", 13 | "anstyle-query", 14 | "anstyle-wincon", 15 | "colorchoice", 16 | "is_terminal_polyfill", 17 | "utf8parse", 18 | ] 19 | 20 | [[package]] 21 | name = "anstyle" 22 | version = "1.0.8" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1" 25 | 26 | [[package]] 27 | name = "anstyle-parse" 28 | version = "0.2.5" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb" 31 | dependencies = [ 32 | "utf8parse", 33 | ] 34 | 35 | [[package]] 36 | name = "anstyle-query" 37 | version = "1.1.1" 38 | source = "registry+https://github.com/rust-lang/crates.io-index" 39 | checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" 40 | dependencies = [ 41 | "windows-sys 0.52.0", 42 | ] 43 | 44 | [[package]] 45 | name = "anstyle-wincon" 46 | version = "3.0.4" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" 49 | dependencies = [ 50 | "anstyle", 51 | "windows-sys 0.52.0", 52 | ] 53 | 54 | [[package]] 55 | name = "atty" 56 | version = "0.2.14" 57 | source = "registry+https://github.com/rust-lang/crates.io-index" 58 | checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" 59 | dependencies = [ 60 | "hermit-abi", 61 | "libc", 62 | "winapi", 63 | ] 64 | 65 | [[package]] 66 | name = "bitflags" 67 | version = "2.6.0" 68 | source = "registry+https://github.com/rust-lang/crates.io-index" 69 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 70 | 71 | [[package]] 72 | name = "clap" 73 | version = "4.5.14" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "c937d4061031a6d0c8da4b9a4f98a172fc2976dfb1c19213a9cf7d0d3c837e36" 76 | dependencies = [ 77 | "clap_builder", 78 | "clap_derive", 79 | ] 80 | 81 | [[package]] 82 | name = "clap_builder" 83 | version = "4.5.14" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "85379ba512b21a328adf887e85f7742d12e96eb31f3ef077df4ffc26b506ffed" 86 | dependencies = [ 87 | "anstream", 88 | "anstyle", 89 | "clap_lex", 90 | "strsim", 91 | "terminal_size", 92 | "unicase", 93 | "unicode-width", 94 | ] 95 | 96 | [[package]] 97 | name = "clap_derive" 98 | version = "4.5.13" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | checksum = "501d359d5f3dcaf6ecdeee48833ae73ec6e42723a1e52419c79abf9507eec0a0" 101 | dependencies = [ 102 | "heck", 103 | "proc-macro2", 104 | "quote", 105 | "syn", 106 | ] 107 | 108 | [[package]] 109 | name = "clap_lex" 110 | version = "0.7.2" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97" 113 | 114 | [[package]] 115 | name = "colorchoice" 116 | version = "1.0.2" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0" 119 | 120 | [[package]] 121 | name = "derive_more" 122 | version = "1.0.0" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" 125 | dependencies = [ 126 | "derive_more-impl", 127 | ] 128 | 129 | [[package]] 130 | name = "derive_more-impl" 131 | version = "1.0.0" 132 | source = "registry+https://github.com/rust-lang/crates.io-index" 133 | checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" 134 | dependencies = [ 135 | "proc-macro2", 136 | "quote", 137 | "syn", 138 | "unicode-xid", 139 | ] 140 | 141 | [[package]] 142 | name = "errno" 143 | version = "0.3.9" 144 | source = "registry+https://github.com/rust-lang/crates.io-index" 145 | checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" 146 | dependencies = [ 147 | "libc", 148 | "windows-sys 0.52.0", 149 | ] 150 | 151 | [[package]] 152 | name = "heck" 153 | version = "0.5.0" 154 | source = "registry+https://github.com/rust-lang/crates.io-index" 155 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 156 | 157 | [[package]] 158 | name = "hermit-abi" 159 | version = "0.1.19" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 162 | dependencies = [ 163 | "libc", 164 | ] 165 | 166 | [[package]] 167 | name = "is_terminal_polyfill" 168 | version = "1.70.1" 169 | source = "registry+https://github.com/rust-lang/crates.io-index" 170 | checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" 171 | 172 | [[package]] 173 | name = "libc" 174 | version = "0.2.155" 175 | source = "registry+https://github.com/rust-lang/crates.io-index" 176 | checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" 177 | 178 | [[package]] 179 | name = "linux-raw-sys" 180 | version = "0.4.14" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" 183 | 184 | [[package]] 185 | name = "nu-ansi-term" 186 | version = "0.50.1" 187 | source = "registry+https://github.com/rust-lang/crates.io-index" 188 | checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" 189 | dependencies = [ 190 | "windows-sys 0.52.0", 191 | ] 192 | 193 | [[package]] 194 | name = "paging-calculator" 195 | version = "0.4.0" 196 | dependencies = [ 197 | "atty", 198 | "clap", 199 | "derive_more", 200 | "nu-ansi-term", 201 | ] 202 | 203 | [[package]] 204 | name = "proc-macro2" 205 | version = "1.0.86" 206 | source = "registry+https://github.com/rust-lang/crates.io-index" 207 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 208 | dependencies = [ 209 | "unicode-ident", 210 | ] 211 | 212 | [[package]] 213 | name = "quote" 214 | version = "1.0.36" 215 | source = "registry+https://github.com/rust-lang/crates.io-index" 216 | checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" 217 | dependencies = [ 218 | "proc-macro2", 219 | ] 220 | 221 | [[package]] 222 | name = "rustix" 223 | version = "0.38.34" 224 | source = "registry+https://github.com/rust-lang/crates.io-index" 225 | checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" 226 | dependencies = [ 227 | "bitflags", 228 | "errno", 229 | "libc", 230 | "linux-raw-sys", 231 | "windows-sys 0.52.0", 232 | ] 233 | 234 | [[package]] 235 | name = "strsim" 236 | version = "0.11.1" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" 239 | 240 | [[package]] 241 | name = "syn" 242 | version = "2.0.72" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" 245 | dependencies = [ 246 | "proc-macro2", 247 | "quote", 248 | "unicode-ident", 249 | ] 250 | 251 | [[package]] 252 | name = "terminal_size" 253 | version = "0.3.0" 254 | source = "registry+https://github.com/rust-lang/crates.io-index" 255 | checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" 256 | dependencies = [ 257 | "rustix", 258 | "windows-sys 0.48.0", 259 | ] 260 | 261 | [[package]] 262 | name = "unicase" 263 | version = "2.7.0" 264 | source = "registry+https://github.com/rust-lang/crates.io-index" 265 | checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" 266 | dependencies = [ 267 | "version_check", 268 | ] 269 | 270 | [[package]] 271 | name = "unicode-ident" 272 | version = "1.0.12" 273 | source = "registry+https://github.com/rust-lang/crates.io-index" 274 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 275 | 276 | [[package]] 277 | name = "unicode-width" 278 | version = "0.1.13" 279 | source = "registry+https://github.com/rust-lang/crates.io-index" 280 | checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" 281 | 282 | [[package]] 283 | name = "unicode-xid" 284 | version = "0.2.4" 285 | source = "registry+https://github.com/rust-lang/crates.io-index" 286 | checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" 287 | 288 | [[package]] 289 | name = "utf8parse" 290 | version = "0.2.2" 291 | source = "registry+https://github.com/rust-lang/crates.io-index" 292 | checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" 293 | 294 | [[package]] 295 | name = "version_check" 296 | version = "0.9.5" 297 | source = "registry+https://github.com/rust-lang/crates.io-index" 298 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 299 | 300 | [[package]] 301 | name = "winapi" 302 | version = "0.3.9" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 305 | dependencies = [ 306 | "winapi-i686-pc-windows-gnu", 307 | "winapi-x86_64-pc-windows-gnu", 308 | ] 309 | 310 | [[package]] 311 | name = "winapi-i686-pc-windows-gnu" 312 | version = "0.4.0" 313 | source = "registry+https://github.com/rust-lang/crates.io-index" 314 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 315 | 316 | [[package]] 317 | name = "winapi-x86_64-pc-windows-gnu" 318 | version = "0.4.0" 319 | source = "registry+https://github.com/rust-lang/crates.io-index" 320 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 321 | 322 | [[package]] 323 | name = "windows-sys" 324 | version = "0.48.0" 325 | source = "registry+https://github.com/rust-lang/crates.io-index" 326 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" 327 | dependencies = [ 328 | "windows-targets 0.48.5", 329 | ] 330 | 331 | [[package]] 332 | name = "windows-sys" 333 | version = "0.52.0" 334 | source = "registry+https://github.com/rust-lang/crates.io-index" 335 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 336 | dependencies = [ 337 | "windows-targets 0.52.6", 338 | ] 339 | 340 | [[package]] 341 | name = "windows-targets" 342 | version = "0.48.5" 343 | source = "registry+https://github.com/rust-lang/crates.io-index" 344 | checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" 345 | dependencies = [ 346 | "windows_aarch64_gnullvm 0.48.5", 347 | "windows_aarch64_msvc 0.48.5", 348 | "windows_i686_gnu 0.48.5", 349 | "windows_i686_msvc 0.48.5", 350 | "windows_x86_64_gnu 0.48.5", 351 | "windows_x86_64_gnullvm 0.48.5", 352 | "windows_x86_64_msvc 0.48.5", 353 | ] 354 | 355 | [[package]] 356 | name = "windows-targets" 357 | version = "0.52.6" 358 | source = "registry+https://github.com/rust-lang/crates.io-index" 359 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 360 | dependencies = [ 361 | "windows_aarch64_gnullvm 0.52.6", 362 | "windows_aarch64_msvc 0.52.6", 363 | "windows_i686_gnu 0.52.6", 364 | "windows_i686_gnullvm", 365 | "windows_i686_msvc 0.52.6", 366 | "windows_x86_64_gnu 0.52.6", 367 | "windows_x86_64_gnullvm 0.52.6", 368 | "windows_x86_64_msvc 0.52.6", 369 | ] 370 | 371 | [[package]] 372 | name = "windows_aarch64_gnullvm" 373 | version = "0.48.5" 374 | source = "registry+https://github.com/rust-lang/crates.io-index" 375 | checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" 376 | 377 | [[package]] 378 | name = "windows_aarch64_gnullvm" 379 | version = "0.52.6" 380 | source = "registry+https://github.com/rust-lang/crates.io-index" 381 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 382 | 383 | [[package]] 384 | name = "windows_aarch64_msvc" 385 | version = "0.48.5" 386 | source = "registry+https://github.com/rust-lang/crates.io-index" 387 | checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" 388 | 389 | [[package]] 390 | name = "windows_aarch64_msvc" 391 | version = "0.52.6" 392 | source = "registry+https://github.com/rust-lang/crates.io-index" 393 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 394 | 395 | [[package]] 396 | name = "windows_i686_gnu" 397 | version = "0.48.5" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" 400 | 401 | [[package]] 402 | name = "windows_i686_gnu" 403 | version = "0.52.6" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 406 | 407 | [[package]] 408 | name = "windows_i686_gnullvm" 409 | version = "0.52.6" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 412 | 413 | [[package]] 414 | name = "windows_i686_msvc" 415 | version = "0.48.5" 416 | source = "registry+https://github.com/rust-lang/crates.io-index" 417 | checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" 418 | 419 | [[package]] 420 | name = "windows_i686_msvc" 421 | version = "0.52.6" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 424 | 425 | [[package]] 426 | name = "windows_x86_64_gnu" 427 | version = "0.48.5" 428 | source = "registry+https://github.com/rust-lang/crates.io-index" 429 | checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" 430 | 431 | [[package]] 432 | name = "windows_x86_64_gnu" 433 | version = "0.52.6" 434 | source = "registry+https://github.com/rust-lang/crates.io-index" 435 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 436 | 437 | [[package]] 438 | name = "windows_x86_64_gnullvm" 439 | version = "0.48.5" 440 | source = "registry+https://github.com/rust-lang/crates.io-index" 441 | checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" 442 | 443 | [[package]] 444 | name = "windows_x86_64_gnullvm" 445 | version = "0.52.6" 446 | source = "registry+https://github.com/rust-lang/crates.io-index" 447 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 448 | 449 | [[package]] 450 | name = "windows_x86_64_msvc" 451 | version = "0.48.5" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" 454 | 455 | [[package]] 456 | name = "windows_x86_64_msvc" 457 | version = "0.52.6" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 460 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "paging-calculator" 3 | description = """ 4 | CLI utility that helps you to calculate indices into the page table from a 5 | virtual address. The tool knows multiple paging implementations, such as x86, 6 | x86 with PAE, x86_64 and x86_64 with a 5-level page table. 7 | """ 8 | version = "0.4.0" 9 | edition = "2021" 10 | keywords = ["paging", "page-table"] 11 | categories = ["command-line-utilities"] 12 | readme = "README.md" 13 | license = "MIT" 14 | homepage = "https://github.com/phip1611/paging-calculator" 15 | repository = "https://github.com/phip1611/paging-calculator" 16 | documentation = "https://docs.rs/paging-calculator" 17 | exclude = [ 18 | ".github" 19 | ] 20 | 21 | [profile.release] 22 | lto = true 23 | codegen-units = 1 24 | strip = true 25 | opt-level = "z" 26 | 27 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 28 | 29 | [dependencies.atty] 30 | version = "0.2" 31 | 32 | [dependencies.derive_more] 33 | version = "1.0" 34 | default-features = false 35 | features = [ 36 | "display" 37 | ] 38 | 39 | [dependencies.nu-ansi-term] 40 | version = "0.50" 41 | 42 | [dependencies.clap] 43 | version = "4.5" 44 | features = [ 45 | "std", 46 | "color", 47 | "help", 48 | "usage", 49 | "error-context", 50 | "suggestions", 51 | "derive", 52 | "unicode", 53 | "wrap_help" 54 | ] 55 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Philipp Schuster 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Paging Address Calculator 2 | 3 | `paging-calculator` is a CLI utility written in Rust that helps you find the indices that a 4 | virtual address will have on different architectures or paging implementations. 5 | 6 | It takes a (virtual) address in hexadecimal format and shows you which index will be used for what 7 | page-table level. It can be installed with `$ cargo install paging-calculator`. 8 | 9 | Valid inputs are: 10 | - `$ paging-calculator 0x1337` 11 | - `$ paging-calculator 0xdead_beef` (underscores are accepted) 12 | - `$ paging-calculator 0xdead_beef x86 --pae` (underscores are accepted) 13 | - `$ paging-calculator 0xdead_beef x86_64` 14 | 15 | Type `$ paging-calculator help` to get a list of all supported options. 16 | 17 | The following screenshot summarizes its functionality: 18 | 19 | ![Screenshot showing the usage of paging-calculator.](screenshot.png "Screenshot showing the usage of paging-calculator.") 20 | 21 | # Supported Platforms 22 | `paging-calculator` builds and runs on Linux, macOS, and Windows. 23 | 24 | # MSRV 25 | The MSRV is `1.75.0` stable. 26 | 27 | # Trivia 28 | I worked on a project where I need to set up page-tables on my own. I had a few 29 | problems to find out what I actually have to do and what indices are used at 30 | which level. With the help of this utility, this task becomes quite easy. 31 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/phip1611/paging-calculator/d696358fee1bcfd7d25a03a01b893eb3585462fa/screenshot.png -------------------------------------------------------------------------------- /src/addr_width.rs: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2024 Philipp Schuster 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | #[derive(Copy, Clone, Debug, derive_more::Display, PartialEq, Eq)] 25 | pub enum AddrWidth { 26 | #[display("32-bits")] 27 | Bits32, 28 | #[display("64-bits")] 29 | Bits64, 30 | } 31 | 32 | impl From for u64 { 33 | fn from(value: AddrWidth) -> Self { 34 | match value { 35 | AddrWidth::Bits32 => 32, 36 | AddrWidth::Bits64 => 64, 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/cli.rs: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2024 Philipp Schuster 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | use clap::{Parser, Subcommand, ValueEnum}; 26 | use std::error::Error; 27 | use std::fmt; 28 | use std::str::FromStr; 29 | 30 | /// A virtual address in hexadecimal representation. It be provided to the CLI 31 | /// as `0x123` or `0x1234_5678`. The `0x` prefix is required. It must be within 32 | /// the range of `u64`. Can be truncated to `u32`. In this case, the upper 32 33 | /// bits are discarded. 34 | #[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Ord, Eq, Hash)] 35 | pub struct VirtualAddress(u64); 36 | 37 | impl fmt::Display for VirtualAddress { 38 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 39 | write!(f, "0x{:016x}", self.0) 40 | } 41 | } 42 | 43 | impl VirtualAddress { 44 | const PREFIX: &'static str = "0x"; 45 | } 46 | 47 | /// Describes errors that happened when users tries to input a [`VirtualAddress`] 48 | /// via the CLI. 49 | #[derive(Copy, Clone, Debug, derive_more::Display, PartialOrd, PartialEq, Ord, Eq, Hash)] 50 | pub enum VirtualAddressError { 51 | /// The virtual address must begin with the prefix 0x. 52 | #[display("The virtual address must begin with the prefix 0x.")] 53 | MissingPrefix, 54 | /// The virtual address could not be parsed as number as `u64` 55 | #[display("The virtual address could not be parsed as number as `u64`.")] 56 | ParseIntError, 57 | } 58 | 59 | impl Error for VirtualAddressError {} 60 | 61 | impl From for VirtualAddress { 62 | fn from(value: u64) -> Self { 63 | Self(value) 64 | } 65 | } 66 | 67 | impl From for u64 { 68 | fn from(value: VirtualAddress) -> Self { 69 | value.0 70 | } 71 | } 72 | 73 | impl From for u32 { 74 | fn from(value: VirtualAddress) -> Self { 75 | (value.0 & 0xffffffff) as Self 76 | } 77 | } 78 | 79 | impl FromStr for VirtualAddress { 80 | type Err = VirtualAddressError; 81 | 82 | fn from_str(s: &str) -> Result { 83 | // Remove underscores and other clutter which are allowed for the input. 84 | let s = s.trim().to_lowercase().replace('_', ""); 85 | 86 | if !s.starts_with(Self::PREFIX) { 87 | return Err(VirtualAddressError::MissingPrefix); 88 | } 89 | 90 | // string without the prefix 91 | let s_without_prefix = &s.as_str()[Self::PREFIX.len()..]; 92 | 93 | u64::from_str_radix(s_without_prefix, 16) 94 | .map(Self) 95 | .map_err(|e| { 96 | eprintln!("{e}"); 97 | VirtualAddressError::ParseIntError 98 | }) 99 | } 100 | } 101 | 102 | /// CLI args definition of this application for `clap`. 103 | #[derive(Parser)] 104 | #[command(author, version, about)] 105 | pub struct CliArgs { 106 | #[arg()] 107 | /// A virtual address in hexadecimal representation. It be provided to 108 | /// the CLI as `0x123` or `0x1234_5678`. The `0x` prefix is required. 109 | /// It must be within the range of `u64`. 110 | pub virtual_address: VirtualAddress, 111 | 112 | /// Architecture/Paging implementation. 113 | #[command(subcommand)] 114 | pub architecture: Architecture, 115 | 116 | #[arg(long, value_enum)] 117 | pub color: Option, 118 | } 119 | 120 | /// Whether colors and other ANSI escape sequences should be used. 121 | #[derive(Copy, Clone, Debug, Default, PartialOrd, PartialEq, Ord, Eq, Hash, ValueEnum)] 122 | pub enum ColorOption { 123 | /// Never use ANSI escape sequences. 124 | Never, 125 | /// Use ANSI escape sequences if stdout points to a TTY, i.e., is not 126 | /// redirected. 127 | #[default] 128 | Auto, 129 | /// Always use ANSI escape sequences. 130 | Always, 131 | } 132 | 133 | /// Supported architectures with options. Each architecture is a subcommand of 134 | /// the CLI. 135 | #[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Ord, Eq, Hash, Subcommand)] 136 | pub enum Architecture { 137 | /// Calculate page table index information for x86. x86 uses a 2-level page 138 | /// table. 139 | X86 { 140 | /// Physical Page Extension. 141 | #[arg(long, default_value = "false")] 142 | pae: bool, 143 | }, 144 | /// Calculate page table index information for x86_64. x86_64 uses a 4-level 145 | /// whose structure is similar to x86 with Page Address Extension (PAE) but 146 | /// with 64-bit virtual addresses. 147 | #[command(id = "x86_64")] 148 | X86_64 { 149 | /// Optional feature of x86_64 that adds one additional level to the 150 | /// 4-level page-table of 151 | /// `x86_64`. 152 | #[arg(short = '5', long, default_value = "false")] 153 | five_level: bool, 154 | }, 155 | } 156 | 157 | #[cfg(test)] 158 | mod tests { 159 | use super::*; 160 | 161 | #[test] 162 | fn test_virtual_addr_from_str() { 163 | assert_eq!(VirtualAddress::from_str("0x123"), Ok(0x123.into())); 164 | assert_eq!( 165 | VirtualAddress::from_str("0xdead_beef"), 166 | Ok(0xdead_beef.into()) 167 | ); 168 | assert_eq!( 169 | VirtualAddress::from_str(" 0xdEAd_bEEF "), 170 | Ok(0xdead_beef.into()) 171 | ); 172 | } 173 | 174 | #[test] 175 | fn test_virtual_addr_64_to_32_bit() { 176 | let v_addr = VirtualAddress::from_str("0xdead_beef_1337_1337"); 177 | assert_eq!(v_addr, Ok(0xdead_beef_1337_1337.into())); 178 | let v_addr = v_addr.unwrap(); 179 | assert_eq!(u32::from(v_addr), 0x1337_1337); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2024 Philipp Schuster 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | //! CLI utility that helps you to calculate indices into the page table from a 26 | //! virtual address. The tool knows multiple paging implementations, such as x86, 27 | //! x86 with PAE, x86_64 and x86_64 with a 5-level page table. 28 | 29 | #![deny( 30 | clippy::all, 31 | clippy::cargo, 32 | clippy::nursery, 33 | // clippy::restriction, 34 | // clippy::pedantic 35 | )] 36 | // now allow a few rules which are denied by the above statement 37 | // --> they are ridiculous and not necessary 38 | #![allow( 39 | clippy::suboptimal_flops, 40 | clippy::redundant_pub_crate, 41 | clippy::fallible_impl_from, 42 | clippy::multiple_crate_versions 43 | )] 44 | // allow: required because of derive_more::Display macro 45 | #![allow(clippy::use_self)] 46 | #![deny(missing_docs)] 47 | #![deny(missing_debug_implementations)] 48 | #![deny(rustdoc::all)] 49 | 50 | mod addr_width; 51 | mod cli; 52 | mod page_table_index; 53 | mod paging_info; 54 | mod print; 55 | 56 | use crate::cli::{CliArgs, ColorOption}; 57 | use crate::print::USE_ANSI; 58 | use clap::Parser; 59 | use std::sync::atomic::Ordering; 60 | 61 | fn main() { 62 | // parse the CLI args. parse() is generated by clap. 63 | let cli: CliArgs = CliArgs::parse(); 64 | 65 | configure_ansi_setting(cli.color.unwrap_or_default()); 66 | 67 | print::print(&cli); 68 | } 69 | 70 | /// Sets the global variable [`USE_ANSI`] depending on the value of 71 | /// [`ColorOption`]. 72 | fn configure_ansi_setting(cfg: ColorOption) { 73 | let use_ansi = match cfg { 74 | ColorOption::Never => false, 75 | ColorOption::Auto => ansi_auto_detection(), 76 | ColorOption::Always => true, 77 | }; 78 | // according to nu_ansi_doc, this is required 79 | #[cfg(target_os = "windows")] 80 | if use_ansi { 81 | let _ = nu_ansi_term::enable_ansi_support(); 82 | } 83 | USE_ANSI.store(use_ansi, Ordering::SeqCst); 84 | } 85 | 86 | /// Performs the auto-detection to see if stdout points to a TTY. If this is 87 | /// the case, I expect that ANSI escape sequences are supported. 88 | fn ansi_auto_detection() -> bool { 89 | atty::is(atty::Stream::Stdout) 90 | } 91 | 92 | #[cfg(test)] 93 | mod tests { 94 | // use super::*; 95 | } 96 | -------------------------------------------------------------------------------- /src/page_table_index.rs: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2024 Philipp Schuster 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | //! Module with utilities to calculate the index into a page table for a given 26 | //! page table and given paging characteristics. 27 | 28 | use crate::addr_width::AddrWidth; 29 | use crate::cli::VirtualAddress; 30 | 31 | /// Creates a bitmask with only ones from a number that describes how many ones 32 | /// there should be `(0..=64)`. The ones are filled in from the right side. 33 | pub fn one_bitmask_of_length(mut val: u64) -> u64 { 34 | assert!(val <= 64); 35 | let mut bitmask = 0; 36 | while val > 0 { 37 | bitmask <<= 1; 38 | bitmask |= 1; 39 | val -= 1; 40 | } 41 | bitmask 42 | } 43 | 44 | /// Contains the page table lookup meta info for a virtual address and a certain 45 | /// level. Meta means that only information for the lookup itself are included 46 | /// but not the lookup itself. 47 | #[derive(Debug)] 48 | pub struct PageTableLookupMetaInfo { 49 | /// Virtual address used to get the lookup info. 50 | #[allow(unused)] 51 | pub v_addr: VirtualAddress, 52 | /// Used level for the lookup. 53 | pub level: u64, 54 | /// Index into the page table. Between 0 and N-1, where N is the amount of 55 | /// entries for the page table. 56 | pub index: u64, 57 | /// Amount of bits needed for a shift of the virtual address so that the 58 | /// index bits stand on the most-right position. 59 | #[allow(unused)] 60 | pub shift: u64, 61 | /// Like `v_addr` but all bits irrelevant for the given level are zeroes. 62 | #[allow(unused)] 63 | pub relevant_part_of_addr: u64, 64 | } 65 | 66 | /// Calculates the index into the page table for the given level and the 67 | /// given paging implementation characteristics. 68 | /// 69 | /// # Parameters 70 | /// - `index_bits` - number of how many bits index into each page table (e.g. 71 | /// 10 on x86 or 9 on x86 with PAE or `x86_64`) 72 | /// - `page_offset_bits` - number of how many bits index into the page (e.g. 12 73 | /// on `x86` and `x86_64`, i.e., 4096 bytes per page) 74 | /// - `addr` - Virtual Address used to look-up the page table. 75 | /// - `level` - Level of the page table. Must be bigger than zero! 76 | /// - `addr_width` - Width of the address. See [`AddrWidth`]. 77 | pub fn calculate_page_table_index( 78 | index_bits: u64, 79 | page_offset_bits: u64, 80 | v_addr: impl Into, 81 | // Level is always at least 1, as level 0 means the page itself is indexed. 82 | level: u64, 83 | addr_width: AddrWidth, 84 | ) -> PageTableLookupMetaInfo { 85 | assert!(index_bits > 0); 86 | assert!(page_offset_bits > 0); 87 | assert!(level > 0); 88 | 89 | let v_addr = v_addr.into(); 90 | let addr = u64::from(v_addr); 91 | let addr = if addr_width == AddrWidth::Bits32 { 92 | addr & u32::MAX as u64 93 | } else { 94 | addr 95 | }; 96 | 97 | // Shift the bits that index into the page table to the right. 98 | // To do that, we calc the number of bits to shift the virtual address. 99 | let shift = index_bits * (level - 1) + page_offset_bits; 100 | 101 | let shifted_addr = addr >> shift; 102 | 103 | let bitmask = one_bitmask_of_length(index_bits); 104 | 105 | let index = shifted_addr & bitmask; 106 | let relevant_part_of_addr = addr & (bitmask << shift); 107 | 108 | PageTableLookupMetaInfo { 109 | v_addr, 110 | level, 111 | index, 112 | shift, 113 | relevant_part_of_addr, 114 | } 115 | } 116 | 117 | #[cfg(test)] 118 | mod tests { 119 | use super::*; 120 | 121 | #[test] 122 | fn test_one_bitmask_of_length() { 123 | assert_eq!(one_bitmask_of_length(0), 0); 124 | assert_eq!(one_bitmask_of_length(1), 1); 125 | assert_eq!(one_bitmask_of_length(2), 0b11); 126 | assert_eq!(one_bitmask_of_length(4), 0xf); 127 | assert_eq!(one_bitmask_of_length(64), !0); 128 | } 129 | 130 | #[test] 131 | fn test_calculate_page_table_index_x86() { 132 | // a 32-bit address written so that it is separated by the corresponding levels 133 | // of page table on x86. 134 | #[allow(clippy::unusual_byte_groupings)] 135 | let addr = 0b1111111111_1010101010_001111000011; 136 | 137 | { 138 | let PageTableLookupMetaInfo { 139 | index: l2_index, 140 | relevant_part_of_addr: l2_bits, 141 | .. 142 | } = calculate_page_table_index(10, 12, addr, 2, AddrWidth::Bits32); 143 | assert_eq!( 144 | l2_index, 0b1111111111, 145 | "Should be 0b1111111111 but is {l2_index:#b}", 146 | ); 147 | let expected_bits: u64 = 0b1111111111 << (10 + 12); 148 | assert_eq!( 149 | l2_bits, expected_bits, 150 | "Should be {l2_bits:#b} but is {expected_bits:#b}" 151 | ); 152 | } 153 | 154 | { 155 | let PageTableLookupMetaInfo { 156 | index: l1_index, 157 | relevant_part_of_addr: l1_bits, 158 | .. 159 | } = calculate_page_table_index(10, 12, addr, 1, AddrWidth::Bits32); 160 | assert_eq!( 161 | l1_index, 0b1010101010, 162 | "Should be 0b1010101010 but is {l1_index:#b}", 163 | ); 164 | let expected_bits: u64 = 0b1010101010 << 12; 165 | assert_eq!( 166 | l1_bits, expected_bits, 167 | "Should be {l1_bits:#b} but is {expected_bits:#b}" 168 | ); 169 | } 170 | } 171 | 172 | #[test] 173 | fn test_calculate_page_table_index_x86_pae() { 174 | // a 32-bit address written so that it is separated by the corresponding 175 | // levels of page table on x86 with PAE. 176 | #[allow(clippy::unusual_byte_groupings)] 177 | let addr = 0b10_111111111_010101010_001111000011; 178 | 179 | { 180 | let PageTableLookupMetaInfo { 181 | index: l3_index, 182 | relevant_part_of_addr: l3_bits, 183 | .. 184 | } = calculate_page_table_index(9, 12, addr, 3, AddrWidth::Bits32); 185 | assert_eq!(l3_index, 0b10, "Should be 0b10 but is {l3_index:#b}",); 186 | let expected_bits: u64 = 0b10 << (9 * 2 + 12); 187 | assert_eq!( 188 | l3_bits, expected_bits, 189 | "Should be {l3_bits:#b} but is {expected_bits:#b}" 190 | ); 191 | } 192 | 193 | { 194 | let PageTableLookupMetaInfo { 195 | index: l2_index, 196 | relevant_part_of_addr: l2_bits, 197 | .. 198 | } = calculate_page_table_index(9, 12, addr, 2, AddrWidth::Bits32); 199 | assert_eq!( 200 | l2_index, 0b111111111, 201 | "Should be 0b111111111 but is {l2_index:#b}", 202 | ); 203 | let expected_bits: u64 = 0b111111111 << (9 + 12); 204 | assert_eq!( 205 | l2_bits, expected_bits, 206 | "Should be {l2_bits:#b} but is {expected_bits:#b}" 207 | ); 208 | } 209 | 210 | { 211 | let PageTableLookupMetaInfo { 212 | index: l1_index, 213 | relevant_part_of_addr: l1_bits, 214 | .. 215 | } = calculate_page_table_index(9, 12, addr, 1, AddrWidth::Bits32); 216 | assert_eq!( 217 | l1_index, 0b010101010, 218 | "Should be 0b010101010 but is {l1_index:#b}", 219 | ); 220 | let expected_bits: u64 = 0b010101010 << 12; 221 | assert_eq!( 222 | l1_bits, expected_bits, 223 | "Should be {l1_bits:#b} but is {expected_bits:#b}" 224 | ); 225 | } 226 | } 227 | 228 | #[test] 229 | #[allow(clippy::identity_op)] 230 | fn test_calculate_page_table_index_64() { 231 | // a 64-bit address written so that it is separated by the corresponding 232 | // levels of page table on x86_64. 233 | #[allow(clippy::unusual_byte_groupings)] 234 | let addr = 0b000100000_000011111_111111111_010101010_001111000011; 235 | 236 | { 237 | let PageTableLookupMetaInfo { 238 | index: l4_index, 239 | relevant_part_of_addr: l4_bits, 240 | .. 241 | } = calculate_page_table_index(9, 12, addr, 4, AddrWidth::Bits64); 242 | assert_eq!( 243 | l4_index, 0b000100000, 244 | "Should be 0b000100000 but is {l4_index:#b}" 245 | ); 246 | let expected_bits: u64 = 0b000100000 << (3 * 9 + 12); 247 | assert_eq!( 248 | l4_bits, expected_bits, 249 | "Should be {l4_bits:#b} but is {expected_bits:#b}" 250 | ); 251 | } 252 | 253 | { 254 | let PageTableLookupMetaInfo { 255 | index: l3_index, 256 | relevant_part_of_addr: l3_bits, 257 | .. 258 | } = calculate_page_table_index(9, 12, addr, 3, AddrWidth::Bits64); 259 | assert_eq!( 260 | l3_index, 0b000011111, 261 | "Should be 0b000011111 but is {l3_index:#b}" 262 | ); 263 | let expected_bits: u64 = 0b000011111 << (2 * 9 + 12); 264 | assert_eq!( 265 | l3_bits, expected_bits, 266 | "Should be {l3_bits:#b} but is {expected_bits:#b}" 267 | ); 268 | } 269 | 270 | { 271 | let PageTableLookupMetaInfo { 272 | index: l2_index, 273 | relevant_part_of_addr: l2_bits, 274 | .. 275 | } = calculate_page_table_index(9, 12, addr, 2, AddrWidth::Bits64); 276 | assert_eq!( 277 | l2_index, 0b111111111, 278 | "Should be 0b111111111 but is {l2_index:#b}" 279 | ); 280 | let expected_bits: u64 = 0b111111111 << (9 + 12); 281 | assert_eq!( 282 | l2_bits, expected_bits, 283 | "Should be {l2_bits:#b} but is {expected_bits:#b}" 284 | ); 285 | } 286 | 287 | { 288 | let PageTableLookupMetaInfo { 289 | index: l1_index, 290 | relevant_part_of_addr: l1_bits, 291 | .. 292 | } = calculate_page_table_index(9, 12, addr, 1, AddrWidth::Bits64); 293 | assert_eq!( 294 | l1_index, 0b010101010, 295 | "Should be 0b010101010 but is {l1_index:#b}" 296 | ); 297 | let expected_bits: u64 = 0b010101010 << 12; 298 | assert_eq!( 299 | l1_bits, expected_bits, 300 | "Should be {l1_bits:#b} but is {expected_bits:#b}" 301 | ); 302 | } 303 | } 304 | } 305 | -------------------------------------------------------------------------------- /src/paging_info.rs: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2024 Philipp Schuster 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | //! Module for specific paging implementations. 25 | 26 | use crate::addr_width::AddrWidth; 27 | use crate::cli::{Architecture, VirtualAddress}; 28 | use crate::page_table_index::{calculate_page_table_index, PageTableLookupMetaInfo}; 29 | 30 | #[derive(Debug)] 31 | pub struct PagingImplInfo { 32 | /// Short name of the paging implementation. 33 | pub name: &'static str, 34 | /// Descriptive text of the paging implementation. 35 | pub description: &'static str, 36 | /// Address width. 37 | pub addr_width: AddrWidth, 38 | /// Number of bits used to index into the page. 2 to the power of this value 39 | /// equals the page size. 40 | pub page_offset_bits: u64, 41 | /// Number of bits used to index into a page table. 2 to the power of this 42 | /// value equals the number of entries per page table. This implementation 43 | /// relies on the fact that the amount of bits indexing a page-table do not 44 | /// dynamically vary in the middle of the address, which is not done by any 45 | /// paging implementation luckily. 46 | pub page_table_index_bits: u64, 47 | /// Size of a page table entry in bytes. 48 | pub page_table_entry_size: u64, 49 | /// Number of page-table levels. 50 | pub levels: u64, 51 | } 52 | 53 | impl PagingImplInfo { 54 | /// Const constructor for [`PagingImplInfo`] from [`Architecture`]. Returns one 55 | /// of the constants of the [`impls`] module. 56 | pub const fn from_arch(arch: Architecture) -> Self { 57 | match arch { 58 | Architecture::X86 { pae: false, .. } => impls::X86, 59 | Architecture::X86 { pae: true, .. } => impls::X86_PAE, 60 | Architecture::X86_64 { 61 | five_level: false, .. 62 | } => impls::X86_64, 63 | Architecture::X86_64 { 64 | five_level: true, .. 65 | } => impls::X86_64_5LEVEL, 66 | } 67 | } 68 | 69 | /// Calculates the [`PageTableLookupMetaInfo`] for all levels for a virtual 70 | /// address and the given paging [`PagingImplInfo`]. The amount of results 71 | /// corresponds to the amount of page-table levels. The first element 72 | /// corresponds to level 1 and the last element to level n. 73 | pub fn calc_page_table_lookup_meta_info( 74 | &self, 75 | v_addr: VirtualAddress, 76 | ) -> Vec { 77 | let mut level = 0; 78 | let mut level_info_vec = vec![]; 79 | while level < self.levels { 80 | level += 1; 81 | let info = calculate_page_table_index( 82 | self.page_table_index_bits, 83 | self.page_offset_bits, 84 | v_addr, 85 | level, 86 | self.addr_width, 87 | ); 88 | level_info_vec.push(info); 89 | } 90 | level_info_vec 91 | } 92 | } 93 | 94 | pub mod impls { 95 | use super::*; 96 | use std::mem::size_of; 97 | 98 | pub const X86: PagingImplInfo = PagingImplInfo { 99 | name: "x86 32-bit paging", 100 | levels: 2, 101 | description: "x86 paging uses a 2-level page table. The page is indexed by 12 bits,\n\ 102 | which results in a page-size of 4096 bytes. Each page table is indexed by 10\n\ 103 | bits and has 2^10 == 1024 entries. Each page-table entry is 32-bit in size.\n\ 104 | Hence, a page table occupies the size of a page. Huge pages have a size of\n\ 105 | 2^22 == 4 MiB.", 106 | addr_width: AddrWidth::Bits32, 107 | page_offset_bits: 12, 108 | page_table_index_bits: 10, 109 | page_table_entry_size: size_of::() as u64, 110 | }; 111 | 112 | pub const X86_PAE: PagingImplInfo = PagingImplInfo { 113 | name: "x86 32-bit paging with PAE", 114 | levels: 3, 115 | description: 116 | "x86 with the Physical Address Extension (PAE) paging uses a 3-level page table,\n\ 117 | that enables to access more than 32-bit of physical address space. The page\n\ 118 | is indexed by 12 bits, which results in a page-size of 4096 bytes. Tables\n\ 119 | at level 1 and 2 are indexed by 9 bits and have 2^9 == 512 entries. The third-\n\ 120 | level page table is indexed by 2 bits and has 2^2 == 4 entries. Each page-table\n\ 121 | entry is 64-bit in size. Hence, a page table at levels 1 and 2 occupies the size\n\ 122 | of a page whereas the level 3 page table occupies 32 byte. Huge pages have a size\n\ 123 | of 2^21 == 2 MiB and are only valid on level 2.", 124 | addr_width: AddrWidth::Bits32, 125 | page_offset_bits: 12, 126 | page_table_index_bits: 9, 127 | page_table_entry_size: size_of::() as u64, 128 | }; 129 | 130 | pub const X86_64: PagingImplInfo = PagingImplInfo { 131 | name: "x86_64 paging", 132 | levels: 4, 133 | description: "x86_64 paging uses a 4-level page table. The page is indexed by 12 bits,\n\ 134 | which results in a page-size of 4096 bytes. Each page table is indexed by 9\n\ 135 | bits and has 2^9 == 512 entries. Each page-table entry is 64-bit in size. Hence,\n\ 136 | a page table occupies the size of a page. Huge pages have a size of\n\ 137 | 2^21 == 2 MiB or 2^30 == 1 GiB. Huge pages are only valid on levels 2 or 3.", 138 | addr_width: AddrWidth::Bits64, 139 | page_offset_bits: 12, 140 | page_table_index_bits: 9, 141 | page_table_entry_size: size_of::() as u64, 142 | }; 143 | 144 | pub const X86_64_5LEVEL: PagingImplInfo = PagingImplInfo { 145 | name: "x86_64 paging (5-level)", 146 | levels: 5, 147 | description: "x86_64 paging optionally uses a 5-level page table. The page is indexed\n\ 148 | by 12 bits, which results in a page-size of 4096 bytes. Each page table is\n\ 149 | indexed by 9 bits and has 2^9 == 512 entries. Each page-table entry is 64-bit in\n\ 150 | size. Hence, a page table occupies the size of a page. Huge pages have a size of\n\ 151 | 2^21 == 2 MiB or 2^30 == 1 GiB. Huge pages are only valid on levels 2 or 3.", 152 | addr_width: AddrWidth::Bits64, 153 | page_offset_bits: 12, 154 | page_table_index_bits: 9, 155 | page_table_entry_size: size_of::() as u64, 156 | }; 157 | } 158 | 159 | #[cfg(test)] 160 | mod tests { 161 | use super::*; 162 | 163 | #[test] 164 | fn test_calc_page_table_lookup_meta_info_x86() { 165 | // a 32-bit address written so that it is separated by the corresponding 166 | // levels of page table on x86. 167 | #[allow(clippy::unusual_byte_groupings)] 168 | let addr = 0b1111111111_1010101010_001111000011.into(); 169 | 170 | let vec = impls::X86.calc_page_table_lookup_meta_info(addr); 171 | assert_eq!(vec[0].index, 0b1010101010); 172 | assert_eq!(vec[1].index, 0b1111111111); 173 | assert_eq!(vec.len(), 2); 174 | } 175 | 176 | #[test] 177 | fn test_calc_page_table_lookup_meta_info_x86_pae() { 178 | // a 32-bit address written so that it is separated by the corresponding 179 | // levels of page table on x86 with PAE. 180 | #[allow(clippy::unusual_byte_groupings)] 181 | let addr = 0b10_111111111_010101010_001111000011.into(); 182 | 183 | let vec = impls::X86_PAE.calc_page_table_lookup_meta_info(addr); 184 | assert_eq!(vec[0].index, 0b010101010); 185 | assert_eq!(vec[1].index, 0b111111111); 186 | assert_eq!(vec[2].index, 0b10); 187 | assert_eq!(vec.len(), 3); 188 | } 189 | 190 | #[test] 191 | fn test_calc_page_table_lookup_meta_info_x86_64() { 192 | // a 64-bit address written so that it is separated by the corresponding 193 | // levels of page table on x86_64. 194 | #[allow(clippy::unusual_byte_groupings)] 195 | let addr = 0b000100000_000011111_111111111_010101010_001111000011.into(); 196 | 197 | let vec = impls::X86_64.calc_page_table_lookup_meta_info(addr); 198 | assert_eq!(vec[0].index, 0b010101010); 199 | assert_eq!(vec[1].index, 0b111111111); 200 | assert_eq!(vec[2].index, 0b000011111); 201 | assert_eq!(vec[3].index, 0b000100000); 202 | assert_eq!(vec.len(), 4); 203 | } 204 | 205 | #[test] 206 | fn test_calc_page_table_lookup_meta_info_x86_64_4level() { 207 | // a 64-bit address written so that it is separated by the corresponding 208 | // levels of page table on x86_64. 209 | #[allow(clippy::unusual_byte_groupings)] 210 | let addr = 0b011101110_000100000_000011111_111111111_010101010_001111000011.into(); 211 | 212 | let vec = impls::X86_64_5LEVEL.calc_page_table_lookup_meta_info(addr); 213 | assert_eq!(vec[0].index, 0b010101010); 214 | assert_eq!(vec[1].index, 0b111111111); 215 | assert_eq!(vec[2].index, 0b000011111); 216 | assert_eq!(vec[3].index, 0b000100000); 217 | assert_eq!(vec[4].index, 0b011101110); 218 | assert_eq!(vec.len(), 5); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/print.rs: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2024 Philipp Schuster 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | const CRATE_VERSION: &str = env!("CARGO_PKG_VERSION"); 26 | 27 | /// Whether ANSI escape sequences should be used or not. 28 | pub static USE_ANSI: AtomicBool = AtomicBool::new(false); 29 | 30 | use crate::addr_width::AddrWidth; 31 | use crate::cli::{CliArgs, VirtualAddress}; 32 | use crate::page_table_index::PageTableLookupMetaInfo; 33 | use crate::paging_info::PagingImplInfo; 34 | use crate::print::ansi_styles::{paint_heading, paint_hint}; 35 | use std::sync::atomic::AtomicBool; 36 | 37 | fn print_header(paging_info: &PagingImplInfo, v_addr: VirtualAddress) { 38 | print!( 39 | "{}", 40 | paint_heading(&format!( 41 | "Page Table Calculator (v{}): {}", 42 | CRATE_VERSION, paging_info.name 43 | )) 44 | ); 45 | println!(); 46 | println!("{}", paging_info.description); 47 | println!(); 48 | if paging_info.addr_width == AddrWidth::Bits32 { 49 | println!( 50 | "address : 0x{:x} {info}", 51 | u64::from(v_addr) & 0xffffffff, 52 | info = paint_hint("(user input truncated to 32-bit)") 53 | ); 54 | println!("address (bits): 0b{:032b}", u64::from(v_addr) & 0xffffffff); 55 | } else { 56 | println!("address : {v_addr}"); 57 | println!("address (bits): 0b{:064b}", u64::from(v_addr)); 58 | } 59 | } 60 | 61 | /// Prints the information to the screen. 62 | pub fn print(cli_input: &CliArgs) { 63 | let v_addr = cli_input.virtual_address; 64 | let paging_impl_info = PagingImplInfo::from_arch(cli_input.architecture); 65 | print_header(&paging_impl_info, v_addr); 66 | 67 | let page_table_lookup_info = paging_impl_info.calc_page_table_lookup_meta_info(v_addr); 68 | 69 | for info in page_table_lookup_info.iter().rev() { 70 | print!("level {} bits : ", info.level); 71 | print_relevant_bits_highlighted(info, &paging_impl_info); 72 | println!(); 73 | } 74 | 75 | for (is_first, info) in page_table_lookup_info 76 | .iter() 77 | .rev() 78 | .enumerate() 79 | .map(|(i, info)| (i == 0, info)) 80 | { 81 | print!("level {} entry index : {:>6}", info.level, info.index); 82 | if is_first { 83 | print!(" {info}", info = paint_hint("(number of entry)")); 84 | } 85 | println!(); 86 | 87 | print!( 88 | "level {} entry offset: 0x{:04x}", 89 | info.level, 90 | info.index * paging_impl_info.page_table_entry_size 91 | ); 92 | if is_first { 93 | print!( 94 | " {info}", 95 | info = paint_hint("(offset into the page table for that entry)") 96 | ); 97 | } 98 | println!(); 99 | } 100 | } 101 | 102 | // Prints the relevant bits used for the indexing and highlights them in red. 103 | // Others are zeroed. 104 | fn print_relevant_bits_highlighted(info: &PageTableLookupMetaInfo, paging_info: &PagingImplInfo) { 105 | let addr_width = u64::from(paging_info.addr_width); 106 | 107 | let zeroes_fill_right_count = 108 | paging_info.page_offset_bits + (info.level - 1) * paging_info.page_table_index_bits; 109 | 110 | let page_index_highlight_bits_count = 111 | if zeroes_fill_right_count + paging_info.page_table_index_bits > addr_width { 112 | addr_width - zeroes_fill_right_count 113 | } else { 114 | paging_info.page_table_index_bits 115 | }; 116 | 117 | let zeroes_fill_left_count = 118 | addr_width - zeroes_fill_right_count - page_index_highlight_bits_count; 119 | 120 | print!( 121 | "0b{zeroes_left_fill}{highlighted_index}{zeroes_right_fill}", 122 | zeroes_left_fill = "0".repeat(zeroes_fill_left_count as usize), 123 | highlighted_index = ansi_styles::paint_highlight(&format!( 124 | "{index:0bits$b}", 125 | index = info.index, 126 | bits = page_index_highlight_bits_count as usize 127 | )), 128 | zeroes_right_fill = "0".repeat(zeroes_fill_right_count as usize) 129 | ); 130 | } 131 | 132 | mod ansi_styles { 133 | use crate::print::USE_ANSI; 134 | use nu_ansi_term::{AnsiGenericString, Color, Style}; 135 | use std::sync::atomic::Ordering; 136 | 137 | pub fn paint_highlight(str: &str) -> AnsiGenericString<'_, str> { 138 | if USE_ANSI.load(Ordering::SeqCst) { 139 | Style::new().fg(Color::Red).bold().paint(str) 140 | } else { 141 | Style::new().paint(str) 142 | } 143 | } 144 | 145 | pub fn paint_heading(str: &str) -> AnsiGenericString<'_, str> { 146 | if USE_ANSI.load(Ordering::SeqCst) { 147 | Style::new().bold().paint(str) 148 | } else { 149 | Style::new().paint(str) 150 | } 151 | } 152 | 153 | pub fn paint_hint(str: &str) -> AnsiGenericString<'_, str> { 154 | if USE_ANSI.load(Ordering::SeqCst) { 155 | Style::new().fg(Color::LightGray).paint(str) 156 | } else { 157 | Style::new().paint(str) 158 | } 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /test_cli.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | DIR=$(dirname "$(realpath "$0")") 6 | cd "$DIR" || exit 7 | 8 | function fn_main() { 9 | test_cmd "test_res/0xdeadbeef_x86.stdout.txt" \ 10 | "cargo run 2>/dev/null -- 0xdeadbeef x86" 11 | 12 | test_cmd "test_res/0xdeadbeef_x86_pae.stdout.txt" \ 13 | "cargo run 2>/dev/null -- 0xdeadbeef x86 --pae" 14 | 15 | test_cmd "test_res/0xdeadbeef_x86_64.stdout.txt" \ 16 | "cargo run 2>/dev/null -- 0xdeadbeef x86_64" 17 | 18 | test_cmd "test_res/0xdeadbeef_x86_64_5level.stdout.txt" \ 19 | "cargo run 2>/dev/null -- 0xdeadbeef x86_64 --five-level" 20 | } 21 | 22 | function test_cmd() { 23 | FILE=$1 24 | CMD=$2 25 | 26 | ACTUAL=$(eval "$CMD") 27 | EXPECTED=$(cat "$FILE") 28 | 29 | if [ "$ACTUAL" != "$EXPECTED" ]; 30 | then 31 | echo "Unexpected output! CMD is: '$CMD'" 32 | diff <(echo "$EXPECTED" ) <(echo "$ACTUAL") 33 | exit 1 34 | fi 35 | } 36 | 37 | fn_main 38 | -------------------------------------------------------------------------------- /test_res/0xdeadbeef_x86.stdout.txt: -------------------------------------------------------------------------------- 1 | Page Table Calculator (v0.4.0): x86 32-bit paging 2 | x86 paging uses a 2-level page table. The page is indexed by 12 bits, 3 | which results in a page-size of 4096 bytes. Each page table is indexed by 10 4 | bits and has 2^10 == 1024 entries. Each page-table entry is 32-bit in size. 5 | Hence, a page table occupies the size of a page. Huge pages have a size of 6 | 2^22 == 4 MiB. 7 | 8 | address : 0xdeadbeef (user input truncated to 32-bit) 9 | address (bits): 0b11011110101011011011111011101111 10 | level 2 bits : 0b11011110100000000000000000000000 11 | level 1 bits : 0b00000000001011011011000000000000 12 | level 2 entry index : 890 (number of entry) 13 | level 2 entry offset: 0x0de8 (offset into the page table for that entry) 14 | level 1 entry index : 731 15 | level 1 entry offset: 0x0b6c 16 | -------------------------------------------------------------------------------- /test_res/0xdeadbeef_x86_64.stdout.txt: -------------------------------------------------------------------------------- 1 | Page Table Calculator (v0.4.0): x86_64 paging 2 | x86_64 paging uses a 4-level page table. The page is indexed by 12 bits, 3 | which results in a page-size of 4096 bytes. Each page table is indexed by 9 4 | bits and has 2^9 == 512 entries. Each page-table entry is 64-bit in size. Hence, 5 | a page table occupies the size of a page. Huge pages have a size of 6 | 2^21 == 2 MiB or 2^30 == 1 GiB. Huge pages are only valid on levels 2 or 3. 7 | 8 | address : 0x00000000deadbeef 9 | address (bits): 0b0000000000000000000000000000000011011110101011011011111011101111 10 | level 4 bits : 0b0000000000000000000000000000000000000000000000000000000000000000 11 | level 3 bits : 0b0000000000000000000000000000000011000000000000000000000000000000 12 | level 2 bits : 0b0000000000000000000000000000000000011110101000000000000000000000 13 | level 1 bits : 0b0000000000000000000000000000000000000000000011011011000000000000 14 | level 4 entry index : 0 (number of entry) 15 | level 4 entry offset: 0x0000 (offset into the page table for that entry) 16 | level 3 entry index : 3 17 | level 3 entry offset: 0x0018 18 | level 2 entry index : 245 19 | level 2 entry offset: 0x07a8 20 | level 1 entry index : 219 21 | level 1 entry offset: 0x06d8 22 | -------------------------------------------------------------------------------- /test_res/0xdeadbeef_x86_64_5level.stdout.txt: -------------------------------------------------------------------------------- 1 | Page Table Calculator (v0.4.0): x86_64 paging (5-level) 2 | x86_64 paging optionally uses a 5-level page table. The page is indexed 3 | by 12 bits, which results in a page-size of 4096 bytes. Each page table is 4 | indexed by 9 bits and has 2^9 == 512 entries. Each page-table entry is 64-bit in 5 | size. Hence, a page table occupies the size of a page. Huge pages have a size of 6 | 2^21 == 2 MiB or 2^30 == 1 GiB. Huge pages are only valid on levels 2 or 3. 7 | 8 | address : 0x00000000deadbeef 9 | address (bits): 0b0000000000000000000000000000000011011110101011011011111011101111 10 | level 5 bits : 0b0000000000000000000000000000000000000000000000000000000000000000 11 | level 4 bits : 0b0000000000000000000000000000000000000000000000000000000000000000 12 | level 3 bits : 0b0000000000000000000000000000000011000000000000000000000000000000 13 | level 2 bits : 0b0000000000000000000000000000000000011110101000000000000000000000 14 | level 1 bits : 0b0000000000000000000000000000000000000000000011011011000000000000 15 | level 5 entry index : 0 (number of entry) 16 | level 5 entry offset: 0x0000 (offset into the page table for that entry) 17 | level 4 entry index : 0 18 | level 4 entry offset: 0x0000 19 | level 3 entry index : 3 20 | level 3 entry offset: 0x0018 21 | level 2 entry index : 245 22 | level 2 entry offset: 0x07a8 23 | level 1 entry index : 219 24 | level 1 entry offset: 0x06d8 25 | -------------------------------------------------------------------------------- /test_res/0xdeadbeef_x86_pae.stdout.txt: -------------------------------------------------------------------------------- 1 | Page Table Calculator (v0.4.0): x86 32-bit paging with PAE 2 | x86 with the Physical Address Extension (PAE) paging uses a 3-level page table, 3 | that enables to access more than 32-bit of physical address space. The page 4 | is indexed by 12 bits, which results in a page-size of 4096 bytes. Tables 5 | at level 1 and 2 are indexed by 9 bits and have 2^9 == 512 entries. The third- 6 | level page table is indexed by 2 bits and has 2^2 == 4 entries. Each page-table 7 | entry is 64-bit in size. Hence, a page table at levels 1 and 2 occupies the size 8 | of a page whereas the level 3 page table occupies 32 byte. Huge pages have a size 9 | of 2^21 == 2 MiB and are only valid on level 2. 10 | 11 | address : 0xdeadbeef (user input truncated to 32-bit) 12 | address (bits): 0b11011110101011011011111011101111 13 | level 3 bits : 0b11000000000000000000000000000000 14 | level 2 bits : 0b00011110101000000000000000000000 15 | level 1 bits : 0b00000000000011011011000000000000 16 | level 3 entry index : 3 (number of entry) 17 | level 3 entry offset: 0x0018 (offset into the page table for that entry) 18 | level 2 entry index : 245 19 | level 2 entry offset: 0x07a8 20 | level 1 entry index : 219 21 | level 1 entry offset: 0x06d8 22 | --------------------------------------------------------------------------------