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

