|
|
nono 1.5.0
//
// nono
// Copyright (C) 2021 nono project
// Licensed under nono-license.txt
//
//
// LUNA NVRAM.DAT エディタ
//
#include "header.h"
#include "mystring.h"
#include <cstdio>
#include <string>
#include <vector>
#include <err.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
using updatefunc = bool (*)(const std::string&, const std::string&);
[[noreturn]] static void usage();
static void show();
static void edit(int ac, char *av[]);
static bool update_luna1(const std::string&, const std::string&);
static bool update_luna88k(const std::string&, const std::string&);
static std::string read_string(uint32 addr);
static void write_string(uint32 addr, const std::string&);
static uint32 find_string(const std::string&);
static uint8 calc_checksum();
static bool verify_checksum();
static int debug;
static bool opt_force;
static bool opt_showaddr;
static uint8 *nvram;
#define DPRINTF(fmt...) do { \
if (debug) { \
printf("%s: ", __func__); \
printf(fmt); \
} \
} while (0)
int
main(int ac, char *av[])
{
const char *filename;
struct stat st;
int c;
int fd;
int mode;
int prot;
size_t maplen;
while ((c = getopt(ac, av, "dfnh")) != -1) {
switch (c) {
case 'd':
debug++;
break;
case 'f':
opt_force = true;
break;
case 'n':
opt_showaddr = true;
break;
case 'h':
default:
usage();
break;
}
}
ac -= optind;
av += optind;
if (ac == 0) {
usage();
}
filename = av[0];
ac--;
av++;
if (ac == 0) {
// show
mode = O_RDONLY;
prot = PROT_READ;
} else {
// edit
mode = O_RDWR;
prot = PROT_READ | PROT_WRITE;
}
fd = open(filename, mode);
if (fd < 0) {
err(1, "%s: open", filename);
}
if (fstat(fd, &st) < 0) {
err(1, "%s: fstat", filename);
}
maplen = 2048;
if (st.st_size == 2040) {
warnx("%s: Old filesize (2040 bytes). Current size is %zu bytes.\n",
filename, maplen);
} else if (st.st_size < maplen) {
errx(1, "%s: File too short. Correct size is %zu bytes.\n",
filename, maplen);
} else if (st.st_size > maplen) {
warnx("%s: File too large. Correct size is %zu bytes. "
"(Ignore the extra space)", filename, maplen);
}
int flag = MAP_FILE | MAP_SHARED;
void *m = mmap(NULL, maplen, prot, flag, fd, 0);
if (m == MAP_FAILED) {
err(1, "%s: mmap", filename);
}
nvram = (uint8 *)m;
if (ac == 0) {
show();
} else {
edit(ac, av);
}
munmap(m, maplen);
close(fd);
return 0;
}
static void
usage()
{
fprintf(stderr,
"usage: %s [<options>] <NVRAM.DAT> .. show all\n",
getprogname());
fprintf(stderr,
" %s [<options>] <NVRAM.DAT> <key=value> .. set\n",
getprogname());
fprintf(stderr,
" -d : debug\n"
" -f : force to show/set even if nvram is broken\n"
" -n : show address\n"
);
exit(1);
}
// 表示する
static void
show()
{
if (verify_checksum() == false && opt_force == false) {
printf("Broken NVRAM. Specify -f to force\n");
return;
}
// LUNA-I の 0x560 には ENADDR があるけど、
// チェックサムの範囲外だしどういう扱いなんだべ。
uint32 end = 0x560;
if (strcmp((const char *)&nvram[0x560], "ENADDR") == 0) {
end = 0x580;
}
for (uint32 addr = 0x20; addr < end; addr += 0x20) {
if (nvram[addr] == '\0') {
continue;
}
std::string key = read_string(addr);
std::string val = read_string(addr + 16);
if (opt_showaddr) {
printf("0x%04x: ", addr);
}
printf("%-16s = %s\n", key.c_str(), val.c_str());
}
}
static void
edit(int ac, char *av[])
{
if (verify_checksum() == false && opt_force == false) {
printf("Broken NVRAM. Specify -f to force\n");
return;
}
// 機種によってたぶんフィールドの扱いが違う気がする。
// LUNA-I か LUNA-88K か厳密には区別できない気がするけど、とりあえず
// 自由フィールドっぽい 0x560 までより後ろに "ENADDR" があれば
// LUNA-I ということにしてみる。LUNA-II と LUNA-88K2 は未調査。
updatefunc updater;
if (strcmp((const char *)&nvram[0x560], "ENADDR") == 0) {
updater = update_luna1;
} else {
updater = update_luna88k;
}
int updated = 0;
for (int i = 0; i < ac; i++) {
const char *s;
const char *e;
s = av[i];
e = strchr(s, '=');
if (e == NULL) {
warnx("%s: syntax error", av[i]);
return;
}
std::string key(s, (e - s));
std::string val(e + 1);
if (key.empty()) {
warnx("%s: key must be specified", av[i]);
return;
}
if (val.empty()) {
warnx("%s: value must be specified", av[i]);
return;
}
if (updater(key, val)) {
updated++;
}
}
// 更新があればチェックサムを再計算
if (updated > 0) {
uint8 eor = calc_checksum();
for (int i = 0; i < 3; i++) {
nvram[0x1c + i] = eor;
}
}
}
// LUNA-I のパラメータ表
struct luna1param {
uint32 addr;
const char *key;
};
static std::vector<luna1param> luna1params = {
{ 0x0020, "BOOT" },
{ 0x0040, "HOST" },
{ 0x0060, "SERVER" },
{ 0x0080, "DKUNIT" },
{ 0x00a0, "DKPART" },
{ 0x00c0, "DKFILE" },
{ 0x00e0, "FLUNIT" },
{ 0x0100, "FLFILE" },
{ 0x0120, "STUNIT" },
{ 0x0140, "STFLNO" },
{ 0x0160, "STFILE" },
{ 0x0180, "ETBOOT" },
{ 0x01a0, "ETFILE" },
{ 0x0560, "ENADDR" },
};
// LUNA-I の場合、キーはたぶん固定じゃないかな。
// 更新したら true を返す。
static bool
update_luna1(const std::string& key, const std::string& val)
{
uint32 addr = 0;
// 探す
for (const auto& p : luna1params) {
if (key == p.key) {
addr = p.addr;
break;
}
}
if (addr == 0) {
warnx("%s: Unknown key name", key.c_str());
return false;
}
// 値だけ更新でいいはず
write_string(addr + 16, val);
return true;
}
// LUNA-88K の場合、キーはフリーダムのように見える。
// 更新したら true を返す。
static bool
update_luna88k(const std::string& key, const std::string& val)
{
uint32 addr = 0;
addr = find_string(key);
if (addr == 0) {
// 見付からなければ新規追加
addr = find_string("");
if (addr == 0) {
// 空きエントリがない
warnx("No spaces available");
return false;
}
}
// 追加の場合もあるので常に更新してしまう。副作用はないはず。
write_string(addr, key);
write_string(addr + 16, val);
return true;
}
// キーと値はいずれも 16文字未満なら '\0' 終端、
// 16文字の場合は '\0' で終端せず 16文字とするフォーマット。
// 文字列を読み込む
static std::string
read_string(uint32 addr)
{
char buf[18];
memset(buf, 0, sizeof(buf));
memcpy(buf, &nvram[addr], 16);
return std::string(buf);
}
// 文字列を書き込む
static void
write_string(uint32 addr, const std::string& str)
{
memset(&nvram[addr], 0, 16);
memcpy(&nvram[addr], str.data(), str.size());
}
// キーの位置を探す。主に LUNA-88K 用。
// str が "" でも探せる (新規追加時の空きエントリ探索用)
// 見付かればアドレスを返す。
// 見付からなければ 0 を返す (0 は変数のアドレスとしては使えない)
static uint32
find_string(const std::string& str)
{
for (uint32 addr = 0x20; addr < 0x560; addr += 0x20) {
std::string key = read_string(addr);
if (str == key) {
return addr;
}
}
return 0;
}
// NVRAM のチェックサムを返す
static uint8
calc_checksum()
{
uint8 eor = 0;
for (uint32 addr = 0x20; addr < 0x560; addr++) {
eor ^= nvram[addr];
}
DPRINTF("Calculated checksum is 0x%02x\n", eor);
return eor;
}
// NVRAM のチェックサムを照合し、正しければ true を返す
static bool
verify_checksum()
{
if (memcmp(&nvram[4], "<nv>", 4) != 0) {
DPRINTF("Magic string mismatch\n");
return false;
}
uint8 eor = calc_checksum();
for (uint32 i = 0; i < 3; i++) {
if (nvram[0x1c + i] != eor) {
DPRINTF("Checksum[%d] mismatch\n", i);
return false;
}
}
DPRINTF("Checksum ok\n");
return true;
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.