xref: /OK3568_Linux_fs/kernel/drivers/acpi/acpica/hwvalid.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
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