xref: /openbsd/sys/arch/octeon/dev/octboot.c (revision d89ec533)
1 /*	$OpenBSD: octboot.c,v 1.4 2020/09/02 16:07:33 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 2019-2020 Visa Hankala
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/exec_elf.h>
22 #include <sys/malloc.h>
23 #include <sys/proc.h>
24 #include <sys/mount.h>
25 
26 #include <uvm/uvm_extern.h>
27 
28 #include <mips64/memconf.h>
29 
30 #include <machine/autoconf.h>
31 #include <machine/octboot.h>
32 #include <machine/octeonvar.h>
33 
34 typedef void (*kentry)(register_t, register_t, register_t, register_t);
35 #define PRIMARY 1
36 
37 int	octboot_kexec(struct octboot_kexec_args *, struct proc *);
38 int	octboot_read(struct octboot_kexec_args *, void *, size_t, off_t);
39 
40 uint64_t	octeon_boot_entry;
41 uint32_t	octeon_boot_ready;
42 
43 void
44 octbootattach(int num)
45 {
46 }
47 
48 int
49 octbootopen(dev_t dev, int flags, int mode, struct proc *p)
50 {
51 	return (0);
52 }
53 
54 int
55 octbootclose(dev_t dev, int flags, int mode, struct proc *p)
56 {
57 	return (0);
58 }
59 
60 int
61 octbootioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
62 {
63 	int error = 0;
64 
65 	switch (cmd) {
66 	case OBIOC_GETROOTDEV:
67 		if (strlen(uboot_rootdev) == 0) {
68 			error = ENOENT;
69 			break;
70 		}
71 		strlcpy((char *)data, uboot_rootdev, PATH_MAX);
72 		break;
73 
74 	case OBIOC_KEXEC:
75 		error = suser(p);
76 		if (error != 0)
77 			break;
78 		error = octboot_kexec((struct octboot_kexec_args *)data, p);
79 		break;
80 
81 	default:
82 		error = ENOTTY;
83 		break;
84 	}
85 
86 	return error;
87 }
88 
89 int
90 octboot_kexec(struct octboot_kexec_args *kargs, struct proc *p)
91 {
92 	extern char start[], end[];
93 	Elf_Ehdr eh;
94 	Elf_Phdr *ph = NULL;
95 	Elf_Shdr *sh = NULL;
96 	paddr_t ekern = 0, elfp, maxp = 0, off, pa, shp;
97 	size_t len, phsize, shsize, shstrsize, size;
98 	char *argbuf = NULL, *argptr;
99 	char *shstr = NULL;
100 	int argc = 0, error, havesyms = 0, i, nalloc = 0;
101 
102 	/*
103 	 * Load kernel arguments into a temporary buffer.
104 	 * This also translates the userspace argv pointers to kernel pointers.
105 	 */
106 	argbuf = malloc(PAGE_SIZE, M_TEMP, M_NOWAIT);
107 	if (argbuf == NULL) {
108 		error = ENOMEM;
109 		goto fail;
110 	}
111 	argptr = argbuf;
112 	for (i = 0; i < OCTBOOT_MAX_ARGS && kargs->argv[i] != NULL; i++) {
113 		len = argbuf + PAGE_SIZE - argptr;
114 		error = copyinstr(kargs->argv[i], argptr, len, &len);
115 		if (error != 0)
116 			goto fail;
117 		kargs->argv[i] = argptr;
118 		argptr += len;
119 		argc++;
120 	}
121 
122 	/*
123 	 * Read the headers and validate them.
124 	 */
125 	error = octboot_read(kargs, &eh, sizeof(eh), 0);
126 	if (error != 0)
127 		goto fail;
128 
129 	/* Load program headers. */
130 	ph = mallocarray(eh.e_phnum, sizeof(Elf_Phdr), M_TEMP, M_NOWAIT);
131 	if (ph == NULL) {
132 		error = ENOMEM;
133 		goto fail;
134 	}
135 	phsize = eh.e_phnum * sizeof(Elf_Phdr);
136 	error = octboot_read(kargs, ph, phsize, eh.e_phoff);
137 	if (error != 0)
138 		goto fail;
139 
140 	/* Load section headers. */
141 	sh = mallocarray(eh.e_shnum, sizeof(Elf_Shdr), M_TEMP, M_NOWAIT);
142 	if (sh == NULL) {
143 		error = ENOMEM;
144 		goto fail;
145 	}
146 	shsize = eh.e_shnum * sizeof(Elf_Shdr);
147 	error = octboot_read(kargs, sh, shsize, eh.e_shoff);
148 	if (error != 0)
149 		goto fail;
150 
151 	/* Sanity-check addresses. */
152 	for (i = 0; i < eh.e_phnum; i++) {
153 		if (ph[i].p_type != PT_LOAD &&
154 		    ph[i].p_type != PT_OPENBSD_RANDOMIZE)
155 			continue;
156 		if (ph[i].p_paddr < CKSEG0_BASE ||
157 		    ph[i].p_paddr + ph[i].p_memsz >= CKSEG0_BASE + CKSEG_SIZE) {
158 			error = ENOEXEC;
159 			goto fail;
160 		}
161 	}
162 
163 	/*
164 	 * Allocate physical memory and load the segments.
165 	 */
166 
167 	for (i = 0; i < eh.e_phnum; i++) {
168 		if (ph[i].p_type != PT_LOAD)
169 			continue;
170 		pa = CKSEG0_TO_PHYS(ph[i].p_paddr);
171 		size = roundup(ph[i].p_memsz, BOOTMEM_BLOCK_ALIGN);
172 		if (bootmem_alloc_region(pa, size) != 0) {
173 			printf("kexec: failed to allocate segment "
174 			    "0x%lx @ 0x%lx\n", size, pa);
175 			error = ENOMEM;
176 			goto fail;
177 		}
178 		if (maxp < pa + size)
179 			maxp = pa + size;
180 		nalloc++;
181 	}
182 
183 	for (i = 0; i < eh.e_phnum; i++) {
184 		if (ph[i].p_type == PT_OPENBSD_RANDOMIZE) {
185 			/* Assume that the segment is inside a LOAD segment. */
186 			arc4random_buf((caddr_t)ph[i].p_paddr, ph[i].p_filesz);
187 			continue;
188 		}
189 
190 		if (ph[i].p_type != PT_LOAD)
191 			continue;
192 
193 		error = octboot_read(kargs, (caddr_t)ph[i].p_paddr,
194 		    ph[i].p_filesz, ph[i].p_offset);
195 		if (error != 0)
196 			goto fail;
197 
198 		/* Clear any BSS. */
199 		if (ph[i].p_memsz > ph[i].p_filesz) {
200 			memset((caddr_t)ph[i].p_paddr + ph[i].p_filesz,
201 			    0, ph[i].p_memsz - ph[i].p_filesz);
202 		}
203 	}
204 	ekern = maxp;
205 
206 	for (i = 0; i < eh.e_shnum; i++) {
207 		if (sh[i].sh_type == SHT_SYMTAB) {
208 			havesyms = 1;
209 			break;
210 		}
211 	}
212 
213 	if (havesyms) {
214 		/* Reserve space for ssym and esym pointers. */
215 		maxp += sizeof(int32_t) * 2;
216 
217 		elfp = roundup(maxp, sizeof(Elf_Addr));
218 		maxp = elfp + sizeof(Elf_Ehdr);
219 		shp = maxp;
220 		maxp = shp + roundup(shsize, sizeof(Elf_Addr));
221 		maxp = roundup(maxp, BOOTMEM_BLOCK_ALIGN);
222 		if (bootmem_alloc_region(ekern, maxp - ekern) != 0) {
223 			printf("kexec: failed to allocate %zu bytes for ELF "
224 			    "and section headers\n", maxp - ekern);
225 			error = ENOMEM;
226 			goto fail;
227 		}
228 
229 		shstrsize = sh[eh.e_shstrndx].sh_size;
230 		shstr = malloc(shstrsize, M_TEMP, M_NOWAIT);
231 		if (shstr == NULL) {
232 			error = ENOMEM;
233 			goto fail;
234 		}
235 		error = octboot_read(kargs, shstr, shstrsize,
236 		    sh[eh.e_shstrndx].sh_offset);
237 		if (error != 0)
238 			goto fail;
239 
240 		off = maxp - elfp;
241 		for (i = 0; i < eh.e_shnum; i++) {
242 			if (sh[i].sh_type == SHT_STRTAB ||
243 			    sh[i].sh_type == SHT_SYMTAB ||
244 			    strcmp(shstr + sh[i].sh_name, ELF_CTF) == 0 ||
245 			    strcmp(shstr + sh[i].sh_name, ".debug_line") == 0) {
246 				size_t bsize = roundup(sh[i].sh_size,
247 				    BOOTMEM_BLOCK_ALIGN);
248 
249 				if (bootmem_alloc_region(maxp, bsize) != 0) {
250 					error = ENOMEM;
251 					goto fail;
252 				}
253 				error = octboot_read(kargs,
254 				    (caddr_t)PHYS_TO_CKSEG0(maxp),
255 				    sh[i].sh_size, sh[i].sh_offset);
256 				maxp += bsize;
257 				if (error != 0)
258 					goto fail;
259 				sh[i].sh_offset = off;
260 				sh[i].sh_flags |= SHF_ALLOC;
261 				off += bsize;
262 			}
263 		}
264 
265 		eh.e_phoff = 0;
266 		eh.e_shoff = sizeof(eh);
267 		eh.e_phentsize = 0;
268 		eh.e_phnum = 0;
269 		memcpy((caddr_t)PHYS_TO_CKSEG0(elfp), &eh, sizeof(eh));
270 		memcpy((caddr_t)PHYS_TO_CKSEG0(shp), sh, shsize);
271 
272 		*(int32_t *)PHYS_TO_CKSEG0(ekern) = PHYS_TO_CKSEG0(elfp);
273 		*((int32_t *)PHYS_TO_CKSEG0(ekern) + 1) = PHYS_TO_CKSEG0(maxp);
274 	}
275 
276 	/*
277 	 * Put kernel arguments in place.
278 	 */
279 	octeon_boot_desc->argc = 0;
280 	for (i = 0; i < OCTEON_ARGV_MAX; i++)
281 		octeon_boot_desc->argv[i] = 0;
282 	if (argptr > argbuf) {
283 		size = roundup(argptr - argbuf, BOOTMEM_BLOCK_ALIGN);
284 		if (bootmem_alloc_region(maxp, size) != 0) {
285 			error = ENOMEM;
286 			goto fail;
287 		}
288 		memcpy((caddr_t)PHYS_TO_CKSEG0(maxp), argbuf, argptr - argbuf);
289 		for (i = 0; i < argc; i++) {
290 			KASSERT(kargs->argv[i] >= argbuf);
291 			KASSERT(kargs->argv[i] < argbuf + PAGE_SIZE);
292 			octeon_boot_desc->argv[i] = kargs->argv[i] - argbuf +
293 			    maxp;
294 		}
295 		octeon_boot_desc->argc = argc;
296 		maxp += size;
297 	}
298 
299 	vfs_shutdown(p);
300 
301 	printf("launching kernel\n");
302 
303 	config_suspend_all(DVACT_POWERDOWN);
304 
305 	intr_disable();
306 
307 	/* Put UVM memory back to the free list. */
308 	for (i = 0; mem_layout[i].mem_last_page != 0; i++) {
309 		uint64_t fp = mem_layout[i].mem_first_page;
310 		uint64_t lp = mem_layout[i].mem_last_page;
311 
312 		bootmem_free(ptoa(fp), ptoa(lp) - ptoa(fp));
313 	}
314 
315 	/*
316 	 * Release the memory of the bootloader kernel.
317 	 * This may overwrite a tiny region at the start of the running image.
318 	 */
319 	bootmem_free(CKSEG0_TO_PHYS((vaddr_t)start), end - start);
320 
321 	/* Let secondary cores proceed to the new kernel. */
322 	octeon_boot_entry = eh.e_entry;
323 	octeon_syncw();		/* Order writes. */
324 	octeon_boot_ready = 1;	/* Open the gate. */
325 	octeon_syncw();		/* Flush writes. */
326 	delay(1000);		/* Give secondary cores a lead. */
327 
328 	__asm__ volatile (
329 	"	cache 1, 0($0)\n"	/* Flush and invalidate dcache. */
330 	"	cache 0, 0($0)\n"	/* Invalidate icache. */
331 	::: "memory");
332 
333 	(*(kentry)eh.e_entry)(0, 0, PRIMARY, (register_t)octeon_boot_desc);
334 
335 	for (;;)
336 		continue;
337 
338 fail:
339 	if (ekern != 0)
340 		bootmem_free(ekern, maxp - ekern);
341 	for (i = 0; i < eh.e_phnum && nalloc > 0; i++) {
342 		if (ph[i].p_type == PT_LOAD) {
343 			pa = CKSEG0_TO_PHYS(ph[i].p_paddr);
344 			bootmem_free(pa, ph[i].p_memsz);
345 			nalloc--;
346 		}
347 	}
348 	free(shstr, M_TEMP, shstrsize);
349 	free(sh, M_TEMP, shsize);
350 	free(ph, M_TEMP, phsize);
351 	free(argbuf, M_TEMP, PAGE_SIZE);
352 	return error;
353 }
354 
355 int
356 octboot_read(struct octboot_kexec_args *kargs, void *buf, size_t size,
357     off_t off)
358 {
359 	if (off + size < off || off + size > kargs->klen)
360 		return ENOEXEC;
361 	return copyin(kargs->kimg + off, buf, size);
362 }
363