]>
Commit | Line | Data |
---|---|---|
f3b70e50 AT |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Kexec image loader | |
4 | ||
5 | * Copyright (C) 2018 Linaro Limited | |
6 | * Author: AKASHI Takahiro <takahiro.akashi@linaro.org> | |
7 | */ | |
8 | ||
9 | #define pr_fmt(fmt) "kexec_file(Image): " fmt | |
10 | ||
11 | #include <linux/err.h> | |
12 | #include <linux/errno.h> | |
13 | #include <linux/kernel.h> | |
14 | #include <linux/kexec.h> | |
15 | #include <linux/string.h> | |
16 | #include <asm/byteorder.h> | |
17 | #include <asm/cpufeature.h> | |
18 | #include <asm/image.h> | |
19 | #include <asm/memory.h> | |
20 | ||
21 | static int image_probe(const char *kernel_buf, unsigned long kernel_len) | |
22 | { | |
23 | const struct arm64_image_header *h; | |
24 | ||
25 | h = (const struct arm64_image_header *)(kernel_buf); | |
26 | ||
27 | if (!h || (kernel_len < sizeof(*h)) || | |
28 | memcmp(&h->magic, ARM64_IMAGE_MAGIC, | |
29 | sizeof(h->magic))) | |
30 | return -EINVAL; | |
31 | ||
32 | return 0; | |
33 | } | |
34 | ||
35 | static void *image_load(struct kimage *image, | |
36 | char *kernel, unsigned long kernel_len, | |
37 | char *initrd, unsigned long initrd_len, | |
38 | char *cmdline, unsigned long cmdline_len) | |
39 | { | |
40 | struct arm64_image_header *h; | |
41 | u64 flags, value; | |
42 | bool be_image, be_kernel; | |
43 | struct kexec_buf kbuf; | |
44 | unsigned long text_offset; | |
45 | struct kexec_segment *kernel_segment; | |
46 | int ret; | |
47 | ||
48 | /* | |
49 | * We require a kernel with an unambiguous Image header. Per | |
50 | * Documentation/booting.txt, this is the case when image_size | |
51 | * is non-zero (practically speaking, since v3.17). | |
52 | */ | |
53 | h = (struct arm64_image_header *)kernel; | |
54 | if (!h->image_size) | |
55 | return ERR_PTR(-EINVAL); | |
56 | ||
57 | /* Check cpu features */ | |
58 | flags = le64_to_cpu(h->flags); | |
59 | be_image = arm64_image_flag_field(flags, ARM64_IMAGE_FLAG_BE); | |
60 | be_kernel = IS_ENABLED(CONFIG_CPU_BIG_ENDIAN); | |
61 | if ((be_image != be_kernel) && !system_supports_mixed_endian()) | |
62 | return ERR_PTR(-EINVAL); | |
63 | ||
64 | value = arm64_image_flag_field(flags, ARM64_IMAGE_FLAG_PAGE_SIZE); | |
65 | if (((value == ARM64_IMAGE_FLAG_PAGE_SIZE_4K) && | |
66 | !system_supports_4kb_granule()) || | |
67 | ((value == ARM64_IMAGE_FLAG_PAGE_SIZE_64K) && | |
68 | !system_supports_64kb_granule()) || | |
69 | ((value == ARM64_IMAGE_FLAG_PAGE_SIZE_16K) && | |
70 | !system_supports_16kb_granule())) | |
71 | return ERR_PTR(-EINVAL); | |
72 | ||
73 | /* Load the kernel */ | |
74 | kbuf.image = image; | |
75 | kbuf.buf_min = 0; | |
76 | kbuf.buf_max = ULONG_MAX; | |
77 | kbuf.top_down = false; | |
78 | ||
79 | kbuf.buffer = kernel; | |
80 | kbuf.bufsz = kernel_len; | |
81 | kbuf.mem = 0; | |
82 | kbuf.memsz = le64_to_cpu(h->image_size); | |
83 | text_offset = le64_to_cpu(h->text_offset); | |
84 | kbuf.buf_align = MIN_KIMG_ALIGN; | |
85 | ||
86 | /* Adjust kernel segment with TEXT_OFFSET */ | |
87 | kbuf.memsz += text_offset; | |
88 | ||
89 | ret = kexec_add_buffer(&kbuf); | |
90 | if (ret) | |
91 | return ERR_PTR(ret); | |
92 | ||
93 | kernel_segment = &image->segment[image->nr_segments - 1]; | |
94 | kernel_segment->mem += text_offset; | |
95 | kernel_segment->memsz -= text_offset; | |
96 | image->start = kernel_segment->mem; | |
97 | ||
98 | pr_debug("Loaded kernel at 0x%lx bufsz=0x%lx memsz=0x%lx\n", | |
99 | kernel_segment->mem, kbuf.bufsz, | |
100 | kernel_segment->memsz); | |
101 | ||
102 | /* Load additional data */ | |
103 | ret = load_other_segments(image, | |
104 | kernel_segment->mem, kernel_segment->memsz, | |
105 | initrd, initrd_len, cmdline); | |
106 | ||
107 | return ERR_PTR(ret); | |
108 | } | |
109 | ||
110 | const struct kexec_file_ops kexec_image_ops = { | |
111 | .probe = image_probe, | |
112 | .load = image_load, | |
113 | }; |