1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/mm/mm.h"
4#include "mos/platform/platform.h"
5
6#include <mos/io/io.h>
7#include <mos/io/io_types.h>
8#include <mos/mm/mm_types.h>
9#include <mos/mos_global.h>
10#include <mos/syslog/printk.h>
11#include <mos_stdio.h>
12
13static size_t _null_read(io_t *io, void *buffer, size_t size)
14{
15 (void) io;
16 (void) buffer;
17 (void) size;
18 return 0;
19}
20
21static size_t _null_write(io_t *io, const void *buffer, size_t size)
22{
23 (void) io;
24 (void) buffer;
25 (void) size;
26 return 0;
27}
28
29static io_t io_null_impl = {
30 .refcount = 1, // never gets closed
31 .type = IO_NULL,
32 .flags = IO_READABLE | IO_WRITABLE,
33 .ops =
34 &(io_op_t){
35 .read = _null_read,
36 .write = _null_write,
37 .seek = NULL,
38 .close = NULL,
39 },
40};
41
42io_t *const io_null = &io_null_impl;
43
44void io_init(io_t *io, io_type_t type, io_flags_t flags, const io_op_t *ops)
45{
46 pr_dinfo2(io, "io_init(%p, %d, %d, %p)", (void *) io, type, flags, (void *) ops);
47
48 if (unlikely(!io))
49 mos_warn("io is NULL");
50
51 if (unlikely(!ops))
52 mos_warn("io->ops is NULL");
53
54 if (flags & IO_READABLE)
55 if (unlikely(!ops->read))
56 mos_warn("ops->read is NULL for readable io");
57
58 if (flags & IO_WRITABLE)
59 if (unlikely(!ops->write))
60 mos_warn("ops->write is NULL for writable io");
61
62 if (flags & IO_SEEKABLE)
63 if (unlikely(!ops->seek))
64 mos_warn("io->ops->seek is NULL for seekable io");
65
66 io->flags = flags;
67 io->type = type;
68 io->ops = ops;
69 io->closed = false;
70 io->refcount = 0;
71}
72
73io_t *io_ref(io_t *io)
74{
75 pr_dinfo2(io, "io_ref(%p)", (void *) io);
76 if (unlikely(!io))
77 {
78 mos_warn("io is NULL");
79 return NULL;
80 }
81
82 if (unlikely(io->closed))
83 {
84 mos_warn("%p is already closed", (void *) io);
85 return 0;
86 }
87
88 io->refcount++;
89 return io;
90}
91
92io_t *io_unref(io_t *io)
93{
94 pr_dinfo2(io, "io_unref(%p)", (void *) io);
95 if (unlikely(io->closed))
96 {
97 mos_warn("%p is already closed", (void *) io);
98 return NULL;
99 }
100
101 if (unlikely(io->refcount == 0))
102 {
103 mos_warn("%p has refcount 0", (void *) io);
104 return NULL;
105 }
106
107 io->refcount--;
108
109 if (io->refcount == 0)
110 {
111 if (io->ops->close)
112 {
113 pr_dinfo2(io, "closing %p", (void *) io);
114 io->closed = true;
115 io->ops->close(io);
116 }
117 else
118 {
119 pr_dinfo2(io, "%p is not closeable", (void *) io);
120 }
121 return NULL;
122 }
123
124 return io;
125}
126
127bool io_valid(const io_t *io)
128{
129 return io && !io->closed && io->refcount > 0 && io->ops;
130}
131
132size_t io_read(io_t *io, void *buf, size_t count)
133{
134 pr_dinfo2(io, "io_read(%p, %p, %zu)", (void *) io, buf, count);
135
136 if (unlikely(io->closed))
137 {
138 mos_warn("%p is already closed", (void *) io);
139 return 0;
140 }
141
142 if (!(io->flags & IO_READABLE))
143 {
144 pr_info2("%p is not readable\n", (void *) io);
145 return 0;
146 }
147
148 return io->ops->read(io, buf, count);
149}
150
151size_t io_pread(io_t *io, void *buf, size_t count, off_t offset)
152{
153 pr_dinfo2(io, "io_pread(%p, %p, %zu, %lu)", (void *) io, buf, count, offset);
154
155 if (unlikely(io->closed))
156 {
157 mos_warn("%p is already closed", (void *) io);
158 return 0;
159 }
160
161 if (!(io->flags & IO_READABLE))
162 {
163 pr_info2("%p is not readable\n", (void *) io);
164 return 0;
165 }
166
167 if (!(io->flags & IO_SEEKABLE))
168 {
169 pr_info2("%p is not seekable\n", (void *) io);
170 return 0;
171 }
172
173 const off_t old_offset = io_tell(io);
174 io_seek(io, offset, whence: IO_SEEK_SET);
175 const size_t ret = io_read(io, buf, count);
176 io_seek(io, offset: old_offset, whence: IO_SEEK_SET);
177 return ret;
178}
179
180size_t io_write(io_t *io, const void *buf, size_t count)
181{
182 pr_dinfo2(io, "io_write(%p, %p, %zu)", (void *) io, buf, count);
183
184 if (unlikely(io->closed))
185 {
186 mos_warn("%p is already closed", (void *) io);
187 return 0;
188 }
189
190 if (!(io->flags & IO_WRITABLE))
191 {
192 pr_info2("%p is not writable", (void *) io);
193 return 0;
194 }
195
196 return io->ops->write(io, buf, count);
197}
198
199off_t io_seek(io_t *io, off_t offset, io_seek_whence_t whence)
200{
201 pr_dinfo2(io, "io_seek(%p, %lu, %d)", (void *) io, offset, whence);
202
203 if (unlikely(io->closed))
204 {
205 mos_warn("%p is already closed", (void *) io);
206 return 0;
207 }
208
209 if (!(io->flags & IO_SEEKABLE))
210 {
211 pr_info2("%p is not seekable", (void *) io);
212 return 0;
213 }
214
215 return io->ops->seek(io, offset, whence);
216}
217
218off_t io_tell(io_t *io)
219{
220 pr_dinfo2(io, "io_tell(%p)", (void *) io);
221 return io_seek(io, offset: 0, whence: IO_SEEK_CURRENT);
222}
223
224bool io_mmap_perm_check(io_t *io, vm_flags flags, bool is_private)
225{
226 if (unlikely(io->closed))
227 {
228 mos_warn("%p is already closed", (void *) io);
229 return false;
230 }
231
232 if (!(io->flags & IO_MMAPABLE))
233 {
234 pr_info2("%p is not mmapable", (void *) io);
235 return false;
236 }
237
238 if (!(io->flags & IO_READABLE))
239 return false; // can't mmap if io is not readable
240
241 if (flags & VM_WRITE)
242 {
243 const bool may_mmap_writeable = is_private || io->flags & IO_WRITABLE;
244 if (!may_mmap_writeable)
245 return false; // can't mmap writable if io is not writable and not private
246 }
247
248 // if (flags & VM_EXEC && !(io->flags & IO_EXECUTABLE))
249 // return false; // can't mmap executable if io is not executable
250
251 return true;
252}
253
254bool io_mmap(io_t *io, vmap_t *vmap, off_t offset)
255{
256 pr_dinfo2(io, "io_mmap(%p, %p, %lu)", (void *) io, (void *) vmap, offset);
257 if (!io_mmap_perm_check(io, flags: vmap->vmflags, is_private: vmap->type == VMAP_TYPE_PRIVATE))
258 return false;
259
260 vmap->io = io;
261 vmap->io_offset = offset;
262
263 if (!io->ops->mmap(io, vmap, offset))
264 return false;
265
266 if (unlikely(!vmap->on_fault))
267 mos_panic("vmap->on_fault is NULL, possibly buggy io->ops->mmap() implementation");
268
269 io_ref(io); // mmap increases refcount
270 return true;
271}
272
273bool io_munmap(io_t *io, vmap_t *vmap, bool *unmapped)
274{
275 pr_dinfo2(io, "io_unmap(%p, %p, %p)", (void *) io, (void *) vmap, (void *) unmapped);
276 if (unlikely(io->closed))
277 {
278 mos_warn("%p is already closed", (void *) io);
279 return false;
280 }
281
282 if (unlikely(!vmap->io))
283 {
284 mos_warn("vmap->io is NULL");
285 return false;
286 }
287
288 if (unlikely(vmap->io != io))
289 {
290 mos_warn("vmap->io != io");
291 return false;
292 }
293
294 if (vmap->io->ops->munmap)
295 {
296 if (unlikely(!vmap->io->ops->munmap(vmap->io, vmap, unmapped)))
297 {
298 mos_warn("vmap->io->ops->unmap() failed");
299 return false;
300 }
301 }
302
303 io_unref(io); // unmap decreases refcount
304 return true;
305}
306
307void io_get_name(const io_t *io, char *buf, size_t size)
308{
309 if (io == NULL || io->ops == NULL || io->ops->get_name == NULL)
310 {
311 snprintf(str: buf, size, format: "<invalid io %p>", (void *) io);
312 return;
313 }
314
315 if (io->ops->get_name)
316 io->ops->get_name(io, buf, size);
317 else
318 snprintf(str: buf, size, format: "<unnamed io %p>", (void *) io);
319}
320