Annotation of nono/util/nvramedit/nvramedit.cpp, revision 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.