Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+28
View File
@@ -0,0 +1,28 @@
# SPDX-License-Identifier: GPL-2.0
#
# Makefile for local APIC drivers and for the IO-APIC code
#
# Leads to non-deterministic coverage that is not a function of syscall inputs.
# In particular, smp_apic_timer_interrupt() is called in random places.
KCOV_INSTRUMENT := n
obj-$(CONFIG_X86_LOCAL_APIC) += apic.o apic_common.o apic_noop.o ipi.o vector.o init.o
obj-y += hw_nmi.o
obj-$(CONFIG_X86_IO_APIC) += io_apic.o
obj-$(CONFIG_PCI_MSI) += msi.o
obj-$(CONFIG_SMP) += ipi.o
ifeq ($(CONFIG_X86_64),y)
# APIC probe will depend on the listing order here
obj-$(CONFIG_X86_NUMACHIP) += apic_numachip.o
obj-$(CONFIG_X86_UV) += x2apic_uv_x.o
obj-$(CONFIG_AMD_SECURE_AVIC) += x2apic_savic.o
obj-$(CONFIG_X86_X2APIC) += x2apic_phys.o
obj-$(CONFIG_X86_X2APIC) += x2apic_cluster.o
obj-y += apic_flat_64.o
endif
# For 32bit, probe_32 need to be listed last
obj-$(CONFIG_X86_LOCAL_APIC) += probe_$(BITS).o
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,42 @@
/*
* Common functions shared between the various APIC flavours
*
* SPDX-License-Identifier: GPL-2.0
*/
#include <linux/irq.h>
#include <linux/kvm_types.h>
#include <asm/apic.h>
#include "local.h"
u32 apic_default_calc_apicid(unsigned int cpu)
{
return per_cpu(x86_cpu_to_apicid, cpu);
}
u32 apic_flat_calc_apicid(unsigned int cpu)
{
return 1U << cpu;
}
u32 default_cpu_present_to_apicid(int mps_cpu)
{
if (mps_cpu < nr_cpu_ids && cpu_present(mps_cpu))
return (int)per_cpu(x86_cpu_to_apicid, mps_cpu);
else
return BAD_APICID;
}
/*
* Set up the logical destination ID when the APIC operates in logical
* destination mode.
*/
void default_init_apic_ldr(void)
{
unsigned long val;
apic_write(APIC_DFR, APIC_DFR_FLAT);
val = apic_read(APIC_LDR) & ~APIC_LDR_MASK;
val |= SET_APIC_LOGICAL_ID(1UL << smp_processor_id());
apic_write(APIC_LDR, val);
}
@@ -0,0 +1,68 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2004 James Cleverdon, IBM.
*
* Flat APIC subarch code.
*
* Hacked for x86-64 by James Cleverdon from i386 architecture code by
* Martin Bligh, Andi Kleen, James Bottomley, John Stultz, and
* James Cleverdon.
*/
#include <linux/export.h>
#include <asm/apic.h>
#include "local.h"
static u32 physflat_get_apic_id(u32 x)
{
return (x >> 24) & 0xFF;
}
static int physflat_probe(void)
{
return 1;
}
static int physflat_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
{
return 1;
}
static struct apic apic_physflat __ro_after_init = {
.name = "physical flat",
.probe = physflat_probe,
.acpi_madt_oem_check = physflat_acpi_madt_oem_check,
.dest_mode_logical = false,
.disable_esr = 0,
.cpu_present_to_apicid = default_cpu_present_to_apicid,
.max_apic_id = 0xFE,
.get_apic_id = physflat_get_apic_id,
.calc_dest_apicid = apic_default_calc_apicid,
.send_IPI = default_send_IPI_single_phys,
.send_IPI_mask = default_send_IPI_mask_sequence_phys,
.send_IPI_mask_allbutself = default_send_IPI_mask_allbutself_phys,
.send_IPI_allbutself = default_send_IPI_allbutself,
.send_IPI_all = default_send_IPI_all,
.send_IPI_self = default_send_IPI_self,
.nmi_to_offline_cpu = true,
.read = native_apic_mem_read,
.write = native_apic_mem_write,
.eoi = native_apic_mem_eoi,
.icr_read = native_apic_icr_read,
.icr_write = native_apic_icr_write,
.wait_icr_idle = apic_mem_wait_icr_idle,
.safe_wait_icr_idle = apic_mem_wait_icr_idle_timeout,
};
apic_driver(apic_physflat);
struct apic *apic __ro_after_init = &apic_physflat;
EXPORT_SYMBOL_GPL(apic);
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: GPL-2.0
/*
* NOOP APIC driver.
*
* Does almost nothing and should be substituted by a real apic driver via
* probe routine.
*
* Though in case if apic is disabled (for some reason) we try
* to not uglify the caller's code and allow to call (some) apic routines
* like self-ipi, etc...
*
* FIXME: Remove this gunk. The above argument which was intentionally left
* in place is silly to begin with because none of the callbacks except for
* APIC::read/write() have a WARN_ON_ONCE() in them. Sigh...
*/
#include <linux/cpumask.h>
#include <linux/thread_info.h>
#include <asm/apic.h>
#include "local.h"
static void noop_send_IPI(int cpu, int vector) { }
static void noop_send_IPI_mask(const struct cpumask *cpumask, int vector) { }
static void noop_send_IPI_mask_allbutself(const struct cpumask *cpumask, int vector) { }
static void noop_send_IPI_allbutself(int vector) { }
static void noop_send_IPI_all(int vector) { }
static void noop_send_IPI_self(int vector) { }
static void noop_apic_icr_write(u32 low, u32 id) { }
static int noop_wakeup_secondary_cpu(u32 apicid, unsigned long start_eip,
unsigned int cpu)
{
return -1;
}
static u64 noop_apic_icr_read(void) { return 0; }
static u32 noop_get_apic_id(u32 apicid) { return 0; }
static void noop_apic_eoi(void) { }
static u32 noop_apic_read(u32 reg)
{
WARN_ON_ONCE(boot_cpu_has(X86_FEATURE_APIC) && !apic_is_disabled);
return 0;
}
static void noop_apic_write(u32 reg, u32 val)
{
WARN_ON_ONCE(boot_cpu_has(X86_FEATURE_APIC) && !apic_is_disabled);
}
struct apic apic_noop __ro_after_init = {
.name = "noop",
.dest_mode_logical = true,
.disable_esr = 0,
.cpu_present_to_apicid = default_cpu_present_to_apicid,
.max_apic_id = 0xFE,
.get_apic_id = noop_get_apic_id,
.calc_dest_apicid = apic_flat_calc_apicid,
.send_IPI = noop_send_IPI,
.send_IPI_mask = noop_send_IPI_mask,
.send_IPI_mask_allbutself = noop_send_IPI_mask_allbutself,
.send_IPI_allbutself = noop_send_IPI_allbutself,
.send_IPI_all = noop_send_IPI_all,
.send_IPI_self = noop_send_IPI_self,
.wakeup_secondary_cpu = noop_wakeup_secondary_cpu,
.read = noop_apic_read,
.write = noop_apic_write,
.eoi = noop_apic_eoi,
.icr_read = noop_apic_icr_read,
.icr_write = noop_apic_icr_write,
};
@@ -0,0 +1,272 @@
/*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*
* Numascale NumaConnect-Specific APIC Code
*
* Copyright (C) 2011 Numascale AS. All rights reserved.
*
* Send feedback to <support@numascale.com>
*
*/
#include <linux/types.h>
#include <linux/init.h>
#include <linux/pgtable.h>
#include <asm/msr.h>
#include <asm/numachip/numachip.h>
#include <asm/numachip/numachip_csr.h>
#include "local.h"
u8 numachip_system __read_mostly;
static const struct apic apic_numachip1;
static const struct apic apic_numachip2;
static void (*numachip_apic_icr_write)(int apicid, unsigned int val) __read_mostly;
static u32 numachip1_get_apic_id(u32 x)
{
unsigned long value;
unsigned int id = (x >> 24) & 0xff;
if (static_cpu_has(X86_FEATURE_NODEID_MSR)) {
rdmsrq(MSR_FAM10H_NODE_ID, value);
id |= (value << 2) & 0xff00;
}
return id;
}
static u32 numachip2_get_apic_id(u32 x)
{
u64 mcfg;
rdmsrq(MSR_FAM10H_MMIO_CONF_BASE, mcfg);
return ((mcfg >> (28 - 8)) & 0xfff00) | (x >> 24);
}
static void numachip1_apic_icr_write(int apicid, unsigned int val)
{
write_lcsr(CSR_G3_EXT_IRQ_GEN, (apicid << 16) | val);
}
static void numachip2_apic_icr_write(int apicid, unsigned int val)
{
numachip2_write32_lcsr(NUMACHIP2_APIC_ICR, (apicid << 12) | val);
}
static int numachip_wakeup_secondary(u32 phys_apicid, unsigned long start_rip, unsigned int cpu)
{
numachip_apic_icr_write(phys_apicid, APIC_DM_INIT);
numachip_apic_icr_write(phys_apicid, APIC_DM_STARTUP |
(start_rip >> 12));
return 0;
}
static void numachip_send_IPI_one(int cpu, int vector)
{
int local_apicid, apicid = per_cpu(x86_cpu_to_apicid, cpu);
unsigned int dmode;
preempt_disable();
local_apicid = __this_cpu_read(x86_cpu_to_apicid);
/* Send via local APIC where non-local part matches */
if (!((apicid ^ local_apicid) >> NUMACHIP_LAPIC_BITS)) {
unsigned long flags;
local_irq_save(flags);
__default_send_IPI_dest_field(apicid, vector,
APIC_DEST_PHYSICAL);
local_irq_restore(flags);
preempt_enable();
return;
}
preempt_enable();
dmode = (vector == NMI_VECTOR) ? APIC_DM_NMI : APIC_DM_FIXED;
numachip_apic_icr_write(apicid, dmode | vector);
}
static void numachip_send_IPI_mask(const struct cpumask *mask, int vector)
{
unsigned int cpu;
for_each_cpu(cpu, mask)
numachip_send_IPI_one(cpu, vector);
}
static void numachip_send_IPI_mask_allbutself(const struct cpumask *mask,
int vector)
{
unsigned int this_cpu = smp_processor_id();
unsigned int cpu;
for_each_cpu(cpu, mask) {
if (cpu != this_cpu)
numachip_send_IPI_one(cpu, vector);
}
}
static void numachip_send_IPI_allbutself(int vector)
{
unsigned int this_cpu = smp_processor_id();
unsigned int cpu;
for_each_online_cpu(cpu) {
if (cpu != this_cpu)
numachip_send_IPI_one(cpu, vector);
}
}
static void numachip_send_IPI_all(int vector)
{
numachip_send_IPI_mask(cpu_online_mask, vector);
}
static void numachip_send_IPI_self(int vector)
{
apic_write(APIC_SELF_IPI, vector);
}
static int __init numachip1_probe(void)
{
return apic == &apic_numachip1;
}
static int __init numachip2_probe(void)
{
return apic == &apic_numachip2;
}
static void fixup_cpu_id(struct cpuinfo_x86 *c, int node)
{
u64 val;
u32 nodes = 1;
c->topo.llc_id = node;
/* Account for nodes per socket in multi-core-module processors */
if (boot_cpu_has(X86_FEATURE_NODEID_MSR)) {
rdmsrq(MSR_FAM10H_NODE_ID, val);
nodes = ((val >> 3) & 7) + 1;
}
c->topo.pkg_id = node / nodes;
}
static int __init numachip_system_init(void)
{
/* Map the LCSR area and set up the apic_icr_write function */
switch (numachip_system) {
case 1:
init_extra_mapping_uc(NUMACHIP_LCSR_BASE, NUMACHIP_LCSR_SIZE);
numachip_apic_icr_write = numachip1_apic_icr_write;
break;
case 2:
init_extra_mapping_uc(NUMACHIP2_LCSR_BASE, NUMACHIP2_LCSR_SIZE);
numachip_apic_icr_write = numachip2_apic_icr_write;
break;
default:
return 0;
}
x86_cpuinit.fixup_cpu_id = fixup_cpu_id;
x86_init.pci.arch_init = pci_numachip_init;
return 0;
}
early_initcall(numachip_system_init);
static int numachip1_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
{
if ((strncmp(oem_id, "NUMASC", 6) != 0) ||
(strncmp(oem_table_id, "NCONNECT", 8) != 0))
return 0;
numachip_system = 1;
return 1;
}
static int numachip2_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
{
if ((strncmp(oem_id, "NUMASC", 6) != 0) ||
(strncmp(oem_table_id, "NCONECT2", 8) != 0))
return 0;
numachip_system = 2;
return 1;
}
static const struct apic apic_numachip1 __refconst = {
.name = "NumaConnect system",
.probe = numachip1_probe,
.acpi_madt_oem_check = numachip1_acpi_madt_oem_check,
.dest_mode_logical = false,
.disable_esr = 0,
.cpu_present_to_apicid = default_cpu_present_to_apicid,
.max_apic_id = UINT_MAX,
.get_apic_id = numachip1_get_apic_id,
.calc_dest_apicid = apic_default_calc_apicid,
.send_IPI = numachip_send_IPI_one,
.send_IPI_mask = numachip_send_IPI_mask,
.send_IPI_mask_allbutself = numachip_send_IPI_mask_allbutself,
.send_IPI_allbutself = numachip_send_IPI_allbutself,
.send_IPI_all = numachip_send_IPI_all,
.send_IPI_self = numachip_send_IPI_self,
.wakeup_secondary_cpu = numachip_wakeup_secondary,
.read = native_apic_mem_read,
.write = native_apic_mem_write,
.eoi = native_apic_mem_eoi,
.icr_read = native_apic_icr_read,
.icr_write = native_apic_icr_write,
};
apic_driver(apic_numachip1);
static const struct apic apic_numachip2 __refconst = {
.name = "NumaConnect2 system",
.probe = numachip2_probe,
.acpi_madt_oem_check = numachip2_acpi_madt_oem_check,
.dest_mode_logical = false,
.disable_esr = 0,
.cpu_present_to_apicid = default_cpu_present_to_apicid,
.max_apic_id = UINT_MAX,
.get_apic_id = numachip2_get_apic_id,
.calc_dest_apicid = apic_default_calc_apicid,
.send_IPI = numachip_send_IPI_one,
.send_IPI_mask = numachip_send_IPI_mask,
.send_IPI_mask_allbutself = numachip_send_IPI_mask_allbutself,
.send_IPI_allbutself = numachip_send_IPI_allbutself,
.send_IPI_all = numachip_send_IPI_all,
.send_IPI_self = numachip_send_IPI_self,
.wakeup_secondary_cpu = numachip_wakeup_secondary,
.read = native_apic_mem_read,
.write = native_apic_mem_write,
.eoi = native_apic_mem_eoi,
.icr_read = native_apic_icr_read,
.icr_write = native_apic_icr_write,
};
apic_driver(apic_numachip2);
+61
View File
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: GPL-2.0
/*
* HW NMI watchdog support
*
* started by Don Zickus, Copyright (C) 2010 Red Hat, Inc.
*
* Arch specific calls to support NMI watchdog
*
* Bits copied from original nmi.c file
*
*/
#include <linux/thread_info.h>
#include <asm/apic.h>
#include <asm/nmi.h>
#include <linux/cpumask.h>
#include <linux/kdebug.h>
#include <linux/notifier.h>
#include <linux/kprobes.h>
#include <linux/nmi.h>
#include <linux/init.h>
#include <linux/delay.h>
#include "local.h"
#ifdef CONFIG_HARDLOCKUP_DETECTOR_PERF
u64 hw_nmi_get_sample_period(int watchdog_thresh)
{
return (u64)(cpu_khz) * 1000 * watchdog_thresh;
}
#endif
#ifdef arch_trigger_cpumask_backtrace
static void nmi_raise_cpu_backtrace(cpumask_t *mask)
{
__apic_send_IPI_mask(mask, NMI_VECTOR);
}
void arch_trigger_cpumask_backtrace(const cpumask_t *mask, int exclude_cpu)
{
nmi_trigger_cpumask_backtrace(mask, exclude_cpu,
nmi_raise_cpu_backtrace);
}
static int nmi_cpu_backtrace_handler(unsigned int cmd, struct pt_regs *regs)
{
if (nmi_cpu_backtrace(regs))
return NMI_HANDLED;
return NMI_DONE;
}
NOKPROBE_SYMBOL(nmi_cpu_backtrace_handler);
static int __init register_nmi_cpu_backtrace_handler(void)
{
register_nmi_handler(NMI_LOCAL, nmi_cpu_backtrace_handler,
0, "arch_bt");
return 0;
}
early_initcall(register_nmi_cpu_backtrace_handler);
#endif
+110
View File
@@ -0,0 +1,110 @@
// 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);
}
File diff suppressed because it is too large Load Diff
+291
View File
@@ -0,0 +1,291 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/cpumask.h>
#include <linux/delay.h>
#include <linux/smp.h>
#include <linux/string_choices.h>
#include <asm/io_apic.h>
#include "local.h"
DEFINE_STATIC_KEY_FALSE(apic_use_ipi_shorthand);
#ifdef CONFIG_SMP
static int apic_ipi_shorthand_off __ro_after_init;
static __init int apic_ipi_shorthand(char *str)
{
get_option(&str, &apic_ipi_shorthand_off);
return 1;
}
__setup("no_ipi_broadcast=", apic_ipi_shorthand);
static int __init print_ipi_mode(void)
{
pr_info("IPI shorthand broadcast: %s\n",
str_disabled_enabled(apic_ipi_shorthand_off));
return 0;
}
late_initcall(print_ipi_mode);
void apic_smt_update(void)
{
/*
* Do not switch to broadcast mode if:
* - Disabled on the command line
* - Only a single CPU is online
* - Not all present CPUs have been at least booted once
*
* The latter is important as the local APIC might be in some
* random state and a broadcast might cause havoc. That's
* especially true for NMI broadcasting.
*/
if (apic_ipi_shorthand_off || num_online_cpus() == 1 ||
!cpumask_equal(cpu_present_mask, &cpus_booted_once_mask)) {
static_branch_disable(&apic_use_ipi_shorthand);
} else {
static_branch_enable(&apic_use_ipi_shorthand);
}
}
void apic_send_IPI_allbutself(unsigned int vector)
{
if (num_online_cpus() < 2)
return;
if (static_branch_likely(&apic_use_ipi_shorthand))
__apic_send_IPI_allbutself(vector);
else
__apic_send_IPI_mask_allbutself(cpu_online_mask, vector);
}
/*
* Send a 'reschedule' IPI to another CPU. It goes straight through and
* wastes no time serializing anything. Worst case is that we lose a
* reschedule ...
*/
void native_smp_send_reschedule(int cpu)
{
if (unlikely(cpu_is_offline(cpu))) {
WARN(1, "sched: Unexpected reschedule of offline CPU#%d!\n", cpu);
return;
}
__apic_send_IPI(cpu, RESCHEDULE_VECTOR);
}
void native_send_call_func_single_ipi(int cpu)
{
__apic_send_IPI(cpu, CALL_FUNCTION_SINGLE_VECTOR);
}
void native_send_call_func_ipi(const struct cpumask *mask)
{
if (static_branch_likely(&apic_use_ipi_shorthand)) {
unsigned int cpu = smp_processor_id();
if (!cpumask_or_equal(mask, cpumask_of(cpu), cpu_online_mask))
goto sendmask;
if (cpumask_test_cpu(cpu, mask))
__apic_send_IPI_all(CALL_FUNCTION_VECTOR);
else if (num_online_cpus() > 1)
__apic_send_IPI_allbutself(CALL_FUNCTION_VECTOR);
return;
}
sendmask:
__apic_send_IPI_mask(mask, CALL_FUNCTION_VECTOR);
}
void apic_send_nmi_to_offline_cpu(unsigned int cpu)
{
if (WARN_ON_ONCE(!apic->nmi_to_offline_cpu))
return;
if (WARN_ON_ONCE(!cpumask_test_cpu(cpu, &cpus_booted_once_mask)))
return;
apic->send_IPI(cpu, NMI_VECTOR);
}
#endif /* CONFIG_SMP */
static inline int __prepare_ICR2(unsigned int mask)
{
return SET_XAPIC_DEST_FIELD(mask);
}
u32 apic_mem_wait_icr_idle_timeout(void)
{
int cnt;
for (cnt = 0; cnt < 1000; cnt++) {
if (!(apic_read(APIC_ICR) & APIC_ICR_BUSY))
return 0;
irq_stat_inc_and_enable(IRQ_COUNT_ICR_READ_RETRY);
udelay(100);
}
return APIC_ICR_BUSY;
}
void apic_mem_wait_icr_idle(void)
{
while (native_apic_mem_read(APIC_ICR) & APIC_ICR_BUSY)
cpu_relax();
}
/*
* This is safe against interruption because it only writes the lower 32
* bits of the APIC_ICR register. The destination field is ignored for
* short hand IPIs.
*
* wait_icr_idle()
* write(ICR2, dest)
* NMI
* wait_icr_idle()
* write(ICR)
* wait_icr_idle()
* write(ICR)
*
* This function does not need to disable interrupts as there is no ICR2
* interaction. The memory write is direct except when the machine is
* affected by the 11AP Pentium erratum, which turns the plain write into
* an XCHG operation.
*/
static void __default_send_IPI_shortcut(unsigned int shortcut, int vector)
{
/*
* Wait for the previous ICR command to complete. Use
* safe_apic_wait_icr_idle() for the NMI vector as there have been
* issues where otherwise the system hangs when the panic CPU tries
* to stop the others before launching the kdump kernel.
*/
if (unlikely(vector == NMI_VECTOR))
apic_mem_wait_icr_idle_timeout();
else
apic_mem_wait_icr_idle();
/* Destination field (ICR2) and the destination mode are ignored */
native_apic_mem_write(APIC_ICR, __prepare_ICR(shortcut, vector, 0));
}
/*
* This is used to send an IPI with no shorthand notation (the destination is
* specified in bits 56 to 63 of the ICR).
*/
void __default_send_IPI_dest_field(unsigned int dest_mask, int vector,
unsigned int dest_mode)
{
/* See comment in __default_send_IPI_shortcut() */
if (unlikely(vector == NMI_VECTOR))
apic_mem_wait_icr_idle_timeout();
else
apic_mem_wait_icr_idle();
/* Set the IPI destination field in the ICR */
native_apic_mem_write(APIC_ICR2, __prepare_ICR2(dest_mask));
/* Send it with the proper destination mode */
native_apic_mem_write(APIC_ICR, __prepare_ICR(0, vector, dest_mode));
}
void default_send_IPI_single_phys(int cpu, int vector)
{
unsigned long flags;
local_irq_save(flags);
__default_send_IPI_dest_field(per_cpu(x86_cpu_to_apicid, cpu),
vector, APIC_DEST_PHYSICAL);
local_irq_restore(flags);
}
void default_send_IPI_mask_sequence_phys(const struct cpumask *mask, int vector)
{
unsigned long flags;
unsigned long cpu;
local_irq_save(flags);
for_each_cpu(cpu, mask) {
__default_send_IPI_dest_field(per_cpu(x86_cpu_to_apicid,
cpu), vector, APIC_DEST_PHYSICAL);
}
local_irq_restore(flags);
}
void default_send_IPI_mask_allbutself_phys(const struct cpumask *mask,
int vector)
{
unsigned int cpu, this_cpu = smp_processor_id();
unsigned long flags;
local_irq_save(flags);
for_each_cpu(cpu, mask) {
if (cpu == this_cpu)
continue;
__default_send_IPI_dest_field(per_cpu(x86_cpu_to_apicid,
cpu), vector, APIC_DEST_PHYSICAL);
}
local_irq_restore(flags);
}
/*
* Helper function for APICs which insist on cpumasks
*/
void default_send_IPI_single(int cpu, int vector)
{
__apic_send_IPI_mask(cpumask_of(cpu), vector);
}
void default_send_IPI_allbutself(int vector)
{
__default_send_IPI_shortcut(APIC_DEST_ALLBUT, vector);
}
void default_send_IPI_all(int vector)
{
__default_send_IPI_shortcut(APIC_DEST_ALLINC, vector);
}
void default_send_IPI_self(int vector)
{
__default_send_IPI_shortcut(APIC_DEST_SELF, vector);
}
#ifdef CONFIG_X86_32
void default_send_IPI_mask_sequence_logical(const struct cpumask *mask, int vector)
{
unsigned long flags;
unsigned int cpu;
local_irq_save(flags);
for_each_cpu(cpu, mask)
__default_send_IPI_dest_field(1U << cpu, vector, APIC_DEST_LOGICAL);
local_irq_restore(flags);
}
void default_send_IPI_mask_allbutself_logical(const struct cpumask *mask,
int vector)
{
unsigned int cpu, this_cpu = smp_processor_id();
unsigned long flags;
local_irq_save(flags);
for_each_cpu(cpu, mask) {
if (cpu == this_cpu)
continue;
__default_send_IPI_dest_field(1U << cpu, vector, APIC_DEST_LOGICAL);
}
local_irq_restore(flags);
}
void default_send_IPI_mask_logical(const struct cpumask *cpumask, int vector)
{
unsigned long mask = cpumask_bits(cpumask)[0];
unsigned long flags;
if (!mask)
return;
local_irq_save(flags);
WARN_ON(mask & ~cpumask_bits(cpu_online_mask)[0]);
__default_send_IPI_dest_field(mask, vector, APIC_DEST_LOGICAL);
local_irq_restore(flags);
}
#endif
+76
View File
@@ -0,0 +1,76 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Historical copyright notices:
*
* Copyright 2004 James Cleverdon, IBM.
* (c) 1995 Alan Cox, Building #3 <alan@redhat.com>
* (c) 1998-99, 2000 Ingo Molnar <mingo@redhat.com>
* (c) 2002,2003 Andi Kleen, SuSE Labs.
*/
#include <linux/jump_label.h>
#include <asm/irq_vectors.h>
#include <asm/apic.h>
/* X2APIC */
u32 x2apic_get_apic_id(u32 id);
void x2apic_send_IPI_all(int vector);
void x2apic_send_IPI_allbutself(int vector);
void x2apic_send_IPI_self(int vector);
extern u32 x2apic_max_apicid;
/* IPI */
DECLARE_STATIC_KEY_FALSE(apic_use_ipi_shorthand);
static inline unsigned int __prepare_ICR(unsigned int shortcut, int vector,
unsigned int dest)
{
unsigned int icr = shortcut | dest;
switch (vector) {
default:
icr |= APIC_DM_FIXED | vector;
break;
case NMI_VECTOR:
icr |= APIC_DM_NMI;
break;
}
return icr;
}
#ifdef CONFIG_X86_X2APIC
static inline void __x2apic_send_IPI_dest(unsigned int apicid, int vector, unsigned int dest)
{
unsigned long cfg = __prepare_ICR(0, vector, dest);
native_x2apic_icr_write(cfg, apicid);
}
#endif
void default_init_apic_ldr(void);
void apic_mem_wait_icr_idle(void);
u32 apic_mem_wait_icr_idle_timeout(void);
/*
* This is used to send an IPI with no shorthand notation (the destination is
* specified in bits 56 to 63 of the ICR).
*/
void __default_send_IPI_dest_field(unsigned int mask, int vector, unsigned int dest);
void default_send_IPI_single(int cpu, int vector);
void default_send_IPI_single_phys(int cpu, int vector);
void default_send_IPI_mask_sequence_phys(const struct cpumask *mask, int vector);
void default_send_IPI_mask_allbutself_phys(const struct cpumask *mask, int vector);
void default_send_IPI_allbutself(int vector);
void default_send_IPI_all(int vector);
void default_send_IPI_self(int vector);
#ifdef CONFIG_X86_32
void default_send_IPI_mask_sequence_logical(const struct cpumask *mask, int vector);
void default_send_IPI_mask_allbutself_logical(const struct cpumask *mask, int vector);
void default_send_IPI_mask_logical(const struct cpumask *mask, int vector);
#endif
+391
View File
@@ -0,0 +1,391 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Support of MSI, HPET and DMAR interrupts.
*
* Copyright (C) 1997, 1998, 1999, 2000, 2009 Ingo Molnar, Hajnalka Szabo
* Moved from arch/x86/kernel/apic/io_apic.c.
* Jiang Liu <jiang.liu@linux.intel.com>
* Convert to hierarchical irqdomain
*/
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/pci.h>
#include <linux/dmar.h>
#include <linux/hpet.h>
#include <linux/msi.h>
#include <asm/irqdomain.h>
#include <asm/hpet.h>
#include <asm/hw_irq.h>
#include <asm/apic.h>
#include <asm/irq_remapping.h>
#include <asm/xen/hypervisor.h>
struct irq_domain *x86_pci_msi_default_domain __ro_after_init;
static void irq_msi_update_msg(struct irq_data *irqd, struct irq_cfg *cfg)
{
struct msi_msg msg[2] = { [1] = { }, };
__irq_msi_compose_msg(cfg, msg, false);
irq_data_get_irq_chip(irqd)->irq_write_msi_msg(irqd, msg);
}
static int
msi_set_affinity(struct irq_data *irqd, const struct cpumask *mask, bool force)
{
struct irq_cfg old_cfg, *cfg = irqd_cfg(irqd);
struct irq_data *parent = irqd->parent_data;
unsigned int cpu;
int ret;
/* Save the current configuration */
cpu = cpumask_first(irq_data_get_effective_affinity_mask(irqd));
old_cfg = *cfg;
/* Allocate a new target vector */
ret = parent->chip->irq_set_affinity(parent, mask, force);
if (ret < 0 || ret == IRQ_SET_MASK_OK_DONE)
return ret;
/*
* For non-maskable and non-remapped MSI interrupts the migration
* to a different destination CPU and a different vector has to be
* done careful to handle the possible stray interrupt which can be
* caused by the non-atomic update of the address/data pair.
*
* Direct update is possible when:
* - The MSI is maskable (remapped MSI does not use this code path).
* The reservation mode bit is set in this case.
* - The new vector is the same as the old vector
* - The old vector is MANAGED_IRQ_SHUTDOWN_VECTOR (interrupt starts up)
* - The interrupt is not yet started up
* - The new destination CPU is the same as the old destination CPU
*/
if (!irqd_can_reserve(irqd) ||
cfg->vector == old_cfg.vector ||
old_cfg.vector == MANAGED_IRQ_SHUTDOWN_VECTOR ||
!irqd_is_started(irqd) ||
cfg->dest_apicid == old_cfg.dest_apicid) {
irq_msi_update_msg(irqd, cfg);
return ret;
}
/*
* Paranoia: Validate that the interrupt target is the local
* CPU.
*/
if (WARN_ON_ONCE(cpu != smp_processor_id())) {
irq_msi_update_msg(irqd, cfg);
return ret;
}
/*
* Redirect the interrupt to the new vector on the current CPU
* first. This might cause a spurious interrupt on this vector if
* the device raises an interrupt right between this update and the
* update to the final destination CPU.
*
* If the vector is in use then the installed device handler will
* denote it as spurious which is no harm as this is a rare event
* and interrupt handlers have to cope with spurious interrupts
* anyway. If the vector is unused, then it is marked so it won't
* trigger the 'No irq handler for vector' warning in
* common_interrupt().
*
* This requires to hold vector lock to prevent concurrent updates to
* the affected vector.
*/
lock_vector_lock();
/*
* Mark the new target vector on the local CPU if it is currently
* unused. Reuse the VECTOR_RETRIGGERED state which is also used in
* the CPU hotplug path for a similar purpose. This cannot be
* undone here as the current CPU has interrupts disabled and
* cannot handle the interrupt before the whole set_affinity()
* section is done. In the CPU unplug case, the current CPU is
* about to vanish and will not handle any interrupts anymore. The
* vector is cleaned up when the CPU comes online again.
*/
if (IS_ERR_OR_NULL(this_cpu_read(vector_irq[cfg->vector])))
this_cpu_write(vector_irq[cfg->vector], VECTOR_RETRIGGERED);
/* Redirect it to the new vector on the local CPU temporarily */
old_cfg.vector = cfg->vector;
irq_msi_update_msg(irqd, &old_cfg);
/* Now transition it to the target CPU */
irq_msi_update_msg(irqd, cfg);
/*
* All interrupts after this point are now targeted at the new
* vector/CPU.
*
* Drop vector lock before testing whether the temporary assignment
* to the local CPU was hit by an interrupt raised in the device,
* because the retrigger function acquires vector lock again.
*/
unlock_vector_lock();
/*
* Check whether the transition raced with a device interrupt and
* is pending in the local APICs IRR. It is safe to do this outside
* of vector lock as the irq_desc::lock of this interrupt is still
* held and interrupts are disabled: The check is not accessing the
* underlying vector store. It's just checking the local APIC's
* IRR.
*/
if (lapic_vector_set_in_irr(cfg->vector))
irq_data_get_irq_chip(irqd)->irq_retrigger(irqd);
return ret;
}
/**
* pci_dev_has_default_msi_parent_domain - Check whether the device has the default
* MSI parent domain associated
* @dev: Pointer to the PCI device
*/
bool pci_dev_has_default_msi_parent_domain(struct pci_dev *dev)
{
struct irq_domain *domain = dev_get_msi_domain(&dev->dev);
if (!domain)
domain = dev_get_msi_domain(&dev->bus->dev);
if (!domain)
return false;
return domain == x86_vector_domain;
}
/**
* x86_msi_prepare - Setup of msi_alloc_info_t for allocations
* @domain: The domain for which this setup happens
* @dev: The device for which interrupts are allocated
* @nvec: The number of vectors to allocate
* @alloc: The allocation info structure to initialize
*
* This function is to be used for all types of MSI domains above the x86
* vector domain and any intermediates. It is always invoked from the
* top level interrupt domain. The domain specific allocation
* functionality is determined via the @domain's bus token which allows to
* map the X86 specific allocation type.
*/
static int x86_msi_prepare(struct irq_domain *domain, struct device *dev,
int nvec, msi_alloc_info_t *alloc)
{
struct msi_domain_info *info = domain->host_data;
init_irq_alloc_info(alloc, NULL);
switch (info->bus_token) {
case DOMAIN_BUS_PCI_DEVICE_MSI:
alloc->type = X86_IRQ_ALLOC_TYPE_PCI_MSI;
return 0;
case DOMAIN_BUS_PCI_DEVICE_MSIX:
alloc->type = X86_IRQ_ALLOC_TYPE_PCI_MSIX;
return 0;
default:
return -EINVAL;
}
}
/**
* x86_init_dev_msi_info - Domain info setup for MSI domains
* @dev: The device for which the domain should be created
* @domain: The (root) domain providing this callback
* @real_parent: The real parent domain of the to initialize domain
* @info: The domain info for the to initialize domain
*
* This function is to be used for all types of MSI domains above the x86
* vector domain and any intermediates. The domain specific functionality
* is determined via the @real_parent.
*/
static bool x86_init_dev_msi_info(struct device *dev, struct irq_domain *domain,
struct irq_domain *real_parent, struct msi_domain_info *info)
{
const struct msi_parent_ops *pops = real_parent->msi_parent_ops;
/* MSI parent domain specific settings */
switch (real_parent->bus_token) {
case DOMAIN_BUS_ANY:
/* Only the vector domain can have the ANY token */
if (WARN_ON_ONCE(domain != real_parent))
return false;
info->chip->irq_set_affinity = msi_set_affinity;
info->chip->flags |= IRQCHIP_MOVE_DEFERRED;
break;
case DOMAIN_BUS_DMAR:
case DOMAIN_BUS_AMDVI:
break;
default:
WARN_ON_ONCE(1);
return false;
}
/* Is the target supported? */
switch(info->bus_token) {
case DOMAIN_BUS_PCI_DEVICE_MSI:
case DOMAIN_BUS_PCI_DEVICE_MSIX:
break;
default:
WARN_ON_ONCE(1);
return false;
}
/*
* Mask out the domain specific MSI feature flags which are not
* supported by the real parent.
*/
info->flags &= pops->supported_flags;
/* Enforce the required flags */
info->flags |= X86_VECTOR_MSI_FLAGS_REQUIRED;
/* This is always invoked from the top level MSI domain! */
info->ops->msi_prepare = x86_msi_prepare;
info->chip->irq_ack = irq_chip_ack_parent;
info->chip->irq_retrigger = irq_chip_retrigger_hierarchy;
info->chip->flags |= IRQCHIP_SKIP_SET_WAKE |
IRQCHIP_AFFINITY_PRE_STARTUP;
info->handler = handle_edge_irq;
info->handler_name = "edge";
return true;
}
static const struct msi_parent_ops x86_vector_msi_parent_ops = {
.supported_flags = X86_VECTOR_MSI_FLAGS_SUPPORTED,
.init_dev_msi_info = x86_init_dev_msi_info,
};
struct irq_domain * __init native_create_pci_msi_domain(void)
{
if (apic_is_disabled)
return NULL;
x86_vector_domain->flags |= IRQ_DOMAIN_FLAG_MSI_PARENT;
x86_vector_domain->msi_parent_ops = &x86_vector_msi_parent_ops;
return x86_vector_domain;
}
void __init x86_create_pci_msi_domain(void)
{
x86_pci_msi_default_domain = x86_init.irqs.create_pci_msi_domain();
}
/* Keep around for hyperV */
int pci_msi_prepare(struct irq_domain *domain, struct device *dev, int nvec,
msi_alloc_info_t *arg)
{
init_irq_alloc_info(arg, NULL);
if (to_pci_dev(dev)->msix_enabled)
arg->type = X86_IRQ_ALLOC_TYPE_PCI_MSIX;
else
arg->type = X86_IRQ_ALLOC_TYPE_PCI_MSI;
return 0;
}
EXPORT_SYMBOL_GPL(pci_msi_prepare);
#ifdef CONFIG_DMAR_TABLE
/*
* The Intel IOMMU (ab)uses the high bits of the MSI address to contain the
* high bits of the destination APIC ID. This can't be done in the general
* case for MSIs as it would be targeting real memory above 4GiB not the
* APIC.
*/
static void dmar_msi_compose_msg(struct irq_data *data, struct msi_msg *msg)
{
__irq_msi_compose_msg(irqd_cfg(data), msg, true);
}
static void dmar_msi_write_msg(struct irq_data *data, struct msi_msg *msg)
{
dmar_msi_write(data->irq, msg);
}
static struct irq_chip dmar_msi_controller = {
.name = "DMAR-MSI",
.irq_unmask = dmar_msi_unmask,
.irq_mask = dmar_msi_mask,
.irq_ack = irq_chip_ack_parent,
.irq_set_affinity = msi_domain_set_affinity,
.irq_retrigger = irq_chip_retrigger_hierarchy,
.irq_compose_msi_msg = dmar_msi_compose_msg,
.irq_write_msi_msg = dmar_msi_write_msg,
.flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MOVE_DEFERRED |
IRQCHIP_AFFINITY_PRE_STARTUP,
};
static int dmar_msi_init(struct irq_domain *domain,
struct msi_domain_info *info, unsigned int virq,
irq_hw_number_t hwirq, msi_alloc_info_t *arg)
{
irq_domain_set_info(domain, virq, arg->devid, info->chip, NULL,
handle_edge_irq, arg->data, "edge");
return 0;
}
static struct msi_domain_ops dmar_msi_domain_ops = {
.msi_init = dmar_msi_init,
};
static struct msi_domain_info dmar_msi_domain_info = {
.ops = &dmar_msi_domain_ops,
.chip = &dmar_msi_controller,
.flags = MSI_FLAG_USE_DEF_DOM_OPS,
};
static struct irq_domain *dmar_get_irq_domain(void)
{
static struct irq_domain *dmar_domain;
static DEFINE_MUTEX(dmar_lock);
struct fwnode_handle *fn;
mutex_lock(&dmar_lock);
if (dmar_domain)
goto out;
fn = irq_domain_alloc_named_fwnode("DMAR-MSI");
if (fn) {
dmar_domain = msi_create_irq_domain(fn, &dmar_msi_domain_info,
x86_vector_domain);
if (!dmar_domain)
irq_domain_free_fwnode(fn);
}
out:
mutex_unlock(&dmar_lock);
return dmar_domain;
}
int dmar_alloc_hwirq(int id, int node, void *arg)
{
struct irq_domain *domain = dmar_get_irq_domain();
struct irq_alloc_info info;
if (!domain)
return -1;
init_irq_alloc_info(&info, NULL);
info.type = X86_IRQ_ALLOC_TYPE_DMAR;
info.devid = id;
info.hwirq = id;
info.data = arg;
return irq_domain_alloc_irqs(domain, 1, node, &info);
}
void dmar_free_hwirq(int irq)
{
irq_domain_free_irqs(irq, 1);
}
#endif
bool arch_restore_msi_irqs(struct pci_dev *dev)
{
return xen_initdom_restore_msi(dev);
}
+111
View File
@@ -0,0 +1,111 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Default generic APIC driver. This handles up to 8 CPUs.
*
* Copyright 2003 Andi Kleen, SuSE Labs.
*
* Generic x86 APIC driver probe layer.
*/
#include <linux/export.h>
#include <linux/errno.h>
#include <linux/smp.h>
#include <xen/xen.h>
#include <asm/io_apic.h>
#include <asm/apic.h>
#include <asm/acpi.h>
#include "local.h"
static u32 default_get_apic_id(u32 x)
{
unsigned int ver = GET_APIC_VERSION(apic_read(APIC_LVR));
if (APIC_XAPIC(ver) || boot_cpu_has(X86_FEATURE_EXTD_APICID))
return (x >> 24) & 0xFF;
else
return (x >> 24) & 0x0F;
}
/* should be called last. */
static int probe_default(void)
{
return 1;
}
static struct apic apic_default __ro_after_init = {
.name = "default",
.probe = probe_default,
.dest_mode_logical = true,
.disable_esr = 0,
.init_apic_ldr = default_init_apic_ldr,
.cpu_present_to_apicid = default_cpu_present_to_apicid,
.max_apic_id = 0xFE,
.get_apic_id = default_get_apic_id,
.calc_dest_apicid = apic_flat_calc_apicid,
.send_IPI = default_send_IPI_single,
.send_IPI_mask = default_send_IPI_mask_logical,
.send_IPI_mask_allbutself = default_send_IPI_mask_allbutself_logical,
.send_IPI_allbutself = default_send_IPI_allbutself,
.send_IPI_all = default_send_IPI_all,
.send_IPI_self = default_send_IPI_self,
.read = native_apic_mem_read,
.write = native_apic_mem_write,
.eoi = native_apic_mem_eoi,
.icr_read = native_apic_icr_read,
.icr_write = native_apic_icr_write,
.wait_icr_idle = apic_mem_wait_icr_idle,
.safe_wait_icr_idle = apic_mem_wait_icr_idle_timeout,
};
apic_driver(apic_default);
struct apic *apic __ro_after_init = &apic_default;
EXPORT_SYMBOL_GPL(apic);
static int cmdline_apic __initdata;
static int __init parse_apic(char *arg)
{
struct apic **drv;
if (!arg)
return -EINVAL;
for (drv = __apicdrivers; drv < __apicdrivers_end; drv++) {
if (!strcmp((*drv)->name, arg)) {
apic_install_driver(*drv);
cmdline_apic = 1;
return 0;
}
}
/* Parsed again by __setup for debug/verbose */
return 0;
}
early_param("apic", parse_apic);
void __init x86_32_probe_apic(void)
{
if (!cmdline_apic) {
struct apic **drv;
for (drv = __apicdrivers; drv < __apicdrivers_end; drv++) {
if ((*drv)->probe()) {
apic_install_driver(*drv);
break;
}
}
/* Not visible without early console */
if (drv == __apicdrivers_end)
panic("Didn't find an APIC driver");
}
}
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2004 James Cleverdon, IBM.
*
* Generic APIC sub-arch probe layer.
*
* Hacked for x86-64 by James Cleverdon from i386 architecture code by
* Martin Bligh, Andi Kleen, James Bottomley, John Stultz, and
* James Cleverdon.
*/
#include <linux/thread_info.h>
#include <asm/apic.h>
#include "local.h"
/* Select the appropriate APIC driver */
void __init x86_64_probe_apic(void)
{
struct apic **drv;
enable_IR_x2apic();
for (drv = __apicdrivers; drv < __apicdrivers_end; drv++) {
if ((*drv)->probe && (*drv)->probe()) {
apic_install_driver(*drv);
break;
}
}
}
int __init default_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
{
struct apic **drv;
for (drv = __apicdrivers; drv < __apicdrivers_end; drv++) {
if ((*drv)->acpi_madt_oem_check(oem_id, oem_table_id)) {
apic_install_driver(*drv);
return 1;
}
}
return 0;
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,261 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/cpuhotplug.h>
#include <linux/cpumask.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <asm/apic.h>
#include "local.h"
#define apic_cluster(apicid) ((apicid) >> 4)
/*
* __x2apic_send_IPI_mask() possibly needs to read
* x86_cpu_to_logical_apicid for all online cpus in a sequential way.
* Using per cpu variable would cost one cache line per cpu.
*/
static u32 *x86_cpu_to_logical_apicid __read_mostly;
static DEFINE_PER_CPU(cpumask_var_t, ipi_mask);
static DEFINE_PER_CPU_READ_MOSTLY(struct cpumask *, cluster_masks);
static int x2apic_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
{
return x2apic_enabled();
}
static void x2apic_send_IPI(int cpu, int vector)
{
u32 dest = x86_cpu_to_logical_apicid[cpu];
/* x2apic MSRs are special and need a special fence: */
weak_wrmsr_fence();
__x2apic_send_IPI_dest(dest, vector, APIC_DEST_LOGICAL);
}
static void
__x2apic_send_IPI_mask(const struct cpumask *mask, int vector, int apic_dest)
{
unsigned int cpu, clustercpu;
struct cpumask *tmpmsk;
unsigned long flags;
u32 dest;
/* x2apic MSRs are special and need a special fence: */
weak_wrmsr_fence();
local_irq_save(flags);
tmpmsk = this_cpu_cpumask_var_ptr(ipi_mask);
cpumask_copy(tmpmsk, mask);
/* If IPI should not be sent to self, clear current CPU */
if (apic_dest != APIC_DEST_ALLINC)
__cpumask_clear_cpu(smp_processor_id(), tmpmsk);
/* Collapse cpus in a cluster so a single IPI per cluster is sent */
for_each_cpu(cpu, tmpmsk) {
struct cpumask *cmsk = per_cpu(cluster_masks, cpu);
dest = 0;
for_each_cpu_and(clustercpu, tmpmsk, cmsk)
dest |= x86_cpu_to_logical_apicid[clustercpu];
if (!dest)
continue;
__x2apic_send_IPI_dest(dest, vector, APIC_DEST_LOGICAL);
/* Remove cluster CPUs from tmpmask */
cpumask_andnot(tmpmsk, tmpmsk, cmsk);
}
local_irq_restore(flags);
}
static void x2apic_send_IPI_mask(const struct cpumask *mask, int vector)
{
__x2apic_send_IPI_mask(mask, vector, APIC_DEST_ALLINC);
}
static void
x2apic_send_IPI_mask_allbutself(const struct cpumask *mask, int vector)
{
__x2apic_send_IPI_mask(mask, vector, APIC_DEST_ALLBUT);
}
static u32 x2apic_calc_apicid(unsigned int cpu)
{
return x86_cpu_to_logical_apicid[cpu];
}
static void init_x2apic_ldr(void)
{
struct cpumask *cmsk = this_cpu_read(cluster_masks);
BUG_ON(!cmsk);
cpumask_set_cpu(smp_processor_id(), cmsk);
}
/*
* As an optimisation during boot, set the cluster_mask for all present
* CPUs at once, to prevent each of them having to iterate over the others
* to find the existing cluster_mask.
*/
static void prefill_clustermask(struct cpumask *cmsk, unsigned int cpu, u32 cluster)
{
int cpu_i;
for_each_present_cpu(cpu_i) {
struct cpumask **cpu_cmsk = &per_cpu(cluster_masks, cpu_i);
u32 apicid = apic->cpu_present_to_apicid(cpu_i);
if (apicid == BAD_APICID || cpu_i == cpu || apic_cluster(apicid) != cluster)
continue;
if (WARN_ON_ONCE(*cpu_cmsk == cmsk))
continue;
BUG_ON(*cpu_cmsk);
*cpu_cmsk = cmsk;
}
}
static int alloc_clustermask(unsigned int cpu, u32 cluster, int node)
{
struct cpumask *cmsk = NULL;
unsigned int cpu_i;
/*
* At boot time, the CPU present mask is stable. The cluster mask is
* allocated for the first CPU in the cluster and propagated to all
* present siblings in the cluster. If the cluster mask is already set
* on entry to this function for a given CPU, there is nothing to do.
*/
if (per_cpu(cluster_masks, cpu))
return 0;
if (system_state < SYSTEM_RUNNING)
goto alloc;
/*
* On post boot hotplug for a CPU which was not present at boot time,
* iterate over all possible CPUs (even those which are not present
* any more) to find any existing cluster mask.
*/
for_each_possible_cpu(cpu_i) {
u32 apicid = apic->cpu_present_to_apicid(cpu_i);
if (apicid != BAD_APICID && apic_cluster(apicid) == cluster) {
cmsk = per_cpu(cluster_masks, cpu_i);
/*
* If the cluster is already initialized, just store
* the mask and return. There's no need to propagate.
*/
if (cmsk) {
per_cpu(cluster_masks, cpu) = cmsk;
return 0;
}
}
}
/*
* No CPU in the cluster has ever been initialized, so fall through to
* the boot time code which will also populate the cluster mask for any
* other CPU in the cluster which is (now) present.
*/
alloc:
cmsk = kzalloc_node(sizeof(*cmsk), GFP_KERNEL, node);
if (!cmsk)
return -ENOMEM;
per_cpu(cluster_masks, cpu) = cmsk;
prefill_clustermask(cmsk, cpu, cluster);
return 0;
}
static int x2apic_prepare_cpu(unsigned int cpu)
{
u32 phys_apicid = apic->cpu_present_to_apicid(cpu);
u32 cluster = apic_cluster(phys_apicid);
u32 logical_apicid = (cluster << 16) | (1 << (phys_apicid & 0xf));
int node = cpu_to_node(cpu);
x86_cpu_to_logical_apicid[cpu] = logical_apicid;
if (alloc_clustermask(cpu, cluster, node) < 0)
return -ENOMEM;
if (!zalloc_cpumask_var_node(&per_cpu(ipi_mask, cpu), GFP_KERNEL, node))
return -ENOMEM;
return 0;
}
static int x2apic_dead_cpu(unsigned int dead_cpu)
{
struct cpumask *cmsk = per_cpu(cluster_masks, dead_cpu);
if (cmsk)
cpumask_clear_cpu(dead_cpu, cmsk);
free_cpumask_var(per_cpu(ipi_mask, dead_cpu));
return 0;
}
static int x2apic_cluster_probe(void)
{
u32 slots;
if (!x2apic_mode)
return 0;
slots = max_t(u32, L1_CACHE_BYTES/sizeof(u32), nr_cpu_ids);
x86_cpu_to_logical_apicid = kcalloc(slots, sizeof(u32), GFP_KERNEL);
if (!x86_cpu_to_logical_apicid)
return 0;
if (cpuhp_setup_state(CPUHP_X2APIC_PREPARE, "x86/x2apic:prepare",
x2apic_prepare_cpu, x2apic_dead_cpu) < 0) {
pr_err("Failed to register X2APIC_PREPARE\n");
kfree(x86_cpu_to_logical_apicid);
x86_cpu_to_logical_apicid = NULL;
return 0;
}
init_x2apic_ldr();
return 1;
}
static struct apic apic_x2apic_cluster __ro_after_init = {
.name = "cluster x2apic",
.probe = x2apic_cluster_probe,
.acpi_madt_oem_check = x2apic_acpi_madt_oem_check,
.dest_mode_logical = true,
.disable_esr = 0,
.init_apic_ldr = init_x2apic_ldr,
.cpu_present_to_apicid = default_cpu_present_to_apicid,
.max_apic_id = UINT_MAX,
.x2apic_set_max_apicid = true,
.get_apic_id = x2apic_get_apic_id,
.calc_dest_apicid = x2apic_calc_apicid,
.send_IPI = x2apic_send_IPI,
.send_IPI_mask = x2apic_send_IPI_mask,
.send_IPI_mask_allbutself = x2apic_send_IPI_mask_allbutself,
.send_IPI_allbutself = x2apic_send_IPI_allbutself,
.send_IPI_all = x2apic_send_IPI_all,
.send_IPI_self = x2apic_send_IPI_self,
.nmi_to_offline_cpu = true,
.read = native_apic_msr_read,
.write = native_apic_msr_write,
.eoi = native_apic_msr_eoi,
.icr_read = native_x2apic_icr_read,
.icr_write = native_x2apic_icr_write,
};
apic_driver(apic_x2apic_cluster);
@@ -0,0 +1,159 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/cpumask.h>
#include <linux/acpi.h>
#include "local.h"
int x2apic_phys;
static struct apic apic_x2apic_phys;
u32 x2apic_max_apicid __ro_after_init = UINT_MAX;
void __init x2apic_set_max_apicid(u32 apicid)
{
x2apic_max_apicid = apicid;
if (apic->x2apic_set_max_apicid)
apic->max_apic_id = apicid;
}
static int __init set_x2apic_phys_mode(char *arg)
{
x2apic_phys = 1;
return 0;
}
early_param("x2apic_phys", set_x2apic_phys_mode);
static bool x2apic_fadt_phys(void)
{
#ifdef CONFIG_ACPI
if ((acpi_gbl_FADT.header.revision >= FADT2_REVISION_ID) &&
(acpi_gbl_FADT.flags & ACPI_FADT_APIC_PHYSICAL)) {
printk(KERN_DEBUG "System requires x2apic physical mode\n");
return true;
}
#endif
return false;
}
static int x2apic_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
{
return x2apic_enabled() && (x2apic_phys || x2apic_fadt_phys());
}
static void x2apic_send_IPI(int cpu, int vector)
{
u32 dest = per_cpu(x86_cpu_to_apicid, cpu);
/* x2apic MSRs are special and need a special fence: */
weak_wrmsr_fence();
__x2apic_send_IPI_dest(dest, vector, APIC_DEST_PHYSICAL);
}
static void
__x2apic_send_IPI_mask(const struct cpumask *mask, int vector, int apic_dest)
{
unsigned long query_cpu;
unsigned long this_cpu;
unsigned long flags;
/* x2apic MSRs are special and need a special fence: */
weak_wrmsr_fence();
local_irq_save(flags);
this_cpu = smp_processor_id();
for_each_cpu(query_cpu, mask) {
if (apic_dest == APIC_DEST_ALLBUT && this_cpu == query_cpu)
continue;
__x2apic_send_IPI_dest(per_cpu(x86_cpu_to_apicid, query_cpu),
vector, APIC_DEST_PHYSICAL);
}
local_irq_restore(flags);
}
static void x2apic_send_IPI_mask(const struct cpumask *mask, int vector)
{
__x2apic_send_IPI_mask(mask, vector, APIC_DEST_ALLINC);
}
static void
x2apic_send_IPI_mask_allbutself(const struct cpumask *mask, int vector)
{
__x2apic_send_IPI_mask(mask, vector, APIC_DEST_ALLBUT);
}
static void __x2apic_send_IPI_shorthand(int vector, u32 which)
{
unsigned long cfg = __prepare_ICR(which, vector, 0);
/* x2apic MSRs are special and need a special fence: */
weak_wrmsr_fence();
native_x2apic_icr_write(cfg, 0);
}
void x2apic_send_IPI_allbutself(int vector)
{
__x2apic_send_IPI_shorthand(vector, APIC_DEST_ALLBUT);
}
void x2apic_send_IPI_all(int vector)
{
__x2apic_send_IPI_shorthand(vector, APIC_DEST_ALLINC);
}
void x2apic_send_IPI_self(int vector)
{
apic_write(APIC_SELF_IPI, vector);
}
static int x2apic_phys_probe(void)
{
if (!x2apic_mode)
return 0;
if (x2apic_phys || x2apic_fadt_phys())
return 1;
return apic == &apic_x2apic_phys;
}
u32 x2apic_get_apic_id(u32 id)
{
return id;
}
static struct apic apic_x2apic_phys __ro_after_init = {
.name = "physical x2apic",
.probe = x2apic_phys_probe,
.acpi_madt_oem_check = x2apic_acpi_madt_oem_check,
.dest_mode_logical = false,
.disable_esr = 0,
.cpu_present_to_apicid = default_cpu_present_to_apicid,
.max_apic_id = UINT_MAX,
.x2apic_set_max_apicid = true,
.get_apic_id = x2apic_get_apic_id,
.calc_dest_apicid = apic_default_calc_apicid,
.send_IPI = x2apic_send_IPI,
.send_IPI_mask = x2apic_send_IPI_mask,
.send_IPI_mask_allbutself = x2apic_send_IPI_mask_allbutself,
.send_IPI_allbutself = x2apic_send_IPI_allbutself,
.send_IPI_all = x2apic_send_IPI_all,
.send_IPI_self = x2apic_send_IPI_self,
.nmi_to_offline_cpu = true,
.read = native_apic_msr_read,
.write = native_apic_msr_write,
.eoi = native_apic_msr_eoi,
.icr_read = native_x2apic_icr_read,
.icr_write = native_x2apic_icr_write,
};
apic_driver(apic_x2apic_phys);
@@ -0,0 +1,428 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* AMD Secure AVIC Support (SEV-SNP Guests)
*
* Copyright (C) 2024 Advanced Micro Devices, Inc.
*
* Author: Neeraj Upadhyay <Neeraj.Upadhyay@amd.com>
*/
#include <linux/cc_platform.h>
#include <linux/cpumask.h>
#include <linux/percpu-defs.h>
#include <linux/align.h>
#include <asm/apic.h>
#include <asm/sev.h>
#include "local.h"
struct secure_avic_page {
u8 regs[PAGE_SIZE];
} __aligned(PAGE_SIZE);
static struct secure_avic_page __percpu *savic_page __ro_after_init;
static int savic_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
{
return x2apic_enabled() && cc_platform_has(CC_ATTR_SNP_SECURE_AVIC);
}
static inline void *get_reg_bitmap(unsigned int cpu, unsigned int offset)
{
return &per_cpu_ptr(savic_page, cpu)->regs[offset];
}
static inline void update_vector(unsigned int cpu, unsigned int offset,
unsigned int vector, bool set)
{
void *bitmap = get_reg_bitmap(cpu, offset);
if (set)
apic_set_vector(vector, bitmap);
else
apic_clear_vector(vector, bitmap);
}
#define SAVIC_ALLOWED_IRR 0x204
/*
* When Secure AVIC is enabled, RDMSR/WRMSR of the APIC registers
* result in #VC exception (for non-accelerated register accesses)
* with VMEXIT_AVIC_NOACCEL error code. The #VC exception handler
* can read/write the x2APIC register in the guest APIC backing page.
*
* Since doing this would increase the latency of accessing x2APIC
* registers, instead of doing RDMSR/WRMSR based accesses and
* handling the APIC register reads/writes in the #VC exception handler,
* the read() and write() callbacks directly read/write the APIC register
* from/to the vCPU's APIC backing page.
*/
static u32 savic_read(u32 reg)
{
void *ap = this_cpu_ptr(savic_page);
switch (reg) {
case APIC_LVTT:
case APIC_TMICT:
case APIC_TMCCT:
case APIC_TDCR:
case APIC_LVTTHMR:
case APIC_LVTPC:
case APIC_LVT0:
case APIC_LVT1:
case APIC_LVTERR:
return savic_ghcb_msr_read(reg);
case APIC_ID:
case APIC_LVR:
case APIC_TASKPRI:
case APIC_ARBPRI:
case APIC_PROCPRI:
case APIC_LDR:
case APIC_SPIV:
case APIC_ESR:
case APIC_EFEAT:
case APIC_ECTRL:
case APIC_SEOI:
case APIC_IER:
case APIC_EILVTn(0) ... APIC_EILVTn(3):
return apic_get_reg(ap, reg);
case APIC_ICR:
return (u32)apic_get_reg64(ap, reg);
case APIC_ISR ... APIC_ISR + 0x70:
case APIC_TMR ... APIC_TMR + 0x70:
if (WARN_ONCE(!IS_ALIGNED(reg, 16),
"APIC register read offset 0x%x not aligned at 16 bytes", reg))
return 0;
return apic_get_reg(ap, reg);
/* IRR and ALLOWED_IRR offset range */
case APIC_IRR ... APIC_IRR + 0x74:
/*
* Valid APIC_IRR/SAVIC_ALLOWED_IRR registers are at 16 bytes strides from
* their respective base offset. APIC_IRRs are in the range
*
* (0x200, 0x210, ..., 0x270)
*
* while the SAVIC_ALLOWED_IRR range starts 4 bytes later, in the range
*
* (0x204, 0x214, ..., 0x274).
*
* Filter out everything else.
*/
if (WARN_ONCE(!(IS_ALIGNED(reg, 16) ||
IS_ALIGNED(reg - 4, 16)),
"Misaligned APIC_IRR/ALLOWED_IRR APIC register read offset 0x%x", reg))
return 0;
return apic_get_reg(ap, reg);
default:
pr_err("Error reading unknown Secure AVIC reg offset 0x%x\n", reg);
return 0;
}
}
#define SAVIC_NMI_REQ 0x278
/*
* On WRMSR to APIC_SELF_IPI register by the guest, Secure AVIC hardware
* updates the APIC_IRR in the APIC backing page of the vCPU. In addition,
* hardware evaluates the new APIC_IRR update for interrupt injection to
* the vCPU. So, self IPIs are hardware-accelerated.
*/
static inline void self_ipi_reg_write(unsigned int vector)
{
native_apic_msr_write(APIC_SELF_IPI, vector);
}
static void send_ipi_dest(unsigned int cpu, unsigned int vector, bool nmi)
{
if (nmi)
apic_set_reg(per_cpu_ptr(savic_page, cpu), SAVIC_NMI_REQ, 1);
else
update_vector(cpu, APIC_IRR, vector, true);
}
static void send_ipi_allbut(unsigned int vector, bool nmi)
{
unsigned int cpu, src_cpu;
guard(irqsave)();
src_cpu = raw_smp_processor_id();
for_each_cpu(cpu, cpu_online_mask) {
if (cpu == src_cpu)
continue;
send_ipi_dest(cpu, vector, nmi);
}
}
static inline void self_ipi(unsigned int vector, bool nmi)
{
u32 icr_low = APIC_SELF_IPI | vector;
if (nmi)
icr_low |= APIC_DM_NMI;
native_x2apic_icr_write(icr_low, 0);
}
static void savic_icr_write(u32 icr_low, u32 icr_high)
{
unsigned int dsh, vector;
u64 icr_data;
bool nmi;
dsh = icr_low & APIC_DEST_ALLBUT;
vector = icr_low & APIC_VECTOR_MASK;
nmi = ((icr_low & APIC_DM_FIXED_MASK) == APIC_DM_NMI);
switch (dsh) {
case APIC_DEST_SELF:
self_ipi(vector, nmi);
break;
case APIC_DEST_ALLINC:
self_ipi(vector, nmi);
fallthrough;
case APIC_DEST_ALLBUT:
send_ipi_allbut(vector, nmi);
break;
default:
send_ipi_dest(icr_high, vector, nmi);
break;
}
icr_data = ((u64)icr_high) << 32 | icr_low;
if (dsh != APIC_DEST_SELF)
savic_ghcb_msr_write(APIC_ICR, icr_data);
apic_set_reg64(this_cpu_ptr(savic_page), APIC_ICR, icr_data);
}
static void savic_write(u32 reg, u32 data)
{
void *ap = this_cpu_ptr(savic_page);
switch (reg) {
case APIC_LVTT:
case APIC_TMICT:
case APIC_TDCR:
case APIC_LVT0:
case APIC_LVT1:
case APIC_LVTTHMR:
case APIC_LVTPC:
case APIC_LVTERR:
savic_ghcb_msr_write(reg, data);
break;
case APIC_TASKPRI:
case APIC_EOI:
case APIC_SPIV:
case SAVIC_NMI_REQ:
case APIC_ESR:
case APIC_ECTRL:
case APIC_SEOI:
case APIC_IER:
case APIC_EILVTn(0) ... APIC_EILVTn(3):
apic_set_reg(ap, reg, data);
break;
case APIC_ICR:
savic_icr_write(data, 0);
break;
case APIC_SELF_IPI:
self_ipi_reg_write(data);
break;
/* ALLOWED_IRR offsets are writable */
case SAVIC_ALLOWED_IRR ... SAVIC_ALLOWED_IRR + 0x70:
if (IS_ALIGNED(reg - 4, 16)) {
apic_set_reg(ap, reg, data);
break;
}
fallthrough;
default:
pr_err("Error writing unknown Secure AVIC reg offset 0x%x\n", reg);
}
}
static void send_ipi(u32 dest, unsigned int vector, unsigned int dsh)
{
unsigned int icr_low;
icr_low = __prepare_ICR(dsh, vector, APIC_DEST_PHYSICAL);
savic_icr_write(icr_low, dest);
}
static void savic_send_ipi(int cpu, int vector)
{
u32 dest = per_cpu(x86_cpu_to_apicid, cpu);
send_ipi(dest, vector, 0);
}
static void send_ipi_mask(const struct cpumask *mask, unsigned int vector, bool excl_self)
{
unsigned int cpu, this_cpu;
guard(irqsave)();
this_cpu = raw_smp_processor_id();
for_each_cpu(cpu, mask) {
if (excl_self && cpu == this_cpu)
continue;
send_ipi(per_cpu(x86_cpu_to_apicid, cpu), vector, 0);
}
}
static void savic_send_ipi_mask(const struct cpumask *mask, int vector)
{
send_ipi_mask(mask, vector, false);
}
static void savic_send_ipi_mask_allbutself(const struct cpumask *mask, int vector)
{
send_ipi_mask(mask, vector, true);
}
static void savic_send_ipi_allbutself(int vector)
{
send_ipi(0, vector, APIC_DEST_ALLBUT);
}
static void savic_send_ipi_all(int vector)
{
send_ipi(0, vector, APIC_DEST_ALLINC);
}
static void savic_send_ipi_self(int vector)
{
self_ipi_reg_write(vector);
}
static void savic_update_vector(unsigned int cpu, unsigned int vector, bool set)
{
update_vector(cpu, SAVIC_ALLOWED_IRR, vector, set);
}
static void savic_eoi(void)
{
unsigned int cpu;
int vec;
cpu = raw_smp_processor_id();
vec = apic_find_highest_vector(get_reg_bitmap(cpu, APIC_ISR));
if (WARN_ONCE(vec == -1, "EOI write while no active interrupt in APIC_ISR"))
return;
/* Is level-triggered interrupt? */
if (apic_test_vector(vec, get_reg_bitmap(cpu, APIC_TMR))) {
update_vector(cpu, APIC_ISR, vec, false);
/*
* Propagate the EOI write to the hypervisor for level-triggered
* interrupts. Return to the guest from GHCB protocol event takes
* care of re-evaluating interrupt state.
*/
savic_ghcb_msr_write(APIC_EOI, 0);
} else {
/*
* Hardware clears APIC_ISR and re-evaluates the interrupt state
* to determine if there is any pending interrupt which can be
* delivered to CPU.
*/
native_apic_msr_eoi();
}
}
static void savic_teardown(void)
{
/* Disable Secure AVIC */
native_wrmsrq(MSR_AMD64_SAVIC_CONTROL, 0);
savic_unregister_gpa(NULL);
}
static void savic_setup(void)
{
void *ap = this_cpu_ptr(savic_page);
enum es_result res;
unsigned long gpa;
/*
* Before Secure AVIC is enabled, APIC MSR reads are intercepted.
* APIC_ID MSR read returns the value from the hypervisor.
*/
apic_set_reg(ap, APIC_ID, native_apic_msr_read(APIC_ID));
gpa = __pa(ap);
/*
* The NPT entry for a vCPU's APIC backing page must always be
* present when the vCPU is running in order for Secure AVIC to
* function. A VMEXIT_BUSY is returned on VMRUN and the vCPU cannot
* be resumed if the NPT entry for the APIC backing page is not
* present. Notify GPA of the vCPU's APIC backing page to the
* hypervisor by calling savic_register_gpa(). Before executing
* VMRUN, the hypervisor makes use of this information to make sure
* the APIC backing page is mapped in NPT.
*/
res = savic_register_gpa(gpa);
if (res != ES_OK)
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SAVIC_FAIL);
native_wrmsrq(MSR_AMD64_SAVIC_CONTROL,
gpa | MSR_AMD64_SAVIC_EN | MSR_AMD64_SAVIC_ALLOWEDNMI);
}
static int savic_probe(void)
{
if (!cc_platform_has(CC_ATTR_SNP_SECURE_AVIC))
return 0;
if (!x2apic_mode) {
pr_err("Secure AVIC enabled in non x2APIC mode\n");
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SAVIC_FAIL);
/* unreachable */
}
savic_page = alloc_percpu(struct secure_avic_page);
if (!savic_page)
sev_es_terminate(SEV_TERM_SET_LINUX, GHCB_TERM_SAVIC_FAIL);
return 1;
}
static struct apic apic_x2apic_savic __ro_after_init = {
.name = "secure avic x2apic",
.probe = savic_probe,
.acpi_madt_oem_check = savic_acpi_madt_oem_check,
.setup = savic_setup,
.teardown = savic_teardown,
.dest_mode_logical = false,
.disable_esr = 0,
.cpu_present_to_apicid = default_cpu_present_to_apicid,
.max_apic_id = UINT_MAX,
.x2apic_set_max_apicid = true,
.get_apic_id = x2apic_get_apic_id,
.calc_dest_apicid = apic_default_calc_apicid,
.send_IPI = savic_send_ipi,
.send_IPI_mask = savic_send_ipi_mask,
.send_IPI_mask_allbutself = savic_send_ipi_mask_allbutself,
.send_IPI_allbutself = savic_send_ipi_allbutself,
.send_IPI_all = savic_send_ipi_all,
.send_IPI_self = savic_send_ipi_self,
.nmi_to_offline_cpu = true,
.read = savic_read,
.write = savic_write,
.eoi = savic_eoi,
.icr_read = native_x2apic_icr_read,
.icr_write = savic_icr_write,
.update_vector = savic_update_vector,
};
apic_driver(apic_x2apic_savic);
File diff suppressed because it is too large Load Diff