diff --git a/protocol/payload_update.c b/protocol/payload_update.c index f8a1d6b..c66fbc6 100644 --- a/protocol/payload_update.c +++ b/protocol/payload_update.c @@ -115,16 +115,16 @@ static libhoth_error payload_update_erase_chunk( &request, sizeof(request), NULL, 0, NULL); } +static const size_t kBlockErase = 64 * 1024; +static const size_t kSectorErase = 4 * 1024; + libhoth_error libhoth_payload_update_erase(struct libhoth_device* const dev, const uint32_t offset, const uint32_t len) { struct libhoth_progress_stderr erase_progress; libhoth_progress_stderr_init(&erase_progress, "Erase staging side"); - const size_t block_erase = 64 * 1024; - const size_t sector_erase = 4 * 1024; - - if (len == 0 || (len % sector_erase) != 0) { + if (len == 0 || (len % kSectorErase) != 0) { fprintf(stderr, "error: erase length (0x%" PRIx32 ") is zero or not sector-aligned.\n", @@ -132,7 +132,7 @@ libhoth_error libhoth_payload_update_erase(struct libhoth_device* const dev, return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, LIBHOTH_ERR_IMAGE_NOT_SECTOR_ALIGNED); } - if ((offset % sector_erase) != 0) { + if ((offset % kSectorErase) != 0) { fprintf(stderr, "error: offset (0x%" PRIx32 ") is not sector-aligned.\n", offset); return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, @@ -154,8 +154,8 @@ libhoth_error libhoth_payload_update_erase(struct libhoth_device* const dev, const uint32_t current_offset = offset + erased; const uint32_t remaining = len - erased; const bool send_block_erase = - (current_offset % block_erase == 0) && (remaining >= block_erase); - const uint32_t chunk_size = send_block_erase ? block_erase : sector_erase; + (current_offset % kBlockErase == 0) && (remaining >= kBlockErase); + const uint32_t chunk_size = send_block_erase ? kBlockErase : kSectorErase; const libhoth_error ret = payload_update_erase_chunk(dev, current_offset, chunk_size); if (ret != HOTH_SUCCESS) { @@ -178,7 +178,21 @@ libhoth_error libhoth_payload_update(struct libhoth_device* dev, uint8_t* image, } if (!skip_erase) { - libhoth_error err = libhoth_payload_update_erase(dev, 0, size); + size_t erase_size = size; + if (binary_file && (erase_size % kSectorErase) != 0) { + const size_t pad = kSectorErase - (erase_size % kSectorErase); + if (erase_size > UINT32_MAX - pad) { + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + LIBHOTH_ERR_INVALID_PARAMETER); + } + erase_size += pad; + } + if (erase_size > UINT32_MAX) { + return LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + LIBHOTH_ERR_INVALID_PARAMETER); + } + libhoth_error err = + libhoth_payload_update_erase(dev, 0, (uint32_t)erase_size); if (err != HOTH_SUCCESS) { return err; } diff --git a/protocol/payload_update_test.cc b/protocol/payload_update_test.cc index 730740c..c59d37e 100644 --- a/protocol/payload_update_test.cc +++ b/protocol/payload_update_test.cc @@ -41,6 +41,8 @@ constexpr int kCmd = constexpr int64_t kMagic = 0x5F435344474D495F; constexpr int64_t kAlign = 1 << 16; constexpr int64_t kDummy = 0; +constexpr size_t kBlockErase = 64 * 1024; +constexpr size_t kSectorErase = 4 * 1024; MATCHER_P2(IsEraseRequest, offset, len, "") { const struct hoth_host_request* req = @@ -461,6 +463,66 @@ TEST_F(LibHothTest, payload_update_test_with_binary_image) { HOTH_SUCCESS); } +TEST_F(LibHothTest, payload_update_unaligned_binary_rounds_up_erase) { + constexpr size_t kUnalignedSize = kBlockErase + 100; + uint8_t buffer[kUnalignedSize]; + std::memset(buffer, 0xFF, kUnalignedSize); + buffer[0] = 0xAA; + + { + InSequence s; + + // 64KB Block Erase for [0, 64KB) + EXPECT_CALL(mock_, send(_, IsEraseRequest(0, kBlockErase), _)) + .WillOnce(Return(LIBHOTH_OK)); + EXPECT_CALL(mock_, receive) + .WillOnce(DoAll(CopyResp(&kDummy, 0), Return(LIBHOTH_OK))); + + // 4KB Sector Erase for [64KB, 68KB) rounded up from 64KB + 100B + EXPECT_CALL(mock_, send(_, IsEraseRequest(kBlockErase, kSectorErase), _)) + .WillOnce(Return(LIBHOTH_OK)); + EXPECT_CALL(mock_, receive) + .WillOnce(DoAll(CopyResp(&kDummy, 0), Return(LIBHOTH_OK))); + + // Flash payload data + EXPECT_CALL(mock_, send(_, UsesCommand(kCmd), _)) + .WillOnce(Return(LIBHOTH_OK)); + EXPECT_CALL(mock_, receive) + .WillOnce(DoAll(CopyResp(&kDummy, 0), Return(LIBHOTH_OK))); + } + + EXPECT_EQ(libhoth_payload_update(&hoth_dev_, buffer, kUnalignedSize, + /*skip_erase=*/false, + /*binary_file=*/true), + HOTH_SUCCESS); +} + +TEST_F(LibHothTest, payload_update_unaligned_non_binary_fails) { + constexpr size_t kUnalignedSize = kSectorErase + 100; + uint8_t buffer[kUnalignedSize] = {0}; + + struct image_descriptor desc = {}; + desc.descriptor_magic = TITAN_IMAGE_DESCRIPTOR_MAGIC; + desc.descriptor_area_size = sizeof(desc); + std::memcpy(buffer, &desc, sizeof(desc)); + + EXPECT_EQ(libhoth_payload_update(&hoth_dev_, buffer, kUnalignedSize, + /*skip_erase=*/false, + /*binary_file=*/false), + LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + LIBHOTH_ERR_IMAGE_NOT_SECTOR_ALIGNED)); +} + +TEST_F(LibHothTest, payload_update_unaligned_binary_erase_overflow_fails) { + uint8_t dummy = 0; + // Rounding 0xFFFFF001 up to the next 4 KiB boundary would overflow uint32_t. + EXPECT_EQ(libhoth_payload_update(&hoth_dev_, &dummy, 0xFFFFF001u, + /*skip_erase=*/false, + /*binary_file=*/true), + LIBHOTH_ERR_CONSTRUCT(HOTH_CTX_CMD_EXEC, HOTH_HOST_SPACE_LIBHOTH, + LIBHOTH_ERR_INVALID_PARAMETER)); +} + TEST_F(LibHothTest, payload_update_erase_cmd_test) { constexpr size_t kBlockErase = 64 * 1024; constexpr size_t kSectorErase = 4 * 1024;