1*210f3d83Skettenis /* $OpenBSD: kexec.c,v 1.1 2020/07/16 19:37:58 kettenis Exp $ */ 2*210f3d83Skettenis 3*210f3d83Skettenis /* 4*210f3d83Skettenis * Copyright (c) 2019-2020 Visa Hankala 5*210f3d83Skettenis * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org> 6*210f3d83Skettenis * 7*210f3d83Skettenis * Permission to use, copy, modify, and/or distribute this software for any 8*210f3d83Skettenis * purpose with or without fee is hereby granted, provided that the above 9*210f3d83Skettenis * copyright notice and this permission notice appear in all copies. 10*210f3d83Skettenis * 11*210f3d83Skettenis * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12*210f3d83Skettenis * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13*210f3d83Skettenis * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14*210f3d83Skettenis * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15*210f3d83Skettenis * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16*210f3d83Skettenis * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17*210f3d83Skettenis * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18*210f3d83Skettenis */ 19*210f3d83Skettenis 20*210f3d83Skettenis #include <sys/param.h> 21*210f3d83Skettenis #include <sys/systm.h> 22*210f3d83Skettenis #include <sys/exec_elf.h> 23*210f3d83Skettenis #include <sys/malloc.h> 24*210f3d83Skettenis #include <sys/proc.h> 25*210f3d83Skettenis 26*210f3d83Skettenis #include <uvm/uvm_extern.h> 27*210f3d83Skettenis 28*210f3d83Skettenis #include <machine/kexec.h> 29*210f3d83Skettenis #include <machine/opal.h> 30*210f3d83Skettenis 31*210f3d83Skettenis int kexec_kexec(struct kexec_args *, struct proc *); 32*210f3d83Skettenis int kexec_read(struct kexec_args *, void *, size_t, off_t); 33*210f3d83Skettenis void kexec(paddr_t, paddr_t); 34*210f3d83Skettenis 35*210f3d83Skettenis void 36*210f3d83Skettenis kexecattach(int num) 37*210f3d83Skettenis { 38*210f3d83Skettenis } 39*210f3d83Skettenis 40*210f3d83Skettenis int 41*210f3d83Skettenis kexecopen(dev_t dev, int flags, int mode, struct proc *p) 42*210f3d83Skettenis { 43*210f3d83Skettenis return (0); 44*210f3d83Skettenis } 45*210f3d83Skettenis 46*210f3d83Skettenis int 47*210f3d83Skettenis kexecclose(dev_t dev, int flags, int mode, struct proc *p) 48*210f3d83Skettenis { 49*210f3d83Skettenis return (0); 50*210f3d83Skettenis } 51*210f3d83Skettenis 52*210f3d83Skettenis int 53*210f3d83Skettenis kexecioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p) 54*210f3d83Skettenis { 55*210f3d83Skettenis int error = 0; 56*210f3d83Skettenis 57*210f3d83Skettenis switch (cmd) { 58*210f3d83Skettenis case KIOC_KEXEC: 59*210f3d83Skettenis error = suser(p); 60*210f3d83Skettenis if (error != 0) 61*210f3d83Skettenis break; 62*210f3d83Skettenis error = kexec_kexec((struct kexec_args *)data, p); 63*210f3d83Skettenis break; 64*210f3d83Skettenis 65*210f3d83Skettenis case KIOC_GETBOOTDUID: 66*210f3d83Skettenis memcpy(data, bootduid, sizeof(bootduid)); 67*210f3d83Skettenis break; 68*210f3d83Skettenis 69*210f3d83Skettenis default: 70*210f3d83Skettenis error = ENOTTY; 71*210f3d83Skettenis break; 72*210f3d83Skettenis } 73*210f3d83Skettenis 74*210f3d83Skettenis return error; 75*210f3d83Skettenis } 76*210f3d83Skettenis 77*210f3d83Skettenis int 78*210f3d83Skettenis kexec_kexec(struct kexec_args *kargs, struct proc *p) 79*210f3d83Skettenis { 80*210f3d83Skettenis extern paddr_t fdt_pa; 81*210f3d83Skettenis struct kmem_pa_mode kp_kexec = { 82*210f3d83Skettenis .kp_constraint = &no_constraint, 83*210f3d83Skettenis .kp_boundary = SEGMENT_SIZE, 84*210f3d83Skettenis .kp_maxseg = 1, 85*210f3d83Skettenis .kp_zero = 1 86*210f3d83Skettenis }; 87*210f3d83Skettenis Elf_Ehdr eh; 88*210f3d83Skettenis Elf_Phdr *ph = NULL; 89*210f3d83Skettenis Elf_Shdr *sh = NULL; 90*210f3d83Skettenis vaddr_t start = VM_MAX_ADDRESS; 91*210f3d83Skettenis vaddr_t end = 0; 92*210f3d83Skettenis paddr_t start_pa; 93*210f3d83Skettenis vsize_t align = 0;; 94*210f3d83Skettenis caddr_t addr; 95*210f3d83Skettenis size_t phsize, shsize, size; 96*210f3d83Skettenis int error, i; 97*210f3d83Skettenis 98*210f3d83Skettenis /* 99*210f3d83Skettenis * Read the headers and validate them. 100*210f3d83Skettenis */ 101*210f3d83Skettenis error = kexec_read(kargs, &eh, sizeof(eh), 0); 102*210f3d83Skettenis if (error != 0) 103*210f3d83Skettenis goto fail; 104*210f3d83Skettenis 105*210f3d83Skettenis /* Load program headers. */ 106*210f3d83Skettenis ph = mallocarray(eh.e_phnum, sizeof(Elf_Phdr), M_TEMP, M_NOWAIT); 107*210f3d83Skettenis if (ph == NULL) { 108*210f3d83Skettenis error = ENOMEM; 109*210f3d83Skettenis goto fail; 110*210f3d83Skettenis } 111*210f3d83Skettenis phsize = eh.e_phnum * sizeof(Elf_Phdr); 112*210f3d83Skettenis error = kexec_read(kargs, ph, phsize, eh.e_phoff); 113*210f3d83Skettenis if (error != 0) 114*210f3d83Skettenis goto fail; 115*210f3d83Skettenis 116*210f3d83Skettenis /* Load section headers. */ 117*210f3d83Skettenis sh = mallocarray(eh.e_shnum, sizeof(Elf_Shdr), M_TEMP, M_NOWAIT); 118*210f3d83Skettenis if (sh == NULL) { 119*210f3d83Skettenis error = ENOMEM; 120*210f3d83Skettenis goto fail; 121*210f3d83Skettenis } 122*210f3d83Skettenis shsize = eh.e_shnum * sizeof(Elf_Shdr); 123*210f3d83Skettenis error = kexec_read(kargs, sh, shsize, eh.e_shoff); 124*210f3d83Skettenis if (error != 0) 125*210f3d83Skettenis goto fail; 126*210f3d83Skettenis 127*210f3d83Skettenis /* 128*210f3d83Skettenis * Allocate physical memory and load the segments. 129*210f3d83Skettenis */ 130*210f3d83Skettenis 131*210f3d83Skettenis for (i = 0; i < eh.e_phnum; i++) { 132*210f3d83Skettenis if (ph[i].p_type != PT_LOAD) 133*210f3d83Skettenis continue; 134*210f3d83Skettenis start = MIN(start, ph[i].p_vaddr); 135*210f3d83Skettenis align = MAX(align, ph[i].p_align); 136*210f3d83Skettenis end = MAX(end, ph[i].p_vaddr + ph[i].p_memsz); 137*210f3d83Skettenis } 138*210f3d83Skettenis size = round_page(end) - start; 139*210f3d83Skettenis 140*210f3d83Skettenis kp_kexec.kp_align = align; 141*210f3d83Skettenis addr = km_alloc(size, &kv_any, &kp_kexec, &kd_nowait); 142*210f3d83Skettenis if (addr == NULL) { 143*210f3d83Skettenis error = ENOMEM; 144*210f3d83Skettenis goto fail; 145*210f3d83Skettenis } 146*210f3d83Skettenis 147*210f3d83Skettenis for (i = 0; i < eh.e_phnum; i++) { 148*210f3d83Skettenis if (ph[i].p_type != PT_LOAD) 149*210f3d83Skettenis continue; 150*210f3d83Skettenis 151*210f3d83Skettenis error = kexec_read(kargs, addr + (ph[i].p_vaddr - start), 152*210f3d83Skettenis ph[i].p_filesz, ph[i].p_offset); 153*210f3d83Skettenis if (error != 0) 154*210f3d83Skettenis goto fail; 155*210f3d83Skettenis 156*210f3d83Skettenis /* Clear any BSS. */ 157*210f3d83Skettenis if (ph[i].p_memsz > ph[i].p_filesz) { 158*210f3d83Skettenis memset(addr + (ph[i].p_vaddr + ph[i].p_filesz) - start, 159*210f3d83Skettenis 0, ph[i].p_memsz - ph[i].p_filesz); 160*210f3d83Skettenis } 161*210f3d83Skettenis } 162*210f3d83Skettenis 163*210f3d83Skettenis for (i = 0; i < eh.e_phnum; i++) { 164*210f3d83Skettenis if (ph[i].p_type != PT_OPENBSD_RANDOMIZE) 165*210f3d83Skettenis continue; 166*210f3d83Skettenis 167*210f3d83Skettenis /* Assume that the segment is inside a LOAD segment. */ 168*210f3d83Skettenis arc4random_buf(addr + ph[i].p_vaddr - start, ph[i].p_filesz); 169*210f3d83Skettenis } 170*210f3d83Skettenis 171*210f3d83Skettenis printf("launching kernel\n"); 172*210f3d83Skettenis 173*210f3d83Skettenis config_suspend_all(DVACT_POWERDOWN); 174*210f3d83Skettenis 175*210f3d83Skettenis intr_disable(); 176*210f3d83Skettenis 177*210f3d83Skettenis pmap_extract(pmap_kernel(), (vaddr_t)addr, &start_pa); 178*210f3d83Skettenis kexec(start_pa + (eh.e_entry - start), fdt_pa); 179*210f3d83Skettenis 180*210f3d83Skettenis for (;;) 181*210f3d83Skettenis continue; 182*210f3d83Skettenis 183*210f3d83Skettenis fail: 184*210f3d83Skettenis free(sh, M_TEMP, shsize); 185*210f3d83Skettenis free(ph, M_TEMP, phsize); 186*210f3d83Skettenis return error; 187*210f3d83Skettenis } 188*210f3d83Skettenis 189*210f3d83Skettenis int 190*210f3d83Skettenis kexec_read(struct kexec_args *kargs, void *buf, size_t size, off_t off) 191*210f3d83Skettenis { 192*210f3d83Skettenis if (off + size < off || off + size > kargs->klen) 193*210f3d83Skettenis return ENOEXEC; 194*210f3d83Skettenis return copyin(kargs->kimg + off, buf, size); 195*210f3d83Skettenis } 196