summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkettenis <kettenis@openbsd.org>2020-07-16 19:37:58 +0000
committerkettenis <kettenis@openbsd.org>2020-07-16 19:37:58 +0000
commit210f3d83b12d46d6e8cd6875bd16d25160721947 (patch)
tree1b936086a8fb4cd23a58c13f52ce42440b602a3d
parentReset firmware state upon reboot. (diff)
downloadwireguard-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.powerpc646
-rw-r--r--sys/arch/powerpc64/dev/kexec.c195
-rw-r--r--sys/arch/powerpc64/dev/kexec_subr.S35
-rw-r--r--sys/arch/powerpc64/include/conf.h8
-rw-r--r--sys/arch/powerpc64/include/kexec.h36
-rw-r--r--sys/arch/powerpc64/powerpc64/conf.c7
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 */