1# Copyright (c) 2018 Foundries.io
2# Copyright (c) 2019 Nordic Semiconductor ASA.
3# Copyright (c) 2020-2021 Gerson Fernando Budke <nandojve@gmail.com>
4#
5# SPDX-License-Identifier: Apache-2.0
6
7import argparse
8import os
9import platform
10from unittest.mock import patch, call
11
12import pytest
13
14from runners.bossac import BossacBinaryRunner
15from conftest import RC_KERNEL_BIN
16
17if platform.system() != 'Linux':
18    pytest.skip("skipping Linux-only bossac tests", allow_module_level=True)
19
20TEST_BOSSAC_PORT = 'test-bossac-serial'
21TEST_BOSSAC_SPEED = '1200'
22TEST_OFFSET = 1234
23TEST_FLASH_ADDRESS = 5678
24TEST_BOARD_NAME = "my_board"
25
26EXPECTED_COMMANDS = [
27    ['stty', '-F', TEST_BOSSAC_PORT, 'raw', 'ispeed', '115200',
28     'ospeed', '115200', 'cs8', '-cstopb', 'ignpar', 'eol', '255',
29     'eof', '255'],
30    ['bossac', '-p', TEST_BOSSAC_PORT, '-R', '-e', '-w', '-v',
31     '-b', RC_KERNEL_BIN],
32]
33
34EXPECTED_COMMANDS_WITH_SPEED = [
35    ['stty', '-F', TEST_BOSSAC_PORT, 'raw', 'ispeed', TEST_BOSSAC_SPEED,
36     'ospeed', TEST_BOSSAC_SPEED, 'cs8', '-cstopb', 'ignpar', 'eol', '255',
37     'eof', '255'],
38    ['bossac', '-p', TEST_BOSSAC_PORT, '-R', '-e', '-w', '-v',
39     '-b', RC_KERNEL_BIN],
40]
41
42EXPECTED_COMMANDS_WITH_OFFSET = [
43    ['stty', '-F', TEST_BOSSAC_PORT, 'raw', 'ispeed', '115200',
44     'ospeed', '115200', 'cs8', '-cstopb', 'ignpar', 'eol', '255',
45     'eof', '255'],
46    ['bossac', '-p', TEST_BOSSAC_PORT, '-R', '-e', '-w', '-v',
47     '-b', RC_KERNEL_BIN, '-o', str(TEST_OFFSET)],
48]
49
50EXPECTED_COMMANDS_WITH_FLASH_ADDRESS = [
51    [
52        'stty', '-F', TEST_BOSSAC_PORT, 'raw', 'ispeed', '115200',
53        'ospeed', '115200', 'cs8', '-cstopb', 'ignpar', 'eol', '255',
54        'eof', '255'
55    ],
56    [
57        'bossac', '-p', TEST_BOSSAC_PORT, '-R', '-e', '-w', '-v',
58        '-b', RC_KERNEL_BIN, '-o', str(TEST_FLASH_ADDRESS),
59    ],
60]
61
62EXPECTED_COMMANDS_WITH_EXTENDED = [
63    [
64        'stty', '-F', TEST_BOSSAC_PORT, 'raw', 'ispeed', '1200',
65        'ospeed', '1200', 'cs8', '-cstopb', 'ignpar', 'eol', '255',
66        'eof', '255'
67    ],
68    [
69        'bossac', '-p', TEST_BOSSAC_PORT, '-R', '-e', '-w', '-v',
70        '-b', RC_KERNEL_BIN, '-o', str(TEST_FLASH_ADDRESS),
71    ],
72]
73
74# SAM-BA ROM without offset
75# No code partition Kconfig
76# No zephyr,code-partition (defined on DT)
77DOTCONFIG_STD = f'''
78CONFIG_BOARD="{TEST_BOARD_NAME}"
79CONFIG_FLASH_LOAD_OFFSET=0x162e
80'''
81
82# SAM-BA ROM/FLASH with offset
83DOTCONFIG_COND1 = f'''
84CONFIG_BOARD="{TEST_BOARD_NAME}"
85CONFIG_USE_DT_CODE_PARTITION=y
86CONFIG_HAS_FLASH_LOAD_OFFSET=y
87CONFIG_FLASH_LOAD_OFFSET=0x162e
88'''
89
90# SAM-BA ROM/FLASH without offset
91# No code partition Kconfig
92DOTCONFIG_COND2 = f'''
93CONFIG_BOARD="{TEST_BOARD_NAME}"
94CONFIG_HAS_FLASH_LOAD_OFFSET=y
95CONFIG_FLASH_LOAD_OFFSET=0x162e
96'''
97
98# SAM-BA Extended Arduino with offset
99DOTCONFIG_COND3 = f'''
100CONFIG_BOARD="{TEST_BOARD_NAME}"
101CONFIG_USE_DT_CODE_PARTITION=y
102CONFIG_BOOTLOADER_BOSSA_ARDUINO=y
103CONFIG_HAS_FLASH_LOAD_OFFSET=y
104CONFIG_FLASH_LOAD_OFFSET=0x162e
105'''
106
107# SAM-BA Extended Adafruit with offset
108DOTCONFIG_COND4 = f'''
109CONFIG_BOARD="{TEST_BOARD_NAME}"
110CONFIG_USE_DT_CODE_PARTITION=y
111CONFIG_BOOTLOADER_BOSSA_ADAFRUIT_UF2=y
112CONFIG_HAS_FLASH_LOAD_OFFSET=y
113CONFIG_FLASH_LOAD_OFFSET=0x162e
114'''
115
116# SAM-BA omit offset
117DOTCONFIG_COND5 = f'''
118CONFIG_BOARD="{TEST_BOARD_NAME}"
119CONFIG_USE_DT_CODE_PARTITION=y
120CONFIG_HAS_FLASH_LOAD_OFFSET=y
121CONFIG_FLASH_LOAD_OFFSET=0x0
122'''
123
124# SAM-BA Legacy Mode
125DOTCONFIG_COND6 = f'''
126CONFIG_BOARD="{TEST_BOARD_NAME}"
127CONFIG_USE_DT_CODE_PARTITION=y
128CONFIG_BOOTLOADER_BOSSA_LEGACY=y
129CONFIG_HAS_FLASH_LOAD_OFFSET=y
130CONFIG_FLASH_LOAD_OFFSET=0x162e
131'''
132
133def adjust_runner_config(runner_config, tmpdir, dotconfig):
134    # Adjust a RunnerConfig object, 'runner_config', by
135    # replacing its build directory with 'tmpdir' after writing
136    # the contents of 'dotconfig' to tmpdir/zephyr/.config.
137
138    zephyr = tmpdir / 'zephyr'
139    zephyr.mkdir()
140    with open(zephyr / '.config', 'w') as f:
141        f.write(dotconfig)
142    return runner_config._replace(build_dir=os.fspath(tmpdir))
143
144def require_patch(program):
145    assert program in ['bossac', 'stty']
146
147os_path_isfile = os.path.isfile
148
149def os_path_isfile_patch(filename):
150    if filename == RC_KERNEL_BIN:
151        return True
152    return os_path_isfile(filename)
153
154@patch('runners.bossac.BossacBinaryRunner.supports',
155	return_value=False)
156@patch('runners.bossac.BossacBinaryRunner.get_chosen_code_partition_node',
157	return_value=None)
158@patch('runners.core.ZephyrBinaryRunner.require',
159	side_effect=require_patch)
160@patch('runners.core.ZephyrBinaryRunner.check_call')
161def test_bossac_init(cc, req, get_cod_par, sup, runner_config, tmpdir):
162    """
163    Test commands using a runner created by constructor.
164
165    Requirements:
166	Any SDK
167
168    Configuration:
169	ROM bootloader
170	CONFIG_USE_DT_CODE_PARTITION=n
171	without zephyr,code-partition
172
173    Input:
174	none
175
176    Output:
177	no --offset
178    """
179    runner_config = adjust_runner_config(runner_config, tmpdir, DOTCONFIG_STD)
180    runner = BossacBinaryRunner(runner_config, port=TEST_BOSSAC_PORT)
181    with patch('os.path.isfile', side_effect=os_path_isfile_patch):
182        runner.run('flash')
183    assert cc.call_args_list == [call(x) for x in EXPECTED_COMMANDS]
184
185
186@patch('runners.bossac.BossacBinaryRunner.supports',
187	return_value=False)
188@patch('runners.bossac.BossacBinaryRunner.get_chosen_code_partition_node',
189	return_value=None)
190@patch('runners.core.ZephyrBinaryRunner.require',
191	side_effect=require_patch)
192@patch('runners.core.ZephyrBinaryRunner.check_call')
193def test_bossac_create(cc, req, get_cod_par, sup, runner_config, tmpdir):
194    """
195    Test commands using a runner created from command line parameters.
196
197    Requirements:
198	Any SDK
199
200    Configuration:
201	ROM bootloader
202	CONFIG_USE_DT_CODE_PARTITION=n
203	without zephyr,code-partition
204
205    Input:
206	--bossac-port
207
208    Output:
209	no --offset
210    """
211    args = ['--bossac-port', str(TEST_BOSSAC_PORT)]
212    parser = argparse.ArgumentParser()
213    BossacBinaryRunner.add_parser(parser)
214    arg_namespace = parser.parse_args(args)
215    runner_config = adjust_runner_config(runner_config, tmpdir, DOTCONFIG_STD)
216    runner = BossacBinaryRunner.create(runner_config, arg_namespace)
217    with patch('os.path.isfile', side_effect=os_path_isfile_patch):
218        runner.run('flash')
219    assert cc.call_args_list == [call(x) for x in EXPECTED_COMMANDS]
220
221
222@patch('runners.bossac.BossacBinaryRunner.supports',
223	return_value=False)
224@patch('runners.bossac.BossacBinaryRunner.get_chosen_code_partition_node',
225	return_value=None)
226@patch('runners.core.ZephyrBinaryRunner.require',
227	side_effect=require_patch)
228@patch('runners.core.ZephyrBinaryRunner.check_call')
229def test_bossac_create_with_speed(cc, req, get_cod_par, sup, runner_config, tmpdir):
230    """
231    Test commands using a runner created from command line parameters.
232
233    Requirements:
234	Any SDK
235
236    Configuration:
237	ROM bootloader
238	CONFIG_USE_DT_CODE_PARTITION=n
239	without zephyr,code-partition
240
241    Input:
242	--bossac-port
243	--speed
244
245    Output:
246	no --offset
247    """
248    args = ['--bossac-port', str(TEST_BOSSAC_PORT),
249            '--speed', str(TEST_BOSSAC_SPEED)]
250    parser = argparse.ArgumentParser()
251    BossacBinaryRunner.add_parser(parser)
252    arg_namespace = parser.parse_args(args)
253    runner_config = adjust_runner_config(runner_config, tmpdir, DOTCONFIG_STD)
254    runner = BossacBinaryRunner.create(runner_config, arg_namespace)
255    with patch('os.path.isfile', side_effect=os_path_isfile_patch):
256        runner.run('flash')
257    assert cc.call_args_list == [call(x) for x in EXPECTED_COMMANDS_WITH_SPEED]
258
259
260@patch('runners.bossac.BossacBinaryRunner.supports',
261	return_value=True)
262@patch('runners.bossac.BossacBinaryRunner.get_chosen_code_partition_node',
263	return_value=True)
264@patch('runners.core.ZephyrBinaryRunner.require',
265	side_effect=require_patch)
266@patch('runners.core.ZephyrBinaryRunner.check_call')
267def test_bossac_create_with_flash_address(cc, req, get_cod_par, sup,
268					  runner_config, tmpdir):
269    """
270    Test command with offset parameter
271
272    Requirements:
273	SDK >= 0.12.0
274
275    Configuration:
276	Any bootloader
277	CONFIG_USE_DT_CODE_PARTITION=y
278	with zephyr,code-partition
279
280    Input:
281	--bossac-port
282
283    Output:
284	--offset
285    """
286    args = [
287        '--bossac-port',
288        str(TEST_BOSSAC_PORT),
289    ]
290    parser = argparse.ArgumentParser()
291    BossacBinaryRunner.add_parser(parser)
292    arg_namespace = parser.parse_args(args)
293    runner_config = adjust_runner_config(runner_config, tmpdir,
294                                         DOTCONFIG_COND1)
295    runner = BossacBinaryRunner.create(runner_config, arg_namespace)
296    with patch('os.path.isfile', side_effect=os_path_isfile_patch):
297        runner.run('flash')
298    assert cc.call_args_list == [
299        call(x) for x in EXPECTED_COMMANDS_WITH_FLASH_ADDRESS
300    ]
301
302
303@patch('runners.bossac.BossacBinaryRunner.supports',
304	return_value=False)
305@patch('runners.bossac.BossacBinaryRunner.get_chosen_code_partition_node',
306	return_value=True)
307@patch('runners.core.ZephyrBinaryRunner.require',
308	side_effect=require_patch)
309@patch('runners.core.ZephyrBinaryRunner.check_call')
310def test_bossac_create_with_omit_address(cc, req, bcfg_ini, sup,
311                                         runner_config, tmpdir):
312    """
313    Test command that will omit offset because CONFIG_FLASH_LOAD_OFFSET is 0.
314    This case is valid for ROM bootloaders that define image start at 0 and
315    define flash partitions, to use the storage capabilities, for instance.
316
317    Requirements:
318	Any SDK
319
320    Configuration:
321	ROM bootloader
322	CONFIG_USE_DT_CODE_PARTITION=y
323	with zephyr,code-partition
324
325    Input:
326	--bossac-port
327
328    Output:
329	no --offset
330    """
331    runner_config = adjust_runner_config(runner_config, tmpdir,
332                                         DOTCONFIG_COND5)
333    runner = BossacBinaryRunner(runner_config, port=TEST_BOSSAC_PORT)
334    with patch('os.path.isfile', side_effect=os_path_isfile_patch):
335        runner.run('flash')
336    assert cc.call_args_list == [call(x) for x in EXPECTED_COMMANDS]
337
338
339@patch('runners.bossac.BossacBinaryRunner.supports',
340	return_value=True)
341@patch('runners.bossac.BossacBinaryRunner.get_chosen_code_partition_node',
342	return_value=True)
343@patch('runners.core.ZephyrBinaryRunner.require',
344	side_effect=require_patch)
345@patch('runners.core.ZephyrBinaryRunner.check_call')
346def test_bossac_create_with_arduino(cc, req, get_cod_par, sup,
347				    runner_config, tmpdir):
348    """
349    Test SAM-BA extended protocol with Arduino variation
350
351    Requirements:
352	SDK >= 0.12.0
353
354    Configuration:
355	Extended bootloader
356	CONFIG_USE_DT_CODE_PARTITION=y
357	CONFIG_BOOTLOADER_BOSSA_ARDUINO=y
358	with zephyr,code-partition
359
360    Input:
361	--bossac-port
362
363    Output:
364	--offset
365    """
366    runner_config = adjust_runner_config(runner_config, tmpdir,
367                                         DOTCONFIG_COND3)
368    runner = BossacBinaryRunner(runner_config, port=TEST_BOSSAC_PORT)
369    with patch('os.path.isfile', side_effect=os_path_isfile_patch):
370        runner.run('flash')
371    assert cc.call_args_list == [call(x) for x in EXPECTED_COMMANDS_WITH_EXTENDED]
372
373@patch('runners.bossac.BossacBinaryRunner.supports',
374	return_value=True)
375@patch('runners.bossac.BossacBinaryRunner.get_chosen_code_partition_node',
376	return_value=True)
377@patch('runners.core.ZephyrBinaryRunner.require',
378	side_effect=require_patch)
379@patch('runners.core.ZephyrBinaryRunner.check_call')
380def test_bossac_create_with_adafruit(cc, req, get_cod_par, sup,
381				     runner_config, tmpdir):
382    """
383    Test SAM-BA extended protocol with Adafruit UF2 variation
384
385    Requirements:
386	SDK >= 0.12.0
387
388    Configuration:
389	Extended bootloader
390	CONFIG_USE_DT_CODE_PARTITION=y
391	CONFIG_BOOTLOADER_BOSSA_ADAFRUIT_UF2=y
392	with zephyr,code-partition
393
394    Input:
395	--bossac-port
396
397    Output:
398	--offset
399    """
400    runner_config = adjust_runner_config(runner_config, tmpdir,
401                                         DOTCONFIG_COND4)
402    runner = BossacBinaryRunner(runner_config, port=TEST_BOSSAC_PORT)
403    with patch('os.path.isfile', side_effect=os_path_isfile_patch):
404        runner.run('flash')
405    assert cc.call_args_list == [call(x) for x in EXPECTED_COMMANDS_WITH_EXTENDED]
406
407
408@patch('runners.bossac.BossacBinaryRunner.supports',
409	return_value=True)
410@patch('runners.bossac.BossacBinaryRunner.get_chosen_code_partition_node',
411	return_value=True)
412@patch('runners.core.ZephyrBinaryRunner.require',
413	side_effect=require_patch)
414@patch('runners.core.ZephyrBinaryRunner.check_call')
415def test_bossac_create_with_legacy(cc, req, get_cod_par, sup,
416				   runner_config, tmpdir):
417    """
418    Test SAM-BA legacy protocol
419
420    Requirements:
421	Any SDK
422
423    Configuration:
424	Extended bootloader
425	CONFIG_USE_DT_CODE_PARTITION=y
426	CONFIG_BOOTLOADER_BOSSA_LEGACY=y
427	with zephyr,code-partition
428
429    Input:
430	--bossac-port
431
432    Output:
433	no --offset
434    """
435    runner_config = adjust_runner_config(runner_config, tmpdir,
436                                         DOTCONFIG_COND6)
437    runner = BossacBinaryRunner(runner_config, port=TEST_BOSSAC_PORT)
438    with patch('os.path.isfile', side_effect=os_path_isfile_patch):
439        runner.run('flash')
440    assert cc.call_args_list == [call(x) for x in EXPECTED_COMMANDS]
441
442
443@patch('runners.bossac.BossacBinaryRunner.supports',
444	return_value=False)
445@patch('runners.bossac.BossacBinaryRunner.get_chosen_code_partition_node',
446	return_value=True)
447@patch('runners.core.ZephyrBinaryRunner.require',
448	side_effect=require_patch)
449@patch('runners.core.ZephyrBinaryRunner.check_call')
450def test_bossac_create_with_oldsdk(cc, req, get_cod_par, sup,
451				   runner_config, tmpdir):
452    """
453    Test old SDK and ask user to upgrade
454
455    Requirements:
456	SDK <= 0.12.0
457
458    Configuration:
459	Any bootloader
460	CONFIG_USE_DT_CODE_PARTITION=y
461	with zephyr,code-partition
462
463    Input:
464
465    Output:
466	Abort
467    """
468    runner_config = adjust_runner_config(runner_config, tmpdir,
469                                         DOTCONFIG_COND1)
470    runner = BossacBinaryRunner(runner_config)
471    with pytest.raises(RuntimeError) as rinfo:
472        with patch('os.path.isfile', side_effect=os_path_isfile_patch):
473            runner.run('flash')
474    assert str(rinfo.value) == "This version of BOSSA does not support the" \
475                               " --offset flag. Please upgrade to a newer" \
476                               " Zephyr SDK version >= 0.12.0."
477
478
479@patch('runners.bossac.BossacBinaryRunner.supports',
480	return_value=False)
481@patch('runners.bossac.BossacBinaryRunner.get_chosen_code_partition_node',
482	return_value=None)
483@patch('runners.core.ZephyrBinaryRunner.require',
484	side_effect=require_patch)
485@patch('runners.core.ZephyrBinaryRunner.check_call')
486def test_bossac_create_error_missing_dt_info(cc, req, get_cod_par, sup,
487					     runner_config, tmpdir):
488    """
489    Test SAM-BA offset wrong configuration. No chosen code partition.
490
491    Requirements:
492	Any SDK
493
494    Configuration:
495	Any bootloader
496	CONFIG_USE_DT_CODE_PARTITION=y
497	with zephyr,code-partition (missing)
498
499    Input:
500
501    Output:
502	Abort
503    """
504    runner_config = adjust_runner_config(runner_config, tmpdir,
505                                         DOTCONFIG_COND1)
506    runner = BossacBinaryRunner(runner_config)
507    with pytest.raises(RuntimeError) as rinfo:
508        with patch('os.path.isfile', side_effect=os_path_isfile_patch):
509            runner.run('flash')
510    assert str(rinfo.value) == "The device tree zephyr,code-partition" \
511                               " chosen node must be defined."
512
513
514@patch('runners.bossac.BossacBinaryRunner.supports',
515	return_value=False)
516@patch('runners.bossac.BossacBinaryRunner.get_chosen_code_partition_node',
517	return_value=True)
518@patch('runners.core.ZephyrBinaryRunner.require',
519	side_effect=require_patch)
520@patch('runners.core.ZephyrBinaryRunner.check_call')
521def test_bossac_create_error_missing_kconfig(cc, req, get_cod_par, sup,
522					     runner_config, tmpdir):
523    """
524    Test SAM-BA offset wrong configuration. No CONFIG_USE_DT_CODE_PARTITION
525    Kconfig definition.
526
527    Requirements:
528	Any SDK
529
530    Configuration:
531	Any bootloader
532	CONFIG_USE_DT_CODE_PARTITION=y (missing)
533	with zephyr,code-partition
534
535    Input:
536
537    Output:
538	Abort
539    """
540    runner_config = adjust_runner_config(runner_config, tmpdir,
541                                         DOTCONFIG_COND2)
542    runner = BossacBinaryRunner(runner_config)
543    with pytest.raises(RuntimeError) as rinfo:
544        with patch('os.path.isfile', side_effect=os_path_isfile_patch):
545            runner.run('flash')
546    assert str(rinfo.value) == \
547        "There is no CONFIG_USE_DT_CODE_PARTITION Kconfig defined at " \
548        + TEST_BOARD_NAME + "_defconfig file.\n This means that" \
549        " zephyr,code-partition device tree node should not be defined." \
550        " Check Zephyr SAM-BA documentation."
551