blob: 7482fe54a54a064987ec85489233f45680072666 [file] [log] [blame]
/*
* Copyright (c) 2022-2023, Arm Limited and Contributors. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <stddef.h>
#include <stdint.h>
#include <drivers/io/io_storage.h>
#include "block_volume.h"
/* Concrete io_dev interface functions */
static io_type_t block_volume_type(
void);
static int block_volume_open(
io_dev_info_t *dev_info, const uintptr_t spec, io_entity_t *entity);
static int block_volume_close(
io_entity_t *entity);
static int block_volume_seek(
io_entity_t *entity, int mode, signed long long offset);
static int block_volume_size(
io_entity_t *entity, size_t *length);
static int block_volume_read(
io_entity_t *entity, uintptr_t buffer, size_t length, size_t *length_read);
static int block_volume_write(
io_entity_t *entity, const uintptr_t buffer, size_t length, size_t *length_written);
static const io_dev_funcs_t block_volume_dev_funcs = {
.type = block_volume_type,
.open = block_volume_open,
.seek = block_volume_seek,
.size = block_volume_size,
.read = block_volume_read,
.write = block_volume_write,
.close = block_volume_close,
.dev_init = NULL,
.dev_close = NULL
};
/* Concrete volume functions that extend the io_dev interface */
static int block_volume_erase(
uintptr_t context);
static int block_volume_get_storage_ids(
uintptr_t context,
struct uuid_octets *partition_guid,
struct uuid_octets *parent_guid);
int block_volume_init(
struct block_volume *this_instance,
struct block_store *block_store,
const struct uuid_octets *partition_guid,
struct volume **volume)
{
/* Initialize base volume structure */
volume_init(
&this_instance->base_volume,
&block_volume_dev_funcs,
(uintptr_t)this_instance);
/* Initialize block_volume specific attributes */
this_instance->base_volume.erase = block_volume_erase;
this_instance->base_volume.get_storage_ids = block_volume_get_storage_ids;
this_instance->block_store = block_store;
this_instance->partition_guid = *partition_guid;
this_instance->file_pos = 0;
this_instance->size = 0;
this_instance->partition_handle = 0;
this_instance->partition_info.block_size = 0;
this_instance->partition_info.num_blocks = 0;
*volume = &this_instance->base_volume;
return 0;
}
void block_volume_deinit(
struct block_volume *this_instance)
{
(void)this_instance;
}
void block_volume_set_partition_guid(
struct block_volume *this_instance,
const struct uuid_octets *partition_guid)
{
this_instance->partition_guid = *partition_guid;
}
static io_type_t block_volume_type(void)
{
return IO_TYPE_BLOCK;
}
static int block_volume_open(
io_dev_info_t *dev_info,
const uintptr_t spec,
io_entity_t *entity)
{
struct block_volume *this_instance = (struct block_volume *)dev_info->info;
psa_status_t psa_status = PSA_ERROR_BAD_STATE;
psa_status = block_store_get_partition_info(this_instance->block_store,
&this_instance->partition_guid,
&this_instance->partition_info);
if (psa_status == PSA_SUCCESS) {
this_instance->file_pos = 0;
this_instance->size =
this_instance->partition_info.block_size * this_instance->partition_info.num_blocks;
psa_status = block_store_open(this_instance->block_store, 0,
&this_instance->partition_guid,
&this_instance->partition_handle);
entity->info = (uintptr_t)this_instance;
}
return (psa_status == PSA_SUCCESS) ? 0 : -EPERM;
}
static int block_volume_close(
io_entity_t *entity)
{
struct block_volume *this_instance = (struct block_volume *)entity->info;
psa_status_t psa_status = block_store_close(this_instance->block_store, 0,
this_instance->partition_handle);
if (psa_status == PSA_SUCCESS) {
this_instance->file_pos = 0;
this_instance->size = 0;
}
return (psa_status == PSA_SUCCESS) ? 0 : -ENXIO;
}
static int block_volume_seek(
io_entity_t *entity,
int mode,
signed long long offset)
{
struct block_volume *this_instance = (struct block_volume *)entity->info;
switch (mode)
{
case IO_SEEK_SET:
{
if (offset <= this_instance->size)
this_instance->file_pos = (size_t)offset;
else
return -EINVAL;
break;
}
case IO_SEEK_CUR:
{
ssize_t target_pos = this_instance->file_pos + offset;
if ((target_pos >= 0) && (target_pos <= this_instance->size))
this_instance->file_pos = (size_t)target_pos;
else
return -EINVAL;
break;
}
default:
return -EINVAL;
}
return 0;
}
static int block_volume_size(
io_entity_t *entity,
size_t *length)
{
struct block_volume *this_instance = (struct block_volume *)entity->info;
*length = this_instance->size;
return 0;
}
static int block_volume_read(
io_entity_t *entity,
uintptr_t buffer,
size_t length,
size_t *length_read)
{
struct block_volume *this_instance = (struct block_volume *)entity->info;
size_t bytes_read = 0;
*length_read = 0;
if (!this_instance->partition_info.block_size)
return -EIO;
while ((bytes_read < length) && (this_instance->file_pos < this_instance->size)) {
uint64_t lba = this_instance->file_pos / this_instance->partition_info.block_size;
size_t offset = this_instance->file_pos % this_instance->partition_info.block_size;
size_t bytes_remaining_in_block = this_instance->partition_info.block_size - offset;
size_t bytes_remaining_in_file = this_instance->size - this_instance->file_pos;
size_t bytes_remaining = length - bytes_read;
if (bytes_remaining > bytes_remaining_in_file) bytes_remaining = bytes_remaining_in_file;
size_t requested_len = (bytes_remaining < bytes_remaining_in_block) ?
bytes_remaining : bytes_remaining_in_block;
size_t actual_len = 0;
psa_status_t psa_status = block_store_read(
this_instance->block_store, 0,
this_instance->partition_handle,
lba, offset,
requested_len,
(uint8_t*)(buffer + bytes_read),
&actual_len);
if (psa_status != PSA_SUCCESS)
return -EIO;
bytes_read += actual_len;
this_instance->file_pos += actual_len;
}
*length_read = bytes_read;
return 0;
}
static int block_volume_write(
io_entity_t *entity,
const uintptr_t buffer,
size_t length,
size_t *length_written)
{
struct block_volume *this_instance = (struct block_volume *)entity->info;
size_t bytes_written = 0;
*length_written = 0;
if (!this_instance->partition_info.block_size)
return -EIO;
while ((bytes_written < length) && (this_instance->file_pos < this_instance->size)) {
uint64_t lba = this_instance->file_pos / this_instance->partition_info.block_size;
size_t offset = this_instance->file_pos % this_instance->partition_info.block_size;
size_t bytes_remaining_in_block = this_instance->partition_info.block_size - offset;
size_t bytes_remaining_in_file = this_instance->size - this_instance->file_pos;
size_t bytes_remaining = length - bytes_written;
if (bytes_remaining > bytes_remaining_in_file) bytes_remaining = bytes_remaining_in_file;
size_t requested_len = (bytes_remaining < bytes_remaining_in_block) ?
bytes_remaining : bytes_remaining_in_block;
size_t actual_len = 0;
psa_status_t psa_status = block_store_write(
this_instance->block_store, 0,
this_instance->partition_handle,
lba, offset,
(uint8_t*)(buffer + bytes_written),
requested_len,
&actual_len);
if (psa_status != PSA_SUCCESS)
return -EIO;
bytes_written += actual_len;
this_instance->file_pos += actual_len;
}
*length_written = bytes_written;
return 0;
}
static int block_volume_erase(uintptr_t context)
{
struct block_volume *this_instance = (struct block_volume *)context;
/* Erase the entire open partition. Note that a block_store will clip
* the number of blocks to erase to the size of the partition so erasing
* a large number of blocks is a safe way to erase the entire partition.
*/
psa_status_t psa_status = block_store_erase(
this_instance->block_store, 0,
this_instance->partition_handle,
0, UINT32_MAX);
if (psa_status != PSA_SUCCESS)
return -EIO;
return 0;
}
static int block_volume_get_storage_ids(
uintptr_t context,
struct uuid_octets *partition_guid,
struct uuid_octets *parent_guid)
{
struct block_volume *this_instance = (struct block_volume *)context;
struct storage_partition_info partition_info;
psa_status_t psa_status = block_store_get_partition_info(this_instance->block_store,
&this_instance->partition_guid,
&partition_info);
if (psa_status == PSA_SUCCESS) {
if (partition_guid)
*partition_guid = partition_info.partition_guid;
if (parent_guid)
*parent_guid = partition_info.parent_guid;
return 0;
}
return -EINVAL;
}