* [PATCH v6 0/5] TDX host: kexec() support
@ 2024-09-09 8:06 Kai Huang
2024-09-09 8:06 ` [PATCH v6 1/5] x86/kexec: do unconditional WBINVD for bare-metal in stop_this_cpu() Kai Huang
` (4 more replies)
0 siblings, 5 replies; 12+ messages in thread
From: Kai Huang @ 2024-09-09 8:06 UTC (permalink / raw)
To: dave.hansen, bp, tglx, peterz, mingo, hpa, kirill.shutemov
Cc: x86, linux-kernel, pbonzini, seanjc, dan.j.williams,
thomas.lendacky, rick.p.edgecombe, isaku.yamahata, ashish.kalra,
bhe, nik.borisov, sagis
Currently kexec() support and TDX host are muturally exclusive in the
Kconfig. This series adds the TDX host kexec support so that they can
be both enabled in Kconfig.
With this series, the user can kexec (including crash kdump) to the new
kernel at any time regardless of whether TDX has been enabled in the
first kernel. One limitation is if the first kernel has ever enabled
TDX, for now the second kernel cannot use TDX. This is the future work
in my TODO list.
Hi maintainers,
This series aims to go through the tip tree, but I also CC'ed Sean/Paolo
due to when KVM TDX comes to play a KVM patch [*] is needed to complete
the kexec support for TDX. Also copy Dan for TDX connect.
Thanks for your time!
=== More information ===
If the kernel has ever enabled TDX, part of system memory remains TDX
private memory when kexec happens. E.g., the PAMT (Physical Address
Metadata Table) pages used by the TDX module to track each TDX memory
page's state are never freed once the TDX module is initialized. TDX
guests also have guest private memory and secure-EPT pages.
Similar to AMD SME, to support kexec the kernel needs to flush dirty
cachelines for TDX private memory before booting to the second kernel.
Also, the kernel needs to reset TDX private memory to normal (using
MOVDIR64B) before booting to the second kernel when the platform has
"partial write machine check" erratum, otherwise the second kernel may
see unexpected machine check.
The majority code change in this series handles "resetting TDX private
memory" (flushing cache part is relatively straightforward). Due to
currently the kernel doesn't have a unified way to tell whether a given
page is TDX private or not, this series chooses to only reset PAMT in
the core-kernel kexec code, but requires the in-kernel TDX users (e.g.,
KVM to reset the TDX private pages that they manage (see [*]).
This series also covers crash kexec, but no special handling is needed
for crash kexec:
1) kdump kernel uses reserved memory from the first kernel, but the
reserved memory will never be used as TDX memory.
2) /proc/vmcore in the kdump kernel will only be used for read, but read
itself won't poison TDX private memory thus won't cause unexpected
machine check (only "partial write" will).
Note, if the first kernel has ever enabled TDX, after kexec the second
kernel for now cannot use TDX anymore. This is because when the second
kernel tries to initialize TDX module it fails on the first SEAMCALL.
More (non-trivial) work will be needed for the second kernel to use TDX,
e.g., one solution is to just reload the TDX module from the location
where BIOS loads the TDX module (/boot/efi/EFI/TDX/). This series
doesn't cover this, but leave this as future work.
v5 -> v6:
- Fixed the issue when rebasing to latest tip/master, conflicting with
commit 93c1800b3799 ("x86/kexec: Fix bug with call depth tracking").
- Use cpu_feature_enabled() instead of boot_cpu_has() -- Boris.
- Improve the coverletter to point out if the first kernel has enabled
TDX the second kernel cannot use TDX anymore, and this will be a
future work (as asked by Sagi in the v5).
v5: https://lore.kernel.org/all/47dbc3b5dd6bd7cc3fa94ffe770e22419daf1d01.camel@intel.com/T/
v4 -> v5:
- Rebase to tip/master.
- Remove the TDX-specific callback due to no need to reset TDX private
memory for crash kexec.
- Add a new patch to make module status immutable in reboot notifier
(split from v1) in order to use module status to tell the presence of
TDX private memory.
- Minor changelog updates, trivial comments improvements.
- Add Tom's Reviewed-by tag.
v4: https://lore.kernel.org/all/cover.1713439632.git.kai.huang@intel.com/
v3 -> v4:
- Updated changelog and comments of patch 1/2 per comments from
Kirill and Tom (see specific patch for details).
v3: https://lore.kernel.org/linux-kernel/cover.1712493366.git.kai.huang@intel.com/
v2 -> v3:
- Change to only do WBINVD for bare-metal, as Kirill/Tom pointed out
WBINVD in TDX guests and SEV-ES/SEV-SNP guests triggers #VE.
v2: https://lore.kernel.org/linux-kernel/cover.1710811610.git.kai.huang@intel.com/
v1 -> v2:
- Do unconditional WBINVD during kexec() -- Boris
- Change to cover crash kexec() -- Rick
- Add a new patch (last one) to add a mechanism to reset all TDX private
pages due to having to cover crash kexec().
- Other code improvements -- Dave
- Rebase to latest tip/master.
v1: https://lore.kernel.org/linux-kernel/cover.1706698706.git.kai.huang@intel.com/
[*]: https://github.com/intel/tdx/commit/f5ef6cf63e34c5364cd88df52f91f05e72cb49b2
Kai Huang (5):
x86/kexec: do unconditional WBINVD for bare-metal in stop_this_cpu()
x86/kexec: do unconditional WBINVD for bare-metal in relocate_kernel()
x86/virt/tdx: Make module initializatiton state immutable in reboot
notifier
x86/kexec: Reset TDX private memory on platforms with TDX erratum
x86/virt/tdx: Remove the !KEXEC_CORE dependency
arch/x86/Kconfig | 1 -
arch/x86/include/asm/kexec.h | 2 +-
arch/x86/include/asm/tdx.h | 2 +
arch/x86/kernel/machine_kexec_64.c | 32 ++++++++----
arch/x86/kernel/process.c | 19 ++++---
arch/x86/kernel/relocate_kernel_64.S | 19 +++++--
arch/x86/virt/vmx/tdx/tdx.c | 78 ++++++++++++++++++++++++++++
7 files changed, 127 insertions(+), 26 deletions(-)
base-commit: d45aab436cf06544abeeffc607110f559a3af3b4
--
2.46.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v6 1/5] x86/kexec: do unconditional WBINVD for bare-metal in stop_this_cpu()
2024-09-09 8:06 [PATCH v6 0/5] TDX host: kexec() support Kai Huang
@ 2024-09-09 8:06 ` Kai Huang
2024-09-09 8:06 ` [PATCH v6 2/5] x86/kexec: do unconditional WBINVD for bare-metal in relocate_kernel() Kai Huang
` (3 subsequent siblings)
4 siblings, 0 replies; 12+ messages in thread
From: Kai Huang @ 2024-09-09 8:06 UTC (permalink / raw)
To: dave.hansen, bp, tglx, peterz, mingo, hpa, kirill.shutemov
Cc: x86, linux-kernel, pbonzini, seanjc, dan.j.williams,
thomas.lendacky, rick.p.edgecombe, isaku.yamahata, ashish.kalra,
bhe, nik.borisov, sagis, Dave Young
TL;DR:
Change to do unconditional WBINVD in stop_this_cpu() for bare metal
to cover kexec support for both AMD SME and Intel TDX, despite there
_was_ some issue preventing from doing so but now it has been fixed.
Long version:
Both AMD SME and Intel TDX can leave caches in an incoherent state due
to memory encryption, which can lead to silent memory corruption during
kexec. To address this issue, it is necessary to flush the caches
before jumping to the second kernel.
Currently, the kernel only performs WBINVD in stop_this_cpu() when SME
is supported by hardware. To support TDX, instead of adding one more
vendor-specific check, it is proposed to perform unconditional WBINVD.
Kexec() is a slow path, and the additional WBINVD is acceptable for the
sake of simplicity and maintainability.
It is important to note that WBINVD should only be done for bare-metal
scenarios, as TDX guests and SEV-ES/SEV-SNP guests may not handle the
unexpected exception (#VE or #VC) caused by WBINVD.
Note:
Historically, there _was_ an issue preventing doing unconditional WBINVD
but that has been fixed.
When SME kexec() support was initially added in commit
bba4ed011a52: ("x86/mm, kexec: Allow kexec to be used with SME")
WBINVD was done unconditionally. However since then some issues were
reported that different Intel systems would hang or reset due to that
commit.
To try to fix, a later commit
f23d74f6c66c: ("x86/mm: Rework wbinvd, hlt operation in stop_this_cpu()")
then changed to only do WBINVD when hardware supports SME.
While this commit made the reported issues go away, it didn't pinpoint
the root cause. Also, it forgot to handle a corner case[*], which
resulted in the reveal of the root cause and the final fix by commit
1f5e7eb7868e: ("x86/smp: Make stop_other_cpus() more robust")
See [1][2] for more information.
Further testing of doing unconditional WBINVD based on the above fix on
the problematic machines (that issues were originally reported)
confirmed the issues couldn't be reproduced.
See [3][4] for more information.
Therefore, it is safe to do unconditional WBINVD for bare-metal now.
[*] The commit didn't check whether the CPUID leaf is available or not.
Making unsupported CPUID leaf on Intel returns garbage resulting in
unintended WBINVD which caused some issue (followed by the analysis and
the reveal of the final root cause). The corner case was independently
fixed by commit
9b040453d444: ("x86/smp: Dont access non-existing CPUID leaf")
[1]: https://lore.kernel.org/lkml/CALu+AoQKmeixJdkO07t7BtttN7v3RM4_aBKi642bQ3fTBbSAVg@mail.gmail.com/T/#m300f3f9790850b5daa20a71abcc200ae8d94a12a
[2]: https://lore.kernel.org/lkml/CALu+AoQKmeixJdkO07t7BtttN7v3RM4_aBKi642bQ3fTBbSAVg@mail.gmail.com/T/#ma7263a7765483db0dabdeef62a1110940e634846
[3]: https://lore.kernel.org/lkml/CALu+AoQKmeixJdkO07t7BtttN7v3RM4_aBKi642bQ3fTBbSAVg@mail.gmail.com/T/#mc043191f2ff860d649c8466775dc61ac1e0ae320
[4]: https://lore.kernel.org/lkml/CALu+AoQKmeixJdkO07t7BtttN7v3RM4_aBKi642bQ3fTBbSAVg@mail.gmail.com/T/#md23f1a8f6afcc59fa2b0ac1967f18e418e24347c
Signed-off-by: Kai Huang <kai.huang@intel.com>
Suggested-by: Borislav Petkov <bp@alien8.de>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Dave Young <dyoung@redhat.com>
Reviewed-by: Tom Lendacky <thomas.lendacky@amd.com>
---
v5 -> v6:
- No change
v4 -> v5:
- Add Tom's tag
v3 -> v4:
- Update part of changelog based on Kirill's version (with minor tweak).
- Use "exception (#VE or #VC)" for TDX and SEV-ES/SEV-SNP in changelog
and comments. (Kirill, Tom)
- Point out "WBINVD is not necessary for TDX and SEV-ES/SEV-SNP guests"
in the comment. (Tom)
v2 -> v3:
- Change to only do WBINVD for bare metal
---
arch/x86/kernel/process.c | 19 +++++++++----------
1 file changed, 9 insertions(+), 10 deletions(-)
diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c
index f63f8fd00a91..d1a20501e686 100644
--- a/arch/x86/kernel/process.c
+++ b/arch/x86/kernel/process.c
@@ -813,18 +813,17 @@ void __noreturn stop_this_cpu(void *dummy)
mcheck_cpu_clear(c);
/*
- * Use wbinvd on processors that support SME. This provides support
- * for performing a successful kexec when going from SME inactive
- * to SME active (or vice-versa). The cache must be cleared so that
- * if there are entries with the same physical address, both with and
- * without the encryption bit, they don't race each other when flushed
- * and potentially end up with the wrong entry being committed to
- * memory.
+ * The kernel could leave caches in incoherent state on SME/TDX
+ * capable platforms. Flush cache to avoid silent memory
+ * corruption for these platforms.
*
- * Test the CPUID bit directly because the machine might've cleared
- * X86_FEATURE_SME due to cmdline options.
+ * stop_this_cpu() isn't a fast path, just do WBINVD for bare-metal
+ * to cover both SME and TDX. It isn't necessary to perform WBINVD
+ * in a guest and performing one could result in an exception (#VE
+ * or #VC) for a TDX or SEV-ES/SEV-SNP guest that the guest may
+ * not be able to handle (e.g., TDX guest panics if it sees #VE).
*/
- if (c->extended_cpuid_level >= 0x8000001f && (cpuid_eax(0x8000001f) & BIT(0)))
+ if (!boot_cpu_has(X86_FEATURE_HYPERVISOR))
native_wbinvd();
/*
--
2.46.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 2/5] x86/kexec: do unconditional WBINVD for bare-metal in relocate_kernel()
2024-09-09 8:06 [PATCH v6 0/5] TDX host: kexec() support Kai Huang
2024-09-09 8:06 ` [PATCH v6 1/5] x86/kexec: do unconditional WBINVD for bare-metal in stop_this_cpu() Kai Huang
@ 2024-09-09 8:06 ` Kai Huang
2024-09-09 13:56 ` Kaplan, David
2024-09-09 8:06 ` [PATCH v6 3/5] x86/virt/tdx: Make module initializatiton state immutable in reboot notifier Kai Huang
` (2 subsequent siblings)
4 siblings, 1 reply; 12+ messages in thread
From: Kai Huang @ 2024-09-09 8:06 UTC (permalink / raw)
To: dave.hansen, bp, tglx, peterz, mingo, hpa, kirill.shutemov
Cc: x86, linux-kernel, pbonzini, seanjc, dan.j.williams,
thomas.lendacky, rick.p.edgecombe, isaku.yamahata, ashish.kalra,
bhe, nik.borisov, sagis, Dave Young, David Kaplan
Both SME and TDX can leave caches in incoherent state due to memory
encryption. During kexec, the caches must be flushed before jumping to
the second kernel to avoid silent memory corruption to the second kernel.
During kexec, the WBINVD in stop_this_cpu() flushes caches for all
remote cpus when they are being stopped. For SME, the WBINVD in
relocate_kernel() flushes the cache for the last running cpu (which is
executing the kexec).
Similarly, to support kexec for TDX host, after stopping all remote cpus
with cache flushed, the kernel needs to flush cache for the last running
cpu.
Use the existing WBINVD in relocate_kernel() to cover TDX host as well.
However, instead of sprinkling around vendor-specific checks, just do
unconditional WBINVD to cover both SME and TDX. Kexec is not a fast path
so having one additional WBINVD for platforms w/o SME/TDX is acceptable.
But only do WBINVD for bare-metal because TDX guests and SEV-ES/SEV-SNP
guests will get unexpected (and yet unnecessary) exception (#VE or #VC)
which the kernel is unable to handle at this stage.
Note commit 93c1800b3799 ("x86/kexec: Fix bug with call depth tracking")
moved calling 'cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT)' as an argument
of relocate_kernel() to an earlier place before load_segments() by
adding a variable 'host_mem_enc_active'. The reason was the call to
cc_platform_has() after load_segments() caused a fault and system crash
when call depth tracking is active because load_segments() resets GS to
0 but call depth tracking uses per-CPU variable to operate.
Use !cpu_feature_enabled(X86_FEATURE_HYPERVISOR) to check whether the
kernel runs on bare-metal. cpu_feature_enabled() is always inline but
not a function call, thus it is safe to use it after load_segments()
when call depth tracking is enabled. Remove the 'host_mem_enc_active'
variable and use cpu_feature_enabled() directly as the argument when
calling relocate_kernel().
Signed-off-by: Kai Huang <kai.huang@intel.com>
Reviewed-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
Cc: Tom Lendacky <thomas.lendacky@amd.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: David Kaplan <david.kaplan@amd.com>
Reviewed-by: Tom Lendacky <thomas.lendacky@amd.com>
---
v5 - >v6:
- Use cpu_feature_enabled() instead of boot_cpu_has() - Boris
- Resolve rebase conflict with commit 93c1800b3799 ("x86/kexec: Fix bug
with call depth tracking")
v4 -> v5:
- Add Tom's tag
v3 -> v4:
- Use "exception (#VE or #VC)" for TDX and SEV-ES/SEV-SNP in changelog
and comments. (Kirill, Tom)
- "Save the bare_metal" -> "Save the bare_metal flag" (Tom)
- Point out "WBINVD is not necessary for TDX and SEV-ES/SEV-SNP guests"
in the comment. (Tom)
v2 -> v3:
- Change to only do WBINVD for bare metal
---
arch/x86/include/asm/kexec.h | 2 +-
arch/x86/kernel/machine_kexec_64.c | 9 +--------
arch/x86/kernel/relocate_kernel_64.S | 19 +++++++++++++++----
3 files changed, 17 insertions(+), 13 deletions(-)
diff --git a/arch/x86/include/asm/kexec.h b/arch/x86/include/asm/kexec.h
index ae5482a2f0ca..b3429c70847d 100644
--- a/arch/x86/include/asm/kexec.h
+++ b/arch/x86/include/asm/kexec.h
@@ -128,7 +128,7 @@ relocate_kernel(unsigned long indirection_page,
unsigned long page_list,
unsigned long start_address,
unsigned int preserve_context,
- unsigned int host_mem_enc_active);
+ unsigned int bare_metal);
#endif
#define ARCH_HAS_KIMAGE_ARCH
diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c
index 9c9ac606893e..225242840467 100644
--- a/arch/x86/kernel/machine_kexec_64.c
+++ b/arch/x86/kernel/machine_kexec_64.c
@@ -322,16 +322,9 @@ void machine_kexec_cleanup(struct kimage *image)
void machine_kexec(struct kimage *image)
{
unsigned long page_list[PAGES_NR];
- unsigned int host_mem_enc_active;
int save_ftrace_enabled;
void *control_page;
- /*
- * This must be done before load_segments() since if call depth tracking
- * is used then GS must be valid to make any function calls.
- */
- host_mem_enc_active = cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT);
-
#ifdef CONFIG_KEXEC_JUMP
if (image->preserve_context)
save_processor_state();
@@ -392,7 +385,7 @@ void machine_kexec(struct kimage *image)
(unsigned long)page_list,
image->start,
image->preserve_context,
- host_mem_enc_active);
+ !cpu_feature_enabled(X86_FEATURE_HYPERVISOR));
#ifdef CONFIG_KEXEC_JUMP
if (image->preserve_context)
diff --git a/arch/x86/kernel/relocate_kernel_64.S b/arch/x86/kernel/relocate_kernel_64.S
index e9e88c342f75..19821c3fbc46 100644
--- a/arch/x86/kernel/relocate_kernel_64.S
+++ b/arch/x86/kernel/relocate_kernel_64.S
@@ -52,7 +52,7 @@ SYM_CODE_START_NOALIGN(relocate_kernel)
* %rsi page_list
* %rdx start address
* %rcx preserve_context
- * %r8 host_mem_enc_active
+ * %r8 bare_metal
*/
/* Save the CPU context, used for jumping back */
@@ -80,7 +80,7 @@ SYM_CODE_START_NOALIGN(relocate_kernel)
pushq $0
popfq
- /* Save SME active flag */
+ /* Save the bare_metal flag */
movq %r8, %r12
/*
@@ -161,9 +161,20 @@ SYM_CODE_START_LOCAL_NOALIGN(identity_mapped)
movq %r9, %cr3
/*
- * If SME is active, there could be old encrypted cache line
+ * The kernel could leave caches in incoherent state on SME/TDX
+ * capable platforms. Just do unconditional WBINVD to avoid
+ * silent memory corruption to the new kernel for these platforms.
+ *
+ * For SME, need to flush cache here before copying the kernel.
+ * When it is active, there could be old encrypted cache line
* entries that will conflict with the now unencrypted memory
- * used by kexec. Flush the caches before copying the kernel.
+ * used by kexec.
+ *
+ * Do WBINVD for bare-metal only to cover both SME and TDX. It
+ * isn't necessary to perform a WBINVD in a guest and performing
+ * one could result in an exception (#VE or #VC) for a TDX or
+ * SEV-ES/SEV-SNP guest that can crash the guest since, at this
+ * stage, the kernel has torn down the IDT.
*/
testq %r12, %r12
jz .Lsme_off
--
2.46.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 3/5] x86/virt/tdx: Make module initializatiton state immutable in reboot notifier
2024-09-09 8:06 [PATCH v6 0/5] TDX host: kexec() support Kai Huang
2024-09-09 8:06 ` [PATCH v6 1/5] x86/kexec: do unconditional WBINVD for bare-metal in stop_this_cpu() Kai Huang
2024-09-09 8:06 ` [PATCH v6 2/5] x86/kexec: do unconditional WBINVD for bare-metal in relocate_kernel() Kai Huang
@ 2024-09-09 8:06 ` Kai Huang
2024-09-09 8:06 ` [PATCH v6 4/5] x86/kexec: Reset TDX private memory on platforms with TDX erratum Kai Huang
2024-09-09 8:06 ` [PATCH v6 5/5] x86/virt/tdx: Remove the !KEXEC_CORE dependency Kai Huang
4 siblings, 0 replies; 12+ messages in thread
From: Kai Huang @ 2024-09-09 8:06 UTC (permalink / raw)
To: dave.hansen, bp, tglx, peterz, mingo, hpa, kirill.shutemov
Cc: x86, linux-kernel, pbonzini, seanjc, dan.j.williams,
thomas.lendacky, rick.p.edgecombe, isaku.yamahata, ashish.kalra,
bhe, nik.borisov, sagis
If the kernel has ever enabled TDX, part of system memory remains TDX
private memory when kexec happens. E.g., the PAMT (Physical Address
Metadata Table) pages used by the TDX module to track each TDX memory
page's state are never freed once the TDX module is initialized.
In kexec, the kernel will need to convert all TDX private pages back to
normal when the platform has the TDX "partial write machine check"
erratum. Such conversion will need to be done after stopping all remote
CPUs so that no more TDX activity can possibly happen.
Register a reboot notifier to make the TDX module initialization state
immutable during the preparation phase of kexec, so that the kernel can
later use module state to determine whether it is possible for the
system to have any TDX private page. Otherwise, the remote CPU could be
stopped when it is in the middle of module initialization and the module
state wouldn't be able to reflect this.
Specifically, upon receiving the reboot notifier, stop further module
initialization if the kernel hasn't enabled TDX yet. If there's any
other thread trying to initialize TDX module, wait until the ongoing
module initialization to finish.
The reboot notifier is triggered when the kernel goes to reboot, kexec,
halt or shutdown. In any case, there's no need to allow the kernel to
continue to initialize the TDX module anyway (if not done yet).
Signed-off-by: Kai Huang <kai.huang@intel.com>
---
v5 -> v6:
- No change
v4 -> v5:
- New patch to split the 'tdx_rebooting' around reboot notifier (Dave).
---
arch/x86/virt/vmx/tdx/tdx.c | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index 4e2b2e2ac9f9..c33417fe4086 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -27,6 +27,7 @@
#include <linux/log2.h>
#include <linux/acpi.h>
#include <linux/suspend.h>
+#include <linux/reboot.h>
#include <asm/page.h>
#include <asm/special_insns.h>
#include <asm/msr-index.h>
@@ -52,6 +53,8 @@ static DEFINE_MUTEX(tdx_module_lock);
/* All TDX-usable memory regions. Protected by mem_hotplug_lock. */
static LIST_HEAD(tdx_memlist);
+static bool tdx_rebooting;
+
typedef void (*sc_err_func_t)(u64 fn, u64 err, struct tdx_module_args *args);
static inline void seamcall_err(u64 fn, u64 err, struct tdx_module_args *args)
@@ -1185,6 +1188,9 @@ static int __tdx_enable(void)
{
int ret;
+ if (tdx_rebooting)
+ return -EINVAL;
+
ret = init_tdx_module();
if (ret) {
pr_err("module initialization failed (%d)\n", ret);
@@ -1418,6 +1424,21 @@ static struct notifier_block tdx_memory_nb = {
.notifier_call = tdx_memory_notifier,
};
+static int tdx_reboot_notifier(struct notifier_block *nb, unsigned long mode,
+ void *unused)
+{
+ /* Wait for ongoing TDX initialization to finish */
+ mutex_lock(&tdx_module_lock);
+ tdx_rebooting = true;
+ mutex_unlock(&tdx_module_lock);
+
+ return NOTIFY_OK;
+}
+
+static struct notifier_block tdx_reboot_nb = {
+ .notifier_call = tdx_reboot_notifier,
+};
+
static void __init check_tdx_erratum(void)
{
/*
@@ -1472,6 +1493,14 @@ void __init tdx_init(void)
return;
}
+ err = register_reboot_notifier(&tdx_reboot_nb);
+ if (err) {
+ pr_err("initialization failed: register_reboot_notifier() failed (%d)\n",
+ err);
+ unregister_memory_notifier(&tdx_memory_nb);
+ return;
+ }
+
#if defined(CONFIG_ACPI) && defined(CONFIG_SUSPEND)
pr_info("Disable ACPI S3. Turn off TDX in the BIOS to use ACPI S3.\n");
acpi_suspend_lowlevel = NULL;
--
2.46.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 4/5] x86/kexec: Reset TDX private memory on platforms with TDX erratum
2024-09-09 8:06 [PATCH v6 0/5] TDX host: kexec() support Kai Huang
` (2 preceding siblings ...)
2024-09-09 8:06 ` [PATCH v6 3/5] x86/virt/tdx: Make module initializatiton state immutable in reboot notifier Kai Huang
@ 2024-09-09 8:06 ` Kai Huang
2024-09-09 8:06 ` [PATCH v6 5/5] x86/virt/tdx: Remove the !KEXEC_CORE dependency Kai Huang
4 siblings, 0 replies; 12+ messages in thread
From: Kai Huang @ 2024-09-09 8:06 UTC (permalink / raw)
To: dave.hansen, bp, tglx, peterz, mingo, hpa, kirill.shutemov
Cc: x86, linux-kernel, pbonzini, seanjc, dan.j.williams,
thomas.lendacky, rick.p.edgecombe, isaku.yamahata, ashish.kalra,
bhe, nik.borisov, sagis
TL;DR:
On the platforms with TDX "partial write machine check" erratum, during
kexec, convert TDX private memory back to normal before jumping to the
second kernel to avoid the second kernel potentially seeing unexpected
machine check.
Long version:
The first few generations of TDX hardware have an erratum. A partial
write to a TDX private memory cacheline will silently "poison" the
line. Subsequent reads will consume the poison and generate a machine
check. According to the TDX hardware spec, neither of these things
should have happened.
== Background ==
Virtually all kernel memory accesses operations happen in full
cachelines. In practice, writing a "byte" of memory usually reads a 64
byte cacheline of memory, modifies it, then writes the whole line back.
Those operations do not trigger this problem.
This problem is triggered by "partial" writes where a write transaction
of less than cacheline lands at the memory controller. The CPU does
these via non-temporal write instructions (like MOVNTI), or through
UC/WC memory mappings. The issue can also be triggered away from the
CPU by devices doing partial writes via DMA.
== Problem ==
A fast warm reset doesn't reset TDX private memory. Kexec() can also
boot into the new kernel directly. Thus if the old kernel has left any
TDX private pages on the platform with this erratum, the new kernel
might get unexpected machine check.
Note that w/o this erratum any kernel read/write on TDX private memory
should never cause machine check, thus it's OK for the old kernel to
leave TDX private pages as is.
Also note only the normal kexec needs to worry about this problem, but
not the crash kexec: 1) The kdump kernel only uses the special memory
reserved by the first kernel, and the reserved memory can never be used
by TDX in the first kernel; 2) The /proc/vmcore, which reflects the
first (crashed) kernel's memory, is only for read. The read will never
"poison" TDX memory thus cause unexpected machine check (only partial
write does).
== Solution ==
In short, with this erratum, the kernel needs to explicitly convert all
TDX private pages back to normal (using MOVDIR64B to reset these pages)
to give the new kernel a clean slate after kexec().
The BIOS is also expected to disable fast warm reset as a workaround to
this erratum, thus this implementation doesn't try to reset TDX private
memory for the normal reboot case in the kernel but depends on the BIOS
to enable the workaround.
Reset TDX private pages in machine_kexec() so that: 1) all remote cpus
are stopped with cache flushed and there's no more TDX activity; 2) no
memory reset overhead for the normal reboot case since the BIOS is
expected to turn on the workaround.
There are different types of TDX private pages. The TDX module itself
uses PAMT (Physical Address Metadata Table) to track each TDX memory
page's state. TDX guests also have guest private memory and secure-EPT
pages.
It would be ideal to reset all types of TDX private memory once for all
in machine_kexec(), but there are practical problems to do so:
1) There's no existing infrastructure to track TDX private pages;
2) It's not feasible to query the TDX module about page type, because
VMX, which making SEAMCALL requires, has already been disabled;
3) Even if it is feasible to query the TDX module, the result may not be
accurate. E.g., the remote CPU could be stopped right before the
MOVDIR64B.
One temporary solution is to blindly convert all memory pages, but it's
problematic to do so too, because not all pages are mapped as writable
in the direct mapping. It can be done by switching to the identical
mapping created for kexec(), or a new page table, but the complication
is overkill.
Therefore, rather than doing something dramatic, only reset PAMT pages
in machine_kexec(). All the in-kernel TDX users (e.g., KVM) need to
reset TDX private pages that they manage before the machine_kexec() by
registering either the reboot notifier or the syscore shutdown ops.
Signed-off-by: Kai Huang <kai.huang@intel.com>
Reviewed-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
---
v5 -> v6:
- No change
v4 -> v5:
- Remove the TDX-specific notifier, since there's no need to handle
crash kexec specially.
- Minor update to changelog and comments.
v3 -> v4:
- No change
v2 -> v3:
- No change
v1 -> v2:
- Remove using reboot notifier to stop TDX module as it doesn't
cover crash kexec. Change to use a variable with barrier instead.
(Rick)
- Introduce kexec_save_processor_start() to make code better, and
make the comment around calling site of tdx_reset_memory() more
concise. (Dave)
- Mention cache for all other cpus have been flushed around
native_wbinvd() in tdx_reset_memory(). (Dave)
- Remove the extended alternaties discussion from the comment, but leave
it in the changelog. Point out what does current code do and point out
risk. (Dave)
---
arch/x86/include/asm/tdx.h | 2 ++
arch/x86/kernel/machine_kexec_64.c | 27 +++++++++++++---
arch/x86/virt/vmx/tdx/tdx.c | 49 ++++++++++++++++++++++++++++++
3 files changed, 74 insertions(+), 4 deletions(-)
diff --git a/arch/x86/include/asm/tdx.h b/arch/x86/include/asm/tdx.h
index eba178996d84..ed3ac9a8a079 100644
--- a/arch/x86/include/asm/tdx.h
+++ b/arch/x86/include/asm/tdx.h
@@ -116,11 +116,13 @@ static inline u64 sc_retry(sc_func_t func, u64 fn,
int tdx_cpu_enable(void);
int tdx_enable(void);
const char *tdx_dump_mce_info(struct mce *m);
+void tdx_reset_memory(void);
#else
static inline void tdx_init(void) { }
static inline int tdx_cpu_enable(void) { return -ENODEV; }
static inline int tdx_enable(void) { return -ENODEV; }
static inline const char *tdx_dump_mce_info(struct mce *m) { return NULL; }
+static inline void tdx_reset_memory(void) { }
#endif /* CONFIG_INTEL_TDX_HOST */
#endif /* !__ASSEMBLY__ */
diff --git a/arch/x86/kernel/machine_kexec_64.c b/arch/x86/kernel/machine_kexec_64.c
index 225242840467..4195b9ee007b 100644
--- a/arch/x86/kernel/machine_kexec_64.c
+++ b/arch/x86/kernel/machine_kexec_64.c
@@ -29,6 +29,7 @@
#include <asm/set_memory.h>
#include <asm/cpu.h>
#include <asm/efi.h>
+#include <asm/tdx.h>
#ifdef CONFIG_ACPI
/*
@@ -315,6 +316,14 @@ void machine_kexec_cleanup(struct kimage *image)
free_transition_pgtable(image);
}
+static void kexec_save_processor_start(struct kimage *image)
+{
+#ifdef CONFIG_KEXEC_JUMP
+ if (image->preserve_context)
+ save_processor_state();
+#endif
+}
+
/*
* Do not allocate memory (or fail in any way) in machine_kexec().
* We are past the point of no return, committed to rebooting now.
@@ -325,10 +334,20 @@ void machine_kexec(struct kimage *image)
int save_ftrace_enabled;
void *control_page;
-#ifdef CONFIG_KEXEC_JUMP
- if (image->preserve_context)
- save_processor_state();
-#endif
+ kexec_save_processor_start(image);
+
+ /*
+ * Convert TDX private memory back to normal (when needed) to
+ * avoid the second kernel potentially seeing unexpected machine
+ * check.
+ *
+ * However skip this when preserve_context is on. By reaching
+ * here, TDX (if ever got enabled by the kernel) has survived
+ * from the suspend when preserve_context is on, and it can
+ * continue to work after jumping back from the second kernel.
+ */
+ if (!image->preserve_context)
+ tdx_reset_memory();
save_ftrace_enabled = __ftrace_enabled_save();
diff --git a/arch/x86/virt/vmx/tdx/tdx.c b/arch/x86/virt/vmx/tdx/tdx.c
index c33417fe4086..a69a65f57616 100644
--- a/arch/x86/virt/vmx/tdx/tdx.c
+++ b/arch/x86/virt/vmx/tdx/tdx.c
@@ -1518,3 +1518,52 @@ void __init tdx_init(void)
check_tdx_erratum();
}
+
+void tdx_reset_memory(void)
+{
+ if (!boot_cpu_has(X86_FEATURE_TDX_HOST_PLATFORM))
+ return;
+
+ /*
+ * Kernel read/write to TDX private memory doesn't cause
+ * machine check on hardware w/o this erratum.
+ */
+ if (!boot_cpu_has_bug(X86_BUG_TDX_PW_MCE))
+ return;
+
+ /*
+ * Converting TDX private pages back to normal must be done
+ * after all remote cpus have been stopped so that no more
+ * TDX activity can happen and caches have been flushed.
+ */
+ WARN_ON_ONCE(num_online_cpus() != 1);
+
+ /*
+ * The system can only have TDX private memory after the TDX
+ * module has been initialized. tdx_reboot_notifier() has made
+ * sure @tdx_module_status reflects the module initialization
+ * status correctly and is immutable by now thus can be read
+ * w/o holding lock.
+ */
+ if (tdx_module_status != TDX_MODULE_INITIALIZED)
+ return;
+
+ /*
+ * All remote cpus have been stopped, and their caches have
+ * been flushed in stop_this_cpu(). Now flush cache for the
+ * last running cpu _before_ converting TDX private pages.
+ */
+ native_wbinvd();
+
+ /*
+ * It's ideal to cover all types of TDX private pages here, but
+ * currently there's no unified way to tell whether a given page
+ * is TDX private page or not.
+ *
+ * Only convert PAMT here. All in-kernel TDX users (e.g., KVM)
+ * are responsible for converting TDX private pages that are
+ * managed by them by either registering reboot notifier or
+ * shutdown syscore ops.
+ */
+ tdmrs_reset_pamt_all(&tdx_tdmr_list);
+}
--
2.46.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v6 5/5] x86/virt/tdx: Remove the !KEXEC_CORE dependency
2024-09-09 8:06 [PATCH v6 0/5] TDX host: kexec() support Kai Huang
` (3 preceding siblings ...)
2024-09-09 8:06 ` [PATCH v6 4/5] x86/kexec: Reset TDX private memory on platforms with TDX erratum Kai Huang
@ 2024-09-09 8:06 ` Kai Huang
4 siblings, 0 replies; 12+ messages in thread
From: Kai Huang @ 2024-09-09 8:06 UTC (permalink / raw)
To: dave.hansen, bp, tglx, peterz, mingo, hpa, kirill.shutemov
Cc: x86, linux-kernel, pbonzini, seanjc, dan.j.williams,
thomas.lendacky, rick.p.edgecombe, isaku.yamahata, ashish.kalra,
bhe, nik.borisov, sagis
Now TDX host can work with kexec(). Remove the !KEXEC_CORE dependency.
Signed-off-by: Kai Huang <kai.huang@intel.com>
---
arch/x86/Kconfig | 1 -
1 file changed, 1 deletion(-)
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index d422247b2882..2da088202969 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -1979,7 +1979,6 @@ config INTEL_TDX_HOST
depends on X86_X2APIC
select ARCH_KEEP_MEMBLOCK
depends on CONTIG_ALLOC
- depends on !KEXEC_CORE
depends on X86_MCE
help
Intel Trust Domain Extensions (TDX) protects guest VMs from malicious
--
2.46.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* RE: [PATCH v6 2/5] x86/kexec: do unconditional WBINVD for bare-metal in relocate_kernel()
2024-09-09 8:06 ` [PATCH v6 2/5] x86/kexec: do unconditional WBINVD for bare-metal in relocate_kernel() Kai Huang
@ 2024-09-09 13:56 ` Kaplan, David
2024-09-10 2:42 ` Huang, Kai
0 siblings, 1 reply; 12+ messages in thread
From: Kaplan, David @ 2024-09-09 13:56 UTC (permalink / raw)
To: Kai Huang, dave.hansen@intel.com, bp@alien8.de,
tglx@linutronix.de, peterz@infradead.org, mingo@redhat.com,
hpa@zytor.com, kirill.shutemov@linux.intel.com
Cc: x86@kernel.org, linux-kernel@vger.kernel.org, pbonzini@redhat.com,
seanjc@google.com, dan.j.williams@intel.com, Lendacky, Thomas,
rick.p.edgecombe@intel.com, isaku.yamahata@intel.com,
Kalra, Ashish, bhe@redhat.com, nik.borisov@suse.com,
sagis@google.com, Dave Young
[AMD Official Use Only - AMD Internal Distribution Only]
> -----Original Message-----
> From: Kai Huang <kai.huang@intel.com>
> Sent: Monday, September 9, 2024 3:06 AM
> To: dave.hansen@intel.com; bp@alien8.de; tglx@linutronix.de;
> peterz@infradead.org; mingo@redhat.com; hpa@zytor.com;
> kirill.shutemov@linux.intel.com
> Cc: x86@kernel.org; linux-kernel@vger.kernel.org; pbonzini@redhat.com;
> seanjc@google.com; dan.j.williams@intel.com; Lendacky, Thomas
> <Thomas.Lendacky@amd.com>; rick.p.edgecombe@intel.com;
> isaku.yamahata@intel.com; Kalra, Ashish <Ashish.Kalra@amd.com>;
> bhe@redhat.com; nik.borisov@suse.com; sagis@google.com; Dave Young
> <dyoung@redhat.com>; Kaplan, David <David.Kaplan@amd.com>
> Subject: [PATCH v6 2/5] x86/kexec: do unconditional WBINVD for bare-metal
> in relocate_kernel()
>
>
> Note commit 93c1800b3799 ("x86/kexec: Fix bug with call depth tracking")
> moved calling 'cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT)' as an
> argument of relocate_kernel() to an earlier place before load_segments() by
> adding a variable 'host_mem_enc_active'. The reason was the call to
> cc_platform_has() after load_segments() caused a fault and system crash
> when call depth tracking is active because load_segments() resets GS to
> 0 but call depth tracking uses per-CPU variable to operate.
>
> #define ARCH_HAS_KIMAGE_ARCH
> diff --git a/arch/x86/kernel/machine_kexec_64.c
> b/arch/x86/kernel/machine_kexec_64.c
> index 9c9ac606893e..225242840467 100644
> --- a/arch/x86/kernel/machine_kexec_64.c
> +++ b/arch/x86/kernel/machine_kexec_64.c
> @@ -322,16 +322,9 @@ void machine_kexec_cleanup(struct kimage *image)
> void machine_kexec(struct kimage *image) {
> unsigned long page_list[PAGES_NR];
> - unsigned int host_mem_enc_active;
> int save_ftrace_enabled;
> void *control_page;
>
> - /*
> - * This must be done before load_segments() since if call depth tracking
> - * is used then GS must be valid to make any function calls.
> - */
> - host_mem_enc_active =
> cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT);
> -
Functionally the patch looks fine. I would suggest keeping some form of this comment though, because the limitation about not being able to make function calls after load_segments() is arguably non-obvious and this comment served as a warning for future modifications in this area.
Thanks
--David Kaplan
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v6 2/5] x86/kexec: do unconditional WBINVD for bare-metal in relocate_kernel()
2024-09-09 13:56 ` Kaplan, David
@ 2024-09-10 2:42 ` Huang, Kai
2024-09-10 2:46 ` Kaplan, David
0 siblings, 1 reply; 12+ messages in thread
From: Huang, Kai @ 2024-09-10 2:42 UTC (permalink / raw)
To: Kaplan, David, Hansen, Dave, bp@alien8.de, tglx@linutronix.de,
peterz@infradead.org, mingo@redhat.com, hpa@zytor.com,
kirill.shutemov@linux.intel.com
Cc: x86@kernel.org, linux-kernel@vger.kernel.org, pbonzini@redhat.com,
seanjc@google.com, Williams, Dan J, Lendacky, Thomas,
Edgecombe, Rick P, Yamahata, Isaku, Kalra, Ashish, bhe@redhat.com,
nik.borisov@suse.com, sagis@google.com, Dave Young
>> --- a/arch/x86/kernel/machine_kexec_64.c
>> +++ b/arch/x86/kernel/machine_kexec_64.c
>> @@ -322,16 +322,9 @@ void machine_kexec_cleanup(struct kimage *image)
>> void machine_kexec(struct kimage *image) {
>> unsigned long page_list[PAGES_NR];
>> - unsigned int host_mem_enc_active;
>> int save_ftrace_enabled;
>> void *control_page;
>>
>> - /*
>> - * This must be done before load_segments() since if call depth tracking
>> - * is used then GS must be valid to make any function calls.
>> - */
>> - host_mem_enc_active =
>> cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT);
>> -
>
> Functionally the patch looks fine. I would suggest keeping some form of this comment though, because the limitation about not being able to make function calls after load_segments() is arguably non-obvious and this comment served as a warning for future modifications in this area.
Yeah this makes sense. Thanks.
I think we can add some text to the existing comment of load_segments()
to call out this. Allow me to dig into more about call depth tracking
to understand it better -- relocate_kernel() after load_segments() seems
to be a real function call and I want to know how does it interact with
call depth tracking.
^ permalink raw reply [flat|nested] 12+ messages in thread
* RE: [PATCH v6 2/5] x86/kexec: do unconditional WBINVD for bare-metal in relocate_kernel()
2024-09-10 2:42 ` Huang, Kai
@ 2024-09-10 2:46 ` Kaplan, David
2024-09-10 9:52 ` Huang, Kai
0 siblings, 1 reply; 12+ messages in thread
From: Kaplan, David @ 2024-09-10 2:46 UTC (permalink / raw)
To: Huang, Kai, Hansen, Dave, bp@alien8.de, tglx@linutronix.de,
peterz@infradead.org, mingo@redhat.com, hpa@zytor.com,
kirill.shutemov@linux.intel.com
Cc: x86@kernel.org, linux-kernel@vger.kernel.org, pbonzini@redhat.com,
seanjc@google.com, Williams, Dan J, Lendacky, Thomas,
Edgecombe, Rick P, Yamahata, Isaku, Kalra, Ashish, bhe@redhat.com,
nik.borisov@suse.com, sagis@google.com, Dave Young
[AMD Official Use Only - AMD Internal Distribution Only]
> -----Original Message-----
> From: Huang, Kai <kai.huang@intel.com>
> Sent: Monday, September 9, 2024 9:42 PM
> To: Kaplan, David <David.Kaplan@amd.com>; Hansen, Dave
> <dave.hansen@intel.com>; bp@alien8.de; tglx@linutronix.de;
> peterz@infradead.org; mingo@redhat.com; hpa@zytor.com;
> kirill.shutemov@linux.intel.com
> Cc: x86@kernel.org; linux-kernel@vger.kernel.org; pbonzini@redhat.com;
> seanjc@google.com; Williams, Dan J <dan.j.williams@intel.com>; Lendacky,
> Thomas <Thomas.Lendacky@amd.com>; Edgecombe, Rick P
> <rick.p.edgecombe@intel.com>; Yamahata, Isaku
> <isaku.yamahata@intel.com>; Kalra, Ashish <Ashish.Kalra@amd.com>;
> bhe@redhat.com; nik.borisov@suse.com; sagis@google.com; Dave Young
> <dyoung@redhat.com>
> Subject: Re: [PATCH v6 2/5] x86/kexec: do unconditional WBINVD for bare-
> metal in relocate_kernel()
>
> Caution: This message originated from an External Source. Use proper
> caution when opening attachments, clicking links, or responding.
>
>
> >> --- a/arch/x86/kernel/machine_kexec_64.c
> >> +++ b/arch/x86/kernel/machine_kexec_64.c
> >> @@ -322,16 +322,9 @@ void machine_kexec_cleanup(struct kimage
> *image)
> >> void machine_kexec(struct kimage *image) {
> >> unsigned long page_list[PAGES_NR];
> >> - unsigned int host_mem_enc_active;
> >> int save_ftrace_enabled;
> >> void *control_page;
> >>
> >> - /*
> >> - * This must be done before load_segments() since if call depth
> tracking
> >> - * is used then GS must be valid to make any function calls.
> >> - */
> >> - host_mem_enc_active =
> >> cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT);
> >> -
> >
> > Functionally the patch looks fine. I would suggest keeping some form of
> this comment though, because the limitation about not being able to make
> function calls after load_segments() is arguably non-obvious and this
> comment served as a warning for future modifications in this area.
>
> Yeah this makes sense. Thanks.
>
> I think we can add some text to the existing comment of load_segments() to
> call out this. Allow me to dig into more about call depth tracking to
> understand it better -- relocate_kernel() after load_segments() seems to be a
> real function call and I want to know how does it interact with call depth
> tracking.
That one is explicitly ignored, see skip_addr() in arch/x86/kernel/callthunks.c
--David Kaplan
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v6 2/5] x86/kexec: do unconditional WBINVD for bare-metal in relocate_kernel()
2024-09-10 2:46 ` Kaplan, David
@ 2024-09-10 9:52 ` Huang, Kai
2024-09-12 2:21 ` Kaplan, David
0 siblings, 1 reply; 12+ messages in thread
From: Huang, Kai @ 2024-09-10 9:52 UTC (permalink / raw)
To: Hansen, Dave, bp@alien8.de, peterz@infradead.org, hpa@zytor.com,
mingo@redhat.com, tglx@linutronix.de, David.Kaplan@amd.com,
kirill.shutemov@linux.intel.com
Cc: Edgecombe, Rick P, seanjc@google.com, x86@kernel.org,
dyoung@redhat.com, sagis@google.com, linux-kernel@vger.kernel.org,
Williams, Dan J, Thomas.Lendacky@amd.com, pbonzini@redhat.com,
Ashish.Kalra@amd.com, Yamahata, Isaku, bhe@redhat.com,
nik.borisov@suse.com
On Tue, 2024-09-10 at 02:46 +0000, Kaplan, David wrote:
> [AMD Official Use Only - AMD Internal Distribution Only]
>
> > -----Original Message-----
> > From: Huang, Kai <kai.huang@intel.com>
> > Sent: Monday, September 9, 2024 9:42 PM
> > To: Kaplan, David <David.Kaplan@amd.com>; Hansen, Dave
> > <dave.hansen@intel.com>; bp@alien8.de; tglx@linutronix.de;
> > peterz@infradead.org; mingo@redhat.com; hpa@zytor.com;
> > kirill.shutemov@linux.intel.com
> > Cc: x86@kernel.org; linux-kernel@vger.kernel.org; pbonzini@redhat.com;
> > seanjc@google.com; Williams, Dan J <dan.j.williams@intel.com>; Lendacky,
> > Thomas <Thomas.Lendacky@amd.com>; Edgecombe, Rick P
> > <rick.p.edgecombe@intel.com>; Yamahata, Isaku
> > <isaku.yamahata@intel.com>; Kalra, Ashish <Ashish.Kalra@amd.com>;
> > bhe@redhat.com; nik.borisov@suse.com; sagis@google.com; Dave Young
> > <dyoung@redhat.com>
> > Subject: Re: [PATCH v6 2/5] x86/kexec: do unconditional WBINVD for bare-
> > metal in relocate_kernel()
> >
> > Caution: This message originated from an External Source. Use proper
> > caution when opening attachments, clicking links, or responding.
> >
> >
> > > > --- a/arch/x86/kernel/machine_kexec_64.c
> > > > +++ b/arch/x86/kernel/machine_kexec_64.c
> > > > @@ -322,16 +322,9 @@ void machine_kexec_cleanup(struct kimage
> > *image)
> > > > void machine_kexec(struct kimage *image) {
> > > > unsigned long page_list[PAGES_NR];
> > > > - unsigned int host_mem_enc_active;
> > > > int save_ftrace_enabled;
> > > > void *control_page;
> > > >
> > > > - /*
> > > > - * This must be done before load_segments() since if call depth
> > tracking
> > > > - * is used then GS must be valid to make any function calls.
> > > > - */
> > > > - host_mem_enc_active =
> > > > cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT);
> > > > -
> > >
> > > Functionally the patch looks fine. I would suggest keeping some form of
> > this comment though, because the limitation about not being able to make
> > function calls after load_segments() is arguably non-obvious and this
> > comment served as a warning for future modifications in this area.
> >
> > Yeah this makes sense. Thanks.
> >
> > I think we can add some text to the existing comment of load_segments() to
> > call out this. Allow me to dig into more about call depth tracking to
> > understand it better -- relocate_kernel() after load_segments() seems to be a
> > real function call and I want to know how does it interact with call depth
> > tracking.
>
> That one is explicitly ignored, see skip_addr() in arch/x86/kernel/callthunks.c
>
That was I thought too. Thanks for pointing out.
How about below?
--- a/arch/x86/kernel/machine_kexec_64.c
+++ b/arch/x86/kernel/machine_kexec_64.c
@@ -351,6 +351,11 @@ void machine_kexec(struct kimage *image)
*
* I take advantage of this here by force loading the
* segments, before I zap the gdt with an invalid value.
+ *
+ * Note this resets GS to 0. Don't make any function call after
+ * here since call depth tracking uses per-cpu variables to
+ * operate (relocate_kernel is explicitly ignored by call depth
+ * tracking).
*/
Btw, it would be very helpful if you can help to verify this patch doesn't break
call depth tracking in your environment. Thanks!
^ permalink raw reply [flat|nested] 12+ messages in thread
* RE: [PATCH v6 2/5] x86/kexec: do unconditional WBINVD for bare-metal in relocate_kernel()
2024-09-10 9:52 ` Huang, Kai
@ 2024-09-12 2:21 ` Kaplan, David
2024-09-12 10:38 ` Huang, Kai
0 siblings, 1 reply; 12+ messages in thread
From: Kaplan, David @ 2024-09-12 2:21 UTC (permalink / raw)
To: Huang, Kai, Hansen, Dave, bp@alien8.de, peterz@infradead.org,
hpa@zytor.com, mingo@redhat.com, tglx@linutronix.de,
kirill.shutemov@linux.intel.com
Cc: Edgecombe, Rick P, seanjc@google.com, x86@kernel.org,
dyoung@redhat.com, sagis@google.com, linux-kernel@vger.kernel.org,
Williams, Dan J, Lendacky, Thomas, pbonzini@redhat.com,
Kalra, Ashish, Yamahata, Isaku, bhe@redhat.com,
nik.borisov@suse.com
[AMD Official Use Only - AMD Internal Distribution Only]
> -----Original Message-----
> From: Huang, Kai <kai.huang@intel.com>
> Sent: Tuesday, September 10, 2024 4:53 AM
> To: Hansen, Dave <dave.hansen@intel.com>; bp@alien8.de;
> peterz@infradead.org; hpa@zytor.com; mingo@redhat.com;
> tglx@linutronix.de; Kaplan, David <David.Kaplan@amd.com>;
> kirill.shutemov@linux.intel.com
> Cc: Edgecombe, Rick P <rick.p.edgecombe@intel.com>; seanjc@google.com;
> x86@kernel.org; dyoung@redhat.com; sagis@google.com; linux-
> kernel@vger.kernel.org; Williams, Dan J <dan.j.williams@intel.com>;
> Lendacky, Thomas <Thomas.Lendacky@amd.com>; pbonzini@redhat.com;
> Kalra, Ashish <Ashish.Kalra@amd.com>; Yamahata, Isaku
> <isaku.yamahata@intel.com>; bhe@redhat.com; nik.borisov@suse.com
> Subject: Re: [PATCH v6 2/5] x86/kexec: do unconditional WBINVD for bare-
> metal in relocate_kernel()
>
> Caution: This message originated from an External Source. Use proper
> caution when opening attachments, clicking links, or responding.
>
>
> On Tue, 2024-09-10 at 02:46 +0000, Kaplan, David wrote:
> > [AMD Official Use Only - AMD Internal Distribution Only]
> >
> > > -----Original Message-----
> > > From: Huang, Kai <kai.huang@intel.com>
> > > Sent: Monday, September 9, 2024 9:42 PM
> > > To: Kaplan, David <David.Kaplan@amd.com>; Hansen, Dave
> > > <dave.hansen@intel.com>; bp@alien8.de; tglx@linutronix.de;
> > > peterz@infradead.org; mingo@redhat.com; hpa@zytor.com;
> > > kirill.shutemov@linux.intel.com
> > > Cc: x86@kernel.org; linux-kernel@vger.kernel.org;
> > > pbonzini@redhat.com; seanjc@google.com; Williams, Dan J
> > > <dan.j.williams@intel.com>; Lendacky, Thomas
> > > <Thomas.Lendacky@amd.com>; Edgecombe, Rick P
> > > <rick.p.edgecombe@intel.com>; Yamahata, Isaku
> > > <isaku.yamahata@intel.com>; Kalra, Ashish <Ashish.Kalra@amd.com>;
> > > bhe@redhat.com; nik.borisov@suse.com; sagis@google.com; Dave
> Young
> > > <dyoung@redhat.com>
> > > Subject: Re: [PATCH v6 2/5] x86/kexec: do unconditional WBINVD for
> > > bare- metal in relocate_kernel()
> > >
> > > Caution: This message originated from an External Source. Use proper
> > > caution when opening attachments, clicking links, or responding.
> > >
> > >
> > > > > --- a/arch/x86/kernel/machine_kexec_64.c
> > > > > +++ b/arch/x86/kernel/machine_kexec_64.c
> > > > > @@ -322,16 +322,9 @@ void machine_kexec_cleanup(struct kimage
> > > *image)
> > > > > void machine_kexec(struct kimage *image) {
> > > > > unsigned long page_list[PAGES_NR];
> > > > > - unsigned int host_mem_enc_active;
> > > > > int save_ftrace_enabled;
> > > > > void *control_page;
> > > > >
> > > > > - /*
> > > > > - * This must be done before load_segments() since if call depth
> > > tracking
> > > > > - * is used then GS must be valid to make any function calls.
> > > > > - */
> > > > > - host_mem_enc_active =
> > > > > cc_platform_has(CC_ATTR_HOST_MEM_ENCRYPT);
> > > > > -
> > > >
> > > > Functionally the patch looks fine. I would suggest keeping some
> > > > form of
> > > this comment though, because the limitation about not being able to
> > > make function calls after load_segments() is arguably non-obvious
> > > and this comment served as a warning for future modifications in this
> area.
> > >
> > > Yeah this makes sense. Thanks.
> > >
> > > I think we can add some text to the existing comment of
> > > load_segments() to call out this. Allow me to dig into more about
> > > call depth tracking to understand it better -- relocate_kernel()
> > > after load_segments() seems to be a real function call and I want to
> > > know how does it interact with call depth tracking.
> >
> > That one is explicitly ignored, see skip_addr() in
> > arch/x86/kernel/callthunks.c
> >
>
> That was I thought too. Thanks for pointing out.
>
> How about below?
>
> --- a/arch/x86/kernel/machine_kexec_64.c
> +++ b/arch/x86/kernel/machine_kexec_64.c
> @@ -351,6 +351,11 @@ void machine_kexec(struct kimage *image)
> *
> * I take advantage of this here by force loading the
> * segments, before I zap the gdt with an invalid value.
> + *
> + * Note this resets GS to 0. Don't make any function call after
> + * here since call depth tracking uses per-cpu variables to
> + * operate (relocate_kernel is explicitly ignored by call depth
> + * tracking).
> */
Looks good, thanks!
>
> Btw, it would be very helpful if you can help to verify this patch doesn't break
> call depth tracking in your environment. Thanks!
Tested it and it seemed fine (kexec worked and disassembly did not show any calls after GS is cleared, as expected).
--David Kaplan
^ permalink raw reply [flat|nested] 12+ messages in thread
* RE: [PATCH v6 2/5] x86/kexec: do unconditional WBINVD for bare-metal in relocate_kernel()
2024-09-12 2:21 ` Kaplan, David
@ 2024-09-12 10:38 ` Huang, Kai
0 siblings, 0 replies; 12+ messages in thread
From: Huang, Kai @ 2024-09-12 10:38 UTC (permalink / raw)
To: Kaplan, David, Hansen, Dave, bp@alien8.de, peterz@infradead.org,
hpa@zytor.com, mingo@redhat.com, tglx@linutronix.de,
kirill.shutemov@linux.intel.com
Cc: Edgecombe, Rick P, seanjc@google.com, x86@kernel.org,
dyoung@redhat.com, sagis@google.com, linux-kernel@vger.kernel.org,
Williams, Dan J, Lendacky, Thomas, pbonzini@redhat.com,
Kalra, Ashish, Yamahata, Isaku, bhe@redhat.com,
nik.borisov@suse.com
> > How about below?
> >
> > --- a/arch/x86/kernel/machine_kexec_64.c
> > +++ b/arch/x86/kernel/machine_kexec_64.c
> > @@ -351,6 +351,11 @@ void machine_kexec(struct kimage *image)
> > *
> > * I take advantage of this here by force loading the
> > * segments, before I zap the gdt with an invalid value.
> > + *
> > + * Note this resets GS to 0. Don't make any function call after
> > + * here since call depth tracking uses per-cpu variables to
> > + * operate (relocate_kernel is explicitly ignored by call depth
> > + * tracking).
> > */
>
> Looks good, thanks!
>
> >
> > Btw, it would be very helpful if you can help to verify this patch
> > doesn't break call depth tracking in your environment. Thanks!
>
> Tested it and it seemed fine (kexec worked and disassembly did not show any
> calls after GS is cleared, as expected).
>
Thanks! Should I add your Tested-by?
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2024-09-12 10:38 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-09-09 8:06 [PATCH v6 0/5] TDX host: kexec() support Kai Huang
2024-09-09 8:06 ` [PATCH v6 1/5] x86/kexec: do unconditional WBINVD for bare-metal in stop_this_cpu() Kai Huang
2024-09-09 8:06 ` [PATCH v6 2/5] x86/kexec: do unconditional WBINVD for bare-metal in relocate_kernel() Kai Huang
2024-09-09 13:56 ` Kaplan, David
2024-09-10 2:42 ` Huang, Kai
2024-09-10 2:46 ` Kaplan, David
2024-09-10 9:52 ` Huang, Kai
2024-09-12 2:21 ` Kaplan, David
2024-09-12 10:38 ` Huang, Kai
2024-09-09 8:06 ` [PATCH v6 3/5] x86/virt/tdx: Make module initializatiton state immutable in reboot notifier Kai Huang
2024-09-09 8:06 ` [PATCH v6 4/5] x86/kexec: Reset TDX private memory on platforms with TDX erratum Kai Huang
2024-09-09 8:06 ` [PATCH v6 5/5] x86/virt/tdx: Remove the !KEXEC_CORE dependency Kai Huang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox