aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYuji Shimada <shimada-yxb@necst.nec.co.jp>2009-03-16 04:13:39 -0400
committerJesse Barnes <jbarnes@virtuousgeek.org>2009-03-20 13:48:15 -0400
commit32a9a682bef2f6fce7026bd94d1ce20028b0e52d (patch)
treea93225b3039585e8364ae3d411b22f63eebc8d70
parent1c8d7b0a562da06d3ebe83f01b1ed553205d1ae4 (diff)
PCI: allow assignment of memory resources with a specified alignment
This patch allows memory resources to be assigned with a specified alignment at boot-time or run-time. The patch is useful when we use PCI pass-through, because page-aligned memory resources are required to securely share PCI resources with guest drivers. If you want to assign the resource at boot time, please set "pci=resource_alignment=" boot parameter. This is format of "pci=resource_alignment=" boot parameter: [<order of align>@][<domain>:]<bus>:<slot>.<func>[; ...] Specifies alignment and device to reassign aligned memory resources. If <order of align> is not specified, PAGE_SIZE is used as alignment. PCI-PCI bridge can be specified, if resource windows need to be expanded. This is example: pci=resource_alignment=20@07:00.0;18@0f:00.0;00:1d.7 If you want to assign the resource at run-time, please set "/sys/bus/pci/resource_alignment" file, and hot-remove the device and hot-add the device. For this purpose, fakephp or PCI hotplug interfaces can be used. The format of "/sys/bus/pci/resource_alignment" file is the same with boot parameter. You can use "," instead of ";". For example: # cd /sys/bus/pci # echo -n 20@12:00.0 > resource_alignment # echo 1 > devices/0000:12:00.0/remove # echo 1 > rescan Reviewed-by: Alex Chiang <achiang@hp.com> Reviewed-by: Yu Zhao <yu.zhao@intel.com> Signed-off-by: Yuji Shimada <shimada-yxb@necst.nec.co.jp> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
-rw-r--r--Documentation/kernel-parameters.txt9
-rw-r--r--drivers/pci/pci.c120
-rw-r--r--drivers/pci/pci.h6
-rw-r--r--drivers/pci/quirks.c60
-rw-r--r--drivers/pci/setup-res.c15
5 files changed, 210 insertions, 0 deletions
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index c7c441e7930e..1754fedc531c 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -1760,6 +1760,15 @@ and is between 256 and 4096 characters. It is defined in the file
1760 cbmemsize=nn[KMG] The fixed amount of bus space which is 1760 cbmemsize=nn[KMG] The fixed amount of bus space which is
1761 reserved for the CardBus bridge's memory 1761 reserved for the CardBus bridge's memory
1762 window. The default value is 64 megabytes. 1762 window. The default value is 64 megabytes.
1763 resource_alignment=
1764 Format:
1765 [<order of align>@][<domain>:]<bus>:<slot>.<func>[; ...]
1766 Specifies alignment and device to reassign
1767 aligned memory resources.
1768 If <order of align> is not specified,
1769 PAGE_SIZE is used as alignment.
1770 PCI-PCI bridge can be specified, if resource
1771 windows need to be expanded.
1763 1772
1764 pcie_aspm= [PCIE] Forcibly enable or disable PCIe Active State Power 1773 pcie_aspm= [PCIE] Forcibly enable or disable PCIe Active State Power
1765 Management. 1774 Management.
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 8310dc2f943b..a35a8b2ba631 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -20,6 +20,8 @@
20#include <linux/pm_wakeup.h> 20#include <linux/pm_wakeup.h>
21#include <linux/interrupt.h> 21#include <linux/interrupt.h>
22#include <asm/dma.h> /* isa_dma_bridge_buggy */ 22#include <asm/dma.h> /* isa_dma_bridge_buggy */
23#include <linux/device.h>
24#include <asm/setup.h>
23#include "pci.h" 25#include "pci.h"
24 26
25unsigned int pci_pm_d3_delay = PCI_PM_D3_WAIT; 27unsigned int pci_pm_d3_delay = PCI_PM_D3_WAIT;
@@ -2370,6 +2372,121 @@ int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type)
2370 return 0; 2372 return 0;
2371} 2373}
2372 2374
2375#define RESOURCE_ALIGNMENT_PARAM_SIZE COMMAND_LINE_SIZE
2376static char resource_alignment_param[RESOURCE_ALIGNMENT_PARAM_SIZE] = {0};
2377spinlock_t resource_alignment_lock = SPIN_LOCK_UNLOCKED;
2378
2379/**
2380 * pci_specified_resource_alignment - get resource alignment specified by user.
2381 * @dev: the PCI device to get
2382 *
2383 * RETURNS: Resource alignment if it is specified.
2384 * Zero if it is not specified.
2385 */
2386resource_size_t pci_specified_resource_alignment(struct pci_dev *dev)
2387{
2388 int seg, bus, slot, func, align_order, count;
2389 resource_size_t align = 0;
2390 char *p;
2391
2392 spin_lock(&resource_alignment_lock);
2393 p = resource_alignment_param;
2394 while (*p) {
2395 count = 0;
2396 if (sscanf(p, "%d%n", &align_order, &count) == 1 &&
2397 p[count] == '@') {
2398 p += count + 1;
2399 } else {
2400 align_order = -1;
2401 }
2402 if (sscanf(p, "%x:%x:%x.%x%n",
2403 &seg, &bus, &slot, &func, &count) != 4) {
2404 seg = 0;
2405 if (sscanf(p, "%x:%x.%x%n",
2406 &bus, &slot, &func, &count) != 3) {
2407 /* Invalid format */
2408 printk(KERN_ERR "PCI: Can't parse resource_alignment parameter: %s\n",
2409 p);
2410 break;
2411 }
2412 }
2413 p += count;
2414 if (seg == pci_domain_nr(dev->bus) &&
2415 bus == dev->bus->number &&
2416 slot == PCI_SLOT(dev->devfn) &&
2417 func == PCI_FUNC(dev->devfn)) {
2418 if (align_order == -1) {
2419 align = PAGE_SIZE;
2420 } else {
2421 align = 1 << align_order;
2422 }
2423 /* Found */
2424 break;
2425 }
2426 if (*p != ';' && *p != ',') {
2427 /* End of param or invalid format */
2428 break;
2429 }
2430 p++;
2431 }
2432 spin_unlock(&resource_alignment_lock);
2433 return align;
2434}
2435
2436/**
2437 * pci_is_reassigndev - check if specified PCI is target device to reassign
2438 * @dev: the PCI device to check
2439 *
2440 * RETURNS: non-zero for PCI device is a target device to reassign,
2441 * or zero is not.
2442 */
2443int pci_is_reassigndev(struct pci_dev *dev)
2444{
2445 return (pci_specified_resource_alignment(dev) != 0);
2446}
2447
2448ssize_t pci_set_resource_alignment_param(const char *buf, size_t count)
2449{
2450 if (count > RESOURCE_ALIGNMENT_PARAM_SIZE - 1)
2451 count = RESOURCE_ALIGNMENT_PARAM_SIZE - 1;
2452 spin_lock(&resource_alignment_lock);
2453 strncpy(resource_alignment_param, buf, count);
2454 resource_alignment_param[count] = '\0';
2455 spin_unlock(&resource_alignment_lock);
2456 return count;
2457}
2458
2459ssize_t pci_get_resource_alignment_param(char *buf, size_t size)
2460{
2461 size_t count;
2462 spin_lock(&resource_alignment_lock);
2463 count = snprintf(buf, size, "%s", resource_alignment_param);
2464 spin_unlock(&resource_alignment_lock);
2465 return count;
2466}
2467
2468static ssize_t pci_resource_alignment_show(struct bus_type *bus, char *buf)
2469{
2470 return pci_get_resource_alignment_param(buf, PAGE_SIZE);
2471}
2472
2473static ssize_t pci_resource_alignment_store(struct bus_type *bus,
2474 const char *buf, size_t count)
2475{
2476 return pci_set_resource_alignment_param(buf, count);
2477}
2478
2479BUS_ATTR(resource_alignment, 0644, pci_resource_alignment_show,
2480 pci_resource_alignment_store);
2481
2482static int __init pci_resource_alignment_sysfs_init(void)
2483{
2484 return bus_create_file(&pci_bus_type,
2485 &bus_attr_resource_alignment);
2486}
2487
2488late_initcall(pci_resource_alignment_sysfs_init);
2489
2373static void __devinit pci_no_domains(void) 2490static void __devinit pci_no_domains(void)
2374{ 2491{
2375#ifdef CONFIG_PCI_DOMAINS 2492#ifdef CONFIG_PCI_DOMAINS
@@ -2418,6 +2535,9 @@ static int __init pci_setup(char *str)
2418 pci_cardbus_io_size = memparse(str + 9, &str); 2535 pci_cardbus_io_size = memparse(str + 9, &str);
2419 } else if (!strncmp(str, "cbmemsize=", 10)) { 2536 } else if (!strncmp(str, "cbmemsize=", 10)) {
2420 pci_cardbus_mem_size = memparse(str + 10, &str); 2537 pci_cardbus_mem_size = memparse(str + 10, &str);
2538 } else if (!strncmp(str, "resource_alignment=", 19)) {
2539 pci_set_resource_alignment_param(str + 19,
2540 strlen(str + 19));
2421 } else { 2541 } else {
2422 printk(KERN_ERR "PCI: Unknown option `%s'\n", 2542 printk(KERN_ERR "PCI: Unknown option `%s'\n",
2423 str); 2543 str);
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 07c0aa5275e6..2cd1cba7236f 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -195,4 +195,10 @@ static inline int pci_ari_enabled(struct pci_bus *bus)
195 return bus->self && bus->self->ari_enabled; 195 return bus->self && bus->self->ari_enabled;
196} 196}
197 197
198#ifdef CONFIG_PCI_QUIRKS
199extern int pci_is_reassigndev(struct pci_dev *dev);
200resource_size_t pci_specified_resource_alignment(struct pci_dev *dev);
201extern void pci_disable_bridge_window(struct pci_dev *dev);
202#endif
203
198#endif /* DRIVERS_PCI_H */ 204#endif /* DRIVERS_PCI_H */
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 5aa2afb23ef9..50233818a763 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -24,6 +24,7 @@
24#include <linux/kallsyms.h> 24#include <linux/kallsyms.h>
25#include <linux/dmi.h> 25#include <linux/dmi.h>
26#include <linux/pci-aspm.h> 26#include <linux/pci-aspm.h>
27#include <linux/ioport.h>
27#include "pci.h" 28#include "pci.h"
28 29
29int isa_dma_bridge_buggy; 30int isa_dma_bridge_buggy;
@@ -34,6 +35,65 @@ int pcie_mch_quirk;
34EXPORT_SYMBOL(pcie_mch_quirk); 35EXPORT_SYMBOL(pcie_mch_quirk);
35 36
36#ifdef CONFIG_PCI_QUIRKS 37#ifdef CONFIG_PCI_QUIRKS
38/*
39 * This quirk function disables the device and releases resources
40 * which is specified by kernel's boot parameter 'pci=resource_alignment='.
41 * It also rounds up size to specified alignment.
42 * Later on, the kernel will assign page-aligned memory resource back
43 * to that device.
44 */
45static void __devinit quirk_resource_alignment(struct pci_dev *dev)
46{
47 int i;
48 struct resource *r;
49 resource_size_t align, size;
50
51 if (!pci_is_reassigndev(dev))
52 return;
53
54 if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL &&
55 (dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) {
56 dev_warn(&dev->dev,
57 "Can't reassign resources to host bridge.\n");
58 return;
59 }
60
61 dev_info(&dev->dev, "Disabling device and release resources.\n");
62 pci_disable_device(dev);
63
64 align = pci_specified_resource_alignment(dev);
65 for (i=0; i < PCI_BRIDGE_RESOURCES; i++) {
66 r = &dev->resource[i];
67 if (!(r->flags & IORESOURCE_MEM))
68 continue;
69 size = resource_size(r);
70 if (size < align) {
71 size = align;
72 dev_info(&dev->dev,
73 "Rounding up size of resource #%d to %#llx.\n",
74 i, (unsigned long long)size);
75 }
76 r->end = size - 1;
77 r->start = 0;
78 }
79 /* Need to disable bridge's resource window,
80 * to enable the kernel to reassign new resource
81 * window later on.
82 */
83 if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE &&
84 (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
85 for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) {
86 r = &dev->resource[i];
87 if (!(r->flags & IORESOURCE_MEM))
88 continue;
89 r->end = resource_size(r) - 1;
90 r->start = 0;
91 }
92 pci_disable_bridge_window(dev);
93 }
94}
95DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_resource_alignment);
96
37/* The Mellanox Tavor device gives false positive parity errors 97/* The Mellanox Tavor device gives false positive parity errors
38 * Mark this device with a broken_parity_status, to allow 98 * Mark this device with a broken_parity_status, to allow
39 * PCI scanning code to "skip" this now blacklisted device. 99 * PCI scanning code to "skip" this now blacklisted device.
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index 32e8d88a4619..3039fcb86afc 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -120,6 +120,21 @@ int pci_claim_resource(struct pci_dev *dev, int resource)
120 return err; 120 return err;
121} 121}
122 122
123#ifdef CONFIG_PCI_QUIRKS
124void pci_disable_bridge_window(struct pci_dev *dev)
125{
126 dev_dbg(&dev->dev, "Disabling bridge window.\n");
127
128 /* MMIO Base/Limit */
129 pci_write_config_dword(dev, PCI_MEMORY_BASE, 0x0000fff0);
130
131 /* Prefetchable MMIO Base/Limit */
132 pci_write_config_dword(dev, PCI_PREF_LIMIT_UPPER32, 0);
133 pci_write_config_dword(dev, PCI_PREF_MEMORY_BASE, 0x0000fff0);
134 pci_write_config_dword(dev, PCI_PREF_BASE_UPPER32, 0xffffffff);
135}
136#endif /* CONFIG_PCI_QUIRKS */
137
123int pci_assign_resource(struct pci_dev *dev, int resno) 138int pci_assign_resource(struct pci_dev *dev, int resno)
124{ 139{
125 struct pci_bus *bus = dev->bus; 140 struct pci_bus *bus = dev->bus;