xref: /openbsd/sys/arch/powerpc64/dev/kexec.c (revision 09467b48)
1 /*	$OpenBSD: kexec.c,v 1.3 2020/07/18 20:02:34 kettenis 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/reboot.h>
26 
27 #include <uvm/uvm_extern.h>
28 
29 #include <machine/kexec.h>
30 #include <machine/opal.h>
31 
32 #include <dev/ofw/fdt.h>
33 
34 int	kexec_kexec(struct kexec_args *, struct proc *);
35 int	kexec_read(struct kexec_args *, void *, size_t, off_t);
36 void	kexec(paddr_t, paddr_t);
37 
38 void
39 kexecattach(int num)
40 {
41 }
42 
43 int
44 kexecopen(dev_t dev, int flags, int mode, struct proc *p)
45 {
46 	return (0);
47 }
48 
49 int
50 kexecclose(dev_t dev, int flags, int mode, struct proc *p)
51 {
52 	return (0);
53 }
54 
55 int
56 kexecioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
57 {
58 	int error = 0;
59 
60 	switch (cmd) {
61 	case KIOC_KEXEC:
62 		error = suser(p);
63 		if (error != 0)
64 			break;
65 		error = kexec_kexec((struct kexec_args *)data, p);
66 		break;
67 
68 	case KIOC_GETBOOTDUID:
69 		memcpy(data, bootduid, sizeof(bootduid));
70 		break;
71 
72 	default:
73 		error = ENOTTY;
74 		break;
75 	}
76 
77 	return error;
78 }
79 
80 int
81 kexec_kexec(struct kexec_args *kargs, struct proc *p)
82 {
83 	extern paddr_t fdt_pa;
84 	struct kmem_pa_mode kp_kexec = {
85 		.kp_constraint = &no_constraint,
86 		.kp_boundary = SEGMENT_SIZE,
87 		.kp_maxseg = 1,
88 		.kp_zero = 1
89 	};
90 	Elf_Ehdr eh;
91 	Elf_Phdr *ph = NULL;
92 	Elf_Shdr *sh = NULL;
93 	vaddr_t start = VM_MAX_ADDRESS;
94 	vaddr_t end = 0;
95 	paddr_t start_pa, initrd_pa;
96 	vsize_t align = 0;;
97 	caddr_t addr = NULL;
98 	caddr_t symaddr = NULL;
99 	size_t phsize, shsize, size, symsize;
100 	void *node;
101 	int error, random, i;
102 
103 	/*
104 	 * Read the headers and validate them.
105 	 */
106 	error = kexec_read(kargs, &eh, sizeof(eh), 0);
107 	if (error != 0)
108 		goto fail;
109 
110 	/* Load program headers. */
111 	ph = mallocarray(eh.e_phnum, sizeof(Elf_Phdr), M_TEMP, M_NOWAIT);
112 	if (ph == NULL) {
113 		error = ENOMEM;
114 		goto fail;
115 	}
116 	phsize = eh.e_phnum * sizeof(Elf_Phdr);
117 	error = kexec_read(kargs, ph, phsize, eh.e_phoff);
118 	if (error != 0)
119 		goto fail;
120 
121 	/* Load section headers. */
122 	sh = mallocarray(eh.e_shnum, sizeof(Elf_Shdr), M_TEMP, M_NOWAIT);
123 	if (sh == NULL) {
124 		error = ENOMEM;
125 		goto fail;
126 	}
127 	shsize = eh.e_shnum * sizeof(Elf_Shdr);
128 	error = kexec_read(kargs, sh, shsize, eh.e_shoff);
129 	if (error != 0)
130 		goto fail;
131 
132 	/*
133 	 * Allocate physical memory and load the segments.
134 	 */
135 
136 	for (i = 0; i < eh.e_phnum; i++) {
137 		if (ph[i].p_type != PT_LOAD)
138 			continue;
139 		start = MIN(start, ph[i].p_vaddr);
140 		align = MAX(align, ph[i].p_align);
141 		end = MAX(end, ph[i].p_vaddr + ph[i].p_memsz);
142 	}
143 	size = round_page(end) - start;
144 
145 	kp_kexec.kp_align = align;
146 	addr = km_alloc(size, &kv_any, &kp_kexec, &kd_nowait);
147 	if (addr == NULL) {
148 		error = ENOMEM;
149 		goto fail;
150 	}
151 
152 	for (i = 0; i < eh.e_phnum; i++) {
153 		if (ph[i].p_type != PT_LOAD)
154 			continue;
155 
156 		error = kexec_read(kargs, addr + (ph[i].p_vaddr - start),
157 		    ph[i].p_filesz, ph[i].p_offset);
158 		if (error != 0)
159 			goto fail;
160 
161 		/* Clear any BSS. */
162 		if (ph[i].p_memsz > ph[i].p_filesz) {
163 			memset(addr + (ph[i].p_vaddr + ph[i].p_filesz) - start,
164 			    0, ph[i].p_memsz - ph[i].p_filesz);
165 		}
166 	}
167 
168 	random = 0;
169 	for (i = 0; i < eh.e_phnum; i++) {
170 		if (ph[i].p_type != PT_OPENBSD_RANDOMIZE)
171 			continue;
172 
173 		/* Assume that the segment is inside a LOAD segment. */
174 		arc4random_buf(addr + ph[i].p_vaddr - start, ph[i].p_filesz);
175 		random = 1;
176 	}
177 
178 	if (random == 0)
179 		kargs->boothowto &= ~RB_GOODRANDOM;
180 
181 	symsize = round_page(kargs->klen);
182 	symaddr = km_alloc(symsize, &kv_any, &kp_kexec, &kd_nowait);
183 	if (symaddr == NULL) {
184 		error = ENOMEM;
185 		goto fail;
186 	}
187 
188 	error = kexec_read(kargs, symaddr, kargs->klen, 0);
189 	if (error != 0)
190 		goto fail;
191 
192 	pmap_extract(pmap_kernel(), (vaddr_t)symaddr, &initrd_pa);
193 
194 	node = fdt_find_node("/chosen");
195 	if (node) {
196 		uint32_t boothowto = htobe32(kargs->boothowto);
197 		uint64_t initrd_start = htobe64(initrd_pa);
198 		uint64_t initrd_end = htobe64(initrd_pa + kargs->klen);
199 
200 		fdt_node_add_property(node, "openbsd,boothowto",
201 		    &boothowto, sizeof(boothowto));
202 		fdt_node_add_property(node, "openbsd,bootduid",
203 		    kargs->bootduid, sizeof(kargs->bootduid));
204 
205 		fdt_node_set_property(node, "linux,initrd-start",
206 		    &initrd_start, sizeof(initrd_start));
207 		fdt_node_set_property(node, "linux,initrd-end",
208 		    &initrd_end, sizeof(initrd_end));
209 
210 		fdt_finalize();
211 	}
212 
213 	printf("launching kernel\n");
214 
215 	config_suspend_all(DVACT_POWERDOWN);
216 
217 	intr_disable();
218 
219 	pmap_extract(pmap_kernel(), (vaddr_t)addr, &start_pa);
220 	kexec(start_pa + (eh.e_entry - start), fdt_pa);
221 
222 	for (;;)
223 		continue;
224 
225 fail:
226 	if (symaddr)
227 		km_free(symaddr, symsize, &kv_any, &kp_kexec);
228 	if (addr)
229 		km_free(addr, size, &kv_any, &kp_kexec);
230 	free(sh, M_TEMP, shsize);
231 	free(ph, M_TEMP, phsize);
232 	return error;
233 }
234 
235 int
236 kexec_read(struct kexec_args *kargs, void *buf, size_t size, off_t off)
237 {
238 	if (off + size < off || off + size > kargs->klen)
239 		return ENOEXEC;
240 	return copyin(kargs->kimg + off, buf, size);
241 }
242