示例#1
0
 def setUp(self):
     self.channel = MockChannel()
     self.rkl = ramkernel.RAMKernelProtocol(self.channel)
     # Pretend we did initialize the chip already so we
     # can test commands.
     # Ensuring that these are set will be done in separate tests.
     self.rkl._flash_init = True
     self.rkl._kernel_init = True
示例#2
0
 def setUp(self):
     self.channel = MockChannel()
     self.rkl = ramkernel.RAMKernelProtocol(self.channel)
     # Pretend we did initialize the chip already so we
     # can test commands.
     # Ensuring that these are set will be done in separate tests.
     self.rkl._flash_init = True
     self.rkl._kernel_init = True
示例#3
0
class RAMKernelTests(unittest.TestCase):
    def setUp(self):
        self.channel = MockChannel()
        self.rkl = ramkernel.RAMKernelProtocol(self.channel)
        # Pretend we did initialize the chip already so we
        # can test commands.
        # Ensuring that these are set will be done in separate tests.
        self.rkl._flash_init = True
        self.rkl._kernel_init = True

    def test_kernel_not_yet_init_exception(self):
        """ Test that working with an uninitialized kernel raises KernelNotInitializedError """
        rkl = ramkernel.RAMKernelProtocol(self.channel)
        for command in (rkl.getver, rkl.flash_get_capacity):
            with self.assertRaises(ramkernel.KernelNotInitializedError) as cm:
                command()

    def test_flash_init_error(self):
        self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_INIT, 0, 0)
        with self.assertRaises(ramkernel.CommandResponseError) as cm:
            self.rkl.flash_initial()

        self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_INIT)
        self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_INITIAL)
        self.assertEqual(cm.exception.length, 0)

    def test_flash_get_capacity(self):
        self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0xbeef, 2057)
        self.assertEqual(self.rkl.flash_get_capacity(), 2057)
        self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0xbeef, 0x1FFFF)
        self.assertEqual(self.rkl.flash_get_capacity(), 0x1FFFF)

    def test_getver(self):
        # Test CMD_GETVER responses of various shapes and sizes, including
        # with payloads containing non-ASCII data.
        for imx_version, flash_model in (
            (0xface, b"HAL 9000"),
            (0xbeef, b"Taste the biscuit"),
            (0x2057, b"Silly rabbit Freescale's for kids"),
            (0xfeed, b"So I should \x1bprobably sleep\xAF\x99 at some point \x00tonight"),
        ):
            self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, imx_version,
                                            len(flash_model), flash_model)

            ver, flash = self.rkl.getver()
            self.assertEqual(ver, imx_version)
            self.assertEqual(flash, flash_model)

    def test_erase(self):
        """ Test the flash_erase API with no callback specified. """
        block_size = 0x20000

        for block_index in range(50):
            self.channel.queue_rkl_response(ramkernel.ACK_FLASH_ERASE, block_index, block_size)
        self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, 0)

        self.rkl.flash_erase(0x0, 1)

    def test_erase_callback(self):
        """ Test flash_erase with erase_callback specified. """
        callback_data = []
        block_size = 0x20000

        # Queue 50 block erase responses, each with a different block size.
        # Generally the block size returned by RKL is fixed, but if it is not,
        # we should be sending the right value.
        for block_index in range(50):
            self.channel.queue_rkl_response(ramkernel.ACK_FLASH_ERASE,
                                            block_index,
                                            block_size + block_index)
        self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, 0)

        # Close over callback_data to make sure it is called correctly.
        def erase_cb(block_idx, block_sz):
            callback_data.append((block_idx, block_sz))

        # Flash erase doesn't care how many bytes you specify, or the start address.
        # We're just testing how it handles RKL proto responses.
        self.rkl.flash_erase(0x0, 1, erase_callback=erase_cb)

        self.assertEqual(len(callback_data), 50)
        for index, (cb_block_idx, cb_block_sz) in enumerate(callback_data):
            self.assertEqual(cb_block_idx, index)
            self.assertEqual(cb_block_sz, block_size + index)

    def test_erase_partial_error(self):
        """ Test flash_erase error handling - partial erase """
        callback_data = []
        block_size = 0x20000

        # queue up 25 good partial responses followed by an error.
        # The callback should be called for each of the 25 good responses.
        for block_index in range(25):
            self.channel.queue_rkl_response(ramkernel.ACK_FLASH_ERASE,
                                            block_index,
                                            block_size + block_index)
        self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_PART_ERASE, 0, 0)

        # Close over callback_data to make sure it is called correctly.
        def erase_cb(block_idx, block_sz):
            callback_data.append((block_idx, block_sz))

        # Ensure an exception is raised
        with self.assertRaises(ramkernel.CommandResponseError) as cm:
            self.rkl.flash_erase(0x0, 1, erase_callback=erase_cb)
        self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_PART_ERASE)
        self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_ERASE)

        self.assertEqual(len(callback_data), 25)
        for index, (cb_block_idx, cb_block_sz) in enumerate(callback_data):
            self.assertEqual(cb_block_idx, index)
            self.assertEqual(cb_block_sz, block_size + index)

    def test_erase_initial_error(self):
        """ Test flash_erase error handling - no blocks erased """
        callback_data = []
        self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_OVER_ADDR, 0, 0)

        # Close over callback_data to make sure it is called correctly.
        def erase_cb(block_idx, block_sz):
            callback_data.append((block_idx, block_sz))

        # Ensure an exception is raised
        with self.assertRaises(ramkernel.CommandResponseError) as cm:
            self.rkl.flash_erase(0x0, 1, erase_callback=erase_cb)
        self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_OVER_ADDR)
        self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_ERASE)

        # The callback should never have been called.
        self.assertEqual(len(callback_data), 0)

    def test_flash_dump(self):
        """
        Ensure flash_dump in normal operation works correctly - multiple FLASH_PARTLY responses,
        checksum validation, etc.
        """
        data = b"testing random \x00 data 1 2 \x03"
        cksum = ramkernel.calculate_checksum(data)
        self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, cksum, len(data), data)
        # NOTE: the RKL main() never sends ACK_SUCCESS for CMD_FLASH_DUMP - only
        # a sequence of ACK_FLASH_PARTLY responses.

        # Don't care about the address or requested size, we're mocking it
        dump_data = self.rkl.flash_dump(0x0000, len(data))

        self.assertEqual(data, dump_data)

        test_data =  (
            b"testing random \x00 data 1 2 \x03",
            b"cold wind to valhalla",
            b"\xff" * 1024,
            b"\x00" * 2048
        )
        # Multipart
        for data_chunk in test_data:
             cksum = ramkernel.calculate_checksum(data_chunk)
             self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, cksum, len(data_chunk), data_chunk)

        total_test_data = b"".join(test_data)
        # Don't care about the address.
        dump_data = self.rkl.flash_dump(0x0000, len(total_test_data))

        self.assertEqual(total_test_data, dump_data)

    def test_flash_dump_checksum_error(self):
        """
        Ensure flash_dump handles checksum errors correctly, by raising ramkernel.ChecksumError.
        flash_dump should set ChecksumError internal values correctly.
        """
        data = b"testing random \x00 data 1 2 \x03"
        real_cksum = ramkernel.calculate_checksum(data)
        fake_cksum = (real_cksum + 1) & 0xFFFF
        self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, fake_cksum, len(data), data)

        with self.assertRaises(ramkernel.ChecksumError) as cm:
            # Don't care about the address or requested size, we're mocking it
            self.rkl.flash_dump(0x0000, len(data))
        self.assertEqual(cm.exception.expected_checksum, fake_cksum)
        self.assertEqual(cm.exception.checksum, real_cksum)

        ## Test with initial successes, then a failed checksum
        test_data =  (
            b"testing random \x00 data 1 2 \x03",
            b"cold wind to valhalla",
            b"\xff" * 1024,
            b"\x00" * 2048
        )
        for data_chunk in test_data:
            # Calculate the real checksum for these chunks
             cksum = ramkernel.calculate_checksum(data_chunk)
             self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, cksum, len(data_chunk), data_chunk)
        # Now for the bad checksum...
        data = b"testing random \x00 data 1 2 \x03"
        real_cksum = ramkernel.calculate_checksum(data)
        fake_cksum = (real_cksum + 1) & 0xFFFF
        self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, fake_cksum, len(data), data)
        self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, 0)

        total_data_length = len(b"".join(test_data + (data,)))
        with self.assertRaises(ramkernel.ChecksumError) as cm:
            self.rkl.flash_dump(0x0000, total_data_length)

        self.assertEqual(cm.exception.expected_checksum, fake_cksum)
        self.assertEqual(cm.exception.checksum, real_cksum)

    def test_flash_dump_partial_error(self):
        """
        Ensure flash_dump handles partial read errors correctly.
        """
        data = b"testing random \x00 data 1 2 \x03"
        cksum = ramkernel.calculate_checksum(data)
        self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, cksum, len(data), data)
        self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_READ, 0, 0)

        with self.assertRaises(ramkernel.CommandResponseError) as cm:
            # We ask for more size than the first response gives, so we try to read again
            # and encounter the FLASH_ERROR_READ.
            self.rkl.flash_dump(0x0000, len(data) * 2)
        self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_READ)
        self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_DUMP)

    def test_flash_dump_initial_error(self):
        """
        Ensure flash_dump handles initial read error correctly.
        """
        self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_OVER_ADDR, 0, 0)

        with self.assertRaises(ramkernel.CommandResponseError) as cm:
            # Don't care about the address or requested size, we're mocking it
            self.rkl.flash_dump(0x0000, 0x400)
        self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_OVER_ADDR)
        self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_DUMP)

    def test_flash_program_arguments(self):
        """ Test sanity checks on flash_program """
        class AVeryLargeObject(object):
            def __init__(self, length):
                self.length = length
            def __len__(self):
                return self.length

        self.assertRaises(ValueError, self.rkl.flash_program, -1, "asdf")
        self.assertRaises(ValueError, self.rkl.flash_program, 0,
                          AVeryLargeObject(ramkernel.FLASH_PROGRAM_MAX_WRITE_SIZE + 1))
        self.assertRaises(ValueError, self.rkl.flash_program, 0, "")
        self.assertRaises(ValueError, self.rkl.flash_program, 0, "asdf", file_format = -1)

    def test_flash_program(self):
        data = b"the quick brown fox is tired of typing today \x00 \x12\x13\r\na"
        partial_data_len = len(data) // 2

        def queue_responses():
            self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, len(data))
            self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, 0, partial_data_len)
            self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, 1, len(data) - partial_data_len)
            self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, 0)

        queue_responses()
        self.rkl.flash_program(0x0000, data)

        # Now again, with a callback defined.
        callback_data = []
        def prog_cb(block, length_written):
            callback_data.append((block, length_written))

        queue_responses()
        self.rkl.flash_program(0x0000, data, program_callback = prog_cb)

        self.assertEqual(len(callback_data), 2)
        self.assertEqual(callback_data[0][0], 0)
        self.assertEqual(callback_data[0][1], partial_data_len)
        self.assertEqual(callback_data[1][0], 1)
        self.assertEqual(callback_data[1][1], len(data) - partial_data_len)

    def test_flash_program_verify(self):
        data = b"the quick brown fox is tired of typing today \x00 \x12\x13\r\na"
        partial_data_len = len(data) // 2

        def queue_responses():
            self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, len(data))
            self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, 0, partial_data_len)
            self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, 1, len(data) - partial_data_len)
            self.channel.queue_rkl_response(ramkernel.ACK_FLASH_VERIFY, 0, partial_data_len)
            self.channel.queue_rkl_response(ramkernel.ACK_FLASH_VERIFY, 1, len(data) - partial_data_len)
            self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, 0)

        queue_responses()
        self.rkl.flash_program(0x0000, data, read_back_verify=True)

        # Now again, with a callback defined.
        prog_cb_data = []
        verify_cb_data = []
        def prog_cb(block, length_written):
            prog_cb_data.append((block, length_written))
        def verify_cb(block, length_verified):
            verify_cb_data.append((block, length_verified))

        queue_responses()
        self.rkl.flash_program(0x0000, data, read_back_verify = True,
                               program_callback = prog_cb, verify_callback = verify_cb)

        for cb_data in prog_cb_data, verify_cb_data:
            self.assertEqual(len(cb_data), 2)
            self.assertEqual(cb_data[0][0], 0)
            self.assertEqual(cb_data[0][1], partial_data_len)
            self.assertEqual(cb_data[1][0], 1)
            self.assertEqual(cb_data[1][1], len(data) - partial_data_len)

    def test_flash_program_initial_error(self):
        """ Test flash_dump with an initial error raises CommandResponseError """
        self.channel.queue_rkl_response(ramkernel.FLASH_FAILED, 0, 0)

        with self.assertRaises(ramkernel.CommandResponseError) as cm:
            # Don't care about the address or requested size, we're mocking it
            self.rkl.flash_program(0x0000, "asdf")
        self.assertEqual(cm.exception.ack, ramkernel.FLASH_FAILED)
        self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_PROGRAM)

    def test_flash_program_partial_error(self):
        """ Ensure flash_program handles partial program errors correctly. """
        data = "testing random \x00 data 1 2 \x03"
        self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, len(data))
        self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_PROG, 0, 0)

        with self.assertRaises(ramkernel.CommandResponseError) as cm:
            self.rkl.flash_program(0x0000, data)
        self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_PROG)
        self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_PROGRAM)

        self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, len(data))
        self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, 0, len(data) - 1)
        self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_PROG, 0, 0)

        with self.assertRaises(ramkernel.CommandResponseError) as cm:
            self.rkl.flash_program(0x0000, data)
        self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_PROG)
        self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_PROGRAM)
示例#4
0
class RAMKernelTests(unittest.TestCase):
    def setUp(self):
        self.channel = MockChannel()
        self.rkl = ramkernel.RAMKernelProtocol(self.channel)
        # Pretend we did initialize the chip already so we
        # can test commands.
        # Ensuring that these are set will be done in separate tests.
        self.rkl._flash_init = True
        self.rkl._kernel_init = True

    def test_kernel_not_yet_init_exception(self):
        """ Test that working with an uninitialized kernel raises KernelNotInitializedError """
        rkl = ramkernel.RAMKernelProtocol(self.channel)
        for command in (rkl.getver, rkl.flash_get_capacity):
            with self.assertRaises(ramkernel.KernelNotInitializedError) as cm:
                command()

    def test_flash_init_error(self):
        self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_INIT, 0, 0)
        with self.assertRaises(ramkernel.CommandResponseError) as cm:
            self.rkl.flash_initial()

        self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_INIT)
        self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_INITIAL)
        self.assertEqual(cm.exception.length, 0)

    def test_flash_get_capacity(self):
        self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0xbeef, 2057)
        self.assertEqual(self.rkl.flash_get_capacity(), 2057)
        self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0xbeef, 0x1FFFF)
        self.assertEqual(self.rkl.flash_get_capacity(), 0x1FFFF)

    def test_getver(self):
        # Test CMD_GETVER responses of various shapes and sizes, including
        # with payloads containing non-ASCII data.
        for imx_version, flash_model in (
            (0xface, b"HAL 9000"),
            (0xbeef, b"Taste the biscuit"),
            (0x2057, b"Silly rabbit Freescale's for kids"),
            (0xfeed,
             b"So I should \x1bprobably sleep\xAF\x99 at some point \x00tonight"
             ),
        ):
            self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, imx_version,
                                            len(flash_model), flash_model)

            ver, flash = self.rkl.getver()
            self.assertEqual(ver, imx_version)
            self.assertEqual(flash, flash_model)

    def test_erase(self):
        """ Test the flash_erase API with no callback specified. """
        block_size = 0x20000

        for block_index in range(50):
            self.channel.queue_rkl_response(ramkernel.ACK_FLASH_ERASE,
                                            block_index, block_size)
        self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, 0)

        self.rkl.flash_erase(0x0, 1)

    def test_erase_callback(self):
        """ Test flash_erase with erase_callback specified. """
        callback_data = []
        block_size = 0x20000

        # Queue 50 block erase responses, each with a different block size.
        # Generally the block size returned by RKL is fixed, but if it is not,
        # we should be sending the right value.
        for block_index in range(50):
            self.channel.queue_rkl_response(ramkernel.ACK_FLASH_ERASE,
                                            block_index,
                                            block_size + block_index)
        self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, 0)

        # Close over callback_data to make sure it is called correctly.
        def erase_cb(block_idx, block_sz):
            callback_data.append((block_idx, block_sz))

        # Flash erase doesn't care how many bytes you specify, or the start address.
        # We're just testing how it handles RKL proto responses.
        self.rkl.flash_erase(0x0, 1, erase_callback=erase_cb)

        self.assertEqual(len(callback_data), 50)
        for index, (cb_block_idx, cb_block_sz) in enumerate(callback_data):
            self.assertEqual(cb_block_idx, index)
            self.assertEqual(cb_block_sz, block_size + index)

    def test_erase_partial_error(self):
        """ Test flash_erase error handling - partial erase """
        callback_data = []
        block_size = 0x20000

        # queue up 25 good partial responses followed by an error.
        # The callback should be called for each of the 25 good responses.
        for block_index in range(25):
            self.channel.queue_rkl_response(ramkernel.ACK_FLASH_ERASE,
                                            block_index,
                                            block_size + block_index)
        self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_PART_ERASE, 0, 0)

        # Close over callback_data to make sure it is called correctly.
        def erase_cb(block_idx, block_sz):
            callback_data.append((block_idx, block_sz))

        # Ensure an exception is raised
        with self.assertRaises(ramkernel.CommandResponseError) as cm:
            self.rkl.flash_erase(0x0, 1, erase_callback=erase_cb)
        self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_PART_ERASE)
        self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_ERASE)

        self.assertEqual(len(callback_data), 25)
        for index, (cb_block_idx, cb_block_sz) in enumerate(callback_data):
            self.assertEqual(cb_block_idx, index)
            self.assertEqual(cb_block_sz, block_size + index)

    def test_erase_initial_error(self):
        """ Test flash_erase error handling - no blocks erased """
        callback_data = []
        self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_OVER_ADDR, 0, 0)

        # Close over callback_data to make sure it is called correctly.
        def erase_cb(block_idx, block_sz):
            callback_data.append((block_idx, block_sz))

        # Ensure an exception is raised
        with self.assertRaises(ramkernel.CommandResponseError) as cm:
            self.rkl.flash_erase(0x0, 1, erase_callback=erase_cb)
        self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_OVER_ADDR)
        self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_ERASE)

        # The callback should never have been called.
        self.assertEqual(len(callback_data), 0)

    def test_flash_dump(self):
        """
        Ensure flash_dump in normal operation works correctly - multiple FLASH_PARTLY responses,
        checksum validation, etc.
        """
        data = b"testing random \x00 data 1 2 \x03"
        cksum = ramkernel.calculate_checksum(data)
        self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, cksum,
                                        len(data), data)
        # NOTE: the RKL main() never sends ACK_SUCCESS for CMD_FLASH_DUMP - only
        # a sequence of ACK_FLASH_PARTLY responses.

        # Don't care about the address or requested size, we're mocking it
        dump_data = self.rkl.flash_dump(0x0000, len(data))

        self.assertEqual(data, dump_data)

        test_data = (b"testing random \x00 data 1 2 \x03",
                     b"cold wind to valhalla", b"\xff" * 1024, b"\x00" * 2048)
        # Multipart
        for data_chunk in test_data:
            cksum = ramkernel.calculate_checksum(data_chunk)
            self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, cksum,
                                            len(data_chunk), data_chunk)

        total_test_data = b"".join(test_data)
        # Don't care about the address.
        dump_data = self.rkl.flash_dump(0x0000, len(total_test_data))

        self.assertEqual(total_test_data, dump_data)

    def test_flash_dump_checksum_error(self):
        """
        Ensure flash_dump handles checksum errors correctly, by raising ramkernel.ChecksumError.
        flash_dump should set ChecksumError internal values correctly.
        """
        data = b"testing random \x00 data 1 2 \x03"
        real_cksum = ramkernel.calculate_checksum(data)
        fake_cksum = (real_cksum + 1) & 0xFFFF
        self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, fake_cksum,
                                        len(data), data)

        with self.assertRaises(ramkernel.ChecksumError) as cm:
            # Don't care about the address or requested size, we're mocking it
            self.rkl.flash_dump(0x0000, len(data))
        self.assertEqual(cm.exception.expected_checksum, fake_cksum)
        self.assertEqual(cm.exception.checksum, real_cksum)

        ## Test with initial successes, then a failed checksum
        test_data = (b"testing random \x00 data 1 2 \x03",
                     b"cold wind to valhalla", b"\xff" * 1024, b"\x00" * 2048)
        for data_chunk in test_data:
            # Calculate the real checksum for these chunks
            cksum = ramkernel.calculate_checksum(data_chunk)
            self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, cksum,
                                            len(data_chunk), data_chunk)
        # Now for the bad checksum...
        data = b"testing random \x00 data 1 2 \x03"
        real_cksum = ramkernel.calculate_checksum(data)
        fake_cksum = (real_cksum + 1) & 0xFFFF
        self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, fake_cksum,
                                        len(data), data)
        self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, 0)

        total_data_length = len(b"".join(test_data + (data, )))
        with self.assertRaises(ramkernel.ChecksumError) as cm:
            self.rkl.flash_dump(0x0000, total_data_length)

        self.assertEqual(cm.exception.expected_checksum, fake_cksum)
        self.assertEqual(cm.exception.checksum, real_cksum)

    def test_flash_dump_partial_error(self):
        """
        Ensure flash_dump handles partial read errors correctly.
        """
        data = b"testing random \x00 data 1 2 \x03"
        cksum = ramkernel.calculate_checksum(data)
        self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, cksum,
                                        len(data), data)
        self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_READ, 0, 0)

        with self.assertRaises(ramkernel.CommandResponseError) as cm:
            # We ask for more size than the first response gives, so we try to read again
            # and encounter the FLASH_ERROR_READ.
            self.rkl.flash_dump(0x0000, len(data) * 2)
        self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_READ)
        self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_DUMP)

    def test_flash_dump_initial_error(self):
        """
        Ensure flash_dump handles initial read error correctly.
        """
        self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_OVER_ADDR, 0, 0)

        with self.assertRaises(ramkernel.CommandResponseError) as cm:
            # Don't care about the address or requested size, we're mocking it
            self.rkl.flash_dump(0x0000, 0x400)
        self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_OVER_ADDR)
        self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_DUMP)

    def test_flash_program_arguments(self):
        """ Test sanity checks on flash_program """
        class AVeryLargeObject(object):
            def __init__(self, length):
                self.length = length

            def __len__(self):
                return self.length

        self.assertRaises(ValueError, self.rkl.flash_program, -1, "asdf")
        self.assertRaises(
            ValueError, self.rkl.flash_program, 0,
            AVeryLargeObject(ramkernel.FLASH_PROGRAM_MAX_WRITE_SIZE + 1))
        self.assertRaises(ValueError, self.rkl.flash_program, 0, "")
        self.assertRaises(ValueError,
                          self.rkl.flash_program,
                          0,
                          "asdf",
                          file_format=-1)

    def test_flash_program(self):
        data = b"the quick brown fox is tired of typing today \x00 \x12\x13\r\na"
        partial_data_len = len(data) // 2

        def queue_responses():
            self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0,
                                            len(data))
            self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, 0,
                                            partial_data_len)
            self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, 1,
                                            len(data) - partial_data_len)
            self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, 0)

        queue_responses()
        self.rkl.flash_program(0x0000, data)

        # Now again, with a callback defined.
        callback_data = []

        def prog_cb(block, length_written):
            callback_data.append((block, length_written))

        queue_responses()
        self.rkl.flash_program(0x0000, data, program_callback=prog_cb)

        self.assertEqual(len(callback_data), 2)
        self.assertEqual(callback_data[0][0], 0)
        self.assertEqual(callback_data[0][1], partial_data_len)
        self.assertEqual(callback_data[1][0], 1)
        self.assertEqual(callback_data[1][1], len(data) - partial_data_len)

    def test_flash_program_verify(self):
        data = b"the quick brown fox is tired of typing today \x00 \x12\x13\r\na"
        partial_data_len = len(data) // 2

        def queue_responses():
            self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0,
                                            len(data))
            self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, 0,
                                            partial_data_len)
            self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, 1,
                                            len(data) - partial_data_len)
            self.channel.queue_rkl_response(ramkernel.ACK_FLASH_VERIFY, 0,
                                            partial_data_len)
            self.channel.queue_rkl_response(ramkernel.ACK_FLASH_VERIFY, 1,
                                            len(data) - partial_data_len)
            self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, 0)

        queue_responses()
        self.rkl.flash_program(0x0000, data, read_back_verify=True)

        # Now again, with a callback defined.
        prog_cb_data = []
        verify_cb_data = []

        def prog_cb(block, length_written):
            prog_cb_data.append((block, length_written))

        def verify_cb(block, length_verified):
            verify_cb_data.append((block, length_verified))

        queue_responses()
        self.rkl.flash_program(0x0000,
                               data,
                               read_back_verify=True,
                               program_callback=prog_cb,
                               verify_callback=verify_cb)

        for cb_data in prog_cb_data, verify_cb_data:
            self.assertEqual(len(cb_data), 2)
            self.assertEqual(cb_data[0][0], 0)
            self.assertEqual(cb_data[0][1], partial_data_len)
            self.assertEqual(cb_data[1][0], 1)
            self.assertEqual(cb_data[1][1], len(data) - partial_data_len)

    def test_flash_program_initial_error(self):
        """ Test flash_dump with an initial error raises CommandResponseError """
        self.channel.queue_rkl_response(ramkernel.FLASH_FAILED, 0, 0)

        with self.assertRaises(ramkernel.CommandResponseError) as cm:
            # Don't care about the address or requested size, we're mocking it
            self.rkl.flash_program(0x0000, "asdf")
        self.assertEqual(cm.exception.ack, ramkernel.FLASH_FAILED)
        self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_PROGRAM)

    def test_flash_program_partial_error(self):
        """ Ensure flash_program handles partial program errors correctly. """
        data = "testing random \x00 data 1 2 \x03"
        self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, len(data))
        self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_PROG, 0, 0)

        with self.assertRaises(ramkernel.CommandResponseError) as cm:
            self.rkl.flash_program(0x0000, data)
        self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_PROG)
        self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_PROGRAM)

        self.channel.queue_rkl_response(ramkernel.ACK_SUCCESS, 0, len(data))
        self.channel.queue_rkl_response(ramkernel.ACK_FLASH_PARTLY, 0,
                                        len(data) - 1)
        self.channel.queue_rkl_response(ramkernel.FLASH_ERROR_PROG, 0, 0)

        with self.assertRaises(ramkernel.CommandResponseError) as cm:
            self.rkl.flash_program(0x0000, data)
        self.assertEqual(cm.exception.ack, ramkernel.FLASH_ERROR_PROG)
        self.assertEqual(cm.exception.command, ramkernel.CMD_FLASH_PROGRAM)
示例#5
0
 def setUp(self):
     self.channel = MockChannel()
     self.sbp = boot.SerialBootProtocol(self.channel)
示例#6
0
class SerialBootProtocolTests(unittest.TestCase):
    def setUp(self):
        self.channel = MockChannel()
        self.sbp = boot.SerialBootProtocol(self.channel)

    def test_get_status_string(self):
        self.assertEqual("Successful operation complete",
                         boot.get_status_string(boot.HAB_PASSED))

        self.assertEqual("Failure not matching any other description",
                         boot.get_status_string(boot.HAB_FAILURE))

        self.assertEqual("Unknown code 0x5643beef",
                         boot.get_status_string(0x5643BEEF))


    def queue_sbp_resp(self, resp):
        self.channel.queue_data(struct.pack(">I", resp))

    def queue_ack_prod(self):
        self.queue_sbp_resp(boot.ACK_PRODUCTION_PART)

    def queue_ack_eng(self):
        self.queue_sbp_resp(boot.ACK_ENGINEERING_PART)

    def test_get_status(self):
        """ Test decoding SBP status """
        self.channel.queue_data(b"\xff\xff\xff\xff")
        status = self.sbp.get_status()
        self.assertEqual(0xffffffff, status)

        self.channel.queue_data(b"\xef\xbe\xad\xde")
        status = self.sbp.get_status()
        self.assertEqual(0xdeadbeef, status)

    def test_read_memory_invalid_address_size(self):
        """
        Test raising ValueError on calling read_memory() with invalid address
        and/or access width.
        """
        for address, datasize in (
            (-1, boot.DATA_SIZE_BYTE),
            (0, 99),
            (0xffffffff + 1, boot.DATA_SIZE_WORD),
        ):
            self.assertRaises(ValueError, self.sbp.read_memory, address, datasize)

    def test_read_memory_halfword(self):
        """ Test basic functionality of read_memory(). """
        # Queue up the response
        self.queue_ack_eng()
        self.channel.queue_data("\xaa\xbb")

        ret = self.sbp.read_memory(0x25, boot.DATA_SIZE_HALFWORD, 1)
        # Ensure we get an array of the correct type with the correctly byte-swapped
        # value.
        self.assertEqual(array.array('H', [0xbbaa]), ret)
        # Ensure we send the correct command data, aligned to 16 bytes.
        self.assertEqual(b"\x01\x01\x00\x00\x00\x25\x10\x00\x00\x00\x01\x00\x00\x00\x00\x00",
                         self.channel.get_data_written())

    def test_read_memory_word_multiple(self):
        """ Test basic functionality of read_memory(). """
        # Queue up the response
        self.queue_ack_eng()
        self.channel.queue_data("\x01\x00\x00\x00\x02\x00\x00\x00")

        ret = self.sbp.read_memory(0x99, boot.DATA_SIZE_WORD, 2)
        # Ensure we get an array of the correct type with the correctly byte-swapped
        # value.
        self.assertEqual(array.array('I', [1, 2]), ret)
        # Ensure we send the correct command data, aligned to 16 bytes.
        self.assertEqual(b"\x01\x01\x00\x00\x00\x99\x20\x00\x00\x00\x02\x00\x00\x00\x00\x00",
                         self.channel.get_data_written())

    def test_read_memory_word_multiple_byteswap(self):
        """ Test basic functionality of read_memory() with byte swapping. """
        self.queue_ack_eng()

        # Switch processor byte order to the opposite byte order of
        # the host system to ensure we need to byte swap
        if "little" == sys.byteorder:
            self.sbp.byteorder = "big"
            self.channel.queue_data("\x00\x00\x00\x01\x00\x00\x00\x02")
        else:
            self.sbp.byteorder = "little"
            self.channel.queue_data("\x01\x00\x00\x00\x02\x00\x00\x00")

        ret = self.sbp.read_memory(0x99, boot.DATA_SIZE_WORD, 2)
        # Ensure we get an array of the correct type with the correctly byte-swapped
        # value.
        self.assertEqual(array.array('I', [1, 2]), ret)
        # Ensure we send the correct command data, aligned to 16 bytes.
        self.assertEqual(b"\x01\x01\x00\x00\x00\x99\x20\x00\x00\x00\x02\x00\x00\x00\x00\x00",
                         self.channel.get_data_written())

    def test_read_memory_short_response(self):
        """ Test error functionality of read_memory() with not enough data read. """
        self.queue_ack_eng()
        self.assertRaises(boot.CommandResponseError,
                          self.sbp.read_memory, 0x99, boot.DATA_SIZE_WORD, 2)

    def test_read_memory_single(self):
        """ Test basic functionality of read_memory(). """
        # Queue up the response
        self.queue_ack_eng()
        self.channel.queue_data("\xaa\xbb")

        ret = self.sbp.read_memory_single(0x25, boot.DATA_SIZE_HALFWORD)
        # Ensure we get an array of the correct type with the correctly byte-swapped
        # value.
        self.assertEqual(0xbbaa, ret)
        # Ensure we send the correct command data, aligned to 16 bytes.
        self.assertEqual(b"\x01\x01\x00\x00\x00\x25\x10\x00\x00\x00\x01\x00\x00\x00\x00\x00",
                         self.channel.get_data_written())

    def test_complete_boot(self):
        """ Test the Completed command for the serial boot protocol. """
        self.queue_sbp_resp(boot.BOOT_PROTOCOL_COMPLETE)
        self.assertEqual(boot.BOOT_PROTOCOL_COMPLETE, self.sbp._complete_boot())

        # Doesn't matter what was written, as long as it is 16 bytes!
        self.assertEqual(16, len(self.channel.get_data_written()))

    def test_complete_boot_bad_response(self):
        """ Test the Completed command for the serial boot protocol. """
        self.queue_sbp_resp(boot.HAB_PASSED)
        self.assertRaises(boot.CommandResponseError, self.sbp._complete_boot)

        # Doesn't matter what was written, as long as it is 16 bytes!
        self.assertEqual(16, len(self.channel.get_data_written()))

    def test_write_memory_byte(self):
        # Queue ACK_ENGINEERING_PART
        self.queue_ack_eng()
        # Queue ACK_WRITE
        self.queue_sbp_resp(boot.ACK_WRITE_SUCCESS)
        self.sbp.write_memory(0xbeefcafe, boot.DATA_SIZE_BYTE, 0x01)
        self.assertEqual(b"\x02\x02\xbe\xef\xca\xfe\x08\x00\x00\x00\x00\x00\x00\x00\x01\x00",
                         self.channel.get_data_written())

    def test_write_memory_halfword(self):
        # Queue ACK_ENGINEERING_PART
        self.queue_ack_eng()
        # Queue ACK_WRITE
        self.queue_sbp_resp(boot.ACK_WRITE_SUCCESS)
        self.sbp.write_memory(0xbeefcafe, boot.DATA_SIZE_HALFWORD, 0xfeed)
        self.assertEqual(b"\x02\x02\xbe\xef\xca\xfe\x10\x00\x00\x00\x00\x00\x00\xfe\xed\x00",
                         self.channel.get_data_written())

    def test_write_memory_word(self):
        # Queue ACK_ENGINEERING_PART
        self.queue_ack_eng()
        # Queue ACK_WRITE
        self.queue_sbp_resp(boot.ACK_WRITE_SUCCESS)
        self.sbp.write_memory(0xbeefcafe, boot.DATA_SIZE_WORD, 0xcafefeed)
        self.assertEqual(b"\x02\x02\xbe\xef\xca\xfe\x20\x00\x00\x00\x00\xca\xfe\xfe\xed\x00",
                         self.channel.get_data_written())