aboutsummaryrefslogtreecommitdiff
path: root/bl2
diff options
context:
space:
mode:
authorSherry Zhnag <sherry.zhang2@arm.com>2021-08-05 16:55:27 +0800
committerAnton Komlev <Anton.Komlev@arm.com>2021-09-08 07:39:44 +0200
commit3fb2687d981465ca83c661774eb5f7dbe08e8379 (patch)
tree75891e5196b637af43f00d00c5968651fddd0a5c /bl2
parent956dc40ce81bf0c8d6c617e18e1084291a3fa912 (diff)
downloadtrusted-firmware-m-3fb2687d981465ca83c661774eb5f7dbe08e8379.tar.gz
Boot & FWU: Support flash write with unaligned address/size
MCUboot and FWU can call the flash_area_write to write the flash with. Failure happens in the flash driver if the target address or the write size is not program unit aligned. Signed-off-by: Sherry Zhnag <sherry.zhang2@arm.com> Change-Id: I8b4eeae7348903805a9ffc12cac5b9106d09c1a6
Diffstat (limited to 'bl2')
-rw-r--r--bl2/src/flash_map.c212
1 files changed, 210 insertions, 2 deletions
diff --git a/bl2/src/flash_map.c b/bl2/src/flash_map.c
index a45355a5e5..112750dfbe 100644
--- a/bl2/src/flash_map.c
+++ b/bl2/src/flash_map.c
@@ -13,6 +13,20 @@
#include "bootutil/bootutil_log.h"
#include "Driver_Flash.h"
+#define FLASH_PROGRAM_UNIT TFM_HAL_FLASH_PROGRAM_UNIT
+
+/**
+ * Return the greatest value not greater than `value` that is aligned to
+ * `alignment`.
+ */
+#define FLOOR_ALIGN(value, alignment) ((value) & ~((alignment) - 1))
+
+/**
+ * Return the least value not less than `value` that is aligned to `alignment`.
+ */
+#define CEILING_ALIGN(value, alignment) \
+ (((value) + ((alignment) - 1)) & ~((alignment) - 1))
+
extern const struct flash_area flash_map[];
extern const int flash_map_entry_num;
@@ -68,28 +82,222 @@ void flash_area_close(const struct flash_area *area)
/* Nothing to do. */
}
+/*
+ * Read/write/erase. Offset is relative from beginning of flash area.
+ * `off` and `len` can be any alignment.
+ * Return 0 on success, other value on failure.
+ */
int flash_area_read(const struct flash_area *area, uint32_t off, void *dst,
uint32_t len)
{
+ uint32_t remaining_len;
+ uint32_t aligned_off;
+ uint8_t temp_buffer[sizeof(uint32_t)];
+ uint8_t align_unit, i = 0;
+ int ret;
+
+ /* Valid entries for data item width */
+ uint32_t data_width_byte[] = {
+ sizeof(uint8_t),
+ sizeof(uint16_t),
+ sizeof(uint32_t),
+ };
+ ARM_FLASH_CAPABILITIES DriverCapabilities;
+
BOOT_LOG_DBG("read area=%d, off=%#x, len=%#x", area->fa_id, off, len);
if (!is_range_valid(area, off, len)) {
return -1;
}
+ remaining_len = len;
+
+ /* CMSIS ARM_FLASH_ReadData API requires the `addr` data type size aligned.
+ * Data type size is specified by the data_width in ARM_FLASH_CAPABILITIES.
+ */
+ DriverCapabilities = DRV_FLASH_AREA(area)->GetCapabilities();
+ align_unit = data_width_byte[DriverCapabilities.data_width];
+ aligned_off = FLOOR_ALIGN(off, align_unit);
+
+ /* Read the first align_unit long data if `off` is not aligned. */
+ if (aligned_off != off) {
+ ret = DRV_FLASH_AREA(area)->ReadData(area->fa_off + aligned_off,
+ temp_buffer,
+ align_unit);
+ if (ret < 0) {
+ return ret;
+ }
+
+ /* Copy the read data from off. */
+ for (i = 0; i + off - aligned_off < align_unit; i++) {
+ ((uint8_t *)dst)[i] = temp_buffer[i + off - aligned_off];
+ }
+ remaining_len -= align_unit - (off - aligned_off);
+ }
+
+ /* CMSIS ARM_FLASH_ReadData does not require the alignment of `cnt`.*/
+ if (remaining_len) {
+ ret = DRV_FLASH_AREA(area)->ReadData(area->fa_off + off + i,
+ (uint8_t *)dst + i,
+ remaining_len);
+ }
- return DRV_FLASH_AREA(area)->ReadData(area->fa_off + off, dst, len);
+ /* CMSIS ARM_FLASH_ReadData can return the number of data items read or
+ * Status Error Codes which are negative for failures.
+ */
+ if (ret < 0) {
+ return ret;
+ } else {
+ return 0;
+ }
}
+/* Writes `len` bytes of flash memory at `off` from the buffer at `src`.
+ * `off` and `len` can be any alignment.
+ */
int flash_area_write(const struct flash_area *area, uint32_t off,
const void *src, uint32_t len)
{
+ uint8_t add_padding[FLASH_PROGRAM_UNIT];
+ uint8_t len_padding[FLASH_PROGRAM_UNIT - 1];
+
+ /* The PROGRAM_UNIT aligned value of `off` */
+ uint32_t aligned_off;
+
+ /* The total write length. */
+ uint32_t aligned_len;
+ uint32_t i, k;
+
+ /* The index in src[] that has been programmed. */
+ uint32_t src_written_idx = 0;
+ uint32_t add_padding_size, len_padding_size;
+ uint32_t write_size;
+ uint32_t last_unit_start_off = 0;
+ /*
+ * aligned_off off last_unit_start_off
+ * | | |
+ * | add_padding_size | | | len_padding_size |
+ * |+++++++++++++++++++**|******************|***@@@@@@@@@@@@@@@@@@@@|
+ * | | | |
+ * ---->--|---- PROGRAM UNIT ---|-- PROGRAM UNIT --|---- PROGRAM UNIT -----|
+ * | | | |
+ * |+++++++++++++++++++**|******************|***@@@@@@@@@@@@@@@@@@@@|
+ * |<-------- len --------->|
+ */
+
BOOT_LOG_DBG("write area=%d, off=%#x, len=%#x", area->fa_id, off, len);
+ /* Align the target address. The area->fa_off should already be aligned. */
+ aligned_off = FLOOR_ALIGN(off, FLASH_PROGRAM_UNIT);
+ add_padding_size = off - aligned_off;
if (!is_range_valid(area, off, len)) {
return -1;
}
- return DRV_FLASH_AREA(area)->ProgramData(area->fa_off + off, src, len);
+ /* Read the bytes from aligned_off to off. */
+ if (flash_area_read(area, aligned_off, add_padding, add_padding_size)) {
+ return -1;
+ }
+
+ /* Align the write size */
+ aligned_len = CEILING_ALIGN(len + add_padding_size, FLASH_PROGRAM_UNIT);
+ len_padding_size = aligned_len - len - add_padding_size;
+ if (!is_range_valid(area, aligned_off, aligned_len)) {
+ return -1;
+ }
+
+ /* Read the bytes from (off + len) to (off + aligned_len). */
+ if (flash_area_read(area, off + len, len_padding,
+ len_padding_size)) {
+ return -1;
+ }
+
+ /* Program the first FLASH_PROGRAM_UNIT. */
+ if (add_padding_size) {
+ /* Fill the first program unit bytes with data from src. */
+ for (i = add_padding_size, src_written_idx = 0;
+ i < FLASH_PROGRAM_UNIT && src_written_idx < len;
+ i++, src_written_idx++) {
+ add_padding[i] = ((uint8_t *)src)[src_written_idx];
+ }
+ if (src_written_idx == len) {
+ /* aligned_len equals to FLASH_PROGRAM_UNIT in this case.
+ * Fill the len_padding_size datas into add_padding.
+ */
+ for (k = 0; i < FLASH_PROGRAM_UNIT && k < len_padding_size;
+ i++, k++) {
+ add_padding[i] = len_padding[k];
+ }
+ if (k != len_padding_size) {
+ return -1;
+ }
+ }
+
+ /* Check the first program unit bytes are all filled. */
+ if (i != FLASH_PROGRAM_UNIT) {
+ return -1;
+ }
+ if (DRV_FLASH_AREA(area)->ProgramData(area->fa_off + aligned_off,
+ add_padding,
+ FLASH_PROGRAM_UNIT)) {
+ return -1;
+ }
+ }
+
+ /* 'src_written_idx' indicates the number of the src data which has already
+ * been programed into flash. 'src_written_idx' equals to 'len' means that
+ * all the data in src has been programmed and aligned_len equals to
+ * FLASH_PROGRAM_UNIT. This case has been handled above.
+ * 'src_written_idx' less than 'len' means that not all the data in src has
+ * been programmed.
+ */
+ if (src_written_idx < len) {
+ /* Program from the first aligned bytes(src_written_idx) to the last
+ * aligned bytes in src.
+ */
+ write_size = FLOOR_ALIGN(len - src_written_idx, FLASH_PROGRAM_UNIT);
+ if (write_size > 0) {
+ if (DRV_FLASH_AREA(area)->ProgramData(
+ area->fa_off + off + src_written_idx,
+ src,
+ write_size)) {
+ return -1;
+ }
+ src_written_idx += write_size;
+ }
+ last_unit_start_off = src_written_idx;
+
+ /* Program the last program unit data into flash. */
+ if (len_padding_size) {
+ /* Copy the last unaligned bytes in src to add_padding. */
+ for (i = 0; i < FLASH_PROGRAM_UNIT && src_written_idx < len;
+ i++, src_written_idx++) {
+ add_padding[i] = ((uint8_t *)src)[src_written_idx];
+ }
+
+ if (src_written_idx != len) {
+ return -1;
+ }
+ /* Copy the len_padding_size bytes in len_padding to add_padding. */
+ for (k = 0; i < FLASH_PROGRAM_UNIT && k < len_padding_size;
+ i++, k++) {
+ add_padding[i] = len_padding[k];
+ }
+ write_size = add_padding_size + last_unit_start_off +
+ FLASH_PROGRAM_UNIT;
+ if (i != FLASH_PROGRAM_UNIT || k != len_padding_size ||
+ aligned_len != write_size) {
+ return -1;
+ }
+ if (DRV_FLASH_AREA(area)->ProgramData(
+ area->fa_off + off + last_unit_start_off,
+ add_padding,
+ FLASH_PROGRAM_UNIT)) {
+ return -1;
+ }
+ }
+ }
+
+ return 0;
}
int flash_area_erase(const struct flash_area *area, uint32_t off, uint32_t len)