Arrow / Mbed OS DAPLink Reset
Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers test_daplink.py Source File

test_daplink.py

00001 #
00002 # DAPLink Interface Firmware
00003 # Copyright (c) 2009-2016, ARM Limited, All Rights Reserved
00004 # SPDX-License-Identifier: Apache-2.0
00005 #
00006 # Licensed under the Apache License, Version 2.0 (the "License"); you may
00007 # not use this file except in compliance with the License.
00008 # You may obtain a copy of the License at
00009 #
00010 # http://www.apache.org/licenses/LICENSE-2.0
00011 #
00012 # Unless required by applicable law or agreed to in writing, software
00013 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
00014 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015 # See the License for the specific language governing permissions and
00016 # limitations under the License.
00017 #
00018 
00019 from __future__ import absolute_import
00020 import future
00021 import os
00022 import binascii
00023 import intelhex
00024 import sys
00025 
00026 try:
00027     from cStringIO import StringIO
00028 except ImportError:
00029     from io import StringIO
00030 
00031 from msd_test import (MassStorageTester, MOCK_DIR_LIST, MOCK_FILE_LIST,
00032                       MOCK_DIR_LIST_AFTER, MOCK_FILE_LIST_AFTER)
00033 
00034 TRIGGER_ASSERT_FILE_NAME = "ASSERT.ACT"
00035 ASSERT_FILE_NAME = "ASSERT.TXT"
00036 NEED_BL_FILE_NAME = "NEED_BL.TXT"
00037 DAPLINK_VECTOR_TABLE_OFFSET = 0x00
00038 DAPLINK_BUILD_KEY_OFFSET = 0x20
00039 DAPLINK_HIC_ID_OFFSET = 0x24
00040 
00041 
00042 def intel_hex_get_sections(intel_hex):
00043     """Return list of address, size tuples"""
00044     addresses = intel_hex.addresses()
00045     addresses.sort()
00046     section_list = []
00047     section_start = None
00048     last_addr = None
00049     for addr in addresses:
00050         if section_start is None:
00051             section_start = addr
00052             last_addr = addr
00053         if addr == last_addr or addr == last_addr + 1:
00054             last_addr = addr
00055         else:
00056             section_size = last_addr - section_start + 1
00057             section_list.append((section_start, section_size))
00058             section_start = addr
00059             last_addr = addr
00060     if section_start is not None:
00061         section_size = last_addr - section_start + 1
00062         section_list.append((section_start, section_size))
00063     return section_list
00064 
00065 
00066 def bin_data_to_hex_data(addr, data):
00067     """Covert binary data to a string in intel hex format"""
00068     intel_hex = intelhex.IntelHex()
00069     if sys.version_info >= (3,0):
00070         data = data.decode('latin1')
00071     intel_hex.puts(addr, data)
00072     sio = StringIO()
00073     intel_hex.tofile(sio, format='hex')
00074     hex_data = sio.getvalue()
00075     return bytearray(hex_data.encode('latin1'))
00076 
00077 
00078 class DLMassStorageTester (MassStorageTester):
00079     """DAPLink mass storage tester"""
00080 
00081     def __init__(self, board, parent_test, test_name, test_mode):
00082         super(DLMassStorageTester, self).__init__(board, parent_test,
00083                                                   test_name)
00084         self._expected_mode  = None
00085         self._actual_mode  = None
00086         self._test_mode  = test_mode
00087         if self._test_mode  == board.MODE_IF:
00088             self._crc_tag  = board.KEY_BL_CRC
00089         elif self._test_mode  == board.MODE_BL:
00090             self._crc_tag  = board.KEY_IF_CRC
00091         else:
00092             assert False
00093 
00094     def _run(self, test_info):
00095         assert self._expected_mode  is not None
00096         # Set board to the correct mode before running test
00097         self.board.set_mode(self._test_mode )
00098         self._actual_mode  = None
00099 
00100         super(DLMassStorageTester, self)._run(test_info)
00101 
00102         if self._actual_mode  is None:
00103             # Set expected mode if it hasn't been set in _check_data_correct
00104             self._actual_mode  = self.board.get_mode()
00105         if self._expected_mode  is not self._actual_mode :
00106             test_info.failure("Wrong mode after test - Expected "
00107                               " %s got %s" % (self._expected_mode ,
00108                                               self._actual_mode ))
00109 
00110     def set_expected_mode(self, mode):
00111         self._expected_mode  = mode
00112 
00113     def _check_data_correct(self, expected_data, test_info):
00114         board = self.board
00115         self._actual_mode  = self.board.get_mode()
00116         board.set_mode(self._test_mode )
00117         if self._crc_tag  not in board.details_txt:
00118             test_info.info("CRC not in details.txt")
00119             return False
00120         actual_crc32 = int(self.board.details_txt[self._crc_tag ], 16)
00121         expected_crc32 = binascii.crc32(expected_data[0:-4]) & 0xFFFFFFFF
00122         test_info.info("Expected CRC: 0x%08x, actual crc: 0x%08x" %
00123                        (expected_crc32, actual_crc32))
00124         return actual_crc32 == expected_crc32
00125 
00126 
00127 def daplink_test(workspace, parent_test):
00128     board = workspace.board
00129     interface = workspace.if_firmware
00130     test_info = parent_test.create_subtest('daplink_test')
00131 
00132     intel_hex = intelhex.IntelHex(interface.hex_path)
00133     section_list = intel_hex_get_sections(intel_hex)
00134     assert len(section_list) == 1, ("Only 1 section supported, found %s" %
00135                                     len(section_list))
00136     start, length = section_list[0]
00137 
00138     bin_data = bytearray(intel_hex.tobinarray(start=start, size=length))    
00139     sio = StringIO()
00140     intel_hex.tofile(sio, format='hex')
00141     hex_data = sio.getvalue()
00142     hex_data = bytearray(hex_data.encode('latin1'))
00143 
00144     # Make sure asserts work as expected
00145     test_assert(workspace, test_info)
00146 
00147     # Test loading a binary file with shutils
00148     test = DLMassStorageTester(board, test_info, "Shutil binary file load "
00149                                "interface", board.MODE_BL)
00150     test.set_shutils_copy(interface.bin_path)
00151     test.set_expected_data(bin_data, start)
00152     test.set_expected_mode(board.MODE_IF)
00153     test.run()
00154 
00155     # Test loading a hex file with shutils
00156     test = DLMassStorageTester(board, test_info, "Shutil hex file load "
00157                                "interface", board.MODE_BL)
00158     test.set_shutils_copy(interface.hex_path)
00159     test.set_expected_data(bin_data, start)
00160     test.set_expected_mode(board.MODE_IF)
00161     test.run()
00162 
00163     test_file_type('bin', board.MODE_BL, board, test_info, start, bin_data)
00164     test_file_type('hex', board.MODE_BL, board, test_info, start, bin_data)
00165 
00166     # Test bootloader updates
00167     firmware = workspace.bl_firmware
00168     intel_hex = intelhex.IntelHex(firmware.hex_path)
00169     section_list = intel_hex_get_sections(intel_hex)
00170     assert len(section_list) == 1, ("Only 1 section supported, found %s" %
00171                                     len(section_list))
00172     start, length = section_list[0]
00173 
00174     bin_data = bytearray(intel_hex.tobinarray(start=start, size=length))
00175     sio = StringIO()
00176     intel_hex.tofile(sio, format='hex')
00177     hex_data = sio.getvalue()
00178     hex_data = bytearray(hex_data.encode('latin1'))
00179 
00180     # Test loading a binary file with shutils
00181     test = DLMassStorageTester(board, test_info, "Shutil binary file load "
00182                                "bootloader", board.MODE_IF)
00183     test.set_shutils_copy(firmware.bin_path)
00184     test.set_expected_data(bin_data, start)
00185     test.set_expected_mode(board.MODE_IF)
00186     test.run()
00187 
00188     # Test loading a hex file with shutils
00189     test = DLMassStorageTester(board, test_info, "Shutil hex file load "
00190                                "bootloader", board.MODE_IF)
00191     test.set_shutils_copy(firmware.hex_path)
00192     test.set_expected_data(bin_data, start)
00193     test.set_expected_mode(board.MODE_IF)
00194     test.run()
00195 
00196     test_file_type('bin', board.MODE_IF, board, test_info, start, bin_data)
00197     test_file_type('hex', board.MODE_IF, board, test_info, start, bin_data)
00198 
00199 
00200 def test_assert(workspace, parent_test):
00201     """Test that asserts work and persist across modes"""
00202     board = workspace.board
00203     test_info = parent_test.create_subtest('Assert Test')
00204 
00205     board.set_assert_auto_manage(False)
00206 
00207     board.set_mode(board.MODE_IF)
00208 
00209     # Create a test assert file
00210     test_info.info('Triggering assert by creating %s' %
00211                    TRIGGER_ASSERT_FILE_NAME)
00212     trigger_assert_path = board.get_file_path(TRIGGER_ASSERT_FILE_NAME)
00213     with open(trigger_assert_path, 'wb') as _:
00214         pass
00215     board.wait_for_remount(test_info)
00216 
00217     test_info.info('Checking that assert file was created')
00218     board.set_mode(board.MODE_IF)
00219     assert_path = board.get_file_path(ASSERT_FILE_NAME)
00220     if not os.path.exists(assert_path):
00221         test_info.failure('Assert file not created')
00222 
00223     test_info.info('Checking that assert file persists if->bl')
00224     board.set_mode(board.MODE_BL)
00225     assert_path = board.get_file_path(ASSERT_FILE_NAME)
00226     if not os.path.exists(assert_path):
00227         test_info.failure('Assert file not created')
00228 
00229     test_info.info('Checking that assert file persists bl->if')
00230     board.set_mode(board.MODE_IF)
00231     assert_path = board.get_file_path(ASSERT_FILE_NAME)
00232     if not os.path.exists(assert_path):
00233         test_info.failure('Assert file not created')
00234 
00235     board.clear_assert()
00236 
00237     board.set_mode(board.MODE_IF)
00238     assert_path = board.get_file_path(ASSERT_FILE_NAME)
00239     if os.path.exists(assert_path):
00240         test_info.failure('Assert file not cleared correctly')
00241 
00242     board.set_mode(board.MODE_BL)
00243     assert_path = board.get_file_path(ASSERT_FILE_NAME)
00244     if os.path.exists(assert_path):
00245         test_info.failure('Assert file not cleared correctly')
00246 
00247     board.set_mode(board.MODE_IF)
00248     assert_path = board.get_file_path(ASSERT_FILE_NAME)
00249     if os.path.exists(assert_path):
00250         test_info.failure('Assert file not cleared correctly')
00251 
00252     board.set_assert_auto_manage(True)
00253 
00254 
00255 def test_file_type(file_type, board_mode, board, parent_test,
00256                    data_start, raw_data):
00257     """Test updates of a given file type using the given mode"""
00258     assert file_type in ('hex', 'bin'), 'Unsupported file type %s' % file_type
00259 
00260     if board_mode == board.MODE_IF:
00261         data_type = board.MODE_BL
00262     elif board_mode == board.MODE_BL:
00263         data_type = board.MODE_IF
00264     else:
00265         assert False
00266 
00267     test_info = parent_test.create_subtest('%s %s filetype test' %
00268                                            (file_type, data_type))
00269 
00270     def get_file_name(base='image'):
00271         """Get the file name to be used for loading"""
00272         return base + '.' + file_type
00273 
00274     def get_file_content(addr, bin_data):
00275         """Get the file contents to be used for loading"""
00276         if file_type == 'bin':
00277             return bytearray(bin_data)  # Make a copy
00278         elif file_type == 'hex':
00279             return bin_data_to_hex_data(addr, bin_data)
00280 
00281     # Test partial update
00282     file_name = get_file_name()
00283     local_data = get_file_content(data_start, raw_data[0:len(raw_data) // 2])
00284     test = DLMassStorageTester(board, test_info, "Load partial",
00285                                board_mode)
00286     test.set_programming_data(local_data, file_name)
00287     test.set_expected_data(None)
00288     test.set_expected_failure_msg("In application programming failed because "
00289                                   "the update sent was incomplete.", "interface")
00290     test.set_expected_mode(board_mode)
00291     test.run()
00292     # If bootloader is missing then this should be indicated by a file
00293     if board_mode == board.MODE_IF:
00294         if not os.path.isfile(board.get_file_path(NEED_BL_FILE_NAME)):
00295             test_info.failure("Bootloader missing but file %s not present" %
00296                               NEED_BL_FILE_NAME)
00297         test_info.info("Testing switch to bootloader")
00298         try:
00299             board.set_mode(board.MODE_BL)
00300             test_info.failure("Board switched to bootloader mode")
00301         except Exception:
00302             pass
00303         finally:
00304             if board.get_mode() == board.MODE_IF:
00305                 test_info.info("Device able to recover from bad BL")
00306             else:
00307                 test_info.failure("Device in wrong mode")
00308 
00309     # Test loading a normal image
00310     file_name = get_file_name()
00311     local_data = get_file_content(data_start, raw_data)
00312     test = DLMassStorageTester(board, test_info, "Normal Load",
00313                                board_mode)
00314     test.set_programming_data(local_data, file_name)
00315     test.set_expected_data(raw_data)
00316     test.set_expected_mode(board.MODE_IF)
00317     test.run()
00318 
00319     # Wrong starting address
00320     if file_type != 'bin':
00321         mode_to_error = {
00322             board.MODE_IF: ('The starting address for the bootloader '
00323                             'update is wrong.'),
00324             board.MODE_BL: ('The starting address for the interface '
00325                             'update is wrong.')
00326         }
00327         file_name = get_file_name()
00328         local_data = get_file_content(data_start + 0x400, raw_data)
00329         test = DLMassStorageTester(board, test_info, "Wrong Address",
00330                                    board_mode)
00331         test.set_expected_failure_msg(mode_to_error[board_mode], 'user')
00332         test.set_programming_data(local_data, file_name)
00333         test.set_expected_data(raw_data)
00334         test.set_expected_mode(board_mode)
00335         test.run()
00336 
00337     # Test flushes during update
00338     file_name = get_file_name()
00339     local_data = get_file_content(data_start, raw_data)
00340     test = DLMassStorageTester(board, test_info, "Load with flushes",
00341                                board_mode)
00342     test.set_programming_data(local_data, file_name)
00343     test.set_expected_data(raw_data)
00344     test.set_flush_size(0x1000)
00345     test.set_expected_mode(board.MODE_IF)
00346     test.run()
00347 
00348     # Test bad crc
00349     file_name = get_file_name()
00350     local_raw_data = bytearray(raw_data)
00351     local_raw_data[-1] = (local_raw_data[-1] + 1) % 0x100  # Corrupt CRC
00352     local_data = get_file_content(data_start, local_raw_data)
00353     test = DLMassStorageTester(board, test_info, 'Wrong CRC',
00354                                board_mode)
00355     test.set_programming_data(local_data, file_name)
00356     if board_mode == board.MODE_IF:
00357         test.set_expected_failure_msg('The bootloader CRC did not pass.', 'interface')
00358         test.set_expected_data(None)
00359     elif board_mode == board.MODE_BL:
00360         # Interface images can be from other vendors and be missing
00361         # the crc, so don't treat this as an error
00362         test.set_expected_data(local_raw_data)
00363     test.set_expected_mode(board.MODE_IF)
00364     test.run()
00365     # If bootloader is missing then this should be indicated by a file
00366     if (board_mode == board.MODE_IF and
00367             not os.path.isfile(board.get_file_path(NEED_BL_FILE_NAME))):
00368         test_info.failure("Bootloader missing but file %s not present" %
00369                           NEED_BL_FILE_NAME)
00370 
00371     # Test load with extra padding
00372     file_name = get_file_name()
00373     local_data = get_file_content(data_start, raw_data)
00374     local_data.extend(b'\xFF' * 0x1000)
00375     test = DLMassStorageTester(board, test_info, "Padded load", board_mode)
00376     test.set_programming_data(local_data, file_name)
00377     test.set_expected_data(raw_data)
00378     test.set_expected_mode(board.MODE_IF)
00379     test.run()
00380 
00381     # Test bad crc in file data
00382     # Note - crc is only a requirment for loading bootloades
00383     if board_mode == board.MODE_IF:
00384         file_name = get_file_name()
00385         local_raw_data = bytearray(raw_data)
00386         local_raw_data[0x100] = (local_raw_data[0x100] + 1) % 0x100  # Corrupt CRC
00387         local_data = get_file_content(data_start, local_raw_data)
00388         test = DLMassStorageTester(board, test_info, 'Wrong data CRC',
00389                                    board_mode)
00390         test.set_programming_data(local_data, file_name)
00391         test.set_expected_failure_msg('The bootloader CRC did not pass.', 'interface')
00392         test.set_expected_data(None)
00393         test.set_expected_mode(board.MODE_IF)
00394         test.run()
00395         # If bootloader is missing then this should be indicated by a file
00396         if not os.path.isfile(board.get_file_path(NEED_BL_FILE_NAME)):
00397             test_info.failure("Bootloader missing but file %s not present" %
00398                               NEED_BL_FILE_NAME)
00399 
00400         # Restore a good image
00401         file_name = get_file_name()
00402         local_data = get_file_content(data_start, raw_data)
00403         test = DLMassStorageTester(board, test_info, "Normal Load",
00404                                    board_mode)
00405         test.set_programming_data(local_data, file_name)
00406         test.set_expected_data(raw_data)
00407         test.set_expected_mode(board.MODE_IF)
00408         test.run()
00409 
00410     # Test wrong HIC ID
00411     # Bootloader should perform interface update regardless of key
00412     if data_type == board.MODE_IF:
00413         local_raw_data = bytearray(raw_data)
00414         local_raw_data[DAPLINK_HIC_ID_OFFSET] = \
00415             (local_raw_data[DAPLINK_HIC_ID_OFFSET] + 1) % 0x100
00416         file_name = get_file_name()
00417         local_data = get_file_content(data_start, local_raw_data)
00418         test = DLMassStorageTester(board, test_info, "Wrong HIC ID",
00419                                    board_mode)
00420         test.set_programming_data(local_data, file_name)
00421         test.set_expected_data(local_raw_data)
00422         test.set_expected_mode(board.MODE_IF)
00423         test.run()
00424 
00425     # TODO future - Wrong type
00426 
00427     # Test a normal load with dummy files created beforehand
00428     file_name = get_file_name()
00429     local_data = get_file_content(data_start, raw_data)
00430     test = DLMassStorageTester(board, test_info, "Extra Files", board_mode)
00431     test.set_programming_data(local_data, file_name)
00432     test.add_mock_dirs(MOCK_DIR_LIST)
00433     test.add_mock_files(MOCK_FILE_LIST)
00434     test.add_mock_dirs_after_load(MOCK_DIR_LIST_AFTER)
00435     test.add_mock_files_after_load(MOCK_FILE_LIST_AFTER)
00436     test.set_expected_data(raw_data)
00437     test.set_expected_mode(board.MODE_IF)
00438     test.run()
00439 
00440     # Restore good image
00441     file_name = get_file_name()
00442     local_data = get_file_content(data_start, raw_data)
00443     test = DLMassStorageTester(board, test_info, "Restore image",
00444                                board_mode)
00445     test.set_programming_data(local_data, file_name)
00446     test.set_expected_data(raw_data)
00447     test.set_expected_mode(board.MODE_IF)
00448     test.run()
00449