1 /** @file 2 SMM STM support functions 3 4 Copyright (c) 2015 - 2016, 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 <PiSmm.h> 16 #include <Library/BaseLib.h> 17 #include <Library/BaseMemoryLib.h> 18 #include <Library/MemoryAllocationLib.h> 19 #include <Library/HobLib.h> 20 #include <Library/DebugLib.h> 21 #include <Library/UefiBootServicesTableLib.h> 22 #include <Library/SmmServicesTableLib.h> 23 #include <Library/TpmMeasurementLib.h> 24 #include <Register/Cpuid.h> 25 #include <Register/ArchitecturalMsr.h> 26 #include <Register/SmramSaveStateMap.h> 27 28 #include <Protocol/MpService.h> 29 30 #include "SmmStm.h" 31 32 #define TXT_EVTYPE_BASE 0x400 33 #define TXT_EVTYPE_STM_HASH (TXT_EVTYPE_BASE + 14) 34 35 #define RDWR_ACCS 3 36 #define FULL_ACCS 7 37 38 /** 39 The constructor function 40 41 @param[in] ImageHandle The firmware allocated handle for the EFI image. 42 @param[in] SystemTable A pointer to the EFI System Table. 43 44 @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS. 45 46 **/ 47 EFI_STATUS 48 EFIAPI 49 SmmCpuFeaturesLibConstructor ( 50 IN EFI_HANDLE ImageHandle, 51 IN EFI_SYSTEM_TABLE *SystemTable 52 ); 53 54 EFI_HANDLE mStmSmmCpuHandle = NULL; 55 56 BOOLEAN mLockLoadMonitor = FALSE; 57 58 // 59 // Template of STM_RSC_END structure for copying. 60 // 61 GLOBAL_REMOVE_IF_UNREFERENCED STM_RSC_END mRscEndNode = { 62 {END_OF_RESOURCES, sizeof (STM_RSC_END)}, 63 }; 64 65 GLOBAL_REMOVE_IF_UNREFERENCED UINT8 *mStmResourcesPtr = NULL; 66 GLOBAL_REMOVE_IF_UNREFERENCED UINTN mStmResourceTotalSize = 0x0; 67 GLOBAL_REMOVE_IF_UNREFERENCED UINTN mStmResourceSizeUsed = 0x0; 68 GLOBAL_REMOVE_IF_UNREFERENCED UINTN mStmResourceSizeAvailable = 0x0; 69 70 GLOBAL_REMOVE_IF_UNREFERENCED UINT32 mStmState = 0; 71 72 // 73 // System Configuration Table pointing to STM Configuration Table 74 // 75 GLOBAL_REMOVE_IF_UNREFERENCED 76 EFI_SM_MONITOR_INIT_PROTOCOL mSmMonitorInitProtocol = { 77 LoadMonitor, 78 AddPiResource, 79 DeletePiResource, 80 GetPiResource, 81 GetMonitorState, 82 }; 83 84 85 86 87 #define CPUID1_EDX_XD_SUPPORT 0x100000 88 89 // 90 // External global variables associated with SMI Handler Template 91 // 92 extern CONST TXT_PROCESSOR_SMM_DESCRIPTOR gcStmPsd; 93 extern UINT32 gStmSmbase; 94 extern volatile UINT32 gStmSmiStack; 95 extern UINT32 gStmSmiCr3; 96 extern volatile UINT8 gcStmSmiHandlerTemplate[]; 97 extern CONST UINT16 gcStmSmiHandlerSize; 98 extern UINT16 gcStmSmiHandlerOffset; 99 extern BOOLEAN gStmXdSupported; 100 101 // 102 // Variables used by SMI Handler 103 // 104 IA32_DESCRIPTOR gStmSmiHandlerIdtr; 105 106 // 107 // MP Services Protocol 108 // 109 EFI_MP_SERVICES_PROTOCOL *mSmmCpuFeaturesLibMpService = NULL; 110 111 // 112 // MSEG Base and Length in SMRAM 113 // 114 UINTN mMsegBase = 0; 115 UINTN mMsegSize = 0; 116 117 BOOLEAN mStmConfigurationTableInitialized = FALSE; 118 119 120 /** 121 The constructor function 122 123 @param[in] ImageHandle The firmware allocated handle for the EFI image. 124 @param[in] SystemTable A pointer to the EFI System Table. 125 126 @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS. 127 128 **/ 129 EFI_STATUS 130 EFIAPI 131 SmmCpuFeaturesLibStmConstructor ( 132 IN EFI_HANDLE ImageHandle, 133 IN EFI_SYSTEM_TABLE *SystemTable 134 ) 135 { 136 EFI_STATUS Status; 137 CPUID_VERSION_INFO_ECX RegEcx; 138 EFI_HOB_GUID_TYPE *GuidHob; 139 EFI_SMRAM_DESCRIPTOR *SmramDescriptor; 140 141 // 142 // Call the common constructor function 143 // 144 Status = SmmCpuFeaturesLibConstructor (ImageHandle, SystemTable); 145 ASSERT_EFI_ERROR (Status); 146 147 // 148 // Lookup the MP Services Protocol 149 // 150 Status = gBS->LocateProtocol ( 151 &gEfiMpServiceProtocolGuid, 152 NULL, 153 (VOID **)&mSmmCpuFeaturesLibMpService 154 ); 155 ASSERT_EFI_ERROR (Status); 156 157 // 158 // If CPU supports VMX, then determine SMRAM range for MSEG. 159 // 160 AsmCpuid (CPUID_VERSION_INFO, NULL, NULL, &RegEcx.Uint32, NULL); 161 if (RegEcx.Bits.VMX == 1) { 162 GuidHob = GetFirstGuidHob (&gMsegSmramGuid); 163 if (GuidHob != NULL) { 164 // 165 // Retrieve MSEG location from MSEG SRAM HOB 166 // 167 SmramDescriptor = (EFI_SMRAM_DESCRIPTOR *) GET_GUID_HOB_DATA (GuidHob); 168 if (SmramDescriptor->PhysicalSize > 0) { 169 mMsegBase = (UINTN)SmramDescriptor->CpuStart; 170 mMsegSize = (UINTN)SmramDescriptor->PhysicalSize; 171 } 172 } else if (PcdGet32 (PcdCpuMsegSize) > 0) { 173 // 174 // Allocate MSEG from SMRAM memory 175 // 176 mMsegBase = (UINTN)AllocatePages (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuMsegSize))); 177 if (mMsegBase > 0) { 178 mMsegSize = ALIGN_VALUE (PcdGet32 (PcdCpuMsegSize), EFI_PAGE_SIZE); 179 } else { 180 DEBUG ((DEBUG_ERROR, "Not enough SMRAM resource to allocate MSEG size %08x\n", PcdGet32 (PcdCpuMsegSize))); 181 } 182 } 183 if (mMsegBase > 0) { 184 DEBUG ((DEBUG_INFO, "MsegBase: 0x%08x, MsegSize: 0x%08x\n", mMsegBase, mMsegSize)); 185 } 186 } 187 188 return EFI_SUCCESS; 189 } 190 191 /** 192 Internal worker function that is called to complete CPU initialization at the 193 end of SmmCpuFeaturesInitializeProcessor(). 194 195 **/ 196 VOID 197 FinishSmmCpuFeaturesInitializeProcessor ( 198 VOID 199 ) 200 { 201 MSR_IA32_SMM_MONITOR_CTL_REGISTER SmmMonitorCtl; 202 203 // 204 // Set MSEG Base Address in SMM Monitor Control MSR. 205 // 206 if (mMsegBase > 0) { 207 SmmMonitorCtl.Uint64 = 0; 208 SmmMonitorCtl.Bits.MsegBase = (UINT32)mMsegBase >> 12; 209 SmmMonitorCtl.Bits.Valid = 1; 210 AsmWriteMsr64 (MSR_IA32_SMM_MONITOR_CTL, SmmMonitorCtl.Uint64); 211 } 212 } 213 214 /** 215 Return the size, in bytes, of a custom SMI Handler in bytes. If 0 is 216 returned, then a custom SMI handler is not provided by this library, 217 and the default SMI handler must be used. 218 219 @retval 0 Use the default SMI handler. 220 @retval > 0 Use the SMI handler installed by SmmCpuFeaturesInstallSmiHandler() 221 The caller is required to allocate enough SMRAM for each CPU to 222 support the size of the custom SMI handler. 223 **/ 224 UINTN 225 EFIAPI 226 SmmCpuFeaturesGetSmiHandlerSize ( 227 VOID 228 ) 229 { 230 return gcStmSmiHandlerSize; 231 } 232 233 /** 234 Install a custom SMI handler for the CPU specified by CpuIndex. This function 235 is only called if SmmCpuFeaturesGetSmiHandlerSize() returns a size is greater 236 than zero and is called by the CPU that was elected as monarch during System 237 Management Mode initialization. 238 239 @param[in] CpuIndex The index of the CPU to install the custom SMI handler. 240 The value must be between 0 and the NumberOfCpus field 241 in the System Management System Table (SMST). 242 @param[in] SmBase The SMBASE address for the CPU specified by CpuIndex. 243 @param[in] SmiStack The stack to use when an SMI is processed by the 244 the CPU specified by CpuIndex. 245 @param[in] StackSize The size, in bytes, if the stack used when an SMI is 246 processed by the CPU specified by CpuIndex. 247 @param[in] GdtBase The base address of the GDT to use when an SMI is 248 processed by the CPU specified by CpuIndex. 249 @param[in] GdtSize The size, in bytes, of the GDT used when an SMI is 250 processed by the CPU specified by CpuIndex. 251 @param[in] IdtBase The base address of the IDT to use when an SMI is 252 processed by the CPU specified by CpuIndex. 253 @param[in] IdtSize The size, in bytes, of the IDT used when an SMI is 254 processed by the CPU specified by CpuIndex. 255 @param[in] Cr3 The base address of the page tables to use when an SMI 256 is processed by the CPU specified by CpuIndex. 257 **/ 258 VOID 259 EFIAPI 260 SmmCpuFeaturesInstallSmiHandler ( 261 IN UINTN CpuIndex, 262 IN UINT32 SmBase, 263 IN VOID *SmiStack, 264 IN UINTN StackSize, 265 IN UINTN GdtBase, 266 IN UINTN GdtSize, 267 IN UINTN IdtBase, 268 IN UINTN IdtSize, 269 IN UINT32 Cr3 270 ) 271 { 272 EFI_STATUS Status; 273 TXT_PROCESSOR_SMM_DESCRIPTOR *Psd; 274 VOID *Hob; 275 UINT32 RegEax; 276 UINT32 RegEdx; 277 EFI_PROCESSOR_INFORMATION ProcessorInfo; 278 279 CopyMem ((VOID *)(UINTN)(SmBase + TXT_SMM_PSD_OFFSET), &gcStmPsd, sizeof (gcStmPsd)); 280 Psd = (TXT_PROCESSOR_SMM_DESCRIPTOR *)(VOID *)(UINTN)(SmBase + TXT_SMM_PSD_OFFSET); 281 Psd->SmmGdtPtr = GdtBase; 282 Psd->SmmGdtSize = (UINT32)GdtSize; 283 284 // 285 // Initialize values in template before copy 286 // 287 gStmSmiStack = (UINT32)((UINTN)SmiStack + StackSize - sizeof (UINTN)); 288 gStmSmiCr3 = Cr3; 289 gStmSmbase = SmBase; 290 gStmSmiHandlerIdtr.Base = IdtBase; 291 gStmSmiHandlerIdtr.Limit = (UINT16)(IdtSize - 1); 292 293 if (gStmXdSupported) { 294 AsmCpuid (CPUID_EXTENDED_FUNCTION, &RegEax, NULL, NULL, NULL); 295 if (RegEax <= CPUID_EXTENDED_FUNCTION) { 296 // 297 // Extended CPUID functions are not supported on this processor. 298 // 299 gStmXdSupported = FALSE; 300 } 301 302 AsmCpuid (CPUID_EXTENDED_CPU_SIG, NULL, NULL, NULL, &RegEdx); 303 if ((RegEdx & CPUID1_EDX_XD_SUPPORT) == 0) { 304 // 305 // Execute Disable Bit feature is not supported on this processor. 306 // 307 gStmXdSupported = FALSE; 308 } 309 } 310 311 // 312 // Set the value at the top of the CPU stack to the CPU Index 313 // 314 *(UINTN*)(UINTN)gStmSmiStack = CpuIndex; 315 316 // 317 // Copy template to CPU specific SMI handler location 318 // 319 CopyMem ( 320 (VOID*)(UINTN)(SmBase + SMM_HANDLER_OFFSET), 321 (VOID*)gcStmSmiHandlerTemplate, 322 gcStmSmiHandlerSize 323 ); 324 325 Psd->SmmSmiHandlerRip = SmBase + SMM_HANDLER_OFFSET + gcStmSmiHandlerOffset; 326 Psd->SmmSmiHandlerRsp = (UINTN)SmiStack + StackSize - sizeof(UINTN); 327 Psd->SmmCr3 = Cr3; 328 329 DEBUG((DEBUG_ERROR, "CpuSmmStmExceptionStackSize - %x\n", PcdGet32(PcdCpuSmmStmExceptionStackSize))); 330 DEBUG((DEBUG_ERROR, "Pages - %x\n", EFI_SIZE_TO_PAGES(PcdGet32(PcdCpuSmmStmExceptionStackSize)))); 331 Psd->StmProtectionExceptionHandler.SpeRsp = (UINT64)(UINTN)AllocatePages (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmStmExceptionStackSize))); 332 Psd->StmProtectionExceptionHandler.SpeRsp += EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (PcdGet32 (PcdCpuSmmStmExceptionStackSize))); 333 334 Psd->BiosHwResourceRequirementsPtr = (UINT64)(UINTN)GetStmResource (); 335 336 // 337 // Get the APIC ID for the CPU specified by CpuIndex 338 // 339 Status = mSmmCpuFeaturesLibMpService->GetProcessorInfo ( 340 mSmmCpuFeaturesLibMpService, 341 CpuIndex, 342 &ProcessorInfo 343 ); 344 ASSERT_EFI_ERROR (Status); 345 346 Psd->LocalApicId = (UINT32)ProcessorInfo.ProcessorId; 347 Psd->AcpiRsdp = 0; 348 349 Hob = GetFirstHob (EFI_HOB_TYPE_CPU); 350 if (Hob != NULL) { 351 Psd->PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace; 352 } else { 353 AsmCpuid (0x80000000, &RegEax, NULL, NULL, NULL); 354 if (RegEax >= 0x80000008) { 355 AsmCpuid (0x80000008, &RegEax, NULL, NULL, NULL); 356 Psd->PhysicalAddressBits = (UINT8) RegEax; 357 } else { 358 Psd->PhysicalAddressBits = 36; 359 } 360 } 361 362 if (!mStmConfigurationTableInitialized) { 363 StmSmmConfigurationTableInit (); 364 mStmConfigurationTableInitialized = TRUE; 365 } 366 } 367 368 /** 369 SMM End Of Dxe event notification handler. 370 371 STM support need patch AcpiRsdp in TXT_PROCESSOR_SMM_DESCRIPTOR. 372 373 @param[in] Protocol Points to the protocol's unique identifier. 374 @param[in] Interface Points to the interface instance. 375 @param[in] Handle The handle on which the interface was installed. 376 377 @retval EFI_SUCCESS Notification handler runs successfully. 378 **/ 379 EFI_STATUS 380 EFIAPI 381 SmmEndOfDxeEventNotify ( 382 IN CONST EFI_GUID *Protocol, 383 IN VOID *Interface, 384 IN EFI_HANDLE Handle 385 ) 386 { 387 VOID *Rsdp; 388 UINTN Index; 389 TXT_PROCESSOR_SMM_DESCRIPTOR *Psd; 390 391 DEBUG ((DEBUG_INFO, "SmmEndOfDxeEventNotify\n")); 392 393 // 394 // found ACPI table RSD_PTR from system table 395 // 396 Rsdp = NULL; 397 for (Index = 0; Index < gST->NumberOfTableEntries; Index++) { 398 if (CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), &gEfiAcpi20TableGuid)) { 399 // 400 // A match was found. 401 // 402 Rsdp = gST->ConfigurationTable[Index].VendorTable; 403 break; 404 } 405 } 406 if (Rsdp == NULL) { 407 for (Index = 0; Index < gST->NumberOfTableEntries; Index++) { 408 if (CompareGuid (&(gST->ConfigurationTable[Index].VendorGuid), &gEfiAcpi10TableGuid)) { 409 // 410 // A match was found. 411 // 412 Rsdp = gST->ConfigurationTable[Index].VendorTable; 413 break; 414 } 415 } 416 } 417 418 for (Index = 0; Index < gSmst->NumberOfCpus; Index++) { 419 Psd = (TXT_PROCESSOR_SMM_DESCRIPTOR *)((UINTN)gSmst->CpuSaveState[Index] - SMRAM_SAVE_STATE_MAP_OFFSET + TXT_SMM_PSD_OFFSET); 420 DEBUG ((DEBUG_INFO, "Index=%d Psd=%p Rsdp=%p\n", Index, Psd, Rsdp)); 421 Psd->AcpiRsdp = (UINT64)(UINTN)Rsdp; 422 } 423 424 mLockLoadMonitor = TRUE; 425 426 return EFI_SUCCESS; 427 } 428 429 /** 430 This function initializes the STM configuration table. 431 **/ 432 VOID 433 StmSmmConfigurationTableInit ( 434 VOID 435 ) 436 { 437 EFI_STATUS Status; 438 VOID *Registration; 439 440 Status = gSmst->SmmInstallProtocolInterface ( 441 &mStmSmmCpuHandle, 442 &gEfiSmMonitorInitProtocolGuid, 443 EFI_NATIVE_INTERFACE, 444 &mSmMonitorInitProtocol 445 ); 446 ASSERT_EFI_ERROR (Status); 447 448 // 449 // 450 // Register SMM End of DXE Event 451 // 452 Status = gSmst->SmmRegisterProtocolNotify ( 453 &gEfiSmmEndOfDxeProtocolGuid, 454 SmmEndOfDxeEventNotify, 455 &Registration 456 ); 457 ASSERT_EFI_ERROR (Status); 458 } 459 460 /** 461 462 Get STM state. 463 464 @return STM state 465 466 **/ 467 EFI_SM_MONITOR_STATE 468 EFIAPI 469 GetMonitorState ( 470 VOID 471 ) 472 { 473 return mStmState; 474 } 475 476 /** 477 478 Handle single Resource to see if it can be merged into Record. 479 480 @param Resource A pointer to resource node to be added 481 @param Record A pointer to record node to be merged 482 483 @retval TRUE resource handled 484 @retval FALSE resource is not handled 485 486 **/ 487 BOOLEAN 488 HandleSingleResource ( 489 IN STM_RSC *Resource, 490 IN STM_RSC *Record 491 ) 492 { 493 UINT64 ResourceLo; 494 UINT64 ResourceHi; 495 UINT64 RecordLo; 496 UINT64 RecordHi; 497 498 ResourceLo = 0; 499 ResourceHi = 0; 500 RecordLo = 0; 501 RecordHi = 0; 502 503 // 504 // Calling code is responsible for making sure that 505 // Resource->Header.RscType == (*Record)->Header.RscType 506 // thus we use just one of them as switch variable. 507 // 508 switch (Resource->Header.RscType) { 509 case MEM_RANGE: 510 case MMIO_RANGE: 511 ResourceLo = Resource->Mem.Base; 512 ResourceHi = Resource->Mem.Base + Resource->Mem.Length; 513 RecordLo = Record->Mem.Base; 514 RecordHi = Record->Mem.Base + Record->Mem.Length; 515 if (Resource->Mem.RWXAttributes != Record->Mem.RWXAttributes) { 516 if ((ResourceLo == RecordLo) && (ResourceHi == RecordHi)) { 517 Record->Mem.RWXAttributes = Resource->Mem.RWXAttributes | Record->Mem.RWXAttributes; 518 return TRUE; 519 } else { 520 return FALSE; 521 } 522 } 523 break; 524 case IO_RANGE: 525 case TRAPPED_IO_RANGE: 526 ResourceLo = (UINT64) Resource->Io.Base; 527 ResourceHi = (UINT64) Resource->Io.Base + (UINT64) Resource->Io.Length; 528 RecordLo = (UINT64) Record->Io.Base; 529 RecordHi = (UINT64) Record->Io.Base + (UINT64) Record->Io.Length; 530 break; 531 case PCI_CFG_RANGE: 532 if ((Resource->PciCfg.OriginatingBusNumber != Record->PciCfg.OriginatingBusNumber) || 533 (Resource->PciCfg.LastNodeIndex != Record->PciCfg.LastNodeIndex)) { 534 return FALSE; 535 } 536 if (CompareMem (Resource->PciCfg.PciDevicePath, Record->PciCfg.PciDevicePath, sizeof(STM_PCI_DEVICE_PATH_NODE) * (Resource->PciCfg.LastNodeIndex + 1)) != 0) { 537 return FALSE; 538 } 539 ResourceLo = (UINT64) Resource->PciCfg.Base; 540 ResourceHi = (UINT64) Resource->PciCfg.Base + (UINT64) Resource->PciCfg.Length; 541 RecordLo = (UINT64) Record->PciCfg.Base; 542 RecordHi = (UINT64) Record->PciCfg.Base + (UINT64) Record->PciCfg.Length; 543 if (Resource->PciCfg.RWAttributes != Record->PciCfg.RWAttributes) { 544 if ((ResourceLo == RecordLo) && (ResourceHi == RecordHi)) { 545 Record->PciCfg.RWAttributes = Resource->PciCfg.RWAttributes | Record->PciCfg.RWAttributes; 546 return TRUE; 547 } else { 548 return FALSE; 549 } 550 } 551 break; 552 case MACHINE_SPECIFIC_REG: 553 // 554 // Special case - merge MSR masks in place. 555 // 556 if (Resource->Msr.MsrIndex != Record->Msr.MsrIndex) { 557 return FALSE; 558 } 559 Record->Msr.ReadMask |= Resource->Msr.ReadMask; 560 Record->Msr.WriteMask |= Resource->Msr.WriteMask; 561 return TRUE; 562 default: 563 return FALSE; 564 } 565 // 566 // If resources are disjoint 567 // 568 if ((ResourceHi < RecordLo) || (ResourceLo > RecordHi)) { 569 return FALSE; 570 } 571 572 // 573 // If resource is consumed by record. 574 // 575 if ((ResourceLo >= RecordLo) && (ResourceHi <= RecordHi)) { 576 return TRUE; 577 } 578 // 579 // Resources are overlapping. 580 // Resource and record are merged. 581 // 582 ResourceLo = (ResourceLo < RecordLo) ? ResourceLo : RecordLo; 583 ResourceHi = (ResourceHi > RecordHi) ? ResourceHi : RecordHi; 584 585 switch (Resource->Header.RscType) { 586 case MEM_RANGE: 587 case MMIO_RANGE: 588 Record->Mem.Base = ResourceLo; 589 Record->Mem.Length = ResourceHi - ResourceLo; 590 break; 591 case IO_RANGE: 592 case TRAPPED_IO_RANGE: 593 Record->Io.Base = (UINT16) ResourceLo; 594 Record->Io.Length = (UINT16) (ResourceHi - ResourceLo); 595 break; 596 case PCI_CFG_RANGE: 597 Record->PciCfg.Base = (UINT16) ResourceLo; 598 Record->PciCfg.Length = (UINT16) (ResourceHi - ResourceLo); 599 break; 600 default: 601 return FALSE; 602 } 603 604 return TRUE; 605 } 606 607 /** 608 609 Add resource node. 610 611 @param Resource A pointer to resource node to be added 612 613 **/ 614 VOID 615 AddSingleResource ( 616 IN STM_RSC *Resource 617 ) 618 { 619 STM_RSC *Record; 620 621 Record = (STM_RSC *)mStmResourcesPtr; 622 623 while (TRUE) { 624 if (Record->Header.RscType == END_OF_RESOURCES) { 625 break; 626 } 627 // 628 // Go to next record if resource and record types don't match. 629 // 630 if (Resource->Header.RscType != Record->Header.RscType) { 631 Record = (STM_RSC *)((UINTN)Record + Record->Header.Length); 632 continue; 633 } 634 // 635 // Record is handled inside of procedure - don't adjust. 636 // 637 if (HandleSingleResource (Resource, Record)) { 638 return ; 639 } 640 Record = (STM_RSC *)((UINTN)Record + Record->Header.Length); 641 } 642 643 // 644 // Add resource to the end of area. 645 // 646 CopyMem ( 647 mStmResourcesPtr + mStmResourceSizeUsed - sizeof(mRscEndNode), 648 Resource, 649 Resource->Header.Length 650 ); 651 CopyMem ( 652 mStmResourcesPtr + mStmResourceSizeUsed - sizeof(mRscEndNode) + Resource->Header.Length, 653 &mRscEndNode, 654 sizeof(mRscEndNode) 655 ); 656 mStmResourceSizeUsed += Resource->Header.Length; 657 mStmResourceSizeAvailable = mStmResourceTotalSize - mStmResourceSizeUsed; 658 659 return ; 660 } 661 662 /** 663 664 Add resource list. 665 666 @param ResourceList A pointer to resource list to be added 667 @param NumEntries Optional number of entries. 668 If 0, list must be terminated by END_OF_RESOURCES. 669 670 **/ 671 VOID 672 AddResource ( 673 IN STM_RSC *ResourceList, 674 IN UINT32 NumEntries OPTIONAL 675 ) 676 { 677 UINT32 Count; 678 UINTN Index; 679 STM_RSC *Resource; 680 681 if (NumEntries == 0) { 682 Count = 0xFFFFFFFF; 683 } else { 684 Count = NumEntries; 685 } 686 687 Resource = ResourceList; 688 689 for (Index = 0; Index < Count; Index++) { 690 if (Resource->Header.RscType == END_OF_RESOURCES) { 691 return ; 692 } 693 AddSingleResource (Resource); 694 Resource = (STM_RSC *)((UINTN)Resource + Resource->Header.Length); 695 } 696 return ; 697 } 698 699 /** 700 701 Validate resource list. 702 703 @param ResourceList A pointer to resource list to be added 704 @param NumEntries Optional number of entries. 705 If 0, list must be terminated by END_OF_RESOURCES. 706 707 @retval TRUE resource valid 708 @retval FALSE resource invalid 709 710 **/ 711 BOOLEAN 712 ValidateResource ( 713 IN STM_RSC *ResourceList, 714 IN UINT32 NumEntries OPTIONAL 715 ) 716 { 717 UINT32 Count; 718 UINTN Index; 719 STM_RSC *Resource; 720 UINTN SubIndex; 721 722 // 723 // If NumEntries == 0 make it very big. Scan will be terminated by 724 // END_OF_RESOURCES. 725 // 726 if (NumEntries == 0) { 727 Count = 0xFFFFFFFF; 728 } else { 729 Count = NumEntries; 730 } 731 732 // 733 // Start from beginning of resource list. 734 // 735 Resource = ResourceList; 736 737 for (Index = 0; Index < Count; Index++) { 738 DEBUG ((DEBUG_ERROR, "ValidateResource (%d) - RscType(%x)\n", Index, Resource->Header.RscType)); 739 // 740 // Validate resource. 741 // 742 switch (Resource->Header.RscType) { 743 case END_OF_RESOURCES: 744 if (Resource->Header.Length != sizeof (STM_RSC_END)) { 745 return FALSE; 746 } 747 // 748 // If we are passed actual number of resources to add, 749 // END_OF_RESOURCES structure between them is considered an 750 // error. If NumEntries == 0 END_OF_RESOURCES is a termination. 751 // 752 if (NumEntries != 0) { 753 return FALSE; 754 } else { 755 // 756 // If NumEntries == 0 and list reached end - return success. 757 // 758 return TRUE; 759 } 760 break; 761 762 case MEM_RANGE: 763 case MMIO_RANGE: 764 if (Resource->Header.Length != sizeof (STM_RSC_MEM_DESC)) { 765 return FALSE; 766 } 767 768 if (Resource->Mem.RWXAttributes > FULL_ACCS) { 769 return FALSE; 770 } 771 break; 772 773 case IO_RANGE: 774 case TRAPPED_IO_RANGE: 775 if (Resource->Header.Length != sizeof (STM_RSC_IO_DESC)) { 776 return FALSE; 777 } 778 779 if ((Resource->Io.Base + Resource->Io.Length) > 0xFFFF) { 780 return FALSE; 781 } 782 break; 783 784 case PCI_CFG_RANGE: 785 DEBUG ((DEBUG_ERROR, "ValidateResource - PCI (0x%02x, 0x%08x, 0x%02x, 0x%02x)\n", Resource->PciCfg.OriginatingBusNumber, Resource->PciCfg.LastNodeIndex, Resource->PciCfg.PciDevicePath[0].PciDevice, Resource->PciCfg.PciDevicePath[0].PciFunction)); 786 if (Resource->Header.Length != sizeof (STM_RSC_PCI_CFG_DESC) + (sizeof(STM_PCI_DEVICE_PATH_NODE) * Resource->PciCfg.LastNodeIndex)) { 787 return FALSE; 788 } 789 for (SubIndex = 0; SubIndex <= Resource->PciCfg.LastNodeIndex; SubIndex++) { 790 if ((Resource->PciCfg.PciDevicePath[SubIndex].PciDevice > 0x1F) || (Resource->PciCfg.PciDevicePath[SubIndex].PciFunction > 7)) { 791 return FALSE; 792 } 793 } 794 if ((Resource->PciCfg.Base + Resource->PciCfg.Length) > 0x1000) { 795 return FALSE; 796 } 797 break; 798 799 case MACHINE_SPECIFIC_REG: 800 if (Resource->Header.Length != sizeof (STM_RSC_MSR_DESC)) { 801 return FALSE; 802 } 803 break; 804 805 default : 806 DEBUG ((DEBUG_ERROR, "ValidateResource - Unknown RscType(%x)\n", Resource->Header.RscType)); 807 return FALSE; 808 } 809 Resource = (STM_RSC *)((UINTN)Resource + Resource->Header.Length); 810 } 811 return TRUE; 812 } 813 814 /** 815 816 Get resource list. 817 EndResource is excluded. 818 819 @param ResourceList A pointer to resource list to be added 820 @param NumEntries Optional number of entries. 821 If 0, list must be terminated by END_OF_RESOURCES. 822 823 @retval TRUE resource valid 824 @retval FALSE resource invalid 825 826 **/ 827 UINTN 828 GetResourceSize ( 829 IN STM_RSC *ResourceList, 830 IN UINT32 NumEntries OPTIONAL 831 ) 832 { 833 UINT32 Count; 834 UINTN Index; 835 STM_RSC *Resource; 836 837 Resource = ResourceList; 838 839 // 840 // If NumEntries == 0 make it very big. Scan will be terminated by 841 // END_OF_RESOURCES. 842 // 843 if (NumEntries == 0) { 844 Count = 0xFFFFFFFF; 845 } else { 846 Count = NumEntries; 847 } 848 849 // 850 // Start from beginning of resource list. 851 // 852 Resource = ResourceList; 853 854 for (Index = 0; Index < Count; Index++) { 855 if (Resource->Header.RscType == END_OF_RESOURCES) { 856 break; 857 } 858 Resource = (STM_RSC *)((UINTN)Resource + Resource->Header.Length); 859 } 860 861 return (UINTN)Resource - (UINTN)ResourceList; 862 } 863 864 /** 865 866 Add resources in list to database. Allocate new memory areas as needed. 867 868 @param ResourceList A pointer to resource list to be added 869 @param NumEntries Optional number of entries. 870 If 0, list must be terminated by END_OF_RESOURCES. 871 872 @retval EFI_SUCCESS If resources are added 873 @retval EFI_INVALID_PARAMETER If nested procedure detected resource failer 874 @retval EFI_OUT_OF_RESOURCES If nested procedure returned it and we cannot allocate more areas. 875 876 **/ 877 EFI_STATUS 878 EFIAPI 879 AddPiResource ( 880 IN STM_RSC *ResourceList, 881 IN UINT32 NumEntries OPTIONAL 882 ) 883 { 884 EFI_STATUS Status; 885 UINTN ResourceSize; 886 EFI_PHYSICAL_ADDRESS NewResource; 887 UINTN NewResourceSize; 888 889 DEBUG ((DEBUG_INFO, "AddPiResource - Enter\n")); 890 891 if (!ValidateResource (ResourceList, NumEntries)) { 892 return EFI_INVALID_PARAMETER; 893 } 894 895 ResourceSize = GetResourceSize (ResourceList, NumEntries); 896 DEBUG ((DEBUG_INFO, "ResourceSize - 0x%08x\n", ResourceSize)); 897 if (ResourceSize == 0) { 898 return EFI_INVALID_PARAMETER; 899 } 900 901 if (mStmResourcesPtr == NULL) { 902 // 903 // First time allocation 904 // 905 NewResourceSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (ResourceSize + sizeof(mRscEndNode))); 906 DEBUG ((DEBUG_INFO, "Allocate - 0x%08x\n", NewResourceSize)); 907 Status = gSmst->SmmAllocatePages ( 908 AllocateAnyPages, 909 EfiRuntimeServicesData, 910 EFI_SIZE_TO_PAGES (NewResourceSize), 911 &NewResource 912 ); 913 if (EFI_ERROR (Status)) { 914 return Status; 915 } 916 917 // 918 // Copy EndResource for intialization 919 // 920 mStmResourcesPtr = (UINT8 *)(UINTN)NewResource; 921 mStmResourceTotalSize = NewResourceSize; 922 CopyMem (mStmResourcesPtr, &mRscEndNode, sizeof(mRscEndNode)); 923 mStmResourceSizeUsed = sizeof(mRscEndNode); 924 mStmResourceSizeAvailable = mStmResourceTotalSize - sizeof(mRscEndNode); 925 926 // 927 // Let SmmCore change resource ptr 928 // 929 NotifyStmResourceChange (mStmResourcesPtr); 930 } else if (mStmResourceSizeAvailable < ResourceSize) { 931 // 932 // Need enlarge 933 // 934 NewResourceSize = mStmResourceTotalSize + (ResourceSize - mStmResourceSizeAvailable); 935 NewResourceSize = EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (NewResourceSize)); 936 DEBUG ((DEBUG_INFO, "ReAllocate - 0x%08x\n", NewResourceSize)); 937 Status = gSmst->SmmAllocatePages ( 938 AllocateAnyPages, 939 EfiRuntimeServicesData, 940 EFI_SIZE_TO_PAGES (NewResourceSize), 941 &NewResource 942 ); 943 if (EFI_ERROR (Status)) { 944 return Status; 945 } 946 CopyMem ((VOID *)(UINTN)NewResource, mStmResourcesPtr, mStmResourceSizeUsed); 947 mStmResourceSizeAvailable = NewResourceSize - mStmResourceSizeUsed; 948 949 gSmst->SmmFreePages ( 950 (EFI_PHYSICAL_ADDRESS)(UINTN)mStmResourcesPtr, 951 EFI_SIZE_TO_PAGES (mStmResourceTotalSize) 952 ); 953 954 mStmResourceTotalSize = NewResourceSize; 955 mStmResourcesPtr = (UINT8 *)(UINTN)NewResource; 956 957 // 958 // Let SmmCore change resource ptr 959 // 960 NotifyStmResourceChange (mStmResourcesPtr); 961 } 962 963 // 964 // Check duplication 965 // 966 AddResource (ResourceList, NumEntries); 967 968 return EFI_SUCCESS; 969 } 970 971 /** 972 973 Delete resources in list to database. 974 975 @param ResourceList A pointer to resource list to be deleted 976 NULL means delete all resources. 977 @param NumEntries Optional number of entries. 978 If 0, list must be terminated by END_OF_RESOURCES. 979 980 @retval EFI_SUCCESS If resources are deleted 981 @retval EFI_INVALID_PARAMETER If nested procedure detected resource failer 982 983 **/ 984 EFI_STATUS 985 EFIAPI 986 DeletePiResource ( 987 IN STM_RSC *ResourceList, 988 IN UINT32 NumEntries OPTIONAL 989 ) 990 { 991 if (ResourceList != NULL) { 992 // TBD 993 ASSERT (FALSE); 994 return EFI_UNSUPPORTED; 995 } 996 // 997 // Delete all 998 // 999 CopyMem (mStmResourcesPtr, &mRscEndNode, sizeof(mRscEndNode)); 1000 mStmResourceSizeUsed = sizeof(mRscEndNode); 1001 mStmResourceSizeAvailable = mStmResourceTotalSize - sizeof(mRscEndNode); 1002 return EFI_SUCCESS; 1003 } 1004 1005 /** 1006 1007 Get BIOS resources. 1008 1009 @param ResourceList A pointer to resource list to be filled 1010 @param ResourceSize On input it means size of resource list input. 1011 On output it means size of resource list filled, 1012 or the size of resource list to be filled if size of too small. 1013 1014 @retval EFI_SUCCESS If resources are returned. 1015 @retval EFI_BUFFER_TOO_SMALL If resource list buffer is too small to hold the whole resources. 1016 1017 **/ 1018 EFI_STATUS 1019 EFIAPI 1020 GetPiResource ( 1021 OUT STM_RSC *ResourceList, 1022 IN OUT UINT32 *ResourceSize 1023 ) 1024 { 1025 if (*ResourceSize < mStmResourceSizeUsed) { 1026 *ResourceSize = (UINT32)mStmResourceSizeUsed; 1027 return EFI_BUFFER_TOO_SMALL; 1028 } 1029 1030 CopyMem (ResourceList, mStmResourcesPtr, mStmResourceSizeUsed); 1031 *ResourceSize = (UINT32)mStmResourceSizeUsed; 1032 return EFI_SUCCESS; 1033 } 1034 1035 /** 1036 1037 Set valid bit for MSEG MSR. 1038 1039 @param Buffer Ap function buffer. (not used) 1040 1041 **/ 1042 VOID 1043 EFIAPI 1044 EnableMsegMsr ( 1045 IN VOID *Buffer 1046 ) 1047 { 1048 MSR_IA32_SMM_MONITOR_CTL_REGISTER SmmMonitorCtl; 1049 1050 SmmMonitorCtl.Uint64 = AsmReadMsr64 (MSR_IA32_SMM_MONITOR_CTL); 1051 SmmMonitorCtl.Bits.Valid = 1; 1052 AsmWriteMsr64 (MSR_IA32_SMM_MONITOR_CTL, SmmMonitorCtl.Uint64); 1053 } 1054 1055 /** 1056 1057 Get 4K page aligned VMCS size. 1058 1059 @return 4K page aligned VMCS size 1060 1061 **/ 1062 UINT32 1063 GetVmcsSize ( 1064 VOID 1065 ) 1066 { 1067 MSR_IA32_VMX_BASIC_REGISTER VmxBasic; 1068 1069 // 1070 // Read VMCS size and and align to 4KB 1071 // 1072 VmxBasic.Uint64 = AsmReadMsr64 (MSR_IA32_VMX_BASIC); 1073 return ALIGN_VALUE (VmxBasic.Bits.VmcsSize, SIZE_4KB); 1074 } 1075 1076 /** 1077 1078 Check STM image size. 1079 1080 @param StmImage STM image 1081 @param StmImageSize STM image size 1082 1083 @retval TRUE check pass 1084 @retval FALSE check fail 1085 **/ 1086 BOOLEAN 1087 StmCheckStmImage ( 1088 IN EFI_PHYSICAL_ADDRESS StmImage, 1089 IN UINTN StmImageSize 1090 ) 1091 { 1092 UINTN MinMsegSize; 1093 STM_HEADER *StmHeader; 1094 IA32_VMX_MISC_REGISTER VmxMiscMsr; 1095 1096 // 1097 // Check to see if STM image is compatible with CPU 1098 // 1099 StmHeader = (STM_HEADER *)(UINTN)StmImage; 1100 VmxMiscMsr.Uint64 = AsmReadMsr64 (MSR_IA32_VMX_MISC); 1101 if (StmHeader->HwStmHdr.MsegHeaderRevision != VmxMiscMsr.Bits.MsegRevisionIdentifier) { 1102 DEBUG ((DEBUG_ERROR, "STM Image not compatible with CPU\n")); 1103 DEBUG ((DEBUG_ERROR, " StmHeader->HwStmHdr.MsegHeaderRevision = %08x\n", StmHeader->HwStmHdr.MsegHeaderRevision)); 1104 DEBUG ((DEBUG_ERROR, " VmxMiscMsr.Bits.MsegRevisionIdentifier = %08x\n", VmxMiscMsr.Bits.MsegRevisionIdentifier)); 1105 return FALSE; 1106 } 1107 1108 // 1109 // Get Minimal required Mseg size 1110 // 1111 MinMsegSize = (EFI_PAGES_TO_SIZE (EFI_SIZE_TO_PAGES (StmHeader->SwStmHdr.StaticImageSize)) + 1112 StmHeader->SwStmHdr.AdditionalDynamicMemorySize + 1113 (StmHeader->SwStmHdr.PerProcDynamicMemorySize + GetVmcsSize () * 2) * gSmst->NumberOfCpus); 1114 if (MinMsegSize < StmImageSize) { 1115 MinMsegSize = StmImageSize; 1116 } 1117 1118 if (StmHeader->HwStmHdr.Cr3Offset >= StmHeader->SwStmHdr.StaticImageSize) { 1119 // 1120 // We will create page table, just in case that SINIT does not create it. 1121 // 1122 if (MinMsegSize < StmHeader->HwStmHdr.Cr3Offset + EFI_PAGES_TO_SIZE(6)) { 1123 MinMsegSize = StmHeader->HwStmHdr.Cr3Offset + EFI_PAGES_TO_SIZE(6); 1124 } 1125 } 1126 1127 // 1128 // Check if it exceeds MSEG size 1129 // 1130 if (MinMsegSize > mMsegSize) { 1131 DEBUG ((DEBUG_ERROR, "MSEG too small. Min MSEG Size = %08x Current MSEG Size = %08x\n", MinMsegSize, mMsegSize)); 1132 DEBUG ((DEBUG_ERROR, " StmHeader->SwStmHdr.StaticImageSize = %08x\n", StmHeader->SwStmHdr.StaticImageSize)); 1133 DEBUG ((DEBUG_ERROR, " StmHeader->SwStmHdr.AdditionalDynamicMemorySize = %08x\n", StmHeader->SwStmHdr.AdditionalDynamicMemorySize)); 1134 DEBUG ((DEBUG_ERROR, " StmHeader->SwStmHdr.PerProcDynamicMemorySize = %08x\n", StmHeader->SwStmHdr.PerProcDynamicMemorySize)); 1135 DEBUG ((DEBUG_ERROR, " VMCS Size = %08x\n", GetVmcsSize ())); 1136 DEBUG ((DEBUG_ERROR, " Max CPUs = %08x\n", gSmst->NumberOfCpus)); 1137 DEBUG ((DEBUG_ERROR, " StmHeader->HwStmHdr.Cr3Offset = %08x\n", StmHeader->HwStmHdr.Cr3Offset)); 1138 return FALSE; 1139 } 1140 1141 return TRUE; 1142 } 1143 1144 /** 1145 1146 Load STM image to MSEG. 1147 1148 @param StmImage STM image 1149 @param StmImageSize STM image size 1150 1151 **/ 1152 VOID 1153 StmLoadStmImage ( 1154 IN EFI_PHYSICAL_ADDRESS StmImage, 1155 IN UINTN StmImageSize 1156 ) 1157 { 1158 MSR_IA32_SMM_MONITOR_CTL_REGISTER SmmMonitorCtl; 1159 UINT32 MsegBase; 1160 STM_HEADER *StmHeader; 1161 1162 // 1163 // Get MSEG base address from MSR_IA32_SMM_MONITOR_CTL 1164 // 1165 SmmMonitorCtl.Uint64 = AsmReadMsr64 (MSR_IA32_SMM_MONITOR_CTL); 1166 MsegBase = SmmMonitorCtl.Bits.MsegBase << 12; 1167 1168 // 1169 // Zero all of MSEG base address 1170 // 1171 ZeroMem ((VOID *)(UINTN)MsegBase, mMsegSize); 1172 1173 // 1174 // Copy STM Image into MSEG 1175 // 1176 CopyMem ((VOID *)(UINTN)MsegBase, (VOID *)(UINTN)StmImage, StmImageSize); 1177 1178 // 1179 // STM Header is at the beginning of the STM Image 1180 // 1181 StmHeader = (STM_HEADER *)(UINTN)StmImage; 1182 1183 StmGen4GPageTable ((UINTN)MsegBase + StmHeader->HwStmHdr.Cr3Offset); 1184 } 1185 1186 /** 1187 1188 Load STM image to MSEG. 1189 1190 @param StmImage STM image 1191 @param StmImageSize STM image size 1192 1193 @retval EFI_SUCCESS Load STM to MSEG successfully 1194 @retval EFI_ALREADY_STARTED STM image is already loaded to MSEG 1195 @retval EFI_BUFFER_TOO_SMALL MSEG is smaller than minimal requirement of STM image 1196 @retval EFI_UNSUPPORTED MSEG is not enabled 1197 1198 **/ 1199 EFI_STATUS 1200 EFIAPI 1201 LoadMonitor ( 1202 IN EFI_PHYSICAL_ADDRESS StmImage, 1203 IN UINTN StmImageSize 1204 ) 1205 { 1206 MSR_IA32_SMM_MONITOR_CTL_REGISTER SmmMonitorCtl; 1207 1208 if (mLockLoadMonitor) { 1209 return EFI_ACCESS_DENIED; 1210 } 1211 1212 SmmMonitorCtl.Uint64 = AsmReadMsr64 (MSR_IA32_SMM_MONITOR_CTL); 1213 if (SmmMonitorCtl.Bits.MsegBase == 0) { 1214 return EFI_UNSUPPORTED; 1215 } 1216 1217 if (!StmCheckStmImage (StmImage, StmImageSize)) { 1218 return EFI_BUFFER_TOO_SMALL; 1219 } 1220 1221 // Record STM_HASH to PCR 0, just in case it is NOT TXT launch, we still need provide the evidence. 1222 TpmMeasureAndLogData( 1223 0, // PcrIndex 1224 TXT_EVTYPE_STM_HASH, // EventType 1225 NULL, // EventLog 1226 0, // LogLen 1227 (VOID *)(UINTN)StmImage, // HashData 1228 StmImageSize // HashDataLen 1229 ); 1230 1231 StmLoadStmImage (StmImage, StmImageSize); 1232 1233 mStmState |= EFI_SM_MONITOR_STATE_ENABLED; 1234 1235 return EFI_SUCCESS; 1236 } 1237 1238 /** 1239 This function return BIOS STM resource. 1240 Produced by SmmStm. 1241 Comsumed by SmmMpService when Init. 1242 1243 @return BIOS STM resource 1244 1245 **/ 1246 VOID * 1247 GetStmResource( 1248 VOID 1249 ) 1250 { 1251 return mStmResourcesPtr; 1252 } 1253 1254 /** 1255 This function notify STM resource change. 1256 1257 @param StmResource BIOS STM resource 1258 1259 **/ 1260 VOID 1261 NotifyStmResourceChange ( 1262 VOID *StmResource 1263 ) 1264 { 1265 UINTN Index; 1266 TXT_PROCESSOR_SMM_DESCRIPTOR *Psd; 1267 1268 for (Index = 0; Index < gSmst->NumberOfCpus; Index++) { 1269 Psd = (TXT_PROCESSOR_SMM_DESCRIPTOR *)((UINTN)gSmst->CpuSaveState[Index] - SMRAM_SAVE_STATE_MAP_OFFSET + TXT_SMM_PSD_OFFSET); 1270 Psd->BiosHwResourceRequirementsPtr = (UINT64)(UINTN)StmResource; 1271 } 1272 return ; 1273 } 1274 1275 1276 /** 1277 This is STM setup BIOS callback. 1278 **/ 1279 VOID 1280 EFIAPI 1281 SmmStmSetup ( 1282 VOID 1283 ) 1284 { 1285 mStmState |= EFI_SM_MONITOR_STATE_ACTIVATED; 1286 } 1287 1288 /** 1289 This is STM teardown BIOS callback. 1290 **/ 1291 VOID 1292 EFIAPI 1293 SmmStmTeardown ( 1294 VOID 1295 ) 1296 { 1297 mStmState &= ~EFI_SM_MONITOR_STATE_ACTIVATED; 1298 } 1299 1300