summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Zammit <damien@zamaudio.com>2018-11-06 02:53:36 -0500
committerDamien Zammit <damien@zamaudio.com>2018-11-10 21:32:09 -0500
commit22758da0b147980c11dbd456f7a4e00404261212 (patch)
treef8d3e8dd003e72927ce1d271bf10984050d36497
parent293c60e6d56bb2244831c00cf90d8e888fb7b5f8 (diff)
ACPI tables translator
Exposes x86 ACPI tables as a netfs on a mount point
-rw-r--r--Makefile3
-rw-r--r--acpi/Makefile35
-rw-r--r--acpi/acpi.c290
-rw-r--r--acpi/acpi.h74
-rw-r--r--acpi/acpifs.c265
-rw-r--r--acpi/acpifs.h147
-rw-r--r--acpi/func_files.c76
-rw-r--r--acpi/func_files.h39
-rw-r--r--acpi/main.c100
-rw-r--r--acpi/ncache.c90
-rw-r--r--acpi/ncache.h32
-rw-r--r--acpi/netfs_impl.c525
-rw-r--r--acpi/netfs_impl.h43
-rw-r--r--acpi/options.c149
-rw-r--r--acpi/options.h51
-rw-r--r--hurd/hurd_types.h1
16 files changed, 1919 insertions, 1 deletions
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