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