[RFC v2 07/17] x86/apic: Support LAPIC timer for Secure AVIC

Neeraj Upadhyay
2025-02-26T09:07:59Z

From: Kishon Vijay Abraham I <kvijayab@amd.com>

Secure AVIC requires LAPIC timer to be emulated by hypervisor. KVM
already supports emulating LAPIC timer using hrtimers. In order
to emulate LAPIC timer, APIC_LVTT, APIC_TMICT and APIC_TDCR register
values need to be propagated to the hypervisor for arming the timer.
APIC_TMCCT register value has to be read from the hypervisor, which
is required for calibrating the APIC timer. So, read/write all APIC
timer registers from/to the hypervisor.

In addition, configure APIC_ALLOWED_IRR for the hypervisor to inject
timer interrupt using LOCAL_TIMER_VECTOR.

Signed-off-by: Kishon Vijay Abraham I <kvijayab@amd.com>
Co-developed-by: Neeraj Upadhyay <Neeraj.Upadhyay@amd.com>
Signed-off-by: Neeraj Upadhyay <Neeraj.Upadhyay@amd.com>
---
Changes since v1:

- Move savic_ghcb_msr_read() definition here.
- Call update_vector() callback only when it is initialized.

arch/x86/coco/sev/core.c | 27 +++++++++++++++++++++++++++
arch/x86/include/asm/sev.h | 2 ++
arch/x86/kernel/apic/apic.c | 4 ++++
arch/x86/kernel/apic/x2apic_savic.c | 7 +++++--
4 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/arch/x86/coco/sev/core.c b/arch/x86/coco/sev/core.c
index 4291cdeb5895..e4c20023e554 100644
--- a/arch/x86/coco/sev/core.c
+++ b/arch/x86/coco/sev/core.c
@@ -1505,6 +1505,33 @@ static enum es_result vc_handle_msr(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
return __vc_handle_msr(ghcb, ctxt, ctxt->insn.opcode.bytes[1] == 0x30);
}

+u64 savic_ghcb_msr_read(u32 reg)
+{
+ u64 msr = APIC_BASE_MSR + (reg >> 4);
+ struct pt_regs regs = { .cx = msr };
+ struct es_em_ctxt ctxt = { .regs = &regs };
+ struct ghcb_state state;
+ unsigned long flags;
+ enum es_result ret;
+ struct ghcb *ghcb;
+
+ local_irq_save(flags);
+ ghcb = __sev_get_ghcb(&state);
+ vc_ghcb_invalidate(ghcb);
+
+ ret = __vc_handle_msr(ghcb, &ctxt, false);
+ if (ret != ES_OK) {
+ pr_err("Secure AVIC msr (0x%llx) read returned error (%d)\n", msr, ret);
+ /* MSR read failures are treated as fatal errors */
+ snp_abort();
+ }
+
+ __sev_put_ghcb(&state);
+ local_irq_restore(flags);
+
+ return regs.ax | regs.dx << 32;
+}
+
void savic_ghcb_msr_write(u32 reg, u64 value)
{
u64 msr = APIC_BASE_MSR + (reg >> 4);
diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
index 1beeb0daf9e6..043fe8115ec7 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -484,6 +484,7 @@ int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req
void __init snp_secure_tsc_prepare(void);
void __init snp_secure_tsc_init(void);
enum es_result savic_register_gpa(u64 apic_id, u64 gpa);
+u64 savic_ghcb_msr_read(u32 reg);
void savic_ghcb_msr_write(u32 reg, u64 value);

#else /* !CONFIG_AMD_MEM_ENCRYPT */
@@ -530,6 +531,7 @@ static inline void __init snp_secure_tsc_prepare(void) { }
static inline void __init snp_secure_tsc_init(void) { }
static inline enum es_result savic_register_gpa(u64 apic_id,
u64 gpa) { return ES_UNSUPPORTED; }
+static inline u64 savic_ghcb_msr_read(u32 reg) { return 0; }
static void savic_ghcb_msr_write(u32 reg, u64 value) { }

#endif /* CONFIG_AMD_MEM_ENCRYPT */
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index 1c0b5f14435e..23a566a82084 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -591,6 +591,10 @@ static void setup_APIC_timer(void)
0xF, ~0UL);
} else
clockevents_register_device(levt);
+
+ if (apic->update_vector)
+ apic->update_vector(smp_processor_id(), LOCAL_TIMER_VECTOR,
+ true);
}

/*
diff --git a/arch/x86/kernel/apic/x2apic_savic.c b/arch/x86/kernel/apic/x2apic_savic.c
index 7e3843154997..af46e1b57017 100644
--- a/arch/x86/kernel/apic/x2apic_savic.c
+++ b/arch/x86/kernel/apic/x2apic_savic.c
@@ -71,6 +71,7 @@ static u32 x2apic_savic_read(u32 reg)
case APIC_TMICT:
case APIC_TMCCT:
case APIC_TDCR:
+ return savic_ghcb_msr_read(reg);
case APIC_ID:
case APIC_LVR:
case APIC_TASKPRI:
@@ -123,10 +124,12 @@ static void x2apic_savic_write(u32 reg, u32 data)

switch (reg) {
case APIC_LVTT:
- case APIC_LVT0:
- case APIC_LVT1:
case APIC_TMICT:
case APIC_TDCR:
+ savic_ghcb_msr_write(reg, data);
+ break;
+ case APIC_LVT0:
+ case APIC_LVT1:
case APIC_TASKPRI:
case APIC_EOI:
case APIC_SPIV:
--
2.34.1