1 /* $OpenBSD: octboot.c,v 1.1 2019/07/17 14:36:32 visa Exp $ */ 2 3 /* 4 * Copyright (c) 2019 Visa Hankala 5 * 6 * Permission to use, copy, modify, and/or distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <sys/param.h> 20 #include <sys/systm.h> 21 #include <sys/exec_elf.h> 22 #include <sys/malloc.h> 23 #include <sys/namei.h> 24 #include <sys/proc.h> 25 #include <sys/vnode.h> 26 27 #include <uvm/uvm_extern.h> 28 29 #include <mips64/memconf.h> 30 31 #include <machine/autoconf.h> 32 #include <machine/octboot.h> 33 #include <machine/octeonvar.h> 34 35 typedef void (*kentry)(register_t, register_t, register_t, register_t); 36 #define PRIMARY 1 37 38 int octboot_kexec(struct octboot_kexec_args *, struct proc *); 39 int octboot_read(struct proc *, struct vnode *, void *, size_t, off_t); 40 41 uint64_t octeon_boot_entry; 42 uint32_t octeon_boot_ready; 43 44 void 45 octbootattach(int num) 46 { 47 } 48 49 int 50 octbootopen(dev_t dev, int flags, int mode, struct proc *p) 51 { 52 return (0); 53 } 54 55 int 56 octbootclose(dev_t dev, int flags, int mode, struct proc *p) 57 { 58 return (0); 59 } 60 61 int 62 octbootioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p) 63 { 64 int error = 0; 65 66 switch (cmd) { 67 case OBIOC_GETROOTDEV: 68 if (strlen(uboot_rootdev) == 0) { 69 error = ENOENT; 70 break; 71 } 72 strlcpy((char *)data, uboot_rootdev, PATH_MAX); 73 break; 74 75 case OBIOC_KEXEC: 76 error = suser(p); 77 if (error != 0) 78 break; 79 error = octboot_kexec((struct octboot_kexec_args *)data, p); 80 break; 81 82 default: 83 error = ENOTTY; 84 break; 85 } 86 87 return error; 88 } 89 90 int 91 octboot_kexec(struct octboot_kexec_args *kargs, struct proc *p) 92 { 93 extern char start[], end[]; 94 Elf_Ehdr eh; 95 Elf_Phdr *ph = NULL; 96 Elf_Shdr *sh = NULL; 97 struct nameidata nid; 98 struct vattr va; 99 paddr_t ekern = 0, elfp, maxp = 0, off, pa, shp; 100 size_t len, phsize, shsize, shstrsize, size; 101 char *argbuf = NULL, *argptr; 102 char *shstr = NULL; 103 int argc = 0, error, havesyms = 0, i, nalloc = 0; 104 105 NDINIT(&nid, LOOKUP, FOLLOW | LOCKLEAF, UIO_USERSPACE, kargs->path, p); 106 error = namei(&nid); 107 if (error != 0) 108 return error; 109 error = VOP_GETATTR(nid.ni_vp, &va, p->p_ucred, p); 110 if (error != 0) 111 goto fail; 112 if (nid.ni_vp->v_type != VREG || va.va_size == 0) { 113 error = EINVAL; 114 goto fail; 115 } 116 error = VOP_ACCESS(nid.ni_vp, VREAD, p->p_ucred, p); 117 if (error != 0) 118 goto fail; 119 120 /* 121 * Load kernel arguments into a temporary buffer. 122 * This also translates the userspace argv pointers to kernel pointers. 123 */ 124 argbuf = malloc(PAGE_SIZE, M_TEMP, M_NOWAIT); 125 if (argbuf == NULL) { 126 error = ENOMEM; 127 goto fail; 128 } 129 argptr = argbuf; 130 for (i = 0; i < OCTBOOT_MAX_ARGS && kargs->argv[i] != NULL; i++) { 131 len = argbuf + PAGE_SIZE - argptr; 132 error = copyinstr(kargs->argv[i], argptr, len, &len); 133 if (error != 0) 134 goto fail; 135 kargs->argv[i] = argptr; 136 argptr += len; 137 argc++; 138 } 139 140 /* 141 * Read the headers and validate them. 142 */ 143 error = octboot_read(p, nid.ni_vp, &eh, sizeof(eh), 0); 144 if (error != 0) 145 goto fail; 146 147 /* Load program headers. */ 148 ph = mallocarray(eh.e_phnum, sizeof(Elf_Phdr), M_TEMP, M_NOWAIT); 149 if (ph == NULL) { 150 error = ENOMEM; 151 goto fail; 152 } 153 phsize = eh.e_phnum * sizeof(Elf_Phdr); 154 error = octboot_read(p, nid.ni_vp, ph, phsize, eh.e_phoff); 155 if (error != 0) 156 goto fail; 157 158 /* Load section headers. */ 159 sh = mallocarray(eh.e_shnum, sizeof(Elf_Shdr), M_TEMP, M_NOWAIT); 160 if (sh == NULL) { 161 error = ENOMEM; 162 goto fail; 163 } 164 shsize = eh.e_shnum * sizeof(Elf_Shdr); 165 error = octboot_read(p, nid.ni_vp, sh, shsize, eh.e_shoff); 166 if (error != 0) 167 goto fail; 168 169 /* Sanity-check addresses. */ 170 for (i = 0; i < eh.e_phnum; i++) { 171 if (ph[i].p_type != PT_LOAD && 172 ph[i].p_type != PT_OPENBSD_RANDOMIZE) 173 continue; 174 if (ph[i].p_paddr < CKSEG0_BASE || 175 ph[i].p_paddr + ph[i].p_memsz >= CKSEG0_BASE + CKSEG_SIZE) { 176 error = ENOEXEC; 177 goto fail; 178 } 179 } 180 181 /* 182 * Allocate physical memory and load the segments. 183 */ 184 185 for (i = 0; i < eh.e_phnum; i++) { 186 if (ph[i].p_type != PT_LOAD) 187 continue; 188 pa = CKSEG0_TO_PHYS(ph[i].p_paddr); 189 size = roundup(ph[i].p_memsz, BOOTMEM_BLOCK_ALIGN); 190 if (bootmem_alloc_region(pa, size) != 0) { 191 printf("kexec: failed to allocate segment " 192 "0x%lx @ 0x%lx\n", size, pa); 193 error = ENOMEM; 194 goto fail; 195 } 196 if (maxp < pa + size) 197 maxp = pa + size; 198 nalloc++; 199 } 200 201 for (i = 0; i < eh.e_phnum; i++) { 202 if (ph[i].p_type == PT_OPENBSD_RANDOMIZE) { 203 /* Assume that the segment is inside a LOAD segment. */ 204 arc4random_buf((caddr_t)ph[i].p_paddr, ph[i].p_filesz); 205 continue; 206 } 207 208 if (ph[i].p_type != PT_LOAD) 209 continue; 210 211 error = octboot_read(p, nid.ni_vp, (caddr_t)ph[i].p_paddr, 212 ph[i].p_filesz, ph[i].p_offset); 213 if (error != 0) 214 goto fail; 215 216 /* Clear any BSS. */ 217 if (ph[i].p_memsz > ph[i].p_filesz) { 218 memset((caddr_t)ph[i].p_paddr + ph[i].p_filesz, 219 0, ph[i].p_memsz - ph[i].p_filesz); 220 } 221 } 222 ekern = maxp; 223 224 for (i = 0; i < eh.e_shnum; i++) { 225 if (sh[i].sh_type == SHT_SYMTAB) { 226 havesyms = 1; 227 break; 228 } 229 } 230 231 if (havesyms) { 232 /* Reserve space for ssym and esym pointers. */ 233 maxp += sizeof(int32_t) * 2; 234 235 elfp = roundup(maxp, sizeof(Elf_Addr)); 236 maxp = elfp + sizeof(Elf_Ehdr); 237 shp = maxp; 238 maxp = shp + roundup(shsize, sizeof(Elf_Addr)); 239 maxp = roundup(maxp, BOOTMEM_BLOCK_ALIGN); 240 if (bootmem_alloc_region(ekern, maxp - ekern) != 0) { 241 printf("kexec: failed to allocate %zu bytes for ELF " 242 "and section headers\n", maxp - ekern); 243 error = ENOMEM; 244 goto fail; 245 } 246 247 shstrsize = sh[eh.e_shstrndx].sh_size; 248 shstr = malloc(shstrsize, M_TEMP, M_NOWAIT); 249 if (shstr == NULL) { 250 error = ENOMEM; 251 goto fail; 252 } 253 error = octboot_read(p, nid.ni_vp, shstr, shstrsize, 254 sh[eh.e_shstrndx].sh_offset); 255 if (error != 0) 256 goto fail; 257 258 off = roundup(sizeof(Elf_Ehdr) + shsize, sizeof(Elf_Addr)); 259 off = maxp - elfp; 260 for (i = 0; i < eh.e_shnum; i++) { 261 if (sh[i].sh_type == SHT_STRTAB || 262 sh[i].sh_type == SHT_SYMTAB || 263 strcmp(shstr + sh[i].sh_name, ELF_CTF) == 0 || 264 strcmp(shstr + sh[i].sh_name, ".debug_line") == 0) { 265 size_t bsize = roundup(sh[i].sh_size, 266 BOOTMEM_BLOCK_ALIGN); 267 268 if (bootmem_alloc_region(maxp, bsize) != 0) { 269 error = ENOMEM; 270 goto fail; 271 } 272 error = octboot_read(p, nid.ni_vp, 273 (caddr_t)PHYS_TO_CKSEG0(maxp), 274 sh[i].sh_size, sh[i].sh_offset); 275 maxp += bsize; 276 if (error != 0) 277 goto fail; 278 sh[i].sh_offset = off; 279 sh[i].sh_flags |= SHF_ALLOC; 280 off += bsize; 281 } 282 } 283 284 eh.e_phoff = 0; 285 eh.e_shoff = sizeof(eh); 286 eh.e_phentsize = 0; 287 eh.e_phnum = 0; 288 memcpy((caddr_t)PHYS_TO_CKSEG0(elfp), &eh, sizeof(eh)); 289 memcpy((caddr_t)PHYS_TO_CKSEG0(shp), sh, shsize); 290 291 *(int32_t *)PHYS_TO_CKSEG0(ekern) = PHYS_TO_CKSEG0(elfp); 292 *((int32_t *)PHYS_TO_CKSEG0(ekern) + 1) = PHYS_TO_CKSEG0(maxp); 293 } 294 295 /* 296 * Put kernel arguments in place. 297 */ 298 octeon_boot_desc->argc = 0; 299 for (i = 0; i < OCTEON_ARGV_MAX; i++) 300 octeon_boot_desc->argv[i] = 0; 301 if (argptr > argbuf) { 302 size = roundup(argptr - argbuf, BOOTMEM_BLOCK_ALIGN); 303 if (bootmem_alloc_region(maxp, size) != 0) { 304 error = ENOMEM; 305 goto fail; 306 } 307 memcpy((caddr_t)PHYS_TO_CKSEG0(maxp), argbuf, argptr - argbuf); 308 for (i = 0; i < argc; i++) { 309 KASSERT(kargs->argv[i] >= argbuf); 310 KASSERT(kargs->argv[i] < argbuf + PAGE_SIZE); 311 octeon_boot_desc->argv[i] = kargs->argv[i] - argbuf + 312 maxp; 313 } 314 octeon_boot_desc->argc = argc; 315 maxp += size; 316 } 317 318 printf("launching kernel\n"); 319 320 config_suspend_all(DVACT_POWERDOWN); 321 322 intr_disable(); 323 324 /* Put UVM memory back to the free list. */ 325 for (i = 0; mem_layout[i].mem_last_page != 0; i++) { 326 uint64_t fp = mem_layout[i].mem_first_page; 327 uint64_t lp = mem_layout[i].mem_last_page; 328 329 bootmem_free(ptoa(fp), ptoa(lp) - ptoa(fp)); 330 } 331 332 /* 333 * Release the memory of the bootloader kernel. 334 * This may overwrite a tiny region at the start of the running image. 335 */ 336 bootmem_free(CKSEG0_TO_PHYS((vaddr_t)start), end - start); 337 338 /* Let secondary cores proceed to the new kernel. */ 339 octeon_boot_entry = eh.e_entry; 340 octeon_syncw(); /* Order writes. */ 341 octeon_boot_ready = 1; /* Open the gate. */ 342 octeon_syncw(); /* Flush writes. */ 343 delay(1000); /* Give secondary cores a lead. */ 344 345 __asm__ volatile ( 346 " cache 1, 0($0)\n" /* Flush and invalidate dcache. */ 347 " cache 0, 0($0)\n" /* Invalidate icache. */ 348 ::: "memory"); 349 350 (*(kentry)eh.e_entry)(0, 0, PRIMARY, (register_t)octeon_boot_desc); 351 352 for (;;) 353 continue; 354 355 fail: 356 if (ekern != 0) 357 bootmem_free(ekern, maxp - ekern); 358 for (i = 0; i < eh.e_phnum && nalloc > 0; i++) { 359 if (ph[i].p_type == PT_LOAD) { 360 pa = CKSEG0_TO_PHYS(ph[i].p_paddr); 361 bootmem_free(pa, ph[i].p_memsz); 362 nalloc--; 363 } 364 } 365 free(shstr, M_TEMP, shstrsize); 366 free(sh, M_TEMP, shsize); 367 free(ph, M_TEMP, phsize); 368 free(argbuf, M_TEMP, PAGE_SIZE); 369 vput(nid.ni_vp); 370 return error; 371 } 372 373 int 374 octboot_read(struct proc *p, struct vnode *vp, void *buf, size_t size, 375 off_t off) 376 { 377 size_t resid; 378 int error; 379 380 error = vn_rdwr(UIO_READ, vp, buf, size, off, UIO_SYSSPACE, 0, 381 p->p_ucred, &resid, p); 382 if (error != 0) 383 return error; 384 if (resid != 0) 385 return ENOEXEC; 386 return 0; 387 } 388