Initial import of amiga-nvram
Signed-off-by: Geert Uytterhoeven <geert@linux-m68k.org>
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..536b35c
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,16 @@
+
+CC = $(CROSS_COMPILE)gcc
+
+CFLAGS = -Wall -Werror -O3 -fomit-frame-pointer -fno-strict-aliasing
+
+.PHONY: all clean
+
+
+all: amiga-nvram
+
+amiga-nvram: amiga-nvram.c
+ $(CC) $(CFLAGS) -o amiga-nvram amiga-nvram.c
+
+clean:
+ $(RM) amiga-nvram
+
diff --git a/amiga-nvram.c b/amiga-nvram.c
new file mode 100644
index 0000000..e71612a
--- /dev/null
+++ b/amiga-nvram.c
@@ -0,0 +1,243 @@
+/*
+ * Amiga NVRAM tool
+ *
+ * (C) Copyright 2009 Geert Uytterhoeven
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.
+ */
+
+#include <assert.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define NVRAM_FILE "/sys/devices/platform/rtc-rp5c01/nvram"
+
+
+static const char *program_name;
+
+static const char *opt_nvram = NVRAM_FILE;
+static _Bool opt_checksum;
+static _Bool opt_quiet;
+
+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 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 file;
+ }
+
+ n = read(file, &nvram, sizeof(nvram));
+ if (n < 0) {
+ perror("Cannot read NVRAM");
+ return n;
+ }
+ if (n < sizeof(nvram.bytes)) {
+ fprintf(stderr, "Could read only %zu NVRAM bytes\n", n);
+ return -1;
+ }
+ if (n < sizeof(nvram)) {
+ fprintf(stderr,
+ "Warning: Could not read checksum, assuming zero\n");
+ nvram.checksum = 0;
+ return 0;
+ }
+
+ close(file);
+
+ memcpy(&backup, &nvram, sizeof(nvram));
+ return 0;
+}
+
+
+static int nvram_write(void)
+{
+ int file;
+ ssize_t n;
+
+ file = open(opt_nvram, O_WRONLY);
+ if (file < 0) {
+ perror("Cannot open NVRAM for writing");
+ return file;
+ }
+
+ n = write(file, &nvram, sizeof(nvram));
+ if (n < 0) {
+ perror("Cannot write NVRAM");
+ return n;
+ }
+ if (n < sizeof(nvram)) {
+ fprintf(stderr, "Could write only %zu NVRAM bytes\n", n);
+ return -1;
+ }
+
+ close(file);
+ return 0;
+}
+
+
+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)
+{
+#if 0
+ nibble = sum ^ (nibble << 4);
+ nibble = (nibble >> 4) | (nibble << 4);
+ sum = (nibble & 0xf0) ^ table[nibble & 0x0f];
+ return sum;
+#else
+ uint8_t index = (sum >> 4) ^ nibble;
+ sum = (sum << 4) ^ table[index];
+ return sum;
+#endif
+}
+
+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 void nvram_dump(void)
+{
+ printf("Amiga bits: 0x%08x\n", be32_to_cpu(nvram.bits.amiga));
+ printf("AMIX bits: 0x%08x\n", be32_to_cpu(nvram.bits.amix));
+ printf("Shared bits: 0x%08x\n", be32_to_cpu(nvram.bits.shared));
+}
+
+
+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"
+ " -f, --file file Specify NVRAM file (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[])
+{
+ program_name = argv[0];
+ int error;
+ uint8_t sum;
+
+ 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
+ usage();
+ }
+
+ error = nvram_read();
+ if (error)
+ exit(-1);
+
+ sum = nvram_checksum();
+
+ if (!opt_quiet) {
+ nvram_dump();
+ printf("Checksum: 0x%02x ", nvram.checksum);
+ if (sum == nvram.checksum)
+ puts("[correct]");
+ else
+ printf("[not correct, should be 0x%02x]\n", sum);
+ }
+
+ if (opt_checksum && sum != nvram.checksum) {
+ msg("Correcting checksum\n");
+ nvram.checksum = sum;
+ }
+
+ if (memcmp(&nvram, &backup, sizeof(nvram))) {
+ msg("Saving NVRAM\n");
+ error = nvram_write();
+ if (error)
+ exit(-1);
+ }
+
+ exit(0);
+}
+