xref: /openbsd/sys/arch/octeon/dev/octboot.c (revision 45294497)
1*45294497Svisa /*	$OpenBSD: octboot.c,v 1.5 2022/01/10 16:21:19 visa Exp $	*/
23a62b615Svisa 
33a62b615Svisa /*
4cf939257Svisa  * Copyright (c) 2019-2020 Visa Hankala
53a62b615Svisa  *
63a62b615Svisa  * Permission to use, copy, modify, and/or distribute this software for any
73a62b615Svisa  * purpose with or without fee is hereby granted, provided that the above
83a62b615Svisa  * copyright notice and this permission notice appear in all copies.
93a62b615Svisa  *
103a62b615Svisa  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
113a62b615Svisa  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
123a62b615Svisa  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
133a62b615Svisa  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
143a62b615Svisa  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
153a62b615Svisa  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
163a62b615Svisa  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
173a62b615Svisa  */
183a62b615Svisa 
193a62b615Svisa #include <sys/param.h>
203a62b615Svisa #include <sys/systm.h>
213a62b615Svisa #include <sys/exec_elf.h>
223a62b615Svisa #include <sys/malloc.h>
233a62b615Svisa #include <sys/proc.h>
24bc7ce91cSderaadt #include <sys/mount.h>
253a62b615Svisa 
263a62b615Svisa #include <uvm/uvm_extern.h>
273a62b615Svisa 
283a62b615Svisa #include <mips64/memconf.h>
293a62b615Svisa 
303a62b615Svisa #include <machine/autoconf.h>
313a62b615Svisa #include <machine/octboot.h>
323a62b615Svisa #include <machine/octeonvar.h>
333a62b615Svisa 
343a62b615Svisa typedef void (*kentry)(register_t, register_t, register_t, register_t);
353a62b615Svisa #define PRIMARY 1
363a62b615Svisa 
373a62b615Svisa int	octboot_kexec(struct octboot_kexec_args *, struct proc *);
38cf939257Svisa int	octboot_read(struct octboot_kexec_args *, void *, size_t, off_t);
393a62b615Svisa 
403a62b615Svisa uint64_t	octeon_boot_entry;
413a62b615Svisa uint32_t	octeon_boot_ready;
423a62b615Svisa 
433a62b615Svisa void
octbootattach(int num)443a62b615Svisa octbootattach(int num)
453a62b615Svisa {
463a62b615Svisa }
473a62b615Svisa 
483a62b615Svisa int
octbootopen(dev_t dev,int flags,int mode,struct proc * p)493a62b615Svisa octbootopen(dev_t dev, int flags, int mode, struct proc *p)
503a62b615Svisa {
513a62b615Svisa 	return (0);
523a62b615Svisa }
533a62b615Svisa 
543a62b615Svisa int
octbootclose(dev_t dev,int flags,int mode,struct proc * p)553a62b615Svisa octbootclose(dev_t dev, int flags, int mode, struct proc *p)
563a62b615Svisa {
573a62b615Svisa 	return (0);
583a62b615Svisa }
593a62b615Svisa 
603a62b615Svisa int
octbootioctl(dev_t dev,u_long cmd,caddr_t data,int flags,struct proc * p)613a62b615Svisa octbootioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
623a62b615Svisa {
633a62b615Svisa 	int error = 0;
643a62b615Svisa 
653a62b615Svisa 	switch (cmd) {
663a62b615Svisa 	case OBIOC_GETROOTDEV:
673a62b615Svisa 		if (strlen(uboot_rootdev) == 0) {
683a62b615Svisa 			error = ENOENT;
693a62b615Svisa 			break;
703a62b615Svisa 		}
713a62b615Svisa 		strlcpy((char *)data, uboot_rootdev, PATH_MAX);
723a62b615Svisa 		break;
733a62b615Svisa 
743a62b615Svisa 	case OBIOC_KEXEC:
753a62b615Svisa 		error = suser(p);
763a62b615Svisa 		if (error != 0)
773a62b615Svisa 			break;
783a62b615Svisa 		error = octboot_kexec((struct octboot_kexec_args *)data, p);
793a62b615Svisa 		break;
803a62b615Svisa 
813a62b615Svisa 	default:
823a62b615Svisa 		error = ENOTTY;
833a62b615Svisa 		break;
843a62b615Svisa 	}
853a62b615Svisa 
863a62b615Svisa 	return error;
873a62b615Svisa }
883a62b615Svisa 
893a62b615Svisa int
octboot_kexec(struct octboot_kexec_args * kargs,struct proc * p)903a62b615Svisa octboot_kexec(struct octboot_kexec_args *kargs, struct proc *p)
913a62b615Svisa {
923a62b615Svisa 	extern char start[], end[];
933a62b615Svisa 	Elf_Ehdr eh;
943a62b615Svisa 	Elf_Phdr *ph = NULL;
953a62b615Svisa 	Elf_Shdr *sh = NULL;
963a62b615Svisa 	paddr_t ekern = 0, elfp, maxp = 0, off, pa, shp;
97*45294497Svisa 	size_t phsize = 0, shsize = 0, shstrsize = 0;
98*45294497Svisa 	size_t len, size;
993a62b615Svisa 	char *argbuf = NULL, *argptr;
1003a62b615Svisa 	char *shstr = NULL;
1013a62b615Svisa 	int argc = 0, error, havesyms = 0, i, nalloc = 0;
1023a62b615Svisa 
103*45294497Svisa 	memset(&eh, 0, sizeof(eh));
104*45294497Svisa 
1053a62b615Svisa 	/*
1063a62b615Svisa 	 * Load kernel arguments into a temporary buffer.
1073a62b615Svisa 	 * This also translates the userspace argv pointers to kernel pointers.
1083a62b615Svisa 	 */
1093a62b615Svisa 	argbuf = malloc(PAGE_SIZE, M_TEMP, M_NOWAIT);
1103a62b615Svisa 	if (argbuf == NULL) {
1113a62b615Svisa 		error = ENOMEM;
1123a62b615Svisa 		goto fail;
1133a62b615Svisa 	}
1143a62b615Svisa 	argptr = argbuf;
1153a62b615Svisa 	for (i = 0; i < OCTBOOT_MAX_ARGS && kargs->argv[i] != NULL; i++) {
1163a62b615Svisa 		len = argbuf + PAGE_SIZE - argptr;
1173a62b615Svisa 		error = copyinstr(kargs->argv[i], argptr, len, &len);
1183a62b615Svisa 		if (error != 0)
1193a62b615Svisa 			goto fail;
1203a62b615Svisa 		kargs->argv[i] = argptr;
1213a62b615Svisa 		argptr += len;
1223a62b615Svisa 		argc++;
1233a62b615Svisa 	}
1243a62b615Svisa 
1253a62b615Svisa 	/*
1263a62b615Svisa 	 * Read the headers and validate them.
1273a62b615Svisa 	 */
128cf939257Svisa 	error = octboot_read(kargs, &eh, sizeof(eh), 0);
1293a62b615Svisa 	if (error != 0)
1303a62b615Svisa 		goto fail;
1313a62b615Svisa 
1323a62b615Svisa 	/* Load program headers. */
1333a62b615Svisa 	ph = mallocarray(eh.e_phnum, sizeof(Elf_Phdr), M_TEMP, M_NOWAIT);
1343a62b615Svisa 	if (ph == NULL) {
1353a62b615Svisa 		error = ENOMEM;
1363a62b615Svisa 		goto fail;
1373a62b615Svisa 	}
1383a62b615Svisa 	phsize = eh.e_phnum * sizeof(Elf_Phdr);
139cf939257Svisa 	error = octboot_read(kargs, ph, phsize, eh.e_phoff);
1403a62b615Svisa 	if (error != 0)
1413a62b615Svisa 		goto fail;
1423a62b615Svisa 
1433a62b615Svisa 	/* Load section headers. */
1443a62b615Svisa 	sh = mallocarray(eh.e_shnum, sizeof(Elf_Shdr), M_TEMP, M_NOWAIT);
1453a62b615Svisa 	if (sh == NULL) {
1463a62b615Svisa 		error = ENOMEM;
1473a62b615Svisa 		goto fail;
1483a62b615Svisa 	}
1493a62b615Svisa 	shsize = eh.e_shnum * sizeof(Elf_Shdr);
150cf939257Svisa 	error = octboot_read(kargs, sh, shsize, eh.e_shoff);
1513a62b615Svisa 	if (error != 0)
1523a62b615Svisa 		goto fail;
1533a62b615Svisa 
1543a62b615Svisa 	/* Sanity-check addresses. */
1553a62b615Svisa 	for (i = 0; i < eh.e_phnum; i++) {
1563a62b615Svisa 		if (ph[i].p_type != PT_LOAD &&
1573a62b615Svisa 		    ph[i].p_type != PT_OPENBSD_RANDOMIZE)
1583a62b615Svisa 			continue;
1593a62b615Svisa 		if (ph[i].p_paddr < CKSEG0_BASE ||
1603a62b615Svisa 		    ph[i].p_paddr + ph[i].p_memsz >= CKSEG0_BASE + CKSEG_SIZE) {
1613a62b615Svisa 			error = ENOEXEC;
1623a62b615Svisa 			goto fail;
1633a62b615Svisa 		}
1643a62b615Svisa 	}
1653a62b615Svisa 
1663a62b615Svisa 	/*
1673a62b615Svisa 	 * Allocate physical memory and load the segments.
1683a62b615Svisa 	 */
1693a62b615Svisa 
1703a62b615Svisa 	for (i = 0; i < eh.e_phnum; i++) {
1713a62b615Svisa 		if (ph[i].p_type != PT_LOAD)
1723a62b615Svisa 			continue;
1733a62b615Svisa 		pa = CKSEG0_TO_PHYS(ph[i].p_paddr);
1743a62b615Svisa 		size = roundup(ph[i].p_memsz, BOOTMEM_BLOCK_ALIGN);
1753a62b615Svisa 		if (bootmem_alloc_region(pa, size) != 0) {
1763a62b615Svisa 			printf("kexec: failed to allocate segment "
1773a62b615Svisa 			    "0x%lx @ 0x%lx\n", size, pa);
1783a62b615Svisa 			error = ENOMEM;
1793a62b615Svisa 			goto fail;
1803a62b615Svisa 		}
1813a62b615Svisa 		if (maxp < pa + size)
1823a62b615Svisa 			maxp = pa + size;
1833a62b615Svisa 		nalloc++;
1843a62b615Svisa 	}
1853a62b615Svisa 
1863a62b615Svisa 	for (i = 0; i < eh.e_phnum; i++) {
1873a62b615Svisa 		if (ph[i].p_type == PT_OPENBSD_RANDOMIZE) {
1883a62b615Svisa 			/* Assume that the segment is inside a LOAD segment. */
1893a62b615Svisa 			arc4random_buf((caddr_t)ph[i].p_paddr, ph[i].p_filesz);
1903a62b615Svisa 			continue;
1913a62b615Svisa 		}
1923a62b615Svisa 
1933a62b615Svisa 		if (ph[i].p_type != PT_LOAD)
1943a62b615Svisa 			continue;
1953a62b615Svisa 
196cf939257Svisa 		error = octboot_read(kargs, (caddr_t)ph[i].p_paddr,
1973a62b615Svisa 		    ph[i].p_filesz, ph[i].p_offset);
1983a62b615Svisa 		if (error != 0)
1993a62b615Svisa 			goto fail;
2003a62b615Svisa 
2013a62b615Svisa 		/* Clear any BSS. */
2023a62b615Svisa 		if (ph[i].p_memsz > ph[i].p_filesz) {
2033a62b615Svisa 			memset((caddr_t)ph[i].p_paddr + ph[i].p_filesz,
2043a62b615Svisa 			    0, ph[i].p_memsz - ph[i].p_filesz);
2053a62b615Svisa 		}
2063a62b615Svisa 	}
2073a62b615Svisa 	ekern = maxp;
2083a62b615Svisa 
2093a62b615Svisa 	for (i = 0; i < eh.e_shnum; i++) {
2103a62b615Svisa 		if (sh[i].sh_type == SHT_SYMTAB) {
2113a62b615Svisa 			havesyms = 1;
2123a62b615Svisa 			break;
2133a62b615Svisa 		}
2143a62b615Svisa 	}
2153a62b615Svisa 
2163a62b615Svisa 	if (havesyms) {
2173a62b615Svisa 		/* Reserve space for ssym and esym pointers. */
2183a62b615Svisa 		maxp += sizeof(int32_t) * 2;
2193a62b615Svisa 
2203a62b615Svisa 		elfp = roundup(maxp, sizeof(Elf_Addr));
2213a62b615Svisa 		maxp = elfp + sizeof(Elf_Ehdr);
2223a62b615Svisa 		shp = maxp;
2233a62b615Svisa 		maxp = shp + roundup(shsize, sizeof(Elf_Addr));
2243a62b615Svisa 		maxp = roundup(maxp, BOOTMEM_BLOCK_ALIGN);
2253a62b615Svisa 		if (bootmem_alloc_region(ekern, maxp - ekern) != 0) {
2263a62b615Svisa 			printf("kexec: failed to allocate %zu bytes for ELF "
2273a62b615Svisa 			    "and section headers\n", maxp - ekern);
2283a62b615Svisa 			error = ENOMEM;
2293a62b615Svisa 			goto fail;
2303a62b615Svisa 		}
2313a62b615Svisa 
2323a62b615Svisa 		shstrsize = sh[eh.e_shstrndx].sh_size;
2333a62b615Svisa 		shstr = malloc(shstrsize, M_TEMP, M_NOWAIT);
2343a62b615Svisa 		if (shstr == NULL) {
2353a62b615Svisa 			error = ENOMEM;
2363a62b615Svisa 			goto fail;
2373a62b615Svisa 		}
238cf939257Svisa 		error = octboot_read(kargs, shstr, shstrsize,
2393a62b615Svisa 		    sh[eh.e_shstrndx].sh_offset);
2403a62b615Svisa 		if (error != 0)
2413a62b615Svisa 			goto fail;
2423a62b615Svisa 
2433a62b615Svisa 		off = maxp - elfp;
2443a62b615Svisa 		for (i = 0; i < eh.e_shnum; i++) {
2453a62b615Svisa 			if (sh[i].sh_type == SHT_STRTAB ||
2463a62b615Svisa 			    sh[i].sh_type == SHT_SYMTAB ||
2473a62b615Svisa 			    strcmp(shstr + sh[i].sh_name, ELF_CTF) == 0 ||
2483a62b615Svisa 			    strcmp(shstr + sh[i].sh_name, ".debug_line") == 0) {
2493a62b615Svisa 				size_t bsize = roundup(sh[i].sh_size,
2503a62b615Svisa 				    BOOTMEM_BLOCK_ALIGN);
2513a62b615Svisa 
2523a62b615Svisa 				if (bootmem_alloc_region(maxp, bsize) != 0) {
2533a62b615Svisa 					error = ENOMEM;
2543a62b615Svisa 					goto fail;
2553a62b615Svisa 				}
256cf939257Svisa 				error = octboot_read(kargs,
2573a62b615Svisa 				    (caddr_t)PHYS_TO_CKSEG0(maxp),
2583a62b615Svisa 				    sh[i].sh_size, sh[i].sh_offset);
2593a62b615Svisa 				maxp += bsize;
2603a62b615Svisa 				if (error != 0)
2613a62b615Svisa 					goto fail;
2623a62b615Svisa 				sh[i].sh_offset = off;
2633a62b615Svisa 				sh[i].sh_flags |= SHF_ALLOC;
2643a62b615Svisa 				off += bsize;
2653a62b615Svisa 			}
2663a62b615Svisa 		}
2673a62b615Svisa 
2683a62b615Svisa 		eh.e_phoff = 0;
2693a62b615Svisa 		eh.e_shoff = sizeof(eh);
2703a62b615Svisa 		eh.e_phentsize = 0;
2713a62b615Svisa 		eh.e_phnum = 0;
2723a62b615Svisa 		memcpy((caddr_t)PHYS_TO_CKSEG0(elfp), &eh, sizeof(eh));
2733a62b615Svisa 		memcpy((caddr_t)PHYS_TO_CKSEG0(shp), sh, shsize);
2743a62b615Svisa 
2753a62b615Svisa 		*(int32_t *)PHYS_TO_CKSEG0(ekern) = PHYS_TO_CKSEG0(elfp);
2763a62b615Svisa 		*((int32_t *)PHYS_TO_CKSEG0(ekern) + 1) = PHYS_TO_CKSEG0(maxp);
2773a62b615Svisa 	}
2783a62b615Svisa 
2793a62b615Svisa 	/*
2803a62b615Svisa 	 * Put kernel arguments in place.
2813a62b615Svisa 	 */
2823a62b615Svisa 	octeon_boot_desc->argc = 0;
2833a62b615Svisa 	for (i = 0; i < OCTEON_ARGV_MAX; i++)
2843a62b615Svisa 		octeon_boot_desc->argv[i] = 0;
2853a62b615Svisa 	if (argptr > argbuf) {
2863a62b615Svisa 		size = roundup(argptr - argbuf, BOOTMEM_BLOCK_ALIGN);
2873a62b615Svisa 		if (bootmem_alloc_region(maxp, size) != 0) {
2883a62b615Svisa 			error = ENOMEM;
2893a62b615Svisa 			goto fail;
2903a62b615Svisa 		}
2913a62b615Svisa 		memcpy((caddr_t)PHYS_TO_CKSEG0(maxp), argbuf, argptr - argbuf);
2923a62b615Svisa 		for (i = 0; i < argc; i++) {
2933a62b615Svisa 			KASSERT(kargs->argv[i] >= argbuf);
2943a62b615Svisa 			KASSERT(kargs->argv[i] < argbuf + PAGE_SIZE);
2953a62b615Svisa 			octeon_boot_desc->argv[i] = kargs->argv[i] - argbuf +
2963a62b615Svisa 			    maxp;
2973a62b615Svisa 		}
2983a62b615Svisa 		octeon_boot_desc->argc = argc;
2993a62b615Svisa 		maxp += size;
3003a62b615Svisa 	}
3013a62b615Svisa 
302bc7ce91cSderaadt 	vfs_shutdown(p);
303bc7ce91cSderaadt 
3043a62b615Svisa 	printf("launching kernel\n");
3053a62b615Svisa 
3063a62b615Svisa 	config_suspend_all(DVACT_POWERDOWN);
3073a62b615Svisa 
3083a62b615Svisa 	intr_disable();
3093a62b615Svisa 
3103a62b615Svisa 	/* Put UVM memory back to the free list. */
3113a62b615Svisa 	for (i = 0; mem_layout[i].mem_last_page != 0; i++) {
3123a62b615Svisa 		uint64_t fp = mem_layout[i].mem_first_page;
3133a62b615Svisa 		uint64_t lp = mem_layout[i].mem_last_page;
3143a62b615Svisa 
3153a62b615Svisa 		bootmem_free(ptoa(fp), ptoa(lp) - ptoa(fp));
3163a62b615Svisa 	}
3173a62b615Svisa 
3183a62b615Svisa 	/*
3193a62b615Svisa 	 * Release the memory of the bootloader kernel.
3203a62b615Svisa 	 * This may overwrite a tiny region at the start of the running image.
3213a62b615Svisa 	 */
3223a62b615Svisa 	bootmem_free(CKSEG0_TO_PHYS((vaddr_t)start), end - start);
3233a62b615Svisa 
3243a62b615Svisa 	/* Let secondary cores proceed to the new kernel. */
3253a62b615Svisa 	octeon_boot_entry = eh.e_entry;
3263a62b615Svisa 	octeon_syncw();		/* Order writes. */
3273a62b615Svisa 	octeon_boot_ready = 1;	/* Open the gate. */
3283a62b615Svisa 	octeon_syncw();		/* Flush writes. */
3293a62b615Svisa 	delay(1000);		/* Give secondary cores a lead. */
3303a62b615Svisa 
3313a62b615Svisa 	__asm__ volatile (
3323a62b615Svisa 	"	cache 1, 0($0)\n"	/* Flush and invalidate dcache. */
3333a62b615Svisa 	"	cache 0, 0($0)\n"	/* Invalidate icache. */
3343a62b615Svisa 	::: "memory");
3353a62b615Svisa 
3363a62b615Svisa 	(*(kentry)eh.e_entry)(0, 0, PRIMARY, (register_t)octeon_boot_desc);
3373a62b615Svisa 
3383a62b615Svisa 	for (;;)
3393a62b615Svisa 		continue;
3403a62b615Svisa 
3413a62b615Svisa fail:
3423a62b615Svisa 	if (ekern != 0)
3433a62b615Svisa 		bootmem_free(ekern, maxp - ekern);
3443a62b615Svisa 	for (i = 0; i < eh.e_phnum && nalloc > 0; i++) {
3453a62b615Svisa 		if (ph[i].p_type == PT_LOAD) {
3463a62b615Svisa 			pa = CKSEG0_TO_PHYS(ph[i].p_paddr);
3473a62b615Svisa 			bootmem_free(pa, ph[i].p_memsz);
3483a62b615Svisa 			nalloc--;
3493a62b615Svisa 		}
3503a62b615Svisa 	}
3513a62b615Svisa 	free(shstr, M_TEMP, shstrsize);
3523a62b615Svisa 	free(sh, M_TEMP, shsize);
3533a62b615Svisa 	free(ph, M_TEMP, phsize);
3543a62b615Svisa 	free(argbuf, M_TEMP, PAGE_SIZE);
3553a62b615Svisa 	return error;
3563a62b615Svisa }
3573a62b615Svisa 
3583a62b615Svisa int
octboot_read(struct octboot_kexec_args * kargs,void * buf,size_t size,off_t off)359cf939257Svisa octboot_read(struct octboot_kexec_args *kargs, void *buf, size_t size,
3603a62b615Svisa     off_t off)
3613a62b615Svisa {
362cf939257Svisa 	if (off + size < off || off + size > kargs->klen)
3633a62b615Svisa 		return ENOEXEC;
364cf939257Svisa 	return copyin(kargs->kimg + off, buf, size);
3653a62b615Svisa }
366