Hello, This patch provides a translator for mounting x86 ACPI tables under a path as read-only files. I believe it is needed so that other things that depend on ACPI to find the base address such as Intel's IOMMU (DMAR table), memory mapped PCI space (MCFG table) etc, can be discovered in userspace. Otherwise, this functionality would need to be built into gnumach which would be a burden to maintain.
The basic principle of this translator is that it searches for the ACPI root table within the PnP Extended System Configuration Data (ESCD) memory region that is sitting at address 0xE0000, then mmaps into it and extracts the ACPI tables from other physical pointers. The crux of my method is in acpi.c and the structure of tables is in acpi.h Please provide feedback, and I can revise as necessary. Cheers, Damien
>From 22758da0b147980c11dbd456f7a4e00404261212 Mon Sep 17 00:00:00 2001 From: Damien Zammit <dam...@zamaudio.com> Date: Tue, 6 Nov 2018 02:53:36 -0500 Subject: [PATCH] ACPI tables translator Exposes x86 ACPI tables as a netfs on a mount point --- Makefile | 3 +- acpi/Makefile | 35 ++++ acpi/acpi.c | 290 +++++++++++++++++++++++++ acpi/acpi.h | 74 +++++++ acpi/acpifs.c | 265 +++++++++++++++++++++++ acpi/acpifs.h | 147 +++++++++++++ acpi/func_files.c | 76 +++++++ acpi/func_files.h | 39 ++++ acpi/main.c | 100 +++++++++ acpi/ncache.c | 90 ++++++++ acpi/ncache.h | 32 +++ acpi/netfs_impl.c | 525 ++++++++++++++++++++++++++++++++++++++++++++++ acpi/netfs_impl.h | 43 ++++ acpi/options.c | 149 +++++++++++++ acpi/options.h | 51 +++++ hurd/hurd_types.h | 1 + 16 files changed, 1919 insertions(+), 1 deletion(-) create mode 100644 acpi/Makefile create mode 100644 acpi/acpi.c create mode 100644 acpi/acpi.h create mode 100644 acpi/acpifs.c create mode 100644 acpi/acpifs.h create mode 100644 acpi/func_files.c create mode 100644 acpi/func_files.h create mode 100644 acpi/main.c create mode 100644 acpi/ncache.c create mode 100644 acpi/ncache.h create mode 100644 acpi/netfs_impl.c create mode 100644 acpi/netfs_impl.h create mode 100644 acpi/options.c create mode 100644 acpi/options.h diff --git a/Makefile b/Makefile index 6288a157..aa4ddc5a 100644 --- a/Makefile +++ b/Makefile @@ -45,7 +45,8 @@ prog-subdirs = auth proc exec term \ init \ devnode \ eth-multiplexer \ - pci-arbiter + pci-arbiter \ + acpi ifeq ($(HAVE_SUN_RPC),yes) prog-subdirs += nfs nfsd diff --git a/acpi/Makefile b/acpi/Makefile new file mode 100644 index 00000000..b3b7407b --- /dev/null +++ b/acpi/Makefile @@ -0,0 +1,35 @@ +# Copyright (C) 2018 Free Software Foundation, Inc. +# +# This file is part of the GNU Hurd. +# +# The GNU Hurd 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 2, or (at +# your option) any later version. +# +# The GNU Hurd 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 the GNU Hurd. If not, see <<a rel="nofollow" href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>>. + +dir = acpi +makemode = server + +PORTDIR = $(srcdir)/port + +SRCS = main.c netfs_impl.c acpi.c \ + acpifs.c ncache.c options.c func_files.c +MIGSRCS = +OBJS = $(patsubst %.S,%.o,$(patsubst %.c,%.o, $(SRCS) $(MIGSRCS))) + +HURDLIBS= fshelp ports shouldbeinlibc netfs +LDLIBS = -lpthread + +target = acpi + +include ../Makeconf + +CFLAGS += -I$(PORTDIR)/include diff --git a/acpi/acpi.c b/acpi/acpi.c new file mode 100644 index 00000000..63066aaf --- /dev/null +++ b/acpi/acpi.c @@ -0,0 +1,290 @@ +/* + Copyright (C) 2018 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd 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 2, or (at + your option) any later version. + + The GNU Hurd 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 the GNU Hurd. If not, see <<a rel="nofollow" href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>>. +*/ + +#include <sys/mman.h> +#include <sys/io.h> +#include <fcntl.h> +#include <inttypes.h> +#include <stdlib.h> +#include <errno.h> +#include <stdio.h> +#include <stdbool.h> +#include <string.h> +#include <unistd.h> + +#include "acpi.h" + +int +mmap_phys_acpi_header(uintptr_t base_addr, struct acpi_header **ptr_to_header, + void **virt_addr, int fd) +{ + /* The memory mapping must be done aligned to page size + * but we have a known physical address we want to inspect, + * therefore we must compute offsets. + */ + uintptr_t pa_acpi = base_addr & ~(sysconf(_SC_PAGE_SIZE) - 1); + uintptr_t pa_start = base_addr - pa_acpi; + + /* Map the ACPI table at the nearest page (rounded down) */ + *virt_addr = 0; + *virt_addr = mmap(NULL, ESCD_SIZE, PROT_READ, MAP_SHARED | MAP_FIXED, + fd, (off_t) pa_acpi); + + if (*virt_addr == MAP_FAILED) + return errno; + + /* Fabricate a pointer to our magic address */ + *ptr_to_header = (struct acpi_header *)(*virt_addr + pa_start); + + return 0; +} + +int +acpi_get_num_tables(size_t *num_tables) +{ + int fd_mem; + int err; + void *virt_addr, *virt_addr2; + bool found = false; + struct rsdp_descr2 rsdp = { 0 }; + uintptr_t sdt_base = (uintptr_t)0; + bool is_64bit = false; + unsigned char *buf; + struct acpi_header *root_sdt; + struct acpi_header *next; + + if ((fd_mem = open("/dev/mem", O_RDWR)) < 0) + return EPERM; + + virt_addr = mmap(NULL, ESCD_SIZE, PROT_READ, + MAP_SHARED | MAP_FIXED, fd_mem, ESCD); + if (virt_addr == MAP_FAILED) + return errno; + + buf = (unsigned char *)virt_addr; + found = false; + + /* RSDP magic string is 16 byte aligned */ + for (int i = 0; i < ESCD_SIZE; i += 16) + { + if (!memcmp(&buf[i], RSDP_MAGIC, 8)) { + rsdp = *((struct rsdp_descr2 *)(&buf[i])); + found = true; + break; + } + } + + if (!found) { + munmap(virt_addr, ESCD_SIZE); + return ENODEV; + } + + if (rsdp.v1.revision == 0) { + // ACPI 1.0 + sdt_base = rsdp.v1.rsdt_addr; + is_64bit = false; + } else if (rsdp.v1.revision == 2) { + // ACPI >= 2.0 + sdt_base = rsdp.xsdt_addr; + is_64bit = true; + } else { + munmap(virt_addr, ESCD_SIZE); + return ENODEV; + } + + munmap(virt_addr, ESCD_SIZE); + + /* Now we have the sdt_base address and knowledge of 32/64 bit ACPI */ + + err = mmap_phys_acpi_header(sdt_base, &root_sdt, &virt_addr, fd_mem); + if (err) { + munmap(virt_addr, ESCD_SIZE); + return err; + } + + /* Get total tables */ + uint32_t ntables; + uint8_t sz_ptr; + sz_ptr = is_64bit ? 8 : 4; + ntables = (root_sdt->length - sizeof(*root_sdt)) / sz_ptr; + + /* Get pointer to first ACPI table */ + uintptr_t acpi_ptr = (uintptr_t)root_sdt + sizeof(*root_sdt); + + /* Get number of readable tables */ + *num_tables = 0; + for (int i = 0; i < ntables; i++) + { + uintptr_t acpi_ptr32 = (uintptr_t)*((uint32_t *)(acpi_ptr + i*sz_ptr)); + uintptr_t acpi_ptr64 = (uintptr_t)*((uint64_t *)(acpi_ptr + i*sz_ptr)); + if (is_64bit) { + err = mmap_phys_acpi_header(acpi_ptr64, &next, &virt_addr2, fd_mem); + } else { + err = mmap_phys_acpi_header(acpi_ptr32, &next, &virt_addr2, fd_mem); + } + + char name[5] = { 0 }; + snprintf(name, 5, "%s", &next->signature[0]); + if (next->signature[0] == '\0' || next->length == 0) { + munmap(virt_addr2, ESCD_SIZE); + continue; + } + *num_tables += 1; + munmap(virt_addr2, ESCD_SIZE); + } + + munmap(virt_addr, ESCD_SIZE); + + return 0; +} + +int +acpi_get_tables(struct acpi_table **tables) +{ + int err; + int fd_mem; + void *virt_addr, *virt_addr2; + uint32_t phys_addr = ESCD; + bool found = false; + struct rsdp_descr2 rsdp = { 0 }; + uintptr_t sdt_base = (uintptr_t)0; + bool is_64bit = false; + unsigned char *buf; + struct acpi_header *root_sdt; + struct acpi_header *next; + size_t ntables_actual; + int cur_tab = 0; + + err = acpi_get_num_tables(&ntables_actual); + if (err) + return err; + + *tables = malloc(ntables_actual * sizeof(**tables)); + if (!*tables) + return ENOMEM; + + if ((fd_mem = open("/dev/mem", O_RDWR)) < 0) + return EPERM; + + virt_addr = mmap(NULL, ESCD_SIZE, PROT_READ, MAP_SHARED | MAP_FIXED, + fd_mem, (off_t) phys_addr); + + if (virt_addr == MAP_FAILED) + return errno; + + buf = (unsigned char *)virt_addr; + found = false; + + /* RSDP magic string is 16 byte aligned */ + for (int i = 0; i < ESCD_SIZE; i += 16) + { + if (!memcmp(&buf[i], RSDP_MAGIC, 8)) { + rsdp = *((struct rsdp_descr2 *)(&buf[i])); + found = true; + break; + } + } + + if (!found) { + munmap(virt_addr, ESCD_SIZE); + return ENODEV; + } + + if (rsdp.v1.revision == 0) { + // ACPI 1.0 + sdt_base = rsdp.v1.rsdt_addr; + is_64bit = false; + } else if (rsdp.v1.revision == 2) { + // ACPI >= 2.0 + sdt_base = rsdp.xsdt_addr; + is_64bit = true; + } else { + munmap(virt_addr, ESCD_SIZE); + return ENODEV; + } + + munmap(virt_addr, ESCD_SIZE); + + /* Now we have the sdt_base address and knowledge of 32/64 bit ACPI */ + + err = mmap_phys_acpi_header(sdt_base, &root_sdt, &virt_addr, fd_mem); + if (err) { + munmap(virt_addr, ESCD_SIZE); + return err; + } + + /* Get total tables */ + uint32_t ntables; + uint8_t sz_ptr; + sz_ptr = is_64bit ? 8 : 4; + ntables = (root_sdt->length - sizeof(*root_sdt)) / sz_ptr; + + /* Get pointer to first ACPI table */ + uintptr_t acpi_ptr = (uintptr_t)root_sdt + sizeof(*root_sdt); + + /* Get all tables and data */ + for (int i = 0; i < ntables; i++) + { + uintptr_t acpi_ptr32 = (uintptr_t)*((uint32_t *)(acpi_ptr + i*sz_ptr)); + uintptr_t acpi_ptr64 = (uintptr_t)*((uint64_t *)(acpi_ptr + i*sz_ptr)); + if (is_64bit) { + err = mmap_phys_acpi_header(acpi_ptr64, &next, &virt_addr2, fd_mem); + if (err) { + munmap(virt_addr, ESCD_SIZE); + return err; + } + } else { + err = mmap_phys_acpi_header(acpi_ptr32, &next, &virt_addr2, fd_mem); + if (err) { + munmap(virt_addr, ESCD_SIZE); + return err; + } + } + + char name[5] = { 0 }; + snprintf(name, 5, "%s", &next->signature[0]); + if (next->signature[0] == '\0' || next->length == 0) { + munmap(virt_addr2, ESCD_SIZE); + continue; + } + uint32_t datalen = next->length - sizeof(*next); + void *data = (void *)((uintptr_t)next + sizeof(*next)); + + /* We now have a pointer to the data, + * its length and header. + */ + struct acpi_table *t = *tables + cur_tab; + memcpy(&t->h, next, sizeof(*next)); + t->datalen = 0; + t->data = malloc(datalen); + if (!t->data) { + munmap(virt_addr2, ESCD_SIZE); + munmap(virt_addr, ESCD_SIZE); + return ENOMEM; + } + t->datalen = datalen; + memcpy(t->data, data, datalen); + cur_tab++; + munmap(virt_addr2, ESCD_SIZE); + } + + munmap(virt_addr, ESCD_SIZE); + + return 0; +} diff --git a/acpi/acpi.h b/acpi/acpi.h new file mode 100644 index 00000000..7c21a442 --- /dev/null +++ b/acpi/acpi.h @@ -0,0 +1,74 @@ +/* + Copyright (C) 2018 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd 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 2, or (at + your option) any later version. + + The GNU Hurd 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 the GNU Hurd. If not, see <<a rel="nofollow" href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>>. +*/ + +/* ACPI tables basic structure */ + +#ifndef ACPI_H +#define ACPI_H + +#include <stdlib.h> +#include <inttypes.h> + +/* PnP Extended System Configuration Data (ESCD) memory region */ +#define ESCD 0xe0000U +#define RSDP_MAGIC (const unsigned char *)"RSD PTR " +#define ESCD_SIZE 0x20000U + +struct rsdp_descr +{ + uint8_t magic[8]; + uint8_t checksum; + uint8_t oem_id[6]; + uint8_t revision; + uint32_t rsdt_addr; +} __attribute__ ((packed)); + +struct rsdp_descr2 +{ + struct rsdp_descr v1; + uint32_t length; + uint64_t xsdt_addr; + uint8_t checksum; + uint8_t reserved[3]; +} __attribute__ ((packed)); + +struct acpi_header +{ + uint8_t signature[4]; + uint32_t length; + uint8_t revision; + uint8_t checksum; + uint8_t oem_id[6]; + uint8_t oem_table_id[8]; + uint32_t oem_revision; + uint32_t creator_id; + uint32_t creator_revision; +} __attribute__ ((packed)); + +struct acpi_table +{ + struct acpi_header h; + void *data; + size_t datalen; +} __attribute__ ((packed)); + +int acpi_get_num_tables(size_t *num_tables); +int acpi_get_tables(struct acpi_table **tables); + +#endif /* ACPI_H */ diff --git a/acpi/acpifs.c b/acpi/acpifs.c new file mode 100644 index 00000000..e779e0f9 --- /dev/null +++ b/acpi/acpifs.c @@ -0,0 +1,265 @@ +/* + Copyright (C) 2018 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd 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 2, or (at + your option) any later version. + + The GNU Hurd 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 the GNU Hurd. If not, see <<a rel="nofollow" href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>>. +*/ + +/* ACPI Filesystem implementation */ + +#include <acpifs.h> + +#include <error.h> +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <pthread.h> +#include <hurd/netfs.h> + +#include <ncache.h> +#include <func_files.h> + +static error_t +create_dir_entry (char *name, struct acpi_table *t, + struct acpifs_dirent *parent, io_statbuf_t stat, + struct node *node, struct acpifs_dirent *entry) +{ + uint16_t parent_num_entries; + + strncpy (entry->name, name, NAME_SIZE); + entry->acpitable = t; + entry->parent = parent; + entry->stat = stat; + entry->dir = 0; + entry->node = node; + + /* Update parent's child list */ + if (entry->parent) + { + if (!entry->parent->dir) + { + /* First child */ + entry->parent->dir = calloc (1, sizeof (struct acpifs_dir)); + if (!entry->parent->dir) + return ENOMEM; + } + + parent_num_entries = entry->parent->dir->num_entries++; + entry->parent->dir->entries = realloc (entry->parent->dir->entries, + entry->parent->dir->num_entries * + sizeof (struct acpifs_dirent *)); + if (!entry->parent->dir->entries) + return ENOMEM; + entry->parent->dir->entries[parent_num_entries] = entry; + } + + return 0; +} + +error_t +alloc_file_system (struct acpifs **fs) +{ + *fs = calloc (1, sizeof (struct acpifs)); + if (!*fs) + return ENOMEM; + + return 0; +} + +error_t +init_file_system (file_t underlying_node, struct acpifs *fs) +{ + error_t err; + struct node *np; + io_statbuf_t underlying_node_stat; + + /* Initialize status from underlying node. */ + err = io_stat (underlying_node, &underlying_node_stat); + if (err) + return err; + + np = netfs_make_node_alloc (sizeof (struct netnode)); + if (!np) + return ENOMEM; + np->nn_stat = underlying_node_stat; + np->nn_stat.st_fsid = getpid (); + np->nn_stat.st_mode = + S_IFDIR | S_IROOT | S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | + S_IXOTH; + np->nn_translated = np->nn_stat.st_mode; + + /* Set times to now */ + fshelp_touch (&np->nn_stat, TOUCH_ATIME | TOUCH_MTIME | TOUCH_CTIME, + acpifs_maptime); + + fs->entries = calloc (1, sizeof (struct acpifs_dirent)); + if (!fs->entries) + { + free (fs->entries); + return ENOMEM; + } + + /* Create the root entry */ + err = create_dir_entry ("", 0, 0, np->nn_stat, np, fs->entries); + + fs->num_entries = 1; + fs->root = netfs_root_node = np; + fs->root->nn->ln = fs->entries; + pthread_mutex_init (&fs->node_cache_lock, 0); + + return 0; +} + +error_t +create_fs_tree (struct acpifs *fs) +{ + error_t err = 0; + int i; + size_t nentries, ntables = 0; + struct acpifs_dirent *e, *list, *parent; + struct stat e_stat; + char entry_name[NAME_SIZE]; + struct acpi_table *iter = NULL; + + /* Copy the root stat */ + e_stat = fs->entries->stat; + + err = acpi_get_num_tables(&ntables); + if (err) + return err; + + /* Allocate enough for / + "tables"/ + each table */ + nentries = ntables + 2; + + list = realloc (fs->entries, nentries * sizeof (struct acpifs_dirent)); + if (!list) { + if (fs->entries) + free(fs->entries); + return ENOMEM; + } + + e = list + 1; + parent = list; + e_stat.st_mode &= ~S_IROOT; /* Remove the root mode */ + memset (entry_name, 0, NAME_SIZE); + strncpy (entry_name, DIR_TABLES_NAME, NAME_SIZE); + + err = create_dir_entry (entry_name, 0, parent, e_stat, 0, e); + if (err) + return err; + + parent = e; + + /* Remove all permissions to others */ + e_stat.st_mode &= ~(S_IROTH | S_IWOTH | S_IXOTH); + + /* Change mode to a regular read-only file */ + e_stat.st_mode &= ~(S_IFDIR | S_IXUSR | S_IXGRP | S_IWUSR | S_IWGRP); + e_stat.st_mode |= S_IFREG; + + /* Get all ACPI tables */ + err = acpi_get_tables(&iter); + if (err) + return err; + + for (i = 0; i < ntables; i++, iter++) + { + e_stat.st_size = iter->datalen; + + // Create ACPI table entry + memset (entry_name, 0, NAME_SIZE); + + snprintf (entry_name, NAME_SIZE, "%c%c%c%c", + iter->h.signature[0], + iter->h.signature[1], + iter->h.signature[2], + iter->h.signature[3]); + e++; + err = create_dir_entry (entry_name, iter, parent, e_stat, 0, e); + if (err) + return err; + } + + /* The root node points to the first element of the entry list */ + fs->entries = list; + fs->num_entries = nentries; + fs->root->nn->ln = fs->entries; + + return err; +} + +error_t +entry_check_perms (struct iouser *user, struct acpifs_dirent *e, int flags) +{ + error_t err = 0; + + if (!err && (flags & O_READ)) + err = fshelp_access (&e->stat, S_IREAD, user); + if (!err && (flags & O_WRITE)) + err = fshelp_access (&e->stat, S_IWRITE, user); + if (!err && (flags & O_EXEC)) + err = fshelp_access (&e->stat, S_IEXEC, user); + + return err; +} + +/* Set default permissions to the given entry */ +static void +entry_default_perms (struct acpifs *fs, struct acpifs_dirent *e) +{ + /* Set default owner and group */ + UPDATE_OWNER (e, fs->root->nn->ln->stat.st_uid); + UPDATE_GROUP (e, fs->root->nn->ln->stat.st_gid); + + /* Update ctime */ + UPDATE_TIMES (e, TOUCH_CTIME); + + return; +} + +static void +entry_set_perms (struct acpifs *fs, struct acpifs_dirent *e) +{ + struct acpifs_perm *perm = &fs->perm; + if (perm->uid >= 0) + UPDATE_OWNER (e, perm->uid); + if (perm->gid >= 0) + UPDATE_GROUP (e, perm->gid); + + /* Update ctime */ + UPDATE_TIMES (e, TOUCH_CTIME); + + return; +} + +/* Update all entries' permissions */ +error_t +fs_set_permissions (struct acpifs *fs) +{ + int i; + struct acpifs_dirent *e; + + for (i = 0, e = fs->entries; i < fs->num_entries; i++, e++) + { + /* Restore default perms, as this may be called from fsysopts */ + entry_default_perms (fs, e); + + /* Set new permissions, if any */ + entry_set_perms (fs, e); + } + + return 0; +} diff --git a/acpi/acpifs.h b/acpi/acpifs.h new file mode 100644 index 00000000..2e9063cc --- /dev/null +++ b/acpi/acpifs.h @@ -0,0 +1,147 @@ +/* + Copyright (C) 2018 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd 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 2, or (at + your option) any later version. + + The GNU Hurd 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 the GNU Hurd. If not, see <<a rel="nofollow" href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>>. +*/ + +/* ACPI Filesystem header */ + +#ifndef ACPIFS_H +#define ACPIFS_H + +#include <hurd/netfs.h> +#include <pthread.h> +#include <maptime.h> + +#include <netfs_impl.h> +#include <acpi.h> + +/* Size of a directory entry name */ +#ifndef NAME_SIZE +#define NAME_SIZE 8 +#endif + +/* Node cache defaults size */ +#define NODE_CACHE_MAX 16 + +/* + * User and group ids to grant permission to acpi + */ +struct acpifs_perm +{ + int32_t uid; + int32_t gid; +}; + +/* + * Directory entry. + * + * All directory entries are created on startup and used to generate the + * fs tree and create or retrieve libnetfs node objects. + * + * From libnetfs' point of view, these are the light nodes. + */ +struct acpifs_dirent +{ + char name[NAME_SIZE]; + struct acpifs_dirent *parent; + io_statbuf_t stat; + + /* + * We only need two kind of nodes: files and directories. + * When `dir' is null, this is a file; when not null, it's a directory. + */ + struct acpifs_dir *dir; + + /* Active node on this entry */ + struct node *node; + + /* ACPI table related to an entry */ + struct acpi_table *acpitable; +}; + +/* + * A directory, it only contains a list of directory entries + */ +struct acpifs_dir +{ + /* Number of directory entries */ + uint16_t num_entries; + + /* Array of directory entries */ + struct acpifs_dirent **entries; +}; + +/* A particular ACPI filesystem. */ +struct acpifs +{ + /* Root of filesystem. */ + struct node *root; + + /* A cache that holds a reference to recently used nodes. */ + struct node *node_cache_mru, *node_cache_lru; + size_t node_cache_len; /* Number of entries in it. */ + size_t node_cache_max; + pthread_mutex_t node_cache_lock; + + struct acpifs_perm perm; + + struct acpifs_dirent *entries; + size_t num_entries; +}; + +/* Main FS pointer */ +struct acpifs *fs; + +/* Global mapped time */ +volatile struct mapped_time_value *acpifs_maptime; + +/* Update entry and node times */ +#define UPDATE_TIMES(e, what) (\ + {\ + fshelp_touch (&e->stat, what, acpifs_maptime);\ + if(e->node)\ + fshelp_touch (&e->node->nn_stat, what, acpifs_maptime);\ + }\ +) + +/* Update entry and node owner */ +#define UPDATE_OWNER(e, uid) (\ + {\ + e->stat.st_uid = uid;\ + if(e->node)\ + e->node->nn_stat.st_uid = uid;\ + }\ +) + +/* Update entry and node group */ +#define UPDATE_GROUP(e, gid) (\ + {\ + e->stat.st_gid = gid;\ + if(e->node)\ + e->node->nn_stat.st_gid = gid;\ + }\ +) + +/* FS manipulation functions */ +error_t alloc_file_system (struct acpifs **fs); +error_t init_file_system (file_t underlying_node, struct acpifs *fs); +error_t create_fs_tree (struct acpifs *fs); +error_t fs_set_permissions (struct acpifs *fs); +error_t entry_check_perms (struct iouser *user, struct acpifs_dirent *e, + int flags); + +#endif /* ACPIFS_H */ diff --git a/acpi/func_files.c b/acpi/func_files.c new file mode 100644 index 00000000..371f989a --- /dev/null +++ b/acpi/func_files.c @@ -0,0 +1,76 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd 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 2, or (at + your option) any later version. + + The GNU Hurd 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 the GNU Hurd. If not, see <<a rel="nofollow" href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>>. +*/ + +/* + * Per-function files implementation. + * + * Implementation of all files repeated for each function. + */ + +#include <func_files.h> +#include <assert.h> + +/* Read an acpi table */ +error_t +io_acpi_table (struct acpi_table *t, off_t offset, size_t *len, void *data) +{ + error_t err; + size_t datalen; + + /* This should never happen */ + assert_backtrace (t != 0); + + datalen = t->datalen; + + /* Don't exceed the size of the acpi table */ + if (offset > datalen) + return EINVAL; + if ((offset + *len) > datalen) + *len = datalen - offset; + + memcpy (data, t->data + offset, *len); + + return err; +} + +/* Read from an acpi table file */ +error_t +io_acpi_file (struct acpifs_dirent *e, off_t offset, size_t *len, + void *data) +{ + size_t datalen; + struct acpi_table *table; + + /* This should never happen */ + assert_backtrace (e->acpitable != 0); + + /* Get the table */ + table = e->acpitable; + + datalen = table->datalen; + /* Don't exceed the region size */ + if (offset > datalen) + return EINVAL; + if ((offset + *len) > datalen) + *len = datalen - offset; + + memcpy (data, table->data + offset, *len); + + return 0; +} diff --git a/acpi/func_files.h b/acpi/func_files.h new file mode 100644 index 00000000..90d92cc3 --- /dev/null +++ b/acpi/func_files.h @@ -0,0 +1,39 @@ +/* + Copyright (C) 2018 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd 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 2, or (at + your option) any later version. + + The GNU Hurd 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 the GNU Hurd. If not, see <<a rel="nofollow" href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>>. +*/ + +/* Per-function files header */ + +#ifndef FUNC_FILES_H +#define FUNC_FILES_H + +#include <acpifs.h> +#include <acpi.h> + +typedef int (*acpi_read_op_t) (struct acpi_table *t, void *data, + off_t offset, size_t *len); + +/* Tables */ +#define DIR_TABLES_NAME "tables" + +error_t io_read_table (struct acpi_table *t, struct acpifs_dirent *e, + off_t offset, size_t *len, void *data); +error_t io_acpi_file (struct acpifs_dirent *e, off_t offset, size_t *len, + void *data); + +#endif /* FUNC_FILES_H */ diff --git a/acpi/main.c b/acpi/main.c new file mode 100644 index 00000000..f675470c --- /dev/null +++ b/acpi/main.c @@ -0,0 +1,100 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd 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 2, or (at + your option) any later version. + + The GNU Hurd 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 the GNU Hurd. If not, see <<a rel="nofollow" href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>>. +*/ + +/* Translator initialization and demuxing */ + +#include <stdio.h> +#include <error.h> +#include <fcntl.h> +#include <version.h> +#include <argp.h> +#include <hurd/netfs.h> + +#include "libnetfs/io_S.h" +#include "libnetfs/fs_S.h" +#include "libports/notify_S.h" +#include "libnetfs/fsys_S.h" +#include "libports/interrupt_S.h" +#include "libnetfs/ifsock_S.h" +#include <acpifs.h> + +/* Libnetfs stuff */ +int netfs_maxsymlinks = 0; +char *netfs_server_name = "acpi"; +char *netfs_server_version = HURD_VERSION; + +int +netfs_demuxer (mach_msg_header_t * inp, mach_msg_header_t * outp) +{ + mig_routine_t routine; + + if ((routine = netfs_io_server_routine (inp)) || + (routine = netfs_fs_server_routine (inp)) || + (routine = ports_notify_server_routine (inp)) || + (routine = netfs_fsys_server_routine (inp)) || + (routine = ports_interrupt_server_routine (inp)) || + (routine = netfs_ifsock_server_routine (inp))) + { + (*routine) (inp, outp); + return TRUE; + } + else + return FALSE; +} + +int +main (int argc, char **argv) +{ + error_t err; + mach_port_t bootstrap; + + /* Parse options */ + alloc_file_system (&fs); + argp_parse (netfs_runtime_argp, argc, argv, 0, 0, 0); + + task_get_bootstrap_port (mach_task_self (), &bootstrap); + if (bootstrap == MACH_PORT_NULL) + error (1, 0, "must be started as a translator"); + + /* Initialize netfs and start the translator. */ + netfs_init (); + + err = maptime_map (0, 0, &acpifs_maptime); + if (err) + error (1, err, "mapping time"); + + /* Create the ACPI filesystem */ + err = init_file_system (netfs_startup (bootstrap, O_READ), fs); + if (err) + error (1, err, "creating the ACPI filesystem"); + + /* Create the filesystem tree */ + err = create_fs_tree (fs); + if (err) + error (1, err, "creating the ACPI filesystem tree"); + + /* Set permissions */ + err = fs_set_permissions (fs); + if (err) + error (1, err, "setting permissions"); + + netfs_server_loop (); /* Never returns. */ + + return 0; +} diff --git a/acpi/ncache.c b/acpi/ncache.c new file mode 100644 index 00000000..61bc6592 --- /dev/null +++ b/acpi/ncache.c @@ -0,0 +1,90 @@ +/* Node caching + + Copyright (C) 1997 Free Software Foundation, Inc. + Written by Miles Bader <address@hidden> + This file is part of the GNU Hurd. + + The GNU Hurd 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 2, or (at + your option) any later version. + + The GNU Hurd 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, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */ + +#include <ncache.h> + +#include <unistd.h> +#include <string.h> +#include <hurd/netfs.h> + +#include <acpifs.h> +#include <netfs_impl.h> + +/* Implementation of node caching functions */ + +/* Remove NN's node from its position in FS's node cache. */ +void +node_unlink (struct node *node, struct acpifs *fs) +{ + struct netnode *nn = node->nn; + if (nn->ncache_next) + nn->ncache_next->nn->ncache_prev = nn->ncache_prev; + if (nn->ncache_prev) + nn->ncache_prev->nn->ncache_next = nn->ncache_next; + if (fs->node_cache_mru == node) + fs->node_cache_mru = nn->ncache_next; + if (fs->node_cache_lru == node) + fs->node_cache_lru = nn->ncache_prev; + nn->ncache_next = 0; + nn->ncache_prev = 0; + fs->node_cache_len--; +} + +/* Add NODE to the recently-used-node cache, which adds a reference to + prevent it from going away. NODE should be locked. */ +void +node_cache (struct node *node) +{ + struct netnode *nn = node->nn; + + pthread_mutex_lock (&fs->node_cache_lock); + + if (fs->node_cache_max > 0 || fs->node_cache_len > 0) + { + if (fs->node_cache_mru != node) + { + if (nn->ncache_next || nn->ncache_prev) + /* Node is already in the cache. */ + node_unlink (node, fs); + else + /* Add a reference from the cache. */ + netfs_nref (node); + + nn->ncache_next = fs->node_cache_mru; + nn->ncache_prev = 0; + if (fs->node_cache_mru) + fs->node_cache_mru->nn->ncache_prev = node; + if (!fs->node_cache_lru) + fs->node_cache_lru = node; + fs->node_cache_mru = node; + fs->node_cache_len++; + } + + /* Forget the least used nodes. */ + while (fs->node_cache_len > fs->node_cache_max) + { + struct node *lru = fs->node_cache_lru; + node_unlink (lru, fs); + netfs_nrele (lru); + } + } + + pthread_mutex_unlock (&fs->node_cache_lock); +} diff --git a/acpi/ncache.h b/acpi/ncache.h new file mode 100644 index 00000000..b4fa430b --- /dev/null +++ b/acpi/ncache.h @@ -0,0 +1,32 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd 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 2, or (at + your option) any later version. + + The GNU Hurd 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 the GNU Hurd. If not, see <<a rel="nofollow" href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>>. +*/ + +/* Header for node caching functions */ + +#ifndef NCACHE_H +#define NCACHE_H + +#include <hurd/netfs.h> + +#include <acpifs.h> + +void node_cache (struct node *node); +void node_unlink (struct node *node, struct acpifs *fs); + +#endif /* NCACHE_H */ diff --git a/acpi/netfs_impl.c b/acpi/netfs_impl.c new file mode 100644 index 00000000..84f52c89 --- /dev/null +++ b/acpi/netfs_impl.c @@ -0,0 +1,525 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + Written by Miles Bader <address@hidden> + This file is part of the GNU Hurd. + + The GNU Hurd 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 2, or (at + your option) any later version. + + The GNU Hurd 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 the GNU Hurd. If not, see <<a rel="nofollow" href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>>. +*/ + +/* Libnetfs callbacks */ + +#include <netfs_impl.h> + +#include <stddef.h> +#include <stdlib.h> +#include <dirent.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/mman.h> +#include <hurd/netfs.h> + +#include <acpifs.h> +#include <ncache.h> +#include <func_files.h> + +#define DIRENTS_CHUNK_SIZE (8*1024) +/* Returned directory entries are aligned to blocks this many bytes long. + * Must be a power of two. */ +#define DIRENT_ALIGN 4 +#define DIRENT_NAME_OFFS offsetof (struct dirent, d_name) + +/* Length is structure before the name + the name + '\0', all + * padded to a four-byte alignment. */ +#define DIRENT_LEN(name_len) \ + ((DIRENT_NAME_OFFS + (name_len) + 1 + (DIRENT_ALIGN - 1)) \ + & ~(DIRENT_ALIGN - 1)) + +/* Fetch a directory, as for netfs_get_dirents. */ +static error_t +get_dirents (struct acpifs_dirent *dir, + int first_entry, int max_entries, char **data, + mach_msg_type_number_t *data_len, + vm_size_t max_data_len, int *data_entries) +{ + struct acpifs_dirent *e; + error_t err = 0; + int i, count; + size_t size; + char *p; + + if (first_entry >= dir->dir->num_entries) + { + *data_len = 0; + *data_entries = 0; + return 0; + } + + if (max_entries < 0) + count = dir->dir->num_entries; + else + { + count = ((first_entry + max_entries) >= dir->dir->num_entries ? + dir->dir->num_entries : max_entries) - first_entry; + } + + size = + (count * DIRENTS_CHUNK_SIZE) > + max_data_len ? max_data_len : count * DIRENTS_CHUNK_SIZE; + + *data = mmap (0, size, PROT_READ | PROT_WRITE, MAP_ANON, 0, 0); + err = ((void *) *data == (void *) -1) ? errno : 0; + if (err) + return err; + + p = *data; + for (i = 0; i < count; i++) + { + struct dirent hdr; + size_t name_len; + size_t sz; + int entry_type; + + e = dir->dir->entries[i + first_entry]; + name_len = strlen (e->name) + 1; + sz = DIRENT_LEN (name_len); + entry_type = IFTODT (e->stat.st_mode); + + hdr.d_namlen = name_len; + hdr.d_fileno = e->stat.st_ino; + hdr.d_reclen = sz; + hdr.d_type = entry_type; + + memcpy (p, &hdr, DIRENT_NAME_OFFS); + strncpy (p + DIRENT_NAME_OFFS, e->name, name_len); + p += sz; + } + + vm_address_t alloc_end = (vm_address_t) (*data + size); + vm_address_t real_end = round_page (p); + if (alloc_end > real_end) + munmap ((caddr_t) real_end, alloc_end - real_end); + *data_len = p - *data; + *data_entries = count; + + return err; +} + +static struct acpifs_dirent * +lookup (struct node *np, char *name) +{ + int i; + struct acpifs_dirent *ret = 0, *e; + + for (i = 0; i < np->nn->ln->dir->num_entries; i++) + { + e = np->nn->ln->dir->entries[i]; + + if (!strncmp (e->name, name, NAME_SIZE)) + { + ret = e; + break; + } + } + + return ret; +} + +static error_t +create_node (struct acpifs_dirent * e, struct node ** node) +{ + struct node *np; + struct netnode *nn; + + np = netfs_make_node_alloc (sizeof (struct netnode)); + if (!np) + return ENOMEM; + np->nn_stat = e->stat; + np->nn_translated = np->nn_stat.st_mode; + + nn = netfs_node_netnode (np); + memset (nn, 0, sizeof (struct netnode)); + nn->ln = e; + + *node = e->node = np; + + return 0; +} + +static void +destroy_node (struct node *node) +{ + if (node->nn->ln) + node->nn->ln->node = 0; + free (node); +} + +/* Attempt to create a file named NAME in DIR for USER with MODE. Set *NODE + to the new node upon return. On any error, clear *NODE. *NODE should be + locked on success; no matter what, unlock DIR before returning. */ +error_t +netfs_attempt_create_file (struct iouser * user, struct node * dir, + char *name, mode_t mode, struct node ** node) +{ + *node = 0; + pthread_mutex_unlock (&dir->lock); + return EOPNOTSUPP; +} + +/* Node NODE is being opened by USER, with FLAGS. NEWNODE is nonzero if we + just created this node. Return an error if we should not permit the open + to complete because of a permission restriction. */ +error_t +netfs_check_open_permissions (struct iouser * user, struct node * node, + int flags, int newnode) +{ + return entry_check_perms (user, node->nn->ln, flags); +} + +/* This should attempt a utimes call for the user specified by CRED on node + NODE, to change the atime to ATIME and the mtime to MTIME. */ +error_t +netfs_attempt_utimes (struct iouser * cred, struct node * node, + struct timespec * atime, struct timespec * mtime) +{ + return EOPNOTSUPP; +} + +/* Return the valid access types (bitwise OR of O_READ, O_WRITE, and O_EXEC) + in *TYPES for file NODE and user CRED. */ +error_t +netfs_report_access (struct iouser * cred, struct node * node, int *types) +{ + return EOPNOTSUPP; +} + +/* Trivial definitions. */ + +/* Make sure that NP->nn_stat is filled with current information. CRED + identifies the user responsible for the operation. */ +error_t +netfs_validate_stat (struct node * node, struct iouser * cred) +{ + /* Nothing to do here */ + return 0; +} + +/* This should sync the file NODE completely to disk, for the user CRED. If + WAIT is set, return only after sync is completely finished. */ +error_t +netfs_attempt_sync (struct iouser * cred, struct node * node, int wait) +{ + return EOPNOTSUPP; +} + +error_t +netfs_get_dirents (struct iouser * cred, struct node * dir, + int first_entry, int max_entries, char **data, + mach_msg_type_number_t * data_len, + vm_size_t max_data_len, int *data_entries) +{ + error_t err = 0; + + if (dir->nn->ln->dir) + { + err = get_dirents (dir->nn->ln, first_entry, max_entries, + data, data_len, max_entries, data_entries); + } + else + err = ENOTDIR; + + if (!err) + /* Update atime */ + UPDATE_TIMES (dir->nn->ln, TOUCH_ATIME); + + return err; +} + +/* Lookup NAME in DIR for USER; set *NODE to the found name upon return. If + the name was not found, then return ENOENT. On any error, clear *NODE. + (*NODE, if found, should be locked, this call should unlock DIR no matter + what.) */ +error_t +netfs_attempt_lookup (struct iouser *user, struct node *dir, + char *name, struct node **node) +{ + error_t err = 0; + struct acpifs_dirent *entry; + + if (*name == '\0' || strcmp (name, ".") == 0) + /* Current directory -- just add an additional reference to DIR's node + and return it. */ + { + netfs_nref (dir); + *node = dir; + return 0; + } + else if (strcmp (name, "..") == 0) + /* Parent directory. */ + { + if (dir->nn->ln->parent) + { + *node = dir->nn->ln->parent->node; + pthread_mutex_lock (&(*node)->lock); + netfs_nref (*node); + } + else + { + err = ENOENT; /* No .. */ + *node = 0; + } + + pthread_mutex_unlock (&dir->lock); + + return err; + } + + /* `name' is not . nor .. */ + if (dir->nn->ln->dir) + { + /* `dir' is a directory */ + + /* Check dir permissions */ + err = entry_check_perms (user, dir->nn->ln, O_READ | O_EXEC); + if (!err) + { + entry = lookup (dir, name); + if (!entry) + { + err = ENOENT; + } + else + { + if (entry->node) + { + netfs_nref (entry->node); + } + else + { + /* + * No active node, create one. + * The new node is created with a reference. + */ + err = create_node (entry, node); + } + + if (!err) + { + *node = entry->node; + /* We have to unlock DIR's node before locking the child node + because the locking order is always child-parent. We know + the child node won't go away because we already hold the + additional reference to it. */ + pthread_mutex_unlock (&dir->lock); + pthread_mutex_lock (&(*node)->lock); + } + } + } + } + else + { + err = ENOTDIR; + } + + if (err) + { + *node = 0; + pthread_mutex_unlock (&dir->lock); + } + else + { + /* Update the node cache */ + node_cache (*node); + } + + return err; +} + +/* Delete NAME in DIR for USER. */ +error_t +netfs_attempt_unlink (struct iouser * user, struct node * dir, char *name) +{ + return EOPNOTSUPP; +} + +/* Note that in this one call, neither of the specific nodes are locked. */ +error_t +netfs_attempt_rename (struct iouser * user, struct node * fromdir, + char *fromname, struct node * todir, + char *toname, int excl) +{ + return EOPNOTSUPP; +} + +/* Attempt to create a new directory named NAME in DIR for USER with mode + MODE. */ +error_t +netfs_attempt_mkdir (struct iouser * user, struct node * dir, + char *name, mode_t mode) +{ + return EOPNOTSUPP; +} + +/* Attempt to remove directory named NAME in DIR for USER. */ +error_t +netfs_attempt_rmdir (struct iouser * user, struct node * dir, char *name) +{ + return EOPNOTSUPP; +} + +/* This should attempt a chmod call for the user specified by CRED on node + NODE, to change the owner to UID and the group to GID. */ +error_t +netfs_attempt_chown (struct iouser * cred, struct node * node, + uid_t uid, uid_t gid) +{ + return EOPNOTSUPP; +} + +/* This should attempt a chauthor call for the user specified by CRED on node + NODE, to change the author to AUTHOR. */ +error_t +netfs_attempt_chauthor (struct iouser * cred, struct node * node, + uid_t author) +{ + return EOPNOTSUPP; +} + +/* This should attempt a chmod call for the user specified by CRED on node + NODE, to change the mode to MODE. Unlike the normal Unix and Hurd meaning + of chmod, this function is also used to attempt to change files into other + types. If such a transition is attempted which is impossible, then return + EOPNOTSUPP. */ +error_t +netfs_attempt_chmod (struct iouser * cred, struct node * node, mode_t mode) +{ + return EOPNOTSUPP; +} + +/* Attempt to turn NODE (user CRED) into a symlink with target NAME. */ +error_t +netfs_attempt_mksymlink (struct iouser * cred, struct node * node, char *name) +{ + return EOPNOTSUPP; +} + +/* Attempt to turn NODE (user CRED) into a device. TYPE is either S_IFBLK or + S_IFCHR. */ +error_t +netfs_attempt_mkdev (struct iouser * cred, struct node * node, + mode_t type, dev_t indexes) +{ + return EOPNOTSUPP; +} + +/* This should attempt a chflags call for the user specified by CRED on node + NODE, to change the flags to FLAGS. */ +error_t +netfs_attempt_chflags (struct iouser * cred, struct node * node, int flags) +{ + return EOPNOTSUPP; +} + +/* This should attempt to set the size of the file NODE (for user CRED) to + SIZE bytes long. */ +error_t +netfs_attempt_set_size (struct iouser * cred, struct node * node, off_t size) +{ + /* Do nothing */ + return 0; +} + +/* This should attempt to fetch filesystem status information for the remote + filesystem, for the user CRED. */ +error_t +netfs_attempt_statfs (struct iouser * cred, struct node * node, + struct statfs * st) +{ + memset (st, 0, sizeof *st); + st->f_type = FSTYPE_ACPI; + st->f_fsid = getpid (); + return 0; +} + +/* This should sync the entire remote filesystem. If WAIT is set, return + only after sync is completely finished. */ +error_t +netfs_attempt_syncfs (struct iouser * cred, int wait) +{ + return 0; +} + +/* Create a link in DIR with name NAME to FILE for USER. Note that neither + DIR nor FILE are locked. If EXCL is set, do not delete the target, but + return EEXIST if NAME is already found in DIR. */ +error_t +netfs_attempt_link (struct iouser * user, struct node * dir, + struct node * file, char *name, int excl) +{ + return EOPNOTSUPP; +} + +/* Attempt to create an anonymous file related to DIR for USER with MODE. + Set *NODE to the returned file upon success. No matter what, unlock DIR. */ +error_t +netfs_attempt_mkfile (struct iouser * user, struct node * dir, + mode_t mode, struct node ** node) +{ + return EOPNOTSUPP; +} + +/* Read the contents of NODE (a symlink), for USER, into BUF. */ +error_t +netfs_attempt_readlink (struct iouser * user, struct node * node, char *buf) +{ + return EOPNOTSUPP; +} + +/* Read from the file NODE for user CRED starting at OFFSET and continuing for + up to *LEN bytes. Put the data at DATA. Set *LEN to the amount + successfully read upon return. */ +error_t +netfs_attempt_read (struct iouser * cred, struct node * node, + off_t offset, size_t * len, void *data) +{ + error_t err; + + if (!strncmp (node->nn->ln->name, DIR_TABLES_NAME, NAME_SIZE)) + return EOPNOTSUPP; + else + { + err = io_acpi_file (node->nn->ln, offset, len, data); + if (!err) + /* Update atime */ + UPDATE_TIMES (node->nn->ln, TOUCH_ATIME); + } + return err; +} + +/* Write to the file NODE for user CRED starting at OFSET and continuing for up + to *LEN bytes from DATA. Set *LEN to the amount seccessfully written upon + return. */ +error_t +netfs_attempt_write (struct iouser * cred, struct node * node, + off_t offset, size_t * len, void *data) +{ + return EOPNOTSUPP; +} + +/* Node NP is all done; free all its associated storage. */ +void +netfs_node_norefs (struct node *node) +{ + destroy_node (node); +} diff --git a/acpi/netfs_impl.h b/acpi/netfs_impl.h new file mode 100644 index 00000000..71717579 --- /dev/null +++ b/acpi/netfs_impl.h @@ -0,0 +1,43 @@ +/* + Copyright (C) 2018 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd 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 2, or (at + your option) any later version. + + The GNU Hurd 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 the GNU Hurd. If not, see <<a rel="nofollow" href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>>. +*/ + +/* Data types required to create a directory tree using libnetfs */ + +#ifndef NETFS_IMPL_H +#define NETFS_IMPL_H + +#include <hurd/netfs.h> + +#include <acpifs.h> + +/* + * A netnode has a 1:1 relation with a generic libnetfs node. Hence, it's only + * purpose is to extend a generic node to add the new attributes our problem + * requires. + */ +struct netnode +{ + /* Light node */ + struct acpifs_dirent *ln; + + /* Position in the node cache. */ + struct node *ncache_next, *ncache_prev; +}; + +#endif /* NETFS_IMPL_H */ diff --git a/acpi/options.c b/acpi/options.c new file mode 100644 index 00000000..8dbcd263 --- /dev/null +++ b/acpi/options.c @@ -0,0 +1,149 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + + Written by Miles Bader <address@hidden> + + This file is part of the GNU Hurd. + + The GNU Hurd 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 2, or (at + your option) any later version. + + The GNU Hurd 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 the GNU Hurd. If not, see <<a rel="nofollow" href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>>. +*/ + +/* Fsysopts and command line option parsing */ + +#include <options.h> + +#include <stdlib.h> +#include <argp.h> +#include <argz.h> +#include <error.h> + +#include <acpifs.h> + +/* Option parser */ +static error_t +parse_opt (int opt, char *arg, struct argp_state *state) +{ + error_t err = 0; + struct parse_hook *h = state->hook; + + /* Return _ERR from this routine */ +#define RETURN(_err) \ + do { return _err; } while (0) + + /* Print a parsing error message and (if exiting is turned off) return the + error code ERR. */ +#define PERR(err, fmt, args...) \ + do { argp_error (state, fmt , ##args); RETURN (err); } while (0) + + /* Like PERR but for non-parsing errors. */ +#define FAIL(rerr, status, perr, fmt, args...) \ + do{ argp_failure (state, status, perr, fmt , ##args); RETURN (rerr); } while(0) + + if (!arg && state->next < state->argc && (*state->argv[state->next] != '-')) + { + arg = state->argv[state->next]; + state->next++; + } + + switch (opt) + { + case 'U': + h->perm.uid = atoi (arg); + break; + case 'G': + h->perm.gid = atoi (arg); + break; + + case ARGP_KEY_INIT: + /* Initialize our parsing state. */ + h = malloc (sizeof (struct parse_hook)); + if (!h) + FAIL (ENOMEM, 1, ENOMEM, "option parsing"); + + h->ncache_len = NODE_CACHE_MAX; + h->perm.uid = 0; + h->perm.gid = 0; + state->hook = h; + break; + + case ARGP_KEY_SUCCESS: + /* Set permissions to FS */ + fs->perm = h->perm; + + /* Set cache len */ + fs->node_cache_max = h->ncache_len; + + if (fs->root) + { + /* + * FS is already initialized, that means we've been called by fsysopts. + * Update permissions. + */ + + /* Don't accept new RPCs during this process */ + err = ports_inhibit_all_rpcs (); + if (err) + return err; + + err = fs_set_permissions (fs); + + /* Accept RPCs again */ + ports_resume_all_rpcs (); + } + + /* Free the hook */ + free (h); + + break; + + case ARGP_KEY_ERROR: + /* Parsing error occurred, free the permissions. */ + free (h); + break; + + default: + return ARGP_ERR_UNKNOWN; + } + + return err; +} + +/* + * Print current permissions. Called by fsysopts. + */ +error_t +netfs_append_args (char **argz, size_t * argz_len) +{ + error_t err = 0; + struct acpifs_perm *p; + +#define ADD_OPT(fmt, args...) \ + do { char buf[100]; \ + if (! err) { \ + snprintf (buf, sizeof buf, fmt , ##args); \ + err = argz_add (argz, argz_len, buf); } } while (0) + + p = &fs->perm; + if (p->uid >= 0) + ADD_OPT ("--uid=%u", p->uid); + if (p->gid >= 0) + ADD_OPT ("--gid=%u", p->gid); + +#undef ADD_OPT + return err; +} + +struct argp acpi_argp = { options, parse_opt, 0, doc }; + +struct argp *netfs_runtime_argp = &acpi_argp; diff --git a/acpi/options.h b/acpi/options.h new file mode 100644 index 00000000..36ccc48b --- /dev/null +++ b/acpi/options.h @@ -0,0 +1,51 @@ +/* + Copyright (C) 2018 Free Software Foundation, Inc. + + This file is part of the GNU Hurd. + + The GNU Hurd 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 2, or (at + your option) any later version. + + The GNU Hurd 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 the GNU Hurd. If not, see <<a rel="nofollow" href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>>. +*/ + +/* Command line option parsing */ + +#ifndef OPTIONS_H +#define OPTIONS_H + +#include <stdint.h> +#include <sys/types.h> +#include <argp.h> + +#include <acpifs.h> + +#define STR2(x) #x +#define STR(x) STR2(x) + +/* Used to hold data during argument parsing. */ +struct parse_hook +{ + struct acpifs_perm perm; + size_t ncache_len; +}; + +/* ACPI translator options. Used for both startup and runtime. */ +static const struct argp_option options[] = { + {0, 0, 0, 0, "These apply to the whole acpi tree:", 1}, + {"uid", 'U', "UID", 0, "User ID to give permissions to"}, + {"gid", 'G', "GID", 0, "Group ID to give permissions to"}, + {0} +}; + +static const char doc[] = "Permissions on acpi are currently global."; + +#endif // OPTIONS_H diff --git a/hurd/hurd_types.h b/hurd/hurd_types.h index eb06029b..a77a9e43 100644 --- a/hurd/hurd_types.h +++ b/hurd/hurd_types.h @@ -360,6 +360,7 @@ typedef const int *const_procinfo_t; #define FSTYPE_MEMFS 0x00000019 /* In-core filesystem */ #define FSTYPE_ISO9660 0x0000001a /* ISO9660 */ #define FSTYPE_PCI 0x0000001b /* PCI filesystem */ +#define FSTYPE_ACPI 0x0000001c /* ACPI filesystem */ /* Standard port assignments for file_exec_paths and exec_* */ enum -- 2.17.1