xref: /rk3399_ARM-atf/drivers/io/io_block.c (revision 82cb2c1ad9897473743f08437d0a3995bed561b9)
19da7a653SHaojian Zhuang /*
232f0d3c6SDouglas Raillard  * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved.
39da7a653SHaojian Zhuang  *
4*82cb2c1aSdp-arm  * SPDX-License-Identifier: BSD-3-Clause
59da7a653SHaojian Zhuang  */
69da7a653SHaojian Zhuang 
79da7a653SHaojian Zhuang #include <assert.h>
89da7a653SHaojian Zhuang #include <debug.h>
99da7a653SHaojian Zhuang #include <errno.h>
109da7a653SHaojian Zhuang #include <io_block.h>
119da7a653SHaojian Zhuang #include <io_driver.h>
129da7a653SHaojian Zhuang #include <io_storage.h>
139da7a653SHaojian Zhuang #include <platform_def.h>
149da7a653SHaojian Zhuang #include <string.h>
1532f0d3c6SDouglas Raillard #include <utils.h>
169da7a653SHaojian Zhuang 
179da7a653SHaojian Zhuang typedef struct {
189da7a653SHaojian Zhuang 	io_block_dev_spec_t	*dev_spec;
199da7a653SHaojian Zhuang 	uintptr_t		base;
209da7a653SHaojian Zhuang 	size_t			file_pos;
219da7a653SHaojian Zhuang 	size_t			size;
229da7a653SHaojian Zhuang } block_dev_state_t;
239da7a653SHaojian Zhuang 
249da7a653SHaojian Zhuang #define is_power_of_2(x)	((x != 0) && ((x & (x - 1)) == 0))
259da7a653SHaojian Zhuang 
269da7a653SHaojian Zhuang io_type_t device_type_block(void);
279da7a653SHaojian Zhuang 
289da7a653SHaojian Zhuang static int block_open(io_dev_info_t *dev_info, const uintptr_t spec,
299da7a653SHaojian Zhuang 		      io_entity_t *entity);
309da7a653SHaojian Zhuang static int block_seek(io_entity_t *entity, int mode, ssize_t offset);
319da7a653SHaojian Zhuang static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
329da7a653SHaojian Zhuang 		      size_t *length_read);
339da7a653SHaojian Zhuang static int block_write(io_entity_t *entity, const uintptr_t buffer,
349da7a653SHaojian Zhuang 		       size_t length, size_t *length_written);
359da7a653SHaojian Zhuang static int block_close(io_entity_t *entity);
369da7a653SHaojian Zhuang static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info);
379da7a653SHaojian Zhuang static int block_dev_close(io_dev_info_t *dev_info);
389da7a653SHaojian Zhuang 
399da7a653SHaojian Zhuang static const io_dev_connector_t block_dev_connector = {
409da7a653SHaojian Zhuang 	.dev_open	= block_dev_open
419da7a653SHaojian Zhuang };
429da7a653SHaojian Zhuang 
439da7a653SHaojian Zhuang static const io_dev_funcs_t block_dev_funcs = {
449da7a653SHaojian Zhuang 	.type		= device_type_block,
459da7a653SHaojian Zhuang 	.open		= block_open,
469da7a653SHaojian Zhuang 	.seek		= block_seek,
479da7a653SHaojian Zhuang 	.size		= NULL,
489da7a653SHaojian Zhuang 	.read		= block_read,
499da7a653SHaojian Zhuang 	.write		= block_write,
509da7a653SHaojian Zhuang 	.close		= block_close,
519da7a653SHaojian Zhuang 	.dev_init	= NULL,
529da7a653SHaojian Zhuang 	.dev_close	= block_dev_close,
539da7a653SHaojian Zhuang };
549da7a653SHaojian Zhuang 
559da7a653SHaojian Zhuang static block_dev_state_t state_pool[MAX_IO_BLOCK_DEVICES];
569da7a653SHaojian Zhuang static io_dev_info_t dev_info_pool[MAX_IO_BLOCK_DEVICES];
579da7a653SHaojian Zhuang 
589da7a653SHaojian Zhuang /* Track number of allocated block state */
599da7a653SHaojian Zhuang static unsigned int block_dev_count;
609da7a653SHaojian Zhuang 
619da7a653SHaojian Zhuang io_type_t device_type_block(void)
629da7a653SHaojian Zhuang {
639da7a653SHaojian Zhuang 	return IO_TYPE_BLOCK;
649da7a653SHaojian Zhuang }
659da7a653SHaojian Zhuang 
669da7a653SHaojian Zhuang /* Locate a block state in the pool, specified by address */
679da7a653SHaojian Zhuang static int find_first_block_state(const io_block_dev_spec_t *dev_spec,
689da7a653SHaojian Zhuang 				  unsigned int *index_out)
699da7a653SHaojian Zhuang {
709da7a653SHaojian Zhuang 	int result = -ENOENT;
719da7a653SHaojian Zhuang 	for (int index = 0; index < MAX_IO_BLOCK_DEVICES; ++index) {
729da7a653SHaojian Zhuang 		/* dev_spec is used as identifier since it's unique */
739da7a653SHaojian Zhuang 		if (state_pool[index].dev_spec == dev_spec) {
749da7a653SHaojian Zhuang 			result = 0;
759da7a653SHaojian Zhuang 			*index_out = index;
769da7a653SHaojian Zhuang 			break;
779da7a653SHaojian Zhuang 		}
789da7a653SHaojian Zhuang 	}
799da7a653SHaojian Zhuang 	return result;
809da7a653SHaojian Zhuang }
819da7a653SHaojian Zhuang 
829da7a653SHaojian Zhuang /* Allocate a device info from the pool and return a pointer to it */
839da7a653SHaojian Zhuang static int allocate_dev_info(io_dev_info_t **dev_info)
849da7a653SHaojian Zhuang {
859da7a653SHaojian Zhuang 	int result = -ENOMEM;
869da7a653SHaojian Zhuang 	assert(dev_info != NULL);
879da7a653SHaojian Zhuang 
889da7a653SHaojian Zhuang 	if (block_dev_count < MAX_IO_BLOCK_DEVICES) {
899da7a653SHaojian Zhuang 		unsigned int index = 0;
909da7a653SHaojian Zhuang 		result = find_first_block_state(NULL, &index);
919da7a653SHaojian Zhuang 		assert(result == 0);
929da7a653SHaojian Zhuang 		/* initialize dev_info */
939da7a653SHaojian Zhuang 		dev_info_pool[index].funcs = &block_dev_funcs;
949da7a653SHaojian Zhuang 		dev_info_pool[index].info = (uintptr_t)&state_pool[index];
959da7a653SHaojian Zhuang 		*dev_info = &dev_info_pool[index];
969da7a653SHaojian Zhuang 		++block_dev_count;
979da7a653SHaojian Zhuang 	}
989da7a653SHaojian Zhuang 
999da7a653SHaojian Zhuang 	return result;
1009da7a653SHaojian Zhuang }
1019da7a653SHaojian Zhuang 
1029da7a653SHaojian Zhuang 
1039da7a653SHaojian Zhuang /* Release a device info to the pool */
1049da7a653SHaojian Zhuang static int free_dev_info(io_dev_info_t *dev_info)
1059da7a653SHaojian Zhuang {
1069da7a653SHaojian Zhuang 	int result;
1079da7a653SHaojian Zhuang 	unsigned int index = 0;
1089da7a653SHaojian Zhuang 	block_dev_state_t *state;
1099da7a653SHaojian Zhuang 	assert(dev_info != NULL);
1109da7a653SHaojian Zhuang 
1119da7a653SHaojian Zhuang 	state = (block_dev_state_t *)dev_info->info;
1129da7a653SHaojian Zhuang 	result = find_first_block_state(state->dev_spec, &index);
1139da7a653SHaojian Zhuang 	if (result ==  0) {
1149da7a653SHaojian Zhuang 		/* free if device info is valid */
11532f0d3c6SDouglas Raillard 		zeromem(state, sizeof(block_dev_state_t));
11632f0d3c6SDouglas Raillard 		zeromem(dev_info, sizeof(io_dev_info_t));
1179da7a653SHaojian Zhuang 		--block_dev_count;
1189da7a653SHaojian Zhuang 	}
1199da7a653SHaojian Zhuang 
1209da7a653SHaojian Zhuang 	return result;
1219da7a653SHaojian Zhuang }
1229da7a653SHaojian Zhuang 
1239da7a653SHaojian Zhuang static int block_open(io_dev_info_t *dev_info, const uintptr_t spec,
1249da7a653SHaojian Zhuang 		      io_entity_t *entity)
1259da7a653SHaojian Zhuang {
1269da7a653SHaojian Zhuang 	block_dev_state_t *cur;
1279da7a653SHaojian Zhuang 	io_block_spec_t *region;
1289da7a653SHaojian Zhuang 
1299da7a653SHaojian Zhuang 	assert((dev_info->info != (uintptr_t)NULL) &&
1309da7a653SHaojian Zhuang 	       (spec != (uintptr_t)NULL) &&
1319da7a653SHaojian Zhuang 	       (entity->info == (uintptr_t)NULL));
1329da7a653SHaojian Zhuang 
1339da7a653SHaojian Zhuang 	region = (io_block_spec_t *)spec;
1349da7a653SHaojian Zhuang 	cur = (block_dev_state_t *)dev_info->info;
1359da7a653SHaojian Zhuang 	assert(((region->offset % cur->dev_spec->block_size) == 0) &&
1369da7a653SHaojian Zhuang 	       ((region->length % cur->dev_spec->block_size) == 0));
1379da7a653SHaojian Zhuang 
1389da7a653SHaojian Zhuang 	cur->base = region->offset;
1399da7a653SHaojian Zhuang 	cur->size = region->length;
1409da7a653SHaojian Zhuang 	cur->file_pos = 0;
1419da7a653SHaojian Zhuang 
1429da7a653SHaojian Zhuang 	entity->info = (uintptr_t)cur;
1439da7a653SHaojian Zhuang 	return 0;
1449da7a653SHaojian Zhuang }
1459da7a653SHaojian Zhuang 
1469da7a653SHaojian Zhuang /* parameter offset is relative address at here */
1479da7a653SHaojian Zhuang static int block_seek(io_entity_t *entity, int mode, ssize_t offset)
1489da7a653SHaojian Zhuang {
1499da7a653SHaojian Zhuang 	block_dev_state_t *cur;
1509da7a653SHaojian Zhuang 
1519da7a653SHaojian Zhuang 	assert(entity->info != (uintptr_t)NULL);
1529da7a653SHaojian Zhuang 
1539da7a653SHaojian Zhuang 	cur = (block_dev_state_t *)entity->info;
1549da7a653SHaojian Zhuang 	assert((offset >= 0) && (offset < cur->size));
1559da7a653SHaojian Zhuang 
1569da7a653SHaojian Zhuang 	switch (mode) {
1579da7a653SHaojian Zhuang 	case IO_SEEK_SET:
1589da7a653SHaojian Zhuang 		cur->file_pos = offset;
1599da7a653SHaojian Zhuang 		break;
1609da7a653SHaojian Zhuang 	case IO_SEEK_CUR:
1619da7a653SHaojian Zhuang 		cur->file_pos += offset;
1629da7a653SHaojian Zhuang 		break;
1639da7a653SHaojian Zhuang 	default:
1649da7a653SHaojian Zhuang 		return -EINVAL;
1659da7a653SHaojian Zhuang 	}
1669da7a653SHaojian Zhuang 	assert(cur->file_pos < cur->size);
1679da7a653SHaojian Zhuang 	return 0;
1689da7a653SHaojian Zhuang }
1699da7a653SHaojian Zhuang 
1709da7a653SHaojian Zhuang static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
1719da7a653SHaojian Zhuang 		      size_t *length_read)
1729da7a653SHaojian Zhuang {
1739da7a653SHaojian Zhuang 	block_dev_state_t *cur;
1749da7a653SHaojian Zhuang 	io_block_spec_t *buf;
1759da7a653SHaojian Zhuang 	io_block_ops_t *ops;
1769da7a653SHaojian Zhuang 	size_t aligned_length, skip, count, left, padding, block_size;
1779da7a653SHaojian Zhuang 	int lba;
1789d063aa2SHaojian Zhuang 	int buffer_not_aligned;
1799da7a653SHaojian Zhuang 
1809da7a653SHaojian Zhuang 	assert(entity->info != (uintptr_t)NULL);
1819da7a653SHaojian Zhuang 	cur = (block_dev_state_t *)entity->info;
1829da7a653SHaojian Zhuang 	ops = &(cur->dev_spec->ops);
1839da7a653SHaojian Zhuang 	buf = &(cur->dev_spec->buffer);
1849da7a653SHaojian Zhuang 	block_size = cur->dev_spec->block_size;
1859da7a653SHaojian Zhuang 	assert((length <= cur->size) &&
1869da7a653SHaojian Zhuang 	       (length > 0) &&
1879da7a653SHaojian Zhuang 	       (ops->read != 0));
1889da7a653SHaojian Zhuang 
1899d063aa2SHaojian Zhuang 	if ((buffer & (block_size - 1)) != 0) {
1909d063aa2SHaojian Zhuang 		/*
1919d063aa2SHaojian Zhuang 		 * buffer isn't aligned with block size.
1929d063aa2SHaojian Zhuang 		 * Block device always relies on DMA operation.
1939d063aa2SHaojian Zhuang 		 * It's better to make the buffer as block size aligned.
1949d063aa2SHaojian Zhuang 		 */
1959d063aa2SHaojian Zhuang 		buffer_not_aligned = 1;
1969d063aa2SHaojian Zhuang 	} else {
1979d063aa2SHaojian Zhuang 		buffer_not_aligned = 0;
1989d063aa2SHaojian Zhuang 	}
1999d063aa2SHaojian Zhuang 
2009da7a653SHaojian Zhuang 	skip = cur->file_pos % block_size;
2019da7a653SHaojian Zhuang 	aligned_length = ((skip + length) + (block_size - 1)) &
2029da7a653SHaojian Zhuang 			 ~(block_size - 1);
2039da7a653SHaojian Zhuang 	padding = aligned_length - (skip + length);
2049da7a653SHaojian Zhuang 	left = aligned_length;
2059da7a653SHaojian Zhuang 	do {
2069da7a653SHaojian Zhuang 		lba = (cur->file_pos + cur->base) / block_size;
2079da7a653SHaojian Zhuang 		if (left >= buf->length) {
2089d063aa2SHaojian Zhuang 			/*
2099d063aa2SHaojian Zhuang 			 * Since left is larger, it's impossible to padding.
2109d063aa2SHaojian Zhuang 			 *
2119d063aa2SHaojian Zhuang 			 * If buffer isn't aligned, we need to use aligned
2129d063aa2SHaojian Zhuang 			 * buffer instead.
2139d063aa2SHaojian Zhuang 			 */
2149d063aa2SHaojian Zhuang 			if (skip || buffer_not_aligned) {
2159da7a653SHaojian Zhuang 				/*
2169da7a653SHaojian Zhuang 				 * The beginning address (file_pos) isn't
2179da7a653SHaojian Zhuang 				 * aligned with block size, we need to use
2189da7a653SHaojian Zhuang 				 * block buffer to read block. Since block
2199da7a653SHaojian Zhuang 				 * device is always relied on DMA operation.
2209da7a653SHaojian Zhuang 				 */
2219da7a653SHaojian Zhuang 				count = ops->read(lba, buf->offset,
2229da7a653SHaojian Zhuang 						  buf->length);
2239da7a653SHaojian Zhuang 			} else {
2249da7a653SHaojian Zhuang 				count = ops->read(lba, buffer, buf->length);
2259da7a653SHaojian Zhuang 			}
2269da7a653SHaojian Zhuang 			assert(count == buf->length);
2279da7a653SHaojian Zhuang 			cur->file_pos += count - skip;
2289d063aa2SHaojian Zhuang 			if (skip || buffer_not_aligned) {
2299da7a653SHaojian Zhuang 				/*
2309d063aa2SHaojian Zhuang 				 * Since there's not aligned block size caused
2319d063aa2SHaojian Zhuang 				 * by skip or not aligned buffer, block buffer
2329d063aa2SHaojian Zhuang 				 * is used to store data.
2339da7a653SHaojian Zhuang 				 */
2349da7a653SHaojian Zhuang 				memcpy((void *)buffer,
2359da7a653SHaojian Zhuang 				       (void *)(buf->offset + skip),
2369da7a653SHaojian Zhuang 				       count - skip);
2379da7a653SHaojian Zhuang 			}
2389da7a653SHaojian Zhuang 			left = left - (count - skip);
2399da7a653SHaojian Zhuang 		} else {
2409d063aa2SHaojian Zhuang 			if (skip || padding || buffer_not_aligned) {
2419da7a653SHaojian Zhuang 				/*
2429da7a653SHaojian Zhuang 				 * The beginning address (file_pos) isn't
2439da7a653SHaojian Zhuang 				 * aligned with block size, we have to read
2449da7a653SHaojian Zhuang 				 * full block by block buffer instead.
2459da7a653SHaojian Zhuang 				 * The size isn't aligned with block size.
2469da7a653SHaojian Zhuang 				 * Use block buffer to avoid overflow.
2479d063aa2SHaojian Zhuang 				 *
2489d063aa2SHaojian Zhuang 				 * If buffer isn't aligned, use block buffer
2499d063aa2SHaojian Zhuang 				 * to avoid DMA error.
2509da7a653SHaojian Zhuang 				 */
2519da7a653SHaojian Zhuang 				count = ops->read(lba, buf->offset, left);
2529da7a653SHaojian Zhuang 			} else
2539da7a653SHaojian Zhuang 				count = ops->read(lba, buffer, left);
2549da7a653SHaojian Zhuang 			assert(count == left);
2559da7a653SHaojian Zhuang 			left = left - (skip + padding);
2569da7a653SHaojian Zhuang 			cur->file_pos += left;
2579d063aa2SHaojian Zhuang 			if (skip || padding || buffer_not_aligned) {
2589da7a653SHaojian Zhuang 				/*
2599d063aa2SHaojian Zhuang 				 * Since there's not aligned block size or
2609d063aa2SHaojian Zhuang 				 * buffer, block buffer is used to store data.
2619da7a653SHaojian Zhuang 				 */
2629da7a653SHaojian Zhuang 				memcpy((void *)buffer,
2639da7a653SHaojian Zhuang 				       (void *)(buf->offset + skip),
2649da7a653SHaojian Zhuang 				       left);
2659da7a653SHaojian Zhuang 			}
2669da7a653SHaojian Zhuang 			/* It's already the last block operation */
2679da7a653SHaojian Zhuang 			left = 0;
2689da7a653SHaojian Zhuang 		}
2699da7a653SHaojian Zhuang 		skip = cur->file_pos % block_size;
2709da7a653SHaojian Zhuang 	} while (left > 0);
2719da7a653SHaojian Zhuang 	*length_read = length;
2729da7a653SHaojian Zhuang 
2739da7a653SHaojian Zhuang 	return 0;
2749da7a653SHaojian Zhuang }
2759da7a653SHaojian Zhuang 
2769da7a653SHaojian Zhuang static int block_write(io_entity_t *entity, const uintptr_t buffer,
2779da7a653SHaojian Zhuang 		       size_t length, size_t *length_written)
2789da7a653SHaojian Zhuang {
2799da7a653SHaojian Zhuang 	block_dev_state_t *cur;
2809da7a653SHaojian Zhuang 	io_block_spec_t *buf;
2819da7a653SHaojian Zhuang 	io_block_ops_t *ops;
2829da7a653SHaojian Zhuang 	size_t aligned_length, skip, count, left, padding, block_size;
2839da7a653SHaojian Zhuang 	int lba;
2849d063aa2SHaojian Zhuang 	int buffer_not_aligned;
2859da7a653SHaojian Zhuang 
2869da7a653SHaojian Zhuang 	assert(entity->info != (uintptr_t)NULL);
2879da7a653SHaojian Zhuang 	cur = (block_dev_state_t *)entity->info;
2889da7a653SHaojian Zhuang 	ops = &(cur->dev_spec->ops);
2899da7a653SHaojian Zhuang 	buf = &(cur->dev_spec->buffer);
2909da7a653SHaojian Zhuang 	block_size = cur->dev_spec->block_size;
2919da7a653SHaojian Zhuang 	assert((length <= cur->size) &&
2929da7a653SHaojian Zhuang 	       (length > 0) &&
2939da7a653SHaojian Zhuang 	       (ops->read != 0) &&
2949da7a653SHaojian Zhuang 	       (ops->write != 0));
2959da7a653SHaojian Zhuang 
2969d063aa2SHaojian Zhuang 	if ((buffer & (block_size - 1)) != 0) {
2979d063aa2SHaojian Zhuang 		/*
2989d063aa2SHaojian Zhuang 		 * buffer isn't aligned with block size.
2999d063aa2SHaojian Zhuang 		 * Block device always relies on DMA operation.
3009d063aa2SHaojian Zhuang 		 * It's better to make the buffer as block size aligned.
3019d063aa2SHaojian Zhuang 		 */
3029d063aa2SHaojian Zhuang 		buffer_not_aligned = 1;
3039d063aa2SHaojian Zhuang 	} else {
3049d063aa2SHaojian Zhuang 		buffer_not_aligned = 0;
3059d063aa2SHaojian Zhuang 	}
3069d063aa2SHaojian Zhuang 
3079da7a653SHaojian Zhuang 	skip = cur->file_pos % block_size;
3089da7a653SHaojian Zhuang 	aligned_length = ((skip + length) + (block_size - 1)) &
3099da7a653SHaojian Zhuang 			 ~(block_size - 1);
3109da7a653SHaojian Zhuang 	padding = aligned_length - (skip + length);
3119da7a653SHaojian Zhuang 	left = aligned_length;
3129da7a653SHaojian Zhuang 	do {
3139da7a653SHaojian Zhuang 		lba = (cur->file_pos + cur->base) / block_size;
3149da7a653SHaojian Zhuang 		if (left >= buf->length) {
3159da7a653SHaojian Zhuang 			/* Since left is larger, it's impossible to padding. */
3169d063aa2SHaojian Zhuang 			if (skip || buffer_not_aligned) {
3179da7a653SHaojian Zhuang 				/*
3189da7a653SHaojian Zhuang 				 * The beginning address (file_pos) isn't
3199d063aa2SHaojian Zhuang 				 * aligned with block size or buffer isn't
3209d063aa2SHaojian Zhuang 				 * aligned, we need to use block buffer to
3219d063aa2SHaojian Zhuang 				 * write block.
3229da7a653SHaojian Zhuang 				 */
3239da7a653SHaojian Zhuang 				count = ops->read(lba, buf->offset,
3249da7a653SHaojian Zhuang 						  buf->length);
3259da7a653SHaojian Zhuang 				assert(count == buf->length);
3269da7a653SHaojian Zhuang 				memcpy((void *)(buf->offset + skip),
3279da7a653SHaojian Zhuang 				       (void *)buffer,
3289da7a653SHaojian Zhuang 				       count - skip);
3299da7a653SHaojian Zhuang 				count = ops->write(lba, buf->offset,
3309da7a653SHaojian Zhuang 						   buf->length);
3319da7a653SHaojian Zhuang 			} else
3329da7a653SHaojian Zhuang 				count = ops->write(lba, buffer, buf->length);
3339da7a653SHaojian Zhuang 			assert(count == buf->length);
3349da7a653SHaojian Zhuang 			cur->file_pos += count - skip;
3359da7a653SHaojian Zhuang 			left = left - (count - skip);
3369da7a653SHaojian Zhuang 		} else {
3379d063aa2SHaojian Zhuang 			if (skip || padding || buffer_not_aligned) {
3389da7a653SHaojian Zhuang 				/*
3399da7a653SHaojian Zhuang 				 * The beginning address (file_pos) isn't
3409da7a653SHaojian Zhuang 				 * aligned with block size, we need to avoid
3419da7a653SHaojian Zhuang 				 * poluate data in the beginning. Reading and
3429da7a653SHaojian Zhuang 				 * skipping the beginning is the only way.
3439da7a653SHaojian Zhuang 				 * The size isn't aligned with block size.
3449da7a653SHaojian Zhuang 				 * Use block buffer to avoid overflow.
3459d063aa2SHaojian Zhuang 				 *
3469d063aa2SHaojian Zhuang 				 * If buffer isn't aligned, use block buffer
3479d063aa2SHaojian Zhuang 				 * to avoid DMA error.
3489da7a653SHaojian Zhuang 				 */
3499da7a653SHaojian Zhuang 				count = ops->read(lba, buf->offset, left);
3509da7a653SHaojian Zhuang 				assert(count == left);
3519da7a653SHaojian Zhuang 				memcpy((void *)(buf->offset + skip),
3529da7a653SHaojian Zhuang 				       (void *)buffer,
3539da7a653SHaojian Zhuang 				       left - skip - padding);
3549da7a653SHaojian Zhuang 				count = ops->write(lba, buf->offset, left);
3559da7a653SHaojian Zhuang 			} else
3569da7a653SHaojian Zhuang 				count = ops->write(lba, buffer, left);
3579da7a653SHaojian Zhuang 			assert(count == left);
3589da7a653SHaojian Zhuang 			cur->file_pos += left - (skip + padding);
3599da7a653SHaojian Zhuang 			/* It's already the last block operation */
3609da7a653SHaojian Zhuang 			left = 0;
3619da7a653SHaojian Zhuang 		}
3629da7a653SHaojian Zhuang 		skip = cur->file_pos % block_size;
3639da7a653SHaojian Zhuang 	} while (left > 0);
3649da7a653SHaojian Zhuang 	*length_written = length;
3659da7a653SHaojian Zhuang 	return 0;
3669da7a653SHaojian Zhuang }
3679da7a653SHaojian Zhuang 
3689da7a653SHaojian Zhuang static int block_close(io_entity_t *entity)
3699da7a653SHaojian Zhuang {
3709da7a653SHaojian Zhuang 	entity->info = (uintptr_t)NULL;
3719da7a653SHaojian Zhuang 	return 0;
3729da7a653SHaojian Zhuang }
3739da7a653SHaojian Zhuang 
3749da7a653SHaojian Zhuang static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info)
3759da7a653SHaojian Zhuang {
3769da7a653SHaojian Zhuang 	block_dev_state_t *cur;
3779da7a653SHaojian Zhuang 	io_block_spec_t *buffer;
3789da7a653SHaojian Zhuang 	io_dev_info_t *info;
3799da7a653SHaojian Zhuang 	size_t block_size;
3809da7a653SHaojian Zhuang 	int result;
3819da7a653SHaojian Zhuang 
3829da7a653SHaojian Zhuang 	assert(dev_info != NULL);
3839da7a653SHaojian Zhuang 	result = allocate_dev_info(&info);
3849da7a653SHaojian Zhuang 	if (result)
3859da7a653SHaojian Zhuang 		return -ENOENT;
3869da7a653SHaojian Zhuang 
3879da7a653SHaojian Zhuang 	cur = (block_dev_state_t *)info->info;
3889da7a653SHaojian Zhuang 	/* dev_spec is type of io_block_dev_spec_t. */
3899da7a653SHaojian Zhuang 	cur->dev_spec = (io_block_dev_spec_t *)dev_spec;
3909da7a653SHaojian Zhuang 	buffer = &(cur->dev_spec->buffer);
3919da7a653SHaojian Zhuang 	block_size = cur->dev_spec->block_size;
3929da7a653SHaojian Zhuang 	assert((block_size > 0) &&
3939da7a653SHaojian Zhuang 	       (is_power_of_2(block_size) != 0) &&
3949da7a653SHaojian Zhuang 	       ((buffer->offset % block_size) == 0) &&
3959da7a653SHaojian Zhuang 	       ((buffer->length % block_size) == 0));
3969da7a653SHaojian Zhuang 
3979da7a653SHaojian Zhuang 	*dev_info = info;	/* cast away const */
3989da7a653SHaojian Zhuang 	(void)block_size;
3999da7a653SHaojian Zhuang 	(void)buffer;
4009da7a653SHaojian Zhuang 	return 0;
4019da7a653SHaojian Zhuang }
4029da7a653SHaojian Zhuang 
4039da7a653SHaojian Zhuang static int block_dev_close(io_dev_info_t *dev_info)
4049da7a653SHaojian Zhuang {
4059da7a653SHaojian Zhuang 	return free_dev_info(dev_info);
4069da7a653SHaojian Zhuang }
4079da7a653SHaojian Zhuang 
4089da7a653SHaojian Zhuang /* Exported functions */
4099da7a653SHaojian Zhuang 
4109da7a653SHaojian Zhuang /* Register the Block driver with the IO abstraction */
4119da7a653SHaojian Zhuang int register_io_dev_block(const io_dev_connector_t **dev_con)
4129da7a653SHaojian Zhuang {
4139da7a653SHaojian Zhuang 	int result;
4149da7a653SHaojian Zhuang 
4159da7a653SHaojian Zhuang 	assert(dev_con != NULL);
4169da7a653SHaojian Zhuang 
4179da7a653SHaojian Zhuang 	/*
4189da7a653SHaojian Zhuang 	 * Since dev_info isn't really used in io_register_device, always
4199da7a653SHaojian Zhuang 	 * use the same device info at here instead.
4209da7a653SHaojian Zhuang 	 */
4219da7a653SHaojian Zhuang 	result = io_register_device(&dev_info_pool[0]);
4229da7a653SHaojian Zhuang 	if (result == 0)
4239da7a653SHaojian Zhuang 		*dev_con = &block_dev_connector;
4249da7a653SHaojian Zhuang 	return result;
4259da7a653SHaojian Zhuang }
426