162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci#include <linux/init.h>
362306a36Sopenharmony_ci#include <linux/thread_info.h>
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include <asm/x86_init.h>
662306a36Sopenharmony_ci#include <asm/apic.h>
762306a36Sopenharmony_ci#include <asm/io_apic.h>
862306a36Sopenharmony_ci#include <asm/xen/hypercall.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <xen/xen.h>
1162306a36Sopenharmony_ci#include <xen/interface/physdev.h>
1262306a36Sopenharmony_ci#include "xen-ops.h"
1362306a36Sopenharmony_ci#include "pmu.h"
1462306a36Sopenharmony_ci#include "smp.h"
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_cistatic unsigned int xen_io_apic_read(unsigned apic, unsigned reg)
1762306a36Sopenharmony_ci{
1862306a36Sopenharmony_ci	struct physdev_apic apic_op;
1962306a36Sopenharmony_ci	int ret;
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	apic_op.apic_physbase = mpc_ioapic_addr(apic);
2262306a36Sopenharmony_ci	apic_op.reg = reg;
2362306a36Sopenharmony_ci	ret = HYPERVISOR_physdev_op(PHYSDEVOP_apic_read, &apic_op);
2462306a36Sopenharmony_ci	if (!ret)
2562306a36Sopenharmony_ci		return apic_op.value;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci	/* fallback to return an emulated IO_APIC values */
2862306a36Sopenharmony_ci	if (reg == 0x1)
2962306a36Sopenharmony_ci		return 0x00170020;
3062306a36Sopenharmony_ci	else if (reg == 0x0)
3162306a36Sopenharmony_ci		return apic << 24;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	return 0xfd;
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_cistatic u32 xen_set_apic_id(unsigned int x)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	WARN_ON(1);
3962306a36Sopenharmony_ci	return x;
4062306a36Sopenharmony_ci}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_cistatic unsigned int xen_get_apic_id(unsigned long x)
4362306a36Sopenharmony_ci{
4462306a36Sopenharmony_ci	return ((x)>>24) & 0xFFu;
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_cistatic u32 xen_apic_read(u32 reg)
4862306a36Sopenharmony_ci{
4962306a36Sopenharmony_ci	struct xen_platform_op op = {
5062306a36Sopenharmony_ci		.cmd = XENPF_get_cpuinfo,
5162306a36Sopenharmony_ci		.interface_version = XENPF_INTERFACE_VERSION,
5262306a36Sopenharmony_ci		.u.pcpu_info.xen_cpuid = 0,
5362306a36Sopenharmony_ci	};
5462306a36Sopenharmony_ci	int ret;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	/* Shouldn't need this as APIC is turned off for PV, and we only
5762306a36Sopenharmony_ci	 * get called on the bootup processor. But just in case. */
5862306a36Sopenharmony_ci	if (!xen_initial_domain() || smp_processor_id())
5962306a36Sopenharmony_ci		return 0;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (reg == APIC_LVR)
6262306a36Sopenharmony_ci		return 0x14;
6362306a36Sopenharmony_ci	if (reg != APIC_ID)
6462306a36Sopenharmony_ci		return 0;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	ret = HYPERVISOR_platform_op(&op);
6762306a36Sopenharmony_ci	if (ret)
6862306a36Sopenharmony_ci		op.u.pcpu_info.apic_id = BAD_APICID;
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	return op.u.pcpu_info.apic_id << 24;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic void xen_apic_write(u32 reg, u32 val)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	if (reg == APIC_LVTPC) {
7662306a36Sopenharmony_ci		(void)pmu_apic_update(reg);
7762306a36Sopenharmony_ci		return;
7862306a36Sopenharmony_ci	}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	/* Warn to see if there's any stray references */
8162306a36Sopenharmony_ci	WARN(1,"register: %x, value: %x\n", reg, val);
8262306a36Sopenharmony_ci}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_cistatic void xen_apic_eoi(void)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	WARN_ON_ONCE(1);
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_cistatic u64 xen_apic_icr_read(void)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	return 0;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic void xen_apic_icr_write(u32 low, u32 id)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	/* Warn to see if there's any stray references */
9762306a36Sopenharmony_ci	WARN_ON(1);
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic int xen_apic_probe_pv(void)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	if (xen_pv_domain())
10362306a36Sopenharmony_ci		return 1;
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	return 0;
10662306a36Sopenharmony_ci}
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_cistatic int xen_madt_oem_check(char *oem_id, char *oem_table_id)
10962306a36Sopenharmony_ci{
11062306a36Sopenharmony_ci	return xen_pv_domain();
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_cistatic int xen_phys_pkg_id(int initial_apic_id, int index_msb)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	return initial_apic_id >> index_msb;
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cistatic int xen_cpu_present_to_apicid(int cpu)
11962306a36Sopenharmony_ci{
12062306a36Sopenharmony_ci	if (cpu_present(cpu))
12162306a36Sopenharmony_ci		return cpu_data(cpu).apicid;
12262306a36Sopenharmony_ci	else
12362306a36Sopenharmony_ci		return BAD_APICID;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic struct apic xen_pv_apic __ro_after_init = {
12762306a36Sopenharmony_ci	.name				= "Xen PV",
12862306a36Sopenharmony_ci	.probe				= xen_apic_probe_pv,
12962306a36Sopenharmony_ci	.acpi_madt_oem_check		= xen_madt_oem_check,
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	/* .delivery_mode and .dest_mode_logical not used by XENPV */
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	.disable_esr			= 0,
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	.cpu_present_to_apicid		= xen_cpu_present_to_apicid,
13662306a36Sopenharmony_ci	.phys_pkg_id			= xen_phys_pkg_id, /* detect_ht */
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	.max_apic_id			= UINT_MAX,
13962306a36Sopenharmony_ci	.get_apic_id			= xen_get_apic_id,
14062306a36Sopenharmony_ci	.set_apic_id			= xen_set_apic_id,
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	.calc_dest_apicid		= apic_flat_calc_apicid,
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_ci#ifdef CONFIG_SMP
14562306a36Sopenharmony_ci	.send_IPI_mask			= xen_send_IPI_mask,
14662306a36Sopenharmony_ci	.send_IPI_mask_allbutself	= xen_send_IPI_mask_allbutself,
14762306a36Sopenharmony_ci	.send_IPI_allbutself		= xen_send_IPI_allbutself,
14862306a36Sopenharmony_ci	.send_IPI_all			= xen_send_IPI_all,
14962306a36Sopenharmony_ci	.send_IPI_self			= xen_send_IPI_self,
15062306a36Sopenharmony_ci#endif
15162306a36Sopenharmony_ci	.read				= xen_apic_read,
15262306a36Sopenharmony_ci	.write				= xen_apic_write,
15362306a36Sopenharmony_ci	.eoi				= xen_apic_eoi,
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	.icr_read			= xen_apic_icr_read,
15662306a36Sopenharmony_ci	.icr_write			= xen_apic_icr_write,
15762306a36Sopenharmony_ci};
15862306a36Sopenharmony_ciapic_driver(xen_pv_apic);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_civoid __init xen_init_apic(void)
16162306a36Sopenharmony_ci{
16262306a36Sopenharmony_ci	x86_apic_ops.io_apic_read = xen_io_apic_read;
16362306a36Sopenharmony_ci}
164