1*4882a593Smuzhiyun // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
2*4882a593Smuzhiyun /******************************************************************************
3*4882a593Smuzhiyun *
4*4882a593Smuzhiyun * Module Name: hwvalid - I/O request validation
5*4882a593Smuzhiyun *
6*4882a593Smuzhiyun * Copyright (C) 2000 - 2020, Intel Corp.
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun *****************************************************************************/
9*4882a593Smuzhiyun
10*4882a593Smuzhiyun #include <acpi/acpi.h>
11*4882a593Smuzhiyun #include "accommon.h"
12*4882a593Smuzhiyun
13*4882a593Smuzhiyun #define _COMPONENT ACPI_HARDWARE
14*4882a593Smuzhiyun ACPI_MODULE_NAME("hwvalid")
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun /* Local prototypes */
17*4882a593Smuzhiyun static acpi_status
18*4882a593Smuzhiyun acpi_hw_validate_io_request(acpi_io_address address, u32 bit_width);
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun /*
21*4882a593Smuzhiyun * Protected I/O ports. Some ports are always illegal, and some are
22*4882a593Smuzhiyun * conditionally illegal. This table must remain ordered by port address.
23*4882a593Smuzhiyun *
24*4882a593Smuzhiyun * The table is used to implement the Microsoft port access rules that
25*4882a593Smuzhiyun * first appeared in Windows XP. Some ports are always illegal, and some
26*4882a593Smuzhiyun * ports are only illegal if the BIOS calls _OSI with a win_XP string or
27*4882a593Smuzhiyun * later (meaning that the BIOS itelf is post-XP.)
28*4882a593Smuzhiyun *
29*4882a593Smuzhiyun * This provides ACPICA with the desired port protections and
30*4882a593Smuzhiyun * Microsoft compatibility.
31*4882a593Smuzhiyun *
32*4882a593Smuzhiyun * Description of port entries:
33*4882a593Smuzhiyun * DMA: DMA controller
34*4882a593Smuzhiyun * PIC0: Programmable Interrupt Controller (8259A)
35*4882a593Smuzhiyun * PIT1: System Timer 1
36*4882a593Smuzhiyun * PIT2: System Timer 2 failsafe
37*4882a593Smuzhiyun * RTC: Real-time clock
38*4882a593Smuzhiyun * CMOS: Extended CMOS
39*4882a593Smuzhiyun * DMA1: DMA 1 page registers
40*4882a593Smuzhiyun * DMA1L: DMA 1 Ch 0 low page
41*4882a593Smuzhiyun * DMA2: DMA 2 page registers
42*4882a593Smuzhiyun * DMA2L: DMA 2 low page refresh
43*4882a593Smuzhiyun * ARBC: Arbitration control
44*4882a593Smuzhiyun * SETUP: Reserved system board setup
45*4882a593Smuzhiyun * POS: POS channel select
46*4882a593Smuzhiyun * PIC1: Cascaded PIC
47*4882a593Smuzhiyun * IDMA: ISA DMA
48*4882a593Smuzhiyun * ELCR: PIC edge/level registers
49*4882a593Smuzhiyun * PCI: PCI configuration space
50*4882a593Smuzhiyun */
51*4882a593Smuzhiyun static const struct acpi_port_info acpi_protected_ports[] = {
52*4882a593Smuzhiyun {"DMA", 0x0000, 0x000F, ACPI_OSI_WIN_XP},
53*4882a593Smuzhiyun {"PIC0", 0x0020, 0x0021, ACPI_ALWAYS_ILLEGAL},
54*4882a593Smuzhiyun {"PIT1", 0x0040, 0x0043, ACPI_OSI_WIN_XP},
55*4882a593Smuzhiyun {"PIT2", 0x0048, 0x004B, ACPI_OSI_WIN_XP},
56*4882a593Smuzhiyun {"RTC", 0x0070, 0x0071, ACPI_OSI_WIN_XP},
57*4882a593Smuzhiyun {"CMOS", 0x0074, 0x0076, ACPI_OSI_WIN_XP},
58*4882a593Smuzhiyun {"DMA1", 0x0081, 0x0083, ACPI_OSI_WIN_XP},
59*4882a593Smuzhiyun {"DMA1L", 0x0087, 0x0087, ACPI_OSI_WIN_XP},
60*4882a593Smuzhiyun {"DMA2", 0x0089, 0x008B, ACPI_OSI_WIN_XP},
61*4882a593Smuzhiyun {"DMA2L", 0x008F, 0x008F, ACPI_OSI_WIN_XP},
62*4882a593Smuzhiyun {"ARBC", 0x0090, 0x0091, ACPI_OSI_WIN_XP},
63*4882a593Smuzhiyun {"SETUP", 0x0093, 0x0094, ACPI_OSI_WIN_XP},
64*4882a593Smuzhiyun {"POS", 0x0096, 0x0097, ACPI_OSI_WIN_XP},
65*4882a593Smuzhiyun {"PIC1", 0x00A0, 0x00A1, ACPI_ALWAYS_ILLEGAL},
66*4882a593Smuzhiyun {"IDMA", 0x00C0, 0x00DF, ACPI_OSI_WIN_XP},
67*4882a593Smuzhiyun {"ELCR", 0x04D0, 0x04D1, ACPI_ALWAYS_ILLEGAL},
68*4882a593Smuzhiyun {"PCI", 0x0CF8, 0x0CFF, ACPI_OSI_WIN_XP}
69*4882a593Smuzhiyun };
70*4882a593Smuzhiyun
71*4882a593Smuzhiyun #define ACPI_PORT_INFO_ENTRIES ACPI_ARRAY_LENGTH (acpi_protected_ports)
72*4882a593Smuzhiyun
73*4882a593Smuzhiyun /******************************************************************************
74*4882a593Smuzhiyun *
75*4882a593Smuzhiyun * FUNCTION: acpi_hw_validate_io_request
76*4882a593Smuzhiyun *
77*4882a593Smuzhiyun * PARAMETERS: Address Address of I/O port/register
78*4882a593Smuzhiyun * bit_width Number of bits (8,16,32)
79*4882a593Smuzhiyun *
80*4882a593Smuzhiyun * RETURN: Status
81*4882a593Smuzhiyun *
82*4882a593Smuzhiyun * DESCRIPTION: Validates an I/O request (address/length). Certain ports are
83*4882a593Smuzhiyun * always illegal and some ports are only illegal depending on
84*4882a593Smuzhiyun * the requests the BIOS AML code makes to the predefined
85*4882a593Smuzhiyun * _OSI method.
86*4882a593Smuzhiyun *
87*4882a593Smuzhiyun ******************************************************************************/
88*4882a593Smuzhiyun
89*4882a593Smuzhiyun static acpi_status
acpi_hw_validate_io_request(acpi_io_address address,u32 bit_width)90*4882a593Smuzhiyun acpi_hw_validate_io_request(acpi_io_address address, u32 bit_width)
91*4882a593Smuzhiyun {
92*4882a593Smuzhiyun u32 i;
93*4882a593Smuzhiyun u32 byte_width;
94*4882a593Smuzhiyun acpi_io_address last_address;
95*4882a593Smuzhiyun const struct acpi_port_info *port_info;
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun ACPI_FUNCTION_TRACE(hw_validate_io_request);
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun /* Supported widths are 8/16/32 */
100*4882a593Smuzhiyun
101*4882a593Smuzhiyun if ((bit_width != 8) && (bit_width != 16) && (bit_width != 32)) {
102*4882a593Smuzhiyun ACPI_ERROR((AE_INFO,
103*4882a593Smuzhiyun "Bad BitWidth parameter: %8.8X", bit_width));
104*4882a593Smuzhiyun return_ACPI_STATUS(AE_BAD_PARAMETER);
105*4882a593Smuzhiyun }
106*4882a593Smuzhiyun
107*4882a593Smuzhiyun port_info = acpi_protected_ports;
108*4882a593Smuzhiyun byte_width = ACPI_DIV_8(bit_width);
109*4882a593Smuzhiyun last_address = address + byte_width - 1;
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun ACPI_DEBUG_PRINT((ACPI_DB_IO,
112*4882a593Smuzhiyun "Address %8.8X%8.8X LastAddress %8.8X%8.8X Length %X",
113*4882a593Smuzhiyun ACPI_FORMAT_UINT64(address),
114*4882a593Smuzhiyun ACPI_FORMAT_UINT64(last_address), byte_width));
115*4882a593Smuzhiyun
116*4882a593Smuzhiyun /* Maximum 16-bit address in I/O space */
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun if (last_address > ACPI_UINT16_MAX) {
119*4882a593Smuzhiyun ACPI_ERROR((AE_INFO,
120*4882a593Smuzhiyun "Illegal I/O port address/length above 64K: %8.8X%8.8X/0x%X",
121*4882a593Smuzhiyun ACPI_FORMAT_UINT64(address), byte_width));
122*4882a593Smuzhiyun return_ACPI_STATUS(AE_LIMIT);
123*4882a593Smuzhiyun }
124*4882a593Smuzhiyun
125*4882a593Smuzhiyun /* Exit if requested address is not within the protected port table */
126*4882a593Smuzhiyun
127*4882a593Smuzhiyun if (address > acpi_protected_ports[ACPI_PORT_INFO_ENTRIES - 1].end) {
128*4882a593Smuzhiyun return_ACPI_STATUS(AE_OK);
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun
131*4882a593Smuzhiyun /* Check request against the list of protected I/O ports */
132*4882a593Smuzhiyun
133*4882a593Smuzhiyun for (i = 0; i < ACPI_PORT_INFO_ENTRIES; i++, port_info++) {
134*4882a593Smuzhiyun /*
135*4882a593Smuzhiyun * Check if the requested address range will write to a reserved
136*4882a593Smuzhiyun * port. There are four cases to consider:
137*4882a593Smuzhiyun *
138*4882a593Smuzhiyun * 1) Address range is contained completely in the port address range
139*4882a593Smuzhiyun * 2) Address range overlaps port range at the port range start
140*4882a593Smuzhiyun * 3) Address range overlaps port range at the port range end
141*4882a593Smuzhiyun * 4) Address range completely encompasses the port range
142*4882a593Smuzhiyun */
143*4882a593Smuzhiyun if ((address <= port_info->end)
144*4882a593Smuzhiyun && (last_address >= port_info->start)) {
145*4882a593Smuzhiyun
146*4882a593Smuzhiyun /* Port illegality may depend on the _OSI calls made by the BIOS */
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun if (acpi_gbl_osi_data >= port_info->osi_dependency) {
149*4882a593Smuzhiyun ACPI_DEBUG_PRINT((ACPI_DB_VALUES,
150*4882a593Smuzhiyun "Denied AML access to port 0x%8.8X%8.8X/%X (%s 0x%.4X-0x%.4X)\n",
151*4882a593Smuzhiyun ACPI_FORMAT_UINT64(address),
152*4882a593Smuzhiyun byte_width, port_info->name,
153*4882a593Smuzhiyun port_info->start,
154*4882a593Smuzhiyun port_info->end));
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun return_ACPI_STATUS(AE_AML_ILLEGAL_ADDRESS);
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun }
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun /* Finished if address range ends before the end of this port */
161*4882a593Smuzhiyun
162*4882a593Smuzhiyun if (last_address <= port_info->end) {
163*4882a593Smuzhiyun break;
164*4882a593Smuzhiyun }
165*4882a593Smuzhiyun }
166*4882a593Smuzhiyun
167*4882a593Smuzhiyun return_ACPI_STATUS(AE_OK);
168*4882a593Smuzhiyun }
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun /******************************************************************************
171*4882a593Smuzhiyun *
172*4882a593Smuzhiyun * FUNCTION: acpi_hw_read_port
173*4882a593Smuzhiyun *
174*4882a593Smuzhiyun * PARAMETERS: Address Address of I/O port/register to read
175*4882a593Smuzhiyun * Value Where value (data) is returned
176*4882a593Smuzhiyun * Width Number of bits
177*4882a593Smuzhiyun *
178*4882a593Smuzhiyun * RETURN: Status and value read from port
179*4882a593Smuzhiyun *
180*4882a593Smuzhiyun * DESCRIPTION: Read data from an I/O port or register. This is a front-end
181*4882a593Smuzhiyun * to acpi_os_read_port that performs validation on both the port
182*4882a593Smuzhiyun * address and the length.
183*4882a593Smuzhiyun *
184*4882a593Smuzhiyun *****************************************************************************/
185*4882a593Smuzhiyun
acpi_hw_read_port(acpi_io_address address,u32 * value,u32 width)186*4882a593Smuzhiyun acpi_status acpi_hw_read_port(acpi_io_address address, u32 *value, u32 width)
187*4882a593Smuzhiyun {
188*4882a593Smuzhiyun acpi_status status;
189*4882a593Smuzhiyun u32 one_byte;
190*4882a593Smuzhiyun u32 i;
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun /* Truncate address to 16 bits if requested */
193*4882a593Smuzhiyun
194*4882a593Smuzhiyun if (acpi_gbl_truncate_io_addresses) {
195*4882a593Smuzhiyun address &= ACPI_UINT16_MAX;
196*4882a593Smuzhiyun }
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun /* Validate the entire request and perform the I/O */
199*4882a593Smuzhiyun
200*4882a593Smuzhiyun status = acpi_hw_validate_io_request(address, width);
201*4882a593Smuzhiyun if (ACPI_SUCCESS(status)) {
202*4882a593Smuzhiyun status = acpi_os_read_port(address, value, width);
203*4882a593Smuzhiyun return (status);
204*4882a593Smuzhiyun }
205*4882a593Smuzhiyun
206*4882a593Smuzhiyun if (status != AE_AML_ILLEGAL_ADDRESS) {
207*4882a593Smuzhiyun return (status);
208*4882a593Smuzhiyun }
209*4882a593Smuzhiyun
210*4882a593Smuzhiyun /*
211*4882a593Smuzhiyun * There has been a protection violation within the request. Fall
212*4882a593Smuzhiyun * back to byte granularity port I/O and ignore the failing bytes.
213*4882a593Smuzhiyun * This provides compatibility with other ACPI implementations.
214*4882a593Smuzhiyun */
215*4882a593Smuzhiyun for (i = 0, *value = 0; i < width; i += 8) {
216*4882a593Smuzhiyun
217*4882a593Smuzhiyun /* Validate and read one byte */
218*4882a593Smuzhiyun
219*4882a593Smuzhiyun if (acpi_hw_validate_io_request(address, 8) == AE_OK) {
220*4882a593Smuzhiyun status = acpi_os_read_port(address, &one_byte, 8);
221*4882a593Smuzhiyun if (ACPI_FAILURE(status)) {
222*4882a593Smuzhiyun return (status);
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun *value |= (one_byte << i);
226*4882a593Smuzhiyun }
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun address++;
229*4882a593Smuzhiyun }
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun return (AE_OK);
232*4882a593Smuzhiyun }
233*4882a593Smuzhiyun
234*4882a593Smuzhiyun /******************************************************************************
235*4882a593Smuzhiyun *
236*4882a593Smuzhiyun * FUNCTION: acpi_hw_write_port
237*4882a593Smuzhiyun *
238*4882a593Smuzhiyun * PARAMETERS: Address Address of I/O port/register to write
239*4882a593Smuzhiyun * Value Value to write
240*4882a593Smuzhiyun * Width Number of bits
241*4882a593Smuzhiyun *
242*4882a593Smuzhiyun * RETURN: Status
243*4882a593Smuzhiyun *
244*4882a593Smuzhiyun * DESCRIPTION: Write data to an I/O port or register. This is a front-end
245*4882a593Smuzhiyun * to acpi_os_write_port that performs validation on both the port
246*4882a593Smuzhiyun * address and the length.
247*4882a593Smuzhiyun *
248*4882a593Smuzhiyun *****************************************************************************/
249*4882a593Smuzhiyun
acpi_hw_write_port(acpi_io_address address,u32 value,u32 width)250*4882a593Smuzhiyun acpi_status acpi_hw_write_port(acpi_io_address address, u32 value, u32 width)
251*4882a593Smuzhiyun {
252*4882a593Smuzhiyun acpi_status status;
253*4882a593Smuzhiyun u32 i;
254*4882a593Smuzhiyun
255*4882a593Smuzhiyun /* Truncate address to 16 bits if requested */
256*4882a593Smuzhiyun
257*4882a593Smuzhiyun if (acpi_gbl_truncate_io_addresses) {
258*4882a593Smuzhiyun address &= ACPI_UINT16_MAX;
259*4882a593Smuzhiyun }
260*4882a593Smuzhiyun
261*4882a593Smuzhiyun /* Validate the entire request and perform the I/O */
262*4882a593Smuzhiyun
263*4882a593Smuzhiyun status = acpi_hw_validate_io_request(address, width);
264*4882a593Smuzhiyun if (ACPI_SUCCESS(status)) {
265*4882a593Smuzhiyun status = acpi_os_write_port(address, value, width);
266*4882a593Smuzhiyun return (status);
267*4882a593Smuzhiyun }
268*4882a593Smuzhiyun
269*4882a593Smuzhiyun if (status != AE_AML_ILLEGAL_ADDRESS) {
270*4882a593Smuzhiyun return (status);
271*4882a593Smuzhiyun }
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun /*
274*4882a593Smuzhiyun * There has been a protection violation within the request. Fall
275*4882a593Smuzhiyun * back to byte granularity port I/O and ignore the failing bytes.
276*4882a593Smuzhiyun * This provides compatibility with other ACPI implementations.
277*4882a593Smuzhiyun */
278*4882a593Smuzhiyun for (i = 0; i < width; i += 8) {
279*4882a593Smuzhiyun
280*4882a593Smuzhiyun /* Validate and write one byte */
281*4882a593Smuzhiyun
282*4882a593Smuzhiyun if (acpi_hw_validate_io_request(address, 8) == AE_OK) {
283*4882a593Smuzhiyun status =
284*4882a593Smuzhiyun acpi_os_write_port(address, (value >> i) & 0xFF, 8);
285*4882a593Smuzhiyun if (ACPI_FAILURE(status)) {
286*4882a593Smuzhiyun return (status);
287*4882a593Smuzhiyun }
288*4882a593Smuzhiyun }
289*4882a593Smuzhiyun
290*4882a593Smuzhiyun address++;
291*4882a593Smuzhiyun }
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun return (AE_OK);
294*4882a593Smuzhiyun }
295*4882a593Smuzhiyun
296*4882a593Smuzhiyun /******************************************************************************
297*4882a593Smuzhiyun *
298*4882a593Smuzhiyun * FUNCTION: acpi_hw_validate_io_block
299*4882a593Smuzhiyun *
300*4882a593Smuzhiyun * PARAMETERS: Address Address of I/O port/register blobk
301*4882a593Smuzhiyun * bit_width Number of bits (8,16,32) in each register
302*4882a593Smuzhiyun * count Number of registers in the block
303*4882a593Smuzhiyun *
304*4882a593Smuzhiyun * RETURN: Status
305*4882a593Smuzhiyun *
306*4882a593Smuzhiyun * DESCRIPTION: Validates a block of I/O ports/registers.
307*4882a593Smuzhiyun *
308*4882a593Smuzhiyun ******************************************************************************/
309*4882a593Smuzhiyun
acpi_hw_validate_io_block(u64 address,u32 bit_width,u32 count)310*4882a593Smuzhiyun acpi_status acpi_hw_validate_io_block(u64 address, u32 bit_width, u32 count)
311*4882a593Smuzhiyun {
312*4882a593Smuzhiyun acpi_status status;
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun while (count--) {
315*4882a593Smuzhiyun status = acpi_hw_validate_io_request((acpi_io_address)address,
316*4882a593Smuzhiyun bit_width);
317*4882a593Smuzhiyun if (ACPI_FAILURE(status))
318*4882a593Smuzhiyun return_ACPI_STATUS(status);
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun address += ACPI_DIV_8(bit_width);
321*4882a593Smuzhiyun }
322*4882a593Smuzhiyun
323*4882a593Smuzhiyun return_ACPI_STATUS(AE_OK);
324*4882a593Smuzhiyun }
325