| /** @file | |
| OVMF ACPI QEMU support | |
| Copyright (c) 2008 - 2014, Intel Corporation. All rights reserved.<BR> | |
| Copyright (C) 2012-2014, Red Hat, Inc. | |
| SPDX-License-Identifier: BSD-2-Clause-Patent | |
| **/ | |
| #include <Library/DebugLib.h> | |
| #include <Library/BaseLib.h> | |
| #include <Library/BaseMemoryLib.h> | |
| #include <Library/MemoryAllocationLib.h> | |
| #include <Library/QemuFwCfgLib.h> | |
| #include <Library/DxeServicesTableLib.h> | |
| #include <Library/PcdLib.h> | |
| #include <Library/OrderedCollectionLib.h> | |
| #include <Library/TdxLib.h> | |
| #include <IndustryStandard/Acpi.h> | |
| #include <Protocol/AcpiSystemDescriptionTable.h> | |
| #include <Protocol/AcpiTable.h> | |
| #include <Library/UefiBootServicesTableLib.h> | |
| #include <Library/UefiLib.h> | |
| #include <Library/TdxMailboxLib.h> | |
| #include <Protocol/Cpu.h> | |
| #include <Uefi.h> | |
| #include <TdxAcpiTable.h> | |
| /** | |
| At the beginning of system boot, a 4K-aligned, 4K-size memory (Td mailbox) is | |
| pre-allocated by host VMM. BSP & APs do the page accept together in that memory | |
| region. | |
| After that TDVF is designed to relocate the mailbox to a 4K-aligned, 4K-size | |
| memory block which is allocated in the ACPI Nvs memory. APs are waken up and | |
| spin around the relocated mailbox for further command. | |
| @return EFI_PHYSICAL_ADDRESS Address of the relocated mailbox | |
| **/ | |
| EFI_PHYSICAL_ADDRESS | |
| EFIAPI | |
| RelocateMailbox ( | |
| VOID | |
| ) | |
| { | |
| EFI_PHYSICAL_ADDRESS Address; | |
| VOID *ApLoopFunc; | |
| UINT32 RelocationPages; | |
| MP_RELOCATION_MAP RelocationMap; | |
| MP_WAKEUP_MAILBOX *RelocatedMailBox; | |
| EFI_STATUS Status; | |
| Address = 0; | |
| ApLoopFunc = NULL; | |
| ZeroMem (&RelocationMap, sizeof (RelocationMap)); | |
| // | |
| // Get information needed to setup aps running in their | |
| // run loop in allocated acpi reserved memory | |
| // Add another page for mailbox | |
| // | |
| AsmGetRelocationMap (&RelocationMap); | |
| if ((RelocationMap.RelocateApLoopFuncAddress == 0) || (RelocationMap.RelocateApLoopFuncSize == 0)) { | |
| DEBUG ((DEBUG_ERROR, "Failed to get the RelocationMap.\n")); | |
| return 0; | |
| } | |
| RelocationPages = EFI_SIZE_TO_PAGES ((UINT32)RelocationMap.RelocateApLoopFuncSize) + 1; | |
| Status = gBS->AllocatePages (AllocateAnyPages, EfiACPIMemoryNVS, RelocationPages, &Address); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "Failed to allocate pages for MailboxRelocation. %r\n", Status)); | |
| return 0; | |
| } | |
| ZeroMem ((VOID *)Address, EFI_PAGES_TO_SIZE (RelocationPages)); | |
| ApLoopFunc = (VOID *)((UINTN)Address + EFI_PAGE_SIZE); | |
| CopyMem ( | |
| ApLoopFunc, | |
| RelocationMap.RelocateApLoopFuncAddress, | |
| RelocationMap.RelocateApLoopFuncSize | |
| ); | |
| DEBUG (( | |
| DEBUG_INFO, | |
| "Ap Relocation: mailbox %llx, loop %p\n", | |
| Address, | |
| ApLoopFunc | |
| )); | |
| // | |
| // Initialize mailbox | |
| // | |
| RelocatedMailBox = (MP_WAKEUP_MAILBOX *)Address; | |
| RelocatedMailBox->Command = MpProtectedModeWakeupCommandNoop; | |
| RelocatedMailBox->ApicId = MP_CPU_PROTECTED_MODE_MAILBOX_APICID_INVALID; | |
| RelocatedMailBox->WakeUpVector = 0; | |
| // | |
| // Wakup APs and have been move to the finalized run loop | |
| // They will spin until guest OS wakes them | |
| // | |
| MpSerializeStart (); | |
| MpSendWakeupCommand ( | |
| MpProtectedModeWakeupCommandWakeup, | |
| (UINT64)ApLoopFunc, | |
| (UINT64)RelocatedMailBox, | |
| 0, | |
| 0, | |
| 0 | |
| ); | |
| return Address; | |
| } | |
| /** | |
| Alter the MADT when ACPI Table from QEMU is available. | |
| @param[in] Event Event whose notification function is being invoked | |
| @param[in] Context Pointer to the notification function's context | |
| **/ | |
| VOID | |
| EFIAPI | |
| AlterAcpiTable ( | |
| IN EFI_EVENT Event, | |
| IN VOID *Context | |
| ) | |
| { | |
| EFI_ACPI_SDT_PROTOCOL *AcpiSdtProtocol; | |
| EFI_ACPI_TABLE_PROTOCOL *AcpiTableProtocol; | |
| EFI_STATUS Status; | |
| UINTN Index; | |
| EFI_ACPI_SDT_HEADER *Table; | |
| EFI_ACPI_TABLE_VERSION Version; | |
| UINTN OriginalTableKey; | |
| UINTN NewTableKey; | |
| UINT8 *NewMadtTable; | |
| UINTN NewMadtTableLength; | |
| EFI_PHYSICAL_ADDRESS RelocateMailboxAddress; | |
| EFI_ACPI_6_4_MULTIPROCESSOR_WAKEUP_STRUCTURE *MadtMpWk; | |
| EFI_ACPI_1_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER *MadtHeader; | |
| Index = 0; | |
| NewMadtTable = NULL; | |
| MadtHeader = NULL; | |
| Status = gBS->LocateProtocol (&gEfiAcpiSdtProtocolGuid, NULL, (void **)&AcpiSdtProtocol); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "Unable to locate ACPI SDT protocol.\n")); | |
| return; | |
| } | |
| RelocateMailboxAddress = RelocateMailbox (); | |
| if (RelocateMailboxAddress == 0) { | |
| ASSERT (FALSE); | |
| DEBUG ((DEBUG_ERROR, "Failed to relocate Td mailbox\n")); | |
| return; | |
| } | |
| do { | |
| Status = AcpiSdtProtocol->GetAcpiTable (Index, &Table, &Version, &OriginalTableKey); | |
| if (!EFI_ERROR (Status) && (Table->Signature == EFI_ACPI_1_0_APIC_SIGNATURE)) { | |
| Status = gBS->LocateProtocol (&gEfiAcpiTableProtocolGuid, NULL, (void **)&AcpiTableProtocol); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "Unable to locate ACPI Table protocol.\n")); | |
| break; | |
| } | |
| NewMadtTableLength = Table->Length + sizeof (EFI_ACPI_6_4_MULTIPROCESSOR_WAKEUP_STRUCTURE); | |
| NewMadtTable = AllocatePool (NewMadtTableLength); | |
| if (NewMadtTable == NULL) { | |
| DEBUG ((DEBUG_ERROR, "%a: OUT_OF_SOURCES error.\n", __func__)); | |
| break; | |
| } | |
| CopyMem (NewMadtTable, (UINT8 *)Table, Table->Length); | |
| MadtHeader = (EFI_ACPI_1_0_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER *)NewMadtTable; | |
| MadtHeader->Header.Length = (UINT32)NewMadtTableLength; | |
| MadtMpWk = (EFI_ACPI_6_4_MULTIPROCESSOR_WAKEUP_STRUCTURE *)(NewMadtTable + Table->Length); | |
| MadtMpWk->Type = EFI_ACPI_6_4_MULTIPROCESSOR_WAKEUP; | |
| MadtMpWk->Length = sizeof (EFI_ACPI_6_4_MULTIPROCESSOR_WAKEUP_STRUCTURE); | |
| MadtMpWk->MailBoxVersion = 0; | |
| MadtMpWk->Reserved = 0; | |
| MadtMpWk->MailBoxAddress = RelocateMailboxAddress; | |
| Status = AcpiTableProtocol->InstallAcpiTable (AcpiTableProtocol, NewMadtTable, NewMadtTableLength, &NewTableKey); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "Failed to install new MADT table. %r\n", Status)); | |
| break; | |
| } | |
| Status = AcpiTableProtocol->UninstallAcpiTable (AcpiTableProtocol, OriginalTableKey); | |
| if (EFI_ERROR (Status)) { | |
| DEBUG ((DEBUG_ERROR, "Uninstall old MADT table error.\n")); | |
| } | |
| break; | |
| } | |
| Index++; | |
| } while (!EFI_ERROR (Status)); | |
| if (NewMadtTable != NULL) { | |
| FreePool (NewMadtTable); | |
| } | |
| } |