hurd_pci.c revision 2029f493
1/*
2 * Copyright (c) 2018, Damien Zammit
3 * Copyright (c) 2017, Joan Lledó
4 * Copyright (c) 2009, 2012 Samuel Thibault
5 * Heavily inspired from the freebsd, netbsd, and openbsd backends
6 * (C) Copyright Eric Anholt 2006
7 * (C) Copyright IBM Corporation 2006
8 * Copyright (c) 2008 Juan Romero Pardines
9 * Copyright (c) 2008 Mark Kettenis
10 *
11 * Permission to use, copy, modify, and distribute this software for any
12 * purpose with or without fee is hereby granted, provided that the above
13 * copyright notice and this permission notice appear in all copies.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
16 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
17 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
18 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
20 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
21 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22 */
23
24#define _GNU_SOURCE
25#include <unistd.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <errno.h>
29#include <sys/types.h>
30#include <sys/stat.h>
31#include <fcntl.h>
32#include <dirent.h>
33#include <sys/mman.h>
34#include <string.h>
35#include <strings.h>
36#include <hurd.h>
37#include <hurd/pci.h>
38#include <hurd/paths.h>
39
40#include "x86_pci.h"
41#include "pciaccess.h"
42#include "pciaccess_private.h"
43
44/*
45 * Hurd PCI access using RPCs.
46 *
47 * Some functions are shared with the x86 module to avoid repeating code.
48 */
49
50/* Server path */
51#define _SERVERS_BUS_PCI	_SERVERS_BUS "/pci"
52
53/* File names */
54#define FILE_CONFIG_NAME "config"
55#define FILE_ROM_NAME "rom"
56
57/* Level in the fs tree */
58typedef enum {
59    LEVEL_NONE,
60    LEVEL_DOMAIN,
61    LEVEL_BUS,
62    LEVEL_DEV,
63    LEVEL_FUNC
64} tree_level;
65
66struct pci_system_hurd {
67    struct pci_system system;
68};
69
70static int
71pci_device_hurd_probe(struct pci_device *dev)
72{
73    uint8_t irq;
74    int err, i;
75    struct pci_bar regions[6];
76    struct pci_xrom_bar rom;
77    struct pci_device_private *d;
78    size_t size;
79    char *buf;
80
81    /* Many of the fields were filled in during initial device enumeration.
82     * At this point, we need to fill in regions, rom_size, and irq.
83     */
84
85    err = pci_device_cfg_read_u8(dev, &irq, PCI_IRQ);
86    if (err)
87        return err;
88    dev->irq = irq;
89
90    /* Get regions */
91    buf = (char *)&regions;
92    size = sizeof(regions);
93    d = (struct pci_device_private *)dev;
94    err = pci_get_dev_regions(d->device_port, &buf, &size);
95    if(err)
96        return err;
97
98    if((char*)&regions != buf)
99    {
100        /* Sanity check for bogus server.  */
101        if(size > sizeof(regions))
102        {
103            vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
104            return EGRATUITOUS;
105        }
106
107        memcpy(&regions, buf, size);
108        vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
109    }
110
111    for(i=0; i<6; i++)
112    {
113        if(regions[i].size == 0)
114            continue;
115
116        dev->regions[i].base_addr = regions[i].base_addr;
117        dev->regions[i].size = regions[i].size;
118        dev->regions[i].is_IO = regions[i].is_IO;
119        dev->regions[i].is_prefetchable = regions[i].is_prefetchable;
120        dev->regions[i].is_64 = regions[i].is_64;
121    }
122
123    /* Get rom info */
124    buf = (char *)&rom;
125    size = sizeof(rom);
126    err = pci_get_dev_rom(d->device_port, &buf, &size);
127    if(err)
128        return err;
129
130    if((char*)&rom != buf)
131    {
132        /* Sanity check for bogus server.  */
133        if(size > sizeof(rom))
134        {
135            vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
136            return EGRATUITOUS;
137        }
138
139        memcpy(&rom, buf, size);
140        vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
141    }
142
143    d->rom_base = rom.base_addr;
144    dev->rom_size = rom.size;
145
146    return 0;
147}
148
149/*
150 * Read `nbytes' bytes from `reg' in device's configuretion space
151 * and store them in `buf'.
152 *
153 * It's assumed that `nbytes' bytes are allocated in `buf'
154 */
155static int
156pciclient_cfg_read(mach_port_t device_port, int reg, char *buf,
157                   size_t * nbytes)
158{
159    int err;
160    size_t nread;
161    char *data;
162
163    data = buf;
164    nread = *nbytes;
165    err = pci_conf_read(device_port, reg, &data, &nread, *nbytes);
166    if (err)
167        return err;
168
169    if (data != buf) {
170        if (nread > *nbytes)	/* Sanity check for bogus server.  */ {
171                vm_deallocate(mach_task_self(), (vm_address_t) data, nread);
172                return EGRATUITOUS;
173        }
174
175        memcpy(buf, data, nread);
176        vm_deallocate(mach_task_self(), (vm_address_t)data, nread);
177    }
178
179    *nbytes = nread;
180
181    return 0;
182}
183
184/* Write `nbytes' bytes from `buf' to `reg' in device's configuration space */
185static int
186pciclient_cfg_write(mach_port_t device_port, int reg, char *buf,
187                    size_t * nbytes)
188{
189    int err;
190    size_t nwrote;
191
192    err = pci_conf_write(device_port, reg, buf, *nbytes, &nwrote);
193
194    if (!err)
195        *nbytes = nwrote;
196
197    return err;
198}
199
200/*
201 * Read up to `size' bytes from `dev' configuration space to `data' starting
202 * at `offset'. Write the amount on read bytes in `bytes_read'.
203 */
204static int
205pci_device_hurd_read(struct pci_device *dev, void *data,
206    pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_read)
207{
208    int err;
209    struct pci_device_private *d;
210
211    *bytes_read = 0;
212    d = (struct pci_device_private *)dev;
213    while (size > 0) {
214        size_t toread = 1 << (ffs(0x4 + (offset & 0x03)) - 1);
215        if (toread > size)
216            toread = size;
217
218        err = pciclient_cfg_read(d->device_port, offset, (char*)data,
219                                 &toread);
220        if (err)
221            return err;
222
223        offset += toread;
224        data = (char*)data + toread;
225        size -= toread;
226        *bytes_read += toread;
227    }
228    return 0;
229}
230
231/*
232 * Write up to `size' bytes from `data' to `dev' configuration space starting
233 * at `offset'. Write the amount on written bytes in `bytes_written'.
234 */
235static int
236pci_device_hurd_write(struct pci_device *dev, const void *data,
237    pciaddr_t offset, pciaddr_t size, pciaddr_t *bytes_written)
238{
239    int err;
240    struct pci_device_private *d;
241
242    *bytes_written = 0;
243    d = (struct pci_device_private *)dev;
244    while (size > 0) {
245        size_t towrite = 4;
246        if (towrite > size)
247            towrite = size;
248        if (towrite > 4 - (offset & 0x3))
249            towrite = 4 - (offset & 0x3);
250
251        err = pciclient_cfg_write(d->device_port, offset, (char*)data,
252                                  &towrite);
253        if (err)
254            return err;
255
256        offset += towrite;
257        data = (const char*)data + towrite;
258        size -= towrite;
259        *bytes_written += towrite;
260    }
261    return 0;
262}
263
264/*
265 * Copy the device's firmware in `buffer'
266 */
267static int
268pci_device_hurd_read_rom(struct pci_device * dev, void * buffer)
269{
270    ssize_t rd;
271    int romfd;
272    char server[NAME_MAX];
273
274    snprintf(server, NAME_MAX, "%s/%04x/%02x/%02x/%01u/%s", _SERVERS_BUS_PCI,
275             dev->domain, dev->bus, dev->dev, dev->func, FILE_ROM_NAME);
276
277    romfd = open(server, O_RDONLY | O_CLOEXEC);
278    if (romfd == -1)
279        return errno;
280
281    rd = read(romfd, buffer, dev->rom_size);
282    if (rd != dev->rom_size) {
283        close(romfd);
284        return errno;
285    }
286
287    close(romfd);
288
289    return 0;
290}
291
292/*
293 * Each device has its own server where send RPC's to.
294 *
295 * Deallocate the port before destroying the device.
296 */
297static void
298pci_device_hurd_destroy(struct pci_device *dev)
299{
300    struct pci_device_private *d = (struct pci_device_private*) dev;
301
302    mach_port_deallocate (mach_task_self (), d->device_port);
303}
304
305/* Walk through the FS tree to see what is allowed for us */
306static int
307enum_devices(const char *parent, struct pci_device_private **device,
308                int domain, int bus, int dev, int func, tree_level lev)
309{
310    int err, ret;
311    DIR *dir;
312    struct dirent *entry;
313    char path[NAME_MAX];
314    char server[NAME_MAX];
315    uint32_t reg;
316    size_t toread;
317    mach_port_t device_port;
318
319    dir = opendir(parent);
320    if (!dir)
321        return errno;
322
323    while ((entry = readdir(dir)) != 0) {
324        snprintf(path, NAME_MAX, "%s/%s", parent, entry->d_name);
325        if (entry->d_type == DT_DIR) {
326            if (!strncmp(entry->d_name, ".", NAME_MAX)
327                || !strncmp(entry->d_name, "..", NAME_MAX))
328                continue;
329
330            errno = 0;
331            ret = strtol(entry->d_name, 0, 16);
332            if (errno)
333                return errno;
334
335            /*
336             * We found a valid directory.
337             * Update the address and switch to the next level.
338             */
339            switch (lev) {
340            case LEVEL_DOMAIN:
341                domain = ret;
342                break;
343            case LEVEL_BUS:
344                bus = ret;
345                break;
346            case LEVEL_DEV:
347                dev = ret;
348                break;
349            case LEVEL_FUNC:
350                func = ret;
351                break;
352            default:
353                return -1;
354            }
355
356            err = enum_devices(path, device, domain, bus, dev, func, lev+1);
357            if (err == EPERM)
358                continue;
359        }
360        else {
361            if (strncmp(entry->d_name, FILE_CONFIG_NAME, NAME_MAX))
362                /* We are looking for the config file */
363                continue;
364
365            /* We found an available virtual device, add it to our list */
366            snprintf(server, NAME_MAX, "%s/%04x/%02x/%02x/%01u/%s",
367                     _SERVERS_BUS_PCI, domain, bus, dev, func,
368                     entry->d_name);
369            device_port = file_name_lookup(server, 0, 0);
370            if (device_port == MACH_PORT_NULL)
371                return errno;
372
373            toread = sizeof(reg);
374            err = pciclient_cfg_read(device_port, PCI_VENDOR_ID, (char*)&reg,
375                                     &toread);
376            if (err)
377                return err;
378            if (toread != sizeof(reg))
379                return -1;
380
381            (*device)->base.domain = domain;
382            (*device)->base.bus = bus;
383            (*device)->base.dev = dev;
384            (*device)->base.func = func;
385            (*device)->base.vendor_id = PCI_VENDOR(reg);
386            (*device)->base.device_id = PCI_DEVICE(reg);
387
388            toread = sizeof(reg);
389            err = pciclient_cfg_read(device_port, PCI_CLASS, (char*)&reg,
390                                     &toread);
391            if (err)
392                return err;
393            if (toread != sizeof(reg))
394                return -1;
395
396            (*device)->base.device_class = reg >> 8;
397            (*device)->base.revision = reg & 0xFF;
398
399            toread = sizeof(reg);
400            err = pciclient_cfg_read(device_port, PCI_SUB_VENDOR_ID,
401                                     (char*)&reg, &toread);
402            if (err)
403                return err;
404            if (toread != sizeof(reg))
405                return -1;
406
407            (*device)->base.subvendor_id = PCI_VENDOR(reg);
408            (*device)->base.subdevice_id = PCI_DEVICE(reg);
409
410            (*device)->device_port = device_port;
411
412            (*device)++;
413        }
414    }
415
416    return 0;
417}
418
419static const struct pci_system_methods hurd_pci_methods = {
420    .destroy = pci_system_x86_destroy,
421    .destroy_device = pci_device_hurd_destroy,
422    .read_rom = pci_device_hurd_read_rom,
423    .probe = pci_device_hurd_probe,
424    .map_range = pci_device_x86_map_range,
425    .unmap_range = pci_device_x86_unmap_range,
426    .read = pci_device_hurd_read,
427    .write = pci_device_hurd_write,
428    .fill_capabilities = pci_fill_capabilities_generic,
429    .open_legacy_io = pci_device_x86_open_legacy_io,
430    .close_io = pci_device_x86_close_io,
431    .read32 = pci_device_x86_read32,
432    .read16 = pci_device_x86_read16,
433    .read8 = pci_device_x86_read8,
434    .write32 = pci_device_x86_write32,
435    .write16 = pci_device_x86_write16,
436    .write8 = pci_device_x86_write8,
437    .map_legacy = pci_device_x86_map_legacy,
438    .unmap_legacy = pci_device_x86_unmap_legacy,
439};
440
441_pci_hidden int
442pci_system_hurd_create(void)
443{
444    struct pci_device_private *device;
445    int err;
446    struct pci_system_hurd *pci_sys_hurd;
447    size_t ndevs;
448    mach_port_t pci_server_port;
449
450    /* If we can open pci cfg io ports on hurd,
451     * we are the arbiter, therefore try x86 method first */
452    err = pci_system_x86_create();
453    if (!err)
454        return 0;
455
456    pci_sys_hurd = calloc(1, sizeof(struct pci_system_hurd));
457    if (pci_sys_hurd == NULL) {
458        x86_disable_io();
459        return ENOMEM;
460    }
461    pci_sys = &pci_sys_hurd->system;
462
463    pci_sys->methods = &hurd_pci_methods;
464
465    pci_server_port = file_name_lookup(_SERVERS_BUS_PCI, 0, 0);
466    if (!pci_server_port) {
467        /* Fall back to x86 access method */
468        return pci_system_x86_create();
469    }
470
471    /* The server gives us the number of available devices for us */
472    err = pci_get_ndevs (pci_server_port, &ndevs);
473    if (err) {
474        mach_port_deallocate (mach_task_self (), pci_server_port);
475        /* Fall back to x86 access method */
476        return pci_system_x86_create();
477    }
478    mach_port_deallocate (mach_task_self (), pci_server_port);
479
480    pci_sys->num_devices = ndevs;
481    pci_sys->devices = calloc(ndevs, sizeof(struct pci_device_private));
482    if (pci_sys->devices == NULL) {
483        x86_disable_io();
484        free(pci_sys_hurd);
485        pci_sys = NULL;
486        return ENOMEM;
487    }
488
489    device = pci_sys->devices;
490    err = enum_devices(_SERVERS_BUS_PCI, &device, -1, -1, -1, -1,
491                       LEVEL_DOMAIN);
492    if (err)
493        return pci_system_x86_create();
494
495    return 0;
496}
497