a83e1385b7
Undo the modifications made in commitd410ee5109
("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>
526 lines
14 KiB
C
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);
|
|
}
|