xref: /aosp_15_r20/external/pciutils/lib/hurd.c (revision c2e0c6b56a71da9abe8df5c8348fb3eb5c2c9251)
1 /*
2  *	The PCI Library -- Hurd access via RPCs
3  *
4  *	Copyright (c) 2017 Joan Lledó <[email protected]>
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
hurd_detect(struct pci_access * a)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
hurd_init(struct pci_access * a UNUSED)64 hurd_init(struct pci_access *a UNUSED)
65 {
66 }
67 
68 static void
hurd_cleanup(struct pci_access * a UNUSED)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
hurd_init_dev(struct pci_dev * d)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
hurd_cleanup_dev(struct pci_dev * d)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
device_port_lookup(struct pci_dev * d)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
enum_devices(const char * parent,struct pci_access * a,int domain,int bus,int dev,int func,tree_level lev)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
hurd_scan(struct pci_access * a)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
hurd_read(struct pci_dev * d,int pos,byte * buf,int len)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
hurd_write(struct pci_dev * d,int pos,byte * buf,int len)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
hurd_fill_regions(struct pci_dev * d)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
hurd_fill_rom(struct pci_dev * d)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
hurd_fill_info(struct pci_dev * d,unsigned int flags)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   .name = "hurd",
360   .help = "Hurd access using RPCs",
361   .detect = hurd_detect,
362   .init = hurd_init,
363   .cleanup = hurd_cleanup,
364   .scan = hurd_scan,
365   .fill_info = hurd_fill_info,
366   .read = hurd_read,
367   .write = hurd_write,
368   .init_dev = hurd_init_dev,
369   .cleanup_dev = hurd_cleanup_dev
370 };
371