2 * The PCI Library -- Hurd access via RPCs
4 * Copyright (c) 2017 Joan Lledó <jlledom@member.fsf.org>
6 * Can be freely distributed and used under the terms of the GNU GPL.
15 #include <sys/types.h>
23 #include <hurd/paths.h>
26 #define _SERVERS_BUS_PCI _SERVERS_BUS "/pci"
29 #define FILE_CONFIG_NAME "config"
30 #define FILE_ROM_NAME "rom"
32 /* Level in the fs tree */
42 /* Check whether there's a pci server */
44 hurd_detect(struct pci_access *a)
49 err = stat(_SERVERS_BUS_PCI, &st);
52 a->error("Could not open file `%s'", _SERVERS_BUS_PCI);
56 /* The node must be a directory and a translator */
57 return S_ISDIR(st.st_mode) && ((st.st_mode & S_ITRANS) == S_IROOT);
60 /* Empty callbacks, we don't need any special init or cleanup */
62 hurd_init(struct pci_access *a UNUSED)
67 hurd_cleanup(struct pci_access *a UNUSED)
71 /* Each device has its own server path. Allocate space for the port. */
73 hurd_init_dev(struct pci_dev *d)
75 d->aux = pci_malloc(d->access, sizeof(mach_port_t));
76 *((mach_port_t *) d->aux) = 0;
79 /* Deallocate the port and free its space */
81 hurd_cleanup_dev(struct pci_dev *d)
83 mach_port_t device_port;
85 device_port = *((mach_port_t *) d->aux);
86 mach_port_deallocate(mach_task_self(), device_port);
92 device_port_lookup(struct pci_dev *d)
94 mach_port_t device_port;
95 char server[NAME_MAX];
97 snprintf(server, NAME_MAX, "%s/%04x/%02x/%02x/%01u/%s",
98 _SERVERS_BUS_PCI, d->domain, d->bus, d->dev, d->func,
100 device_port = file_name_lookup(server, 0, 0);
102 *((mach_port_t *) d->aux) = device_port;
105 /* Walk through the FS tree to see what is allowed for us */
107 enum_devices(const char *parent, struct pci_access *a, int domain, int bus,
108 int dev, int func, tree_level lev)
112 struct dirent *entry;
118 dir = opendir(parent);
121 if (errno == EPERM || errno == EACCES)
122 /* The client lacks the permissions to access this function, skip */
125 a->error("Cannot open directory: %s (%s)", parent, strerror(errno));
128 while ((entry = readdir(dir)) != 0)
130 snprintf(path, NAME_MAX, "%s/%s", parent, entry->d_name);
131 if (entry->d_type == DT_DIR)
133 if (!strncmp(entry->d_name, ".", NAME_MAX)
134 || !strncmp(entry->d_name, "..", NAME_MAX))
138 ret = strtol(entry->d_name, 0, 16);
141 if (closedir(dir) < 0)
142 a->warning("Cannot close directory: %s (%s)", parent,
144 a->error("Wrong directory name: %s (number expected) probably "
145 "not connected to an arbiter", entry->d_name);
149 * We found a valid directory.
150 * Update the address and switch to the next level.
167 if (closedir(dir) < 0)
168 a->warning("Cannot close directory: %s (%s)", parent,
170 a->error("Wrong directory tree, probably not connected to an arbiter");
173 enum_devices(path, a, domain, bus, dev, func, lev + 1);
177 if (strncmp(entry->d_name, FILE_CONFIG_NAME, NAME_MAX))
178 /* We are looking for the config file */
181 /* We found an available virtual device, add it to our list */
182 d = pci_alloc_dev(a);
188 /* Get the arbiter port */
189 device_port_lookup(d);
190 if (d->aux == MACH_PORT_NULL)
192 if (closedir(dir) < 0)
193 a->warning("Cannot close directory: %s (%s)", parent,
195 a->error("Cannot find the PCI arbiter");
200 vd = pci_read_long(d, PCI_VENDOR_ID);
201 ht = pci_read_byte(d, PCI_HEADER_TYPE);
203 d->vendor_id = vd & 0xffff;
204 d->device_id = vd >> 16U;
205 d->known_fields = PCI_FILL_IDENT;
210 if (closedir(dir) < 0)
211 a->error("Cannot close directory: %s (%s)", parent, strerror(errno));
214 /* Enumerate devices */
216 hurd_scan(struct pci_access *a)
218 enum_devices(_SERVERS_BUS_PCI, a, -1, -1, -1, -1, LEVEL_DOMAIN);
222 * Read `len' bytes to `buf'.
224 * Returns error when the number of read bytes does not match `len'.
227 hurd_read(struct pci_dev *d, int pos, byte * buf, int len)
232 mach_port_t device_port;
235 if (*((mach_port_t *) d->aux) == 0)
237 /* We still don't have the port for this device */
238 device_port_lookup(d);
239 if (d->aux == MACH_PORT_NULL)
241 d->access->error("Cannot find the PCI arbiter");
244 device_port = *((mach_port_t *) d->aux);
247 err = !pci_generic_block_read(d, pos, buf, nread);
251 err = pci_conf_read(device_port, pos, &data, &nread, len);
253 if (data != (char *) buf)
255 if (nread > (size_t) len) /* Sanity check for bogus server. */
257 vm_deallocate(mach_task_self(), (vm_address_t) data, nread);
261 memcpy(buf, data, nread);
262 vm_deallocate(mach_task_self(), (vm_address_t) data, nread);
268 return nread == (size_t) len;
272 * Write `len' bytes from `buf'.
274 * Returns error when the number of written bytes does not match `len'.
277 hurd_write(struct pci_dev *d, int pos, byte * buf, int len)
281 mach_port_t device_port;
284 if (*((mach_port_t *) d->aux) == 0)
286 /* We still don't have the port for this device */
287 device_port_lookup(d);
288 if (d->aux == MACH_PORT_NULL)
290 d->access->error("Cannot find the PCI arbiter");
293 device_port = *((mach_port_t *) d->aux);
296 err = !pci_generic_block_write(d, pos, buf, len);
298 err = pci_conf_write(device_port, pos, (char *) buf, len, &nwrote);
302 return nwrote == (size_t) len;
305 /* Get requested info from the server */
307 hurd_fill_info(struct pci_dev *d, int flags)
310 struct pci_bar regions[6];
311 struct pci_xrom_bar rom;
314 mach_port_t device_port;
316 device_port = *((mach_port_t *) d->aux);
318 if (flags & PCI_FILL_BASES)
320 buf = (char *) ®ions;
321 size = sizeof(regions);
323 err = pci_get_dev_regions(device_port, &buf, &size);
327 if ((char *) ®ions != buf)
329 /* Sanity check for bogus server. */
330 if (size > sizeof(regions))
332 vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
336 memcpy(®ions, buf, size);
337 vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
340 for (i = 0; i < 6; i++)
342 if (regions[i].size == 0)
345 d->base_addr[i] = regions[i].base_addr;
346 d->base_addr[i] |= regions[i].is_IO;
347 d->base_addr[i] |= regions[i].is_64 << 2;
348 d->base_addr[i] |= regions[i].is_prefetchable << 3;
350 if (flags & PCI_FILL_SIZES)
351 d->size[i] = regions[i].size;
355 if (flags & PCI_FILL_ROM_BASE)
360 err = pci_get_dev_rom(device_port, &buf, &size);
364 if ((char *) &rom != buf)
366 /* Sanity check for bogus server. */
367 if (size > sizeof(rom))
369 vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
373 memcpy(&rom, buf, size);
374 vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
377 d->rom_base_addr = rom.base_addr;
378 d->rom_size = rom.size;
381 return pci_generic_fill_info(d, flags);
384 struct pci_methods pm_hurd = {
386 "Hurd access using RPCs",