]> mj.ucw.cz Git - pciutils.git/blob - lib/hurd.c
Merge remote-tracking branch 'jlledom/hurd-fix-dev-aux'
[pciutils.git] / lib / hurd.c
1 /*
2  *      The PCI Library -- Hurd access via RPCs
3  *
4  *      Copyright (c) 2017 Joan Lledó <jlledom@member.fsf.org>
5  *
6  *      Can be freely distributed and used under the terms of the GNU GPL.
7  */
8
9 #define _GNU_SOURCE
10
11 #include "internal.h"
12
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 #include <unistd.h>
18 #include <dirent.h>
19 #include <fcntl.h>
20 #include <string.h>
21 #include <hurd.h>
22 #include <hurd/pci.h>
23 #include <hurd/paths.h>
24
25 /* Server path */
26 #define _SERVERS_BUS_PCI        _SERVERS_BUS "/pci"
27
28 /* File names */
29 #define FILE_CONFIG_NAME "config"
30 #define FILE_ROM_NAME "rom"
31
32 /* Level in the fs tree */
33 typedef enum
34 {
35   LEVEL_NONE,
36   LEVEL_DOMAIN,
37   LEVEL_BUS,
38   LEVEL_DEV,
39   LEVEL_FUNC
40 } tree_level;
41
42 /* Check whether there's a pci server */
43 static int
44 hurd_detect(struct pci_access *a)
45 {
46   int err;
47   struct stat st;
48
49   err = stat(_SERVERS_BUS_PCI, &st);
50   if (err)
51     {
52       a->error("Could not open file `%s'", _SERVERS_BUS_PCI);
53       return 0;
54     }
55
56   /* The node must be a directory and a translator */
57   return S_ISDIR(st.st_mode) && ((st.st_mode & S_ITRANS) == S_IROOT);
58 }
59
60 /* Empty callbacks, we don't need any special init or cleanup */
61 static void
62 hurd_init(struct pci_access *a UNUSED)
63 {
64 }
65
66 static void
67 hurd_cleanup(struct pci_access *a UNUSED)
68 {
69 }
70
71 /* Each device has its own server path. Allocate space for the port. */
72 static void
73 hurd_init_dev(struct pci_dev *d)
74 {
75   d->aux = pci_malloc(d->access, sizeof(mach_port_t));
76   *((mach_port_t *) d->aux) = MACH_PORT_NULL;
77 }
78
79 /* Deallocate the port and free its space */
80 static void
81 hurd_cleanup_dev(struct pci_dev *d)
82 {
83   mach_port_t device_port;
84
85   device_port = *((mach_port_t *) d->aux);
86   mach_port_deallocate(mach_task_self(), device_port);
87
88   pci_mfree(d->aux);
89 }
90
91 static int
92 device_port_lookup(struct pci_dev *d)
93 {
94   mach_port_t device_port;
95   char server[NAME_MAX];
96
97   snprintf(server, NAME_MAX, "%s/%04x/%02x/%02x/%01u/%s",
98      _SERVERS_BUS_PCI, d->domain, d->bus, d->dev, d->func,
99      FILE_CONFIG_NAME);
100   device_port = file_name_lookup(server, 0, 0);
101
102   *((mach_port_t *) d->aux) = device_port;
103
104   return d->aux != MACH_PORT_NULL;
105 }
106
107 /* Walk through the FS tree to see what is allowed for us */
108 static void
109 enum_devices(const char *parent, struct pci_access *a, int domain, int bus,
110              int dev, int func, tree_level lev)
111 {
112   int ret;
113   DIR *dir;
114   struct dirent *entry;
115   char path[NAME_MAX];
116   uint32_t vd;
117   uint8_t ht;
118   struct pci_dev *d;
119
120   dir = opendir(parent);
121   if (!dir)
122     {
123       if (errno == EPERM || errno == EACCES)
124         /* The client lacks the permissions to access this function, skip */
125         return;
126       else
127         a->error("Cannot open directory: %s (%s)", parent, strerror(errno));
128     }
129
130   while ((entry = readdir(dir)) != 0)
131     {
132       snprintf(path, NAME_MAX, "%s/%s", parent, entry->d_name);
133       if (entry->d_type == DT_DIR)
134         {
135           if (!strncmp(entry->d_name, ".", NAME_MAX)
136               || !strncmp(entry->d_name, "..", NAME_MAX))
137             continue;
138
139           errno = 0;
140           ret = strtol(entry->d_name, 0, 16);
141           if (errno)
142             {
143               if (closedir(dir) < 0)
144                 a->warning("Cannot close directory: %s (%s)", parent,
145                            strerror(errno));
146               a->error("Wrong directory name: %s (number expected) probably "
147                        "not connected to an arbiter", entry->d_name);
148             }
149
150           /*
151            * We found a valid directory.
152            * Update the address and switch to the next level.
153            */
154           switch (lev)
155             {
156             case LEVEL_DOMAIN:
157               domain = ret;
158               break;
159             case LEVEL_BUS:
160               bus = ret;
161               break;
162             case LEVEL_DEV:
163               dev = ret;
164               break;
165             case LEVEL_FUNC:
166               func = ret;
167               break;
168             default:
169               if (closedir(dir) < 0)
170                 a->warning("Cannot close directory: %s (%s)", parent,
171                            strerror(errno));
172               a->error("Wrong directory tree, probably not connected to an arbiter");
173             }
174
175           enum_devices(path, a, domain, bus, dev, func, lev + 1);
176         }
177       else
178         {
179           if (strncmp(entry->d_name, FILE_CONFIG_NAME, NAME_MAX))
180             /* We are looking for the config file */
181             continue;
182
183           /* We found an available virtual device, add it to our list */
184           d = pci_alloc_dev(a);
185           d->domain = domain;
186           d->bus = bus;
187           d->dev = dev;
188           d->func = func;
189
190           /* Get the arbiter port */
191           if (!device_port_lookup(d))
192             {
193               if (closedir(dir) < 0)
194                 a->warning("Cannot close directory: %s (%s)", parent,
195                            strerror(errno));
196               a->error("Cannot find the PCI arbiter");
197             }
198
199           pci_link_dev(a, d);
200
201           vd = pci_read_long(d, PCI_VENDOR_ID);
202           ht = pci_read_byte(d, PCI_HEADER_TYPE);
203
204           d->vendor_id = vd & 0xffff;
205           d->device_id = vd >> 16U;
206           d->known_fields = PCI_FILL_IDENT;
207           d->hdrtype = ht;
208         }
209     }
210
211   if (closedir(dir) < 0)
212     a->error("Cannot close directory: %s (%s)", parent, strerror(errno));
213 }
214
215 /* Enumerate devices */
216 static void
217 hurd_scan(struct pci_access *a)
218 {
219   enum_devices(_SERVERS_BUS_PCI, a, -1, -1, -1, -1, LEVEL_DOMAIN);
220 }
221
222 /*
223  * Read `len' bytes to `buf'.
224  *
225  * Returns error when the number of read bytes does not match `len'.
226  */
227 static int
228 hurd_read(struct pci_dev *d, int pos, byte * buf, int len)
229 {
230   int err;
231   size_t nread;
232   char *data;
233   mach_port_t device_port;
234
235   nread = len;
236   if (*((mach_port_t *) d->aux) == MACH_PORT_NULL)
237     {
238       /* We still don't have the port for this device */
239       if (device_port_lookup(d))
240         {
241           d->access->error("Cannot find the PCI arbiter");
242         }
243     }
244   device_port = *((mach_port_t *) d->aux);
245
246   if (len > 4)
247     err = !pci_generic_block_read(d, pos, buf, nread);
248   else
249     {
250       data = (char *) buf;
251       err = pci_conf_read(device_port, pos, &data, &nread, len);
252
253       if (data != (char *) buf)
254         {
255           if (nread > (size_t) len)     /* Sanity check for bogus server.  */
256             {
257               vm_deallocate(mach_task_self(), (vm_address_t) data, nread);
258               return 0;
259             }
260
261           memcpy(buf, data, nread);
262           vm_deallocate(mach_task_self(), (vm_address_t) data, nread);
263         }
264     }
265   if (err)
266     return 0;
267
268   return nread == (size_t) len;
269 }
270
271 /*
272  * Write `len' bytes from `buf'.
273  *
274  * Returns error when the number of written bytes does not match `len'.
275  */
276 static int
277 hurd_write(struct pci_dev *d, int pos, byte * buf, int len)
278 {
279   int err;
280   size_t nwrote;
281   mach_port_t device_port;
282
283   nwrote = len;
284   if (*((mach_port_t *) d->aux) == MACH_PORT_NULL)
285     {
286       /* We still don't have the port for this device */
287       if (device_port_lookup(d))
288         {
289           d->access->error("Cannot find the PCI arbiter");
290         }
291     }
292   device_port = *((mach_port_t *) d->aux);
293
294   if (len > 4)
295     err = !pci_generic_block_write(d, pos, buf, len);
296   else
297     err = pci_conf_write(device_port, pos, (char *) buf, len, &nwrote);
298   if (err)
299     return 0;
300
301   return nwrote == (size_t) len;
302 }
303
304 /* Get requested info from the server */
305
306 static void
307 hurd_fill_regions(struct pci_dev *d)
308 {
309   mach_port_t device_port = *((mach_port_t *) d->aux);
310   struct pci_bar regions[6];
311   char *buf = (char *) &regions;
312   size_t size = sizeof(regions);
313
314   int err = pci_get_dev_regions(device_port, &buf, &size);
315   if (err)
316     return;
317
318   if ((char *) &regions != buf)
319     {
320       /* Sanity check for bogus server.  */
321       if (size > sizeof(regions))
322         {
323           vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
324           return;
325         }
326
327       memcpy(&regions, buf, size);
328       vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
329     }
330
331   for (int i = 0; i < 6; i++)
332     {
333       if (regions[i].size == 0)
334         continue;
335
336       d->base_addr[i] = regions[i].base_addr;
337       d->base_addr[i] |= regions[i].is_IO;
338       d->base_addr[i] |= regions[i].is_64 << 2;
339       d->base_addr[i] |= regions[i].is_prefetchable << 3;
340
341       d->size[i] = regions[i].size;
342     }
343 }
344
345 static void
346 hurd_fill_rom(struct pci_dev *d)
347 {
348   struct pci_xrom_bar rom;
349   mach_port_t device_port = *((mach_port_t *) d->aux);
350   char *buf = (char *) &rom;
351   size_t size = sizeof(rom);
352
353   int err = pci_get_dev_rom(device_port, &buf, &size);
354   if (err)
355     return;
356
357   if ((char *) &rom != buf)
358     {
359       /* Sanity check for bogus server.  */
360       if (size > sizeof(rom))
361         {
362           vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
363           return;
364         }
365
366       memcpy(&rom, buf, size);
367       vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
368     }
369
370   d->rom_base_addr = rom.base_addr;
371   d->rom_size = rom.size;
372 }
373
374 static unsigned int
375 hurd_fill_info(struct pci_dev *d, unsigned int flags)
376 {
377   unsigned int done = 0;
378
379   if (!d->access->buscentric)
380     {
381       if (flags & (PCI_FILL_BASES | PCI_FILL_SIZES))
382         {
383           hurd_fill_regions(d);
384           done |= PCI_FILL_BASES | PCI_FILL_SIZES;
385         }
386       if (flags & PCI_FILL_ROM_BASE)
387         {
388           hurd_fill_rom(d);
389           done |= PCI_FILL_ROM_BASE;
390         }
391     }
392
393   return done | pci_generic_fill_info(d, flags & ~done);
394 }
395
396 struct pci_methods pm_hurd = {
397   "hurd",
398   "Hurd access using RPCs",
399   NULL,                         /* config */
400   hurd_detect,
401   hurd_init,
402   hurd_cleanup,
403   hurd_scan,
404   hurd_fill_info,
405   hurd_read,
406   hurd_write,
407   NULL,                         /* read_vpd */
408   hurd_init_dev,
409   hurd_cleanup_dev
410 };