1 /* $OpenBSD: kexec.c,v 1.7 2022/02/22 13:34:23 visa 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/mount.h> 26 #include <sys/reboot.h> 27 28 #include <uvm/uvm_extern.h> 29 30 #include <machine/kexec.h> 31 #include <machine/opal.h> 32 33 #include <dev/ofw/fdt.h> 34 35 int kexec_kexec(struct kexec_args *, struct proc *); 36 int kexec_read(struct kexec_args *, void *, size_t, off_t); 37 void kexec(paddr_t, paddr_t); 38 39 void 40 kexecattach(int num) 41 { 42 } 43 44 int 45 kexecopen(dev_t dev, int flags, int mode, struct proc *p) 46 { 47 return (0); 48 } 49 50 int 51 kexecclose(dev_t dev, int flags, int mode, struct proc *p) 52 { 53 return (0); 54 } 55 56 int 57 kexecioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p) 58 { 59 int error = 0; 60 61 switch (cmd) { 62 case KIOC_KEXEC: 63 error = suser(p); 64 if (error != 0) 65 break; 66 error = kexec_kexec((struct kexec_args *)data, p); 67 break; 68 69 case KIOC_GETBOOTDUID: 70 memcpy(data, bootduid, sizeof(bootduid)); 71 break; 72 73 default: 74 error = ENOTTY; 75 break; 76 } 77 78 return error; 79 } 80 81 int 82 kexec_kexec(struct kexec_args *kargs, struct proc *p) 83 { 84 extern paddr_t fdt_pa; 85 struct kmem_pa_mode kp_kexec = { 86 .kp_constraint = &no_constraint, 87 .kp_boundary = SEGMENT_SIZE, 88 .kp_maxseg = 1, 89 .kp_zero = 1 90 }; 91 Elf_Ehdr eh; 92 Elf_Phdr *ph = NULL; 93 Elf_Shdr *sh = NULL, *shp; 94 vaddr_t start = VM_MAX_ADDRESS; 95 vaddr_t end = 0; 96 paddr_t start_pa, initrd_pa; 97 vsize_t align = 0; 98 caddr_t addr = NULL; 99 caddr_t symaddr = NULL; 100 size_t phsize, shsize, size, symsize; 101 char *shstr; 102 void *node; 103 int error, random, i; 104 105 /* 106 * Read the headers and validate them. 107 */ 108 error = kexec_read(kargs, &eh, sizeof(eh), 0); 109 if (error != 0) 110 goto fail; 111 112 /* Load program headers. */ 113 ph = mallocarray(eh.e_phnum, sizeof(Elf_Phdr), M_TEMP, M_NOWAIT); 114 if (ph == NULL) { 115 error = ENOMEM; 116 goto fail; 117 } 118 phsize = eh.e_phnum * sizeof(Elf_Phdr); 119 error = kexec_read(kargs, ph, phsize, eh.e_phoff); 120 if (error != 0) 121 goto fail; 122 123 /* Load section headers. */ 124 sh = mallocarray(eh.e_shnum, sizeof(Elf_Shdr), M_TEMP, M_NOWAIT); 125 if (sh == NULL) { 126 error = ENOMEM; 127 goto fail; 128 } 129 shsize = eh.e_shnum * sizeof(Elf_Shdr); 130 error = kexec_read(kargs, sh, shsize, eh.e_shoff); 131 if (error != 0) 132 goto fail; 133 134 /* 135 * Allocate physical memory and load the segments. 136 */ 137 138 for (i = 0; i < eh.e_phnum; i++) { 139 if (ph[i].p_type != PT_LOAD) 140 continue; 141 start = MIN(start, ph[i].p_vaddr); 142 align = MAX(align, ph[i].p_align); 143 end = MAX(end, ph[i].p_vaddr + ph[i].p_memsz); 144 } 145 size = round_page(end) - start; 146 147 kp_kexec.kp_align = align; 148 addr = km_alloc(size, &kv_any, &kp_kexec, &kd_nowait); 149 if (addr == NULL) { 150 error = ENOMEM; 151 goto fail; 152 } 153 154 for (i = 0; i < eh.e_phnum; i++) { 155 if (ph[i].p_type != PT_LOAD) 156 continue; 157 158 error = kexec_read(kargs, addr + (ph[i].p_vaddr - start), 159 ph[i].p_filesz, ph[i].p_offset); 160 if (error != 0) 161 goto fail; 162 163 /* Clear any BSS. */ 164 if (ph[i].p_memsz > ph[i].p_filesz) { 165 memset(addr + (ph[i].p_vaddr + ph[i].p_filesz) - start, 166 0, ph[i].p_memsz - ph[i].p_filesz); 167 } 168 } 169 170 random = 0; 171 for (i = 0; i < eh.e_phnum; i++) { 172 if (ph[i].p_type != PT_OPENBSD_RANDOMIZE) 173 continue; 174 175 /* Assume that the segment is inside a LOAD segment. */ 176 arc4random_buf(addr + ph[i].p_vaddr - start, ph[i].p_filesz); 177 random = 1; 178 } 179 180 if (random == 0) 181 kargs->boothowto &= ~RB_GOODRANDOM; 182 183 symsize = round_page(kargs->klen); 184 symaddr = km_alloc(symsize, &kv_any, &kp_kexec, &kd_nowait); 185 if (symaddr == NULL) { 186 error = ENOMEM; 187 goto fail; 188 } 189 190 error = kexec_read(kargs, symaddr, kargs->klen, 0); 191 if (error != 0) 192 goto fail; 193 194 vfs_shutdown(p); 195 196 shp = (Elf64_Shdr *)(symaddr + eh.e_shoff); 197 shstr = symaddr + shp[eh.e_shstrndx].sh_offset; 198 for (i = 0; i < eh.e_shnum; i++) { 199 if (shp[i].sh_type == SHT_SYMTAB || 200 shp[i].sh_type == SHT_STRTAB || 201 strcmp(shstr + shp[i].sh_name, ".debug_line") == 0 || 202 strcmp(shstr + shp[i].sh_name, ELF_CTF) == 0) 203 if (shp[i].sh_offset + shp[i].sh_size <= symsize) 204 shp[i].sh_flags |= SHF_ALLOC; 205 } 206 207 pmap_extract(pmap_kernel(), (vaddr_t)symaddr, &initrd_pa); 208 209 node = fdt_find_node("/chosen"); 210 if (node) { 211 uint32_t boothowto = htobe32(kargs->boothowto); 212 uint64_t initrd_start = htobe64(initrd_pa); 213 uint64_t initrd_end = htobe64(initrd_pa + kargs->klen); 214 215 fdt_node_add_property(node, "openbsd,boothowto", 216 &boothowto, sizeof(boothowto)); 217 fdt_node_add_property(node, "openbsd,bootduid", 218 kargs->bootduid, sizeof(kargs->bootduid)); 219 220 fdt_node_set_property(node, "linux,initrd-start", 221 &initrd_start, sizeof(initrd_start)); 222 fdt_node_set_property(node, "linux,initrd-end", 223 &initrd_end, sizeof(initrd_end)); 224 225 fdt_finalize(); 226 } 227 228 printf("launching kernel\n"); 229 230 config_suspend_all(DVACT_POWERDOWN); 231 232 intr_disable(); 233 234 pmap_extract(pmap_kernel(), (vaddr_t)addr, &start_pa); 235 kexec(start_pa + (eh.e_entry - start), fdt_pa); 236 237 for (;;) 238 continue; 239 240 fail: 241 if (symaddr) 242 km_free(symaddr, symsize, &kv_any, &kp_kexec); 243 if (addr) 244 km_free(addr, size, &kv_any, &kp_kexec); 245 if (sh) 246 free(sh, M_TEMP, shsize); 247 if (ph) 248 free(ph, M_TEMP, phsize); 249 return error; 250 } 251 252 int 253 kexec_read(struct kexec_args *kargs, void *buf, size_t size, off_t off) 254 { 255 if (off + size < off || off + size > kargs->klen) 256 return ENOEXEC; 257 return copyin(kargs->kimg + off, buf, size); 258 } 259