/* * Amiga NVRAM tool * * (C) Copyright 2009 Geert Uytterhoeven * * This file is subject to the terms and conditions of the GNU General Public * License. */ #include #include #include #include #include #include #include #define NVRAM_FILE "/sys/devices/platform/rtc-rp5c01/nvram" #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) static const char *program_name; static const char *opt_nvram = NVRAM_FILE; static _Bool opt_checksum; static _Bool opt_quiet; static char *opt_field; struct nvram { union { uint32_t words[3]; uint8_t bytes[12]; struct { uint32_t amiga; uint32_t amix; uint32_t shared; } bits; }; uint8_t checksum; } __attribute__ ((packed)); static struct nvram nvram, backup; static const struct nvram_field { const char *name; unsigned int addr; unsigned int len; } nvram_fields[] = { { "amiga", 0, 32 }, { "amiga.amnesia", 0, 1 }, { "amiga.scsi_timeout", 1, 1 }, { "amiga.scsi_luns", 2, 1 }, { "amix", 32, 32 }, { "shared", 64, 32 }, { "shared.amnesia", 64, 1 }, { "shared.scsi_host_id", 65, 3 }, { "shared.scsi_sync_xfer", 68, 1 }, { "shared.scsi_fast_sync", 69, 1 }, { "shared.scsi_tag_queues", 70, 1 }, { "shared.ide_extra_wait_addr", 71, 1 }, }; static void msg(const char *fmt, ...) __attribute__((format(printf, 1, 2))); static void msg(const char *fmt, ...) { va_list ap; if (!opt_quiet) { va_start(ap, fmt); vprintf(fmt, ap); va_end(ap); } } static int nvram_read(void) { int file; ssize_t n; file = open(opt_nvram, O_RDONLY); if (file < 0) { perror("Cannot open NVRAM for reading"); return -1; } n = read(file, &nvram, sizeof(nvram)); if (n < 0) { perror("Cannot read NVRAM"); goto failed; } if (n < sizeof(nvram.bytes)) { fprintf(stderr, "Could read only %zu NVRAM bytes\n", n); goto failed; } if (n < sizeof(nvram)) { fprintf(stderr, "Warning: Could not read checksum, assuming zero\n"); nvram.checksum = 0; } close(file); memcpy(&backup, &nvram, sizeof(nvram)); return 0; failed: close(file); return -1; } static int nvram_write(void) { int file; ssize_t n; if (!memcmp(&nvram, &backup, sizeof(nvram))) return 0; msg("Saving NVRAM\n"); file = open(opt_nvram, O_WRONLY); if (file < 0) { perror("Cannot open NVRAM for writing"); return -1; } n = write(file, &nvram, sizeof(nvram)); if (n < 0) { perror("Cannot write NVRAM"); goto failed; } if (n < sizeof(nvram)) { fprintf(stderr, "Could write only %zu NVRAM bytes\n", n); goto failed; } close(file); return 0; failed: close(file); return -1; } static const uint8_t table[16] = { 0x00, 0x57, 0xae, 0xf9, 0x0b, 0x5c, 0xa5, 0xf2, 0x16, 0x41, 0xb8, 0x7f, 0x1d, 0x4a, 0xb3, 0xe4 }; static uint8_t checksum_nibble(uint8_t sum, uint8_t nibble) { uint8_t index = (sum >> 4) ^ nibble; sum = (sum << 4) ^ table[index]; return sum; } static uint8_t nvram_checksum(void) { unsigned int i; uint8_t sum = 0xff; for (i = 0; i < sizeof(nvram.bytes); i++) { sum = checksum_nibble(sum, nvram.bytes[i] >> 4); sum = checksum_nibble(sum, nvram.bytes[i] & 0x0f); } return sum; } uint32_t be32_to_cpu(uint32_t x) { uint8_t bytes[4]; *(uint32_t *)bytes = x; return bytes[0] << 24 | bytes[1] << 16 | bytes[2] << 8 | bytes[3]; } static uint32_t nvram_get_field(const struct nvram_field *field) { unsigned int word = field->addr / 32; unsigned int offset = field->addr % 32; unsigned int len = field->len; uint32_t val = be32_to_cpu(nvram.words[word]); if (len == 32) return val; val >>= 32 - offset - len; val &= (1 << len) - 1; return val; } static void nvram_set_field(const struct nvram_field *field, uint32_t val) { unsigned int word = field->addr / 32; unsigned int offset = field->addr % 32; unsigned int len = field->len; uint32_t mask, shift, t; if (len == 32) { nvram.words[word] = be32_to_cpu(val); return; } mask = (1 << len) - 1; val &= mask; shift = 32 - offset - len; val <<= shift; mask <<= shift; t = be32_to_cpu(nvram.words[word]); nvram.words[word] = be32_to_cpu((t & ~mask) | val); } static const struct nvram_field *nvram_find_field(const char *name) { unsigned int i; for (i = 0; i < ARRAY_SIZE(nvram_fields); i++) if (!strcmp(nvram_fields[i].name, name)) return &nvram_fields[i]; return NULL; } static void nvram_dump(void) { unsigned int i; unsigned int max_name_len = 0; uint8_t sum; for (i = 0; i < ARRAY_SIZE(nvram_fields); i++) { size_t len = strlen(nvram_fields[i].name); if (len > max_name_len) max_name_len = len; } for (i = 0; i < ARRAY_SIZE(nvram_fields); i++) { const struct nvram_field *field = &nvram_fields[i]; uint32_t val = nvram_get_field(field); if (field->len == 32) printf("%-*s = 0x%08x\n", max_name_len, field->name, val); else printf("%-*s = %u\n", max_name_len, field->name, val); } printf("%-*s = 0x%02x ", max_name_len, "checksum", nvram.checksum); sum = nvram_checksum(); if (sum == nvram.checksum) puts("[correct]"); else printf("[not correct, should be 0x%02x]\n", sum); } static void usage(void) __attribute__ ((noreturn)); static void usage(void) { fprintf(stderr, "\n" "%s: [options] [[=]]\n\n" "Valid options are:\n" " -c, --checksum Recalculate checksum\n" " (auto-enabled when a field is modified)\n" " -f, --file file Specify NVRAM file\n" " (default: %s)\n" " -h, --help Display this usage information\n" " -q, --quiet Enable quiet mode\n" "\n", program_name, opt_nvram); exit(1); } int main(int argc, char *argv[]) { const char *slash = strrchr(argv[0], '/'); program_name = slash ? slash + 1 : argv[0]; int error; while (argc > 1) { if (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) usage(); else if (!strcmp(argv[1], "-c") || !strcmp(argv[1], "--checksum")) { opt_checksum = 1; argv++; argc--; } else if (!strcmp(argv[1], "-f") || !strcmp(argv[1], "--file")) { if (argc <= 2) usage(); opt_nvram = argv[2]; argv += 2; argc -= 2; } else if (!strcmp(argv[1], "-q") || !strcmp(argv[1], "--quiet")) { opt_quiet = 1; argv++; argc--; } else if (!opt_field) { opt_field = argv[1]; argv++; argc--; } else usage(); } error = nvram_read(); if (error) exit(-1); if (opt_field) { uint32_t val; char *equal = strchr(opt_field, '='); if (equal) *equal = '\0'; const struct nvram_field *field = nvram_find_field(opt_field); if (!field) { fprintf(stderr, "Cannot find NVRAM field %s\n", opt_field); exit(-1); } if (!equal) { val = nvram_get_field(field); if (field->len == 32) printf("%s = 0x%08x\n", field->name, val); else printf("%s = %u\n", field->name, val); } else { val = strtoul(equal+1, NULL, 0); nvram_set_field(field, val); opt_checksum = 1; } } else if (!opt_quiet) nvram_dump(); if (opt_checksum) { uint8_t sum = nvram_checksum(); if (sum != nvram.checksum) { msg("Correcting checksum\n"); nvram.checksum = sum; } } error = nvram_write(); if (error) exit(-1); exit(0); }