Commit 048fe061 authored by Damien Zammit's avatar Damien Zammit Committed by Adam Jackson
Browse files

New module for the Hurd



This new module uses Hurd's RPCs for accessing the PCI configuration space.
Direct access as in {read_write}_{8,16,32} functions is done by the old x86
module.

Some x86 function prototypes are now declared in a new header for the Hurd
module to use them, in order to duplicate as little code as possible.

Author:  Joan Lledó <joanlluislledo@gmail.com>
Also-by: Damien Zammit's avatarDamien Zammit <damien@zamaudio.com>
Signed-off-by: Damien Zammit's avatarDamien Zammit <damien@zamaudio.com>
parent 6e383728
......@@ -52,12 +52,12 @@ VGA_ARBITER = common_vgaarb_stub.c
endif
if GNU
OS_SUPPORT = x86_pci.c
OS_SUPPORT = hurd_pci.c x86_pci.c x86_pci.h
VGA_ARBITER = common_vgaarb_stub.c
endif
if CYGWIN
OS_SUPPORT = x86_pci.c
OS_SUPPORT = x86_pci.c x86_pci.h
VGA_ARBITER = common_vgaarb_stub.c
endif
......
......@@ -65,7 +65,9 @@ pci_system_init( void )
err = pci_system_openbsd_create();
#elif defined(__sun)
err = pci_system_solx_devfs_create();
#elif defined(__GNU__) || defined(__CYGWIN__)
#elif defined(__GNU__)
err = pci_system_hurd_create();
#elif defined(__CYGWIN__)
err = pci_system_x86_create();
#else
# error "Unsupported OS"
......
/*
* Copyright (c) 2018, Damien Zammit
* Copyright (c) 2017, Joan Lledó
* 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.
*/
#define _GNU_SOURCE
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
#include <sys/mman.h>
#include <string.h>
#include <strings.h>
#include <hurd.h>
#include <hurd/pci.h>
#include <hurd/paths.h>
#include "x86_pci.h"
#include "pciaccess.h"
#include "pciaccess_private.h"
/*
* Hurd PCI access using RPCs.
*
* Some functions are shared with the x86 module to avoid repeating code.
*/
/* Server path */
#define _SERVERS_BUS_PCI _SERVERS_BUS "/pci"
/* File names */
#define FILE_CONFIG_NAME "config"
#define FILE_ROM_NAME "rom"
/* Level in the fs tree */
typedef enum {
LEVEL_NONE,
LEVEL_DOMAIN,
LEVEL_BUS,
LEVEL_DEV,
LEVEL_FUNC
} tree_level;
struct pci_system_hurd {
struct pci_system system;
};
static int
pci_device_hurd_probe(struct pci_device *dev)
{
uint8_t irq;
int err, i;
struct pci_bar regions[6];
struct pci_xrom_bar rom;
struct pci_device_private *d;
size_t size;
char *buf;
/* Many of the fields were filled in during initial device enumeration.
* At this point, we need to fill in regions, rom_size, and irq.
*/
err = pci_device_cfg_read_u8(dev, &irq, PCI_IRQ);
if (err)
return err;
dev->irq = irq;
/* Get regions */
buf = (char *)&regions;
size = sizeof(regions);
d = (struct pci_device_private *)dev;
err = pci_get_dev_regions(d->device_port, &buf, &size);
if(err)
return err;
if((char*)&regions != buf)
{
/* Sanity check for bogus server. */
if(size > sizeof(regions))
{
vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
return EGRATUITOUS;
}
memcpy(&regions, buf, size);
vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
}
for(i=0; i<6; i++)
{
if(regions[i].size == 0)
continue;
dev->regions[i].base_addr = regions[i].base_addr;
dev->regions[i].size = regions[i].size;
dev->regions[i].is_IO = regions[i].is_IO;
dev->regions[i].is_prefetchable = regions[i].is_prefetchable;
dev->regions[i].is_64 = regions[i].is_64;
}
/* Get rom info */
buf = (char *)&rom;
size = sizeof(rom);
err = pci_get_dev_rom(d->device_port, &buf, &size);
if(err)
return err;
if((char*)&rom != buf)
{
/* Sanity check for bogus server. */
if(size > sizeof(rom))
{
vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
return EGRATUITOUS;
}
memcpy(&rom, buf, size);
vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
}
d->rom_base = rom.base_addr;
dev->rom_size = rom.size;
return 0;
}
/*
* Read `nbytes' bytes from `reg' in device's configuretion space
* and store them in `buf'.
*
* It's assumed that `nbytes' bytes are allocated in `buf'
*/
static int
pciclient_cfg_read(mach_port_t device_port, int reg, char *buf,
size_t * nbytes)
{
int err;
size_t nread;
char *data;
data = buf;
nread = *nbytes;
err = pci_conf_read(device_port, reg, &data, &nread, *nbytes);
if (err)
return err;
if (data != buf) {
if (nread > *nbytes) /* Sanity check for bogus server. */ {
vm_deallocate(mach_task_self(), (vm_address_t) data, nread);
return EGRATUITOUS;
}
memcpy(buf, data, nread);
vm_deallocate(mach_task_self(), (vm_address_t)data, nread);
}
*nbytes = nread;
return 0;
}
/* Write `nbytes' bytes from `buf' to `reg' in device's configuration space */
static int
pciclient_cfg_write(mach_port_t device_port, int reg, char *buf,
size_t * nbytes)
{
int err;
size_t nwrote;
err = pci_conf_write(device_port, reg, buf, *nbytes, &nwrote);
if (!err)
*nbytes = nwrote;
return err;
}
/*
* Read up to `size' bytes from `dev' configuration space to `data' starting
* at `offset'. Write the amount on read bytes in `bytes_read'.
*/
static int
pci_device_hurd_read(struct pci_device *dev, void *data,
pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read)
{
int err;
struct pci_device_private *d;
*bytes_read = 0;
d = (struct pci_device_private *)dev;
while (size > 0) {
size_t toread = 1 << (ffs(0x4 + (offset & 0x03)) - 1);
if (toread > size)
toread = size;
err = pciclient_cfg_read(d->device_port, offset, (char*)data,
&toread);
if (err)
return err;
offset += toread;
data = (char*)data + toread;
size -= toread;
*bytes_read += toread;
}
return 0;
}
/*
* Write up to `size' bytes from `data' to `dev' configuration space starting
* at `offset'. Write the amount on written bytes in `bytes_written'.
*/
static int
pci_device_hurd_write(struct pci_device *dev, const void *data,
pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written)
{
int err;
struct pci_device_private *d;
*bytes_written = 0;
d = (struct pci_device_private *)dev;
while (size > 0) {
size_t towrite = 4;
if (towrite > size)
towrite = size;
if (towrite > 4 - (offset & 0x3))
towrite = 4 - (offset & 0x3);
err = pciclient_cfg_write(d->device_port, offset, (char*)data,
&towrite);
if (err)
return err;
offset += towrite;
data = (const char*)data + towrite;
size -= towrite;
*bytes_written += towrite;
}
return 0;
}
/*
* Copy the device's firmware in `buffer'
*/
static int
pci_device_hurd_read_rom(struct pci_device * dev, void * buffer)
{
ssize_t rd;
int romfd;
char server[NAME_MAX];
snprintf(server, NAME_MAX, "%s/%04x/%02x/%02x/%01u/%s", _SERVERS_BUS_PCI,
dev->domain, dev->bus, dev->dev, dev->func, FILE_ROM_NAME);
romfd = open(server, O_RDONLY | O_CLOEXEC);
if (romfd == -1)
return errno;
rd = read(romfd, buffer, dev->rom_size);
if (rd != dev->rom_size) {
close(romfd);
return errno;
}
close(romfd);
return 0;
}
/*
* Each device has its own server where send RPC's to.
*
* Deallocate the port before destroying the device.
*/
static void
pci_device_hurd_destroy(struct pci_device *dev)
{
struct pci_device_private *d = (struct pci_device_private*) dev;
mach_port_deallocate (mach_task_self (), d->device_port);
}
/* Walk through the FS tree to see what is allowed for us */
static int
enum_devices(const char *parent, struct pci_device_private **device,
int domain, int bus, int dev, int func, tree_level lev)
{
int err, ret;
DIR *dir;
struct dirent *entry;
char path[NAME_MAX];
char server[NAME_MAX];
uint32_t reg;
size_t toread;
mach_port_t device_port;
dir = opendir(parent);
if (!dir)
return errno;
while ((entry = readdir(dir)) != 0) {
snprintf(path, NAME_MAX, "%s/%s", parent, entry->d_name);
if (entry->d_type == DT_DIR) {
if (!strncmp(entry->d_name, ".", NAME_MAX)
|| !strncmp(entry->d_name, "..", NAME_MAX))
continue;
errno = 0;
ret = strtol(entry->d_name, 0, 16);
if (errno)
return errno;
/*
* We found a valid directory.
* Update the address and switch to the next level.
*/
switch (lev) {
case LEVEL_DOMAIN:
domain = ret;
break;
case LEVEL_BUS:
bus = ret;
break;
case LEVEL_DEV:
dev = ret;
break;
case LEVEL_FUNC:
func = ret;
break;
default:
return -1;
}
err = enum_devices(path, device, domain, bus, dev, func, lev+1);
if (err == EPERM)
continue;
}
else {
if (strncmp(entry->d_name, FILE_CONFIG_NAME, NAME_MAX))
/* We are looking for the config file */
continue;
/* We found an available virtual device, add it to our list */
snprintf(server, NAME_MAX, "%s/%04x/%02x/%02x/%01u/%s",
_SERVERS_BUS_PCI, domain, bus, dev, func,
entry->d_name);
device_port = file_name_lookup(server, 0, 0);
if (device_port == MACH_PORT_NULL)
return errno;
toread = sizeof(reg);
err = pciclient_cfg_read(device_port, PCI_VENDOR_ID, (char*)&reg,
&toread);
if (err)
return err;
if (toread != sizeof(reg))
return -1;
(*device)->base.domain = domain;
(*device)->base.bus = bus;
(*device)->base.dev = dev;
(*device)->base.func = func;
(*device)->base.vendor_id = PCI_VENDOR(reg);
(*device)->base.device_id = PCI_DEVICE(reg);
toread = sizeof(reg);
err = pciclient_cfg_read(device_port, PCI_CLASS, (char*)&reg,
&toread);
if (err)
return err;
if (toread != sizeof(reg))
return -1;
(*device)->base.device_class = reg >> 8;
(*device)->base.revision = reg & 0xFF;
toread = sizeof(reg);
err = pciclient_cfg_read(device_port, PCI_SUB_VENDOR_ID,
(char*)&reg, &toread);
if (err)
return err;
if (toread != sizeof(reg))
return -1;
(*device)->base.subvendor_id = PCI_VENDOR(reg);
(*device)->base.subdevice_id = PCI_DEVICE(reg);
(*device)->device_port = device_port;
(*device)++;
}
}
return 0;
}
static const struct pci_system_methods hurd_pci_methods = {
.destroy = pci_system_x86_destroy,
.destroy_device = pci_device_hurd_destroy,
.read_rom = pci_device_hurd_read_rom,
.probe = pci_device_hurd_probe,
.map_range = pci_device_x86_map_range,
.unmap_range = pci_device_x86_unmap_range,
.read = pci_device_hurd_read,
.write = pci_device_hurd_write,
.fill_capabilities = pci_fill_capabilities_generic,
.open_legacy_io = pci_device_x86_open_legacy_io,
.close_io = pci_device_x86_close_io,
.read32 = pci_device_x86_read32,
.read16 = pci_device_x86_read16,
.read8 = pci_device_x86_read8,
.write32 = pci_device_x86_write32,
.write16 = pci_device_x86_write16,
.write8 = pci_device_x86_write8,
.map_legacy = pci_device_x86_map_legacy,
.unmap_legacy = pci_device_x86_unmap_legacy,
};
_pci_hidden int
pci_system_hurd_create(void)
{
struct pci_device_private *device;
int err;
struct pci_system_hurd *pci_sys_hurd;
size_t ndevs;
mach_port_t pci_server_port;
/* If we can open pci cfg io ports on hurd,
* we are the arbiter, therefore try x86 method first */
err = pci_system_x86_create();
if (!err)
return 0;
pci_sys_hurd = calloc(1, sizeof(struct pci_system_hurd));
if (pci_sys_hurd == NULL) {
x86_disable_io();
return ENOMEM;
}
pci_sys = &pci_sys_hurd->system;
pci_sys->methods = &hurd_pci_methods;
pci_server_port = file_name_lookup(_SERVERS_BUS_PCI, 0, 0);
if (!pci_server_port) {
/* Fall back to x86 access method */
return pci_system_x86_create();
}
/* The server gives us the number of available devices for us */
err = pci_get_ndevs (pci_server_port, &ndevs);
if (err) {
mach_port_deallocate (mach_task_self (), pci_server_port);
/* Fall back to x86 access method */
return pci_system_x86_create();
}
mach_port_deallocate (mach_task_self (), pci_server_port);
pci_sys->num_devices = ndevs;
pci_sys->devices = calloc(ndevs, sizeof(struct pci_device_private));
if (pci_sys->devices == NULL) {
x86_disable_io();
free(pci_sys_hurd);
pci_sys = NULL;
return ENOMEM;
}
device = pci_sys->devices;
err = enum_devices(_SERVERS_BUS_PCI, &device, -1, -1, -1, -1,
LEVEL_DOMAIN);
if (err)
return pci_system_x86_create();
return 0;
}
......@@ -29,6 +29,9 @@
* \author Ian Romanick <idr@us.ibm.com>
*/
#ifndef PCIACCESS_PRIVATE_H
#define PCIACCESS_PRIVATE_H
#if defined(__GNUC__) && (__GNUC__ >= 4)
# define _pci_hidden __attribute__((visibility("hidden")))
#elif defined(__SUNPRO_C) && (__SUNPRO_C >= 0x550)
......@@ -150,6 +153,9 @@ struct pci_device_private {
#ifdef __sun
int is_primary;
#endif
#ifdef __GNU__
unsigned long device_port;
#endif
};
......@@ -190,5 +196,8 @@ extern int pci_system_netbsd_create( void );
extern int pci_system_openbsd_create( void );
extern void pci_system_openbsd_init_dev_mem( int );
extern int pci_system_solx_devfs_create( void );
extern int pci_system_hurd_create( void );
extern int pci_system_x86_create( void );
extern void pci_io_cleanup( void );
#endif /* PCIACCESS_PRIVATE_H */
/*
* Copyright (c) 2018 Damien Zammit
* Copyright (c) 2017 Joan Lledó
* Copyright (c) 2009, 2012 Samuel Thibault
* Heavily inspired from the freebsd, netbsd, and openbsd backends
* (C) Copyright Eric Anholt 2006
......@@ -22,6 +24,8 @@
#include "config.h"
#endif
#include "x86_pci.h"
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
......@@ -38,7 +42,7 @@
#include <sys/io.h>
static int
int
x86_enable_io(void)
{
if (!ioperm(0, 0xffff, 1))
......@@ -46,7 +50,7 @@ x86_enable_io(void)
return errno;
}
static int
int
x86_disable_io(void)
{
if (!ioperm(0, 0xffff, 0))
......@@ -207,34 +211,6 @@ outl(uint32_t value, uint16_t port)
#endif
#define PCI_VENDOR(reg) ((reg) & 0xFFFF)
#define PCI_VENDOR_INVALID 0xFFFF
#define PCI_VENDOR_ID 0x00
#define PCI_SUB_VENDOR_ID 0x2c
#define PCI_VENDOR_ID_COMPAQ 0x0e11
#define PCI_VENDOR_ID_INTEL 0x8086
#define PCI_DEVICE(reg) (((reg) >> 16) & 0xFFFF)
#define PCI_DEVICE_INVALID 0xFFFF
#define PCI_CLASS 0x08
#define PCI_CLASS_DEVICE 0x0a
#define PCI_CLASS_DISPLAY_VGA 0x0300
#define PCI_CLASS_BRIDGE_HOST 0x0600
#define PCIC_DISPLAY 0x03
#define PCIS_DISPLAY_VGA 0x00
#define PCI_HDRTYPE 0x0E
#define PCI_IRQ 0x3C
struct pci_system_x86 {
struct pci_system system;
int (*read)(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, void *data, unsigned size);
int (*write)(unsigned bus, unsigned dev, unsigned func, pciaddr_t reg, const void *data, unsigned size);
};
static int
pci_system_x86_conf1_probe(void)
{
......@@ -408,62 +384,68 @@ pci_system_x86_conf2_write(unsigned bus, unsigned dev, unsigned func, pciaddr_t
}
/* Check that this really looks like a PCI configuration. */
static int
pci_system_x86_check(struct pci_system_x86 *pci_sys_x86)
static error_t
pci_system_x86_check (void)
{
int dev;
uint16_t class, vendor;
struct pci_device tmpdev = { 0 };
/* Look on bus 0 for a device that is a host bridge, a VGA card,
* or an intel or compaq device. */
tmpdev.bus = 0;