mirror of https://github.com/acidanthera/audk.git
OvmfPkg/CpuHotplugSmm: add function for collecting CPUs with events
Add a function that collects the APIC IDs of CPUs that have just been hot-plugged, or are about to be hot-unplugged. Pending events are only located and never cleared; QEMU's AML needs the firmware to leave the status bits intact in the hotplug register block. Cc: Ard Biesheuvel <ard.biesheuvel@linaro.org> Cc: Igor Mammedov <imammedo@redhat.com> Cc: Jiewen Yao <jiewen.yao@intel.com> Cc: Jordan Justen <jordan.l.justen@intel.com> Cc: Michael Kinney <michael.d.kinney@intel.com> Cc: Philippe Mathieu-Daudé <philmd@redhat.com> Ref: https://bugzilla.tianocore.org/show_bug.cgi?id=1512 Signed-off-by: Laszlo Ersek <lersek@redhat.com> Message-Id: <20200226221156.29589-10-lersek@redhat.com> Reviewed-by: Ard Biesheuvel <ard.biesheuvel@linaro.org> Reviewed-by: Philippe Mathieu-Daude <philmd@redhat.com> Tested-by: Boris Ostrovsky <boris.ostrovsky@oracle.com>
This commit is contained in:
parent
f668e78871
commit
763840c9ab
|
@ -0,0 +1,23 @@
|
||||||
|
/** @file
|
||||||
|
Type and macro definitions for representing and printing APIC IDs, compatibly
|
||||||
|
with the LocalApicLib and PrintLib classes, respectively.
|
||||||
|
|
||||||
|
Copyright (c) 2020, Red Hat, Inc.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef APIC_ID_H_
|
||||||
|
#define APIC_ID_H_
|
||||||
|
|
||||||
|
//
|
||||||
|
// The type that LocalApicLib represents an APIC ID with.
|
||||||
|
//
|
||||||
|
typedef UINT32 APIC_ID;
|
||||||
|
|
||||||
|
//
|
||||||
|
// The PrintLib conversion specification for formatting an APIC_ID.
|
||||||
|
//
|
||||||
|
#define FMT_APIC_ID "0x%08x"
|
||||||
|
|
||||||
|
#endif // APIC_ID_H_
|
|
@ -22,6 +22,7 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
[Sources]
|
[Sources]
|
||||||
|
ApicId.h
|
||||||
CpuHotplug.c
|
CpuHotplug.c
|
||||||
QemuCpuhp.c
|
QemuCpuhp.c
|
||||||
QemuCpuhp.h
|
QemuCpuhp.h
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/** @file
|
/** @file
|
||||||
Simple wrapper functions that access QEMU's modern CPU hotplug register
|
Simple wrapper functions and utility functions that access QEMU's modern CPU
|
||||||
block.
|
hotplug register block.
|
||||||
|
|
||||||
These functions thinly wrap some of the registers described in
|
These functions manipulate some of the registers described in
|
||||||
"docs/specs/acpi_cpu_hotplug.txt" in the QEMU source. IO Ports are accessed
|
"docs/specs/acpi_cpu_hotplug.txt" in the QEMU source. IO Ports are accessed
|
||||||
via EFI_MM_CPU_IO_PROTOCOL. If a protocol call fails, these functions don't
|
via EFI_MM_CPU_IO_PROTOCOL. If a protocol call fails, these functions don't
|
||||||
return.
|
return.
|
||||||
|
@ -134,3 +134,168 @@ QemuCpuhpWriteCommand (
|
||||||
CpuDeadLoop ();
|
CpuDeadLoop ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Collect the APIC IDs of
|
||||||
|
- the CPUs that have been hot-plugged,
|
||||||
|
- the CPUs that are about to be hot-unplugged.
|
||||||
|
|
||||||
|
This function only scans for events -- it does not modify them -- in the
|
||||||
|
hotplug registers.
|
||||||
|
|
||||||
|
On error, the contents of the output parameters are undefined.
|
||||||
|
|
||||||
|
@param[in] MmCpuIo The EFI_MM_CPU_IO_PROTOCOL instance for
|
||||||
|
accessing IO Ports.
|
||||||
|
|
||||||
|
@param[in] PossibleCpuCount The number of possible CPUs in the system. Must
|
||||||
|
be positive.
|
||||||
|
|
||||||
|
@param[in] ApicIdCount The number of elements each one of the
|
||||||
|
PluggedApicIds and ToUnplugApicIds arrays can
|
||||||
|
accommodate. Must be positive.
|
||||||
|
|
||||||
|
@param[out] PluggedApicIds The APIC IDs of the CPUs that have been
|
||||||
|
hot-plugged.
|
||||||
|
|
||||||
|
@param[out] PluggedCount The number of filled-in APIC IDs in
|
||||||
|
PluggedApicIds.
|
||||||
|
|
||||||
|
@param[out] ToUnplugApicIds The APIC IDs of the CPUs that are about to be
|
||||||
|
hot-unplugged.
|
||||||
|
|
||||||
|
@param[out] ToUnplugCount The number of filled-in APIC IDs in
|
||||||
|
ToUnplugApicIds.
|
||||||
|
|
||||||
|
@retval EFI_INVALID_PARAMETER PossibleCpuCount is zero, or ApicIdCount is
|
||||||
|
zero.
|
||||||
|
|
||||||
|
@retval EFI_PROTOCOL_ERROR Invalid bitmap detected in the
|
||||||
|
QEMU_CPUHP_R_CPU_STAT register.
|
||||||
|
|
||||||
|
@retval EFI_BUFFER_TOO_SMALL There was an attempt to place more than
|
||||||
|
ApicIdCount APIC IDs into one of the
|
||||||
|
PluggedApicIds and ToUnplugApicIds arrays.
|
||||||
|
|
||||||
|
@retval EFI_SUCCESS Output parameters have been set successfully.
|
||||||
|
**/
|
||||||
|
EFI_STATUS
|
||||||
|
QemuCpuhpCollectApicIds (
|
||||||
|
IN CONST EFI_MM_CPU_IO_PROTOCOL *MmCpuIo,
|
||||||
|
IN UINT32 PossibleCpuCount,
|
||||||
|
IN UINT32 ApicIdCount,
|
||||||
|
OUT APIC_ID *PluggedApicIds,
|
||||||
|
OUT UINT32 *PluggedCount,
|
||||||
|
OUT APIC_ID *ToUnplugApicIds,
|
||||||
|
OUT UINT32 *ToUnplugCount
|
||||||
|
)
|
||||||
|
{
|
||||||
|
UINT32 CurrentSelector;
|
||||||
|
|
||||||
|
if (PossibleCpuCount == 0 || ApicIdCount == 0) {
|
||||||
|
return EFI_INVALID_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
|
*PluggedCount = 0;
|
||||||
|
*ToUnplugCount = 0;
|
||||||
|
|
||||||
|
CurrentSelector = 0;
|
||||||
|
do {
|
||||||
|
UINT32 PendingSelector;
|
||||||
|
UINT8 CpuStatus;
|
||||||
|
APIC_ID *ExtendIds;
|
||||||
|
UINT32 *ExtendCount;
|
||||||
|
APIC_ID NewApicId;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Write CurrentSelector (which is valid) to the CPU selector register.
|
||||||
|
// Consequences:
|
||||||
|
//
|
||||||
|
// - Other register accesses will be permitted.
|
||||||
|
//
|
||||||
|
// - The QEMU_CPUHP_CMD_GET_PENDING command will start scanning for a CPU
|
||||||
|
// with pending events at CurrentSelector (inclusive).
|
||||||
|
//
|
||||||
|
QemuCpuhpWriteCpuSelector (MmCpuIo, CurrentSelector);
|
||||||
|
//
|
||||||
|
// Write the QEMU_CPUHP_CMD_GET_PENDING command. Consequences
|
||||||
|
// (independently of each other):
|
||||||
|
//
|
||||||
|
// - If there is a CPU with pending events, starting at CurrentSelector
|
||||||
|
// (inclusive), the CPU selector will be updated to that CPU. Note that
|
||||||
|
// the scanning in QEMU may wrap around, because we must never clear the
|
||||||
|
// event bits.
|
||||||
|
//
|
||||||
|
// - The QEMU_CPUHP_RW_CMD_DATA register will return the (possibly updated)
|
||||||
|
// CPU selector value.
|
||||||
|
//
|
||||||
|
QemuCpuhpWriteCommand (MmCpuIo, QEMU_CPUHP_CMD_GET_PENDING);
|
||||||
|
PendingSelector = QemuCpuhpReadCommandData (MmCpuIo);
|
||||||
|
if (PendingSelector < CurrentSelector) {
|
||||||
|
DEBUG ((DEBUG_VERBOSE, "%a: CurrentSelector=%u PendingSelector=%u: "
|
||||||
|
"wrap-around\n", __FUNCTION__, CurrentSelector, PendingSelector));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
CurrentSelector = PendingSelector;
|
||||||
|
|
||||||
|
//
|
||||||
|
// Check the known status / event bits for the currently selected CPU.
|
||||||
|
//
|
||||||
|
CpuStatus = QemuCpuhpReadCpuStatus (MmCpuIo);
|
||||||
|
if ((CpuStatus & QEMU_CPUHP_STAT_INSERT) != 0) {
|
||||||
|
//
|
||||||
|
// The "insert" event guarantees the "enabled" status; plus it excludes
|
||||||
|
// the "remove" event.
|
||||||
|
//
|
||||||
|
if ((CpuStatus & QEMU_CPUHP_STAT_ENABLED) == 0 ||
|
||||||
|
(CpuStatus & QEMU_CPUHP_STAT_REMOVE) != 0) {
|
||||||
|
DEBUG ((DEBUG_ERROR, "%a: CurrentSelector=%u CpuStatus=0x%x: "
|
||||||
|
"inconsistent CPU status\n", __FUNCTION__, CurrentSelector,
|
||||||
|
CpuStatus));
|
||||||
|
return EFI_PROTOCOL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG ((DEBUG_VERBOSE, "%a: CurrentSelector=%u: insert\n", __FUNCTION__,
|
||||||
|
CurrentSelector));
|
||||||
|
|
||||||
|
ExtendIds = PluggedApicIds;
|
||||||
|
ExtendCount = PluggedCount;
|
||||||
|
} else if ((CpuStatus & QEMU_CPUHP_STAT_REMOVE) != 0) {
|
||||||
|
DEBUG ((DEBUG_VERBOSE, "%a: CurrentSelector=%u: remove\n", __FUNCTION__,
|
||||||
|
CurrentSelector));
|
||||||
|
|
||||||
|
ExtendIds = ToUnplugApicIds;
|
||||||
|
ExtendCount = ToUnplugCount;
|
||||||
|
} else {
|
||||||
|
DEBUG ((DEBUG_VERBOSE, "%a: CurrentSelector=%u: no event\n",
|
||||||
|
__FUNCTION__, CurrentSelector));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Save the APIC ID of the CPU with the pending event, to the corresponding
|
||||||
|
// APIC ID array.
|
||||||
|
//
|
||||||
|
if (*ExtendCount == ApicIdCount) {
|
||||||
|
DEBUG ((DEBUG_ERROR, "%a: APIC ID array too small\n", __FUNCTION__));
|
||||||
|
return EFI_BUFFER_TOO_SMALL;
|
||||||
|
}
|
||||||
|
QemuCpuhpWriteCommand (MmCpuIo, QEMU_CPUHP_CMD_GET_ARCH_ID);
|
||||||
|
NewApicId = QemuCpuhpReadCommandData (MmCpuIo);
|
||||||
|
DEBUG ((DEBUG_VERBOSE, "%a: ApicId=" FMT_APIC_ID "\n", __FUNCTION__,
|
||||||
|
NewApicId));
|
||||||
|
ExtendIds[(*ExtendCount)++] = NewApicId;
|
||||||
|
|
||||||
|
//
|
||||||
|
// We've processed the CPU with (known) pending events, but we must never
|
||||||
|
// clear events. Therefore we need to advance past this CPU manually;
|
||||||
|
// otherwise, QEMU_CPUHP_CMD_GET_PENDING would stick to the currently
|
||||||
|
// selected CPU.
|
||||||
|
//
|
||||||
|
CurrentSelector++;
|
||||||
|
} while (CurrentSelector < PossibleCpuCount);
|
||||||
|
|
||||||
|
DEBUG ((DEBUG_VERBOSE, "%a: PluggedCount=%u ToUnplugCount=%u\n",
|
||||||
|
__FUNCTION__, *PluggedCount, *ToUnplugCount));
|
||||||
|
return EFI_SUCCESS;
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/** @file
|
/** @file
|
||||||
Simple wrapper functions that access QEMU's modern CPU hotplug register
|
Simple wrapper functions and utility functions that access QEMU's modern CPU
|
||||||
block.
|
hotplug register block.
|
||||||
|
|
||||||
These functions thinly wrap some of the registers described in
|
These functions manipulate some of the registers described in
|
||||||
"docs/specs/acpi_cpu_hotplug.txt" in the QEMU source. IO Ports are accessed
|
"docs/specs/acpi_cpu_hotplug.txt" in the QEMU source. IO Ports are accessed
|
||||||
via EFI_MM_CPU_IO_PROTOCOL. If a protocol call fails, these functions don't
|
via EFI_MM_CPU_IO_PROTOCOL. If a protocol call fails, these functions don't
|
||||||
return.
|
return.
|
||||||
|
@ -16,6 +16,9 @@
|
||||||
#define QEMU_CPUHP_H_
|
#define QEMU_CPUHP_H_
|
||||||
|
|
||||||
#include <Protocol/MmCpuIo.h> // EFI_MM_CPU_IO_PROTOCOL
|
#include <Protocol/MmCpuIo.h> // EFI_MM_CPU_IO_PROTOCOL
|
||||||
|
#include <Uefi/UefiBaseType.h> // EFI_STATUS
|
||||||
|
|
||||||
|
#include "ApicId.h" // APIC_ID
|
||||||
|
|
||||||
UINT32
|
UINT32
|
||||||
QemuCpuhpReadCommandData2 (
|
QemuCpuhpReadCommandData2 (
|
||||||
|
@ -44,4 +47,15 @@ QemuCpuhpWriteCommand (
|
||||||
IN UINT8 Command
|
IN UINT8 Command
|
||||||
);
|
);
|
||||||
|
|
||||||
|
EFI_STATUS
|
||||||
|
QemuCpuhpCollectApicIds (
|
||||||
|
IN CONST EFI_MM_CPU_IO_PROTOCOL *MmCpuIo,
|
||||||
|
IN UINT32 PossibleCpuCount,
|
||||||
|
IN UINT32 ApicIdCount,
|
||||||
|
OUT APIC_ID *PluggedApicIds,
|
||||||
|
OUT UINT32 *PluggedCount,
|
||||||
|
OUT APIC_ID *ToUnplugApicIds,
|
||||||
|
OUT UINT32 *ToUnplugCount
|
||||||
|
);
|
||||||
|
|
||||||
#endif // QEMU_CPUHP_H_
|
#endif // QEMU_CPUHP_H_
|
||||||
|
|
|
@ -32,6 +32,8 @@
|
||||||
|
|
||||||
#define QEMU_CPUHP_R_CPU_STAT 0x4
|
#define QEMU_CPUHP_R_CPU_STAT 0x4
|
||||||
#define QEMU_CPUHP_STAT_ENABLED BIT0
|
#define QEMU_CPUHP_STAT_ENABLED BIT0
|
||||||
|
#define QEMU_CPUHP_STAT_INSERT BIT1
|
||||||
|
#define QEMU_CPUHP_STAT_REMOVE BIT2
|
||||||
|
|
||||||
#define QEMU_CPUHP_RW_CMD_DATA 0x8
|
#define QEMU_CPUHP_RW_CMD_DATA 0x8
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue