#include "pcan_boot.h" #include #include #include typedef struct { uint8_t flash[2][64]; uint32_t last_id; uint8_t last_data[8]; uint8_t last_dlc; unsigned sends; unsigned erases; unsigned writes; uint8_t pending_slot; bool authorized; bool verified; bool rebooted; } fake_t; static bool fake_send(void *user, uint32_t id, const uint8_t *data, uint8_t dlc) { fake_t *f = (fake_t *)user; f->last_id = id; f->last_dlc = dlc; (void)memset(f->last_data, 0, sizeof(f->last_data)); if (dlc != 0U) { (void)memcpy(f->last_data, data, dlc); } f->sends++; return true; } static bool fake_erase(void *user, uint8_t slot) { fake_t *f = (fake_t *)user; (void)memset(f->flash[slot], 0xFF, sizeof(f->flash[slot])); f->erases++; return true; } static bool fake_write(void *user, uint8_t slot, uint32_t offset, const uint8_t *data, uint8_t length) { fake_t *f = (fake_t *)user; if ((slot > 1U) || (offset + length > sizeof(f->flash[slot]))) { return false; } (void)memcpy(&f->flash[slot][offset], data, length); f->writes++; return true; } static bool fake_authorize(void *user, const pcan_boot_manifest_t *manifest) { fake_t *f = (fake_t *)user; (void)manifest; return f->authorized; } static bool fake_verify(void *user, uint8_t slot, const pcan_boot_manifest_t *manifest) { fake_t *f = (fake_t *)user; (void)slot; (void)manifest; return f->verified; } static bool fake_pending(void *user, uint8_t slot, const pcan_boot_manifest_t *manifest) { fake_t *f = (fake_t *)user; (void)manifest; f->pending_slot = slot; return true; } static bool fake_confirm(void *user) { (void)user; return true; } static void fake_reboot(void *user) { ((fake_t *)user)->rebooted = true; } static uint32_t crc32(const uint8_t *data, size_t length) { uint32_t crc = 0xFFFFFFFFUL; size_t i; for (i = 0U; i < length; ++i) { uint8_t bit; crc ^= data[i]; for (bit = 0U; bit < 8U; ++bit) { crc = (crc >> 1) ^ ((crc & 1U) != 0U ? 0xEDB88320UL : 0U); } } return ~crc; } static void put_u16(uint8_t *p, uint16_t v) { p[0] = (uint8_t)v; p[1] = (uint8_t)(v >> 8); } static void put_u32(uint8_t *p, uint32_t v) { p[0] = (uint8_t)v; p[1] = (uint8_t)(v >> 8); p[2] = (uint8_t)(v >> 16); p[3] = (uint8_t)(v >> 24); } static uint32_t request_id(uint8_t type, uint16_t body) { return pcan_boot_make_id(1U, PCAN_BOOT_ROUTE_FROM_PM, 3U, 5U, type, body); } static bool command(pcan_boot_t *boot, uint8_t session, uint8_t cmd, const uint8_t *data, uint8_t dlc) { uint16_t body = (uint16_t)(((uint16_t)session << 8) | cmd); return pcan_boot_process(boot, request_id(PCAN_BOOT_MSG_CONTROL, body), data, dlc); } static void setup(pcan_boot_t *boot, fake_t *fake) { pcan_boot_config_t config; pcan_boot_port_t port; (void)memset(fake, 0, sizeof(*fake)); (void)memset(&config, 0, sizeof(config)); (void)memset(&port, 0, sizeof(port)); fake->authorized = true; fake->verified = true; fake->pending_slot = PCAN_BOOT_SLOT_NONE; config.device_type = 3U; config.device_id = 5U; config.product_type = 0x1234U; config.hardware_revision = 2U; config.firmware_version = 0x01020304UL; config.active_slot = 0U; config.ack_window = 2U; port.send = fake_send; port.erase_slot = fake_erase; port.write_slot = fake_write; port.authorize = fake_authorize; port.verify_image = fake_verify; port.set_pending_slot = fake_pending; port.confirm_running_slot = fake_confirm; port.reboot = fake_reboot; assert(pcan_boot_init(boot, &config, &port, fake)); } static void begin_update(pcan_boot_t *boot, const uint8_t *image, uint32_t size) { uint8_t data[8]; assert(command(boot, 7U, PCAN_BOOT_CMD_ENTER_BOOT, NULL, 0U)); put_u32(&data[0], size); put_u32(&data[4], crc32(image, size)); assert(command(boot, 7U, PCAN_BOOT_CMD_BEGIN_IMAGE, data, 8U)); put_u16(&data[0], 0x1234U); data[2] = 1U; data[3] = 3U; put_u32(&data[4], 0x02000000UL); assert(command(boot, 7U, PCAN_BOOT_CMD_BEGIN_COMPAT, data, 8U)); } static void test_id_layout(void) { uint32_t id = pcan_boot_make_id(1U, 0U, 7U, 15U, 0xAU, 0x1234U); assert(id == 0x17FA1234UL); assert(pcan_boot_id_device_type(id) == 7U); assert(pcan_boot_id_device_id(id) == 15U); assert(pcan_boot_id_msg_type(id) == 0xAU); assert(pcan_boot_id_msg_body(id) == 0x1234U); } static void test_crc32_reference(void) { static const uint8_t reference[] = "123456789"; assert(crc32(reference, sizeof(reference) - 1U) == 0xCBF43926UL); } static void test_complete_update(void) { static const uint8_t image[13] = { 0x10U, 0x11U, 0x12U, 0x13U, 0x14U, 0x15U, 0x16U, 0x17U, 0x20U, 0x21U, 0x22U, 0x23U, 0x24U }; uint8_t block[8]; pcan_boot_t boot; fake_t fake; setup(&boot, &fake); begin_update(&boot, image, sizeof(image)); assert(boot.state == PCAN_BOOT_STATE_READY_TO_ERASE); assert(boot.target_slot == 1U); assert(command(&boot, 7U, PCAN_BOOT_CMD_ERASE, NULL, 0U)); assert(fake.erases == 1U); assert(pcan_boot_process(&boot, request_id(PCAN_BOOT_MSG_DATA_B, 0U), image, 8U)); (void)memset(block, 0xFF, sizeof(block)); (void)memcpy(block, &image[8], 5U); assert(pcan_boot_process(&boot, request_id(PCAN_BOOT_MSG_DATA_B, 1U), block, 8U)); assert(boot.bytes_received == sizeof(image)); assert(boot.next_block == 2U); assert(memcmp(fake.flash[1], image, sizeof(image)) == 0); assert(command(&boot, 7U, PCAN_BOOT_CMD_VERIFY, NULL, 0U)); assert(boot.state == PCAN_BOOT_STATE_VERIFIED); assert(command(&boot, 7U, PCAN_BOOT_CMD_COMMIT, NULL, 0U)); assert(fake.pending_slot == 1U); assert(command(&boot, 7U, PCAN_BOOT_CMD_REBOOT, NULL, 0U)); assert(fake.rebooted); } static void test_rejects_wrong_address_and_sequence(void) { uint8_t image[8] = {0U}; pcan_boot_t boot; fake_t fake; uint32_t other = pcan_boot_make_id(1U, 0U, 3U, 6U, PCAN_BOOT_MSG_CONTROL, (uint16_t)((7U << 8) | PCAN_BOOT_CMD_ENTER_BOOT)); setup(&boot, &fake); assert(!pcan_boot_process(&boot, other, NULL, 0U)); begin_update(&boot, image, sizeof(image)); assert(command(&boot, 7U, PCAN_BOOT_CMD_ERASE, NULL, 0U)); assert(pcan_boot_process(&boot, request_id(PCAN_BOOT_MSG_DATA_B, 1U), image, 8U)); assert(fake.last_data[0] == PCAN_BOOT_STATUS_SEQUENCE_ERROR); assert(fake.writes == 0U); } static void test_rejects_wrong_session_and_compatibility(void) { uint8_t data[8] = {0U}; pcan_boot_t boot; fake_t fake; setup(&boot, &fake); assert(command(&boot, 7U, PCAN_BOOT_CMD_ENTER_BOOT, NULL, 0U)); assert(command(&boot, 8U, PCAN_BOOT_CMD_ERASE, NULL, 0U)); assert(fake.last_data[0] == PCAN_BOOT_STATUS_SESSION_ERROR); put_u32(&data[0], 8U); put_u32(&data[4], 0U); assert(command(&boot, 7U, PCAN_BOOT_CMD_BEGIN_IMAGE, data, 8U)); put_u16(&data[0], 0x9999U); data[2] = 1U; data[3] = 3U; put_u32(&data[4], 1U); assert(command(&boot, 7U, PCAN_BOOT_CMD_BEGIN_COMPAT, data, 8U)); assert(fake.last_data[0] == PCAN_BOOT_STATUS_WRONG_HARDWARE); assert(boot.state == PCAN_BOOT_STATE_FAILED); } int main(void) { test_id_layout(); test_crc32_reference(); test_complete_update(); test_rejects_wrong_address_and_sequence(); test_rejects_wrong_session_and_compatibility(); puts("pcan_boot tests: OK"); return 0; }