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