Annotation of nono/util/nvramedit/nvramedit.cpp, revision 1.1.1.3

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: 
1.1.1.3 ! root      105:        maplen = 2048;
        !           106:        if (st.st_size == 2040) {
        !           107:                warnx("%s: Old filesize (2040 bytes). Current size is %zu bytes.\n",
        !           108:                        filename, maplen);
        !           109:        } else if (st.st_size < maplen) {
1.1       root      110:                errx(1, "%s: File too short.  Correct size is %zu bytes.\n",
                    111:                        filename, maplen);
                    112:        } else if (st.st_size > maplen) {
                    113:                warnx("%s: File too large.  Correct size is %zu bytes. "
                    114:                        "(Ignore the extra space)", filename, maplen);
                    115:        }
                    116: 
                    117:        int flag = MAP_FILE | MAP_SHARED;
                    118:        void *m = mmap(NULL, maplen, prot, flag, fd, 0);
                    119:        if (m == MAP_FAILED) {
                    120:                err(1, "%s: mmap", filename);
                    121:        }
                    122:        nvram = (uint8 *)m;
                    123: 
                    124:        if (ac == 0) {
                    125:                show();
                    126:        } else {
                    127:                edit(ac, av);
                    128:        }
                    129: 
                    130:        munmap(m, maplen);
                    131:        close(fd);
                    132:        return 0;
                    133: }
                    134: 
                    135: static void
                    136: usage()
                    137: {
                    138:        fprintf(stderr,
                    139:                "usage: %s [<options>] <NVRAM.DAT>             .. show all\n",
                    140:                getprogname());
                    141:        fprintf(stderr,
                    142:                "       %s [<options>] <NVRAM.DAT> <key=value> .. set\n",
                    143:                getprogname());
                    144:        fprintf(stderr,
                    145:                " -d : debug\n"
                    146:                " -f : force to show/set even if nvram is broken\n"
                    147:                " -n : show address\n"
                    148:        );
                    149:        exit(1);
                    150: }
                    151: 
                    152: // 表示する
                    153: static void
                    154: show()
                    155: {
                    156:        if (verify_checksum() == false && opt_force == false) {
                    157:                printf("Broken NVRAM.  Specify -f to force\n");
                    158:                return;
                    159:        }
                    160: 
                    161:        // LUNA-I の 0x560 には ENADDR があるけど、
                    162:        // チェックサムの範囲外だしどういう扱いなんだべ。
                    163:        uint32 end = 0x560;
                    164:        if (strcmp((const char *)&nvram[0x560], "ENADDR") == 0) {
                    165:                end = 0x580;
                    166:        }
                    167: 
                    168:        for (uint32 addr = 0x20; addr < end; addr += 0x20) {
                    169:                if (nvram[addr] == '\0') {
                    170:                        continue;
                    171:                }
                    172: 
                    173:                std::string key = read_string(addr);
                    174:                std::string val = read_string(addr + 16);
                    175: 
                    176:                if (opt_showaddr) {
                    177:                        printf("0x%04x: ", addr);
                    178:                }
                    179:                printf("%-16s = %s\n", key.c_str(), val.c_str());
                    180:        }
                    181: }
                    182: 
                    183: static void
                    184: edit(int ac, char *av[])
                    185: {
                    186:        if (verify_checksum() == false && opt_force == false) {
                    187:                printf("Broken NVRAM.  Specify -f to force\n");
                    188:                return;
                    189:        }
                    190: 
                    191:        // 機種によってたぶんフィールドの扱いが違う気がする。
1.1.1.2   root      192:        // LUNA-I か LUNA-88K か厳密には区別できない気がするけど、とりあえず
1.1       root      193:        // 自由フィールドっぽい 0x560 までより後ろに "ENADDR" があれば
                    194:        // LUNA-I ということにしてみる。LUNA-II と LUNA-88K2 は未調査。
                    195:        updatefunc updater;
                    196:        if (strcmp((const char *)&nvram[0x560], "ENADDR") == 0) {
                    197:                updater = update_luna1;
                    198:        } else {
                    199:                updater = update_luna88k;
                    200:        }
                    201: 
                    202:        int updated = 0;
                    203:        for (int i = 0; i < ac; i++) {
                    204:                const char *s;
                    205:                const char *e;
                    206: 
                    207:                s = av[i];
                    208:                e = strchr(s, '=');
                    209:                if (e == NULL) {
                    210:                        warnx("%s: syntax error", av[i]);
                    211:                        return;
                    212:                }
                    213: 
                    214:                std::string key(s, (e - s));
                    215:                std::string val(e + 1);
                    216: 
                    217:                if (key.empty()) {
                    218:                        warnx("%s: key must be specified", av[i]);
                    219:                        return;
                    220:                }
                    221:                if (val.empty()) {
                    222:                        warnx("%s: value must be specified", av[i]);
                    223:                        return;
                    224:                }
                    225: 
                    226:                if (updater(key, val)) {
                    227:                        updated++;
                    228:                }
                    229:        }
                    230: 
                    231:        // 更新があればチェックサムを再計算
                    232:        if (updated > 0) {
                    233:                uint8 eor = calc_checksum();
                    234:                for (int i = 0; i < 3; i++) {
                    235:                        nvram[0x1c + i] = eor;
                    236:                }
                    237:        }
                    238: }
                    239: 
                    240: // LUNA-I のパラメータ表
                    241: struct luna1param {
                    242:        uint32 addr;
                    243:        const char *key;
                    244: };
                    245: static std::vector<luna1param> luna1params = {
                    246:        { 0x0020, "BOOT" },
                    247:        { 0x0040, "HOST" },
                    248:        { 0x0060, "SERVER" },
                    249:        { 0x0080, "DKUNIT" },
                    250:        { 0x00a0, "DKPART" },
                    251:        { 0x00c0, "DKFILE" },
                    252:        { 0x00e0, "FLUNIT" },
                    253:        { 0x0100, "FLFILE" },
                    254:        { 0x0120, "STUNIT" },
                    255:        { 0x0140, "STFLNO" },
                    256:        { 0x0160, "STFILE" },
                    257:        { 0x0180, "ETBOOT" },
                    258:        { 0x01a0, "ETFILE" },
                    259:        { 0x0560, "ENADDR" },
                    260: };
                    261: 
                    262: // LUNA-I の場合、キーはたぶん固定じゃないかな。
                    263: // 更新したら true を返す。
                    264: static bool
                    265: update_luna1(const std::string& key, const std::string& val)
                    266: {
                    267:        uint32 addr = 0;
                    268: 
                    269:        // 探す
                    270:        for (const auto& p : luna1params) {
                    271:                if (key == p.key) {
                    272:                        addr = p.addr;
                    273:                        break;
                    274:                }
                    275:        }
                    276:        if (addr == 0) {
                    277:                warnx("%s: Unknown key name", key.c_str());
                    278:                return false;
                    279:        }
                    280: 
                    281:        // 値だけ更新でいいはず
                    282:        write_string(addr + 16, val);
                    283:        return true;
                    284: }
                    285: 
1.1.1.2   root      286: // LUNA-88K の場合、キーはフリーダムのように見える。
1.1       root      287: // 更新したら true を返す。
                    288: static bool
                    289: update_luna88k(const std::string& key, const std::string& val)
                    290: {
                    291:        uint32 addr = 0;
                    292: 
                    293:        addr = find_string(key);
                    294:        if (addr == 0) {
                    295:                // 見付からなければ新規追加
                    296:                addr = find_string("");
                    297:                if (addr == 0) {
                    298:                        // 空きエントリがない
                    299:                        warnx("No spaces available");
                    300:                        return false;
                    301:                }
                    302:        }
                    303: 
                    304:        // 追加の場合もあるので常に更新してしまう。副作用はないはず。
                    305:        write_string(addr, key);
                    306:        write_string(addr + 16, val);
                    307:        return true;
                    308: }
                    309: 
                    310: // キーと値はいずれも 16文字未満なら '\0' 終端、
                    311: // 16文字の場合は '\0' で終端せず 16文字とするフォーマット。
                    312: 
                    313: // 文字列を読み込む
                    314: static std::string
                    315: read_string(uint32 addr)
                    316: {
                    317:        char buf[18];
                    318: 
                    319:        memset(buf, 0, sizeof(buf));
                    320:        memcpy(buf, &nvram[addr], 16);
                    321: 
                    322:        return std::string(buf);
                    323: }
                    324: 
                    325: // 文字列を書き込む
                    326: static void
                    327: write_string(uint32 addr, const std::string& str)
                    328: {
                    329:        memset(&nvram[addr], 0, 16);
                    330:        memcpy(&nvram[addr], str.data(), str.size());
                    331: }
                    332: 
1.1.1.2   root      333: // キーの位置を探す。主に LUNA-88K 用。
1.1       root      334: // str が "" でも探せる (新規追加時の空きエントリ探索用)
                    335: // 見付かればアドレスを返す。
                    336: // 見付からなければ 0 を返す (0 は変数のアドレスとしては使えない)
                    337: static uint32
                    338: find_string(const std::string& str)
                    339: {
                    340:        for (uint32 addr = 0x20; addr < 0x560; addr += 0x20) {
                    341:                std::string key = read_string(addr);
                    342:                if (str == key) {
                    343:                        return addr;
                    344:                }
                    345:        }
                    346: 
                    347:        return 0;
                    348: }
                    349: 
                    350: // NVRAM のチェックサムを返す
                    351: static uint8
                    352: calc_checksum()
                    353: {
                    354:        uint8 eor = 0;
                    355: 
                    356:        for (uint32 addr = 0x20; addr < 0x560; addr++) {
                    357:                eor ^= nvram[addr];
                    358:        }
                    359:        DPRINTF("Calculated checksum is 0x%02x\n", eor);
                    360:        return eor;
                    361: }
                    362: 
                    363: // NVRAM のチェックサムを照合し、正しければ true を返す
                    364: static bool
                    365: verify_checksum()
                    366: {
                    367:        if (memcmp(&nvram[4], "<nv>", 4) != 0) {
                    368:                DPRINTF("Magic string mismatch\n");
                    369:                return false;
                    370:        }
                    371: 
                    372:        uint8 eor = calc_checksum();
                    373:        for (uint32 i = 0; i < 3; i++) {
                    374:                if (nvram[0x1c + i] != eor) {
                    375:                        DPRINTF("Checksum[%d] mismatch\n", i);
                    376:                        return false;
                    377:                }
                    378:        }
                    379:        DPRINTF("Checksum ok\n");
                    380:        return true;
                    381: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.