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