1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define TLOG_TAG "hwcrypto_bench"
18 
19 #include <inttypes.h>
20 #include <stddef.h>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include <interface/hwkey/hwkey.h>
26 #include <lib/hwkey/hwkey.h>
27 #include <lib/rng/trusty_rng.h>
28 #include <trusty/time.h>
29 #include <trusty_benchmark.h>
30 #include <uapi/err.h>
31 
32 #define BUF_SIZE 4096
33 
34 typedef struct {
35     uint8_t rng_buf[BUF_SIZE];
36 } crypto_state_t;
37 
38 static crypto_state_t* crypto_state;
39 
40 struct query_param {
41     size_t sz;
42     int (*rng_call)(uint8_t*, size_t);
43 };
44 
45 /* Parameters Array for the parametric benchmark */
46 static const struct query_param fixed_total_size_chunked[] = {
47         {2, trusty_rng_hw_rand},        {4, trusty_rng_hw_rand},
48         {8, trusty_rng_hw_rand},        {16, trusty_rng_hw_rand},
49         {32, trusty_rng_hw_rand},       {64, trusty_rng_hw_rand},
50         {128, trusty_rng_hw_rand},      {256, trusty_rng_hw_rand},
51         {512, trusty_rng_hw_rand},      {1024, trusty_rng_hw_rand},
52         {2048, trusty_rng_hw_rand},     {4096, trusty_rng_hw_rand},
53         {2, trusty_rng_secure_rand},    {4, trusty_rng_secure_rand},
54         {8, trusty_rng_secure_rand},    {16, trusty_rng_secure_rand},
55         {32, trusty_rng_secure_rand},   {64, trusty_rng_secure_rand},
56         {128, trusty_rng_secure_rand},  {256, trusty_rng_secure_rand},
57         {512, trusty_rng_secure_rand},  {1024, trusty_rng_secure_rand},
58         {2048, trusty_rng_secure_rand}, {4096, trusty_rng_secure_rand},
59 };
60 
61 static const struct query_param* variable_sizes = fixed_total_size_chunked;
62 
63 /*
64  * Construct the Column Header for each param. Can have any name, but must be
65  * assigned to trusty_bench_get_param_name_cb global in BENCH_SETUP
66  */
get_param_name_cb(char * buf,size_t buf_size,size_t param_idx)67 static void get_param_name_cb(char* buf, size_t buf_size, size_t param_idx) {
68     snprintf(buf, buf_size, "%zu Bytes - %s",
69              fixed_total_size_chunked[param_idx].sz,
70              fixed_total_size_chunked[param_idx].rng_call == trusty_rng_hw_rand
71                      ? "hw_rand"
72                      : "secure_rand");
73 }
74 
75 /*
76  * Construct the Column Header for each param. Can have any name, but must be
77  * assigned to trusty_bench_get_param_name_cb global in BENCH_SETUP
78  */
get_param_name_cb_fixed(char * buf,size_t buf_size,size_t param_idx)79 static void get_param_name_cb_fixed(char* buf,
80                                     size_t buf_size,
81                                     size_t param_idx) {
82     snprintf(buf, buf_size, "%d Total - %zu Bytes Chunks - %s", BUF_SIZE,
83              fixed_total_size_chunked[param_idx].sz,
84              fixed_total_size_chunked[param_idx].rng_call == trusty_rng_hw_rand
85                      ? "hw_rand"
86                      : "secure_rand");
87 }
88 /*
89  * Construct the Formatted Aggregate Values. Can have any name, but must be
90  * assigned to trusty_bench_get_formatted_value_cb global in BENCH_SETUP
91  */
get_formatted_value_cb(char * buf,size_t buf_size,int64_t value,const char * metric_name)92 static void get_formatted_value_cb(char* buf,
93                                    size_t buf_size,
94                                    int64_t value,
95                                    const char* metric_name) {
96     if (strcmp("time_micro_seconds", metric_name) == 0 ||
97         strcmp("micro_sec_per_byte", metric_name) == 0) {
98         int64_t mic_sec = value / 1000;
99         int64_t n_sec = value % 1000;
100         snprintf(buf, buf_size, "%" PRId64 ".%03" PRId64 "", mic_sec, n_sec);
101     } else {
102         snprintf(buf, buf_size, "%" PRId64, value);
103     }
104 }
105 
106 static uint64_t crypto_pmu_evt_arr[] = {PMU_EV_BR_MIS_PRED,
107                                         PMU_EV_INST_RETIRED};
108 
109 /*
110  * Executed before each atomic execution of a BENCH(crypto, ...) Macro.
111  */
BENCH_SETUP(crypto)112 BENCH_SETUP(crypto) {
113     BENCH_INIT_PMU(crypto_pmu_evt_arr);
114     /*
115      * Let Framework know how to print param column header. Default is the
116      * current param index. Will be reset to NULL after BENCH_TEARDOWN(crypto,
117      * hwrng, ...)
118      */
119     trusty_bench_get_param_name_cb = &get_param_name_cb;
120 
121     /*
122      * Let Framework know how to print formatted aggregate values. Default is
123      * printing the int64_t value as such. Will be reset to NULL after
124      * BENCH_TEARDOWN(crypto, hwrng, ...)
125      */
126     trusty_bench_get_formatted_value_cb = &get_formatted_value_cb;
127     /*
128      * Let Framework know how to print results. Defaults to vertical. This line
129      * is here for demonstration purpose only. Feel free to uncomment.
130      */
131 
132     /* trusty_bench_print_cb = &trusty_bench_print_horizontal_metric_list; */
133 
134     crypto_state = calloc(1, sizeof(crypto_state_t));
135     if (crypto_state == NULL) {
136         TLOGE("Failed to Allocate memory for crypto_state!");
137         return ERR_NO_MEMORY;
138     }
139 
140     return NO_ERROR;
141 }
142 
143 /*
144  * Executed after each atomic execution of a BENCH(crypto, ...) Macro.
145  */
BENCH_TEARDOWN(crypto)146 BENCH_TEARDOWN(crypto) {
147     free(crypto_state);
148     crypto_state = NULL;
149 }
150 
151 /*
152  * BENCH with 3 parameters (suite_name, test_name, nb_of_runs).
153  * The inner content is run 100 times.
154  * BENCH_SETUP/BENCH_TEARDOWN are executed before/after each individual run
155  * respectively.
156  */
157 BENCH(crypto, hwrng_hw_rand, 20) {
158     int rc;
159     rc = trusty_rng_hw_rand(crypto_state->rng_buf, BUF_SIZE);
160     ASSERT_EQ(NO_ERROR, rc);
161 
162 test_abort:
163     return rc;
164 }
165 
166 /* the returned time is in nanoseconds, the formatter will make it micro but the
167  * name here is the one used in printing */
BENCH_RESULT(crypto,hwrng_hw_rand,time_micro_seconds)168 BENCH_RESULT(crypto, hwrng_hw_rand, time_micro_seconds) {
169     /*
170      * bench_get_duration_ns() is the ns time from trusty_get_time with clock ID
171      * 0 for the last execution of 'BENCH'.
172      */
173     return bench_get_duration_ns();
174 }
175 
BENCH_RESULT(crypto,hwrng_hw_rand,cycle_counter)176 BENCH_RESULT(crypto, hwrng_hw_rand, cycle_counter) {
177     return bench_get_pmu_cnt(1);
178 }
179 
BENCH_RESULT(crypto,hwrng_hw_rand,inst_retired)180 BENCH_RESULT(crypto, hwrng_hw_rand, inst_retired) {
181     return bench_get_pmu_cnt(2);
182 }
183 
BENCH_RESULT(crypto,hwrng_hw_rand,micro_sec_per_byte)184 BENCH_RESULT(crypto, hwrng_hw_rand, micro_sec_per_byte) {
185     return bench_get_duration_ns() / BUF_SIZE;
186 }
187 
188 /*
189  * BENCH with 4 parameters (suite_name, test_name, nb_of_runs, params).
190  * For each parameter in query_params, the inner content is run 5 times.
191  * BENCH_SETUP/BENCH_TEARDOWN are executed before/after each individual run
192  * respectively.
193  */
194 BENCH(crypto, hwrng_fixed_total, 20, fixed_total_size_chunked) {
195     int rc;
196     size_t i;
197 
198     trusty_bench_get_param_name_cb = &get_param_name_cb_fixed;
199 
200     size_t nq_query =
201             BUF_SIZE / fixed_total_size_chunked[bench_get_param_idx()].sz;
202     for (i = 0; i < nq_query; i++) {
203         rc = fixed_total_size_chunked[bench_get_param_idx()].rng_call(
204                 crypto_state->rng_buf,
205                 fixed_total_size_chunked[bench_get_param_idx()].sz);
206         ASSERT_EQ(NO_ERROR, rc);
207     }
208 
209 test_abort:
210     return rc;
211 }
212 
213 /* the returned time is in nanoseconds, the formatter will make it micro but the
214  * name here is the one used in printing */
BENCH_RESULT(crypto,hwrng_fixed_total,time_micro_seconds)215 BENCH_RESULT(crypto, hwrng_fixed_total, time_micro_seconds) {
216     return bench_get_duration_ns();
217 }
218 
BENCH_RESULT(crypto,hwrng_fixed_total,micro_sec_per_byte)219 BENCH_RESULT(crypto, hwrng_fixed_total, micro_sec_per_byte) {
220     return bench_get_duration_ns() / BUF_SIZE;
221 }
222 
BENCH_RESULT(crypto,hwrng_fixed_total,cycle_counter)223 BENCH_RESULT(crypto, hwrng_fixed_total, cycle_counter) {
224     return bench_get_pmu_cnt(1);
225 }
226 
BENCH_RESULT(crypto,hwrng_fixed_total,inst_retired)227 BENCH_RESULT(crypto, hwrng_fixed_total, inst_retired) {
228     return bench_get_pmu_cnt(2);
229 }
230 
231 /*
232  * BENCH with 5 parameters (suite_name, test_name, nb_of_runs, params).
233  * For each parameter in query_params, the inner content is run 100 times.
234  * BENCH_SETUP/BENCH_TEARDOWN are executed before/after each individual run
235  * respectively. For convenience, one can reuse (suite_name, test_name) from a 3
236  * or 4 param BENCH macro. This allows sharing
237  * BENCH_SETUP/BENCH_TEARDOWN/BENCH_SETUP/BENCH_RESULT macros. you need a
238  * different parameter name, hence the aliasing here.
239  */
240 BENCH(crypto,
241       hwrng_var_size,
242       20,
243       variable_sizes,
244       countof(fixed_total_size_chunked)) {
245     int rc;
246 
247     rc = variable_sizes[bench_get_param_idx()].rng_call(
248             crypto_state->rng_buf, variable_sizes[bench_get_param_idx()].sz);
249     ASSERT_EQ(NO_ERROR, rc);
250 
251 test_abort:
252     return rc;
253 }
254 
255 /* the returned time is in nanoseconds, the formatter will make it micro but the
256  * name here is the one used in printing */
BENCH_RESULT(crypto,hwrng_var_size,time_micro_seconds)257 BENCH_RESULT(crypto, hwrng_var_size, time_micro_seconds) {
258     return bench_get_duration_ns();
259 }
BENCH_RESULT(crypto,hwrng_var_size,micro_sec_per_byte)260 BENCH_RESULT(crypto, hwrng_var_size, micro_sec_per_byte) {
261     return bench_get_duration_ns() / variable_sizes[bench_get_param_idx()].sz;
262 }
263 
264 PORT_TEST(hwcrypto, "com.android.trusty.hwrng.bench")
265