]>
Commit | Line | Data |
---|---|---|
443cbaf9 BH |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* | |
3 | * crash.c - kernel crash support code. | |
4 | * Copyright (C) 2002-2004 Eric Biederman <[email protected]> | |
5 | */ | |
6 | ||
7 | #include <linux/buildid.h> | |
8 | #include <linux/init.h> | |
9 | #include <linux/utsname.h> | |
10 | #include <linux/vmalloc.h> | |
11 | #include <linux/sizes.h> | |
12 | #include <linux/kexec.h> | |
13 | #include <linux/memory.h> | |
14 | #include <linux/cpuhotplug.h> | |
15 | #include <linux/memblock.h> | |
16 | #include <linux/kmemleak.h> | |
17 | ||
18 | #include <asm/page.h> | |
19 | #include <asm/sections.h> | |
20 | ||
21 | #include <crypto/sha1.h> | |
22 | ||
23 | #include "kallsyms_internal.h" | |
24 | #include "kexec_internal.h" | |
25 | ||
26 | /* vmcoreinfo stuff */ | |
27 | unsigned char *vmcoreinfo_data; | |
28 | size_t vmcoreinfo_size; | |
29 | u32 *vmcoreinfo_note; | |
30 | ||
31 | /* trusted vmcoreinfo, e.g. we can make a copy in the crash memory */ | |
32 | static unsigned char *vmcoreinfo_data_safecopy; | |
33 | ||
34 | Elf_Word *append_elf_note(Elf_Word *buf, char *name, unsigned int type, | |
35 | void *data, size_t data_len) | |
36 | { | |
37 | struct elf_note *note = (struct elf_note *)buf; | |
38 | ||
39 | note->n_namesz = strlen(name) + 1; | |
40 | note->n_descsz = data_len; | |
41 | note->n_type = type; | |
42 | buf += DIV_ROUND_UP(sizeof(*note), sizeof(Elf_Word)); | |
43 | memcpy(buf, name, note->n_namesz); | |
44 | buf += DIV_ROUND_UP(note->n_namesz, sizeof(Elf_Word)); | |
45 | memcpy(buf, data, data_len); | |
46 | buf += DIV_ROUND_UP(data_len, sizeof(Elf_Word)); | |
47 | ||
48 | return buf; | |
49 | } | |
50 | ||
51 | void final_note(Elf_Word *buf) | |
52 | { | |
53 | memset(buf, 0, sizeof(struct elf_note)); | |
54 | } | |
55 | ||
56 | static void update_vmcoreinfo_note(void) | |
57 | { | |
58 | u32 *buf = vmcoreinfo_note; | |
59 | ||
60 | if (!vmcoreinfo_size) | |
61 | return; | |
62 | buf = append_elf_note(buf, VMCOREINFO_NOTE_NAME, 0, vmcoreinfo_data, | |
63 | vmcoreinfo_size); | |
64 | final_note(buf); | |
65 | } | |
66 | ||
67 | void crash_update_vmcoreinfo_safecopy(void *ptr) | |
68 | { | |
69 | if (ptr) | |
70 | memcpy(ptr, vmcoreinfo_data, vmcoreinfo_size); | |
71 | ||
72 | vmcoreinfo_data_safecopy = ptr; | |
73 | } | |
74 | ||
75 | void crash_save_vmcoreinfo(void) | |
76 | { | |
77 | if (!vmcoreinfo_note) | |
78 | return; | |
79 | ||
80 | /* Use the safe copy to generate vmcoreinfo note if have */ | |
81 | if (vmcoreinfo_data_safecopy) | |
82 | vmcoreinfo_data = vmcoreinfo_data_safecopy; | |
83 | ||
84 | vmcoreinfo_append_str("CRASHTIME=%lld\n", ktime_get_real_seconds()); | |
85 | update_vmcoreinfo_note(); | |
86 | } | |
87 | ||
88 | void vmcoreinfo_append_str(const char *fmt, ...) | |
89 | { | |
90 | va_list args; | |
91 | char buf[0x50]; | |
92 | size_t r; | |
93 | ||
94 | va_start(args, fmt); | |
95 | r = vscnprintf(buf, sizeof(buf), fmt, args); | |
96 | va_end(args); | |
97 | ||
98 | r = min(r, (size_t)VMCOREINFO_BYTES - vmcoreinfo_size); | |
99 | ||
100 | memcpy(&vmcoreinfo_data[vmcoreinfo_size], buf, r); | |
101 | ||
102 | vmcoreinfo_size += r; | |
103 | ||
104 | WARN_ONCE(vmcoreinfo_size == VMCOREINFO_BYTES, | |
105 | "vmcoreinfo data exceeds allocated size, truncating"); | |
106 | } | |
107 | ||
108 | /* | |
109 | * provide an empty default implementation here -- architecture | |
110 | * code may override this | |
111 | */ | |
112 | void __weak arch_crash_save_vmcoreinfo(void) | |
113 | {} | |
114 | ||
115 | phys_addr_t __weak paddr_vmcoreinfo_note(void) | |
116 | { | |
117 | return __pa(vmcoreinfo_note); | |
118 | } | |
119 | EXPORT_SYMBOL(paddr_vmcoreinfo_note); | |
120 | ||
121 | static int __init crash_save_vmcoreinfo_init(void) | |
122 | { | |
123 | vmcoreinfo_data = (unsigned char *)get_zeroed_page(GFP_KERNEL); | |
124 | if (!vmcoreinfo_data) { | |
125 | pr_warn("Memory allocation for vmcoreinfo_data failed\n"); | |
126 | return -ENOMEM; | |
127 | } | |
128 | ||
129 | vmcoreinfo_note = alloc_pages_exact(VMCOREINFO_NOTE_SIZE, | |
130 | GFP_KERNEL | __GFP_ZERO); | |
131 | if (!vmcoreinfo_note) { | |
132 | free_page((unsigned long)vmcoreinfo_data); | |
133 | vmcoreinfo_data = NULL; | |
134 | pr_warn("Memory allocation for vmcoreinfo_note failed\n"); | |
135 | return -ENOMEM; | |
136 | } | |
137 | ||
138 | VMCOREINFO_OSRELEASE(init_uts_ns.name.release); | |
139 | VMCOREINFO_BUILD_ID(); | |
140 | VMCOREINFO_PAGESIZE(PAGE_SIZE); | |
141 | ||
142 | VMCOREINFO_SYMBOL(init_uts_ns); | |
143 | VMCOREINFO_OFFSET(uts_namespace, name); | |
144 | VMCOREINFO_SYMBOL(node_online_map); | |
145 | #ifdef CONFIG_MMU | |
146 | VMCOREINFO_SYMBOL_ARRAY(swapper_pg_dir); | |
147 | #endif | |
148 | VMCOREINFO_SYMBOL(_stext); | |
149 | vmcoreinfo_append_str("NUMBER(VMALLOC_START)=0x%lx\n", (unsigned long) VMALLOC_START); | |
150 | ||
151 | #ifndef CONFIG_NUMA | |
152 | VMCOREINFO_SYMBOL(mem_map); | |
153 | VMCOREINFO_SYMBOL(contig_page_data); | |
154 | #endif | |
d3246b6e HS |
155 | #ifdef CONFIG_SPARSEMEM_VMEMMAP |
156 | VMCOREINFO_SYMBOL_ARRAY(vmemmap); | |
157 | #endif | |
443cbaf9 BH |
158 | #ifdef CONFIG_SPARSEMEM |
159 | VMCOREINFO_SYMBOL_ARRAY(mem_section); | |
160 | VMCOREINFO_LENGTH(mem_section, NR_SECTION_ROOTS); | |
161 | VMCOREINFO_STRUCT_SIZE(mem_section); | |
162 | VMCOREINFO_OFFSET(mem_section, section_mem_map); | |
163 | VMCOREINFO_NUMBER(SECTION_SIZE_BITS); | |
164 | VMCOREINFO_NUMBER(MAX_PHYSMEM_BITS); | |
165 | #endif | |
166 | VMCOREINFO_STRUCT_SIZE(page); | |
167 | VMCOREINFO_STRUCT_SIZE(pglist_data); | |
168 | VMCOREINFO_STRUCT_SIZE(zone); | |
169 | VMCOREINFO_STRUCT_SIZE(free_area); | |
170 | VMCOREINFO_STRUCT_SIZE(list_head); | |
171 | VMCOREINFO_SIZE(nodemask_t); | |
172 | VMCOREINFO_OFFSET(page, flags); | |
173 | VMCOREINFO_OFFSET(page, _refcount); | |
174 | VMCOREINFO_OFFSET(page, mapping); | |
175 | VMCOREINFO_OFFSET(page, lru); | |
176 | VMCOREINFO_OFFSET(page, _mapcount); | |
177 | VMCOREINFO_OFFSET(page, private); | |
178 | VMCOREINFO_OFFSET(page, compound_head); | |
179 | VMCOREINFO_OFFSET(pglist_data, node_zones); | |
180 | VMCOREINFO_OFFSET(pglist_data, nr_zones); | |
181 | #ifdef CONFIG_FLATMEM | |
182 | VMCOREINFO_OFFSET(pglist_data, node_mem_map); | |
183 | #endif | |
184 | VMCOREINFO_OFFSET(pglist_data, node_start_pfn); | |
185 | VMCOREINFO_OFFSET(pglist_data, node_spanned_pages); | |
186 | VMCOREINFO_OFFSET(pglist_data, node_id); | |
187 | VMCOREINFO_OFFSET(zone, free_area); | |
188 | VMCOREINFO_OFFSET(zone, vm_stat); | |
189 | VMCOREINFO_OFFSET(zone, spanned_pages); | |
190 | VMCOREINFO_OFFSET(free_area, free_list); | |
191 | VMCOREINFO_OFFSET(list_head, next); | |
192 | VMCOREINFO_OFFSET(list_head, prev); | |
193 | VMCOREINFO_LENGTH(zone.free_area, NR_PAGE_ORDERS); | |
194 | log_buf_vmcoreinfo_setup(); | |
195 | VMCOREINFO_LENGTH(free_area.free_list, MIGRATE_TYPES); | |
196 | VMCOREINFO_NUMBER(NR_FREE_PAGES); | |
197 | VMCOREINFO_NUMBER(PG_lru); | |
198 | VMCOREINFO_NUMBER(PG_private); | |
199 | VMCOREINFO_NUMBER(PG_swapcache); | |
200 | VMCOREINFO_NUMBER(PG_swapbacked); | |
46df8e73 MWO |
201 | #define PAGE_SLAB_MAPCOUNT_VALUE (~PG_slab) |
202 | VMCOREINFO_NUMBER(PAGE_SLAB_MAPCOUNT_VALUE); | |
443cbaf9 BH |
203 | #ifdef CONFIG_MEMORY_FAILURE |
204 | VMCOREINFO_NUMBER(PG_hwpoison); | |
205 | #endif | |
206 | VMCOREINFO_NUMBER(PG_head_mask); | |
207 | #define PAGE_BUDDY_MAPCOUNT_VALUE (~PG_buddy) | |
208 | VMCOREINFO_NUMBER(PAGE_BUDDY_MAPCOUNT_VALUE); | |
d99e3140 MWO |
209 | #define PAGE_HUGETLB_MAPCOUNT_VALUE (~PG_hugetlb) |
210 | VMCOREINFO_NUMBER(PAGE_HUGETLB_MAPCOUNT_VALUE); | |
443cbaf9 BH |
211 | #define PAGE_OFFLINE_MAPCOUNT_VALUE (~PG_offline) |
212 | VMCOREINFO_NUMBER(PAGE_OFFLINE_MAPCOUNT_VALUE); | |
443cbaf9 BH |
213 | |
214 | #ifdef CONFIG_KALLSYMS | |
215 | VMCOREINFO_SYMBOL(kallsyms_names); | |
216 | VMCOREINFO_SYMBOL(kallsyms_num_syms); | |
217 | VMCOREINFO_SYMBOL(kallsyms_token_table); | |
218 | VMCOREINFO_SYMBOL(kallsyms_token_index); | |
219 | #ifdef CONFIG_KALLSYMS_BASE_RELATIVE | |
220 | VMCOREINFO_SYMBOL(kallsyms_offsets); | |
221 | VMCOREINFO_SYMBOL(kallsyms_relative_base); | |
222 | #else | |
223 | VMCOREINFO_SYMBOL(kallsyms_addresses); | |
224 | #endif /* CONFIG_KALLSYMS_BASE_RELATIVE */ | |
225 | #endif /* CONFIG_KALLSYMS */ | |
226 | ||
227 | arch_crash_save_vmcoreinfo(); | |
228 | update_vmcoreinfo_note(); | |
229 | ||
230 | return 0; | |
231 | } | |
232 | ||
233 | subsys_initcall(crash_save_vmcoreinfo_init); |