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