hwvalid.c revision 306536
140460Sken/******************************************************************************
257207Sgibbs *
340460Sken * Module Name: hwvalid - I/O request validation
440460Sken *
540460Sken *****************************************************************************/
640460Sken
740460Sken/*
840460Sken * Copyright (C) 2000 - 2016, Intel Corp.
940460Sken * All rights reserved.
1040460Sken *
1182229Sdd * Redistribution and use in source and binary forms, with or without
1240460Sken * modification, are permitted provided that the following conditions
1340460Sken * are met:
1440460Sken * 1. Redistributions of source code must retain the above copyright
1540460Sken *    notice, this list of conditions, and the following disclaimer,
1640460Sken *    without modification.
1740460Sken * 2. Redistributions in binary form must reproduce at minimum a disclaimer
1840460Sken *    substantially similar to the "NO WARRANTY" disclaimer below
1940460Sken *    ("Disclaimer") and any redistribution must be conditioned upon
2040460Sken *    including a substantially similar Disclaimer requirement for further
2140460Sken *    binary redistribution.
2240460Sken * 3. Neither the names of the above-listed copyright holders nor the names
2340460Sken *    of any contributors may be used to endorse or promote products derived
2450476Speter *    from this software without specific prior written permission.
2540460Sken *
26133338Ssimon * Alternatively, this software may be distributed under the terms of the
2772762Sru * GNU General Public License ("GPL") version 2 as published by the Free
2879538Sru * Software Foundation.
2940460Sken *
3040460Sken * NO WARRANTY
3140460Sken * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
3240460Sken * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
33159719Sbrueffer * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR
34159719Sbrueffer * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
35159719Sbrueffer * HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
36159719Sbrueffer * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
37159719Sbrueffer * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
38159719Sbrueffer * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
3940460Sken * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
40159719Sbrueffer * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
41159719Sbrueffer * POSSIBILITY OF SUCH DAMAGES.
42159719Sbrueffer */
43159719Sbrueffer
44159719Sbrueffer#include <contrib/dev/acpica/include/acpi.h>
45159719Sbrueffer#include <contrib/dev/acpica/include/accommon.h>
4684877Syokota
4784877Syokota#define _COMPONENT          ACPI_HARDWARE
4884877Syokota        ACPI_MODULE_NAME    ("hwvalid")
4984877Syokota
5040460Sken/* Local prototypes */
51159719Sbrueffer
52159719Sbruefferstatic ACPI_STATUS
5340460SkenAcpiHwValidateIoRequest (
5440460Sken    ACPI_IO_ADDRESS         Address,
5540460Sken    UINT32                  BitWidth);
5640460Sken
5740460Sken
5840460Sken/*
5940460Sken * Protected I/O ports. Some ports are always illegal, and some are
6040460Sken * conditionally illegal. This table must remain ordered by port address.
6157207Sgibbs *
6240460Sken * The table is used to implement the Microsoft port access rules that
6340460Sken * first appeared in Windows XP. Some ports are always illegal, and some
6440460Sken * ports are only illegal if the BIOS calls _OSI with a WinXP string or
6540460Sken * later (meaning that the BIOS itelf is post-XP.)
6640460Sken *
6740460Sken * This provides ACPICA with the desired port protections and
6840460Sken * Microsoft compatibility.
6972126Sru *
7040460Sken * Description of port entries:
71233992Sjoel *  DMA:   DMA controller
72233992Sjoel *  PIC0:  Programmable Interrupt Controller (8259A)
73233992Sjoel *  PIT1:  System Timer 1
74233992Sjoel *  PIT2:  System Timer 2 failsafe
75233992Sjoel *  RTC:   Real-time clock
76233992Sjoel *  CMOS:  Extended CMOS
77233992Sjoel *  DMA1:  DMA 1 page registers
78233992Sjoel *  DMA1L: DMA 1 Ch 0 low page
79233992Sjoel *  DMA2:  DMA 2 page registers
80233992Sjoel *  DMA2L: DMA 2 low page refresh
81233992Sjoel *  ARBC:  Arbitration control
82233992Sjoel *  SETUP: Reserved system board setup
83233992Sjoel *  POS:   POS channel select
84233992Sjoel *  PIC1:  Cascaded PIC
85233992Sjoel *  IDMA:  ISA DMA
86233992Sjoel *  ELCR:  PIC edge/level registers
8779727Sschweikh *  PCI:   PCI configuration space
8840460Sken */
8940460Skenstatic const ACPI_PORT_INFO     AcpiProtectedPorts[] =
9040460Sken{
9140460Sken    {"DMA",     0x0000, 0x000F, ACPI_OSI_WIN_XP},
9240460Sken    {"PIC0",    0x0020, 0x0021, ACPI_ALWAYS_ILLEGAL},
9340460Sken    {"PIT1",    0x0040, 0x0043, ACPI_OSI_WIN_XP},
9440460Sken    {"PIT2",    0x0048, 0x004B, ACPI_OSI_WIN_XP},
9540460Sken    {"RTC",     0x0070, 0x0071, ACPI_OSI_WIN_XP},
9640460Sken    {"CMOS",    0x0074, 0x0076, ACPI_OSI_WIN_XP},
9740460Sken    {"DMA1",    0x0081, 0x0083, ACPI_OSI_WIN_XP},
9840460Sken    {"DMA1L",   0x0087, 0x0087, ACPI_OSI_WIN_XP},
9940460Sken    {"DMA2",    0x0089, 0x008B, ACPI_OSI_WIN_XP},
10040460Sken    {"DMA2L",   0x008F, 0x008F, ACPI_OSI_WIN_XP},
10140460Sken    {"ARBC",    0x0090, 0x0091, ACPI_OSI_WIN_XP},
10240460Sken    {"SETUP",   0x0093, 0x0094, ACPI_OSI_WIN_XP},
10340460Sken    {"POS",     0x0096, 0x0097, ACPI_OSI_WIN_XP},
10479727Sschweikh    {"PIC1",    0x00A0, 0x00A1, ACPI_ALWAYS_ILLEGAL},
10572126Sru    {"IDMA",    0x00C0, 0x00DF, ACPI_OSI_WIN_XP},
10640460Sken    {"ELCR",    0x04D0, 0x04D1, ACPI_ALWAYS_ILLEGAL},
107233992Sjoel    {"PCI",     0x0CF8, 0x0CFF, ACPI_OSI_WIN_XP}
108233992Sjoel};
109233992Sjoel
110233992Sjoel#define ACPI_PORT_INFO_ENTRIES  ACPI_ARRAY_LENGTH (AcpiProtectedPorts)
111233992Sjoel
112233992Sjoel
113233992Sjoel/******************************************************************************
114233992Sjoel *
115233992Sjoel * FUNCTION:    AcpiHwValidateIoRequest
116233992Sjoel *
117233992Sjoel * PARAMETERS:  Address             Address of I/O port/register
11879727Sschweikh *              BitWidth            Number of bits (8,16,32)
11979727Sschweikh *
12072126Sru * RETURN:      Status
12140460Sken *
122233992Sjoel * DESCRIPTION: Validates an I/O request (address/length). Certain ports are
123233992Sjoel *              always illegal and some ports are only illegal depending on
124233992Sjoel *              the requests the BIOS AML code makes to the predefined
125233992Sjoel *              _OSI method.
126233992Sjoel *
127233992Sjoel ******************************************************************************/
128233992Sjoel
129233992Sjoelstatic ACPI_STATUS
13079727SschweikhAcpiHwValidateIoRequest (
13179727Sschweikh    ACPI_IO_ADDRESS         Address,
13240460Sken    UINT32                  BitWidth)
13384877Syokota{
13484877Syokota    UINT32                  i;
13584877Syokota    UINT32                  ByteWidth;
13684877Syokota    ACPI_IO_ADDRESS         LastAddress;
13784877Syokota    const ACPI_PORT_INFO    *PortInfo;
13884877Syokota
13984877Syokota
14084877Syokota    ACPI_FUNCTION_TRACE (HwValidateIoRequest);
14184877Syokota
14279727Sschweikh
14340460Sken    /* Supported widths are 8/16/32 */
14440460Sken
14540460Sken    if ((BitWidth != 8) &&
14640460Sken        (BitWidth != 16) &&
14782327Sdd        (BitWidth != 32))
14840460Sken    {
14940460Sken        ACPI_ERROR ((AE_INFO,
15040460Sken            "Bad BitWidth parameter: %8.8X", BitWidth));
15140460Sken        return (AE_BAD_PARAMETER);
15240460Sken    }
153133335Ssimon
154133335Ssimon    PortInfo = AcpiProtectedPorts;
155133335Ssimon    ByteWidth = ACPI_DIV_8 (BitWidth);
156133335Ssimon    LastAddress = Address + ByteWidth - 1;
157133335Ssimon
158133335Ssimon    ACPI_DEBUG_PRINT ((ACPI_DB_IO, "Address %8.8X%8.8X LastAddress %8.8X%8.8X Length %X",
159133335Ssimon        ACPI_FORMAT_UINT64 (Address), ACPI_FORMAT_UINT64 (LastAddress),
160133335Ssimon        ByteWidth));
161133335Ssimon
162133335Ssimon    /* Maximum 16-bit address in I/O space */
163133335Ssimon
164133335Ssimon    if (LastAddress > ACPI_UINT16_MAX)
165133335Ssimon    {
166133335Ssimon        ACPI_ERROR ((AE_INFO,
167133335Ssimon            "Illegal I/O port address/length above 64K: %8.8X%8.8X/0x%X",
168133335Ssimon            ACPI_FORMAT_UINT64 (Address), ByteWidth));
169133335Ssimon        return_ACPI_STATUS (AE_LIMIT);
170133335Ssimon    }
171133335Ssimon
172133335Ssimon    /* Exit if requested address is not within the protected port table */
173133335Ssimon
174133335Ssimon    if (Address > AcpiProtectedPorts[ACPI_PORT_INFO_ENTRIES - 1].End)
175133335Ssimon    {
176133335Ssimon        return_ACPI_STATUS (AE_OK);
177133335Ssimon    }
178133335Ssimon
179133335Ssimon    /* Check request against the list of protected I/O ports */
180133335Ssimon
181133335Ssimon    for (i = 0; i < ACPI_PORT_INFO_ENTRIES; i++, PortInfo++)
182133335Ssimon    {
183133335Ssimon        /*
184133335Ssimon         * Check if the requested address range will write to a reserved
185133335Ssimon         * port. Four cases to consider:
186133335Ssimon         *
187133335Ssimon         * 1) Address range is contained completely in the port address range
188133335Ssimon         * 2) Address range overlaps port range at the port range start
189133335Ssimon         * 3) Address range overlaps port range at the port range end
190133335Ssimon         * 4) Address range completely encompasses the port range
191133335Ssimon         */
192133335Ssimon        if ((Address <= PortInfo->End) && (LastAddress >= PortInfo->Start))
193133335Ssimon        {
194133335Ssimon            /* Port illegality may depend on the _OSI calls made by the BIOS */
195133335Ssimon
196133335Ssimon            if (AcpiGbl_OsiData >= PortInfo->OsiDependency)
197133335Ssimon            {
198133335Ssimon                ACPI_DEBUG_PRINT ((ACPI_DB_IO,
199133335Ssimon                    "Denied AML access to port 0x%8.8X%8.8X/%X (%s 0x%.4X-0x%.4X)",
200133335Ssimon                    ACPI_FORMAT_UINT64 (Address), ByteWidth, PortInfo->Name,
201133335Ssimon                    PortInfo->Start, PortInfo->End));
202133335Ssimon
203133335Ssimon                return_ACPI_STATUS (AE_AML_ILLEGAL_ADDRESS);
204133335Ssimon            }
205133335Ssimon        }
206133335Ssimon
207133335Ssimon        /* Finished if address range ends before the end of this port */
208133335Ssimon
209133335Ssimon        if (LastAddress <= PortInfo->End)
210133335Ssimon        {
211133335Ssimon            break;
212133335Ssimon        }
213133335Ssimon    }
21440460Sken
21540460Sken    return_ACPI_STATUS (AE_OK);
21640460Sken}
21740460Sken
21840460Sken
21940460Sken/******************************************************************************
22040460Sken *
22140460Sken * FUNCTION:    AcpiHwReadPort
22240460Sken *
223140561Sru * PARAMETERS:  Address             Address of I/O port/register to read
224140561Sru *              Value               Where value is placed
225140561Sru *              Width               Number of bits
226140561Sru *
227140561Sru * RETURN:      Status and value read from port
22840460Sken *
22969027Sru * DESCRIPTION: Read data from an I/O port or register. This is a front-end
23040460Sken *              to AcpiOsReadPort that performs validation on both the port
23168962Sru *              address and the length.
23240460Sken *
23340460Sken *****************************************************************************/
23440460Sken
23540460SkenACPI_STATUS
23640460SkenAcpiHwReadPort (
23740460Sken    ACPI_IO_ADDRESS         Address,
23840460Sken    UINT32                  *Value,
23968716Sru    UINT32                  Width)
24068716Sru{
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
310ACPI_STATUS
311AcpiHwWritePort (
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