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