|
|
1.1 ! root 1: // ! 2: // nono ! 3: // Copyright (C) 2021 nono project ! 4: // Licensed under nono-license.txt ! 5: // ! 6: ! 7: // ! 8: // LUNA NVRAM.DAT エディタ ! 9: // ! 10: ! 11: #include "header.h" ! 12: #include "mystring.h" ! 13: #include <cstdio> ! 14: #include <string> ! 15: #include <vector> ! 16: #include <err.h> ! 17: #include <fcntl.h> ! 18: #include <unistd.h> ! 19: #include <sys/mman.h> ! 20: #include <sys/stat.h> ! 21: ! 22: using updatefunc = bool (*)(const std::string&, const std::string&); ! 23: ! 24: [[noreturn]] static void usage(); ! 25: static void show(); ! 26: static void edit(int ac, char *av[]); ! 27: static bool update_luna1(const std::string&, const std::string&); ! 28: static bool update_luna88k(const std::string&, const std::string&); ! 29: static std::string read_string(uint32 addr); ! 30: static void write_string(uint32 addr, const std::string&); ! 31: static uint32 find_string(const std::string&); ! 32: static uint8 calc_checksum(); ! 33: static bool verify_checksum(); ! 34: ! 35: static int debug; ! 36: static bool opt_force; ! 37: static bool opt_showaddr; ! 38: static uint8 *nvram; ! 39: ! 40: #define DPRINTF(fmt...) do { \ ! 41: if (debug) { \ ! 42: printf("%s: ", __func__); \ ! 43: printf(fmt); \ ! 44: } \ ! 45: } while (0) ! 46: ! 47: int ! 48: main(int ac, char *av[]) ! 49: { ! 50: const char *filename; ! 51: struct stat st; ! 52: int c; ! 53: int fd; ! 54: int mode; ! 55: int prot; ! 56: size_t maplen; ! 57: ! 58: while ((c = getopt(ac, av, "dfnh")) != -1) { ! 59: switch (c) { ! 60: case 'd': ! 61: debug++; ! 62: break; ! 63: case 'f': ! 64: opt_force = true; ! 65: break; ! 66: case 'n': ! 67: opt_showaddr = true; ! 68: break; ! 69: case 'h': ! 70: default: ! 71: usage(); ! 72: break; ! 73: } ! 74: } ! 75: ac -= optind; ! 76: av += optind; ! 77: ! 78: if (ac == 0) { ! 79: usage(); ! 80: } ! 81: ! 82: filename = av[0]; ! 83: ac--; ! 84: av++; ! 85: ! 86: if (ac == 0) { ! 87: // show ! 88: mode = O_RDONLY; ! 89: prot = PROT_READ; ! 90: } else { ! 91: // edit ! 92: mode = O_RDWR; ! 93: prot = PROT_READ | PROT_WRITE; ! 94: } ! 95: ! 96: fd = open(filename, mode); ! 97: if (fd < 0) { ! 98: err(1, "%s: open", filename); ! 99: } ! 100: ! 101: if (fstat(fd, &st) < 0) { ! 102: err(1, "%s: fstat", filename); ! 103: } ! 104: ! 105: maplen = 2040; ! 106: if (st.st_size < maplen) { ! 107: errx(1, "%s: File too short. Correct size is %zu bytes.\n", ! 108: filename, maplen); ! 109: } else if (st.st_size > maplen) { ! 110: warnx("%s: File too large. Correct size is %zu bytes. " ! 111: "(Ignore the extra space)", filename, maplen); ! 112: } ! 113: ! 114: int flag = MAP_FILE | MAP_SHARED; ! 115: void *m = mmap(NULL, maplen, prot, flag, fd, 0); ! 116: if (m == MAP_FAILED) { ! 117: err(1, "%s: mmap", filename); ! 118: } ! 119: nvram = (uint8 *)m; ! 120: ! 121: if (ac == 0) { ! 122: show(); ! 123: } else { ! 124: edit(ac, av); ! 125: } ! 126: ! 127: munmap(m, maplen); ! 128: close(fd); ! 129: return 0; ! 130: } ! 131: ! 132: static void ! 133: usage() ! 134: { ! 135: fprintf(stderr, ! 136: "usage: %s [<options>] <NVRAM.DAT> .. show all\n", ! 137: getprogname()); ! 138: fprintf(stderr, ! 139: " %s [<options>] <NVRAM.DAT> <key=value> .. set\n", ! 140: getprogname()); ! 141: fprintf(stderr, ! 142: " -d : debug\n" ! 143: " -f : force to show/set even if nvram is broken\n" ! 144: " -n : show address\n" ! 145: ); ! 146: exit(1); ! 147: } ! 148: ! 149: // 表示する ! 150: static void ! 151: show() ! 152: { ! 153: if (verify_checksum() == false && opt_force == false) { ! 154: printf("Broken NVRAM. Specify -f to force\n"); ! 155: return; ! 156: } ! 157: ! 158: // LUNA-I の 0x560 には ENADDR があるけど、 ! 159: // チェックサムの範囲外だしどういう扱いなんだべ。 ! 160: uint32 end = 0x560; ! 161: if (strcmp((const char *)&nvram[0x560], "ENADDR") == 0) { ! 162: end = 0x580; ! 163: } ! 164: ! 165: for (uint32 addr = 0x20; addr < end; addr += 0x20) { ! 166: if (nvram[addr] == '\0') { ! 167: continue; ! 168: } ! 169: ! 170: std::string key = read_string(addr); ! 171: std::string val = read_string(addr + 16); ! 172: ! 173: if (opt_showaddr) { ! 174: printf("0x%04x: ", addr); ! 175: } ! 176: printf("%-16s = %s\n", key.c_str(), val.c_str()); ! 177: } ! 178: } ! 179: ! 180: static void ! 181: edit(int ac, char *av[]) ! 182: { ! 183: if (verify_checksum() == false && opt_force == false) { ! 184: printf("Broken NVRAM. Specify -f to force\n"); ! 185: return; ! 186: } ! 187: ! 188: // 機種によってたぶんフィールドの扱いが違う気がする。 ! 189: // LUNA-I か LUNA88K か厳密には区別できない気がするけど、とりあえず ! 190: // 自由フィールドっぽい 0x560 までより後ろに "ENADDR" があれば ! 191: // LUNA-I ということにしてみる。LUNA-II と LUNA-88K2 は未調査。 ! 192: updatefunc updater; ! 193: if (strcmp((const char *)&nvram[0x560], "ENADDR") == 0) { ! 194: updater = update_luna1; ! 195: } else { ! 196: updater = update_luna88k; ! 197: } ! 198: ! 199: int updated = 0; ! 200: for (int i = 0; i < ac; i++) { ! 201: const char *s; ! 202: const char *e; ! 203: ! 204: s = av[i]; ! 205: e = strchr(s, '='); ! 206: if (e == NULL) { ! 207: warnx("%s: syntax error", av[i]); ! 208: return; ! 209: } ! 210: ! 211: std::string key(s, (e - s)); ! 212: std::string val(e + 1); ! 213: ! 214: if (key.empty()) { ! 215: warnx("%s: key must be specified", av[i]); ! 216: return; ! 217: } ! 218: if (val.empty()) { ! 219: warnx("%s: value must be specified", av[i]); ! 220: return; ! 221: } ! 222: ! 223: if (updater(key, val)) { ! 224: updated++; ! 225: } ! 226: } ! 227: ! 228: // 更新があればチェックサムを再計算 ! 229: if (updated > 0) { ! 230: uint8 eor = calc_checksum(); ! 231: for (int i = 0; i < 3; i++) { ! 232: nvram[0x1c + i] = eor; ! 233: } ! 234: } ! 235: } ! 236: ! 237: // LUNA-I のパラメータ表 ! 238: struct luna1param { ! 239: uint32 addr; ! 240: const char *key; ! 241: }; ! 242: static std::vector<luna1param> luna1params = { ! 243: { 0x0020, "BOOT" }, ! 244: { 0x0040, "HOST" }, ! 245: { 0x0060, "SERVER" }, ! 246: { 0x0080, "DKUNIT" }, ! 247: { 0x00a0, "DKPART" }, ! 248: { 0x00c0, "DKFILE" }, ! 249: { 0x00e0, "FLUNIT" }, ! 250: { 0x0100, "FLFILE" }, ! 251: { 0x0120, "STUNIT" }, ! 252: { 0x0140, "STFLNO" }, ! 253: { 0x0160, "STFILE" }, ! 254: { 0x0180, "ETBOOT" }, ! 255: { 0x01a0, "ETFILE" }, ! 256: { 0x0560, "ENADDR" }, ! 257: }; ! 258: ! 259: // LUNA-I の場合、キーはたぶん固定じゃないかな。 ! 260: // 更新したら true を返す。 ! 261: static bool ! 262: update_luna1(const std::string& key, const std::string& val) ! 263: { ! 264: uint32 addr = 0; ! 265: ! 266: // 探す ! 267: for (const auto& p : luna1params) { ! 268: if (key == p.key) { ! 269: addr = p.addr; ! 270: break; ! 271: } ! 272: } ! 273: if (addr == 0) { ! 274: warnx("%s: Unknown key name", key.c_str()); ! 275: return false; ! 276: } ! 277: ! 278: // 値だけ更新でいいはず ! 279: write_string(addr + 16, val); ! 280: return true; ! 281: } ! 282: ! 283: // LUNA88K の場合、キーはフリーダムのように見える。 ! 284: // 更新したら true を返す。 ! 285: static bool ! 286: update_luna88k(const std::string& key, const std::string& val) ! 287: { ! 288: uint32 addr = 0; ! 289: ! 290: addr = find_string(key); ! 291: if (addr == 0) { ! 292: // 見付からなければ新規追加 ! 293: addr = find_string(""); ! 294: if (addr == 0) { ! 295: // 空きエントリがない ! 296: warnx("No spaces available"); ! 297: return false; ! 298: } ! 299: } ! 300: ! 301: // 追加の場合もあるので常に更新してしまう。副作用はないはず。 ! 302: write_string(addr, key); ! 303: write_string(addr + 16, val); ! 304: return true; ! 305: } ! 306: ! 307: // キーと値はいずれも 16文字未満なら '\0' 終端、 ! 308: // 16文字の場合は '\0' で終端せず 16文字とするフォーマット。 ! 309: ! 310: // 文字列を読み込む ! 311: static std::string ! 312: read_string(uint32 addr) ! 313: { ! 314: char buf[18]; ! 315: ! 316: memset(buf, 0, sizeof(buf)); ! 317: memcpy(buf, &nvram[addr], 16); ! 318: ! 319: return std::string(buf); ! 320: } ! 321: ! 322: // 文字列を書き込む ! 323: static void ! 324: write_string(uint32 addr, const std::string& str) ! 325: { ! 326: memset(&nvram[addr], 0, 16); ! 327: memcpy(&nvram[addr], str.data(), str.size()); ! 328: } ! 329: ! 330: // キーの位置を探す。主に LUNA88K 用。 ! 331: // str が "" でも探せる (新規追加時の空きエントリ探索用) ! 332: // 見付かればアドレスを返す。 ! 333: // 見付からなければ 0 を返す (0 は変数のアドレスとしては使えない) ! 334: static uint32 ! 335: find_string(const std::string& str) ! 336: { ! 337: for (uint32 addr = 0x20; addr < 0x560; addr += 0x20) { ! 338: std::string key = read_string(addr); ! 339: if (str == key) { ! 340: return addr; ! 341: } ! 342: } ! 343: ! 344: return 0; ! 345: } ! 346: ! 347: // NVRAM のチェックサムを返す ! 348: static uint8 ! 349: calc_checksum() ! 350: { ! 351: uint8 eor = 0; ! 352: ! 353: for (uint32 addr = 0x20; addr < 0x560; addr++) { ! 354: eor ^= nvram[addr]; ! 355: } ! 356: DPRINTF("Calculated checksum is 0x%02x\n", eor); ! 357: return eor; ! 358: } ! 359: ! 360: // NVRAM のチェックサムを照合し、正しければ true を返す ! 361: static bool ! 362: verify_checksum() ! 363: { ! 364: if (memcmp(&nvram[4], "<nv>", 4) != 0) { ! 365: DPRINTF("Magic string mismatch\n"); ! 366: return false; ! 367: } ! 368: ! 369: uint8 eor = calc_checksum(); ! 370: for (uint32 i = 0; i < 3; i++) { ! 371: if (nvram[0x1c + i] != eor) { ! 372: DPRINTF("Checksum[%d] mismatch\n", i); ! 373: return false; ! 374: } ! 375: } ! 376: DPRINTF("Checksum ok\n"); ! 377: return true; ! 378: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.