blob: a28817aac651c8574b5080c8d1749e1e8a9bfc51 [file] [log] [blame]
/*
* Copyright (c) 2020 Embedded Planet
* Copyright (c) 2020 ARM Limited
* SPDX-License-Identifier: Apache-2.0
*
* 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 <assert.h>
#include <cstring>
#include "flash_map_backend/flash_map_backend.h"
#include "flash_map_backend/secondary_bd.h"
#include "sysflash/sysflash.h"
#include "blockdevice/BlockDevice.h"
#include "FlashIAP/FlashIAPBlockDevice.h"
#include "mcuboot_config/mcuboot_logging.h"
#define FLASH_DEVICE_INTERNAL_FLASH 0
#define FLASH_AREAS 3
/** Application defined secondary block device */
mbed::BlockDevice* mcuboot_secondary_bd = get_secondary_bd();
/** Internal application block device */
static FlashIAPBlockDevice mcuboot_primary_bd(MCUBOOT_PRIMARY_SLOT_START_ADDR, MCUBOOT_SLOT_SIZE);
#ifndef MCUBOOT_OVERWRITE_ONLY
/** Scratch space is at the end of internal flash, after the main application */
static FlashIAPBlockDevice mcuboot_scratch_bd(MCUBOOT_SCRATCH_START_ADDR, MCUBOOT_SCRATCH_SIZE);
#endif
static mbed::BlockDevice* flash_map_bd[FLASH_AREAS] = {
(mbed::BlockDevice*) &mcuboot_primary_bd, /** Primary (loadable) image area */
mcuboot_secondary_bd, /** Secondary (update candidate) image area */
#ifndef MCUBOOT_OVERWRITE_ONLY
(mbed::BlockDevice*) &mcuboot_scratch_bd /** Scratch space for swapping images */
#else
nullptr
#endif
};
static struct flash_area flash_areas[FLASH_AREAS];
int flash_area_open(uint8_t id, const struct flash_area** fapp) {
*fapp = &flash_areas[id];
struct flash_area* fap = (struct flash_area*)*fapp;
// The offset of the slot is from the beginning of the flash device.
switch (id) {
case PRIMARY_ID:
fap->fa_off = MCUBOOT_PRIMARY_SLOT_START_ADDR;
break;
case SECONDARY_ID:
// The offset of the secondary slot is not currently used.
fap->fa_off = 0;
break;
#ifndef MCUBOOT_OVERWRITE_ONLY
case SCRATCH_ID:
fap->fa_off = MCUBOOT_SCRATCH_START_ADDR;
break;
#endif
default:
MCUBOOT_LOG_ERR("flash_area_open, unknown id %d", id);
return -1;
}
fap->fa_id = id;
fap->fa_device_id = 0; // not relevant
mbed::BlockDevice* bd = flash_map_bd[id];
fap->fa_size = (uint32_t) bd->size();
return bd->init();
}
void flash_area_close(const struct flash_area* fap) {
mbed::BlockDevice* bd = flash_map_bd[fap->fa_id];
bd->deinit();
}
/*
* Read/write/erase. Offset is relative from beginning of flash area.
*/
int flash_area_read(const struct flash_area* fap, uint32_t off, void* dst, uint32_t len) {
mbed::BlockDevice* bd = flash_map_bd[fap->fa_id];
// Note: The address must be aligned to bd->get_read_size(). If MCUBOOT_READ_GRANULARITY
// is defined, the length does not need to be aligned.
#ifdef MCUBOOT_READ_GRANULARITY
uint32_t read_size = bd->get_read_size();
if (read_size == 0) {
MCUBOOT_LOG_ERR("Invalid read size: must be non-zero");
return -1;
}
if (MCUBOOT_READ_GRANULARITY < read_size) {
MCUBOOT_LOG_ERR("Please increase MCUBOOT_READ_GRANULARITY (currently %u) to be at least %u",
MCUBOOT_READ_GRANULARITY, read_size);
return -1;
}
uint32_t remainder = len % read_size;
len -= remainder;
if (len != 0) {
#endif
if (!bd->is_valid_read(off, len)) {
MCUBOOT_LOG_ERR("Invalid read: fa_id %d offset 0x%x len 0x%x", fap->fa_id, off, len);
return -1;
}
else {
int ret = bd->read(dst, off, len);
if (ret != 0) {
MCUBOOT_LOG_ERR("Read failed: fa_id %d offset 0x%x len 0x%x", fap->fa_id, off, len);
return ret;
}
}
#ifdef MCUBOOT_READ_GRANULARITY
}
if (remainder) {
if (!bd->is_valid_read(off + len, read_size)) {
MCUBOOT_LOG_ERR("Invalid read: fa_id %d offset 0x%x len 0x%x", fap->fa_id, off + len, read_size);
return -1;
}
else {
uint8_t buffer[MCUBOOT_READ_GRANULARITY];
int ret = bd->read(buffer, off + len, read_size);
if (ret != 0) {
MCUBOOT_LOG_ERR("Read failed: %d", ret);
return ret;
}
memcpy((uint8_t *)dst + len, buffer, remainder);
}
}
#endif
return 0;
}
int flash_area_write(const struct flash_area* fap, uint32_t off, const void* src, uint32_t len) {
mbed::BlockDevice* bd = flash_map_bd[fap->fa_id];
return bd->program(src, off, len);
}
int flash_area_erase(const struct flash_area* fap, uint32_t off, uint32_t len) {
mbed::BlockDevice* bd = flash_map_bd[fap->fa_id];
return bd->erase(off, len);
}
uint8_t flash_area_align(const struct flash_area* fap) {
mbed::BlockDevice* bd = flash_map_bd[fap->fa_id];
return bd->get_program_size();
}
uint8_t flash_area_erased_val(const struct flash_area* fap) {
mbed::BlockDevice* bd = flash_map_bd[fap->fa_id];
return bd->get_erase_value();
}
int flash_area_get_sectors(int fa_id, uint32_t* count, struct flash_sector* sectors) {
mbed::BlockDevice* bd = flash_map_bd[fa_id];
// Loop through sectors and collect information on them
bd_addr_t offset = 0;
*count = 0;
while (*count < MCUBOOT_MAX_IMG_SECTORS && bd->is_valid_read(offset, bd->get_read_size())) {
sectors[*count].fs_off = offset;
bd_size_t erase_size = bd->get_erase_size(offset);
sectors[*count].fs_size = erase_size;
offset += erase_size;
*count += 1;
}
return 0;
}
int flash_area_id_from_image_slot(int slot) {
return slot;
}
int flash_area_id_to_image_slot(int area_id) {
return area_id;
}
/**
* Multi images support not implemented yet
*/
int flash_area_id_from_multi_image_slot(int image_index, int slot)
{
assert(image_index == 0);
return slot;
}
int flash_area_id_to_multi_image_slot(int image_index, int area_id)
{
assert(image_index == 0);
return area_id;
}