├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── bors.toml ├── snapcraft.yaml └── src ├── args.rs ├── image.rs ├── lib.rs ├── main.rs ├── qemu.rs └── startup.nsh /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | - staging 8 | - trying 9 | tags: 10 | - '*' 11 | pull_request: 12 | branches: 13 | - master 14 | 15 | jobs: 16 | build: 17 | strategy: 18 | matrix: 19 | os: 20 | - ubuntu-latest 21 | - macOS-latest 22 | - windows-latest 23 | runs-on: ${{ matrix.os }} 24 | steps: 25 | - name: Checkout 26 | uses: actions/checkout@v2 27 | 28 | - name: Build 29 | run: cargo build --verbose 30 | 31 | - name: Run tests 32 | run: cargo test --verbose 33 | 34 | build_snap: 35 | runs-on: ubuntu-latest 36 | steps: 37 | - name: Checkout 38 | uses: actions/checkout@v2 39 | 40 | - name: Snapcraft 41 | id: snapcraft 42 | uses: snapcore/action-build@v1 43 | 44 | - name: Upload Snap Artifact 45 | uses: actions/upload-artifact@v2 46 | with: 47 | name: snap 48 | path: ${{ steps.snapcraft.outputs.snap }} 49 | 50 | clippy: 51 | runs-on: ubuntu-latest 52 | steps: 53 | - name: Checkout 54 | uses: actions/checkout@v2 55 | 56 | - name: Run clippy 57 | run: cargo clippy --verbose --all --tests 58 | 59 | format: 60 | runs-on: ubuntu-latest 61 | steps: 62 | - name: Checkout 63 | uses: actions/checkout@v2 64 | 65 | - name: Check formatting 66 | run: cargo fmt --all -- --check 67 | 68 | publish_snap: 69 | runs-on: ubuntu-latest 70 | if: ${{ github.ref == 'refs/heads/master' }} 71 | needs: [build, build_snap, clippy, format] 72 | steps: 73 | - name: Download Snap Artifact 74 | uses: actions/download-artifact@v2 75 | with: 76 | name: snap 77 | 78 | - name: Get snap filename 79 | id: get_filename 80 | run: 'echo ::set-output name=filename::$(ls *.snap)' 81 | 82 | - name: Publish snap 83 | uses: snapcore/action-publish@v1 84 | env: 85 | SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPSTORE_LOGIN }} 86 | with: 87 | snap: ${{ steps.get_filename.outputs.filename }} 88 | release: edge 89 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | *.swp 4 | *.swo 5 | *.snap 6 | -------------------------------------------------------------------------------- /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 = "android_system_properties" 7 | version = "0.1.5" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 10 | dependencies = [ 11 | "libc", 12 | ] 13 | 14 | [[package]] 15 | name = "anyhow" 16 | version = "1.0.69" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "224afbd727c3d6e4b90103ece64b8d1b67fbb1973b1046c2281eed3f3803f800" 19 | 20 | [[package]] 21 | name = "autocfg" 22 | version = "1.1.0" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 25 | 26 | [[package]] 27 | name = "bitflags" 28 | version = "1.3.2" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 31 | 32 | [[package]] 33 | name = "bumpalo" 34 | version = "3.12.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "0d261e256854913907f67ed06efbc3338dfe6179796deefc1ff763fc1aee5535" 37 | 38 | [[package]] 39 | name = "byteorder" 40 | version = "1.4.3" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" 43 | 44 | [[package]] 45 | name = "cc" 46 | version = "1.0.79" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" 49 | 50 | [[package]] 51 | name = "cfg-if" 52 | version = "1.0.0" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 55 | 56 | [[package]] 57 | name = "chrono" 58 | version = "0.4.23" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "16b0a3d9ed01224b22057780a37bb8c5dbfe1be8ba48678e7bf57ec4b385411f" 61 | dependencies = [ 62 | "iana-time-zone", 63 | "js-sys", 64 | "num-integer", 65 | "num-traits", 66 | "time", 67 | "wasm-bindgen", 68 | "winapi", 69 | ] 70 | 71 | [[package]] 72 | name = "clap" 73 | version = "4.1.8" 74 | source = "registry+https://github.com/rust-lang/crates.io-index" 75 | checksum = "c3d7ae14b20b94cb02149ed21a86c423859cbe18dc7ed69845cace50e52b40a5" 76 | dependencies = [ 77 | "bitflags", 78 | "clap_derive", 79 | "clap_lex", 80 | "is-terminal", 81 | "once_cell", 82 | "strsim", 83 | "termcolor", 84 | ] 85 | 86 | [[package]] 87 | name = "clap_derive" 88 | version = "4.1.8" 89 | source = "registry+https://github.com/rust-lang/crates.io-index" 90 | checksum = "44bec8e5c9d09e439c4335b1af0abaab56dcf3b94999a936e1bb47b9134288f0" 91 | dependencies = [ 92 | "heck", 93 | "proc-macro-error", 94 | "proc-macro2", 95 | "quote", 96 | "syn", 97 | ] 98 | 99 | [[package]] 100 | name = "clap_lex" 101 | version = "0.3.2" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "350b9cf31731f9957399229e9b2adc51eeabdfbe9d71d9a0552275fd12710d09" 104 | dependencies = [ 105 | "os_str_bytes", 106 | ] 107 | 108 | [[package]] 109 | name = "codespan-reporting" 110 | version = "0.11.1" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" 113 | dependencies = [ 114 | "termcolor", 115 | "unicode-width", 116 | ] 117 | 118 | [[package]] 119 | name = "core-foundation-sys" 120 | version = "0.8.3" 121 | source = "registry+https://github.com/rust-lang/crates.io-index" 122 | checksum = "5827cebf4670468b8772dd191856768aedcb1b0278a04f989f7766351917b9dc" 123 | 124 | [[package]] 125 | name = "ctrlc" 126 | version = "3.2.5" 127 | source = "registry+https://github.com/rust-lang/crates.io-index" 128 | checksum = "bbcf33c2a618cbe41ee43ae6e9f2e48368cd9f9db2896f10167d8d762679f639" 129 | dependencies = [ 130 | "nix", 131 | "windows-sys 0.45.0", 132 | ] 133 | 134 | [[package]] 135 | name = "cxx" 136 | version = "1.0.91" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "86d3488e7665a7a483b57e25bdd90d0aeb2bc7608c8d0346acf2ad3f1caf1d62" 139 | dependencies = [ 140 | "cc", 141 | "cxxbridge-flags", 142 | "cxxbridge-macro", 143 | "link-cplusplus", 144 | ] 145 | 146 | [[package]] 147 | name = "cxx-build" 148 | version = "1.0.91" 149 | source = "registry+https://github.com/rust-lang/crates.io-index" 150 | checksum = "48fcaf066a053a41a81dfb14d57d99738b767febb8b735c3016e469fac5da690" 151 | dependencies = [ 152 | "cc", 153 | "codespan-reporting", 154 | "once_cell", 155 | "proc-macro2", 156 | "quote", 157 | "scratch", 158 | "syn", 159 | ] 160 | 161 | [[package]] 162 | name = "cxxbridge-flags" 163 | version = "1.0.91" 164 | source = "registry+https://github.com/rust-lang/crates.io-index" 165 | checksum = "a2ef98b8b717a829ca5603af80e1f9e2e48013ab227b68ef37872ef84ee479bf" 166 | 167 | [[package]] 168 | name = "cxxbridge-macro" 169 | version = "1.0.91" 170 | source = "registry+https://github.com/rust-lang/crates.io-index" 171 | checksum = "086c685979a698443656e5cf7856c95c642295a38599f12fb1ff76fb28d19892" 172 | dependencies = [ 173 | "proc-macro2", 174 | "quote", 175 | "syn", 176 | ] 177 | 178 | [[package]] 179 | name = "errno" 180 | version = "0.2.8" 181 | source = "registry+https://github.com/rust-lang/crates.io-index" 182 | checksum = "f639046355ee4f37944e44f60642c6f3a7efa3cf6b78c78a0d989a8ce6c396a1" 183 | dependencies = [ 184 | "errno-dragonfly", 185 | "libc", 186 | "winapi", 187 | ] 188 | 189 | [[package]] 190 | name = "errno-dragonfly" 191 | version = "0.1.2" 192 | source = "registry+https://github.com/rust-lang/crates.io-index" 193 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" 194 | dependencies = [ 195 | "cc", 196 | "libc", 197 | ] 198 | 199 | [[package]] 200 | name = "fastrand" 201 | version = "1.9.0" 202 | source = "registry+https://github.com/rust-lang/crates.io-index" 203 | checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" 204 | dependencies = [ 205 | "instant", 206 | ] 207 | 208 | [[package]] 209 | name = "fatfs" 210 | version = "0.3.6" 211 | source = "registry+https://github.com/rust-lang/crates.io-index" 212 | checksum = "05669f8e7e2d7badc545c513710f0eba09c2fbef683eb859fd79c46c355048e0" 213 | dependencies = [ 214 | "bitflags", 215 | "byteorder", 216 | "chrono", 217 | "log", 218 | ] 219 | 220 | [[package]] 221 | name = "heck" 222 | version = "0.4.1" 223 | source = "registry+https://github.com/rust-lang/crates.io-index" 224 | checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" 225 | 226 | [[package]] 227 | name = "hermit-abi" 228 | version = "0.3.1" 229 | source = "registry+https://github.com/rust-lang/crates.io-index" 230 | checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" 231 | 232 | [[package]] 233 | name = "iana-time-zone" 234 | version = "0.1.53" 235 | source = "registry+https://github.com/rust-lang/crates.io-index" 236 | checksum = "64c122667b287044802d6ce17ee2ddf13207ed924c712de9a66a5814d5b64765" 237 | dependencies = [ 238 | "android_system_properties", 239 | "core-foundation-sys", 240 | "iana-time-zone-haiku", 241 | "js-sys", 242 | "wasm-bindgen", 243 | "winapi", 244 | ] 245 | 246 | [[package]] 247 | name = "iana-time-zone-haiku" 248 | version = "0.1.1" 249 | source = "registry+https://github.com/rust-lang/crates.io-index" 250 | checksum = "0703ae284fc167426161c2e3f1da3ea71d94b21bedbcc9494e92b28e334e3dca" 251 | dependencies = [ 252 | "cxx", 253 | "cxx-build", 254 | ] 255 | 256 | [[package]] 257 | name = "instant" 258 | version = "0.1.12" 259 | source = "registry+https://github.com/rust-lang/crates.io-index" 260 | checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" 261 | dependencies = [ 262 | "cfg-if", 263 | ] 264 | 265 | [[package]] 266 | name = "io-lifetimes" 267 | version = "1.0.5" 268 | source = "registry+https://github.com/rust-lang/crates.io-index" 269 | checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3" 270 | dependencies = [ 271 | "libc", 272 | "windows-sys 0.45.0", 273 | ] 274 | 275 | [[package]] 276 | name = "is-terminal" 277 | version = "0.4.4" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "21b6b32576413a8e69b90e952e4a026476040d81017b80445deda5f2d3921857" 280 | dependencies = [ 281 | "hermit-abi", 282 | "io-lifetimes", 283 | "rustix", 284 | "windows-sys 0.45.0", 285 | ] 286 | 287 | [[package]] 288 | name = "js-sys" 289 | version = "0.3.61" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "445dde2150c55e483f3d8416706b97ec8e8237c307e5b7b4b8dd15e6af2a0730" 292 | dependencies = [ 293 | "wasm-bindgen", 294 | ] 295 | 296 | [[package]] 297 | name = "libc" 298 | version = "0.2.139" 299 | source = "registry+https://github.com/rust-lang/crates.io-index" 300 | checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" 301 | 302 | [[package]] 303 | name = "link-cplusplus" 304 | version = "1.0.8" 305 | source = "registry+https://github.com/rust-lang/crates.io-index" 306 | checksum = "ecd207c9c713c34f95a097a5b029ac2ce6010530c7b49d7fea24d977dede04f5" 307 | dependencies = [ 308 | "cc", 309 | ] 310 | 311 | [[package]] 312 | name = "linux-raw-sys" 313 | version = "0.1.4" 314 | source = "registry+https://github.com/rust-lang/crates.io-index" 315 | checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" 316 | 317 | [[package]] 318 | name = "log" 319 | version = "0.4.17" 320 | source = "registry+https://github.com/rust-lang/crates.io-index" 321 | checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" 322 | dependencies = [ 323 | "cfg-if", 324 | ] 325 | 326 | [[package]] 327 | name = "nix" 328 | version = "0.26.2" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" 331 | dependencies = [ 332 | "bitflags", 333 | "cfg-if", 334 | "libc", 335 | "static_assertions", 336 | ] 337 | 338 | [[package]] 339 | name = "num-integer" 340 | version = "0.1.45" 341 | source = "registry+https://github.com/rust-lang/crates.io-index" 342 | checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" 343 | dependencies = [ 344 | "autocfg", 345 | "num-traits", 346 | ] 347 | 348 | [[package]] 349 | name = "num-traits" 350 | version = "0.2.15" 351 | source = "registry+https://github.com/rust-lang/crates.io-index" 352 | checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" 353 | dependencies = [ 354 | "autocfg", 355 | ] 356 | 357 | [[package]] 358 | name = "once_cell" 359 | version = "1.17.1" 360 | source = "registry+https://github.com/rust-lang/crates.io-index" 361 | checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" 362 | 363 | [[package]] 364 | name = "os_str_bytes" 365 | version = "6.4.1" 366 | source = "registry+https://github.com/rust-lang/crates.io-index" 367 | checksum = "9b7820b9daea5457c9f21c69448905d723fbd21136ccf521748f23fd49e723ee" 368 | 369 | [[package]] 370 | name = "proc-macro-error" 371 | version = "1.0.4" 372 | source = "registry+https://github.com/rust-lang/crates.io-index" 373 | checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" 374 | dependencies = [ 375 | "proc-macro-error-attr", 376 | "proc-macro2", 377 | "quote", 378 | "syn", 379 | "version_check", 380 | ] 381 | 382 | [[package]] 383 | name = "proc-macro-error-attr" 384 | version = "1.0.4" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" 387 | dependencies = [ 388 | "proc-macro2", 389 | "quote", 390 | "version_check", 391 | ] 392 | 393 | [[package]] 394 | name = "proc-macro2" 395 | version = "1.0.51" 396 | source = "registry+https://github.com/rust-lang/crates.io-index" 397 | checksum = "5d727cae5b39d21da60fa540906919ad737832fe0b1c165da3a34d6548c849d6" 398 | dependencies = [ 399 | "unicode-ident", 400 | ] 401 | 402 | [[package]] 403 | name = "quote" 404 | version = "1.0.23" 405 | source = "registry+https://github.com/rust-lang/crates.io-index" 406 | checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b" 407 | dependencies = [ 408 | "proc-macro2", 409 | ] 410 | 411 | [[package]] 412 | name = "redox_syscall" 413 | version = "0.2.16" 414 | source = "registry+https://github.com/rust-lang/crates.io-index" 415 | checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" 416 | dependencies = [ 417 | "bitflags", 418 | ] 419 | 420 | [[package]] 421 | name = "rustix" 422 | version = "0.36.9" 423 | source = "registry+https://github.com/rust-lang/crates.io-index" 424 | checksum = "fd5c6ff11fecd55b40746d1995a02f2eb375bf8c00d192d521ee09f42bef37bc" 425 | dependencies = [ 426 | "bitflags", 427 | "errno", 428 | "io-lifetimes", 429 | "libc", 430 | "linux-raw-sys", 431 | "windows-sys 0.45.0", 432 | ] 433 | 434 | [[package]] 435 | name = "scratch" 436 | version = "1.0.4" 437 | source = "registry+https://github.com/rust-lang/crates.io-index" 438 | checksum = "5d5e082f6ea090deaf0e6dd04b68360fd5cddb152af6ce8927c9d25db299f98c" 439 | 440 | [[package]] 441 | name = "static_assertions" 442 | version = "1.1.0" 443 | source = "registry+https://github.com/rust-lang/crates.io-index" 444 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 445 | 446 | [[package]] 447 | name = "strsim" 448 | version = "0.10.0" 449 | source = "registry+https://github.com/rust-lang/crates.io-index" 450 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" 451 | 452 | [[package]] 453 | name = "syn" 454 | version = "1.0.109" 455 | source = "registry+https://github.com/rust-lang/crates.io-index" 456 | checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" 457 | dependencies = [ 458 | "proc-macro2", 459 | "quote", 460 | "unicode-ident", 461 | ] 462 | 463 | [[package]] 464 | name = "tempfile" 465 | version = "3.4.0" 466 | source = "registry+https://github.com/rust-lang/crates.io-index" 467 | checksum = "af18f7ae1acd354b992402e9ec5864359d693cd8a79dcbef59f76891701c1e95" 468 | dependencies = [ 469 | "cfg-if", 470 | "fastrand", 471 | "redox_syscall", 472 | "rustix", 473 | "windows-sys 0.42.0", 474 | ] 475 | 476 | [[package]] 477 | name = "termcolor" 478 | version = "1.2.0" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" 481 | dependencies = [ 482 | "winapi-util", 483 | ] 484 | 485 | [[package]] 486 | name = "time" 487 | version = "0.1.45" 488 | source = "registry+https://github.com/rust-lang/crates.io-index" 489 | checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" 490 | dependencies = [ 491 | "libc", 492 | "wasi", 493 | "winapi", 494 | ] 495 | 496 | [[package]] 497 | name = "uefi-run" 498 | version = "0.6.1" 499 | dependencies = [ 500 | "anyhow", 501 | "clap", 502 | "ctrlc", 503 | "fatfs", 504 | "tempfile", 505 | "wait-timeout", 506 | ] 507 | 508 | [[package]] 509 | name = "unicode-ident" 510 | version = "1.0.7" 511 | source = "registry+https://github.com/rust-lang/crates.io-index" 512 | checksum = "775c11906edafc97bc378816b94585fbd9a054eabaf86fdd0ced94af449efab7" 513 | 514 | [[package]] 515 | name = "unicode-width" 516 | version = "0.1.10" 517 | source = "registry+https://github.com/rust-lang/crates.io-index" 518 | checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" 519 | 520 | [[package]] 521 | name = "version_check" 522 | version = "0.9.4" 523 | source = "registry+https://github.com/rust-lang/crates.io-index" 524 | checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" 525 | 526 | [[package]] 527 | name = "wait-timeout" 528 | version = "0.2.0" 529 | source = "registry+https://github.com/rust-lang/crates.io-index" 530 | checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" 531 | dependencies = [ 532 | "libc", 533 | ] 534 | 535 | [[package]] 536 | name = "wasi" 537 | version = "0.10.0+wasi-snapshot-preview1" 538 | source = "registry+https://github.com/rust-lang/crates.io-index" 539 | checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" 540 | 541 | [[package]] 542 | name = "wasm-bindgen" 543 | version = "0.2.84" 544 | source = "registry+https://github.com/rust-lang/crates.io-index" 545 | checksum = "31f8dcbc21f30d9b8f2ea926ecb58f6b91192c17e9d33594b3df58b2007ca53b" 546 | dependencies = [ 547 | "cfg-if", 548 | "wasm-bindgen-macro", 549 | ] 550 | 551 | [[package]] 552 | name = "wasm-bindgen-backend" 553 | version = "0.2.84" 554 | source = "registry+https://github.com/rust-lang/crates.io-index" 555 | checksum = "95ce90fd5bcc06af55a641a86428ee4229e44e07033963a2290a8e241607ccb9" 556 | dependencies = [ 557 | "bumpalo", 558 | "log", 559 | "once_cell", 560 | "proc-macro2", 561 | "quote", 562 | "syn", 563 | "wasm-bindgen-shared", 564 | ] 565 | 566 | [[package]] 567 | name = "wasm-bindgen-macro" 568 | version = "0.2.84" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "4c21f77c0bedc37fd5dc21f897894a5ca01e7bb159884559461862ae90c0b4c5" 571 | dependencies = [ 572 | "quote", 573 | "wasm-bindgen-macro-support", 574 | ] 575 | 576 | [[package]] 577 | name = "wasm-bindgen-macro-support" 578 | version = "0.2.84" 579 | source = "registry+https://github.com/rust-lang/crates.io-index" 580 | checksum = "2aff81306fcac3c7515ad4e177f521b5c9a15f2b08f4e32d823066102f35a5f6" 581 | dependencies = [ 582 | "proc-macro2", 583 | "quote", 584 | "syn", 585 | "wasm-bindgen-backend", 586 | "wasm-bindgen-shared", 587 | ] 588 | 589 | [[package]] 590 | name = "wasm-bindgen-shared" 591 | version = "0.2.84" 592 | source = "registry+https://github.com/rust-lang/crates.io-index" 593 | checksum = "0046fef7e28c3804e5e38bfa31ea2a0f73905319b677e57ebe37e49358989b5d" 594 | 595 | [[package]] 596 | name = "winapi" 597 | version = "0.3.9" 598 | source = "registry+https://github.com/rust-lang/crates.io-index" 599 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 600 | dependencies = [ 601 | "winapi-i686-pc-windows-gnu", 602 | "winapi-x86_64-pc-windows-gnu", 603 | ] 604 | 605 | [[package]] 606 | name = "winapi-i686-pc-windows-gnu" 607 | version = "0.4.0" 608 | source = "registry+https://github.com/rust-lang/crates.io-index" 609 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 610 | 611 | [[package]] 612 | name = "winapi-util" 613 | version = "0.1.5" 614 | source = "registry+https://github.com/rust-lang/crates.io-index" 615 | checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" 616 | dependencies = [ 617 | "winapi", 618 | ] 619 | 620 | [[package]] 621 | name = "winapi-x86_64-pc-windows-gnu" 622 | version = "0.4.0" 623 | source = "registry+https://github.com/rust-lang/crates.io-index" 624 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 625 | 626 | [[package]] 627 | name = "windows-sys" 628 | version = "0.42.0" 629 | source = "registry+https://github.com/rust-lang/crates.io-index" 630 | checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" 631 | dependencies = [ 632 | "windows_aarch64_gnullvm", 633 | "windows_aarch64_msvc", 634 | "windows_i686_gnu", 635 | "windows_i686_msvc", 636 | "windows_x86_64_gnu", 637 | "windows_x86_64_gnullvm", 638 | "windows_x86_64_msvc", 639 | ] 640 | 641 | [[package]] 642 | name = "windows-sys" 643 | version = "0.45.0" 644 | source = "registry+https://github.com/rust-lang/crates.io-index" 645 | checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" 646 | dependencies = [ 647 | "windows-targets", 648 | ] 649 | 650 | [[package]] 651 | name = "windows-targets" 652 | version = "0.42.1" 653 | source = "registry+https://github.com/rust-lang/crates.io-index" 654 | checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7" 655 | dependencies = [ 656 | "windows_aarch64_gnullvm", 657 | "windows_aarch64_msvc", 658 | "windows_i686_gnu", 659 | "windows_i686_msvc", 660 | "windows_x86_64_gnu", 661 | "windows_x86_64_gnullvm", 662 | "windows_x86_64_msvc", 663 | ] 664 | 665 | [[package]] 666 | name = "windows_aarch64_gnullvm" 667 | version = "0.42.1" 668 | source = "registry+https://github.com/rust-lang/crates.io-index" 669 | checksum = "8c9864e83243fdec7fc9c5444389dcbbfd258f745e7853198f365e3c4968a608" 670 | 671 | [[package]] 672 | name = "windows_aarch64_msvc" 673 | version = "0.42.1" 674 | source = "registry+https://github.com/rust-lang/crates.io-index" 675 | checksum = "4c8b1b673ffc16c47a9ff48570a9d85e25d265735c503681332589af6253c6c7" 676 | 677 | [[package]] 678 | name = "windows_i686_gnu" 679 | version = "0.42.1" 680 | source = "registry+https://github.com/rust-lang/crates.io-index" 681 | checksum = "de3887528ad530ba7bdbb1faa8275ec7a1155a45ffa57c37993960277145d640" 682 | 683 | [[package]] 684 | name = "windows_i686_msvc" 685 | version = "0.42.1" 686 | source = "registry+https://github.com/rust-lang/crates.io-index" 687 | checksum = "bf4d1122317eddd6ff351aa852118a2418ad4214e6613a50e0191f7004372605" 688 | 689 | [[package]] 690 | name = "windows_x86_64_gnu" 691 | version = "0.42.1" 692 | source = "registry+https://github.com/rust-lang/crates.io-index" 693 | checksum = "c1040f221285e17ebccbc2591ffdc2d44ee1f9186324dd3e84e99ac68d699c45" 694 | 695 | [[package]] 696 | name = "windows_x86_64_gnullvm" 697 | version = "0.42.1" 698 | source = "registry+https://github.com/rust-lang/crates.io-index" 699 | checksum = "628bfdf232daa22b0d64fdb62b09fcc36bb01f05a3939e20ab73aaf9470d0463" 700 | 701 | [[package]] 702 | name = "windows_x86_64_msvc" 703 | version = "0.42.1" 704 | source = "registry+https://github.com/rust-lang/crates.io-index" 705 | checksum = "447660ad36a13288b1db4d4248e857b510e8c3a225c822ba4fb748c0aafecffd" 706 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "uefi-run" 3 | description = "Run UEFI applications" 4 | version = "0.6.1" 5 | authors = ["Richard Wiedenhöft "] 6 | license = "MIT" 7 | repository = "https://github.com/richard-w/uefi-run" 8 | edition = "2018" 9 | 10 | [lib] 11 | path = "src/lib.rs" 12 | 13 | [[bin]] 14 | name = "uefi-run" 15 | path = "src/main.rs" 16 | 17 | [dependencies] 18 | anyhow = "1.0" 19 | clap = { version = "4.0", features = ["cargo", "derive"] } 20 | ctrlc = { version = "3.1", features = ["termination"] } 21 | fatfs = "0.3" 22 | tempfile = "3.0" 23 | wait-timeout = "0.2" 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Richard Wiedenhöft 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # uefi-run [![Latest Version]][crates.io] [![Build Status]][travis] 2 | 3 | [Build Status]: https://travis-ci.org/Richard-W/uefi-run.svg?branch=master 4 | [travis]: https://travis-ci.org/Richard-W/uefi-run 5 | [Latest Version]: https://img.shields.io/crates/v/uefi-run.svg 6 | [crates.io]: https://crates.io/crates/uefi-run 7 | 8 | **Directly run UEFI applications in qemu** 9 | 10 | --- 11 | 12 | This helper application takes an EFI executable, builds a FAT filesystem around 13 | it, adds a startup script and runs qemu to run the executable. 14 | 15 | It does not require root permissions since it uses the [fatfs](https://crates.io/crates/fatfs) 16 | crate to build the filesystem image directly without involving `mkfs`, `mount`, 17 | etc. 18 | 19 | ## Installation 20 | 21 | ### Snap 22 | 23 | uefi-run can be installed from the snapstore: 24 | ```bash 25 | snap install --edge uefi-run 26 | ``` 27 | The confinement of this snap is somewhat strict. It can only access non-hidden files in the user's 28 | home directory. Also it has no network access. 29 | 30 | ### Cargo 31 | 32 | You can install cargo and rust using the rustup tool: 33 | ```bash 34 | curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh 35 | ``` 36 | 37 | After cargo has been installed you can build and install uefi-run: 38 | ```bash 39 | cargo install uefi-run 40 | ``` 41 | 42 | ### Packages provided by third parties 43 | 44 | Third-party packages are controlled by their respective maintainers. They are not associated to 45 | this project. Use at your own risk. 46 | 47 | * [AUR PKGBUILD for Arch Linux](https://aur.archlinux.org/packages/uefi-run) contributed by @rubo3 48 | -------------------------------------------------------------------------------- /bors.toml: -------------------------------------------------------------------------------- 1 | status = [ 2 | "build (ubuntu-latest)", 3 | "build (macOS-latest)", 4 | "build (windows-latest)", 5 | "build_snap", 6 | "clippy", 7 | "format", 8 | ] 9 | -------------------------------------------------------------------------------- /snapcraft.yaml: -------------------------------------------------------------------------------- 1 | name: uefi-run 2 | summary: Run UEFI applications 3 | license: MIT 4 | description: | 5 | Run an UEFI executable in qemu. This tool builds an EFI partition image 6 | with the provided application. The application is then started inside 7 | qemu from the EFI shell. 8 | 9 | adopt-info: uefi-run 10 | base: core18 11 | confinement: strict 12 | 13 | layout: 14 | /usr/lib/ipxe: 15 | bind: $SNAP/usr/lib/ipxe 16 | /usr/share/ovmf: 17 | bind: $SNAP/usr/share/ovmf 18 | /usr/share/qemu: 19 | bind: $SNAP/usr/share/qemu 20 | /usr/share/seabios: 21 | bind: $SNAP/usr/share/seabios 22 | 23 | plugs: 24 | home: 25 | x11: 26 | 27 | parts: 28 | uefi-run: 29 | plugin: rust 30 | source: . 31 | build-packages: 32 | - build-essential 33 | stage-packages: 34 | - freeglut3 35 | - libglu1-mesa 36 | - libslang2 37 | - ovmf 38 | - qemu-system-x86 39 | override-pull: | 40 | snapcraftctl pull 41 | grade=$(if git describe | grep -qP '^v\d\.\d\.\d$'; then echo stable; else echo devel; fi) 42 | version=$(grep -oP '(?<=version = ")[^"]+(?=")' < Cargo.toml | head -n1) 43 | if [ "${grade}" = "devel" ]; then 44 | version="${version}-$(git describe --exclude '*' --always)" 45 | fi 46 | snapcraftctl set-version ${version} 47 | snapcraftctl set-grade ${grade} 48 | 49 | apps: 50 | uefi-run: 51 | command: bin/uefi-run 52 | -------------------------------------------------------------------------------- /src/args.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use clap::Parser; 3 | use std::path::PathBuf; 4 | 5 | /// Command line arguments for uefi-run 6 | #[derive(Parser, Debug, Default, PartialEq)] 7 | #[clap( 8 | version, 9 | author, 10 | about, 11 | trailing_var_arg = true, 12 | dont_delimit_trailing_values = true 13 | )] 14 | pub struct Args { 15 | /// Bios image 16 | #[clap(long, short = 'b', default_value = "OVMF.fd")] 17 | pub bios_path: String, 18 | /// Path to qemu executable 19 | #[clap(long, short = 'q', default_value = "qemu-system-x86_64")] 20 | pub qemu_path: String, 21 | /// Size of the image in MiB 22 | #[clap(long, short = 's', default_value_t = 10)] 23 | pub size: u64, 24 | /// Additional files to be added to the efi image 25 | /// 26 | /// Additional files to be added to the efi image. If no inner location is provided, it will 27 | /// default to the root of the image with the same name as the provided file. 28 | #[clap(long, short = 'f')] 29 | pub add_file: Vec, 30 | /// EFI Executable 31 | pub efi_exe: String, 32 | /// Additional arguments for qemu 33 | pub qemu_args: Vec, 34 | /// Load the application as a bootloader instead of in an EFI shell 35 | /// 36 | /// This effectively skips the 5 second startup delay. 37 | #[clap(long, short = 'd')] 38 | pub boot: bool, 39 | } 40 | 41 | impl Args { 42 | /// Parse `--add-file` arguments into `(outer, inner)` tuples of `PathBuf` 43 | pub fn parse_add_file_args(&self) -> impl Iterator> + '_ { 44 | self.add_file.iter().map(|file| { 45 | // Split the argument to get the inner and outer files 46 | file.split_once(':') 47 | .map(|(x, y)| Ok((PathBuf::from(x), PathBuf::from(y)))) 48 | .unwrap_or_else(|| { 49 | let outer = PathBuf::from(&file); 50 | let inner = PathBuf::from(&file) 51 | .file_name() 52 | .ok_or_else(|| Error::msg("Invalid --add-file argument"))? 53 | .into(); 54 | Ok((outer, inner)) 55 | }) 56 | }) 57 | } 58 | } 59 | 60 | #[cfg(test)] 61 | mod tests { 62 | use super::*; 63 | 64 | #[test] 65 | fn test_parse_add_file_args() { 66 | let mut args = Args::default(); 67 | args.add_file = vec![ 68 | "/full/path/to/outer:/full/path/to/inner".to_string(), 69 | "/full/path/to/outer:inner".to_string(), 70 | "outer:inner".to_string(), 71 | "/full/path/to/outer".to_string(), 72 | "outer".to_string(), 73 | ]; 74 | #[rustfmt::skip] 75 | let expected = vec![ 76 | (PathBuf::from("/full/path/to/outer"), PathBuf::from("/full/path/to/inner")), 77 | (PathBuf::from("/full/path/to/outer"), PathBuf::from("inner")), 78 | (PathBuf::from("outer"), PathBuf::from("inner")), 79 | (PathBuf::from("/full/path/to/outer"), PathBuf::from("outer")), 80 | (PathBuf::from("outer"), PathBuf::from("outer")), 81 | ]; 82 | let actual = args 83 | .parse_add_file_args() 84 | .map(|x| x.unwrap()) 85 | .collect::>(); 86 | assert_eq!(actual, expected); 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/image.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use std::ffi::OsStr; 3 | use std::fs; 4 | use std::io::Write; 5 | use std::path::Path; 6 | 7 | /// Default startup script that just runs `run.efi` 8 | pub const DEFAULT_STARTUP_NSH: &[u8] = include_bytes!("startup.nsh"); 9 | 10 | /// Handle to a FAT filesystem used as an EFI partition 11 | pub struct EfiImage { 12 | fs: fatfs::FileSystem, 13 | } 14 | 15 | impl EfiImage { 16 | /// Create a new image at the given path 17 | pub fn new>(path: P, size: u64) -> Result { 18 | // Create regular file and truncate it to size. 19 | let file = std::fs::OpenOptions::new() 20 | .read(true) 21 | .write(true) 22 | .create_new(true) 23 | .open(&path)?; 24 | file.set_len(size)?; 25 | 26 | // Create FAT fs and open it 27 | fatfs::format_volume(&file, fatfs::FormatVolumeOptions::new())?; 28 | let fs = fatfs::FileSystem::new(file, fatfs::FsOptions::new())?; 29 | 30 | Ok(Self { fs }) 31 | } 32 | 33 | /// Add file to the image 34 | fn add_file>(&mut self, path: P) -> Result> { 35 | let path = path.as_ref(); 36 | let file_name = path 37 | .file_name() 38 | .ok_or_else(|| Error::msg("Invalid path"))? 39 | .to_str() 40 | .ok_or_else(|| Error::msg("Invalid filename encoding"))?; 41 | let mut dir = self.fs.root_dir(); 42 | if let Some(dir_path) = path.parent() { 43 | for dir_path_component in dir_path.iter() { 44 | if dir_path_component == OsStr::new(&std::path::MAIN_SEPARATOR.to_string()) { 45 | continue; 46 | } 47 | let dir_path_component = dir_path_component 48 | .to_str() 49 | .ok_or_else(|| Error::msg("Cannot convert path to string"))?; 50 | dir = dir.create_dir(dir_path_component)?; 51 | } 52 | } 53 | let mut file = dir.create_file(file_name)?; 54 | file.truncate()?; 55 | Ok(file) 56 | } 57 | 58 | /// Copy file from host filesystem to the image 59 | pub fn copy_host_file, P2: AsRef>( 60 | &mut self, 61 | src: P1, 62 | dst: P2, 63 | ) -> Result<()> { 64 | let file_contents = fs::read(src)?; 65 | let mut file = self.add_file(dst)?; 66 | file.write_all(&file_contents)?; 67 | Ok(()) 68 | } 69 | 70 | /// Write file contents 71 | pub fn set_file_contents, B: AsRef<[u8]>>( 72 | &mut self, 73 | path: P, 74 | contents: B, 75 | ) -> Result<()> { 76 | let mut file = self.add_file(path)?; 77 | file.write_all(contents.as_ref())?; 78 | Ok(()) 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Error, Result}; 2 | 3 | mod args; 4 | pub use args::*; 5 | 6 | mod image; 7 | pub use image::*; 8 | 9 | mod qemu; 10 | pub use qemu::*; 11 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::Parser; 2 | use std::path::PathBuf; 3 | use std::sync::atomic::{AtomicBool, Ordering}; 4 | use std::sync::Arc; 5 | use std::time::Duration; 6 | use uefi_run::*; 7 | 8 | fn main() { 9 | // Parse command line 10 | let args = Args::parse(); 11 | 12 | // Install termination signal handler. This ensures that the destructor of 13 | // `temp_dir` which is constructed in the next step is really called and 14 | // the files are cleaned up properly. 15 | let terminating = Arc::new(AtomicBool::new(false)); 16 | { 17 | let term = terminating.clone(); 18 | ctrlc::set_handler(move || { 19 | println!("uefi-run terminating..."); 20 | // Tell the main thread to stop waiting. 21 | term.store(true, Ordering::SeqCst); 22 | }) 23 | .expect("Error setting termination handler"); 24 | } 25 | 26 | // Create temporary dir for the image file. 27 | let temp_dir = tempfile::tempdir().expect("Unable to create temporary directory"); 28 | let temp_dir_path = PathBuf::from(temp_dir.path()); 29 | 30 | // Path to the image file 31 | let image_file_path = { 32 | let mut path_buf = temp_dir_path; 33 | path_buf.push("image.fat"); 34 | path_buf 35 | }; 36 | 37 | { 38 | let mut image = 39 | EfiImage::new(&image_file_path, args.size * 0x10_0000).expect("Failed to create image"); 40 | 41 | // Create EFI executable 42 | if args.boot { 43 | // Copy the application to where the firmware expects a bootloader. 44 | image.copy_host_file(&args.efi_exe, "EFI/Boot/BootX64.efi") 45 | } else { 46 | // Use startup.nsh to start the application from the EFI shell. 47 | image 48 | .copy_host_file(&args.efi_exe, "run.efi") 49 | .and_then(|_| image.set_file_contents("startup.nsh", DEFAULT_STARTUP_NSH)) 50 | } 51 | .expect("Failed to copy EFI executable"); 52 | 53 | // Create user provided additional files 54 | for (outer, inner) in args.parse_add_file_args().map(|x| x.unwrap()) { 55 | // Copy the file into the image 56 | image 57 | .copy_host_file(outer, inner) 58 | .expect("Failed to copy user-defined file"); 59 | } 60 | } 61 | 62 | let mut qemu_config = QemuConfig { 63 | qemu_path: args.qemu_path, 64 | bios_path: args.bios_path, 65 | drives: vec![QemuDriveConfig { 66 | file: image_file_path.to_str().unwrap().to_string(), 67 | media: "disk".to_string(), 68 | format: "raw".to_string(), 69 | }], 70 | ..Default::default() 71 | }; 72 | qemu_config 73 | .additional_args 74 | .extend(args.qemu_args.iter().cloned()); 75 | 76 | // Run qemu 77 | let mut qemu_process = qemu_config.run().expect("Failed to start qemu"); 78 | 79 | // Wait for qemu to exit or signal. 80 | let mut qemu_exit_code; 81 | loop { 82 | qemu_exit_code = qemu_process.wait(Duration::from_millis(500)); 83 | if qemu_exit_code.is_some() || terminating.load(Ordering::SeqCst) { 84 | break; 85 | } 86 | } 87 | 88 | // The above loop may have been broken by a signal 89 | if qemu_exit_code.is_none() { 90 | // In this case we wait for qemu to exit for one second 91 | qemu_exit_code = qemu_process.wait(Duration::from_secs(1)); 92 | } 93 | 94 | // Qemu may still be running 95 | if qemu_exit_code.is_none() { 96 | // In this case we need to kill it 97 | qemu_process 98 | .kill() 99 | .or_else(|e| match e.kind() { 100 | // Not running anymore 101 | std::io::ErrorKind::InvalidInput => Ok(()), 102 | _ => Err(e), 103 | }) 104 | .expect("Unable to kill qemu process"); 105 | qemu_exit_code = qemu_process.wait(Duration::from_secs(1)); 106 | } 107 | 108 | let exit_code = qemu_exit_code.expect("qemu should have exited by now but did not"); 109 | std::process::exit(exit_code); 110 | } 111 | -------------------------------------------------------------------------------- /src/qemu.rs: -------------------------------------------------------------------------------- 1 | use super::*; 2 | use std::process::{Child, Command}; 3 | use std::time::Duration; 4 | use wait_timeout::ChildExt; 5 | 6 | /// Qemu run configuration 7 | #[derive(Debug, Clone)] 8 | pub struct QemuConfig { 9 | pub qemu_path: String, 10 | pub bios_path: String, 11 | pub drives: Vec, 12 | pub additional_args: Vec, 13 | } 14 | 15 | impl Default for QemuConfig { 16 | fn default() -> Self { 17 | Self { 18 | qemu_path: "qemu-system-x86_64".to_string(), 19 | bios_path: "OVMF.fd".to_string(), 20 | drives: Vec::new(), 21 | additional_args: vec!["-net".to_string(), "none".to_string()], 22 | } 23 | } 24 | } 25 | 26 | impl QemuConfig { 27 | /// Run an instance of qemu with the given config 28 | pub fn run(&self) -> Result { 29 | let mut args = vec!["-bios".to_string(), self.bios_path.clone()]; 30 | for (index, drive) in self.drives.iter().enumerate() { 31 | args.push("-drive".to_string()); 32 | args.push(format!( 33 | "file={},index={},media={},format={}", 34 | drive.file, index, drive.media, drive.format 35 | )); 36 | } 37 | args.extend(self.additional_args.iter().cloned()); 38 | 39 | let child = Command::new(&self.qemu_path).args(args).spawn()?; 40 | Ok(QemuProcess { child }) 41 | } 42 | } 43 | 44 | /// Qemu drive configuration 45 | #[derive(Debug, Clone)] 46 | pub struct QemuDriveConfig { 47 | pub file: String, 48 | pub media: String, 49 | pub format: String, 50 | } 51 | 52 | impl QemuDriveConfig { 53 | pub fn new(file: &str, media: &str, format: &str) -> Self { 54 | Self { 55 | file: file.to_string(), 56 | media: media.to_string(), 57 | format: format.to_string(), 58 | } 59 | } 60 | } 61 | 62 | pub struct QemuProcess { 63 | child: Child, 64 | } 65 | 66 | impl QemuProcess { 67 | /// Wait for the process to exit for `duration`. 68 | /// 69 | /// Returns `true` if the process exited and false if the timeout expired. 70 | pub fn wait(&mut self, duration: Duration) -> Option { 71 | self.child 72 | .wait_timeout(duration) 73 | .expect("Failed to wait on child process") 74 | .map(|exit_status| exit_status.code().unwrap_or(0)) 75 | } 76 | 77 | /// Kill the process. 78 | pub fn kill(&mut self) -> std::io::Result<()> { 79 | self.child.kill() 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/startup.nsh: -------------------------------------------------------------------------------- 1 | @echo -off 2 | echo Starting UEFI application... 3 | fs0: 4 | run.efi 5 | --------------------------------------------------------------------------------