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