[RFC v2 04/17] x86/apic: Initialize APIC ID for Secure AVIC

Neeraj Upadhyay
2025-02-26T09:06:58Z

Initialize the APIC ID in the Secure AVIC APIC backing page with
the APIC_ID msr value read from Hypervisor. Maintain a hashmap to
check and report same APIC_ID value returned by Hypervisor for two
different vCPUs.

Signed-off-by: Neeraj Upadhyay <Neeraj.Upadhyay@amd.com>
---
Changes since v1:

- Do not read APIC_ID from CPUID. Read APIC_ID from Hv and check for
duplicates.
- Add a more user-friendly log message on detecting duplicate APIC
IDs.

arch/x86/kernel/apic/x2apic_savic.c | 59 +++++++++++++++++++++++++++++
1 file changed, 59 insertions(+)

diff --git a/arch/x86/kernel/apic/x2apic_savic.c b/arch/x86/kernel/apic/x2apic_savic.c
index ba904f241d34..505ef2d29311 100644
--- a/arch/x86/kernel/apic/x2apic_savic.c
+++ b/arch/x86/kernel/apic/x2apic_savic.c
@@ -11,6 +11,8 @@
#include <linux/cc_platform.h>
#include <linux/percpu-defs.h>
#include <linux/align.h>
+#include <linux/sizes.h>
+#include <linux/llist.h>

#include <asm/apic.h>
#include <asm/sev.h>
@@ -19,6 +21,16 @@

static DEFINE_PER_CPU(void *, apic_backing_page);

+struct apic_id_node {
+ struct llist_node node;
+ u32 apic_id;
+ int cpu;
+};
+
+static DEFINE_PER_CPU(struct apic_id_node, apic_id_node);
+
+static struct llist_head *apic_id_map;
+
static int x2apic_savic_acpi_madt_oem_check(char *oem_id, char *oem_table_id)
{
return x2apic_enabled() && cc_platform_has(CC_ATTR_SNP_SECURE_AVIC);
@@ -180,6 +192,44 @@ static void x2apic_savic_send_IPI_mask_allbutself(const struct cpumask *mask, in
__send_IPI_mask(mask, vector, APIC_DEST_ALLBUT);
}

+static void init_backing_page(void *backing_page)
+{
+ struct apic_id_node *next_node, *this_cpu_node;
+ unsigned int apic_map_slot;
+ u32 apic_id;
+ int cpu;
+
+ /*
+ * Before Secure AVIC is enabled, APIC msr reads are
+ * intercepted. APIC_ID msr read returns the value
+ * from hv.
+ */
+ apic_id = native_apic_msr_read(APIC_ID);
+ set_reg(backing_page, APIC_ID, apic_id);
+
+ if (!apic_id_map)
+ return;
+
+ cpu = smp_processor_id();
+ this_cpu_node = &per_cpu(apic_id_node, cpu);
+ this_cpu_node->apic_id = apic_id;
+ this_cpu_node->cpu = cpu;
+ /*
+ * In common case, apic_ids for CPUs are sequentially numbered.
+ * So, each CPU should hash to a different slot in the apic id
+ * map.
+ */
+ apic_map_slot = apic_id % nr_cpu_ids;
+ llist_add(&this_cpu_node->node, &apic_id_map[apic_map_slot]);
+ /* Each CPU checks only its next nodes for duplicates. */
+ llist_for_each_entry(next_node, this_cpu_node->node.next, node) {
+ if (WARN_ONCE(next_node->apic_id == apic_id,
+ "Duplicate APIC %u for cpu %d and cpu %d. IPI handling will suffer!",
+ apic_id, cpu, next_node->cpu))
+ break;
+ }
+}
+
static void x2apic_savic_setup(void)
{
void *backing_page;
@@ -193,6 +243,7 @@ static void x2apic_savic_setup(void)
if (!backing_page)
snp_abort();
this_cpu_write(apic_backing_page, backing_page);
+ init_backing_page(backing_page);
gpa = __pa(backing_page);

/*
@@ -212,6 +263,8 @@ static void x2apic_savic_setup(void)

static int x2apic_savic_probe(void)
{
+ int i;
+
if (!cc_platform_has(CC_ATTR_SNP_SECURE_AVIC))
return 0;

@@ -220,6 +273,12 @@ static int x2apic_savic_probe(void)
snp_abort();
}

+ apic_id_map = kvmalloc(nr_cpu_ids * sizeof(*apic_id_map), GFP_KERNEL);
+
+ if (apic_id_map)
+ for (i = 0; i < nr_cpu_ids; i++)
+ init_llist_head(&apic_id_map[i]);
+
pr_info("Secure AVIC Enabled\n");

return 1;
--
2.34.1