aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJiang Liu <jiang.liu@linux.intel.com>2015-07-08 03:26:39 -0400
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2015-07-09 20:46:52 -0400
commit1fb01ca93a1348a1469b8777326cd7632483de77 (patch)
tree675f8760bb9bb95dcdcde426cf73a5bdd1226aa8
parentd770e558e21961ad6cfdf0ff7df0eb5d7d4f0754 (diff)
ACPI / PCI: Fix regressions caused by resource_size_t overflow with 32-bit kernel
Zoltan Boszormenyi reported this regression: "There's a Realtek RTL8111/8168/8411 (PCI ID 10ec:8168, Subsystem ID 1565:230e) network chip on the mainboard. After the r8169 driver loaded the IRQs in the machine went berserk. Keyboard keypressed arrived with considerable latency and duplicated, so no real work was possible. The machine responded to the power button but didn't actually power down. It just stuck at the powering down message. I had to press the power button for 4 seconds to power it down. The computer is a POS machine with a big battery inside. Because of this, either ACPI or the Realtek chip kept the bad state and after rebooting, the network chip didn't even show up in lspci. Not even the PXE ROM announced itself during boot. I had to disconnect the battery to beat some sense back to the computer. The regression happens with 4.0.5, 4.1.0-rc8 and 4.1.0-final. 3.18.16 was good." The regression is caused by commit 593669c2ac0f (x86/PCI/ACPI: Use common ACPI resource interfaces to simplify implementation). Since commit 593669c2ac0f, x86 PCI ACPI host bridge driver validates ACPI resources by first converting an ACPI resource to a 'struct resource' structure and then applying checks against the converted resource structure. The 'start' and 'end' fields in 'struct resource' are defined to be type of resource_size_t, which may be 32 bits or 64 bits depending on CONFIG_PHYS_ADDR_T_64BIT. This may cause incorrect resource validation results with 32-bit kernels because 64-bit ACPI resource descriptors may get truncated when converting to 32-bit 'start' and 'end' fields in 'struct resource'. It eventually affects PCI resource allocation subsystem and makes some PCI devices and the system behave abnormally due to incorrect resource assignment. So enhance the ACPI resource parsing interfaces to ignore ACPI resource descriptors with address/offset above 4G when running in 32-bit mode. With the fix applied, the behavior of the machine was restored to how 3.18.16 worked, i.e. the memory range that is over 4GB is ignored again, and lspci -vvxxx shows that everything is at the same memory window as they were with 3.18.16. Reported-and-tested-by: Boszormenyi Zoltan <zboszor@pr.hu> Fixes: 593669c2ac0f (x86/PCI/ACPI: Use common ACPI resource interfaces to simplify implementation) Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com> Cc: 4.0+ <stable@vger.kernel.org> # 4.0+ Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r--drivers/acpi/resource.c24
1 files changed, 15 insertions, 9 deletions
diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c
index 10561ce16ed1..e8d281739cbc 100644
--- a/drivers/acpi/resource.c
+++ b/drivers/acpi/resource.c
@@ -194,6 +194,7 @@ static bool acpi_decode_space(struct resource_win *win,
194 u8 iodec = attr->granularity == 0xfff ? ACPI_DECODE_10 : ACPI_DECODE_16; 194 u8 iodec = attr->granularity == 0xfff ? ACPI_DECODE_10 : ACPI_DECODE_16;
195 bool wp = addr->info.mem.write_protect; 195 bool wp = addr->info.mem.write_protect;
196 u64 len = attr->address_length; 196 u64 len = attr->address_length;
197 u64 start, end, offset = 0;
197 struct resource *res = &win->res; 198 struct resource *res = &win->res;
198 199
199 /* 200 /*
@@ -205,9 +206,6 @@ static bool acpi_decode_space(struct resource_win *win,
205 pr_debug("ACPI: Invalid address space min_addr_fix %d, max_addr_fix %d, len %llx\n", 206 pr_debug("ACPI: Invalid address space min_addr_fix %d, max_addr_fix %d, len %llx\n",
206 addr->min_address_fixed, addr->max_address_fixed, len); 207 addr->min_address_fixed, addr->max_address_fixed, len);
207 208
208 res->start = attr->minimum;
209 res->end = attr->maximum;
210
211 /* 209 /*
212 * For bridges that translate addresses across the bridge, 210 * For bridges that translate addresses across the bridge,
213 * translation_offset is the offset that must be added to the 211 * translation_offset is the offset that must be added to the
@@ -215,12 +213,22 @@ static bool acpi_decode_space(struct resource_win *win,
215 * primary side. Non-bridge devices must list 0 for all Address 213 * primary side. Non-bridge devices must list 0 for all Address
216 * Translation offset bits. 214 * Translation offset bits.
217 */ 215 */
218 if (addr->producer_consumer == ACPI_PRODUCER) { 216 if (addr->producer_consumer == ACPI_PRODUCER)
219 res->start += attr->translation_offset; 217 offset = attr->translation_offset;
220 res->end += attr->translation_offset; 218 else if (attr->translation_offset)
221 } else if (attr->translation_offset) {
222 pr_debug("ACPI: translation_offset(%lld) is invalid for non-bridge device.\n", 219 pr_debug("ACPI: translation_offset(%lld) is invalid for non-bridge device.\n",
223 attr->translation_offset); 220 attr->translation_offset);
221 start = attr->minimum + offset;
222 end = attr->maximum + offset;
223
224 win->offset = offset;
225 res->start = start;
226 res->end = end;
227 if (sizeof(resource_size_t) < sizeof(u64) &&
228 (offset != win->offset || start != res->start || end != res->end)) {
229 pr_warn("acpi resource window ([%#llx-%#llx] ignored, not CPU addressable)\n",
230 attr->minimum, attr->maximum);
231 return false;
224 } 232 }
225 233
226 switch (addr->resource_type) { 234 switch (addr->resource_type) {
@@ -237,8 +245,6 @@ static bool acpi_decode_space(struct resource_win *win,
237 return false; 245 return false;
238 } 246 }
239 247
240 win->offset = attr->translation_offset;
241
242 if (addr->producer_consumer == ACPI_PRODUCER) 248 if (addr->producer_consumer == ACPI_PRODUCER)
243 res->flags |= IORESOURCE_WINDOW; 249 res->flags |= IORESOURCE_WINDOW;
244 250