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