xref: /freebsd/sys/contrib/dev/acpica/components/hardware/hwvalid.c (revision 8d20be1e22095c27faf8fe8b2f0d089739cc742e)
1 /******************************************************************************
2  *
3  * Module Name: hwvalid - I/O request validation
4  *
5  *****************************************************************************/
6 
7 /*
8  * Copyright (C) 2000 - 2013, 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 #define __HWVALID_C__
45 
46 #include <contrib/dev/acpica/include/acpi.h>
47 #include <contrib/dev/acpica/include/accommon.h>
48 
49 #define _COMPONENT          ACPI_HARDWARE
50         ACPI_MODULE_NAME    ("hwvalid")
51 
52 /* Local prototypes */
53 
54 static ACPI_STATUS
55 AcpiHwValidateIoRequest (
56     ACPI_IO_ADDRESS         Address,
57     UINT32                  BitWidth);
58 
59 
60 /*
61  * Protected I/O ports. Some ports are always illegal, and some are
62  * conditionally illegal. This table must remain ordered by port address.
63  *
64  * The table is used to implement the Microsoft port access rules that
65  * first appeared in Windows XP. Some ports are always illegal, and some
66  * ports are only illegal if the BIOS calls _OSI with a WinXP string or
67  * later (meaning that the BIOS itelf is post-XP.)
68  *
69  * This provides ACPICA with the desired port protections and
70  * Microsoft compatibility.
71  *
72  * Description of port entries:
73  *  DMA:   DMA controller
74  *  PIC0:  Programmable Interrupt Controller (8259A)
75  *  PIT1:  System Timer 1
76  *  PIT2:  System Timer 2 failsafe
77  *  RTC:   Real-time clock
78  *  CMOS:  Extended CMOS
79  *  DMA1:  DMA 1 page registers
80  *  DMA1L: DMA 1 Ch 0 low page
81  *  DMA2:  DMA 2 page registers
82  *  DMA2L: DMA 2 low page refresh
83  *  ARBC:  Arbitration control
84  *  SETUP: Reserved system board setup
85  *  POS:   POS channel select
86  *  PIC1:  Cascaded PIC
87  *  IDMA:  ISA DMA
88  *  ELCR:  PIC edge/level registers
89  *  PCI:   PCI configuration space
90  */
91 static const ACPI_PORT_INFO     AcpiProtectedPorts[] =
92 {
93     {"DMA",     0x0000, 0x000F, ACPI_OSI_WIN_XP},
94     {"PIC0",    0x0020, 0x0021, ACPI_ALWAYS_ILLEGAL},
95     {"PIT1",    0x0040, 0x0043, ACPI_OSI_WIN_XP},
96     {"PIT2",    0x0048, 0x004B, ACPI_OSI_WIN_XP},
97     {"RTC",     0x0070, 0x0071, ACPI_OSI_WIN_XP},
98     {"CMOS",    0x0074, 0x0076, ACPI_OSI_WIN_XP},
99     {"DMA1",    0x0081, 0x0083, ACPI_OSI_WIN_XP},
100     {"DMA1L",   0x0087, 0x0087, ACPI_OSI_WIN_XP},
101     {"DMA2",    0x0089, 0x008B, ACPI_OSI_WIN_XP},
102     {"DMA2L",   0x008F, 0x008F, ACPI_OSI_WIN_XP},
103     {"ARBC",    0x0090, 0x0091, ACPI_OSI_WIN_XP},
104     {"SETUP",   0x0093, 0x0094, ACPI_OSI_WIN_XP},
105     {"POS",     0x0096, 0x0097, ACPI_OSI_WIN_XP},
106     {"PIC1",    0x00A0, 0x00A1, ACPI_ALWAYS_ILLEGAL},
107     {"IDMA",    0x00C0, 0x00DF, ACPI_OSI_WIN_XP},
108     {"ELCR",    0x04D0, 0x04D1, ACPI_ALWAYS_ILLEGAL},
109     {"PCI",     0x0CF8, 0x0CFF, ACPI_OSI_WIN_XP}
110 };
111 
112 #define ACPI_PORT_INFO_ENTRIES  ACPI_ARRAY_LENGTH (AcpiProtectedPorts)
113 
114 
115 /******************************************************************************
116  *
117  * FUNCTION:    AcpiHwValidateIoRequest
118  *
119  * PARAMETERS:  Address             Address of I/O port/register
120  *              BitWidth            Number of bits (8,16,32)
121  *
122  * RETURN:      Status
123  *
124  * DESCRIPTION: Validates an I/O request (address/length). Certain ports are
125  *              always illegal and some ports are only illegal depending on
126  *              the requests the BIOS AML code makes to the predefined
127  *              _OSI method.
128  *
129  ******************************************************************************/
130 
131 static ACPI_STATUS
132 AcpiHwValidateIoRequest (
133     ACPI_IO_ADDRESS         Address,
134     UINT32                  BitWidth)
135 {
136     UINT32                  i;
137     UINT32                  ByteWidth;
138     ACPI_IO_ADDRESS         LastAddress;
139     const ACPI_PORT_INFO    *PortInfo;
140 
141 
142     ACPI_FUNCTION_TRACE (HwValidateIoRequest);
143 
144 
145     /* Supported widths are 8/16/32 */
146 
147     if ((BitWidth != 8) &&
148         (BitWidth != 16) &&
149         (BitWidth != 32))
150     {
151         ACPI_ERROR ((AE_INFO,
152             "Bad BitWidth parameter: %8.8X", BitWidth));
153         return (AE_BAD_PARAMETER);
154     }
155 
156     PortInfo = AcpiProtectedPorts;
157     ByteWidth = ACPI_DIV_8 (BitWidth);
158     LastAddress = Address + ByteWidth - 1;
159 
160     ACPI_DEBUG_PRINT ((ACPI_DB_IO, "Address %p LastAddress %p Length %X",
161         ACPI_CAST_PTR (void, Address), ACPI_CAST_PTR (void, LastAddress),
162         ByteWidth));
163 
164     /* Maximum 16-bit address in I/O space */
165 
166     if (LastAddress > ACPI_UINT16_MAX)
167     {
168         ACPI_ERROR ((AE_INFO,
169             "Illegal I/O port address/length above 64K: %p/0x%X",
170             ACPI_CAST_PTR (void, Address), ByteWidth));
171         return_ACPI_STATUS (AE_LIMIT);
172     }
173 
174     /* Exit if requested address is not within the protected port table */
175 
176     if (Address > AcpiProtectedPorts[ACPI_PORT_INFO_ENTRIES - 1].End)
177     {
178         return_ACPI_STATUS (AE_OK);
179     }
180 
181     /* Check request against the list of protected I/O ports */
182 
183     for (i = 0; i < ACPI_PORT_INFO_ENTRIES; i++, PortInfo++)
184     {
185         /*
186          * Check if the requested address range will write to a reserved
187          * port. Four cases to consider:
188          *
189          * 1) Address range is contained completely in the port address range
190          * 2) Address range overlaps port range at the port range start
191          * 3) Address range overlaps port range at the port range end
192          * 4) Address range completely encompasses the port range
193          */
194         if ((Address <= PortInfo->End) && (LastAddress >= PortInfo->Start))
195         {
196             /* Port illegality may depend on the _OSI calls made by the BIOS */
197 
198             if (AcpiGbl_OsiData >= PortInfo->OsiDependency)
199             {
200                 ACPI_DEBUG_PRINT ((ACPI_DB_IO,
201                     "Denied AML access to port 0x%p/%X (%s 0x%.4X-0x%.4X)",
202                     ACPI_CAST_PTR (void, Address), ByteWidth, PortInfo->Name,
203                     PortInfo->Start, PortInfo->End));
204 
205                 return_ACPI_STATUS (AE_AML_ILLEGAL_ADDRESS);
206             }
207         }
208 
209         /* Finished if address range ends before the end of this port */
210 
211         if (LastAddress <= PortInfo->End)
212         {
213             break;
214         }
215     }
216 
217     return_ACPI_STATUS (AE_OK);
218 }
219 
220 
221 /******************************************************************************
222  *
223  * FUNCTION:    AcpiHwReadPort
224  *
225  * PARAMETERS:  Address             Address of I/O port/register to read
226  *              Value               Where value is placed
227  *              Width               Number of bits
228  *
229  * RETURN:      Status and value read from port
230  *
231  * DESCRIPTION: Read data from an I/O port or register. This is a front-end
232  *              to AcpiOsReadPort that performs validation on both the port
233  *              address and the length.
234  *
235  *****************************************************************************/
236 
237 ACPI_STATUS
238 AcpiHwReadPort (
239     ACPI_IO_ADDRESS         Address,
240     UINT32                  *Value,
241     UINT32                  Width)
242 {
243     ACPI_STATUS             Status;
244     UINT32                  OneByte;
245     UINT32                  i;
246 
247 
248     /* Truncate address to 16 bits if requested */
249 
250     if (AcpiGbl_TruncateIoAddresses)
251     {
252         Address &= ACPI_UINT16_MAX;
253     }
254 
255     /* Validate the entire request and perform the I/O */
256 
257     Status = AcpiHwValidateIoRequest (Address, Width);
258     if (ACPI_SUCCESS (Status))
259     {
260         Status = AcpiOsReadPort (Address, Value, Width);
261         return (Status);
262     }
263 
264     if (Status != AE_AML_ILLEGAL_ADDRESS)
265     {
266         return (Status);
267     }
268 
269     /*
270      * There has been a protection violation within the request. Fall
271      * back to byte granularity port I/O and ignore the failing bytes.
272      * This provides Windows compatibility.
273      */
274     for (i = 0, *Value = 0; i < Width; i += 8)
275     {
276         /* Validate and read one byte */
277 
278         if (AcpiHwValidateIoRequest (Address, 8) == AE_OK)
279         {
280             Status = AcpiOsReadPort (Address, &OneByte, 8);
281             if (ACPI_FAILURE (Status))
282             {
283                 return (Status);
284             }
285 
286             *Value |= (OneByte << i);
287         }
288 
289         Address++;
290     }
291 
292     return (AE_OK);
293 }
294 
295 
296 /******************************************************************************
297  *
298  * FUNCTION:    AcpiHwWritePort
299  *
300  * PARAMETERS:  Address             Address of I/O port/register to write
301  *              Value               Value to write
302  *              Width               Number of bits
303  *
304  * RETURN:      Status
305  *
306  * DESCRIPTION: Write data to an I/O port or register. This is a front-end
307  *              to AcpiOsWritePort that performs validation on both the port
308  *              address and the length.
309  *
310  *****************************************************************************/
311 
312 ACPI_STATUS
313 AcpiHwWritePort (
314     ACPI_IO_ADDRESS         Address,
315     UINT32                  Value,
316     UINT32                  Width)
317 {
318     ACPI_STATUS             Status;
319     UINT32                  i;
320 
321 
322     /* Truncate address to 16 bits if requested */
323 
324     if (AcpiGbl_TruncateIoAddresses)
325     {
326         Address &= ACPI_UINT16_MAX;
327     }
328 
329     /* Validate the entire request and perform the I/O */
330 
331     Status = AcpiHwValidateIoRequest (Address, Width);
332     if (ACPI_SUCCESS (Status))
333     {
334         Status = AcpiOsWritePort (Address, Value, Width);
335         return (Status);
336     }
337 
338     if (Status != AE_AML_ILLEGAL_ADDRESS)
339     {
340         return (Status);
341     }
342 
343     /*
344      * There has been a protection violation within the request. Fall
345      * back to byte granularity port I/O and ignore the failing bytes.
346      * This provides Windows compatibility.
347      */
348     for (i = 0; i < Width; i += 8)
349     {
350         /* Validate and write one byte */
351 
352         if (AcpiHwValidateIoRequest (Address, 8) == AE_OK)
353         {
354             Status = AcpiOsWritePort (Address, (Value >> i) & 0xFF, 8);
355             if (ACPI_FAILURE (Status))
356             {
357                 return (Status);
358             }
359         }
360 
361         Address++;
362     }
363 
364     return (AE_OK);
365 }
366