diff options
author | Joan Lledó <joanlluislledo@gmail.com> | 2018-01-16 12:54:28 +0100 |
---|---|---|
committer | Samuel Thibault <samuel.thibault@ens-lyon.org> | 2018-10-30 00:47:36 +0100 |
commit | a830bef66e689f6f1defa9988f257a7ec2bb034d (patch) | |
tree | 3d49249a05195148fc8a8e6a84f9c56b46dbab7b /pci-arbiter | |
parent | 6783a1b124eff9383571dfd4b5e83a03ee63866a (diff) | |
download | hurd-a830bef66e689f6f1defa9988f257a7ec2bb034d.tar.gz hurd-a830bef66e689f6f1defa9988f257a7ec2bb034d.tar.bz2 hurd-a830bef66e689f6f1defa9988f257a7ec2bb034d.zip |
PCI Arbiter
Diffstat (limited to 'pci-arbiter')
-rw-r--r-- | pci-arbiter/Makefile | 41 | ||||
-rw-r--r-- | pci-arbiter/func_files.c | 210 | ||||
-rw-r--r-- | pci-arbiter/func_files.h | 44 | ||||
-rw-r--r-- | pci-arbiter/main.c | 117 | ||||
-rw-r--r-- | pci-arbiter/mig-mutate.h | 28 | ||||
-rw-r--r-- | pci-arbiter/ncache.c | 90 | ||||
-rw-r--r-- | pci-arbiter/ncache.h | 32 | ||||
-rw-r--r-- | pci-arbiter/netfs_impl.c | 568 | ||||
-rw-r--r-- | pci-arbiter/netfs_impl.h | 43 | ||||
-rw-r--r-- | pci-arbiter/options.c | 282 | ||||
-rw-r--r-- | pci-arbiter/options.h | 74 | ||||
-rw-r--r-- | pci-arbiter/pci-ops.c | 273 | ||||
-rw-r--r-- | pci-arbiter/pci_access.c | 51 | ||||
-rw-r--r-- | pci-arbiter/pci_access.h | 187 | ||||
-rw-r--r-- | pci-arbiter/pcifs.c | 409 | ||||
-rw-r--r-- | pci-arbiter/pcifs.h | 208 | ||||
-rw-r--r-- | pci-arbiter/startup-ops.c | 40 | ||||
-rw-r--r-- | pci-arbiter/startup.c | 58 | ||||
-rw-r--r-- | pci-arbiter/startup.h | 31 | ||||
-rw-r--r-- | pci-arbiter/x86_pci.c | 843 | ||||
-rw-r--r-- | pci-arbiter/x86_pci.h | 34 |
21 files changed, 3663 insertions, 0 deletions
diff --git a/pci-arbiter/Makefile b/pci-arbiter/Makefile new file mode 100644 index 00000000..3fc3010b --- /dev/null +++ b/pci-arbiter/Makefile @@ -0,0 +1,41 @@ +# 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 <http://www.gnu.org/licenses/>. + +dir = pci-arbiter +makemode = server + +PORTDIR = $(srcdir)/port + +SRCS = main.c pci-ops.c pci_access.c x86_pci.c netfs_impl.c \ + pcifs.c ncache.c options.c func_files.c startup.c \ + startup-ops.c +MIGSRCS = pciServer.c startup_notifyServer.c +OBJS = $(patsubst %.S,%.o,$(patsubst %.c,%.o, $(SRCS) $(MIGSRCS))) + +HURDLIBS= fshelp ports shouldbeinlibc netfs +LDLIBS = -lpthread + +target = pci-arbiter + +include ../Makeconf + +CFLAGS += -I$(PORTDIR)/include + +pci-MIGSFLAGS = -imacros $(srcdir)/mig-mutate.h + +# cpp doesn't automatically make dependencies for -imacros dependencies. argh. +pci_S.h pciServer.c: mig-mutate.h diff --git a/pci-arbiter/func_files.c b/pci-arbiter/func_files.c new file mode 100644 index 00000000..7df94d2f --- /dev/null +++ b/pci-arbiter/func_files.c @@ -0,0 +1,210 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +/* + * Per-function files implementation. + * + * Implementation of all files repeated for each function. + */ + +#include "func_files.h" + +#include <assert.h> +#include <sys/io.h> + +/* Read or write a block of data from/to the configuration space */ +static error_t +config_block_op (struct pci_device *dev, off_t offset, size_t * len, + void *data, pci_io_op_t op) +{ + error_t err; + size_t pendent = *len; + + while (pendent >= 4) + { + err = op (dev->bus, dev->dev, dev->func, offset, data, 4); + if (err) + return err; + + offset += 4; + data += 4; + pendent -= 4; + } + + if (pendent >= 2) + { + err = op (dev->bus, dev->dev, dev->func, offset, data, 2); + if (err) + return err; + + offset += 2; + data += 2; + pendent -= 2; + } + + if (pendent) + { + err = op (dev->bus, dev->dev, dev->func, offset, data, 1); + if (err) + return err; + + offset++; + data++; + pendent--; + } + + *len -= pendent; + + return 0; +} + +/* Read or write from/to the config file */ +error_t +io_config_file (struct pci_device * dev, off_t offset, size_t * len, + void *data, pci_io_op_t op) +{ + error_t err; + + /* This should never happen */ + assert_backtrace (dev != 0); + + /* Don't exceed the config space size */ + if (offset > dev->config_size) + return EINVAL; + if ((offset + *len) > dev->config_size) + *len = dev->config_size - offset; + + pthread_mutex_lock (&fs->pci_conf_lock); + err = config_block_op (dev, offset, len, data, op); + pthread_mutex_unlock (&fs->pci_conf_lock); + + return err; +} + +/* Read the mapped ROM */ +error_t +read_rom_file (struct pci_device * dev, off_t offset, size_t * len, + void *data) +{ + error_t err; + + /* This should never happen */ + assert_backtrace (dev != 0); + + /* Refresh the ROM */ + err = pci_sys->device_refresh (dev, -1, 1); + if (err) + return err; + + /* Don't exceed the ROM size */ + if (offset > dev->rom_size) + return EINVAL; + if ((offset + *len) > dev->rom_size) + *len = dev->rom_size - offset; + + memcpy (data, dev->rom_memory + offset, *len); + + return 0; +} + +/* Read or write from/to a memory region by using I/O ports */ +static error_t +region_block_ioport_op (uint16_t port, off_t offset, size_t * len, + void *data, int read) +{ + size_t pending = *len; + + while (pending >= 4) + { + /* read == true: read; else: write */ + if (read) + *((unsigned int *) data) = inl (port + offset); + else + outl (*((unsigned int *) data), port + offset); + + offset += 4; + data += 4; + pending -= 4; + } + + if (pending >= 2) + { + if (read) + *((unsigned short *) data) = inw (port + offset); + else + outw (*((unsigned short *) data), port + offset); + + offset += 2; + data += 2; + pending -= 2; + } + + if (pending) + { + if (read) + *((unsigned char *) data) = inb (port + offset); + else + outb (*((unsigned char *) data), port + offset); + + offset++; + data++; + pending--; + } + + *len -= pending; + + return 0; +} + +/* Read or write from/to a region file */ +error_t +io_region_file (struct pcifs_dirent * e, off_t offset, size_t * len, + void *data, int read) +{ + error_t err; + size_t reg_num; + struct pci_mem_region *region; + + /* This should never happen */ + assert_backtrace (e->device != 0); + + /* Get the region */ + reg_num = strtol (&e->name[strlen (e->name) - 1], 0, 16); + region = &e->device->regions[reg_num]; + + /* Refresh the region */ + err = pci_sys->device_refresh (e->device, reg_num, -1); + if (err) + return err; + + /* Don't exceed the region size */ + if (offset > region->size) + return EINVAL; + if ((offset + *len) > region->size) + *len = region->size - offset; + + if (region->is_IO) + region_block_ioport_op (region->base_addr, offset, len, data, read); + else if (read) + memcpy (data, region->memory + offset, *len); + else + memcpy (region->memory + offset, data, *len); + + return 0; +} diff --git a/pci-arbiter/func_files.h b/pci-arbiter/func_files.h new file mode 100644 index 00000000..b65d2c57 --- /dev/null +++ b/pci-arbiter/func_files.h @@ -0,0 +1,44 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +/* Per-function files header */ + +#ifndef FUNC_FILES_H +#define FUNC_FILES_H + +#include "pcifs.h" + +/* Config */ +#define FILE_CONFIG_NAME "config" + +/* Rom */ +#define FILE_ROM_NAME "rom" + +/* Region */ +#define FILE_REGION_NAME "region" + +error_t io_config_file (struct pci_device * dev, off_t offset, size_t * len, + void *data, pci_io_op_t op); + +error_t read_rom_file (struct pci_device *dev, off_t offset, size_t * len, + void *data); + +error_t io_region_file (struct pcifs_dirent *e, off_t offset, size_t * len, + void *data, int read); +#endif /* FUNC_FILES_H */ diff --git a/pci-arbiter/main.c b/pci-arbiter/main.c new file mode 100644 index 00000000..bc085274 --- /dev/null +++ b/pci-arbiter/main.c @@ -0,0 +1,117 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +/* Translator initialization and demuxing */ + +#include <stdio.h> +#include <error.h> +#include <fcntl.h> +#include <version.h> +#include <argp.h> +#include <hurd/netfs.h> + +#include <pci_S.h> +#include <startup_notify_S.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 "pci_access.h" +#include "pcifs.h" +#include "startup.h" + +/* Libnetfs stuff */ +int netfs_maxsymlinks = 0; +char *netfs_server_name = "pci-arbiter"; +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 = pci_server_routine (inp)) || + (routine = startup_notify_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, &pcifs_maptime); + if (err) + error (1, err, "mapping time"); + + /* Start the PCI system */ + err = pci_system_init (); + if (err) + error (1, err, "Starting the PCI system"); + + /* Create the PCI filesystem */ + err = init_file_system (netfs_startup (bootstrap, O_READ), fs); + if (err) + error (1, err, "Creating the PCI filesystem"); + + /* Create the filesystem tree */ + err = create_fs_tree (fs, pci_sys); + if (err) + error (1, err, "Creating the PCI filesystem tree"); + + /* Set permissions */ + err = fs_set_permissions (fs); + if (err) + error (1, err, "Setting permissions"); + + /* + * Ask init to tell us when the system is going down, + * so we can try to be friendly to our correspondents on the network. + */ + arrange_shutdown_notification (); + + netfs_server_loop (); /* Never returns. */ + + return 0; +} diff --git a/pci-arbiter/mig-mutate.h b/pci-arbiter/mig-mutate.h new file mode 100644 index 00000000..f5098c97 --- /dev/null +++ b/pci-arbiter/mig-mutate.h @@ -0,0 +1,28 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + Written by Michael I. Bushnell, p/BSG. + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* Only CPP macro definitions should go in this file. */ + +#define PCI_IMPORTS \ + import "../libnetfs/priv.h"; \ + +#define PCI_INTRAN protid_t begin_using_protid_port (pci_t) +#define PCI_INTRAN_PAYLOAD protid_t begin_using_protid_payload +#define PCI_DESTRUCTOR end_using_protid_port (protid_t) diff --git a/pci-arbiter/ncache.c b/pci-arbiter/ncache.c new file mode 100644 index 00000000..6a513e37 --- /dev/null +++ b/pci-arbiter/ncache.c @@ -0,0 +1,90 @@ +/* Node caching + + Copyright (C) 1997 Free Software Foundation, Inc. + Written by Miles Bader <miles@gnu.org> + 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 "pcifs.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 pcifs *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->params.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->params.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/pci-arbiter/ncache.h b/pci-arbiter/ncache.h new file mode 100644 index 00000000..7ebc0c77 --- /dev/null +++ b/pci-arbiter/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 <http://www.gnu.org/licenses/>. +*/ + +/* Header for node caching functions */ + +#ifndef NCACHE_H +#define NCACHE_H + +#include <hurd/netfs.h> + +#include "pcifs.h" + +void node_cache (struct node *node); +void node_unlink (struct node *node, struct pcifs *fs); + +#endif /* NCACHE_H */ diff --git a/pci-arbiter/netfs_impl.c b/pci-arbiter/netfs_impl.c new file mode 100644 index 00000000..b96c7d8d --- /dev/null +++ b/pci-arbiter/netfs_impl.c @@ -0,0 +1,568 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + Written by Miles Bader <miles@gnu.org> + 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 <http://www.gnu.org/licenses/>. +*/ + +/* 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 "pcifs.h" +#include "ncache.h" +#include "pci_access.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 pcifs_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 pcifs_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 pcifs_dirent * +lookup (struct node *np, char *name) +{ + int i; + struct pcifs_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 pcifs_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 pcifs_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_PCI; + 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, FILE_CONFIG_NAME, NAME_SIZE)) + { + err = + io_config_file (node->nn->ln->device, offset, len, data, + pci_sys->read); + if (!err) + /* Update atime */ + UPDATE_TIMES (node->nn->ln, TOUCH_ATIME); + } + else if (!strncmp (node->nn->ln->name, FILE_ROM_NAME, NAME_SIZE)) + { + err = read_rom_file (node->nn->ln->device, offset, len, data); + if (!err) + /* Update atime */ + UPDATE_TIMES (node->nn->ln, TOUCH_ATIME); + } + else if (!strncmp + (node->nn->ln->name, FILE_REGION_NAME, strlen (FILE_REGION_NAME))) + { + err = io_region_file (node->nn->ln, offset, len, data, 1); + if (!err) + /* Update atime */ + UPDATE_TIMES (node->nn->ln, TOUCH_ATIME); + } + else + return EOPNOTSUPP; + + 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) +{ + error_t err; + + if (!strncmp (node->nn->ln->name, FILE_CONFIG_NAME, NAME_SIZE)) + { + err = + io_config_file (node->nn->ln->device, offset, len, data, + pci_sys->write); + if (!err) + { + /* Update mtime and ctime */ + UPDATE_TIMES (node->nn->ln, TOUCH_MTIME | TOUCH_CTIME); + } + } + else if (!strncmp + (node->nn->ln->name, FILE_REGION_NAME, strlen (FILE_REGION_NAME))) + { + err = io_region_file (node->nn->ln, offset, len, data, 0); + if (!err) + /* Update atime */ + UPDATE_TIMES (node->nn->ln, TOUCH_MTIME | TOUCH_CTIME); + } + else + return EOPNOTSUPP; + + return err; +} + +/* Node NP is all done; free all its associated storage. */ +void +netfs_node_norefs (struct node *node) +{ + destroy_node (node); +} diff --git a/pci-arbiter/netfs_impl.h b/pci-arbiter/netfs_impl.h new file mode 100644 index 00000000..ca0662fd --- /dev/null +++ b/pci-arbiter/netfs_impl.h @@ -0,0 +1,43 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +/* Data types required to create a directory tree using libnetfs */ + +#ifndef NETFS_IMPL_H +#define NETFS_IMPL_H + +#include <hurd/netfs.h> + +#include "pcifs.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 pcifs_dirent *ln; + + /* Position in the node cache. */ + struct node *ncache_next, *ncache_prev; +}; + +#endif /* NETFS_IMPL_H */ diff --git a/pci-arbiter/options.c b/pci-arbiter/options.c new file mode 100644 index 00000000..76ce6460 --- /dev/null +++ b/pci-arbiter/options.c @@ -0,0 +1,282 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.org> + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* Fsysopts and command line option parsing */ + +#include "options.h" + +#include <stdlib.h> +#include <argp.h> +#include <argz.h> +#include <error.h> + +#include "pcifs.h" + +/* Fsysopts and command line option parsing */ + +/* Adds an empty interface slot to H, and sets H's current interface to it, or + returns an error. */ +static error_t +parse_hook_add_set (struct parse_hook *h) +{ + struct pcifs_perm *new = realloc (h->permsets, + (h->num_permsets + + 1) * sizeof (struct pcifs_perm)); + if (!new) + return ENOMEM; + + h->permsets = new; + h->num_permsets++; + h->curset = new + h->num_permsets - 1; + h->curset->domain = -1; + h->curset->bus = -1; + h->curset->dev = -1; + h->curset->func = -1; + h->curset->d_class = -1; + h->curset->d_subclass = -1; + h->curset->uid = -1; + h->curset->gid = -1; + + return 0; +} + +/* + * Some options depend on other options to be valid. Check whether all + * dependences are met. + */ +static error_t +check_options_validity (struct parse_hook *h) +{ + int i; + struct pcifs_perm *p; + + for (p = h->permsets, i = 0; i < h->num_permsets; i++, p++) + { + if ((p->func >= 0 && p->dev < 0) + || (p->dev >= 0 && p->bus < 0) + || (p->bus >= 0 && p->domain < 0) + || (p->d_subclass >= 0 && p->d_class < 0) + || ((p->uid >= 0 || p->gid >= 0) + && (p->d_class < 0 && p->domain < 0)) || ((p->d_class >= 0 + || p->domain >= 0) + && !(p->uid >= 0 + || p->gid >= 0))) + return EINVAL; + } + + return 0; +} + +/* 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 'C': + /* Init a new set if the current one already has a value for this option */ + if (h->curset->d_class >= 0) + parse_hook_add_set (h); + + h->curset->d_class = strtol (arg, 0, 16); + break; + case 's': + h->curset->d_subclass = strtol (arg, 0, 16); + break; + case 'D': + if (h->curset->domain >= 0) + parse_hook_add_set (h); + + h->curset->domain = strtol (arg, 0, 16); + break; + case 'b': + h->curset->bus = strtol (arg, 0, 16); + break; + case 'd': + h->curset->dev = strtol (arg, 0, 16); + break; + case 'f': + h->curset->func = strtol (arg, 0, 16); + break; + case 'U': + if (h->curset->uid >= 0) + parse_hook_add_set (h); + + h->curset->uid = atoi (arg); + break; + case 'G': + if (h->curset->gid >= 0) + parse_hook_add_set (h); + + h->curset->gid = atoi (arg); + break; + case 'n': + h->ncache_len = 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->permsets = 0; + h->num_permsets = 0; + h->ncache_len = NODE_CACHE_MAX; + err = parse_hook_add_set (h); + if (err) + FAIL (err, 1, err, "option parsing"); + + state->hook = h; + break; + + case ARGP_KEY_SUCCESS: + /* Check option dependencies */ + err = check_options_validity (h); + if (err) + { + if (fs->root) + { + /* + * If there's an option dependence error but the server is yet + * running, print the error and do nothing. + */ + free (h->permsets); + free (h); + PERR (err, "Invalid options, no changes were applied"); + } + else + /* Invalid options on a non-started server, exit() */ + PERR (err, "Option dependence error"); + } + + /* Set permissions to FS */ + if (fs->params.perms) + free (fs->params.perms); + fs->params.perms = h->permsets; + fs->params.num_perms = h->num_permsets; + + /* Set cache len */ + fs->params.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->permsets); + 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 pcifs_perm *p; + int i; + +#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) + + for (i = 0, p = fs->params.perms; i < fs->params.num_perms; i++, p++) + { + if (p->d_class >= 0) + ADD_OPT ("--class=0x%02x", p->d_class); + if (p->d_subclass >= 0) + ADD_OPT ("--subclass=0x%02x", p->d_subclass); + if (p->domain >= 0) + ADD_OPT ("--domain=0x%04x", p->domain); + if (p->bus >= 0) + ADD_OPT ("--bus=0x%02x", p->bus); + if (p->dev >= 0) + ADD_OPT ("--dev=0x%02x", p->dev); + if (p->func >= 0) + ADD_OPT ("--func=%01u", p->func); + if (p->uid >= 0) + ADD_OPT ("--uid=%u", p->uid); + if (p->gid >= 0) + ADD_OPT ("--gid=%u", p->gid); + } + + if (fs->params.node_cache_max != NODE_CACHE_MAX) + ADD_OPT ("--ncache=%u", fs->params.node_cache_max); + +#undef ADD_OPT + return err; +} + +struct argp pci_argp = { options, parse_opt, 0, doc }; + +struct argp *netfs_runtime_argp = &pci_argp; diff --git a/pci-arbiter/options.h b/pci-arbiter/options.h new file mode 100644 index 00000000..814f81ad --- /dev/null +++ b/pci-arbiter/options.h @@ -0,0 +1,74 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.org> + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* Fsysopts and command line option parsing */ + +#ifndef OPTIONS_H +#define OPTIONS_H + +#include <stdint.h> +#include <sys/types.h> +#include <argp.h> + +#include "pcifs.h" + +#define STR2(x) #x +#define STR(x) STR2(x) + +/* Used to hold data during argument parsing. */ +struct parse_hook +{ + /* A list of specified permission sets and their corresponding options. */ + struct pcifs_perm *permsets; + size_t num_permsets; + + /* Permission set to which options apply. */ + struct pcifs_perm *curset; + + /* Node cache length */ + size_t ncache_len; +}; + +/* Lwip translator options. Used for both startup and runtime. */ +static const struct argp_option options[] = { + {0, 0, 0, 0, "Permission scope:", 1}, + {"class", 'C', "CLASS", 0, "Device class in hexadecimal"}, + {"subclass", 's', "SUBCLASS", 0, + "Device subclass in hexadecimal, only valid with -c"}, + {"domain", 'D', "DOMAIN", 0, "Device domain in hexadecimal"}, + {"bus", 'b', "BUS", 0, "Device bus in hexadecimal, only valid with -D"}, + {"dev", 'd', "DEV", 0, "Device dev in hexadecimal, only valid with -b"}, + {"func", 'f', "FUNC", 0, "Device func in hexadecimal, only valid with -d"}, + {0, 0, 0, 0, "These apply to a given permission scope:", 2}, + {"uid", 'U', "UID", 0, "User ID to give permissions to"}, + {"gid", 'G', "GID", 0, "Group ID to give permissions to"}, + {0, 0, 0, 0, "Global configuration options:", 3}, + {"ncache", 'n', "LENGTH", 0, + "Node cache length. " STR (NODE_CACHE_MAX) " by default"}, + {0} +}; + +static const char doc[] = "More than one permission scope may be specified. \ +Uppercase options create a new permission scope if the current one already \ +has a value for that option. If one node is covered by more than one \ +permission scope, only the first permission is applied to that node."; + +#endif // OPTIONS_H diff --git a/pci-arbiter/pci-ops.c b/pci-arbiter/pci-ops.c new file mode 100644 index 00000000..328db5b2 --- /dev/null +++ b/pci-arbiter/pci-ops.c @@ -0,0 +1,273 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +/* Implementation of PCI operations */ + +#include <pci_S.h> + +#include <fcntl.h> +#include <hurd/netfs.h> +#include <sys/mman.h> + +#include "pci_access.h" +#include "pcifs.h" +#include "func_files.h" + +static error_t +check_permissions (struct protid *master, int flags) +{ + error_t err = 0; + struct node *node; + struct pcifs_dirent *e; + + node = master->po->np; + e = node->nn->ln; + + /* Check wheter the user has permissions to access this node */ + err = entry_check_perms (master->user, e, flags); + if (err) + return err; + + /* Check wheter the request has been sent to the proper node */ + if (e->domain != 0 /* Only domain 0 can be accessed by I/O ports */ + || e->bus < 0 || e->dev < 0 || e->func < 0) + err = EINVAL; + + return err; +} + +static size_t +calculate_ndevs (struct iouser *user) +{ + size_t ndevs = 0; + int i; + struct pcifs_dirent *e; + + for (i = 0, e = fs->entries; i < fs->num_entries; i++, e++) + { + if (e->func < 0 /* Skip entries without a full address */ + || !S_ISDIR (e->stat.st_mode)) /* and entries that are not folders */ + continue; + + if (!entry_check_perms (user, e, O_READ)) + /* If no error, user may access this device */ + ndevs++; + } + + return ndevs; +} + +/* + * Read min(amount,*datalen) bytes and store them on `*data'. + * + * `*datalen' is updated. + */ +error_t +S_pci_conf_read (struct protid * master, int reg, char **data, + size_t * datalen, mach_msg_type_number_t amount) +{ + error_t err; + pthread_mutex_t *lock; + struct pcifs_dirent *e; + + if (!master) + return EOPNOTSUPP; + + e = master->po->np->nn->ln; + if (strncmp (e->name, FILE_CONFIG_NAME, NAME_SIZE)) + /* This operation may only be addressed to the config file */ + return EINVAL; + + lock = &fs->pci_conf_lock; + + err = check_permissions (master, O_READ); + if (err) + return err; + + /* + * We don't allocate new memory since we expect no more than 4 bytes-long + * buffers. Instead, we just take the lower value as length. + */ + if (amount > *datalen) + amount = *datalen; + + /* + * The server is not single-threaded anymore. Incoming rpcs are handled by + * libnetfs which is multi-threaded. A lock is needed for arbitration. + */ + pthread_mutex_lock (lock); + err = pci_sys->read (e->bus, e->dev, e->func, reg, *data, amount); + pthread_mutex_unlock (lock); + + if (!err) + { + *datalen = amount; + /* Update atime */ + UPDATE_TIMES (e, TOUCH_ATIME); + } + + return err; +} + +/* Write `datalen' bytes from `data'. `amount' is updated. */ +error_t +S_pci_conf_write (struct protid * master, int reg, char *data, size_t datalen, + mach_msg_type_number_t * amount) +{ + error_t err; + pthread_mutex_t *lock; + struct pcifs_dirent *e; + + if (!master) + return EOPNOTSUPP; + + e = master->po->np->nn->ln; + if (strncmp (e->name, FILE_CONFIG_NAME, NAME_SIZE)) + /* This operation may only be addressed to the config file */ + return EINVAL; + + lock = &fs->pci_conf_lock; + + err = check_permissions (master, O_WRITE); + if (err) + return err; + + pthread_mutex_lock (lock); + err = pci_sys->write (e->bus, e->dev, e->func, reg, data, datalen); + pthread_mutex_unlock (lock); + + if (!err) + { + *amount = datalen; + /* Update mtime and ctime */ + UPDATE_TIMES (e, TOUCH_MTIME | TOUCH_CTIME); + } + + return err; +} + +/* Write in `amount' the number of devices allowed for the user. */ +error_t +S_pci_get_ndevs (struct protid * master, mach_msg_type_number_t * amount) +{ + /* This RPC may only be addressed to the root node */ + if (master->po->np != fs->root) + return EINVAL; + + *amount = calculate_ndevs (master->user); + + return 0; +} + +/* + * Return in `data' the information about the available memory + * regions in the given device. + */ +error_t +S_pci_get_dev_regions (struct protid * master, char **data, size_t * datalen) +{ + error_t err; + struct pcifs_dirent *e; + struct pci_bar regions[6], *r; + size_t size; + int i; + + if (!master) + return EOPNOTSUPP; + + e = master->po->np->nn->ln; + if (strncmp (e->name, FILE_CONFIG_NAME, NAME_SIZE)) + /* This operation may only be addressed to the config file */ + return EINVAL; + + err = check_permissions (master, O_READ); + if (err) + return err; + + /* Allocate memory if needed */ + size = sizeof (regions); + if (size > *datalen) + { + *data = mmap (0, size, PROT_READ | PROT_WRITE, MAP_ANON, 0, 0); + if (*data == MAP_FAILED) + return ENOMEM; + } + + /* Copy the regions info */ + for (i = 0, r = (struct pci_bar *) *data; i < 6; i++, r++) + { + r->base_addr = e->device->regions[i].base_addr; + r->size = e->device->regions[i].size; + r->is_IO = e->device->regions[i].is_IO; + r->is_prefetchable = e->device->regions[i].is_prefetchable; + r->is_64 = e->device->regions[i].is_64; + } + + /* Update atime */ + UPDATE_TIMES (e, TOUCH_ATIME); + + *datalen = size; + + return 0; +} + +/* + * Return in `data' the information about the expansion rom of the given device + */ +error_t +S_pci_get_dev_rom (struct protid * master, char **data, size_t * datalen) +{ + error_t err; + struct pcifs_dirent *e; + struct pci_xrom_bar rom; + size_t size; + + if (!master) + return EOPNOTSUPP; + + e = master->po->np->nn->ln; + if (strncmp (e->name, FILE_CONFIG_NAME, NAME_SIZE)) + /* This operation may only be addressed to the config file */ + return EINVAL; + + err = check_permissions (master, O_READ); + if (err) + return err; + + /* Allocate memory if needed */ + size = sizeof (rom); + if (size > *datalen) + { + *data = mmap (0, size, PROT_READ | PROT_WRITE, MAP_ANON, 0, 0); + if (*data == MAP_FAILED) + return ENOMEM; + } + + /* Copy the regions info */ + rom.base_addr = e->device->rom_base; + rom.size = e->device->rom_size; + memcpy (*data, &rom, size); + + /* Update atime */ + UPDATE_TIMES (e, TOUCH_ATIME); + + *datalen = size; + + return 0; +} diff --git a/pci-arbiter/pci_access.c b/pci-arbiter/pci_access.c new file mode 100644 index 00000000..4d7fc68f --- /dev/null +++ b/pci-arbiter/pci_access.c @@ -0,0 +1,51 @@ +/* + * (C) Copyright IBM Corporation 2006 + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/* + * PCI access general header. + * + * Following code is borrowed from libpciaccess: + * https://cgit.freedesktop.org/xorg/lib/libpciaccess/ + */ + +#include "pci_access.h" + +#include <errno.h> + +#include "x86_pci.h" + +/* Configure PCI parameters */ +int +pci_system_init (void) +{ + int err = ENOSYS; + +#ifdef __GNU__ + err = pci_system_x86_create (); +#else +#error "Unsupported OS" +#endif + + return err; +} diff --git a/pci-arbiter/pci_access.h b/pci-arbiter/pci_access.h new file mode 100644 index 00000000..cf42cf62 --- /dev/null +++ b/pci-arbiter/pci_access.h @@ -0,0 +1,187 @@ +/* + * (C) Copyright IBM Corporation 2006 + * Copyright 2009 Red Hat, Inc. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * on the rights to use, copy, modify, merge, publish, distribute, sub + * license, and/or sell copies of the Software, and to permit persons to whom + * the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL + * IBM AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ +/* + * Copyright (c) 2007 Paulo R. Zanoni, Tiago Vignatti + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ + +/* + * PCI access general header. + * + * Following code is borrowed from libpciaccess: + * https://cgit.freedesktop.org/xorg/lib/libpciaccess/ + */ + +#ifndef PCI_ACCESS_H +#define PCI_ACCESS_H + +#include <stddef.h> +#include <stdint.h> +#include <errno.h> + +typedef uint64_t pciaddr_t; + +/* + * BAR descriptor for a PCI device. + */ +struct pci_mem_region +{ + /* + * When the region is mapped, this is the pointer to the memory. + */ + void *memory; + + /* + * Base physical address of the region from the CPU's point of view. + * + * This address is typically passed to \c pci_device_map_range to create + * a mapping of the region to the CPU's virtual address space. + */ + pciaddr_t base_addr; + + + /* + * Size, in bytes, of the region. + */ + pciaddr_t size; + + + /* + * Is the region I/O ports or memory? + */ + unsigned is_IO:1; + + /* + * Is the memory region prefetchable? + * + * \note + * This can only be set if \c is_IO is not set. + */ + unsigned is_prefetchable:1; + + + /* + * Is the memory at a 64-bit address? + * + * \note + * This can only be set if \c is_IO is not set. + */ + unsigned is_64:1; +}; + +/* + * PCI device. + * + * Contains all of the information about a particular PCI device. + */ +struct pci_device +{ + /* + * Complete bus identification, including domain, of the device. On + * platforms that do not support PCI domains (e.g., 32-bit x86 hardware), + * the domain will always be zero. + */ + uint16_t domain; + uint8_t bus; + uint8_t dev; + uint8_t func; + + /* + * Device's class, subclass, and programming interface packed into a + * single 32-bit value. The class is at bits [23:16], subclass is at + * bits [15:8], and programming interface is at [7:0]. + */ + uint32_t device_class; + + /* + * BAR descriptors for the device. + */ + struct pci_mem_region regions[6]; + + /* + * Size, in bytes, of the device's expansion ROM. + */ + pciaddr_t rom_size; + + /* + * Physical address of the ROM + */ + pciaddr_t rom_base; + + /* + * Mapped ROM + */ + void *rom_memory; + + /* + * Size of the configuration space + */ + size_t config_size; +}; + +typedef error_t (*pci_io_op_t) (unsigned bus, unsigned dev, unsigned func, + pciaddr_t reg, void *data, unsigned size); + +typedef error_t (*pci_refresh_dev_op_t) (struct pci_device * dev, + int num_region, int rom); + +/* Global PCI data */ +struct pci_system +{ + size_t num_devices; + struct pci_device *devices; + + /* Callbacks */ + pci_io_op_t read; + pci_io_op_t write; + pci_refresh_dev_op_t device_refresh; +}; + +struct pci_system *pci_sys; + +int pci_system_init (void); + +#endif /* PCI_ACCESS_H */ diff --git a/pci-arbiter/pcifs.c b/pci-arbiter/pcifs.c new file mode 100644 index 00000000..e7f495e1 --- /dev/null +++ b/pci-arbiter/pcifs.c @@ -0,0 +1,409 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +/* PCI Filesystem implementation */ + +#include "pcifs.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 (int32_t domain, int16_t bus, int16_t dev, + int16_t func, int32_t device_class, char *name, + struct pcifs_dirent *parent, io_statbuf_t stat, + struct node *node, struct pci_device *device, + struct pcifs_dirent *entry) +{ + uint16_t parent_num_entries; + + entry->domain = domain; + entry->bus = bus; + entry->dev = dev; + entry->func = func; + entry->device_class = device_class; + strncpy (entry->name, name, NAME_SIZE); + entry->parent = parent; + entry->stat = stat; + entry->dir = 0; + entry->node = node; + entry->device = device; + + /* Update parent's child list */ + if (entry->parent) + { + if (!entry->parent->dir) + { + /* First child */ + entry->parent->dir = calloc (1, sizeof (struct pcifs_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 pcifs_dirent *)); + if (!entry->parent->dir->entries) + return ENOMEM; + entry->parent->dir->entries[parent_num_entries] = entry; + } + + return 0; +} + +error_t +alloc_file_system (struct pcifs ** fs) +{ + *fs = calloc (1, sizeof (struct pcifs)); + if (!*fs) + return ENOMEM; + + return 0; +} + +error_t +init_file_system (file_t underlying_node, struct pcifs * 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, + pcifs_maptime); + + fs->entries = calloc (1, sizeof (struct pcifs_dirent)); + if (!fs->entries) + { + free (fs->entries); + return ENOMEM; + } + + /* Create the root entry */ + err = + create_dir_entry (-1, -1, -1, -1, -1, "", 0, np->nn_stat, np, 0, + 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); + pthread_mutex_init (&fs->pci_conf_lock, 0); + + return 0; +} + +error_t +create_fs_tree (struct pcifs * fs, struct pci_system * pci_sys) +{ + error_t err = 0; + int c_domain, c_bus, c_dev, i, j; + size_t nentries; + struct pci_device *device; + struct pcifs_dirent *e, *domain_parent, *bus_parent, *dev_parent, + *func_parent, *list; + struct stat e_stat; + char entry_name[NAME_SIZE]; + + nentries = 1; /* Skip root entry */ + c_domain = c_bus = c_dev = -1; + for (i = 0, device = pci_sys->devices; i < pci_sys->num_devices; + i++, device++) + { + if (device->domain != c_domain) + { + c_domain = device->domain; + c_bus = -1; + c_dev = -1; + nentries++; + } + + if (device->bus != c_bus) + { + c_bus = device->bus; + c_dev = -1; + nentries++; + } + + if (device->dev != c_dev) + { + c_dev = device->dev; + nentries++; + } + + nentries += 2; /* func dir + config */ + + for (j = 0; j < 6; j++) + if (device->regions[j].size > 0) + nentries++; /* + memory region */ + + if (device->rom_size) + nentries++; /* + rom */ + } + + list = realloc (fs->entries, nentries * sizeof (struct pcifs_dirent)); + if (!list) + return ENOMEM; + + e = list + 1; + c_domain = c_bus = c_dev = -1; + domain_parent = bus_parent = dev_parent = func_parent = 0; + for (i = 0, device = pci_sys->devices; i < pci_sys->num_devices; + i++, device++) + { + if (device->domain != c_domain) + { + /* We've found a new domain. Add an entry for it */ + e_stat = list->stat; + e_stat.st_mode &= ~S_IROOT; /* Remove the root mode */ + memset (entry_name, 0, NAME_SIZE); + snprintf (entry_name, NAME_SIZE, "%04x", device->domain); + err = + create_dir_entry (device->domain, -1, -1, -1, -1, entry_name, + list, e_stat, 0, 0, e); + if (err) + return err; + + /* Switch to bus level */ + domain_parent = e++; + c_domain = device->domain; + c_bus = -1; + c_dev = -1; + } + + if (device->bus != c_bus) + { + /* We've found a new bus. Add an entry for it */ + memset (entry_name, 0, NAME_SIZE); + snprintf (entry_name, NAME_SIZE, "%02x", device->bus); + err = + create_dir_entry (device->domain, device->bus, -1, -1, -1, + entry_name, domain_parent, domain_parent->stat, + 0, 0, e); + if (err) + return err; + + /* Switch to dev level */ + bus_parent = e++; + c_bus = device->bus; + c_dev = -1; + } + + if (device->dev != c_dev) + { + /* We've found a new dev. Add an entry for it */ + memset (entry_name, 0, NAME_SIZE); + snprintf (entry_name, NAME_SIZE, "%02x", device->dev); + err = + create_dir_entry (device->domain, device->bus, device->dev, -1, + -1, entry_name, bus_parent, bus_parent->stat, 0, + 0, e); + if (err) + return err; + + /* Switch to func level */ + dev_parent = e++; + c_dev = device->dev; + } + + /* Remove all permissions to others */ + e_stat = dev_parent->stat; + e_stat.st_mode &= ~(S_IROTH | S_IWOTH | S_IXOTH); + + /* Add func entry */ + memset (entry_name, 0, NAME_SIZE); + snprintf (entry_name, NAME_SIZE, "%01u", device->func); + err = + create_dir_entry (device->domain, device->bus, device->dev, + device->func, device->device_class, entry_name, + dev_parent, e_stat, 0, device, e); + if (err) + return err; + + /* Switch to the lowest level */ + func_parent = e++; + + /* Change mode to a regular file */ + e_stat = func_parent->stat; + e_stat.st_mode &= ~(S_IFDIR | S_IXUSR | S_IXGRP); + e_stat.st_mode |= S_IFREG | S_IWUSR | S_IWGRP; + e_stat.st_size = device->config_size; + + /* Create config entry */ + strncpy (entry_name, FILE_CONFIG_NAME, NAME_SIZE); + err = + create_dir_entry (device->domain, device->bus, device->dev, + device->func, device->device_class, entry_name, + func_parent, e_stat, 0, device, e++); + if (err) + return err; + + /* Create regions entries */ + for (j = 0; j < 6; j++) + { + if (device->regions[j].size > 0) + { + e_stat.st_size = device->regions[j].size; + snprintf (entry_name, NAME_SIZE, "%s%01u", FILE_REGION_NAME, j); + err = + create_dir_entry (device->domain, device->bus, device->dev, + device->func, device->device_class, + entry_name, func_parent, e_stat, 0, device, + e++); + if (err) + return err; + } + } + + /* Create rom entry */ + if (device->rom_size) + { + /* Make rom is read only */ + e_stat.st_mode &= ~(S_IWUSR | S_IWGRP); + e_stat.st_size = device->rom_size; + strncpy (entry_name, FILE_ROM_NAME, NAME_SIZE); + err = + create_dir_entry (device->domain, device->bus, device->dev, + device->func, device->device_class, entry_name, + func_parent, e_stat, 0, device, 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 pcifs_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 pcifs *fs, struct pcifs_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 pcifs *fs, struct pcifs_dirent *e) +{ + int i; + struct pcifs_perm *perms = fs->params.perms, *p; + size_t num_perms = fs->params.num_perms; + + for (i = 0, p = perms; i < num_perms; i++, p++) + { + uint8_t e_class = e->device_class >> 16; + uint8_t e_subclass = ((e->device_class >> 8) & 0xFF); + + /* Check whether the entry is convered by this permission scope */ + if (p->d_class >= 0 && e_class != p->d_class) + continue; + if (p->d_subclass >= 0 && e_subclass != p->d_subclass) + continue; + if (p->domain >= 0 && p->domain != e->domain) + continue; + if (p->bus >= 0 && e->bus != p->bus) + continue; + if (p->dev >= 0 && e->dev != p->dev) + continue; + if (p->func >= 0 && e->func != p->func) + continue; + + /* This permission set covers this entry */ + if (p->uid >= 0) + UPDATE_OWNER (e, p->uid); + if (p->gid >= 0) + UPDATE_GROUP (e, p->gid); + + /* Update ctime */ + UPDATE_TIMES (e, TOUCH_CTIME); + + /* Break, as only one permission set can cover each node */ + break; + } + + return; +} + +/* Update all entries' permissions */ +error_t +fs_set_permissions (struct pcifs * fs) +{ + int i; + struct pcifs_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/pci-arbiter/pcifs.h b/pci-arbiter/pcifs.h new file mode 100644 index 00000000..7a35c77d --- /dev/null +++ b/pci-arbiter/pcifs.h @@ -0,0 +1,208 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + Written by Miles Bader <miles@gnu.org> + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* PCI Filesystem header */ + +#ifndef PCIFS_H +#define PCIFS_H + +#include <hurd/netfs.h> +#include <pthread.h> +#include <maptime.h> + +#include "pci_access.h" +#include "netfs_impl.h" + +/* Size of a directory entry name */ +#ifndef NAME_SIZE +#define NAME_SIZE 16 +#endif + +/* Node cache defaults size */ +#define NODE_CACHE_MAX 16 + +/* + * Directory entry. Contains all per-node data our problem requires. + * + * 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 pcifs_dirent +{ + /* + * Complete bus identification, including domain, of the device. On + * platforms that do not support PCI domains (e.g., 32-bit x86 hardware), + * the domain will always be zero. + * + * Negative value means no value. + */ + int32_t domain; + int16_t bus; + int16_t dev; + int8_t func; + + /* + * Device's class, subclass, and programming interface packed into a + * single 32-bit value. The class is at bits [23:16], subclass is at + * bits [15:8], and programming interface is at [7:0]. + * + * Negative value means no value. + */ + int32_t device_class; + + char name[NAME_SIZE]; + struct pcifs_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 pcifs_dir *dir; + + /* Active node on this entry */ + struct node *node; + + /* + * Underlying virtual device if any. + * + * Only for entries having a full B/D/F address. + */ + struct pci_device *device; +}; + +/* + * A directory, it only contains a list of directory entries + */ +struct pcifs_dir +{ + /* Number of directory entries */ + uint16_t num_entries; + + /* Array of directory entries */ + struct pcifs_dirent **entries; +}; + +/* + * Set of permissions. + * + * For each Class[,subclass] and/or Domain[,bus[,dev[,func]]], one UID and/or GID. + */ +struct pcifs_perm +{ + /* + * D/b/d/f scope of permissions. + * + * Negative value means no value. + */ + int32_t domain; + int16_t bus; + int16_t dev; + int8_t func; + + /* + * Class and subclass scope of permissions + * + * Negative value means no value. + */ + int16_t d_class; + int16_t d_subclass; + + /* User and group ids */ + int32_t uid; + int32_t gid; +}; + +/* Various parameters that can be used to change the behavior of an ftpfs. */ +struct pcifs_params +{ + /* The size of the node cache. */ + size_t node_cache_max; + + /* FS permissions. */ + struct pcifs_perm *perms; + size_t num_perms; +}; + +/* A particular filesystem. */ +struct pcifs +{ + /* Root of filesystem. */ + struct node *root; + + /* FS configuration */ + struct pcifs_params params; + + /* 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. */ + pthread_mutex_t node_cache_lock; + + /* Lock for pci_conf operations */ + pthread_mutex_t pci_conf_lock; + + struct pcifs_dirent *entries; + size_t num_entries; +}; + +/* Main FS pointer */ +struct pcifs *fs; + +/* Global mapped time */ +volatile struct mapped_time_value *pcifs_maptime; + +/* Update entry and node times */ +#define UPDATE_TIMES(e, what) (\ + {\ + fshelp_touch (&e->stat, what, pcifs_maptime);\ + if(e->node)\ + fshelp_touch (&e->node->nn_stat, what, pcifs_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 pcifs **fs); +error_t init_file_system (file_t underlying_node, struct pcifs *fs); +error_t create_fs_tree (struct pcifs *fs, struct pci_system *pci_sys); +error_t fs_set_permissions (struct pcifs *fs); +error_t entry_check_perms (struct iouser *user, struct pcifs_dirent *e, + int flags); + +#endif /* PCIFS_H */ diff --git a/pci-arbiter/startup-ops.c b/pci-arbiter/startup-ops.c new file mode 100644 index 00000000..f3506c42 --- /dev/null +++ b/pci-arbiter/startup-ops.c @@ -0,0 +1,40 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + Written by Michael I. Bushnell, p/BSG. + + 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 <http://www.gnu.org/licenses/>. +*/ + +#include <startup_notify_S.h> + +#include <hurd/netfs.h> + +#include "startup.h" + +/* The system is going down. Call netfs_shutdown() */ +error_t +S_startup_dosync (mach_port_t handle) +{ + struct port_info *inpi = ports_lookup_port (netfs_port_bucket, handle, + pci_shutdown_notify_class); + + if (!inpi) + return EOPNOTSUPP; + + ports_port_deref (inpi); + + return netfs_shutdown (FSYS_GOAWAY_FORCE); +} diff --git a/pci-arbiter/startup.c b/pci-arbiter/startup.c new file mode 100644 index 00000000..6ffb8c60 --- /dev/null +++ b/pci-arbiter/startup.c @@ -0,0 +1,58 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + Written by Michael I. Bushnell, p/BSG. + + 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 <http://www.gnu.org/licenses/>. +*/ + +/* Startup and shutdown notifications management */ + +#include "startup.h" + +#include <unistd.h> +#include <hurd/paths.h> +#include <hurd/startup.h> +#include <hurd/netfs.h> + +void +arrange_shutdown_notification () +{ + error_t err; + mach_port_t initport, notify; + struct port_info *pi; + + pci_shutdown_notify_class = ports_create_class (0, 0); + + /* Arrange to get notified when the system goes down, + but if we fail for some reason, just silently give up. No big deal. */ + + err = ports_create_port (pci_shutdown_notify_class, netfs_port_bucket, + sizeof (struct port_info), &pi); + if (err) + return; + + initport = file_name_lookup (_SERVERS_STARTUP, 0, 0); + if (initport == MACH_PORT_NULL) + return; + + notify = ports_get_send_right (pi); + ports_port_deref (pi); + startup_request_notification (initport, notify, + MACH_MSG_TYPE_MAKE_SEND, + program_invocation_short_name); + mach_port_deallocate (mach_task_self (), notify); + mach_port_deallocate (mach_task_self (), initport); +} diff --git a/pci-arbiter/startup.h b/pci-arbiter/startup.h new file mode 100644 index 00000000..12746f3b --- /dev/null +++ b/pci-arbiter/startup.h @@ -0,0 +1,31 @@ +/* + Copyright (C) 2017 Free Software Foundation, Inc. + Written by Michael I. Bushnell, p/BSG. + + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef STARTUP_H +#define STARTUP_H + +/* Startup and shutdown notifications management */ + +/* Port class for startup requests */ +struct port_class *pci_shutdown_notify_class; + +void arrange_shutdown_notification (); + +#endif /* STARTUP_H */ diff --git a/pci-arbiter/x86_pci.c b/pci-arbiter/x86_pci.c new file mode 100644 index 00000000..9cf1f54a --- /dev/null +++ b/pci-arbiter/x86_pci.c @@ -0,0 +1,843 @@ +/* + * Copyright (c) 2017 Joan Lledó + * Copyright (c) 2009, 2012, 2018 Samuel Thibault + * Heavily inspired from the freebsd, netbsd, and openbsd backends + * (C) Copyright Eric Anholt 2006 + * (C) Copyright IBM Corporation 2006 + * Copyright (c) 2008 Juan Romero Pardines + * Copyright (c) 2008 Mark Kettenis + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * PCI backend for x86 (32 and 64 bit) architectures. + * + * Following code is borrowed from libpciaccess: + * https://cgit.freedesktop.org/xorg/lib/libpciaccess/ + */ + +#include "x86_pci.h" + +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/mman.h> +#include <sys/io.h> +#include <string.h> + +#include "pci_access.h" + +#define PCI_VENDOR(reg) ((reg) & 0xFFFF) +#define PCI_VENDOR_INVALID 0xFFFF + +#define PCI_VENDOR_ID 0x00 +#define PCI_VENDOR_ID_COMPAQ 0x0e11 +#define PCI_VENDOR_ID_INTEL 0x8086 + +#define PCI_CLASS 0x08 +#define PCI_CLASS_DEVICE 0x0a +#define PCI_CLASS_DISPLAY_VGA 0x0300 +#define PCI_CLASS_BRIDGE_HOST 0x0600 + +#define PCI_BAR_ADDR_0 0x10 +#define PCI_XROMBAR_ADDR_00 0x30 +#define PCI_XROMBAR_ADDR_01 0x38 + +#define PCI_HDRTYPE 0x0E +#define PCI_HDRTYPE_DEVICE 0x00 +#define PCI_HDRTYPE_BRIDGE 0x01 +#define PCI_HDRTYPE_CARDBUS 0x02 + +#define PCI_COMMAND 0x04 +#define PCI_SECONDARY_BUS 0x19 + +#define PCI_CONFIG_SIZE 256 + +static error_t +x86_enable_io (void) +{ + if (!ioperm (0, 0xffff, 1)) + return 0; + return errno; +} + +static error_t +x86_disable_io (void) +{ + if (!ioperm (0, 0xffff, 0)) + return 0; + return errno; +} + +static error_t +pci_system_x86_conf1_probe (void) +{ + unsigned long sav; + int res = ENODEV; + + outb (0x01, 0xCFB); + sav = inl (0xCF8); + outl (0x80000000, 0xCF8); + if (inl (0xCF8) == 0x80000000) + res = 0; + outl (sav, 0xCF8); + + return res; +} + +static error_t +pci_system_x86_conf1_read (unsigned bus, unsigned dev, unsigned func, + pciaddr_t reg, void *data, unsigned size) +{ + unsigned addr = 0xCFC + (reg & 3); + unsigned long sav; + error_t ret = 0; + + if (bus >= 0x100 || dev >= 32 || func >= 8 || reg >= 0x100 || size > 4 + || size == 3) + return EIO; + + sav = inl (0xCF8); + outl (0x80000000 | (bus << 16) | (dev << 11) | (func << 8) | (reg & ~3), + 0xCF8); + /* NOTE: x86 is already LE */ + switch (size) + { + case 1: + { + uint8_t *val = data; + *val = inb (addr); + break; + } + case 2: + { + uint16_t *val = data; + *val = inw (addr); + break; + } + case 4: + { + uint32_t *val = data; + *val = inl (addr); + break; + } + } + outl (sav, 0xCF8); + + return ret; +} + +static error_t +pci_system_x86_conf1_write (unsigned bus, unsigned dev, unsigned func, + pciaddr_t reg, void *data, unsigned size) +{ + unsigned addr = 0xCFC + (reg & 3); + unsigned long sav; + error_t ret = 0; + + if (bus >= 0x100 || dev >= 32 || func >= 8 || reg >= 0x100 || size > 4 + || size == 3) + return EIO; + + sav = inl (0xCF8); + outl (0x80000000 | (bus << 16) | (dev << 11) | (func << 8) | (reg & ~3), + 0xCF8); + /* NOTE: x86 is already LE */ + switch (size) + { + case 1: + { + const uint8_t *val = data; + outb (*val, addr); + break; + } + case 2: + { + const uint16_t *val = data; + outw (*val, addr); + break; + } + case 4: + { + const uint32_t *val = data; + outl (*val, addr); + break; + } + } + outl (sav, 0xCF8); + + return ret; +} + +static error_t +pci_system_x86_conf2_probe (void) +{ + outb (0, 0xCFB); + outb (0, 0xCF8); + outb (0, 0xCFA); + if (inb (0xCF8) == 0 && inb (0xCFA) == 0) + return 0; + + return ENODEV; +} + +static error_t +pci_system_x86_conf2_read (unsigned bus, unsigned dev, unsigned func, + pciaddr_t reg, void *data, unsigned size) +{ + unsigned addr = 0xC000 | dev << 8 | reg; + error_t ret = 0; + + if (bus >= 0x100 || dev >= 16 || func >= 8 || reg >= 0x100) + return EIO; + + outb ((func << 1) | 0xF0, 0xCF8); + outb (bus, 0xCFA); + /* NOTE: x86 is already LE */ + switch (size) + { + case 1: + { + uint8_t *val = data; + *val = inb (addr); + break; + } + case 2: + { + uint16_t *val = data; + *val = inw (addr); + break; + } + case 4: + { + uint32_t *val = data; + *val = inl (addr); + break; + } + default: + ret = EIO; + break; + } + outb (0, 0xCF8); + + return ret; +} + +static error_t +pci_system_x86_conf2_write (unsigned bus, unsigned dev, unsigned func, + pciaddr_t reg, void *data, unsigned size) +{ + unsigned addr = 0xC000 | dev << 8 | reg; + error_t ret = 0; + + if (bus >= 0x100 || dev >= 16 || func >= 8 || reg >= 0x100) + return EIO; + + outb ((func << 1) | 0xF0, 0xCF8); + outb (bus, 0xCFA); + /* NOTE: x86 is already LE */ + switch (size) + { + case 1: + { + const uint8_t *val = data; + outb (*val, addr); + break; + } + case 2: + { + const uint16_t *val = data; + outw (*val, addr); + break; + } + case 4: + { + const uint32_t *val = data; + outl (*val, addr); + break; + } + default: + ret = EIO; + break; + } + outb (0, 0xCF8); + + return ret; +} + +/* Returns the number of regions (base address registers) the device has */ +static int +pci_device_x86_get_num_regions (uint8_t header_type) +{ + switch (header_type & 0x7f) + { + case 0: + return 6; + case 1: + return 2; + case 2: + return 1; + default: + return 0; + } +} + +/* Masks out the flag bigs of the base address register value */ +static uint32_t +get_map_base (uint32_t val) +{ + if (val & 0x01) + return val & ~0x03; + else + return val & ~0x0f; +} + +/* Returns the size of a region based on the all-ones test value */ +static unsigned +get_test_val_size (uint32_t testval) +{ + unsigned size = 1; + + if (testval == 0) + return 0; + + /* Mask out the flag bits */ + testval = get_map_base (testval); + if (!testval) + return 0; + + while ((testval & 1) == 0) + { + size <<= 1; + testval >>= 1; + } + + return size; +} + +/* Read BAR `reg_num' in `dev' and map the data if any */ +static error_t +pci_device_x86_region_probe (struct pci_device *dev, int reg_num) +{ + error_t err; + uint8_t offset; + uint32_t reg, addr, testval; + int memfd; + + offset = PCI_BAR_ADDR_0 + 0x4 * reg_num; + + /* Get the base address */ + err = + pci_sys->read (dev->bus, dev->dev, dev->func, offset, &addr, + sizeof (addr)); + if (err) + return err; + + /* Test write all ones to the register, then restore it. */ + reg = 0xffffffff; + err = pci_sys->write (dev->bus, dev->dev, dev->func, offset, ®, + sizeof (reg)); + if (err) + return err; + err = pci_sys->read (dev->bus, dev->dev, dev->func, offset, &testval, + sizeof (testval)); + if (err) + return err; + err = pci_sys->write (dev->bus, dev->dev, dev->func, offset, &addr, + sizeof (addr)); + if (err) + return err; + + if (addr & 0x01) + dev->regions[reg_num].is_IO = 1; + if (addr & 0x04) + dev->regions[reg_num].is_64 = 1; + if (addr & 0x08) + dev->regions[reg_num].is_prefetchable = 1; + + /* Set the size */ + dev->regions[reg_num].size = get_test_val_size (testval); + + /* Set the base address value */ + dev->regions[reg_num].base_addr = get_map_base (addr); + + if (dev->regions[reg_num].is_64) + { + err = + pci_sys->read (dev->bus, dev->dev, dev->func, offset + 4, &addr, + sizeof (addr)); + if (err) + return err; + + dev->regions[reg_num].base_addr |= ((uint64_t) addr << 32); + } + + if (dev->regions[reg_num].is_IO) + { + /* Enable the I/O Space bit */ + err = + pci_sys->read (dev->bus, dev->dev, dev->func, PCI_COMMAND, ®, + sizeof (reg)); + if (err) + return err; + + if (!(reg & 0x1)) + { + reg |= 0x1; + + err = + pci_sys->write (dev->bus, dev->dev, dev->func, PCI_COMMAND, + ®, sizeof (reg)); + if (err) + return err; + } + + /* Clear the map pointer */ + dev->regions[reg_num].memory = 0; + } + else if (dev->regions[reg_num].size > 0) + { + /* Enable the Memory Space bit */ + err = + pci_sys->read (dev->bus, dev->dev, dev->func, PCI_COMMAND, ®, + sizeof (reg)); + if (err) + return err; + + if (!(reg & 0x2)) + { + reg |= 0x2; + + err = + pci_sys->write (dev->bus, dev->dev, dev->func, PCI_COMMAND, + ®, sizeof (reg)); + if (err) + return err; + } + + /* Map the region in our space */ + memfd = open ("/dev/mem", O_RDONLY | O_CLOEXEC); + if (memfd == -1) + return errno; + + dev->regions[reg_num].memory = + mmap (NULL, dev->regions[reg_num].size, PROT_READ | PROT_WRITE, 0, + memfd, dev->regions[reg_num].base_addr); + if (dev->regions[reg_num].memory == MAP_FAILED) + { + dev->regions[reg_num].memory = 0; + close (memfd); + return errno; + } + + close (memfd); + } + + return 0; +} + +/* Read the XROMBAR in `dev' and map the data if any */ +static error_t +pci_device_x86_rom_probe (struct pci_device *dev) +{ + error_t err; + uint8_t reg_8, xrombar_addr; + uint32_t reg, reg_back; + pciaddr_t rom_size; + pciaddr_t rom_base; + void *rom_mapped; + int memfd; + + /* First we need to know which type of header is this */ + err = pci_sys->read (dev->bus, dev->dev, dev->func, PCI_HDRTYPE, ®_8, + sizeof (reg_8)); + if (err) + return err; + + /* Get the XROMBAR register address */ + switch (reg_8 & 0x3) + { + case PCI_HDRTYPE_DEVICE: + xrombar_addr = PCI_XROMBAR_ADDR_00; + break; + case PCI_HDRTYPE_BRIDGE: + xrombar_addr = PCI_XROMBAR_ADDR_01; + break; + default: + return -1; + } + + /* Get size and physical address */ + err = pci_sys->read (dev->bus, dev->dev, dev->func, xrombar_addr, ®, + sizeof (reg)); + if (err) + return err; + + reg_back = reg; + reg = 0xFFFFF800; /* Base address: first 21 bytes */ + err = pci_sys->write (dev->bus, dev->dev, dev->func, xrombar_addr, ®, + sizeof (reg)); + if (err) + return err; + err = pci_sys->read (dev->bus, dev->dev, dev->func, xrombar_addr, ®, + sizeof (reg)); + if (err) + return err; + + rom_size = (~reg + 1); + rom_base = reg_back & reg; + + if (rom_size == 0) + return 0; + + /* Enable the address decoder and write the physical address back */ + reg_back |= 0x1; + err = pci_sys->write + (dev->bus, dev->dev, dev->func, xrombar_addr, ®_back, + sizeof (reg_back)); + if (err) + return err; + + /* Enable the Memory Space bit */ + err = pci_sys->read (dev->bus, dev->dev, dev->func, PCI_COMMAND, ®, + sizeof (reg)); + if (err) + return err; + + if (!(reg & 0x2)) + { + reg |= 0x2; + + err = + pci_sys->write (dev->bus, dev->dev, dev->func, PCI_COMMAND, ®, + sizeof (reg)); + if (err) + return err; + } + + /* Map the ROM in our space */ + memfd = open ("/dev/mem", O_RDONLY | O_CLOEXEC); + if (memfd == -1) + return errno; + + rom_mapped = mmap (NULL, rom_size, PROT_READ, 0, memfd, rom_base); + if (rom_mapped == MAP_FAILED) + { + close (memfd); + return errno; + } + + close (memfd); + + dev->rom_size = rom_size; + dev->rom_base = rom_base; + dev->rom_memory = rom_mapped; + + return 0; +} + +/* Configure BARs and ROM */ +static error_t +pci_device_x86_probe (struct pci_device *dev) +{ + error_t err; + uint8_t hdrtype; + int i; + + /* Probe BARs */ + err = pci_sys->read (dev->bus, dev->dev, dev->func, PCI_HDRTYPE, &hdrtype, + sizeof (hdrtype)); + if (err) + return err; + + for (i = 0; i < pci_device_x86_get_num_regions (hdrtype); i++) + { + err = pci_device_x86_region_probe (dev, i); + if (err) + return err; + + if (dev->regions[i].is_64) + /* Move the pointer one BAR ahead */ + i++; + } + + /* Probe ROM */ + err = pci_device_x86_rom_probe (dev); + if (err) + return err; + + return 0; +} + +/* + * Refresh the device. Check for updates in region `reg_num' + * or in ROM if `rom' = true. `reg_num' < 0 means no region check. + */ +static error_t +pci_device_x86_refresh (struct pci_device *dev, int reg_num, int rom) +{ + error_t err; + uint8_t offset, hdrtype; + uint32_t addr; + + if (reg_num >= 0 && dev->regions[reg_num].size > 0) + { + /* Read the BAR */ + offset = PCI_BAR_ADDR_0 + 0x4 * reg_num; + err = + pci_sys->read (dev->bus, dev->dev, dev->func, offset, &addr, + sizeof (addr)); + if (err) + return err; + + /* Check whether the region is outdated, if so, the refresh it */ + if (dev->regions[reg_num].base_addr != get_map_base (addr)) + { + err = pci_device_x86_region_probe (dev, reg_num); + if (err) + return err; + } + } + + if (rom && dev->rom_size > 0) + { + /* Read the BAR */ + err = + pci_sys->read (dev->bus, dev->dev, dev->func, PCI_HDRTYPE, &hdrtype, + sizeof (hdrtype)); + if (err) + return err; + + switch (hdrtype & 0x3) + { + case PCI_HDRTYPE_DEVICE: + offset = PCI_XROMBAR_ADDR_00; + break; + case PCI_HDRTYPE_BRIDGE: + offset = PCI_XROMBAR_ADDR_01; + break; + default: + return -1; + } + + err = pci_sys->read (dev->bus, dev->dev, dev->func, offset, &addr, + sizeof (addr)); + if (err) + return err; + + /* Check whether the ROM is outdated, if so, the refresh it */ + if (dev->rom_base != (addr & 0xFFFFF800)) + { + err = pci_device_x86_rom_probe (dev); + if (err) + return err; + } + } + + return 0; +} + +/* Check that this really looks like a PCI configuration. */ +static error_t +pci_system_x86_check (struct pci_system *pci_sys) +{ + int dev; + uint16_t class, vendor; + + /* Look on bus 0 for a device that is a host bridge, a VGA card, + * or an intel or compaq device. */ + + for (dev = 0; dev < 32; dev++) + { + if (pci_sys->read (0, dev, 0, PCI_CLASS_DEVICE, &class, sizeof (class))) + continue; + if (class == PCI_CLASS_BRIDGE_HOST || class == PCI_CLASS_DISPLAY_VGA) + return 0; + if (pci_sys->read (0, dev, 0, PCI_VENDOR_ID, &vendor, sizeof (vendor))) + continue; + if (vendor == PCI_VENDOR_ID_INTEL || class == PCI_VENDOR_ID_COMPAQ) + return 0; + } + + return ENODEV; +} + +/* Find out which conf access method use */ +static error_t +pci_probe (struct pci_system *pci_sys) +{ + if (pci_system_x86_conf1_probe () == 0) + { + pci_sys->read = pci_system_x86_conf1_read; + pci_sys->write = pci_system_x86_conf1_write; + if (pci_system_x86_check (pci_sys) == 0) + return 0; + } + + if (pci_system_x86_conf2_probe () == 0) + { + pci_sys->read = pci_system_x86_conf2_read; + pci_sys->write = pci_system_x86_conf2_write; + if (pci_system_x86_check (pci_sys) == 0) + return 0; + } + + return ENODEV; +} + +static error_t +pci_nfuncs (struct pci_system *pci_sys, int bus, int dev, uint8_t * nfuncs) +{ + uint8_t hdrtype; + error_t err; + + err = pci_sys->read (bus, dev, 0, PCI_HDRTYPE, &hdrtype, sizeof (hdrtype)); + if (err) + return err; + + *nfuncs = hdrtype & 0x80 ? 8 : 1; + + return 0; +} + +/* Recursively scan bus number `bus' */ +static error_t +pci_system_x86_scan_bus (struct pci_system *pci_sys, uint8_t bus) +{ + error_t err; + uint8_t dev, func, nfuncs, hdrtype, secbus; + uint32_t reg; + struct pci_device *d, *devices; + + for (dev = 0; dev < 32; dev++) + { + err = pci_nfuncs (pci_sys, bus, dev, &nfuncs); + if (err) + return err; + + for (func = 0; func < nfuncs; func++) + { + err = + pci_sys->read (bus, dev, func, PCI_VENDOR_ID, ®, sizeof (reg)); + if (err) + return err; + + if (PCI_VENDOR (reg) == PCI_VENDOR_INVALID || PCI_VENDOR (reg) == 0) + continue; + + err = pci_sys->read (bus, dev, func, PCI_CLASS, ®, sizeof (reg)); + if (err) + return err; + + err = + pci_sys->read (bus, dev, func, PCI_HDRTYPE, &hdrtype, + sizeof (hdrtype)); + if (err) + return err; + + devices = + realloc (pci_sys->devices, + (pci_sys->num_devices + 1) * sizeof (struct pci_device)); + if (!devices) + return ENOMEM; + + d = devices + pci_sys->num_devices; + memset (d, 0, sizeof (struct pci_device)); + + /* Fixed values as PCI express is still not supported */ + d->domain = 0; + d->config_size = PCI_CONFIG_SIZE; + + d->bus = bus; + d->dev = dev; + d->func = func; + + d->device_class = reg >> 8; + + err = pci_device_x86_probe (d); + if (err) + return err; + + pci_sys->devices = devices; + pci_sys->num_devices++; + + switch (hdrtype & 0x3) + { + case PCI_HDRTYPE_DEVICE: + break; + case PCI_HDRTYPE_BRIDGE: + case PCI_HDRTYPE_CARDBUS: + { + err = + pci_sys->read (bus, dev, func, PCI_SECONDARY_BUS, &secbus, + sizeof (secbus)); + if (err) + return err; + + err = pci_system_x86_scan_bus (pci_sys, secbus); + if (err) + return err; + + break; + } + default: + /* Unknown header, do nothing */ + break; + } + } + } + + return 0; +} + +/* Initialize the x86 module */ +error_t +pci_system_x86_create (void) +{ + error_t err; + + err = x86_enable_io (); + if (err) + return err; + + pci_sys = calloc (1, sizeof (struct pci_system)); + if (pci_sys == NULL) + { + x86_disable_io (); + return ENOMEM; + } + + err = pci_probe (pci_sys); + if (err) + { + x86_disable_io (); + free (pci_sys); + return err; + } + pci_sys->device_refresh = pci_device_x86_refresh; + + /* Recursive scan */ + pci_sys->num_devices = 0; + err = pci_system_x86_scan_bus (pci_sys, 0); + if (err) + { + x86_disable_io (); + free (pci_sys); + pci_sys = NULL; + return err; + } + + return 0; +} diff --git a/pci-arbiter/x86_pci.h b/pci-arbiter/x86_pci.h new file mode 100644 index 00000000..fb2374a6 --- /dev/null +++ b/pci-arbiter/x86_pci.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2009, 2012 Samuel Thibault + * Heavily inspired from the freebsd, netbsd, and openbsd backends + * (C) Copyright Eric Anholt 2006 + * (C) Copyright IBM Corporation 2006 + * Copyright (c) 2008 Juan Romero Pardines + * Copyright (c) 2008 Mark Kettenis + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* + * PCI backend header for x86 (32 and 64 bit) architectures. + * + * Following code is borrowed from libpciaccess: + * https://cgit.freedesktop.org/xorg/lib/libpciaccess/ + */ + +#ifndef X86_PCI_H +#define X86_PCI_H + +int pci_system_x86_create (void); + +#endif /* X86_PCI_H */ |