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

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: }

unix.superglobalmegacorp.com

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