blob: d83f2e101fb3e7e38a00450c16a162914c22e925 [file] [log] [blame]
/*
*
* Copyright (c) 2021-2022 Project CHIP Authors
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "OTAImageProcessorImpl.h"
#include <lib/support/CHIPMemString.h>
#include <lib/support/logging/CHIPLogging.h>
#include <platform/CHIPDeviceLayer.h>
#ifdef BOOT_ENABLED
#include "bootutil/bootutil.h"
#include "flash_map_backend/secondary_bd.h"
#include "platform/mbed_power_mgmt.h"
#include "rtos/ThisThread.h"
#endif
using namespace ::chip::DeviceLayer::Internal;
namespace chip {
OTAImageProcessorImpl::OTAImageProcessorImpl()
{
#ifdef BOOT_ENABLED
// Set block device - memory for image update
mBlockDevice = get_secondary_bd();
#endif
}
int OTAImageProcessorImpl::MemoryTest()
{
int ret = 0;
bd_size_t read_size;
bd_size_t program_size;
bd_size_t erase_size;
bd_size_t full_size;
size_t buffer_size;
char * buffer = nullptr;
if (!mBlockDevice)
{
ChipLogError(SoftwareUpdate, "Block device not set");
return 1;
}
// Initialize the block device
ret = mBlockDevice->init();
if (ret)
{
ChipLogError(SoftwareUpdate, "Block device initialization failed [%d]", ret);
goto exit;
}
// Get the block device type
ChipLogProgress(SoftwareUpdate, "Block device type: %s", mBlockDevice->get_type());
// Get device geometry
read_size = mBlockDevice->get_read_size();
program_size = mBlockDevice->get_program_size();
erase_size = mBlockDevice->get_erase_size();
full_size = mBlockDevice->size();
ChipLogProgress(SoftwareUpdate, "--- Block device geometry ---");
ChipLogProgress(SoftwareUpdate, "read_size: %lld B", read_size);
ChipLogProgress(SoftwareUpdate, "program_size: %lld B", program_size);
ChipLogProgress(SoftwareUpdate, "erase_size: %lld B", erase_size);
ChipLogProgress(SoftwareUpdate, "size: %lld B", full_size);
ChipLogProgress(SoftwareUpdate, "---\n");
// Allocate a block with enough space for our data, aligned to the
// nearest program_size. This is the minimum size necessary to write
// data to a block.
buffer_size = sizeof("Hello Storage!") + program_size - 1;
buffer_size = buffer_size - (buffer_size % program_size);
buffer = new char[buffer_size];
// Read what is currently stored on the block device. We haven't written
// yet so this may be garbage
ret = mBlockDevice->read(buffer, 0, buffer_size);
if (ret)
{
ChipLogError(SoftwareUpdate, "Block device read failed [%d]", ret);
goto exit;
}
ChipLogProgress(SoftwareUpdate, "--- Currently stored data ---");
for (size_t i = 0; i < buffer_size; i += 16)
{
for (size_t j = 0; j < 16; j++)
{
if (i + j < buffer_size)
{
ChipLogProgress(SoftwareUpdate, "%02x ", buffer[i + j]);
}
else
{
ChipLogProgress(SoftwareUpdate, " ");
}
}
ChipLogProgress(SoftwareUpdate, " ");
}
ChipLogProgress(SoftwareUpdate, "---\n");
// Write data to first block, write occurs in two parts,
// an erase followed by a program
ret = mBlockDevice->erase(0, erase_size);
if (ret)
{
ChipLogError(SoftwareUpdate, "Block device erase failed [%d]", ret);
goto exit;
}
// Clear the buffer so we don't get old data
memset(buffer, 0x0, buffer_size);
// Read the data from the first block
ret = mBlockDevice->read(buffer, 0, buffer_size);
if (ret)
{
ChipLogError(SoftwareUpdate, "Block device read failed [%d]", ret);
goto exit;
}
ChipLogProgress(SoftwareUpdate, "--- Stored data after erase ---");
for (size_t i = 0; i < buffer_size; i += 16)
{
for (size_t j = 0; j < 16; j++)
{
if (i + j < buffer_size)
{
ChipLogProgress(SoftwareUpdate, "%02x ", buffer[i + j]);
}
else
{
ChipLogProgress(SoftwareUpdate, " ");
}
ChipLogProgress(SoftwareUpdate, " ");
}
}
ChipLogProgress(SoftwareUpdate, "---\n");
// Clear the buffer so we don't get old data
memset(buffer, 0x0, buffer_size);
// Update buffer with our string we want to store
Platform::CopyString(buffer, buffer_size, "Hello Storage!");
ret = mBlockDevice->program(buffer, 0, buffer_size);
if (ret)
{
ChipLogError(SoftwareUpdate, "Block device program failed [%d]", ret);
goto exit;
}
// Clear the buffer so we don't get old data
memset(buffer, 0x0, buffer_size);
// Read the data from the first block, note that the program_size must be
// a multiple of the read_size, so we don't have to check for alignment
ret = mBlockDevice->read(buffer, 0, buffer_size);
if (ret)
{
ChipLogError(SoftwareUpdate, "Block device read failed [%d]", ret);
goto exit;
}
ChipLogProgress(SoftwareUpdate, "--- Stored data after write ---");
for (size_t i = 0; i < buffer_size; i += 16)
{
for (size_t j = 0; j < 16; j++)
{
if (i + j < buffer_size)
{
ChipLogProgress(SoftwareUpdate, "%02x ", buffer[i + j]);
}
else
{
ChipLogProgress(SoftwareUpdate, " ");
}
}
ChipLogProgress(SoftwareUpdate, " %.*s", buffer_size - i, &buffer[i]);
}
ChipLogProgress(SoftwareUpdate, "---\n");
ret = strcmp(buffer, "Hello Storage!");
if (ret)
{
ChipLogError(SoftwareUpdate, "Data compare failed");
}
else
{
ChipLogProgress(SoftwareUpdate, "--- MEMORY TEST PASS ---");
}
exit:
if (ret)
{
ChipLogError(SoftwareUpdate, "--- MEMORY TEST FAILED ---");
}
if (buffer)
{
delete buffer;
}
// Deinitialize the block device
ret = mBlockDevice->deinit();
if (ret)
{
ChipLogError(SoftwareUpdate, "Block deinitialization read failed [%d]", ret);
goto exit;
}
return ret;
}
CHIP_ERROR OTAImageProcessorImpl::PrepareDownload()
{
ChipLogProgress(SoftwareUpdate, "Prepare download");
ClearDownloadParams();
DeviceLayer::PlatformMgr().ScheduleWork(HandlePrepareDownload, reinterpret_cast<intptr_t>(this));
return CHIP_NO_ERROR;
}
CHIP_ERROR OTAImageProcessorImpl::Finalize()
{
ChipLogProgress(SoftwareUpdate, "Finalize");
DeviceLayer::PlatformMgr().ScheduleWork(HandleFinalize, reinterpret_cast<intptr_t>(this));
return CHIP_NO_ERROR;
}
CHIP_ERROR OTAImageProcessorImpl::Apply()
{
ChipLogProgress(SoftwareUpdate, "Apply");
DeviceLayer::PlatformMgr().ScheduleWork(HandleApply, reinterpret_cast<intptr_t>(this));
return CHIP_NO_ERROR;
}
CHIP_ERROR OTAImageProcessorImpl::Abort()
{
ChipLogProgress(SoftwareUpdate, "Abort");
ClearDownloadParams();
DeviceLayer::PlatformMgr().ScheduleWork(HandleAbort, reinterpret_cast<intptr_t>(this));
return CHIP_NO_ERROR;
};
CHIP_ERROR OTAImageProcessorImpl::ProcessBlock(ByteSpan & block)
{
ChipLogProgress(SoftwareUpdate, "Process block");
CHIP_ERROR err = SetBlock(block);
if (err != CHIP_NO_ERROR)
{
ChipLogError(SoftwareUpdate, "Cannot set block data: %" CHIP_ERROR_FORMAT, err.Format());
return err;
}
DeviceLayer::PlatformMgr().ScheduleWork(HandleProcessBlock, reinterpret_cast<intptr_t>(this));
return CHIP_NO_ERROR;
}
void OTAImageProcessorImpl::HandlePrepareDownload(intptr_t context)
{
auto * imageProcessor = reinterpret_cast<OTAImageProcessorImpl *>(context);
if (imageProcessor == nullptr)
{
ChipLogError(SoftwareUpdate, "ImageProcessor context is null");
return;
}
else if (imageProcessor->mDownloader == nullptr)
{
ChipLogError(SoftwareUpdate, "mDownloader is null");
return;
}
auto ret = imageProcessor->PrepareMemory();
if (ret)
{
ChipLogError(SoftwareUpdate, "Prepare download memory failed");
imageProcessor->mDownloader->OnPreparedForDownload(CHIP_ERROR_INTERNAL);
return;
}
ret = imageProcessor->ClearMemory();
if (ret)
{
ChipLogError(SoftwareUpdate, "Clear download memory failed");
imageProcessor->mDownloader->OnPreparedForDownload(CHIP_ERROR_INTERNAL);
return;
}
imageProcessor->mDownloader->OnPreparedForDownload(CHIP_NO_ERROR);
}
void OTAImageProcessorImpl::HandleFinalize(intptr_t context)
{
auto * imageProcessor = reinterpret_cast<OTAImageProcessorImpl *>(context);
if (imageProcessor == nullptr)
{
ChipLogError(SoftwareUpdate, "ImageProcessor context is null");
return;
}
imageProcessor->mParams.totalFileBytes = imageProcessor->mParams.downloadedBytes;
imageProcessor->ReleaseBlock();
auto ret = imageProcessor->CloseMemory();
if (ret)
{
ChipLogError(SoftwareUpdate, "Close download memory failed");
}
ChipLogProgress(SoftwareUpdate, "OTA image downloaded size %lldB", imageProcessor->mParams.totalFileBytes);
}
void OTAImageProcessorImpl::HandleAbort(intptr_t context)
{
auto * imageProcessor = reinterpret_cast<OTAImageProcessorImpl *>(context);
if (imageProcessor == nullptr)
{
ChipLogError(SoftwareUpdate, "ImageProcessor context is null");
return;
}
auto ret = imageProcessor->ClearMemory();
if (ret)
{
ChipLogError(SoftwareUpdate, "Clear download memory failed");
}
ret = imageProcessor->CloseMemory();
if (ret)
{
ChipLogError(SoftwareUpdate, "Close download memory failed");
}
imageProcessor->ReleaseBlock();
}
void OTAImageProcessorImpl::HandleProcessBlock(intptr_t context)
{
auto * imageProcessor = reinterpret_cast<OTAImageProcessorImpl *>(context);
if (imageProcessor == nullptr)
{
ChipLogError(SoftwareUpdate, "ImageProcessor context is null");
return;
}
else if (imageProcessor->mDownloader == nullptr)
{
ChipLogError(SoftwareUpdate, "mDownloader is null");
return;
}
auto ret = imageProcessor->ProgramMemory();
if (ret)
{
ChipLogError(SoftwareUpdate, "Program download memory failed");
}
imageProcessor->mDownloader->FetchNextData();
}
void OTAImageProcessorImpl::HandleApply(intptr_t context)
{
auto * imageProcessor = reinterpret_cast<OTAImageProcessorImpl *>(context);
if (imageProcessor == nullptr)
{
ChipLogError(SoftwareUpdate, "ImageProcessor context is null");
return;
}
#ifdef BOOT_ENABLED
ChipLogProgress(SoftwareUpdate, "Set secondary image pending");
auto ret = boot_set_pending_multi(/*image index*/ 0, /*permanent*/ 0);
if (ret)
{
ChipLogError(SoftwareUpdate, "Setting the update candidate as pending failed: %d", ret);
}
// Restart the device
ChipLogProgress(SoftwareUpdate, "Device restarting....");
rtos::ThisThread::sleep_for(3000);
system_reset();
#endif
}
CHIP_ERROR OTAImageProcessorImpl::SetBlock(ByteSpan & block)
{
if (!IsSpanUsable(block))
{
ReleaseBlock();
return CHIP_NO_ERROR;
}
if (mBlock.size() < block.size())
{
if (!mBlock.empty())
{
ReleaseBlock();
}
size_t buffer_size = 0;
if (mBlockDevice)
{
// Aligned to the nearest program_size. This is the minimum size necessary to write data to a block.
bd_size_t program_size = mBlockDevice->get_program_size();
buffer_size = block.size() + program_size - 1;
buffer_size = buffer_size - (buffer_size % program_size);
}
else
{
buffer_size = block.size();
}
uint8_t * mBlock_ptr = static_cast<uint8_t *>(chip::Platform::MemoryAlloc(buffer_size));
if (mBlock_ptr == nullptr)
{
return CHIP_ERROR_NO_MEMORY;
}
mBlock = MutableByteSpan(mBlock_ptr, buffer_size);
}
CHIP_ERROR err = CopySpanToMutableSpan(block, mBlock);
if (err != CHIP_NO_ERROR)
{
ChipLogError(SoftwareUpdate, "Cannot copy block data: %" CHIP_ERROR_FORMAT, err.Format());
return err;
}
return CHIP_NO_ERROR;
}
CHIP_ERROR OTAImageProcessorImpl::ReleaseBlock()
{
if (mBlock.data() != nullptr)
{
chip::Platform::MemoryFree(mBlock.data());
}
mBlock = MutableByteSpan();
return CHIP_NO_ERROR;
}
void OTAImageProcessorImpl::ClearDownloadParams()
{
mParams.downloadedBytes = 0;
mParams.totalFileBytes = 0;
}
int OTAImageProcessorImpl::PrepareMemory()
{
int ret = 0;
if (!mBlockDevice)
{
ChipLogError(SoftwareUpdate, "Block device not set");
return 1;
}
// Initialize the block device
ret = mBlockDevice->init();
if (ret)
{
ChipLogError(SoftwareUpdate, "Block device initialization failed [%d]", ret);
return ret;
}
ChipLogProgress(SoftwareUpdate, "Block device initialize");
// Initialization read from the block device
bd_size_t read_size = mBlockDevice->get_read_size();
char buff[read_size];
ret = mBlockDevice->read(&buff, 0, read_size);
if (ret)
{
ChipLogError(SoftwareUpdate, "Block device read failed [%d]", ret);
return ret;
}
return ret;
}
int OTAImageProcessorImpl::CloseMemory()
{
int ret = 0;
if (!mBlockDevice)
{
ChipLogError(SoftwareUpdate, "Block device not set");
return 1;
}
// Deinitialize the block device
ret = mBlockDevice->deinit();
if (ret)
{
ChipLogError(SoftwareUpdate, "Block device deinitialization failed [%d]", ret);
return ret;
}
ChipLogProgress(SoftwareUpdate, "Block device deinitialization");
return ret;
}
int OTAImageProcessorImpl::ClearMemory()
{
int ret = 0;
if (!mBlockDevice)
{
ChipLogError(SoftwareUpdate, "Block device not set");
return 1;
}
// Erase memory of block device
ret = mBlockDevice->erase(0, mBlockDevice->size());
if (ret)
{
ChipLogError(SoftwareUpdate, "Erase block device failed [%d]", ret);
return ret;
}
return ret;
}
int OTAImageProcessorImpl::ProgramMemory()
{
int ret = 0;
if (!mBlockDevice)
{
ChipLogError(SoftwareUpdate, "Block device not set");
return 1;
}
// Write data to memory
ret = mBlockDevice->program(mBlock.data(), mParams.downloadedBytes, mBlock.size());
if (ret)
{
ChipLogError(SoftwareUpdate, "Block device program failed [%d]", ret);
return ret;
}
ChipLogProgress(SoftwareUpdate,
"Secondary slot program with offset: "
"0x%" PRIx64,
mParams.downloadedBytes);
mParams.downloadedBytes += mBlock.size();
return ret;
}
} // namespace chip