19da7a653SHaojian Zhuang /* 2*32f0d3c6SDouglas Raillard * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved. 39da7a653SHaojian Zhuang * 49da7a653SHaojian Zhuang * Redistribution and use in source and binary forms, with or without 59da7a653SHaojian Zhuang * modification, are permitted provided that the following conditions are met: 69da7a653SHaojian Zhuang * 79da7a653SHaojian Zhuang * Redistributions of source code must retain the above copyright notice, this 89da7a653SHaojian Zhuang * list of conditions and the following disclaimer. 99da7a653SHaojian Zhuang * 109da7a653SHaojian Zhuang * Redistributions in binary form must reproduce the above copyright notice, 119da7a653SHaojian Zhuang * this list of conditions and the following disclaimer in the documentation 129da7a653SHaojian Zhuang * and/or other materials provided with the distribution. 139da7a653SHaojian Zhuang * 149da7a653SHaojian Zhuang * Neither the name of ARM nor the names of its contributors may be used 159da7a653SHaojian Zhuang * to endorse or promote products derived from this software without specific 169da7a653SHaojian Zhuang * prior written permission. 179da7a653SHaojian Zhuang * 189da7a653SHaojian Zhuang * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 199da7a653SHaojian Zhuang * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 209da7a653SHaojian Zhuang * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 219da7a653SHaojian Zhuang * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 229da7a653SHaojian Zhuang * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 239da7a653SHaojian Zhuang * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 249da7a653SHaojian Zhuang * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 259da7a653SHaojian Zhuang * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 269da7a653SHaojian Zhuang * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 279da7a653SHaojian Zhuang * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 289da7a653SHaojian Zhuang * POSSIBILITY OF SUCH DAMAGE. 299da7a653SHaojian Zhuang */ 309da7a653SHaojian Zhuang 319da7a653SHaojian Zhuang #include <assert.h> 329da7a653SHaojian Zhuang #include <debug.h> 339da7a653SHaojian Zhuang #include <errno.h> 349da7a653SHaojian Zhuang #include <io_block.h> 359da7a653SHaojian Zhuang #include <io_driver.h> 369da7a653SHaojian Zhuang #include <io_storage.h> 379da7a653SHaojian Zhuang #include <platform_def.h> 389da7a653SHaojian Zhuang #include <string.h> 39*32f0d3c6SDouglas Raillard #include <utils.h> 409da7a653SHaojian Zhuang 419da7a653SHaojian Zhuang typedef struct { 429da7a653SHaojian Zhuang io_block_dev_spec_t *dev_spec; 439da7a653SHaojian Zhuang uintptr_t base; 449da7a653SHaojian Zhuang size_t file_pos; 459da7a653SHaojian Zhuang size_t size; 469da7a653SHaojian Zhuang } block_dev_state_t; 479da7a653SHaojian Zhuang 489da7a653SHaojian Zhuang #define is_power_of_2(x) ((x != 0) && ((x & (x - 1)) == 0)) 499da7a653SHaojian Zhuang 509da7a653SHaojian Zhuang io_type_t device_type_block(void); 519da7a653SHaojian Zhuang 529da7a653SHaojian Zhuang static int block_open(io_dev_info_t *dev_info, const uintptr_t spec, 539da7a653SHaojian Zhuang io_entity_t *entity); 549da7a653SHaojian Zhuang static int block_seek(io_entity_t *entity, int mode, ssize_t offset); 559da7a653SHaojian Zhuang static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length, 569da7a653SHaojian Zhuang size_t *length_read); 579da7a653SHaojian Zhuang static int block_write(io_entity_t *entity, const uintptr_t buffer, 589da7a653SHaojian Zhuang size_t length, size_t *length_written); 599da7a653SHaojian Zhuang static int block_close(io_entity_t *entity); 609da7a653SHaojian Zhuang static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info); 619da7a653SHaojian Zhuang static int block_dev_close(io_dev_info_t *dev_info); 629da7a653SHaojian Zhuang 639da7a653SHaojian Zhuang static const io_dev_connector_t block_dev_connector = { 649da7a653SHaojian Zhuang .dev_open = block_dev_open 659da7a653SHaojian Zhuang }; 669da7a653SHaojian Zhuang 679da7a653SHaojian Zhuang static const io_dev_funcs_t block_dev_funcs = { 689da7a653SHaojian Zhuang .type = device_type_block, 699da7a653SHaojian Zhuang .open = block_open, 709da7a653SHaojian Zhuang .seek = block_seek, 719da7a653SHaojian Zhuang .size = NULL, 729da7a653SHaojian Zhuang .read = block_read, 739da7a653SHaojian Zhuang .write = block_write, 749da7a653SHaojian Zhuang .close = block_close, 759da7a653SHaojian Zhuang .dev_init = NULL, 769da7a653SHaojian Zhuang .dev_close = block_dev_close, 779da7a653SHaojian Zhuang }; 789da7a653SHaojian Zhuang 799da7a653SHaojian Zhuang static block_dev_state_t state_pool[MAX_IO_BLOCK_DEVICES]; 809da7a653SHaojian Zhuang static io_dev_info_t dev_info_pool[MAX_IO_BLOCK_DEVICES]; 819da7a653SHaojian Zhuang 829da7a653SHaojian Zhuang /* Track number of allocated block state */ 839da7a653SHaojian Zhuang static unsigned int block_dev_count; 849da7a653SHaojian Zhuang 859da7a653SHaojian Zhuang io_type_t device_type_block(void) 869da7a653SHaojian Zhuang { 879da7a653SHaojian Zhuang return IO_TYPE_BLOCK; 889da7a653SHaojian Zhuang } 899da7a653SHaojian Zhuang 909da7a653SHaojian Zhuang /* Locate a block state in the pool, specified by address */ 919da7a653SHaojian Zhuang static int find_first_block_state(const io_block_dev_spec_t *dev_spec, 929da7a653SHaojian Zhuang unsigned int *index_out) 939da7a653SHaojian Zhuang { 949da7a653SHaojian Zhuang int result = -ENOENT; 959da7a653SHaojian Zhuang for (int index = 0; index < MAX_IO_BLOCK_DEVICES; ++index) { 969da7a653SHaojian Zhuang /* dev_spec is used as identifier since it's unique */ 979da7a653SHaojian Zhuang if (state_pool[index].dev_spec == dev_spec) { 989da7a653SHaojian Zhuang result = 0; 999da7a653SHaojian Zhuang *index_out = index; 1009da7a653SHaojian Zhuang break; 1019da7a653SHaojian Zhuang } 1029da7a653SHaojian Zhuang } 1039da7a653SHaojian Zhuang return result; 1049da7a653SHaojian Zhuang } 1059da7a653SHaojian Zhuang 1069da7a653SHaojian Zhuang /* Allocate a device info from the pool and return a pointer to it */ 1079da7a653SHaojian Zhuang static int allocate_dev_info(io_dev_info_t **dev_info) 1089da7a653SHaojian Zhuang { 1099da7a653SHaojian Zhuang int result = -ENOMEM; 1109da7a653SHaojian Zhuang assert(dev_info != NULL); 1119da7a653SHaojian Zhuang 1129da7a653SHaojian Zhuang if (block_dev_count < MAX_IO_BLOCK_DEVICES) { 1139da7a653SHaojian Zhuang unsigned int index = 0; 1149da7a653SHaojian Zhuang result = find_first_block_state(NULL, &index); 1159da7a653SHaojian Zhuang assert(result == 0); 1169da7a653SHaojian Zhuang /* initialize dev_info */ 1179da7a653SHaojian Zhuang dev_info_pool[index].funcs = &block_dev_funcs; 1189da7a653SHaojian Zhuang dev_info_pool[index].info = (uintptr_t)&state_pool[index]; 1199da7a653SHaojian Zhuang *dev_info = &dev_info_pool[index]; 1209da7a653SHaojian Zhuang ++block_dev_count; 1219da7a653SHaojian Zhuang } 1229da7a653SHaojian Zhuang 1239da7a653SHaojian Zhuang return result; 1249da7a653SHaojian Zhuang } 1259da7a653SHaojian Zhuang 1269da7a653SHaojian Zhuang 1279da7a653SHaojian Zhuang /* Release a device info to the pool */ 1289da7a653SHaojian Zhuang static int free_dev_info(io_dev_info_t *dev_info) 1299da7a653SHaojian Zhuang { 1309da7a653SHaojian Zhuang int result; 1319da7a653SHaojian Zhuang unsigned int index = 0; 1329da7a653SHaojian Zhuang block_dev_state_t *state; 1339da7a653SHaojian Zhuang assert(dev_info != NULL); 1349da7a653SHaojian Zhuang 1359da7a653SHaojian Zhuang state = (block_dev_state_t *)dev_info->info; 1369da7a653SHaojian Zhuang result = find_first_block_state(state->dev_spec, &index); 1379da7a653SHaojian Zhuang if (result == 0) { 1389da7a653SHaojian Zhuang /* free if device info is valid */ 139*32f0d3c6SDouglas Raillard zeromem(state, sizeof(block_dev_state_t)); 140*32f0d3c6SDouglas Raillard zeromem(dev_info, sizeof(io_dev_info_t)); 1419da7a653SHaojian Zhuang --block_dev_count; 1429da7a653SHaojian Zhuang } 1439da7a653SHaojian Zhuang 1449da7a653SHaojian Zhuang return result; 1459da7a653SHaojian Zhuang } 1469da7a653SHaojian Zhuang 1479da7a653SHaojian Zhuang static int block_open(io_dev_info_t *dev_info, const uintptr_t spec, 1489da7a653SHaojian Zhuang io_entity_t *entity) 1499da7a653SHaojian Zhuang { 1509da7a653SHaojian Zhuang block_dev_state_t *cur; 1519da7a653SHaojian Zhuang io_block_spec_t *region; 1529da7a653SHaojian Zhuang 1539da7a653SHaojian Zhuang assert((dev_info->info != (uintptr_t)NULL) && 1549da7a653SHaojian Zhuang (spec != (uintptr_t)NULL) && 1559da7a653SHaojian Zhuang (entity->info == (uintptr_t)NULL)); 1569da7a653SHaojian Zhuang 1579da7a653SHaojian Zhuang region = (io_block_spec_t *)spec; 1589da7a653SHaojian Zhuang cur = (block_dev_state_t *)dev_info->info; 1599da7a653SHaojian Zhuang assert(((region->offset % cur->dev_spec->block_size) == 0) && 1609da7a653SHaojian Zhuang ((region->length % cur->dev_spec->block_size) == 0)); 1619da7a653SHaojian Zhuang 1629da7a653SHaojian Zhuang cur->base = region->offset; 1639da7a653SHaojian Zhuang cur->size = region->length; 1649da7a653SHaojian Zhuang cur->file_pos = 0; 1659da7a653SHaojian Zhuang 1669da7a653SHaojian Zhuang entity->info = (uintptr_t)cur; 1679da7a653SHaojian Zhuang return 0; 1689da7a653SHaojian Zhuang } 1699da7a653SHaojian Zhuang 1709da7a653SHaojian Zhuang /* parameter offset is relative address at here */ 1719da7a653SHaojian Zhuang static int block_seek(io_entity_t *entity, int mode, ssize_t offset) 1729da7a653SHaojian Zhuang { 1739da7a653SHaojian Zhuang block_dev_state_t *cur; 1749da7a653SHaojian Zhuang 1759da7a653SHaojian Zhuang assert(entity->info != (uintptr_t)NULL); 1769da7a653SHaojian Zhuang 1779da7a653SHaojian Zhuang cur = (block_dev_state_t *)entity->info; 1789da7a653SHaojian Zhuang assert((offset >= 0) && (offset < cur->size)); 1799da7a653SHaojian Zhuang 1809da7a653SHaojian Zhuang switch (mode) { 1819da7a653SHaojian Zhuang case IO_SEEK_SET: 1829da7a653SHaojian Zhuang cur->file_pos = offset; 1839da7a653SHaojian Zhuang break; 1849da7a653SHaojian Zhuang case IO_SEEK_CUR: 1859da7a653SHaojian Zhuang cur->file_pos += offset; 1869da7a653SHaojian Zhuang break; 1879da7a653SHaojian Zhuang default: 1889da7a653SHaojian Zhuang return -EINVAL; 1899da7a653SHaojian Zhuang } 1909da7a653SHaojian Zhuang assert(cur->file_pos < cur->size); 1919da7a653SHaojian Zhuang return 0; 1929da7a653SHaojian Zhuang } 1939da7a653SHaojian Zhuang 1949da7a653SHaojian Zhuang static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length, 1959da7a653SHaojian Zhuang size_t *length_read) 1969da7a653SHaojian Zhuang { 1979da7a653SHaojian Zhuang block_dev_state_t *cur; 1989da7a653SHaojian Zhuang io_block_spec_t *buf; 1999da7a653SHaojian Zhuang io_block_ops_t *ops; 2009da7a653SHaojian Zhuang size_t aligned_length, skip, count, left, padding, block_size; 2019da7a653SHaojian Zhuang int lba; 2029d063aa2SHaojian Zhuang int buffer_not_aligned; 2039da7a653SHaojian Zhuang 2049da7a653SHaojian Zhuang assert(entity->info != (uintptr_t)NULL); 2059da7a653SHaojian Zhuang cur = (block_dev_state_t *)entity->info; 2069da7a653SHaojian Zhuang ops = &(cur->dev_spec->ops); 2079da7a653SHaojian Zhuang buf = &(cur->dev_spec->buffer); 2089da7a653SHaojian Zhuang block_size = cur->dev_spec->block_size; 2099da7a653SHaojian Zhuang assert((length <= cur->size) && 2109da7a653SHaojian Zhuang (length > 0) && 2119da7a653SHaojian Zhuang (ops->read != 0)); 2129da7a653SHaojian Zhuang 2139d063aa2SHaojian Zhuang if ((buffer & (block_size - 1)) != 0) { 2149d063aa2SHaojian Zhuang /* 2159d063aa2SHaojian Zhuang * buffer isn't aligned with block size. 2169d063aa2SHaojian Zhuang * Block device always relies on DMA operation. 2179d063aa2SHaojian Zhuang * It's better to make the buffer as block size aligned. 2189d063aa2SHaojian Zhuang */ 2199d063aa2SHaojian Zhuang buffer_not_aligned = 1; 2209d063aa2SHaojian Zhuang } else { 2219d063aa2SHaojian Zhuang buffer_not_aligned = 0; 2229d063aa2SHaojian Zhuang } 2239d063aa2SHaojian Zhuang 2249da7a653SHaojian Zhuang skip = cur->file_pos % block_size; 2259da7a653SHaojian Zhuang aligned_length = ((skip + length) + (block_size - 1)) & 2269da7a653SHaojian Zhuang ~(block_size - 1); 2279da7a653SHaojian Zhuang padding = aligned_length - (skip + length); 2289da7a653SHaojian Zhuang left = aligned_length; 2299da7a653SHaojian Zhuang do { 2309da7a653SHaojian Zhuang lba = (cur->file_pos + cur->base) / block_size; 2319da7a653SHaojian Zhuang if (left >= buf->length) { 2329d063aa2SHaojian Zhuang /* 2339d063aa2SHaojian Zhuang * Since left is larger, it's impossible to padding. 2349d063aa2SHaojian Zhuang * 2359d063aa2SHaojian Zhuang * If buffer isn't aligned, we need to use aligned 2369d063aa2SHaojian Zhuang * buffer instead. 2379d063aa2SHaojian Zhuang */ 2389d063aa2SHaojian Zhuang if (skip || buffer_not_aligned) { 2399da7a653SHaojian Zhuang /* 2409da7a653SHaojian Zhuang * The beginning address (file_pos) isn't 2419da7a653SHaojian Zhuang * aligned with block size, we need to use 2429da7a653SHaojian Zhuang * block buffer to read block. Since block 2439da7a653SHaojian Zhuang * device is always relied on DMA operation. 2449da7a653SHaojian Zhuang */ 2459da7a653SHaojian Zhuang count = ops->read(lba, buf->offset, 2469da7a653SHaojian Zhuang buf->length); 2479da7a653SHaojian Zhuang } else { 2489da7a653SHaojian Zhuang count = ops->read(lba, buffer, buf->length); 2499da7a653SHaojian Zhuang } 2509da7a653SHaojian Zhuang assert(count == buf->length); 2519da7a653SHaojian Zhuang cur->file_pos += count - skip; 2529d063aa2SHaojian Zhuang if (skip || buffer_not_aligned) { 2539da7a653SHaojian Zhuang /* 2549d063aa2SHaojian Zhuang * Since there's not aligned block size caused 2559d063aa2SHaojian Zhuang * by skip or not aligned buffer, block buffer 2569d063aa2SHaojian Zhuang * is used to store data. 2579da7a653SHaojian Zhuang */ 2589da7a653SHaojian Zhuang memcpy((void *)buffer, 2599da7a653SHaojian Zhuang (void *)(buf->offset + skip), 2609da7a653SHaojian Zhuang count - skip); 2619da7a653SHaojian Zhuang } 2629da7a653SHaojian Zhuang left = left - (count - skip); 2639da7a653SHaojian Zhuang } else { 2649d063aa2SHaojian Zhuang if (skip || padding || buffer_not_aligned) { 2659da7a653SHaojian Zhuang /* 2669da7a653SHaojian Zhuang * The beginning address (file_pos) isn't 2679da7a653SHaojian Zhuang * aligned with block size, we have to read 2689da7a653SHaojian Zhuang * full block by block buffer instead. 2699da7a653SHaojian Zhuang * The size isn't aligned with block size. 2709da7a653SHaojian Zhuang * Use block buffer to avoid overflow. 2719d063aa2SHaojian Zhuang * 2729d063aa2SHaojian Zhuang * If buffer isn't aligned, use block buffer 2739d063aa2SHaojian Zhuang * to avoid DMA error. 2749da7a653SHaojian Zhuang */ 2759da7a653SHaojian Zhuang count = ops->read(lba, buf->offset, left); 2769da7a653SHaojian Zhuang } else 2779da7a653SHaojian Zhuang count = ops->read(lba, buffer, left); 2789da7a653SHaojian Zhuang assert(count == left); 2799da7a653SHaojian Zhuang left = left - (skip + padding); 2809da7a653SHaojian Zhuang cur->file_pos += left; 2819d063aa2SHaojian Zhuang if (skip || padding || buffer_not_aligned) { 2829da7a653SHaojian Zhuang /* 2839d063aa2SHaojian Zhuang * Since there's not aligned block size or 2849d063aa2SHaojian Zhuang * buffer, block buffer is used to store data. 2859da7a653SHaojian Zhuang */ 2869da7a653SHaojian Zhuang memcpy((void *)buffer, 2879da7a653SHaojian Zhuang (void *)(buf->offset + skip), 2889da7a653SHaojian Zhuang left); 2899da7a653SHaojian Zhuang } 2909da7a653SHaojian Zhuang /* It's already the last block operation */ 2919da7a653SHaojian Zhuang left = 0; 2929da7a653SHaojian Zhuang } 2939da7a653SHaojian Zhuang skip = cur->file_pos % block_size; 2949da7a653SHaojian Zhuang } while (left > 0); 2959da7a653SHaojian Zhuang *length_read = length; 2969da7a653SHaojian Zhuang 2979da7a653SHaojian Zhuang return 0; 2989da7a653SHaojian Zhuang } 2999da7a653SHaojian Zhuang 3009da7a653SHaojian Zhuang static int block_write(io_entity_t *entity, const uintptr_t buffer, 3019da7a653SHaojian Zhuang size_t length, size_t *length_written) 3029da7a653SHaojian Zhuang { 3039da7a653SHaojian Zhuang block_dev_state_t *cur; 3049da7a653SHaojian Zhuang io_block_spec_t *buf; 3059da7a653SHaojian Zhuang io_block_ops_t *ops; 3069da7a653SHaojian Zhuang size_t aligned_length, skip, count, left, padding, block_size; 3079da7a653SHaojian Zhuang int lba; 3089d063aa2SHaojian Zhuang int buffer_not_aligned; 3099da7a653SHaojian Zhuang 3109da7a653SHaojian Zhuang assert(entity->info != (uintptr_t)NULL); 3119da7a653SHaojian Zhuang cur = (block_dev_state_t *)entity->info; 3129da7a653SHaojian Zhuang ops = &(cur->dev_spec->ops); 3139da7a653SHaojian Zhuang buf = &(cur->dev_spec->buffer); 3149da7a653SHaojian Zhuang block_size = cur->dev_spec->block_size; 3159da7a653SHaojian Zhuang assert((length <= cur->size) && 3169da7a653SHaojian Zhuang (length > 0) && 3179da7a653SHaojian Zhuang (ops->read != 0) && 3189da7a653SHaojian Zhuang (ops->write != 0)); 3199da7a653SHaojian Zhuang 3209d063aa2SHaojian Zhuang if ((buffer & (block_size - 1)) != 0) { 3219d063aa2SHaojian Zhuang /* 3229d063aa2SHaojian Zhuang * buffer isn't aligned with block size. 3239d063aa2SHaojian Zhuang * Block device always relies on DMA operation. 3249d063aa2SHaojian Zhuang * It's better to make the buffer as block size aligned. 3259d063aa2SHaojian Zhuang */ 3269d063aa2SHaojian Zhuang buffer_not_aligned = 1; 3279d063aa2SHaojian Zhuang } else { 3289d063aa2SHaojian Zhuang buffer_not_aligned = 0; 3299d063aa2SHaojian Zhuang } 3309d063aa2SHaojian Zhuang 3319da7a653SHaojian Zhuang skip = cur->file_pos % block_size; 3329da7a653SHaojian Zhuang aligned_length = ((skip + length) + (block_size - 1)) & 3339da7a653SHaojian Zhuang ~(block_size - 1); 3349da7a653SHaojian Zhuang padding = aligned_length - (skip + length); 3359da7a653SHaojian Zhuang left = aligned_length; 3369da7a653SHaojian Zhuang do { 3379da7a653SHaojian Zhuang lba = (cur->file_pos + cur->base) / block_size; 3389da7a653SHaojian Zhuang if (left >= buf->length) { 3399da7a653SHaojian Zhuang /* Since left is larger, it's impossible to padding. */ 3409d063aa2SHaojian Zhuang if (skip || buffer_not_aligned) { 3419da7a653SHaojian Zhuang /* 3429da7a653SHaojian Zhuang * The beginning address (file_pos) isn't 3439d063aa2SHaojian Zhuang * aligned with block size or buffer isn't 3449d063aa2SHaojian Zhuang * aligned, we need to use block buffer to 3459d063aa2SHaojian Zhuang * write block. 3469da7a653SHaojian Zhuang */ 3479da7a653SHaojian Zhuang count = ops->read(lba, buf->offset, 3489da7a653SHaojian Zhuang buf->length); 3499da7a653SHaojian Zhuang assert(count == buf->length); 3509da7a653SHaojian Zhuang memcpy((void *)(buf->offset + skip), 3519da7a653SHaojian Zhuang (void *)buffer, 3529da7a653SHaojian Zhuang count - skip); 3539da7a653SHaojian Zhuang count = ops->write(lba, buf->offset, 3549da7a653SHaojian Zhuang buf->length); 3559da7a653SHaojian Zhuang } else 3569da7a653SHaojian Zhuang count = ops->write(lba, buffer, buf->length); 3579da7a653SHaojian Zhuang assert(count == buf->length); 3589da7a653SHaojian Zhuang cur->file_pos += count - skip; 3599da7a653SHaojian Zhuang left = left - (count - skip); 3609da7a653SHaojian Zhuang } else { 3619d063aa2SHaojian Zhuang if (skip || padding || buffer_not_aligned) { 3629da7a653SHaojian Zhuang /* 3639da7a653SHaojian Zhuang * The beginning address (file_pos) isn't 3649da7a653SHaojian Zhuang * aligned with block size, we need to avoid 3659da7a653SHaojian Zhuang * poluate data in the beginning. Reading and 3669da7a653SHaojian Zhuang * skipping the beginning is the only way. 3679da7a653SHaojian Zhuang * The size isn't aligned with block size. 3689da7a653SHaojian Zhuang * Use block buffer to avoid overflow. 3699d063aa2SHaojian Zhuang * 3709d063aa2SHaojian Zhuang * If buffer isn't aligned, use block buffer 3719d063aa2SHaojian Zhuang * to avoid DMA error. 3729da7a653SHaojian Zhuang */ 3739da7a653SHaojian Zhuang count = ops->read(lba, buf->offset, left); 3749da7a653SHaojian Zhuang assert(count == left); 3759da7a653SHaojian Zhuang memcpy((void *)(buf->offset + skip), 3769da7a653SHaojian Zhuang (void *)buffer, 3779da7a653SHaojian Zhuang left - skip - padding); 3789da7a653SHaojian Zhuang count = ops->write(lba, buf->offset, left); 3799da7a653SHaojian Zhuang } else 3809da7a653SHaojian Zhuang count = ops->write(lba, buffer, left); 3819da7a653SHaojian Zhuang assert(count == left); 3829da7a653SHaojian Zhuang cur->file_pos += left - (skip + padding); 3839da7a653SHaojian Zhuang /* It's already the last block operation */ 3849da7a653SHaojian Zhuang left = 0; 3859da7a653SHaojian Zhuang } 3869da7a653SHaojian Zhuang skip = cur->file_pos % block_size; 3879da7a653SHaojian Zhuang } while (left > 0); 3889da7a653SHaojian Zhuang *length_written = length; 3899da7a653SHaojian Zhuang return 0; 3909da7a653SHaojian Zhuang } 3919da7a653SHaojian Zhuang 3929da7a653SHaojian Zhuang static int block_close(io_entity_t *entity) 3939da7a653SHaojian Zhuang { 3949da7a653SHaojian Zhuang entity->info = (uintptr_t)NULL; 3959da7a653SHaojian Zhuang return 0; 3969da7a653SHaojian Zhuang } 3979da7a653SHaojian Zhuang 3989da7a653SHaojian Zhuang static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info) 3999da7a653SHaojian Zhuang { 4009da7a653SHaojian Zhuang block_dev_state_t *cur; 4019da7a653SHaojian Zhuang io_block_spec_t *buffer; 4029da7a653SHaojian Zhuang io_dev_info_t *info; 4039da7a653SHaojian Zhuang size_t block_size; 4049da7a653SHaojian Zhuang int result; 4059da7a653SHaojian Zhuang 4069da7a653SHaojian Zhuang assert(dev_info != NULL); 4079da7a653SHaojian Zhuang result = allocate_dev_info(&info); 4089da7a653SHaojian Zhuang if (result) 4099da7a653SHaojian Zhuang return -ENOENT; 4109da7a653SHaojian Zhuang 4119da7a653SHaojian Zhuang cur = (block_dev_state_t *)info->info; 4129da7a653SHaojian Zhuang /* dev_spec is type of io_block_dev_spec_t. */ 4139da7a653SHaojian Zhuang cur->dev_spec = (io_block_dev_spec_t *)dev_spec; 4149da7a653SHaojian Zhuang buffer = &(cur->dev_spec->buffer); 4159da7a653SHaojian Zhuang block_size = cur->dev_spec->block_size; 4169da7a653SHaojian Zhuang assert((block_size > 0) && 4179da7a653SHaojian Zhuang (is_power_of_2(block_size) != 0) && 4189da7a653SHaojian Zhuang ((buffer->offset % block_size) == 0) && 4199da7a653SHaojian Zhuang ((buffer->length % block_size) == 0)); 4209da7a653SHaojian Zhuang 4219da7a653SHaojian Zhuang *dev_info = info; /* cast away const */ 4229da7a653SHaojian Zhuang (void)block_size; 4239da7a653SHaojian Zhuang (void)buffer; 4249da7a653SHaojian Zhuang return 0; 4259da7a653SHaojian Zhuang } 4269da7a653SHaojian Zhuang 4279da7a653SHaojian Zhuang static int block_dev_close(io_dev_info_t *dev_info) 4289da7a653SHaojian Zhuang { 4299da7a653SHaojian Zhuang return free_dev_info(dev_info); 4309da7a653SHaojian Zhuang } 4319da7a653SHaojian Zhuang 4329da7a653SHaojian Zhuang /* Exported functions */ 4339da7a653SHaojian Zhuang 4349da7a653SHaojian Zhuang /* Register the Block driver with the IO abstraction */ 4359da7a653SHaojian Zhuang int register_io_dev_block(const io_dev_connector_t **dev_con) 4369da7a653SHaojian Zhuang { 4379da7a653SHaojian Zhuang int result; 4389da7a653SHaojian Zhuang 4399da7a653SHaojian Zhuang assert(dev_con != NULL); 4409da7a653SHaojian Zhuang 4419da7a653SHaojian Zhuang /* 4429da7a653SHaojian Zhuang * Since dev_info isn't really used in io_register_device, always 4439da7a653SHaojian Zhuang * use the same device info at here instead. 4449da7a653SHaojian Zhuang */ 4459da7a653SHaojian Zhuang result = io_register_device(&dev_info_pool[0]); 4469da7a653SHaojian Zhuang if (result == 0) 4479da7a653SHaojian Zhuang *dev_con = &block_dev_connector; 4489da7a653SHaojian Zhuang return result; 4499da7a653SHaojian Zhuang } 450