linux/arch/x86/xen/apic.c
Mohit Gambhir cc272163ea x86/xen: Fix APIC id mismatch warning on Intel
This patch fixes the following warning message seen when booting the
kernel as Dom0 with Xen on Intel machines.

[0.003000] [Firmware Bug]: CPU1: APIC id mismatch. Firmware: 0 APIC: 1]

The code generating the warning in validate_apic_and_package_id() matches
cpu_data(cpu).apicid (initialized in init_intel()->
detect_extended_topology() using cpuid) against the apicid returned from
xen_apic_read(). Now, xen_apic_read() makes a hypercall to retrieve apicid
for the boot  cpu but returns 0 otherwise. Hence the warning gets thrown
for all but the boot cpu.

The idea behind xen_apic_read() returning 0 for apicid is that the
guests (even Dom0) should not need to know what physical processor their
vcpus are running on. This is because we currently  do not have topology
information in Xen and also because xen allows more vcpus than physical
processors. However, boot cpu's apicid is required for loading
xen-acpi-processor driver on AMD machines. Look at following patch for
details:

commit 558daa289a ("xen/apic: Return the APIC ID (and version) for CPU
0.")

So to get rid of the warning, this patch modifies
xen_cpu_present_to_apicid() to return cpu_data(cpu).apicid instead of
calling xen_apic_read().

The warning is not seen on AMD machines because init_amd() populates
cpu_data(cpu).apicid by calling hard_smp_processor_id()->xen_apic_read()
as opposed to using apicid from cpuid as is done on Intel machines.

Signed-off-by: Mohit Gambhir <mohit.gambhir@oracle.com>
Reviewed-by: Juergen Gross <jgross@suse.com>
Signed-off-by: Boris Ostrovsky <boris.ostrovsky@oracle.com>
2017-01-29 18:59:16 -05:00

228 lines
5.1 KiB
C

#include <linux/init.h>
#include <asm/x86_init.h>
#include <asm/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 unsigned long xen_set_apic_id(unsigned int x)
{
WARN_ON(1);
return x;
}
static unsigned int xen_get_apic_id(unsigned long 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,
.u.pcpu_info.xen_cpuid = 0,
};
int ret = 0;
/* Shouldn't need this as APIC is turned off for PV, and we only
* get called on the bootup processor. But just in case. */
if (!xen_initial_domain() || smp_processor_id())
return 0;
if (reg == APIC_LVR)
return 0x10;
#ifdef CONFIG_X86_32
if (reg == APIC_LDR)
return SET_APIC_LOGICAL_ID(1UL << smp_processor_id());
#endif
if (reg != APIC_ID)
return 0;
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 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 u32 xen_safe_apic_wait_icr_idle(void)
{
return 0;
}
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 int xen_id_always_valid(int apicid)
{
return 1;
}
static int xen_id_always_registered(void)
{
return 1;
}
static int xen_phys_pkg_id(int initial_apic_id, int index_msb)
{
return initial_apic_id >> index_msb;
}
#ifdef CONFIG_X86_32
static int xen_x86_32_early_logical_apicid(int cpu)
{
/* Match with APIC_LDR read. Otherwise setup_local_APIC complains. */
return 1 << cpu;
}
#endif
static void xen_noop(void)
{
}
static void xen_silent_inquire(int apicid)
{
}
static int xen_cpu_present_to_apicid(int cpu)
{
if (cpu_present(cpu))
return cpu_data(cpu).apicid;
else
return BAD_APICID;
}
static struct apic xen_pv_apic = {
.name = "Xen PV",
.probe = xen_apic_probe_pv,
.acpi_madt_oem_check = xen_madt_oem_check,
.apic_id_valid = xen_id_always_valid,
.apic_id_registered = xen_id_always_registered,
/* .irq_delivery_mode - used in native_compose_msi_msg only */
/* .irq_dest_mode - used in native_compose_msi_msg only */
.target_cpus = default_target_cpus,
.disable_esr = 0,
/* .dest_logical - default_send_IPI_ use it but we use our own. */
.check_apicid_used = default_check_apicid_used, /* Used on 32-bit */
.vector_allocation_domain = flat_vector_allocation_domain,
.init_apic_ldr = xen_noop, /* setup_local_APIC calls it */
.ioapic_phys_id_map = default_ioapic_phys_id_map, /* Used on 32-bit */
.setup_apic_routing = NULL,
.cpu_present_to_apicid = xen_cpu_present_to_apicid,
.apicid_to_cpu_present = physid_set_mask_of_physid, /* Used on 32-bit */
.check_phys_apicid_present = default_check_phys_apicid_present, /* smp_sanity_check needs it */
.phys_pkg_id = xen_phys_pkg_id, /* detect_ht */
.get_apic_id = xen_get_apic_id,
.set_apic_id = xen_set_apic_id, /* Can be NULL on 32-bit. */
.cpu_mask_to_apicid_and = flat_cpu_mask_to_apicid_and,
#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
/* .wait_for_init_deassert- used by AP bootup - smp_callin which we don't use */
.inquire_remote_apic = xen_silent_inquire,
.read = xen_apic_read,
.write = xen_apic_write,
.eoi_write = xen_apic_write,
.icr_read = xen_apic_icr_read,
.icr_write = xen_apic_icr_write,
.wait_icr_idle = xen_noop,
.safe_wait_icr_idle = xen_safe_apic_wait_icr_idle,
#ifdef CONFIG_X86_32
/* generic_processor_info and setup_local_APIC. */
.x86_32_early_logical_apicid = xen_x86_32_early_logical_apicid,
#endif
};
static void __init xen_apic_check(void)
{
if (apic == &xen_pv_apic)
return;
pr_info("Switched APIC routing from %s to %s.\n", apic->name,
xen_pv_apic.name);
apic = &xen_pv_apic;
}
void __init xen_init_apic(void)
{
x86_io_apic_ops.read = xen_io_apic_read;
/* On PV guests the APIC CPUID bit is disabled so none of the
* routines end up executing. */
if (!xen_initial_domain())
apic = &xen_pv_apic;
x86_platform.apic_post_init = xen_apic_check;
}
apic_driver(xen_pv_apic);