xref: /openbsd-src/sys/arch/octeon/dev/octboot.c (revision 45294497de97f1403a0220fe95d41c81aad08c99)
1 /*	$OpenBSD: octboot.c,v 1.5 2022/01/10 16:21:19 visa 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
octbootattach(int num)44 octbootattach(int num)
45 {
46 }
47 
48 int
octbootopen(dev_t dev,int flags,int mode,struct proc * p)49 octbootopen(dev_t dev, int flags, int mode, struct proc *p)
50 {
51 	return (0);
52 }
53 
54 int
octbootclose(dev_t dev,int flags,int mode,struct proc * p)55 octbootclose(dev_t dev, int flags, int mode, struct proc *p)
56 {
57 	return (0);
58 }
59 
60 int
octbootioctl(dev_t dev,u_long cmd,caddr_t data,int flags,struct proc * p)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
octboot_kexec(struct octboot_kexec_args * kargs,struct proc * p)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 phsize = 0, shsize = 0, shstrsize = 0;
98 	size_t len, size;
99 	char *argbuf = NULL, *argptr;
100 	char *shstr = NULL;
101 	int argc = 0, error, havesyms = 0, i, nalloc = 0;
102 
103 	memset(&eh, 0, sizeof(eh));
104 
105 	/*
106 	 * Load kernel arguments into a temporary buffer.
107 	 * This also translates the userspace argv pointers to kernel pointers.
108 	 */
109 	argbuf = malloc(PAGE_SIZE, M_TEMP, M_NOWAIT);
110 	if (argbuf == NULL) {
111 		error = ENOMEM;
112 		goto fail;
113 	}
114 	argptr = argbuf;
115 	for (i = 0; i < OCTBOOT_MAX_ARGS && kargs->argv[i] != NULL; i++) {
116 		len = argbuf + PAGE_SIZE - argptr;
117 		error = copyinstr(kargs->argv[i], argptr, len, &len);
118 		if (error != 0)
119 			goto fail;
120 		kargs->argv[i] = argptr;
121 		argptr += len;
122 		argc++;
123 	}
124 
125 	/*
126 	 * Read the headers and validate them.
127 	 */
128 	error = octboot_read(kargs, &eh, sizeof(eh), 0);
129 	if (error != 0)
130 		goto fail;
131 
132 	/* Load program headers. */
133 	ph = mallocarray(eh.e_phnum, sizeof(Elf_Phdr), M_TEMP, M_NOWAIT);
134 	if (ph == NULL) {
135 		error = ENOMEM;
136 		goto fail;
137 	}
138 	phsize = eh.e_phnum * sizeof(Elf_Phdr);
139 	error = octboot_read(kargs, ph, phsize, eh.e_phoff);
140 	if (error != 0)
141 		goto fail;
142 
143 	/* Load section headers. */
144 	sh = mallocarray(eh.e_shnum, sizeof(Elf_Shdr), M_TEMP, M_NOWAIT);
145 	if (sh == NULL) {
146 		error = ENOMEM;
147 		goto fail;
148 	}
149 	shsize = eh.e_shnum * sizeof(Elf_Shdr);
150 	error = octboot_read(kargs, sh, shsize, eh.e_shoff);
151 	if (error != 0)
152 		goto fail;
153 
154 	/* Sanity-check addresses. */
155 	for (i = 0; i < eh.e_phnum; i++) {
156 		if (ph[i].p_type != PT_LOAD &&
157 		    ph[i].p_type != PT_OPENBSD_RANDOMIZE)
158 			continue;
159 		if (ph[i].p_paddr < CKSEG0_BASE ||
160 		    ph[i].p_paddr + ph[i].p_memsz >= CKSEG0_BASE + CKSEG_SIZE) {
161 			error = ENOEXEC;
162 			goto fail;
163 		}
164 	}
165 
166 	/*
167 	 * Allocate physical memory and load the segments.
168 	 */
169 
170 	for (i = 0; i < eh.e_phnum; i++) {
171 		if (ph[i].p_type != PT_LOAD)
172 			continue;
173 		pa = CKSEG0_TO_PHYS(ph[i].p_paddr);
174 		size = roundup(ph[i].p_memsz, BOOTMEM_BLOCK_ALIGN);
175 		if (bootmem_alloc_region(pa, size) != 0) {
176 			printf("kexec: failed to allocate segment "
177 			    "0x%lx @ 0x%lx\n", size, pa);
178 			error = ENOMEM;
179 			goto fail;
180 		}
181 		if (maxp < pa + size)
182 			maxp = pa + size;
183 		nalloc++;
184 	}
185 
186 	for (i = 0; i < eh.e_phnum; i++) {
187 		if (ph[i].p_type == PT_OPENBSD_RANDOMIZE) {
188 			/* Assume that the segment is inside a LOAD segment. */
189 			arc4random_buf((caddr_t)ph[i].p_paddr, ph[i].p_filesz);
190 			continue;
191 		}
192 
193 		if (ph[i].p_type != PT_LOAD)
194 			continue;
195 
196 		error = octboot_read(kargs, (caddr_t)ph[i].p_paddr,
197 		    ph[i].p_filesz, ph[i].p_offset);
198 		if (error != 0)
199 			goto fail;
200 
201 		/* Clear any BSS. */
202 		if (ph[i].p_memsz > ph[i].p_filesz) {
203 			memset((caddr_t)ph[i].p_paddr + ph[i].p_filesz,
204 			    0, ph[i].p_memsz - ph[i].p_filesz);
205 		}
206 	}
207 	ekern = maxp;
208 
209 	for (i = 0; i < eh.e_shnum; i++) {
210 		if (sh[i].sh_type == SHT_SYMTAB) {
211 			havesyms = 1;
212 			break;
213 		}
214 	}
215 
216 	if (havesyms) {
217 		/* Reserve space for ssym and esym pointers. */
218 		maxp += sizeof(int32_t) * 2;
219 
220 		elfp = roundup(maxp, sizeof(Elf_Addr));
221 		maxp = elfp + sizeof(Elf_Ehdr);
222 		shp = maxp;
223 		maxp = shp + roundup(shsize, sizeof(Elf_Addr));
224 		maxp = roundup(maxp, BOOTMEM_BLOCK_ALIGN);
225 		if (bootmem_alloc_region(ekern, maxp - ekern) != 0) {
226 			printf("kexec: failed to allocate %zu bytes for ELF "
227 			    "and section headers\n", maxp - ekern);
228 			error = ENOMEM;
229 			goto fail;
230 		}
231 
232 		shstrsize = sh[eh.e_shstrndx].sh_size;
233 		shstr = malloc(shstrsize, M_TEMP, M_NOWAIT);
234 		if (shstr == NULL) {
235 			error = ENOMEM;
236 			goto fail;
237 		}
238 		error = octboot_read(kargs, shstr, shstrsize,
239 		    sh[eh.e_shstrndx].sh_offset);
240 		if (error != 0)
241 			goto fail;
242 
243 		off = maxp - elfp;
244 		for (i = 0; i < eh.e_shnum; i++) {
245 			if (sh[i].sh_type == SHT_STRTAB ||
246 			    sh[i].sh_type == SHT_SYMTAB ||
247 			    strcmp(shstr + sh[i].sh_name, ELF_CTF) == 0 ||
248 			    strcmp(shstr + sh[i].sh_name, ".debug_line") == 0) {
249 				size_t bsize = roundup(sh[i].sh_size,
250 				    BOOTMEM_BLOCK_ALIGN);
251 
252 				if (bootmem_alloc_region(maxp, bsize) != 0) {
253 					error = ENOMEM;
254 					goto fail;
255 				}
256 				error = octboot_read(kargs,
257 				    (caddr_t)PHYS_TO_CKSEG0(maxp),
258 				    sh[i].sh_size, sh[i].sh_offset);
259 				maxp += bsize;
260 				if (error != 0)
261 					goto fail;
262 				sh[i].sh_offset = off;
263 				sh[i].sh_flags |= SHF_ALLOC;
264 				off += bsize;
265 			}
266 		}
267 
268 		eh.e_phoff = 0;
269 		eh.e_shoff = sizeof(eh);
270 		eh.e_phentsize = 0;
271 		eh.e_phnum = 0;
272 		memcpy((caddr_t)PHYS_TO_CKSEG0(elfp), &eh, sizeof(eh));
273 		memcpy((caddr_t)PHYS_TO_CKSEG0(shp), sh, shsize);
274 
275 		*(int32_t *)PHYS_TO_CKSEG0(ekern) = PHYS_TO_CKSEG0(elfp);
276 		*((int32_t *)PHYS_TO_CKSEG0(ekern) + 1) = PHYS_TO_CKSEG0(maxp);
277 	}
278 
279 	/*
280 	 * Put kernel arguments in place.
281 	 */
282 	octeon_boot_desc->argc = 0;
283 	for (i = 0; i < OCTEON_ARGV_MAX; i++)
284 		octeon_boot_desc->argv[i] = 0;
285 	if (argptr > argbuf) {
286 		size = roundup(argptr - argbuf, BOOTMEM_BLOCK_ALIGN);
287 		if (bootmem_alloc_region(maxp, size) != 0) {
288 			error = ENOMEM;
289 			goto fail;
290 		}
291 		memcpy((caddr_t)PHYS_TO_CKSEG0(maxp), argbuf, argptr - argbuf);
292 		for (i = 0; i < argc; i++) {
293 			KASSERT(kargs->argv[i] >= argbuf);
294 			KASSERT(kargs->argv[i] < argbuf + PAGE_SIZE);
295 			octeon_boot_desc->argv[i] = kargs->argv[i] - argbuf +
296 			    maxp;
297 		}
298 		octeon_boot_desc->argc = argc;
299 		maxp += size;
300 	}
301 
302 	vfs_shutdown(p);
303 
304 	printf("launching kernel\n");
305 
306 	config_suspend_all(DVACT_POWERDOWN);
307 
308 	intr_disable();
309 
310 	/* Put UVM memory back to the free list. */
311 	for (i = 0; mem_layout[i].mem_last_page != 0; i++) {
312 		uint64_t fp = mem_layout[i].mem_first_page;
313 		uint64_t lp = mem_layout[i].mem_last_page;
314 
315 		bootmem_free(ptoa(fp), ptoa(lp) - ptoa(fp));
316 	}
317 
318 	/*
319 	 * Release the memory of the bootloader kernel.
320 	 * This may overwrite a tiny region at the start of the running image.
321 	 */
322 	bootmem_free(CKSEG0_TO_PHYS((vaddr_t)start), end - start);
323 
324 	/* Let secondary cores proceed to the new kernel. */
325 	octeon_boot_entry = eh.e_entry;
326 	octeon_syncw();		/* Order writes. */
327 	octeon_boot_ready = 1;	/* Open the gate. */
328 	octeon_syncw();		/* Flush writes. */
329 	delay(1000);		/* Give secondary cores a lead. */
330 
331 	__asm__ volatile (
332 	"	cache 1, 0($0)\n"	/* Flush and invalidate dcache. */
333 	"	cache 0, 0($0)\n"	/* Invalidate icache. */
334 	::: "memory");
335 
336 	(*(kentry)eh.e_entry)(0, 0, PRIMARY, (register_t)octeon_boot_desc);
337 
338 	for (;;)
339 		continue;
340 
341 fail:
342 	if (ekern != 0)
343 		bootmem_free(ekern, maxp - ekern);
344 	for (i = 0; i < eh.e_phnum && nalloc > 0; i++) {
345 		if (ph[i].p_type == PT_LOAD) {
346 			pa = CKSEG0_TO_PHYS(ph[i].p_paddr);
347 			bootmem_free(pa, ph[i].p_memsz);
348 			nalloc--;
349 		}
350 	}
351 	free(shstr, M_TEMP, shstrsize);
352 	free(sh, M_TEMP, shsize);
353 	free(ph, M_TEMP, phsize);
354 	free(argbuf, M_TEMP, PAGE_SIZE);
355 	return error;
356 }
357 
358 int
octboot_read(struct octboot_kexec_args * kargs,void * buf,size_t size,off_t off)359 octboot_read(struct octboot_kexec_args *kargs, void *buf, size_t size,
360     off_t off)
361 {
362 	if (off + size < off || off + size > kargs->klen)
363 		return ENOEXEC;
364 	return copyin(kargs->kimg + off, buf, size);
365 }
366