1 /*
2 * Copyright (c) 2014-2017, ARM Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <assert.h>
8 #include <debug.h>
9 #include <io_driver.h>
10 #include <io_storage.h>
11 #include <string.h>
12 #include <utils.h>
13
14 /* As we need to be able to keep state for seek, only one file can be open
15 * at a time. Make this a structure and point to the entity->info. When we
16 * can malloc memory we can change this to support more open files.
17 */
18 typedef struct {
19 /* Use the 'in_use' flag as any value for base and file_pos could be
20 * valid.
21 */
22 int in_use;
23 uintptr_t base;
24 size_t file_pos;
25 size_t size;
26 } file_state_t;
27
28 static file_state_t current_file = {0};
29
30 /* Identify the device type as memmap */
device_type_memmap(void)31 io_type_t device_type_memmap(void)
32 {
33 return IO_TYPE_MEMMAP;
34 }
35
36 /* Memmap device functions */
37 static int memmap_dev_open(const uintptr_t dev_spec, io_dev_info_t **dev_info);
38 static int memmap_block_open(io_dev_info_t *dev_info, const uintptr_t spec,
39 io_entity_t *entity);
40 static int memmap_block_seek(io_entity_t *entity, int mode,
41 ssize_t offset);
42 static int memmap_block_len(io_entity_t *entity, size_t *length);
43 static int memmap_block_read(io_entity_t *entity, uintptr_t buffer,
44 size_t length, size_t *length_read);
45 static int memmap_block_write(io_entity_t *entity, const uintptr_t buffer,
46 size_t length, size_t *length_written);
47 static int memmap_block_close(io_entity_t *entity);
48 static int memmap_dev_close(io_dev_info_t *dev_info);
49
50
51 static const io_dev_connector_t memmap_dev_connector = {
52 .dev_open = memmap_dev_open
53 };
54
55
56 static const io_dev_funcs_t memmap_dev_funcs = {
57 .type = device_type_memmap,
58 .open = memmap_block_open,
59 .seek = memmap_block_seek,
60 .size = memmap_block_len,
61 .read = memmap_block_read,
62 .write = memmap_block_write,
63 .close = memmap_block_close,
64 .dev_init = NULL,
65 .dev_close = memmap_dev_close,
66 };
67
68
69 /* No state associated with this device so structure can be const */
70 static const io_dev_info_t memmap_dev_info = {
71 .funcs = &memmap_dev_funcs,
72 .info = (uintptr_t)NULL
73 };
74
75
76 /* Open a connection to the memmap device */
memmap_dev_open(const uintptr_t dev_spec __unused,io_dev_info_t ** dev_info)77 static int memmap_dev_open(const uintptr_t dev_spec __unused,
78 io_dev_info_t **dev_info)
79 {
80 assert(dev_info != NULL);
81 *dev_info = (io_dev_info_t *)&memmap_dev_info; /* cast away const */
82
83 return 0;
84 }
85
86
87
88 /* Close a connection to the memmap device */
memmap_dev_close(io_dev_info_t * dev_info)89 static int memmap_dev_close(io_dev_info_t *dev_info)
90 {
91 /* NOP */
92 /* TODO: Consider tracking open files and cleaning them up here */
93 return 0;
94 }
95
96
97 /* Open a file on the memmap device */
memmap_block_open(io_dev_info_t * dev_info,const uintptr_t spec,io_entity_t * entity)98 static int memmap_block_open(io_dev_info_t *dev_info, const uintptr_t spec,
99 io_entity_t *entity)
100 {
101 int result = -ENOMEM;
102 const io_block_spec_t *block_spec = (io_block_spec_t *)spec;
103
104 /* Since we need to track open state for seek() we only allow one open
105 * spec at a time. When we have dynamic memory we can malloc and set
106 * entity->info.
107 */
108 if (current_file.in_use == 0) {
109 assert(block_spec != NULL);
110 assert(entity != NULL);
111
112 current_file.in_use = 1;
113 current_file.base = block_spec->offset;
114 /* File cursor offset for seek and incremental reads etc. */
115 current_file.file_pos = 0;
116 current_file.size = block_spec->length;
117 entity->info = (uintptr_t)¤t_file;
118 result = 0;
119 } else {
120 WARN("A Memmap device is already active. Close first.\n");
121 }
122
123 return result;
124 }
125
126
127 /* Seek to a particular file offset on the memmap device */
memmap_block_seek(io_entity_t * entity,int mode,ssize_t offset)128 static int memmap_block_seek(io_entity_t *entity, int mode, ssize_t offset)
129 {
130 int result = -ENOENT;
131 file_state_t *fp;
132
133 /* We only support IO_SEEK_SET for the moment. */
134 if (mode == IO_SEEK_SET) {
135 assert(entity != NULL);
136
137 fp = (file_state_t *) entity->info;
138
139 /* Assert that new file position is valid */
140 assert((offset >= 0) && (offset < fp->size));
141
142 /* Reset file position */
143 fp->file_pos = offset;
144 result = 0;
145 }
146
147 return result;
148 }
149
150
151 /* Return the size of a file on the memmap device */
memmap_block_len(io_entity_t * entity,size_t * length)152 static int memmap_block_len(io_entity_t *entity, size_t *length)
153 {
154 assert(entity != NULL);
155 assert(length != NULL);
156
157 *length = ((file_state_t *)entity->info)->size;
158
159 return 0;
160 }
161
162
163 /* Read data from a file on the memmap device */
memmap_block_read(io_entity_t * entity,uintptr_t buffer,size_t length,size_t * length_read)164 static int memmap_block_read(io_entity_t *entity, uintptr_t buffer,
165 size_t length, size_t *length_read)
166 {
167 file_state_t *fp;
168 size_t pos_after;
169
170 assert(entity != NULL);
171 assert(buffer != (uintptr_t)NULL);
172 assert(length_read != NULL);
173
174 fp = (file_state_t *) entity->info;
175
176 /* Assert that file position is valid for this read operation */
177 pos_after = fp->file_pos + length;
178 assert((pos_after >= fp->file_pos) && (pos_after <= fp->size));
179
180 memcpy((void *)buffer, (void *)(fp->base + fp->file_pos), length);
181
182 *length_read = length;
183
184 /* Set file position after read */
185 fp->file_pos = pos_after;
186
187 return 0;
188 }
189
190
191 /* Write data to a file on the memmap device */
memmap_block_write(io_entity_t * entity,const uintptr_t buffer,size_t length,size_t * length_written)192 static int memmap_block_write(io_entity_t *entity, const uintptr_t buffer,
193 size_t length, size_t *length_written)
194 {
195 file_state_t *fp;
196 size_t pos_after;
197
198 assert(entity != NULL);
199 assert(buffer != (uintptr_t)NULL);
200 assert(length_written != NULL);
201
202 fp = (file_state_t *) entity->info;
203
204 /* Assert that file position is valid for this write operation */
205 pos_after = fp->file_pos + length;
206 assert((pos_after >= fp->file_pos) && (pos_after <= fp->size));
207
208 memcpy((void *)(fp->base + fp->file_pos), (void *)buffer, length);
209
210 *length_written = length;
211
212 /* Set file position after write */
213 fp->file_pos = pos_after;
214
215 return 0;
216 }
217
218
219 /* Close a file on the memmap device */
memmap_block_close(io_entity_t * entity)220 static int memmap_block_close(io_entity_t *entity)
221 {
222 assert(entity != NULL);
223
224 entity->info = 0;
225
226 /* This would be a mem free() if we had malloc.*/
227 zeromem((void *)¤t_file, sizeof(current_file));
228
229 return 0;
230 }
231
232
233 /* Exported functions */
234
235 /* Register the memmap driver with the IO abstraction */
register_io_dev_memmap(const io_dev_connector_t ** dev_con)236 int register_io_dev_memmap(const io_dev_connector_t **dev_con)
237 {
238 int result;
239 assert(dev_con != NULL);
240
241 result = io_register_device(&memmap_dev_info);
242 if (result == 0)
243 *dev_con = &memmap_dev_connector;
244
245 return result;
246 }
247