Skip to content

Commit f75b99d

Browse files
Yinghai Lubjorn-helgaas
authored andcommitted
PCI: Enforce bus address limits in resource allocation
When allocating space for 32-bit BARs, we previously limited RESOURCE addresses so they would fit in 32 bits. However, the BUS address need not be the same as the resource address, and it's the bus address that must fit in the 32-bit BAR. This patch adds: - pci_clip_resource_to_region(), which clips a resource so it contains only the range that maps to the specified bus address region, e.g., to clip a resource to 32-bit bus addresses, and - pci_bus_alloc_from_region(), which allocates space for a resource from the specified bus address region, and changes pci_bus_alloc_resource() to allocate space for 64-bit BARs from the entire bus address region, and space for 32-bit BARs from only the bus address region below 4GB. If we had this window: pci_root HWP0002:0a: host bridge window [mem 0xf0180000000-0xf01fedfffff] (bus address [0x80000000-0xfedfffff]) we previously could not put a 32-bit BAR there, because the CPU addresses don't fit in 32 bits. This patch fixes this, so we can use this space for 32-bit BARs. It's also possible (though unlikely) to have resources with 32-bit CPU addresses but bus addresses above 4GB. In this case the previous code would allocate space that a 32-bit BAR could not map. Remove PCIBIOS_MAX_MEM_32, which is no longer used. [bhelgaas: reworked starting from http://lkml.kernel.org/r/1386658484-15774-3-git-send-email-yinghai@kernel.org] Signed-off-by: Yinghai Lu <yinghai@kernel.org> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
1 parent 36e097a commit f75b99d

File tree

3 files changed

+83
-33
lines changed

3 files changed

+83
-33
lines changed

arch/x86/include/asm/pci.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,6 @@ int setup_msi_irq(struct pci_dev *dev, struct msi_desc *msidesc,
125125

126126
/* generic pci stuff */
127127
#include <asm-generic/pci.h>
128-
#define PCIBIOS_MAX_MEM_32 0xffffffff
129128

130129
#ifdef CONFIG_NUMA
131130
/* Returns the node based on pci bus */

drivers/pci/bus.c

Lines changed: 83 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -98,41 +98,52 @@ void pci_bus_remove_resources(struct pci_bus *bus)
9898
}
9999
}
100100

101-
/**
102-
* pci_bus_alloc_resource - allocate a resource from a parent bus
103-
* @bus: PCI bus
104-
* @res: resource to allocate
105-
* @size: size of resource to allocate
106-
* @align: alignment of resource to allocate
107-
* @min: minimum /proc/iomem address to allocate
108-
* @type_mask: IORESOURCE_* type flags
109-
* @alignf: resource alignment function
110-
* @alignf_data: data argument for resource alignment function
111-
*
112-
* Given the PCI bus a device resides on, the size, minimum address,
113-
* alignment and type, try to find an acceptable resource allocation
114-
* for a specific device resource.
101+
static struct pci_bus_region pci_32_bit = {0, 0xffffffffULL};
102+
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
103+
static struct pci_bus_region pci_64_bit = {0,
104+
(dma_addr_t) 0xffffffffffffffffULL};
105+
#endif
106+
107+
/*
108+
* @res contains CPU addresses. Clip it so the corresponding bus addresses
109+
* on @bus are entirely within @region. This is used to control the bus
110+
* addresses of resources we allocate, e.g., we may need a resource that
111+
* can be mapped by a 32-bit BAR.
115112
*/
116-
int
117-
pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
113+
static void pci_clip_resource_to_region(struct pci_bus *bus,
114+
struct resource *res,
115+
struct pci_bus_region *region)
116+
{
117+
struct pci_bus_region r;
118+
119+
pcibios_resource_to_bus(bus, &r, res);
120+
if (r.start < region->start)
121+
r.start = region->start;
122+
if (r.end > region->end)
123+
r.end = region->end;
124+
125+
if (r.end < r.start)
126+
res->end = res->start - 1;
127+
else
128+
pcibios_bus_to_resource(bus, res, &r);
129+
}
130+
131+
static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res,
118132
resource_size_t size, resource_size_t align,
119133
resource_size_t min, unsigned int type_mask,
120134
resource_size_t (*alignf)(void *,
121135
const struct resource *,
122136
resource_size_t,
123137
resource_size_t),
124-
void *alignf_data)
138+
void *alignf_data,
139+
struct pci_bus_region *region)
125140
{
126-
int i, ret = -ENOMEM;
127-
struct resource *r;
128-
resource_size_t max = -1;
141+
int i, ret;
142+
struct resource *r, avail;
143+
resource_size_t max;
129144

130145
type_mask |= IORESOURCE_IO | IORESOURCE_MEM;
131146

132-
/* don't allocate too high if the pref mem doesn't support 64bit*/
133-
if (!(res->flags & IORESOURCE_MEM_64))
134-
max = PCIBIOS_MAX_MEM_32;
135-
136147
pci_bus_for_each_resource(bus, r, i) {
137148
if (!r)
138149
continue;
@@ -147,22 +158,66 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
147158
!(res->flags & IORESOURCE_PREFETCH))
148159
continue;
149160

161+
avail = *r;
162+
pci_clip_resource_to_region(bus, &avail, region);
163+
if (!resource_size(&avail))
164+
continue;
165+
150166
/*
151167
* "min" is typically PCIBIOS_MIN_IO or PCIBIOS_MIN_MEM to
152168
* protect badly documented motherboard resources, but if
153169
* this is an already-configured bridge window, its start
154170
* overrides "min".
155171
*/
156-
if (r->start)
157-
min = r->start;
172+
if (avail.start)
173+
min = avail.start;
174+
175+
max = avail.end;
158176

159177
/* Ok, try it out.. */
160178
ret = allocate_resource(r, res, size, min, max,
161179
align, alignf, alignf_data);
162180
if (ret == 0)
163-
break;
181+
return 0;
164182
}
165-
return ret;
183+
return -ENOMEM;
184+
}
185+
186+
/**
187+
* pci_bus_alloc_resource - allocate a resource from a parent bus
188+
* @bus: PCI bus
189+
* @res: resource to allocate
190+
* @size: size of resource to allocate
191+
* @align: alignment of resource to allocate
192+
* @min: minimum /proc/iomem address to allocate
193+
* @type_mask: IORESOURCE_* type flags
194+
* @alignf: resource alignment function
195+
* @alignf_data: data argument for resource alignment function
196+
*
197+
* Given the PCI bus a device resides on, the size, minimum address,
198+
* alignment and type, try to find an acceptable resource allocation
199+
* for a specific device resource.
200+
*/
201+
int
202+
pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
203+
resource_size_t size, resource_size_t align,
204+
resource_size_t min, unsigned int type_mask,
205+
resource_size_t (*alignf)(void *,
206+
const struct resource *,
207+
resource_size_t,
208+
resource_size_t),
209+
void *alignf_data)
210+
{
211+
#ifdef CONFIG_ARCH_DMA_ADDR_T_64BIT
212+
if (res->flags & IORESOURCE_MEM_64)
213+
return pci_bus_alloc_from_region(bus, res, size, align, min,
214+
type_mask, alignf, alignf_data,
215+
&pci_64_bit);
216+
#endif
217+
218+
return pci_bus_alloc_from_region(bus, res, size, align, min,
219+
type_mask, alignf, alignf_data,
220+
&pci_32_bit);
166221
}
167222

168223
void __weak pcibios_resource_survey_bus(struct pci_bus *bus) { }

include/linux/pci.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1493,10 +1493,6 @@ static inline struct pci_dev *pci_dev_get(struct pci_dev *dev)
14931493

14941494
#include <asm/pci.h>
14951495

1496-
#ifndef PCIBIOS_MAX_MEM_32
1497-
#define PCIBIOS_MAX_MEM_32 (-1)
1498-
#endif
1499-
15001496
/* these helpers provide future and backwards compatibility
15011497
* for accessing popular PCI BAR info */
15021498
#define pci_resource_start(dev, bar) ((dev)->resource[(bar)].start)

0 commit comments

Comments
 (0)