1// SPDX-License-Identifier: GPL-3.0-or-later
2
3#include "mos/mm/mm.hpp"
4
5#include <mos/io/io.hpp>
6#include <mos/io/io_types.h>
7#include <mos/mm/mm_types.h>
8#include <mos/mos_global.h>
9#include <mos/syslog/printk.hpp>
10#include <mos_stdio.hpp>
11
12struct NullIO final : IO
13{
14 NullIO() : IO(IO_READABLE | IO_WRITABLE, IO_NULL) {};
15 virtual ~NullIO() {};
16 void on_closed() override
17 {
18 mos_panic("null io cannot be closed");
19 }
20
21 size_t on_read(void *, size_t) override
22 {
23 return 0;
24 }
25
26 size_t on_write(const void *, size_t) override
27 {
28 return 0;
29 }
30};
31
32static NullIO io_null_impl;
33IO *const io_null = &io_null_impl;
34
35IO::IO(IOFlags flags, io_type_t type) : io_flags(flags), io_type(type)
36{
37}
38
39IO::~IO()
40{
41 if (!io_closed)
42 mEmerg << "IO::~IO: " << (void *) (this) << " is not closed at destruction";
43}
44
45mos::string IO::name() const
46{
47 return "<unnamed io " + mos::to_string(value: this) + ">";
48}
49
50size_t IO::read(void *buf, size_t count)
51{
52 dInfo2<io> << "io_read(" << (void *) this << ", " << buf << ", " << count << ")";
53
54 if (unlikely(io_closed))
55 {
56 mos_warn("%p is already closed", (void *) this);
57 return 0;
58 }
59
60 if (!io_flags.test(b: IO_READABLE))
61 {
62 mInfo << (void *) this << " is not readable\n";
63 return 0;
64 }
65
66 return on_read(buf, count);
67}
68
69size_t IO::pread(void *buf, size_t count, off_t offset)
70{
71 dInfo2<io> << "io_pread(" << (void *) this << ", " << buf << ", " << count << ", " << offset << ")";
72
73 if (unlikely(io_closed))
74 {
75 mos_warn("%p is already closed", (void *) this);
76 return 0;
77 }
78
79 if (!(io_flags.test(b: IO_READABLE)))
80 {
81 mInfo << (void *) this << " is not readable\n";
82 return 0;
83 }
84
85 if (!(io_flags.test(b: IO_SEEKABLE)))
86 {
87 mInfo << (void *) this << " is not seekable\n";
88 return 0;
89 }
90
91 const off_t old_offset = this->tell();
92 this->seek(offset, IO_SEEK_SET);
93 const size_t ret = read(buf, count);
94 this->seek(old_offset, IO_SEEK_SET);
95 return ret;
96}
97
98size_t IO::write(const void *buf, size_t count)
99{
100 dInfo2<io> << "io_write(" << (void *) this << ", " << buf << ", " << count << ")";
101
102 if (unlikely(io_closed))
103 {
104 mos_warn("%p is already closed", (void *) this);
105 return 0;
106 }
107
108 if (!(io_flags.test(b: IO_WRITABLE)))
109 {
110 mInfo << (void *) this << " is not writable";
111 return 0;
112 }
113
114 return on_write(buf, count);
115}
116
117off_t IO::seek(off_t offset, io_seek_whence_t whence)
118{
119 dInfo2<io> << "io_seek(" << (void *) this << ", " << offset << ", " << whence << ")";
120
121 if (unlikely(io_closed))
122 {
123 mos_warn("%p is already closed", (void *) this);
124 return 0;
125 }
126
127 if (!io_flags.test(b: IO_SEEKABLE))
128 {
129 mInfo << (void *) this << " is not seekable";
130 return 0;
131 }
132
133 return on_seek(offset, whence);
134}
135
136off_t IO::tell()
137{
138 dInfo2<io> << fmt("io_tell({})", (void *) this);
139 return seek(offset: 0, whence: IO_SEEK_CURRENT);
140}
141
142bool IO::VerifyMMapPermissions(VMFlags flags, bool is_private)
143{
144 if (unlikely(io_closed))
145 {
146 mos_warn("%p is already closed", (void *) io);
147 return false;
148 }
149
150 if (!(io_flags.test(b: IO_MMAPABLE)))
151 {
152 mInfo << (void *) io << " is not mmapable";
153 return false;
154 }
155
156 if (!(io_flags.test(b: IO_READABLE)))
157 return false; // can't mmap if io is not readable
158
159 if (flags & VM_WRITE)
160 {
161 const bool may_mmap_writeable = is_private || io_flags.test(b: IO_WRITABLE);
162 if (!may_mmap_writeable)
163 return false; // can't mmap writable if io is not writable and not private
164 }
165
166 // if (flags & VM_EXEC && !(io->flags & IO_EXECUTABLE))
167 // return false; // can't mmap executable if io is not executable
168
169 return true;
170}
171
172bool IO::map(vmap_t *vmap, off_t offset)
173{
174 dInfo2<io> << "io_mmap(" << (void *) this << ", " << (void *) vmap << ", " << offset << ")";
175 if (!VerifyMMapPermissions(flags: vmap->vmflags, is_private: vmap->type == VMAP_TYPE_PRIVATE))
176 return false;
177
178 vmap->io = this;
179 vmap->io_offset = offset;
180
181 if (!this->on_mmap(vmap, offset))
182 return false;
183
184 if (unlikely(!vmap->on_fault))
185 mos_panic("vmap->on_fault is NULL, possibly buggy io->ops->mmap() implementation");
186
187 this->ref(); // mmap increases refcount
188 return true;
189}
190
191bool IO::unmap(vmap_t *vmap, bool *unmapped)
192{
193 dInfo2<io> << "io_unmap(" << (void *) this << ", " << (void *) vmap << ", " << (void *) unmapped << ")";
194 if (unlikely(io_closed))
195 {
196 mos_warn("%p is already closed", (void *) this);
197 return false;
198 }
199
200 if (unlikely(!vmap->io))
201 {
202 mos_warn("vmap->io is NULL");
203 return false;
204 }
205
206 if (unlikely(vmap->io != this))
207 {
208 mos_warn("vmap->io != io");
209 return false;
210 }
211
212 if (unlikely(!on_munmap(vmap, unmapped)))
213 {
214 mos_warn("vmap->io->ops->unmap() failed");
215 return false;
216 }
217
218 this->unref(); // unmap decreases refcount
219 return true;
220}
221
222size_t IO::on_read(void *, size_t)
223{
224 MOS_UNREACHABLE_X("IO %p is readable but does not implement on_read", (void *) this);
225 return -ENOTSUP;
226}
227
228size_t IO::on_write(const void *, size_t)
229{
230 MOS_UNREACHABLE_X("IO %p is writable but does not implement on_write", (void *) this);
231 return -ENOTSUP;
232}
233
234bool IO::on_mmap(vmap_t *, off_t)
235{
236 MOS_UNREACHABLE_X("IO %p is mappable but does not implement on_mmap", (void *) this);
237 return false;
238}
239
240bool IO::on_munmap(vmap_t *, bool *)
241{
242 return false;
243}
244
245off_t IO::on_seek(off_t, io_seek_whence_t)
246{
247 MOS_UNREACHABLE_X("IO %p is seekable but does not implement on_seek", (void *) this);
248}
249