efi-stub: reset all APs after entering guest mode

In current hv implementation, we assume all AP have no context before
jumping into guest mode. But this is not true in all UEFI bios. BIOS
could have enabled all (or some of) APs at first. These APs could stay
in a run loop or wait for a semaphore. But after hv takes over control from
efi-stub, all of these AP environments will be simply dropped because
we don't support AP context save/restore for now. As a result,
BSP's ExitBootService will hang forver because it's waiting for AP in its
way (by waiting for a semaphore for example), unfortunately APs are now
in the context that hv provides in which they usually stay in idle loop.

To fix the issue above, we could have two solutions:
1. Save AP's runtime context before entering hv and restore context
after hv jumps back.
2. After hv jumps back, reset all the APs in the UEFI way, so the
previous context will be thrown away and a fresh new starts. Moreover
this new one is under virtualization.

Currently, we adopt the second one by disabling all the APs before
virtualization and then enabling them after hv jumps back. A reset
will be triggered. And this is guaranteed by UEFI MP Service protocol.

Tracked-On: #2435
Signed-off-by: Tw <wei.tan@intel.com>
Reviewed-by: Jason Chen CJ <jason.cj.chen@intel.com>
This commit is contained in:
Tw
2019-01-30 22:08:26 +08:00
committed by wenlingz
parent 5c9f1662db
commit 18b04451e1
2 changed files with 684 additions and 0 deletions

View File

@@ -38,6 +38,7 @@
#include "boot.h"
#include "acrn_common.h"
#include "uefi.h"
#include "MpService.h"
EFI_SYSTEM_TABLE *sys_table;
EFI_BOOT_SERVICES *boot;
@@ -45,6 +46,47 @@ char *cmdline = NULL;
extern const uint64_t guest_entry;
static UINT64 hv_hpa;
static void
enable_disable_all_ap(BOOLEAN enable)
{
EFI_MP_SERVICES_PROTOCOL *mp = NULL;
EFI_STATUS err;
EFI_GUID mp_guid = EFI_MP_SERVICES_PROTOCOL_GUID;
UINTN n_proc, n_enabled_proc, bsp, i;
err = uefi_call_wrapper(boot->LocateProtocol, 3, &mp_guid, NULL, (void **)&mp);
if (err != EFI_SUCCESS) {
Print(L"Unable to locate MP service protocol: %r, skip %s all AP\n",
err, enable ? "enable" : "disable");
return;
}
err = uefi_call_wrapper(mp->GetNumberOfProcessors, 3, mp, &n_proc, &n_enabled_proc);
if (err != EFI_SUCCESS) {
Print(L"failed to GetNumberOfProcessors: %r\n", err);
return;
}
Print(L"detected %d processes, %d enabled\n", n_proc, n_enabled_proc);
err = uefi_call_wrapper(mp->WhoAmI, 2, mp, &bsp);
if (err != EFI_SUCCESS) {
Print(L"failed to WhoAmI: %r\n", err);
return;
}
Print(L"current on process %d\n", bsp);
for (i = 0; i < n_proc; i++) {
if (i == bsp) {
continue;
}
err = uefi_call_wrapper(mp->EnableDisableAP, 4, mp, i, enable, NULL);
if (err != EFI_SUCCESS) {
Print(L"failed to %s AP%d: %r\n", enable ? "enable" : "disable", i, err);
}
}
}
static inline void hv_jump(EFI_PHYSICAL_ADDRESS hv_start,
struct multiboot_info *mbi, struct efi_context *efi_ctx)
{
@@ -325,6 +367,9 @@ efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table)
if (CheckCrc(sys_table->Hdr.HeaderSize, &sys_table->Hdr) != TRUE)
return EFI_LOAD_ERROR;
/* make sure only bsp is enable before entering hv */
enable_disable_all_ap(FALSE);
err = handle_protocol(image, &LoadedImageProtocol, (void **)&info);
if (err != EFI_SUCCESS)
goto failed;
@@ -388,6 +433,12 @@ efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *_table)
if (err != EFI_SUCCESS)
goto failed;
/*
* enable all AP here will reset all APs,
* so acrn can handle their ctx from now on.
*/
enable_disable_all_ap(TRUE);
/* load and start the default bootloader */
path = FileDevicePath(info->DeviceHandle, bootloader_name);
if (!path)