Mark Dykes | d5d19ae | 2025-04-23 15:13:28 -0500 | [diff] [blame^] | 1 | Overview of Bias Tree |
| 2 | ========================= |
| 3 | |
| 4 | The bias tree functions as a mechanism to randomly determine the order |
| 5 | of SMC calls directed to TF-A. Here is an example. |
| 6 | |
| 7 | |
| 8 | |Bias Tree Example| |
| 9 | *Figure 2: Bias Tree Example* |
| 10 | |
| 11 | Each SMC call is categorized as in a feature list(i. e. SDEI, TSP...etc) |
| 12 | and then could be subcategorized into smaller lists relevant to testing |
| 13 | strategy. This grouping allows a user to assert control over which flavors |
| 14 | of SMC calls are executed within a given test scenario. In this example |
| 15 | we have feature sets corresponding to the different areas of TF-A that |
| 16 | can be biased or turned off altogether. This permits very focused testing |
| 17 | to very general by altering the bias numbers contained in the tree. |
| 18 | |
| 19 | Here is a generalized example bias tree: |
| 20 | |
| 21 | .. code-block:: devicetree |
| 22 | |
| 23 | svc_var1 { |
| 24 | bias = <65>; |
| 25 | svc_var1_var1 { |
| 26 | bias = <30>; |
| 27 | functionname = "svc_var1_var1"; |
| 28 | }; |
| 29 | smc_var1_var2 { |
| 30 | bias = <30>; |
| 31 | functionname = "smc_var1_var2"; |
| 32 | }; |
| 33 | smc_var1_var3 { |
| 34 | bias = <35>; |
| 35 | smc_var1_var3_var1 { |
| 36 | bias = <30>; |
| 37 | functionname = "smc_var1_var3_var1"; |
| 38 | }; |
| 39 | smc_var1_var3_var2 { |
| 40 | bias = <30>; |
| 41 | functionname = "smc_var1_var3_var2"; |
| 42 | }; |
| 43 | smc_var1_var3_var3 { |
| 44 | bias = <40>; |
| 45 | functionname = "smc_var1_var3_var3"; |
| 46 | }; |
| 47 | smc_var1_var3_var4 { |
| 48 | bias = <55>; |
| 49 | smc_var1_var3_var4_var1 { |
| 50 | bias = <89>; |
| 51 | functionname = "smc_var1_var3_var4_var1"; |
| 52 | }; |
| 53 | smc_var1_var3_var4_var2 { |
| 54 | bias = <95>; |
| 55 | functionname = "smc_var1_var3_var4_var2"; |
| 56 | }; |
| 57 | smc_var1_var3_var4_var3 { |
| 58 | bias = <35>; |
| 59 | smc_var1_var3_var4_var3_var1 { |
| 60 | bias = <30>; |
| 61 | functionname = "smc_var1_var3_var4_var3_var1"; |
| 62 | }; |
| 63 | smc_var1_var3_var4_var3_var2 { |
| 64 | bias = <30>; |
| 65 | functionname = "smc_var1_var3_var4_var3_var2"; |
| 66 | }; |
| 67 | smc_var1_var3_var4_var3_var3 { |
| 68 | bias = <40>; |
| 69 | functionname = "smc_var1_var3_var4_var3_var3"; |
| 70 | }; |
| 71 | smc_var1_var3_var4_var3_var4 { |
| 72 | bias = <55>; |
| 73 | smc_var1_var3_var4_var3_var4_var1 { |
| 74 | bias = <89>; |
| 75 | functionname = "smc_var1_var3_var4_var3_var4_var1"; |
| 76 | }; |
| 77 | smc_var1_var3_var4_var3_var4_var2 { |
| 78 | bias = <95>; |
| 79 | functionname = "smc_var1_var3_var4_var3_var4_var2"; |
| 80 | }; |
| 81 | }; |
| 82 | }; |
| 83 | }; |
| 84 | }; |
| 85 | smc_var1_var4 { |
| 86 | bias = <35>; |
| 87 | smc_var1_var4_var1 { |
| 88 | bias = <30>; |
| 89 | functionname = "smc_var1_var4_var1"; |
| 90 | }; |
| 91 | smc_var1_var4_var2 { |
| 92 | bias = <30>; |
| 93 | functionname = "smc_var1_var4_var2"; |
| 94 | }; |
| 95 | smc_var1_var4_var3 { |
| 96 | bias = <40>; |
| 97 | functionname = "smc_var1_var4_var3"; |
| 98 | }; |
| 99 | }; |
| 100 | }; |
| 101 | smc_var2 { |
| 102 | bias = <35>; |
| 103 | smc_var2_var1 { |
| 104 | bias = <30>; |
| 105 | functionname = "smc_var2_var1"; |
| 106 | }; |
| 107 | smc_var2_var2 { |
| 108 | bias = <30>; |
| 109 | functionname = "smc_var2_var2"; |
| 110 | }; |
| 111 | smc_var2_var3 { |
| 112 | bias = <40>; |
| 113 | functionname = "smc_var2_var3"; |
| 114 | }; |
| 115 | }; |
| 116 | smc_var3 { |
| 117 | bias = <55>; |
| 118 | smc_var3_var1 { |
| 119 | bias = <30>; |
| 120 | functionname = "smc_var3_var1"; |
| 121 | }; |
| 122 | smc_var3_var2 { |
| 123 | bias = <30>; |
| 124 | functionname = "smc_var3_var2"; |
| 125 | }; |
| 126 | smc_var3_var3 { |
| 127 | bias = <40>; |
| 128 | functionname = "smc_var3_var3"; |
| 129 | }; |
| 130 | }; |
| 131 | |
| 132 | The number of levels of hierachy can be unlimited. A specific |
| 133 | example that is relevant to TF-A(SDEI) is: |
| 134 | |
| 135 | .. code-block:: devicetree |
| 136 | |
| 137 | sdei { |
| 138 | bias = <30>; |
| 139 | sdei_version { |
| 140 | bias = <30>; |
| 141 | functionname = "sdei_version"; |
| 142 | }; |
| 143 | sdei_pe_unmask { |
| 144 | bias = <30>; |
| 145 | functionname = "sdei_pe_unmask"; |
| 146 | }; |
| 147 | sdei_pe_mask { |
| 148 | bias = <30>; |
| 149 | functionname = "sdei_pe_mask"; |
| 150 | }; |
| 151 | sdei_event_status { |
| 152 | bias = <30>; |
| 153 | functionname = "sdei_event_status"; |
| 154 | }; |
| 155 | sdei_event_signal { |
| 156 | bias = <30>; |
| 157 | functionname = "sdei_event_signal"; |
| 158 | }; |
| 159 | sdei_private_reset { |
| 160 | bias = <30>; |
| 161 | functionname = "sdei_private_reset"; |
| 162 | }; |
| 163 | sdei_shared_reset { |
| 164 | bias = <30>; |
| 165 | functionname = "sdei_shared_reset"; |
| 166 | }; |
| 167 | }; |
| 168 | |
| 169 | Here we see a single level of hierarchy where all the |
| 170 | flavors of SDEI calls have an equal chance of being selected. |
| 171 | To incorporate additional calls of another feature set we |
| 172 | could add another level and then bias each as separate |
| 173 | branches. Here is an example for adding TSP based calls: |
| 174 | |
| 175 | .. code-block:: devicetree |
| 176 | |
| 177 | sdei { |
| 178 | bias = <30>; |
| 179 | sdei_version { |
| 180 | bias = <30>; |
| 181 | functionname = "sdei_version_funcid"; |
| 182 | }; |
| 183 | sdei_pe_unmask { |
| 184 | bias = <30>; |
| 185 | functionname = "sdei_pe_unmask_funcid"; |
| 186 | }; |
| 187 | sdei_pe_mask { |
| 188 | bias = <30>; |
| 189 | functionname = "sdei_pe_mask_funcid"; |
| 190 | }; |
| 191 | sdei_event_status { |
| 192 | bias = <30>; |
| 193 | functionname = "sdei_event_status_funcid"; |
| 194 | }; |
| 195 | sdei_event_signal { |
| 196 | bias = <30>; |
| 197 | functionname = "sdei_event_signal_funcid"; |
| 198 | }; |
| 199 | sdei_private_reset { |
| 200 | bias = <30>; |
| 201 | functionname = "sdei_private_reset_funcid"; |
| 202 | }; |
| 203 | sdei_shared_reset { |
| 204 | bias = <30>; |
| 205 | functionname = "sdei_shared_reset_funcid"; |
| 206 | }; |
| 207 | }; |
| 208 | tsp { |
| 209 | bias = <30>; |
| 210 | tsp_add_op { |
| 211 | bias = <30>; |
| 212 | functionname = "tsp_add_op_funcid"; |
| 213 | }; |
| 214 | tsp_sub_op { |
| 215 | bias = <30>; |
| 216 | functionname = "tsp_sub_op_funcid"; |
| 217 | }; |
| 218 | tsp_mul_op { |
| 219 | bias = <30>; |
| 220 | functionname = "tsp_mul_op_funcid"; |
| 221 | }; |
| 222 | tsp_div_op { |
| 223 | bias = <30>; |
| 224 | functionname = "tsp_div_op_funcid"; |
| 225 | }; |
| 226 | }; |
| 227 | |
| 228 | Either TSP or SDEI biases could be set to zero for no selection |
| 229 | or blended together with a desired emphasis of one over the other. |
| 230 | Also further fine grained tuning could be applied to each with |
| 231 | specific biases specified to the SMC calls themselves. This is |
| 232 | ideal for various maturity levels of the code base where features |
| 233 | and/or calls can be controlled over the course of a software |
| 234 | project. |
| 235 | |
| 236 | Detailed description of the Nodes in the Bias Tree |
| 237 | ==================================================== |
| 238 | |
| 239 | Each node in the tree represents a point where the fuzzer engine |
| 240 | randomly selects a given path based on the biases given. The |
| 241 | absolute values given are not meaningful but only in the context of |
| 242 | relative weights against the other biases in the node. For example, |
| 243 | a bias node with two options could have relative biases of 1:1, 20:20, |
| 244 | 50:50 and the same effect on the path is implied - there would be an |
| 245 | even chance either option is selected (although there is greater |
| 246 | efficiency with lower numbers for fuzz performance). It is suggested |
| 247 | to keep the numbers as low as possible for each node. |
| 248 | |
| 249 | When adding new instructions it would be ideal to classify them in some |
| 250 | meaningful way so they can be optimally positioned in the tree to |
| 251 | facilitate testing of various types of scenarios. For instance, if there |
| 252 | is a set of instructions that are performance intensive or can cause |
| 253 | undefined behavior there may be a justification to reduce or eliminate |
| 254 | them in a particular run or perhaps instead to emphasize their selection |
| 255 | to enhance bug finding. Another approach might be to favor a certain |
| 256 | generalized type of testing such as SDEI or world switching of various types. |
| 257 | It should be noted that the instructions do not have to be SMC calls only |
| 258 | but can be groups of SMC or some other form of granularity that could be |
| 259 | useful but should work in a generalized context as much as possible. |
| 260 | |
| 261 | More nodes and branches provides more flexibility in testing as a general rule. |
| 262 | As an additional note of usage more bias tree types can be stored where each |
| 263 | gives a preference for testing in a certain domain. However for bug finding |
| 264 | especially in CI it will be useful to have a large tree to mix as many scenarios |
| 265 | as possible. A "top" tree would be used for this purpose. It is probably in |
| 266 | the best interest for QA for the introducer to add to the top level tree in |
| 267 | some way for inclusion into CI runs where they can decide on frequency of |
| 268 | execution(as well as the categories they fall under). The first step would |
| 269 | be to graft to this tree with the appropriate biases with sub class designations |
| 270 | where each instruction would be assigned a uniquified string. Next, a header |
| 271 | file would be created in the fuzzing include directory where the string |
| 272 | identification would take place and then a subsequent execution of the SMC |
| 273 | call(s) would occur. |
| 274 | |
| 275 | Adding Fuzzing arguments to the SMC Call |
| 276 | ========================================= |
| 277 | |
| 278 | The above description will serve to provide a means to add the SMC calls but |
| 279 | only covers the call itself without reference to the input arguments. This next |
| 280 | section will detail how to add the arguments/fields to the call and also to give |
| 281 | the developer the means to control how those fields are derived in the fuzzing |
| 282 | process. Before giving the steps it is necessary to explain how the fuzzer |
| 283 | views the arguments from what is called differing sanity levels. |
| 284 | |
| 285 | The sanity level is a top level designation of how the arguments are randomized |
| 286 | for a given test. The higher the sanity the more constraints are applied to the |
| 287 | arguments. There are four levels(0 - 3) and each is described below: |
| 288 | |
| 289 | Sanity level 0: |
| 290 | |
| 291 | Here all the input register inputs are fully randomized without reference to field |
| 292 | sizes or obeying any given constraints. This would mean that a 64 bit register |
| 293 | has a full 64 bit random value across all input arguments even those that would be |
| 294 | specified as reserved. The purpose is to find any hangs or assert fails lurking |
| 295 | in the code base that otherwise would be unseen. The number of arguments contained |
| 296 | in a given SMC call is determined by the developer that will be shown in a later |
| 297 | section |
| 298 | |
| 299 | Sanity level 1: |
| 300 | |
| 301 | The next level above zero would be similar where all arguments are fully randomized |
| 302 | but with the exception of one argument that is randomly chosen that is randomized |
| 303 | based on the field size contained within. So if an argument has a field that is |
| 304 | smaller and contained within a reserved section, only those bits within the field |
| 305 | would be randomized and the rest would be left at zero. For instance if there is |
| 306 | a field that is 4 bits wide starting at bit 0 only those four bits would be given a |
| 307 | random value and the rest would be zero in the remaining portion of the argument/register. |
| 308 | If there is only one argument, then that argument will be chosen as the more constrained |
| 309 | input. |
| 310 | |
| 311 | Sanity level 2: |
| 312 | |
| 313 | This level does the same as sanity level one but only performs the constrained |
| 314 | randomization as sanity level 1 on all the registers (instead of just one) so no fully |
| 315 | random arguments are given. They will all obey the field sizes and leave the rest zero. |
| 316 | |
| 317 | Sanity level 3: |
| 318 | |
| 319 | The last level is fully constrained by the developer using the constraint modeler |
| 320 | provided. All reserved values are observed and the fields can be randomized in a |
| 321 | specialized fashion using the fuzz setconstraint function. The details of how this can |
| 322 | be done will be shown in the following sections. This mode will be the most functional |
| 323 | and where outcomes can be predicted based on the provided values given to the SMC call. |
| 324 | Errors can be explicitly found and detail given for codes returning from the call. |
| 325 | |
| 326 | Specifying Input Arguments |
| 327 | --------------------------- |
| 328 | |
| 329 | For all of this to work there is a method to specify the makeup of a given SMC call that |
| 330 | is a text file provided by the user. This will specify the various arguments and fields |
| 331 | needed and their respective default values. the format would look like: |
| 332 | |
| 333 | .. code-block:: none |
| 334 | |
| 335 | smc: <name of call> |
| 336 | arg<register number 1-17>: <name of register> |
| 337 | field:<name of field>:[<starting bit>,<ending bit>] = <default value decimal or 0x> |
| 338 | |
| 339 | For arguments that are reserved where no constraint is needed(only default values passed |
| 340 | to call) |
| 341 | |
| 342 | Range of registers: |
| 343 | |
| 344 | .. code-block:: none |
| 345 | |
| 346 | arg<starting register>-arg<ending register> = <default value> |
| 347 | |
| 348 | Single register: |
| 349 | |
| 350 | .. code-block:: none |
| 351 | |
| 352 | arg<register number> = <default value> |
| 353 | |
| 354 | An arbitrary example of a SMC definition file would look like: |
| 355 | |
| 356 | .. code-block:: none |
| 357 | |
| 358 | smc: SDEI_EVENT_STATUS_CALL |
| 359 | arg1:bev |
| 360 | field:bev:[0,24] = 0xAABB00 |
| 361 | field:t1:[2,3] = 2 |
| 362 | field:t2:[4,6] = 0x667 |
| 363 | field:ert:[25,48]=0xA544 |
| 364 | arg2:tup |
| 365 | field:fjs:[4,5] = 3 |
| 366 | field:yeu:[2,3] = 2 |
| 367 | field:sjd:[6,10] = 7 |
| 368 | field:ndjs:[14,30]=523 |
| 369 | field:csa:[0,0]=1 |
| 370 | arg3:jfgh |
| 371 | field:bbc:[4,5] = 3 |
| 372 | field:xee:[2,3] = 2 |
| 373 | field:jhfs:[7,11] = 7 |
| 374 | field:nivt:[14,30]=523 |
| 375 | field:vtt:[0,0]=1 |
| 376 | smc: SDEI_INTERRUPT_BIND_CALL |
| 377 | arg1:interruptnum |
| 378 | field:inum:[0,31] = 1 |
| 379 | field:t12:[32,47] = 0xafaf |
| 380 | field:t34:[48,63] = 0xaddad |
| 381 | arg2:yyyre |
| 382 | field:hhhj:[0,31] = 1 |
| 383 | field:cyu:[32,47] = 0xafaf |
| 384 | field:jjd:[48,63] = 78 |
| 385 | arg3-arg10 = 0x1adf |
| 386 | arg11 = 0x235a |
| 387 | |
| 388 | If no constraints are applied to a field then the default values will |
| 389 | be used as shown above. |
| 390 | |
| 391 | A test can contain only calls without constraints applied but only default |
| 392 | values will be given which is much less useful for bug finding/coverage for |
| 393 | sanity level 3. Only sanity level 3 will have the default values applied. |
| 394 | |
| 395 | Once the developer is finished with the file it is necessary to run a script |
| 396 | to add the calls to the fuzzer. It is found in the smc fuzzer directory under |
| 397 | scripts. It is invoked as follows: |
| 398 | |
| 399 | .. code-block:: none |
| 400 | |
| 401 | python3 script/generate_smc.py -s <name of file created by developer |
| 402 | for smc input parameters> |
| 403 | |
| 404 | For CI runs it is necessary to specify the SMC definition file in the TFTF |
| 405 | config as follows: |
| 406 | |
| 407 | .. code-block:: none |
| 408 | |
| 409 | SMC_FUZZ_DEFFILE=<name of the SMC definition file> |
| 410 | |
| 411 | |
| 412 | Adding SMC calls to Fuzzing engine |
| 413 | --------------------------------------------- |
| 414 | |
| 415 | Setting the constraints would be the next step after specifying the calls shown |
| 416 | above. The user has to determine what would be a useful set of random values to |
| 417 | apply to a given field. The constraint model assumes no interdependency between |
| 418 | the fields although that can be done using other means. For a field, there are |
| 419 | three options available to constrain the random values. The first is the simplest |
| 420 | since it is just a single value similar to the default value and will be used every |
| 421 | time the call is invoked (with a caveat mentioned later). The next would be a range |
| 422 | of values where the fuzzer chooses a value amongst them. The last is passing a set |
| 423 | of values (vector) where the fuzzer also chooses within the set. Only sanity level |
| 424 | 3 applies the constraints given here, all other sanity levels will ignore this |
| 425 | constraint application. |
| 426 | |
| 427 | Using one of these three will yield a randomly selected value(with the exception of |
| 428 | the first method) to give to the SMC call. Once again, if no constraint is applied |
| 429 | to a field, it will use the default value. |
| 430 | |
| 431 | A specific example would be the C file that intercepts the function ID from the |
| 432 | generated bias tree and then applies the contraints as desired. For the SDEI example |
| 433 | the following would be a representation of the function called in the fuzzer: |
| 434 | |
| 435 | .. code-block:: c |
| 436 | |
| 437 | void run_sdei_fuzz(int funcid, struct memmod *mmod) |
| 438 | { |
| 439 | if (funcid == sdei_version_funcid) { |
| 440 | long long ret = sdei_version(); |
| 441 | if (ret != MAKE_SDEI_VERSION(1, 0, 0)) { |
| 442 | tftf_testcase_printf("Unexpected SDEI version: 0x%llx\n", |
| 443 | ret); |
| 444 | } |
| 445 | } else if (funcid == sdei_pe_unmask_funcid) { |
| 446 | long long ret = sdei_pe_unmask(); |
| 447 | if (ret < 0) { |
| 448 | tftf_testcase_printf("SDEI pe unmask failed: 0x%llx\n",ret); |
| 449 | } |
| 450 | } else if (funcid == sdei_pe_mask_funcid) { |
| 451 | int64_t ret = sdei_pe_mask(); |
| 452 | if (ret < 0) { |
| 453 | tftf_testcase_printf("SDEI pe mask failed: 0x%llx\n", ret); |
| 454 | } |
| 455 | } else if (funcid == sdei_event_status_funcid) { |
| 456 | int64_t ret = sdei_event_status(0); |
| 457 | if (ret < 0) { |
| 458 | tftf_testcase_printf("SDEI event status failed: 0x%llx\n", |
| 459 | ret); |
| 460 | } |
| 461 | } else if (funcid == sdei_event_signal_funcid) { |
| 462 | int64_t ret = sdei_event_signal(0); |
| 463 | if (ret < 0) { |
| 464 | tftf_testcase_printf("SDEI event signal failed: 0x%llx\n", |
| 465 | ret); |
| 466 | } |
| 467 | } else if (funcid == sdei_private_reset_funcid) { |
| 468 | int64_t ret = sdei_private_reset(); |
| 469 | if (ret < 0) { |
| 470 | tftf_testcase_printf("SDEI private reset failed: 0x%llx\n", |
| 471 | ret); |
| 472 | } |
| 473 | } else if (funcid == sdei_shared_reset_funcid) { |
| 474 | int64_t ret = sdei_shared_reset(); |
| 475 | if (ret < 0) { |
| 476 | tftf_testcase_printf("SDEI shared reset failed: 0x%llx\n", |
| 477 | ret); |
| 478 | } |
| 479 | } |
| 480 | |
| 481 | For single function testing the feature developer would create these two files similar |
| 482 | to what is shown above, and then place the specific function into the fuzzer runtestfunction.c |
| 483 | as in: |
| 484 | |
| 485 | .. code-block:: c |
| 486 | |
| 487 | void runtestfunction(char *funcstr) |
| 488 | { |
| 489 | run_sdei_fuzz(funcstr); |
| 490 | } |
| 491 | |
| 492 | All of these locations are in tf-a-tests/smc_fuzz. Place the header files in the |
| 493 | include and the device tree file in the dts. Ensure that the header file is included in |
| 494 | runtestfunction.c. |
| 495 | |
| 496 | To include in top level fuzz testing, the same header file can be used and included but |
| 497 | the developer would have to add to the top.dts file instead of a separate device tree file |
| 498 | (both can be used). Typically a new branch would be created with an associated bias that is |
| 499 | reasonable against the biases of the other instruction classes. |
| 500 | |
| 501 | To run with a particular bias tree file and run the fuzzer the following has to be included |
| 502 | in the tftf config file: |
| 503 | |
| 504 | .. code-block:: none |
| 505 | |
| 506 | TESTS=smcfuzzing |
| 507 | SMC_FUZZING=1 |
| 508 | SMC_FUZZ_DTS=smc_fuzz/dts/top.dts |
| 509 | SMC_FUZZ_SANITY_LEVEL=3 |
| 510 | SMC_FUZZ_CALLS_PER_INSTANCE=10000 |
| 511 | SMC_FUZZ_DEFFILE=sdei_and_vendor_smc_calls.txt |
| 512 | |
| 513 | This configuration calls the top.dts but can be redirected to any other. This presently exists |
| 514 | in the tftf config file fvp-smcfuzzing. The sanity level needs to be explicitly specified and |
| 515 | the SMC definition file is to be included when running in CI. This step is not necessary if |
| 516 | running locally and the user is manually running the generate_smc script as shown. |
| 517 | |
| 518 | Once this basic infrastructure is in place the application of constraints can now begin. |
| 519 | |
| 520 | The place to apply the constraint would be in the body of the run_sdei_fuzz function shown above |
| 521 | as an example. The function call to constrain the input looks like: |
| 522 | |
| 523 | .. code-block:: none |
| 524 | |
| 525 | setconstraint(<constraint type>,<constraint>,<length of constraint>,<field>, |
| 526 | <memory pointer(user just passed without consideration)>,<constraint mode>) |
| 527 | |
| 528 | The constraint mode functions as a means to control a set of constraints applied to a given SMC call. |
| 529 | The user has the option to add as many constraints as desired to a given field for all types |
| 530 | if the setconstraint function is in what is called accumulation mode. What this means is that adding |
| 531 | a single value consraint can be done multiple times and the fuzzer will randomly choose amongst them. |
| 532 | The same is true for any constraint type added so there can be a diversity of types within and the |
| 533 | fuzzer will only choose one and then randomly select a value next if a range or vector. |
| 534 | |
| 535 | The other mode is what is called exclusive mode. This is where the constraint function only observes |
| 536 | the passed constraint and throws away any other invoked in the past. Once a constraint is passed in |
| 537 | this mode it removes all others and cannot be recovered in a later call. This is useful if a user |
| 538 | wishes to reset the values in the accumulation and start over or just wants to have only one set of |
| 539 | constraints applied. |
| 540 | |
| 541 | It should be noted that the argument names to specify the constraints are found in the SMC definition |
| 542 | file. |
| 543 | |
| 544 | The format of the arguments to the function is as follows: |
| 545 | |
| 546 | .. code-block:: none |
| 547 | |
| 548 | Constraint type: |
| 549 | |
| 550 | Either |
| 551 | |
| 552 | FUZZER_CONSTRAINT_SVALUE → single value |
| 553 | |
| 554 | FUZZER_CONSTRAINT_RANGE → range of values |
| 555 | |
| 556 | FUZZER_CONSTRAINT_VECTOR → vector of values |
| 557 | |
| 558 | Constraint: |
| 559 | |
| 560 | single input or array depending on type above |
| 561 | |
| 562 | Length of constraint: |
| 563 | |
| 564 | single value would be 1, range would be 2, and vector would be |
| 565 | the number of values of the vector |
| 566 | |
| 567 | Field: |
| 568 | |
| 569 | <SMC name>_ARG<number>_<capitalized field> |
| 570 | |
| 571 | Memory pointer: |
| 572 | |
| 573 | mmod → always the same |
| 574 | |
| 575 | Constraint mode: |
| 576 | |
| 577 | FUZZER_CONSTRAINT_ACCMODE → accumulation mode |
| 578 | |
| 579 | FUZZER_CONSTRAINT_EXCMODE → exclusive mode |
| 580 | |
| 581 | An example call would look something like: |
| 582 | |
| 583 | .. code-block:: none |
| 584 | |
| 585 | uint64_t inputbind[2] = {170,220}; |
| 586 | setconstraint(FUZZER_CONSTRAINT_RANGE, inputbind, 2, |
| 587 | SDEI_INTERRUPT_BIND_CALL_ARG1_INUM, mmod, FUZZER_CONSTRAINT_ACCMODE); |
| 588 | |
| 589 | Generating the arguments after Constraint Specification |
| 590 | --------------------------------------------------------- |
| 591 | |
| 592 | What follows once the constraints are applied is the generation of the arguments to be |
| 593 | given to the SMC call in the test. Here is where the sanity level is stated and the |
| 594 | specific SMC desired. The format is as follows: |
| 595 | |
| 596 | .. code-block:: none |
| 597 | |
| 598 | generate_args(<SMC call>, <sanity level>); |
| 599 | |
| 600 | The sanity level can be explicity stated as: |
| 601 | |
| 602 | .. code-block:: none |
| 603 | |
| 604 | SANITY_LEVEL_0 |
| 605 | |
| 606 | SANITY_LEVEL_1 |
| 607 | |
| 608 | SANITY_LEVEL_2 |
| 609 | |
| 610 | SANITY_LEVEL_3 |
| 611 | |
| 612 | Or can be passed from the TFTF config as: |
| 613 | |
| 614 | .. code-block:: none |
| 615 | |
| 616 | SMC_FUZZ_SANITY_LEVEL |
| 617 | |
| 618 | The parameter in the TFTF config would look like(to set at top level) |
| 619 | |
| 620 | .. code-block:: none |
| 621 | |
| 622 | SMC_FUZZ_SANITY_LEVEL=<level> |
| 623 | |
| 624 | where the value would be 0,1,2.3.... |
| 625 | |
| 626 | The generate function would return the argument/register values and can be applied |
| 627 | to the SMC after invocation. Here the user has an opportunity to read the values |
| 628 | returned and can anticipate the behavior of the call and therefore have a mechanism |
| 629 | to error handle the result. A typical invocation would resemble: |
| 630 | |
| 631 | .. code-block:: none |
| 632 | |
| 633 | struct inputparameters inp = generate_args(SDEI_INTERRUPT_BIND_CALL, |
| 634 | SMC_FUZZ_SANITY_LEVEL); |
| 635 | |
| 636 | Getting the field values after generation of the arguments |
| 637 | ------------------------------------------------------------ |
| 638 | |
| 639 | The last function to describe would be the mechanism to find the generated field after |
| 640 | running generate_args. The format is as follows: |
| 641 | |
| 642 | .. code-block:: none |
| 643 | |
| 644 | get_generated_value(<argument name>, <Field>, <struct inputparameters>) |
| 645 | |
| 646 | Both argument name and field are the same format as shown in the setconstraint function. |
| 647 | A typical invocation would be: |
| 648 | |
| 649 | .. code-block:: none |
| 650 | |
| 651 | uint64_t genfield = get_generated_value(SDEI_INTERRUPT_BIND_CALL_ARG1, |
| 652 | SDEI_INTERRUPT_BIND_CALL_ARG1_INUM,inp) |
| 653 | |
| 654 | To aid understanding here is an example in SDEI of what would be a typical scenario |
| 655 | of constraint specification and generation of the arguments: |
| 656 | |
| 657 | .. code-block:: c |
| 658 | |
| 659 | } else if (funcid == sdei_event_signal_funcid) { |
| 660 | |
| 661 | setconstraint(FUZZER_CONSTRAINT_SVALUE, PE_SVALUE, 1, |
| 662 | SDEI_EVENT_SIGNAL_CALL_ARG2_PE, mmod, FUZZER_CONSTRAINT_ACCMODE); |
| 663 | struct inputparameters inp = generate_args(SDEI_EVENT_SIGNAL_CALL, SMC_FUZZ_SANITY_LEVEL); |
| 664 | int64_t ret; |
| 665 | |
| 666 | if (inrange) { |
| 667 | ret = sdei_event_signal(inp.x2); |
| 668 | if (ret < 0) { |
| 669 | tftf_testcase_printf("sdei_event_signal failed: %lld\n", ret); |
| 670 | } |
| 671 | } |
| 672 | } |
| 673 | |
| 674 | |
| 675 | *Copyright (c) 2024, Arm Limited. All rights reserved.* |
| 676 | |
| 677 | .. |Bias Tree Example| image:: ../resources/bias_tree_example.png |
| 678 | |