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
54static ACPI_STATUS
55AcpiHwValidateIoRequest (
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 */
91static 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
131static ACPI_STATUS
132AcpiHwValidateIoRequest (
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
237ACPI_STATUS
238AcpiHwReadPort (
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
312ACPI_STATUS
313AcpiHwWritePort (
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