| From 1db9afdbf70eb9708640debe5d7d24558fe0f63a Mon Sep 17 00:00:00 2001 |
| From: Mohamed Omar Asaker <mohamed.omarasaker@arm.com> |
| 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 <mohamed.omarasaker@arm.com> |
| 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 |
| $<$<NOT:$<BOOL:${PLATFORM_DEFAULT_OTP}>>:${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 <assert.h> |
| +#include <errno.h> |
| + |
| +#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 <io_storage.h> |
| +#include <stdint.h> |
| + |
| +/* 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 <assert.h> |
| +#include <errno.h> |
| + |
| +#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 <assert.h> |
| +#include <errno.h> |
| +#include <stdbool.h> |
| + |
| +#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 <stddef.h> |
| +#include <stdint.h> |
| + |
| +/* 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 |
| |