From c80f3db3855748541e50851de10b9c0526285f11 Mon Sep 17 00:00:00 2001 From: Matheus Date: Mon, 1 May 2023 21:25:34 -0300 Subject: [PATCH] kernel: devfs --- kernel-src/arch/x86-64/main.c | 10 ++ kernel-src/fs/devfs.c | 281 ++++++++++++++++++++++++++++++ kernel-src/include/kernel/devfs.h | 24 +++ 3 files changed, 315 insertions(+) create mode 100644 kernel-src/fs/devfs.c create mode 100644 kernel-src/include/kernel/devfs.h diff --git a/kernel-src/arch/x86-64/main.c b/kernel-src/arch/x86-64/main.c index 86d3fbac..2b1e037f 100644 --- a/kernel-src/arch/x86-64/main.c +++ b/kernel-src/arch/x86-64/main.c @@ -17,6 +17,7 @@ #include #include #include +#include static cpu_t bsp_cpu; @@ -42,8 +43,17 @@ void kernel_entry() { sched_init(); vfs_init(); tmpfs_init(); + devfs_init(); printf("mounting tmpfs on /\n"); __assert(vfs_mount(NULL, vfsroot, "/", "tmpfs", NULL) == 0); initrd_unpack(); + + vattr_t attr = {0}; + attr.mode = 0644; + __assert(vfs_create(vfsroot, "/dev", &attr, V_TYPE_DIR, NULL) == 0); + + printf("mounting devfs on /dev\n"); + __assert(vfs_mount(NULL, vfsroot, "/dev", "devfs", NULL) == 0); + __assert(!"kernel entry end"); } diff --git a/kernel-src/fs/devfs.c b/kernel-src/fs/devfs.c new file mode 100644 index 00000000..34103906 --- /dev/null +++ b/kernel-src/fs/devfs.c @@ -0,0 +1,281 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +static devnode_t *devfsroot; + +static spinlock_t tablelock; +static hashtable_t devtable; +static hashtable_t nametable; + +static scache_t *nodecache; +static uintmax_t currentinode; + +static vfsops_t vfsops; +static vops_t vnops; + +static int devfs_mount(vfs_t **vfs, vnode_t *mountpoint, vnode_t *backing, void *data) { + vfs_t *vfsp = alloc(sizeof(vfs_t)); + if (vfs == NULL) + return ENOMEM; + + *vfs = vfsp; + vfsp->ops = &vfsops; + + return 0; +} + +static int devfs_root(vfs_t *vfs, vnode_t **node) { + devfsroot->vnode.vfs = vfs; + *node = (vnode_t *)(&devfsroot); + return 0; +} + +int devfs_open(vnode_t **node, int flags, cred_t *cred) { + devnode_t *devnode = (devnode_t *)(*node); + if (devnode->master) + devnode = devnode->master; + + return devnode->devops->open(devnode->attr.rdevminor, flags); +} + +int devfs_close(vnode_t *node, int flags, cred_t *cred) { + devnode_t *devnode = (devnode_t *)node; + if (devnode->master) + devnode = devnode->master; + + return devnode->devops->close(devnode->attr.rdevminor, flags); +} + +int devfs_read(vnode_t *node, void *buffer, size_t size, uintmax_t offset, int flags, size_t *readc, cred_t *cred) { + devnode_t *devnode = (devnode_t *)node; + if (devnode->master) + devnode = devnode->master; + + return devnode->devops->read(devnode->attr.rdevminor, buffer, size, offset, flags, readc); +} + +int devfs_write(vnode_t *node, void *buffer, size_t size, uintmax_t offset, int flags, size_t *writec, cred_t *cred) { + devnode_t *devnode = (devnode_t *)node; + if (devnode->master) + devnode = devnode->master; + + return devnode->devops->write(devnode->attr.rdevminor, buffer, size, offset, flags, writec); +} + +int devfs_lookup(vnode_t *node, char *name, vnode_t **result, cred_t *cred) { + if (node != (vnode_t *)devfsroot) + return EINVAL; + + void *v; + int error = hashtable_get(&nametable, &v, name, strlen(name)); + if (error) + return error; + + vnode_t *rnode = v; + VOP_HOLD(rnode); + *result = rnode; + + return 0; +} + +int devfs_getattr(vnode_t *node, vattr_t *attr, cred_t *cred) { + devnode_t *devnode = (devnode_t *)node; + __assert(devnode->physical); + VOP_LOCK(devnode->physical); + int err = VOP_SETATTR(devnode->physical, attr, cred); + VOP_UNLOCK(devnode->physical); + return err; +} + +int devfs_setattr(vnode_t *node, vattr_t *attr, cred_t *cred) { + devnode_t *devnode = (devnode_t *)node; + __assert(devnode->physical); + VOP_LOCK(devnode->physical); + int err = VOP_GETATTR(devnode->physical, attr, cred); + VOP_UNLOCK(devnode->physical); + return err; +} + +int devfs_poll(vnode_t *node, int events) { + devnode_t *devnode = (devnode_t *)node; + if (devnode->master) + devnode = devnode->master; + + return devnode->devops->poll(devnode->attr.rdevminor, events); +} + +int devfs_access(vnode_t *node, mode_t mode, cred_t *cred) { + // TODO permission checks + return 0; +} + +int devfs_inactive(vnode_t *node) { + devnode_t *devnode = (devnode_t *)node; + vnode_t *master = (vnode_t *)devnode->master; + if (devnode->physical && devnode->physical != node) + VOP_RELEASE(devnode->physical); + if (devnode->master) + VOP_RELEASE(master); + slab_free(nodecache, node); + return 0; +} + +int devfs_create(vnode_t *parent, char *name, vattr_t *attr, int type, vnode_t **result, cred_t *cred) { + if (parent != (vnode_t *)devfsroot || (type != V_TYPE_CHDEV && type != V_TYPE_BLKDEV)) + return EINVAL; + + size_t namelen = strlen(name); + void *v; + if (hashtable_get(&nametable, &v, name, namelen) == 0) + return EEXIST; + + devnode_t *node = slab_allocate(nodecache); + if (node == NULL) + return ENOMEM; + + timespec_t time = timekeeper_time(); + vattr_t tmpattr = *attr; + tmpattr.atime = time; + tmpattr.ctime = time; + tmpattr.mtime = time; + tmpattr.size = 0; + __assert(devfs_setattr(&node->vnode, &tmpattr, cred) == 0); + node->attr.nlinks = 1; + node->attr.rdevmajor = tmpattr.rdevmajor; + node->attr.rdevminor = tmpattr.rdevminor; + node->physical = &node->vnode; + node->vnode.vfs = parent->vfs; + + int error = hashtable_set(&nametable, node, name, namelen, true); + if (error) { + slab_free(nodecache, node); + return error; + } + + VOP_HOLD(&node->vnode); + *result = &node->vnode; + + return 0; +} + +static int devfs_enosys() { + return ENOSYS; +} + +static vfsops_t vfsops = { + .mount = devfs_mount, + .root = devfs_root +}; + +static vops_t vnops = { + .create = devfs_create, + .open = devfs_open, + .close = devfs_close, + .getattr = devfs_getattr, + .setattr = devfs_setattr, + .lookup = devfs_lookup, + .poll = devfs_poll, + .read = devfs_read, + .write = devfs_write, + .access = devfs_access, + .unlink = devfs_enosys, + .link = devfs_enosys, + .symlink = devfs_enosys, + .readlink = devfs_enosys, + .inactive = devfs_enosys +}; + +static void ctor(scache_t *cache, void *obj) { + devnode_t *node = obj; + memset(node, 0, sizeof(devnode_t)); + node->vnode.refcount = 1; + SPINLOCK_INIT(node->vnode.lock); + node->vnode.ops = &vnops; + node->attr.inode = ++currentinode; +} + +void devfs_init() { + __assert(hashtable_init(&devtable, 50) == 0); + __assert(hashtable_init(&nametable, 50) == 0); + nodecache = slab_newcache(sizeof(devnode_t), 0, ctor, ctor); + SPINLOCK_INIT(tablelock); + __assert(nodecache); + + devfsroot = slab_allocate(nodecache); + __assert(devfsroot); + devfsroot->vnode.type = V_TYPE_DIR; + devfsroot->vnode.flags = V_FLAGS_ROOT; + __assert(vfs_register(&vfsops, "devfs") == 0); +} + +// register new device +int devfs_register(devops_t *devops, char *name, int type, int major, int minor, mode_t mode) { + __assert(type == V_TYPE_CHDEV || type == V_TYPE_BLKDEV); + devnode_t *master = slab_allocate(nodecache); + if (master == NULL) + return ENOMEM; + + master->devops = devops; + + int key[2] = {major, minor}; + + spinlock_acquire(&tablelock); + int error = hashtable_set(&devtable, master, key, sizeof(key), true); + spinlock_release(&tablelock); + if (error) { + slab_free(nodecache, master); + return error; + } + + vattr_t attr = {0}; + attr.mode = mode; + attr.rdevmajor = major; + attr.rdevminor = minor; + + vnode_t *newvnode = NULL; + + error = vfs_create((vnode_t *)(&devfsroot), name, &attr, type, &newvnode); + if (error) { + spinlock_acquire(&tablelock); + __assert(hashtable_remove(&devtable, key, sizeof(key) == 0)); + spinlock_release(&tablelock); + slab_free(nodecache, master); + return error; + } + + devnode_t *newdevnode = (devnode_t *)newvnode; + master->attr = newdevnode->attr; + newdevnode->master = master; + VOP_RELEASE(newvnode); + + return 0; +} + +// allocate a pointer node to the master node associated with device major and minor +int devfs_getnode(vnode_t *physical, int major, int minor, vnode_t **node) { + int key[2] = {major, minor}; + spinlock_acquire(&tablelock); + void *r; + int err = hashtable_get(&devtable, &r, key, sizeof(key)); + spinlock_release(&tablelock); + if (err) + return err; + + devnode_t *newnode = slab_allocate(nodecache); + if (newnode) + return ENOMEM; + + devnode_t *master = r; + newnode->master = master; + VOP_HOLD(&master->vnode); + newnode->physical = physical; + VOP_HOLD(physical); + *node = &newnode->vnode; + return 0; +} diff --git a/kernel-src/include/kernel/devfs.h b/kernel-src/include/kernel/devfs.h new file mode 100644 index 00000000..9c39044e --- /dev/null +++ b/kernel-src/include/kernel/devfs.h @@ -0,0 +1,24 @@ +#ifndef _DEVFS_H +#define _DEVFS_H + +#include + +typedef struct { + int (*open)(int minor, int flags); + int (*close)(int minor, int flags); + int (*read)(int minor, void *buffer, size_t size, uintmax_t offset, int flags, size_t *readc); + int (*write)(int minor, void *buffer, size_t size, uintmax_t offset, int flags, size_t *writec); + int (*poll)(int minor, int events); +} devops_t; + +typedef struct devnode_t { + vnode_t vnode; + vattr_t attr; + devops_t *devops; + vnode_t *physical; + struct devnode_t *master; +} devnode_t; + +void devfs_init(); + +#endif