diff options
Diffstat (limited to 'kernel/kexec.c')
-rw-r--r-- | kernel/kexec.c | 544 |
1 files changed, 543 insertions, 1 deletions
diff --git a/kernel/kexec.c b/kernel/kexec.c index 9b46219254dd..669e331aa9ec 100644 --- a/kernel/kexec.c +++ b/kernel/kexec.c | |||
@@ -42,6 +42,9 @@ | |||
42 | #include <asm/io.h> | 42 | #include <asm/io.h> |
43 | #include <asm/sections.h> | 43 | #include <asm/sections.h> |
44 | 44 | ||
45 | #include <crypto/hash.h> | ||
46 | #include <crypto/sha.h> | ||
47 | |||
45 | /* Per cpu memory for storing cpu states in case of system crash. */ | 48 | /* Per cpu memory for storing cpu states in case of system crash. */ |
46 | note_buf_t __percpu *crash_notes; | 49 | note_buf_t __percpu *crash_notes; |
47 | 50 | ||
@@ -54,6 +57,15 @@ size_t vmcoreinfo_max_size = sizeof(vmcoreinfo_data); | |||
54 | /* Flag to indicate we are going to kexec a new kernel */ | 57 | /* Flag to indicate we are going to kexec a new kernel */ |
55 | bool kexec_in_progress = false; | 58 | bool kexec_in_progress = false; |
56 | 59 | ||
60 | /* | ||
61 | * Declare these symbols weak so that if architecture provides a purgatory, | ||
62 | * these will be overridden. | ||
63 | */ | ||
64 | char __weak kexec_purgatory[0]; | ||
65 | size_t __weak kexec_purgatory_size = 0; | ||
66 | |||
67 | static int kexec_calculate_store_digests(struct kimage *image); | ||
68 | |||
57 | /* Location of the reserved area for the crash kernel */ | 69 | /* Location of the reserved area for the crash kernel */ |
58 | struct resource crashk_res = { | 70 | struct resource crashk_res = { |
59 | .name = "Crash kernel", | 71 | .name = "Crash kernel", |
@@ -404,6 +416,24 @@ void __weak arch_kimage_file_post_load_cleanup(struct kimage *image) | |||
404 | { | 416 | { |
405 | } | 417 | } |
406 | 418 | ||
419 | /* Apply relocations of type RELA */ | ||
420 | int __weak | ||
421 | arch_kexec_apply_relocations_add(const Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, | ||
422 | unsigned int relsec) | ||
423 | { | ||
424 | pr_err("RELA relocation unsupported.\n"); | ||
425 | return -ENOEXEC; | ||
426 | } | ||
427 | |||
428 | /* Apply relocations of type REL */ | ||
429 | int __weak | ||
430 | arch_kexec_apply_relocations(const Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, | ||
431 | unsigned int relsec) | ||
432 | { | ||
433 | pr_err("REL relocation unsupported.\n"); | ||
434 | return -ENOEXEC; | ||
435 | } | ||
436 | |||
407 | /* | 437 | /* |
408 | * Free up memory used by kernel, initrd, and comand line. This is temporary | 438 | * Free up memory used by kernel, initrd, and comand line. This is temporary |
409 | * memory allocation which is not needed any more after these buffers have | 439 | * memory allocation which is not needed any more after these buffers have |
@@ -411,6 +441,8 @@ void __weak arch_kimage_file_post_load_cleanup(struct kimage *image) | |||
411 | */ | 441 | */ |
412 | static void kimage_file_post_load_cleanup(struct kimage *image) | 442 | static void kimage_file_post_load_cleanup(struct kimage *image) |
413 | { | 443 | { |
444 | struct purgatory_info *pi = &image->purgatory_info; | ||
445 | |||
414 | vfree(image->kernel_buf); | 446 | vfree(image->kernel_buf); |
415 | image->kernel_buf = NULL; | 447 | image->kernel_buf = NULL; |
416 | 448 | ||
@@ -420,6 +452,12 @@ static void kimage_file_post_load_cleanup(struct kimage *image) | |||
420 | kfree(image->cmdline_buf); | 452 | kfree(image->cmdline_buf); |
421 | image->cmdline_buf = NULL; | 453 | image->cmdline_buf = NULL; |
422 | 454 | ||
455 | vfree(pi->purgatory_buf); | ||
456 | pi->purgatory_buf = NULL; | ||
457 | |||
458 | vfree(pi->sechdrs); | ||
459 | pi->sechdrs = NULL; | ||
460 | |||
423 | /* See if architecture has anything to cleanup post load */ | 461 | /* See if architecture has anything to cleanup post load */ |
424 | arch_kimage_file_post_load_cleanup(image); | 462 | arch_kimage_file_post_load_cleanup(image); |
425 | } | 463 | } |
@@ -1105,7 +1143,7 @@ static int kimage_load_crash_segment(struct kimage *image, | |||
1105 | } | 1143 | } |
1106 | ubytes -= uchunk; | 1144 | ubytes -= uchunk; |
1107 | maddr += mchunk; | 1145 | maddr += mchunk; |
1108 | buf += mchunk; | 1146 | buf += mchunk; |
1109 | mbytes -= mchunk; | 1147 | mbytes -= mchunk; |
1110 | } | 1148 | } |
1111 | out: | 1149 | out: |
@@ -1340,6 +1378,10 @@ SYSCALL_DEFINE5(kexec_file_load, int, kernel_fd, int, initrd_fd, | |||
1340 | if (ret) | 1378 | if (ret) |
1341 | goto out; | 1379 | goto out; |
1342 | 1380 | ||
1381 | ret = kexec_calculate_store_digests(image); | ||
1382 | if (ret) | ||
1383 | goto out; | ||
1384 | |||
1343 | for (i = 0; i < image->nr_segments; i++) { | 1385 | for (i = 0; i < image->nr_segments; i++) { |
1344 | struct kexec_segment *ksegment; | 1386 | struct kexec_segment *ksegment; |
1345 | 1387 | ||
@@ -2092,6 +2134,506 @@ int kexec_add_buffer(struct kimage *image, char *buffer, unsigned long bufsz, | |||
2092 | return 0; | 2134 | return 0; |
2093 | } | 2135 | } |
2094 | 2136 | ||
2137 | /* Calculate and store the digest of segments */ | ||
2138 | static int kexec_calculate_store_digests(struct kimage *image) | ||
2139 | { | ||
2140 | struct crypto_shash *tfm; | ||
2141 | struct shash_desc *desc; | ||
2142 | int ret = 0, i, j, zero_buf_sz, sha_region_sz; | ||
2143 | size_t desc_size, nullsz; | ||
2144 | char *digest; | ||
2145 | void *zero_buf; | ||
2146 | struct kexec_sha_region *sha_regions; | ||
2147 | struct purgatory_info *pi = &image->purgatory_info; | ||
2148 | |||
2149 | zero_buf = __va(page_to_pfn(ZERO_PAGE(0)) << PAGE_SHIFT); | ||
2150 | zero_buf_sz = PAGE_SIZE; | ||
2151 | |||
2152 | tfm = crypto_alloc_shash("sha256", 0, 0); | ||
2153 | if (IS_ERR(tfm)) { | ||
2154 | ret = PTR_ERR(tfm); | ||
2155 | goto out; | ||
2156 | } | ||
2157 | |||
2158 | desc_size = crypto_shash_descsize(tfm) + sizeof(*desc); | ||
2159 | desc = kzalloc(desc_size, GFP_KERNEL); | ||
2160 | if (!desc) { | ||
2161 | ret = -ENOMEM; | ||
2162 | goto out_free_tfm; | ||
2163 | } | ||
2164 | |||
2165 | sha_region_sz = KEXEC_SEGMENT_MAX * sizeof(struct kexec_sha_region); | ||
2166 | sha_regions = vzalloc(sha_region_sz); | ||
2167 | if (!sha_regions) | ||
2168 | goto out_free_desc; | ||
2169 | |||
2170 | desc->tfm = tfm; | ||
2171 | desc->flags = 0; | ||
2172 | |||
2173 | ret = crypto_shash_init(desc); | ||
2174 | if (ret < 0) | ||
2175 | goto out_free_sha_regions; | ||
2176 | |||
2177 | digest = kzalloc(SHA256_DIGEST_SIZE, GFP_KERNEL); | ||
2178 | if (!digest) { | ||
2179 | ret = -ENOMEM; | ||
2180 | goto out_free_sha_regions; | ||
2181 | } | ||
2182 | |||
2183 | for (j = i = 0; i < image->nr_segments; i++) { | ||
2184 | struct kexec_segment *ksegment; | ||
2185 | |||
2186 | ksegment = &image->segment[i]; | ||
2187 | /* | ||
2188 | * Skip purgatory as it will be modified once we put digest | ||
2189 | * info in purgatory. | ||
2190 | */ | ||
2191 | if (ksegment->kbuf == pi->purgatory_buf) | ||
2192 | continue; | ||
2193 | |||
2194 | ret = crypto_shash_update(desc, ksegment->kbuf, | ||
2195 | ksegment->bufsz); | ||
2196 | if (ret) | ||
2197 | break; | ||
2198 | |||
2199 | /* | ||
2200 | * Assume rest of the buffer is filled with zero and | ||
2201 | * update digest accordingly. | ||
2202 | */ | ||
2203 | nullsz = ksegment->memsz - ksegment->bufsz; | ||
2204 | while (nullsz) { | ||
2205 | unsigned long bytes = nullsz; | ||
2206 | |||
2207 | if (bytes > zero_buf_sz) | ||
2208 | bytes = zero_buf_sz; | ||
2209 | ret = crypto_shash_update(desc, zero_buf, bytes); | ||
2210 | if (ret) | ||
2211 | break; | ||
2212 | nullsz -= bytes; | ||
2213 | } | ||
2214 | |||
2215 | if (ret) | ||
2216 | break; | ||
2217 | |||
2218 | sha_regions[j].start = ksegment->mem; | ||
2219 | sha_regions[j].len = ksegment->memsz; | ||
2220 | j++; | ||
2221 | } | ||
2222 | |||
2223 | if (!ret) { | ||
2224 | ret = crypto_shash_final(desc, digest); | ||
2225 | if (ret) | ||
2226 | goto out_free_digest; | ||
2227 | ret = kexec_purgatory_get_set_symbol(image, "sha_regions", | ||
2228 | sha_regions, sha_region_sz, 0); | ||
2229 | if (ret) | ||
2230 | goto out_free_digest; | ||
2231 | |||
2232 | ret = kexec_purgatory_get_set_symbol(image, "sha256_digest", | ||
2233 | digest, SHA256_DIGEST_SIZE, 0); | ||
2234 | if (ret) | ||
2235 | goto out_free_digest; | ||
2236 | } | ||
2237 | |||
2238 | out_free_digest: | ||
2239 | kfree(digest); | ||
2240 | out_free_sha_regions: | ||
2241 | vfree(sha_regions); | ||
2242 | out_free_desc: | ||
2243 | kfree(desc); | ||
2244 | out_free_tfm: | ||
2245 | kfree(tfm); | ||
2246 | out: | ||
2247 | return ret; | ||
2248 | } | ||
2249 | |||
2250 | /* Actually load purgatory. Lot of code taken from kexec-tools */ | ||
2251 | static int __kexec_load_purgatory(struct kimage *image, unsigned long min, | ||
2252 | unsigned long max, int top_down) | ||
2253 | { | ||
2254 | struct purgatory_info *pi = &image->purgatory_info; | ||
2255 | unsigned long align, buf_align, bss_align, buf_sz, bss_sz, bss_pad; | ||
2256 | unsigned long memsz, entry, load_addr, curr_load_addr, bss_addr, offset; | ||
2257 | unsigned char *buf_addr, *src; | ||
2258 | int i, ret = 0, entry_sidx = -1; | ||
2259 | const Elf_Shdr *sechdrs_c; | ||
2260 | Elf_Shdr *sechdrs = NULL; | ||
2261 | void *purgatory_buf = NULL; | ||
2262 | |||
2263 | /* | ||
2264 | * sechdrs_c points to section headers in purgatory and are read | ||
2265 | * only. No modifications allowed. | ||
2266 | */ | ||
2267 | sechdrs_c = (void *)pi->ehdr + pi->ehdr->e_shoff; | ||
2268 | |||
2269 | /* | ||
2270 | * We can not modify sechdrs_c[] and its fields. It is read only. | ||
2271 | * Copy it over to a local copy where one can store some temporary | ||
2272 | * data and free it at the end. We need to modify ->sh_addr and | ||
2273 | * ->sh_offset fields to keep track of permanent and temporary | ||
2274 | * locations of sections. | ||
2275 | */ | ||
2276 | sechdrs = vzalloc(pi->ehdr->e_shnum * sizeof(Elf_Shdr)); | ||
2277 | if (!sechdrs) | ||
2278 | return -ENOMEM; | ||
2279 | |||
2280 | memcpy(sechdrs, sechdrs_c, pi->ehdr->e_shnum * sizeof(Elf_Shdr)); | ||
2281 | |||
2282 | /* | ||
2283 | * We seem to have multiple copies of sections. First copy is which | ||
2284 | * is embedded in kernel in read only section. Some of these sections | ||
2285 | * will be copied to a temporary buffer and relocated. And these | ||
2286 | * sections will finally be copied to their final destination at | ||
2287 | * segment load time. | ||
2288 | * | ||
2289 | * Use ->sh_offset to reflect section address in memory. It will | ||
2290 | * point to original read only copy if section is not allocatable. | ||
2291 | * Otherwise it will point to temporary copy which will be relocated. | ||
2292 | * | ||
2293 | * Use ->sh_addr to contain final address of the section where it | ||
2294 | * will go during execution time. | ||
2295 | */ | ||
2296 | for (i = 0; i < pi->ehdr->e_shnum; i++) { | ||
2297 | if (sechdrs[i].sh_type == SHT_NOBITS) | ||
2298 | continue; | ||
2299 | |||
2300 | sechdrs[i].sh_offset = (unsigned long)pi->ehdr + | ||
2301 | sechdrs[i].sh_offset; | ||
2302 | } | ||
2303 | |||
2304 | /* | ||
2305 | * Identify entry point section and make entry relative to section | ||
2306 | * start. | ||
2307 | */ | ||
2308 | entry = pi->ehdr->e_entry; | ||
2309 | for (i = 0; i < pi->ehdr->e_shnum; i++) { | ||
2310 | if (!(sechdrs[i].sh_flags & SHF_ALLOC)) | ||
2311 | continue; | ||
2312 | |||
2313 | if (!(sechdrs[i].sh_flags & SHF_EXECINSTR)) | ||
2314 | continue; | ||
2315 | |||
2316 | /* Make entry section relative */ | ||
2317 | if (sechdrs[i].sh_addr <= pi->ehdr->e_entry && | ||
2318 | ((sechdrs[i].sh_addr + sechdrs[i].sh_size) > | ||
2319 | pi->ehdr->e_entry)) { | ||
2320 | entry_sidx = i; | ||
2321 | entry -= sechdrs[i].sh_addr; | ||
2322 | break; | ||
2323 | } | ||
2324 | } | ||
2325 | |||
2326 | /* Determine how much memory is needed to load relocatable object. */ | ||
2327 | buf_align = 1; | ||
2328 | bss_align = 1; | ||
2329 | buf_sz = 0; | ||
2330 | bss_sz = 0; | ||
2331 | |||
2332 | for (i = 0; i < pi->ehdr->e_shnum; i++) { | ||
2333 | if (!(sechdrs[i].sh_flags & SHF_ALLOC)) | ||
2334 | continue; | ||
2335 | |||
2336 | align = sechdrs[i].sh_addralign; | ||
2337 | if (sechdrs[i].sh_type != SHT_NOBITS) { | ||
2338 | if (buf_align < align) | ||
2339 | buf_align = align; | ||
2340 | buf_sz = ALIGN(buf_sz, align); | ||
2341 | buf_sz += sechdrs[i].sh_size; | ||
2342 | } else { | ||
2343 | /* bss section */ | ||
2344 | if (bss_align < align) | ||
2345 | bss_align = align; | ||
2346 | bss_sz = ALIGN(bss_sz, align); | ||
2347 | bss_sz += sechdrs[i].sh_size; | ||
2348 | } | ||
2349 | } | ||
2350 | |||
2351 | /* Determine the bss padding required to align bss properly */ | ||
2352 | bss_pad = 0; | ||
2353 | if (buf_sz & (bss_align - 1)) | ||
2354 | bss_pad = bss_align - (buf_sz & (bss_align - 1)); | ||
2355 | |||
2356 | memsz = buf_sz + bss_pad + bss_sz; | ||
2357 | |||
2358 | /* Allocate buffer for purgatory */ | ||
2359 | purgatory_buf = vzalloc(buf_sz); | ||
2360 | if (!purgatory_buf) { | ||
2361 | ret = -ENOMEM; | ||
2362 | goto out; | ||
2363 | } | ||
2364 | |||
2365 | if (buf_align < bss_align) | ||
2366 | buf_align = bss_align; | ||
2367 | |||
2368 | /* Add buffer to segment list */ | ||
2369 | ret = kexec_add_buffer(image, purgatory_buf, buf_sz, memsz, | ||
2370 | buf_align, min, max, top_down, | ||
2371 | &pi->purgatory_load_addr); | ||
2372 | if (ret) | ||
2373 | goto out; | ||
2374 | |||
2375 | /* Load SHF_ALLOC sections */ | ||
2376 | buf_addr = purgatory_buf; | ||
2377 | load_addr = curr_load_addr = pi->purgatory_load_addr; | ||
2378 | bss_addr = load_addr + buf_sz + bss_pad; | ||
2379 | |||
2380 | for (i = 0; i < pi->ehdr->e_shnum; i++) { | ||
2381 | if (!(sechdrs[i].sh_flags & SHF_ALLOC)) | ||
2382 | continue; | ||
2383 | |||
2384 | align = sechdrs[i].sh_addralign; | ||
2385 | if (sechdrs[i].sh_type != SHT_NOBITS) { | ||
2386 | curr_load_addr = ALIGN(curr_load_addr, align); | ||
2387 | offset = curr_load_addr - load_addr; | ||
2388 | /* We already modifed ->sh_offset to keep src addr */ | ||
2389 | src = (char *) sechdrs[i].sh_offset; | ||
2390 | memcpy(buf_addr + offset, src, sechdrs[i].sh_size); | ||
2391 | |||
2392 | /* Store load address and source address of section */ | ||
2393 | sechdrs[i].sh_addr = curr_load_addr; | ||
2394 | |||
2395 | /* | ||
2396 | * This section got copied to temporary buffer. Update | ||
2397 | * ->sh_offset accordingly. | ||
2398 | */ | ||
2399 | sechdrs[i].sh_offset = (unsigned long)(buf_addr + offset); | ||
2400 | |||
2401 | /* Advance to the next address */ | ||
2402 | curr_load_addr += sechdrs[i].sh_size; | ||
2403 | } else { | ||
2404 | bss_addr = ALIGN(bss_addr, align); | ||
2405 | sechdrs[i].sh_addr = bss_addr; | ||
2406 | bss_addr += sechdrs[i].sh_size; | ||
2407 | } | ||
2408 | } | ||
2409 | |||
2410 | /* Update entry point based on load address of text section */ | ||
2411 | if (entry_sidx >= 0) | ||
2412 | entry += sechdrs[entry_sidx].sh_addr; | ||
2413 | |||
2414 | /* Make kernel jump to purgatory after shutdown */ | ||
2415 | image->start = entry; | ||
2416 | |||
2417 | /* Used later to get/set symbol values */ | ||
2418 | pi->sechdrs = sechdrs; | ||
2419 | |||
2420 | /* | ||
2421 | * Used later to identify which section is purgatory and skip it | ||
2422 | * from checksumming. | ||
2423 | */ | ||
2424 | pi->purgatory_buf = purgatory_buf; | ||
2425 | return ret; | ||
2426 | out: | ||
2427 | vfree(sechdrs); | ||
2428 | vfree(purgatory_buf); | ||
2429 | return ret; | ||
2430 | } | ||
2431 | |||
2432 | static int kexec_apply_relocations(struct kimage *image) | ||
2433 | { | ||
2434 | int i, ret; | ||
2435 | struct purgatory_info *pi = &image->purgatory_info; | ||
2436 | Elf_Shdr *sechdrs = pi->sechdrs; | ||
2437 | |||
2438 | /* Apply relocations */ | ||
2439 | for (i = 0; i < pi->ehdr->e_shnum; i++) { | ||
2440 | Elf_Shdr *section, *symtab; | ||
2441 | |||
2442 | if (sechdrs[i].sh_type != SHT_RELA && | ||
2443 | sechdrs[i].sh_type != SHT_REL) | ||
2444 | continue; | ||
2445 | |||
2446 | /* | ||
2447 | * For section of type SHT_RELA/SHT_REL, | ||
2448 | * ->sh_link contains section header index of associated | ||
2449 | * symbol table. And ->sh_info contains section header | ||
2450 | * index of section to which relocations apply. | ||
2451 | */ | ||
2452 | if (sechdrs[i].sh_info >= pi->ehdr->e_shnum || | ||
2453 | sechdrs[i].sh_link >= pi->ehdr->e_shnum) | ||
2454 | return -ENOEXEC; | ||
2455 | |||
2456 | section = &sechdrs[sechdrs[i].sh_info]; | ||
2457 | symtab = &sechdrs[sechdrs[i].sh_link]; | ||
2458 | |||
2459 | if (!(section->sh_flags & SHF_ALLOC)) | ||
2460 | continue; | ||
2461 | |||
2462 | /* | ||
2463 | * symtab->sh_link contain section header index of associated | ||
2464 | * string table. | ||
2465 | */ | ||
2466 | if (symtab->sh_link >= pi->ehdr->e_shnum) | ||
2467 | /* Invalid section number? */ | ||
2468 | continue; | ||
2469 | |||
2470 | /* | ||
2471 | * Respective archicture needs to provide support for applying | ||
2472 | * relocations of type SHT_RELA/SHT_REL. | ||
2473 | */ | ||
2474 | if (sechdrs[i].sh_type == SHT_RELA) | ||
2475 | ret = arch_kexec_apply_relocations_add(pi->ehdr, | ||
2476 | sechdrs, i); | ||
2477 | else if (sechdrs[i].sh_type == SHT_REL) | ||
2478 | ret = arch_kexec_apply_relocations(pi->ehdr, | ||
2479 | sechdrs, i); | ||
2480 | if (ret) | ||
2481 | return ret; | ||
2482 | } | ||
2483 | |||
2484 | return 0; | ||
2485 | } | ||
2486 | |||
2487 | /* Load relocatable purgatory object and relocate it appropriately */ | ||
2488 | int kexec_load_purgatory(struct kimage *image, unsigned long min, | ||
2489 | unsigned long max, int top_down, | ||
2490 | unsigned long *load_addr) | ||
2491 | { | ||
2492 | struct purgatory_info *pi = &image->purgatory_info; | ||
2493 | int ret; | ||
2494 | |||
2495 | if (kexec_purgatory_size <= 0) | ||
2496 | return -EINVAL; | ||
2497 | |||
2498 | if (kexec_purgatory_size < sizeof(Elf_Ehdr)) | ||
2499 | return -ENOEXEC; | ||
2500 | |||
2501 | pi->ehdr = (Elf_Ehdr *)kexec_purgatory; | ||
2502 | |||
2503 | if (memcmp(pi->ehdr->e_ident, ELFMAG, SELFMAG) != 0 | ||
2504 | || pi->ehdr->e_type != ET_REL | ||
2505 | || !elf_check_arch(pi->ehdr) | ||
2506 | || pi->ehdr->e_shentsize != sizeof(Elf_Shdr)) | ||
2507 | return -ENOEXEC; | ||
2508 | |||
2509 | if (pi->ehdr->e_shoff >= kexec_purgatory_size | ||
2510 | || (pi->ehdr->e_shnum * sizeof(Elf_Shdr) > | ||
2511 | kexec_purgatory_size - pi->ehdr->e_shoff)) | ||
2512 | return -ENOEXEC; | ||
2513 | |||
2514 | ret = __kexec_load_purgatory(image, min, max, top_down); | ||
2515 | if (ret) | ||
2516 | return ret; | ||
2517 | |||
2518 | ret = kexec_apply_relocations(image); | ||
2519 | if (ret) | ||
2520 | goto out; | ||
2521 | |||
2522 | *load_addr = pi->purgatory_load_addr; | ||
2523 | return 0; | ||
2524 | out: | ||
2525 | vfree(pi->sechdrs); | ||
2526 | vfree(pi->purgatory_buf); | ||
2527 | return ret; | ||
2528 | } | ||
2529 | |||
2530 | static Elf_Sym *kexec_purgatory_find_symbol(struct purgatory_info *pi, | ||
2531 | const char *name) | ||
2532 | { | ||
2533 | Elf_Sym *syms; | ||
2534 | Elf_Shdr *sechdrs; | ||
2535 | Elf_Ehdr *ehdr; | ||
2536 | int i, k; | ||
2537 | const char *strtab; | ||
2538 | |||
2539 | if (!pi->sechdrs || !pi->ehdr) | ||
2540 | return NULL; | ||
2541 | |||
2542 | sechdrs = pi->sechdrs; | ||
2543 | ehdr = pi->ehdr; | ||
2544 | |||
2545 | for (i = 0; i < ehdr->e_shnum; i++) { | ||
2546 | if (sechdrs[i].sh_type != SHT_SYMTAB) | ||
2547 | continue; | ||
2548 | |||
2549 | if (sechdrs[i].sh_link >= ehdr->e_shnum) | ||
2550 | /* Invalid strtab section number */ | ||
2551 | continue; | ||
2552 | strtab = (char *)sechdrs[sechdrs[i].sh_link].sh_offset; | ||
2553 | syms = (Elf_Sym *)sechdrs[i].sh_offset; | ||
2554 | |||
2555 | /* Go through symbols for a match */ | ||
2556 | for (k = 0; k < sechdrs[i].sh_size/sizeof(Elf_Sym); k++) { | ||
2557 | if (ELF_ST_BIND(syms[k].st_info) != STB_GLOBAL) | ||
2558 | continue; | ||
2559 | |||
2560 | if (strcmp(strtab + syms[k].st_name, name) != 0) | ||
2561 | continue; | ||
2562 | |||
2563 | if (syms[k].st_shndx == SHN_UNDEF || | ||
2564 | syms[k].st_shndx >= ehdr->e_shnum) { | ||
2565 | pr_debug("Symbol: %s has bad section index %d.\n", | ||
2566 | name, syms[k].st_shndx); | ||
2567 | return NULL; | ||
2568 | } | ||
2569 | |||
2570 | /* Found the symbol we are looking for */ | ||
2571 | return &syms[k]; | ||
2572 | } | ||
2573 | } | ||
2574 | |||
2575 | return NULL; | ||
2576 | } | ||
2577 | |||
2578 | void *kexec_purgatory_get_symbol_addr(struct kimage *image, const char *name) | ||
2579 | { | ||
2580 | struct purgatory_info *pi = &image->purgatory_info; | ||
2581 | Elf_Sym *sym; | ||
2582 | Elf_Shdr *sechdr; | ||
2583 | |||
2584 | sym = kexec_purgatory_find_symbol(pi, name); | ||
2585 | if (!sym) | ||
2586 | return ERR_PTR(-EINVAL); | ||
2587 | |||
2588 | sechdr = &pi->sechdrs[sym->st_shndx]; | ||
2589 | |||
2590 | /* | ||
2591 | * Returns the address where symbol will finally be loaded after | ||
2592 | * kexec_load_segment() | ||
2593 | */ | ||
2594 | return (void *)(sechdr->sh_addr + sym->st_value); | ||
2595 | } | ||
2596 | |||
2597 | /* | ||
2598 | * Get or set value of a symbol. If "get_value" is true, symbol value is | ||
2599 | * returned in buf otherwise symbol value is set based on value in buf. | ||
2600 | */ | ||
2601 | int kexec_purgatory_get_set_symbol(struct kimage *image, const char *name, | ||
2602 | void *buf, unsigned int size, bool get_value) | ||
2603 | { | ||
2604 | Elf_Sym *sym; | ||
2605 | Elf_Shdr *sechdrs; | ||
2606 | struct purgatory_info *pi = &image->purgatory_info; | ||
2607 | char *sym_buf; | ||
2608 | |||
2609 | sym = kexec_purgatory_find_symbol(pi, name); | ||
2610 | if (!sym) | ||
2611 | return -EINVAL; | ||
2612 | |||
2613 | if (sym->st_size != size) { | ||
2614 | pr_err("symbol %s size mismatch: expected %lu actual %u\n", | ||
2615 | name, (unsigned long)sym->st_size, size); | ||
2616 | return -EINVAL; | ||
2617 | } | ||
2618 | |||
2619 | sechdrs = pi->sechdrs; | ||
2620 | |||
2621 | if (sechdrs[sym->st_shndx].sh_type == SHT_NOBITS) { | ||
2622 | pr_err("symbol %s is in a bss section. Cannot %s\n", name, | ||
2623 | get_value ? "get" : "set"); | ||
2624 | return -EINVAL; | ||
2625 | } | ||
2626 | |||
2627 | sym_buf = (unsigned char *)sechdrs[sym->st_shndx].sh_offset + | ||
2628 | sym->st_value; | ||
2629 | |||
2630 | if (get_value) | ||
2631 | memcpy((void *)buf, sym_buf, size); | ||
2632 | else | ||
2633 | memcpy((void *)sym_buf, buf, size); | ||
2634 | |||
2635 | return 0; | ||
2636 | } | ||
2095 | 2637 | ||
2096 | /* | 2638 | /* |
2097 | * Move into place and start executing a preloaded standalone | 2639 | * Move into place and start executing a preloaded standalone |