blob: c096b6333e8beb7c65fb52e658a77109b544518a [file] [log] [blame]
/*
* Copyright (c) 2021-2022 Arm Limited
*
* 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 "emulated_flash_drv.h"
#include <arm_cmse.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
static int32_t is_range_valid(struct emulated_flash_dev_t * flash_dev, uint32_t offset)
{
uint32_t flash_limit = 0;
int32_t rc = 0;
flash_limit = (flash_dev->data->sector_count * flash_dev->data->sector_size) - 1;
if (offset > flash_limit)
{
rc = -1;
}
return rc;
}
static int32_t is_write_aligned(struct emulated_flash_dev_t * flash_dev, uint32_t param)
{
int32_t rc = 0;
if ((param % flash_dev->data->program_unit) != 0)
{
rc = -1;
}
return rc;
}
static int32_t is_sector_aligned(struct emulated_flash_dev_t * flash_dev, uint32_t offset)
{
int32_t rc = 0;
if ((offset % flash_dev->data->sector_size) != 0)
{
rc = -1;
}
return rc;
}
static int32_t is_secure_alias_needed(uint32_t addr)
{
int32_t rc = -1;
/* Only have to check it if the object is building for secure side */
#if defined(__ARM_FEATURE_CMSE) && (__ARM_FEATURE_CMSE == 3U)
cmse_address_info_t address_info;
/* Check if address can be accessed from non-secure */
address_info = cmse_TTA((void *) addr);
/* We only care about the security of the address here */
if (address_info.flags.nonsecure_read_ok)
{
rc = 0;
}
else
{
rc = 1;
}
#else
rc = 0;
#endif
return rc;
}
static int32_t is_flash_ready_to_write(const uint8_t * start_addr, uint32_t cnt)
{
int32_t rc = 0;
uint32_t i;
for (i = 0; i < cnt; i++)
{
if (start_addr[i] != EMULATED_FLASH_DRV_ERASE_VALUE)
{
rc = -1;
break;
}
}
return rc;
}
enum emulated_flash_error_t emulated_flash_read_data(struct emulated_flash_dev_t * dev, uint32_t addr, void * data, uint32_t cnt)
{
uint32_t start_addr = 0;
int32_t rc = 0;
/* Check flash memory boundaries */
rc = is_range_valid(dev, addr + cnt);
if (rc != 0)
{
return EMULATED_FLASH_ERR_INVALID_PARAM;
}
/* Check which alias(S or NS) should be used to access the data */
rc = is_secure_alias_needed(addr + dev->memory_base_ns);
if (rc == 1)
{
start_addr = dev->memory_base_s + addr;
}
else if (rc == 0)
{
start_addr = dev->memory_base_ns + addr;
}
/* Flash interface just emulated over SRAM, use memcpy */
memcpy(data, (void *) start_addr, cnt);
return EMULATED_FLASH_ERR_NONE;
}
enum emulated_flash_error_t emulated_flash_program_data(struct emulated_flash_dev_t * dev, uint32_t addr, const void * data,
uint32_t cnt)
{
uint32_t start_addr = 0;
int32_t rc = 0;
/* Check flash memory boundaries and alignment with minimal write size */
rc = is_range_valid(dev, addr + cnt);
rc |= is_write_aligned(dev, addr);
rc |= is_write_aligned(dev, cnt);
if (rc != 0)
{
return EMULATED_FLASH_ERR_INVALID_PARAM;
}
/* Check which alias(S or NS) should be used to access the data */
rc = is_secure_alias_needed(addr + dev->memory_base_ns);
if (rc == 1)
{
start_addr = dev->memory_base_s + addr;
}
else if (rc == 0)
{
start_addr = dev->memory_base_ns + addr;
}
/* Check if the flash area to write the data was erased previously */
rc = is_flash_ready_to_write((const uint8_t *) start_addr, cnt);
if (rc != 0)
{
return EMULATED_FLASH_NOT_READY;
}
/* Flash interface just emulated over SRAM, use memcpy */
memcpy((void *) start_addr, data, cnt);
return EMULATED_FLASH_ERR_NONE;
}
enum emulated_flash_error_t emulated_flash_erase_sector(struct emulated_flash_dev_t * dev, uint32_t addr)
{
uint32_t start_addr = 0;
int32_t rc = 0;
rc = is_range_valid(dev, addr);
rc |= is_sector_aligned(dev, addr);
if (rc != 0)
{
return EMULATED_FLASH_ERR_INVALID_PARAM;
}
/* Check which alias(S or NS) should be used to access the data */
rc = is_secure_alias_needed(addr + dev->memory_base_ns);
if (rc == 1)
{
start_addr = dev->memory_base_s + addr;
}
else if (rc == 0)
{
start_addr = dev->memory_base_ns + addr;
}
/* Flash interface just emulated over SRAM, use memset */
memset((void *) start_addr, dev->data->erased_value, dev->data->sector_size);
return EMULATED_FLASH_ERR_NONE;
}
void emulated_flash_erase_chip(struct emulated_flash_dev_t * dev)
{
uint32_t i;
uint32_t addr = 0;
int32_t rc = 0;
/* Only check 1 byte, as the whole memory should have the same security */
rc = is_secure_alias_needed(dev->memory_base_ns);
if (rc == 1)
{
addr = dev->memory_base_s;
}
else if (rc == 0)
{
addr = dev->memory_base_ns;
}
for (i = 0; i < dev->data->sector_count; i++)
{
/* Flash interface just emulated over SRAM, use memset */
memset((void *) addr, dev->data->erased_value, dev->data->sector_size);
addr += dev->data->sector_size;
}
}
ARM_FLASH_INFO * emulated_flash_get_info(struct emulated_flash_dev_t * dev)
{
return dev->data;
}