blob: 056eba073a5d5ea68f58f0dfbe9645ac38e9738e [file] [log] [blame]
Mark Dykesd5d19ae2025-04-23 15:13:28 -05001Overview of Bias Tree
2=========================
3
4The bias tree functions as a mechanism to randomly determine the order
5of SMC calls directed to TF-A. Here is an example.
6
7
8|Bias Tree Example|
9*Figure 2: Bias Tree Example*
10
11Each SMC call is categorized as in a feature list(i. e. SDEI, TSP...etc)
12and then could be subcategorized into smaller lists relevant to testing
13strategy. This grouping allows a user to assert control over which flavors
14of SMC calls are executed within a given test scenario. In this example
15we have feature sets corresponding to the different areas of TF-A that
16can be biased or turned off altogether. This permits very focused testing
17to very general by altering the bias numbers contained in the tree.
18
19Here 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
132The number of levels of hierachy can be unlimited. A specific
133example 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
169Here we see a single level of hierarchy where all the
170flavors of SDEI calls have an equal chance of being selected.
171To incorporate additional calls of another feature set we
172could add another level and then bias each as separate
173branches. 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
228Either TSP or SDEI biases could be set to zero for no selection
229or blended together with a desired emphasis of one over the other.
230Also further fine grained tuning could be applied to each with
231specific biases specified to the SMC calls themselves. This is
232ideal for various maturity levels of the code base where features
233and/or calls can be controlled over the course of a software
234project.
235
236Detailed description of the Nodes in the Bias Tree
237====================================================
238
239Each node in the tree represents a point where the fuzzer engine
240randomly selects a given path based on the biases given. The
241absolute values given are not meaningful but only in the context of
242relative weights against the other biases in the node. For example,
243a bias node with two options could have relative biases of 1:1, 20:20,
24450:50 and the same effect on the path is implied - there would be an
245even chance either option is selected (although there is greater
246efficiency with lower numbers for fuzz performance). It is suggested
247to keep the numbers as low as possible for each node.
248
249When adding new instructions it would be ideal to classify them in some
250meaningful way so they can be optimally positioned in the tree to
251facilitate testing of various types of scenarios. For instance, if there
252is a set of instructions that are performance intensive or can cause
253undefined behavior there may be a justification to reduce or eliminate
254them in a particular run or perhaps instead to emphasize their selection
255to enhance bug finding. Another approach might be to favor a certain
256generalized type of testing such as SDEI or world switching of various types.
257It should be noted that the instructions do not have to be SMC calls only
258but can be groups of SMC or some other form of granularity that could be
259useful but should work in a generalized context as much as possible.
260
261More nodes and branches provides more flexibility in testing as a general rule.
262As an additional note of usage more bias tree types can be stored where each
263gives a preference for testing in a certain domain. However for bug finding
264especially in CI it will be useful to have a large tree to mix as many scenarios
265as possible. A "top" tree would be used for this purpose. It is probably in
266the best interest for QA for the introducer to add to the top level tree in
267some way for inclusion into CI runs where they can decide on frequency of
268execution(as well as the categories they fall under). The first step would
269be to graft to this tree with the appropriate biases with sub class designations
270where each instruction would be assigned a uniquified string. Next, a header
271file would be created in the fuzzing include directory where the string
272identification would take place and then a subsequent execution of the SMC
273call(s) would occur.
274
275Adding Fuzzing arguments to the SMC Call
276=========================================
277
278The above description will serve to provide a means to add the SMC calls but
279only covers the call itself without reference to the input arguments. This next
280section will detail how to add the arguments/fields to the call and also to give
281the developer the means to control how those fields are derived in the fuzzing
282process. Before giving the steps it is necessary to explain how the fuzzer
283views the arguments from what is called differing sanity levels.
284
285The sanity level is a top level designation of how the arguments are randomized
286for a given test. The higher the sanity the more constraints are applied to the
287arguments. There are four levels(0 - 3) and each is described below:
288
289Sanity level 0:
290
291Here all the input register inputs are fully randomized without reference to field
292sizes or obeying any given constraints. This would mean that a 64 bit register
293has a full 64 bit random value across all input arguments even those that would be
294specified as reserved. The purpose is to find any hangs or assert fails lurking
295in the code base that otherwise would be unseen. The number of arguments contained
296in a given SMC call is determined by the developer that will be shown in a later
297section
298
299Sanity level 1:
300
301The next level above zero would be similar where all arguments are fully randomized
302but with the exception of one argument that is randomly chosen that is randomized
303based on the field size contained within. So if an argument has a field that is
304smaller and contained within a reserved section, only those bits within the field
305would be randomized and the rest would be left at zero. For instance if there is
306a field that is 4 bits wide starting at bit 0 only those four bits would be given a
307random value and the rest would be zero in the remaining portion of the argument/register.
308If there is only one argument, then that argument will be chosen as the more constrained
309input.
310
311Sanity level 2:
312
313This level does the same as sanity level one but only performs the constrained
314randomization as sanity level 1 on all the registers (instead of just one) so no fully
315random arguments are given. They will all obey the field sizes and leave the rest zero.
316
317Sanity level 3:
318
319The last level is fully constrained by the developer using the constraint modeler
320provided. All reserved values are observed and the fields can be randomized in a
321specialized fashion using the fuzz setconstraint function. The details of how this can
322be done will be shown in the following sections. This mode will be the most functional
323and where outcomes can be predicted based on the provided values given to the SMC call.
324Errors can be explicitly found and detail given for codes returning from the call.
325
326Specifying Input Arguments
327---------------------------
328
329For all of this to work there is a method to specify the makeup of a given SMC call that
330is a text file provided by the user. This will specify the various arguments and fields
331needed 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
339For arguments that are reserved where no constraint is needed(only default values passed
340to call)
341
342Range of registers:
343
344.. code-block:: none
345
346 arg<starting register>-arg<ending register> = <default value>
347
348Single register:
349
350.. code-block:: none
351
352 arg<register number> = <default value>
353
354An 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
388If no constraints are applied to a field then the default values will
389be used as shown above.
390
391A test can contain only calls without constraints applied but only default
392values will be given which is much less useful for bug finding/coverage for
393sanity level 3. Only sanity level 3 will have the default values applied.
394
395Once the developer is finished with the file it is necessary to run a script
396to add the calls to the fuzzer. It is found in the smc fuzzer directory under
397scripts. 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
404For CI runs it is necessary to specify the SMC definition file in the TFTF
405config as follows:
406
407.. code-block:: none
408
409 SMC_FUZZ_DEFFILE=<name of the SMC definition file>
410
411
412Adding SMC calls to Fuzzing engine
413---------------------------------------------
414
415Setting the constraints would be the next step after specifying the calls shown
416above. The user has to determine what would be a useful set of random values to
417apply to a given field. The constraint model assumes no interdependency between
418the fields although that can be done using other means. For a field, there are
419three options available to constrain the random values. The first is the simplest
420since it is just a single value similar to the default value and will be used every
421time the call is invoked (with a caveat mentioned later). The next would be a range
422of values where the fuzzer chooses a value amongst them. The last is passing a set
423of values (vector) where the fuzzer also chooses within the set. Only sanity level
4243 applies the constraints given here, all other sanity levels will ignore this
425constraint application.
426
427Using one of these three will yield a randomly selected value(with the exception of
428the first method) to give to the SMC call. Once again, if no constraint is applied
429to a field, it will use the default value.
430
431A specific example would be the C file that intercepts the function ID from the
432generated bias tree and then applies the contraints as desired. For the SDEI example
433the 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
481For single function testing the feature developer would create these two files similar
482to what is shown above, and then place the specific function into the fuzzer runtestfunction.c
483as in:
484
485.. code-block:: c
486
487 void runtestfunction(char *funcstr)
488 {
489 run_sdei_fuzz(funcstr);
490 }
491
492All of these locations are in tf-a-tests/smc_fuzz. Place the header files in the
493include and the device tree file in the dts. Ensure that the header file is included in
494runtestfunction.c.
495
496To include in top level fuzz testing, the same header file can be used and included but
497the 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
499reasonable against the biases of the other instruction classes.
500
501To run with a particular bias tree file and run the fuzzer the following has to be included
502in 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
513This configuration calls the top.dts but can be redirected to any other. This presently exists
514in the tftf config file fvp-smcfuzzing. The sanity level needs to be explicitly specified and
515the SMC definition file is to be included when running in CI. This step is not necessary if
516running locally and the user is manually running the generate_smc script as shown.
517
518Once this basic infrastructure is in place the application of constraints can now begin.
519
520The place to apply the constraint would be in the body of the run_sdei_fuzz function shown above
521as 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
528The constraint mode functions as a means to control a set of constraints applied to a given SMC call.
529The user has the option to add as many constraints as desired to a given field for all types
530if the setconstraint function is in what is called accumulation mode. What this means is that adding
531a single value consraint can be done multiple times and the fuzzer will randomly choose amongst them.
532The same is true for any constraint type added so there can be a diversity of types within and the
533fuzzer will only choose one and then randomly select a value next if a range or vector.
534
535The other mode is what is called exclusive mode. This is where the constraint function only observes
536the passed constraint and throws away any other invoked in the past. Once a constraint is passed in
537this mode it removes all others and cannot be recovered in a later call. This is useful if a user
538wishes to reset the values in the accumulation and start over or just wants to have only one set of
539constraints applied.
540
541It should be noted that the argument names to specify the constraints are found in the SMC definition
542file.
543
544The 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
581An 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
589Generating the arguments after Constraint Specification
590---------------------------------------------------------
591
592What follows once the constraints are applied is the generation of the arguments to be
593given to the SMC call in the test. Here is where the sanity level is stated and the
594specific SMC desired. The format is as follows:
595
596.. code-block:: none
597
598 generate_args(<SMC call>, <sanity level>);
599
600The 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
612Or can be passed from the TFTF config as:
613
614.. code-block:: none
615
616 SMC_FUZZ_SANITY_LEVEL
617
618The 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
624where the value would be 0,1,2.3....
625
626The generate function would return the argument/register values and can be applied
627to the SMC after invocation. Here the user has an opportunity to read the values
628returned and can anticipate the behavior of the call and therefore have a mechanism
629to 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
636Getting the field values after generation of the arguments
637------------------------------------------------------------
638
639The last function to describe would be the mechanism to find the generated field after
640running generate_args. The format is as follows:
641
642.. code-block:: none
643
644 get_generated_value(<argument name>, <Field>, <struct inputparameters>)
645
646Both argument name and field are the same format as shown in the setconstraint function.
647A 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
654To aid understanding here is an example in SDEI of what would be a typical scenario
655of 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