blob: 28d40b892b249548a697bab5aea4d721716beb66 [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
Elena Uziunaiteb90a3402023-11-13 16:24:28 +0000278.. code-block:: console
279
280 $ cd tools/irq_test/
281 $ python3 tools/generate_breakpoints.py ../.. example/breakpoints.json
Mate Toth-Palf4b87ba2020-06-05 21:18:13 +0200282
283The example also require the regression test suite being present in the TF-M
284binary, so either ``ConfigRegressionIPC.cmake`` or ``ConfigRegression.cmake``
285have to be used to compile TF-M. Also ``-DCMAKE_BUILD_TYPE=Debug`` config option
286have to be used in the cmake generation command, to be sure that the debug
287information is generated in the axf files.
288
289The sequence of running the testcase in the ``example`` folder looks like the
290following:
291
292#. Check out a version of TF-M that contains the ``IRQ_TEST_TOOL_*`` macros for
293 the testcase
294#. Generate breakpoints.json using the TF-M working copy above
295#. Build TF-M checked out above
296#. Start the debugger, connect to the target, and stop the target. (Make sure
297 that the target is stopped before the IRQ testcase of the positive core test
298 suite in TF-M starts executing, as the IRQ test tool's testcase uses the
299 entry of that TF-M test as a trigger to start.)
300#. Execute the script. The script automatically sets the breakpoint for the
301 first step of the testcase, and continues the target execution.
302#. Examine the output of the script. Successful execution is signalled by the
303 following output line:
304
305 .. code::
306
307 ===== INFO: All the steps in the test file are executed successfully with the expected result.
308
309
310
Mate Toth-Pala8797e12020-06-05 21:14:26 +0200311Arm Development Studio
312======================
313
314The script can be called directly from the debugger's command window:
315
Mate Toth-Palf4b87ba2020-06-05 21:18:13 +0200316.. note::
317
318 In the command absolute path have to be used both for the ``irq_test.py``
319 and for the parameters.
320
Mate Toth-Pala8797e12020-06-05 21:14:26 +0200321.. code:: shell
322
Mate Toth-Palf4b87ba2020-06-05 21:18:13 +0200323 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 +0200324
325GDB
326===
327
328The script should be sourced inside GDB, without passing any arguments to
329it.
330
331.. code:: shell
332
333 (gdb) source irq_test.py
334
335
336That registers a custom command ``test_irq``. ``test_irq`` should be called
337with three parameters: breakpoints, irqs, and the test file. This command will
338actually execute the tests.
339
340.. note::
341
342 This indirection in case of GDB is necessary because it is not possible to
343 pass parameters to the script when it is sourced.
344
345.. important::
346
347 The script needs to be run from the <TF-M root>/tools/irq_test directory
348 as the 'current working dir' is added as module search path.
349
350A typical execution of the script in GDB would look like the following:
351
352.. code::
353
Mate Toth-Palf4b87ba2020-06-05 21:18:13 +0200354 (gdb) target remote localhost: 2331
355 (gdb) add-symbol-file /path/to/binaries/tfm_s.axf 0x10000000
356 (gdb) add-symbol-file /path/to/binaries/tfm_ns.axf 0x00040000
Mate Toth-Pala8797e12020-06-05 21:14:26 +0200357 (gdb) source /path/to/script/irq_test.py
Mate Toth-Palf4b87ba2020-06-05 21:18:13 +0200358 (gdb) test_irq -q example/irqs_LPC55S69.json -b example/breakpoints.json -t example/testcase.json
Mate Toth-Pala8797e12020-06-05 21:14:26 +0200359
360.. note::
361 ``add-symbol-file`` command is used above as other commands like ``file``
362 and ``symbol-file`` seem to be dropping the previously loaded symbols. The
363 addresses the axf files are loaded at are depending on the platform they
364 are built to. The address needs to be specified is the start of the code
365 section
366
367**********************
368Implementation details
369**********************
370
371Class hierarchy:
372
373.. uml::
374
375 @startuml
376
377 class gdb.Command
378 note right: Library provided by GDB
379
380 class TestIRQsCommand
381 note right: Only used in case debugger is GDB
382
383 gdb.Command <|.. TestIRQsCommand : implements
384
385 TestIRQsCommand o-- TestExecutor : Creates >
386
387 "<Main>" o-- TestExecutor : Creates >
388 note right on link
389 Only if running in Arm DS
390 end note
391
392 TestExecutor o-- AbstractDebugger : has a concrete >
393
394 AbstractDebugger <|.. GDBDebugger : implements
395 AbstractDebugger <|.. DummyDebugger : implements
396 AbstractDebugger <|.. ArmDSDebugger : implements
397
398 GDBDebugger o-- Breakpoint : has multiple >
399
400 GDBDebugger o-- Location : has multiple >
401 DummyDebugger o-- Location : has multiple >
402 ArmDSDebugger o-- Location : has multiple >
403
404 @enduml
405
406
407*****************************
408Possible further improvements
409*****************************
410
411- Add priority property to the IRQs data file
412- Add possibility to run randomized scenarios, to realise stress testing.
413
414
415--------------
416
417*Copyright (c) 2020, Arm Limited. All rights reserved.*