1 /* $OpenBSD: kexec.c,v 1.7 2022/02/22 13:34:23 visa Exp $ */
2
3 /*
4 * Copyright (c) 2019-2020 Visa Hankala
5 * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org>
6 *
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include <sys/param.h>
21 #include <sys/systm.h>
22 #include <sys/exec_elf.h>
23 #include <sys/malloc.h>
24 #include <sys/proc.h>
25 #include <sys/mount.h>
26 #include <sys/reboot.h>
27
28 #include <uvm/uvm_extern.h>
29
30 #include <machine/kexec.h>
31 #include <machine/opal.h>
32
33 #include <dev/ofw/fdt.h>
34
35 int kexec_kexec(struct kexec_args *, struct proc *);
36 int kexec_read(struct kexec_args *, void *, size_t, off_t);
37 void kexec(paddr_t, paddr_t);
38
39 void
kexecattach(int num)40 kexecattach(int num)
41 {
42 }
43
44 int
kexecopen(dev_t dev,int flags,int mode,struct proc * p)45 kexecopen(dev_t dev, int flags, int mode, struct proc *p)
46 {
47 return (0);
48 }
49
50 int
kexecclose(dev_t dev,int flags,int mode,struct proc * p)51 kexecclose(dev_t dev, int flags, int mode, struct proc *p)
52 {
53 return (0);
54 }
55
56 int
kexecioctl(dev_t dev,u_long cmd,caddr_t data,int flags,struct proc * p)57 kexecioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
58 {
59 int error = 0;
60
61 switch (cmd) {
62 case KIOC_KEXEC:
63 error = suser(p);
64 if (error != 0)
65 break;
66 error = kexec_kexec((struct kexec_args *)data, p);
67 break;
68
69 case KIOC_GETBOOTDUID:
70 memcpy(data, bootduid, sizeof(bootduid));
71 break;
72
73 default:
74 error = ENOTTY;
75 break;
76 }
77
78 return error;
79 }
80
81 int
kexec_kexec(struct kexec_args * kargs,struct proc * p)82 kexec_kexec(struct kexec_args *kargs, struct proc *p)
83 {
84 extern paddr_t fdt_pa;
85 struct kmem_pa_mode kp_kexec = {
86 .kp_constraint = &no_constraint,
87 .kp_boundary = SEGMENT_SIZE,
88 .kp_maxseg = 1,
89 .kp_zero = 1
90 };
91 Elf_Ehdr eh;
92 Elf_Phdr *ph = NULL;
93 Elf_Shdr *sh = NULL, *shp;
94 vaddr_t start = VM_MAX_ADDRESS;
95 vaddr_t end = 0;
96 paddr_t start_pa, initrd_pa;
97 vsize_t align = 0;
98 caddr_t addr = NULL;
99 caddr_t symaddr = NULL;
100 size_t phsize, shsize, size, symsize;
101 char *shstr;
102 void *node;
103 int error, random, i;
104
105 /*
106 * Read the headers and validate them.
107 */
108 error = kexec_read(kargs, &eh, sizeof(eh), 0);
109 if (error != 0)
110 goto fail;
111
112 /* Load program headers. */
113 ph = mallocarray(eh.e_phnum, sizeof(Elf_Phdr), M_TEMP, M_NOWAIT);
114 if (ph == NULL) {
115 error = ENOMEM;
116 goto fail;
117 }
118 phsize = eh.e_phnum * sizeof(Elf_Phdr);
119 error = kexec_read(kargs, ph, phsize, eh.e_phoff);
120 if (error != 0)
121 goto fail;
122
123 /* Load section headers. */
124 sh = mallocarray(eh.e_shnum, sizeof(Elf_Shdr), M_TEMP, M_NOWAIT);
125 if (sh == NULL) {
126 error = ENOMEM;
127 goto fail;
128 }
129 shsize = eh.e_shnum * sizeof(Elf_Shdr);
130 error = kexec_read(kargs, sh, shsize, eh.e_shoff);
131 if (error != 0)
132 goto fail;
133
134 /*
135 * Allocate physical memory and load the segments.
136 */
137
138 for (i = 0; i < eh.e_phnum; i++) {
139 if (ph[i].p_type != PT_LOAD)
140 continue;
141 start = MIN(start, ph[i].p_vaddr);
142 align = MAX(align, ph[i].p_align);
143 end = MAX(end, ph[i].p_vaddr + ph[i].p_memsz);
144 }
145 size = round_page(end) - start;
146
147 kp_kexec.kp_align = align;
148 addr = km_alloc(size, &kv_any, &kp_kexec, &kd_nowait);
149 if (addr == NULL) {
150 error = ENOMEM;
151 goto fail;
152 }
153
154 for (i = 0; i < eh.e_phnum; i++) {
155 if (ph[i].p_type != PT_LOAD)
156 continue;
157
158 error = kexec_read(kargs, addr + (ph[i].p_vaddr - start),
159 ph[i].p_filesz, ph[i].p_offset);
160 if (error != 0)
161 goto fail;
162
163 /* Clear any BSS. */
164 if (ph[i].p_memsz > ph[i].p_filesz) {
165 memset(addr + (ph[i].p_vaddr + ph[i].p_filesz) - start,
166 0, ph[i].p_memsz - ph[i].p_filesz);
167 }
168 }
169
170 random = 0;
171 for (i = 0; i < eh.e_phnum; i++) {
172 if (ph[i].p_type != PT_OPENBSD_RANDOMIZE)
173 continue;
174
175 /* Assume that the segment is inside a LOAD segment. */
176 arc4random_buf(addr + ph[i].p_vaddr - start, ph[i].p_filesz);
177 random = 1;
178 }
179
180 if (random == 0)
181 kargs->boothowto &= ~RB_GOODRANDOM;
182
183 symsize = round_page(kargs->klen);
184 symaddr = km_alloc(symsize, &kv_any, &kp_kexec, &kd_nowait);
185 if (symaddr == NULL) {
186 error = ENOMEM;
187 goto fail;
188 }
189
190 error = kexec_read(kargs, symaddr, kargs->klen, 0);
191 if (error != 0)
192 goto fail;
193
194 vfs_shutdown(p);
195
196 shp = (Elf64_Shdr *)(symaddr + eh.e_shoff);
197 shstr = symaddr + shp[eh.e_shstrndx].sh_offset;
198 for (i = 0; i < eh.e_shnum; i++) {
199 if (shp[i].sh_type == SHT_SYMTAB ||
200 shp[i].sh_type == SHT_STRTAB ||
201 strcmp(shstr + shp[i].sh_name, ".debug_line") == 0 ||
202 strcmp(shstr + shp[i].sh_name, ELF_CTF) == 0)
203 if (shp[i].sh_offset + shp[i].sh_size <= symsize)
204 shp[i].sh_flags |= SHF_ALLOC;
205 }
206
207 pmap_extract(pmap_kernel(), (vaddr_t)symaddr, &initrd_pa);
208
209 node = fdt_find_node("/chosen");
210 if (node) {
211 uint32_t boothowto = htobe32(kargs->boothowto);
212 uint64_t initrd_start = htobe64(initrd_pa);
213 uint64_t initrd_end = htobe64(initrd_pa + kargs->klen);
214
215 fdt_node_add_property(node, "openbsd,boothowto",
216 &boothowto, sizeof(boothowto));
217 fdt_node_add_property(node, "openbsd,bootduid",
218 kargs->bootduid, sizeof(kargs->bootduid));
219
220 fdt_node_set_property(node, "linux,initrd-start",
221 &initrd_start, sizeof(initrd_start));
222 fdt_node_set_property(node, "linux,initrd-end",
223 &initrd_end, sizeof(initrd_end));
224
225 fdt_finalize();
226 }
227
228 printf("launching kernel\n");
229
230 config_suspend_all(DVACT_POWERDOWN);
231
232 intr_disable();
233
234 pmap_extract(pmap_kernel(), (vaddr_t)addr, &start_pa);
235 kexec(start_pa + (eh.e_entry - start), fdt_pa);
236
237 for (;;)
238 continue;
239
240 fail:
241 if (symaddr)
242 km_free(symaddr, symsize, &kv_any, &kp_kexec);
243 if (addr)
244 km_free(addr, size, &kv_any, &kp_kexec);
245 if (sh)
246 free(sh, M_TEMP, shsize);
247 if (ph)
248 free(ph, M_TEMP, phsize);
249 return error;
250 }
251
252 int
kexec_read(struct kexec_args * kargs,void * buf,size_t size,off_t off)253 kexec_read(struct kexec_args *kargs, void *buf, size_t size, off_t off)
254 {
255 if (off + size < off || off + size > kargs->klen)
256 return ENOEXEC;
257 return copyin(kargs->kimg + off, buf, size);
258 }
259