Hi Sagi,

On 8/14/25 10:20 PM, Sagi Shahar wrote:
> On Tue, Aug 12, 2025 at 11:22 PM Binbin Wu <binbin...@linux.intel.com> wrote:
>>
>>
>>
>> On 8/12/2025 4:13 AM, Sean Christopherson wrote:
>>> On Thu, Aug 07, 2025, Sagi Shahar wrote:
>> [...]
>>>> +
>>>> +/*
>>>> + * Boot parameters for the TD.
>>>> + *
>>>> + * Unlike a regular VM, KVM cannot set registers such as esp, eip, etc
>>>> + * before boot, so to run selftests, these registers' values have to be
>>>> + * initialized by the TD.
>>>> + *
>>>> + * This struct is loaded in TD private memory at TD_BOOT_PARAMETERS_GPA.
>>>> + *
>>>> + * The TD boot code will read off parameters from this struct and set up 
>>>> the
>>>> + * vCPU for executing selftests.
>>>> + */
>>>> +struct __packed td_boot_parameters {
>>> None of these comments explain why these structures are __packed, and I 
>>> suspect
>>> _that_ is the most interesting/relevant information for unfamiliar readers.
>> I guess because the fields defined in this structure are accessed by 
>> hard-coded
>> offsets in boot code.
>> But as you suggested below, replicating the functionality of the kernel's
>> OFFSET() could get rid of "__packed".
>>
> 
> I agree, I think the reason for using __packed is because of the hard
> coded offsets. I tried using OFFSET() as Sean suggested but couldn't
> make it work.
> 
> I can't get the Kbuild scripts to work inside the kvm selftests
> Makefile. I tried adding the following rules based on a reference I
> found:
> 
> +include/x86/tdx/td_boot_offsets.h: lib/x86/tdx/td_boot_offsets.s
> +       $(call filechk,offsets,__TDX_BOOT_OFFSETS_H__)
> +
> +lib/x86/tdx/td_boot_offsets.s: lib/x86/tdx/td_boot_offsets.c
> +       $(call if_changed_dep,cc_s_c)
> 
> But I'm getting the following error when trying to generate the header:
> 
> /bin/sh: -c: line 1: syntax error near unexpected token `;'
> /bin/sh: -c: line 1: `set -e;  ;  printf '# cannot find fixdep (%s)\n'
>  > lib/x86/tdx/.td_boot_offsets.s.cmd; printf '# using basic dep
> data\n\n' >> lib/x86/tdx/.td_boot_offsets.s.cmd; cat
> lib/x86/tdx/.td_boot_offsets.s.d >>
> lib/x86/tdx/.td_boot_offsets.s.cmd; printf '\n%s\n'
> 'cmd_lib/x86/tdx/td_boot_offsets.s := ' >>
> lib/x86/tdx/.td_boot_offsets.s.cmd'
> make: *** [Makefile.kvm:44: lib/x86/tdx/td_boot_offsets.s] Error 2
> 

I do not believe that the selftests can directly use the Kbuild
infrastructure. From what I understand, when similar things are needed they
are duplicated. Take for example tools/build/Build.include that is included
in Makefile.kvm and contains the needed things duplicated from
scripts/Kbuild.include.

I see two options here:
a) Selftests relies on the kernel to generate the header file. This 
unfortunately
   will create dependency on kernel being built first and I expect a no-go.
b) - Duplicate the filechk (that depends on tmp-target), filechk_offsets, and 
     sed-offsets defines in from scripts/Makefile.lib to 
tools/build/Build.include
   - Duplicate include/linux/kbuild.h in tools/include/linux

(b) will allow your rule duplicated below to work:
        include/x86/tdx/td_boot_offsets.h: lib/x86/tdx/td_boot_offsets.s
               $(call filechk,offsets,__TDX_BOOT_OFFSETS_H__)

tools/build/Build.include already contains if_changed_dep and the command
it calls may just be locally defined to make the other rule you are trying work.

I played around with this a bit and found changes below to work. I certainly
admit that it is crude but "it works" as a proof-of-concept. I also look 
forward to
learn from other folks on how to to this right.

If you think something like this is useful then please consider below only as a 
proof-of-concept since it needs to be split appropriately, missing cleanups
(handling "make clean"), may not need to be TDX specific, etc.

---8<---
 tools/build/Build.include                     | 56 +++++++++++++++++++
 tools/include/linux/kbuild.h                  | 16 ++++++
 tools/testing/selftests/kvm/Makefile.kvm      | 11 ++++
 .../selftests/kvm/lib/x86/asm-tdx-offsets.c   | 28 ++++++++++
 .../selftests/kvm/lib/x86/tdx/tdcall.S        | 16 +-----
 5 files changed, 113 insertions(+), 14 deletions(-)
 create mode 100644 tools/include/linux/kbuild.h
 create mode 100644 tools/testing/selftests/kvm/lib/x86/asm-tdx-offsets.c

diff --git a/tools/build/Build.include b/tools/build/Build.include
index e45b2eb0d24a..330892a1ba15 100644
--- a/tools/build/Build.include
+++ b/tools/build/Build.include
@@ -20,6 +20,10 @@ space   := $(empty) $(empty)
 # Name of target with a '.' as filename prefix. foo/bar.o => foo/.bar.o
 dot-target = $(dir $@).$(notdir $@)
 
+###
+# Name of target with a '.tmp_' as filename prefix. foo/bar.o => foo/.tmp_bar.o
+tmp-target = $(dir $@).tmp_$(notdir $@)
+
 ###
 # filename of target with directory and extension stripped
 basetarget = $(basename $(notdir $@))
@@ -43,6 +47,58 @@ escsq = $(subst $(squote),'\$(squote)',$1)
 echo-cmd = $(if $($(quiet)cmd_$(1)),\
            echo '  $(call escsq,$($(quiet)cmd_$(1)))';)
 
+# Default sed regexp - multiline due to syntax constraints
+#
+# Use [:space:] because LLVM's integrated assembler inserts <tab> around
+# the .ascii directive whereas GCC keeps the <space> as-is.
+define sed-offsets
+       's:^[[:space:]]*\.ascii[[:space:]]*"\(.*\)".*:\1:; \
+       /^->/{s:->#\(.*\):/* \1 */:; \
+       s:^->\([^ ]*\) [\$$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:; \
+       s:->::; p;}'
+endef
+
+# Use filechk to avoid rebuilds when a header changes, but the resulting file
+# does not
+define filechk_offsets
+        echo "#ifndef $2"; \
+        echo "#define $2"; \
+        echo "/*"; \
+        echo " * DO NOT MODIFY."; \
+        echo " *"; \
+        echo " * This file was generated by Kbuild"; \
+        echo " */"; \
+        echo ""; \
+        sed -ne $(sed-offsets) < $<; \
+        echo ""; \
+        echo "#endif"
+endef
+
+###
+# filechk is used to check if the content of a generated file is updated.
+# Sample usage:
+#
+# filechk_sample = echo $(KERNELRELEASE)
+# version.h: FORCE
+#      $(call filechk,sample)
+#
+# The rule defined shall write to stdout the content of the new file.
+# The existing file will be compared with the new one.
+# - If no file exist it is created
+# - If the content differ the new file is used
+# - If they are equal no change, and no timestamp update
+define filechk
+       echo $(tmp-target);                                     \
+       $(Q)set -e;                                             \
+       mkdir -p $(dir $@);                                     \
+       trap "rm -f $(tmp-target)" EXIT;                        \
+       { $(filechk_$(1)); } > $(tmp-target);                   \
+       if [ ! -r $@ ] || ! cmp -s $@ $(tmp-target); then       \
+               $(echo-cmd) '  UPD     $@';                     \
+               mv -f $(tmp-target) $@;                         \
+       fi
+endef
+
 ###
 # Replace >$< with >$$< to preserve $ when reloading the .cmd file
 # (needed for make)
diff --git a/tools/include/linux/kbuild.h b/tools/include/linux/kbuild.h
new file mode 100644
index 000000000000..e7be517aaaf6
--- /dev/null
+++ b/tools/include/linux/kbuild.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LINUX_KBUILD_H
+#define __LINUX_KBUILD_H
+
+#define DEFINE(sym, val) \
+       asm volatile("\n.ascii \"->" #sym " %0 " #val "\"" : : "i" (val))
+
+#define BLANK() asm volatile("\n.ascii \"->\"" : : )
+
+#define OFFSET(sym, str, mem) \
+       DEFINE(sym, offsetof(struct str, mem))
+
+#define COMMENT(x) \
+       asm volatile("\n.ascii \"->#" x "\"")
+
+#endif
diff --git a/tools/testing/selftests/kvm/Makefile.kvm 
b/tools/testing/selftests/kvm/Makefile.kvm
index ef2b1a47719c..e7bd835a56db 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -241,12 +241,14 @@ INSTALL_HDR_PATH = $(top_srcdir)/usr
 LINUX_HDR_PATH = $(INSTALL_HDR_PATH)/include/
 LINUX_TOOL_INCLUDE = $(top_srcdir)/tools/include
 LINUX_TOOL_ARCH_INCLUDE = $(top_srcdir)/tools/arch/$(ARCH)/include
+LINUX_TOOL_OUTPUT_ARCH_INCLUDE = $(OUTPUT)/include/$(ARCH)
 CFLAGS += -Wall -Wstrict-prototypes -Wuninitialized -O2 -g -std=gnu99 \
        -Wno-gnu-variable-sized-type-not-at-end -MD -MP -DCONFIG_64BIT \
        -fno-builtin-memcmp -fno-builtin-memcpy \
        -fno-builtin-memset -fno-builtin-strnlen \
        -fno-stack-protector -fno-PIE -fno-strict-aliasing \
        -I$(LINUX_TOOL_INCLUDE) -I$(LINUX_TOOL_ARCH_INCLUDE) \
+       -I$(LINUX_TOOL_OUTPUT_ARCH_INCLUDE) \
        -I$(LINUX_HDR_PATH) -Iinclude -I$(<D) -Iinclude/$(ARCH) \
        -I ../rseq -I.. $(EXTRA_CFLAGS) $(KHDR_INCLUDES)
 ifeq ($(ARCH),s390)
@@ -256,6 +258,15 @@ ifeq ($(ARCH),x86)
 ifeq ($(shell echo "void foo(void) { }" | $(CC) -march=x86-64-v2 -x c - -c -o 
/dev/null 2>/dev/null; echo "$$?"),0)
        CFLAGS += -march=x86-64-v2
 endif
+
+GEN_HDRS := $(OUTPUT)/include/$(ARCH)/generated/asm-tdx-offsets.h
+
+$(OUTPUT)/lib/$(ARCH)/asm-tdx-offsets.s: lib/$(ARCH)/asm-tdx-offsets.c
+       $(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -S $< -o $@
+
+$(GEN_HDRS): $(OUTPUT)/lib/$(ARCH)/asm-tdx-offsets.s
+       $(call filechk,offsets,__ASM_KVM_SELFTESTS_OFFSETS_H__)
+
 endif
 ifeq ($(ARCH),arm64)
 tools_dir := $(top_srcdir)/tools
diff --git a/tools/testing/selftests/kvm/lib/x86/asm-tdx-offsets.c 
b/tools/testing/selftests/kvm/lib/x86/asm-tdx-offsets.c
new file mode 100644
index 000000000000..ad56d99b56be
--- /dev/null
+++ b/tools/testing/selftests/kvm/lib/x86/asm-tdx-offsets.c
@@ -0,0 +1,28 @@
+#include <linux/kbuild.h>
+
+#include "tdx/tdcall.h"
+
+static inline void common(void)
+{
+       OFFSET(TDX_HYPERCALL_r10, tdx_hypercall_args, r10);
+       OFFSET(TDX_HYPERCALL_r11, tdx_hypercall_args, r11);
+       OFFSET(TDX_HYPERCALL_r12, tdx_hypercall_args, r12);
+       OFFSET(TDX_HYPERCALL_r13, tdx_hypercall_args, r13);
+       OFFSET(TDX_HYPERCALL_r14, tdx_hypercall_args, r14);
+       OFFSET(TDX_HYPERCALL_r15, tdx_hypercall_args, r15);
+
+       BLANK();
+       OFFSET(TDX_MODULE_rcx, tdx_module_output, rcx);
+       OFFSET(TDX_MODULE_rdx, tdx_module_output, rdx);
+       OFFSET(TDX_MODULE_r8, tdx_module_output, r8);
+       OFFSET(TDX_MODULE_r9, tdx_module_output, r9);
+       OFFSET(TDX_MODULE_r10, tdx_module_output, r10);
+       OFFSET(TDX_MODULE_r11, tdx_module_output, r11);
+}
+
+int main(void)
+{
+       common();
+
+       return 0;
+}
diff --git a/tools/testing/selftests/kvm/lib/x86/tdx/tdcall.S 
b/tools/testing/selftests/kvm/lib/x86/tdx/tdcall.S
index c393a0fb35be..e98aa5178db9 100644
--- a/tools/testing/selftests/kvm/lib/x86/tdx/tdcall.S
+++ b/tools/testing/selftests/kvm/lib/x86/tdx/tdcall.S
@@ -1,18 +1,13 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /* Adapted from arch/x86/coco/tdx/tdcall.S */
 
+#include "generated/asm-tdx-offsets.h"
+
 /*
  * TDCALL is supported in Binutils >= 2.36, add it for older version.
  */
 #define tdcall         .byte 0x66,0x0f,0x01,0xcc
 
-#define TDX_HYPERCALL_r10 0 /* offsetof(struct tdx_hypercall_args, r10) */
-#define TDX_HYPERCALL_r11 8 /* offsetof(struct tdx_hypercall_args, r11) */
-#define TDX_HYPERCALL_r12 16 /* offsetof(struct tdx_hypercall_args, r12) */
-#define TDX_HYPERCALL_r13 24 /* offsetof(struct tdx_hypercall_args, r13) */
-#define TDX_HYPERCALL_r14 32 /* offsetof(struct tdx_hypercall_args, r14) */
-#define TDX_HYPERCALL_r15 40 /* offsetof(struct tdx_hypercall_args, r15) */
-
 /*
  * Bitmasks of exposed registers (with VMM).
  */
@@ -91,13 +86,6 @@ __tdx_hypercall:
        pop %rbp
        ret
 
-#define TDX_MODULE_rcx 0 /* offsetof(struct tdx_module_output, rcx) */
-#define TDX_MODULE_rdx 8 /* offsetof(struct tdx_module_output, rdx) */
-#define TDX_MODULE_r8 16 /* offsetof(struct tdx_module_output, r8) */
-#define TDX_MODULE_r9 24 /* offsetof(struct tdx_module_output, r9) */
-#define TDX_MODULE_r10 32 /* offsetof(struct tdx_module_output, r10) */
-#define TDX_MODULE_r11 40 /* offsetof(struct tdx_module_output, r11) */
-
 .globl __tdx_module_call
 .type __tdx_module_call, @function
 __tdx_module_call:
---8<---




Reply via email to