Merge tag 'vfio-v5.14-rc1' of git://github.com/awilliam/linux-vfio
[linux-2.6-microblaze.git] / arch / arm64 / kernel / machine_kexec_file.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * kexec_file for arm64
4  *
5  * Copyright (C) 2018 Linaro Limited
6  * Author: AKASHI Takahiro <takahiro.akashi@linaro.org>
7  *
8  * Most code is derived from arm64 port of kexec-tools
9  */
10
11 #define pr_fmt(fmt) "kexec_file: " fmt
12
13 #include <linux/ioport.h>
14 #include <linux/kernel.h>
15 #include <linux/kexec.h>
16 #include <linux/libfdt.h>
17 #include <linux/memblock.h>
18 #include <linux/of.h>
19 #include <linux/of_fdt.h>
20 #include <linux/slab.h>
21 #include <linux/string.h>
22 #include <linux/types.h>
23 #include <linux/vmalloc.h>
24
25 const struct kexec_file_ops * const kexec_file_loaders[] = {
26         &kexec_image_ops,
27         NULL
28 };
29
30 int arch_kimage_file_post_load_cleanup(struct kimage *image)
31 {
32         kvfree(image->arch.dtb);
33         image->arch.dtb = NULL;
34
35         vfree(image->elf_headers);
36         image->elf_headers = NULL;
37         image->elf_headers_sz = 0;
38
39         return kexec_image_post_load_cleanup_default(image);
40 }
41
42 static int prepare_elf_headers(void **addr, unsigned long *sz)
43 {
44         struct crash_mem *cmem;
45         unsigned int nr_ranges;
46         int ret;
47         u64 i;
48         phys_addr_t start, end;
49
50         nr_ranges = 1; /* for exclusion of crashkernel region */
51         for_each_mem_range(i, &start, &end)
52                 nr_ranges++;
53
54         cmem = kmalloc(struct_size(cmem, ranges, nr_ranges), GFP_KERNEL);
55         if (!cmem)
56                 return -ENOMEM;
57
58         cmem->max_nr_ranges = nr_ranges;
59         cmem->nr_ranges = 0;
60         for_each_mem_range(i, &start, &end) {
61                 cmem->ranges[cmem->nr_ranges].start = start;
62                 cmem->ranges[cmem->nr_ranges].end = end - 1;
63                 cmem->nr_ranges++;
64         }
65
66         /* Exclude crashkernel region */
67         ret = crash_exclude_mem_range(cmem, crashk_res.start, crashk_res.end);
68
69         if (!ret)
70                 ret =  crash_prepare_elf64_headers(cmem, true, addr, sz);
71
72         kfree(cmem);
73         return ret;
74 }
75
76 /*
77  * Tries to add the initrd and DTB to the image. If it is not possible to find
78  * valid locations, this function will undo changes to the image and return non
79  * zero.
80  */
81 int load_other_segments(struct kimage *image,
82                         unsigned long kernel_load_addr,
83                         unsigned long kernel_size,
84                         char *initrd, unsigned long initrd_len,
85                         char *cmdline)
86 {
87         struct kexec_buf kbuf;
88         void *headers, *dtb = NULL;
89         unsigned long headers_sz, initrd_load_addr = 0, dtb_len,
90                       orig_segments = image->nr_segments;
91         int ret = 0;
92
93         kbuf.image = image;
94         /* not allocate anything below the kernel */
95         kbuf.buf_min = kernel_load_addr + kernel_size;
96
97         /* load elf core header */
98         if (image->type == KEXEC_TYPE_CRASH) {
99                 ret = prepare_elf_headers(&headers, &headers_sz);
100                 if (ret) {
101                         pr_err("Preparing elf core header failed\n");
102                         goto out_err;
103                 }
104
105                 kbuf.buffer = headers;
106                 kbuf.bufsz = headers_sz;
107                 kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
108                 kbuf.memsz = headers_sz;
109                 kbuf.buf_align = SZ_64K; /* largest supported page size */
110                 kbuf.buf_max = ULONG_MAX;
111                 kbuf.top_down = true;
112
113                 ret = kexec_add_buffer(&kbuf);
114                 if (ret) {
115                         vfree(headers);
116                         goto out_err;
117                 }
118                 image->elf_headers = headers;
119                 image->elf_load_addr = kbuf.mem;
120                 image->elf_headers_sz = headers_sz;
121
122                 pr_debug("Loaded elf core header at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
123                          image->elf_load_addr, kbuf.bufsz, kbuf.memsz);
124         }
125
126         /* load initrd */
127         if (initrd) {
128                 kbuf.buffer = initrd;
129                 kbuf.bufsz = initrd_len;
130                 kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
131                 kbuf.memsz = initrd_len;
132                 kbuf.buf_align = 0;
133                 /* within 1GB-aligned window of up to 32GB in size */
134                 kbuf.buf_max = round_down(kernel_load_addr, SZ_1G)
135                                                 + (unsigned long)SZ_1G * 32;
136                 kbuf.top_down = false;
137
138                 ret = kexec_add_buffer(&kbuf);
139                 if (ret)
140                         goto out_err;
141                 initrd_load_addr = kbuf.mem;
142
143                 pr_debug("Loaded initrd at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
144                                 initrd_load_addr, kbuf.bufsz, kbuf.memsz);
145         }
146
147         /* load dtb */
148         dtb = of_kexec_alloc_and_setup_fdt(image, initrd_load_addr,
149                                            initrd_len, cmdline, 0);
150         if (!dtb) {
151                 pr_err("Preparing for new dtb failed\n");
152                 goto out_err;
153         }
154
155         /* trim it */
156         fdt_pack(dtb);
157         dtb_len = fdt_totalsize(dtb);
158         kbuf.buffer = dtb;
159         kbuf.bufsz = dtb_len;
160         kbuf.mem = KEXEC_BUF_MEM_UNKNOWN;
161         kbuf.memsz = dtb_len;
162         /* not across 2MB boundary */
163         kbuf.buf_align = SZ_2M;
164         kbuf.buf_max = ULONG_MAX;
165         kbuf.top_down = true;
166
167         ret = kexec_add_buffer(&kbuf);
168         if (ret)
169                 goto out_err;
170         image->arch.dtb = dtb;
171         image->arch.dtb_mem = kbuf.mem;
172
173         pr_debug("Loaded dtb at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
174                         kbuf.mem, kbuf.bufsz, kbuf.memsz);
175
176         return 0;
177
178 out_err:
179         image->nr_segments = orig_segments;
180         kvfree(dtb);
181         return ret;
182 }