|
|
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.