aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
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 /drivers
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>
Diffstat (limited to 'drivers')
-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
4 files changed, 201 insertions, 0 deletions
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;