blob: 198b723b4be4f80652379393680bc007d49f9164 [file] [log] [blame]
Haojian Zhuang12ade162016-03-18 15:14:19 +08001/*
2 * Copyright (c) 2016, 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
40typedef struct {
41 io_block_dev_spec_t *dev_spec;
42 uintptr_t base;
43 size_t file_pos;
44 size_t size;
45} block_dev_state_t;
46
47#define is_power_of_2(x) ((x != 0) && ((x & (x - 1)) == 0))
48
49io_type_t device_type_block(void);
50
51static int block_open(io_dev_info_t *dev_info, const uintptr_t spec,
52 io_entity_t *entity);
53static int block_seek(io_entity_t *entity, int mode, ssize_t offset);
54static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
55 size_t *length_read);
56static int block_write(io_entity_t *entity, const uintptr_t buffer,
57 size_t length, size_t *length_written);
58static int block_close(io_entity_t *entity);
59static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info);
60static int block_dev_close(io_dev_info_t *dev_info);
61
62static const io_dev_connector_t block_dev_connector = {
63 .dev_open = block_dev_open
64};
65
66static const io_dev_funcs_t block_dev_funcs = {
67 .type = device_type_block,
68 .open = block_open,
69 .seek = block_seek,
70 .size = NULL,
71 .read = block_read,
72 .write = block_write,
73 .close = block_close,
74 .dev_init = NULL,
75 .dev_close = block_dev_close,
76};
77
78static block_dev_state_t state_pool[MAX_IO_BLOCK_DEVICES];
79static io_dev_info_t dev_info_pool[MAX_IO_BLOCK_DEVICES];
80
81/* Track number of allocated block state */
82static unsigned int block_dev_count;
83
84io_type_t device_type_block(void)
85{
86 return IO_TYPE_BLOCK;
87}
88
89/* Locate a block state in the pool, specified by address */
90static int find_first_block_state(const io_block_dev_spec_t *dev_spec,
91 unsigned int *index_out)
92{
93 int result = -ENOENT;
94 for (int index = 0; index < MAX_IO_BLOCK_DEVICES; ++index) {
95 /* dev_spec is used as identifier since it's unique */
96 if (state_pool[index].dev_spec == dev_spec) {
97 result = 0;
98 *index_out = index;
99 break;
100 }
101 }
102 return result;
103}
104
105/* Allocate a device info from the pool and return a pointer to it */
106static int allocate_dev_info(io_dev_info_t **dev_info)
107{
108 int result = -ENOMEM;
109 assert(dev_info != NULL);
110
111 if (block_dev_count < MAX_IO_BLOCK_DEVICES) {
112 unsigned int index = 0;
113 result = find_first_block_state(NULL, &index);
114 assert(result == 0);
115 /* initialize dev_info */
116 dev_info_pool[index].funcs = &block_dev_funcs;
117 dev_info_pool[index].info = (uintptr_t)&state_pool[index];
118 *dev_info = &dev_info_pool[index];
119 ++block_dev_count;
120 }
121
122 return result;
123}
124
125
126/* Release a device info to the pool */
127static int free_dev_info(io_dev_info_t *dev_info)
128{
129 int result;
130 unsigned int index = 0;
131 block_dev_state_t *state;
132 assert(dev_info != NULL);
133
134 state = (block_dev_state_t *)dev_info->info;
135 result = find_first_block_state(state->dev_spec, &index);
136 if (result == 0) {
137 /* free if device info is valid */
138 memset(state, 0, sizeof(block_dev_state_t));
139 memset(dev_info, 0, sizeof(io_dev_info_t));
140 --block_dev_count;
141 }
142
143 return result;
144}
145
146static int block_open(io_dev_info_t *dev_info, const uintptr_t spec,
147 io_entity_t *entity)
148{
149 block_dev_state_t *cur;
150 io_block_spec_t *region;
151
152 assert((dev_info->info != (uintptr_t)NULL) &&
153 (spec != (uintptr_t)NULL) &&
154 (entity->info == (uintptr_t)NULL));
155
156 region = (io_block_spec_t *)spec;
157 cur = (block_dev_state_t *)dev_info->info;
158 assert(((region->offset % cur->dev_spec->block_size) == 0) &&
159 ((region->length % cur->dev_spec->block_size) == 0));
160
161 cur->base = region->offset;
162 cur->size = region->length;
163 cur->file_pos = 0;
164
165 entity->info = (uintptr_t)cur;
166 return 0;
167}
168
169/* parameter offset is relative address at here */
170static int block_seek(io_entity_t *entity, int mode, ssize_t offset)
171{
172 block_dev_state_t *cur;
173
174 assert(entity->info != (uintptr_t)NULL);
175
176 cur = (block_dev_state_t *)entity->info;
177 assert((offset >= 0) && (offset < cur->size));
178
179 switch (mode) {
180 case IO_SEEK_SET:
181 cur->file_pos = offset;
182 break;
183 case IO_SEEK_CUR:
184 cur->file_pos += offset;
185 break;
186 default:
187 return -EINVAL;
188 }
189 assert(cur->file_pos < cur->size);
190 return 0;
191}
192
193static int block_read(io_entity_t *entity, uintptr_t buffer, size_t length,
194 size_t *length_read)
195{
196 block_dev_state_t *cur;
197 io_block_spec_t *buf;
198 io_block_ops_t *ops;
199 size_t aligned_length, skip, count, left, padding, block_size;
200 int lba;
201
202 assert(entity->info != (uintptr_t)NULL);
203 cur = (block_dev_state_t *)entity->info;
204 ops = &(cur->dev_spec->ops);
205 buf = &(cur->dev_spec->buffer);
206 block_size = cur->dev_spec->block_size;
207 assert((length <= cur->size) &&
208 (length > 0) &&
209 (ops->read != 0));
210
211 skip = cur->file_pos % block_size;
212 aligned_length = ((skip + length) + (block_size - 1)) &
213 ~(block_size - 1);
214 padding = aligned_length - (skip + length);
215 left = aligned_length;
216 do {
217 lba = (cur->file_pos + cur->base) / block_size;
218 if (left >= buf->length) {
219 /* Since left is larger, it's impossible to padding. */
220 if (skip) {
221 /*
222 * The beginning address (file_pos) isn't
223 * aligned with block size, we need to use
224 * block buffer to read block. Since block
225 * device is always relied on DMA operation.
226 */
227 count = ops->read(lba, buf->offset,
228 buf->length);
229 } else {
230 count = ops->read(lba, buffer, buf->length);
231 }
232 assert(count == buf->length);
233 cur->file_pos += count - skip;
234 if (skip) {
235 /*
236 * Since it's not aligned with block size,
237 * block buffer is used to store data.
238 */
239 memcpy((void *)buffer,
240 (void *)(buf->offset + skip),
241 count - skip);
242 }
243 left = left - (count - skip);
244 } else {
245 if (skip || padding) {
246 /*
247 * The beginning address (file_pos) isn't
248 * aligned with block size, we have to read
249 * full block by block buffer instead.
250 * The size isn't aligned with block size.
251 * Use block buffer to avoid overflow.
252 */
253 count = ops->read(lba, buf->offset, left);
254 } else
255 count = ops->read(lba, buffer, left);
256 assert(count == left);
257 left = left - (skip + padding);
258 cur->file_pos += left;
259 if (skip || padding) {
260 /*
261 * Since it's not aligned with block size,
262 * block buffer is used to store data.
263 */
264 memcpy((void *)buffer,
265 (void *)(buf->offset + skip),
266 left);
267 }
268 /* It's already the last block operation */
269 left = 0;
270 }
271 skip = cur->file_pos % block_size;
272 } while (left > 0);
273 *length_read = length;
274
275 return 0;
276}
277
278static int block_write(io_entity_t *entity, const uintptr_t buffer,
279 size_t length, size_t *length_written)
280{
281 block_dev_state_t *cur;
282 io_block_spec_t *buf;
283 io_block_ops_t *ops;
284 size_t aligned_length, skip, count, left, padding, block_size;
285 int lba;
286
287 assert(entity->info != (uintptr_t)NULL);
288 cur = (block_dev_state_t *)entity->info;
289 ops = &(cur->dev_spec->ops);
290 buf = &(cur->dev_spec->buffer);
291 block_size = cur->dev_spec->block_size;
292 assert((length <= cur->size) &&
293 (length > 0) &&
294 (ops->read != 0) &&
295 (ops->write != 0));
296
297 skip = cur->file_pos % block_size;
298 aligned_length = ((skip + length) + (block_size - 1)) &
299 ~(block_size - 1);
300 padding = aligned_length - (skip + length);
301 left = aligned_length;
302 do {
303 lba = (cur->file_pos + cur->base) / block_size;
304 if (left >= buf->length) {
305 /* Since left is larger, it's impossible to padding. */
306 if (skip) {
307 /*
308 * The beginning address (file_pos) isn't
309 * aligned with block size, we need to use
310 * block buffer to write block. Since block
311 * device is always relied on DMA operation.
312 */
313 count = ops->read(lba, buf->offset,
314 buf->length);
315 assert(count == buf->length);
316 memcpy((void *)(buf->offset + skip),
317 (void *)buffer,
318 count - skip);
319 count = ops->write(lba, buf->offset,
320 buf->length);
321 } else
322 count = ops->write(lba, buffer, buf->length);
323 assert(count == buf->length);
324 cur->file_pos += count - skip;
325 left = left - (count - skip);
326 } else {
327 if (skip || padding) {
328 /*
329 * The beginning address (file_pos) isn't
330 * aligned with block size, we need to avoid
331 * poluate data in the beginning. Reading and
332 * skipping the beginning is the only way.
333 * The size isn't aligned with block size.
334 * Use block buffer to avoid overflow.
335 */
336 count = ops->read(lba, buf->offset, left);
337 assert(count == left);
338 memcpy((void *)(buf->offset + skip),
339 (void *)buffer,
340 left - skip - padding);
341 count = ops->write(lba, buf->offset, left);
342 } else
343 count = ops->write(lba, buffer, left);
344 assert(count == left);
345 cur->file_pos += left - (skip + padding);
346 /* It's already the last block operation */
347 left = 0;
348 }
349 skip = cur->file_pos % block_size;
350 } while (left > 0);
351 *length_written = length;
352 return 0;
353}
354
355static int block_close(io_entity_t *entity)
356{
357 entity->info = (uintptr_t)NULL;
358 return 0;
359}
360
361static int block_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info)
362{
363 block_dev_state_t *cur;
364 io_block_spec_t *buffer;
365 io_dev_info_t *info;
366 size_t block_size;
367 int result;
368
369 assert(dev_info != NULL);
370 result = allocate_dev_info(&info);
371 if (result)
372 return -ENOENT;
373
374 cur = (block_dev_state_t *)info->info;
375 /* dev_spec is type of io_block_dev_spec_t. */
376 cur->dev_spec = (io_block_dev_spec_t *)dev_spec;
377 buffer = &(cur->dev_spec->buffer);
378 block_size = cur->dev_spec->block_size;
379 assert((block_size > 0) &&
380 (is_power_of_2(block_size) != 0) &&
381 ((buffer->offset % block_size) == 0) &&
382 ((buffer->length % block_size) == 0));
383
384 *dev_info = info; /* cast away const */
385 (void)block_size;
386 (void)buffer;
387 return 0;
388}
389
390static int block_dev_close(io_dev_info_t *dev_info)
391{
392 return free_dev_info(dev_info);
393}
394
395/* Exported functions */
396
397/* Register the Block driver with the IO abstraction */
398int register_io_dev_block(const io_dev_connector_t **dev_con)
399{
400 int result;
401
402 assert(dev_con != NULL);
403
404 /*
405 * Since dev_info isn't really used in io_register_device, always
406 * use the same device info at here instead.
407 */
408 result = io_register_device(&dev_info_pool[0]);
409 if (result == 0)
410 *dev_con = &block_dev_connector;
411 return result;
412}