linux/arch/x86/xen/apic.c
Thomas Gleixner 354da4cf57 x86/cpu/topology: Let XEN/PV use topology from CPUID/MADT
It turns out that XEN/PV Dom0 has halfways usable CPUID/MADT enumeration
except that it cannot deal with CPUs which are enumerated as disabled in
MADT.

DomU has no MADT and provides at least rudimentary topology information in
CPUID leaves 1 and 4.

For both it's important that there are not more possible Linux CPUs than
vCPUs provided by the hypervisor.

As this is ensured by counting the vCPUs before enumeration happens:

  - lift the restrictions in the CPUID evaluation and the MADT parser

  - Utilize MADT registration for Dom0

  - Keep the fake APIC ID registration for DomU

  - Fix the XEN APIC fake so the readout of the local APIC ID works for
    Dom0 via the hypercall and for DomU by returning the registered
    fake APIC IDs.

With that the XEN/PV fake approximates usefulness.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Tested-by: Michael Kelley <mhklinux@outlook.com>
Tested-by: Sohil Mehta <sohil.mehta@intel.com>
Link: https://lore.kernel.org/r/20240213210252.626195405@linutronix.de
2024-02-15 22:07:44 +01:00

151 lines
2.9 KiB
C

// SPDX-License-Identifier: GPL-2.0
#include <linux/init.h>
#include <linux/thread_info.h>
#include <asm/x86_init.h>
#include <asm/apic.h>
#include <asm/io_apic.h>
#include <asm/xen/hypercall.h>
#include <xen/xen.h>
#include <xen/interface/physdev.h>
#include "xen-ops.h"
#include "pmu.h"
#include "smp.h"
static unsigned int xen_io_apic_read(unsigned apic, unsigned reg)
{
struct physdev_apic apic_op;
int ret;
apic_op.apic_physbase = mpc_ioapic_addr(apic);
apic_op.reg = reg;
ret = HYPERVISOR_physdev_op(PHYSDEVOP_apic_read, &apic_op);
if (!ret)
return apic_op.value;
/* fallback to return an emulated IO_APIC values */
if (reg == 0x1)
return 0x00170020;
else if (reg == 0x0)
return apic << 24;
return 0xfd;
}
static u32 xen_get_apic_id(u32 x)
{
return ((x)>>24) & 0xFFu;
}
static u32 xen_apic_read(u32 reg)
{
struct xen_platform_op op = {
.cmd = XENPF_get_cpuinfo,
.interface_version = XENPF_INTERFACE_VERSION,
};
int ret, cpu;
if (reg == APIC_LVR)
return 0x14;
if (reg != APIC_ID)
return 0;
cpu = smp_processor_id();
if (!xen_initial_domain())
return cpu ? cpuid_to_apicid[cpu] << 24 : 0;
op.u.pcpu_info.xen_cpuid = cpu;
ret = HYPERVISOR_platform_op(&op);
if (ret)
op.u.pcpu_info.apic_id = BAD_APICID;
return op.u.pcpu_info.apic_id << 24;
}
static void xen_apic_write(u32 reg, u32 val)
{
if (reg == APIC_LVTPC) {
(void)pmu_apic_update(reg);
return;
}
/* Warn to see if there's any stray references */
WARN(1,"register: %x, value: %x\n", reg, val);
}
static void xen_apic_eoi(void)
{
WARN_ON_ONCE(1);
}
static u64 xen_apic_icr_read(void)
{
return 0;
}
static void xen_apic_icr_write(u32 low, u32 id)
{
/* Warn to see if there's any stray references */
WARN_ON(1);
}
static int xen_apic_probe_pv(void)
{
if (xen_pv_domain())
return 1;
return 0;
}
static int xen_madt_oem_check(char *oem_id, char *oem_table_id)
{
return xen_pv_domain();
}
static u32 xen_cpu_present_to_apicid(int cpu)
{
if (cpu_present(cpu))
return cpu_data(cpu).topo.apicid;
else
return BAD_APICID;
}
static struct apic xen_pv_apic __ro_after_init = {
.name = "Xen PV",
.probe = xen_apic_probe_pv,
.acpi_madt_oem_check = xen_madt_oem_check,
/* .delivery_mode and .dest_mode_logical not used by XENPV */
.disable_esr = 0,
.cpu_present_to_apicid = xen_cpu_present_to_apicid,
.max_apic_id = UINT_MAX,
.get_apic_id = xen_get_apic_id,
.calc_dest_apicid = apic_flat_calc_apicid,
#ifdef CONFIG_SMP
.send_IPI_mask = xen_send_IPI_mask,
.send_IPI_mask_allbutself = xen_send_IPI_mask_allbutself,
.send_IPI_allbutself = xen_send_IPI_allbutself,
.send_IPI_all = xen_send_IPI_all,
.send_IPI_self = xen_send_IPI_self,
#endif
.read = xen_apic_read,
.write = xen_apic_write,
.eoi = xen_apic_eoi,
.icr_read = xen_apic_icr_read,
.icr_write = xen_apic_icr_write,
};
apic_driver(xen_pv_apic);
void __init xen_init_apic(void)
{
x86_apic_ops.io_apic_read = xen_io_apic_read;
}