1 /* $NetBSD: zbsdmod.c,v 1.9 2013/12/02 18:36:11 joerg 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_DATA(phdr[i]) && IS_BSS(phdr[i])) { 143 posv += phdr[i].p_memsz; 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 } 266 267 addr = (int *)(elf->e_entry); 268 __asm volatile ( 269 /* Clean D-cache */ 270 "mov r0, %1;" 271 "mov r1, #65536;" 272 "1:" 273 "ldr r2, [r0], #32;" 274 "subs r1, r1, #32;" 275 "bne 1b;" 276 "mcr p15, 0, r1, c7, c10, 4;" /*drain write and fill buffer*/ 277 "mrc p15, 0, r1, c2, c0, 0;" /* CPWAIT */ 278 "mov r1, r1;" 279 "sub pc, pc, #4;" 280 /* Disable MMU and jump to kernel entry address */ 281 "mov r0, %0;" 282 "mcr p15, 0, r1, c7, c7, 0;" /* flush I+D cache */ 283 "mrc p15, 0, r1, c2, c0, 0;" /* CPWAIT */ 284 "mov r1, r1;" 285 "sub pc, pc, #4;" 286 "mov r1, #(0x00000010 | 0x00000020);" 287 "mcr p15, 0, r1, c1, c0, 0;" /* Write new control register */ 288 "mcr p15, 0, r1, c8, c7, 0;" /* invalidate I+D TLB */ 289 "mcr p15, 0, r1, c7, c5, 0;" /* invalidate I$ and BTB */ 290 "mcr p15, 0, r1, c7, c10, 4;" /*drain write and fill buffer*/ 291 "mrc p15, 0, r1, c2, c0, 0;" /* CPWAIT_AND_RETURN */ 292 "sub pc, r0, r1, lsr #32;" 293 :: "r" (addr), "r" (datacacheclean) : "r0", "r1", "r2"); 294 } 295 296 /* 297 * Initialize the module. 298 */ 299 int 300 init_module(void) 301 { 302 struct proc_dir_entry *entry; 303 int rc; 304 305 rc = register_chrdev(ZBOOTDEV_MAJOR, ZBOOTDEV_NAME, &fops); 306 if (rc != 0) { 307 printk("%s: register_chrdev(%d, ...): error %d\n", 308 ZBOOTMOD_NAME, ZBOOTDEV_MAJOR, -rc); 309 return 1; 310 } 311 312 entry = proc_mknod(ZBOOTDEV_NAME, ZBOOTDEV_MODE | S_IFCHR, 313 &proc_root, MKDEV(ZBOOTDEV_MAJOR, 0)); 314 if (entry == (struct proc_dir_entry *)0) { 315 (void)unregister_chrdev(ZBOOTDEV_MAJOR, ZBOOTDEV_NAME); 316 return 1; 317 } 318 319 printk("%s: NetBSD/" MACHINE " bootstrap device is %d,0\n", 320 ZBOOTMOD_NAME, ZBOOTDEV_MAJOR); 321 322 return 0; 323 } 324 325 /* 326 * Cleanup - undo whatever init_module did. 327 */ 328 void 329 cleanup_module(void) 330 { 331 332 (void)unregister_chrdev(ZBOOTDEV_MAJOR, ZBOOTDEV_NAME); 333 remove_proc_entry(ZBOOTDEV_NAME, &proc_root); 334 335 printk("%s: NetBSD/" MACHINE " bootstrap device unloaded\n", 336 ZBOOTMOD_NAME); 337 } 338 339 static ssize_t 340 zbsdmod_write(struct file *f, const char *buf, size_t len, loff_t *offp) 341 { 342 343 if (len < 1) 344 return 0; 345 346 if (*offp + len >= sizeof(bsdimage)) 347 return -EFBIG; 348 349 memcpy(((char *)bsdimage) + *offp, buf, len); 350 351 *offp += len; 352 if (*offp > position) 353 position = *offp; 354 355 return len; 356 } 357 358 static int 359 zbsdmod_open(struct inode *ino, struct file *f) 360 { 361 362 /* XXX superuser check */ 363 364 if (isopen) 365 return -EBUSY; 366 367 isopen = 1; 368 position = 0; 369 370 return 0; 371 } 372 373 static int 374 zbsdmod_close(struct inode *ino, struct file *f) 375 { 376 377 if (!isopen) 378 return -EBUSY; 379 380 if (position > 0) { 381 printk("%s: loaded %ld bytes\n", ZBOOTDEV_NAME, position); 382 if (position < (loff_t)BOOTINFO_MAXSIZE) { 383 *(u_int *)bootargs = BOOTARGS_MAGIC; 384 memcpy(bootargs + sizeof(u_int), bsdimage, position); 385 } else { 386 elf32bsdboot(); 387 printk("%s: boot failed\n", ZBOOTDEV_NAME); 388 } 389 } 390 isopen = 0; 391 392 return 0; 393 } 394