diff options
author | AKASHI Takahiro <takahiro.akashi@linaro.org> | 2018-04-13 18:36:06 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2018-04-13 20:10:27 -0400 |
commit | babac4a84a88842bec477a5bdada1460f3bc374c (patch) | |
tree | 88e84c21942b051c2dfe941cadb8d8cdb4aa7ec0 /kernel/kexec_file.c | |
parent | eb7dae947ef5c50e0121f673fcb43ca3583e5849 (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.c | 175 |
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 | |||
1088 | int 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 | |||
1159 | int 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 | } | ||