EFI_STATUS EFIAPI ReservedS3Memory ( UINTN SystemMemoryLength ) /*++ Routine Description: Reserved S3 memory for InstallS3Memory Arguments: Returns: EFI_OUT_OF_RESOURCES - Insufficient resources to complete function. EFI_SUCCESS - Function has completed successfully. --*/ { VOID *GuidHob; EFI_SMRAM_HOB_DESCRIPTOR_BLOCK *DescriptorBlock; VOID *AcpiReservedBase; UINTN TsegIndex; UINTN TsegSize; UINTN TsegBase; RESERVED_ACPI_S3_RANGE *AcpiS3Range; // // Get Hob list for SMRAM desc // GuidHob = GetFirstGuidHob (&gEfiSmmPeiSmramMemoryReserveGuid); ASSERT (GuidHob); DescriptorBlock = GET_GUID_HOB_DATA (GuidHob); ASSERT (DescriptorBlock); // // Use the hob to get SMRAM capabilities // TsegIndex = DescriptorBlock->NumberOfSmmReservedRegions - 1; ASSERT (TsegIndex <= (MAX_SMRAM_RANGES - 1)); TsegBase = (UINTN)DescriptorBlock->Descriptor[TsegIndex].PhysicalStart; TsegSize = (UINTN)DescriptorBlock->Descriptor[TsegIndex].PhysicalSize; DEBUG ((EFI_D_INFO, "SMM Base: %08X\n", TsegBase)); DEBUG ((EFI_D_INFO, "SMM Size: %08X\n", TsegSize)); // // Now find the location of the data structure that is used to store the address // of the S3 reserved memory. // AcpiS3Range = (RESERVED_ACPI_S3_RANGE*) (UINTN) (TsegBase + RESERVED_ACPI_S3_RANGE_OFFSET); // // Allocate reserved ACPI memory for S3 resume. Pointer to this region is // stored in SMRAM in the first page of TSEG. // AcpiReservedBase = AllocateAcpiNvsMemoryBelow4G (PcdGet32 (PcdS3AcpiReservedMemorySize)); if (AcpiReservedBase != NULL) { AcpiS3Range->AcpiReservedMemoryBase = (UINT32)(UINTN) AcpiReservedBase; AcpiS3Range->AcpiReservedMemorySize = PcdGet32 (PcdS3AcpiReservedMemorySize); } AcpiS3Range->SystemMemoryLength = (UINT32)SystemMemoryLength; DEBUG ((EFI_D_INFO, "S3 Memory Base: %08X\n", AcpiS3Range->AcpiReservedMemoryBase)); DEBUG ((EFI_D_INFO, "S3 Memory Size: %08X\n", AcpiS3Range->AcpiReservedMemorySize)); DEBUG ((EFI_D_INFO, "S3 SysMemoryLength: %08X\n", AcpiS3Range->SystemMemoryLength)); return EFI_SUCCESS; }
/** DxeSmmReadyToLock Protocol notification event handler. We reuse S3 ACPI NVS reserved memory to do capsule process after reset. @param[in] Event Event whose notification function is being invoked. @param[in] Context Pointer to the notification function's context. **/ VOID EFIAPI DxeSmmReadyToLockNotification ( IN EFI_EVENT Event, IN VOID *Context ) { EFI_STATUS Status; VOID *DxeSmmReadyToLock; UINTN VarSize; EFI_PHYSICAL_ADDRESS TempAcpiS3Context; ACPI_S3_CONTEXT *AcpiS3Context; EFI_CAPSULE_LONG_MODE_BUFFER LongModeBuffer; UINTN TotalPagesNum; UINT8 PhysicalAddressBits; VOID *Hob; UINT32 NumberOfPml4EntriesNeeded; UINT32 NumberOfPdpEntriesNeeded; BOOLEAN LockBoxFound; Status = gBS->LocateProtocol ( &gEfiDxeSmmReadyToLockProtocolGuid, NULL, &DxeSmmReadyToLock ); if (EFI_ERROR (Status)) { return ; } // // Get the ACPI NVS pages reserved by AcpiS3Save // LockBoxFound = FALSE; VarSize = sizeof (EFI_PHYSICAL_ADDRESS); Status = RestoreLockBox ( &gEfiAcpiVariableGuid, &TempAcpiS3Context, &VarSize ); if (!EFI_ERROR (Status)) { AcpiS3Context = (ACPI_S3_CONTEXT *)(UINTN)TempAcpiS3Context; ASSERT (AcpiS3Context != NULL); Status = RestoreLockBox ( &gEfiAcpiS3ContextGuid, NULL, NULL ); if (!EFI_ERROR (Status)) { LongModeBuffer.PageTableAddress = AcpiS3Context->S3NvsPageTableAddress; LongModeBuffer.StackBaseAddress = AcpiS3Context->BootScriptStackBase; LongModeBuffer.StackSize = AcpiS3Context->BootScriptStackSize; LockBoxFound = TRUE; } } if (!LockBoxFound) { // // Page table base address and stack base address can not be found in lock box, // allocate both here. // // // Get physical address bits supported from CPU HOB. // PhysicalAddressBits = 36; Hob = GetFirstHob (EFI_HOB_TYPE_CPU); if (Hob != NULL) { PhysicalAddressBits = ((EFI_HOB_CPU *) Hob)->SizeOfMemorySpace; } // // IA-32e paging translates 48-bit linear addresses to 52-bit physical addresses. // ASSERT (PhysicalAddressBits <= 52); if (PhysicalAddressBits > 48) { PhysicalAddressBits = 48; } // // Calculate page table size and allocate memory for it. // if (PhysicalAddressBits <= 39 ) { NumberOfPml4EntriesNeeded = 1; NumberOfPdpEntriesNeeded = 1 << (PhysicalAddressBits - 30); } else { NumberOfPml4EntriesNeeded = 1 << (PhysicalAddressBits - 39); NumberOfPdpEntriesNeeded = 512; } TotalPagesNum = (NumberOfPdpEntriesNeeded + 1) * NumberOfPml4EntriesNeeded + 1; LongModeBuffer.PageTableAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateAcpiNvsMemoryBelow4G (EFI_PAGES_TO_SIZE (TotalPagesNum)); ASSERT (LongModeBuffer.PageTableAddress != 0); // // Allocate stack // LongModeBuffer.StackSize = PcdGet32 (PcdCapsulePeiLongModeStackSize); LongModeBuffer.StackBaseAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)AllocateAcpiNvsMemoryBelow4G (PcdGet32 (PcdCapsulePeiLongModeStackSize)); ASSERT (LongModeBuffer.StackBaseAddress != 0); } Status = gRT->SetVariable ( EFI_CAPSULE_LONG_MODE_BUFFER_NAME, &gEfiCapsuleVendorGuid, EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS, sizeof (EFI_CAPSULE_LONG_MODE_BUFFER), &LongModeBuffer ); ASSERT_EFI_ERROR (Status); // // Close event, so it will not be invoked again. // gBS->CloseEvent (Event); return ; }
/** The entry function of the CpuS3Data driver. Allocate and initialize all fields of the ACPI_CPU_DATA structure except the MTRR settings. Register an event notification on gEfiEndOfDxeEventGroupGuid to capture the ACPI_CPU_DATA MTRR settings. The PcdCpuS3DataAddress is set to the address that ACPI_CPU_DATA is allocated at. @param[in] ImageHandle The firmware allocated handle for the EFI image. @param[in] SystemTable A pointer to the EFI System Table. @retval EFI_SUCCESS The entry point is executed successfully. @retval EFI_UNSUPPORTED Do not support ACPI S3. @retval other Some error occurs when executing this entry point. **/ EFI_STATUS EFIAPI CpuS3DataInitialize ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; ACPI_CPU_DATA_EX *AcpiCpuDataEx; ACPI_CPU_DATA *AcpiCpuData; EFI_MP_SERVICES_PROTOCOL *MpServices; UINTN NumberOfCpus; UINTN NumberOfEnabledProcessors; VOID *Stack; UINTN TableSize; CPU_REGISTER_TABLE *RegisterTable; UINTN Index; EFI_PROCESSOR_INFORMATION ProcessorInfoBuffer; UINTN GdtSize; UINTN IdtSize; VOID *Gdt; VOID *Idt; EFI_EVENT Event; ACPI_CPU_DATA *OldAcpiCpuData; if (!PcdGetBool (PcdAcpiS3Enable)) { return EFI_UNSUPPORTED; } // // Set PcdCpuS3DataAddress to the base address of the ACPI_CPU_DATA structure // OldAcpiCpuData = (ACPI_CPU_DATA *) (UINTN) PcdGet64 (PcdCpuS3DataAddress); // // Allocate ACPI NVS memory below 4G memory for use on ACPI S3 resume. // AcpiCpuDataEx = AllocateAcpiNvsMemoryBelow4G (sizeof (ACPI_CPU_DATA_EX)); ASSERT (AcpiCpuDataEx != NULL); AcpiCpuData = &AcpiCpuDataEx->AcpiCpuData; // // Get MP Services Protocol // Status = gBS->LocateProtocol ( &gEfiMpServiceProtocolGuid, NULL, (VOID **)&MpServices ); ASSERT_EFI_ERROR (Status); // // Get the number of CPUs // Status = MpServices->GetNumberOfProcessors ( MpServices, &NumberOfCpus, &NumberOfEnabledProcessors ); ASSERT_EFI_ERROR (Status); AcpiCpuData->NumberOfCpus = (UINT32)NumberOfCpus; // // Initialize ACPI_CPU_DATA fields // AcpiCpuData->StackSize = PcdGet32 (PcdCpuApStackSize); AcpiCpuData->ApMachineCheckHandlerBase = 0; AcpiCpuData->ApMachineCheckHandlerSize = 0; AcpiCpuData->GdtrProfile = (EFI_PHYSICAL_ADDRESS)(UINTN)&AcpiCpuDataEx->GdtrProfile; AcpiCpuData->IdtrProfile = (EFI_PHYSICAL_ADDRESS)(UINTN)&AcpiCpuDataEx->IdtrProfile; AcpiCpuData->MtrrTable = (EFI_PHYSICAL_ADDRESS)(UINTN)&AcpiCpuDataEx->MtrrTable; // // Allocate stack space for all CPUs // Stack = AllocateAcpiNvsMemoryBelow4G (NumberOfCpus * AcpiCpuData->StackSize); ASSERT (Stack != NULL); AcpiCpuData->StackAddress = (EFI_PHYSICAL_ADDRESS)(UINTN)Stack; // // Get the boot processor's GDT and IDT // AsmReadGdtr (&AcpiCpuDataEx->GdtrProfile); AsmReadIdtr (&AcpiCpuDataEx->IdtrProfile); // // Allocate GDT and IDT in ACPI NVS and copy current GDT and IDT contents // GdtSize = AcpiCpuDataEx->GdtrProfile.Limit + 1; IdtSize = AcpiCpuDataEx->IdtrProfile.Limit + 1; Gdt = AllocateAcpiNvsMemoryBelow4G (GdtSize + IdtSize); ASSERT (Gdt != NULL); Idt = (VOID *)((UINTN)Gdt + GdtSize); CopyMem (Gdt, (VOID *)AcpiCpuDataEx->GdtrProfile.Base, GdtSize); CopyMem (Idt, (VOID *)AcpiCpuDataEx->IdtrProfile.Base, IdtSize); AcpiCpuDataEx->GdtrProfile.Base = (UINTN)Gdt; AcpiCpuDataEx->IdtrProfile.Base = (UINTN)Idt; if (OldAcpiCpuData != NULL) { AcpiCpuData->RegisterTable = OldAcpiCpuData->RegisterTable; AcpiCpuData->PreSmmInitRegisterTable = OldAcpiCpuData->PreSmmInitRegisterTable; } else { // // Allocate buffer for empty RegisterTable and PreSmmInitRegisterTable for all CPUs // TableSize = 2 * NumberOfCpus * sizeof (CPU_REGISTER_TABLE); RegisterTable = (CPU_REGISTER_TABLE *)AllocateAcpiNvsMemoryBelow4G (TableSize); ASSERT (RegisterTable != NULL); for (Index = 0; Index < NumberOfCpus; Index++) { Status = MpServices->GetProcessorInfo ( MpServices, Index, &ProcessorInfoBuffer ); ASSERT_EFI_ERROR (Status); RegisterTable[Index].InitialApicId = (UINT32)ProcessorInfoBuffer.ProcessorId; RegisterTable[Index].TableLength = 0; RegisterTable[Index].AllocatedSize = 0; RegisterTable[Index].RegisterTableEntry = 0; RegisterTable[NumberOfCpus + Index].InitialApicId = (UINT32)ProcessorInfoBuffer.ProcessorId; RegisterTable[NumberOfCpus + Index].TableLength = 0; RegisterTable[NumberOfCpus + Index].AllocatedSize = 0; RegisterTable[NumberOfCpus + Index].RegisterTableEntry = 0; } AcpiCpuData->RegisterTable = (EFI_PHYSICAL_ADDRESS)(UINTN)RegisterTable; AcpiCpuData->PreSmmInitRegisterTable = (EFI_PHYSICAL_ADDRESS)(UINTN)(RegisterTable + NumberOfCpus); } // // Set PcdCpuS3DataAddress to the base address of the ACPI_CPU_DATA structure // Status = PcdSet64S (PcdCpuS3DataAddress, (UINT64)(UINTN)AcpiCpuData); ASSERT_EFI_ERROR (Status); // // Register EFI_END_OF_DXE_EVENT_GROUP_GUID event. // The notification function allocates StartupVector and saves MTRRs for ACPI_CPU_DATA // Status = gBS->CreateEventEx ( EVT_NOTIFY_SIGNAL, TPL_CALLBACK, CpuS3DataOnEndOfDxe, AcpiCpuData, &gEfiEndOfDxeEventGroupGuid, &Event ); ASSERT_EFI_ERROR (Status); return EFI_SUCCESS; }