Home | History | Annotate | Download | only in SmmControlDxe
      1 /** @file
      2 This module produces the SMM COntrol2 Protocol for QNC
      3 
      4 Copyright (c) 2013-2015 Intel Corporation.
      5 
      6 This program and the accompanying materials
      7 are licensed and made available under the terms and conditions of the BSD License
      8 which accompanies this distribution.  The full text of the license may be found at
      9 http://opensource.org/licenses/bsd-license.php
     10 
     11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     13 
     14 **/
     15 
     16 #include <PiDxe.h>
     17 #include <Protocol/SmmControl2.h>
     18 #include <IndustryStandard/Pci.h>
     19 #include <Library/DebugLib.h>
     20 #include <Library/UefiBootServicesTableLib.h>
     21 #include <Library/UefiRuntimeServicesTableLib.h>
     22 #include <Library/PcdLib.h>
     23 #include <Library/IoLib.h>
     24 #include <Library/PciLib.h>
     25 #include <IntelQNCDxe.h>
     26 #include <Library/QNCAccessLib.h>
     27 #include <Uefi/UefiBaseType.h>
     28 
     29 #define EFI_INTERNAL_POINTER  0x00000004
     30 
     31 extern EFI_GUID gEfiEventVirtualAddressChangeGuid;
     32 
     33 /**
     34   Generates an SMI using the parameters passed in.
     35 
     36   @param  This                A pointer to an instance of
     37                               EFI_SMM_CONTROL2_PROTOCOL
     38   @param  ArgumentBuffer      The argument buffer
     39   @param  ArgumentBufferSize  The size of the argument buffer
     40   @param  Periodic            TRUE to indicate a periodical SMI
     41   @param  ActivationInterval  Interval of the periodical SMI
     42 
     43   @retval EFI_INVALID_PARAMETER Periodic is TRUE or ArgumentBufferSize > 1
     44   @return Return value from SmmTrigger().
     45 
     46 **/
     47 EFI_STATUS
     48 EFIAPI
     49 Activate (
     50   IN CONST EFI_SMM_CONTROL2_PROTOCOL     *This,
     51   IN OUT  UINT8                          *CommandPort       OPTIONAL,
     52   IN OUT  UINT8                          *DataPort          OPTIONAL,
     53   IN      BOOLEAN                        Periodic           OPTIONAL,
     54   IN      EFI_SMM_PERIOD                 ActivationInterval OPTIONAL
     55                   );
     56 
     57 /**
     58   Clears an SMI.
     59 
     60   @param  This      Pointer to an instance of EFI_SMM_CONTROL2_PROTOCOL
     61   @param  Periodic  TRUE to indicate a periodical SMI
     62 
     63   @return Return value from SmmClear()
     64 
     65 **/
     66 EFI_STATUS
     67 EFIAPI
     68 Deactivate (
     69   IN CONST     EFI_SMM_CONTROL2_PROTOCOL  *This,
     70   IN      BOOLEAN                         Periodic OPTIONAL
     71   );
     72 
     73 ///
     74 /// Handle for the SMM Control2 Protocol
     75 ///
     76 EFI_HANDLE  mSmmControl2Handle = NULL;
     77 
     78 ///
     79 /// SMM COntrol2 Protocol instance
     80 ///
     81 EFI_SMM_CONTROL2_PROTOCOL mSmmControl2 = {
     82   Activate,
     83   Deactivate,
     84   0
     85 };
     86 
     87 VOID
     88 EFIAPI
     89 SmmControlVirtualddressChangeEvent (
     90   IN EFI_EVENT                  Event,
     91   IN VOID                       *Context
     92   )
     93 /*++
     94 
     95 Routine Description:
     96 
     97   Fixup internal data pointers so that the services can be called in virtual mode.
     98 
     99 Arguments:
    100 
    101   Event                         The event registered.
    102   Context                       Event context.
    103 
    104 Returns:
    105 
    106   None.
    107 
    108 --*/
    109 {
    110   gRT->ConvertPointer (EFI_INTERNAL_POINTER, (VOID *) &(mSmmControl2.Trigger));
    111   gRT->ConvertPointer (EFI_INTERNAL_POINTER, (VOID *) &(mSmmControl2.Clear));
    112 }
    113 
    114 /**
    115   Clear SMI related chipset status and re-enable SMI by setting the EOS bit.
    116 
    117   @retval EFI_SUCCESS The requested operation has been carried out successfully
    118   @retval EFI_DEVICE_ERROR  The EOS bit could not be set.
    119 
    120 **/
    121 EFI_STATUS
    122 SmmClear (
    123   VOID
    124   )
    125 {
    126   UINT16                       PM1BLK_Base;
    127   UINT16                       GPE0BLK_Base;
    128 
    129   //
    130   // Get PM1BLK_Base & GPE0BLK_Base
    131   //
    132   PM1BLK_Base  = PcdGet16 (PcdPm1blkIoBaseAddress);
    133   GPE0BLK_Base = PcdGet16 (PcdGpe0blkIoBaseAddress);
    134 
    135   //
    136   // Clear the Power Button Override Status Bit, it gates EOS from being set.
    137   // In QuarkNcSocId - Bit is read only. Handled by external SMC, do nothing.
    138   //
    139 
    140   //
    141   // Clear the APM SMI Status Bit
    142   //
    143   IoWrite32 ((GPE0BLK_Base + R_QNC_GPE0BLK_SMIS), B_QNC_GPE0BLK_SMIS_APM);
    144 
    145   //
    146   // Set the EOS Bit
    147   //
    148   IoOr32 ((GPE0BLK_Base + R_QNC_GPE0BLK_SMIS), B_QNC_GPE0BLK_SMIS_EOS);
    149 
    150   return EFI_SUCCESS;
    151 }
    152 
    153 /**
    154   Generates an SMI using the parameters passed in.
    155 
    156   @param  This                A pointer to an instance of
    157                               EFI_SMM_CONTROL_PROTOCOL
    158   @param  ArgumentBuffer      The argument buffer
    159   @param  ArgumentBufferSize  The size of the argument buffer
    160   @param  Periodic            TRUE to indicate a periodical SMI
    161   @param  ActivationInterval  Interval of the periodical SMI
    162 
    163   @retval EFI_INVALID_PARAMETER Periodic is TRUE or ArgumentBufferSize > 1
    164   @retval EFI_SUCCESS            SMI generated
    165 
    166 **/
    167 EFI_STATUS
    168 EFIAPI
    169 Activate (
    170   IN CONST EFI_SMM_CONTROL2_PROTOCOL     *This,
    171   IN OUT  UINT8                          *CommandPort       OPTIONAL,
    172   IN OUT  UINT8                          *DataPort          OPTIONAL,
    173   IN      BOOLEAN                        Periodic           OPTIONAL,
    174   IN      EFI_SMM_PERIOD                 ActivationInterval OPTIONAL
    175   )
    176 {
    177   UINT16        GPE0BLK_Base;
    178   UINT32        NewValue;
    179 
    180   //
    181   // Get GPE0BLK_Base
    182   //
    183   GPE0BLK_Base = PcdGet16 (PcdGpe0blkIoBaseAddress);
    184 
    185   if (Periodic) {
    186     return EFI_INVALID_PARAMETER;
    187   }
    188 
    189   //
    190   // Clear any pending the APM SMI
    191   //
    192   if (EFI_ERROR (SmmClear())) {
    193     return EFI_DEVICE_ERROR;
    194     }
    195 
    196   //
    197   // Enable the APMC SMI
    198   //
    199   IoOr32 (GPE0BLK_Base + R_QNC_GPE0BLK_SMIE, B_QNC_GPE0BLK_SMIE_APM);
    200 
    201   //
    202   // Enable SMI globally
    203   //
    204   NewValue = QNCPortRead (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QNC_MSG_FSBIC_REG_HMISC);
    205   NewValue |= SMI_EN;
    206   QNCPortWrite (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QNC_MSG_FSBIC_REG_HMISC, NewValue);
    207 
    208 
    209   //
    210   // Set APMC_STS
    211   //
    212   if (DataPort == NULL) {
    213     IoWrite8 (PcdGet16 (PcdSmmDataPort), 0xFF);
    214   } else {
    215     IoWrite8 (PcdGet16 (PcdSmmDataPort), *DataPort);
    216   }
    217 
    218   //
    219   // Generate the APMC SMI
    220   //
    221   if (CommandPort == NULL) {
    222     IoWrite8 (PcdGet16 (PcdSmmActivationPort), 0xFF);
    223   } else {
    224     IoWrite8 (PcdGet16 (PcdSmmActivationPort), *CommandPort);
    225   }
    226 
    227   return EFI_SUCCESS;
    228 }
    229 
    230 /**
    231   Clears an SMI.
    232 
    233   @param  This      Pointer to an instance of EFI_SMM_CONTROL_PROTOCOL
    234   @param  Periodic  TRUE to indicate a periodical SMI
    235 
    236   @return Return value from SmmClear()
    237 
    238 **/
    239 EFI_STATUS
    240 EFIAPI
    241 Deactivate (
    242   IN CONST EFI_SMM_CONTROL2_PROTOCOL     *This,
    243   IN      BOOLEAN                   Periodic
    244   )
    245 {
    246   if (Periodic) {
    247     return EFI_INVALID_PARAMETER;
    248   }
    249 
    250   return SmmClear();
    251 }
    252 
    253 /**
    254   This is the constructor for the SMM Control protocol.
    255 
    256   This function installs EFI_SMM_CONTROL2_PROTOCOL.
    257 
    258   @param  ImageHandle Handle for the image of this driver
    259   @param  SystemTable Pointer to the EFI System Table
    260 
    261   @retval EFI_UNSUPPORTED There's no Intel ICH on this platform
    262   @return The status returned from InstallProtocolInterface().
    263 
    264 --*/
    265 EFI_STATUS
    266 SmmControl2Init (
    267   IN EFI_HANDLE        ImageHandle,
    268   IN EFI_SYSTEM_TABLE  *SystemTable
    269   )
    270 {
    271   EFI_STATUS  Status;
    272   EFI_EVENT   Event;
    273   UINT16      PM1BLK_Base;
    274   UINT16      GPE0BLK_Base;
    275   BOOLEAN     SciEn;
    276   UINT32      NewValue;
    277 
    278   //
    279   // Get PM1BLK_Base & GPE0BLK_Base
    280   //
    281   PM1BLK_Base  = PcdGet16 (PcdPm1blkIoBaseAddress);
    282   GPE0BLK_Base = PcdGet16 (PcdGpe0blkIoBaseAddress);
    283 
    284   //
    285   // Install our protocol interfaces on the device's handle
    286   //
    287   Status = gBS->InstallMultipleProtocolInterfaces (
    288                   &mSmmControl2Handle,
    289                   &gEfiSmmControl2ProtocolGuid,  &mSmmControl2,
    290                   NULL
    291                   );
    292   ASSERT_EFI_ERROR (Status);
    293 
    294   //
    295   // Determine whether an ACPI OS is present (via the SCI_EN bit)
    296   //
    297   SciEn = (BOOLEAN)((IoRead16 (PM1BLK_Base + R_QNC_PM1BLK_PM1C) & B_QNC_PM1BLK_PM1C_SCIEN) != 0);
    298   if (!SciEn) {
    299     //
    300     // Clear any SMIs that double as SCIs (when SCI_EN==0)
    301     //
    302     IoWrite16 ((PM1BLK_Base + R_QNC_PM1BLK_PM1S), B_QNC_PM1BLK_PM1S_ALL);
    303     IoWrite16 ((PM1BLK_Base + R_QNC_PM1BLK_PM1E), 0x00000000);
    304     IoWrite32 ((PM1BLK_Base + R_QNC_PM1BLK_PM1C),  0x00000000);
    305     IoWrite32 ((GPE0BLK_Base + R_QNC_GPE0BLK_GPE0S), B_QNC_GPE0BLK_GPE0S_ALL);
    306     IoWrite32 ((GPE0BLK_Base + R_QNC_GPE0BLK_GPE0E), 0x00000000);
    307   }
    308 
    309   //
    310   // Clear and disable all SMIs that are unaffected by SCI_EN
    311   // Set EOS
    312   //
    313   IoWrite32 ((GPE0BLK_Base + R_QNC_GPE0BLK_SMIE), 0x00000000);
    314   IoWrite32 ((GPE0BLK_Base + R_QNC_GPE0BLK_SMIS), (B_QNC_GPE0BLK_SMIS_EOS + B_QNC_GPE0BLK_SMIS_ALL));
    315 
    316   //
    317   // Enable SMI globally
    318   //
    319   NewValue = QNCPortRead (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QNC_MSG_FSBIC_REG_HMISC);
    320   NewValue |= SMI_EN;
    321   QNCPortWrite (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QNC_MSG_FSBIC_REG_HMISC, NewValue);
    322 
    323   //
    324   // Make sure to write this register last -- EOS re-enables SMIs for the QNC
    325   //
    326   IoAndThenOr32 (
    327     GPE0BLK_Base + R_QNC_GPE0BLK_SMIE,
    328     (UINT32)(~B_QNC_GPE0BLK_SMIE_ALL),
    329     B_QNC_GPE0BLK_SMIE_APM
    330     );
    331 
    332   //
    333   // Make sure EOS bit cleared
    334   //
    335   DEBUG_CODE_BEGIN ();
    336   if (IoRead32 (GPE0BLK_Base + R_QNC_GPE0BLK_SMIS) & B_QNC_GPE0BLK_SMIS_EOS) {
    337     DEBUG ((
    338       EFI_D_ERROR,
    339       "******************************************************************************\n"
    340       "BIG ERROR: SmmControl constructor couldn't properly initialize the ACPI table.\n"
    341       "           SmmControl->Clear will probably hang.                              \n"
    342       "              NOTE: SCI_EN = %d                                               \n"
    343       "******************************************************************************\n",
    344       SciEn
    345       ));
    346 
    347     //
    348     // If we want the system to stop, then keep the ASSERT(FALSE).
    349     // Otherwise, comment it out.
    350     //
    351     ASSERT (FALSE);
    352   }
    353   DEBUG_CODE_END ();
    354 
    355   Status = gBS->CreateEventEx (
    356                 EVT_NOTIFY_SIGNAL,
    357                 TPL_NOTIFY,
    358                 SmmControlVirtualddressChangeEvent,
    359                 NULL,
    360                 &gEfiEventVirtualAddressChangeGuid,
    361                 &Event
    362                 );
    363   ASSERT_EFI_ERROR (Status);
    364 
    365   return Status;
    366 }
    367