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