diff options
Diffstat (limited to 'drivers/acpi/acpica/hwvalid.c')
| -rw-r--r-- | drivers/acpi/acpica/hwvalid.c | 115 |
1 files changed, 93 insertions, 22 deletions
diff --git a/drivers/acpi/acpica/hwvalid.c b/drivers/acpi/acpica/hwvalid.c index 7737afb157c..6b919127cd9 100644 --- a/drivers/acpi/acpica/hwvalid.c +++ b/drivers/acpi/acpica/hwvalid.c @@ -1,4 +1,3 @@ - /****************************************************************************** * * Module Name: hwvalid - I/O request validation @@ -6,7 +5,7 @@ *****************************************************************************/ /* - * Copyright (C) 2000 - 2009, Intel Corp. + * Copyright (C) 2000 - 2014, Intel Corp. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -58,7 +57,7 @@ acpi_hw_validate_io_request(acpi_io_address address, u32 bit_width); * * The table is used to implement the Microsoft port access rules that * first appeared in Windows XP. Some ports are always illegal, and some - * ports are only illegal if the BIOS calls _OSI with a win_xP string or + * ports are only illegal if the BIOS calls _OSI with a win_XP string or * later (meaning that the BIOS itelf is post-XP.) * * This provides ACPICA with the desired port protections and @@ -66,7 +65,7 @@ acpi_hw_validate_io_request(acpi_io_address address, u32 bit_width); * * Description of port entries: * DMA: DMA controller - * PIC0: Programmable Interrupt Controller (8259_a) + * PIC0: Programmable Interrupt Controller (8259A) * PIT1: System Timer 1 * PIT2: System Timer 2 failsafe * RTC: Real-time clock @@ -90,6 +89,7 @@ static const struct acpi_port_info acpi_protected_ports[] = { {"PIT2", 0x0048, 0x004B, ACPI_OSI_WIN_XP}, {"RTC", 0x0070, 0x0071, ACPI_OSI_WIN_XP}, {"CMOS", 0x0074, 0x0076, ACPI_OSI_WIN_XP}, + {"DMA1", 0x0081, 0x0083, ACPI_OSI_WIN_XP}, {"DMA1L", 0x0087, 0x0087, ACPI_OSI_WIN_XP}, {"DMA2", 0x0089, 0x008B, ACPI_OSI_WIN_XP}, {"DMA2L", 0x008F, 0x008F, ACPI_OSI_WIN_XP}, @@ -133,7 +133,9 @@ acpi_hw_validate_io_request(acpi_io_address address, u32 bit_width) /* Supported widths are 8/16/32 */ if ((bit_width != 8) && (bit_width != 16) && (bit_width != 32)) { - return AE_BAD_PARAMETER; + ACPI_ERROR((AE_INFO, + "Bad BitWidth parameter: %8.8X", bit_width)); + return (AE_BAD_PARAMETER); } port_info = acpi_protected_ports; @@ -149,9 +151,9 @@ acpi_hw_validate_io_request(acpi_io_address address, u32 bit_width) if (last_address > ACPI_UINT16_MAX) { ACPI_ERROR((AE_INFO, - "Illegal I/O port address/length above 64K: 0x%p/%X", + "Illegal I/O port address/length above 64K: %p/0x%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 +180,12 @@ acpi_hw_validate_io_request(acpi_io_address address, u32 bit_width) /* 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 +209,7 @@ acpi_hw_validate_io_request(acpi_io_address address, u32 bit_width) * 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 +220,49 @@ acpi_hw_validate_io_request(acpi_io_address address, u32 bit_width) acpi_status acpi_hw_read_port(acpi_io_address address, u32 *value, u32 width) { acpi_status status; + u32 one_byte; + u32 i; + + /* Truncate address to 16 bits if requested */ + + if (acpi_gbl_truncate_io_addresses) { + address &= ACPI_UINT16_MAX; + } + + /* Validate the entire request and perform the I/O */ status = acpi_hw_validate_io_request(address, width); - if (ACPI_FAILURE(status)) { - return status; + if (ACPI_SUCCESS(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++; } - status = acpi_os_read_port(address, value, width); - return status; + return (AE_OK); } /****************************************************************************** @@ -235,7 +273,7 @@ acpi_status acpi_hw_read_port(acpi_io_address address, u32 *value, u32 width) * 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 +284,45 @@ acpi_status acpi_hw_read_port(acpi_io_address address, u32 *value, u32 width) acpi_status acpi_hw_write_port(acpi_io_address address, u32 value, u32 width) { acpi_status status; + u32 i; + + /* Truncate address to 16 bits if requested */ + + if (acpi_gbl_truncate_io_addresses) { + address &= ACPI_UINT16_MAX; + } + + /* Validate the entire request and perform the I/O */ status = acpi_hw_validate_io_request(address, width); - if (ACPI_FAILURE(status)) { - return status; + if (ACPI_SUCCESS(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++; } - status = acpi_os_write_port(address, value, width); - return status; + return (AE_OK); } |
