xref: /rk3399_ARM-atf/drivers/io/io_block.c (revision 51faada71a219a8b94cd8d8e423f0f22e9da4d8f)
1 /*
2  * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9  *
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * Neither the name of ARM nor the names of its contributors may be used
15  * to endorse or promote products derived from this software without specific
16  * prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include <assert.h>
32 #include <debug.h>
33 #include <errno.h>
34 #include <io_block.h>
35 #include <io_driver.h>
36 #include <io_storage.h>
37 #include <platform_def.h>
38 #include <string.h>
39 #include <utils.h>
40 
41 typedef struct {
42 	io_block_dev_spec_t	*dev_spec;
43 	uintptr_t		base;
44 	size_t			file_pos;
45 	size_t			size;
46 } block_dev_state_t;
47 
48 #define is_power_of_2(x)	((x != 0) && ((x & (x - 1)) == 0))
49 
50 io_type_t device_type_block(void);
51 
52 static int block_open(io_dev_info_t *dev_info, const uintptr_t spec,
53 		      io_entity_t *entity);
54 static int block_seek(io_entity_t *entity, int mode, ssize_t offset);
55 static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
56 		      size_t *length_read);
57 static int block_write(io_entity_t *entity, const uintptr_t buffer,
58 		       size_t length, size_t *length_written);
59 static int block_close(io_entity_t *entity);
60 static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info);
61 static int block_dev_close(io_dev_info_t *dev_info);
62 
63 static const io_dev_connector_t block_dev_connector = {
64 	.dev_open	= block_dev_open
65 };
66 
67 static const io_dev_funcs_t block_dev_funcs = {
68 	.type		= device_type_block,
69 	.open		= block_open,
70 	.seek		= block_seek,
71 	.size		= NULL,
72 	.read		= block_read,
73 	.write		= block_write,
74 	.close		= block_close,
75 	.dev_init	= NULL,
76 	.dev_close	= block_dev_close,
77 };
78 
79 static block_dev_state_t state_pool[MAX_IO_BLOCK_DEVICES];
80 static io_dev_info_t dev_info_pool[MAX_IO_BLOCK_DEVICES];
81 
82 /* Track number of allocated block state */
83 static unsigned int block_dev_count;
84 
85 io_type_t device_type_block(void)
86 {
87 	return IO_TYPE_BLOCK;
88 }
89 
90 /* Locate a block state in the pool, specified by address */
91 static int find_first_block_state(const io_block_dev_spec_t *dev_spec,
92 				  unsigned int *index_out)
93 {
94 	int result = -ENOENT;
95 	for (int index = 0; index < MAX_IO_BLOCK_DEVICES; ++index) {
96 		/* dev_spec is used as identifier since it's unique */
97 		if (state_pool[index].dev_spec == dev_spec) {
98 			result = 0;
99 			*index_out = index;
100 			break;
101 		}
102 	}
103 	return result;
104 }
105 
106 /* Allocate a device info from the pool and return a pointer to it */
107 static int allocate_dev_info(io_dev_info_t **dev_info)
108 {
109 	int result = -ENOMEM;
110 	assert(dev_info != NULL);
111 
112 	if (block_dev_count < MAX_IO_BLOCK_DEVICES) {
113 		unsigned int index = 0;
114 		result = find_first_block_state(NULL, &index);
115 		assert(result == 0);
116 		/* initialize dev_info */
117 		dev_info_pool[index].funcs = &block_dev_funcs;
118 		dev_info_pool[index].info = (uintptr_t)&state_pool[index];
119 		*dev_info = &dev_info_pool[index];
120 		++block_dev_count;
121 	}
122 
123 	return result;
124 }
125 
126 
127 /* Release a device info to the pool */
128 static int free_dev_info(io_dev_info_t *dev_info)
129 {
130 	int result;
131 	unsigned int index = 0;
132 	block_dev_state_t *state;
133 	assert(dev_info != NULL);
134 
135 	state = (block_dev_state_t *)dev_info->info;
136 	result = find_first_block_state(state->dev_spec, &index);
137 	if (result ==  0) {
138 		/* free if device info is valid */
139 		zeromem(state, sizeof(block_dev_state_t));
140 		zeromem(dev_info, sizeof(io_dev_info_t));
141 		--block_dev_count;
142 	}
143 
144 	return result;
145 }
146 
147 static int block_open(io_dev_info_t *dev_info, const uintptr_t spec,
148 		      io_entity_t *entity)
149 {
150 	block_dev_state_t *cur;
151 	io_block_spec_t *region;
152 
153 	assert((dev_info->info != (uintptr_t)NULL) &&
154 	       (spec != (uintptr_t)NULL) &&
155 	       (entity->info == (uintptr_t)NULL));
156 
157 	region = (io_block_spec_t *)spec;
158 	cur = (block_dev_state_t *)dev_info->info;
159 	assert(((region->offset % cur->dev_spec->block_size) == 0) &&
160 	       ((region->length % cur->dev_spec->block_size) == 0));
161 
162 	cur->base = region->offset;
163 	cur->size = region->length;
164 	cur->file_pos = 0;
165 
166 	entity->info = (uintptr_t)cur;
167 	return 0;
168 }
169 
170 /* parameter offset is relative address at here */
171 static int block_seek(io_entity_t *entity, int mode, ssize_t offset)
172 {
173 	block_dev_state_t *cur;
174 
175 	assert(entity->info != (uintptr_t)NULL);
176 
177 	cur = (block_dev_state_t *)entity->info;
178 	assert((offset >= 0) && (offset < cur->size));
179 
180 	switch (mode) {
181 	case IO_SEEK_SET:
182 		cur->file_pos = offset;
183 		break;
184 	case IO_SEEK_CUR:
185 		cur->file_pos += offset;
186 		break;
187 	default:
188 		return -EINVAL;
189 	}
190 	assert(cur->file_pos < cur->size);
191 	return 0;
192 }
193 
194 static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
195 		      size_t *length_read)
196 {
197 	block_dev_state_t *cur;
198 	io_block_spec_t *buf;
199 	io_block_ops_t *ops;
200 	size_t aligned_length, skip, count, left, padding, block_size;
201 	int lba;
202 	int buffer_not_aligned;
203 
204 	assert(entity->info != (uintptr_t)NULL);
205 	cur = (block_dev_state_t *)entity->info;
206 	ops = &(cur->dev_spec->ops);
207 	buf = &(cur->dev_spec->buffer);
208 	block_size = cur->dev_spec->block_size;
209 	assert((length <= cur->size) &&
210 	       (length > 0) &&
211 	       (ops->read != 0));
212 
213 	if ((buffer & (block_size - 1)) != 0) {
214 		/*
215 		 * buffer isn't aligned with block size.
216 		 * Block device always relies on DMA operation.
217 		 * It's better to make the buffer as block size aligned.
218 		 */
219 		buffer_not_aligned = 1;
220 	} else {
221 		buffer_not_aligned = 0;
222 	}
223 
224 	skip = cur->file_pos % block_size;
225 	aligned_length = ((skip + length) + (block_size - 1)) &
226 			 ~(block_size - 1);
227 	padding = aligned_length - (skip + length);
228 	left = aligned_length;
229 	do {
230 		lba = (cur->file_pos + cur->base) / block_size;
231 		if (left >= buf->length) {
232 			/*
233 			 * Since left is larger, it's impossible to padding.
234 			 *
235 			 * If buffer isn't aligned, we need to use aligned
236 			 * buffer instead.
237 			 */
238 			if (skip || buffer_not_aligned) {
239 				/*
240 				 * The beginning address (file_pos) isn't
241 				 * aligned with block size, we need to use
242 				 * block buffer to read block. Since block
243 				 * device is always relied on DMA operation.
244 				 */
245 				count = ops->read(lba, buf->offset,
246 						  buf->length);
247 			} else {
248 				count = ops->read(lba, buffer, buf->length);
249 			}
250 			assert(count == buf->length);
251 			cur->file_pos += count - skip;
252 			if (skip || buffer_not_aligned) {
253 				/*
254 				 * Since there's not aligned block size caused
255 				 * by skip or not aligned buffer, block buffer
256 				 * is used to store data.
257 				 */
258 				memcpy((void *)buffer,
259 				       (void *)(buf->offset + skip),
260 				       count - skip);
261 			}
262 			left = left - (count - skip);
263 		} else {
264 			if (skip || padding || buffer_not_aligned) {
265 				/*
266 				 * The beginning address (file_pos) isn't
267 				 * aligned with block size, we have to read
268 				 * full block by block buffer instead.
269 				 * The size isn't aligned with block size.
270 				 * Use block buffer to avoid overflow.
271 				 *
272 				 * If buffer isn't aligned, use block buffer
273 				 * to avoid DMA error.
274 				 */
275 				count = ops->read(lba, buf->offset, left);
276 			} else
277 				count = ops->read(lba, buffer, left);
278 			assert(count == left);
279 			left = left - (skip + padding);
280 			cur->file_pos += left;
281 			if (skip || padding || buffer_not_aligned) {
282 				/*
283 				 * Since there's not aligned block size or
284 				 * buffer, block buffer is used to store data.
285 				 */
286 				memcpy((void *)buffer,
287 				       (void *)(buf->offset + skip),
288 				       left);
289 			}
290 			/* It's already the last block operation */
291 			left = 0;
292 		}
293 		skip = cur->file_pos % block_size;
294 	} while (left > 0);
295 	*length_read = length;
296 
297 	return 0;
298 }
299 
300 static int block_write(io_entity_t *entity, const uintptr_t buffer,
301 		       size_t length, size_t *length_written)
302 {
303 	block_dev_state_t *cur;
304 	io_block_spec_t *buf;
305 	io_block_ops_t *ops;
306 	size_t aligned_length, skip, count, left, padding, block_size;
307 	int lba;
308 	int buffer_not_aligned;
309 
310 	assert(entity->info != (uintptr_t)NULL);
311 	cur = (block_dev_state_t *)entity->info;
312 	ops = &(cur->dev_spec->ops);
313 	buf = &(cur->dev_spec->buffer);
314 	block_size = cur->dev_spec->block_size;
315 	assert((length <= cur->size) &&
316 	       (length > 0) &&
317 	       (ops->read != 0) &&
318 	       (ops->write != 0));
319 
320 	if ((buffer & (block_size - 1)) != 0) {
321 		/*
322 		 * buffer isn't aligned with block size.
323 		 * Block device always relies on DMA operation.
324 		 * It's better to make the buffer as block size aligned.
325 		 */
326 		buffer_not_aligned = 1;
327 	} else {
328 		buffer_not_aligned = 0;
329 	}
330 
331 	skip = cur->file_pos % block_size;
332 	aligned_length = ((skip + length) + (block_size - 1)) &
333 			 ~(block_size - 1);
334 	padding = aligned_length - (skip + length);
335 	left = aligned_length;
336 	do {
337 		lba = (cur->file_pos + cur->base) / block_size;
338 		if (left >= buf->length) {
339 			/* Since left is larger, it's impossible to padding. */
340 			if (skip || buffer_not_aligned) {
341 				/*
342 				 * The beginning address (file_pos) isn't
343 				 * aligned with block size or buffer isn't
344 				 * aligned, we need to use block buffer to
345 				 * write block.
346 				 */
347 				count = ops->read(lba, buf->offset,
348 						  buf->length);
349 				assert(count == buf->length);
350 				memcpy((void *)(buf->offset + skip),
351 				       (void *)buffer,
352 				       count - skip);
353 				count = ops->write(lba, buf->offset,
354 						   buf->length);
355 			} else
356 				count = ops->write(lba, buffer, buf->length);
357 			assert(count == buf->length);
358 			cur->file_pos += count - skip;
359 			left = left - (count - skip);
360 		} else {
361 			if (skip || padding || buffer_not_aligned) {
362 				/*
363 				 * The beginning address (file_pos) isn't
364 				 * aligned with block size, we need to avoid
365 				 * poluate data in the beginning. Reading and
366 				 * skipping the beginning is the only way.
367 				 * The size isn't aligned with block size.
368 				 * Use block buffer to avoid overflow.
369 				 *
370 				 * If buffer isn't aligned, use block buffer
371 				 * to avoid DMA error.
372 				 */
373 				count = ops->read(lba, buf->offset, left);
374 				assert(count == left);
375 				memcpy((void *)(buf->offset + skip),
376 				       (void *)buffer,
377 				       left - skip - padding);
378 				count = ops->write(lba, buf->offset, left);
379 			} else
380 				count = ops->write(lba, buffer, left);
381 			assert(count == left);
382 			cur->file_pos += left - (skip + padding);
383 			/* It's already the last block operation */
384 			left = 0;
385 		}
386 		skip = cur->file_pos % block_size;
387 	} while (left > 0);
388 	*length_written = length;
389 	return 0;
390 }
391 
392 static int block_close(io_entity_t *entity)
393 {
394 	entity->info = (uintptr_t)NULL;
395 	return 0;
396 }
397 
398 static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info)
399 {
400 	block_dev_state_t *cur;
401 	io_block_spec_t *buffer;
402 	io_dev_info_t *info;
403 	size_t block_size;
404 	int result;
405 
406 	assert(dev_info != NULL);
407 	result = allocate_dev_info(&info);
408 	if (result)
409 		return -ENOENT;
410 
411 	cur = (block_dev_state_t *)info->info;
412 	/* dev_spec is type of io_block_dev_spec_t. */
413 	cur->dev_spec = (io_block_dev_spec_t *)dev_spec;
414 	buffer = &(cur->dev_spec->buffer);
415 	block_size = cur->dev_spec->block_size;
416 	assert((block_size > 0) &&
417 	       (is_power_of_2(block_size) != 0) &&
418 	       ((buffer->offset % block_size) == 0) &&
419 	       ((buffer->length % block_size) == 0));
420 
421 	*dev_info = info;	/* cast away const */
422 	(void)block_size;
423 	(void)buffer;
424 	return 0;
425 }
426 
427 static int block_dev_close(io_dev_info_t *dev_info)
428 {
429 	return free_dev_info(dev_info);
430 }
431 
432 /* Exported functions */
433 
434 /* Register the Block driver with the IO abstraction */
435 int register_io_dev_block(const io_dev_connector_t **dev_con)
436 {
437 	int result;
438 
439 	assert(dev_con != NULL);
440 
441 	/*
442 	 * Since dev_info isn't really used in io_register_device, always
443 	 * use the same device info at here instead.
444 	 */
445 	result = io_register_device(&dev_info_pool[0]);
446 	if (result == 0)
447 		*dev_con = &block_dev_connector;
448 	return result;
449 }
450