]> mj.ucw.cz Git - pciutils.git/blob - lib/hurd.c
Hurd: Further simplification
[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 mach_port_t
92 device_port_lookup(struct pci_dev *d)
93 {
94   char server[NAME_MAX];
95   mach_port_t device_port = *((mach_port_t *) d->aux);
96
97   if (device_port != MACH_PORT_NULL)
98     return device_port;
99
100   snprintf(server, NAME_MAX, "%s/%04x/%02x/%02x/%01u/%s",
101     _SERVERS_BUS_PCI, d->domain, d->bus, d->dev, d->func,
102     FILE_CONFIG_NAME);
103   device_port = file_name_lookup(server, 0, 0);
104
105   *((mach_port_t *) d->aux) = device_port;
106   return device_port;
107 }
108
109 /* Walk through the FS tree to see what is allowed for us */
110 static void
111 enum_devices(const char *parent, struct pci_access *a, int domain, int bus,
112              int dev, int func, tree_level lev)
113 {
114   int ret;
115   DIR *dir;
116   struct dirent *entry;
117   char path[NAME_MAX];
118   uint32_t vd;
119   uint8_t ht;
120   struct pci_dev *d;
121
122   dir = opendir(parent);
123   if (!dir)
124     {
125       if (errno == EPERM || errno == EACCES)
126         /* The client lacks the permissions to access this function, skip */
127         return;
128       else
129         a->error("Cannot open directory: %s (%s)", parent, strerror(errno));
130     }
131
132   while ((entry = readdir(dir)) != 0)
133     {
134       snprintf(path, NAME_MAX, "%s/%s", parent, entry->d_name);
135       if (entry->d_type == DT_DIR)
136         {
137           if (!strncmp(entry->d_name, ".", NAME_MAX)
138               || !strncmp(entry->d_name, "..", NAME_MAX))
139             continue;
140
141           errno = 0;
142           ret = strtol(entry->d_name, 0, 16);
143           if (errno)
144             {
145               if (closedir(dir) < 0)
146                 a->warning("Cannot close directory: %s (%s)", parent,
147                            strerror(errno));
148               a->error("Wrong directory name: %s (number expected) probably "
149                        "not connected to an arbiter", entry->d_name);
150             }
151
152           /*
153            * We found a valid directory.
154            * Update the address and switch to the next level.
155            */
156           switch (lev)
157             {
158             case LEVEL_DOMAIN:
159               domain = ret;
160               break;
161             case LEVEL_BUS:
162               bus = ret;
163               break;
164             case LEVEL_DEV:
165               dev = ret;
166               break;
167             case LEVEL_FUNC:
168               func = ret;
169               break;
170             default:
171               if (closedir(dir) < 0)
172                 a->warning("Cannot close directory: %s (%s)", parent,
173                            strerror(errno));
174               a->error("Wrong directory tree, probably not connected to an arbiter");
175             }
176
177           enum_devices(path, a, domain, bus, dev, func, lev + 1);
178         }
179       else
180         {
181           if (strncmp(entry->d_name, FILE_CONFIG_NAME, NAME_MAX))
182             /* We are looking for the config file */
183             continue;
184
185           /* We found an available virtual device, add it to our list */
186           d = pci_alloc_dev(a);
187           d->domain = domain;
188           d->bus = bus;
189           d->dev = dev;
190           d->func = func;
191
192           /* Get the arbiter port */
193           if (device_port_lookup(d) == MACH_PORT_NULL)
194             {
195               if (closedir(dir) < 0)
196                 a->warning("Cannot close directory: %s (%s)", parent,
197                            strerror(errno));
198               a->error("Cannot find the PCI arbiter");
199             }
200
201           pci_link_dev(a, d);
202
203           vd = pci_read_long(d, PCI_VENDOR_ID);
204           ht = pci_read_byte(d, PCI_HEADER_TYPE);
205
206           d->vendor_id = vd & 0xffff;
207           d->device_id = vd >> 16U;
208           d->known_fields = PCI_FILL_IDENT;
209           d->hdrtype = ht;
210         }
211     }
212
213   if (closedir(dir) < 0)
214     a->error("Cannot close directory: %s (%s)", parent, strerror(errno));
215 }
216
217 /* Enumerate devices */
218 static void
219 hurd_scan(struct pci_access *a)
220 {
221   enum_devices(_SERVERS_BUS_PCI, a, -1, -1, -1, -1, LEVEL_DOMAIN);
222 }
223
224 /*
225  * Read `len' bytes to `buf'.
226  *
227  * Returns error when the number of read bytes does not match `len'.
228  */
229 static int
230 hurd_read(struct pci_dev *d, int pos, byte * buf, int len)
231 {
232   int err;
233   size_t nread;
234   char *data;
235   mach_port_t device_port;
236
237   nread = len;
238   device_port = device_port_lookup(d);
239   if (device_port == MACH_PORT_NULL)
240     d->access->error("Cannot find the PCI arbiter");
241
242   if (len > 4)
243     err = !pci_generic_block_read(d, pos, buf, nread);
244   else
245     {
246       data = (char *) buf;
247       err = pci_conf_read(device_port, pos, &data, &nread, len);
248
249       if (data != (char *) buf)
250         {
251           if (nread > (size_t) len)     /* Sanity check for bogus server.  */
252             {
253               vm_deallocate(mach_task_self(), (vm_address_t) data, nread);
254               return 0;
255             }
256
257           memcpy(buf, data, nread);
258           vm_deallocate(mach_task_self(), (vm_address_t) data, nread);
259         }
260     }
261   if (err)
262     return 0;
263
264   return nread == (size_t) len;
265 }
266
267 /*
268  * Write `len' bytes from `buf'.
269  *
270  * Returns error when the number of written bytes does not match `len'.
271  */
272 static int
273 hurd_write(struct pci_dev *d, int pos, byte * buf, int len)
274 {
275   int err;
276   size_t nwrote;
277   mach_port_t device_port;
278
279   nwrote = len;
280   device_port = device_port_lookup(d);
281   if (device_port == MACH_PORT_NULL)
282     d->access->error("Cannot find the PCI arbiter");
283
284   if (len > 4)
285     err = !pci_generic_block_write(d, pos, buf, len);
286   else
287     err = pci_conf_write(device_port, pos, (char *) buf, len, &nwrote);
288   if (err)
289     return 0;
290
291   return nwrote == (size_t) len;
292 }
293
294 /* Get requested info from the server */
295
296 static void
297 hurd_fill_regions(struct pci_dev *d)
298 {
299   mach_port_t device_port = *((mach_port_t *) d->aux);
300   struct pci_bar regions[6];
301   char *buf = (char *) &regions;
302   size_t size = sizeof(regions);
303
304   int err = pci_get_dev_regions(device_port, &buf, &size);
305   if (err)
306     return;
307
308   if ((char *) &regions != buf)
309     {
310       /* Sanity check for bogus server.  */
311       if (size > sizeof(regions))
312         {
313           vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
314           return;
315         }
316
317       memcpy(&regions, buf, size);
318       vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
319     }
320
321   for (int i = 0; i < 6; i++)
322     {
323       if (regions[i].size == 0)
324         continue;
325
326       d->base_addr[i] = regions[i].base_addr;
327       d->base_addr[i] |= regions[i].is_IO;
328       d->base_addr[i] |= regions[i].is_64 << 2;
329       d->base_addr[i] |= regions[i].is_prefetchable << 3;
330
331       d->size[i] = regions[i].size;
332     }
333 }
334
335 static void
336 hurd_fill_rom(struct pci_dev *d)
337 {
338   struct pci_xrom_bar rom;
339   mach_port_t device_port = *((mach_port_t *) d->aux);
340   char *buf = (char *) &rom;
341   size_t size = sizeof(rom);
342
343   int err = pci_get_dev_rom(device_port, &buf, &size);
344   if (err)
345     return;
346
347   if ((char *) &rom != buf)
348     {
349       /* Sanity check for bogus server.  */
350       if (size > sizeof(rom))
351         {
352           vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
353           return;
354         }
355
356       memcpy(&rom, buf, size);
357       vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
358     }
359
360   d->rom_base_addr = rom.base_addr;
361   d->rom_size = rom.size;
362 }
363
364 static unsigned int
365 hurd_fill_info(struct pci_dev *d, unsigned int flags)
366 {
367   unsigned int done = 0;
368
369   if (!d->access->buscentric)
370     {
371       if (flags & (PCI_FILL_BASES | PCI_FILL_SIZES))
372         {
373           hurd_fill_regions(d);
374           done |= PCI_FILL_BASES | PCI_FILL_SIZES;
375         }
376       if (flags & PCI_FILL_ROM_BASE)
377         {
378           hurd_fill_rom(d);
379           done |= PCI_FILL_ROM_BASE;
380         }
381     }
382
383   return done | pci_generic_fill_info(d, flags & ~done);
384 }
385
386 struct pci_methods pm_hurd = {
387   "hurd",
388   "Hurd access using RPCs",
389   NULL,                         /* config */
390   hurd_detect,
391   hurd_init,
392   hurd_cleanup,
393   hurd_scan,
394   hurd_fill_info,
395   hurd_read,
396   hurd_write,
397   NULL,                         /* read_vpd */
398   hurd_init_dev,
399   hurd_cleanup_dev
400 };