Home | History | Annotate | Download | only in CpuIo2Smm
      1 /** @file
      2   Produces the SMM CPU I/O Protocol.
      3 
      4 Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.<BR>
      5 This program and the accompanying materials
      6 are licensed and made available under the terms and conditions of the BSD License
      7 which accompanies this distribution.  The full text of the license may be found at
      8 http://opensource.org/licenses/bsd-license.php
      9 
     10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13 **/
     14 
     15 #include "CpuIo2Smm.h"
     16 
     17 //
     18 // Handle for the SMM CPU I/O Protocol
     19 //
     20 EFI_HANDLE  mHandle = NULL;
     21 
     22 //
     23 // SMM CPU I/O Protocol instance
     24 //
     25 EFI_SMM_CPU_IO2_PROTOCOL mSmmCpuIo2 = {
     26   {
     27     CpuMemoryServiceRead,
     28     CpuMemoryServiceWrite
     29   },
     30   {
     31     CpuIoServiceRead,
     32     CpuIoServiceWrite
     33   }
     34 };
     35 
     36 //
     37 // Lookup table for increment values based on transfer widths
     38 //
     39 UINT8 mStride[] = {
     40   1, // SMM_IO_UINT8
     41   2, // SMM_IO_UINT16
     42   4, // SMM_IO_UINT32
     43   8  // SMM_IO_UINT64
     44 };
     45 
     46 /**
     47   Check parameters to a SMM CPU I/O Protocol service request.
     48 
     49   @param[in]  MmioOperation  TRUE for an MMIO operation, FALSE for I/O Port operation.
     50   @param[in]  Width          Signifies the width of the I/O operations.
     51   @param[in]  Address        The base address of the I/O operations.  The caller is
     52                              responsible for aligning the Address if required.
     53   @param[in]  Count          The number of I/O operations to perform.
     54   @param[in]  Buffer         For read operations, the destination buffer to store
     55                              the results.  For write operations, the source buffer
     56                              from which to write data.
     57 
     58   @retval EFI_SUCCESS            The data was read from or written to the device.
     59   @retval EFI_UNSUPPORTED        The Address is not valid for this system.
     60   @retval EFI_INVALID_PARAMETER  Width or Count, or both, were invalid.
     61 
     62 **/
     63 EFI_STATUS
     64 CpuIoCheckParameter (
     65   IN BOOLEAN           MmioOperation,
     66   IN EFI_SMM_IO_WIDTH  Width,
     67   IN UINT64            Address,
     68   IN UINTN             Count,
     69   IN VOID              *Buffer
     70   )
     71 {
     72   UINT64  MaxCount;
     73   UINT64  Limit;
     74 
     75   //
     76   // Check to see if Buffer is NULL
     77   //
     78   if (Buffer == NULL) {
     79     return EFI_INVALID_PARAMETER;
     80   }
     81 
     82   //
     83   // Check to see if Width is in the valid range
     84   //
     85   if ((UINT32)Width > SMM_IO_UINT64) {
     86     return EFI_INVALID_PARAMETER;
     87   }
     88 
     89   //
     90   // Check to see if Width is in the valid range for I/O Port operations
     91   //
     92   if (!MmioOperation && (Width == SMM_IO_UINT64)) {
     93     return EFI_INVALID_PARAMETER;
     94   }
     95 
     96   //
     97   // Check to see if any address associated with this transfer exceeds the maximum
     98   // allowed address.  The maximum address implied by the parameters passed in is
     99   // Address + Size * Count.  If the following condition is met, then the transfer
    100   // is not supported.
    101   //
    102   //    Address + Size * Count > (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS) + 1
    103   //
    104   // Since MAX_ADDRESS can be the maximum integer value supported by the CPU and Count
    105   // can also be the maximum integer value supported by the CPU, this range
    106   // check must be adjusted to avoid all overflow conditions.
    107   //
    108   // The following form of the range check is equivalent but assumes that
    109   // MAX_ADDRESS and MAX_IO_PORT_ADDRESS are of the form (2^n - 1).
    110   //
    111   Limit = (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS);
    112   if (Count == 0) {
    113     if (Address > Limit) {
    114       return EFI_UNSUPPORTED;
    115     }
    116   } else {
    117     MaxCount = RShiftU64 (Limit, Width);
    118     if (MaxCount < (Count - 1)) {
    119       return EFI_UNSUPPORTED;
    120     }
    121     if (Address > LShiftU64 (MaxCount - Count + 1, Width)) {
    122       return EFI_UNSUPPORTED;
    123     }
    124   }
    125 
    126   //
    127   // Check to see if Address is aligned
    128   //
    129   if ((Address & (UINT64)(mStride[Width] - 1)) != 0) {
    130     return EFI_UNSUPPORTED;
    131   }
    132 
    133   return EFI_SUCCESS;
    134 }
    135 
    136 /**
    137   Reads memory-mapped registers.
    138 
    139   The I/O operations are carried out exactly as requested.  The caller is
    140   responsible for any alignment and I/O width issues that the bus, device,
    141   platform, or type of I/O might require.
    142 
    143   @param[in]  This     The EFI_SMM_CPU_IO2_PROTOCOL instance.
    144   @param[in]  Width    Signifies the width of the I/O operations.
    145   @param[in]  Address  The base address of the I/O operations.  The caller is
    146                        responsible for aligning the Address if required.
    147   @param[in]  Count    The number of I/O operations to perform.
    148   @param[out] Buffer   For read operations, the destination buffer to store
    149                        the results.  For write operations, the source buffer
    150                        from which to write data.
    151 
    152   @retval EFI_SUCCESS            The data was read from or written to the device.
    153   @retval EFI_UNSUPPORTED        The Address is not valid for this system.
    154   @retval EFI_INVALID_PARAMETER  Width or Count, or both, were invalid.
    155   @retval EFI_OUT_OF_RESOURCES   The request could not be completed due to a
    156                                  lack of resources
    157 
    158 **/
    159 EFI_STATUS
    160 EFIAPI
    161 CpuMemoryServiceRead (
    162   IN  CONST EFI_SMM_CPU_IO2_PROTOCOL  *This,
    163   IN  EFI_SMM_IO_WIDTH                Width,
    164   IN  UINT64                          Address,
    165   IN  UINTN                           Count,
    166   OUT VOID                            *Buffer
    167   )
    168 {
    169   EFI_STATUS  Status;
    170   UINT8       Stride;
    171   UINT8       *Uint8Buffer;
    172 
    173   Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);
    174   if (EFI_ERROR (Status)) {
    175     return Status;
    176   }
    177 
    178   //
    179   // Select loop based on the width of the transfer
    180   //
    181   Stride = mStride[Width];
    182   for (Uint8Buffer = Buffer; Count > 0; Address += Stride, Uint8Buffer += Stride, Count--) {
    183     if (Width == SMM_IO_UINT8) {
    184       *Uint8Buffer = MmioRead8 ((UINTN)Address);
    185     } else if (Width == SMM_IO_UINT16) {
    186       *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address);
    187     } else if (Width == SMM_IO_UINT32) {
    188       *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address);
    189     } else if (Width == SMM_IO_UINT64) {
    190       *((UINT64 *)Uint8Buffer) = MmioRead64 ((UINTN)Address);
    191     }
    192   }
    193   return EFI_SUCCESS;
    194 }
    195 
    196 /**
    197   Writes memory-mapped registers.
    198 
    199   The I/O operations are carried out exactly as requested.  The caller is
    200   responsible for any alignment and I/O width issues that the bus, device,
    201   platform, or type of I/O might require.
    202 
    203   @param[in]  This     The EFI_SMM_CPU_IO2_PROTOCOL instance.
    204   @param[in]  Width    Signifies the width of the I/O operations.
    205   @param[in]  Address  The base address of the I/O operations.  The caller is
    206                        responsible for aligning the Address if required.
    207   @param[in]  Count    The number of I/O operations to perform.
    208   @param[in]  Buffer   For read operations, the destination buffer to store
    209                        the results.  For write operations, the source buffer
    210                        from which to write data.
    211 
    212   @retval EFI_SUCCESS            The data was read from or written to the device.
    213   @retval EFI_UNSUPPORTED        The Address is not valid for this system.
    214   @retval EFI_INVALID_PARAMETER  Width or Count, or both, were invalid.
    215   @retval EFI_OUT_OF_RESOURCES   The request could not be completed due to a
    216                                  lack of resources
    217 
    218 **/
    219 EFI_STATUS
    220 EFIAPI
    221 CpuMemoryServiceWrite (
    222   IN CONST EFI_SMM_CPU_IO2_PROTOCOL  *This,
    223   IN EFI_SMM_IO_WIDTH                Width,
    224   IN UINT64                          Address,
    225   IN UINTN                           Count,
    226   IN VOID                            *Buffer
    227   )
    228 {
    229   EFI_STATUS  Status;
    230   UINT8       Stride;
    231   UINT8       *Uint8Buffer;
    232 
    233   Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer);
    234   if (EFI_ERROR (Status)) {
    235     return Status;
    236   }
    237 
    238   //
    239   // Select loop based on the width of the transfer
    240   //
    241   Stride = mStride[Width];
    242   for (Uint8Buffer = Buffer; Count > 0; Address += Stride, Uint8Buffer += Stride, Count--) {
    243     if (Width == SMM_IO_UINT8) {
    244       MmioWrite8 ((UINTN)Address, *Uint8Buffer);
    245     } else if (Width == SMM_IO_UINT16) {
    246       MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));
    247     } else if (Width == SMM_IO_UINT32) {
    248       MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));
    249     } else if (Width == SMM_IO_UINT64) {
    250       MmioWrite64 ((UINTN)Address, *((UINT64 *)Uint8Buffer));
    251     }
    252   }
    253   return EFI_SUCCESS;
    254 }
    255 
    256 /**
    257   Reads I/O registers.
    258 
    259   The I/O operations are carried out exactly as requested.  The caller is
    260   responsible for any alignment and I/O width issues that the bus, device,
    261   platform, or type of I/O might require.
    262 
    263   @param[in]  This     The EFI_SMM_CPU_IO2_PROTOCOL instance.
    264   @param[in]  Width    Signifies the width of the I/O operations.
    265   @param[in]  Address  The base address of the I/O operations.  The caller is
    266                        responsible for aligning the Address if required.
    267   @param[in]  Count    The number of I/O operations to perform.
    268   @param[out] Buffer   For read operations, the destination buffer to store
    269                        the results.  For write operations, the source buffer
    270                        from which to write data.
    271 
    272   @retval EFI_SUCCESS            The data was read from or written to the device.
    273   @retval EFI_UNSUPPORTED        The Address is not valid for this system.
    274   @retval EFI_INVALID_PARAMETER  Width or Count, or both, were invalid.
    275   @retval EFI_OUT_OF_RESOURCES   The request could not be completed due to a
    276                                  lack of resources
    277 
    278 **/
    279 EFI_STATUS
    280 EFIAPI
    281 CpuIoServiceRead (
    282   IN  CONST EFI_SMM_CPU_IO2_PROTOCOL  *This,
    283   IN  EFI_SMM_IO_WIDTH                Width,
    284   IN  UINT64                          Address,
    285   IN  UINTN                           Count,
    286   OUT VOID                            *Buffer
    287   )
    288 {
    289   EFI_STATUS  Status;
    290   UINT8       Stride;
    291   UINT8       *Uint8Buffer;
    292 
    293   Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);
    294   if (EFI_ERROR (Status)) {
    295     return Status;
    296   }
    297 
    298   //
    299   // Select loop based on the width of the transfer
    300   //
    301   Stride = mStride[Width];
    302   for (Uint8Buffer = Buffer; Count > 0; Address += Stride, Uint8Buffer += Stride, Count--) {
    303     if (Width == SMM_IO_UINT8) {
    304       *Uint8Buffer = IoRead8 ((UINTN)Address);
    305     } else if (Width == SMM_IO_UINT16) {
    306       *((UINT16 *)Uint8Buffer) = IoRead16 ((UINTN)Address);
    307     } else if (Width == SMM_IO_UINT32) {
    308       *((UINT32 *)Uint8Buffer) = IoRead32 ((UINTN)Address);
    309     }
    310   }
    311 
    312   return EFI_SUCCESS;
    313 }
    314 
    315 /**
    316   Write I/O registers.
    317 
    318   The I/O operations are carried out exactly as requested.  The caller is
    319   responsible for any alignment and I/O width issues that the bus, device,
    320   platform, or type of I/O might require.
    321 
    322   @param[in]  This     The EFI_SMM_CPU_IO2_PROTOCOL instance.
    323   @param[in]  Width    Signifies the width of the I/O operations.
    324   @param[in]  Address  The base address of the I/O operations.  The caller is
    325                        responsible for aligning the Address if required.
    326   @param[in]  Count    The number of I/O operations to perform.
    327   @param[in]  Buffer   For read operations, the destination buffer to store
    328                        the results.  For write operations, the source buffer
    329                        from which to write data.
    330 
    331   @retval EFI_SUCCESS            The data was read from or written to the device.
    332   @retval EFI_UNSUPPORTED        The Address is not valid for this system.
    333   @retval EFI_INVALID_PARAMETER  Width or Count, or both, were invalid.
    334   @retval EFI_OUT_OF_RESOURCES   The request could not be completed due to a
    335                                  lack of resources
    336 
    337 **/
    338 EFI_STATUS
    339 EFIAPI
    340 CpuIoServiceWrite (
    341   IN CONST EFI_SMM_CPU_IO2_PROTOCOL  *This,
    342   IN EFI_SMM_IO_WIDTH                Width,
    343   IN UINT64                          Address,
    344   IN UINTN                           Count,
    345   IN VOID                            *Buffer
    346   )
    347 {
    348   EFI_STATUS  Status;
    349   UINT8       Stride;
    350   UINT8       *Uint8Buffer;
    351 
    352   //
    353   // Make sure the parameters are valid
    354   //
    355   Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer);
    356   if (EFI_ERROR (Status)) {
    357     return Status;
    358   }
    359 
    360   //
    361   // Select loop based on the width of the transfer
    362   //
    363   Stride = mStride[Width];
    364   for (Uint8Buffer = (UINT8 *)Buffer; Count > 0; Address += Stride, Uint8Buffer += Stride, Count--) {
    365     if (Width == SMM_IO_UINT8) {
    366       IoWrite8 ((UINTN)Address, *Uint8Buffer);
    367     } else if (Width == SMM_IO_UINT16) {
    368       IoWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer));
    369     } else if (Width == SMM_IO_UINT32) {
    370       IoWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer));
    371     }
    372   }
    373 
    374   return EFI_SUCCESS;
    375 }
    376 
    377 /**
    378   The module Entry Point SmmCpuIoProtocol driver
    379 
    380   @param[in] ImageHandle  The firmware allocated handle for the EFI image.
    381   @param[in] SystemTable  A pointer to the EFI System Table.
    382 
    383   @retval EFI_SUCCESS  The entry point is executed successfully.
    384   @retval Other        Some error occurs when executing this entry point.
    385 
    386 **/
    387 EFI_STATUS
    388 EFIAPI
    389 SmmCpuIo2Initialize (
    390   IN EFI_HANDLE        ImageHandle,
    391   IN EFI_SYSTEM_TABLE  *SystemTable
    392   )
    393 {
    394   EFI_STATUS  Status;
    395 
    396   //
    397   // Copy the SMM CPU I/O Protocol instance into the System Management System Table
    398   //
    399   CopyMem (&gSmst->SmmIo, &mSmmCpuIo2, sizeof (mSmmCpuIo2));
    400 
    401   //
    402   // Install the SMM CPU I/O Protocol into the SMM protocol database
    403   //
    404   Status = gSmst->SmmInstallProtocolInterface (
    405                     &mHandle,
    406                     &gEfiSmmCpuIo2ProtocolGuid,
    407                     EFI_NATIVE_INTERFACE,
    408                     &mSmmCpuIo2
    409                     );
    410   ASSERT_EFI_ERROR (Status);
    411 
    412   return Status;
    413 }
    414