1/* $NetBSD: mkboot.c,v 1.4 2024/03/20 00:34:32 christos Exp $ */ 2 3/* $OpenBSD: mkboot.c,v 1.9 2001/05/17 00:57:55 pvalchev Exp $ */ 4 5/* 6 * Copyright (c) 1990, 1993 7 * The Regents of the University of California. All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 3. Neither the name of the University nor the names of its contributors 18 * may be used to endorse or promote products derived from this software 19 * without specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31 * SUCH DAMAGE. 32 * 33 * @(#)mkboot.c 8.1 (Berkeley) 7/15/93 34 */ 35 36#if 0 37#ifndef lint 38static char copyright[] = 39"@(#) Copyright (c) 1990, 1993\n\ 40 The Regents of the University of California. All rights reserved.\n"; 41#endif /* not lint */ 42 43#ifndef lint 44static char rcsid[] = "$OpenBSD: mkboot.c,v 1.9 2001/05/17 00:57:55 pvalchev Exp $"; 45#endif /* not lint */ 46#endif 47 48#if HAVE_NBTOOL_CONFIG_H 49#include "nbtool_config.h" 50#include "../../sys/sys/bootblock.h" 51#else 52#include <sys/bootblock.h> 53#endif 54 55#include <sys/param.h> 56#include <sys/file.h> 57#include <sys/stat.h> 58#include <string.h> 59#include <stdlib.h> 60#include <unistd.h> 61#include <time.h> 62#include <err.h> 63 64/* BFD ELF headers */ 65#include <elf/common.h> 66#include <elf/external.h> 67 68#define IS_ELF(ehdr) ((ehdr).e_ident[EI_MAG0] == ELFMAG0 && \ 69 (ehdr).e_ident[EI_MAG1] == ELFMAG1 && \ 70 (ehdr).e_ident[EI_MAG2] == ELFMAG2 && \ 71 (ehdr).e_ident[EI_MAG3] == ELFMAG3) 72 73/* 74 * Macros to get values from multi-byte ELF header fields. These assume 75 * a big-endian image. 76 */ 77#define ELFGET16(x) (((x)[0] << 8) | (x)[1]) 78 79#define ELFGET32(x) (((x)[0] << 24) | ((x)[1] << 16) | \ 80 ((x)[2] << 8) | (x)[3]) 81 82/* 83 * Header prepended to each a.out file. 84 */ 85struct exec { 86 u_long a_midmag; /* htonl(flags<<26 | mid<<16 | magic) */ 87 u_long a_text; /* text segment size */ 88 u_long a_data; /* initialized data size */ 89 u_long a_bss; /* uninitialized data size */ 90 u_long a_syms; /* symbol table size */ 91 u_long a_entry; /* entry point */ 92 u_long a_trsize; /* text relocation size */ 93 u_long a_drsize; /* data relocation size */ 94}; 95 96/* a_magic */ 97#define OMAGIC 0407 /* old impure format */ 98#define NMAGIC 0410 /* read-only text */ 99#define ZMAGIC 0413 /* demand load format */ 100#define QMAGIC 0314 /* "compact" demand load format; deprecated */ 101 102#define N_GETMAGIC(ex) \ 103 ((((ex).a_midmag)&0xffff0000) ? \ 104 (ntohl((uint32_t)((ex).a_midmag))&0xffff) : ((ex).a_midmag)) 105 106#include <stdio.h> 107#include <ctype.h> 108 109int putfile(char *, int); 110void __dead usage(void); 111void bcddate(char *, char *); 112char *lifname(char *); 113int cksum(int, int *, int); 114 115char *to_file; 116int loadpoint, verbose; 117u_long entry; 118time_t repro_epoch = 0; 119 120/* 121 * Old Format: 122 * sector 0: LIF volume header (40 bytes) 123 * sector 1: <unused> 124 * sector 2: LIF directory (8 x 32 == 256 bytes) 125 * sector 3-: LIF file 0, LIF file 1, etc. 126 * where sectors are 256 bytes. 127 * 128 * New Format: 129 * sector 0: LIF volume header (40 bytes) 130 * sector 1: <unused> 131 * sector 2: LIF directory (8 x 32 == 256 bytes) 132 * sector 3: <unused> 133 * sector 4-31: disklabel (~300 bytes right now) 134 * sector 32-: LIF file 0, LIF file 1, etc. 135 */ 136int 137main(int argc, char **argv) 138{ 139 int to, n, pos, c; 140 char buf[HPPA_LIF_FILESTART]; 141 struct hppa_lifvol *lifv = (struct hppa_lifvol *)buf; 142 struct hppa_lifdir *lifd = (struct hppa_lifdir *)(buf + HPPA_LIF_DIRSTART); 143 144 while ((c = getopt(argc, argv, "l:t:v")) != -1) { 145 switch (c) { 146 case 'l': 147 loadpoint = strtol(optarg, NULL, 0); 148 break; 149 case 't': 150 repro_epoch = atoll(optarg); 151 break; 152 case 'v': 153 verbose++; 154 break; 155 default: 156 usage(); 157 } 158 } 159 if (argc - optind < 2) 160 usage(); 161 else if (argc - optind > 8) 162 errx(1, "too many boot programs (max 8 supported)"); 163 164 to_file = argv[--argc]; 165 if ((to = open(to_file, O_RDWR | O_TRUNC | O_CREAT, 0644)) < 0) 166 err(1, "%s: open", to_file); 167 168 memset(buf, 0, sizeof(buf)); 169 170 /* record volume info */ 171 lifv->vol_id = htobe16(HPPA_LIF_VOL_ID); 172 strncpy(lifv->vol_label, "MKBOOT", 6); 173 lifv->vol_addr = htobe32(hppa_btolifs(HPPA_LIF_DIRSTART)); 174 lifv->vol_oct = htobe16(HPPA_LIF_VOL_OCT); 175 lifv->vol_dirsize = htobe32(hppa_btolifs(HPPA_LIF_DIRSIZE)); 176 lifv->vol_version = htobe16(1); 177 lifv->vol_number = htobe32(1); 178 lifv->vol_lastvol = htobe32(1); 179 lifv->vol_length = HPPA_LIF_FILESTART; /* ... so far. */ 180 bcddate(to_file, lifv->vol_toc); 181 lifv->ipl_addr = htobe32(HPPA_LIF_FILESTART); 182 183 argv += optind; 184 argc -= optind; 185 optind = 0; 186 for (pos = HPPA_LIF_FILESTART; optind < argc; optind++) { 187 188 /* output bootfile */ 189 if (lseek(to, pos, SEEK_SET) < 0) 190 err(1, "%s: lseek", to_file); 191 lifd[optind].dir_addr = htobe32(hppa_btolifs(pos)); 192 n = hppa_btolifs(putfile(argv[optind], to)); 193 if (lifv->ipl_entry == 0) { 194 lifv->ipl_entry = htobe32(loadpoint + entry); 195 lifv->ipl_size = htobe32(hppa_lifstob(n)); 196 lifd[optind].dir_type = htobe16(HPPA_LIF_DIR_ISL); 197 lifd[optind].dir_implement = 0; 198 } else { 199 lifd[optind].dir_type = htobe16(HPPA_LIF_DIR_TYPE); 200 lifd[optind].dir_implement = htobe32(loadpoint + entry); 201 } 202 203 memcpy(lifd[optind].dir_name, lifname(argv[optind]), 204 sizeof(lifd[optind].dir_name)); 205 lifd[optind].dir_length = htobe32(n); 206 bcddate(argv[optind], lifd[optind].dir_toc); 207 lifd[optind].dir_flag = htobe16(HPPA_LIF_DIR_FLAG); 208 209 lifv->vol_length += n; 210 pos += hppa_lifstob(n); 211 } 212 213 /* terminate the directory */ 214 lifd[optind].dir_type = htobe16(0xffff); 215 216 /* byte-swap the length now that we're done computing it */ 217 lifv->vol_length = htobe32(lifv->vol_length); 218 219 /* output volume/directory header info */ 220 if (lseek(to, HPPA_LIF_VOLSTART, SEEK_SET) < 0) 221 err(1, "%s: lseek", to_file); 222 if (write(to, buf, sizeof(buf)) != sizeof(buf)) 223 err(1, "%s: write LIF volume", to_file); 224 lseek(to, 0, SEEK_END); 225 226 if (close(to) < 0) 227 err(1, "%s", to_file); 228 229 return(0); 230} 231 232int 233putfile(char *from_file, int to) 234{ 235 struct exec ex; 236 char buf[2048]; 237 int n, total; 238 int from, check_sum = 0; 239 struct hppa_lifload load; 240 Elf32_External_Ehdr elf_header; 241 Elf32_External_Phdr *elf_segments = NULL; 242 int i, header_count, memory_needed, elf_load_image_segment; 243 244 if ((from = open(from_file, O_RDONLY)) < 0) 245 err(1, "%s", from_file); 246 247 n = read(from, &ex, sizeof(ex)); 248 if (n != sizeof(ex)) 249 err(1, "%s: reading file header", from_file); 250 251 entry = ex.a_entry; 252 if (N_GETMAGIC(ex) == OMAGIC || N_GETMAGIC(ex) == NMAGIC) 253 entry += sizeof(ex); 254 else if (IS_ELF(*(Elf32_External_Ehdr *)&ex)) { 255 256 if (lseek(from, 0, SEEK_SET) < 0) 257 err(1, "lseek"); 258 n = read(from, &elf_header, sizeof (elf_header)); 259 if (n != sizeof (elf_header)) 260 err(1, "%s: reading ELF header", from_file); 261 header_count = ELFGET16(elf_header.e_phnum); 262 memory_needed = header_count * sizeof (Elf32_External_Phdr); 263 elf_segments = malloc(memory_needed); 264 if (elf_segments == NULL) 265 err(1, "malloc"); 266 if (lseek(from, ELFGET32(elf_header.e_phoff), SEEK_SET) < 0) 267 err(1, "lseek"); 268 n = read(from, elf_segments, memory_needed); 269 if (n != memory_needed) 270 err(1, "%s: reading ELF segments", from_file); 271 elf_load_image_segment = -1; 272 for (i = 0; i < header_count; i++) { 273 if (ELFGET32(elf_segments[i].p_filesz) && 274 ELFGET32(elf_segments[i].p_flags) & PF_X) { 275 if (elf_load_image_segment != -1) 276 errx(1, "%s: more than one ELF program " 277 "segment", from_file); 278 elf_load_image_segment = i; 279 } 280 } 281 if (elf_load_image_segment == -1) 282 errx(1, "%s: no suitable ELF program segment", 283 from_file); 284 entry = ELFGET32(elf_header.e_entry) + 285 ELFGET32(elf_segments[elf_load_image_segment].p_offset) - 286 ELFGET32(elf_segments[elf_load_image_segment].p_vaddr); 287 } else if (*(uint8_t *)&ex == 0x1f && ((uint8_t *)&ex)[1] == 0x8b) { 288 entry = 0; 289 } else 290 errx(1, "%s: bad magic number", from_file); 291 292 entry += sizeof(load); 293 lseek(to, sizeof(load), SEEK_CUR); 294 295 total = 0; 296 n = sizeof(buf) - sizeof(load); 297 /* copy the whole file */ 298 for (lseek(from, 0, SEEK_SET); ; n = sizeof(buf)) { 299 memset(buf, 0, sizeof(buf)); 300 if ((n = read(from, buf, n)) < 0) 301 err(1, "%s", from_file); 302 else if (n == 0) 303 break; 304 305 if (write(to, buf, n) != n) 306 err(1, "%s", to_file); 307 308 total += n; 309 check_sum = cksum(check_sum, (int *)buf, n); 310 } 311 312 /* load header */ 313 load.address = htobe32(loadpoint + sizeof(load)); 314 load.count = htobe32(4 + total); 315 check_sum = cksum(check_sum, (int *)&load, sizeof(load)); 316 317 if (verbose) 318 warnx("wrote %d bytes of file \'%s\'", total, from_file); 319 320 total += sizeof(load); 321 /* insert the header */ 322 lseek(to, -total, SEEK_CUR); 323 if (write(to, &load, sizeof(load)) != sizeof(load)) 324 err(1, "%s", to_file); 325 lseek(to, total - sizeof(load), SEEK_CUR); 326 327 memset(buf, 0, sizeof(buf)); 328 /* pad to int */ 329 n = sizeof(int) - total % sizeof(int); 330 if (total % sizeof(int)) { 331 if (write(to, buf, n) != n) 332 err(1, "%s", to_file); 333 else 334 total += n; 335 } 336 337 /* pad to the blocksize */ 338 n = sizeof(buf) - total % sizeof(buf); 339 340 if (n < sizeof(int)) { 341 n += sizeof(buf); 342 total += sizeof(buf); 343 } else 344 total += n; 345 346 /* TODO should pad here to the 65k boundary for tape boot */ 347 348 if (verbose) 349 warnx("checksum is 0x%08x", -check_sum); 350 351 check_sum = htobe32(-check_sum); 352 if (write(to, &check_sum, sizeof(int)) != sizeof(int)) 353 err(1, "%s", to_file); 354 355 n -= sizeof(int); 356 357 if (write(to, buf, n) != n) 358 err(1, "%s", to_file); 359 360 if (close(from) < 0) 361 err(1, "%s", from_file); 362 363 free(elf_segments); 364 return total; 365} 366 367int 368cksum(int ck, int *p, int size) 369{ 370 /* we assume size is int-aligned */ 371 for (size = (size + sizeof(int) - 1) / sizeof(int); size--; p++ ) 372 ck += be32toh(*p); 373 374 return ck; 375} 376 377void __dead 378usage(void) 379{ 380 fprintf(stderr, 381 "Usage: %s [-v] [-l <loadpoint>] [-t <timestamp>] prog1 {progN} outfile\n", 382 getprogname()); 383 exit(1); 384} 385 386char * 387lifname(char *str) 388{ 389 static char lname[10] = "XXXXXXXXXX"; 390 char *cp; 391 int i; 392 393 cp = strrchr(str, '/'); 394 if (cp != NULL) { 395 str = cp + 1; 396 } 397 for (i = 0; i < 9; i++) { 398 if (islower(*str)) 399 lname[i] = toupper(*str); 400 else if (isalnum(*str) || *str == '_') 401 lname[i] = *str; 402 else 403 break; 404 str++; 405 } 406 for ( ; i < 10; i++) 407 lname[i] = ' '; 408 return(lname); 409} 410 411 412void 413bcddate(char *file, char *toc) 414{ 415 struct stat statb; 416 struct tm *tm; 417 418 if (repro_epoch) 419 tm = gmtime(&repro_epoch); 420 else { 421 stat(file, &statb); 422 tm = localtime(&statb.st_ctime); 423 } 424 tm->tm_year %= 100; 425 *toc = (tm->tm_year / 10) << 4; 426 *toc++ |= tm->tm_year % 10; 427 *toc = ((tm->tm_mon+1) / 10) << 4; 428 *toc++ |= (tm->tm_mon+1) % 10; 429 *toc = (tm->tm_mday / 10) << 4; 430 *toc++ |= tm->tm_mday % 10; 431 *toc = (tm->tm_hour / 10) << 4; 432 *toc++ |= tm->tm_hour % 10; 433 *toc = (tm->tm_min / 10) << 4; 434 *toc++ |= tm->tm_min % 10; 435 *toc = (tm->tm_sec / 10) << 4; 436 *toc |= tm->tm_sec % 10; 437} 438