xref: /openbsd/sys/arch/powerpc64/dev/kexec.c (revision 210f3d83)
1*210f3d83Skettenis /*	$OpenBSD: kexec.c,v 1.1 2020/07/16 19:37:58 kettenis Exp $	*/
2*210f3d83Skettenis 
3*210f3d83Skettenis /*
4*210f3d83Skettenis  * Copyright (c) 2019-2020 Visa Hankala
5*210f3d83Skettenis  * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org>
6*210f3d83Skettenis  *
7*210f3d83Skettenis  * Permission to use, copy, modify, and/or distribute this software for any
8*210f3d83Skettenis  * purpose with or without fee is hereby granted, provided that the above
9*210f3d83Skettenis  * copyright notice and this permission notice appear in all copies.
10*210f3d83Skettenis  *
11*210f3d83Skettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12*210f3d83Skettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13*210f3d83Skettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14*210f3d83Skettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15*210f3d83Skettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16*210f3d83Skettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17*210f3d83Skettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18*210f3d83Skettenis  */
19*210f3d83Skettenis 
20*210f3d83Skettenis #include <sys/param.h>
21*210f3d83Skettenis #include <sys/systm.h>
22*210f3d83Skettenis #include <sys/exec_elf.h>
23*210f3d83Skettenis #include <sys/malloc.h>
24*210f3d83Skettenis #include <sys/proc.h>
25*210f3d83Skettenis 
26*210f3d83Skettenis #include <uvm/uvm_extern.h>
27*210f3d83Skettenis 
28*210f3d83Skettenis #include <machine/kexec.h>
29*210f3d83Skettenis #include <machine/opal.h>
30*210f3d83Skettenis 
31*210f3d83Skettenis int	kexec_kexec(struct kexec_args *, struct proc *);
32*210f3d83Skettenis int	kexec_read(struct kexec_args *, void *, size_t, off_t);
33*210f3d83Skettenis void	kexec(paddr_t, paddr_t);
34*210f3d83Skettenis 
35*210f3d83Skettenis void
36*210f3d83Skettenis kexecattach(int num)
37*210f3d83Skettenis {
38*210f3d83Skettenis }
39*210f3d83Skettenis 
40*210f3d83Skettenis int
41*210f3d83Skettenis kexecopen(dev_t dev, int flags, int mode, struct proc *p)
42*210f3d83Skettenis {
43*210f3d83Skettenis 	return (0);
44*210f3d83Skettenis }
45*210f3d83Skettenis 
46*210f3d83Skettenis int
47*210f3d83Skettenis kexecclose(dev_t dev, int flags, int mode, struct proc *p)
48*210f3d83Skettenis {
49*210f3d83Skettenis 	return (0);
50*210f3d83Skettenis }
51*210f3d83Skettenis 
52*210f3d83Skettenis int
53*210f3d83Skettenis kexecioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
54*210f3d83Skettenis {
55*210f3d83Skettenis 	int error = 0;
56*210f3d83Skettenis 
57*210f3d83Skettenis 	switch (cmd) {
58*210f3d83Skettenis 	case KIOC_KEXEC:
59*210f3d83Skettenis 		error = suser(p);
60*210f3d83Skettenis 		if (error != 0)
61*210f3d83Skettenis 			break;
62*210f3d83Skettenis 		error = kexec_kexec((struct kexec_args *)data, p);
63*210f3d83Skettenis 		break;
64*210f3d83Skettenis 
65*210f3d83Skettenis 	case KIOC_GETBOOTDUID:
66*210f3d83Skettenis 		memcpy(data, bootduid, sizeof(bootduid));
67*210f3d83Skettenis 		break;
68*210f3d83Skettenis 
69*210f3d83Skettenis 	default:
70*210f3d83Skettenis 		error = ENOTTY;
71*210f3d83Skettenis 		break;
72*210f3d83Skettenis 	}
73*210f3d83Skettenis 
74*210f3d83Skettenis 	return error;
75*210f3d83Skettenis }
76*210f3d83Skettenis 
77*210f3d83Skettenis int
78*210f3d83Skettenis kexec_kexec(struct kexec_args *kargs, struct proc *p)
79*210f3d83Skettenis {
80*210f3d83Skettenis 	extern paddr_t fdt_pa;
81*210f3d83Skettenis 	struct kmem_pa_mode kp_kexec = {
82*210f3d83Skettenis 		.kp_constraint = &no_constraint,
83*210f3d83Skettenis 		.kp_boundary = SEGMENT_SIZE,
84*210f3d83Skettenis 		.kp_maxseg = 1,
85*210f3d83Skettenis 		.kp_zero = 1
86*210f3d83Skettenis 	};
87*210f3d83Skettenis 	Elf_Ehdr eh;
88*210f3d83Skettenis 	Elf_Phdr *ph = NULL;
89*210f3d83Skettenis 	Elf_Shdr *sh = NULL;
90*210f3d83Skettenis 	vaddr_t start = VM_MAX_ADDRESS;
91*210f3d83Skettenis 	vaddr_t end = 0;
92*210f3d83Skettenis 	paddr_t start_pa;
93*210f3d83Skettenis 	vsize_t align = 0;;
94*210f3d83Skettenis 	caddr_t addr;
95*210f3d83Skettenis 	size_t phsize, shsize, size;
96*210f3d83Skettenis 	int error, i;
97*210f3d83Skettenis 
98*210f3d83Skettenis 	/*
99*210f3d83Skettenis 	 * Read the headers and validate them.
100*210f3d83Skettenis 	 */
101*210f3d83Skettenis 	error = kexec_read(kargs, &eh, sizeof(eh), 0);
102*210f3d83Skettenis 	if (error != 0)
103*210f3d83Skettenis 		goto fail;
104*210f3d83Skettenis 
105*210f3d83Skettenis 	/* Load program headers. */
106*210f3d83Skettenis 	ph = mallocarray(eh.e_phnum, sizeof(Elf_Phdr), M_TEMP, M_NOWAIT);
107*210f3d83Skettenis 	if (ph == NULL) {
108*210f3d83Skettenis 		error = ENOMEM;
109*210f3d83Skettenis 		goto fail;
110*210f3d83Skettenis 	}
111*210f3d83Skettenis 	phsize = eh.e_phnum * sizeof(Elf_Phdr);
112*210f3d83Skettenis 	error = kexec_read(kargs, ph, phsize, eh.e_phoff);
113*210f3d83Skettenis 	if (error != 0)
114*210f3d83Skettenis 		goto fail;
115*210f3d83Skettenis 
116*210f3d83Skettenis 	/* Load section headers. */
117*210f3d83Skettenis 	sh = mallocarray(eh.e_shnum, sizeof(Elf_Shdr), M_TEMP, M_NOWAIT);
118*210f3d83Skettenis 	if (sh == NULL) {
119*210f3d83Skettenis 		error = ENOMEM;
120*210f3d83Skettenis 		goto fail;
121*210f3d83Skettenis 	}
122*210f3d83Skettenis 	shsize = eh.e_shnum * sizeof(Elf_Shdr);
123*210f3d83Skettenis 	error = kexec_read(kargs, sh, shsize, eh.e_shoff);
124*210f3d83Skettenis 	if (error != 0)
125*210f3d83Skettenis 		goto fail;
126*210f3d83Skettenis 
127*210f3d83Skettenis 	/*
128*210f3d83Skettenis 	 * Allocate physical memory and load the segments.
129*210f3d83Skettenis 	 */
130*210f3d83Skettenis 
131*210f3d83Skettenis 	for (i = 0; i < eh.e_phnum; i++) {
132*210f3d83Skettenis 		if (ph[i].p_type != PT_LOAD)
133*210f3d83Skettenis 			continue;
134*210f3d83Skettenis 		start = MIN(start, ph[i].p_vaddr);
135*210f3d83Skettenis 		align = MAX(align, ph[i].p_align);
136*210f3d83Skettenis 		end = MAX(end, ph[i].p_vaddr + ph[i].p_memsz);
137*210f3d83Skettenis 	}
138*210f3d83Skettenis 	size = round_page(end) - start;
139*210f3d83Skettenis 
140*210f3d83Skettenis 	kp_kexec.kp_align = align;
141*210f3d83Skettenis 	addr = km_alloc(size, &kv_any, &kp_kexec, &kd_nowait);
142*210f3d83Skettenis 	if (addr == NULL) {
143*210f3d83Skettenis 		error = ENOMEM;
144*210f3d83Skettenis 		goto fail;
145*210f3d83Skettenis 	}
146*210f3d83Skettenis 
147*210f3d83Skettenis 	for (i = 0; i < eh.e_phnum; i++) {
148*210f3d83Skettenis 		if (ph[i].p_type != PT_LOAD)
149*210f3d83Skettenis 			continue;
150*210f3d83Skettenis 
151*210f3d83Skettenis 		error = kexec_read(kargs, addr + (ph[i].p_vaddr - start),
152*210f3d83Skettenis 		    ph[i].p_filesz, ph[i].p_offset);
153*210f3d83Skettenis 		if (error != 0)
154*210f3d83Skettenis 			goto fail;
155*210f3d83Skettenis 
156*210f3d83Skettenis 		/* Clear any BSS. */
157*210f3d83Skettenis 		if (ph[i].p_memsz > ph[i].p_filesz) {
158*210f3d83Skettenis 			memset(addr + (ph[i].p_vaddr + ph[i].p_filesz) - start,
159*210f3d83Skettenis 			    0, ph[i].p_memsz - ph[i].p_filesz);
160*210f3d83Skettenis 		}
161*210f3d83Skettenis 	}
162*210f3d83Skettenis 
163*210f3d83Skettenis 	for (i = 0; i < eh.e_phnum; i++) {
164*210f3d83Skettenis 		if (ph[i].p_type != PT_OPENBSD_RANDOMIZE)
165*210f3d83Skettenis 			continue;
166*210f3d83Skettenis 
167*210f3d83Skettenis 		/* Assume that the segment is inside a LOAD segment. */
168*210f3d83Skettenis 		arc4random_buf(addr + ph[i].p_vaddr - start, ph[i].p_filesz);
169*210f3d83Skettenis 	}
170*210f3d83Skettenis 
171*210f3d83Skettenis 	printf("launching kernel\n");
172*210f3d83Skettenis 
173*210f3d83Skettenis 	config_suspend_all(DVACT_POWERDOWN);
174*210f3d83Skettenis 
175*210f3d83Skettenis 	intr_disable();
176*210f3d83Skettenis 
177*210f3d83Skettenis 	pmap_extract(pmap_kernel(), (vaddr_t)addr, &start_pa);
178*210f3d83Skettenis 	kexec(start_pa + (eh.e_entry - start), fdt_pa);
179*210f3d83Skettenis 
180*210f3d83Skettenis 	for (;;)
181*210f3d83Skettenis 		continue;
182*210f3d83Skettenis 
183*210f3d83Skettenis fail:
184*210f3d83Skettenis 	free(sh, M_TEMP, shsize);
185*210f3d83Skettenis 	free(ph, M_TEMP, phsize);
186*210f3d83Skettenis 	return error;
187*210f3d83Skettenis }
188*210f3d83Skettenis 
189*210f3d83Skettenis int
190*210f3d83Skettenis kexec_read(struct kexec_args *kargs, void *buf, size_t size, off_t off)
191*210f3d83Skettenis {
192*210f3d83Skettenis 	if (off + size < off || off + size > kargs->klen)
193*210f3d83Skettenis 		return ENOEXEC;
194*210f3d83Skettenis 	return copyin(kargs->kimg + off, buf, size);
195*210f3d83Skettenis }
196