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]
|
||||
ApicId.h
|
||||
CpuHotplug.c
|
||||
QemuCpuhp.c
|
||||
QemuCpuhp.h
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
/** @file
|
||||
Simple wrapper functions that access QEMU's modern CPU hotplug register
|
||||
block.
|
||||
Simple wrapper functions and utility functions that access QEMU's modern CPU
|
||||
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
|
||||
via EFI_MM_CPU_IO_PROTOCOL. If a protocol call fails, these functions don't
|
||||
return.
|
||||
|
@ -134,3 +134,168 @@ QemuCpuhpWriteCommand (
|
|||
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
|
||||
Simple wrapper functions that access QEMU's modern CPU hotplug register
|
||||
block.
|
||||
Simple wrapper functions and utility functions that access QEMU's modern CPU
|
||||
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
|
||||
via EFI_MM_CPU_IO_PROTOCOL. If a protocol call fails, these functions don't
|
||||
return.
|
||||
|
@ -16,6 +16,9 @@
|
|||
#define QEMU_CPUHP_H_
|
||||
|
||||
#include <Protocol/MmCpuIo.h> // EFI_MM_CPU_IO_PROTOCOL
|
||||
#include <Uefi/UefiBaseType.h> // EFI_STATUS
|
||||
|
||||
#include "ApicId.h" // APIC_ID
|
||||
|
||||
UINT32
|
||||
QemuCpuhpReadCommandData2 (
|
||||
|
@ -44,4 +47,15 @@ QemuCpuhpWriteCommand (
|
|||
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_
|
||||
|
|
|
@ -32,6 +32,8 @@
|
|||
|
||||
#define QEMU_CPUHP_R_CPU_STAT 0x4
|
||||
#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
|
||||
|
||||
|
|
Loading…
Reference in New Issue