blob: a855581b3d0abc33027e97eebe2a78c16d2ca0e1 [file] [log] [blame]
Haojian Zhuang12ade162016-03-18 15:14:19 +08001/*
Douglas Raillarda8954fc2017-01-26 15:54:44 +00002 * Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved.
Haojian Zhuang12ade162016-03-18 15:14:19 +08003 *
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>
Douglas Raillarda8954fc2017-01-26 15:54:44 +000039#include <utils.h>
Haojian Zhuang12ade162016-03-18 15:14:19 +080040
41typedef 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
50io_type_t device_type_block(void);
51
52static int block_open(io_dev_info_t *dev_info, const uintptr_t spec,
53 io_entity_t *entity);
54static int block_seek(io_entity_t *entity, int mode, ssize_t offset);
55static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
56 size_t *length_read);
57static int block_write(io_entity_t *entity, const uintptr_t buffer,
58 size_t length, size_t *length_written);
59static int block_close(io_entity_t *entity);
60static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info);
61static int block_dev_close(io_dev_info_t *dev_info);
62
63static const io_dev_connector_t block_dev_connector = {
64 .dev_open = block_dev_open
65};
66
67static 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
79static block_dev_state_t state_pool[MAX_IO_BLOCK_DEVICES];
80static io_dev_info_t dev_info_pool[MAX_IO_BLOCK_DEVICES];
81
82/* Track number of allocated block state */
83static unsigned int block_dev_count;
84
85io_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 */
91static 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 */
107static 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 */
128static 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 */
Douglas Raillarda8954fc2017-01-26 15:54:44 +0000139 zeromem(state, sizeof(block_dev_state_t));
140 zeromem(dev_info, sizeof(io_dev_info_t));
Haojian Zhuang12ade162016-03-18 15:14:19 +0800141 --block_dev_count;
142 }
143
144 return result;
145}
146
147static 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 */
171static 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
194static 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;
Haojian Zhuangf800ccf2016-07-28 10:15:32 +0800202 int buffer_not_aligned;
Haojian Zhuang12ade162016-03-18 15:14:19 +0800203
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
Haojian Zhuangf800ccf2016-07-28 10:15:32 +0800213 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
Haojian Zhuang12ade162016-03-18 15:14:19 +0800224 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) {
Haojian Zhuangf800ccf2016-07-28 10:15:32 +0800232 /*
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) {
Haojian Zhuang12ade162016-03-18 15:14:19 +0800239 /*
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;
Haojian Zhuangf800ccf2016-07-28 10:15:32 +0800252 if (skip || buffer_not_aligned) {
Haojian Zhuang12ade162016-03-18 15:14:19 +0800253 /*
Haojian Zhuangf800ccf2016-07-28 10:15:32 +0800254 * Since there's not aligned block size caused
255 * by skip or not aligned buffer, block buffer
256 * is used to store data.
Haojian Zhuang12ade162016-03-18 15:14:19 +0800257 */
258 memcpy((void *)buffer,
259 (void *)(buf->offset + skip),
260 count - skip);
261 }
262 left = left - (count - skip);
263 } else {
Haojian Zhuangf800ccf2016-07-28 10:15:32 +0800264 if (skip || padding || buffer_not_aligned) {
Haojian Zhuang12ade162016-03-18 15:14:19 +0800265 /*
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.
Haojian Zhuangf800ccf2016-07-28 10:15:32 +0800271 *
272 * If buffer isn't aligned, use block buffer
273 * to avoid DMA error.
Haojian Zhuang12ade162016-03-18 15:14:19 +0800274 */
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;
Haojian Zhuangf800ccf2016-07-28 10:15:32 +0800281 if (skip || padding || buffer_not_aligned) {
Haojian Zhuang12ade162016-03-18 15:14:19 +0800282 /*
Haojian Zhuangf800ccf2016-07-28 10:15:32 +0800283 * Since there's not aligned block size or
284 * buffer, block buffer is used to store data.
Haojian Zhuang12ade162016-03-18 15:14:19 +0800285 */
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
300static 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;
Haojian Zhuangf800ccf2016-07-28 10:15:32 +0800308 int buffer_not_aligned;
Haojian Zhuang12ade162016-03-18 15:14:19 +0800309
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
Haojian Zhuangf800ccf2016-07-28 10:15:32 +0800320 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
Haojian Zhuang12ade162016-03-18 15:14:19 +0800331 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. */
Haojian Zhuangf800ccf2016-07-28 10:15:32 +0800340 if (skip || buffer_not_aligned) {
Haojian Zhuang12ade162016-03-18 15:14:19 +0800341 /*
342 * The beginning address (file_pos) isn't
Haojian Zhuangf800ccf2016-07-28 10:15:32 +0800343 * aligned with block size or buffer isn't
344 * aligned, we need to use block buffer to
345 * write block.
Haojian Zhuang12ade162016-03-18 15:14:19 +0800346 */
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 {
Haojian Zhuangf800ccf2016-07-28 10:15:32 +0800361 if (skip || padding || buffer_not_aligned) {
Haojian Zhuang12ade162016-03-18 15:14:19 +0800362 /*
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.
Haojian Zhuangf800ccf2016-07-28 10:15:32 +0800369 *
370 * If buffer isn't aligned, use block buffer
371 * to avoid DMA error.
Haojian Zhuang12ade162016-03-18 15:14:19 +0800372 */
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
392static int block_close(io_entity_t *entity)
393{
394 entity->info = (uintptr_t)NULL;
395 return 0;
396}
397
398static 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
427static 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 */
435int 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}