1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * mkbossimg - wrap a firmware image in a Bintec/Teldat BOSS header 4 * 5 * Copyright (C) 2009-2013 Sebastien Decourriere <sebtx452@gmail.com> 6 * Copyright (C) 2026 Simon Wunderlich <sw@simonwunderlich.de> 7 * Based on mkbrnimg.c 8 * 9 * The BOSS bootmonitor found on Bintec elmeg RS-series routers expects a 10 * 52-byte big-endian header in front of a gzip-compressed image. The board 11 * family is identified by a magic string, which the bootmonitor reports as 12 * "Identification" under "Show System Information". 13 * 14 * The input file is expected to carry a 52-byte placeholder header, so that 15 * the payload behind the real header stays aligned to the flash sector size. 16 * The placeholder is skipped; length and CRC are computed over the remainder. 17 */ 18 19 #include <arpa/inet.h> 20 #include <errno.h> 21 #include <fcntl.h> 22 #include <inttypes.h> 23 #include <stdio.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <sys/mman.h> 27 #include <sys/stat.h> 28 #include <unistd.h> 29 #include <zlib.h> 30 31 /* Image types */ 32 #define IMG_TYPE_BOSS 0x00 /* Compressed BOSS */ 33 #define IMG_TYPE_UBOSS 0x01 /* Uncompressed BOSS (uBoss) */ 34 #define IMG_TYPE_LOGIC 0x02 /* Logic aka first bootloader */ 35 #define IMG_TYPE_BOOTMON 0x03 /* Bootmonitor aka second bootloader */ 36 #define IMG_TYPE_FIRMWARE 0x04 /* Firmware (unknown) */ 37 38 #define IMG_VER 0x00000000 39 40 /* 41 * In vendor images the two fields called "unknown" here hold the length and 42 * CRC of an uncompressed section. For gzip images the bootmonitor accepts the 43 * values below, which is what the original tool always wrote. 44 */ 45 #define UNKNOWN_1 0x00FA0524 46 #define UNKNOWN_2 0x00000000 47 #define UNKNOWN_3 0x00000000 48 49 /* Version 42, high enough that the vendor web UI accepts the upgrade */ 50 #define FW_VERSION 0x002A0000 51 52 #define MAGIC_CHAOSENDDRAGON "BINTEC ChaosEndDragon" 53 #define MAGIC_CLOSEDEYEVISUAL "TELDAT ClosedEyeVisual" 54 55 struct boss_header { 56 char magic[23]; 57 uint32_t fw_version; 58 uint8_t image_type; 59 uint32_t image_version; 60 uint32_t image_length; 61 uint32_t unknown1; 62 uint32_t unknown2; 63 uint32_t crc32; 64 uint32_t unknown3; 65 } __attribute__((packed)); 66 67 static const struct board { 68 const char *name; 69 const char *magic; 70 } boards[] = { 71 /* 72 * Verified on the device itself via the bootmonitor's 73 * "Show System Information" -> "Identification:" field. 74 */ 75 { "rs123", MAGIC_CLOSEDEYEVISUAL }, 76 { "rs230", MAGIC_CHAOSENDDRAGON }, 77 { "rs353", MAGIC_CLOSEDEYEVISUAL }, 78 }; 79 80 static void usage(const char *mess) 81 { 82 size_t i; 83 84 fprintf(stderr, "Error: %s\n", mess); 85 fprintf(stderr, "Usage: mkbossimg <board> <input file> <output file>\n"); 86 fprintf(stderr, "Supported boards:"); 87 for (i = 0; i < sizeof(boards) / sizeof(boards[0]); i++) 88 fprintf(stderr, " %s", boards[i].name); 89 fprintf(stderr, "\n"); 90 exit(1); 91 } 92 93 static ssize_t write_all(int fd, const void *buf, size_t len) 94 { 95 const char *p = buf; 96 size_t done = 0; 97 98 while (done < len) { 99 ssize_t n = write(fd, p + done, len - done); 100 101 if (n < 0) { 102 if (errno == EINTR) 103 continue; 104 return -1; 105 } 106 done += (size_t)n; 107 } 108 return (ssize_t)done; 109 } 110 111 int main(int argc, char **argv) 112 { 113 const char *board_name, *in_path, *out_path; 114 const char *magic = NULL; 115 struct boss_header hdr; 116 struct stat st; 117 char *map, *payload; 118 size_t payload_len; 119 uint32_t crc; 120 size_t i; 121 int fd, outfd; 122 123 if (argc != 4) 124 usage("wrong number of arguments"); 125 126 board_name = argv[1]; 127 in_path = argv[2]; 128 out_path = argv[3]; 129 130 for (i = 0; i < sizeof(boards) / sizeof(boards[0]); i++) { 131 if (strcmp(board_name, boards[i].name) == 0) { 132 magic = boards[i].magic; 133 break; 134 } 135 } 136 if (!magic) { 137 fprintf(stderr, "Invalid board type '%s'\n", board_name); 138 usage("unknown board"); 139 } 140 141 fd = open(in_path, O_RDONLY); 142 if (fd < 0 || fstat(fd, &st) < 0) { 143 fprintf(stderr, "Error opening '%s': %s\n", in_path, strerror(errno)); 144 exit(1); 145 } 146 if ((size_t)st.st_size <= sizeof(hdr)) { 147 fprintf(stderr, "Input '%s' is too small (%ju bytes)\n", 148 in_path, (uintmax_t)st.st_size); 149 exit(1); 150 } 151 152 map = mmap(NULL, (size_t)st.st_size, PROT_READ, MAP_SHARED, fd, 0); 153 if (map == MAP_FAILED) { 154 fprintf(stderr, "Error mapping '%s': %s\n", in_path, strerror(errno)); 155 exit(1); 156 } 157 close(fd); 158 159 /* Skip the placeholder header that pads the payload to a sector */ 160 payload = map + sizeof(hdr); 161 payload_len = (size_t)st.st_size - sizeof(hdr); 162 163 if (!((unsigned char)payload[0] == 0x1F && (unsigned char)payload[1] == 0x8B)) { 164 fprintf(stderr, "Invalid image type: expected gzip magic, got %02X %02X\n", 165 (unsigned char)payload[0], (unsigned char)payload[1]); 166 exit(1); 167 } 168 169 memset(&hdr, 0, sizeof(hdr)); 170 strncpy(hdr.magic, magic, sizeof(hdr.magic) - 1); 171 hdr.fw_version = htonl(FW_VERSION); 172 hdr.image_type = IMG_TYPE_BOSS; 173 hdr.image_version = htonl(IMG_VER); 174 hdr.unknown1 = htonl(UNKNOWN_1); 175 hdr.unknown2 = htonl(UNKNOWN_2); 176 hdr.unknown3 = htonl(UNKNOWN_3); 177 178 crc = crc32(0, (const unsigned char *)payload, payload_len); 179 hdr.image_length = htonl((uint32_t)payload_len); 180 hdr.crc32 = htonl(crc); 181 182 fprintf(stderr, "Board '%s' magic '%s', crc32 %08x, length %08zx\n", 183 board_name, magic, crc, payload_len); 184 185 outfd = open(out_path, O_WRONLY | O_CREAT | O_TRUNC, 0644); 186 if (outfd < 0) { 187 fprintf(stderr, "Error opening '%s' for writing: %s\n", 188 out_path, strerror(errno)); 189 exit(1); 190 } 191 if (write_all(outfd, &hdr, sizeof(hdr)) < 0 || 192 write_all(outfd, payload, payload_len) < 0) { 193 fprintf(stderr, "Error writing '%s': %s\n", out_path, strerror(errno)); 194 exit(1); 195 } 196 close(outfd); 197 munmap(map, (size_t)st.st_size); 198 199 return EXIT_SUCCESS; 200 } 201
This page was automatically generated by LXR 0.3.1. • OpenWrt