xref: /openbsd/sys/arch/powerpc64/dev/kexec.c (revision d415bd75)
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
40 kexecattach(int num)
41 {
42 }
43 
44 int
45 kexecopen(dev_t dev, int flags, int mode, struct proc *p)
46 {
47 	return (0);
48 }
49 
50 int
51 kexecclose(dev_t dev, int flags, int mode, struct proc *p)
52 {
53 	return (0);
54 }
55 
56 int
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
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
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