aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/kexec.c
diff options
context:
space:
mode:
authorVivek Goyal <vgoyal@redhat.com>2014-08-08 17:26:04 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2014-08-08 18:57:32 -0400
commit12db5562e0352986a265841638482b84f3a6899b (patch)
tree533100da8331131037128616b3f9d501c07a6def /kernel/kexec.c
parent8fc5b4d4121c95482b2583a07863c6b0aba2d9e1 (diff)
kexec: load and relocate purgatory at kernel load time
Load purgatory code in RAM and relocate it based on the location. Relocation code has been inspired by module relocation code and purgatory relocation code in kexec-tools. Also compute the checksums of loaded kexec segments and store them in purgatory. Arch independent code provides this functionality so that arch dependent bootloaders can make use of it. Helper functions are provided to get/set symbol values in purgatory which are used by bootloaders later to set things like stack and entry point of second kernel etc. Signed-off-by: Vivek Goyal <vgoyal@redhat.com> Cc: Borislav Petkov <bp@suse.de> Cc: Michael Kerrisk <mtk.manpages@gmail.com> Cc: Yinghai Lu <yinghai@kernel.org> Cc: Eric Biederman <ebiederm@xmission.com> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Matthew Garrett <mjg59@srcf.ucam.org> Cc: Greg Kroah-Hartman <greg@kroah.com> Cc: Dave Young <dyoung@redhat.com> Cc: WANG Chao <chaowang@redhat.com> Cc: Baoquan He <bhe@redhat.com> Cc: Andy Lutomirski <luto@amacapital.net> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel/kexec.c')
-rw-r--r--kernel/kexec.c544
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. */
46note_buf_t __percpu *crash_notes; 49note_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 */
55bool kexec_in_progress = false; 58bool 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 */
64char __weak kexec_purgatory[0];
65size_t __weak kexec_purgatory_size = 0;
66
67static 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 */
58struct resource crashk_res = { 70struct 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 */
420int __weak
421arch_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 */
429int __weak
430arch_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 */
412static void kimage_file_post_load_cleanup(struct kimage *image) 442static 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 }
1111out: 1149out:
@@ -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 */
2138static 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
2238out_free_digest:
2239 kfree(digest);
2240out_free_sha_regions:
2241 vfree(sha_regions);
2242out_free_desc:
2243 kfree(desc);
2244out_free_tfm:
2245 kfree(tfm);
2246out:
2247 return ret;
2248}
2249
2250/* Actually load purgatory. Lot of code taken from kexec-tools */
2251static 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;
2426out:
2427 vfree(sechdrs);
2428 vfree(purgatory_buf);
2429 return ret;
2430}
2431
2432static 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 */
2488int 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;
2524out:
2525 vfree(pi->sechdrs);
2526 vfree(pi->purgatory_buf);
2527 return ret;
2528}
2529
2530static 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
2578void *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 */
2601int 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