1 /** @file 2 QNC Smm Library Services that implements SMM Region access, S/W SMI generation and detection. 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 17 #include <Base.h> 18 #include <IntelQNCRegs.h> 19 #include <Library/DebugLib.h> 20 #include <Library/PcdLib.h> 21 #include <Library/IoLib.h> 22 #include <Uefi/UefiBaseType.h> 23 #include <Library/QNCAccessLib.h> 24 25 #define BOOT_SERVICE_SOFTWARE_SMI_DATA 0 26 #define RUNTIME_SOFTWARE_SMI_DATA 1 27 28 /** 29 Triggers a run time or boot time SMI. 30 31 This function triggers a software SMM interrupt and set the APMC status with an 8-bit Data. 32 33 @param Data The value to set the APMC status. 34 35 **/ 36 VOID 37 InternalTriggerSmi ( 38 IN UINT8 Data 39 ) 40 { 41 UINT16 PM1BLK_Base; 42 UINT16 GPE0BLK_Base; 43 UINT32 NewValue; 44 45 // 46 // Get PM1BLK_Base & GPE0BLK_Base 47 // 48 PM1BLK_Base = PcdGet16 (PcdPm1blkIoBaseAddress); 49 GPE0BLK_Base = (UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF); 50 51 52 // 53 // Enable APM SMI 54 // 55 IoOr32 ((GPE0BLK_Base + R_QNC_GPE0BLK_SMIE), B_QNC_GPE0BLK_SMIE_APM); 56 57 // 58 // Enable SMI globally 59 // 60 NewValue = QNCPortRead (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QNC_MSG_FSBIC_REG_HMISC); 61 NewValue |= SMI_EN; 62 QNCPortWrite (QUARK_NC_HOST_BRIDGE_SB_PORT_ID, QNC_MSG_FSBIC_REG_HMISC, NewValue); 63 64 // 65 // Set APM_STS 66 // 67 IoWrite8 (PcdGet16 (PcdSmmDataPort), Data); 68 69 // 70 // Generate the APM SMI 71 // 72 IoWrite8 (PcdGet16 (PcdSmmActivationPort), PcdGet8 (PcdSmmActivationData)); 73 74 // 75 // Clear the APM SMI Status Bit 76 // 77 IoWrite32 ((GPE0BLK_Base + R_QNC_GPE0BLK_SMIS), B_QNC_GPE0BLK_SMIS_APM); 78 79 // 80 // Set the EOS Bit 81 // 82 IoOr32 ((GPE0BLK_Base + R_QNC_GPE0BLK_SMIS), B_QNC_GPE0BLK_SMIS_EOS); 83 } 84 85 86 /** 87 Triggers an SMI at boot time. 88 89 This function triggers a software SMM interrupt at boot time. 90 91 **/ 92 VOID 93 EFIAPI 94 TriggerBootServiceSoftwareSmi ( 95 VOID 96 ) 97 { 98 InternalTriggerSmi (BOOT_SERVICE_SOFTWARE_SMI_DATA); 99 } 100 101 102 /** 103 Triggers an SMI at run time. 104 105 This function triggers a software SMM interrupt at run time. 106 107 **/ 108 VOID 109 EFIAPI 110 TriggerRuntimeSoftwareSmi ( 111 VOID 112 ) 113 { 114 InternalTriggerSmi (RUNTIME_SOFTWARE_SMI_DATA); 115 } 116 117 118 /** 119 Gets the software SMI data. 120 121 This function tests if a software SMM interrupt happens. If a software SMI happens, 122 it retrieves the SMM data and returns it as a non-negative value; otherwise a negative 123 value is returned. 124 125 @return Data The data retrieved from SMM data port in case of a software SMI; 126 otherwise a negative value. 127 128 **/ 129 INTN 130 InternalGetSwSmiData ( 131 VOID 132 ) 133 { 134 UINT8 SmiStatus; 135 UINT8 Data; 136 137 SmiStatus = IoRead8 ((UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF) + R_QNC_GPE0BLK_SMIS); 138 if (((SmiStatus & B_QNC_GPE0BLK_SMIS_APM) != 0) && 139 (IoRead8 (PcdGet16 (PcdSmmActivationPort)) == PcdGet8 (PcdSmmActivationData))) { 140 Data = IoRead8 (PcdGet16 (PcdSmmDataPort)); 141 return (INTN)(UINTN)Data; 142 } 143 144 return -1; 145 } 146 147 148 /** 149 Test if a boot time software SMI happened. 150 151 This function tests if a software SMM interrupt happened. If a software SMM interrupt happened and 152 it was triggered at boot time, it returns TRUE. Otherwise, it returns FALSE. 153 154 @retval TRUE A software SMI triggered at boot time happened. 155 @retval FLASE No software SMI happened or the software SMI was triggered at run time. 156 157 **/ 158 BOOLEAN 159 EFIAPI 160 IsBootServiceSoftwareSmi ( 161 VOID 162 ) 163 { 164 return (BOOLEAN) (InternalGetSwSmiData () == BOOT_SERVICE_SOFTWARE_SMI_DATA); 165 } 166 167 168 /** 169 Test if a run time software SMI happened. 170 171 This function tests if a software SMM interrupt happened. If a software SMM interrupt happened and 172 it was triggered at run time, it returns TRUE. Otherwise, it returns FALSE. 173 174 @retval TRUE A software SMI triggered at run time happened. 175 @retval FLASE No software SMI happened or the software SMI was triggered at boot time. 176 177 **/ 178 BOOLEAN 179 EFIAPI 180 IsRuntimeSoftwareSmi ( 181 VOID 182 ) 183 { 184 return (BOOLEAN) (InternalGetSwSmiData () == RUNTIME_SOFTWARE_SMI_DATA); 185 } 186 187 188 189 /** 190 191 Clear APM SMI Status Bit; Set the EOS bit. 192 193 **/ 194 VOID 195 EFIAPI 196 ClearSmi ( 197 VOID 198 ) 199 { 200 201 UINT16 GPE0BLK_Base; 202 203 // 204 // Get GpeBase 205 // 206 GPE0BLK_Base = (UINT16)(LpcPciCfg32 (R_QNC_LPC_GPE0BLK) & 0xFFFF); 207 208 // 209 // Clear the APM SMI Status Bit 210 // 211 IoOr16 (GPE0BLK_Base + R_QNC_GPE0BLK_SMIS, B_QNC_GPE0BLK_SMIS_APM); 212 213 // 214 // Set the EOS Bit 215 // 216 IoOr32 (GPE0BLK_Base + R_QNC_GPE0BLK_SMIS, B_QNC_GPE0BLK_SMIS_EOS); 217 } 218 219 /** 220 This routine is the chipset code that accepts a request to "open" a region of SMRAM. 221 The region could be legacy ABSEG, HSEG, or TSEG near top of physical memory. 222 The use of "open" means that the memory is visible from all boot-service 223 and SMM agents. 224 225 @retval FALSE Cannot open a locked SMRAM region 226 @retval TRUE Success to open SMRAM region. 227 **/ 228 BOOLEAN 229 EFIAPI 230 QNCOpenSmramRegion ( 231 VOID 232 ) 233 { 234 UINT32 Smram; 235 236 // Read the SMRAM register 237 Smram = QncHsmmcRead (); 238 239 // 240 // Is the platform locked? 241 // 242 if (Smram & SMM_LOCKED) { 243 // Cannot Open a locked region 244 DEBUG ((EFI_D_WARN, "Cannot open a locked SMRAM region\n")); 245 return FALSE; 246 } 247 248 // 249 // Open all SMRAM regions for Host access only 250 // 251 Smram |= (SMM_WRITE_OPEN | SMM_READ_OPEN); // Open for Host. 252 Smram &= ~(NON_HOST_SMM_WR_OPEN | NON_HOST_SMM_RD_OPEN); // Not for others. 253 254 // 255 // Write the SMRAM register 256 // 257 QncHsmmcWrite (Smram); 258 259 return TRUE; 260 } 261 262 /** 263 This routine is the chipset code that accepts a request to "close" a region of SMRAM. 264 The region could be legacy AB or TSEG near top of physical memory. 265 The use of "close" means that the memory is only visible from SMM agents, 266 not from BS or RT code. 267 268 @retval FALSE Cannot open a locked SMRAM region 269 @retval TRUE Success to open SMRAM region. 270 **/ 271 BOOLEAN 272 EFIAPI 273 QNCCloseSmramRegion ( 274 VOID 275 ) 276 { 277 UINT32 Smram; 278 279 // Read the SMRAM register. 280 Smram = QncHsmmcRead (); 281 282 // 283 // Is the platform locked? 284 // 285 if(Smram & SMM_LOCKED) { 286 // Cannot Open a locked region 287 DEBUG ((EFI_D_WARN, "Cannot close a locked SMRAM region\n")); 288 return FALSE; 289 } 290 291 Smram &= (~(SMM_WRITE_OPEN | SMM_READ_OPEN | NON_HOST_SMM_WR_OPEN | NON_HOST_SMM_RD_OPEN)); 292 293 QncHsmmcWrite (Smram); 294 295 return TRUE; 296 } 297 298 /** 299 This routine is the chipset code that accepts a request to "lock" SMRAM. 300 The region could be legacy AB or TSEG near top of physical memory. 301 The use of "lock" means that the memory can no longer be opened 302 to BS state. 303 **/ 304 VOID 305 EFIAPI 306 QNCLockSmramRegion ( 307 VOID 308 ) 309 { 310 UINT32 Smram; 311 312 // Read the SMRAM register. 313 Smram = QncHsmmcRead (); 314 if(Smram & SMM_LOCKED) { 315 DEBUG ((EFI_D_WARN, "SMRAM region already locked!\n")); 316 } 317 Smram |= SMM_LOCKED; 318 319 QncHsmmcWrite (Smram); 320 321 return; 322 } 323