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 v2+.
8 * SPDX-License-Identifier: GPL-2.0-or-later
17 #include <sys/types.h>
25 #include <hurd/paths.h>
28 #define _SERVERS_BUS_PCI _SERVERS_BUS "/pci"
31 #define FILE_CONFIG_NAME "config"
32 #define FILE_ROM_NAME "rom"
34 /* Level in the fs tree */
44 /* Check whether there's a pci server */
46 hurd_detect(struct pci_access *a)
51 err = stat(_SERVERS_BUS_PCI, &st);
54 a->error("Could not open file `%s'", _SERVERS_BUS_PCI);
58 /* The node must be a directory and a translator */
59 return S_ISDIR(st.st_mode) && ((st.st_mode & S_ITRANS) == S_IROOT);
62 /* Empty callbacks, we don't need any special init or cleanup */
64 hurd_init(struct pci_access *a UNUSED)
69 hurd_cleanup(struct pci_access *a UNUSED)
73 /* Each device has its own server path. Allocate space for the port. */
75 hurd_init_dev(struct pci_dev *d)
77 d->aux = pci_malloc(d->access, sizeof(mach_port_t));
78 *((mach_port_t *) d->aux) = MACH_PORT_NULL;
81 /* Deallocate the port and free its space */
83 hurd_cleanup_dev(struct pci_dev *d)
85 mach_port_t device_port;
87 device_port = *((mach_port_t *) d->aux);
88 mach_port_deallocate(mach_task_self(), device_port);
94 device_port_lookup(struct pci_dev *d)
96 char server[NAME_MAX];
97 mach_port_t device_port = *((mach_port_t *) d->aux);
99 if (device_port != MACH_PORT_NULL)
102 snprintf(server, NAME_MAX, "%s/%04x/%02x/%02x/%01u/%s",
103 _SERVERS_BUS_PCI, d->domain, d->bus, d->dev, d->func,
105 device_port = file_name_lookup(server, 0, 0);
107 if (device_port == MACH_PORT_NULL)
108 d->access->error("Cannot find the PCI arbiter");
110 *((mach_port_t *) d->aux) = device_port;
114 /* Walk through the FS tree to see what is allowed for us */
116 enum_devices(const char *parent, struct pci_access *a, int domain, int bus,
117 int dev, int func, tree_level lev)
121 struct dirent *entry;
125 dir = opendir(parent);
128 if (errno == EPERM || errno == EACCES)
129 /* The client lacks the permissions to access this function, skip */
132 a->error("Cannot open directory: %s (%s)", parent, strerror(errno));
135 while ((entry = readdir(dir)) != 0)
137 snprintf(path, NAME_MAX, "%s/%s", parent, entry->d_name);
138 if (entry->d_type == DT_DIR)
140 if (!strncmp(entry->d_name, ".", NAME_MAX)
141 || !strncmp(entry->d_name, "..", NAME_MAX))
145 ret = strtol(entry->d_name, 0, 16);
148 if (closedir(dir) < 0)
149 a->warning("Cannot close directory: %s (%s)", parent,
151 a->error("Wrong directory name: %s (number expected) probably "
152 "not connected to an arbiter", entry->d_name);
156 * We found a valid directory.
157 * Update the address and switch to the next level.
174 if (closedir(dir) < 0)
175 a->warning("Cannot close directory: %s (%s)", parent,
177 a->error("Wrong directory tree, probably not connected to an arbiter");
180 enum_devices(path, a, domain, bus, dev, func, lev + 1);
184 if (strncmp(entry->d_name, FILE_CONFIG_NAME, NAME_MAX))
185 /* We are looking for the config file */
188 /* We found an available virtual device, add it to our list */
189 d = pci_alloc_dev(a);
198 if (closedir(dir) < 0)
199 a->error("Cannot close directory: %s (%s)", parent, strerror(errno));
202 /* Enumerate devices */
204 hurd_scan(struct pci_access *a)
206 enum_devices(_SERVERS_BUS_PCI, a, -1, -1, -1, -1, LEVEL_DOMAIN);
210 * Read `len' bytes to `buf'.
212 * Returns error when the number of read bytes does not match `len'.
215 hurd_read(struct pci_dev *d, int pos, byte * buf, int len)
220 mach_port_t device_port = device_port_lookup(d);
223 return pci_generic_block_read(d, pos, buf, len);
226 err = pci_conf_read(device_port, pos, &data, &nread, len);
228 if (data != (char *) buf)
230 if (nread > (size_t) len) /* Sanity check for bogus server. */
232 vm_deallocate(mach_task_self(), (vm_address_t) data, nread);
236 memcpy(buf, data, nread);
237 vm_deallocate(mach_task_self(), (vm_address_t) data, nread);
240 return !err && nread == (size_t) len;
244 * Write `len' bytes from `buf'.
246 * Returns error when the number of written bytes does not match `len'.
249 hurd_write(struct pci_dev *d, int pos, byte * buf, int len)
253 mach_port_t device_port = device_port_lookup(d);
256 return pci_generic_block_write(d, pos, buf, len);
258 err = pci_conf_write(device_port, pos, (char *) buf, len, &nwrote);
260 return !err && nwrote == (size_t) len;
263 /* Get requested info from the server */
266 hurd_fill_regions(struct pci_dev *d)
268 mach_port_t device_port = device_port_lookup(d);
269 struct pci_bar regions[6];
270 char *buf = (char *) ®ions;
271 size_t size = sizeof(regions);
273 int err = pci_get_dev_regions(device_port, &buf, &size);
277 if ((char *) ®ions != buf)
279 /* Sanity check for bogus server. */
280 if (size > sizeof(regions))
282 vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
286 memcpy(®ions, buf, size);
287 vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
290 for (int i = 0; i < 6; i++)
292 if (regions[i].size == 0)
295 d->base_addr[i] = regions[i].base_addr;
296 d->base_addr[i] |= regions[i].is_IO;
297 d->base_addr[i] |= regions[i].is_64 << 2;
298 d->base_addr[i] |= regions[i].is_prefetchable << 3;
300 d->size[i] = regions[i].size;
307 hurd_fill_rom(struct pci_dev *d)
309 struct pci_xrom_bar rom;
310 mach_port_t device_port = device_port_lookup(d);
311 char *buf = (char *) &rom;
312 size_t size = sizeof(rom);
314 int err = pci_get_dev_rom(device_port, &buf, &size);
318 if ((char *) &rom != buf)
320 /* Sanity check for bogus server. */
321 if (size > sizeof(rom))
323 vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
327 memcpy(&rom, buf, size);
328 vm_deallocate(mach_task_self(), (vm_address_t) buf, size);
331 d->rom_base_addr = rom.base_addr;
332 d->rom_size = rom.size;
338 hurd_fill_info(struct pci_dev *d, unsigned int flags)
340 if (!d->access->buscentric)
342 if (want_fill(d, flags, PCI_FILL_BASES | PCI_FILL_SIZES))
344 if (hurd_fill_regions(d))
345 clear_fill(d, PCI_FILL_BASES | PCI_FILL_SIZES);
347 if (want_fill(d, flags, PCI_FILL_ROM_BASE))
349 if (hurd_fill_rom(d))
350 clear_fill(d, PCI_FILL_ROM_BASE);
354 pci_generic_fill_info(d, flags);
357 struct pci_methods pm_hurd = {
359 "Hurd access using RPCs",