diff options
author | 2020-07-16 19:37:58 +0000 | |
---|---|---|
committer | 2020-07-16 19:37:58 +0000 | |
commit | 210f3d83b12d46d6e8cd6875bd16d25160721947 (patch) | |
tree | 1b936086a8fb4cd23a58c13f52ce42440b602a3d | |
parent | Reset firmware state upon reboot. (diff) | |
download | wireguard-openbsd-210f3d83b12d46d6e8cd6875bd16d25160721947.tar.xz wireguard-openbsd-210f3d83b12d46d6e8cd6875bd16d25160721947.zip |
Add a pseudo-driver to "kexec" an OpenBSD/powerpc64 kernel. Heavily
based on the octboot driver that we use for octeon. To be used in the
bootloader kernel.
-rw-r--r-- | sys/arch/powerpc64/conf/files.powerpc64 | 6 | ||||
-rw-r--r-- | sys/arch/powerpc64/dev/kexec.c | 195 | ||||
-rw-r--r-- | sys/arch/powerpc64/dev/kexec_subr.S | 35 | ||||
-rw-r--r-- | sys/arch/powerpc64/include/conf.h | 8 | ||||
-rw-r--r-- | sys/arch/powerpc64/include/kexec.h | 36 | ||||
-rw-r--r-- | sys/arch/powerpc64/powerpc64/conf.c | 7 |
6 files changed, 283 insertions, 4 deletions
diff --git a/sys/arch/powerpc64/conf/files.powerpc64 b/sys/arch/powerpc64/conf/files.powerpc64 index f3c4518717d..68af464f2e1 100644 --- a/sys/arch/powerpc64/conf/files.powerpc64 +++ b/sys/arch/powerpc64/conf/files.powerpc64 @@ -1,4 +1,4 @@ -# $OpenBSD: files.powerpc64,v 1.21 2020/07/14 20:39:40 kettenis Exp $ +# $OpenBSD: files.powerpc64,v 1.22 2020/07/16 19:37:58 kettenis Exp $ maxpartitions 16 maxusers 2 8 128 @@ -38,6 +38,10 @@ file arch/powerpc64/dev/pci_machdep.c file netinet/in_cksum.c file netinet/in4_cksum.c +pseudo-device kexec +file arch/powerpc64/dev/kexec.c kexec needs-flag +file arch/powerpc64/dev/kexec_subr.S kexec needs-flag + pseudo-device openprom file arch/powerpc64/powerpc64/openprom.c openprom needs-flag diff --git a/sys/arch/powerpc64/dev/kexec.c b/sys/arch/powerpc64/dev/kexec.c new file mode 100644 index 00000000000..01c881603a7 --- /dev/null +++ b/sys/arch/powerpc64/dev/kexec.c @@ -0,0 +1,195 @@ +/* $OpenBSD: kexec.c,v 1.1 2020/07/16 19:37:58 kettenis Exp $ */ + +/* + * Copyright (c) 2019-2020 Visa Hankala + * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/exec_elf.h> +#include <sys/malloc.h> +#include <sys/proc.h> + +#include <uvm/uvm_extern.h> + +#include <machine/kexec.h> +#include <machine/opal.h> + +int kexec_kexec(struct kexec_args *, struct proc *); +int kexec_read(struct kexec_args *, void *, size_t, off_t); +void kexec(paddr_t, paddr_t); + +void +kexecattach(int num) +{ +} + +int +kexecopen(dev_t dev, int flags, int mode, struct proc *p) +{ + return (0); +} + +int +kexecclose(dev_t dev, int flags, int mode, struct proc *p) +{ + return (0); +} + +int +kexecioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p) +{ + int error = 0; + + switch (cmd) { + case KIOC_KEXEC: + error = suser(p); + if (error != 0) + break; + error = kexec_kexec((struct kexec_args *)data, p); + break; + + case KIOC_GETBOOTDUID: + memcpy(data, bootduid, sizeof(bootduid)); + break; + + default: + error = ENOTTY; + break; + } + + return error; +} + +int +kexec_kexec(struct kexec_args *kargs, struct proc *p) +{ + extern paddr_t fdt_pa; + struct kmem_pa_mode kp_kexec = { + .kp_constraint = &no_constraint, + .kp_boundary = SEGMENT_SIZE, + .kp_maxseg = 1, + .kp_zero = 1 + }; + Elf_Ehdr eh; + Elf_Phdr *ph = NULL; + Elf_Shdr *sh = NULL; + vaddr_t start = VM_MAX_ADDRESS; + vaddr_t end = 0; + paddr_t start_pa; + vsize_t align = 0;; + caddr_t addr; + size_t phsize, shsize, size; + int error, i; + + /* + * Read the headers and validate them. + */ + error = kexec_read(kargs, &eh, sizeof(eh), 0); + if (error != 0) + goto fail; + + /* Load program headers. */ + ph = mallocarray(eh.e_phnum, sizeof(Elf_Phdr), M_TEMP, M_NOWAIT); + if (ph == NULL) { + error = ENOMEM; + goto fail; + } + phsize = eh.e_phnum * sizeof(Elf_Phdr); + error = kexec_read(kargs, ph, phsize, eh.e_phoff); + if (error != 0) + goto fail; + + /* Load section headers. */ + sh = mallocarray(eh.e_shnum, sizeof(Elf_Shdr), M_TEMP, M_NOWAIT); + if (sh == NULL) { + error = ENOMEM; + goto fail; + } + shsize = eh.e_shnum * sizeof(Elf_Shdr); + error = kexec_read(kargs, sh, shsize, eh.e_shoff); + if (error != 0) + goto fail; + + /* + * Allocate physical memory and load the segments. + */ + + for (i = 0; i < eh.e_phnum; i++) { + if (ph[i].p_type != PT_LOAD) + continue; + start = MIN(start, ph[i].p_vaddr); + align = MAX(align, ph[i].p_align); + end = MAX(end, ph[i].p_vaddr + ph[i].p_memsz); + } + size = round_page(end) - start; + + kp_kexec.kp_align = align; + addr = km_alloc(size, &kv_any, &kp_kexec, &kd_nowait); + if (addr == NULL) { + error = ENOMEM; + goto fail; + } + + for (i = 0; i < eh.e_phnum; i++) { + if (ph[i].p_type != PT_LOAD) + continue; + + error = kexec_read(kargs, addr + (ph[i].p_vaddr - start), + ph[i].p_filesz, ph[i].p_offset); + if (error != 0) + goto fail; + + /* Clear any BSS. */ + if (ph[i].p_memsz > ph[i].p_filesz) { + memset(addr + (ph[i].p_vaddr + ph[i].p_filesz) - start, + 0, ph[i].p_memsz - ph[i].p_filesz); + } + } + + for (i = 0; i < eh.e_phnum; i++) { + if (ph[i].p_type != PT_OPENBSD_RANDOMIZE) + continue; + + /* Assume that the segment is inside a LOAD segment. */ + arc4random_buf(addr + ph[i].p_vaddr - start, ph[i].p_filesz); + } + + printf("launching kernel\n"); + + config_suspend_all(DVACT_POWERDOWN); + + intr_disable(); + + pmap_extract(pmap_kernel(), (vaddr_t)addr, &start_pa); + kexec(start_pa + (eh.e_entry - start), fdt_pa); + + for (;;) + continue; + +fail: + free(sh, M_TEMP, shsize); + free(ph, M_TEMP, phsize); + return error; +} + +int +kexec_read(struct kexec_args *kargs, void *buf, size_t size, off_t off) +{ + if (off + size < off || off + size > kargs->klen) + return ENOEXEC; + return copyin(kargs->kimg + off, buf, size); +} diff --git a/sys/arch/powerpc64/dev/kexec_subr.S b/sys/arch/powerpc64/dev/kexec_subr.S new file mode 100644 index 00000000000..2f604996da8 --- /dev/null +++ b/sys/arch/powerpc64/dev/kexec_subr.S @@ -0,0 +1,35 @@ +/* $OpenBSD: kexec_subr.S,v 1.1 2020/07/16 19:37:58 kettenis Exp $ */ + +/* + * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "assym.h" + +#include <machine/psl.h> + + .abiversion 2 + + .text + + .globl kexec +kexec: + mtctr %r3 + mr %r3, %r4 + mfmsr %r31 + andi. %r31, %r31, ~(PSL_DR|PSL_IR|PSL_ME|PSL_RI)@l + mtmsr %r31 + isync + bctr diff --git a/sys/arch/powerpc64/include/conf.h b/sys/arch/powerpc64/include/conf.h index 62f960b0155..28f660fe733 100644 --- a/sys/arch/powerpc64/include/conf.h +++ b/sys/arch/powerpc64/include/conf.h @@ -7,10 +7,18 @@ cdev_decl(mm); cdev_decl(opalcons); /* open, close, ioctl */ +#define cdev_kexec_init(c,n) { \ + dev_init(c,n,open), dev_init(c,n,close), (dev_type_read((*))) enodev, \ + (dev_type_write((*))) enodev, dev_init(c,n,ioctl), \ + (dev_type_stop((*))) nullop, 0, selfalse, \ + (dev_type_mmap((*))) enodev } + +/* open, close, ioctl */ #define cdev_openprom_init(c,n) { \ dev_init(c,n,open), dev_init(c,n,close), (dev_type_read((*))) enodev, \ (dev_type_write((*))) enodev, dev_init(c,n,ioctl), \ (dev_type_stop((*))) nullop, 0, selfalse, \ (dev_type_mmap((*))) enodev } +cdev_decl(kexec); cdev_decl(openprom); diff --git a/sys/arch/powerpc64/include/kexec.h b/sys/arch/powerpc64/include/kexec.h new file mode 100644 index 00000000000..ca018e00b0b --- /dev/null +++ b/sys/arch/powerpc64/include/kexec.h @@ -0,0 +1,36 @@ +/* $OpenBSD: kexec.h,v 1.1 2020/07/16 19:37:58 kettenis Exp $ */ + +/* + * Copyright (c) 2019-2020 Visa Hankala + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef _MACHINE_KEXEC_H_ +#define _MACHINE_KEXEC_H_ + +#include <sys/ioccom.h> + +#define KEXEC_MAX_ARGS 8 /* maximum number of boot arguments */ + +struct kexec_args { + char *kimg; /* kernel image buffer */ + size_t klen; /* size of kernel image */ + char *argv[KEXEC_MAX_ARGS]; + /* kernel boot arguments */ +}; + +#define KIOC_KEXEC _IOW('K', 1, struct kexec_args) +#define KIOC_GETBOOTDUID _IOW('K', 2, char[8]) + +#endif /* _MACHINE_KEXEC_H_ */ diff --git a/sys/arch/powerpc64/powerpc64/conf.c b/sys/arch/powerpc64/powerpc64/conf.c index 34021ad6798..0165105b500 100644 --- a/sys/arch/powerpc64/powerpc64/conf.c +++ b/sys/arch/powerpc64/powerpc64/conf.c @@ -1,4 +1,4 @@ -/* $OpenBSD: conf.c,v 1.8 2020/07/06 04:32:25 dlg Exp $ */ +/* $OpenBSD: conf.c,v 1.9 2020/07/16 19:37:58 kettenis Exp $ */ /*- * Copyright (c) 1991 The Regents of the University of California. @@ -67,8 +67,9 @@ cdev_decl(com); #include "hotplug.h" #include "ipmi.h" #include "kcov.h" -#include "ksyms.h" +#include "kexec.h" #include "kstat.h" +#include "ksyms.h" #include "lpt.h" cdev_decl(lpt); #include "midi.h" @@ -117,7 +118,7 @@ struct cdevsw cdevsw[] = cdev_dt_init(NDT,dt), /* 13: dynamic tracer */ cdev_kcov_init(NKCOV,kcov), /* 14: kcov */ cdev_kstat_init(NKSTAT,kstat), /* 15: kernel statistics */ - cdev_notdef(), /* 16 */ + cdev_kexec_init(NKEXEC,kexec), /* 16: kexec */ cdev_notdef(), /* 17 */ cdev_notdef(), /* 18 */ cdev_notdef(), /* 19 */ |