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