// SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
/******************************************************************************
*
* Module Name : hwvalid - I / O request validation
*
* Copyright ( C ) 2000 - 2025 , Intel Corp .
*
*****************************************************************************/
#include <acpi/acpi.h>
#include "accommon.h"
#define _COMPONENT ACPI_HARDWARE
ACPI_MODULE_NAME("hwvalid" )
/* Local prototypes */
static acpi_status
acpi_hw_validate_io_request(acpi_io_address address, u32 bit_width);
/*
* Protected I / O ports . Some ports are always illegal , and some are
* conditionally illegal . This table must remain ordered by port address .
*
* 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 nothing newer than
* the specific _ OSI strings .
*
* This provides ACPICA with the desired port protections and
* Microsoft compatibility .
*
* Description of port entries :
* DMA : DMA controller
* PIC0 : Programmable Interrupt Controller ( 8259 A )
* PIT1 : System Timer 1
* PIT2 : System Timer 2 failsafe
* RTC : Real - time clock
* CMOS : Extended CMOS
* DMA1 : DMA 1 page registers
* DMA1L : DMA 1 Ch 0 low page
* DMA2 : DMA 2 page registers
* DMA2L : DMA 2 low page refresh
* ARBC : Arbitration control
* SETUP : Reserved system board setup
* POS : POS channel select
* PIC1 : Cascaded PIC
* IDMA : ISA DMA
* ELCR : PIC edge / level registers
* PCI : PCI configuration space
*/
static const struct acpi_port_info acpi_protected_ports[] = {
{"DMA" , 0 x0000, 0 x000F, ACPI_OSI_WIN_XP},
{"PIC0" , 0 x0020, 0 x0021, ACPI_ALWAYS_ILLEGAL},
{"PIT1" , 0 x0040, 0 x0043, ACPI_OSI_WIN_XP},
{"PIT2" , 0 x0048, 0 x004B, ACPI_OSI_WIN_XP},
{"RTC" , 0 x0070, 0 x0071, ACPI_OSI_WIN_XP},
{"CMOS" , 0 x0074, 0 x0076, ACPI_OSI_WIN_XP},
{"DMA1" , 0 x0081, 0 x0083, ACPI_OSI_WIN_XP},
{"DMA1L" , 0 x0087, 0 x0087, ACPI_OSI_WIN_XP},
{"DMA2" , 0 x0089, 0 x008B, ACPI_OSI_WIN_XP},
{"DMA2L" , 0 x008F, 0 x008F, ACPI_OSI_WIN_XP},
{"ARBC" , 0 x0090, 0 x0091, ACPI_OSI_WIN_XP},
{"SETUP" , 0 x0093, 0 x0094, ACPI_OSI_WIN_XP},
{"POS" , 0 x0096, 0 x0097, ACPI_OSI_WIN_XP},
{"PIC1" , 0 x00A0, 0 x00A1, ACPI_ALWAYS_ILLEGAL},
{"IDMA" , 0 x00C0, 0 x00DF, ACPI_OSI_WIN_XP},
{"ELCR" , 0 x04D0, 0 x04D1, ACPI_ALWAYS_ILLEGAL},
{"PCI" , 0 x0CF8, 0 x0CFF, ACPI_OSI_WIN_XP}
};
#define ACPI_PORT_INFO_ENTRIES ACPI_ARRAY_LENGTH (acpi_protected_ports)
/******************************************************************************
*
* FUNCTION : acpi_hw_validate_io_request
*
* PARAMETERS : Address Address of I / O port / register
* bit_width Number of bits ( 8 , 16 , 32 )
*
* RETURN : Status
*
* DESCRIPTION : Validates an I / O request ( address / length ) . Certain ports are
* always illegal and some ports are only illegal depending on
* the requests the BIOS AML code makes to the predefined
* _ OSI method .
*
******************************************************************************/
static acpi_status
acpi_hw_validate_io_request(acpi_io_address address, u32 bit_width)
{
u32 i;
u32 byte_width;
acpi_io_address last_address;
const struct acpi_port_info *port_info;
ACPI_FUNCTION_TRACE(hw_validate_io_request);
/* Supported widths are 8/16/32 */
if ((bit_width != 8 ) && (bit_width != 16 ) && (bit_width != 32 )) {
ACPI_ERROR((AE_INFO,
"Bad BitWidth parameter: %8.8X" , bit_width));
return_ACPI_STATUS(AE_BAD_PARAMETER);
}
port_info = acpi_protected_ports;
byte_width = ACPI_DIV_8(bit_width);
last_address = address + byte_width - 1 ;
ACPI_DEBUG_PRINT((ACPI_DB_IO,
"Address %8.8X%8.8X LastAddress %8.8X%8.8X Length %X" ,
ACPI_FORMAT_UINT64(address),
ACPI_FORMAT_UINT64(last_address), byte_width));
/* Maximum 16-bit address in I/O space */
if (last_address > ACPI_UINT16_MAX) {
ACPI_ERROR((AE_INFO,
"Illegal I/O port address/length above 64K: %8.8X%8.8X/0x%X" ,
ACPI_FORMAT_UINT64(address), byte_width));
return_ACPI_STATUS(AE_LIMIT);
}
/* Exit if requested address is not within the protected port table */
if (address > acpi_protected_ports[ACPI_PORT_INFO_ENTRIES - 1 ].end) {
return_ACPI_STATUS(AE_OK);
}
/* Check request against the list of protected I/O ports */
for (i = 0 ; i < ACPI_PORT_INFO_ENTRIES; i++, port_info++) {
/*
* Check if the requested address range will write to a reserved
* port . There are four cases to consider :
*
* 1 ) Address range is contained completely in the port address range
* 2 ) Address range overlaps port range at the port range start
* 3 ) Address range overlaps port range at the port range end
* 4 ) Address range completely encompasses the port range
*/
if ((address <= port_info->end)
&& (last_address >= port_info->start)) {
/* Port illegality may depend on the _OSI calls made by the BIOS */
if (port_info->osi_dependency == ACPI_ALWAYS_ILLEGAL ||
acpi_gbl_osi_data == port_info->osi_dependency) {
ACPI_DEBUG_PRINT((ACPI_DB_VALUES,
"Denied AML access to port 0x%8.8X%8.8X/%X (%s 0x%.4X-0x%.4X)\n" ,
ACPI_FORMAT_UINT64(address),
byte_width, port_info->name,
port_info->start,
port_info->end));
return_ACPI_STATUS(AE_AML_ILLEGAL_ADDRESS);
}
}
/* Finished if address range ends before the end of this port */
if (last_address <= port_info->end) {
break ;
}
}
return_ACPI_STATUS(AE_OK);
}
/******************************************************************************
*
* FUNCTION : acpi_hw_read_port
*
* PARAMETERS : Address Address of I / O port / register to read
* Value Where value ( data ) is returned
* Width Number of bits
*
* 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
* address and the length .
*
*****************************************************************************/
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_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 compatibility with other ACPI implementations .
*/
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);
}
/******************************************************************************
*
* FUNCTION : acpi_hw_write_port
*
* PARAMETERS : Address Address of I / O port / register to write
* Value Value to write
* Width Number of bits
*
* 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
* address and the length .
*
*****************************************************************************/
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_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 compatibility with other ACPI implementations .
*/
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) & 0 xFF, 8 );
if (ACPI_FAILURE(status)) {
return (status);
}
}
address++;
}
return (AE_OK);
}
/******************************************************************************
*
* FUNCTION : acpi_hw_validate_io_block
*
* PARAMETERS : Address Address of I / O port / register blobk
* bit_width Number of bits ( 8 , 16 , 32 ) in each register
* count Number of registers in the block
*
* RETURN : Status
*
* DESCRIPTION : Validates a block of I / O ports / registers .
*
******************************************************************************/
acpi_status acpi_hw_validate_io_block(u64 address, u32 bit_width, u32 count)
{
acpi_status status;
while (count--) {
status = acpi_hw_validate_io_request((acpi_io_address)address,
bit_width);
if (ACPI_FAILURE(status))
return_ACPI_STATUS(status);
address += ACPI_DIV_8(bit_width);
}
return_ACPI_STATUS(AE_OK);
}
Messung V0.5 in Prozent C=94 H=96 G=94
¤ Dauer der Verarbeitung: 0.21 Sekunden
(vorverarbeitet am 2026-09-28)
¤
*© Formatika GbR, Deutschland