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