blob: 1b3b04f4c78c2317b72c00556838cf16383d166b [file] [log] [blame]
Mate Toth-Pala8797e12020-06-05 21:14:26 +02001#############
2IRQ test tool
3#############
4
5************
6Introduction
7************
8
9This tool is to test interrupt handling in TF-M. Testing different interrupt
10scenarios is important as the ARMv8-M architecture does complex operations when
11interrupt happens, especially when security boundary crossing happens. These
12operations need to be considered by the TF-M implementation, as in a typical use
13case there is a separate scheduler on the Non-Secure and the secure side as
14well, and the SPM needs to maintain a consistent state, which might not be
15trivial.
16
17The aim of the tool is to be able to test scenarios, that are identified to be
18problematic, in a reproducible way, and do this in an automated way, so regular
19regression testing can have a low cost.
20
21******************
22How the tool works
23******************
24
25The tool is a set of Python scripts which need to be run **inside** a debugger.
26Currently Arm Development Studio and GDB are supported. During the test run, the
27script interacts with the debugger, sets breakpoints, triggers interrupts by
28writing into system registers, starts the target, and when the target is
29stopped, it examines the target's state.
30
31A typical execution scenario looks like this:
32
33.. uml::
34
35 @startuml
36
37 participant CPU
38 participant Debugger
39
40 CPU -> CPU: start_from_reset_handler
41 activate CPU
42
43 Debugger -> CPU: Attach & pause
44 deactivate CPU
45 Debugger-> Debugger: start_script
46 Activate Debugger
47
48 note right
49 Read config files ...
50
51 execute step 1
52 end note
53
54 Debugger -> CPU: set_breakpoint
55
56 Debugger -> CPU: Continue
57 deactivate Debugger
58 activate CPU
59
60
61 ... executing ...
62
63 loop for all the remaining steps
64
65 CPU->Debugger: bkpt hit
66 deactivate CPU
67 activate Debugger
68
69 note right
70 Sanity check on the triggered breakpoint
71 (is this the breakpoint expected)
72 If so, continue the sequence
73 end note
74
75 Debugger -> CPU: set_breakpoint
76
77 alt if required by step
78 Debugger -> CPU: set interrupt pending
79 end alt
80
81 Debugger -> CPU: Continue
82 deactivate Debugger
83 activate CPU
84
85 ... executing ...
86
87 end loop
88
89 CPU->Debugger: bkpt hit
90 deactivate CPU
91 activate Debugger
92
93 Debugger->Debugger: End of script
94 Deactivate Debugger
95
96
97 @enduml
98
99Once started inside the debugger, the script automatically deduces the debugger
100it is running in, by trying to import the support libraries for a specific
101debugger. The order the debuggers are tried in the following order:
102
103#. Arm Development studio
104#. GDB
105
106If both check fails, the script falls back to 'dummy' mode which means that the
107calls to the debugger log the call, and returns successfully.
108
109.. note::
110
111 This 'dummy' mode can be used out of a debugger environment as well.
112
113.. important::
114
115 The script assumes that the symbols for the software being debugged/tested
116 are loaded in the debugger.
117
118The available parameters are:
119
120+----------------------+---------------------------------+--------------------------------------------------+
121| short option | long option | meaning |
122+======================+=================================+==================================================+
123| ``-w`` | ``--sw-break`` | Use sw breakpoint (the default is HW breakpoint) |
124+----------------------+---------------------------------+--------------------------------------------------+
125| ``-q <IRQS>`` | ``--irqs <IRQS>`` | The name of the IRQs json |
126+----------------------+---------------------------------+--------------------------------------------------+
127| ``-t <TESTCASE>`` | ``--testcase <TESTCASE>`` | The name of the file containing the testcase |
128+----------------------+---------------------------------+--------------------------------------------------+
129| ``-b <BREAKPOINTS>`` | ``--breakpoints <BREAKPOINTS>`` | The name of the breakpoints json file |
130+----------------------+---------------------------------+--------------------------------------------------+
131
132***********
133Input files
134***********
135
136Breakpoints
137===========
138
139below is a sample file for breakpoints:
140
141.. code:: json
142
143 {
144 "breakpoints": {
145 "irq_test_iteration_before_service_calls": {
146 "file": "core_ns_positive_testsuite.c",
147 "line": 692
148 },
149 "irq_test_service1_high_handler": {
150 "symbol": "SPM_CORE_IRQ_TEST_1_SIGNAL_HIGH_isr"
151 },
152 "irq_test_service2_prepare_veneer": {
153 "offset": "4",
154 "symbol": "tfm_spm_irq_test_2_prepare_test_scenario_veneer"
155 }
156 }
157 }
158
159Each point where a breakpoint is to be set by the tool should be enumerated in
160this file, in the "breakpoints" object. For each breakpoint an object needs to
161be created. The name of the object can be used in the testcase description. The
162possible fields for a breakpoint object can be seen in the example above.
163
164tools/generate_breakpoints.py
165-----------------------------
166
167This script helps to automate the generation of the breakpoints from source files.
168Each code location that is to be used in a testcase, should be annotated with
169one of the following macro in the source files:
170
171.. code:: c
172
173 /* Put breakpoint on the address of the symbol */
174 #define IRQ_TEST_TOOL_SYMBOL(name, symbol)
175
176 /* Put a breakpoint on the address symbol + offset */
177 #define IRQ_TEST_TOOL_SYMBOL_OFFSET(name, symbol, offset)
178
179 /* Put a breakpoint at the specific location in the code where the macro is
180 * called. This creates a file + line type breakpoint
181 */
182 #define IRQ_TEST_TOOL_CODE_LOCATION(name)
183
184Usage of the script:
185
186.. code::
187
188 $ python3 generate_breakpoints.py --help
189 usage: generate_breakpoints.py [-h] tfm_source outfile
190
191 positional arguments:
192 tfm_source path to the TF-M source code
193 outfile The output json file with the breakpoints
194
195 optional arguments:
196 -h, --help show this help message and exit
197
198
199
200IRQs
201====
202
203.. code:: json
204
205 {
206 "irqs": {
207 "test_service1_low": {
208 "line_num" : 51
209 },
210 "ns_irq_low": {
211 "line_num" : 40
212 }
213 }
214 }
215
216Each IRQ that is to be triggered should have an object created inside the "irqs"
217object. The name of these objects is the name that could be used in a testcase
218description. The only valid field of the IRQ objects is "line_num" which refers
219to the number of the interrupt line.
220
221Testcase
222========
223
224.. code:: json
225
226 {
227 "description" : ["Trigger Non-Secure interrupt during SPM execution in",
228 "privileged mode"],
229 "steps": [
230 {
231 "wait_for" : "irq_test_iteration_start"
232 },
233 {
234 "wait_for" : "spm_partition_start"
235 },
236 {
237 "description" : ["Trigger the interrupt, but expect the operation",
238 "to be finished before the handler is called"],
239 "expect" : "spm_partition_start_ret_success",
240 "trigger" : "ns_irq_low"
241 },
242 {
243 "wait_for" : "ns_irq_low_handler"
244 },
245 {
246 "wait_for" : "irq_test_service2_prepare"
247 }
248 ]
249 }
250
251The test is executed by the script on a step by step basis. When the script is
252started, it processes the first step, then starts the target. After a breakpoint
253is hit, it processes the next target, and continues. This iteration is repeated
254until all the steps are processed
255
256For each step, the following activities are executed:
257
258#. All the breakpoints are cleared in the debugger
259#. If there is a 'wait_for' field, a breakpoint is set for the location
260 specified.
261#. If there is a 'trigger' field, an IRQ is pended by writing to NVIC
262 registers.
263#. If there is an 'expect' field, a breakpoint is set for the location
264 specified. Then the testcase file is scanned starting with the next step,
265 and a breakpoint is set at the first location specified with a 'wait_for'
266 field. Next time, when the execution is stopped, the breakpoint that was hit
267 is compared to the expected breakpoint.
268
269Each object can have a description field to add comments.
270
271**********************
Mate Toth-Palf4b87ba2020-06-05 21:18:13 +0200272How to run the example
Mate Toth-Pala8797e12020-06-05 21:14:26 +0200273**********************
274
Mate Toth-Palf4b87ba2020-06-05 21:18:13 +0200275Before running the example, the breakpoints.json needs to be generated from the
276TF-M source tree:
277
278.. code:: shell
279 $ cd tools/irq_test/
280 $ python3 tools/generate_breakpoints.py ../.. example/breakpoints.json
281
282The example also require the regression test suite being present in the TF-M
283binary, so either ``ConfigRegressionIPC.cmake`` or ``ConfigRegression.cmake``
284have to be used to compile TF-M. Also ``-DCMAKE_BUILD_TYPE=Debug`` config option
285have to be used in the cmake generation command, to be sure that the debug
286information is generated in the axf files.
287
288The sequence of running the testcase in the ``example`` folder looks like the
289following:
290
291#. Check out a version of TF-M that contains the ``IRQ_TEST_TOOL_*`` macros for
292 the testcase
293#. Generate breakpoints.json using the TF-M working copy above
294#. Build TF-M checked out above
295#. Start the debugger, connect to the target, and stop the target. (Make sure
296 that the target is stopped before the IRQ testcase of the positive core test
297 suite in TF-M starts executing, as the IRQ test tool's testcase uses the
298 entry of that TF-M test as a trigger to start.)
299#. Execute the script. The script automatically sets the breakpoint for the
300 first step of the testcase, and continues the target execution.
301#. Examine the output of the script. Successful execution is signalled by the
302 following output line:
303
304 .. code::
305
306 ===== INFO: All the steps in the test file are executed successfully with the expected result.
307
308
309
Mate Toth-Pala8797e12020-06-05 21:14:26 +0200310Arm Development Studio
311======================
312
313The script can be called directly from the debugger's command window:
314
Mate Toth-Palf4b87ba2020-06-05 21:18:13 +0200315.. note::
316
317 In the command absolute path have to be used both for the ``irq_test.py``
318 and for the parameters.
319
Mate Toth-Pala8797e12020-06-05 21:14:26 +0200320.. code:: shell
321
Mate Toth-Palf4b87ba2020-06-05 21:18:13 +0200322 source irq_test.py -q example/irqs_AN521.json -b example/breakpoints.json -t example/testcase.json
Mate Toth-Pala8797e12020-06-05 21:14:26 +0200323
324GDB
325===
326
327The script should be sourced inside GDB, without passing any arguments to
328it.
329
330.. code:: shell
331
332 (gdb) source irq_test.py
333
334
335That registers a custom command ``test_irq``. ``test_irq`` should be called
336with three parameters: breakpoints, irqs, and the test file. This command will
337actually execute the tests.
338
339.. note::
340
341 This indirection in case of GDB is necessary because it is not possible to
342 pass parameters to the script when it is sourced.
343
344.. important::
345
346 The script needs to be run from the <TF-M root>/tools/irq_test directory
347 as the 'current working dir' is added as module search path.
348
349A typical execution of the script in GDB would look like the following:
350
351.. code::
352
Mate Toth-Palf4b87ba2020-06-05 21:18:13 +0200353 (gdb) target remote localhost: 2331
354 (gdb) add-symbol-file /path/to/binaries/tfm_s.axf 0x10000000
355 (gdb) add-symbol-file /path/to/binaries/tfm_ns.axf 0x00040000
Mate Toth-Pala8797e12020-06-05 21:14:26 +0200356 (gdb) source /path/to/script/irq_test.py
Mate Toth-Palf4b87ba2020-06-05 21:18:13 +0200357 (gdb) test_irq -q example/irqs_LPC55S69.json -b example/breakpoints.json -t example/testcase.json
Mate Toth-Pala8797e12020-06-05 21:14:26 +0200358
359.. note::
360 ``add-symbol-file`` command is used above as other commands like ``file``
361 and ``symbol-file`` seem to be dropping the previously loaded symbols. The
362 addresses the axf files are loaded at are depending on the platform they
363 are built to. The address needs to be specified is the start of the code
364 section
365
366**********************
367Implementation details
368**********************
369
370Class hierarchy:
371
372.. uml::
373
374 @startuml
375
376 class gdb.Command
377 note right: Library provided by GDB
378
379 class TestIRQsCommand
380 note right: Only used in case debugger is GDB
381
382 gdb.Command <|.. TestIRQsCommand : implements
383
384 TestIRQsCommand o-- TestExecutor : Creates >
385
386 "<Main>" o-- TestExecutor : Creates >
387 note right on link
388 Only if running in Arm DS
389 end note
390
391 TestExecutor o-- AbstractDebugger : has a concrete >
392
393 AbstractDebugger <|.. GDBDebugger : implements
394 AbstractDebugger <|.. DummyDebugger : implements
395 AbstractDebugger <|.. ArmDSDebugger : implements
396
397 GDBDebugger o-- Breakpoint : has multiple >
398
399 GDBDebugger o-- Location : has multiple >
400 DummyDebugger o-- Location : has multiple >
401 ArmDSDebugger o-- Location : has multiple >
402
403 @enduml
404
405
406*****************************
407Possible further improvements
408*****************************
409
410- Add priority property to the IRQs data file
411- Add possibility to run randomized scenarios, to realise stress testing.
412
413
414--------------
415
416*Copyright (c) 2020, Arm Limited. All rights reserved.*