hwrng-bench: HWRNG Benchmark

Bug: 259511186
Test: "com.android.trusty.hwrng.bench"

Change-Id: Ica8ce4983f077f7cb83a9c90a86bdd987767313c
diff --git a/build-config-usertests b/build-config-usertests
index 018680d..3b31ee6 100644
--- a/build-config-usertests
+++ b/build-config-usertests
@@ -23,7 +23,7 @@
     porttest("com.android.timer-unittest"),
     porttest("com.android.trusty.binder.test"),
     porttest("com.android.trusty.hwcrypto.test"),
+    porttest("com.android.trusty.hwrng.bench"),
     porttest("com.android.trusty.prebuilts.test"),
     porttest("com.android.trusty.swspi.test"),
-
 ]
diff --git a/hwrng-bench/main.c b/hwrng-bench/main.c
new file mode 100644
index 0000000..5332012
--- /dev/null
+++ b/hwrng-bench/main.c
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define TLOG_TAG "hwcrypto_bench"
+
+#include <inttypes.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <interface/hwkey/hwkey.h>
+#include <lib/hwkey/hwkey.h>
+#include <lib/rng/trusty_rng.h>
+#include <trusty/time.h>
+#include <trusty_benchmark.h>
+#include <uapi/err.h>
+
+#define BUF_SIZE 4096
+
+typedef struct {
+    uint8_t rng_buf[BUF_SIZE];
+} crypto_state_t;
+
+static crypto_state_t* crypto_state;
+
+struct query_param {
+    size_t sz;
+    int (*rng_call)(uint8_t*, size_t);
+};
+
+/* Parameters Array for the parametric benchmark */
+static const struct query_param fixed_total_size_chunked[] = {
+        {2, trusty_rng_hw_rand},        {4, trusty_rng_hw_rand},
+        {8, trusty_rng_hw_rand},        {16, trusty_rng_hw_rand},
+        {32, trusty_rng_hw_rand},       {64, trusty_rng_hw_rand},
+        {128, trusty_rng_hw_rand},      {256, trusty_rng_hw_rand},
+        {512, trusty_rng_hw_rand},      {1024, trusty_rng_hw_rand},
+        {2048, trusty_rng_hw_rand},     {4096, trusty_rng_hw_rand},
+        {2, trusty_rng_secure_rand},    {4, trusty_rng_secure_rand},
+        {8, trusty_rng_secure_rand},    {16, trusty_rng_secure_rand},
+        {32, trusty_rng_secure_rand},   {64, trusty_rng_secure_rand},
+        {128, trusty_rng_secure_rand},  {256, trusty_rng_secure_rand},
+        {512, trusty_rng_secure_rand},  {1024, trusty_rng_secure_rand},
+        {2048, trusty_rng_secure_rand}, {4096, trusty_rng_secure_rand},
+};
+
+static const struct query_param* variable_sizes = fixed_total_size_chunked;
+
+/*
+ * Construct the Column Header for each param. Can have any name, but must be
+ * assigned to trusty_bench_get_param_name_cb global in BENCH_SETUP
+ */
+static void get_param_name_cb(char* buf, size_t buf_size, size_t param_idx) {
+    snprintf(buf, buf_size, "%zu Bytes - %s",
+             fixed_total_size_chunked[param_idx].sz,
+             fixed_total_size_chunked[param_idx].rng_call == trusty_rng_hw_rand
+                     ? "hw_rand"
+                     : "secure_rand");
+}
+
+/*
+ * Construct the Column Header for each param. Can have any name, but must be
+ * assigned to trusty_bench_get_param_name_cb global in BENCH_SETUP
+ */
+static void get_param_name_cb_fixed(char* buf,
+                                    size_t buf_size,
+                                    size_t param_idx) {
+    snprintf(buf, buf_size, "%d Total - %zu Bytes Chunks - %s", BUF_SIZE,
+             fixed_total_size_chunked[param_idx].sz,
+             fixed_total_size_chunked[param_idx].rng_call == trusty_rng_hw_rand
+                     ? "hw_rand"
+                     : "secure_rand");
+}
+/*
+ * Construct the Formatted Aggregate Values. Can have any name, but must be
+ * assigned to trusty_bench_get_formatted_value_cb global in BENCH_SETUP
+ */
+static void get_formatted_value_cb(char* buf,
+                                   size_t buf_size,
+                                   int64_t value,
+                                   const char* metric_name) {
+    if (strcmp("time_micro_seconds", metric_name) == 0 ||
+        strcmp("micro_sec_per_byte", metric_name) == 0) {
+        int64_t mic_sec = value / 1000;
+        int64_t n_sec = value % 1000;
+        snprintf(buf, buf_size, "%" PRId64 ".%03" PRId64 "", mic_sec, n_sec);
+    } else {
+        snprintf(buf, buf_size, "%" PRId64, value);
+    }
+}
+
+/*
+ * Executed before each atomic execution of a BENCH(crypto, ...) Macro.
+ */
+BENCH_SETUP(crypto) {
+    /*
+     * Let Framework know how to print param column header. Default is the
+     * current param index. Will be reset to NULL after BENCH_TEARDOWN(crypto,
+     * hwrng, ...)
+     */
+    trusty_bench_get_param_name_cb = &get_param_name_cb;
+
+    /*
+     * Let Framework know how to print formatted aggregate values. Default is
+     * printing the int64_t value as such. Will be reset to NULL after
+     * BENCH_TEARDOWN(crypto, hwrng, ...)
+     */
+    trusty_bench_get_formatted_value_cb = &get_formatted_value_cb;
+    /*
+     * Let Framework know how to print results. Defaults to vertical. This line
+     * is here for demonstration purpose only. Feel free to uncomment.
+     */
+
+    /* trusty_bench_print_cb = &trusty_bench_print_horizontal_metric_list; */
+
+    crypto_state = calloc(1, sizeof(crypto_state_t));
+    if (crypto_state == NULL) {
+        TLOGE("Failed to Allocate memory for crypto_state!");
+        return ERR_NO_MEMORY;
+    }
+
+    return NO_ERROR;
+}
+
+/*
+ * Executed after each atomic execution of a BENCH(crypto, ...) Macro.
+ */
+BENCH_TEARDOWN(crypto) {
+    free(crypto_state);
+    crypto_state = NULL;
+}
+
+/*
+ * BENCH with 3 parameters (suite_name, test_name, nb_of_runs).
+ * The inner content is run 100 times.
+ * BENCH_SETUP/BENCH_TEARDOWN are executed before/after each individual run
+ * respectively.
+ */
+BENCH(crypto, hwrng_hw_rand, 20) {
+    int rc;
+    rc = trusty_rng_hw_rand(crypto_state->rng_buf, BUF_SIZE);
+    ASSERT_EQ(NO_ERROR, rc);
+
+test_abort:
+    return rc;
+}
+
+/* the returned time is in nanoseconds, the formatter will make it micro but the
+ * name here is the one used in printing */
+BENCH_RESULT(crypto, hwrng_hw_rand, time_micro_seconds) {
+    /*
+     * bench_get_duration_ns() is the ns time from trusty_get_time with clock ID
+     * 0 for the last execution of 'BENCH'.
+     */
+    return bench_get_duration_ns();
+}
+
+BENCH_RESULT(crypto, hwrng_hw_rand, micro_sec_per_byte) {
+    return bench_get_duration_ns() / BUF_SIZE;
+}
+
+/*
+ * BENCH with 4 parameters (suite_name, test_name, nb_of_runs, params).
+ * For each parameter in query_params, the inner content is run 5 times.
+ * BENCH_SETUP/BENCH_TEARDOWN are executed before/after each individual run
+ * respectively.
+ */
+BENCH(crypto, hwrng_fixed_total, 20, fixed_total_size_chunked) {
+    int rc;
+    size_t i;
+
+    trusty_bench_get_param_name_cb = &get_param_name_cb_fixed;
+
+    size_t nq_query =
+            BUF_SIZE / fixed_total_size_chunked[bench_get_param_idx()].sz;
+    for (i = 0; i < nq_query; i++) {
+        rc = fixed_total_size_chunked[bench_get_param_idx()].rng_call(
+                crypto_state->rng_buf,
+                fixed_total_size_chunked[bench_get_param_idx()].sz);
+        ASSERT_EQ(NO_ERROR, rc);
+    }
+
+test_abort:
+    return rc;
+}
+
+/* the returned time is in nanoseconds, the formatter will make it micro but the
+ * name here is the one used in printing */
+BENCH_RESULT(crypto, hwrng_fixed_total, time_micro_seconds) {
+    return bench_get_duration_ns();
+}
+
+BENCH_RESULT(crypto, hwrng_fixed_total, micro_sec_per_byte) {
+    return bench_get_duration_ns() / BUF_SIZE;
+}
+
+/*
+ * BENCH with 5 parameters (suite_name, test_name, nb_of_runs, params).
+ * For each parameter in query_params, the inner content is run 100 times.
+ * BENCH_SETUP/BENCH_TEARDOWN are executed before/after each individual run
+ * respectively. For convenience, one can reuse (suite_name, test_name) from a 3
+ * or 4 param BENCH macro. This allows sharing
+ * BENCH_SETUP/BENCH_TEARDOWN/BENCH_SETUP/BENCH_RESULT macros. you need a
+ * different parameter name, hence the aliasing here.
+ */
+BENCH(crypto,
+      hwrng_var_size,
+      20,
+      variable_sizes,
+      countof(fixed_total_size_chunked)) {
+    int rc;
+
+    rc = variable_sizes[bench_get_param_idx()].rng_call(
+            crypto_state->rng_buf, variable_sizes[bench_get_param_idx()].sz);
+    ASSERT_EQ(NO_ERROR, rc);
+
+test_abort:
+    return rc;
+}
+
+/* the returned time is in nanoseconds, the formatter will make it micro but the
+ * name here is the one used in printing */
+BENCH_RESULT(crypto, hwrng_var_size, time_micro_seconds) {
+    return bench_get_duration_ns();
+}
+BENCH_RESULT(crypto, hwrng_var_size, micro_sec_per_byte) {
+    return bench_get_duration_ns() / variable_sizes[bench_get_param_idx()].sz;
+}
+
+PORT_TEST(hwcrypto, "com.android.trusty.hwrng.bench")
diff --git a/hwrng-bench/manifest.json b/hwrng-bench/manifest.json
new file mode 100644
index 0000000..d3e5149
--- /dev/null
+++ b/hwrng-bench/manifest.json
@@ -0,0 +1,5 @@
+{
+    "uuid": "83e2c228-0789-40fb-82da-dc5f1bba8fe9",
+    "min_heap": 16384,
+    "min_stack": 16384
+}
diff --git a/hwrng-bench/rules.mk b/hwrng-bench/rules.mk
new file mode 100644
index 0000000..6838f42
--- /dev/null
+++ b/hwrng-bench/rules.mk
@@ -0,0 +1,39 @@
+# Copyright (C) 2022 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_DIR := $(GET_LOCAL_DIR)
+
+MODULE := $(LOCAL_DIR)
+
+MANIFEST := $(LOCAL_DIR)/manifest.json
+
+MODULE_SRCS += \
+	$(LOCAL_DIR)/main.c \
+
+MODULE_DEPS += \
+	$(HWCRYPTO_UNITTEST_DEVICE_MODULE) \
+
+MODULE_LIBRARY_DEPS += \
+	trusty/user/base/lib/libc-trusty \
+	trusty/user/base/lib/hwkey \
+	trusty/user/base/lib/rng \
+	trusty/user/base/lib/unittest \
+
+ifeq (true,$(call TOBOOL,$(WITH_FAKE_HWRNG)))
+MODULE_DEFINES += WITH_FAKE_HWRNG=1
+endif
+
+include make/trusted_app.mk
+
diff --git a/usertests-inc.mk b/usertests-inc.mk
index 96a354a..00c9666 100644
--- a/usertests-inc.mk
+++ b/usertests-inc.mk
@@ -18,6 +18,7 @@
 	trusty/user/app/sample/binder-test/client \
 	trusty/user/app/sample/binder-test/service \
 	trusty/user/app/sample/hwcrypto-unittest \
+	trusty/user/app/sample/hwrng-bench \
 	trusty/user/app/sample/manifest-test \
 	trusty/user/app/sample/memref-test \
 	trusty/user/app/sample/memref-test/lender \