Merge tag 'sound-5.0-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai...
[sfrench/cifs-2.6.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_fdt.h>
19 #include <linux/random.h>
20 #include <linux/string.h>
21 #include <linux/types.h>
22 #include <linux/vmalloc.h>
23 #include <asm/byteorder.h>
24
25 /* relevant device tree properties */
26 #define FDT_PROP_INITRD_START   "linux,initrd-start"
27 #define FDT_PROP_INITRD_END     "linux,initrd-end"
28 #define FDT_PROP_BOOTARGS       "bootargs"
29 #define FDT_PROP_KASLR_SEED     "kaslr-seed"
30
31 const struct kexec_file_ops * const kexec_file_loaders[] = {
32         &kexec_image_ops,
33         NULL
34 };
35
36 int arch_kimage_file_post_load_cleanup(struct kimage *image)
37 {
38         vfree(image->arch.dtb);
39         image->arch.dtb = NULL;
40
41         return kexec_image_post_load_cleanup_default(image);
42 }
43
44 static int setup_dtb(struct kimage *image,
45                      unsigned long initrd_load_addr, unsigned long initrd_len,
46                      char *cmdline, void *dtb)
47 {
48         int off, ret;
49
50         ret = fdt_path_offset(dtb, "/chosen");
51         if (ret < 0)
52                 goto out;
53
54         off = ret;
55
56         /* add bootargs */
57         if (cmdline) {
58                 ret = fdt_setprop_string(dtb, off, FDT_PROP_BOOTARGS, cmdline);
59                 if (ret)
60                         goto out;
61         } else {
62                 ret = fdt_delprop(dtb, off, FDT_PROP_BOOTARGS);
63                 if (ret && (ret != -FDT_ERR_NOTFOUND))
64                         goto out;
65         }
66
67         /* add initrd-* */
68         if (initrd_load_addr) {
69                 ret = fdt_setprop_u64(dtb, off, FDT_PROP_INITRD_START,
70                                       initrd_load_addr);
71                 if (ret)
72                         goto out;
73
74                 ret = fdt_setprop_u64(dtb, off, FDT_PROP_INITRD_END,
75                                       initrd_load_addr + initrd_len);
76                 if (ret)
77                         goto out;
78         } else {
79                 ret = fdt_delprop(dtb, off, FDT_PROP_INITRD_START);
80                 if (ret && (ret != -FDT_ERR_NOTFOUND))
81                         goto out;
82
83                 ret = fdt_delprop(dtb, off, FDT_PROP_INITRD_END);
84                 if (ret && (ret != -FDT_ERR_NOTFOUND))
85                         goto out;
86         }
87
88         /* add kaslr-seed */
89         ret = fdt_delprop(dtb, off, FDT_PROP_KASLR_SEED);
90         if  (ret == -FDT_ERR_NOTFOUND)
91                 ret = 0;
92         else if (ret)
93                 goto out;
94
95         if (rng_is_initialized()) {
96                 u64 seed = get_random_u64();
97                 ret = fdt_setprop_u64(dtb, off, FDT_PROP_KASLR_SEED, seed);
98                 if (ret)
99                         goto out;
100         } else {
101                 pr_notice("RNG is not initialised: omitting \"%s\" property\n",
102                                 FDT_PROP_KASLR_SEED);
103         }
104
105 out:
106         if (ret)
107                 return (ret == -FDT_ERR_NOSPACE) ? -ENOMEM : -EINVAL;
108
109         return 0;
110 }
111
112 /*
113  * More space needed so that we can add initrd, bootargs and kaslr-seed.
114  */
115 #define DTB_EXTRA_SPACE 0x1000
116
117 static int create_dtb(struct kimage *image,
118                       unsigned long initrd_load_addr, unsigned long initrd_len,
119                       char *cmdline, void **dtb)
120 {
121         void *buf;
122         size_t buf_size;
123         int ret;
124
125         buf_size = fdt_totalsize(initial_boot_params)
126                         + strlen(cmdline) + DTB_EXTRA_SPACE;
127
128         for (;;) {
129                 buf = vmalloc(buf_size);
130                 if (!buf)
131                         return -ENOMEM;
132
133                 /* duplicate a device tree blob */
134                 ret = fdt_open_into(initial_boot_params, buf, buf_size);
135                 if (ret)
136                         return -EINVAL;
137
138                 ret = setup_dtb(image, initrd_load_addr, initrd_len,
139                                 cmdline, buf);
140                 if (ret) {
141                         vfree(buf);
142                         if (ret == -ENOMEM) {
143                                 /* unlikely, but just in case */
144                                 buf_size += DTB_EXTRA_SPACE;
145                                 continue;
146                         } else {
147                                 return ret;
148                         }
149                 }
150
151                 /* trim it */
152                 fdt_pack(buf);
153                 *dtb = buf;
154
155                 return 0;
156         }
157 }
158
159 int load_other_segments(struct kimage *image,
160                         unsigned long kernel_load_addr,
161                         unsigned long kernel_size,
162                         char *initrd, unsigned long initrd_len,
163                         char *cmdline)
164 {
165         struct kexec_buf kbuf;
166         void *dtb = NULL;
167         unsigned long initrd_load_addr = 0, dtb_len;
168         int ret = 0;
169
170         kbuf.image = image;
171         /* not allocate anything below the kernel */
172         kbuf.buf_min = kernel_load_addr + kernel_size;
173
174         /* load initrd */
175         if (initrd) {
176                 kbuf.buffer = initrd;
177                 kbuf.bufsz = initrd_len;
178                 kbuf.mem = 0;
179                 kbuf.memsz = initrd_len;
180                 kbuf.buf_align = 0;
181                 /* within 1GB-aligned window of up to 32GB in size */
182                 kbuf.buf_max = round_down(kernel_load_addr, SZ_1G)
183                                                 + (unsigned long)SZ_1G * 32;
184                 kbuf.top_down = false;
185
186                 ret = kexec_add_buffer(&kbuf);
187                 if (ret)
188                         goto out_err;
189                 initrd_load_addr = kbuf.mem;
190
191                 pr_debug("Loaded initrd at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
192                                 initrd_load_addr, initrd_len, initrd_len);
193         }
194
195         /* load dtb */
196         ret = create_dtb(image, initrd_load_addr, initrd_len, cmdline, &dtb);
197         if (ret) {
198                 pr_err("Preparing for new dtb failed\n");
199                 goto out_err;
200         }
201
202         dtb_len = fdt_totalsize(dtb);
203         kbuf.buffer = dtb;
204         kbuf.bufsz = dtb_len;
205         kbuf.mem = 0;
206         kbuf.memsz = dtb_len;
207         /* not across 2MB boundary */
208         kbuf.buf_align = SZ_2M;
209         kbuf.buf_max = ULONG_MAX;
210         kbuf.top_down = true;
211
212         ret = kexec_add_buffer(&kbuf);
213         if (ret)
214                 goto out_err;
215         image->arch.dtb = dtb;
216         image->arch.dtb_mem = kbuf.mem;
217
218         pr_debug("Loaded dtb at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
219                         kbuf.mem, dtb_len, dtb_len);
220
221         return 0;
222
223 out_err:
224         vfree(dtb);
225         return ret;
226 }