diff --git a/fastrpc/hexagonfs.c b/fastrpc/hexagonfs.c new file mode 100644 index 0000000..7bc6133 --- /dev/null +++ b/fastrpc/hexagonfs.c @@ -0,0 +1,368 @@ +/* + * Virtual read-only filesystem for Hexagon processors + * + * Copyright (C) 2023 Richard Acayan + * + * This file is part of sensh. + * + * Sensh is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "hexagonfs.h" + +#define HEXAGONFS_MAX_FD 256 + +#define DEFINE_VIRT_DIR(dirname, files...) \ + (struct hexagonfs_dirent) { \ + .name = dirname, \ + .ops = &hexagonfs_virt_dir_ops, \ + .u.dir = (struct hexagonfs_dirent *[]) { files }, \ + } + +#define DEFINE_MAPPED(dirname, path) \ + (struct hexagonfs_dirent) { \ + .name = dirname, \ + .ops = &hexagonfs_mapped_ops, \ + .u.phys = path, \ + } + +#define DEFINE_SYSFILE(dirname, func) \ + (struct hexagonfs_dirent) { \ + .name = dirname, \ + .ops = &hexagonfs_sysfs_ops, \ + .u.func = func, \ + } + +static struct hexagonfs_dirent root_dir = DEFINE_VIRT_DIR("/", + &DEFINE_VIRT_DIR("mnt", + &DEFINE_VIRT_DIR("vendor", + &DEFINE_VIRT_DIR("persist", + &DEFINE_VIRT_DIR("sensors", + &DEFINE_MAPPED("registry", + "/var/lib/qcom/sensors"), + NULL, + ), + NULL, + ), + NULL, + ), + NULL, + ), + &DEFINE_VIRT_DIR("sys", + &DEFINE_VIRT_DIR("devices", + &DEFINE_VIRT_DIR("soc0", + &DEFINE_MAPPED("hw_platform", + "/sys/kernel/debug/qcom_socinfo/hardware_platform"), + &(struct hexagonfs_dirent) { + .name = "platform_subtype", + .ops = &hexagonfs_plat_subtype_name_ops, + .u.phys = "/sys/kernel/debug/qcom_socinfo/hardware_platform_subtype", + }, + &DEFINE_MAPPED("platform_subtype_id", + "/sys/kernel/debug/qcom_socinfo/hardware_platform_subtype"), + &DEFINE_MAPPED("platform_version", + "/sys/kernel/debug/qcom_socinfo/platform_version"), + &DEFINE_MAPPED("revision", + "/sys/devices/soc0/revision"), + &DEFINE_MAPPED("soc_id", + "/sys/devices/soc0/soc_id"), + NULL, + ), + NULL, + ), + NULL, + ), + &DEFINE_VIRT_DIR("usr", + &DEFINE_VIRT_DIR("lib", + &DEFINE_VIRT_DIR("qcom", + &DEFINE_MAPPED("adsp", + "/usr/lib/qcom/adsp/"), + NULL, + ), + NULL, + ), + NULL, + ), + &DEFINE_VIRT_DIR("vendor", + &DEFINE_VIRT_DIR("etc", + &DEFINE_VIRT_DIR("sensors", + &DEFINE_MAPPED("config", + "/etc/qcom/sensors.d/"), + &DEFINE_MAPPED("sns_reg_config", + "/etc/qcom/sns_reg.conf"), + NULL, + ), + NULL, + ), + NULL, + ), + NULL, +); + +static struct hexagonfs_fd *fds[HEXAGONFS_MAX_FD]; + +static char *copy_segment_and_advance(const char *path, + bool *trailing_slash, + const char **next) +{ + const char *next_tmp; + char *segment; + size_t segment_len; + + next_tmp = strchr(path, '/'); + if (next_tmp == NULL) { + next_tmp = &path[strlen(path)]; + *trailing_slash = false; + } else { + *trailing_slash = true; + } + + segment_len = next_tmp - path; + + while (*next_tmp == '/') + next_tmp++; + + segment = malloc(segment_len + 1); + if (segment == NULL) + return NULL; + + memcpy(segment, path, segment_len); + segment[segment_len] = 0; + + *next = next_tmp; + + return segment; +} + +static struct hexagonfs_fd *pop_dir(struct hexagonfs_fd *dir, + struct hexagonfs_fd *root) +{ + struct hexagonfs_fd *up; + + if (dir != root && dir->up != NULL) { + up = dir->up; + dir->ops->close(dir->data); + free(dir); + } else { + up = dir; + } + + return up; +} + +static int allocate_file_number(struct hexagonfs_fd *fd) +{ + size_t i; + + for (i = 0; i < HEXAGONFS_MAX_FD; i++) { + if (fds[i] == NULL) { + fd->is_assigned = true; + fds[i] = fd; + return i; + } + } + + return -EMFILE; +} + +static void destroy_file_descriptor(struct hexagonfs_fd *fd) +{ + struct hexagonfs_fd *curr = fd; + struct hexagonfs_fd *next; + + while (curr != NULL && !curr->is_assigned) { + next = curr->up; + curr->ops->close(curr->data); + free(curr); + + curr = next; + } +} + +int hexagonfs_open_root() +{ + struct hexagonfs_fd *fd; + int ret; + + fd = malloc(sizeof(struct hexagonfs_fd)); + if (fd == NULL) + return -ENOMEM; + + fd->off = 0; + fd->is_assigned = false; + fd->up = NULL; + fd->ops = root_dir.ops; + + ret = root_dir.ops->from_dirent(root_dir.u.ptr, true, &fd->data); + if (ret) + goto err; + + ret = allocate_file_number(fd); + if (ret < 0) + goto err; + + return ret; + +err: + destroy_file_descriptor(fd); + return ret; +} + +int hexagonfs_openat(int rootfd, int dirfd, const char *name) +{ + struct hexagonfs_fd *fd; + const char *curr = name; + char *segment; + bool expect_dir; + int selected = dirfd; + int ret = 0; + + if (*curr == '/') { + selected = rootfd; + + while (*curr == '/') + curr++; + } + + fd = fds[selected]; + + while (*curr != '\0' && !ret) { + segment = copy_segment_and_advance(curr, &expect_dir, &curr); + if (segment == NULL) { + ret = -ENOMEM; + goto err; + } + + if (!strcmp(segment, ".")) { + goto next; + } else if (!strcmp(segment, "..")) { + fd = pop_dir(fd, fds[rootfd]); + } else { + ret = fd->ops->openat(fd, segment, expect_dir, &fd); + } + + next: + free(segment); + } + + if (ret) + goto err; + + ret = allocate_file_number(fd); + if (ret) + goto err; + + return ret; + +err: + destroy_file_descriptor(fd); + + return ret; +} + +int hexagonfs_close(int fileno) +{ + struct hexagonfs_fd *fd; + + if (fileno >= HEXAGONFS_MAX_FD) + return -EBADF; + + fd = fds[fileno]; + if (fd == NULL || fd->ops == NULL) + return -EBADF; + + fd->is_assigned = false; + destroy_file_descriptor(fd); + + fds[fileno] = NULL; + + return 0; +} + +int hexagonfs_lseek(int fileno, off_t off, int whence) +{ + struct hexagonfs_fd *fd; + + if (fileno >= HEXAGONFS_MAX_FD) + return -EBADF; + + fd = fds[fileno]; + if (fd == NULL) + return -EBADF; + + if (fd->ops->seek == NULL) + return -ENOSYS; + + return fd->ops->seek(fd, off, whence); +} + +ssize_t hexagonfs_read(int fileno, size_t size, void *ptr) +{ + struct hexagonfs_fd *fd; + + if (fileno >= HEXAGONFS_MAX_FD) + return -EBADF; + + fd = fds[fileno]; + if (fd == NULL) + return -EBADF; + + if (fd->ops->read == NULL) + return -ENOSYS; + + return fd->ops->read(fd, size, ptr); +} + +int hexagonfs_readdir(int fileno, size_t ent_size, char *ent) +{ + struct hexagonfs_fd *fd; + int ret; + + if (fileno >= HEXAGONFS_MAX_FD) + return -EBADF; + + fd = fds[fileno]; + if (fd == NULL) + return -EBADF; + + if (fd->ops->readdir == NULL) + return -ENOSYS; + + return fd->ops->readdir(fd, ent_size, ent); +} + +int hexagonfs_fstat(int fileno, struct stat *stats) +{ + struct hexagonfs_fd *fd; + + if (fileno >= HEXAGONFS_MAX_FD) + return -EBADF; + + fd = fds[fileno]; + if (fd == NULL) + return -EBADF; + + if (fd->ops->stat == NULL) + return -ENOSYS; + + return fd->ops->stat(fd, stats); +} diff --git a/fastrpc/hexagonfs.h b/fastrpc/hexagonfs.h new file mode 100644 index 0000000..0c0b368 --- /dev/null +++ b/fastrpc/hexagonfs.h @@ -0,0 +1,78 @@ +/* + * Virtual read-only filesystem for Hexagon processors + * + * Copyright (C) 2023 Richard Acayan + * + * This file is part of sensh. + * + * Sensh is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef HEXAGONFS_H +#define HEXAGONFS_H + +#include +#include +#include +#include + +struct hexagonfs_fd; + +struct hexagonfs_file_ops { + void (*close)(void *fd_data); + int (*from_dirent)(void *dirent_data, bool dir, void **fd_data); + int (*openat)(struct hexagonfs_fd *dir, + const char *segment, + bool expect_dir, + struct hexagonfs_fd **out); + int (*readdir)(struct hexagonfs_fd *fd, size_t size, char *out); + ssize_t (*read)(struct hexagonfs_fd *fd, size_t size, void *ptr); + int (*stat)(struct hexagonfs_fd *fd, struct stat *stats); + int (*seek)(struct hexagonfs_fd *fd, off_t off, int whence); +}; + +struct hexagonfs_dirent { + const char *name; + + struct hexagonfs_file_ops *ops; + union hexagonfs_dirent_data { + void *ptr; + struct hexagonfs_dirent **dir; + const char *phys; + } u; +}; + +struct hexagonfs_fd { + off_t off; + bool is_assigned; + struct hexagonfs_fd *up; + void *data; + + struct hexagonfs_file_ops *ops; +}; + +extern struct hexagonfs_file_ops hexagonfs_mapped_ops; +extern struct hexagonfs_file_ops hexagonfs_plat_subtype_name_ops; +extern struct hexagonfs_file_ops hexagonfs_virt_dir_ops; + +int hexagonfs_open_root(); +int hexagonfs_openat(int rootfd, int dirfd, const char *name); +int hexagonfs_close(int fileno); + +int hexagonfs_fstat(int fileno, struct stat *stats); +int hexagonfs_lseek(int fileno, off_t pos, int whence); +int hexagonfs_readdir(int fileno, size_t size, char *name); +ssize_t hexagonfs_read(int fileno, size_t size, void *ptr); + +#endif diff --git a/fastrpc/hexagonfs_mapped.c b/fastrpc/hexagonfs_mapped.c new file mode 100644 index 0000000..c4bbbba --- /dev/null +++ b/fastrpc/hexagonfs_mapped.c @@ -0,0 +1,226 @@ +/* + * HexagonFS mapped file/directory operations + * + * Copyright (C) 2023 Richard Acayan + * + * This file is part of sensh. + * + * Sensh is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "hexagonfs.h" + +struct mapped_ctx { + int fd; + DIR *dir; +}; + +static void mapped_close(void *fd_data) +{ + struct mapped_ctx *ctx = fd_data; + + if (ctx->dir != NULL) + closedir(ctx->dir); + else + close(ctx->fd); + + free(ctx); +} + +static int mapped_from_dirent(void *dirent_data, bool dir, void **fd_data) +{ + struct mapped_ctx *ctx; + const char *name = dirent_data; + int flags = O_RDONLY; + int fd; + int ret; + + ctx = malloc(sizeof(struct mapped_ctx)); + if (ctx == NULL) + return -ENOMEM; + + if (dir) + flags |= O_DIRECTORY; + + ctx->fd = open(name, flags); + if (ctx->fd == -1) { + ret = -errno; + goto err; + } + + ctx->dir = NULL; + + *fd_data = ctx; + + return 0; + +err: + free(ctx); + return ret; +} + +static int mapped_openat(struct hexagonfs_fd *dir, + const char *segment, + bool expect_dir, + struct hexagonfs_fd **out) +{ + struct mapped_ctx *dir_ctx = dir->data; + struct hexagonfs_fd *fd; + struct mapped_ctx *ctx; + int flags = O_RDONLY; + int ret; + + ctx = malloc(sizeof(struct mapped_ctx)); + if (ctx == NULL) + return -ENOMEM; + + fd = malloc(sizeof(struct hexagonfs_fd)); + if (fd == NULL) { + ret = -ENOMEM; + goto err; + } + + if (expect_dir) + flags |= O_DIRECTORY; + + ctx->fd = openat(dir_ctx->fd, segment, flags); + if (ctx->fd == -1) { + ret = -errno; + goto err_free_fd; + } + + ctx->dir = NULL; + + fd->off = 0; + fd->up = dir; + fd->ops = &hexagonfs_mapped_ops; + fd->data = ctx; + + *out = fd; + + return 0; + +err_free_fd: + free(fd); +err: + free(ctx); + return ret; +} + +ssize_t mapped_read(struct hexagonfs_fd *fd, size_t size, void *out) +{ + struct mapped_ctx *ctx = fd->data; + ssize_t ret; + + ret = read(ctx->fd, out, size); + if (ret < 0) + return -errno; + + return ret; +} + +int mapped_readdir(struct hexagonfs_fd *fd, size_t size, char *out) +{ + struct mapped_ctx *ctx = fd->data; + struct dirent *ent; + + if (ctx->dir == NULL) { + ctx->dir = fdopendir(ctx->fd); + if (ctx->dir == NULL) + return -errno; + + ctx->fd = dirfd(ctx->dir); + if (ctx->fd == -1) + return -errno; + } + + errno = 0; + + ent = readdir(ctx->dir); + if (ent == NULL) + return -errno; + + strncpy(out, ent->d_name, size); + out[size - 1] = '\0'; + + return 0; +} + +static int mapped_seek(struct hexagonfs_fd *fd, off_t off, int whence) +{ + struct mapped_ctx *ctx = fd->data; + int ret; + + ret = lseek(ctx->fd, off, whence); + if (ret == -1) + return -errno; + + return 0; +} + +static int mapped_stat(struct hexagonfs_fd *fd, struct stat *stats) +{ + struct mapped_ctx *ctx = fd->data; + struct stat phys; + int ret; + + ret = fstat(ctx->fd, &phys); + if (ret) + return -errno; + + stats->st_size = phys.st_size; + + stats->st_dev = 0; + stats->st_rdev = 0; + + stats->st_ino = 0; + stats->st_nlink = 0; + + if (phys.st_mode & S_IFDIR) { + stats->st_mode = S_IFDIR + | S_IRUSR | S_IXUSR + | S_IRGRP | S_IXGRP + | S_IROTH | S_IXOTH; + } else { + stats->st_mode = S_IRUSR | S_IRGRP | S_IROTH; + } + + stats->st_atim.tv_sec = phys.st_atim.tv_sec; + stats->st_atim.tv_nsec = phys.st_atim.tv_nsec; + stats->st_ctim.tv_sec = phys.st_ctim.tv_sec; + stats->st_ctim.tv_nsec = phys.st_ctim.tv_nsec; + stats->st_mtim.tv_sec = phys.st_mtim.tv_sec; + stats->st_mtim.tv_nsec = phys.st_mtim.tv_nsec; + + return 0; +} + +struct hexagonfs_file_ops hexagonfs_mapped_ops = { + .close = mapped_close, + .from_dirent = mapped_from_dirent, + .openat = mapped_openat, + .read = mapped_read, + .readdir = mapped_readdir, + .seek = mapped_seek, + .stat = mapped_stat, +}; diff --git a/fastrpc/hexagonfs_plat_subtype_name.c b/fastrpc/hexagonfs_plat_subtype_name.c new file mode 100644 index 0000000..55ba4cc --- /dev/null +++ b/fastrpc/hexagonfs_plat_subtype_name.c @@ -0,0 +1,100 @@ +/* + * HexagonFS operations for a missing sysfs file + * + * Copyright (C) 2023 Richard Acayan + * + * This file is part of sensh. + * + * Sensh is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include "hexagonfs.h" + +struct plat_subtype_ctx { + int fd; + char *name; +}; + +static void plat_subtype_name_close(void *fd_data) +{ + struct plat_subtype_ctx *ctx = fd_data; + + close(ctx->fd); + + if (ctx->name != NULL) + free(ctx->name); +} + +static int plat_subtype_name_from_dirent(void *dirent_data, + bool dir, + void **fd_data) +{ + struct plat_subtype_ctx *ctx; + + ctx = malloc(sizeof(struct plat_subtype_ctx)); + if (ctx == NULL) + return -ENOMEM; + + ctx->fd = open((const char *) dirent_data, O_RDONLY); + if (ctx->fd == -1) + return -errno; + + ctx->name = NULL; + + *fd_data = ctx; + + return 0; +} + +static int plat_subtype_name_openat(struct hexagonfs_fd *dir, + const char *segment, + bool expect_dir, + struct hexagonfs_fd **out) +{ + return -ENOTDIR; +} + +static int plat_subtype_stat(struct hexagonfs_fd *fd, struct stat *stats) +{ + stats->st_size = 0; + + stats->st_dev = 0; + stats->st_rdev = 0; + + stats->st_ino = 0; + stats->st_nlink = 0; + + stats->st_mode = S_IRUSR | S_IRGRP | S_IROTH; + + stats->st_atim.tv_sec = 0; + stats->st_atim.tv_nsec = 0; + stats->st_ctim.tv_sec = 0; + stats->st_ctim.tv_nsec = 0; + stats->st_mtim.tv_sec = 0; + stats->st_mtim.tv_nsec = 0; + + return 0; +} + +struct hexagonfs_file_ops hexagonfs_plat_subtype_name_ops = { + .close = plat_subtype_name_close, + .from_dirent = plat_subtype_name_from_dirent, + .openat = plat_subtype_name_openat, + .stat = plat_subtype_stat, +}; diff --git a/fastrpc/hexagonfs_virt_dir.c b/fastrpc/hexagonfs_virt_dir.c new file mode 100644 index 0000000..8490ba8 --- /dev/null +++ b/fastrpc/hexagonfs_virt_dir.c @@ -0,0 +1,122 @@ +/* + * HexagonFS virtual directory operations + * + * Copyright (C) 2023 Richard Acayan + * + * This file is part of sensh. + * + * Sensh is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#include "hexagonfs.h" + +/* + * This function searches for the relevant path segment in the path directory. + * It is not optimized because it is dealing with small directories. + */ +static struct hexagonfs_dirent *walk_dir(struct hexagonfs_dirent **dir, + const char *segment) +{ + struct hexagonfs_dirent **curr = dir; + + while (*curr != NULL) { + if (!strcmp(segment, (*curr)->name)) + break; + + curr++; + } + + return *curr; +} + +static int virt_dir_from_dirent(void *dirent_data, bool dir, void **fd_data) +{ + *fd_data = dirent_data; + return 0; +} + +static int virt_dir_openat(struct hexagonfs_fd *dir, + const char *segment, + bool expect_dir, + struct hexagonfs_fd **out) +{ + struct hexagonfs_fd *fd; + struct hexagonfs_dirent *ent; + struct hexagonfs_dirent **dirlist = dir->data; + int ret; + + ent = walk_dir(dirlist, segment); + if (ent == NULL) + return -ENOENT; + + fd = malloc(sizeof(struct hexagonfs_fd)); + if (fd == NULL) + return -ENOMEM; + + fd->off = 0; + fd->up = dir; + fd->ops = ent->ops; + + ret = ent->ops->from_dirent(ent->u.ptr, expect_dir, &fd->data); + if (ret) + goto err; + + *out = fd; + + return 0; + +err: + free(fd); + return ret; +} + +static void virt_dir_close(void *fd_data) +{ +} + +static int virt_dir_stat(struct hexagonfs_fd *fd, struct stat *stats) +{ + stats->st_size = 0; + + stats->st_dev = 0; + stats->st_rdev = 0; + + stats->st_ino = 0; + stats->st_nlink = 0; + + stats->st_mode = S_IRUSR | S_IXUSR + | S_IRGRP | S_IXGRP + | S_IROTH | S_IXOTH; + + stats->st_atim.tv_sec = 0; + stats->st_atim.tv_nsec = 0; + stats->st_ctim.tv_sec = 0; + stats->st_ctim.tv_nsec = 0; + stats->st_mtim.tv_sec = 0; + stats->st_mtim.tv_nsec = 0; + + return 0; +} + +struct hexagonfs_file_ops hexagonfs_virt_dir_ops = { + .close = virt_dir_close, + .from_dirent = virt_dir_from_dirent, + .openat = virt_dir_openat, + .stat = virt_dir_stat, +};