1 /* 2 * Copyright (c) 2014-2015, Linaro Ltd and Contributors. All rights reserved. 3 * Copyright (c) 2014-2015, Hisilicon Ltd and Contributors. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * Neither the name of ARM nor the names of its contributors may be used 16 * to endorse or promote products derived from this software without specific 17 * prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <assert.h> 33 #include <bl_common.h> 34 #include <debug.h> 35 #include <errno.h> 36 #include <io_block.h> 37 #include <io_driver.h> 38 #include <io_storage.h> 39 #include <mmio.h> 40 #include <stdint.h> 41 #include <string.h> 42 43 /* As we need to be able to keep state for seek, only one file can be open 44 * at a time. Make this a structure and point to the entity->info. When we 45 * can malloc memory we can change this to support more open files. 46 */ 47 typedef struct { 48 /* Use the 'in_use' flag as any value for base and file_pos could be 49 * valid. 50 */ 51 int in_use; 52 uintptr_t base; 53 size_t file_pos; 54 uint32_t flags; 55 } file_state_t; 56 57 struct block_info { 58 struct block_ops ops; 59 int init; 60 uint32_t flags; 61 }; 62 63 static file_state_t current_file = {0}; 64 65 static struct block_info block_info; 66 67 static int block_open(io_dev_info_t *dev_info, const uintptr_t spec, 68 io_entity_t *entity); 69 static int block_seek(io_entity_t *entity, int mode, ssize_t offset); 70 static int block_read(io_entity_t *entity, uintptr_t buffer, 71 size_t length, size_t *length_read); 72 static int block_write(io_entity_t *entity, uintptr_t buffer, 73 size_t length, size_t *length_written); 74 static int block_close(io_entity_t *entity); 75 76 static int blk_dev_init(io_dev_info_t *dev_info, 77 const uintptr_t init_params); 78 static int blk_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info); 79 static int blk_dev_close(io_dev_info_t *dev_info); 80 81 /* Identify the device type as block */ 82 io_type_t device_type_block(void) 83 { 84 return IO_TYPE_BLOCK; 85 } 86 87 static const io_dev_connector_t blk_dev_connector = { 88 .dev_open = blk_dev_open 89 }; 90 91 static const io_dev_funcs_t blk_dev_funcs = { 92 .type = device_type_block, 93 .open = block_open, 94 .seek = block_seek, 95 .size = NULL, 96 .read = block_read, 97 .write = block_write, 98 .close = block_close, 99 .dev_init = blk_dev_init, 100 .dev_close = blk_dev_close, 101 }; 102 103 104 /* No state associated with this device so structure can be const */ 105 static const io_dev_info_t blk_dev_info = { 106 .funcs = &blk_dev_funcs, 107 .info = (uintptr_t)&block_info, 108 }; 109 110 /* Open a connection to the block device */ 111 static int blk_dev_open(const uintptr_t dev_spec __attribute__((unused)), 112 io_dev_info_t **dev_info) 113 { 114 struct block_ops *funcs, *block_spec; 115 116 assert(dev_info != NULL); 117 *dev_info = (io_dev_info_t *)&blk_dev_info; /* cast away const */ 118 119 if (dev_spec) { 120 funcs = &block_info.ops; 121 block_spec = (struct block_ops *)dev_spec; 122 funcs->init = block_spec->init; 123 funcs->read = block_spec->read; 124 funcs->write = block_spec->write; 125 } 126 127 return IO_SUCCESS; 128 } 129 130 /* Close a connection to the block device */ 131 static int blk_dev_close(io_dev_info_t *dev_info) 132 { 133 /* NOP */ 134 /* TODO: Consider tracking open files and cleaning them up here */ 135 return IO_SUCCESS; 136 } 137 138 139 /* Open a file on the block device */ 140 /* TODO: Can we do any sensible limit checks on requested memory */ 141 static int block_open(io_dev_info_t *dev_info, const uintptr_t spec, 142 io_entity_t *entity) 143 { 144 int result = IO_FAIL; 145 const io_block_spec_t *block_spec = (io_block_spec_t *)spec; 146 struct block_info *info = (struct block_info *)(dev_info->info); 147 148 /* Since we need to track open state for seek() we only allow one open 149 * spec at a time. When we have dynamic memory we can malloc and set 150 * entity->info. 151 */ 152 if (current_file.in_use == 0) { 153 assert(block_spec != NULL); 154 assert(entity != NULL); 155 156 current_file.in_use = 1; 157 current_file.base = block_spec->offset; 158 /* File cursor offset for seek and incremental reads etc. */ 159 current_file.file_pos = 0; 160 current_file.flags = info->flags; 161 entity->info = (uintptr_t)¤t_file; 162 result = IO_SUCCESS; 163 } else { 164 WARN("A block device is already active. Close first.\n"); 165 result = IO_RESOURCES_EXHAUSTED; 166 } 167 168 return result; 169 } 170 171 /* Seek to a particular file offset on the block device */ 172 static int block_seek(io_entity_t *entity, int mode, ssize_t offset) 173 { 174 int result = IO_FAIL; 175 176 /* We only support IO_SEEK_SET for the moment. */ 177 if (mode == IO_SEEK_SET) { 178 assert(entity != NULL); 179 180 /* TODO: can we do some basic limit checks on seek? */ 181 ((file_state_t *)entity->info)->file_pos = offset; 182 result = IO_SUCCESS; 183 } else { 184 result = IO_FAIL; 185 } 186 187 return result; 188 } 189 190 191 /* Read data from a file on the block device */ 192 static int block_read(io_entity_t *entity, uintptr_t buffer, 193 size_t length, size_t *length_read) 194 { 195 file_state_t *fp; 196 int result; 197 198 assert(entity != NULL); 199 assert(buffer != (uintptr_t)NULL); 200 assert(length_read != NULL); 201 202 fp = (file_state_t *)entity->info; 203 204 if (!block_info.ops.read) { 205 ERROR("There's no read function on the block device.\n"); 206 return IO_NOT_SUPPORTED; 207 } 208 result = block_info.ops.read(fp->base + fp->file_pos, length, 209 buffer, fp->flags); 210 if (result) { 211 WARN("Failed to read block offset 0x%x\n", 212 fp->base + fp->file_pos); 213 return result; 214 } 215 216 *length_read = length; 217 /* advance the file 'cursor' for incremental reads */ 218 fp->file_pos += length; 219 220 return IO_SUCCESS; 221 } 222 223 static int block_write(io_entity_t *entity, uintptr_t buffer, 224 size_t length, size_t *length_written) 225 { 226 file_state_t *fp; 227 int result; 228 229 assert(entity != NULL); 230 assert(buffer != (uintptr_t)NULL); 231 assert(length_written != NULL); 232 233 fp = (file_state_t *)entity->info; 234 235 if (!block_info.ops.write) { 236 ERROR("There's no write function on the block device.\n"); 237 return IO_NOT_SUPPORTED; 238 } 239 result = block_info.ops.write(fp->base + fp->file_pos, length, 240 buffer, fp->flags); 241 if (result) { 242 WARN("Failed to write block offset 0x%x\n", 243 fp->base + fp->file_pos); 244 return result; 245 } 246 247 *length_written = length; 248 /* advance the file 'cursor' for incremental reads */ 249 fp->file_pos += length; 250 251 return IO_SUCCESS; 252 } 253 254 /* Close a file on the BLOCK device */ 255 static int block_close(io_entity_t *entity) 256 { 257 assert(entity != NULL); 258 259 entity->info = 0; 260 261 /* This would be a mem free() if we had malloc.*/ 262 memset((void *)¤t_file, 0, sizeof(current_file)); 263 264 return IO_SUCCESS; 265 } 266 267 static int blk_dev_init(io_dev_info_t *dev_info, const uintptr_t init_params) 268 { 269 struct block_info *info = (struct block_info *)(dev_info->info); 270 271 if (!info->init) { 272 if (block_info.ops.init) 273 block_info.ops.init(); 274 info->init = 1; 275 } 276 info->flags = init_params; 277 return IO_SUCCESS; 278 } 279 280 /* Exported functions */ 281 282 /* Register the block driver with the IO abstraction */ 283 int register_io_dev_block(const io_dev_connector_t **dev_con) 284 { 285 int result = IO_FAIL; 286 assert(dev_con != NULL); 287 288 result = io_register_device(&blk_dev_info); 289 if (result == IO_SUCCESS) 290 *dev_con = &blk_dev_connector; 291 292 return result; 293 } 294