diff --git a/.github/workflows/ci-workflow.yml b/.github/workflows/ci-workflow.yml new file mode 100644 index 0000000..6b79045 --- /dev/null +++ b/.github/workflows/ci-workflow.yml @@ -0,0 +1,37 @@ +# @file ci-workflow.yml +# +# A workflow that runs basic CI tests and Rust CI tests, triggered on a pull_request or push to +# main. +# +# NOTE: This file is automatically synchronized from Patina DevOps. Update the original file there +# instead of the file in this repo. +# +# - Patina DevOps Repo: https://github.com/OpenDevicePartnership/patina-devops +# - File Sync Settings: https://github.com/OpenDevicePartnership/patina-devops/blob/main/.sync/Files.yml +# +## +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 +## +name: CI + +on: + workflow_dispatch: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + ci_workflow: + name: Run + uses: OpenDevicePartnership/patina-devops/.github/workflows/CiWorkflow.yml@v0.3.5 + with: + build-tasks: build + run-cargo-vet: false + additional-artifacts: | + target/efi/services_benchmark_test_debug.efi + target/efi/services_benchmark_test.efi + secrets: inherit diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..04034d6 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,566 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aarch64-cpu" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a21cd0131c25c438e19cd6a774adf7e3f64f7f4d723022882facc2dee0f8bc9" +dependencies = [ + "tock-registers", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bit_field" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "downcast" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1435fa1053d8b2fbbe9be7e97eca7f33d37b28409959813daefc1446a14247f1" + +[[package]] +name = "fallible-streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" + +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + +[[package]] +name = "fragile" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619" + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + +[[package]] +name = "linkme" +version = "0.3.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e3283ed2d0e50c06dd8602e0ab319bb048b6325d0bba739db64ed8205179898" +dependencies = [ + "linkme-impl", +] + +[[package]] +name = "linkme-impl" +version = "0.3.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5cec0ec4228b4853bb129c84dbf093a27e6c7a20526da046defc334a1b017f7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "mockall" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39a6bfcc6c8c7eed5ee98b9c3e33adc726054389233e201c95dab2d41a3839d2" +dependencies = [ + "cfg-if", + "downcast", + "fragile", + "mockall_derive", + "predicates", + "predicates-tree", +] + +[[package]] +name = "mockall_derive" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "mu_rust_helpers" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028b489eb1f6ce61c83feee2cc5e8fb60978115a466c74b84b78aafed6041f34" +dependencies = [ + "mu_uefi_decompress", + "mu_uefi_guid", + "mu_uefi_perf_timer", +] + +[[package]] +name = "mu_uefi_decompress" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24b85df261b0b0241dee3c95a186fac29234b0e60bfabef7d57d3099ec2b7cb6" +dependencies = [ + "bitvec", + "log", +] + +[[package]] +name = "mu_uefi_guid" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "091e08bf3b952c68a5d3915d53b4221dc41fad3e00d3eb2020d2dd46f83ae5e2" +dependencies = [ + "r-efi", + "uuid", +] + +[[package]] +name = "mu_uefi_perf_timer" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df471d72676adaa480d61a0c9ed30a844a90842f876c188a0b30cb760d2444aa" +dependencies = [ + "aarch64-cpu", + "log", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "patina" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9043d0e24ef5412c1a8fe0fd7323c847a49f89e2eb1e17bab8d842a116fe90b7" +dependencies = [ + "cfg-if", + "fallible-streaming-iterator", + "fixedbitset", + "indoc", + "linkme", + "log", + "mu_rust_helpers", + "num-traits", + "patina_macro", + "r-efi", + "scroll", + "spin", + "uart_16550", + "uuid", + "x86_64", + "zerocopy", + "zerocopy-derive", +] + +[[package]] +name = "patina_macro" +version = "15.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c21d92c8e60f349edb3ed875d75445447da2c71e124710bb94db8097dde5142e" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "uuid", +] + +[[package]] +name = "predicates" +version = "3.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5d19ee57562043d37e82899fade9a22ebab7be9cef5026b07fda9cdd4293573" +dependencies = [ + "anstyle", + "predicates-core", +] + +[[package]] +name = "predicates-core" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727e462b119fe9c93fd0eb1429a5f7647394014cf3c04ab2c0350eeb09095ffa" + +[[package]] +name = "predicates-tree" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72dd2d6d381dfb73a193c7fca536518d7caee39fc8503f74e7dc0be0531b425c" +dependencies = [ + "predicates-core", + "termtree", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "ptr_meta" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b9a0cf95a1196af61d4f1cbdab967179516d9a4a4312af1f31948f8f6224a79" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7347867d0a7e1208d93b46767be83e2b8f978c3dad35f775ac8d8847551d6fe1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "raw-cpuid" +version = "10.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "rolling-stats" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f48d37da86a2b3e7a04b9514c2be259017995e48fbba9c8fc01c78ed4bd21812" +dependencies = [ + "num-traits", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scroll" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1257cd4248b4132760d6524d6dda4e053bc648c9070b960929bf50cfb1e7add" +dependencies = [ + "scroll_derive", +] + +[[package]] +name = "scroll_derive" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed76efe62313ab6610570951494bdaa81568026e0318eaa55f167de70eeea67d" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "services_benchmark_test" +version = "0.1.0" +dependencies = [ + "cfg-if", + "log", + "mockall", + "mu_rust_helpers", + "patina", + "r-efi", + "rolling-stats", + "uefi", + "uuid", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "syn" +version = "2.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "termtree" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f50febec83f5ee1df3015341d8bd429f2d1cc62bcba7ea2076759d315084683" + +[[package]] +name = "tock-registers" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b9e2fdb3a1e862c0661768b7ed25390811df1947a8acbfbefe09b47078d93c4" + +[[package]] +name = "uart_16550" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e492212ac378a5e00da953718dafb1340d9fbaf4f27d6f3c5cab03d931d1c049" +dependencies = [ + "bitflags 2.10.0", + "rustversion", + "x86", +] + +[[package]] +name = "ucs2" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79298e11f316400c57ec268f3c2c29ac3c4d4777687955cd3d4f3a35ce7eba" +dependencies = [ + "bit_field", +] + +[[package]] +name = "uefi" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe9058b73ee2b6559524af9e33199c13b2485ddbf3ad1181b68051cdc50c17" +dependencies = [ + "bitflags 2.10.0", + "cfg-if", + "log", + "ptr_meta", + "ucs2", + "uefi-macros", + "uefi-raw", + "uguid", +] + +[[package]] +name = "uefi-macros" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4687412b5ac74d245d5bfb1733ede50c31be19bf8a4b6a967a29b451bab49e67" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "uefi-raw" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f64fe59e11af447d12fd60a403c74106eb104309f34b4c6dbce6e927d97da9d" +dependencies = [ + "bitflags 2.10.0", + "uguid", +] + +[[package]] +name = "uguid" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8352f8c05e47892e7eaf13b34abd76a7f4aeaf817b716e88789381927f199c" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "uuid" +version = "1.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" + +[[package]] +name = "volatile" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x86" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2781db97787217ad2a2845c396a5efe286f87467a5810836db6d74926e94a385" +dependencies = [ + "bit_field", + "bitflags 1.3.2", + "raw-cpuid", +] + +[[package]] +name = "x86_64" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f042214de98141e9c8706e8192b73f56494087cc55ebec28ce10f26c5c364ae" +dependencies = [ + "bit_field", + "bitflags 2.10.0", + "rustversion", + "volatile", +] + +[[package]] +name = "zerocopy" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..b125339 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,40 @@ +[workspace] +resolver = "2" + +members = ["services_benchmark_test"] + +[workspace.package] +version = "0.1.0" +edition = "2024" +license = "Apache-2.0" +rust-version = "1.85" +description = "Patina helper applications" +repository = "https://github.com/OpenDevicePartnership/patina-apps" + +[workspace.dependencies] +mu_rust_helpers = { version = "3" } + +patina = { version = "15"} + +cfg-if = "1.0.4" +log = { version = "^0.4", default-features = false, features = [ + "release_max_level_warn", +] } +r-efi = { version = "5.3.0", default-features = false } +rolling-stats = "0.8.0" +uuid = { version = "1.8", default-features = false } + +[profile.dev] +opt-level = 3 + +[profile.test] +opt-level = 0 + +[profile.release] +opt-level = "s" +codegen-units = 1 +lto = true +incremental = true + +[workspace.lints.clippy] +undocumented_unsafe_blocks = "warn" diff --git a/Makefile.toml b/Makefile.toml new file mode 100644 index 0000000..bcc0fec --- /dev/null +++ b/Makefile.toml @@ -0,0 +1,200 @@ +# Makefile.toml for building Patina EFI applications +# This file uses cargo-make to build specific packages as EFI applications + +[config] +default_to_workspace = false +skip_core_tasks = true + +[env] +# Default values +CARGO_MAKE_RUST_TARGET_ARCH = { value = "x86_64", condition = { env_not_set = ["CARGO_MAKE_RUST_TARGET_ARCH"] } } +PACKAGE_NAME = { value = "services_benchmark_test", condition = { env_not_set = ["PACKAGE_NAME"] } } +BUILD_MODE = { value = "release", condition = { env_not_set = ["BUILD_MODE"] } } + +# Target configuration based on architecture +UEFI_TARGET_X64 = "x86_64-unknown-uefi" +UEFI_TARGET_ARM64 = "aarch64-unknown-uefi" +UEFI_TARGET = "x86_64-unknown-uefi" + +# Output paths +TARGET_DIR = "target" +EFI_DIR = "${TARGET_DIR}/${UEFI_TARGET}/${BUILD_MODE}" +OUTPUT_DIR = "${TARGET_DIR}/efi" + +[tasks.default] +alias = "build-efi" + +# Clean task +[tasks.clean] +description = "Clean build artifacts" +command = "cargo" +args = ["clean"] + +# Setup task +[tasks.setup] +description = "Setup toolchain and targets" +script = [ + "rustup target add x86_64-unknown-uefi", + "rustup target add aarch64-unknown-uefi" +] + +# Build library +[tasks.build-lib] +description = "Build the library for UEFI target" +command = "cargo" +args = ["build", "--lib", "-p", "${PACKAGE_NAME}", "--target", "${UEFI_TARGET}"] +dependencies = ["setup"] + +# Build library in release mode +[tasks.build-lib-release] +description = "Build the library for UEFI target in release mode" +command = "cargo" +args = ["build", "--lib", "-p", "${PACKAGE_NAME}", "--target", "${UEFI_TARGET}", "--release"] +dependencies = ["setup"] + +# Build binary +[tasks.build-bin] +description = "Build the binary for UEFI target" +command = "cargo" +args = ["build", "--bin", "${PACKAGE_NAME}", "-p", "${PACKAGE_NAME}", "--target", "${UEFI_TARGET}"] +dependencies = ["setup"] + +# Build binary in release mode +[tasks.build-bin-release] +description = "Build the binary for UEFI target in release mode" +command = "cargo" +args = ["build", "--bin", "${PACKAGE_NAME}", "-p", "${PACKAGE_NAME}", "--target", "${UEFI_TARGET}", "--release"] +dependencies = ["setup"] + +# Build EFI application +[tasks.build-efi] +description = "Build EFI application for specified package (release mode)" +dependencies = ["build-bin-release", "copy-efi-release"] + +# Create output directory +[tasks.create-output-dir] +description = "Create EFI output directory" +script_runner = "@duckscript" +script = ''' +mkdir ${TARGET_DIR}/efi +''' + +# Copy EFI file (release) +[tasks.copy-efi-release] +description = "Copy release EFI executable to output directory" +script_runner = "@duckscript" +script = ''' +echo "Copying release EFI executable..." +cp target/${UEFI_TARGET}/release/${PACKAGE_NAME}.efi target/efi/${PACKAGE_NAME}.efi +echo "EFI application built: target/efi/${PACKAGE_NAME}.efi" +''' +dependencies = ["create-output-dir"] + +# Copy EFI file (debug) +[tasks.copy-efi-debug] +description = "Copy debug EFI executable to output directory" +script_runner = "@duckscript" +script = ''' +echo "Copying debug EFI executable..." +cp target/${UEFI_TARGET}/debug/${PACKAGE_NAME}.efi target/efi/${PACKAGE_NAME}_debug.efi +echo "Debug EFI application built: target/efi/${PACKAGE_NAME}_debug.efi" +''' +dependencies = ["create-output-dir"] + +# Build specific package with custom name +[tasks.build-package] +description = "Build a specific package as EFI application" +script_runner = "@duckscript" +script = ''' +if is_empty ${PACKAGE} + echo "Error: PACKAGE environment variable must be set" + echo "Usage: cargo make build-package --env PACKAGE=package_name" + exit 1 +end + +set_env PACKAGE_NAME ${PACKAGE} +cm_run_task build-efi +''' + +# Build for x86_64 architecture +[tasks.build-x64] +description = "Build EFI application for x86_64" +env = { CARGO_MAKE_RUST_TARGET_ARCH = "x86_64", UEFI_TARGET = "x86_64-unknown-uefi" } +run_task = "build-efi" + +# Build for aarch64 architecture +[tasks.build-arm64] +description = "Build EFI application for aarch64" +env = { CARGO_MAKE_RUST_TARGET_ARCH = "aarch64", UEFI_TARGET = "aarch64-unknown-uefi" } +run_task = "build-efi" + +# Build for both architectures +[tasks.build] +description = "Build EFI application for all supported architectures" +dependencies = ["build-x64", "build-arm64"] + +# Development build +[tasks.build-debug] +description = "Build EFI application in debug mode" +dependencies = ["build-bin", "copy-efi-debug"] + +[tasks.test] +description = "Run tests and collect coverage data without generating reports." +install_crate = false +clear = true +command = "cargo" +args = ["llvm-cov", "--no-report"] + +# Clippy linting +[tasks.clippy] +description = "Run cargo clippy." +clear = true +command = "cargo" +args = ["clippy", "--all-targets", "--all-features", "--", "-D", "warnings"] + +# Format code +[tasks.fmt] +description = "Format code" +command = "cargo" +args = ["fmt", "-p", "${PACKAGE_NAME}"] + +# Check code +[tasks.check] +description = "Check code for the package" +command = "cargo" +args = ["check", "-p", "${PACKAGE_NAME}", "--target", "${UEFI_TARGET}"] +dependencies = ["setup"] + +# Full CI pipeline +[tasks.ci] +description = "Run full CI pipeline" +dependencies = ["fmt", "clippy", "test", "build-efi"] + +[tasks.deny] +description = "Run cargo deny." +install_crate = false +clear = true +command = "cargo" +args = ["deny", "check"] + +[tasks.coverage] +description = "Generate an LCOV coverage report from collected data." +install_crate = false +clear = true +command = "cargo" +args = ["llvm-cov", "report", "--lcov", "--output-path", "${CARGO_MAKE_WORKSPACE_WORKING_DIRECTORY}/target/lcov.info"] +dependencies = ["test"] + +[tasks.doc] +env = { RUSTDOCFLAGS = "-D warnings" } +description = "Builds all rust documentation in the workspace. Example `cargo make doc`" +command = "cargo" +args = ["doc", "@@split(INDIVIDUAL_PACKAGE_TARGETS, )", "--open"] + +[tasks.cspell] +description = "Run cspell for spell checking." # npm install -g cspell@latest +script = "cspell --quiet --no-progress --no-summary --dot --gitignore -e \"{.git/**,.github/**,.vscode/**,.azurepipelines/**}\" ." + +[tasks.all] +description = "Run all tasks for PR readiness." +dependencies = ["deny", "clippy", "cspell", "build", "test", "coverage", "fmt", "doc"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..0d3fca8 --- /dev/null +++ b/README.md @@ -0,0 +1,35 @@ +# Patina Apps + +A collection of EFI applications built with Rust for Patina UEFI environments. + +## Quick Start + +### Prerequisites + +- Rust toolchain with the appropriate UEFI target (based on VM/hardware) + +```bash +rustup target add [x86_64|aarch64|i686]-unknown-uefi +``` + +### Build + +```bash +# Build all applications in release mode. +cargo make build + +# Build all applications in debug mode. +cargo make build-debug + +# Build specific app. +cargo make --env PACKAGE= build-package +``` + +### Running + +Applications will be built into `target/efi/.efi`. +Copy the `.efi` to the system's drive (using USB drive or other methods) and run inside the UEFI shell. + +## Applications + +- **services_benchmark_test**: Benchmarks for core Patina service calls. Compares Rust timings to C. diff --git a/cspell.yml b/cspell.yml new file mode 100644 index 0000000..9c988a4 --- /dev/null +++ b/cspell.yml @@ -0,0 +1,33 @@ +# @file cspell.yml +# +# CSpell configuration file +# +## +# Copyright (c) Microsoft Corporation +# SPDX-License-Identifier: Apache-2.0 +## +version: "0.2" +language: en +dictionaries: + - "makefile" + - "rust" +ignorePaths: + - "**/target/**" + - "deny.toml" + - "Makefile.toml" +# ignore hex numbers, asm blocks and patterns like xxxx'xxxxx used in describing bit patterns +ignoreRegExpList: + - "/0x[0-9a-fA-F]+/" + - ".*asm!\\([\\s\\S]*?\\);" + - ".*'x*" + - ".*(?:read_sysreg!|write_sysreg!|print_sysreg!)\\([^)]*\\).*" +minWordLength: 5 +caseSensitive: false +allowCompoundWords: true +words: + - "Depex" + - "efiapi" + - "guids" + - "mdbook" + - "sysreg" + - "unsignaled" diff --git a/deny.toml b/deny.toml new file mode 100644 index 0000000..dd07394 --- /dev/null +++ b/deny.toml @@ -0,0 +1,209 @@ +# This template contains all of the possible sections and their default values + +# NOTE: This file is automatically synchronized from Patina DevOps. Update the original file there +# instead of the file in this repo. +# +# - Patina DevOps Repo: https://github.com/OpenDevicePartnership/patina-devops +# - File Sync Settings: https://github.com/OpenDevicePartnership/patina-devops/blob/main/.sync/Files.yml + +# Note that all fields that take a lint level have these possible values: +# * deny - An error will be produced and the check will fail +# * warn - A warning will be produced, but the check will not fail +# * allow - No warning or error will be produced, though in some cases a note +# will be + +# The values provided in this template are the default values that will be used +# when any section or field is not specified in your own configuration + +# Root options + +# The graph table configures how the dependency graph is constructed and thus +# which crates the checks are performed against +[graph] +targets = [] + +# When creating the dependency graph used as the source of truth when checks are +# executed, this field can be used to prune crates from the graph, removing them +# from the view of cargo-deny. This is an extremely heavy hammer, as if a crate +# is pruned from the graph, all of its dependencies will also be pruned unless +# they are connected to another crate in the graph that hasn't been pruned, +# so it should be used with care. The identifiers are [Package ID Specifications] +# (https://doc.rust-lang.org/cargo/reference/pkgid-spec.html) +#exclude = [] + +# If true, metadata will be collected with `--all-features`. Note that this can't +# be toggled off if true, if you want to conditionally enable `--all-features` it +# is recommended to pass `--all-features` on the cmd line instead +all-features = false + +# If true, metadata will be collected with `--no-default-features`. The same +# caveat with `all-features` applies +no-default-features = false + +# If set, these feature will be enabled when collecting metadata. If `--features` +# is specified on the cmd line they will take precedence over this option. +#features = [] + +# The output table provides options for how/if diagnostics are outputted +[output] +feature-depth = 1 + +# This section is considered when running `cargo deny check advisories` +# More documentation for the advisories section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/advisories/cfg.html +[advisories] +# A list of advisory IDs to ignore. Note that ignored advisories will still +# output a note when they are encountered. +ignore = [ + #"RUSTSEC-0000-0000", + #{ id = "RUSTSEC-0000-0000", reason = "you can specify a reason the advisory is ignored" }, + #"a-crate-that-is-yanked@0.1.1", # you can also ignore yanked crate versions if you wish + #{ crate = "a-crate-that-is-yanked@0.1.1", reason = "you can specify why you are ignoring the yanked crate" }, + + # None right now +] +# If this is true, then cargo deny will use the git executable to fetch advisory database. +# If this is false, then it uses a built-in git library. +git-fetch-with-cli = true +disable-yank-checking = true + +# This section is considered when running `cargo deny check licenses` +# More documentation for the licenses section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/licenses/cfg.html +[licenses] +unused-allowed-license = "allow" +# List of explicitly allowed licenses +# See https://spdx.org/licenses/ for list of possible licenses +# [possible values: any SPDX 3.11 short identifier (+ optional exception)]. +allow = [ + "Apache-2.0", + "BSD-2-Clause-Patent", + "MIT", +] +# The confidence threshold for detecting a license from license text. +# The higher the value, the more closely the license text must be to the +# canonical license text of a valid SPDX license file. +# [possible values: any between 0.0 and 1.0]. +confidence-threshold = 0.93 +# Allow 1 or more licenses on a per-crate basis, so that particular licenses +# aren't accepted for every possible crate as with the normal allow list +exceptions = [ + # Each entry is the crate and version constraint, and its specific allow + # list + #{ allow = ["Zlib"], crate = "adler32" }, + { allow = ["Unicode-3.0"], crate = "unicode-ident" }, + { allow = ["MPL-2.0"], crate = "colored" }, + { allow = ["MPL-2.0"], crate = "ucs2" } +] + +[licenses.private] +# If true, ignores workspace crates that aren't published, or are only +# published to private registries. +# To see how to mark a crate as unpublished (to the official registry), +# visit https://doc.rust-lang.org/cargo/reference/manifest.html#the-publish-field. +ignore = false +# One or more private registries that you might publish crates to, if a crate +# is only published to private registries, and ignore is true, the crate will +# not have its license(s) checked +registries = [] + +# This section is considered when running `cargo deny check bans`. +# More documentation about the 'bans' section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/bans/cfg.html +[bans] +# Lint level for when multiple versions of the same crate are detected +multiple-versions = "warn" +# Lint level for when a crate version requirement is `*` +wildcards = "allow" +# The graph highlighting used when creating dotgraphs for crates +# with multiple versions +# * lowest-version - The path to the lowest versioned duplicate is highlighted +# * simplest-path - The path to the version with the fewest edges is highlighted +# * all - Both lowest-version and simplest-path are used +highlight = "all" +# The default lint level for `default` features for crates that are members of +# the workspace that is being checked. This can be overridden by allowing/denying +# `default` on a crate-by-crate basis if desired. +workspace-default-features = "allow" +# The default lint level for `default` features for external crates that are not +# members of the workspace. This can be overridden by allowing/denying `default` +# on a crate-by-crate basis if desired. +external-default-features = "allow" +# List of crates that are allowed. Use with care! +allow = [ + #"ansi_term@0.11.0", + #{ crate = "ansi_term@0.11.0", reason = "you can specify a reason it is allowed" }, +] +# List of crates to deny +deny = [ + #"ansi_term@0.11.0", + #{ crate = "ansi_term@0.11.0", reason = "you can specify a reason it is banned" }, + # Wrapper crates can optionally be specified to allow the crate when it + # is a direct dependency of the otherwise banned crate + #{ crate = "ansi_term@0.11.0", wrappers = ["this-crate-directly-depends-on-ansi_term"] }, + { crate = "tiny-keccak", reason = "Not updated in 5 years. Use alternative." } +] + +# List of features to allow/deny +# Each entry the name of a crate and a version range. If version is +# not specified, all versions will be matched. +#[[bans.features]] +#crate = "reqwest" +# Features to not allow +#deny = ["json"] +# Features to allow +#allow = [ +# "rustls", +# "__rustls", +# "__tls", +# "hyper-rustls", +# "rustls", +# "rustls-pemfile", +# "rustls-tls-webpki-roots", +# "tokio-rustls", +# "webpki-roots", +#] +# If true, the allowed features must exactly match the enabled feature set. If +# this is set there is no point setting `deny` +#exact = true + +# Certain crates/versions that will be skipped when doing duplicate detection. +skip = [ + #"ansi_term@0.11.0", + #{ crate = "ansi_term@0.11.0", reason = "you can specify a reason why it can't be updated/removed" }, + + { crate = "bitflags", reason = "Need https://github.com/gz/rust-x86/pull/150 to be merged into rust-x86." }, + { crate = "windows-sys", reason = "https://crates.io/crates/colored latest (v3.0.0) uses 0.59. https://crates.io/crates/anstyle-query latest (v1.1.5) uses 0.61." } +] +# Similarly to `skip` allows you to skip certain crates during duplicate +# detection. Unlike skip, it also includes the entire tree of transitive +# dependencies starting at the specified crate, up to a certain depth, which is +# by default infinite. +skip-tree = [ + #"ansi_term@0.11.0", # will be skipped along with _all_ of its direct and transitive dependencies + #{ crate = "ansi_term@0.11.0", depth = 20 }, +] + +# This section is considered when running `cargo deny check sources`. +# More documentation about the 'sources' section can be found here: +# https://embarkstudios.github.io/cargo-deny/checks/sources/cfg.html +[sources] +# Lint level for what to happen when a crate from a crate registry that is not +# in the allow list is encountered +unknown-registry = "deny" +# Lint level for what to happen when a crate from a git repository that is not +# in the allow list is encountered +unknown-git = "deny" +# List of URLs for allowed crate registries. Defaults to the crates.io index +# if not specified. If it is specified but empty, no registries are allowed. +# allow-registry = ["registry+https://github.com/rust-lang/crates.io-index"] +# List of URLs for allowed Git repositories +allow-git = [] +allow-registry = [ + "registry+https://github.com/rust-lang/crates.io-index", +] + +[sources.allow-org] +# github.com organizations to allow git sources for +# No source is currently used from the microsoft GitHub organization so it is commented +# github = ["microsoft"] diff --git a/docs/services_benchmark_test/benchmarks.md b/docs/services_benchmark_test/benchmarks.md new file mode 100644 index 0000000..201a2dc --- /dev/null +++ b/docs/services_benchmark_test/benchmarks.md @@ -0,0 +1,247 @@ +# Core Services Benchmarks + +`services_benchmark_test` compares core service performance between the Patina (Rust) implementation and canonical C implementation. + +## Usage + +To build and use the benchmark tool: + +### Build Benchmark Package + +```bash +# Build a specific package +cargo make --env PACKAGE=services_benchmark_test build-package +``` + +## Benchmark Categories + +The benchmark suite tests the performance of 30 different UEFI Boot Services across 6 categories. +For more information on boot services, see the [UEFI spec](https://uefi.org/specs/UEFI/2.9_A/07_Services_Boot_Services.html). + +### Iterations + +The number of iterations per benchmark is derived from operation counts during normal operation of the Patina core. +The exact counts can be found in [memory_safety_strategy.md in Patina](https://opendevicepartnership.github.io/patina/background/memory_safety_strategy.html). +The benchmarks here use similar orders of magnitude rather than exact counts. + +### 1. Controller Services + +#### `connect_controller` (100 iterations) + +**File**: `bench/controller.rs` + +Benchmarks the UEFI driver model's controller connection mechanism. This primarily measures device driver performance +in UEFI systems. + +### 2. Event Services + +#### `bench_check_event_signaled` (10000 iterations) + +**File**: `bench/event.rs` + +Benchmarks checking the state of an already-signaled event. This is the fast path of `check_event`. + +#### `bench_check_event_unsignaled` (10000 iterations) + +**File**: `bench/event.rs` + +Benchmarks checking the state of an unsignaled event. +This is the slow path of `check_event` and is important for event polling scenarios. + +#### `create_event` (1000 iterations) + +**File**: `bench/event.rs` + +Benchmarks event creation performance. + +#### `close_event` (1000 iterations) + +**File**: `bench/event.rs` + +Benchmarks event cleanup (close) performance. + +#### `signal_event` (100000 iterations) + +**File**: `bench/event.rs` + +Benchmarks individual event signaling. + +#### `signal_event_group` (100 iterations) + +**File**: `bench/event.rs` + +Benchmarks signaling multiple events as a group. +The time taken by `signal_event` is scales with the number of events in the group, +so this benchmark gradually increases the number of events by 1 per iteration. + +### 3. Image Services + +#### `start_image, exit` (100 iterations) + +**File**: `bench/image.rs` + +Benchmarks UEFI image execution performance through a no-op image that exits immediately. +This does not benchmark an individual function as it is difficult to measure `start_image` and `exit` indpendently. +Instead, this benchmark roughly measures the performance of a complete image execution lifecycle. + +#### `load_image` (100 iterations) + +**File**: `bench/image.rs` + +Benchmarks UEFI image loading performance. + +### 4. Memory Services + +#### `allocate_pages` (1000 iterations) + +**File**: `bench/memory.rs` + +Benchmarks page-level memory allocation (with size 1 page / 4KB). + +#### `allocate_pool` (10000 iterations) + +**File**: `bench/memory.rs` + +Benchmarks pool memory allocation (of size 1KB). Models smaller, more frequent memory allocations as compared to `allocate_pages`. + +#### `free_pages` (100 iterations) + +**File**: `bench/memory.rs` + +Benchmarks page deallocation performance. + +#### `free_pool` (10000 iterations) + +**File**: `bench/memory.rs` + +Benchmarks pool memory deallocation. +Like `allocate_pool`, this represents smaller, more frequent memory allocations in the core. + +#### `copy_mem` (10 iterations) + +**File**: `bench/memory.rs` + +Benchmarks memory copying performance. This is not currently used in the Patina DXE core. + +#### `set_mem` (10 iterations) + +**File**: `bench/memory.rs` + +Benchmarks memory initialization performance. This is not currently used in the Patina DXE core. + +#### `get_memory_map` (10 iterations) + +**File**: `bench/memory.rs` + +Benchmarks system memory map retrieval. This is critical for OS loaders and memory managers. + +### 5. Miscellaneous Services + +#### `calculate_crc32` (100 iterations) + +**File**: `bench/misc.rs` + +Benchmarks checksum calculation performance (over 128 bytes of data). + +#### `install_configuration_table` (10 iterations) + +**File**: `bench/misc.rs` + +Benchmarks configuration table installation. + +### 6. Protocol Services + +#### `install_protocol_interface` (100 iterations) + +**File**: `bench/protocol.rs` + +Benchmarks protocol installation performance. + +#### `open_protocol` (10000 iterations) + +**File**: `bench/protocol.rs` + +Benchmarks protocol access performance. This is the preferred method for retrieving protocol interfaces in modern UEFI (2.0+). + +#### `handle_protocol` (10000 iterations) + +**File**: `bench/protocol.rs` + +Benchmarks protocol access. This is a legacy method but is still included due to needing to support legacy UEFI (1.0). + +#### `close_protocol` (100 iterations) + +**File**: `bench/protocol.rs` + +Benchmarks protocol cleanup performance. + +#### `locate_device_path` (100 iterations) + +**File**: `bench/protocol.rs` + +Benchmarks device path resolution. + +#### `open_protocol_information` (100 iterations) + +**File**: `bench/protocol.rs` + +Benchmarks protocol metadata retrieval. + +#### `protocols_per_handle` (100 iterations) + +**File**: `bench/protocol.rs` + +Tests handle protocol enumeration. + +#### `register_protocol_notify` (10 iterations) + +**File**: `bench/protocol.rs` + +Benchmarks protocol notification registration. This is used infrequently in the Patina DXE core. + +#### `reinstall_protocol_interface` (100 iterations) + +**File**: `bench/protocol.rs` + +Benchmarks protocol update performance. This sometimes triggers `connect/disconnect_controller` +and can be more time-consuming than `install_protocol_interface`. + +#### `uninstall_protocol_interface` (10 iterations) + +**File**: `bench/protocol.rs` + +Benchmarks protocol removal performance. This is used infrequently in the Patina DXE core. + +### 7. Task Priority Level (TPL) Services + +#### `raise_tpl` (1000000 iterations) + +**File**: `bench/tpl.rs` + +Tests interrupt disable performance. Raises to TPL_HIGH_LEVEL to test the maximum performance impact of interrupts. + +#### `restore_tpl` (1000000 iterations) + +**File**: `bench/tpl.rs` + +Tests interrupt restore performance. Uses all TPL levels to test the performance impact of restoring to each level. + +## Performance Characteristics + +The benchmarks measure cycle counts using CPU performance counters, providing: + +- **Total Cycles**: Raw CPU cycles consumed +- **Cycles/Operation**: Average cycles per function call +- **Total Time**: Wall-clock time in milliseconds +- **Statistical Data**: Min, max, and standard deviation +- **Call Count**: Number of iterations for statistical significance + +## Output Format + +Results are displayed as a markdown table in the UEFI shell (one sample row shown below): + +```plain-text +| Name | Total cycles | Total calls | Cycles/op | Total time (ms) | Min cycles | Max cycles | SD [cycles] | +| ------------------ | ------------ | ----------- | --------- | --------------- | ---------- | ---------- | ----------- | +| connect_controller | 1234567 | 100 | 12345.67 | 45.67 | 10000 | 15000 | 1500 | +``` diff --git a/services_benchmark_test/Cargo.toml b/services_benchmark_test/Cargo.toml new file mode 100644 index 0000000..d0d077e --- /dev/null +++ b/services_benchmark_test/Cargo.toml @@ -0,0 +1,42 @@ +# +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 +# +[package] +name = "services_benchmark_test" +version.workspace = true +edition.workspace = true +repository.workspace = true +license.workspace = true +description = "Core service benchmarks" + +[lints] +workspace = true + +[lib] +name = "services_benchmark_test" +path = "src/lib.rs" + +[[bin]] +name = "services_benchmark_test" +path = "src/main.rs" + +[dependencies] +mu_rust_helpers = { workspace = true } + +cfg-if = { workspace = true } +log = { workspace = true } +r-efi = { workspace = true } +rolling-stats = { workspace = true } +uuid = { workspace = true } + +patina = { workspace = true } + +[target.'cfg(target_os = "uefi")'.dependencies] +uefi = { version = "0.36.0", features = ["alloc", "logger", "global_allocator"] } + +[target.'cfg(not(target_os = "uefi"))'.dependencies] +uefi = { version = "0.36.0", default-features = false } + +[dev-dependencies] +mockall = "0.13.0" diff --git a/services_benchmark_test/resources/NoopImage.efi b/services_benchmark_test/resources/NoopImage.efi new file mode 100644 index 0000000..5999cc9 Binary files /dev/null and b/services_benchmark_test/resources/NoopImage.efi differ diff --git a/services_benchmark_test/resources/test_image.te b/services_benchmark_test/resources/test_image.te new file mode 100644 index 0000000..8ac7c77 Binary files /dev/null and b/services_benchmark_test/resources/test_image.te differ diff --git a/services_benchmark_test/src/bench.rs b/services_benchmark_test/src/bench.rs new file mode 100644 index 0000000..c3e4211 --- /dev/null +++ b/services_benchmark_test/src/bench.rs @@ -0,0 +1,37 @@ +//! Defines constants and benchmarks used to evaluate core performance. +//! +//! Copyright (c) Microsoft Corporation. +//! +//! SPDX-License-Identifier: Apache-2.0 +//! + +use patina::uefi_protocol::ProtocolInterface; +use r_efi::efi; + +pub(crate) mod controller; +pub(crate) mod event; +pub(crate) mod image; +pub(crate) mod memory; +pub(crate) mod misc; +pub(crate) mod protocol; +pub(crate) mod tpl; + +/// Some static test guids for protocols. +const TEST_GUID1: efi::Guid = + efi::Guid::from_fields(0x12345678, 0x1234, 0x5678, 0x9a, 0xbc, &[0xde, 0xf0, 0x12, 0x34, 0x56, 0x78]); +const TEST_GUID2: efi::Guid = + efi::Guid::from_fields(0x87654321, 0x4321, 0x8765, 0xba, 0x98, &[0x76, 0x54, 0x32, 0x10, 0xfe, 0xdc]); + +pub struct TestProtocol1 {} + +// SAFETY: This is a test protocol with no layout requirements. +unsafe impl ProtocolInterface for TestProtocol1 { + const PROTOCOL_GUID: efi::Guid = TEST_GUID1; +} + +pub struct TestProtocol2 {} + +// SAFETY: This is a test protocol with no layout requirements. +unsafe impl ProtocolInterface for TestProtocol2 { + const PROTOCOL_GUID: efi::Guid = TEST_GUID2; +} diff --git a/services_benchmark_test/src/bench/controller.rs b/services_benchmark_test/src/bench/controller.rs new file mode 100644 index 0000000..58b0dc5 --- /dev/null +++ b/services_benchmark_test/src/bench/controller.rs @@ -0,0 +1,109 @@ +//! Benchmarks for UEFI controller connection mechanism. +//! +//! Copyright (c) Microsoft Corporation. +//! +//! SPDX-License-Identifier: Apache-2.0 +//! + +use crate::alloc::{boxed::Box, vec}; + +use mu_rust_helpers::perf_timer::{Arch, ArchFunctionality as _}; +use patina::boot_services::BootServices; +use r_efi::efi; +use rolling_stats::Stats; + +use crate::{ + BOOT_SERVICES, + bench::{TestProtocol1, TestProtocol2}, + error::BenchError, +}; + +/// Benchmarks the UEFI driver model's controller connection mechanism. +pub(crate) fn bench_connect_controller(_handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + /// Mock driver binding protocols definitions. + extern "efiapi" fn mock_supported( + _this: *mut efi::protocols::driver_binding::Protocol, + _controller_handle: efi::Handle, + _remaining_device_path: *mut efi::protocols::device_path::Protocol, + ) -> efi::Status { + efi::Status::SUCCESS + } + + extern "efiapi" fn mock_start( + _this: *mut efi::protocols::driver_binding::Protocol, + _controller_handle: efi::Handle, + _remaining_device_path: *mut efi::protocols::device_path::Protocol, + ) -> efi::Status { + efi::Status::SUCCESS + } + + extern "efiapi" fn mock_stop( + _this: *mut efi::protocols::driver_binding::Protocol, + _controller_handle: efi::Handle, + _num_children: usize, + _child_handle_buffer: *mut efi::Handle, + ) -> efi::Status { + efi::Status::SUCCESS + } + + // Setup controller, driver, and image handles with test protocols. + let controller_install = BOOT_SERVICES + .install_protocol_interface(None, Box::new(TestProtocol1 {})) + .map_err(|e| BenchError::BenchSetup("Failed to install protocol interface for controller", e))?; + + let driver_install = BOOT_SERVICES + .install_protocol_interface( + None, + Box::new(efi::protocols::device_path::Protocol { r#type: 4, sub_type: 5, length: [0, 0] }), + ) + .map_err(|e| BenchError::BenchSetup("Failed to install protocol interface for driver", e))?; + + let image_install = BOOT_SERVICES + .install_protocol_interface(None, Box::new(TestProtocol2 {})) + .map_err(|e| BenchError::BenchSetup("Failed to install protocol interface for image", e))?; + + let binding = Box::new(efi::protocols::driver_binding::Protocol { + version: 10, + supported: mock_supported, + start: mock_start, + stop: mock_stop, + driver_binding_handle: driver_install.0, + image_handle: image_install.0, + }); + + let driver_binding = BOOT_SERVICES + .install_protocol_interface(Some(driver_install.0), binding) + .map_err(|e| BenchError::BenchSetup("Failed to install protocol interface for driver binding", e))?; + + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let start = Arch::cpu_count(); + // SAFETY: All handles and pointers are valid (constructed by benchmark). + unsafe { + BOOT_SERVICES + .connect_controller(controller_install.0, vec![driver_install.0], core::ptr::null_mut(), false) + .map_err(|e| BenchError::BenchTest("Failed to connect controller", e))?; + } + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + BOOT_SERVICES + .disconnect_controller(controller_install.0, None, None) + .map_err(|e| BenchError::BenchCleanup("Failed to disconnect controller", e))?; + } + + // Uninstall protocols to prevent side effects. + BOOT_SERVICES + .uninstall_protocol_interface(driver_binding.0, driver_binding.1) + .map_err(|e| BenchError::BenchCleanup("Failed to uninstall protocol interface", e))?; + BOOT_SERVICES + .uninstall_protocol_interface(driver_install.0, driver_install.1) + .map_err(|e| BenchError::BenchCleanup("Failed to uninstall protocol interface", e))?; + BOOT_SERVICES + .uninstall_protocol_interface(image_install.0, image_install.1) + .map_err(|e| BenchError::BenchCleanup("Failed to uninstall protocol interface", e))?; + BOOT_SERVICES + .uninstall_protocol_interface(controller_install.0, controller_install.1) + .map_err(|e| BenchError::BenchCleanup("Failed to uninstall protocol interface", e))?; + + Ok(stats) +} diff --git a/services_benchmark_test/src/bench/event.rs b/services_benchmark_test/src/bench/event.rs new file mode 100644 index 0000000..7485f1e --- /dev/null +++ b/services_benchmark_test/src/bench/event.rs @@ -0,0 +1,155 @@ +//! Benchmarks for event handling. +//! +//! Copyright (c) Microsoft Corporation. +//! +//! SPDX-License-Identifier: Apache-2.0 +//! + +use crate::alloc::vec::Vec; + +use core::{ffi::c_void, ptr}; + +use mu_rust_helpers::perf_timer::{Arch, ArchFunctionality as _}; +use patina::boot_services::{BootServices, event::EventType, tpl::Tpl}; +use r_efi::efi; +use rolling_stats::Stats; + +use crate::{BOOT_SERVICES, error::BenchError}; + +/// Benchmarks checking the state of an already-signaled event (fast path). +pub(crate) fn bench_check_event_signaled(_handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + extern "efiapi" fn test_notify(_event: efi::Event, _context: *mut c_void) {} + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let event_handle = BOOT_SERVICES + .create_event(EventType::NOTIFY_WAIT, Tpl::NOTIFY, Some(test_notify), ptr::null_mut()) + .map_err(|e| BenchError::BenchSetup("Failed to create event", e))?; + // Signal the event to set it to the signaled state. + BOOT_SERVICES.signal_event(event_handle).map_err(|e| BenchError::BenchSetup("Failed to signal event", e))?; + + let start = Arch::cpu_count(); + BOOT_SERVICES.check_event(event_handle).map_err(|e| BenchError::BenchTest("check_event failed", e))?; + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + + BOOT_SERVICES.close_event(event_handle).map_err(|e| BenchError::BenchCleanup("Failed to close event", e))?; + } + Ok(stats) +} + +/// Benchmarks checking the state of an unsignaled event (slow path). +pub(crate) fn bench_check_event_unsignaled(_handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + extern "efiapi" fn test_notify(_event: efi::Event, _context: *mut c_void) {} + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let event_handle = BOOT_SERVICES + .create_event(EventType::NOTIFY_WAIT, Tpl::NOTIFY, Some(test_notify), ptr::null_mut()) + .map_err(|e| BenchError::BenchSetup("Failed to create event", e))?; + + let start = Arch::cpu_count(); + if let Err(e) = BOOT_SERVICES.check_event(event_handle) { + // In this case a NOT_READY error is acceptable since the event is unsignaled. + if e != efi::Status::SUCCESS && e != efi::Status::NOT_READY { + return Err(BenchError::BenchTest("check_event returned unexpected status", e)); + } + } + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + + BOOT_SERVICES.close_event(event_handle).map_err(|e| BenchError::BenchCleanup("Failed to close event", e))?; + } + Ok(stats) +} + +/// Benchmarks event creation performance. +pub(crate) fn bench_create_event(_handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + extern "efiapi" fn test_notify(_event: efi::Event, _context: *mut c_void) {} + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let start = Arch::cpu_count(); + let event_handle = BOOT_SERVICES + .create_event(EventType::NOTIFY_WAIT, Tpl::NOTIFY, Some(test_notify), ptr::null_mut()) + .map_err(|e| BenchError::BenchTest("Failed to create event", e))?; + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + + // Clean up the created event. + BOOT_SERVICES.close_event(event_handle).map_err(|e| BenchError::BenchCleanup("Failed to close event", e))?; + } + Ok(stats) +} + +/// Benchmarks event closing performance. +pub(crate) fn bench_close_event(_handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + extern "efiapi" fn test_notify(_event: efi::Event, _context: *mut c_void) {} + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let event_handle = BOOT_SERVICES + .create_event(EventType::NOTIFY_WAIT, Tpl::NOTIFY, Some(test_notify), ptr::null_mut()) + .map_err(|e| BenchError::BenchSetup("Failed to create event", e))?; + let start = Arch::cpu_count(); + BOOT_SERVICES.close_event(event_handle).map_err(|e| BenchError::BenchTest("Failed to close event", e))?; + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + } + Ok(stats) +} + +/// Benchmarks individual event signaling. +pub(crate) fn bench_signal_event(_handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + extern "efiapi" fn test_notify(_event: efi::Event, _context: *mut c_void) {} + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let event_handle = BOOT_SERVICES + .create_event(EventType::NOTIFY_WAIT, Tpl::NOTIFY, Some(test_notify), ptr::null_mut()) + .map_err(|e| BenchError::BenchSetup("Failed to create event", e))?; + + let start = Arch::cpu_count(); + BOOT_SERVICES.signal_event(event_handle).map_err(|e| BenchError::BenchTest("Failed to signal event", e))?; + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + + BOOT_SERVICES.close_event(event_handle).map_err(|e| BenchError::BenchCleanup("Failed to close event", e))?; + } + Ok(stats) +} + +/// Tests signaling multiple events as a group. +pub(crate) fn bench_signal_event_group(_handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + let mut stats: Stats = Stats::new(); + + // No-op notify function. We want to measure only the signaling overhead. + extern "efiapi" fn test_notify(_event: efi::Event, _context: *mut c_void) {} + + // Use a mock GUID to avoid signalling real event groups. + const BENCH_EVENT_GROUP: efi::Guid = + efi::Guid::from_fields(0x12345678, 0x9abc, 0xdef0, 0x12, 0x34, &[0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0]); + + // The event group will increase in size with each iteration to test the impact of group size on signaling time. + let mut event_grp = Vec::with_capacity(num_calls); + for _ in 0..num_calls { + let event_handle = BOOT_SERVICES + .create_event_ex( + EventType::NOTIFY_WAIT, + Tpl::NOTIFY, + Some(test_notify), + ptr::null_mut(), + &BENCH_EVENT_GROUP, + ) + .map_err(|e| BenchError::BenchSetup("Failed to create event", e))?; + event_grp.push(event_handle); + + let start = Arch::cpu_count(); + // Signals the most recently created event in the group. + BOOT_SERVICES.signal_event(event_handle).map_err(|e| BenchError::BenchTest("Failed to signal event", e))?; + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + } + + // Clean up all created events. + for event_handle in event_grp { + BOOT_SERVICES.close_event(event_handle).map_err(|e| BenchError::BenchCleanup("Failed to close event", e))?; + } + + Ok(stats) +} diff --git a/services_benchmark_test/src/bench/image.rs b/services_benchmark_test/src/bench/image.rs new file mode 100644 index 0000000..2021463 --- /dev/null +++ b/services_benchmark_test/src/bench/image.rs @@ -0,0 +1,58 @@ +//! Benchmarks for image loading and execution. +//! +//! Copyright (c) Microsoft Corporation. +//! +//! SPDX-License-Identifier: Apache-2.0 +//! + +use mu_rust_helpers::perf_timer::{Arch, ArchFunctionality as _}; +use patina::boot_services::BootServices; +use r_efi::efi; +use rolling_stats::Stats; + +use crate::{BOOT_SERVICES, error::BenchError}; + +/// Benchmarks UEFI image execution performance through a no-op image that exits immediately. +/// As `start_image` and `exit` are difficult to bench individually, this benchmark combines them. +pub(crate) fn bench_start_image_and_exit( + parent_handle: efi::Handle, + num_calls: usize, +) -> Result, BenchError> { + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + // The image `NoopImage.efi` is a no-op image that exits immediately. + let image_bytes = include_bytes!("../../resources/NoopImage.efi"); + let loaded_image_handle = BOOT_SERVICES + .load_image(false, parent_handle, core::ptr::null_mut(), Some(image_bytes)) + .map_err(|e| BenchError::BenchSetup("Failed to load image", e))?; + + let start = Arch::cpu_count(); + // This also includes `exit` as the image exits immediately. + BOOT_SERVICES + .start_image(loaded_image_handle) + .map_err(|e| BenchError::BenchTest("Failed to start image", e.0))?; + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + } + Ok(stats) +} + +/// Measures UEFI image loading performance using a no-op image. +pub(crate) fn bench_load_image(parent_handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let image_bytes = include_bytes!("../../resources/NoopImage.efi"); + let start = Arch::cpu_count(); + let _loaded_image_handle = BOOT_SERVICES + .load_image(false, parent_handle, core::ptr::null_mut(), Some(image_bytes)) + .map_err(|e| BenchError::BenchTest("Failed to load image", e))?; + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + + // Unload the image to avoid resource leaks. + BOOT_SERVICES + .unload_image(_loaded_image_handle) + .map_err(|e| BenchError::BenchCleanup("Failed to unload image", e))?; + } + Ok(stats) +} diff --git a/services_benchmark_test/src/bench/memory.rs b/services_benchmark_test/src/bench/memory.rs new file mode 100644 index 0000000..9ba1990 --- /dev/null +++ b/services_benchmark_test/src/bench/memory.rs @@ -0,0 +1,124 @@ +//! Benchmarks for memory operations. +//! +//! Copyright (c) Microsoft Corporation. +//! +//! SPDX-License-Identifier: Apache-2.0 +//! + +use mu_rust_helpers::perf_timer::{Arch, ArchFunctionality as _}; +use patina::{ + base::UEFI_PAGE_SIZE, + boot_services::{self, BootServices as _}, + efi_types::EfiMemoryType, +}; +use r_efi::efi; +use rolling_stats::Stats; + +use crate::{BOOT_SERVICES, error::BenchError}; + +/// Benchmarks page-level memory allocation. +pub(crate) fn bench_allocate_pages(_handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let start = Arch::cpu_count(); + // Use `BOOT_SERVICES_DATA` as it is commonly allocated during boot services/driver initialization. + let pages = BOOT_SERVICES + .allocate_pages(boot_services::allocation::AllocType::AnyPage, EfiMemoryType::BootServicesData, 1) + .map_err(|e| BenchError::BenchTest("Failed to allocate pages", e))?; + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + + BOOT_SERVICES.free_pages(pages, 1).map_err(|e| BenchError::BenchCleanup("Failed to free pages", e))?; + } + Ok(stats) +} + +/// Benchmarks pool memory allocation. +pub(crate) fn bench_allocate_pool(_handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let start = Arch::cpu_count(); + // Use `BOOT_SERVICES_DATA` as it is commonly allocated during boot services/driver initialization. + let pool = BOOT_SERVICES + .allocate_pool(EfiMemoryType::BootServicesData, UEFI_PAGE_SIZE / 4) + .map_err(|e| BenchError::BenchTest("Failed to allocate pool", e))?; + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + + BOOT_SERVICES.free_pool(pool).map_err(|e| BenchError::BenchCleanup("Failed to free pool", e))?; + } + Ok(stats) +} + +/// Benchmarks page memory deallocation. +pub(crate) fn bench_free_pages(_handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + // Use `BOOT_SERVICES_DATA` as it is commonly allocated during boot services/driver initialization. + let pages = BOOT_SERVICES + .allocate_pages(boot_services::allocation::AllocType::AnyPage, EfiMemoryType::BootServicesData, 1) + .map_err(|e| BenchError::BenchSetup("Failed to allocate pages", e))?; + + let start = Arch::cpu_count(); + BOOT_SERVICES.free_pages(pages, 1).map_err(|e| BenchError::BenchTest("Failed to free pages", e))?; + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + } + Ok(stats) +} + +/// Benchmarks pool memory deallocation. +pub(crate) fn bench_free_pool(_handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + // Use `BOOT_SERVICES_DATA` as it is commonly allocated during boot services/driver initialization. + let pool = BOOT_SERVICES + .allocate_pool(EfiMemoryType::BootServicesData, UEFI_PAGE_SIZE / 4) + .map_err(|e| BenchError::BenchSetup("Failed to allocate pool", e))?; + + let start = Arch::cpu_count(); + BOOT_SERVICES.free_pool(pool).map_err(|e| BenchError::BenchTest("Failed to free pool", e))?; + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + } + Ok(stats) +} + +/// Benchmarks memory copying performance. +pub(crate) fn bench_copy_mem(_handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + let src: u64 = 5678; + let mut dst: u64 = 1234; + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let start = Arch::cpu_count(); + BOOT_SERVICES.copy_mem::(&mut dst, &src); + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + } + Ok(stats) +} + +/// Benchmarks memory initialization performance. +pub(crate) fn bench_set_mem(_handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + let mut dst: [u8; 128] = [0; 128]; + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let start = Arch::cpu_count(); + BOOT_SERVICES.set_mem(&mut dst, 1); + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + } + Ok(stats) +} + +/// Benchmarks system memory map retrieval. +pub(crate) fn bench_get_memory_map(_handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let start = Arch::cpu_count(); + BOOT_SERVICES.get_memory_map().map_err(|e| BenchError::BenchTest("Failed to get memory map", e.0))?; + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + } + Ok(stats) +} diff --git a/services_benchmark_test/src/bench/misc.rs b/services_benchmark_test/src/bench/misc.rs new file mode 100644 index 0000000..392c1e0 --- /dev/null +++ b/services_benchmark_test/src/bench/misc.rs @@ -0,0 +1,60 @@ +//! Benchmarks for general UEFI services. +//! +//! Copyright (c) Microsoft Corporation. +//! +//! SPDX-License-Identifier: Apache-2.0 +//! + +use core::ffi::c_void; + +use mu_rust_helpers::perf_timer::{Arch, ArchFunctionality as _}; +use patina::boot_services::BootServices as _; +use r_efi::efi; +use rolling_stats::Stats; + +use crate::{BOOT_SERVICES, bench::TEST_GUID1, error::BenchError}; + +/// Benchmarks checksum calculation performance. +pub(crate) fn bench_calculate_crc32(_handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + // The actual data values do not affect CRC32 performance, so use all zeroes for simplicity. + let data: [u8; 128] = [0; 128]; + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let start = Arch::cpu_count(); + let _crc = + BOOT_SERVICES.calculate_crc_32(&data).map_err(|e| BenchError::BenchTest("Failed to calculate CRC32", e))?; + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + } + Ok(stats) +} + +/// Benchmarks installation of a configuration table. +pub(crate) fn bench_install_configuration_table( + _handle: efi::Handle, + num_calls: usize, +) -> Result, BenchError> { + let table: u64 = 0xDEADBEEF; + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let start = Arch::cpu_count(); + // SAFETY: The test configuration table has no specific layout requirements. + unsafe { + // We do not need to clean up the installed table on each iteration as + // installing a table with a duplicate GUID simply overwrites the previous entry. + BOOT_SERVICES + .install_configuration_table(&TEST_GUID1, &table as *const u64 as *mut c_void) + .map_err(|e| BenchError::BenchTest("Failed to install configuration table", e))?; + } + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + } + // Remove the table by passing a NULL pointer. + // SAFETY: The test configuration table has no specific layout requirements. + (unsafe { + BOOT_SERVICES + .install_configuration_table(&TEST_GUID1, core::ptr::null_mut() as *const u64 as *mut c_void) + .map_err(|e| BenchError::BenchCleanup("Failed to remove configuration table", e)) + })?; + Ok(stats) +} diff --git a/services_benchmark_test/src/bench/protocol.rs b/services_benchmark_test/src/bench/protocol.rs new file mode 100644 index 0000000..4cdf22d --- /dev/null +++ b/services_benchmark_test/src/bench/protocol.rs @@ -0,0 +1,312 @@ +//! Benchmarks for protocol handling. +//! +//! Copyright (c) Microsoft Corporation. +//! +//! SPDX-License-Identifier: Apache-2.0 +//! + +use core::ffi::c_void; + +use mu_rust_helpers::perf_timer::{Arch, ArchFunctionality as _}; +use patina::boot_services::{BootServices, event::EventType, tpl::Tpl}; +use r_efi::efi; +use rolling_stats::Stats; + +use crate::{ + BOOT_SERVICES, + bench::{TEST_GUID1, TestProtocol1}, + error::BenchError, +}; + +use crate::alloc::boxed::Box; + +/// Benchmarks protocol installation performance. +pub(crate) fn bench_install_protocol_interface( + _handle: efi::Handle, + num_calls: usize, +) -> Result, BenchError> { + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let start = Arch::cpu_count(); + let protocol_install = BOOT_SERVICES + .install_protocol_interface(None, Box::new(TestProtocol1 {})) + .map_err(|e| BenchError::BenchTest("Failed to install protocol", e))?; + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + BOOT_SERVICES + .uninstall_protocol_interface(protocol_install.0, protocol_install.1) + .map_err(|e| BenchError::BenchCleanup("Failed to uninstall protocol", e))?; + } + Ok(stats) +} + +/// Benchmarks protocol opening performance. +/// This is the preferred method (over `handle_protocol`) for retrieving protocol interfaces in modern UEFI (2.0+). +pub(crate) fn bench_open_protocol(_handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + // Set up and install the protocol to be opened. + let agent_install = BOOT_SERVICES + .install_protocol_interface(None, Box::new(TestProtocol1 {})) + .map_err(|e| BenchError::BenchSetup("Failed to install agent protocol", e))?; + let controller_install = BOOT_SERVICES + .install_protocol_interface(None, Box::new(TestProtocol1 {})) + .map_err(|e| BenchError::BenchSetup("Failed to install controller protocol", e))?; + let protocol_install = BOOT_SERVICES + .install_protocol_interface(None, Box::new(TestProtocol1 {})) + .map_err(|e| BenchError::BenchSetup("Failed to install protocol", e))?; + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let start = Arch::cpu_count(); + // SAFETY: The resulting interface reference is not used at all during the test. + (unsafe { + BOOT_SERVICES + .open_protocol::( + protocol_install.0, + agent_install.0, + controller_install.0, + efi::OPEN_PROTOCOL_BY_DRIVER, + ) + .map_err(|e| BenchError::BenchTest("Failed to open protocol", e)) + })?; + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + + BOOT_SERVICES + .close_protocol(protocol_install.0, &TEST_GUID1, agent_install.0, controller_install.0) + .map_err(|e| BenchError::BenchCleanup("Failed to close protocol", e))?; + } + + // Uninstall mock protocols after benchmarking. + BOOT_SERVICES + .uninstall_protocol_interface(protocol_install.0, protocol_install.1) + .map_err(|e| BenchError::BenchCleanup("Failed to uninstall protocol", e))?; + BOOT_SERVICES + .uninstall_protocol_interface(agent_install.0, agent_install.1) + .map_err(|e| BenchError::BenchCleanup("Failed to uninstall agent protocol", e))?; + BOOT_SERVICES + .uninstall_protocol_interface(controller_install.0, controller_install.1) + .map_err(|e| BenchError::BenchCleanup("Failed to uninstall controller protocol", e))?; + + Ok(stats) +} + +/// Benchmarks protocol closing performance. +pub(crate) fn bench_close_protocol(_handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + // Set up and install the necessary protocol. + let agent_install = BOOT_SERVICES + .install_protocol_interface(None, Box::new(TestProtocol1 {})) + .map_err(|e| BenchError::BenchSetup("Failed install agent handle", e))?; + let controller_install = BOOT_SERVICES + .install_protocol_interface(None, Box::new(TestProtocol1 {})) + .map_err(|e| BenchError::BenchSetup("Failed to install controller handle.", e))?; + let protocol_install = BOOT_SERVICES + .install_protocol_interface(None, Box::new(TestProtocol1 {})) + .map_err(|e| BenchError::BenchSetup("Failed to install protocol handle", e))?; + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + // SAFETY: The resulting interface reference is not used at all during the test. + unsafe { + BOOT_SERVICES + .open_protocol::( + protocol_install.0, + agent_install.0, + controller_install.0, + efi::OPEN_PROTOCOL_BY_DRIVER, + ) + .map_err(|e| BenchError::BenchSetup("Failed to open protocol", e))?; + } + + let start = Arch::cpu_count(); + BOOT_SERVICES + .close_protocol(protocol_install.0, &TEST_GUID1, agent_install.0, controller_install.0) + .map_err(|e| BenchError::BenchTest("Failed to close protocol", e))?; + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + } + + // Uninstall mock protocols after benchmarking. + BOOT_SERVICES + .uninstall_protocol_interface(protocol_install.0, protocol_install.1) + .map_err(|e| BenchError::BenchCleanup("Failed to uninstall protocol", e))?; + BOOT_SERVICES + .uninstall_protocol_interface(agent_install.0, agent_install.1) + .map_err(|e| BenchError::BenchCleanup("Failed to uninstall agent protocol", e))?; + BOOT_SERVICES + .uninstall_protocol_interface(controller_install.0, controller_install.1) + .map_err(|e| BenchError::BenchCleanup("Failed to uninstall controller protocol", e))?; + + Ok(stats) +} + +/// Benchmarks protocol handling performance. +/// This is a legacy method but is still included due to needing to support legacy UEFI (1.0). +pub(crate) fn bench_handle_protocol(_handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + // Set up and install the protocol to be accessed. + let protocol_install = BOOT_SERVICES + .install_protocol_interface(None, Box::new(TestProtocol1 {})) + .map_err(|e| BenchError::BenchSetup("Failed to install protocol", e))?; + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let start = Arch::cpu_count(); + // SAFETY: The resulting interface reference is not used at all during the test. + (unsafe { + BOOT_SERVICES + .handle_protocol::(protocol_install.0) + .map_err(|e| BenchError::BenchTest("Failed to handle protocol", e)) + })?; + + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + } + // Uninstall mock protocol after benchmarking. + BOOT_SERVICES + .uninstall_protocol_interface(protocol_install.0, protocol_install.1) + .map_err(|e| BenchError::BenchCleanup("Failed to uninstall protocol", e))?; + Ok(stats) +} + +/// Benchmarks device path resolution. +pub(crate) fn bench_locate_device_path(handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + // Find existing protocol handles to locate device path. + // SAFETY: There is only one reference to the `loaded_image_protocol` interface. + let loaded_image_protocol = unsafe { + BOOT_SERVICES + .handle_protocol::(handle) + .map_err(|e| BenchError::BenchSetup("Failed to get loaded image protocol.", e))? + }; + // SAFETY: There is only one reference to the `device_path_protocol` interface. + let device_path_protocol = unsafe { + BOOT_SERVICES + .handle_protocol::(loaded_image_protocol.device_handle) + .map_err(|e| BenchError::BenchSetup("Failed to device path protocol.", e))? + }; + + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let mut device_path_ptr = device_path_protocol as *mut efi::protocols::device_path::Protocol; + let start = Arch::cpu_count(); + // SAFETY: The device path has been constructed above as a valid pointer. + unsafe { + BOOT_SERVICES + .locate_device_path(&efi::protocols::device_path::PROTOCOL_GUID, &mut device_path_ptr as *mut _) + .map_err(|e| BenchError::BenchTest("Failed to locate device path", e)) + }?; + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + } + + Ok(stats) +} + +/// Benchmarks protocol metadata retrieval. +pub(crate) fn bench_open_protocol_information(handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let start = Arch::cpu_count(); + let _info = BOOT_SERVICES + .open_protocol_information(handle, &efi::protocols::loaded_image::PROTOCOL_GUID) + .map_err(|e| BenchError::BenchTest("Failed to get open protocol information", e))?; + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + } + + Ok(stats) +} + +/// Benchmarks handle protocol enumeration. +pub(crate) fn bench_protocols_per_handle(handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let start = Arch::cpu_count(); + let _protocols = BOOT_SERVICES + .protocols_per_handle(handle) + .map_err(|e| BenchError::BenchTest("Failed to get protocols per handle", e))?; + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + } + + Ok(stats) +} + +/// Benchmarks protocol notification registration. +pub(crate) fn bench_register_protocol_notify(_handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + // Mock notify does nothing. + extern "efiapi" fn mock_notify(_ptr: *mut c_void, _data: *mut i32) {} + + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let event = BOOT_SERVICES + .create_event(EventType::NOTIFY_SIGNAL, Tpl::NOTIFY, Some(mock_notify), &mut 0 as *mut i32) + .map_err(|e| BenchError::BenchSetup("Failed to create valid event", e))?; + let start = Arch::cpu_count(); + BOOT_SERVICES + .register_protocol_notify(&efi::protocols::loaded_image::PROTOCOL_GUID, event) + .map_err(|e| BenchError::BenchTest("Failed to register protocol notify", e))?; + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + + BOOT_SERVICES.close_event(event).map_err(|e| BenchError::BenchCleanup("Failed to close event", e))?; + } + + Ok(stats) +} + +/// Benchmarks protocol update performance. +pub(crate) fn bench_reinstall_protocol_interface( + _handle: efi::Handle, + num_calls: usize, +) -> Result, BenchError> { + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let prev_interface = Box::new(TestProtocol1 {}); + let new_interface = Box::new(TestProtocol1 {}); + let protocol_install = BOOT_SERVICES + .install_protocol_interface(None, prev_interface) + .map_err(|e| BenchError::BenchSetup("Failed to install dummy protocol", e))?; + + let start = Arch::cpu_count(); + let reinstall = BOOT_SERVICES + .reinstall_protocol_interface(protocol_install.0, protocol_install.1, new_interface) + .map_err(|e| BenchError::BenchTest("Failed to reinstall protocol interface", e))?; + + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + + // Cleanup: Uninstall the protocol after benchmarking. (It will be installed and reinstalled in the next iteration.) + BOOT_SERVICES + .uninstall_protocol_interface(protocol_install.0, reinstall.0) + .map_err(|e| BenchError::BenchCleanup("Failed to uninstall protocol interface", e))?; + } + + Ok(stats) +} + +/// Benchmarks protocol removal performance. +pub(crate) fn bench_uninstall_protocol_interface( + _handle: efi::Handle, + num_calls: usize, +) -> Result, BenchError> { + let mut protocol_install = BOOT_SERVICES + .install_protocol_interface(None, Box::new(TestProtocol1 {})) + .map_err(|e| BenchError::BenchSetup("Failed to install dummy protocol", e))?; + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let start = Arch::cpu_count(); + BOOT_SERVICES + .uninstall_protocol_interface(protocol_install.0, protocol_install.1) + .map_err(|e| BenchError::BenchTest("Failed to uninstall protocol interface", e))?; + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + + // Reinstall for next iteration. + protocol_install = BOOT_SERVICES + .install_protocol_interface(None, Box::new(TestProtocol1 {})) + .map_err(|e| BenchError::BenchCleanup("Failed to install a new dummy protocol", e))?; + } + + // Installation from last iteration cleanup. + BOOT_SERVICES + .uninstall_protocol_interface(protocol_install.0, protocol_install.1) + .map_err(|e| BenchError::BenchCleanup("Failed to uninstall protocol interface", e))?; + + Ok(stats) +} diff --git a/services_benchmark_test/src/bench/tpl.rs b/services_benchmark_test/src/bench/tpl.rs new file mode 100644 index 0000000..a0097c6 --- /dev/null +++ b/services_benchmark_test/src/bench/tpl.rs @@ -0,0 +1,48 @@ +//! Benchmarks for TPL manipulation operations. +//! +//! Copyright (c) Microsoft Corporation. +//! +//! SPDX-License-Identifier: Apache-2.0 +//! + +use mu_rust_helpers::perf_timer::{Arch, ArchFunctionality as _}; +use patina::boot_services::{BootServices as _, tpl::Tpl}; +use r_efi::efi::{self}; +use rolling_stats::Stats; + +use crate::{BOOT_SERVICES, error::BenchError}; + +const TPL_HIGH_LEVEL: Tpl = Tpl(31); + +/// Benchmarks interrupt disable performance. +pub(crate) fn bench_raise_tpl(_handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + let mut stats: Stats = Stats::new(); + for _ in 0..num_calls { + let start = Arch::cpu_count(); + // Use TPL_HIGH_LEVEL to test impact of interrupts. + let old_tpl = BOOT_SERVICES.raise_tpl(TPL_HIGH_LEVEL); + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + + BOOT_SERVICES.restore_tpl(old_tpl); + } + + Ok(stats) +} + +/// Benchmarks interrupt enable performance. +pub(crate) fn bench_restore_tpl(_handle: efi::Handle, num_calls: usize) -> Result, BenchError> { + let mut stats: Stats = Stats::new(); + let tpl_options = [Tpl::APPLICATION, Tpl::CALLBACK, Tpl::NOTIFY, TPL_HIGH_LEVEL]; + for i in 0..num_calls { + // Rotate between different TPL levels to test all scenarios. + let old_tpl = BOOT_SERVICES.raise_tpl(tpl_options[i % tpl_options.len()]); + + let start = Arch::cpu_count(); + BOOT_SERVICES.restore_tpl(old_tpl); + let end = Arch::cpu_count(); + stats.update((end - start) as f64); + } + + Ok(stats) +} diff --git a/services_benchmark_test/src/error.rs b/services_benchmark_test/src/error.rs new file mode 100644 index 0000000..fa8cfb5 --- /dev/null +++ b/services_benchmark_test/src/error.rs @@ -0,0 +1,33 @@ +//! Errors that can occur during benchmarking. +//! +//! Copyright (c) Microsoft Corporation. +//! +//! SPDX-License-Identifier: Apache-2.0 +//! + +use core::fmt; + +use r_efi::efi; + +#[derive(Debug)] +pub enum BenchError { + BenchSetup(&'static str, efi::Status), + BenchTest(&'static str, efi::Status), + BenchCleanup(&'static str, efi::Status), + WriteOutput(&'static str, core::fmt::Error), +} + +impl fmt::Display for BenchError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + BenchError::BenchSetup(msg, status) + | BenchError::BenchTest(msg, status) + | BenchError::BenchCleanup(msg, status) => { + write!(f, "{} with error {:?}", msg, status) + } + BenchError::WriteOutput(msg, err) => { + write!(f, "{} with formatting error {:?}", msg, err) + } + } + } +} diff --git a/services_benchmark_test/src/lib.rs b/services_benchmark_test/src/lib.rs new file mode 100644 index 0000000..947f927 --- /dev/null +++ b/services_benchmark_test/src/lib.rs @@ -0,0 +1,169 @@ +//! Services Benchmark Test Library +//! +//! This crate provides a set of benchmarks for measuring the performance of various UEFI services. +//! It is intended to be run in a UEFI environment to collect timing and call statistics for selected +//! UEFI service functions. The results are output in a markdown-formatted table for easy analysis. +//! +//! ## Usage +//! +//! Invoke the `bench_start` function from your UEFI application or test harness, passing the UEFI +//! image handle and system table. The library will execute a set of predefined benchmarks and print +//! the results to the UEFI console. +//! +//! ## Output +//! +//! The benchmark results include the name of each tested service, total cycles consumed, number of calls, +//! and average cycles per operation. +//! +//! ## License +//! +//! Copyright (c) Microsoft Corporation. +//! +//! SPDX-License-Identifier: Apache-2.0 +//! +#![cfg_attr(target_os = "uefi", no_std)] + +/// Global instance of UEFI Boot Services. +pub static BOOT_SERVICES: StandardBootServices = StandardBootServices::new_uninit(); + +#[cfg(target_os = "uefi")] +extern crate alloc; + +#[cfg(not(target_os = "uefi"))] +use std as alloc; + +use alloc::{ + string::{String, ToString}, + vec::Vec, +}; + +use core::fmt::Write; +use mu_rust_helpers::perf_timer::{Arch, ArchFunctionality as _}; +use rolling_stats::Stats; + +use patina::boot_services::StandardBootServices; +use r_efi::efi; + +use crate::{error::BenchError, measure::BENCH_FNS}; + +pub fn bench_start(handle: efi::Handle) -> Result<(), BenchError> { + log::info!("Starting Services Benchmark Test..."); + + let mut output_buf = String::new(); + + write_headers(&mut output_buf)?; + + for (bf, num_calls) in BENCH_FNS { + // Run a few warmup iterations. (10% of the benchmark iterations). + (bf.func)(handle, num_calls / 10)?; + + let (bench_name, bench_func) = (bf.name, bf.func); + let cycles_res = bench_func(handle, num_calls); + match cycles_res { + Ok(cycles_stats) => { + // Calculate total time in milliseconds. Formula: ms = cycles / (cycles / s) * 1000. + let total_time_ms = (cycles_stats.count as f64) / (Arch::perf_frequency() as f64) * 1000.0; + write_result_row(&mut output_buf, bench_name, cycles_stats, total_time_ms, num_calls)?; + } + Err(e) => { + log::error!("Benchmark {} failed: {:?}", bench_name, e); + debug_assert!(false); + // In case of failure write 0s and note failure. + write_result_row( + &mut output_buf, + (bench_name.to_string() + " (Failed)").as_str(), + Stats::default(), + 0.0, + 0, + )?; + } + } + } + + log::info!("{}", output_buf); + // SAFETY: `st` is a valid pointer to SystemTable provided by UEFI firmware in `efi_main`. + unsafe { print_to_console(output_buf.as_str()) }; + + Ok(()) +} + +// Writes the header rows for the fixed-width results markdown table. +pub fn write_headers(output_buf: &mut String) -> Result<(), BenchError> { + // Column headers. + writeln!( + output_buf, + "| {:<32} | {:>14} | {:>12} | {:>15} | {:>15} | {:>12} | {:>12} | {:>12} |", + "Name", + "Total cycles", + "Total calls", + "Cycles/op", + "Total time (ms)", + "Min cycles", + "Max cycles", + "SD [cycles]" + ) + .map_err(|e| BenchError::WriteOutput("Write table header failed", e))?; + // Column separators. + writeln!( + output_buf, + "| {:-<32} | {:-<14} | {:-<12} | {:-<15} | {:-<15} | {:-<12} | {:-<12} | {:-<12} |", + "-", "-", "-", "-", "-", "-", "-", "-" + ) + .map_err(|e| BenchError::WriteOutput("Write table header failed", e))?; + Ok(()) +} + +pub fn write_result_row( + output_buf: &mut String, + bench_name: &str, + stats: Stats, + total_time_ms: f64, + num_calls: usize, +) -> Result<(), BenchError> { + writeln!( + output_buf, + "| {:<32} | {:>14} | {:>12} | {:>15} | {:>15.3} | {:>12} | {:>12} | {:>12.2} |", + bench_name, + stats.count, // Format as usize for better readability. Partial cycles don't really matter. + num_calls, + stats.mean, + total_time_ms, + stats.min, + stats.max, + stats.std_dev as usize, // Format as usize for better readability. Partial cycles don't really matter. + ) + .map_err(|e| BenchError::WriteOutput("Write table header failed", e))?; + Ok(()) +} + +/// Print a message to the UEFI console output. +/// +/// # Safety +/// The caller must ensure that the UEFI System Table pointer has been initialized. +pub unsafe fn print_to_console(message: &str) { + let st = uefi::table::system_table_raw(); + if let Some(st_ptr) = st { + let st = st_ptr.as_ptr(); + // SAFETY: The `uefi` crate guarantees that the System Table pointer is valid after initialization. + let system_table = unsafe { &*st }; + let con_out = system_table.stdout; + + if con_out.is_null() { + return; + } + + // Convert the message to UTF-16 for UEFI console output. + let mut utf16_buffer: Vec = message.encode_utf16().collect(); + utf16_buffer.push(0); // Null terminator. + + // Call the UEFI console output function. + // SAFETY: If the safety conditions are met, the UEFI console output function will be valid. + let output_string = unsafe { (*con_out).output_string }; + // SAFETY: If the safety conditions are met, the UEFI console output function will be valid. + let _ = unsafe { output_string(con_out, utf16_buffer.as_ptr() as *mut u16) }; + } +} + +mod bench; +mod error; +mod measure; diff --git a/services_benchmark_test/src/main.rs b/services_benchmark_test/src/main.rs new file mode 100644 index 0000000..ae81520 --- /dev/null +++ b/services_benchmark_test/src/main.rs @@ -0,0 +1,55 @@ +//! UEFI shell app benchmark test for basic boot and runtime services. +//! +//! ## License +//! +//! Copyright (c) Microsoft Corporation. +//! +//! SPDX-License-Identifier: Apache-2.0 +//! +#![cfg_attr(target_os = "uefi", no_std)] +#![cfg_attr(target_os = "uefi", no_main)] + +cfg_if::cfg_if! { + if #[cfg(all(target_os = "uefi"))] { + use core::panic::PanicInfo; + use uefi::prelude::*; + use services_benchmark_test::bench_start; + use r_efi::efi; + use services_benchmark_test::BOOT_SERVICES; + use log::LevelFilter; + use patina::boot_services::protocol_handler::HandleSearchType; + use patina::boot_services::BootServices; + + #[entry] + fn main() -> Status { + uefi::helpers::init().unwrap(); + log::info!("UEFI Services Benchmark Test Entry Point"); + + let st = uefi::table::system_table_raw(); + if let Some(st_ptr) = st { + let st = st_ptr.as_ptr(); + // SAFETY: `uefi` crate ensures that the system table pointer is valid after initialization. + let system_table = unsafe { &*st }; + // SAFETY: `uefi` crate ensures that the boot services pointer is valid after initialization. + let bs = unsafe { &*(system_table.boot_services as *const efi::BootServices) }; + BOOT_SERVICES.init(bs); + } + + // Convert UEFI types to r-efi compatible types. + let handle = uefi::boot::image_handle().as_ptr(); + + bench_start(handle as r_efi::efi::Handle).unwrap_or_else(|e| { + log::error!("Services Benchmark Test failed: {:?}", e); + }); + + Status::SUCCESS + } + + #[panic_handler] + fn panic(_info: &PanicInfo) -> ! { + loop {} + } + } else { + fn main() {} + } +} diff --git a/services_benchmark_test/src/measure.rs b/services_benchmark_test/src/measure.rs new file mode 100644 index 0000000..9568dd0 --- /dev/null +++ b/services_benchmark_test/src/measure.rs @@ -0,0 +1,81 @@ +//! Wrapper for benchmarks for easy measurement. +//! +//! Copyright (c) Microsoft Corporation. +//! +//! SPDX-License-Identifier: Apache-2.0 +//! + +use r_efi::efi; +use rolling_stats::Stats; + +use crate::{ + bench::{ + controller::bench_connect_controller, + event::{ + bench_check_event_signaled, bench_check_event_unsignaled, bench_close_event, bench_create_event, + bench_signal_event, bench_signal_event_group, + }, + image::{bench_load_image, bench_start_image_and_exit}, + memory::{ + bench_allocate_pages, bench_allocate_pool, bench_copy_mem, bench_free_pages, bench_free_pool, + bench_get_memory_map, bench_set_mem, + }, + misc::{bench_calculate_crc32, bench_install_configuration_table}, + protocol::{ + bench_close_protocol, bench_handle_protocol, bench_install_protocol_interface, bench_locate_device_path, + bench_open_protocol, bench_open_protocol_information, bench_protocols_per_handle, + bench_register_protocol_notify, bench_reinstall_protocol_interface, bench_uninstall_protocol_interface, + }, + tpl::{bench_raise_tpl, bench_restore_tpl}, + }, + error::BenchError, +}; + +// A BenchFn returns total cycles for one call +// Takes in number of calls to make to measured fn +type BenchFn = fn(efi::Handle, usize) -> Result, BenchError>; + +#[derive(Copy, Clone)] +pub(crate) struct BenchFnWrapper { + pub(crate) func: BenchFn, + pub(crate) name: &'static str, +} + +pub static BENCH_FNS: [(BenchFnWrapper, usize); 30] = [ + /* CONTROLLER SERVICES */ + (BenchFnWrapper { func: bench_connect_controller, name: "connect_controller" }, 100), + /* EVENT SERVICES */ + (BenchFnWrapper { func: bench_check_event_signaled, name: "bench_check_event_signaled" }, 10_000), + (BenchFnWrapper { func: bench_check_event_unsignaled, name: "bench_check_event_unsignaled" }, 10_000), + (BenchFnWrapper { func: bench_create_event, name: "create_event" }, 1000), + (BenchFnWrapper { func: bench_close_event, name: "close_event" }, 1000), + (BenchFnWrapper { func: bench_signal_event, name: "signal_event" }, 100_000), + (BenchFnWrapper { func: bench_signal_event_group, name: "signal_event_group" }, 100), + /* IMAGE SERVICES */ + (BenchFnWrapper { func: bench_start_image_and_exit, name: "start_image, exit" }, 100), + (BenchFnWrapper { func: bench_load_image, name: "load_image" }, 100), + /* MEMORY SERVICES */ + (BenchFnWrapper { func: bench_allocate_pages, name: "allocate_pages" }, 1000), + (BenchFnWrapper { func: bench_allocate_pool, name: "allocate_pool" }, 10_000), + (BenchFnWrapper { func: bench_free_pages, name: "free_pages" }, 100), + (BenchFnWrapper { func: bench_free_pool, name: "free_pool" }, 10_000), + (BenchFnWrapper { func: bench_copy_mem, name: "copy_mem" }, 10), + (BenchFnWrapper { func: bench_set_mem, name: "set_mem" }, 10), + (BenchFnWrapper { func: bench_get_memory_map, name: "get_memory_map" }, 10), + /* MISC SERVICES */ + (BenchFnWrapper { func: bench_calculate_crc32, name: "calculate_crc32" }, 100), + (BenchFnWrapper { func: bench_install_configuration_table, name: "install_configuration_table" }, 10), + /* PROTOCOL SERVICES */ + (BenchFnWrapper { func: bench_install_protocol_interface, name: "install_protocol_interface" }, 100), + (BenchFnWrapper { func: bench_open_protocol, name: "open_protocol" }, 10_000), + (BenchFnWrapper { func: bench_handle_protocol, name: "handle_protocol" }, 10_000), + (BenchFnWrapper { func: bench_close_protocol, name: "close_protocol" }, 100), + (BenchFnWrapper { func: bench_locate_device_path, name: "locate_device_path" }, 100), + (BenchFnWrapper { func: bench_open_protocol_information, name: "open_protocol_information" }, 100), + (BenchFnWrapper { func: bench_protocols_per_handle, name: "protocols_per_handle" }, 100), + (BenchFnWrapper { func: bench_register_protocol_notify, name: "register_protocol_notify" }, 10), + (BenchFnWrapper { func: bench_reinstall_protocol_interface, name: "reinstall_protocol_interface" }, 100), + (BenchFnWrapper { func: bench_uninstall_protocol_interface, name: "uninstall_protocol_interface" }, 10), + (BenchFnWrapper { func: bench_raise_tpl, name: "raise_tpl" }, 1_000_000), + (BenchFnWrapper { func: bench_restore_tpl, name: "restore_tpl" }, 1_000_000), +];