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