diff options
Diffstat (limited to 'drivers/pnp/pnpacpi/rsparser.c')
| -rw-r--r-- | drivers/pnp/pnpacpi/rsparser.c | 43 | 
1 files changed, 31 insertions, 12 deletions
diff --git a/drivers/pnp/pnpacpi/rsparser.c b/drivers/pnp/pnpacpi/rsparser.c index 54514aa35b0..35bb44af49b 100644 --- a/drivers/pnp/pnpacpi/rsparser.c +++ b/drivers/pnp/pnpacpi/rsparser.c @@ -24,6 +24,7 @@  #include <linux/acpi.h>  #include <linux/pci.h>  #include <linux/pnp.h> +#include <linux/slab.h>  #include "../base.h"  #include "pnpacpi.h" @@ -273,12 +274,33 @@ static void pnpacpi_parse_allocated_busresource(struct pnp_dev *dev,  	pnp_add_bus_resource(dev, start, end);  } +static u64 addr_space_length(struct pnp_dev *dev, u64 min, u64 max, u64 len) +{ +	u64 max_len; + +	max_len = max - min + 1; +	if (len <= max_len) +		return len; + +	/* +	 * Per 6.4.3.5, _LEN cannot exceed _MAX - _MIN + 1, but some BIOSes +	 * don't do this correctly, e.g., +	 * https://bugzilla.kernel.org/show_bug.cgi?id=15480 +	 */ +	dev_info(&dev->dev, +	         "resource length %#llx doesn't fit in %#llx-%#llx, trimming\n", +		 (unsigned long long) len, (unsigned long long) min, +		 (unsigned long long) max); +	return max_len; +} +  static void pnpacpi_parse_allocated_address_space(struct pnp_dev *dev,  						  struct acpi_resource *res)  {  	struct acpi_resource_address64 addr, *p = &addr;  	acpi_status status;  	int window; +	u64 len;  	status = acpi_resource_to_address64(res, p);  	if (!ACPI_SUCCESS(status)) { @@ -287,20 +309,18 @@ static void pnpacpi_parse_allocated_address_space(struct pnp_dev *dev,  		return;  	} +	len = addr_space_length(dev, p->minimum, p->maximum, p->address_length);  	window = (p->producer_consumer == ACPI_PRODUCER) ? 1 : 0;  	if (p->resource_type == ACPI_MEMORY_RANGE) -		pnpacpi_parse_allocated_memresource(dev, -			p->minimum, p->address_length, +		pnpacpi_parse_allocated_memresource(dev, p->minimum, len,  			p->info.mem.write_protect, window);  	else if (p->resource_type == ACPI_IO_RANGE) -		pnpacpi_parse_allocated_ioresource(dev, -			p->minimum, p->address_length, +		pnpacpi_parse_allocated_ioresource(dev, p->minimum, len,  			p->granularity == 0xfff ? ACPI_DECODE_10 :  				ACPI_DECODE_16, window);  	else if (p->resource_type == ACPI_BUS_NUMBER_RANGE) -		pnpacpi_parse_allocated_busresource(dev, p->minimum, -						    p->address_length); +		pnpacpi_parse_allocated_busresource(dev, p->minimum, len);  }  static void pnpacpi_parse_allocated_ext_address_space(struct pnp_dev *dev, @@ -308,21 +328,20 @@ static void pnpacpi_parse_allocated_ext_address_space(struct pnp_dev *dev,  {  	struct acpi_resource_extended_address64 *p = &res->data.ext_address64;  	int window; +	u64 len; +	len = addr_space_length(dev, p->minimum, p->maximum, p->address_length);  	window = (p->producer_consumer == ACPI_PRODUCER) ? 1 : 0;  	if (p->resource_type == ACPI_MEMORY_RANGE) -		pnpacpi_parse_allocated_memresource(dev, -			p->minimum, p->address_length, +		pnpacpi_parse_allocated_memresource(dev, p->minimum, len,  			p->info.mem.write_protect, window);  	else if (p->resource_type == ACPI_IO_RANGE) -		pnpacpi_parse_allocated_ioresource(dev, -			p->minimum, p->address_length, +		pnpacpi_parse_allocated_ioresource(dev, p->minimum, len,  			p->granularity == 0xfff ? ACPI_DECODE_10 :  				ACPI_DECODE_16, window);  	else if (p->resource_type == ACPI_BUS_NUMBER_RANGE) -		pnpacpi_parse_allocated_busresource(dev, p->minimum, -						    p->address_length); +		pnpacpi_parse_allocated_busresource(dev, p->minimum, len);  }  static acpi_status pnpacpi_allocated_resource(struct acpi_resource *res,  |