1 /* 2 * Copyright (c) 2014-2018, ARM Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <assert.h> 8 #include <debug.h> 9 #include <io_driver.h> 10 #include <io_memmap.h> 11 #include <io_storage.h> 12 #include <string.h> 13 #include <utils.h> 14 15 /* As we need to be able to keep state for seek, only one file can be open 16 * at a time. Make this a structure and point to the entity->info. When we 17 * can malloc memory we can change this to support more open files. 18 */ 19 typedef struct { 20 /* Use the 'in_use' flag as any value for base and file_pos could be 21 * valid. 22 */ 23 int in_use; 24 uintptr_t base; 25 size_t file_pos; 26 size_t size; 27 } file_state_t; 28 29 static file_state_t current_file = {0}; 30 31 /* Identify the device type as memmap */ 32 static io_type_t device_type_memmap(void) 33 { 34 return IO_TYPE_MEMMAP; 35 } 36 37 /* Memmap device functions */ 38 static int memmap_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info); 39 static int memmap_block_open(io_dev_info_t *dev_info, const uintptr_t spec, 40 io_entity_t *entity); 41 static int memmap_block_seek(io_entity_t *entity, int mode, 42 ssize_t offset); 43 static int memmap_block_len(io_entity_t *entity, size_t *length); 44 static int memmap_block_read(io_entity_t *entity, uintptr_t buffer, 45 size_t length, size_t *length_read); 46 static int memmap_block_write(io_entity_t *entity, const uintptr_t buffer, 47 size_t length, size_t *length_written); 48 static int memmap_block_close(io_entity_t *entity); 49 static int memmap_dev_close(io_dev_info_t *dev_info); 50 51 52 static const io_dev_connector_t memmap_dev_connector = { 53 .dev_open = memmap_dev_open 54 }; 55 56 57 static const io_dev_funcs_t memmap_dev_funcs = { 58 .type = device_type_memmap, 59 .open = memmap_block_open, 60 .seek = memmap_block_seek, 61 .size = memmap_block_len, 62 .read = memmap_block_read, 63 .write = memmap_block_write, 64 .close = memmap_block_close, 65 .dev_init = NULL, 66 .dev_close = memmap_dev_close, 67 }; 68 69 70 /* No state associated with this device so structure can be const */ 71 static const io_dev_info_t memmap_dev_info = { 72 .funcs = &memmap_dev_funcs, 73 .info = (uintptr_t)NULL 74 }; 75 76 77 /* Open a connection to the memmap device */ 78 static int memmap_dev_open(const uintptr_t dev_spec __unused, 79 io_dev_info_t **dev_info) 80 { 81 assert(dev_info != NULL); 82 *dev_info = (io_dev_info_t *)&memmap_dev_info; /* cast away const */ 83 84 return 0; 85 } 86 87 88 89 /* Close a connection to the memmap device */ 90 static int memmap_dev_close(io_dev_info_t *dev_info) 91 { 92 /* NOP */ 93 /* TODO: Consider tracking open files and cleaning them up here */ 94 return 0; 95 } 96 97 98 /* Open a file on the memmap device */ 99 static int memmap_block_open(io_dev_info_t *dev_info, const uintptr_t spec, 100 io_entity_t *entity) 101 { 102 int result = -ENOMEM; 103 const io_block_spec_t *block_spec = (io_block_spec_t *)spec; 104 105 /* Since we need to track open state for seek() we only allow one open 106 * spec at a time. When we have dynamic memory we can malloc and set 107 * entity->info. 108 */ 109 if (current_file.in_use == 0) { 110 assert(block_spec != NULL); 111 assert(entity != NULL); 112 113 current_file.in_use = 1; 114 current_file.base = block_spec->offset; 115 /* File cursor offset for seek and incremental reads etc. */ 116 current_file.file_pos = 0; 117 current_file.size = block_spec->length; 118 entity->info = (uintptr_t)¤t_file; 119 result = 0; 120 } else { 121 WARN("A Memmap device is already active. Close first.\n"); 122 } 123 124 return result; 125 } 126 127 128 /* Seek to a particular file offset on the memmap device */ 129 static int memmap_block_seek(io_entity_t *entity, int mode, ssize_t offset) 130 { 131 int result = -ENOENT; 132 file_state_t *fp; 133 134 /* We only support IO_SEEK_SET for the moment. */ 135 if (mode == IO_SEEK_SET) { 136 assert(entity != NULL); 137 138 fp = (file_state_t *) entity->info; 139 140 /* Assert that new file position is valid */ 141 assert((offset >= 0) && (offset < fp->size)); 142 143 /* Reset file position */ 144 fp->file_pos = offset; 145 result = 0; 146 } 147 148 return result; 149 } 150 151 152 /* Return the size of a file on the memmap device */ 153 static int memmap_block_len(io_entity_t *entity, size_t *length) 154 { 155 assert(entity != NULL); 156 assert(length != NULL); 157 158 *length = ((file_state_t *)entity->info)->size; 159 160 return 0; 161 } 162 163 164 /* Read data from a file on the memmap device */ 165 static int memmap_block_read(io_entity_t *entity, uintptr_t buffer, 166 size_t length, size_t *length_read) 167 { 168 file_state_t *fp; 169 size_t pos_after; 170 171 assert(entity != NULL); 172 assert(buffer != (uintptr_t)NULL); 173 assert(length_read != NULL); 174 175 fp = (file_state_t *) entity->info; 176 177 /* Assert that file position is valid for this read operation */ 178 pos_after = fp->file_pos + length; 179 assert((pos_after >= fp->file_pos) && (pos_after <= fp->size)); 180 181 memcpy((void *)buffer, (void *)(fp->base + fp->file_pos), length); 182 183 *length_read = length; 184 185 /* Set file position after read */ 186 fp->file_pos = pos_after; 187 188 return 0; 189 } 190 191 192 /* Write data to a file on the memmap device */ 193 static int memmap_block_write(io_entity_t *entity, const uintptr_t buffer, 194 size_t length, size_t *length_written) 195 { 196 file_state_t *fp; 197 size_t pos_after; 198 199 assert(entity != NULL); 200 assert(buffer != (uintptr_t)NULL); 201 assert(length_written != NULL); 202 203 fp = (file_state_t *) entity->info; 204 205 /* Assert that file position is valid for this write operation */ 206 pos_after = fp->file_pos + length; 207 assert((pos_after >= fp->file_pos) && (pos_after <= fp->size)); 208 209 memcpy((void *)(fp->base + fp->file_pos), (void *)buffer, length); 210 211 *length_written = length; 212 213 /* Set file position after write */ 214 fp->file_pos = pos_after; 215 216 return 0; 217 } 218 219 220 /* Close a file on the memmap device */ 221 static int memmap_block_close(io_entity_t *entity) 222 { 223 assert(entity != NULL); 224 225 entity->info = 0; 226 227 /* This would be a mem free() if we had malloc.*/ 228 zeromem((void *)¤t_file, sizeof(current_file)); 229 230 return 0; 231 } 232 233 234 /* Exported functions */ 235 236 /* Register the memmap driver with the IO abstraction */ 237 int register_io_dev_memmap(const io_dev_connector_t **dev_con) 238 { 239 int result; 240 assert(dev_con != NULL); 241 242 result = io_register_device(&memmap_dev_info); 243 if (result == 0) 244 *dev_con = &memmap_dev_connector; 245 246 return result; 247 } 248