xref: /openbsd/sys/arch/powerpc64/dev/kexec.c (revision 49047c64)
1*49047c64Svisa /*	$OpenBSD: kexec.c,v 1.7 2022/02/22 13:34:23 visa Exp $	*/
2210f3d83Skettenis 
3210f3d83Skettenis /*
4210f3d83Skettenis  * Copyright (c) 2019-2020 Visa Hankala
5210f3d83Skettenis  * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org>
6210f3d83Skettenis  *
7210f3d83Skettenis  * Permission to use, copy, modify, and/or distribute this software for any
8210f3d83Skettenis  * purpose with or without fee is hereby granted, provided that the above
9210f3d83Skettenis  * copyright notice and this permission notice appear in all copies.
10210f3d83Skettenis  *
11210f3d83Skettenis  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12210f3d83Skettenis  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13210f3d83Skettenis  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14210f3d83Skettenis  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15210f3d83Skettenis  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16210f3d83Skettenis  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17210f3d83Skettenis  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18210f3d83Skettenis  */
19210f3d83Skettenis 
20210f3d83Skettenis #include <sys/param.h>
21210f3d83Skettenis #include <sys/systm.h>
22210f3d83Skettenis #include <sys/exec_elf.h>
23210f3d83Skettenis #include <sys/malloc.h>
24210f3d83Skettenis #include <sys/proc.h>
25bc7ce91cSderaadt #include <sys/mount.h>
2624add1c0Skettenis #include <sys/reboot.h>
27210f3d83Skettenis 
28210f3d83Skettenis #include <uvm/uvm_extern.h>
29210f3d83Skettenis 
30210f3d83Skettenis #include <machine/kexec.h>
31210f3d83Skettenis #include <machine/opal.h>
32210f3d83Skettenis 
3324add1c0Skettenis #include <dev/ofw/fdt.h>
3424add1c0Skettenis 
35210f3d83Skettenis int	kexec_kexec(struct kexec_args *, struct proc *);
36210f3d83Skettenis int	kexec_read(struct kexec_args *, void *, size_t, off_t);
37210f3d83Skettenis void	kexec(paddr_t, paddr_t);
38210f3d83Skettenis 
39210f3d83Skettenis void
kexecattach(int num)40210f3d83Skettenis kexecattach(int num)
41210f3d83Skettenis {
42210f3d83Skettenis }
43210f3d83Skettenis 
44210f3d83Skettenis int
kexecopen(dev_t dev,int flags,int mode,struct proc * p)45210f3d83Skettenis kexecopen(dev_t dev, int flags, int mode, struct proc *p)
46210f3d83Skettenis {
47210f3d83Skettenis 	return (0);
48210f3d83Skettenis }
49210f3d83Skettenis 
50210f3d83Skettenis int
kexecclose(dev_t dev,int flags,int mode,struct proc * p)51210f3d83Skettenis kexecclose(dev_t dev, int flags, int mode, struct proc *p)
52210f3d83Skettenis {
53210f3d83Skettenis 	return (0);
54210f3d83Skettenis }
55210f3d83Skettenis 
56210f3d83Skettenis int
kexecioctl(dev_t dev,u_long cmd,caddr_t data,int flags,struct proc * p)57210f3d83Skettenis kexecioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
58210f3d83Skettenis {
59210f3d83Skettenis 	int error = 0;
60210f3d83Skettenis 
61210f3d83Skettenis 	switch (cmd) {
62210f3d83Skettenis 	case KIOC_KEXEC:
63210f3d83Skettenis 		error = suser(p);
64210f3d83Skettenis 		if (error != 0)
65210f3d83Skettenis 			break;
66210f3d83Skettenis 		error = kexec_kexec((struct kexec_args *)data, p);
67210f3d83Skettenis 		break;
68210f3d83Skettenis 
69210f3d83Skettenis 	case KIOC_GETBOOTDUID:
70210f3d83Skettenis 		memcpy(data, bootduid, sizeof(bootduid));
71210f3d83Skettenis 		break;
72210f3d83Skettenis 
73210f3d83Skettenis 	default:
74210f3d83Skettenis 		error = ENOTTY;
75210f3d83Skettenis 		break;
76210f3d83Skettenis 	}
77210f3d83Skettenis 
78210f3d83Skettenis 	return error;
79210f3d83Skettenis }
80210f3d83Skettenis 
81210f3d83Skettenis int
kexec_kexec(struct kexec_args * kargs,struct proc * p)82210f3d83Skettenis kexec_kexec(struct kexec_args *kargs, struct proc *p)
83210f3d83Skettenis {
84210f3d83Skettenis 	extern paddr_t fdt_pa;
85210f3d83Skettenis 	struct kmem_pa_mode kp_kexec = {
86210f3d83Skettenis 		.kp_constraint = &no_constraint,
87210f3d83Skettenis 		.kp_boundary = SEGMENT_SIZE,
88210f3d83Skettenis 		.kp_maxseg = 1,
89210f3d83Skettenis 		.kp_zero = 1
90210f3d83Skettenis 	};
91210f3d83Skettenis 	Elf_Ehdr eh;
92210f3d83Skettenis 	Elf_Phdr *ph = NULL;
930e5ba2a7Skettenis 	Elf_Shdr *sh = NULL, *shp;
94210f3d83Skettenis 	vaddr_t start = VM_MAX_ADDRESS;
95210f3d83Skettenis 	vaddr_t end = 0;
9669126009Skettenis 	paddr_t start_pa, initrd_pa;
97*49047c64Svisa 	vsize_t align = 0;
9869126009Skettenis 	caddr_t addr = NULL;
9969126009Skettenis 	caddr_t symaddr = NULL;
10069126009Skettenis 	size_t phsize, shsize, size, symsize;
1010e5ba2a7Skettenis 	char *shstr;
10224add1c0Skettenis 	void *node;
10324add1c0Skettenis 	int error, random, i;
104210f3d83Skettenis 
105210f3d83Skettenis 	/*
106210f3d83Skettenis 	 * Read the headers and validate them.
107210f3d83Skettenis 	 */
108210f3d83Skettenis 	error = kexec_read(kargs, &eh, sizeof(eh), 0);
109210f3d83Skettenis 	if (error != 0)
110210f3d83Skettenis 		goto fail;
111210f3d83Skettenis 
112210f3d83Skettenis 	/* Load program headers. */
113210f3d83Skettenis 	ph = mallocarray(eh.e_phnum, sizeof(Elf_Phdr), M_TEMP, M_NOWAIT);
114210f3d83Skettenis 	if (ph == NULL) {
115210f3d83Skettenis 		error = ENOMEM;
116210f3d83Skettenis 		goto fail;
117210f3d83Skettenis 	}
118210f3d83Skettenis 	phsize = eh.e_phnum * sizeof(Elf_Phdr);
119210f3d83Skettenis 	error = kexec_read(kargs, ph, phsize, eh.e_phoff);
120210f3d83Skettenis 	if (error != 0)
121210f3d83Skettenis 		goto fail;
122210f3d83Skettenis 
123210f3d83Skettenis 	/* Load section headers. */
124210f3d83Skettenis 	sh = mallocarray(eh.e_shnum, sizeof(Elf_Shdr), M_TEMP, M_NOWAIT);
125210f3d83Skettenis 	if (sh == NULL) {
126210f3d83Skettenis 		error = ENOMEM;
127210f3d83Skettenis 		goto fail;
128210f3d83Skettenis 	}
129210f3d83Skettenis 	shsize = eh.e_shnum * sizeof(Elf_Shdr);
130210f3d83Skettenis 	error = kexec_read(kargs, sh, shsize, eh.e_shoff);
131210f3d83Skettenis 	if (error != 0)
132210f3d83Skettenis 		goto fail;
133210f3d83Skettenis 
134210f3d83Skettenis 	/*
135210f3d83Skettenis 	 * Allocate physical memory and load the segments.
136210f3d83Skettenis 	 */
137210f3d83Skettenis 
138210f3d83Skettenis 	for (i = 0; i < eh.e_phnum; i++) {
139210f3d83Skettenis 		if (ph[i].p_type != PT_LOAD)
140210f3d83Skettenis 			continue;
141210f3d83Skettenis 		start = MIN(start, ph[i].p_vaddr);
142210f3d83Skettenis 		align = MAX(align, ph[i].p_align);
143210f3d83Skettenis 		end = MAX(end, ph[i].p_vaddr + ph[i].p_memsz);
144210f3d83Skettenis 	}
145210f3d83Skettenis 	size = round_page(end) - start;
146210f3d83Skettenis 
147210f3d83Skettenis 	kp_kexec.kp_align = align;
148210f3d83Skettenis 	addr = km_alloc(size, &kv_any, &kp_kexec, &kd_nowait);
149210f3d83Skettenis 	if (addr == NULL) {
150210f3d83Skettenis 		error = ENOMEM;
151210f3d83Skettenis 		goto fail;
152210f3d83Skettenis 	}
153210f3d83Skettenis 
154210f3d83Skettenis 	for (i = 0; i < eh.e_phnum; i++) {
155210f3d83Skettenis 		if (ph[i].p_type != PT_LOAD)
156210f3d83Skettenis 			continue;
157210f3d83Skettenis 
158210f3d83Skettenis 		error = kexec_read(kargs, addr + (ph[i].p_vaddr - start),
159210f3d83Skettenis 		    ph[i].p_filesz, ph[i].p_offset);
160210f3d83Skettenis 		if (error != 0)
161210f3d83Skettenis 			goto fail;
162210f3d83Skettenis 
163210f3d83Skettenis 		/* Clear any BSS. */
164210f3d83Skettenis 		if (ph[i].p_memsz > ph[i].p_filesz) {
165210f3d83Skettenis 			memset(addr + (ph[i].p_vaddr + ph[i].p_filesz) - start,
166210f3d83Skettenis 			    0, ph[i].p_memsz - ph[i].p_filesz);
167210f3d83Skettenis 		}
168210f3d83Skettenis 	}
169210f3d83Skettenis 
17024add1c0Skettenis 	random = 0;
171210f3d83Skettenis 	for (i = 0; i < eh.e_phnum; i++) {
172210f3d83Skettenis 		if (ph[i].p_type != PT_OPENBSD_RANDOMIZE)
173210f3d83Skettenis 			continue;
174210f3d83Skettenis 
175210f3d83Skettenis 		/* Assume that the segment is inside a LOAD segment. */
176210f3d83Skettenis 		arc4random_buf(addr + ph[i].p_vaddr - start, ph[i].p_filesz);
17724add1c0Skettenis 		random = 1;
17824add1c0Skettenis 	}
17924add1c0Skettenis 
18024add1c0Skettenis 	if (random == 0)
18124add1c0Skettenis 		kargs->boothowto &= ~RB_GOODRANDOM;
18224add1c0Skettenis 
18369126009Skettenis 	symsize = round_page(kargs->klen);
18469126009Skettenis 	symaddr = km_alloc(symsize, &kv_any, &kp_kexec, &kd_nowait);
18569126009Skettenis 	if (symaddr == NULL) {
18669126009Skettenis 		error = ENOMEM;
18769126009Skettenis 		goto fail;
18869126009Skettenis 	}
18969126009Skettenis 
19069126009Skettenis 	error = kexec_read(kargs, symaddr, kargs->klen, 0);
19169126009Skettenis 	if (error != 0)
19269126009Skettenis 		goto fail;
19369126009Skettenis 
194bc7ce91cSderaadt 	vfs_shutdown(p);
195bc7ce91cSderaadt 
1960e5ba2a7Skettenis 	shp = (Elf64_Shdr *)(symaddr + eh.e_shoff);
1970e5ba2a7Skettenis 	shstr = symaddr + shp[eh.e_shstrndx].sh_offset;
1980e5ba2a7Skettenis 	for (i = 0; i < eh.e_shnum; i++) {
1990e5ba2a7Skettenis 		if (shp[i].sh_type == SHT_SYMTAB ||
2000e5ba2a7Skettenis 		    shp[i].sh_type == SHT_STRTAB ||
2010e5ba2a7Skettenis 		    strcmp(shstr + shp[i].sh_name, ".debug_line") == 0 ||
2020e5ba2a7Skettenis 		    strcmp(shstr + shp[i].sh_name, ELF_CTF) == 0)
2030e5ba2a7Skettenis 			if (shp[i].sh_offset + shp[i].sh_size <= symsize)
2040e5ba2a7Skettenis 				shp[i].sh_flags |= SHF_ALLOC;
2050e5ba2a7Skettenis 	}
2060e5ba2a7Skettenis 
20769126009Skettenis 	pmap_extract(pmap_kernel(), (vaddr_t)symaddr, &initrd_pa);
20869126009Skettenis 
20924add1c0Skettenis 	node = fdt_find_node("/chosen");
21024add1c0Skettenis 	if (node) {
21124add1c0Skettenis 		uint32_t boothowto = htobe32(kargs->boothowto);
21269126009Skettenis 		uint64_t initrd_start = htobe64(initrd_pa);
21369126009Skettenis 		uint64_t initrd_end = htobe64(initrd_pa + kargs->klen);
21424add1c0Skettenis 
21524add1c0Skettenis 		fdt_node_add_property(node, "openbsd,boothowto",
21624add1c0Skettenis 		    &boothowto, sizeof(boothowto));
21724add1c0Skettenis 		fdt_node_add_property(node, "openbsd,bootduid",
21824add1c0Skettenis 		    kargs->bootduid, sizeof(kargs->bootduid));
21924add1c0Skettenis 
22069126009Skettenis 		fdt_node_set_property(node, "linux,initrd-start",
22169126009Skettenis 		    &initrd_start, sizeof(initrd_start));
22269126009Skettenis 		fdt_node_set_property(node, "linux,initrd-end",
22369126009Skettenis 		    &initrd_end, sizeof(initrd_end));
22469126009Skettenis 
22524add1c0Skettenis 		fdt_finalize();
226210f3d83Skettenis 	}
227210f3d83Skettenis 
228210f3d83Skettenis 	printf("launching kernel\n");
229210f3d83Skettenis 
230210f3d83Skettenis 	config_suspend_all(DVACT_POWERDOWN);
231210f3d83Skettenis 
232210f3d83Skettenis 	intr_disable();
233210f3d83Skettenis 
234210f3d83Skettenis 	pmap_extract(pmap_kernel(), (vaddr_t)addr, &start_pa);
235210f3d83Skettenis 	kexec(start_pa + (eh.e_entry - start), fdt_pa);
236210f3d83Skettenis 
237210f3d83Skettenis 	for (;;)
238210f3d83Skettenis 		continue;
239210f3d83Skettenis 
240210f3d83Skettenis fail:
24169126009Skettenis 	if (symaddr)
24269126009Skettenis 		km_free(symaddr, symsize, &kv_any, &kp_kexec);
24369126009Skettenis 	if (addr)
24469126009Skettenis 		km_free(addr, size, &kv_any, &kp_kexec);
245fee0938bSderaadt 	if (sh)
246210f3d83Skettenis 		free(sh, M_TEMP, shsize);
247fee0938bSderaadt 	if (ph)
248210f3d83Skettenis 		free(ph, M_TEMP, phsize);
249210f3d83Skettenis 	return error;
250210f3d83Skettenis }
251210f3d83Skettenis 
252210f3d83Skettenis int
kexec_read(struct kexec_args * kargs,void * buf,size_t size,off_t off)253210f3d83Skettenis kexec_read(struct kexec_args *kargs, void *buf, size_t size, off_t off)
254210f3d83Skettenis {
255210f3d83Skettenis 	if (off + size < off || off + size > kargs->klen)
256210f3d83Skettenis 		return ENOEXEC;
257210f3d83Skettenis 	return copyin(kargs->kimg + off, buf, size);
258210f3d83Skettenis }
259