Although the kernel switches over to a stable TSC clocksource instead of
kvm-clock, TSC frequency calibration still relies on the kvm-clock based
frequency calibration. This is due to kvmclock_init() unconditionally
updating the x86_platform's CPU and TSC callbacks.
For Secure TSC enabled guests, use the GUEST_TSC_FREQ MSR to discover the
TSC frequency instead of relying on kvm-clock based frequency calibration.
Override both CPU and TSC frequency calibration callbacks with
securetsc_get_tsc_khz(). Since the difference between CPU base and TSC
frequency does not apply in this case, the same callback is being used.
Additionally, warn users when kvm-clock is selected as the clocksource for
Secure TSC enabled guests. Users can change the clocksource to kvm-clock
using the sysfs interface while running on a Secure TSC enabled guest.
Switching to the hypervisor-controlled kvm-clock can lead to potential
security issues.
Taint the kernel and issue a warning to the user when the clocksource
switches to kvm-clock, ensuring they are aware of the change and its
implications.
Signed-off-by: Nikunj A Dadhania <nikunj@amd.com>
---
arch/x86/include/asm/sev.h | 2 ++
arch/x86/coco/sev/core.c | 21 +++++++++++++++++++++
arch/x86/kernel/kvmclock.c | 11 +++++++++++
arch/x86/kernel/tsc.c | 4 ++++
4 files changed, 38 insertions(+)
diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
index bdcdaac4df1c..5d9685f92e5c 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -482,6 +482,7 @@ int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req
struct snp_guest_request_ioctl *rio);
void __init snp_secure_tsc_prepare(void);
+void __init snp_secure_tsc_init(void);
#else /* !CONFIG_AMD_MEM_ENCRYPT */
@@ -524,6 +525,7 @@ static inline void snp_msg_free(struct snp_msg_desc *mdesc) { }
static inline int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req,
struct snp_guest_request_ioctl *rio) { return -ENODEV; }
static inline void __init snp_secure_tsc_prepare(void) { }
+static inline void __init snp_secure_tsc_init(void) { }
#endif /* CONFIG_AMD_MEM_ENCRYPT */
diff --git a/arch/x86/coco/sev/core.c b/arch/x86/coco/sev/core.c
index dbf4531c6271..9c971637e56b 100644
--- a/arch/x86/coco/sev/core.c
+++ b/arch/x86/coco/sev/core.c
@@ -103,6 +103,7 @@ static u64 secrets_pa __ro_after_init;
*/
static u64 snp_tsc_scale __ro_after_init;
static u64 snp_tsc_offset __ro_after_init;
+static u64 snp_tsc_freq_khz __ro_after_init;
/* #VC handler runtime per-CPU data */
struct sev_es_runtime_data {
@@ -3273,3 +3274,23 @@ void __init snp_secure_tsc_prepare(void)
pr_debug("SecureTSC enabled");
}
+
+static unsigned long securetsc_get_tsc_khz(void)
+{
+ return snp_tsc_freq_khz;
+}
+
+void __init snp_secure_tsc_init(void)
+{
+ unsigned long long tsc_freq_mhz;
+
+ if (!cc_platform_has(CC_ATTR_GUEST_SNP_SECURE_TSC))
+ return;
+
+ setup_force_cpu_cap(X86_FEATURE_TSC_KNOWN_FREQ);
+ rdmsrl(MSR_AMD64_GUEST_TSC_FREQ, tsc_freq_mhz);
+ snp_tsc_freq_khz = (unsigned long)(tsc_freq_mhz * 1000);
+
+ x86_platform.calibrate_cpu = securetsc_get_tsc_khz;
+ x86_platform.calibrate_tsc = securetsc_get_tsc_khz;
+}
diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c
index 5b2c15214a6b..960260a8d884 100644
--- a/arch/x86/kernel/kvmclock.c
+++ b/arch/x86/kernel/kvmclock.c
@@ -21,6 +21,7 @@
#include <asm/hypervisor.h>
#include <asm/x86_init.h>
#include <asm/kvmclock.h>
+#include <asm/sev.h>
static int kvmclock __initdata = 1;
static int kvmclock_vsyscall __initdata = 1;
@@ -150,6 +151,16 @@ bool kvm_check_and_clear_guest_paused(void)
static int kvm_cs_enable(struct clocksource *cs)
{
+ /*
+ * TSC clocksource should be used for a guest with Secure TSC enabled,
+ * taint the kernel and warn when the user changes the clocksource to
+ * kvm-clock.
+ */
+ if (cc_platform_has(CC_ATTR_GUEST_SNP_SECURE_TSC)) {
+ add_taint(TAINT_WARN, LOCKDEP_STILL_OK);
+ WARN_ONCE(1, "For Secure TSC guest, changing the clocksource is not allowed!\n");
+ }
+
vclocks_set_used(VDSO_CLOCKMODE_PVCLOCK);
return 0;
}
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
index a85594644e13..34dec0b72ea8 100644
--- a/arch/x86/kernel/tsc.c
+++ b/arch/x86/kernel/tsc.c
@@ -31,6 +31,7 @@
#include <asm/i8259.h>
#include <asm/topology.h>
#include <asm/uv/uv.h>
+#include <asm/sev.h>
unsigned int __read_mostly cpu_khz; /* TSC clocks / usec, not used here */
EXPORT_SYMBOL(cpu_khz);
@@ -1514,6 +1515,9 @@ void __init tsc_early_init(void)
/* Don't change UV TSC multi-chassis synchronization */
if (is_early_uv_system())
return;
+
+ snp_secure_tsc_init();
+
if (!determine_cpu_tsc_frequencies(true))
return;
tsc_enable_sched_clock();
--
2.34.1
On Mon, Jan 06, 2025 at 06:16:30PM +0530, Nikunj A Dadhania wrote:
> static int kvm_cs_enable(struct clocksource *cs)
> {
> + /*
> + * TSC clocksource should be used for a guest with Secure TSC enabled,
> + * taint the kernel and warn when the user changes the clocksource to
> + * kvm-clock.
> + */
> + if (cc_platform_has(CC_ATTR_GUEST_SNP_SECURE_TSC)) {
> + add_taint(TAINT_WARN, LOCKDEP_STILL_OK);
> + WARN_ONCE(1, "For Secure TSC guest, changing the clocksource is not allowed!\n");
So this thing is trying to state that changing the clocksource is not allowed
but it still allows it. Why not simply do this:
diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c
index 960260a8d884..d8fef3a65a35 100644
--- a/arch/x86/kernel/kvmclock.c
+++ b/arch/x86/kernel/kvmclock.c
@@ -151,14 +151,10 @@ bool kvm_check_and_clear_guest_paused(void)
static int kvm_cs_enable(struct clocksource *cs)
{
- /*
- * TSC clocksource should be used for a guest with Secure TSC enabled,
- * taint the kernel and warn when the user changes the clocksource to
- * kvm-clock.
- */
+ /* Only the TSC should be used in a Secure TSC guest. */
if (cc_platform_has(CC_ATTR_GUEST_SNP_SECURE_TSC)) {
- add_taint(TAINT_WARN, LOCKDEP_STILL_OK);
- WARN_ONCE(1, "For Secure TSC guest, changing the clocksource is not allowed!\n");
+ WARN_ONCE(1, "Secure TSC guest, changing the clocksource is not allowed!\n");
+ return 1;
}
vclocks_set_used(VDSO_CLOCKMODE_PVCLOCK);
?
--
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquette
On 1/7/2025 8:46 PM, Borislav Petkov wrote:
> On Mon, Jan 06, 2025 at 06:16:30PM +0530, Nikunj A Dadhania wrote:
>> static int kvm_cs_enable(struct clocksource *cs)
>> {
>> + /*
>> + * TSC clocksource should be used for a guest with Secure TSC enabled,
>> + * taint the kernel and warn when the user changes the clocksource to
>> + * kvm-clock.
>> + */
>> + if (cc_platform_has(CC_ATTR_GUEST_SNP_SECURE_TSC)) {
>> + add_taint(TAINT_WARN, LOCKDEP_STILL_OK);
>> + WARN_ONCE(1, "For Secure TSC guest, changing the clocksource is not allowed!\n");
>
> So this thing is trying to state that changing the clocksource is not allowed
> but it still allows it. Why not simply do this:
>
> diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c
> index 960260a8d884..d8fef3a65a35 100644
> --- a/arch/x86/kernel/kvmclock.c
> +++ b/arch/x86/kernel/kvmclock.c
> @@ -151,14 +151,10 @@ bool kvm_check_and_clear_guest_paused(void)
>
> static int kvm_cs_enable(struct clocksource *cs)
> {
> - /*
> - * TSC clocksource should be used for a guest with Secure TSC enabled,
> - * taint the kernel and warn when the user changes the clocksource to
> - * kvm-clock.
> - */
> + /* Only the TSC should be used in a Secure TSC guest. */
> if (cc_platform_has(CC_ATTR_GUEST_SNP_SECURE_TSC)) {
> - add_taint(TAINT_WARN, LOCKDEP_STILL_OK);
> - WARN_ONCE(1, "For Secure TSC guest, changing the clocksource is not allowed!\n");
> + WARN_ONCE(1, "Secure TSC guest, changing the clocksource is not allowed!\n");
> + return 1;
> }
>
> vclocks_set_used(VDSO_CLOCKMODE_PVCLOCK);
>
> ?
Works as expected:
$ echo 'kvm-clock' > /sys/devices/system/clocksource/clocksource0/current_clocksource
[ 30.333603] ------------[ cut here ]------------
[ 30.334802] Secure TSC guest, changing the clocksource is not allowed!
[ 30.336460] WARNING: CPU: 0 PID: 19 at arch/x86/kernel/kvmclock.c:156 kvm_cs_enable+0x57/0x70
Regards
Nikunj
The following commit has been merged into the x86/sev branch of tip:
Commit-ID: 73bbf3b0fbba9aa27fef07a1fbd837661a863f03
Gitweb: https://git.kernel.org/tip/73bbf3b0fbba9aa27fef07a1fbd837661a863f03
Author: Nikunj A Dadhania <nikunj@amd.com>
AuthorDate: Mon, 06 Jan 2025 18:16:30 +05:30
Committer: Borislav Petkov (AMD) <bp@alien8.de>
CommitterDate: Wed, 08 Jan 2025 21:26:19 +01:00
x86/tsc: Init the TSC for Secure TSC guests
Use the GUEST_TSC_FREQ MSR to discover the TSC frequency instead of
relying on kvm-clock based frequency calibration. Override both CPU and
TSC frequency calibration callbacks with securetsc_get_tsc_khz(). Since
the difference between CPU base and TSC frequency does not apply in this
case, the same callback is being used.
[ bp: Carve out from
https://lore.kernel.org/r/20250106124633.1418972-11-nikunj@amd.com ]
Signed-off-by: Nikunj A Dadhania <nikunj@amd.com>
Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de>
Link: https://lore.kernel.org/r/20250106124633.1418972-11-nikunj@amd.com
---
arch/x86/coco/sev/core.c | 21 +++++++++++++++++++++
arch/x86/include/asm/sev.h | 2 ++
arch/x86/kernel/tsc.c | 4 ++++
3 files changed, 27 insertions(+)
diff --git a/arch/x86/coco/sev/core.c b/arch/x86/coco/sev/core.c
index 106bded..65d676c 100644
--- a/arch/x86/coco/sev/core.c
+++ b/arch/x86/coco/sev/core.c
@@ -103,6 +103,7 @@ static u64 secrets_pa __ro_after_init;
*/
static u64 snp_tsc_scale __ro_after_init;
static u64 snp_tsc_offset __ro_after_init;
+static u64 snp_tsc_freq_khz __ro_after_init;
/* #VC handler runtime per-CPU data */
struct sev_es_runtime_data {
@@ -3278,3 +3279,23 @@ void __init snp_secure_tsc_prepare(void)
pr_debug("SecureTSC enabled");
}
+
+static unsigned long securetsc_get_tsc_khz(void)
+{
+ return snp_tsc_freq_khz;
+}
+
+void __init snp_secure_tsc_init(void)
+{
+ unsigned long long tsc_freq_mhz;
+
+ if (!cc_platform_has(CC_ATTR_GUEST_SNP_SECURE_TSC))
+ return;
+
+ setup_force_cpu_cap(X86_FEATURE_TSC_KNOWN_FREQ);
+ rdmsrl(MSR_AMD64_GUEST_TSC_FREQ, tsc_freq_mhz);
+ snp_tsc_freq_khz = (unsigned long)(tsc_freq_mhz * 1000);
+
+ x86_platform.calibrate_cpu = securetsc_get_tsc_khz;
+ x86_platform.calibrate_tsc = securetsc_get_tsc_khz;
+}
diff --git a/arch/x86/include/asm/sev.h b/arch/x86/include/asm/sev.h
index bdcdaac..5d9685f 100644
--- a/arch/x86/include/asm/sev.h
+++ b/arch/x86/include/asm/sev.h
@@ -482,6 +482,7 @@ int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req
struct snp_guest_request_ioctl *rio);
void __init snp_secure_tsc_prepare(void);
+void __init snp_secure_tsc_init(void);
#else /* !CONFIG_AMD_MEM_ENCRYPT */
@@ -524,6 +525,7 @@ static inline void snp_msg_free(struct snp_msg_desc *mdesc) { }
static inline int snp_send_guest_request(struct snp_msg_desc *mdesc, struct snp_guest_req *req,
struct snp_guest_request_ioctl *rio) { return -ENODEV; }
static inline void __init snp_secure_tsc_prepare(void) { }
+static inline void __init snp_secure_tsc_init(void) { }
#endif /* CONFIG_AMD_MEM_ENCRYPT */
diff --git a/arch/x86/kernel/tsc.c b/arch/x86/kernel/tsc.c
index 67aeaba..0864b31 100644
--- a/arch/x86/kernel/tsc.c
+++ b/arch/x86/kernel/tsc.c
@@ -30,6 +30,7 @@
#include <asm/i8259.h>
#include <asm/topology.h>
#include <asm/uv/uv.h>
+#include <asm/sev.h>
unsigned int __read_mostly cpu_khz; /* TSC clocks / usec, not used here */
EXPORT_SYMBOL(cpu_khz);
@@ -1515,6 +1516,9 @@ void __init tsc_early_init(void)
/* Don't change UV TSC multi-chassis synchronization */
if (is_early_uv_system())
return;
+
+ snp_secure_tsc_init();
+
if (!determine_cpu_tsc_frequencies(true))
return;
tsc_enable_sched_clock();
© 2016 - 2026 Red Hat, Inc.