1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
// SPDX-License-Identifier: GPL-2.0-only
/*
* RISC-V crash hotplug support
*
* Copyright (C) 2025 Bytedance Ltd.
*/
#include <linux/kexec.h>
#include <linux/crash_core.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#ifdef CONFIG_CRASH_HOTPLUG
#undef pr_fmt
#define pr_fmt(fmt) "crash hp: " fmt
int arch_crash_hotplug_support(struct kimage *image, unsigned long kexec_flags)
{
#ifdef CONFIG_KEXEC_FILE
if (image->file_mode)
return 1;
#endif
/*
* For the kexec_load() syscall path, the user space kexec tool
* needs to indicate that the elfcorehdr segment is excluded from
* SHA verification by setting the appropriate flags.
*/
return (kexec_flags & KEXEC_UPDATE_ELFCOREHDR ||
kexec_flags & KEXEC_CRASH_HOTPLUG_SUPPORT);
}
unsigned int arch_crash_get_elfcorehdr_size(void)
{
unsigned int sz;
/* kernel_map, VMCOREINFO and maximum CPUs */
sz = 2 + CONFIG_NR_CPUS;
if (IS_ENABLED(CONFIG_MEMORY_HOTPLUG))
sz += CONFIG_CRASH_MAX_MEMORY_RANGES;
sz *= sizeof(Elf64_Phdr);
return sz;
}
/**
* arch_crash_handle_hotplug_event() - Handle hotplug elfcorehdr changes
* @image: a pointer to kexec_crash_image
* @arg: struct memory_notify handler for memory hotplug case and
* NULL for CPU hotplug case.
*
* Prepare the new elfcorehdr and replace the existing elfcorehdr.
*/
void arch_crash_handle_hotplug_event(struct kimage *image, void *arg)
{
void *elfbuf = NULL, *old_elfcorehdr;
unsigned long mem, memsz;
unsigned long elfsz = 0;
/*
* As crash_prepare_elf64_headers() has already described all
* possible CPUs, there is no need to update the elfcorehdr
* for additional CPU changes.
*/
if ((image->file_mode || image->elfcorehdr_updated) &&
((image->hp_action == KEXEC_CRASH_HP_ADD_CPU) ||
(image->hp_action == KEXEC_CRASH_HP_REMOVE_CPU)))
return;
/*
* Create the new elfcorehdr reflecting the changes to CPU and/or
* memory resources.
*/
if (crash_prepare_headers(true, &elfbuf, &elfsz, NULL)) {
pr_err("unable to create new elfcorehdr");
goto out;
}
/*
* Obtain address and size of the elfcorehdr segment, and
* check it against the new elfcorehdr buffer.
*/
mem = image->segment[image->elfcorehdr_index].mem;
memsz = image->segment[image->elfcorehdr_index].memsz;
if (elfsz > memsz) {
pr_err("update elfcorehdr elfsz %lu > memsz %lu",
elfsz, memsz);
goto out;
}
/*
* Copy new elfcorehdr over the old elfcorehdr at destination.
*/
old_elfcorehdr = kmap_local_page(pfn_to_page(mem >> PAGE_SHIFT));
if (!old_elfcorehdr) {
pr_err("mapping elfcorehdr segment failed\n");
goto out;
}
/*
* Temporarily invalidate the crash image while the
* elfcorehdr is updated.
*/
xchg(&kexec_crash_image, NULL);
memcpy_flushcache(old_elfcorehdr, elfbuf, elfsz);
xchg(&kexec_crash_image, image);
kunmap_local(old_elfcorehdr);
pr_debug("updated elfcorehdr\n");
out:
vfree(elfbuf);
}
#endif /* CONFIG_CRASH_HOTPLUG */
|