1 /* $NetBSD: zbsdmod.c,v 1.12 2019/11/13 17:59:56 tsutsui Exp $ */ 2 /* $OpenBSD: zbsdmod.c,v 1.7 2005/05/02 02:45:29 uwe Exp $ */ 3 4 /* 5 * Copyright (c) 2005 Uwe Stuehler <uwe@bsdx.de> 6 * 7 * Permission to use, copy, modify, and 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 /* 21 * Zaurus NetBSD bootstrap loader. 22 */ 23 24 #include <sys/cdefs.h> 25 #define ELFSIZE 32 26 #include <sys/exec_elf.h> 27 #include <sys/types.h> 28 #include <sys/errno.h> 29 30 #include <machine/bootinfo.h> 31 32 #include "compat_linux.h" 33 34 /* Linux LKM support */ 35 const char __module_kernel_version[] __attribute__((section(".modinfo"))) = 36 "kernel_version=" UTS_RELEASE; 37 const char __module_using_checksums[] __attribute__((section(".modinfo"))) = 38 "using_checksums=1"; 39 40 #define ZBOOTDEV_MAJOR 99 41 #define ZBOOTDEV_MODE 0222 42 #define ZBOOTDEV_NAME "zboot" 43 #define ZBOOTMOD_NAME "zbsdmod" 44 45 /* Prototypes */ 46 int init_module(void); 47 void cleanup_module(void); 48 49 static ssize_t zbsdmod_write(struct file *, const char *, size_t, loff_t *); 50 static int zbsdmod_open(struct inode *, struct file *); 51 static int zbsdmod_close(struct inode *, struct file *); 52 53 static void elf32bsdboot(void); 54 55 static struct file_operations fops = { 56 0, /* struct module *owner */ 57 0, /* lseek */ 58 0, /* read */ 59 zbsdmod_write, /* write */ 60 0, /* readdir */ 61 0, /* poll */ 62 0, /* ioctl */ 63 0, /* mmap */ 64 zbsdmod_open, /* open */ 65 0, /* flush */ 66 zbsdmod_close, /* release */ 67 0, /* sync */ 68 0, /* async */ 69 0, /* check media change */ 70 0, /* revalidate */ 71 0, /* lock */ 72 0, /* sendpage */ 73 0, /* get_unmapped_area */ 74 #ifdef MAGIC_ROM_PTR 75 0, /* romptr */ 76 #endif /* MAGIC_ROM_PTR */ 77 }; 78 79 static int isopen; 80 static loff_t position; 81 82 /* Outcast local variables to avoid stack usage in elf32bsdboot(). */ 83 static int cpsr; 84 static unsigned int sz; 85 static int i; 86 static vaddr_t minv, maxv, posv; 87 static vaddr_t elfv, shpv; 88 static int *addr; 89 static vaddr_t *esymp; 90 static Elf_Shdr *shp; 91 static Elf_Off off; 92 static int havesyms; 93 94 /* The maximum size of a kernel image is restricted to 5MB. */ 95 static u_int bsdimage[5242880/sizeof(u_int)]; /* XXX use kmalloc() */ 96 static char bootargs[BOOTARGS_BUFSIZ]; 97 static u_int datacacheclean[65536/sizeof(u_int)] __attribute__((aligned(32))); 98 99 /* 100 * Boot the loaded BSD kernel image, or return if an error is found. 101 * Part of this routine is borrowed from sys/lib/libsa/loadfile.c. 102 */ 103 static void 104 elf32bsdboot(void) 105 { 106 107 #define elf ((Elf32_Ehdr *)bsdimage) 108 #define phdr ((Elf32_Phdr *)((char *)elf + elf->e_phoff)) 109 110 if (memcmp(elf->e_ident, ELFMAG, SELFMAG) != 0 || 111 elf->e_ident[EI_CLASS] != ELFCLASS32) 112 return; 113 114 minv = (vaddr_t)~0; 115 maxv = (vaddr_t)0; 116 posv = (vaddr_t)0; 117 esymp = 0; 118 119 /* 120 * Get min and max addresses used by the loaded kernel. 121 */ 122 for (i = 0; i < elf->e_phnum; i++) { 123 124 if (phdr[i].p_type != PT_LOAD || 125 (phdr[i].p_flags & (PF_W|PF_R|PF_X)) == 0) 126 continue; 127 128 #define IS_TEXT(p) (p.p_flags & PF_X) 129 #define IS_DATA(p) (p.p_flags & PF_W) 130 #define IS_BSS(p) (p.p_filesz < p.p_memsz) 131 /* 132 * XXX: Assume first address is lowest 133 */ 134 if (IS_TEXT(phdr[i]) || IS_DATA(phdr[i])) { 135 posv = phdr[i].p_vaddr; 136 if (minv > posv) 137 minv = posv; 138 posv += phdr[i].p_filesz; 139 if (maxv < posv) 140 maxv = posv; 141 } 142 if (IS_BSS(phdr[i])) { 143 posv += phdr[i].p_memsz - phdr[i].p_filesz; 144 if (maxv < posv) 145 maxv = posv; 146 } 147 /* 148 * 'esym' is the first word in the .data section, 149 * and marks the end of the symbol table. 150 */ 151 if (IS_DATA(phdr[i]) && !IS_BSS(phdr[i])) 152 esymp = (vaddr_t *)phdr[i].p_vaddr; 153 } 154 155 __asm volatile ("mrs %0, cpsr" : "=r" (cpsr)); 156 cpsr |= 0xc0; /* set FI */ 157 __asm volatile ("msr cpsr_all, %0" :: "r" (cpsr)); 158 159 /* 160 * Copy the boot arguments. 161 */ 162 sz = BOOTARGS_BUFSIZ; 163 while (sz > 0) { 164 sz--; 165 ((char *)minv - BOOTARGS_BUFSIZ)[sz] = bootargs[sz]; 166 } 167 168 /* 169 * Set up pointers to copied ELF and section headers. 170 */ 171 #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) 172 elfv = maxv = roundup(maxv, sizeof(long)); 173 maxv += sizeof(Elf_Ehdr); 174 175 sz = elf->e_shnum * sizeof(Elf_Shdr); 176 shp = (Elf_Shdr *)((vaddr_t)elf + elf->e_shoff); 177 shpv = maxv; 178 maxv += roundup(sz, sizeof(long)); 179 180 /* 181 * Now load the symbol sections themselves. Make sure the 182 * sections are aligned, and offsets are relative to the 183 * copied ELF header. Don't bother with string tables if 184 * there are no symbol sections. 185 */ 186 off = roundup((sizeof(Elf_Ehdr) + sz), sizeof(long)); 187 for (havesyms = i = 0; i < elf->e_shnum; i++) 188 if (shp[i].sh_type == SHT_SYMTAB) 189 havesyms = 1; 190 for (i = 0; i < elf->e_shnum; i++) { 191 if (shp[i].sh_type == SHT_SYMTAB || 192 shp[i].sh_type == SHT_STRTAB) { 193 if (havesyms) { 194 sz = shp[i].sh_size; 195 while (sz > 0) { 196 sz--; 197 ((char *)maxv)[sz] = 198 ((char *)elf + 199 shp[i].sh_offset)[sz]; 200 } 201 } 202 maxv += roundup(shp[i].sh_size, sizeof(long)); 203 shp[i].sh_offset = off; 204 off += roundup(shp[i].sh_size, sizeof(long)); 205 } 206 } 207 208 /* 209 * Copy the ELF and section headers. 210 */ 211 sz = sizeof(Elf_Ehdr); 212 while (sz > 0) { 213 sz--; 214 ((char *)elfv)[sz] = ((char *)elf)[sz]; 215 } 216 sz = elf->e_shnum * sizeof(Elf_Shdr); 217 while (sz > 0) { 218 sz--; 219 ((char *)shpv)[sz] = ((char *)shp)[sz]; 220 } 221 222 /* 223 * Frob the copied ELF header to give information relative 224 * to elfv. 225 */ 226 ((Elf_Ehdr *)elfv)->e_phoff = 0; 227 ((Elf_Ehdr *)elfv)->e_shoff = sizeof(Elf_Ehdr); 228 ((Elf_Ehdr *)elfv)->e_phentsize = 0; 229 ((Elf_Ehdr *)elfv)->e_phnum = 0; 230 231 /* 232 * Tell locore.S where the symbol table ends, and arrange 233 * to skip esym when loading the data section. 234 */ 235 if (esymp != 0) 236 *esymp = (vaddr_t)maxv; 237 for (i = 0; esymp != 0 && i < elf->e_phnum; i++) { 238 if (phdr[i].p_type != PT_LOAD || 239 (phdr[i].p_flags & (PF_W|PF_R|PF_X)) == 0) 240 continue; 241 if (phdr[i].p_vaddr == (vaddr_t)esymp) { 242 phdr[i].p_vaddr = (vaddr_t)((char *)phdr[i].p_vaddr + sizeof(long)); 243 phdr[i].p_offset = (vaddr_t)((char *)phdr[i].p_offset + sizeof(long)); 244 phdr[i].p_filesz -= sizeof(long); 245 break; 246 } 247 } 248 249 /* 250 * Load text and data. 251 */ 252 for (i = 0; i < elf->e_phnum; i++) { 253 if (phdr[i].p_type != PT_LOAD || 254 (phdr[i].p_flags & (PF_W|PF_R|PF_X)) == 0) 255 continue; 256 257 if (IS_TEXT(phdr[i]) || IS_DATA(phdr[i])) { 258 sz = phdr[i].p_filesz; 259 while (sz > 0) { 260 sz--; 261 ((char *)phdr[i].p_vaddr)[sz] = 262 (((char *)elf) + phdr[i].p_offset)[sz]; 263 } 264 } 265 if (IS_BSS(phdr[i])) { 266 memset((void *)(phdr[i].p_vaddr + phdr[i].p_filesz), 0, 267 phdr[i].p_memsz - phdr[i].p_filesz); 268 } 269 } 270 271 addr = (int *)(elf->e_entry); 272 __asm volatile ( 273 /* Clean D-cache */ 274 "mov r0, %1;" 275 "mov r1, #65536;" 276 "1:" 277 "ldr r2, [r0], #32;" 278 "subs r1, r1, #32;" 279 "bne 1b;" 280 "mcr p15, 0, r1, c7, c10, 4;" /*drain write and fill buffer*/ 281 "mrc p15, 0, r1, c2, c0, 0;" /* CPWAIT */ 282 "mov r1, r1;" 283 "sub pc, pc, #4;" 284 /* Disable MMU and jump to kernel entry address */ 285 "mov r0, %0;" 286 "mcr p15, 0, r1, c7, c7, 0;" /* flush I+D cache */ 287 "mrc p15, 0, r1, c2, c0, 0;" /* CPWAIT */ 288 "mov r1, r1;" 289 "sub pc, pc, #4;" 290 "mov r1, #(0x00000010 | 0x00000020);" 291 /* 292 * Put the rest of instructions into the same cacheline 293 * to make sure no I$ refill after invalidation. 294 */ 295 "b 2f;" 296 ".align 5;" 297 "2:" 298 "mcr p15, 0, r1, c1, c0, 0;" /* Write new control register */ 299 "mcr p15, 0, r1, c8, c7, 0;" /* invalidate I+D TLB */ 300 "mcr p15, 0, r1, c7, c5, 0;" /* invalidate I$ and BTB */ 301 "mcr p15, 0, r1, c7, c10, 4;" /*drain write and fill buffer*/ 302 "mrc p15, 0, r1, c2, c0, 0;" /* CPWAIT_AND_RETURN */ 303 "sub pc, r0, r1, lsr #32;" 304 :: "r" (addr), "r" (datacacheclean) : "r0", "r1", "r2"); 305 } 306 307 /* 308 * Initialize the module. 309 */ 310 int 311 init_module(void) 312 { 313 struct proc_dir_entry *entry; 314 int rc; 315 316 rc = register_chrdev(ZBOOTDEV_MAJOR, ZBOOTDEV_NAME, &fops); 317 if (rc != 0) { 318 printk("%s: register_chrdev(%d, ...): error %d\n", 319 ZBOOTMOD_NAME, ZBOOTDEV_MAJOR, -rc); 320 return 1; 321 } 322 323 entry = proc_mknod(ZBOOTDEV_NAME, ZBOOTDEV_MODE | S_IFCHR, 324 &proc_root, MKDEV(ZBOOTDEV_MAJOR, 0)); 325 if (entry == (struct proc_dir_entry *)0) { 326 (void)unregister_chrdev(ZBOOTDEV_MAJOR, ZBOOTDEV_NAME); 327 return 1; 328 } 329 330 printk("%s: NetBSD/" MACHINE " bootstrap device is %d,0\n", 331 ZBOOTMOD_NAME, ZBOOTDEV_MAJOR); 332 333 return 0; 334 } 335 336 /* 337 * Cleanup - undo whatever init_module did. 338 */ 339 void 340 cleanup_module(void) 341 { 342 343 (void)unregister_chrdev(ZBOOTDEV_MAJOR, ZBOOTDEV_NAME); 344 remove_proc_entry(ZBOOTDEV_NAME, &proc_root); 345 346 printk("%s: NetBSD/" MACHINE " bootstrap device unloaded\n", 347 ZBOOTMOD_NAME); 348 } 349 350 static ssize_t 351 zbsdmod_write(struct file *f, const char *buf, size_t len, loff_t *offp) 352 { 353 354 if (len < 1) 355 return 0; 356 357 if (*offp + len >= sizeof(bsdimage)) 358 return -EFBIG; 359 360 memcpy(((char *)bsdimage) + *offp, buf, len); 361 362 *offp += len; 363 if (*offp > position) 364 position = *offp; 365 366 return len; 367 } 368 369 static int 370 zbsdmod_open(struct inode *ino, struct file *f) 371 { 372 373 /* XXX superuser check */ 374 375 if (isopen) 376 return -EBUSY; 377 378 isopen = 1; 379 position = 0; 380 381 return 0; 382 } 383 384 static int 385 zbsdmod_close(struct inode *ino, struct file *f) 386 { 387 388 if (!isopen) 389 return -EBUSY; 390 391 if (position > 0) { 392 printk("%s: loaded %ld bytes\n", ZBOOTDEV_NAME, position); 393 if (position < (loff_t)BOOTINFO_MAXSIZE) { 394 *(u_int *)bootargs = BOOTARGS_MAGIC; 395 memcpy(bootargs + sizeof(u_int), bsdimage, position); 396 } else { 397 elf32bsdboot(); 398 printk("%s: boot failed\n", ZBOOTDEV_NAME); 399 } 400 } 401 isopen = 0; 402 403 return 0; 404 } 405