MOS Source Code
Loading...
Searching...
No Matches
sysfs.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-3.0-only
2
3#define pr_fmt(fmt) "sysfs: " fmt
5
10#include "mos/misc/setup.hpp"
11#include "mos/mm/mm.hpp"
13#include "mos/syslog/printk.hpp"
14
15#include <algorithm>
17#include <mos/io/io_types.h>
19#include <mos/type_utils.hpp>
20#include <mos/types.hpp>
21#include <mos_stdio.hpp>
22#include <mos_stdlib.hpp>
23#include <mos_string.hpp>
24
35
39static const file_ops_t sysfs_dir_file_ops = { 0 };
40
41static void sysfs_do_register(sysfs_dir_t *sysfs_dir);
42
43static u64 sysfs_get_ino(void)
44{
45 static u64 ino = 1;
46 return ino++;
47}
48
50{
53 pr_dinfo2(sysfs, "registering '%s'", dir->name.c_str());
56}
57
58static void sysfs_expand_buffer(sysfs_file_t *buffer, size_t new_npages)
59{
60 phyframe_t *oldbuf_page = buffer->buf_page;
61 const size_t oldbuf_npages = buffer->buf_npages;
62
63 // We need to allocate more pages
64 buffer->buf_npages = new_npages;
65 buffer->buf_page = mm_get_free_pages(buffer->buf_npages);
66
67 if (oldbuf_page)
68 {
69 memcpy((char *) phyframe_va(buffer->buf_page), (char *) phyframe_va(oldbuf_page), oldbuf_npages * MOS_PAGE_SIZE);
70 mm_free_pages(oldbuf_page, oldbuf_npages);
71 }
72}
73
74ssize_t sysfs_printf(sysfs_file_t *file, const char *fmt, ...)
75{
76retry_printf:;
77 const size_t spaces_left = file->buf_npages * MOS_PAGE_SIZE - file->buf_head_offset;
78
79 va_list args;
80 va_start(args, fmt);
81 const size_t should_write = vsnprintf((char *) phyframe_va(file->buf_page) + file->buf_head_offset, spaces_left, fmt, args);
82 va_end(args);
83
84 if (should_write >= spaces_left)
85 {
86 sysfs_expand_buffer(file, file->buf_npages + 1);
87 goto retry_printf;
88 }
89
90 file->buf_head_offset += should_write;
91 return should_write;
92}
93
94ssize_t sysfs_put_data(sysfs_file_t *file, const void *data, size_t count)
95{
96 const size_t spaces_left = file->buf_npages * MOS_PAGE_SIZE - file->buf_head_offset;
97
98 if (count > spaces_left)
99 sysfs_expand_buffer(file, file->buf_npages + (count - spaces_left) / MOS_PAGE_SIZE + 1);
100
101 memcpy((char *) phyframe_va(file->buf_page) + file->buf_head_offset, data, count);
102 file->buf_head_offset += count;
103 return count;
104}
105
106void sysfs_file_set_data(sysfs_file_t *file, void *data)
107{
108 file->data = data;
109}
110
112{
113 return file->data;
114}
115
117{
118 return file->item;
119}
120
121static bool sysfs_fops_open(inode_t *inode, file_t *file, bool created)
122{
123 MOS_UNUSED(created);
125 sysfs_file->item = (sysfs_item_t *) inode->private_data;
126
127 file->private_data = sysfs_file;
128 return true;
129}
130
131static void sysfs_fops_release(file_t *file)
132{
133 pr_dinfo2(sysfs, "closing %s in %s", file->dentry->name.c_str(), dentry_parent(*file->dentry)->name.c_str());
135 if (f->buf_page)
137 delete f;
138}
139
141{
143 if (f->buf_head_offset == 0)
144 {
145 if (!f->item->show(f))
146 return false;
147 }
148
149 return true;
150}
151
152static ssize_t sysfs_fops_read(const file_t *file, void *buf, size_t size, off_t offset)
153{
155 if (f->item->type != SYSFS_RO && f->item->type != SYSFS_RW)
156 return -ENOTSUP;
157
158 if (!sysfs_file_ensure_ready(file))
159 return -ETXTBSY;
160
161 if (offset >= f->buf_head_offset)
162 return 0;
163
164 const char *const buf_va = (char *) phyframe_va(f->buf_page);
165 const size_t begin = offset;
166 const size_t end = std::min(offset + size, (size_t) f->buf_head_offset);
167
168 memcpy((char *) buf, buf_va + begin, end - begin);
169 return end - begin;
170}
171
172static ssize_t sysfs_fops_write(const file_t *file, const void *buf, size_t size, off_t offset)
173{
175 if (f->item->type != SYSFS_WO && f->item->type != SYSFS_RW)
176 return -ENOTSUP;
177 return f->item->store(f, (const char *) buf, size, offset);
178}
179
180static off_t sysfs_fops_seek(file_t *file, off_t offset, io_seek_whence_t whence)
181{
182 if (offset != 0)
183 return -1; // cannot change its internal buffer state
184
185 if (whence == IO_SEEK_DATA || whence == IO_SEEK_HOLE)
186 return -1; // not supported
187
188 if (whence == IO_SEEK_SET)
189 return -1;
190
192
193 if (f->item->type == SYSFS_MEM)
194 return -1;
195
196 if (!sysfs_file_ensure_ready(file))
197 return -1;
198
199 return f->buf_head_offset;
200}
201
202bool sysfs_fops_mmap(file_t *file, vmap_t *vmap, off_t offset)
203{
204 MOS_UNUSED(vmap);
205 MOS_UNUSED(offset);
206
208 if (f->item->type == SYSFS_MEM)
209 {
210 return f->item->mem.mmap(f, vmap, offset);
211 }
212 else
213 {
214 if (!sysfs_file_ensure_ready(file))
215 return false;
216
217 if (offset > f->buf_head_offset)
218 return false; // cannot map past the end of the file
219 }
220
221 return true;
222}
223
224bool sysfs_fops_munmap(file_t *file, vmap_t *vmap, bool *unmapped)
225{
227 if (f->item->type == SYSFS_MEM)
228 {
229 return f->item->mem.munmap(f, vmap, unmapped);
230 }
231
232 return true;
233}
234
236 .open = sysfs_fops_open,
237 .read = sysfs_fops_read,
238 .write = sysfs_fops_write,
239 .release = sysfs_fops_release,
240 .seek = sysfs_fops_seek,
241 .mmap = sysfs_fops_mmap,
242 .munmap = sysfs_fops_munmap,
243};
244
246{
247 // root directory
248 if (dentry->inode == sysfs_sb->root->inode)
249 {
250 // list all the directories in the dcache
251 vfs_generic_iterate_dir(dentry, state, add_record);
252 return;
253 }
254
255 sysfs_dir_t *dir = (sysfs_dir_t *) dentry->inode->private_data;
256 MOS_ASSERT_X(dir, "invalid sysfs entry, possibly a VFS bug");
257
258 // non-dynamic directory
259 if (list_is_empty(&dir->_dynamic_items))
260 {
261 vfs_generic_iterate_dir(dentry, state, add_record);
262 return;
263 }
264
265 for (size_t j = 0; j < dir->num_items; j++)
266 {
267 sysfs_item_t *item = &dir->items[j];
268 if (item->type == _SYSFS_INVALID || item->type == SYSFS_DYN)
269 continue;
270
271 add_record(state, item->ino, item->name, FILE_TYPE_REGULAR);
272 }
273
274 // iterate the dynamic items
275 list_node_foreach(item_node, &dir->_dynamic_items)
276 {
277 sysfs_item_t *const dynitem = container_of(item_node, sysfs_item_t, list_node);
278 dynitem->dyn_iterate(dynitem, dentry, state, add_record);
279 }
280}
281
282static bool sysfs_iops_lookup(inode_t *dir, dentry_t *dentry)
283{
284 // if we get here, it means the expected dentry cannot be found in the dentry cache
285 // that means either it's a dynamic item, or the user is trying to access a file that doesn't exist
286
287 sysfs_dir_t *sysfs_dir = (sysfs_dir_t *) dir->private_data;
288
289 // if it's NULL, it implies that the dir must be the root directory /sys
290 MOS_ASSERT_X(sysfs_dir || dir == sysfs_sb->root->inode, "invalid sysfs entry, possibly a VFS bug");
291
292 if (!sysfs_dir)
293 return false; // sysfs root dir doesn't have any dynamic items
294
295 if (list_is_empty(&sysfs_dir->_dynamic_items))
296 return false;
297
298 list_node_foreach(item_node, &sysfs_dir->_dynamic_items)
299 {
300 sysfs_item_t *const dynitem = container_of(item_node, sysfs_item_t, list_node);
301 if (dynitem->dyn_lookup(dir, dentry))
302 return true;
303 }
304
305 return false;
306}
307
308static bool sysfs_iops_create(inode_t *dir, dentry_t *dentry, file_type_t type, file_perm_t perm)
309{
310 sysfs_dir_t *sysfs_dir = (sysfs_dir_t *) dir->private_data;
311 MOS_ASSERT_X(sysfs_dir || dir == sysfs_sb->root->inode, "invalid sysfs entry, possibly a VFS bug");
312
313 if (!sysfs_dir)
314 return false; // sysfs root dir doesn't have any dynamic items
315
316 if (list_is_empty(&sysfs_dir->_dynamic_items))
317 return false;
318
319 list_node_foreach(item_node, &sysfs_dir->_dynamic_items)
320 {
321 sysfs_item_t *const dynitem = container_of(item_node, sysfs_item_t, list_node);
322 if (dynitem->dyn_create(dir, dentry, type, perm))
323 return true;
324 }
325
326 return false;
327}
328
330 .iterate_dir = sysfs_iops_iterate_dir,
331 .lookup = sysfs_iops_lookup,
332 .newfile = sysfs_iops_create,
333};
334
335static PtrResult<dentry_t> sysfs_fsop_mount(filesystem_t *fs, const char *dev, const char *options)
336{
337 MOS_ASSERT(fs == &fs_sysfs);
338 if (strcmp(dev, "none") != 0)
339 {
340 mos_warn("device not supported");
341 return -EINVAL;
342 }
343
344 if (options && strlen(options) != 0 && strcmp(options, "defaults") != 0)
345 {
346 mos_warn("options '%s' not supported", options);
347 return -ENOTSUP;
348 }
349
350 return sysfs_sb->root;
351}
352
354 .name = "sysfs",
355 .mount = sysfs_fsop_mount,
356};
357
358static const file_perm_t sysfs_dir_perm = PERM_READ | PERM_EXEC; // r-xr-xr-x
359
360static void sysfs_do_register(sysfs_dir_t *sysfs_dir)
361{
363 dir_i->perm = sysfs_dir_perm;
364 dir_i->ops = &sysfs_dir_i_ops;
366 dir_i->private_data = sysfs_dir;
367
368 dentry_t *vfs_dir = dentry_get_from_parent(sysfs_sb, sysfs_sb->root, sysfs_dir->name);
369 dentry_attach(vfs_dir, dir_i);
370 sysfs_dir->_dentry = vfs_dir;
371
372 for (size_t i = 0; i < sysfs_dir->num_items; i++)
373 sysfs_register_file(sysfs_dir, &sysfs_dir->items[i]);
374}
375
377{
378 inode_t *inode = inode_create(sysfs_sb, sysfs_get_ino(), type);
379 inode->private_data = data;
380 return inode;
381}
382
384{
385 if (item->type == _SYSFS_INVALID)
386 return;
387
388 if (item->type == SYSFS_DYN)
389 {
390 MOS_ASSERT(item->dyn_iterate);
392 list_node_append(&sysfs_dir->_dynamic_items, list_node(item));
393 return;
394 }
395
397 file_i->file_ops = &sysfs_file_ops;
398 file_i->private_data = item;
399 item->ino = file_i->ino;
400
401 switch (item->type)
402 {
405 case SYSFS_RO: file_i->perm |= PERM_READ; break;
406 case SYSFS_RW: file_i->perm |= PERM_READ | PERM_WRITE; break;
407 case SYSFS_WO: file_i->perm |= PERM_WRITE; break;
408 case SYSFS_MEM:
409 file_i->perm |= PERM_READ | PERM_WRITE | PERM_EXEC;
410 file_i->size = item->mem.size;
411 break;
412 }
413
414 if (unlikely(item->name.empty()))
415 pr_warn("no name specified for sysfs entry '%s'", sysfs_dir ? sysfs_dir->name.c_str() : "/");
416
417 dentry_t *const target_dentry = sysfs_dir ? sysfs_dir->_dentry : sysfs_sb->root;
418 MOS_ASSERT_X(target_dentry, "registering sysfs entry '%s' failed", item->name.c_str());
419 dentry_t *d = dentry_get_from_parent(sysfs_sb, target_dentry, item->name);
420 dentry_attach(d, file_i);
421}
422
423MOS_INIT(VFS, register_sysfs)
424{
426
428 sysfs_sb->fs = &fs_sysfs;
431 sysfs_root_inode->perm = PERM_READ | PERM_EXEC;
432 sysfs_root_inode->file_ops = &sysfs_dir_file_ops;
433 dentry_attach(sysfs_sb->root, sysfs_root_inode);
434 sysfs_sb->root->inode->ops = &sysfs_dir_i_ops;
435}
#define MOS_ASSERT_X(cond, msg,...)
Definition assert.hpp:15
#define MOS_ASSERT(cond)
Definition assert.hpp:14
#define MOS_UNREACHABLE()
Definition assert.hpp:11
#define mos_warn(fmt,...)
Definition assert.hpp:23
#define MOS_PAGE_SIZE
Definition autoconf.h:6
char args[3][16]
Definition avr_io.c:16
const Char * c_str() const
Definition string.hpp:215
bool empty() const
Definition string.hpp:235
#define PERM_WRITE
Definition fs_types.h:58
#define PERM_READ
Definition fs_types.h:57
u16 file_perm_t
Definition fs_types.h:52
#define PERM_EXEC
Definition fs_types.h:59
file_type_t
Definition fs_types.h:14
@ FILE_TYPE_REGULAR
Definition fs_types.h:15
@ FILE_TYPE_DIRECTORY
Definition fs_types.h:16
void dentry_attach(dentry_t *d, inode_t *inode)
Attach an inode to a dentry.
Definition dentry.cpp:306
should_inline dentry_t * dentry_parent(const dentry_t &dentry)
Definition dentry.hpp:67
MOSAPI s32 strcmp(const char *str1, const char *str2)
MOSAPI int vsnprintf(char *__restrict buf, size_t size, const char *__restrict format, va_list args)
MOSAPI void linked_list_init(list_node_t *head_node)
Initialise a circular double linked list.
Definition list.cpp:15
MOSAPI void list_node_append(list_node_t *head, list_node_t *item)
Definition list.cpp:68
#define list_node(element)
Get the ‘list_node’ of a list element. This is exactly the reverse of ‘list_entry’ above.
Definition list.hpp:74
#define list_node_foreach(v, h)
Definition list.hpp:94
list_node_t list_head
A linked list head.
Definition list.hpp:23
MOSAPI bool list_is_empty(const list_node_t *head)
Definition list.cpp:21
#define phyframe_va(frame)
Definition mm.hpp:80
#define mm_free_pages(frame, npages)
Definition mm.hpp:88
phyframe_t * mm_get_free_pages(size_t npages)
Definition mm.cpp:48
void vfs_register_filesystem(filesystem_t *fs)
Definition vfs.cpp:446
inode_t * inode_create(superblock_t *sb, u64 ino, file_type_t type)
Definition inode.cpp:59
io_seek_whence_t
Definition io_types.h:6
@ IO_SEEK_DATA
Definition io_types.h:10
@ IO_SEEK_SET
Definition io_types.h:9
@ IO_SEEK_HOLE
Definition io_types.h:11
#define __nodiscard
Definition mos_global.h:35
#define MOS_UNUSED(x)
Definition mos_global.h:65
#define unlikely(x)
Definition mos_global.h:40
T * create(Args &&...args)
Definition allocator.hpp:10
static void * memcpy(void *s1, const void *s2, size_t n)
Definition pb_syshdr.h:90
#define NULL
Definition pb_syshdr.h:46
static size_t strlen(const char *s)
Definition pb_syshdr.h:80
#define pr_warn(fmt,...)
Definition printk.hpp:38
#define pr_dinfo2(feat, fmt,...)
Definition printk.hpp:27
#define MOS_INIT(_comp, _fn)
Definition setup.hpp:38
size_t size
Definition slab.cpp:34
mos::string name
inode_t * inode
void * private_data
dentry_t * dentry
file_perm_t perm
const inode_ops_t * ops
size_t size
const file_ops_t * file_ops
void * private_data
const size_t num_items
Definition sysfs.hpp:66
sysfs_item_t *const items
Definition sysfs.hpp:65
mos::string name
Definition sysfs.hpp:64
list_head _dynamic_items
for internal use only
Definition sysfs.hpp:71
dentry_t * _dentry
for internal use only
Definition sysfs.hpp:70
ssize_t buf_head_offset
Definition sysfs.cpp:30
ssize_t buf_npages
Definition sysfs.cpp:31
void * data
Definition sysfs.cpp:33
sysfs_item_t * item
Definition sysfs.cpp:27
phyframe_t * buf_page
Definition sysfs.cpp:29
void(* dyn_iterate)(struct _sysfs_item *item, dentry_t *dentry, vfs_listdir_state_t *iterator_state, dentry_iterator_op op)
Definition sysfs.hpp:36
sysfs_item_type_t type
Definition sysfs.hpp:23
size_t(* store)(sysfs_file_t *file, const char *buf, size_t count, off_t offset)
Definition sysfs.hpp:25
bool(* mmap)(sysfs_file_t *file, vmap_t *vmap, off_t offset)
Definition sysfs.hpp:30
size_t size
Definition sysfs.hpp:32
const mos::string name
Definition sysfs.hpp:22
bool(* munmap)(sysfs_file_t *file, vmap_t *vmap, bool *unmapped)
Definition sysfs.hpp:31
struct sysfs_item_t::@153140136221376072256077077307321051166012334217 mem
bool(* dyn_create)(inode_t *parent_dir, dentry_t *dentry, file_type_t type, file_perm_t perm)
Definition sysfs.hpp:38
ino_t ino
Definition sysfs.hpp:26
bool(* dyn_lookup)(inode_t *parent_dir, dentry_t *dentry)
Definition sysfs.hpp:37
bool(* show)(sysfs_file_t *file)
Definition sysfs.hpp:24
Definition mm.hpp:59
static __nodiscard bool sysfs_file_ensure_ready(const file_t *file)
Definition sysfs.cpp:140
void sysfs_file_set_data(sysfs_file_t *file, void *data)
Definition sysfs.cpp:106
static const file_perm_t sysfs_dir_perm
Definition sysfs.cpp:358
void sysfs_register(sysfs_dir_t *dir)
Register a sysfs directory.
Definition sysfs.cpp:49
void sysfs_register_file(sysfs_dir_t *sysfs_dir, sysfs_item_t *item)
Register an entry in a sysfs directory.
Definition sysfs.cpp:383
static list_head sysfs_dirs
Definition sysfs.cpp:36
ssize_t sysfs_printf(sysfs_file_t *file, const char *fmt,...)
Definition sysfs.cpp:74
static void sysfs_expand_buffer(sysfs_file_t *buffer, size_t new_npages)
Definition sysfs.cpp:58
static off_t sysfs_fops_seek(file_t *file, off_t offset, io_seek_whence_t whence)
Definition sysfs.cpp:180
inode_t * sysfs_create_inode(file_type_t type, void *data)
Definition sysfs.cpp:376
bool sysfs_fops_munmap(file_t *file, vmap_t *vmap, bool *unmapped)
Definition sysfs.cpp:224
static const inode_ops_t sysfs_dir_i_ops
Definition sysfs.cpp:329
ssize_t sysfs_put_data(sysfs_file_t *file, const void *data, size_t count)
Definition sysfs.cpp:94
static ssize_t sysfs_fops_write(const file_t *file, const void *buf, size_t size, off_t offset)
Definition sysfs.cpp:172
static void sysfs_fops_release(file_t *file)
Definition sysfs.cpp:131
static PtrResult< dentry_t > sysfs_fsop_mount(filesystem_t *fs, const char *dev, const char *options)
Definition sysfs.cpp:335
static bool sysfs_fops_open(inode_t *inode, file_t *file, bool created)
Definition sysfs.cpp:121
static void sysfs_iops_iterate_dir(dentry_t *dentry, vfs_listdir_state_t *state, dentry_iterator_op add_record)
Definition sysfs.cpp:245
static superblock_t * sysfs_sb
Definition sysfs.cpp:38
bool sysfs_fops_mmap(file_t *file, vmap_t *vmap, off_t offset)
Definition sysfs.cpp:202
static const file_ops_t sysfs_dir_file_ops
Definition sysfs.cpp:39
static const file_ops_t sysfs_file_ops
Definition sysfs.cpp:235
static bool sysfs_iops_create(inode_t *dir, dentry_t *dentry, file_type_t type, file_perm_t perm)
Definition sysfs.cpp:308
sysfs_item_t * sysfs_file_get_item(sysfs_file_t *file)
Definition sysfs.cpp:116
static u64 sysfs_get_ino(void)
Definition sysfs.cpp:43
void * sysfs_file_get_data(sysfs_file_t *file)
Definition sysfs.cpp:111
static void sysfs_do_register(sysfs_dir_t *sysfs_dir)
Definition sysfs.cpp:360
static ssize_t sysfs_fops_read(const file_t *file, void *buf, size_t size, off_t offset)
Definition sysfs.cpp:152
static bool sysfs_iops_lookup(inode_t *dir, dentry_t *dentry)
Definition sysfs.cpp:282
filesystem_t fs_sysfs
Definition sysfs.cpp:353
@ SYSFS_RW
Definition sysfs.hpp:14
@ SYSFS_MEM
memory-backed file
Definition sysfs.hpp:16
@ SYSFS_DYN
dynamic directory items
Definition sysfs.hpp:17
@ _SYSFS_INVALID
Definition sysfs.hpp:12
@ SYSFS_RO
Definition sysfs.hpp:13
@ SYSFS_WO
Definition sysfs.hpp:15
static char buffer[2048]
ssize_t off_t
Definition types.h:80
unsigned long long u64
Definition types.h:19
signed long ssize_t
Definition types.h:79
#define container_of(ptr, type, member)
Definition types.hpp:31
void dentry_iterator_op(vfs_listdir_state_t *state, u64 ino, mos::string_view name, file_type_t type)
Definition vfs_types.hpp:61
void vfs_generic_iterate_dir(const dentry_t *dir, vfs_listdir_state_t *state, dentry_iterator_op add_record)
dentry_t * dentry_get_from_parent(superblock_t *sb, dentry_t *parent, mos::string_view name)
Create a new dentry with the given name and parent.
Definition vfs_utils.cpp:37