diff options
| -rw-r--r-- | arch/x86/pci/acpi.c | 103 | ||||
| -rw-r--r-- | drivers/pci/bus.c | 70 | ||||
| -rw-r--r-- | drivers/pci/hotplug/ibmphp_ebda.c | 6 | ||||
| -rw-r--r-- | drivers/pci/pci-sysfs.c | 23 | ||||
| -rw-r--r-- | drivers/pci/pci.c | 12 | ||||
| -rw-r--r-- | drivers/pci/pci.h | 7 | ||||
| -rw-r--r-- | drivers/pci/proc.c | 2 |
7 files changed, 174 insertions, 49 deletions
diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 15466c096ba5..0972315c3860 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c | |||
| @@ -138,7 +138,6 @@ setup_resource(struct acpi_resource *acpi_res, void *data) | |||
| 138 | struct acpi_resource_address64 addr; | 138 | struct acpi_resource_address64 addr; |
| 139 | acpi_status status; | 139 | acpi_status status; |
| 140 | unsigned long flags; | 140 | unsigned long flags; |
| 141 | struct resource *root, *conflict; | ||
| 142 | u64 start, end; | 141 | u64 start, end; |
| 143 | 142 | ||
| 144 | status = resource_to_addr(acpi_res, &addr); | 143 | status = resource_to_addr(acpi_res, &addr); |
| @@ -146,12 +145,10 @@ setup_resource(struct acpi_resource *acpi_res, void *data) | |||
| 146 | return AE_OK; | 145 | return AE_OK; |
| 147 | 146 | ||
| 148 | if (addr.resource_type == ACPI_MEMORY_RANGE) { | 147 | if (addr.resource_type == ACPI_MEMORY_RANGE) { |
| 149 | root = &iomem_resource; | ||
| 150 | flags = IORESOURCE_MEM; | 148 | flags = IORESOURCE_MEM; |
| 151 | if (addr.info.mem.caching == ACPI_PREFETCHABLE_MEMORY) | 149 | if (addr.info.mem.caching == ACPI_PREFETCHABLE_MEMORY) |
| 152 | flags |= IORESOURCE_PREFETCH; | 150 | flags |= IORESOURCE_PREFETCH; |
| 153 | } else if (addr.resource_type == ACPI_IO_RANGE) { | 151 | } else if (addr.resource_type == ACPI_IO_RANGE) { |
| 154 | root = &ioport_resource; | ||
| 155 | flags = IORESOURCE_IO; | 152 | flags = IORESOURCE_IO; |
| 156 | } else | 153 | } else |
| 157 | return AE_OK; | 154 | return AE_OK; |
| @@ -172,25 +169,90 @@ setup_resource(struct acpi_resource *acpi_res, void *data) | |||
| 172 | return AE_OK; | 169 | return AE_OK; |
| 173 | } | 170 | } |
| 174 | 171 | ||
| 175 | conflict = insert_resource_conflict(root, res); | 172 | info->res_num++; |
| 176 | if (conflict) { | 173 | if (addr.translation_offset) |
| 177 | dev_err(&info->bridge->dev, | 174 | dev_info(&info->bridge->dev, "host bridge window %pR " |
| 178 | "address space collision: host bridge window %pR " | 175 | "(PCI address [%#llx-%#llx])\n", |
| 179 | "conflicts with %s %pR\n", | 176 | res, res->start - addr.translation_offset, |
| 180 | res, conflict->name, conflict); | 177 | res->end - addr.translation_offset); |
| 181 | } else { | 178 | else |
| 182 | pci_bus_add_resource(info->bus, res, 0); | 179 | dev_info(&info->bridge->dev, "host bridge window %pR\n", res); |
| 183 | info->res_num++; | 180 | |
| 184 | if (addr.translation_offset) | 181 | return AE_OK; |
| 185 | dev_info(&info->bridge->dev, "host bridge window %pR " | 182 | } |
| 186 | "(PCI address [%#llx-%#llx])\n", | 183 | |
| 187 | res, res->start - addr.translation_offset, | 184 | static bool resource_contains(struct resource *res, resource_size_t point) |
| 188 | res->end - addr.translation_offset); | 185 | { |
| 186 | if (res->start <= point && point <= res->end) | ||
| 187 | return true; | ||
| 188 | return false; | ||
| 189 | } | ||
| 190 | |||
| 191 | static void coalesce_windows(struct pci_root_info *info, int type) | ||
| 192 | { | ||
| 193 | int i, j; | ||
| 194 | struct resource *res1, *res2; | ||
| 195 | |||
| 196 | for (i = 0; i < info->res_num; i++) { | ||
| 197 | res1 = &info->res[i]; | ||
| 198 | if (!(res1->flags & type)) | ||
| 199 | continue; | ||
| 200 | |||
| 201 | for (j = i + 1; j < info->res_num; j++) { | ||
| 202 | res2 = &info->res[j]; | ||
| 203 | if (!(res2->flags & type)) | ||
| 204 | continue; | ||
| 205 | |||
| 206 | /* | ||
| 207 | * I don't like throwing away windows because then | ||
| 208 | * our resources no longer match the ACPI _CRS, but | ||
| 209 | * the kernel resource tree doesn't allow overlaps. | ||
| 210 | */ | ||
| 211 | if (resource_contains(res1, res2->start) || | ||
| 212 | resource_contains(res1, res2->end) || | ||
| 213 | resource_contains(res2, res1->start) || | ||
| 214 | resource_contains(res2, res1->end)) { | ||
| 215 | res1->start = min(res1->start, res2->start); | ||
| 216 | res1->end = max(res1->end, res2->end); | ||
| 217 | dev_info(&info->bridge->dev, | ||
| 218 | "host bridge window expanded to %pR; %pR ignored\n", | ||
| 219 | res1, res2); | ||
| 220 | res2->flags = 0; | ||
| 221 | } | ||
| 222 | } | ||
| 223 | } | ||
| 224 | } | ||
| 225 | |||
| 226 | static void add_resources(struct pci_root_info *info) | ||
| 227 | { | ||
| 228 | int i; | ||
| 229 | struct resource *res, *root, *conflict; | ||
| 230 | |||
| 231 | if (!pci_use_crs) | ||
| 232 | return; | ||
| 233 | |||
| 234 | coalesce_windows(info, IORESOURCE_MEM); | ||
| 235 | coalesce_windows(info, IORESOURCE_IO); | ||
| 236 | |||
| 237 | for (i = 0; i < info->res_num; i++) { | ||
| 238 | res = &info->res[i]; | ||
| 239 | |||
| 240 | if (res->flags & IORESOURCE_MEM) | ||
| 241 | root = &iomem_resource; | ||
| 242 | else if (res->flags & IORESOURCE_IO) | ||
| 243 | root = &ioport_resource; | ||
| 189 | else | 244 | else |
| 190 | dev_info(&info->bridge->dev, | 245 | continue; |
| 191 | "host bridge window %pR\n", res); | 246 | |
| 247 | conflict = insert_resource_conflict(root, res); | ||
| 248 | if (conflict) | ||
| 249 | dev_err(&info->bridge->dev, | ||
| 250 | "address space collision: host bridge window %pR " | ||
| 251 | "conflicts with %s %pR\n", | ||
| 252 | res, conflict->name, conflict); | ||
| 253 | else | ||
| 254 | pci_bus_add_resource(info->bus, res, 0); | ||
| 192 | } | 255 | } |
| 193 | return AE_OK; | ||
| 194 | } | 256 | } |
| 195 | 257 | ||
| 196 | static void | 258 | static void |
| @@ -224,6 +286,7 @@ get_current_resources(struct acpi_device *device, int busnum, | |||
| 224 | acpi_walk_resources(device->handle, METHOD_NAME__CRS, setup_resource, | 286 | acpi_walk_resources(device->handle, METHOD_NAME__CRS, setup_resource, |
| 225 | &info); | 287 | &info); |
| 226 | 288 | ||
| 289 | add_resources(&info); | ||
| 227 | return; | 290 | return; |
| 228 | 291 | ||
| 229 | name_alloc_fail: | 292 | name_alloc_fail: |
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 5624db8c9ad0..003170ea2e39 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c | |||
| @@ -64,17 +64,57 @@ void pci_bus_remove_resources(struct pci_bus *bus) | |||
| 64 | } | 64 | } |
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | static bool pci_bus_resource_better(struct resource *res1, bool pos1, | ||
| 68 | struct resource *res2, bool pos2) | ||
| 69 | { | ||
| 70 | /* If exactly one is positive decode, always prefer that one */ | ||
| 71 | if (pos1 != pos2) | ||
| 72 | return pos1 ? true : false; | ||
| 73 | |||
| 74 | /* Prefer the one that contains the highest address */ | ||
| 75 | if (res1->end != res2->end) | ||
| 76 | return (res1->end > res2->end) ? true : false; | ||
| 77 | |||
| 78 | /* Otherwise, prefer the one with highest "center of gravity" */ | ||
| 79 | if (res1->start != res2->start) | ||
| 80 | return (res1->start > res2->start) ? true : false; | ||
| 81 | |||
| 82 | /* Otherwise, choose one arbitrarily (but consistently) */ | ||
| 83 | return (res1 > res2) ? true : false; | ||
| 84 | } | ||
| 85 | |||
| 86 | static bool pci_bus_resource_positive(struct pci_bus *bus, struct resource *res) | ||
| 87 | { | ||
| 88 | struct pci_bus_resource *bus_res; | ||
| 89 | |||
| 90 | /* | ||
| 91 | * This relies on the fact that pci_bus.resource[] refers to P2P or | ||
| 92 | * CardBus bridge base/limit registers, which are always positively | ||
| 93 | * decoded. The pci_bus.resources list contains host bridge or | ||
| 94 | * subtractively decoded resources. | ||
| 95 | */ | ||
| 96 | list_for_each_entry(bus_res, &bus->resources, list) { | ||
| 97 | if (bus_res->res == res) | ||
| 98 | return (bus_res->flags & PCI_SUBTRACTIVE_DECODE) ? | ||
| 99 | false : true; | ||
| 100 | } | ||
| 101 | return true; | ||
| 102 | } | ||
| 103 | |||
| 67 | /* | 104 | /* |
| 68 | * Find the highest-address bus resource below the cursor "res". If the | 105 | * Find the next-best bus resource after the cursor "res". If the cursor is |
| 69 | * cursor is NULL, return the highest resource. | 106 | * NULL, return the best resource. "Best" means that we prefer positive |
| 107 | * decode regions over subtractive decode, then those at higher addresses. | ||
| 70 | */ | 108 | */ |
| 71 | static struct resource *pci_bus_find_resource_prev(struct pci_bus *bus, | 109 | static struct resource *pci_bus_find_resource_prev(struct pci_bus *bus, |
| 72 | unsigned int type, | 110 | unsigned int type, |
| 73 | struct resource *res) | 111 | struct resource *res) |
| 74 | { | 112 | { |
| 113 | bool res_pos, r_pos, prev_pos = false; | ||
| 75 | struct resource *r, *prev = NULL; | 114 | struct resource *r, *prev = NULL; |
| 76 | int i; | 115 | int i; |
| 77 | 116 | ||
| 117 | res_pos = pci_bus_resource_positive(bus, res); | ||
| 78 | pci_bus_for_each_resource(bus, r, i) { | 118 | pci_bus_for_each_resource(bus, r, i) { |
| 79 | if (!r) | 119 | if (!r) |
| 80 | continue; | 120 | continue; |
| @@ -82,26 +122,14 @@ static struct resource *pci_bus_find_resource_prev(struct pci_bus *bus, | |||
| 82 | if ((r->flags & IORESOURCE_TYPE_BITS) != type) | 122 | if ((r->flags & IORESOURCE_TYPE_BITS) != type) |
| 83 | continue; | 123 | continue; |
| 84 | 124 | ||
| 85 | /* If this resource is at or past the cursor, skip it */ | 125 | r_pos = pci_bus_resource_positive(bus, r); |
| 86 | if (res) { | 126 | if (!res || pci_bus_resource_better(res, res_pos, r, r_pos)) { |
| 87 | if (r == res) | 127 | if (!prev || pci_bus_resource_better(r, r_pos, |
| 88 | continue; | 128 | prev, prev_pos)) { |
| 89 | if (r->end > res->end) | 129 | prev = r; |
| 90 | continue; | 130 | prev_pos = r_pos; |
| 91 | if (r->end == res->end && r->start > res->start) | 131 | } |
| 92 | continue; | ||
| 93 | } | 132 | } |
| 94 | |||
| 95 | if (!prev) | ||
| 96 | prev = r; | ||
| 97 | |||
| 98 | /* | ||
| 99 | * A small resource is higher than a large one that ends at | ||
| 100 | * the same address. | ||
| 101 | */ | ||
| 102 | if (r->end > prev->end || | ||
| 103 | (r->end == prev->end && r->start > prev->start)) | ||
| 104 | prev = r; | ||
| 105 | } | 133 | } |
| 106 | 134 | ||
| 107 | return prev; | 135 | return prev; |
diff --git a/drivers/pci/hotplug/ibmphp_ebda.c b/drivers/pci/hotplug/ibmphp_ebda.c index 5becbdee4027..2850e64dedae 100644 --- a/drivers/pci/hotplug/ibmphp_ebda.c +++ b/drivers/pci/hotplug/ibmphp_ebda.c | |||
| @@ -276,6 +276,12 @@ int __init ibmphp_access_ebda (void) | |||
| 276 | 276 | ||
| 277 | for (;;) { | 277 | for (;;) { |
| 278 | offset = next_offset; | 278 | offset = next_offset; |
| 279 | |||
| 280 | /* Make sure what we read is still in the mapped section */ | ||
| 281 | if (WARN(offset > (ebda_sz * 1024 - 4), | ||
| 282 | "ibmphp_ebda: next read is beyond ebda_sz\n")) | ||
| 283 | break; | ||
| 284 | |||
| 279 | next_offset = readw (io_mem + offset); /* offset of next blk */ | 285 | next_offset = readw (io_mem + offset); /* offset of next blk */ |
| 280 | 286 | ||
| 281 | offset += 2; | 287 | offset += 2; |
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index b5a7d9bfcb24..95712a375cd5 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c | |||
| @@ -705,17 +705,21 @@ void pci_remove_legacy_files(struct pci_bus *b) | |||
| 705 | 705 | ||
| 706 | #ifdef HAVE_PCI_MMAP | 706 | #ifdef HAVE_PCI_MMAP |
| 707 | 707 | ||
| 708 | int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma) | 708 | int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma, |
| 709 | enum pci_mmap_api mmap_api) | ||
| 709 | { | 710 | { |
| 710 | unsigned long nr, start, size; | 711 | unsigned long nr, start, size, pci_start; |
| 711 | 712 | ||
| 713 | if (pci_resource_len(pdev, resno) == 0) | ||
| 714 | return 0; | ||
| 712 | nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; | 715 | nr = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT; |
| 713 | start = vma->vm_pgoff; | 716 | start = vma->vm_pgoff; |
| 714 | size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1; | 717 | size = ((pci_resource_len(pdev, resno) - 1) >> PAGE_SHIFT) + 1; |
| 715 | if (start < size && size - start >= nr) | 718 | pci_start = (mmap_api == PCI_MMAP_SYSFS) ? |
| 719 | pci_resource_start(pdev, resno) >> PAGE_SHIFT : 0; | ||
| 720 | if (start >= pci_start && start < pci_start + size && | ||
| 721 | start + nr <= pci_start + size) | ||
| 716 | return 1; | 722 | return 1; |
| 717 | WARN(1, "process \"%s\" tried to map 0x%08lx-0x%08lx on %s BAR %d (size 0x%08lx)\n", | ||
| 718 | current->comm, start, start+nr, pci_name(pdev), resno, size); | ||
| 719 | return 0; | 723 | return 0; |
| 720 | } | 724 | } |
| 721 | 725 | ||
| @@ -745,8 +749,15 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, | |||
| 745 | if (i >= PCI_ROM_RESOURCE) | 749 | if (i >= PCI_ROM_RESOURCE) |
| 746 | return -ENODEV; | 750 | return -ENODEV; |
| 747 | 751 | ||
| 748 | if (!pci_mmap_fits(pdev, i, vma)) | 752 | if (!pci_mmap_fits(pdev, i, vma, PCI_MMAP_SYSFS)) { |
| 753 | WARN(1, "process \"%s\" tried to map 0x%08lx bytes " | ||
| 754 | "at page 0x%08lx on %s BAR %d (start 0x%16Lx, size 0x%16Lx)\n", | ||
| 755 | current->comm, vma->vm_end-vma->vm_start, vma->vm_pgoff, | ||
| 756 | pci_name(pdev), i, | ||
| 757 | (u64)pci_resource_start(pdev, i), | ||
| 758 | (u64)pci_resource_len(pdev, i)); | ||
| 749 | return -EINVAL; | 759 | return -EINVAL; |
| 760 | } | ||
| 750 | 761 | ||
| 751 | /* pci_mmap_page_range() expects the same kind of entry as coming | 762 | /* pci_mmap_page_range() expects the same kind of entry as coming |
| 752 | * from /proc/bus/pci/ which is a "user visible" value. If this is | 763 | * from /proc/bus/pci/ which is a "user visible" value. If this is |
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e98c8104297b..710c8a29be0d 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
| @@ -1007,6 +1007,18 @@ static int __pci_enable_device_flags(struct pci_dev *dev, | |||
| 1007 | int err; | 1007 | int err; |
| 1008 | int i, bars = 0; | 1008 | int i, bars = 0; |
| 1009 | 1009 | ||
| 1010 | /* | ||
| 1011 | * Power state could be unknown at this point, either due to a fresh | ||
| 1012 | * boot or a device removal call. So get the current power state | ||
| 1013 | * so that things like MSI message writing will behave as expected | ||
| 1014 | * (e.g. if the device really is in D0 at enable time). | ||
| 1015 | */ | ||
| 1016 | if (dev->pm_cap) { | ||
| 1017 | u16 pmcsr; | ||
| 1018 | pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); | ||
| 1019 | dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); | ||
| 1020 | } | ||
| 1021 | |||
| 1010 | if (atomic_add_return(1, &dev->enable_cnt) > 1) | 1022 | if (atomic_add_return(1, &dev->enable_cnt) > 1) |
| 1011 | return 0; /* already enabled */ | 1023 | return 0; /* already enabled */ |
| 1012 | 1024 | ||
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index f5c7c382765f..7d33f6673868 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h | |||
| @@ -22,8 +22,13 @@ extern void pci_remove_firmware_label_files(struct pci_dev *pdev); | |||
| 22 | #endif | 22 | #endif |
| 23 | extern void pci_cleanup_rom(struct pci_dev *dev); | 23 | extern void pci_cleanup_rom(struct pci_dev *dev); |
| 24 | #ifdef HAVE_PCI_MMAP | 24 | #ifdef HAVE_PCI_MMAP |
| 25 | enum pci_mmap_api { | ||
| 26 | PCI_MMAP_SYSFS, /* mmap on /sys/bus/pci/devices/<BDF>/resource<N> */ | ||
| 27 | PCI_MMAP_PROCFS /* mmap on /proc/bus/pci/<BDF> */ | ||
| 28 | }; | ||
| 25 | extern int pci_mmap_fits(struct pci_dev *pdev, int resno, | 29 | extern int pci_mmap_fits(struct pci_dev *pdev, int resno, |
| 26 | struct vm_area_struct *vma); | 30 | struct vm_area_struct *vmai, |
| 31 | enum pci_mmap_api mmap_api); | ||
| 27 | #endif | 32 | #endif |
| 28 | int pci_probe_reset_function(struct pci_dev *dev); | 33 | int pci_probe_reset_function(struct pci_dev *dev); |
| 29 | 34 | ||
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index 297b72c880a1..ea00647f4732 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c | |||
| @@ -257,7 +257,7 @@ static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma) | |||
| 257 | 257 | ||
| 258 | /* Make sure the caller is mapping a real resource for this device */ | 258 | /* Make sure the caller is mapping a real resource for this device */ |
| 259 | for (i = 0; i < PCI_ROM_RESOURCE; i++) { | 259 | for (i = 0; i < PCI_ROM_RESOURCE; i++) { |
| 260 | if (pci_mmap_fits(dev, i, vma)) | 260 | if (pci_mmap_fits(dev, i, vma, PCI_MMAP_PROCFS)) |
| 261 | break; | 261 | break; |
| 262 | } | 262 | } |
| 263 | 263 | ||
