1
linux/drivers/acpi/acpica/exregion.c
Raju Rangoju a83e1385b7 ACPICA: Revert "ACPICA: avoid Info: mapping multiple BARs. Your kernel is fine."
Undo the modifications made in commit d410ee5109 ("ACPICA: avoid
"Info: mapping multiple BARs. Your kernel is fine.""). The initial
purpose of this commit was to stop memory mappings for operation
regions from overlapping page boundaries, as it can trigger warnings
if different page attributes are present.

However, it was found that when this situation arises, mapping
continues until the boundary's end, but there is still an attempt to
read/write the entire length of the map, leading to a NULL pointer
deference. For example, if a four-byte mapping request is made but
only one byte is mapped because it hits the current page boundary's
end, a four-byte read/write attempt is still made, resulting in a NULL
pointer deference.

Instead, map the entire length, as the ACPI specification does not
mandate that it must be within the same page boundary. It is
permissible for it to be mapped across different regions.

Link: https://github.com/acpica/acpica/pull/954
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=218849
Fixes: d410ee5109 ("ACPICA: avoid "Info: mapping multiple BARs. Your kernel is fine."")
Co-developed-by: Sanath S <Sanath.S@amd.com>
Signed-off-by: Sanath S <Sanath.S@amd.com>
Signed-off-by: Raju Rangoju <Raju.Rangoju@amd.com>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
2024-06-17 21:13:25 +02:00

526 lines
14 KiB
C

// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name: exregion - ACPI default op_region (address space) handlers
*
* Copyright (C) 2000 - 2023, Intel Corp.
*
*****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
#include "acinterp.h"
#define _COMPONENT ACPI_EXECUTER
ACPI_MODULE_NAME("exregion")
/*******************************************************************************
*
* FUNCTION: acpi_ex_system_memory_space_handler
*
* PARAMETERS: function - Read or Write operation
* address - Where in the space to read or write
* bit_width - Field width in bits (8, 16, or 32)
* value - Pointer to in or out value
* handler_context - Pointer to Handler's context
* region_context - Pointer to context specific to the
* accessed region
*
* RETURN: Status
*
* DESCRIPTION: Handler for the System Memory address space (Op Region)
*
******************************************************************************/
acpi_status
acpi_ex_system_memory_space_handler(u32 function,
acpi_physical_address address,
u32 bit_width,
u64 *value,
void *handler_context, void *region_context)
{
acpi_status status = AE_OK;
void *logical_addr_ptr = NULL;
struct acpi_mem_space_context *mem_info = region_context;
struct acpi_mem_mapping *mm = mem_info->cur_mm;
u32 length;
acpi_size map_length;
#ifdef ACPI_MISALIGNMENT_NOT_SUPPORTED
u32 remainder;
#endif
ACPI_FUNCTION_TRACE(ex_system_memory_space_handler);
/* Validate and translate the bit width */
switch (bit_width) {
case 8:
length = 1;
break;
case 16:
length = 2;
break;
case 32:
length = 4;
break;
case 64:
length = 8;
break;
default:
ACPI_ERROR((AE_INFO, "Invalid SystemMemory width %u",
bit_width));
return_ACPI_STATUS(AE_AML_OPERAND_VALUE);
}
#ifdef ACPI_MISALIGNMENT_NOT_SUPPORTED
/*
* Hardware does not support non-aligned data transfers, we must verify
* the request.
*/
(void)acpi_ut_short_divide((u64) address, length, NULL, &remainder);
if (remainder != 0) {
return_ACPI_STATUS(AE_AML_ALIGNMENT);
}
#endif
/*
* Does the request fit into the cached memory mapping?
* Is 1) Address below the current mapping? OR
* 2) Address beyond the current mapping?
*/
if (!mm || (address < mm->physical_address) ||
((u64) address + length > (u64) mm->physical_address + mm->length)) {
/*
* The request cannot be resolved by the current memory mapping.
*
* Look for an existing saved mapping covering the address range
* at hand. If found, save it as the current one and carry out
* the access.
*/
for (mm = mem_info->first_mm; mm; mm = mm->next_mm) {
if (mm == mem_info->cur_mm)
continue;
if (address < mm->physical_address)
continue;
if ((u64) address + length >
(u64) mm->physical_address + mm->length)
continue;
mem_info->cur_mm = mm;
goto access;
}
/* Create a new mappings list entry */
mm = ACPI_ALLOCATE_ZEROED(sizeof(*mm));
if (!mm) {
ACPI_ERROR((AE_INFO,
"Unable to save memory mapping at 0x%8.8X%8.8X, size %u",
ACPI_FORMAT_UINT64(address), length));
return_ACPI_STATUS(AE_NO_MEMORY);
}
/*
* October 2009: Attempt to map from the requested address to the
* end of the region. However, we will never map more than one
* page, nor will we cross a page boundary.
*/
map_length = (acpi_size)
((mem_info->address + mem_info->length) - address);
if (map_length > ACPI_DEFAULT_PAGE_SIZE)
map_length = ACPI_DEFAULT_PAGE_SIZE;
/* Create a new mapping starting at the address given */
logical_addr_ptr = acpi_os_map_memory(address, map_length);
if (!logical_addr_ptr) {
ACPI_ERROR((AE_INFO,
"Could not map memory at 0x%8.8X%8.8X, size %u",
ACPI_FORMAT_UINT64(address),
(u32)map_length));
ACPI_FREE(mm);
return_ACPI_STATUS(AE_NO_MEMORY);
}
/* Save the physical address and mapping size */
mm->logical_address = logical_addr_ptr;
mm->physical_address = address;
mm->length = map_length;
/*
* Add the new entry to the mappigs list and save it as the
* current mapping.
*/
mm->next_mm = mem_info->first_mm;
mem_info->first_mm = mm;
mem_info->cur_mm = mm;
}
access:
/*
* Generate a logical pointer corresponding to the address we want to
* access
*/
logical_addr_ptr = mm->logical_address +
((u64) address - (u64) mm->physical_address);
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"System-Memory (width %u) R/W %u Address=%8.8X%8.8X\n",
bit_width, function, ACPI_FORMAT_UINT64(address)));
/*
* Perform the memory read or write
*
* Note: For machines that do not support non-aligned transfers, the target
* address was checked for alignment above. We do not attempt to break the
* transfer up into smaller (byte-size) chunks because the AML specifically
* asked for a transfer width that the hardware may require.
*/
switch (function) {
case ACPI_READ:
*value = 0;
switch (bit_width) {
case 8:
*value = (u64)ACPI_GET8(logical_addr_ptr);
break;
case 16:
*value = (u64)ACPI_GET16(logical_addr_ptr);
break;
case 32:
*value = (u64)ACPI_GET32(logical_addr_ptr);
break;
case 64:
*value = (u64)ACPI_GET64(logical_addr_ptr);
break;
default:
/* bit_width was already validated */
break;
}
break;
case ACPI_WRITE:
switch (bit_width) {
case 8:
ACPI_SET8(logical_addr_ptr, *value);
break;
case 16:
ACPI_SET16(logical_addr_ptr, *value);
break;
case 32:
ACPI_SET32(logical_addr_ptr, *value);
break;
case 64:
ACPI_SET64(logical_addr_ptr, *value);
break;
default:
/* bit_width was already validated */
break;
}
break;
default:
status = AE_BAD_PARAMETER;
break;
}
return_ACPI_STATUS(status);
}
/*******************************************************************************
*
* FUNCTION: acpi_ex_system_io_space_handler
*
* PARAMETERS: function - Read or Write operation
* address - Where in the space to read or write
* bit_width - Field width in bits (8, 16, or 32)
* value - Pointer to in or out value
* handler_context - Pointer to Handler's context
* region_context - Pointer to context specific to the
* accessed region
*
* RETURN: Status
*
* DESCRIPTION: Handler for the System IO address space (Op Region)
*
******************************************************************************/
acpi_status
acpi_ex_system_io_space_handler(u32 function,
acpi_physical_address address,
u32 bit_width,
u64 *value,
void *handler_context, void *region_context)
{
acpi_status status = AE_OK;
u32 value32;
ACPI_FUNCTION_TRACE(ex_system_io_space_handler);
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"System-IO (width %u) R/W %u Address=%8.8X%8.8X\n",
bit_width, function, ACPI_FORMAT_UINT64(address)));
/* Decode the function parameter */
switch (function) {
case ACPI_READ:
status = acpi_hw_read_port((acpi_io_address)address,
&value32, bit_width);
*value = value32;
break;
case ACPI_WRITE:
status = acpi_hw_write_port((acpi_io_address)address,
(u32)*value, bit_width);
break;
default:
status = AE_BAD_PARAMETER;
break;
}
return_ACPI_STATUS(status);
}
#ifdef ACPI_PCI_CONFIGURED
/*******************************************************************************
*
* FUNCTION: acpi_ex_pci_config_space_handler
*
* PARAMETERS: function - Read or Write operation
* address - Where in the space to read or write
* bit_width - Field width in bits (8, 16, or 32)
* value - Pointer to in or out value
* handler_context - Pointer to Handler's context
* region_context - Pointer to context specific to the
* accessed region
*
* RETURN: Status
*
* DESCRIPTION: Handler for the PCI Config address space (Op Region)
*
******************************************************************************/
acpi_status
acpi_ex_pci_config_space_handler(u32 function,
acpi_physical_address address,
u32 bit_width,
u64 *value,
void *handler_context, void *region_context)
{
acpi_status status = AE_OK;
struct acpi_pci_id *pci_id;
u16 pci_register;
ACPI_FUNCTION_TRACE(ex_pci_config_space_handler);
/*
* The arguments to acpi_os(Read|Write)pci_configuration are:
*
* pci_segment is the PCI bus segment range 0-31
* pci_bus is the PCI bus number range 0-255
* pci_device is the PCI device number range 0-31
* pci_function is the PCI device function number
* pci_register is the Config space register range 0-255 bytes
*
* value - input value for write, output address for read
*
*/
pci_id = (struct acpi_pci_id *)region_context;
pci_register = (u16) (u32) address;
ACPI_DEBUG_PRINT((ACPI_DB_INFO,
"Pci-Config %u (%u) Seg(%04x) Bus(%04x) "
"Dev(%04x) Func(%04x) Reg(%04x)\n",
function, bit_width, pci_id->segment, pci_id->bus,
pci_id->device, pci_id->function, pci_register));
switch (function) {
case ACPI_READ:
*value = 0;
status =
acpi_os_read_pci_configuration(pci_id, pci_register, value,
bit_width);
break;
case ACPI_WRITE:
status =
acpi_os_write_pci_configuration(pci_id, pci_register,
*value, bit_width);
break;
default:
status = AE_BAD_PARAMETER;
break;
}
return_ACPI_STATUS(status);
}
#endif
/*******************************************************************************
*
* FUNCTION: acpi_ex_cmos_space_handler
*
* PARAMETERS: function - Read or Write operation
* address - Where in the space to read or write
* bit_width - Field width in bits (8, 16, or 32)
* value - Pointer to in or out value
* handler_context - Pointer to Handler's context
* region_context - Pointer to context specific to the
* accessed region
*
* RETURN: Status
*
* DESCRIPTION: Handler for the CMOS address space (Op Region)
*
******************************************************************************/
acpi_status
acpi_ex_cmos_space_handler(u32 function,
acpi_physical_address address,
u32 bit_width,
u64 *value,
void *handler_context, void *region_context)
{
acpi_status status = AE_OK;
ACPI_FUNCTION_TRACE(ex_cmos_space_handler);
return_ACPI_STATUS(status);
}
#ifdef ACPI_PCI_CONFIGURED
/*******************************************************************************
*
* FUNCTION: acpi_ex_pci_bar_space_handler
*
* PARAMETERS: function - Read or Write operation
* address - Where in the space to read or write
* bit_width - Field width in bits (8, 16, or 32)
* value - Pointer to in or out value
* handler_context - Pointer to Handler's context
* region_context - Pointer to context specific to the
* accessed region
*
* RETURN: Status
*
* DESCRIPTION: Handler for the PCI bar_target address space (Op Region)
*
******************************************************************************/
acpi_status
acpi_ex_pci_bar_space_handler(u32 function,
acpi_physical_address address,
u32 bit_width,
u64 *value,
void *handler_context, void *region_context)
{
acpi_status status = AE_OK;
ACPI_FUNCTION_TRACE(ex_pci_bar_space_handler);
return_ACPI_STATUS(status);
}
#endif
/*******************************************************************************
*
* FUNCTION: acpi_ex_data_table_space_handler
*
* PARAMETERS: function - Read or Write operation
* address - Where in the space to read or write
* bit_width - Field width in bits (8, 16, or 32)
* value - Pointer to in or out value
* handler_context - Pointer to Handler's context
* region_context - Pointer to context specific to the
* accessed region
*
* RETURN: Status
*
* DESCRIPTION: Handler for the Data Table address space (Op Region)
*
******************************************************************************/
acpi_status
acpi_ex_data_table_space_handler(u32 function,
acpi_physical_address address,
u32 bit_width,
u64 *value,
void *handler_context, void *region_context)
{
struct acpi_data_table_mapping *mapping;
char *pointer;
ACPI_FUNCTION_TRACE(ex_data_table_space_handler);
mapping = (struct acpi_data_table_mapping *) region_context;
pointer = ACPI_CAST_PTR(char, mapping->pointer) +
(address - ACPI_PTR_TO_PHYSADDR(mapping->pointer));
/*
* Perform the memory read or write. The bit_width was already
* validated.
*/
switch (function) {
case ACPI_READ:
memcpy(ACPI_CAST_PTR(char, value), pointer,
ACPI_DIV_8(bit_width));
break;
case ACPI_WRITE:
memcpy(pointer, ACPI_CAST_PTR(char, value),
ACPI_DIV_8(bit_width));
break;
default:
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
return_ACPI_STATUS(AE_OK);
}