From a830bef66e689f6f1defa9988f257a7ec2bb034d Mon Sep 17 00:00:00 2001 From: Joan Lledó Date: Tue, 16 Jan 2018 12:54:28 +0100 Subject: PCI Arbiter --- pci-arbiter/pci-ops.c | 273 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 273 insertions(+) create mode 100644 pci-arbiter/pci-ops.c (limited to 'pci-arbiter/pci-ops.c') 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 . +*/ + +/* Implementation of PCI operations */ + +#include + +#include +#include +#include + +#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; +} -- cgit v1.2.3