1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | |
25 | |
26 | |
27 | |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | #define _GNU_SOURCE |
36 | |
37 | #include <stdlib.h> |
38 | #include <string.h> |
39 | #include <stdio.h> |
40 | #include <unistd.h> |
41 | #include <sys/types.h> |
42 | #include <sys/stat.h> |
43 | #include <fcntl.h> |
44 | #include <sys/mman.h> |
45 | #include <dirent.h> |
46 | #include <errno(*__errno_location ()).h> |
47 | |
48 | #if defined(__i386__) || defined(__x86_64__) || defined(__arm__) |
49 | #include <sys/io.h> |
50 | #else |
51 | #define inb(x)-1 -1 |
52 | #define inw(x)-1 -1 |
53 | #define inl(x)-1 -1 |
54 | #define outb(x,y)do {} while (0) do {} while (0) |
55 | #define outw(x,y)do {} while (0) do {} while (0) |
56 | #define outl(x,y)do {} while (0) do {} while (0) |
57 | #define iopl(x)-1 -1 |
58 | #endif |
59 | |
60 | #include "config.h" |
61 | |
62 | #ifdef HAVE_MTRR |
63 | #include <asm/mtrr.h> |
64 | #include <sys/ioctl.h> |
65 | #endif |
66 | |
67 | #include "pciaccess.h" |
68 | #include "pciaccess_private.h" |
69 | #include "linux_devmem.h" |
70 | |
71 | static const struct pci_system_methods linux_sysfs_methods; |
72 | |
73 | #define SYS_BUS_PCI"/sys/bus/pci/devices" "/sys/bus/pci/devices" |
74 | |
75 | static int |
76 | pci_device_linux_sysfs_read( struct pci_device * dev, void * data, |
77 | pciaddr_t offset, pciaddr_t size, |
78 | pciaddr_t * bytes_read ); |
79 | |
80 | static int populate_entries(struct pci_system * pci_sys); |
81 | |
82 | |
83 | |
84 | |
85 | _pci_hidden__attribute__((visibility("hidden"))) int |
86 | pci_system_linux_sysfs_create( void ) |
87 | { |
88 | int err = 0; |
89 | struct stat st; |
90 | |
91 | |
92 | |
93 | |
94 | |
95 | |
96 | if ( stat( SYS_BUS_PCI"/sys/bus/pci/devices", & st ) == 0 ) { |
97 | pci_sys = calloc( 1, sizeof( struct pci_system ) ); |
98 | if ( pci_sys != NULL((void*)0) ) { |
99 | pci_sys->methods = & linux_sysfs_methods; |
100 | #ifdef HAVE_MTRR |
101 | pci_sys->mtrr_fd = open("/proc/mtrr", O_WRONLY01 | O_CLOEXEC02000000); |
102 | #endif |
103 | err = populate_entries(pci_sys); |
104 | } |
105 | else { |
106 | err = ENOMEM12; |
107 | } |
108 | } |
109 | else { |
110 | err = errno(*__errno_location ()); |
111 | } |
112 | |
113 | return err; |
114 | } |
115 | |
116 | |
117 | |
118 | |
119 | |
120 | |
121 | |
122 | |
123 | |
124 | |
125 | |
126 | |
127 | static int |
128 | scan_sys_pci_filter( const struct dirent * d ) |
129 | { |
130 | return !((strcmp( d->d_name, "." ) == 0) |
131 | || (strcmp( d->d_name, ".." ) == 0)); |
132 | } |
133 | |
134 | |
135 | int |
136 | populate_entries( struct pci_system * p ) |
137 | { |
138 | struct dirent ** devices = NULL((void*)0); |
139 | int n; |
140 | int i; |
141 | int err = 0; |
142 | |
143 | |
144 | n = scandir( SYS_BUS_PCI"/sys/bus/pci/devices", & devices, scan_sys_pci_filter, alphasort ); |
145 | if ( n > 0 ) { |
146 | p->num_devices = n; |
147 | p->devices = calloc( n, sizeof( struct pci_device_private ) ); |
148 | |
149 | if (p->devices != NULL((void*)0)) { |
150 | for (i = 0 ; i < n ; i++) { |
151 | uint8_t config[48]; |
152 | pciaddr_t bytes; |
153 | unsigned dom, bus, dev, func; |
154 | struct pci_device_private *device = |
155 | (struct pci_device_private *) &p->devices[i]; |
156 | |
157 | |
158 | sscanf(devices[i]->d_name, "%04x:%02x:%02x.%1u", |
159 | & dom, & bus, & dev, & func); |
160 | |
161 | device->base.domain = dom; |
162 | device->base.bus = bus; |
163 | device->base.dev = dev; |
164 | device->base.func = func; |
165 | |
166 | |
167 | err = pci_device_linux_sysfs_read(& device->base, config, 0, |
168 | 48, & bytes); |
169 | if ((bytes == 48) && !err) { |
170 | device->base.vendor_id = (uint16_t)config[0] |
171 | + ((uint16_t)config[1] << 8); |
172 | device->base.device_id = (uint16_t)config[2] |
173 | + ((uint16_t)config[3] << 8); |
174 | device->base.device_class = (uint32_t)config[9] |
175 | + ((uint32_t)config[10] << 8) |
176 | + ((uint32_t)config[11] << 16); |
177 | device->base.revision = config[8]; |
178 | device->base.subvendor_id = (uint16_t)config[44] |
179 | + ((uint16_t)config[45] << 8); |
180 | device->base.subdevice_id = (uint16_t)config[46] |
181 | + ((uint16_t)config[47] << 8); |
182 | } |
183 | |
184 | if (err) { |
185 | break; |
186 | } |
187 | } |
188 | } |
189 | else { |
190 | err = ENOMEM12; |
191 | } |
192 | } |
193 | |
194 | for (i = 0; i < n; i++) |
195 | free(devices[i]); |
196 | free(devices); |
197 | |
198 | if (err) { |
199 | free(p->devices); |
200 | p->devices = NULL((void*)0); |
201 | } |
202 | |
203 | return err; |
204 | } |
205 | |
206 | |
207 | static int |
208 | pci_device_linux_sysfs_probe( struct pci_device * dev ) |
209 | { |
210 | char name[256]; |
211 | uint8_t config[256]; |
212 | char resource[512]; |
213 | int fd; |
214 | pciaddr_t bytes; |
215 | unsigned i; |
216 | int err; |
217 | |
218 | |
219 | err = pci_device_linux_sysfs_read( dev, config, 0, 256, & bytes ); |
220 | if ( bytes >= 64 ) { |
221 | struct pci_device_private *priv = (struct pci_device_private *) dev; |
222 | |
223 | dev->irq = config[60]; |
224 | priv->header_type = config[14]; |
225 | |
226 | |
227 | |
228 | |
229 | |
230 | |
231 | |
232 | |
233 | |
234 | |
235 | |
236 | |
237 | |
238 | |
239 | |
240 | |
241 | |
242 | snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/resource", |
243 | SYS_BUS_PCI"/sys/bus/pci/devices", |
244 | dev->domain, |
245 | dev->bus, |
246 | dev->dev, |
247 | dev->func ); |
248 | fd = open( name, O_RDONLY00 | O_CLOEXEC02000000); |
249 | if ( fd != -1 ) { |
250 | char * next; |
251 | pciaddr_t low_addr; |
252 | pciaddr_t high_addr; |
253 | pciaddr_t flags; |
254 | |
255 | |
256 | bytes = read( fd, resource, 512 ); |
257 | resource[511] = '\0'; |
258 | |
259 | close( fd ); |
260 | |
261 | next = resource; |
262 | for ( i = 0 ; i < 6 ; i++ ) { |
263 | |
264 | dev->regions[i].base_addr = strtoull( next, & next, 16 ); |
265 | high_addr = strtoull( next, & next, 16 ); |
266 | flags = strtoull( next, & next, 16 ); |
267 | |
268 | if ( dev->regions[i].base_addr != 0 ) { |
269 | dev->regions[i].size = (high_addr |
270 | - dev->regions[i].base_addr) + 1; |
271 | |
272 | dev->regions[i].is_IO = (flags & 0x01); |
273 | dev->regions[i].is_64 = (flags & 0x04); |
274 | dev->regions[i].is_prefetchable = (flags & 0x08); |
275 | } |
276 | } |
277 | |
278 | low_addr = strtoull( next, & next, 16 ); |
279 | high_addr = strtoull( next, & next, 16 ); |
280 | flags = strtoull( next, & next, 16 ); |
| Value stored to 'flags' is never read |
281 | if ( low_addr != 0 ) { |
282 | priv->rom_base = low_addr; |
283 | dev->rom_size = (high_addr - low_addr) + 1; |
284 | } |
285 | } |
286 | } |
287 | |
288 | return err; |
289 | } |
290 | |
291 | |
292 | static int |
293 | pci_device_linux_sysfs_read_rom( struct pci_device * dev, void * buffer ) |
294 | { |
295 | char name[256]; |
296 | int fd; |
297 | struct stat st; |
298 | int err = 0; |
299 | size_t rom_size; |
300 | size_t total_bytes; |
301 | |
302 | |
303 | snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/rom", |
304 | SYS_BUS_PCI"/sys/bus/pci/devices", |
305 | dev->domain, |
306 | dev->bus, |
307 | dev->dev, |
308 | dev->func ); |
309 | |
310 | fd = open( name, O_RDWR02 | O_CLOEXEC02000000); |
311 | if ( fd == -1 ) { |
312 | #ifdef LINUX_ROM |
313 | |
314 | |
315 | |
316 | |
317 | return pci_device_linux_devmem_read_rom(dev, buffer); |
318 | #else |
319 | return errno(*__errno_location ()); |
320 | #endif |
321 | } |
322 | |
323 | |
324 | if ( fstat( fd, & st ) == -1 ) { |
325 | close( fd ); |
326 | return errno(*__errno_location ()); |
327 | } |
328 | |
329 | rom_size = st.st_size; |
330 | if ( rom_size == 0 ) |
331 | rom_size = 0x10000; |
332 | |
333 | |
334 | |
335 | |
336 | |
337 | |
338 | write( fd, "1", 1 ); |
339 | lseek( fd, 0, SEEK_SET0 ); |
340 | |
341 | for ( total_bytes = 0 ; total_bytes < rom_size ; ) { |
342 | const int bytes = read( fd, (char *) buffer + total_bytes, |
343 | rom_size - total_bytes ); |
344 | if ( bytes == -1 ) { |
345 | err = errno(*__errno_location ()); |
346 | break; |
347 | } |
348 | else if ( bytes == 0 ) { |
349 | break; |
350 | } |
351 | |
352 | total_bytes += bytes; |
353 | } |
354 | |
355 | |
356 | lseek( fd, 0, SEEK_SET0 ); |
357 | write( fd, "0", 1 ); |
358 | |
359 | close( fd ); |
360 | return err; |
361 | } |
362 | |
363 | |
364 | static int |
365 | pci_device_linux_sysfs_read( struct pci_device * dev, void * data, |
366 | pciaddr_t offset, pciaddr_t size, |
367 | pciaddr_t * bytes_read ) |
368 | { |
369 | char name[256]; |
370 | pciaddr_t temp_size = size; |
371 | int err = 0; |
372 | int fd; |
373 | char *data_bytes = data; |
374 | |
375 | if ( bytes_read != NULL((void*)0) ) { |
376 | *bytes_read = 0; |
377 | } |
378 | |
379 | |
380 | |
381 | |
382 | |
383 | |
384 | snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/config", |
385 | SYS_BUS_PCI"/sys/bus/pci/devices", |
386 | dev->domain, |
387 | dev->bus, |
388 | dev->dev, |
389 | dev->func ); |
390 | |
391 | fd = open( name, O_RDONLY00 | O_CLOEXEC02000000); |
392 | if ( fd == -1 ) { |
393 | return errno(*__errno_location ()); |
394 | } |
395 | |
396 | |
397 | while ( temp_size > 0 ) { |
398 | const ssize_t bytes = pread64( fd, data_bytes, temp_size, offset ); |
399 | |
400 | |
401 | |
402 | |
403 | if (bytes == 0) |
404 | break; |
405 | if ( bytes < 0 ) { |
406 | err = errno(*__errno_location ()); |
407 | break; |
408 | } |
409 | |
410 | temp_size -= bytes; |
411 | offset += bytes; |
412 | data_bytes += bytes; |
413 | } |
414 | |
415 | if ( bytes_read != NULL((void*)0) ) { |
416 | *bytes_read = size - temp_size; |
417 | } |
418 | |
419 | close( fd ); |
420 | return err; |
421 | } |
422 | |
423 | |
424 | static int |
425 | pci_device_linux_sysfs_write( struct pci_device * dev, const void * data, |
426 | pciaddr_t offset, pciaddr_t size, |
427 | pciaddr_t * bytes_written ) |
428 | { |
429 | char name[256]; |
430 | pciaddr_t temp_size = size; |
431 | int err = 0; |
432 | int fd; |
433 | const char *data_bytes = data; |
434 | |
435 | if ( bytes_written != NULL((void*)0) ) { |
436 | *bytes_written = 0; |
437 | } |
438 | |
439 | |
440 | |
441 | |
442 | |
443 | |
444 | snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/config", |
445 | SYS_BUS_PCI"/sys/bus/pci/devices", |
446 | dev->domain, |
447 | dev->bus, |
448 | dev->dev, |
449 | dev->func ); |
450 | |
451 | fd = open( name, O_WRONLY01 | O_CLOEXEC02000000); |
452 | if ( fd == -1 ) { |
453 | return errno(*__errno_location ()); |
454 | } |
455 | |
456 | |
457 | while ( temp_size > 0 ) { |
458 | const ssize_t bytes = pwrite64( fd, data_bytes, temp_size, offset ); |
459 | |
460 | |
461 | |
462 | |
463 | if ( bytes == 0 ) |
464 | break; |
465 | if ( bytes < 0 ) { |
466 | err = errno(*__errno_location ()); |
467 | break; |
468 | } |
469 | |
470 | temp_size -= bytes; |
471 | offset += bytes; |
472 | data_bytes += bytes; |
473 | } |
474 | |
475 | if ( bytes_written != NULL((void*)0) ) { |
476 | *bytes_written = size - temp_size; |
477 | } |
478 | |
479 | close( fd ); |
480 | return err; |
481 | } |
482 | |
483 | static int |
484 | pci_device_linux_sysfs_map_range_wc(struct pci_device *dev, |
485 | struct pci_device_mapping *map) |
486 | { |
487 | char name[256]; |
488 | int fd; |
489 | const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE(1U<<0)) != 0) |
490 | ? (PROT_READ0x1 | PROT_WRITE0x2) : PROT_READ0x1; |
491 | const int open_flags = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE(1U<<0)) != 0) |
492 | ? O_RDWR02 : O_RDONLY00; |
493 | const off_t offset = map->base - dev->regions[map->region].base_addr; |
494 | |
495 | snprintf(name, 255, "%s/%04x:%02x:%02x.%1u/resource%u_wc", |
496 | SYS_BUS_PCI"/sys/bus/pci/devices", |
497 | dev->domain, |
498 | dev->bus, |
499 | dev->dev, |
500 | dev->func, |
501 | map->region); |
502 | fd = open(name, open_flags | O_CLOEXEC02000000); |
503 | if (fd == -1) |
504 | return errno(*__errno_location ()); |
505 | |
506 | map->memory = mmap(NULL((void*)0), map->size, prot, MAP_SHARED0x001, fd, offset); |
507 | if (map->memory == MAP_FAILED((void *) -1)) { |
508 | map->memory = NULL((void*)0); |
509 | close(fd); |
510 | return errno(*__errno_location ()); |
511 | } |
512 | |
513 | close(fd); |
514 | |
515 | return 0; |
516 | } |
517 | |
518 | |
519 | |
520 | |
521 | |
522 | |
523 | |
524 | |
525 | |
526 | |
527 | |
528 | |
529 | |
530 | |
531 | |
532 | |
533 | |
534 | static int |
535 | pci_device_linux_sysfs_map_range(struct pci_device *dev, |
536 | struct pci_device_mapping *map) |
537 | { |
538 | char name[256]; |
539 | int fd; |
540 | int err = 0; |
541 | const int prot = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE(1U<<0)) != 0) |
542 | ? (PROT_READ0x1 | PROT_WRITE0x2) : PROT_READ0x1; |
543 | const int open_flags = ((map->flags & PCI_DEV_MAP_FLAG_WRITABLE(1U<<0)) != 0) |
544 | ? O_RDWR02 : O_RDONLY00; |
545 | const off_t offset = map->base - dev->regions[map->region].base_addr; |
546 | #ifdef HAVE_MTRR |
547 | struct mtrr_sentry sentry = { |
548 | .base = map->base, |
549 | .size = map->size, |
550 | .type = MTRR_TYPE_UNCACHABLE |
551 | }; |
552 | #endif |
553 | |
554 | |
555 | if ((map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE(1U<<1)) && |
556 | !pci_device_linux_sysfs_map_range_wc(dev, map)) |
557 | return 0; |
558 | |
559 | snprintf(name, 255, "%s/%04x:%02x:%02x.%1u/resource%u", |
560 | SYS_BUS_PCI"/sys/bus/pci/devices", |
561 | dev->domain, |
562 | dev->bus, |
563 | dev->dev, |
564 | dev->func, |
565 | map->region); |
566 | |
567 | fd = open(name, open_flags | O_CLOEXEC02000000); |
568 | if (fd == -1) { |
569 | return errno(*__errno_location ()); |
570 | } |
571 | |
572 | |
573 | map->memory = mmap(NULL((void*)0), map->size, prot, MAP_SHARED0x001, fd, offset); |
574 | if (map->memory == MAP_FAILED((void *) -1)) { |
575 | map->memory = NULL((void*)0); |
576 | close(fd); |
577 | return errno(*__errno_location ()); |
578 | } |
579 | |
580 | #ifdef HAVE_MTRR |
581 | if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE(1U<<2)) != 0) { |
582 | sentry.type = MTRR_TYPE_WRBACK; |
583 | } else if ((map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE(1U<<1)) != 0) { |
584 | sentry.type = MTRR_TYPE_WRCOMB; |
585 | } |
586 | |
587 | if (pci_sys->mtrr_fd != -1 && sentry.type != MTRR_TYPE_UNCACHABLE) { |
588 | if (ioctl(pci_sys->mtrr_fd, MTRRIOC_ADD_ENTRY, &sentry) < 0) { |
589 | |
590 | |
591 | fprintf(stderrstderr, "error setting MTRR " |
592 | "(base = 0x%08lx, size = 0x%08x, type = %u) %s (%d)\n", |
593 | sentry.base, sentry.size, sentry.type, |
594 | strerror(errno(*__errno_location ())), errno(*__errno_location ())); |
595 | |
596 | } |
597 | |
598 | mprotect (map->memory, map->size, PROT_NONE0x0); |
599 | err = mprotect (map->memory, map->size, PROT_READ0x1|PROT_WRITE0x2); |
600 | |
601 | if (err != 0) { |
602 | fprintf(stderrstderr, "mprotect(PROT_READ | PROT_WRITE) failed: %s\n", |
603 | strerror(errno(*__errno_location ()))); |
604 | fprintf(stderrstderr, "remapping without mprotect performance kludge.\n"); |
605 | |
606 | munmap(map->memory, map->size); |
607 | map->memory = mmap(NULL((void*)0), map->size, prot, MAP_SHARED0x001, fd, offset); |
608 | if (map->memory == MAP_FAILED((void *) -1)) { |
609 | map->memory = NULL((void*)0); |
610 | close(fd); |
611 | return errno(*__errno_location ()); |
612 | } |
613 | } |
614 | } |
615 | #endif |
616 | |
617 | close(fd); |
618 | |
619 | return 0; |
620 | } |
621 | |
622 | |
623 | |
624 | |
625 | |
626 | |
627 | |
628 | |
629 | |
630 | |
631 | |
632 | |
633 | |
634 | |
635 | |
636 | |
637 | |
638 | static int |
639 | pci_device_linux_sysfs_unmap_range(struct pci_device *dev, |
640 | struct pci_device_mapping *map) |
641 | { |
642 | int err = 0; |
643 | #ifdef HAVE_MTRR |
644 | struct mtrr_sentry sentry = { |
645 | .base = map->base, |
646 | .size = map->size, |
647 | .type = MTRR_TYPE_UNCACHABLE |
648 | }; |
649 | #endif |
650 | |
651 | err = pci_device_generic_unmap_range (dev, map); |
652 | if (err) |
653 | return err; |
654 | |
655 | #ifdef HAVE_MTRR |
656 | if ((map->flags & PCI_DEV_MAP_FLAG_CACHABLE(1U<<2)) != 0) { |
657 | sentry.type = MTRR_TYPE_WRBACK; |
658 | } else if ((map->flags & PCI_DEV_MAP_FLAG_WRITE_COMBINE(1U<<1)) != 0) { |
659 | sentry.type = MTRR_TYPE_WRCOMB; |
660 | } |
661 | |
662 | if (pci_sys->mtrr_fd != -1 && sentry.type != MTRR_TYPE_UNCACHABLE) { |
663 | if (ioctl(pci_sys->mtrr_fd, MTRRIOC_DEL_ENTRY, &sentry) < 0) { |
664 | |
665 | |
666 | fprintf(stderrstderr, "error setting MTRR " |
667 | "(base = 0x%08lx, size = 0x%08x, type = %u) %s (%d)\n", |
668 | sentry.base, sentry.size, sentry.type, |
669 | strerror(errno(*__errno_location ())), errno(*__errno_location ())); |
670 | |
671 | } |
672 | } |
673 | #endif |
674 | |
675 | return err; |
676 | } |
677 | |
678 | static void pci_device_linux_sysfs_enable(struct pci_device *dev) |
679 | { |
680 | char name[256]; |
681 | int fd; |
682 | |
683 | snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/enable", |
684 | SYS_BUS_PCI"/sys/bus/pci/devices", |
685 | dev->domain, |
686 | dev->bus, |
687 | dev->dev, |
688 | dev->func ); |
689 | |
690 | fd = open( name, O_RDWR02 | O_CLOEXEC02000000); |
691 | if (fd == -1) |
692 | return; |
693 | |
694 | write( fd, "1", 1 ); |
695 | close(fd); |
696 | } |
697 | |
698 | static int pci_device_linux_sysfs_boot_vga(struct pci_device *dev) |
699 | { |
700 | char name[256]; |
701 | char reply[3]; |
702 | int fd, bytes_read; |
703 | int ret = 0; |
704 | |
705 | snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/boot_vga", |
706 | SYS_BUS_PCI"/sys/bus/pci/devices", |
707 | dev->domain, |
708 | dev->bus, |
709 | dev->dev, |
710 | dev->func ); |
711 | |
712 | fd = open( name, O_RDONLY00 | O_CLOEXEC02000000); |
713 | if (fd == -1) |
714 | return 0; |
715 | |
716 | bytes_read = read(fd, reply, 1); |
717 | if (bytes_read != 1) |
718 | goto out; |
719 | if (reply[0] == '1') |
720 | ret = 1; |
721 | out: |
722 | close(fd); |
723 | return ret; |
724 | } |
725 | |
726 | static int pci_device_linux_sysfs_has_kernel_driver(struct pci_device *dev) |
727 | { |
728 | char name[256]; |
729 | struct stat dummy; |
730 | int ret; |
731 | |
732 | snprintf( name, 255, "%s/%04x:%02x:%02x.%1u/driver", |
733 | SYS_BUS_PCI"/sys/bus/pci/devices", |
734 | dev->domain, |
735 | dev->bus, |
736 | dev->dev, |
737 | dev->func ); |
738 | |
739 | ret = stat(name, &dummy); |
740 | if (ret < 0) |
741 | return 0; |
742 | return 1; |
743 | } |
744 | |
745 | static struct pci_io_handle * |
746 | pci_device_linux_sysfs_open_device_io(struct pci_io_handle *ret, |
747 | struct pci_device *dev, int bar, |
748 | pciaddr_t base, pciaddr_t size) |
749 | { |
750 | char name[PATH_MAX4096]; |
751 | |
752 | snprintf(name, PATH_MAX4096, "%s/%04x:%02x:%02x.%1u/resource%d", |
753 | SYS_BUS_PCI"/sys/bus/pci/devices", dev->domain, dev->bus, dev->dev, dev->func, bar); |
754 | |
755 | ret->fd = open(name, O_RDWR02 | O_CLOEXEC02000000); |
756 | |
757 | if (ret->fd < 0) |
758 | return NULL((void*)0); |
759 | |
760 | ret->base = base; |
761 | ret->size = size; |
762 | |
763 | return ret; |
764 | } |
765 | |
766 | static struct pci_io_handle * |
767 | pci_device_linux_sysfs_open_legacy_io(struct pci_io_handle *ret, |
768 | struct pci_device *dev, pciaddr_t base, |
769 | pciaddr_t size) |
770 | { |
771 | char name[PATH_MAX4096]; |
772 | |
773 | |
774 | while (dev) { |
775 | snprintf(name, PATH_MAX4096, "/sys/class/pci_bus/%04x:%02x/legacy_io", |
776 | dev->domain, dev->bus); |
777 | |
778 | ret->fd = open(name, O_RDWR02 | O_CLOEXEC02000000); |
779 | if (ret->fd >= 0) |
780 | break; |
781 | |
782 | dev = pci_device_get_parent_bridge(dev); |
783 | } |
784 | |
785 | |
786 | |
787 | |
788 | |
789 | |
790 | |
791 | |
792 | if (ret->fd < 0) { |
793 | if (iopl(3)-1) |
794 | return NULL((void*)0); |
795 | } |
796 | |
797 | ret->base = base; |
798 | ret->size = size; |
799 | |
800 | return ret; |
801 | } |
802 | |
803 | static void |
804 | pci_device_linux_sysfs_close_io(struct pci_device *dev, |
805 | struct pci_io_handle *handle) |
806 | { |
807 | if (handle->fd > -1) |
808 | close(handle->fd); |
809 | } |
810 | |
811 | static uint32_t |
812 | pci_device_linux_sysfs_read32(struct pci_io_handle *handle, uint32_t port) |
813 | { |
814 | uint32_t ret; |
815 | |
816 | if (handle->fd > -1) |
817 | pread(handle->fd, &ret, 4, port + handle->base); |
818 | else |
819 | ret = inl(port + handle->base)-1; |
820 | |
821 | return ret; |
822 | } |
823 | |
824 | static uint16_t |
825 | pci_device_linux_sysfs_read16(struct pci_io_handle *handle, uint32_t port) |
826 | { |
827 | uint16_t ret; |
828 | |
829 | if (handle->fd > -1) |
830 | pread(handle->fd, &ret, 2, port + handle->base); |
831 | else |
832 | ret = inw(port + handle->base)-1; |
833 | |
834 | return ret; |
835 | } |
836 | |
837 | static uint8_t |
838 | pci_device_linux_sysfs_read8(struct pci_io_handle *handle, uint32_t port) |
839 | { |
840 | uint8_t ret; |
841 | |
842 | if (handle->fd > -1) |
843 | pread(handle->fd, &ret, 1, port + handle->base); |
844 | else |
845 | ret = inb(port + handle->base)-1; |
846 | |
847 | return ret; |
848 | } |
849 | |
850 | static void |
851 | pci_device_linux_sysfs_write32(struct pci_io_handle *handle, uint32_t port, |
852 | uint32_t data) |
853 | { |
854 | if (handle->fd > -1) |
855 | pwrite(handle->fd, &data, 4, port + handle->base); |
856 | else |
857 | outl(data, port + handle->base)do {} while (0); |
858 | } |
859 | |
860 | static void |
861 | pci_device_linux_sysfs_write16(struct pci_io_handle *handle, uint32_t port, |
862 | uint16_t data) |
863 | { |
864 | if (handle->fd > -1) |
865 | pwrite(handle->fd, &data, 2, port + handle->base); |
866 | else |
867 | outw(data, port + handle->base)do {} while (0); |
868 | } |
869 | |
870 | static void |
871 | pci_device_linux_sysfs_write8(struct pci_io_handle *handle, uint32_t port, |
872 | uint8_t data) |
873 | { |
874 | if (handle->fd > -1) |
875 | pwrite(handle->fd, &data, 1, port + handle->base); |
876 | else |
877 | outb(data, port + handle->base)do {} while (0); |
878 | } |
879 | |
880 | static int |
881 | pci_device_linux_sysfs_map_legacy(struct pci_device *dev, pciaddr_t base, |
882 | pciaddr_t size, unsigned map_flags, void **addr) |
883 | { |
884 | char name[PATH_MAX4096]; |
885 | int flags = O_RDONLY00; |
886 | int prot = PROT_READ0x1; |
887 | int fd; |
888 | int ret=0; |
889 | |
890 | if (map_flags & PCI_DEV_MAP_FLAG_WRITABLE(1U<<0)) { |
891 | flags = O_RDWR02; ; |
892 | prot |= PROT_WRITE0x2; |
893 | } |
894 | |
895 | |
896 | while (dev) { |
897 | snprintf(name, PATH_MAX4096, "/sys/class/pci_bus/%04x:%02x/legacy_mem", |
898 | dev->domain, dev->bus); |
899 | |
900 | fd = open(name, flags | O_CLOEXEC02000000); |
901 | if (fd >= 0) |
902 | break; |
903 | |
904 | dev = pci_device_get_parent_bridge(dev); |
905 | } |
906 | |
907 | |
908 | if (!dev) |
909 | fd = open("/dev/mem", flags | O_CLOEXEC02000000); |
910 | |
911 | if (fd < 0) |
912 | return errno(*__errno_location ()); |
913 | |
914 | *addr = mmap(NULL((void*)0), size, prot, MAP_SHARED0x001, fd, base); |
915 | if (*addr == MAP_FAILED((void *) -1)) { |
916 | ret = errno(*__errno_location ()); |
917 | } |
918 | |
919 | close(fd); |
920 | return ret; |
921 | } |
922 | |
923 | static int |
924 | pci_device_linux_sysfs_unmap_legacy(struct pci_device *dev, void *addr, pciaddr_t size) |
925 | { |
926 | return munmap(addr, size); |
927 | } |
928 | |
929 | |
930 | static void |
931 | pci_system_linux_destroy(void) |
932 | { |
933 | #ifdef HAVE_MTRR |
934 | if (pci_sys->mtrr_fd != -1) |
935 | close(pci_sys->mtrr_fd); |
936 | #endif |
937 | } |
938 | |
939 | static const struct pci_system_methods linux_sysfs_methods = { |
940 | .destroy = pci_system_linux_destroy, |
941 | .destroy_device = NULL((void*)0), |
942 | .read_rom = pci_device_linux_sysfs_read_rom, |
943 | .probe = pci_device_linux_sysfs_probe, |
944 | .map_range = pci_device_linux_sysfs_map_range, |
945 | .unmap_range = pci_device_linux_sysfs_unmap_range, |
946 | |
947 | .read = pci_device_linux_sysfs_read, |
948 | .write = pci_device_linux_sysfs_write, |
949 | |
950 | .fill_capabilities = pci_fill_capabilities_generic, |
951 | .enable = pci_device_linux_sysfs_enable, |
952 | .boot_vga = pci_device_linux_sysfs_boot_vga, |
953 | .has_kernel_driver = pci_device_linux_sysfs_has_kernel_driver, |
954 | |
955 | .open_device_io = pci_device_linux_sysfs_open_device_io, |
956 | .open_legacy_io = pci_device_linux_sysfs_open_legacy_io, |
957 | .close_io = pci_device_linux_sysfs_close_io, |
958 | .read32 = pci_device_linux_sysfs_read32, |
959 | .read16 = pci_device_linux_sysfs_read16, |
960 | .read8 = pci_device_linux_sysfs_read8, |
961 | .write32 = pci_device_linux_sysfs_write32, |
962 | .write16 = pci_device_linux_sysfs_write16, |
963 | .write8 = pci_device_linux_sysfs_write8, |
964 | |
965 | .map_legacy = pci_device_linux_sysfs_map_legacy, |
966 | .unmap_legacy = pci_device_linux_sysfs_unmap_legacy, |
967 | }; |