summaryrefslogtreecommitdiffstats
path: root/kernel/kexec_file.c
diff options
context:
space:
mode:
authorAKASHI Takahiro <takahiro.akashi@linaro.org>2018-04-13 18:36:06 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2018-04-13 20:10:27 -0400
commitbabac4a84a88842bec477a5bdada1460f3bc374c (patch)
tree88e84c21942b051c2dfe941cadb8d8cdb4aa7ec0 /kernel/kexec_file.c
parenteb7dae947ef5c50e0121f673fcb43ca3583e5849 (diff)
kexec_file, x86: move re-factored code to generic side
In the previous patches, commonly-used routines, exclude_mem_range() and prepare_elf64_headers(), were carved out. Now place them in kexec common code. A prefix "crash_" is given to each of their names to avoid possible name collisions. Link: http://lkml.kernel.org/r/20180306102303.9063-8-takahiro.akashi@linaro.org Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org> Acked-by: Dave Young <dyoung@redhat.com> Tested-by: Dave Young <dyoung@redhat.com> Cc: Vivek Goyal <vgoyal@redhat.com> Cc: Baoquan He <bhe@redhat.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/kexec_file.c')
-rw-r--r--kernel/kexec_file.c175
1 files changed, 175 insertions, 0 deletions
diff --git a/kernel/kexec_file.c b/kernel/kexec_file.c
index 332c4fd12cb1..b06b1fac5252 100644
--- a/kernel/kexec_file.c
+++ b/kernel/kexec_file.c
@@ -22,6 +22,11 @@
22#include <linux/ima.h> 22#include <linux/ima.h>
23#include <crypto/hash.h> 23#include <crypto/hash.h>
24#include <crypto/sha.h> 24#include <crypto/sha.h>
25#include <linux/elf.h>
26#include <linux/elfcore.h>
27#include <linux/kernel.h>
28#include <linux/kexec.h>
29#include <linux/slab.h>
25#include <linux/syscalls.h> 30#include <linux/syscalls.h>
26#include <linux/vmalloc.h> 31#include <linux/vmalloc.h>
27#include "kexec_internal.h" 32#include "kexec_internal.h"
@@ -1079,3 +1084,173 @@ int kexec_purgatory_get_set_symbol(struct kimage *image, const char *name,
1079 return 0; 1084 return 0;
1080} 1085}
1081#endif /* CONFIG_ARCH_HAS_KEXEC_PURGATORY */ 1086#endif /* CONFIG_ARCH_HAS_KEXEC_PURGATORY */
1087
1088int crash_exclude_mem_range(struct crash_mem *mem,
1089 unsigned long long mstart, unsigned long long mend)
1090{
1091 int i, j;
1092 unsigned long long start, end;
1093 struct crash_mem_range temp_range = {0, 0};
1094
1095 for (i = 0; i < mem->nr_ranges; i++) {
1096 start = mem->ranges[i].start;
1097 end = mem->ranges[i].end;
1098
1099 if (mstart > end || mend < start)
1100 continue;
1101
1102 /* Truncate any area outside of range */
1103 if (mstart < start)
1104 mstart = start;
1105 if (mend > end)
1106 mend = end;
1107
1108 /* Found completely overlapping range */
1109 if (mstart == start && mend == end) {
1110 mem->ranges[i].start = 0;
1111 mem->ranges[i].end = 0;
1112 if (i < mem->nr_ranges - 1) {
1113 /* Shift rest of the ranges to left */
1114 for (j = i; j < mem->nr_ranges - 1; j++) {
1115 mem->ranges[j].start =
1116 mem->ranges[j+1].start;
1117 mem->ranges[j].end =
1118 mem->ranges[j+1].end;
1119 }
1120 }
1121 mem->nr_ranges--;
1122 return 0;
1123 }
1124
1125 if (mstart > start && mend < end) {
1126 /* Split original range */
1127 mem->ranges[i].end = mstart - 1;
1128 temp_range.start = mend + 1;
1129 temp_range.end = end;
1130 } else if (mstart != start)
1131 mem->ranges[i].end = mstart - 1;
1132 else
1133 mem->ranges[i].start = mend + 1;
1134 break;
1135 }
1136
1137 /* If a split happened, add the split to array */
1138 if (!temp_range.end)
1139 return 0;
1140
1141 /* Split happened */
1142 if (i == mem->max_nr_ranges - 1)
1143 return -ENOMEM;
1144
1145 /* Location where new range should go */
1146 j = i + 1;
1147 if (j < mem->nr_ranges) {
1148 /* Move over all ranges one slot towards the end */
1149 for (i = mem->nr_ranges - 1; i >= j; i--)
1150 mem->ranges[i + 1] = mem->ranges[i];
1151 }
1152
1153 mem->ranges[j].start = temp_range.start;
1154 mem->ranges[j].end = temp_range.end;
1155 mem->nr_ranges++;
1156 return 0;
1157}
1158
1159int crash_prepare_elf64_headers(struct crash_mem *mem, int kernel_map,
1160 void **addr, unsigned long *sz)
1161{
1162 Elf64_Ehdr *ehdr;
1163 Elf64_Phdr *phdr;
1164 unsigned long nr_cpus = num_possible_cpus(), nr_phdr, elf_sz;
1165 unsigned char *buf;
1166 unsigned int cpu, i;
1167 unsigned long long notes_addr;
1168 unsigned long mstart, mend;
1169
1170 /* extra phdr for vmcoreinfo elf note */
1171 nr_phdr = nr_cpus + 1;
1172 nr_phdr += mem->nr_ranges;
1173
1174 /*
1175 * kexec-tools creates an extra PT_LOAD phdr for kernel text mapping
1176 * area (for example, ffffffff80000000 - ffffffffa0000000 on x86_64).
1177 * I think this is required by tools like gdb. So same physical
1178 * memory will be mapped in two elf headers. One will contain kernel
1179 * text virtual addresses and other will have __va(physical) addresses.
1180 */
1181
1182 nr_phdr++;
1183 elf_sz = sizeof(Elf64_Ehdr) + nr_phdr * sizeof(Elf64_Phdr);
1184 elf_sz = ALIGN(elf_sz, ELF_CORE_HEADER_ALIGN);
1185
1186 buf = vzalloc(elf_sz);
1187 if (!buf)
1188 return -ENOMEM;
1189
1190 ehdr = (Elf64_Ehdr *)buf;
1191 phdr = (Elf64_Phdr *)(ehdr + 1);
1192 memcpy(ehdr->e_ident, ELFMAG, SELFMAG);
1193 ehdr->e_ident[EI_CLASS] = ELFCLASS64;
1194 ehdr->e_ident[EI_DATA] = ELFDATA2LSB;
1195 ehdr->e_ident[EI_VERSION] = EV_CURRENT;
1196 ehdr->e_ident[EI_OSABI] = ELF_OSABI;
1197 memset(ehdr->e_ident + EI_PAD, 0, EI_NIDENT - EI_PAD);
1198 ehdr->e_type = ET_CORE;
1199 ehdr->e_machine = ELF_ARCH;
1200 ehdr->e_version = EV_CURRENT;
1201 ehdr->e_phoff = sizeof(Elf64_Ehdr);
1202 ehdr->e_ehsize = sizeof(Elf64_Ehdr);
1203 ehdr->e_phentsize = sizeof(Elf64_Phdr);
1204
1205 /* Prepare one phdr of type PT_NOTE for each present cpu */
1206 for_each_present_cpu(cpu) {
1207 phdr->p_type = PT_NOTE;
1208 notes_addr = per_cpu_ptr_to_phys(per_cpu_ptr(crash_notes, cpu));
1209 phdr->p_offset = phdr->p_paddr = notes_addr;
1210 phdr->p_filesz = phdr->p_memsz = sizeof(note_buf_t);
1211 (ehdr->e_phnum)++;
1212 phdr++;
1213 }
1214
1215 /* Prepare one PT_NOTE header for vmcoreinfo */
1216 phdr->p_type = PT_NOTE;
1217 phdr->p_offset = phdr->p_paddr = paddr_vmcoreinfo_note();
1218 phdr->p_filesz = phdr->p_memsz = VMCOREINFO_NOTE_SIZE;
1219 (ehdr->e_phnum)++;
1220 phdr++;
1221
1222 /* Prepare PT_LOAD type program header for kernel text region */
1223 if (kernel_map) {
1224 phdr->p_type = PT_LOAD;
1225 phdr->p_flags = PF_R|PF_W|PF_X;
1226 phdr->p_vaddr = (Elf64_Addr)_text;
1227 phdr->p_filesz = phdr->p_memsz = _end - _text;
1228 phdr->p_offset = phdr->p_paddr = __pa_symbol(_text);
1229 ehdr->e_phnum++;
1230 phdr++;
1231 }
1232
1233 /* Go through all the ranges in mem->ranges[] and prepare phdr */
1234 for (i = 0; i < mem->nr_ranges; i++) {
1235 mstart = mem->ranges[i].start;
1236 mend = mem->ranges[i].end;
1237
1238 phdr->p_type = PT_LOAD;
1239 phdr->p_flags = PF_R|PF_W|PF_X;
1240 phdr->p_offset = mstart;
1241
1242 phdr->p_paddr = mstart;
1243 phdr->p_vaddr = (unsigned long long) __va(mstart);
1244 phdr->p_filesz = phdr->p_memsz = mend - mstart + 1;
1245 phdr->p_align = 0;
1246 ehdr->e_phnum++;
1247 phdr++;
1248 pr_debug("Crash PT_LOAD elf header. phdr=%p vaddr=0x%llx, paddr=0x%llx, sz=0x%llx e_phnum=%d p_offset=0x%llx\n",
1249 phdr, phdr->p_vaddr, phdr->p_paddr, phdr->p_filesz,
1250 ehdr->e_phnum, phdr->p_offset);
1251 }
1252
1253 *addr = buf;
1254 *sz = elf_sz;
1255 return 0;
1256}