Allow CONFIG_TEST_KEXEC_HANDOVER to be enabled with
CONFIG_KEXEC_HANDOVER_READONLY and update test_kho to verify preserved
state when booted in read-only KHO mode.

In read-only mode, test_kho retrieves the test FDT subtree, maps the FDT
folio via kho_ro_map_folio(..., PAGE_KERNEL_RO), restores the preserved
vmalloc folio descriptor array via kho_restore_vmalloc(), verifies each
folio's preserved order in the KHO radix tree via
kho_ro_lookup_folio_order(), maps each folio via kho_ro_map_folio() (and
unmaps via kho_ro_unmap()), and verifies the checksum over all preserved
pages.

Signed-off-by: Pasha Tatashin <[email protected]>
---
 lib/Kconfig.debug |  2 +-
 lib/test_kho.c    | 68 ++++++++++++++++++++++++++++++++++-------------
 2 files changed, 51 insertions(+), 19 deletions(-)

diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index 134b15a44625..74223ef8bd1c 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -3381,7 +3381,7 @@ config TEST_OBJPOOL
 config TEST_KEXEC_HANDOVER
        bool "Test for Kexec HandOver"
        default n
-       depends on KEXEC_HANDOVER
+       depends on KEXEC_HANDOVER || KEXEC_HANDOVER_READONLY
        help
          This option enables test for Kexec HandOver (KHO).
          The test consists of two parts: saving kernel data before kexec and
diff --git a/lib/test_kho.c b/lib/test_kho.c
index aa6a0956bb8b..1e0f257a5854 100644
--- a/lib/test_kho.c
+++ b/lib/test_kho.c
@@ -239,7 +239,7 @@ static int kho_test_restore_data(const void *fdt, int node)
        phys_addr_t *folios_info;
        const __wsum *old_csum;
        __wsum csum = 0;
-       int len;
+       int len, i;
 
        node = fdt_path_offset(fdt, "/data");
 
@@ -259,11 +259,28 @@ static int kho_test_restore_data(const void *fdt, int 
node)
        if (!folios_info)
                return -EINVAL;
 
-       for (int i = 0; i < *nr_folios; i++) {
+       for (i = 0; i < *nr_folios; i++) {
                unsigned int order = folios_info[i] & ~PAGE_MASK;
                phys_addr_t phys = folios_info[i] & PAGE_MASK;
-               unsigned int size = PAGE_SIZE << order;
                struct folio *folio;
+               size_t size;
+
+               if (order > MAX_PAGE_ORDER)
+                       break;
+               size = PAGE_SIZE << order;
+
+               if (kho_is_ro()) {
+                       void *addr;
+
+                       if (kho_ro_lookup_folio_order(phys) != order)
+                               break;
+                       addr = kho_ro_map_folio(phys, PAGE_KERNEL_RO);
+                       if (!addr)
+                               break;
+                       csum = csum_partial(addr, size, csum);
+                       kho_ro_unmap(addr);
+                       continue;
+               }
 
                folio = kho_restore_folio(phys);
                if (!folio)
@@ -278,50 +295,65 @@ static int kho_test_restore_data(const void *fdt, int 
node)
 
        vfree(folios_info);
 
-       if (csum != *old_csum)
+       if (i != *nr_folios || csum != *old_csum)
                return -EINVAL;
 
        return 0;
 }
 
-static int kho_test_restore(phys_addr_t fdt_phys)
+static int kho_test_restore(phys_addr_t fdt_phys, size_t fdt_size)
 {
-       void *fdt = phys_to_virt(fdt_phys);
        const unsigned int *magic;
-       int node, len, err;
+       int node, len, err = -EINVAL;
+       void *fdt;
+
+       if (!fdt_size)
+               return -EINVAL;
+
+       if (kho_is_ro()) {
+               if (kho_ro_lookup_folio_order(fdt_phys) < get_order(fdt_size))
+                       return -EINVAL;
+               fdt = kho_ro_map_folio(fdt_phys, PAGE_KERNEL_RO);
+               if (!fdt)
+                       return -EINVAL;
+       } else {
+               fdt = phys_to_virt(fdt_phys);
+       }
 
        node = fdt_path_offset(fdt, "/");
        if (node < 0)
-               return -EINVAL;
+               goto out_unmap;
 
        if (fdt_node_check_compatible(fdt, node, KHO_TEST_COMPAT))
-               return -EINVAL;
+               goto out_unmap;
 
        magic = fdt_getprop(fdt, node, "magic", &len);
        if (!magic || len != sizeof(*magic))
-               return -EINVAL;
+               goto out_unmap;
 
        if (*magic != KHO_TEST_MAGIC)
-               return -EINVAL;
+               goto out_unmap;
 
        err = kho_test_restore_data(fdt, node);
-       if (err)
-               return err;
 
-       return 0;
+out_unmap:
+       if (kho_is_ro())
+               kho_ro_unmap(fdt);
+       return err;
 }
 
 static int __init kho_test_init(void)
 {
        phys_addr_t fdt_phys;
+       size_t fdt_size;
        int err;
 
-       if (!kho_is_enabled())
+       if (!kho_is_enabled() && !kho_is_ro())
                return 0;
 
-       err = kho_retrieve_subtree(KHO_TEST_FDT, &fdt_phys, NULL);
+       err = kho_retrieve_subtree(KHO_TEST_FDT, &fdt_phys, &fdt_size);
        if (!err) {
-               err = kho_test_restore(fdt_phys);
+               err = kho_test_restore(fdt_phys, fdt_size);
                if (err)
                        pr_err("KHO restore failed\n");
                else
@@ -330,7 +362,7 @@ static int __init kho_test_init(void)
                return err;
        }
 
-       if (err != -ENOENT) {
+       if (err != -ENOENT || kho_is_ro()) {
                pr_warn("failed to retrieve %s FDT: %d\n", KHO_TEST_FDT, err);
                return err;
        }
-- 
2.56.0.176.g3575e65682-goog


Reply via email to