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