Extend the multi-session Live Update test to specifically verify the new
LIVEUPDATE_SESSION_RETRIEVE_INTO_FD ioctl path. In Stage 2 of the kexec
workflow, the test explicitly delegates topology by allocating a clean
tmpfs target and driving the folios into it through the new API,
verifying that the data preserves identically to the native KHO mapping.

Signed-off-by: David Matlack <[email protected]>
---
 .../liveupdate/lib/include/libliveupdate.h    |  1 +
 .../selftests/liveupdate/lib/lu_utils.c       | 33 +++++++++++
 .../selftests/liveupdate/luo_multi_session.c  | 56 ++++++++++++++++---
 3 files changed, 83 insertions(+), 7 deletions(-)

diff --git a/tools/testing/selftests/liveupdate/lib/include/libliveupdate.h 
b/tools/testing/selftests/liveupdate/lib/include/libliveupdate.h
index fa07fed08364..8ab1e662eeaa 100644
--- a/tools/testing/selftests/liveupdate/lib/include/libliveupdate.h
+++ b/tools/testing/selftests/liveupdate/lib/include/libliveupdate.h
@@ -33,6 +33,7 @@ int luo_session_retrieve_fd(int session_fd, __u64 token);
 
 int create_and_preserve_memfd(int session_fd, int token, const char *data);
 int restore_and_verify_memfd(int session_fd, int token, const char 
*expected_data);
+int luo_verify_retrieve_into_memfd(int session_fd, int token, int mfd, const 
char *expected_data);
 
 void create_state_file(int luo_fd, const char *session_name, int token,
                       int next_stage);
diff --git a/tools/testing/selftests/liveupdate/lib/lu_utils.c 
b/tools/testing/selftests/liveupdate/lib/lu_utils.c
index 74d41115c281..8ed9c367f92e 100644
--- a/tools/testing/selftests/liveupdate/lib/lu_utils.c
+++ b/tools/testing/selftests/liveupdate/lib/lu_utils.c
@@ -324,3 +324,36 @@ int luo_test(int argc, char *argv[],
 
        return 0;
 }
+
+int luo_verify_retrieve_into_memfd(int session_fd, int token, int mfd,
+                                  const char *expected_data)
+{
+       struct liveupdate_session_retrieve_into_fd retrieve_args = {
+               .size = sizeof(retrieve_args),
+               .token = token,
+               .fd = mfd,
+       };
+       long page_size = getpagesize();
+       void *map = MAP_FAILED;
+       int ret = -1;
+
+       ret = ioctl(session_fd, LIVEUPDATE_SESSION_RETRIEVE_INTO_FD, 
&retrieve_args);
+       if (ret < 0) {
+               ksft_print_msg("ioctl LIVEUPDATE_SESSION_RETRIEVE_INTO_FD 
failed\n");
+               return -errno;
+       }
+
+       map = mmap(NULL, page_size, PROT_READ, MAP_SHARED, mfd, 0);
+       if (map == MAP_FAILED)
+               return -errno;
+
+       if (expected_data && strcmp(expected_data, map) != 0) {
+               ksft_print_msg("Data mismatch! Expected '%s', Got '%s'\n",
+                              expected_data, (char *)map);
+               munmap(map, page_size);
+               return -EINVAL;
+       }
+
+       munmap(map, page_size);
+       return 0;
+}
diff --git a/tools/testing/selftests/liveupdate/luo_multi_session.c 
b/tools/testing/selftests/liveupdate/luo_multi_session.c
index aac24a5f5ce3..7882f49aadaf 100644
--- a/tools/testing/selftests/liveupdate/luo_multi_session.c
+++ b/tools/testing/selftests/liveupdate/luo_multi_session.c
@@ -1,4 +1,9 @@
 // SPDX-License-Identifier: GPL-2.0-only
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
 
 /*
  * Copyright (c) 2025, Google LLC.
@@ -31,6 +36,8 @@
 static void run_stage_1(int luo_fd)
 {
        int s_empty1_fd, s_empty2_fd, s_files1_fd, s_files2_fd;
+       int tmpfs_fd;
+       void *map;
 
        ksft_print_msg("[STAGE 1] Starting pre-kexec setup for multi-session 
test...\n");
 
@@ -53,25 +60,50 @@ static void run_stage_1(int luo_fd)
        s_files1_fd = luo_create_session(luo_fd, SESSION_FILES_1);
        if (s_files1_fd < 0)
                fail_exit("luo_create_session for '%s'", SESSION_FILES_1);
+       ksft_print_msg("  -> Preserving memfd (token %#x, content: \"%s\")\n",
+                      MFD1_TOKEN, MFD1_DATA);
        if (create_and_preserve_memfd(s_files1_fd, MFD1_TOKEN, MFD1_DATA) < 0) {
                fail_exit("create_and_preserve_memfd for token %#x",
                          MFD1_TOKEN);
        }
 
-       ksft_print_msg("[STAGE 1] Creating session '%s' with two memfds...\n",
+       ksft_print_msg("[STAGE 1] Creating session '%s' with memfd and tmpfs 
file...\n",
                       SESSION_FILES_2);
 
        s_files2_fd = luo_create_session(luo_fd, SESSION_FILES_2);
        if (s_files2_fd < 0)
                fail_exit("luo_create_session for '%s'", SESSION_FILES_2);
+       ksft_print_msg("  -> Preserving memfd (token %#x, content: \"%s\")\n",
+                      MFD2_TOKEN, MFD2_DATA);
        if (create_and_preserve_memfd(s_files2_fd, MFD2_TOKEN, MFD2_DATA) < 0) {
                fail_exit("create_and_preserve_memfd for token %#x",
                          MFD2_TOKEN);
        }
-       if (create_and_preserve_memfd(s_files2_fd, MFD3_TOKEN, MFD3_DATA) < 0) {
-               fail_exit("create_and_preserve_memfd for token %#x",
-                         MFD3_TOKEN);
-       }
+       ksft_print_msg("  -> Populating custom tmpfs file at 
/mnt/mfd3_source...\n");
+       mkdir("/mnt", 0777);
+       mount("tmpfs", "/mnt", "tmpfs", 0, NULL);
+       tmpfs_fd = open("/mnt/mfd3_source", O_CREAT | O_RDWR, 0666);
+       if (tmpfs_fd < 0)
+               fail_exit("failed to open tmpfs_fd");
+       ksft_print_msg("  -> Preserving generic tmpfs file (token %#x, content: 
\"%s\")\n",
+                      MFD3_TOKEN, MFD3_DATA);
+       if (ftruncate(tmpfs_fd, getpagesize()) < 0)
+               fail_exit("ftruncate tmpfs_fd");
+       map = mmap(NULL, getpagesize(), PROT_WRITE, MAP_SHARED, tmpfs_fd, 0);
+       if (map == MAP_FAILED)
+               fail_exit("mmap tmpfs_fd");
+       strcpy(map, MFD3_DATA);
+       munmap(map, getpagesize());
+
+       struct liveupdate_session_preserve_fd preserve_args = {
+               .size = sizeof(preserve_args),
+               .token = MFD3_TOKEN,
+               .fd = tmpfs_fd,
+       };
+       if (ioctl(s_files2_fd, LIVEUPDATE_SESSION_PRESERVE_FD, &preserve_args) 
< 0)
+               fail_exit("preserve tmpfs fd");
+
+       close(tmpfs_fd);
 
        close(luo_fd);
        daemonize_and_wait();
@@ -110,6 +142,7 @@ static void run_stage_2(int luo_fd, int state_session_fd)
 
        ksft_print_msg("[STAGE 2] Verifying contents of session '%s'...\n",
                       SESSION_FILES_1);
+       ksft_print_msg("  -> Retrieving memfd (token %#x) from session...\n", 
MFD1_TOKEN);
        mfd1 = restore_and_verify_memfd(s_files1_fd, MFD1_TOKEN, MFD1_DATA);
        if (mfd1 < 0)
                fail_exit("restore_and_verify_memfd for token %#x", MFD1_TOKEN);
@@ -118,14 +151,23 @@ static void run_stage_2(int luo_fd, int state_session_fd)
        ksft_print_msg("[STAGE 2] Verifying contents of session '%s'...\n",
                       SESSION_FILES_2);
 
+       ksft_print_msg("  -> Retrieving memfd (token %#x) from session...\n", 
MFD2_TOKEN);
        mfd2 = restore_and_verify_memfd(s_files2_fd, MFD2_TOKEN, MFD2_DATA);
        if (mfd2 < 0)
                fail_exit("restore_and_verify_memfd for token %#x", MFD2_TOKEN);
        close(mfd2);
 
-       mfd3 = restore_and_verify_memfd(s_files2_fd, MFD3_TOKEN, MFD3_DATA);
+       ksft_print_msg("  -> Creating empty tmpfs file at 
/mnt/mfd3_target...\n");
+       mkdir("/mnt", 0777);
+       mount("tmpfs", "/mnt", "tmpfs", 0, NULL);
+       mfd3 = open("/mnt/mfd3_target", O_CREAT | O_RDWR, 0666);
        if (mfd3 < 0)
-               fail_exit("restore_and_verify_memfd for token %#x", MFD3_TOKEN);
+               fail_exit("open tmpfs target for retrieve_into");
+
+       ksft_print_msg("  -> Validating LIVEUPDATE_SESSION_RETRIEVE_INTO_FD 
(token %#x)...\n",
+                      MFD3_TOKEN);
+       if (luo_verify_retrieve_into_memfd(s_files2_fd, MFD3_TOKEN, mfd3, 
MFD3_DATA) < 0)
+               fail_exit("luo_verify_retrieve_into_memfd for token %#x", 
MFD3_TOKEN);
        close(mfd3);
 
        ksft_print_msg("[STAGE 2] Test data verified successfully.\n");
-- 
2.55.0.966.g6673acef38-goog


Reply via email to