1 | /****************************************************************************** |
2 | * |
3 | * Module Name: hwvalid - I/O request validation |
4 | * |
5 | *****************************************************************************/ |
6 | |
7 | /* |
8 | * Copyright (C) 2000 - 2016, Intel Corp. |
9 | * All rights reserved. |
10 | * |
11 | * Redistribution and use in source and binary forms, with or without |
12 | * modification, are permitted provided that the following conditions |
13 | * are met: |
14 | * 1. Redistributions of source code must retain the above copyright |
15 | * notice, this list of conditions, and the following disclaimer, |
16 | * without modification. |
17 | * 2. Redistributions in binary form must reproduce at minimum a disclaimer |
18 | * substantially similar to the "NO WARRANTY" disclaimer below |
19 | * ("Disclaimer") and any redistribution must be conditioned upon |
20 | * including a substantially similar Disclaimer requirement for further |
21 | * binary redistribution. |
22 | * 3. Neither the names of the above-listed copyright holders nor the names |
23 | * of any contributors may be used to endorse or promote products derived |
24 | * from this software without specific prior written permission. |
25 | * |
26 | * Alternatively, this software may be distributed under the terms of the |
27 | * GNU General Public License ("GPL") version 2 as published by the Free |
28 | * Software Foundation. |
29 | * |
30 | * NO WARRANTY |
31 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
32 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
33 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR |
34 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
35 | * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
36 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
37 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
38 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
39 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
40 | * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
41 | * POSSIBILITY OF SUCH DAMAGES. |
42 | */ |
43 | |
44 | #include "acpi.h" |
45 | #include "accommon.h" |
46 | |
47 | #define _COMPONENT ACPI_HARDWARE |
48 | ACPI_MODULE_NAME ("hwvalid" ) |
49 | |
50 | /* Local prototypes */ |
51 | |
52 | static ACPI_STATUS |
53 | AcpiHwValidateIoRequest ( |
54 | ACPI_IO_ADDRESS Address, |
55 | UINT32 BitWidth); |
56 | |
57 | |
58 | /* |
59 | * Protected I/O ports. Some ports are always illegal, and some are |
60 | * conditionally illegal. This table must remain ordered by port address. |
61 | * |
62 | * The table is used to implement the Microsoft port access rules that |
63 | * first appeared in Windows XP. Some ports are always illegal, and some |
64 | * ports are only illegal if the BIOS calls _OSI with a WinXP string or |
65 | * later (meaning that the BIOS itelf is post-XP.) |
66 | * |
67 | * This provides ACPICA with the desired port protections and |
68 | * Microsoft compatibility. |
69 | * |
70 | * Description of port entries: |
71 | * DMA: DMA controller |
72 | * PIC0: Programmable Interrupt Controller (8259A) |
73 | * PIT1: System Timer 1 |
74 | * PIT2: System Timer 2 failsafe |
75 | * RTC: Real-time clock |
76 | * CMOS: Extended CMOS |
77 | * DMA1: DMA 1 page registers |
78 | * DMA1L: DMA 1 Ch 0 low page |
79 | * DMA2: DMA 2 page registers |
80 | * DMA2L: DMA 2 low page refresh |
81 | * ARBC: Arbitration control |
82 | * SETUP: Reserved system board setup |
83 | * POS: POS channel select |
84 | * PIC1: Cascaded PIC |
85 | * IDMA: ISA DMA |
86 | * ELCR: PIC edge/level registers |
87 | * PCI: PCI configuration space |
88 | */ |
89 | static const ACPI_PORT_INFO AcpiProtectedPorts[] = |
90 | { |
91 | {"DMA" , 0x0000, 0x000F, ACPI_OSI_WIN_XP}, |
92 | {"PIC0" , 0x0020, 0x0021, ACPI_ALWAYS_ILLEGAL}, |
93 | {"PIT1" , 0x0040, 0x0043, ACPI_OSI_WIN_XP}, |
94 | {"PIT2" , 0x0048, 0x004B, ACPI_OSI_WIN_XP}, |
95 | {"RTC" , 0x0070, 0x0071, ACPI_OSI_WIN_XP}, |
96 | {"CMOS" , 0x0074, 0x0076, ACPI_OSI_WIN_XP}, |
97 | {"DMA1" , 0x0081, 0x0083, ACPI_OSI_WIN_XP}, |
98 | {"DMA1L" , 0x0087, 0x0087, ACPI_OSI_WIN_XP}, |
99 | {"DMA2" , 0x0089, 0x008B, ACPI_OSI_WIN_XP}, |
100 | {"DMA2L" , 0x008F, 0x008F, ACPI_OSI_WIN_XP}, |
101 | {"ARBC" , 0x0090, 0x0091, ACPI_OSI_WIN_XP}, |
102 | {"SETUP" , 0x0093, 0x0094, ACPI_OSI_WIN_XP}, |
103 | {"POS" , 0x0096, 0x0097, ACPI_OSI_WIN_XP}, |
104 | {"PIC1" , 0x00A0, 0x00A1, ACPI_ALWAYS_ILLEGAL}, |
105 | {"IDMA" , 0x00C0, 0x00DF, ACPI_OSI_WIN_XP}, |
106 | {"ELCR" , 0x04D0, 0x04D1, ACPI_ALWAYS_ILLEGAL}, |
107 | {"PCI" , 0x0CF8, 0x0CFF, ACPI_OSI_WIN_XP} |
108 | }; |
109 | |
110 | #define ACPI_PORT_INFO_ENTRIES ACPI_ARRAY_LENGTH (AcpiProtectedPorts) |
111 | |
112 | |
113 | /****************************************************************************** |
114 | * |
115 | * FUNCTION: AcpiHwValidateIoRequest |
116 | * |
117 | * PARAMETERS: Address Address of I/O port/register |
118 | * BitWidth Number of bits (8,16,32) |
119 | * |
120 | * RETURN: Status |
121 | * |
122 | * DESCRIPTION: Validates an I/O request (address/length). Certain ports are |
123 | * always illegal and some ports are only illegal depending on |
124 | * the requests the BIOS AML code makes to the predefined |
125 | * _OSI method. |
126 | * |
127 | ******************************************************************************/ |
128 | |
129 | static ACPI_STATUS |
130 | AcpiHwValidateIoRequest ( |
131 | ACPI_IO_ADDRESS Address, |
132 | UINT32 BitWidth) |
133 | { |
134 | UINT32 i; |
135 | UINT32 ByteWidth; |
136 | ACPI_IO_ADDRESS LastAddress; |
137 | const ACPI_PORT_INFO *PortInfo; |
138 | |
139 | |
140 | ACPI_FUNCTION_TRACE (HwValidateIoRequest); |
141 | |
142 | |
143 | /* Supported widths are 8/16/32 */ |
144 | |
145 | if ((BitWidth != 8) && |
146 | (BitWidth != 16) && |
147 | (BitWidth != 32)) |
148 | { |
149 | ACPI_ERROR ((AE_INFO, |
150 | "Bad BitWidth parameter: %8.8X" , BitWidth)); |
151 | return (AE_BAD_PARAMETER); |
152 | } |
153 | |
154 | PortInfo = AcpiProtectedPorts; |
155 | ByteWidth = ACPI_DIV_8 (BitWidth); |
156 | LastAddress = Address + ByteWidth - 1; |
157 | |
158 | ACPI_DEBUG_PRINT ((ACPI_DB_IO, "Address %8.8X%8.8X LastAddress %8.8X%8.8X Length %X" , |
159 | ACPI_FORMAT_UINT64 (Address), ACPI_FORMAT_UINT64 (LastAddress), |
160 | ByteWidth)); |
161 | |
162 | /* Maximum 16-bit address in I/O space */ |
163 | |
164 | if (LastAddress > ACPI_UINT16_MAX) |
165 | { |
166 | ACPI_ERROR ((AE_INFO, |
167 | "Illegal I/O port address/length above 64K: %8.8X%8.8X/0x%X" , |
168 | ACPI_FORMAT_UINT64 (Address), ByteWidth)); |
169 | return_ACPI_STATUS (AE_LIMIT); |
170 | } |
171 | |
172 | /* Exit if requested address is not within the protected port table */ |
173 | |
174 | if (Address > AcpiProtectedPorts[ACPI_PORT_INFO_ENTRIES - 1].End) |
175 | { |
176 | return_ACPI_STATUS (AE_OK); |
177 | } |
178 | |
179 | /* Check request against the list of protected I/O ports */ |
180 | |
181 | for (i = 0; i < ACPI_PORT_INFO_ENTRIES; i++, PortInfo++) |
182 | { |
183 | /* |
184 | * Check if the requested address range will write to a reserved |
185 | * port. Four cases to consider: |
186 | * |
187 | * 1) Address range is contained completely in the port address range |
188 | * 2) Address range overlaps port range at the port range start |
189 | * 3) Address range overlaps port range at the port range end |
190 | * 4) Address range completely encompasses the port range |
191 | */ |
192 | if ((Address <= PortInfo->End) && (LastAddress >= PortInfo->Start)) |
193 | { |
194 | /* Port illegality may depend on the _OSI calls made by the BIOS */ |
195 | |
196 | if (AcpiGbl_OsiData >= PortInfo->OsiDependency) |
197 | { |
198 | ACPI_DEBUG_PRINT ((ACPI_DB_IO, |
199 | "Denied AML access to port 0x%8.8X%8.8X/%X (%s 0x%.4X-0x%.4X)" , |
200 | ACPI_FORMAT_UINT64 (Address), ByteWidth, PortInfo->Name, |
201 | PortInfo->Start, PortInfo->End)); |
202 | |
203 | return_ACPI_STATUS (AE_AML_ILLEGAL_ADDRESS); |
204 | } |
205 | } |
206 | |
207 | /* Finished if address range ends before the end of this port */ |
208 | |
209 | if (LastAddress <= PortInfo->End) |
210 | { |
211 | break; |
212 | } |
213 | } |
214 | |
215 | return_ACPI_STATUS (AE_OK); |
216 | } |
217 | |
218 | |
219 | /****************************************************************************** |
220 | * |
221 | * FUNCTION: AcpiHwReadPort |
222 | * |
223 | * PARAMETERS: Address Address of I/O port/register to read |
224 | * Value Where value is placed |
225 | * Width Number of bits |
226 | * |
227 | * RETURN: Status and value read from port |
228 | * |
229 | * DESCRIPTION: Read data from an I/O port or register. This is a front-end |
230 | * to AcpiOsReadPort that performs validation on both the port |
231 | * address and the length. |
232 | * |
233 | *****************************************************************************/ |
234 | |
235 | ACPI_STATUS |
236 | AcpiHwReadPort ( |
237 | ACPI_IO_ADDRESS Address, |
238 | UINT32 *Value, |
239 | UINT32 Width) |
240 | { |
241 | ACPI_STATUS Status; |
242 | UINT32 OneByte; |
243 | UINT32 i; |
244 | |
245 | |
246 | /* Truncate address to 16 bits if requested */ |
247 | |
248 | if (AcpiGbl_TruncateIoAddresses) |
249 | { |
250 | Address &= ACPI_UINT16_MAX; |
251 | } |
252 | |
253 | /* Validate the entire request and perform the I/O */ |
254 | |
255 | Status = AcpiHwValidateIoRequest (Address, Width); |
256 | if (ACPI_SUCCESS (Status)) |
257 | { |
258 | Status = AcpiOsReadPort (Address, Value, Width); |
259 | return (Status); |
260 | } |
261 | |
262 | if (Status != AE_AML_ILLEGAL_ADDRESS) |
263 | { |
264 | return (Status); |
265 | } |
266 | |
267 | /* |
268 | * There has been a protection violation within the request. Fall |
269 | * back to byte granularity port I/O and ignore the failing bytes. |
270 | * This provides Windows compatibility. |
271 | */ |
272 | for (i = 0, *Value = 0; i < Width; i += 8) |
273 | { |
274 | /* Validate and read one byte */ |
275 | |
276 | if (AcpiHwValidateIoRequest (Address, 8) == AE_OK) |
277 | { |
278 | Status = AcpiOsReadPort (Address, &OneByte, 8); |
279 | if (ACPI_FAILURE (Status)) |
280 | { |
281 | return (Status); |
282 | } |
283 | |
284 | *Value |= (OneByte << i); |
285 | } |
286 | |
287 | Address++; |
288 | } |
289 | |
290 | return (AE_OK); |
291 | } |
292 | |
293 | |
294 | /****************************************************************************** |
295 | * |
296 | * FUNCTION: AcpiHwWritePort |
297 | * |
298 | * PARAMETERS: Address Address of I/O port/register to write |
299 | * Value Value to write |
300 | * Width Number of bits |
301 | * |
302 | * RETURN: Status |
303 | * |
304 | * DESCRIPTION: Write data to an I/O port or register. This is a front-end |
305 | * to AcpiOsWritePort that performs validation on both the port |
306 | * address and the length. |
307 | * |
308 | *****************************************************************************/ |
309 | |
310 | ACPI_STATUS |
311 | AcpiHwWritePort ( |
312 | ACPI_IO_ADDRESS Address, |
313 | UINT32 Value, |
314 | UINT32 Width) |
315 | { |
316 | ACPI_STATUS Status; |
317 | UINT32 i; |
318 | |
319 | |
320 | /* Truncate address to 16 bits if requested */ |
321 | |
322 | if (AcpiGbl_TruncateIoAddresses) |
323 | { |
324 | Address &= ACPI_UINT16_MAX; |
325 | } |
326 | |
327 | /* Validate the entire request and perform the I/O */ |
328 | |
329 | Status = AcpiHwValidateIoRequest (Address, Width); |
330 | if (ACPI_SUCCESS (Status)) |
331 | { |
332 | Status = AcpiOsWritePort (Address, Value, Width); |
333 | return (Status); |
334 | } |
335 | |
336 | if (Status != AE_AML_ILLEGAL_ADDRESS) |
337 | { |
338 | return (Status); |
339 | } |
340 | |
341 | /* |
342 | * There has been a protection violation within the request. Fall |
343 | * back to byte granularity port I/O and ignore the failing bytes. |
344 | * This provides Windows compatibility. |
345 | */ |
346 | for (i = 0; i < Width; i += 8) |
347 | { |
348 | /* Validate and write one byte */ |
349 | |
350 | if (AcpiHwValidateIoRequest (Address, 8) == AE_OK) |
351 | { |
352 | Status = AcpiOsWritePort (Address, (Value >> i) & 0xFF, 8); |
353 | if (ACPI_FAILURE (Status)) |
354 | { |
355 | return (Status); |
356 | } |
357 | } |
358 | |
359 | Address++; |
360 | } |
361 | |
362 | return (AE_OK); |
363 | } |
364 | |