diff options
Diffstat (limited to 'drivers/pci/pci-sysfs.c')
-rw-r--r-- | drivers/pci/pci-sysfs.c | 260 |
1 files changed, 195 insertions, 65 deletions
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 9c718583a237..110022d78689 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c | |||
@@ -16,6 +16,7 @@ | |||
16 | 16 | ||
17 | 17 | ||
18 | #include <linux/kernel.h> | 18 | #include <linux/kernel.h> |
19 | #include <linux/sched.h> | ||
19 | #include <linux/pci.h> | 20 | #include <linux/pci.h> |
20 | #include <linux/stat.h> | 21 | #include <linux/stat.h> |
21 | #include <linux/topology.h> | 22 | #include <linux/topology.h> |
@@ -422,7 +423,7 @@ pci_write_vpd(struct kobject *kobj, struct bin_attribute *bin_attr, | |||
422 | * Reads 1, 2, or 4 bytes from legacy I/O port space using an arch specific | 423 | * Reads 1, 2, or 4 bytes from legacy I/O port space using an arch specific |
423 | * callback routine (pci_legacy_read). | 424 | * callback routine (pci_legacy_read). |
424 | */ | 425 | */ |
425 | ssize_t | 426 | static ssize_t |
426 | pci_read_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr, | 427 | pci_read_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr, |
427 | char *buf, loff_t off, size_t count) | 428 | char *buf, loff_t off, size_t count) |
428 | { | 429 | { |
@@ -447,7 +448,7 @@ pci_read_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr, | |||
447 | * Writes 1, 2, or 4 bytes from legacy I/O port space using an arch specific | 448 | * Writes 1, 2, or 4 bytes from legacy I/O port space using an arch specific |
448 | * callback routine (pci_legacy_write). | 449 | * callback routine (pci_legacy_write). |
449 | */ | 450 | */ |
450 | ssize_t | 451 | static ssize_t |
451 | pci_write_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr, | 452 | pci_write_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr, |
452 | char *buf, loff_t off, size_t count) | 453 | char *buf, loff_t off, size_t count) |
453 | { | 454 | { |
@@ -467,11 +468,11 @@ pci_write_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr, | |||
467 | * @attr: struct bin_attribute for this file | 468 | * @attr: struct bin_attribute for this file |
468 | * @vma: struct vm_area_struct passed to mmap | 469 | * @vma: struct vm_area_struct passed to mmap |
469 | * | 470 | * |
470 | * Uses an arch specific callback, pci_mmap_legacy_page_range, to mmap | 471 | * Uses an arch specific callback, pci_mmap_legacy_mem_page_range, to mmap |
471 | * legacy memory space (first meg of bus space) into application virtual | 472 | * legacy memory space (first meg of bus space) into application virtual |
472 | * memory space. | 473 | * memory space. |
473 | */ | 474 | */ |
474 | int | 475 | static int |
475 | pci_mmap_legacy_mem(struct kobject *kobj, struct bin_attribute *attr, | 476 | pci_mmap_legacy_mem(struct kobject *kobj, struct bin_attribute *attr, |
476 | struct vm_area_struct *vma) | 477 | struct vm_area_struct *vma) |
477 | { | 478 | { |
@@ -479,11 +480,109 @@ pci_mmap_legacy_mem(struct kobject *kobj, struct bin_attribute *attr, | |||
479 | struct device, | 480 | struct device, |
480 | kobj)); | 481 | kobj)); |
481 | 482 | ||
482 | return pci_mmap_legacy_page_range(bus, vma); | 483 | return pci_mmap_legacy_page_range(bus, vma, pci_mmap_mem); |
484 | } | ||
485 | |||
486 | /** | ||
487 | * pci_mmap_legacy_io - map legacy PCI IO into user memory space | ||
488 | * @kobj: kobject corresponding to device to be mapped | ||
489 | * @attr: struct bin_attribute for this file | ||
490 | * @vma: struct vm_area_struct passed to mmap | ||
491 | * | ||
492 | * Uses an arch specific callback, pci_mmap_legacy_io_page_range, to mmap | ||
493 | * legacy IO space (first meg of bus space) into application virtual | ||
494 | * memory space. Returns -ENOSYS if the operation isn't supported | ||
495 | */ | ||
496 | static int | ||
497 | pci_mmap_legacy_io(struct kobject *kobj, struct bin_attribute *attr, | ||
498 | struct vm_area_struct *vma) | ||
499 | { | ||
500 | struct pci_bus *bus = to_pci_bus(container_of(kobj, | ||
501 | struct device, | ||
502 | kobj)); | ||
503 | |||
504 | return pci_mmap_legacy_page_range(bus, vma, pci_mmap_io); | ||
505 | } | ||
506 | |||
507 | /** | ||
508 | * pci_create_legacy_files - create legacy I/O port and memory files | ||
509 | * @b: bus to create files under | ||
510 | * | ||
511 | * Some platforms allow access to legacy I/O port and ISA memory space on | ||
512 | * a per-bus basis. This routine creates the files and ties them into | ||
513 | * their associated read, write and mmap files from pci-sysfs.c | ||
514 | * | ||
515 | * On error unwind, but don't propogate the error to the caller | ||
516 | * as it is ok to set up the PCI bus without these files. | ||
517 | */ | ||
518 | void pci_create_legacy_files(struct pci_bus *b) | ||
519 | { | ||
520 | int error; | ||
521 | |||
522 | b->legacy_io = kzalloc(sizeof(struct bin_attribute) * 2, | ||
523 | GFP_ATOMIC); | ||
524 | if (!b->legacy_io) | ||
525 | goto kzalloc_err; | ||
526 | |||
527 | b->legacy_io->attr.name = "legacy_io"; | ||
528 | b->legacy_io->size = 0xffff; | ||
529 | b->legacy_io->attr.mode = S_IRUSR | S_IWUSR; | ||
530 | b->legacy_io->read = pci_read_legacy_io; | ||
531 | b->legacy_io->write = pci_write_legacy_io; | ||
532 | b->legacy_io->mmap = pci_mmap_legacy_io; | ||
533 | error = device_create_bin_file(&b->dev, b->legacy_io); | ||
534 | if (error) | ||
535 | goto legacy_io_err; | ||
536 | |||
537 | /* Allocated above after the legacy_io struct */ | ||
538 | b->legacy_mem = b->legacy_io + 1; | ||
539 | b->legacy_mem->attr.name = "legacy_mem"; | ||
540 | b->legacy_mem->size = 1024*1024; | ||
541 | b->legacy_mem->attr.mode = S_IRUSR | S_IWUSR; | ||
542 | b->legacy_mem->mmap = pci_mmap_legacy_mem; | ||
543 | error = device_create_bin_file(&b->dev, b->legacy_mem); | ||
544 | if (error) | ||
545 | goto legacy_mem_err; | ||
546 | |||
547 | return; | ||
548 | |||
549 | legacy_mem_err: | ||
550 | device_remove_bin_file(&b->dev, b->legacy_io); | ||
551 | legacy_io_err: | ||
552 | kfree(b->legacy_io); | ||
553 | b->legacy_io = NULL; | ||
554 | kzalloc_err: | ||
555 | printk(KERN_WARNING "pci: warning: could not create legacy I/O port " | ||
556 | "and ISA memory resources to sysfs\n"); | ||
557 | return; | ||
558 | } | ||
559 | |||
560 | void pci_remove_legacy_files(struct pci_bus *b) | ||
561 | { | ||
562 | if (b->legacy_io) { | ||
563 | device_remove_bin_file(&b->dev, b->legacy_io); | ||
564 | device_remove_bin_file(&b->dev, b->legacy_mem); | ||
565 | kfree(b->legacy_io); /* both are allocated here */ | ||
566 | } | ||
483 | } | 567 | } |
484 | #endif /* HAVE_PCI_LEGACY */ | 568 | #endif /* HAVE_PCI_LEGACY */ |
485 | 569 | ||
486 | #ifdef HAVE_PCI_MMAP | 570 | #ifdef HAVE_PCI_MMAP |
571 | |||
572 | static int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma) | ||
573 | { | ||
574 | unsigned long nr, start, size; | ||
575 | |||
576 | nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; | ||
577 | start = vma->vm_pgoff; | ||
578 | size = pci_resource_len(pdev, resno) >> PAGE_SHIFT; | ||
579 | if (start < size && size - start >= nr) | ||
580 | return 1; | ||
581 | WARN(1, "process \"%s\" tried to map 0x%08lx-0x%08lx on %s BAR %d (size 0x%08lx)\n", | ||
582 | current->comm, start, start+nr, pci_name(pdev), resno, size); | ||
583 | return 0; | ||
584 | } | ||
585 | |||
487 | /** | 586 | /** |
488 | * pci_mmap_resource - map a PCI resource into user memory space | 587 | * pci_mmap_resource - map a PCI resource into user memory space |
489 | * @kobj: kobject for mapping | 588 | * @kobj: kobject for mapping |
@@ -510,6 +609,9 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, | |||
510 | if (i >= PCI_ROM_RESOURCE) | 609 | if (i >= PCI_ROM_RESOURCE) |
511 | return -ENODEV; | 610 | return -ENODEV; |
512 | 611 | ||
612 | if (!pci_mmap_fits(pdev, i, vma)) | ||
613 | return -EINVAL; | ||
614 | |||
513 | /* pci_mmap_page_range() expects the same kind of entry as coming | 615 | /* pci_mmap_page_range() expects the same kind of entry as coming |
514 | * from /proc/bus/pci/ which is a "user visible" value. If this is | 616 | * from /proc/bus/pci/ which is a "user visible" value. If this is |
515 | * different from the resource itself, arch will do necessary fixup. | 617 | * different from the resource itself, arch will do necessary fixup. |
@@ -696,7 +798,7 @@ static struct bin_attribute pci_config_attr = { | |||
696 | .name = "config", | 798 | .name = "config", |
697 | .mode = S_IRUGO | S_IWUSR, | 799 | .mode = S_IRUGO | S_IWUSR, |
698 | }, | 800 | }, |
699 | .size = 256, | 801 | .size = PCI_CFG_SPACE_SIZE, |
700 | .read = pci_read_config, | 802 | .read = pci_read_config, |
701 | .write = pci_write_config, | 803 | .write = pci_write_config, |
702 | }; | 804 | }; |
@@ -706,7 +808,7 @@ static struct bin_attribute pcie_config_attr = { | |||
706 | .name = "config", | 808 | .name = "config", |
707 | .mode = S_IRUGO | S_IWUSR, | 809 | .mode = S_IRUGO | S_IWUSR, |
708 | }, | 810 | }, |
709 | .size = 4096, | 811 | .size = PCI_CFG_SPACE_EXP_SIZE, |
710 | .read = pci_read_config, | 812 | .read = pci_read_config, |
711 | .write = pci_write_config, | 813 | .write = pci_write_config, |
712 | }; | 814 | }; |
@@ -716,86 +818,103 @@ int __attribute__ ((weak)) pcibios_add_platform_entries(struct pci_dev *dev) | |||
716 | return 0; | 818 | return 0; |
717 | } | 819 | } |
718 | 820 | ||
821 | static int pci_create_capabilities_sysfs(struct pci_dev *dev) | ||
822 | { | ||
823 | int retval; | ||
824 | struct bin_attribute *attr; | ||
825 | |||
826 | /* If the device has VPD, try to expose it in sysfs. */ | ||
827 | if (dev->vpd) { | ||
828 | attr = kzalloc(sizeof(*attr), GFP_ATOMIC); | ||
829 | if (!attr) | ||
830 | return -ENOMEM; | ||
831 | |||
832 | attr->size = dev->vpd->len; | ||
833 | attr->attr.name = "vpd"; | ||
834 | attr->attr.mode = S_IRUSR | S_IWUSR; | ||
835 | attr->read = pci_read_vpd; | ||
836 | attr->write = pci_write_vpd; | ||
837 | retval = sysfs_create_bin_file(&dev->dev.kobj, attr); | ||
838 | if (retval) { | ||
839 | kfree(dev->vpd->attr); | ||
840 | return retval; | ||
841 | } | ||
842 | dev->vpd->attr = attr; | ||
843 | } | ||
844 | |||
845 | /* Active State Power Management */ | ||
846 | pcie_aspm_create_sysfs_dev_files(dev); | ||
847 | |||
848 | return 0; | ||
849 | } | ||
850 | |||
719 | int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) | 851 | int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) |
720 | { | 852 | { |
721 | struct bin_attribute *attr = NULL; | ||
722 | int retval; | 853 | int retval; |
854 | int rom_size = 0; | ||
855 | struct bin_attribute *attr; | ||
723 | 856 | ||
724 | if (!sysfs_initialized) | 857 | if (!sysfs_initialized) |
725 | return -EACCES; | 858 | return -EACCES; |
726 | 859 | ||
727 | if (pdev->cfg_size < 4096) | 860 | if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE) |
728 | retval = sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr); | 861 | retval = sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr); |
729 | else | 862 | else |
730 | retval = sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr); | 863 | retval = sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr); |
731 | if (retval) | 864 | if (retval) |
732 | goto err; | 865 | goto err; |
733 | 866 | ||
734 | /* If the device has VPD, try to expose it in sysfs. */ | ||
735 | if (pdev->vpd) { | ||
736 | attr = kzalloc(sizeof(*attr), GFP_ATOMIC); | ||
737 | if (attr) { | ||
738 | pdev->vpd->attr = attr; | ||
739 | attr->size = pdev->vpd->len; | ||
740 | attr->attr.name = "vpd"; | ||
741 | attr->attr.mode = S_IRUSR | S_IWUSR; | ||
742 | attr->read = pci_read_vpd; | ||
743 | attr->write = pci_write_vpd; | ||
744 | retval = sysfs_create_bin_file(&pdev->dev.kobj, attr); | ||
745 | if (retval) | ||
746 | goto err_vpd; | ||
747 | } else { | ||
748 | retval = -ENOMEM; | ||
749 | goto err_config_file; | ||
750 | } | ||
751 | } | ||
752 | |||
753 | retval = pci_create_resource_files(pdev); | 867 | retval = pci_create_resource_files(pdev); |
754 | if (retval) | 868 | if (retval) |
755 | goto err_vpd_file; | 869 | goto err_config_file; |
870 | |||
871 | if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) | ||
872 | rom_size = pci_resource_len(pdev, PCI_ROM_RESOURCE); | ||
873 | else if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW) | ||
874 | rom_size = 0x20000; | ||
756 | 875 | ||
757 | /* If the device has a ROM, try to expose it in sysfs. */ | 876 | /* If the device has a ROM, try to expose it in sysfs. */ |
758 | if (pci_resource_len(pdev, PCI_ROM_RESOURCE) || | 877 | if (rom_size) { |
759 | (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW)) { | ||
760 | attr = kzalloc(sizeof(*attr), GFP_ATOMIC); | 878 | attr = kzalloc(sizeof(*attr), GFP_ATOMIC); |
761 | if (attr) { | 879 | if (!attr) { |
762 | pdev->rom_attr = attr; | ||
763 | attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE); | ||
764 | attr->attr.name = "rom"; | ||
765 | attr->attr.mode = S_IRUSR; | ||
766 | attr->read = pci_read_rom; | ||
767 | attr->write = pci_write_rom; | ||
768 | retval = sysfs_create_bin_file(&pdev->dev.kobj, attr); | ||
769 | if (retval) | ||
770 | goto err_rom; | ||
771 | } else { | ||
772 | retval = -ENOMEM; | 880 | retval = -ENOMEM; |
773 | goto err_resource_files; | 881 | goto err_resource_files; |
774 | } | 882 | } |
883 | attr->size = rom_size; | ||
884 | attr->attr.name = "rom"; | ||
885 | attr->attr.mode = S_IRUSR; | ||
886 | attr->read = pci_read_rom; | ||
887 | attr->write = pci_write_rom; | ||
888 | retval = sysfs_create_bin_file(&pdev->dev.kobj, attr); | ||
889 | if (retval) { | ||
890 | kfree(attr); | ||
891 | goto err_resource_files; | ||
892 | } | ||
893 | pdev->rom_attr = attr; | ||
775 | } | 894 | } |
895 | |||
776 | /* add platform-specific attributes */ | 896 | /* add platform-specific attributes */ |
777 | if (pcibios_add_platform_entries(pdev)) | 897 | retval = pcibios_add_platform_entries(pdev); |
898 | if (retval) | ||
778 | goto err_rom_file; | 899 | goto err_rom_file; |
779 | 900 | ||
780 | pcie_aspm_create_sysfs_dev_files(pdev); | 901 | /* add sysfs entries for various capabilities */ |
902 | retval = pci_create_capabilities_sysfs(pdev); | ||
903 | if (retval) | ||
904 | goto err_rom_file; | ||
781 | 905 | ||
782 | return 0; | 906 | return 0; |
783 | 907 | ||
784 | err_rom_file: | 908 | err_rom_file: |
785 | if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) | 909 | if (rom_size) { |
786 | sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr); | 910 | sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr); |
787 | err_rom: | 911 | kfree(pdev->rom_attr); |
788 | kfree(pdev->rom_attr); | 912 | pdev->rom_attr = NULL; |
913 | } | ||
789 | err_resource_files: | 914 | err_resource_files: |
790 | pci_remove_resource_files(pdev); | 915 | pci_remove_resource_files(pdev); |
791 | err_vpd_file: | ||
792 | if (pdev->vpd) { | ||
793 | sysfs_remove_bin_file(&pdev->dev.kobj, pdev->vpd->attr); | ||
794 | err_vpd: | ||
795 | kfree(pdev->vpd->attr); | ||
796 | } | ||
797 | err_config_file: | 916 | err_config_file: |
798 | if (pdev->cfg_size < 4096) | 917 | if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE) |
799 | sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr); | 918 | sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr); |
800 | else | 919 | else |
801 | sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr); | 920 | sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr); |
@@ -803,6 +922,16 @@ err: | |||
803 | return retval; | 922 | return retval; |
804 | } | 923 | } |
805 | 924 | ||
925 | static void pci_remove_capabilities_sysfs(struct pci_dev *dev) | ||
926 | { | ||
927 | if (dev->vpd && dev->vpd->attr) { | ||
928 | sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr); | ||
929 | kfree(dev->vpd->attr); | ||
930 | } | ||
931 | |||
932 | pcie_aspm_remove_sysfs_dev_files(dev); | ||
933 | } | ||
934 | |||
806 | /** | 935 | /** |
807 | * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files | 936 | * pci_remove_sysfs_dev_files - cleanup PCI specific sysfs files |
808 | * @pdev: device whose entries we should free | 937 | * @pdev: device whose entries we should free |
@@ -811,27 +940,28 @@ err: | |||
811 | */ | 940 | */ |
812 | void pci_remove_sysfs_dev_files(struct pci_dev *pdev) | 941 | void pci_remove_sysfs_dev_files(struct pci_dev *pdev) |
813 | { | 942 | { |
943 | int rom_size = 0; | ||
944 | |||
814 | if (!sysfs_initialized) | 945 | if (!sysfs_initialized) |
815 | return; | 946 | return; |
816 | 947 | ||
817 | pcie_aspm_remove_sysfs_dev_files(pdev); | 948 | pci_remove_capabilities_sysfs(pdev); |
818 | 949 | ||
819 | if (pdev->vpd) { | 950 | if (pdev->cfg_size < PCI_CFG_SPACE_EXP_SIZE) |
820 | sysfs_remove_bin_file(&pdev->dev.kobj, pdev->vpd->attr); | ||
821 | kfree(pdev->vpd->attr); | ||
822 | } | ||
823 | if (pdev->cfg_size < 4096) | ||
824 | sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr); | 951 | sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr); |
825 | else | 952 | else |
826 | sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr); | 953 | sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr); |
827 | 954 | ||
828 | pci_remove_resource_files(pdev); | 955 | pci_remove_resource_files(pdev); |
829 | 956 | ||
830 | if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) { | 957 | if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) |
831 | if (pdev->rom_attr) { | 958 | rom_size = pci_resource_len(pdev, PCI_ROM_RESOURCE); |
832 | sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr); | 959 | else if (pdev->resource[PCI_ROM_RESOURCE].flags & IORESOURCE_ROM_SHADOW) |
833 | kfree(pdev->rom_attr); | 960 | rom_size = 0x20000; |
834 | } | 961 | |
962 | if (rom_size && pdev->rom_attr) { | ||
963 | sysfs_remove_bin_file(&pdev->dev.kobj, pdev->rom_attr); | ||
964 | kfree(pdev->rom_attr); | ||
835 | } | 965 | } |
836 | } | 966 | } |
837 | 967 | ||