I/O port protection: update for windows compatibility.

For windows compatibility,
1) On a port protection violation, simply ignore the request and
   do not return an exception (allow the control method to continue execution.)
2) If only part of the request overlaps a protected port,
   read/write the individual ports that are not protected.

http://bugzilla.kernel.org/show_bug.cgi?id=13036

Signed-off-by: Lin Ming <ming.m.lin@intel.com>
Signed-off-by: Bob Moore <robert.moore@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
diff --git a/drivers/acpi/acpica/hwvalid.c b/drivers/acpi/acpica/hwvalid.c
index 7737afb..9c83457 100644
--- a/drivers/acpi/acpica/hwvalid.c
+++ b/drivers/acpi/acpica/hwvalid.c
@@ -151,7 +151,7 @@
 		ACPI_ERROR((AE_INFO,
 			    "Illegal I/O port address/length above 64K: 0x%p/%X",
 			    ACPI_CAST_PTR(void, address), byte_width));
-		return_ACPI_STATUS(AE_AML_ILLEGAL_ADDRESS);
+		return_ACPI_STATUS(AE_LIMIT);
 	}
 
 	/* Exit if requested address is not within the protected port table */
@@ -178,11 +178,12 @@
 			/* Port illegality may depend on the _OSI calls made by the BIOS */
 
 			if (acpi_gbl_osi_data >= port_info->osi_dependency) {
-				ACPI_ERROR((AE_INFO,
-					    "Denied AML access to port 0x%p/%X (%s 0x%.4X-0x%.4X)",
-					    ACPI_CAST_PTR(void, address),
-					    byte_width, port_info->name,
-					    port_info->start, port_info->end));
+				ACPI_DEBUG_PRINT((ACPI_DB_IO,
+						  "Denied AML access to port 0x%p/%X (%s 0x%.4X-0x%.4X)",
+						  ACPI_CAST_PTR(void, address),
+						  byte_width, port_info->name,
+						  port_info->start,
+						  port_info->end));
 
 				return_ACPI_STATUS(AE_AML_ILLEGAL_ADDRESS);
 			}
@@ -206,7 +207,7 @@
  *              Value               Where value is placed
  *              Width               Number of bits
  *
- * RETURN:      Value read from port
+ * RETURN:      Status and value read from port
  *
  * DESCRIPTION: Read data from an I/O port or register. This is a front-end
  *              to acpi_os_read_port that performs validation on both the port
@@ -217,14 +218,43 @@
 acpi_status acpi_hw_read_port(acpi_io_address address, u32 *value, u32 width)
 {
 	acpi_status status;
+	u32 one_byte;
+	u32 i;
+
+	/* Validate the entire request and perform the I/O */
 
 	status = acpi_hw_validate_io_request(address, width);
-	if (ACPI_FAILURE(status)) {
+	if (ACPI_SUCCESS(status)) {
+		status = acpi_os_read_port(address, value, width);
 		return status;
 	}
 
-	status = acpi_os_read_port(address, value, width);
-	return status;
+	if (status != AE_AML_ILLEGAL_ADDRESS) {
+		return status;
+	}
+
+	/*
+	 * There has been a protection violation within the request. Fall
+	 * back to byte granularity port I/O and ignore the failing bytes.
+	 * This provides Windows compatibility.
+	 */
+	for (i = 0, *value = 0; i < width; i += 8) {
+
+		/* Validate and read one byte */
+
+		if (acpi_hw_validate_io_request(address, 8) == AE_OK) {
+			status = acpi_os_read_port(address, &one_byte, 8);
+			if (ACPI_FAILURE(status)) {
+				return status;
+			}
+
+			*value |= (one_byte << i);
+		}
+
+		address++;
+	}
+
+	return AE_OK;
 }
 
 /******************************************************************************
@@ -235,7 +265,7 @@
  *              Value               Value to write
  *              Width               Number of bits
  *
- * RETURN:      None
+ * RETURN:      Status
  *
  * DESCRIPTION: Write data to an I/O port or register. This is a front-end
  *              to acpi_os_write_port that performs validation on both the port
@@ -246,12 +276,39 @@
 acpi_status acpi_hw_write_port(acpi_io_address address, u32 value, u32 width)
 {
 	acpi_status status;
+	u32 i;
+
+	/* Validate the entire request and perform the I/O */
 
 	status = acpi_hw_validate_io_request(address, width);
-	if (ACPI_FAILURE(status)) {
+	if (ACPI_SUCCESS(status)) {
+		status = acpi_os_write_port(address, value, width);
 		return status;
 	}
 
-	status = acpi_os_write_port(address, value, width);
-	return status;
+	if (status != AE_AML_ILLEGAL_ADDRESS) {
+		return status;
+	}
+
+	/*
+	 * There has been a protection violation within the request. Fall
+	 * back to byte granularity port I/O and ignore the failing bytes.
+	 * This provides Windows compatibility.
+	 */
+	for (i = 0; i < width; i += 8) {
+
+		/* Validate and write one byte */
+
+		if (acpi_hw_validate_io_request(address, 8) == AE_OK) {
+			status =
+			    acpi_os_write_port(address, (value >> i) & 0xFF, 8);
+			if (ACPI_FAILURE(status)) {
+				return status;
+			}
+		}
+
+		address++;
+	}
+
+	return AE_OK;
 }