// SPDX-License-Identifier: GPL-2.0-only
#define pr_fmt(fmt) "APIC: " fmt

#include <asm/apic.h>

#include "local.h"

/*
 * Use DEFINE_STATIC_CALL_NULL() to avoid having to provide stub functions
 * for each callback. The callbacks are setup during boot and all except
 * wait_icr_idle() must be initialized before usage. The IPI wrappers
 * use static_call() and not static_call_cond() to catch any fails.
 */
#define DEFINE_APIC_CALL(__cb)						\
	DEFINE_STATIC_CALL_NULL(apic_call_##__cb, *apic->__cb)

DEFINE_APIC_CALL(eoi);
DEFINE_APIC_CALL(native_eoi);
DEFINE_APIC_CALL(icr_read);
DEFINE_APIC_CALL(icr_write);
DEFINE_APIC_CALL(read);
DEFINE_APIC_CALL(send_IPI);
DEFINE_APIC_CALL(send_IPI_mask);
DEFINE_APIC_CALL(send_IPI_mask_allbutself);
DEFINE_APIC_CALL(send_IPI_allbutself);
DEFINE_APIC_CALL(send_IPI_all);
DEFINE_APIC_CALL(send_IPI_self);
DEFINE_APIC_CALL(wait_icr_idle);
DEFINE_APIC_CALL(wakeup_secondary_cpu);
DEFINE_APIC_CALL(wakeup_secondary_cpu_64);
DEFINE_APIC_CALL(write);

EXPORT_STATIC_CALL_TRAMP_GPL(apic_call_send_IPI_mask);
EXPORT_STATIC_CALL_TRAMP_GPL(apic_call_send_IPI_self);

/* The container for function call overrides */
struct apic_override __x86_apic_override __initdata;

#define apply_override(__cb)					\
	if (__x86_apic_override.__cb)				\
		apic->__cb = __x86_apic_override.__cb

static __init void restore_override_callbacks(void)
{
	apply_override(eoi);
	apply_override(native_eoi);
	apply_override(write);
	apply_override(read);
	apply_override(send_IPI);
	apply_override(send_IPI_mask);
	apply_override(send_IPI_mask_allbutself);
	apply_override(send_IPI_allbutself);
	apply_override(send_IPI_all);
	apply_override(send_IPI_self);
	apply_override(icr_read);
	apply_override(icr_write);
	apply_override(wakeup_secondary_cpu);
	apply_override(wakeup_secondary_cpu_64);
}

#define update_call(__cb)					\
	static_call_update(apic_call_##__cb, *apic->__cb)

static __init void update_static_calls(void)
{
	update_call(eoi);
	update_call(native_eoi);
	update_call(write);
	update_call(read);
	update_call(send_IPI);
	update_call(send_IPI_mask);
	update_call(send_IPI_mask_allbutself);
	update_call(send_IPI_allbutself);
	update_call(send_IPI_all);
	update_call(send_IPI_self);
	update_call(icr_read);
	update_call(icr_write);
	update_call(wait_icr_idle);
	update_call(wakeup_secondary_cpu);
	update_call(wakeup_secondary_cpu_64);
}

void __init apic_setup_apic_calls(void)
{
	/* Ensure that the default APIC has native_eoi populated */
	apic->native_eoi = apic->eoi;
	update_static_calls();
	pr_info("Static calls initialized\n");
}

void __init apic_install_driver(struct apic *driver)
{
	if (apic == driver)
		return;

	apic = driver;

	if (IS_ENABLED(CONFIG_X86_X2APIC) && apic->x2apic_set_max_apicid)
		apic->max_apic_id = x2apic_max_apicid;

	/* Copy the original eoi() callback as KVM/HyperV might overwrite it */
	if (!apic->native_eoi)
		apic->native_eoi = apic->eoi;

	/* Apply any already installed callback overrides */
	restore_override_callbacks();
	update_static_calls();

	pr_info("Switched APIC routing to: %s\n", driver->name);
}