From 1db9afdbf70eb9708640debe5d7d24558fe0f63a Mon Sep 17 00:00:00 2001 From: Mohamed Omar Asaker Date: Mon, 7 Nov 2022 12:49:11 +0000 Subject: [PATCH 01/10] Platform: corstone1000: Introduce IO framework - Introduce IO storage framework - Add IO flash to abstract flash implementation details from upper layer Signed-off-by: Mohamed Omar Asaker Upstream-Status: Accepted [TF-Mv1.8.0] --- .../target/arm/corstone1000/CMakeLists.txt | 4 + .../ext/target/arm/corstone1000/io/io_block.c | 527 ++++++++++++++++++ .../ext/target/arm/corstone1000/io/io_block.h | 40 ++ .../ext/target/arm/corstone1000/io/io_defs.h | 27 + .../target/arm/corstone1000/io/io_driver.h | 54 ++ .../ext/target/arm/corstone1000/io/io_flash.c | 183 ++++++ .../ext/target/arm/corstone1000/io/io_flash.h | 37 ++ .../target/arm/corstone1000/io/io_storage.c | 289 ++++++++++ .../target/arm/corstone1000/io/io_storage.h | 92 +++ 9 files changed, 1253 insertions(+) create mode 100644 platform/ext/target/arm/corstone1000/io/io_block.c create mode 100644 platform/ext/target/arm/corstone1000/io/io_block.h create mode 100644 platform/ext/target/arm/corstone1000/io/io_defs.h create mode 100644 platform/ext/target/arm/corstone1000/io/io_driver.h create mode 100644 platform/ext/target/arm/corstone1000/io/io_flash.c create mode 100644 platform/ext/target/arm/corstone1000/io/io_flash.h create mode 100644 platform/ext/target/arm/corstone1000/io/io_storage.c create mode 100644 platform/ext/target/arm/corstone1000/io/io_storage.h diff --git a/platform/ext/target/arm/corstone1000/CMakeLists.txt b/platform/ext/target/arm/corstone1000/CMakeLists.txt index cfbaffc995..7808efae68 100644 --- a/platform/ext/target/arm/corstone1000/CMakeLists.txt +++ b/platform/ext/target/arm/corstone1000/CMakeLists.txt @@ -125,6 +125,9 @@ target_sources(platform_bl2 fw_update_agent/fwu_agent.c bl2_security_cnt.c $<$>:${PLATFORM_DIR}/ext/accelerator/cc312/otp_cc312.c> + io/io_block.c + io/io_flash.c + io/io_storage.c ) if (PLATFORM_IS_FVP) @@ -182,6 +185,7 @@ target_include_directories(platform_bl2 fip_parser Native_Driver fw_update_agent + io . INTERFACE cc312 diff --git a/platform/ext/target/arm/corstone1000/io/io_block.c b/platform/ext/target/arm/corstone1000/io/io_block.c new file mode 100644 index 0000000000..f7eaf7444c --- /dev/null +++ b/platform/ext/target/arm/corstone1000/io/io_block.c @@ -0,0 +1,527 @@ +/* + * Copyright (c) 2022 Arm Limited. All rights reserved. + * + * 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 "io_block.h" + +#include +#include + +#include "io_defs.h" +#include "io_driver.h" +#include "io_storage.h" + +typedef struct { + io_block_dev_spec_t *dev_spec; + uintptr_t base; + uint32_t file_pos; + uint32_t size; +} block_dev_state_t; + +#define is_power_of_2(x) (((x) != 0U) && (((x) & ((x)-1U)) == 0U)) + +io_type_t device_type_block(void); + +static int block_open(io_dev_info_t *dev_info, const uintptr_t spec, + io_entity_t *entity); +static int block_seek(io_entity_t *entity, int mode, size_t offset); +static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length, + size_t *length_read); +static int block_write(io_entity_t *entity, const uintptr_t buffer, + size_t length, size_t *length_written); +static int block_close(io_entity_t *entity); +static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info); +static int block_dev_close(io_dev_info_t *dev_info); + +static const io_dev_connector_t block_dev_connector = {.dev_open = + block_dev_open}; + +static const io_dev_funcs_t block_dev_funcs = { + .type = device_type_block, + .open = block_open, + .seek = block_seek, + .size = NULL, + .read = block_read, + .write = block_write, + .close = block_close, + .dev_init = NULL, + .dev_close = block_dev_close, +}; + +static block_dev_state_t state_pool[MAX_IO_BLOCK_DEVICES]; +static io_dev_info_t dev_info_pool[MAX_IO_BLOCK_DEVICES]; + +/* Track number of allocated block state */ +static unsigned int block_dev_count; + +io_type_t device_type_block(void) { return IO_TYPE_BLOCK; } + +/* Locate a block state in the pool, specified by address */ +static int find_first_block_state(const io_block_dev_spec_t *dev_spec, + unsigned int *index_out) { + unsigned int index; + int result = -ENOENT; + + for (index = 0U; index < MAX_IO_BLOCK_DEVICES; ++index) { + /* dev_spec is used as identifier since it's unique */ + if (state_pool[index].dev_spec == dev_spec) { + result = 0; + *index_out = index; + break; + } + } + return result; +} + +/* Allocate a device info from the pool and return a pointer to it */ +static int allocate_dev_info(io_dev_info_t **dev_info) { + int result = -ENOMEM; + assert(dev_info != NULL); + + if (block_dev_count < MAX_IO_BLOCK_DEVICES) { + unsigned int index = 0; + result = find_first_block_state(NULL, &index); + assert(result == 0); + /* initialize dev_info */ + dev_info_pool[index].funcs = &block_dev_funcs; + dev_info_pool[index].info = (uintptr_t)&state_pool[index]; + *dev_info = &dev_info_pool[index]; + ++block_dev_count; + } + + return result; +} + +/* Release a device info to the pool */ +static int free_dev_info(io_dev_info_t *dev_info) { + int result; + unsigned int index = 0; + block_dev_state_t *state; + assert(dev_info != NULL); + + state = (block_dev_state_t *)dev_info->info; + result = find_first_block_state(state->dev_spec, &index); + if (result == 0) { + /* free if device info is valid */ + memset(state, 0, sizeof(block_dev_state_t)); + memset(dev_info, 0, sizeof(io_dev_info_t)); + --block_dev_count; + } + + return result; +} + +static int block_open(io_dev_info_t *dev_info, const uintptr_t spec, + io_entity_t *entity) { + block_dev_state_t *cur; + io_block_spec_t *region; + + assert((dev_info->info != (uintptr_t)NULL) && (spec != (uintptr_t)NULL) && + (entity->info == (uintptr_t)NULL)); + + region = (io_block_spec_t *)spec; + cur = (block_dev_state_t *)dev_info->info; + assert(((region->offset % cur->dev_spec->block_size) == 0) && + ((region->length % cur->dev_spec->block_size) == 0)); + + cur->base = region->offset; + cur->size = region->length; + cur->file_pos = 0; + + entity->info = (uintptr_t)cur; + return 0; +} + +/* parameter offset is relative address at here */ +static int block_seek(io_entity_t *entity, int mode, size_t offset) { + block_dev_state_t *cur; + + assert(entity->info != (uintptr_t)NULL); + + cur = (block_dev_state_t *)entity->info; + + assert((offset >= 0) && ((uint32_t)offset < cur->size)); + switch (mode) { + case IO_SEEK_SET: + cur->file_pos = (uint32_t)offset; + break; + case IO_SEEK_CUR: + cur->file_pos += (uint32_t)offset; + break; + default: + return -EINVAL; + } + assert(cur->file_pos < cur->size); + return 0; +} + +/* + * This function allows the caller to read any number of bytes + * from any position. It hides from the caller that the low level + * driver only can read aligned blocks of data. For this reason + * we need to handle the use case where the first byte to be read is not + * aligned to start of the block, the last byte to be read is also not + * aligned to the end of a block, and there are zero or more blocks-worth + * of data in between. + * + * In such a case we need to read more bytes than requested (i.e. full + * blocks) and strip-out the leading bytes (aka skip) and the trailing + * bytes (aka padding). See diagram below + * + * cur->file_pos ------------ + * | + * cur->base | + * | | + * v v<---- length ----> + * -------------------------------------------------------------- + * | | block#1 | | block#n | + * | block#0 | + | ... | + | + * | | <- skip -> + | | + <- padding ->| + * ------------------------+----------------------+-------------- + * ^ ^ + * | | + * v iteration#1 iteration#n v + * -------------------------------------------------- + * | | | | + * |<---- request ---->| ... |<----- request ---->| + * | | | | + * -------------------------------------------------- + * / / | | + * / / | | + * / / | | + * / / | | + * / / | | + * / / | | + * / / | | + * / / | | + * / / | | + * / / | | + * <---- request ------> <------ request -----> + * --------------------- ----------------------- + * | | | | | | + * |<-skip->|<-nbytes->| -------->|<-nbytes->|<-padding->| + * | | | | | | | + * --------------------- | ----------------------- + * ^ \ \ | | | + * | \ \ | | | + * | \ \ | | | + * buf->offset \ \ buf->offset | | + * \ \ | | + * \ \ | | + * \ \ | | + * \ \ | | + * \ \ | | + * \ \ | | + * \ \ | | + * -------------------------------- + * | | | | + * buffer-------------->| | ... | | + * | | | | + * -------------------------------- + * <-count#1->| | + * <---------- count#n --------> + * <---------- length ----------> + * + * Additionally, the IO driver has an underlying buffer that is at least + * one block-size and may be big enough to allow. + */ +static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length, + size_t *length_read) { + block_dev_state_t *cur; + io_block_spec_t *buf; + io_block_ops_t *ops; + int lba; + size_t block_size, left; + size_t nbytes; /* number of bytes read in one iteration */ + size_t request; /* number of requested bytes in one iteration */ + size_t count; /* number of bytes already read */ + /* + * number of leading bytes from start of the block + * to the first byte to be read + */ + size_t skip; + + /* + * number of trailing bytes between the last byte + * to be read and the end of the block + */ + size_t padding; + + assert(entity->info != (uintptr_t)NULL); + cur = (block_dev_state_t *)entity->info; + ops = &(cur->dev_spec->ops); + buf = &(cur->dev_spec->buffer); + block_size = cur->dev_spec->block_size; + assert((length <= cur->size) && (length > 0U) && (ops->read != 0)); + + /* + * We don't know the number of bytes that we are going + * to read in every iteration, because it will depend + * on the low level driver. + */ + count = 0; + for (left = length; left > 0U; left -= nbytes) { + /* + * We must only request operations aligned to the block + * size. Therefore if file_pos is not block-aligned, + * we have to request the operation to start at the + * previous block boundary and skip the leading bytes. And + * similarly, the number of bytes requested must be a + * block size multiple + */ + skip = cur->file_pos & (block_size - 1U); + + /* + * Calculate the block number containing file_pos + * - e.g. block 3. + */ + lba = (cur->file_pos + cur->base) / block_size; + + if ((skip + left) > buf->length) { + /* + * The underlying read buffer is too small to + * read all the required data - limit to just + * fill the buffer, and then read again. + */ + request = buf->length; + } else { + /* + * The underlying read buffer is big enough to + * read all the required data. Calculate the + * number of bytes to read to align with the + * block size. + */ + request = skip + left; + request = (request + (block_size - 1U)) & ~(block_size - 1U); + } + request = ops->read(lba, buf->offset, request); + + if (request <= skip) { + /* + * We couldn't read enough bytes to jump over + * the skip bytes, so we should have to read + * again the same block, thus generating + * the same error. + */ + return -EIO; + } + + /* + * Need to remove skip and padding bytes,if any, from + * the read data when copying to the user buffer. + */ + nbytes = request - skip; + padding = (nbytes > left) ? nbytes - left : 0U; + nbytes -= padding; + + memcpy((void *)(buffer + count), (void *)(buf->offset + skip), nbytes); + + cur->file_pos += nbytes; + count += nbytes; + } + assert(count == length); + *length_read = count; + + return 0; +} + +/* + * This function allows the caller to write any number of bytes + * from any position. It hides from the caller that the low level + * driver only can write aligned blocks of data. + * See comments for block_read for more details. + */ +static int block_write(io_entity_t *entity, const uintptr_t buffer, + size_t length, size_t *length_written) { + block_dev_state_t *cur; + io_block_spec_t *buf; + io_block_ops_t *ops; + int lba; + size_t block_size, left; + size_t nbytes; /* number of bytes read in one iteration */ + size_t request; /* number of requested bytes in one iteration */ + size_t count; /* number of bytes already read */ + /* + * number of leading bytes from start of the block + * to the first byte to be read + */ + size_t skip; + + /* + * number of trailing bytes between the last byte + * to be read and the end of the block + */ + size_t padding; + assert(entity->info != (uintptr_t)NULL); + cur = (block_dev_state_t *)entity->info; + ops = &(cur->dev_spec->ops); + buf = &(cur->dev_spec->buffer); + block_size = cur->dev_spec->block_size; + assert((length <= cur->size) && (length > 0U) && (ops->read != 0) && + (ops->write != 0)); + + /* + * We don't know the number of bytes that we are going + * to write in every iteration, because it will depend + * on the low level driver. + */ + count = 0; + for (left = length; left > 0U; left -= nbytes) { + /* + * We must only request operations aligned to the block + * size. Therefore if file_pos is not block-aligned, + * we have to request the operation to start at the + * previous block boundary and skip the leading bytes. And + * similarly, the number of bytes requested must be a + * block size multiple + */ + skip = cur->file_pos & (block_size - 1U); + + /* + * Calculate the block number containing file_pos + * - e.g. block 3. + */ + lba = (cur->file_pos + cur->base) / block_size; + + if ((skip + left) > buf->length) { + /* + * The underlying read buffer is too small to + * read all the required data - limit to just + * fill the buffer, and then read again. + */ + request = buf->length; + } else { + /* + * The underlying read buffer is big enough to + * read all the required data. Calculate the + * number of bytes to read to align with the + * block size. + */ + request = skip + left; + request = (request + (block_size - 1U)) & ~(block_size - 1U); + } + + /* + * The number of bytes that we are going to write + * from the user buffer will depend of the size + * of the current request. + */ + nbytes = request - skip; + padding = (nbytes > left) ? nbytes - left : 0U; + nbytes -= padding; + + /* + * If we have skip or padding bytes then we have to preserve + * some content and it means that we have to read before + * writing + */ + if ((skip > 0U) || (padding > 0U)) { + request = ops->read(lba, buf->offset, request); + /* + * The read may return size less than + * requested. Round down to the nearest block + * boundary + */ + request &= ~(block_size - 1U); + if (request <= skip) { + /* + * We couldn't read enough bytes to jump over + * the skip bytes, so we should have to read + * again the same block, thus generating + * the same error. + */ + return -EIO; + } + nbytes = request - skip; + padding = (nbytes > left) ? nbytes - left : 0U; + nbytes -= padding; + } + + memcpy((void *)(buf->offset + skip), (void *)(buffer + count), nbytes); + + request = ops->write(lba, buf->offset, request); + if (request <= skip) return -EIO; + + /* + * And the previous write operation may modify the size + * of the request, so again, we have to calculate the + * number of bytes that we consumed from the user + * buffer + */ + nbytes = request - skip; + padding = (nbytes > left) ? nbytes - left : 0U; + nbytes -= padding; + + cur->file_pos += nbytes; + count += nbytes; + } + assert(count == length); + *length_written = count; + + return 0; +} + +static int block_close(io_entity_t *entity) { + entity->info = (uintptr_t)NULL; + return 0; +} + +static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info) { + block_dev_state_t *cur; + io_block_spec_t *buffer; + io_dev_info_t *info; + size_t block_size; + int result; + assert(dev_info != NULL); + result = allocate_dev_info(&info); + if (result != 0) return -ENOENT; + + cur = (block_dev_state_t *)info->info; + /* dev_spec is type of io_block_dev_spec_t. */ + cur->dev_spec = (io_block_dev_spec_t *)dev_spec; + buffer = &(cur->dev_spec->buffer); + block_size = cur->dev_spec->block_size; + + assert((block_size > 0U) && (is_power_of_2(block_size) != 0U) && + ((buffer->length % block_size) == 0U)); + + *dev_info = info; /* cast away const */ + (void)block_size; + (void)buffer; + return 0; +} + +static int block_dev_close(io_dev_info_t *dev_info) { + return free_dev_info(dev_info); +} + +/* Exported functions */ + +/* Register the Block driver with the IO abstraction */ +int register_io_dev_block(const io_dev_connector_t **dev_con) { + int result; + + assert(dev_con != NULL); + + /* + * Since dev_info isn't really used in io_register_device, always + * use the same device info at here instead. + */ + result = io_register_device(&dev_info_pool[0]); + if (result == 0) *dev_con = &block_dev_connector; + return result; +} diff --git a/platform/ext/target/arm/corstone1000/io/io_block.h b/platform/ext/target/arm/corstone1000/io/io_block.h new file mode 100644 index 0000000000..1603aa74c5 --- /dev/null +++ b/platform/ext/target/arm/corstone1000/io/io_block.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2022 Arm Limited. All rights reserved. + * + * 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. + */ + +#ifndef __IO_BLOCK_H__ +#define __IO_BLOCK_H__ + +#include "io_storage.h" + +/* block devices ops */ +typedef struct io_block_ops { + size_t (*read)(int lba, uintptr_t buf, size_t size); + size_t (*write)(int lba, const uintptr_t buf, size_t size); +} io_block_ops_t; + +typedef struct io_block_dev_spec { + io_block_spec_t buffer; + io_block_ops_t ops; + size_t block_size; +} io_block_dev_spec_t; + +struct io_dev_connector; + +int register_io_dev_block(const struct io_dev_connector **dev_con); + +#endif /* __IO_BLOCK_H__ */ \ No newline at end of file diff --git a/platform/ext/target/arm/corstone1000/io/io_defs.h b/platform/ext/target/arm/corstone1000/io/io_defs.h new file mode 100644 index 0000000000..acba969ed6 --- /dev/null +++ b/platform/ext/target/arm/corstone1000/io/io_defs.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022 Arm Limited. All rights reserved. + * + * 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. + */ + +#ifndef __IO_DEFS_H__ +#define __IO_DEFS_H__ + +#define MAX_IO_DEVICES (2) +#define MAX_IO_HANDLES (2) +#define MAX_IO_BLOCK_DEVICES (2) +#define MAX_IO_FLASH_DEVICES (2) + +#endif /* __IO_DEFS_H__ */ \ No newline at end of file diff --git a/platform/ext/target/arm/corstone1000/io/io_driver.h b/platform/ext/target/arm/corstone1000/io/io_driver.h new file mode 100644 index 0000000000..cf9e21a6d4 --- /dev/null +++ b/platform/ext/target/arm/corstone1000/io/io_driver.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2014-2022, ARM Limited and Contributors. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef __IO_DRIVER_H__ +#define __IO_DRIVER_H__ + +#include +#include + +/* Generic IO entity structure,representing an accessible IO construct on the + * device, such as a file */ +typedef struct io_entity { + struct io_dev_info *dev_handle; + uintptr_t info; +} io_entity_t; + +/* Device info structure, providing device-specific functions and a means of + * adding driver-specific state */ +typedef struct io_dev_info { + const struct io_dev_funcs *funcs; + uintptr_t info; +} io_dev_info_t; + +/* Structure used to create a connection to a type of device */ +typedef struct io_dev_connector { + /* dev_open opens a connection to a particular device driver */ + int (*dev_open)(const uintptr_t dev_spec, io_dev_info_t **dev_info); +} io_dev_connector_t; + +/* Structure to hold device driver function pointers */ +typedef struct io_dev_funcs { + io_type_t (*type)(void); + int (*open)(io_dev_info_t *dev_info, const uintptr_t spec, + io_entity_t *entity); + int (*seek)(io_entity_t *entity, int mode, size_t offset); + int (*size)(io_entity_t *entity, size_t *length); + int (*read)(io_entity_t *entity, uintptr_t buffer, size_t length, + size_t *length_read); + int (*write)(io_entity_t *entity, const uintptr_t buffer, size_t length, + size_t *length_written); + int (*close)(io_entity_t *entity); + int (*dev_init)(io_dev_info_t *dev_info, const uintptr_t init_params); + int (*dev_close)(io_dev_info_t *dev_info); +} io_dev_funcs_t; + +/* Operations intended to be performed during platform initialisation */ + +/* Register an IO device */ +int io_register_device(const io_dev_info_t *dev_info); + +#endif /* __IO_DRIVER_H__ */ diff --git a/platform/ext/target/arm/corstone1000/io/io_flash.c b/platform/ext/target/arm/corstone1000/io/io_flash.c new file mode 100644 index 0000000000..ff4524e9c5 --- /dev/null +++ b/platform/ext/target/arm/corstone1000/io/io_flash.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2022 Arm Limited. All rights reserved. + * + * 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 "io_flash.h" + +#include +#include + +#include "Driver_Flash.h" +#include "io_block.h" +#include "io_defs.h" +#include "io_driver.h" +#include "io_storage.h" + +#if MAX_IO_FLASH_DEVICES > MAX_IO_BLOCK_DEVICES +#error \ + "FLASH devices are BLOCK devices .. MAX_IO_FLASH_DEVICES should be less or equal to MAX_IO_BLOCK_DEVICES" +#endif + +/* Private Prototypes */ + +static int flash_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info); +static size_t flash_read(int lba, uintptr_t buf, size_t size, size_t flash_id); +static size_t flash_write(int lba, const uintptr_t buf, size_t size, + size_t flash_id); +static size_t flash0_read(int lba, uintptr_t buf, size_t size); +static size_t flash0_write(int lba, uintptr_t buf, size_t size); +static size_t flash1_read(int lba, uintptr_t buf, size_t size); +static size_t flash1_write(int lba, uintptr_t buf, size_t size); + +/** Private Data **/ + +/* Flash device data */ +static const io_dev_connector_t flash_dev_connector = {.dev_open = + flash_dev_open}; +static size_t flash_dev_count = 0; +static io_flash_dev_spec_t *flash_dev_specs[MAX_IO_FLASH_DEVICES]; + +/* Block device data */ +static io_dev_connector_t block_dev_connectors[MAX_IO_FLASH_DEVICES]; +static io_block_dev_spec_t block_dev_spec[MAX_IO_FLASH_DEVICES]; + +/* Flash devices read/write function pointers */ +static io_block_ops_t flashs_ops[MAX_IO_FLASH_DEVICES] = { + [0] = {.read = flash0_read, .write = flash0_write}, + [1] = {.read = flash1_read, .write = flash1_write}, +}; + +/* Flash ops functions */ +static size_t flash_read(int lba, uintptr_t buf, size_t size, size_t flash_id) { + ARM_DRIVER_FLASH *flash_driver = + ((ARM_DRIVER_FLASH *)flash_dev_specs[flash_id]->flash_driver); + ARM_FLASH_INFO *info = flash_driver->GetInfo(); + uint32_t addr = info->sector_size * lba; + uint32_t offset = addr - flash_dev_specs[flash_id]->base_addr; + size_t rem = info->sector_count * info->sector_size - offset; + size_t cnt = size < rem ? size : rem; + + return flash_driver->ReadData(offset, buf, cnt); +} + +static size_t flash_write(int lba, const uintptr_t buf, size_t size, + size_t flash_id) { + ARM_DRIVER_FLASH *flash_driver = + ((ARM_DRIVER_FLASH *)flash_dev_specs[flash_id]->flash_driver); + ARM_FLASH_INFO *info = flash_driver->GetInfo(); + int32_t rc = 0; + uint32_t addr = info->sector_size * lba; + uint32_t offset = addr - flash_dev_specs[flash_id]->base_addr; + size_t rem = info->sector_count * info->sector_size - offset; + size_t cnt = size < rem ? size : rem; + + flash_driver->EraseSector(offset); + rc = flash_driver->ProgramData(offset, buf, cnt); + return rc; +} + +/* Flash ops functions wrapper for each device */ + +static size_t flash0_read(int lba, uintptr_t buf, size_t size) { + return flash_read(lba, buf, size, 0); +} + +static size_t flash0_write(int lba, uintptr_t buf, size_t size) { + return flash_write(lba, buf, size, 0); +} + +static size_t flash1_read(int lba, uintptr_t buf, size_t size) { + return flash_read(lba, buf, size, 1); +} + +static size_t flash1_write(int lba, uintptr_t buf, size_t size) { + return flash_write(lba, buf, size, 1); +} + +/** + * Helper function to find the index of stored flash_dev_specs or + * return a free slot in case of a new dev_spec + */ +static int find_flash_dev_specs(const uintptr_t dev_spec) { + /* Search in the saved ones */ + for (int i = 0; i < flash_dev_count; ++i) { + if (flash_dev_specs[i] != NULL && + flash_dev_specs[i]->flash_driver == + ((io_flash_dev_spec_t *)dev_spec)->flash_driver) { + return i; + } + } + /* Find the first empty flash_dev_specs to be used */ + for (int i = 0; i < flash_dev_count; ++i) { + if (flash_dev_specs[i] == NULL) { + return i; + } + } + return -1; +} + +/** + * This function should be called + */ +static int flash_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info) { + ARM_DRIVER_FLASH *flash_driver; + assert(dev_info != NULL); + assert(dev_spec != NULL); + + size_t index = find_flash_dev_specs(dev_spec); + + /* Check if Flash ops functions are defined for this flash */ + assert(flashs_ops[index].read && flashs_ops[index].write); + + flash_dev_specs[index] = dev_spec; + flash_driver = flash_dev_specs[index]->flash_driver; + + block_dev_spec[index].block_size = flash_driver->GetInfo()->sector_size; + block_dev_spec[index].buffer.offset = flash_dev_specs[index]->buffer; + block_dev_spec[index].buffer.length = flash_dev_specs[index]->bufferlen; + block_dev_spec[index].ops = flashs_ops[index]; + + flash_driver->Initialize(NULL); + + block_dev_connectors[index].dev_open(&block_dev_spec[index], dev_info); + + return 0; +} + +/* Exported functions */ + +/** + * Register the flash device. + * Internally it register a block device. + */ +int register_io_dev_flash(const io_dev_connector_t **dev_con) { + int result; + + if (flash_dev_count >= MAX_IO_FLASH_DEVICES) { + return -ENOENT; + } + assert(dev_con != NULL); + + result = register_io_dev_block(dev_con); + if (result == 0) { + /* Store the block dev connector */ + block_dev_connectors[flash_dev_count++] = **dev_con; + /* Override dev_con with the flash dev connector */ + *dev_con = &flash_dev_connector; + } + return result; +} diff --git a/platform/ext/target/arm/corstone1000/io/io_flash.h b/platform/ext/target/arm/corstone1000/io/io_flash.h new file mode 100644 index 0000000000..8bc38b5824 --- /dev/null +++ b/platform/ext/target/arm/corstone1000/io/io_flash.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2022 Arm Limited. All rights reserved. + * + * 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. + */ + +#ifndef __IO_FLASH_H__ +#define __IO_FLASH_H__ + +#include "io_storage.h" + +typedef struct io_flash_dev_spec { + uintptr_t buffer; + size_t bufferlen; + uint32_t base_addr; + uintptr_t flash_driver; +} io_flash_dev_spec_t; + +struct io_dev_connector; + +/* Register the flash driver with the IO abstraction internally it register a + * block device*/ +int register_io_dev_flash(const struct io_dev_connector **dev_con); + +#endif /* __IO_FLASH_H__ */ diff --git a/platform/ext/target/arm/corstone1000/io/io_storage.c b/platform/ext/target/arm/corstone1000/io/io_storage.c new file mode 100644 index 0000000000..f26f4980f0 --- /dev/null +++ b/platform/ext/target/arm/corstone1000/io/io_storage.c @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2022 Arm Limited. All rights reserved. + * + * 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 +#include +#include + +#include "io_defs.h" +#include "io_driver.h" + +/* Storage for a fixed maximum number of IO entities, definable by platform */ +static io_entity_t entity_pool[MAX_IO_HANDLES]; + +/* Simple way of tracking used storage - each entry is NULL or a pointer to an + * entity */ +static io_entity_t *entity_map[MAX_IO_HANDLES]; + +/* Track number of allocated entities */ +static unsigned int entity_count; + +/* Array of fixed maximum of registered devices, definable by platform */ +static const io_dev_info_t *devices[MAX_IO_DEVICES]; + +/* Number of currently registered devices */ +static unsigned int dev_count; + +/* Return a boolean value indicating whether a device connector is valid */ +static bool is_valid_dev_connector(const io_dev_connector_t *dev_con) { + return (dev_con != NULL) && (dev_con->dev_open != NULL); +} + +/* Return a boolean value indicating whether a device handle is valid */ +static bool is_valid_dev(const uintptr_t dev_handle) { + const io_dev_info_t *dev = (io_dev_info_t *)dev_handle; + + return (dev != NULL) && (dev->funcs != NULL) && + (dev->funcs->type != NULL) && (dev->funcs->type() < IO_TYPE_MAX); +} + +/* Return a boolean value indicating whether an IO entity is valid */ +static bool is_valid_entity(const uintptr_t handle) { + const io_entity_t *entity = (io_entity_t *)handle; + + return (entity != NULL) && (is_valid_dev((uintptr_t)entity->dev_handle)); +} + +/* Return a boolean value indicating whether a seek mode is valid */ +static bool is_valid_seek_mode(io_seek_mode_t mode) { + return ((mode != IO_SEEK_INVALID) && (mode < IO_SEEK_MAX)); +} + +/* Open a connection to a specific device */ +static int io_storage_dev_open(const io_dev_connector_t *dev_con, + const uintptr_t dev_spec, + io_dev_info_t **dev_info) { + assert(dev_info != NULL); + assert(is_valid_dev_connector(dev_con)); + + return dev_con->dev_open(dev_spec, dev_info); +} + +/* Set a handle to track an entity */ +static void set_handle(uintptr_t *handle, io_entity_t *entity) { + assert(handle != NULL); + *handle = (uintptr_t)entity; +} + +/* Locate an entity in the pool, specified by address */ +static int find_first_entity(const io_entity_t *entity, + unsigned int *index_out) { + int result = -ENOENT; + for (unsigned int index = 0; index < MAX_IO_HANDLES; ++index) { + if (entity_map[index] == entity) { + result = 0; + *index_out = index; + break; + } + } + return result; +} + +/* Allocate an entity from the pool and return a pointer to it */ +static int allocate_entity(io_entity_t **entity) { + int result = -ENOMEM; + assert(entity != NULL); + + if (entity_count < MAX_IO_HANDLES) { + unsigned int index = 0; + result = find_first_entity(NULL, &index); + assert(result == 0); + *entity = &entity_pool[index]; + entity_map[index] = &entity_pool[index]; + ++entity_count; + } + + return result; +} + +/* Release an entity back to the pool */ +static int free_entity(const io_entity_t *entity) { + int result; + unsigned int index = 0; + assert(entity != NULL); + + result = find_first_entity(entity, &index); + if (result == 0) { + entity_map[index] = NULL; + --entity_count; + } + + return result; +} + +/* Exported API */ + +/* Register an io device */ +int io_register_device(const io_dev_info_t *dev_info) { + int result = -ENOMEM; + assert(dev_info != NULL); + + if (dev_count < MAX_IO_DEVICES) { + devices[dev_count] = dev_info; + dev_count++; + result = 0; + } + + return result; +} + +/* Open a connection to an IO device */ +int io_dev_open(const io_dev_connector_t *dev_con, const uintptr_t dev_spec, + uintptr_t *handle) { + assert(handle != NULL); + return io_storage_dev_open(dev_con, dev_spec, (io_dev_info_t **)handle); +} + +/* Initialise an IO device explicitly - to permit lazy initialisation or + * re-initialisation */ +int io_dev_init(uintptr_t dev_handle, const uintptr_t init_params) { + int result = 0; + assert(dev_handle != (uintptr_t)NULL); + assert(is_valid_dev(dev_handle)); + + io_dev_info_t *dev = (io_dev_info_t *)dev_handle; + + /* Absence of registered function implies NOP here */ + if (dev->funcs->dev_init != NULL) { + result = dev->funcs->dev_init(dev, init_params); + } + + return result; +} + +/* Close a connection to a device */ +int io_dev_close(uintptr_t dev_handle) { + int result = 0; + assert(dev_handle != (uintptr_t)NULL); + assert(is_valid_dev(dev_handle)); + + io_dev_info_t *dev = (io_dev_info_t *)dev_handle; + + /* Absence of registered function implies NOP here */ + if (dev->funcs->dev_close != NULL) { + result = dev->funcs->dev_close(dev); + } + + return result; +} + +/* Synchronous operations */ + +/* Open an IO entity */ +int io_open(uintptr_t dev_handle, const uintptr_t spec, uintptr_t *handle) { + int result; + assert((spec != (uintptr_t)NULL) && (handle != NULL)); + assert(is_valid_dev(dev_handle)); + + io_dev_info_t *dev = (io_dev_info_t *)dev_handle; + io_entity_t *entity; + + result = allocate_entity(&entity); + + if (result == 0) { + assert(dev->funcs->open != NULL); + result = dev->funcs->open(dev, spec, entity); + + if (result == 0) { + entity->dev_handle = dev; + set_handle(handle, entity); + } else + free_entity(entity); + } + return result; +} + +/* Seek to a specific position in an IO entity */ +int io_seek(uintptr_t handle, io_seek_mode_t mode, int32_t offset) { + int result = -ENODEV; + assert(is_valid_entity(handle) && is_valid_seek_mode(mode)); + + io_entity_t *entity = (io_entity_t *)handle; + + io_dev_info_t *dev = entity->dev_handle; + + if (dev->funcs->seek != NULL) + result = dev->funcs->seek(entity, mode, offset); + + return result; +} + +/* Determine the length of an IO entity */ +int io_size(uintptr_t handle, size_t *length) { + int result = -ENODEV; + assert(is_valid_entity(handle) && (length != NULL)); + + io_entity_t *entity = (io_entity_t *)handle; + + io_dev_info_t *dev = entity->dev_handle; + + if (dev->funcs->size != NULL) result = dev->funcs->size(entity, length); + + return result; +} + +/* Read data from an IO entity */ +int io_read(uintptr_t handle, uintptr_t buffer, size_t length, + size_t *length_read) { + int result = -ENODEV; + assert(is_valid_entity(handle)); + + io_entity_t *entity = (io_entity_t *)handle; + + io_dev_info_t *dev = entity->dev_handle; + + if (dev->funcs->read != NULL) + result = dev->funcs->read(entity, buffer, length, length_read); + + return result; +} + +/* Write data to an IO entity */ +int io_write(uintptr_t handle, const uintptr_t buffer, size_t length, + size_t *length_written) { + int result = -ENODEV; + assert(is_valid_entity(handle)); + + io_entity_t *entity = (io_entity_t *)handle; + + io_dev_info_t *dev = entity->dev_handle; + + if (dev->funcs->write != NULL) { + result = dev->funcs->write(entity, buffer, length, length_written); + } + + return result; +} + +/* Close an IO entity */ +int io_close(uintptr_t handle) { + int result = 0; + assert(is_valid_entity(handle)); + + io_entity_t *entity = (io_entity_t *)handle; + + io_dev_info_t *dev = entity->dev_handle; + + /* Absence of registered function implies NOP here */ + if (dev->funcs->close != NULL) result = dev->funcs->close(entity); + + /* Ignore improbable free_entity failure */ + (void)free_entity(entity); + + return result; +} diff --git a/platform/ext/target/arm/corstone1000/io/io_storage.h b/platform/ext/target/arm/corstone1000/io/io_storage.h new file mode 100644 index 0000000000..0cdca5b269 --- /dev/null +++ b/platform/ext/target/arm/corstone1000/io/io_storage.h @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2022 Arm Limited. All rights reserved. + * + * 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. + */ + +#ifndef __IO_STORAGE_H__ +#define __IO_STORAGE_H__ + +#include +#include + +/* Access modes used when accessing data on a device */ +#define IO_MODE_INVALID (0) +#define IO_MODE_RO (1 << 0) +#define IO_MODE_RW (1 << 1) + +/* Device type which can be used to enable policy decisions about which device + * to access */ +typedef enum { + IO_TYPE_INVALID, + IO_TYPE_SEMIHOSTING, + IO_TYPE_MEMMAP, + IO_TYPE_DUMMY, + IO_TYPE_FIRMWARE_IMAGE_PACKAGE, + IO_TYPE_BLOCK, + IO_TYPE_MTD, + IO_TYPE_MMC, + IO_TYPE_STM32IMAGE, + IO_TYPE_ENCRYPTED, + IO_TYPE_MAX +} io_type_t; + +/* Modes used when seeking data on a supported device */ +typedef enum { + IO_SEEK_INVALID, + IO_SEEK_SET, + IO_SEEK_END, + IO_SEEK_CUR, + IO_SEEK_MAX +} io_seek_mode_t; + +/* Connector type, providing a means of identifying a device to open */ +struct io_dev_connector; + +/* Block specification - used to refer to data on a device supporting + * block-like entities */ +typedef struct io_block_spec { + size_t offset; + size_t length; +} io_block_spec_t; + + +/* Open a connection to a device */ +int io_dev_open(const struct io_dev_connector *dev_con, + const uintptr_t dev_spec, uintptr_t *handle); + +/* Initialise a device explicitly - to permit lazy initialisation or + * re-initialisation */ +int io_dev_init(uintptr_t dev_handle, const uintptr_t init_params); + +/* Close a connection to a device */ +int io_dev_close(uintptr_t dev_handle); + +/* Synchronous operations */ +int io_open(uintptr_t dev_handle, const uintptr_t spec, uintptr_t *handle); + +int io_seek(uintptr_t handle, io_seek_mode_t mode, int32_t offset); + +int io_size(uintptr_t handle, size_t *length); + +int io_read(uintptr_t handle, uintptr_t buffer, size_t length, + size_t *length_read); + +int io_write(uintptr_t handle, const uintptr_t buffer, size_t length, + size_t *length_written); + +int io_close(uintptr_t handle); + +#endif /* __IO_STORAGE_H__ */ -- 2.25.1