From: Pingfan Liu <pi...@redhat.com> Signed-off-by: Pingfan Liu <pi...@redhat.com> Cc: Jiri Bohac <jbo...@suse.cz> Cc: Michal Hocko <mho...@suse.com> Cc: Philipp Rudo <pr...@redhat.com> Cc: Baoquan He <b...@redhat.com> Cc: Dave Young <dyo...@redhat.com> To: kexec@lists.infradead.org --- include/linux/kexec.h | 1 + init/main.c | 4 +++ kernel/crash_dump.c | 80 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+)
diff --git a/include/linux/kexec.h b/include/linux/kexec.h index 8227455192b7..95e19c814c93 100644 --- a/include/linux/kexec.h +++ b/include/linux/kexec.h @@ -410,6 +410,7 @@ extern bool kexec_in_progress; int crash_shrink_memory(unsigned long new_size); ssize_t crash_get_memory_size(void); +void kdump_kernel_reuse_mem(void); #ifndef arch_kexec_protect_crashkres /* diff --git a/init/main.c b/init/main.c index e24b0780fdff..ed90097caaa8 100644 --- a/init/main.c +++ b/init/main.c @@ -99,6 +99,7 @@ #include <linux/init_syscalls.h> #include <linux/stackdepot.h> #include <linux/randomize_kstack.h> +#include <linux/kexec.h> #include <net/net_namespace.h> #include <asm/io.h> @@ -1068,6 +1069,9 @@ void start_kernel(void) arch_post_acpi_subsys_init(); kcsan_init(); + /* */ + kdump_kernel_reuse_mem(); + /* Do the rest non-__init'ed, we're now alive */ arch_call_rest_init(); diff --git a/kernel/crash_dump.c b/kernel/crash_dump.c index 92da32275af5..4605005f9534 100644 --- a/kernel/crash_dump.c +++ b/kernel/crash_dump.c @@ -4,6 +4,7 @@ #include <linux/init.h> #include <linux/errno.h> #include <linux/export.h> +#include <linux/cc_platform.h> /* * stores the physical address of elf header of crash image @@ -39,3 +40,82 @@ static int __init setup_elfcorehdr(char *arg) return end > arg ? 0 : -EINVAL; } early_param("elfcorehdr", setup_elfcorehdr); + +u64 kdump_cma_pfn; +u64 kdump_cma_pg_cnt; +u64 kdump_cma_pg_paddr; + +/* In old kernel, wreck should correspond to pfn */ +static void check_poison_page(struct page *wreck, int cnt, unsigned long pfn) +{ + int i = 0; + int order; + /* Info copied from old kernel */ + struct page *check = wreck; + struct page *p; + + /* + * Strictly check, but there is still a rare case, where both _refcnt and + * _mapcount are forged by some wrong code + */ + for (; i < cnt; i += 1 << order) { + order = folio_order((struct folio *)check); + if (PageBuddy(check)) { + if (page_count(check) != 0 || total_mapcount(check) != 0) + goto fail; + if (check->index != 0) + goto fail; + if (check->lru.next != LIST_POISON1 || + check->lru.prev != LIST_POISON2) + goto fail; + + } else if (folio_test_anon((struct folio *)check)) { + /* check PAGE_MAPPING_ANON bit in mapping */ + if (!((unsigned long)check->mapping & PAGE_MAPPING_ANON)) + goto fail; + if (page_count(check) == 0) + goto fail; + if (total_mapcount(check) != page_count(check)) + goto fail; + } else { + goto fail; + } + + p = pfn_to_page(pfn); + for (int j = 0; j < 1 << order; j++) + ClearPageReserved(p + j); + __free_pages(p, order); + +fail: + check += 1 << order; + pfn += 1 << order; + } +} + +void kdump_kernel_reuse_mem(void) +{ + u64 size; + struct page *wreck; + struct kvec kvec; + struct iov_iter iter; + char *buf; + + if (!is_kdump_kernel()) + return; + + if (!kdump_cma_pfn || !kdump_cma_pg_cnt || !kdump_cma_pg_paddr) + return; + + size = kdump_cma_pg_cnt * sizeof(struct page); + /* copy the wreck's page[] */ + buf = kmalloc(size, GFP_KERNEL); + if (!buf) + return; + wreck = (struct page *)buf; + kvec.iov_base = buf; + kvec.iov_len = size; + iov_iter_kvec(&iter, ITER_DEST, &kvec, 1, size); + read_from_oldmem(&iter, size, &kdump_cma_pg_paddr, + cc_platform_has(CC_ATTR_GUEST_MEM_ENCRYPT)); + check_poison_page(wreck, kdump_cma_pg_cnt, kdump_cma_pfn); +} -- 2.31.1 _______________________________________________ kexec mailing list kexec@lists.infradead.org http://lists.infradead.org/mailman/listinfo/kexec