summaryrefslogtreecommitdiff
path: root/src/nvram.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvram.c')
-rw-r--r--src/nvram.c450
1 files changed, 450 insertions, 0 deletions
diff --git a/src/nvram.c b/src/nvram.c
new file mode 100644
index 0000000..58d63df
--- /dev/null
+++ b/src/nvram.c
@@ -0,0 +1,450 @@
+/*
+ * <nvram.c>
+ *
+ * Open Hack'Ware BIOS NVRAM management routines.
+ *
+ * Copyright (c) 2004-2005 Jocelyn Mayer
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License V2
+ * as published by the Free Software Foundation
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "bios.h"
+
+#define NVRAM_MAX_SIZE 0x2000
+#define NVRAM_IO_BASE 0x0074
+
+struct nvram_t {
+ uint16_t io_base;
+ uint16_t size;
+};
+
+/* NVRAM access */
+static void NVRAM_set_byte (nvram_t *nvram, uint32_t addr, uint8_t value)
+{
+ NVRAM_write(nvram, addr, value);
+}
+
+static uint8_t NVRAM_get_byte (nvram_t *nvram, uint16_t addr)
+{
+ return NVRAM_read(nvram, addr);
+}
+
+static void NVRAM_set_word (nvram_t *nvram, uint16_t addr, uint16_t value)
+{
+ NVRAM_write(nvram, addr, value >> 8);
+ NVRAM_write(nvram, addr + 1, value);
+}
+
+static uint16_t NVRAM_get_word (nvram_t *nvram, uint16_t addr)
+{
+ uint16_t tmp;
+
+ tmp = NVRAM_read(nvram, addr) << 8;
+ tmp |= NVRAM_read(nvram, addr + 1);
+
+ return tmp;
+}
+
+static void NVRAM_set_lword (nvram_t *nvram, uint16_t addr, uint32_t value)
+{
+ NVRAM_write(nvram, addr, value >> 24);
+ NVRAM_write(nvram, addr + 1, value >> 16);
+ NVRAM_write(nvram, addr + 2, value >> 8);
+ NVRAM_write(nvram, addr + 3, value);
+}
+
+static uint32_t NVRAM_get_lword (nvram_t *nvram, uint16_t addr)
+{
+ uint32_t tmp;
+
+ tmp = NVRAM_read(nvram, addr) << 24;
+ tmp |= NVRAM_read(nvram, addr + 1) << 16;
+ tmp |= NVRAM_read(nvram, addr + 2) << 8;
+ tmp |= NVRAM_read(nvram, addr + 3);
+
+ return tmp;
+}
+
+static void NVRAM_set_string (nvram_t *nvram, uint32_t addr,
+ const unsigned char *str, uint32_t max)
+{
+ uint32_t i;
+
+ for (i = 0; i < max && str[i] != '\0'; i++) {
+ NVRAM_write(nvram, addr + i, str[i]);
+ }
+ NVRAM_write(nvram, addr + i, '\0');
+}
+
+static int NVRAM_get_string (nvram_t *nvram, uint8_t *dst,
+ uint16_t addr, int max)
+{
+ int i;
+
+ memset(dst, 0, max);
+ for (i = 0; i < max; i++) {
+ dst[i] = NVRAM_get_byte(nvram, addr + i);
+ if (dst[i] == '\0')
+ break;
+ }
+
+ return i;
+}
+
+static uint16_t NVRAM_crc_update (uint16_t prev, uint16_t value)
+{
+ uint16_t tmp;
+ uint16_t pd, pd1, pd2;
+
+ tmp = prev >> 8;
+ pd = prev ^ value;
+ pd1 = pd & 0x000F;
+ pd2 = ((pd >> 4) & 0x000F) ^ pd1;
+ tmp ^= (pd1 << 3) | (pd1 << 8);
+ tmp ^= pd2 | (pd2 << 7) | (pd2 << 12);
+
+ return tmp;
+}
+
+static uint16_t NVRAM_compute_crc (nvram_t *nvram,
+ uint32_t start, uint32_t count)
+{
+ uint32_t i;
+ uint16_t crc = 0xFFFF;
+ int odd;
+
+ odd = count & 1;
+ count &= ~1;
+ for (i = 0; i != count; i++) {
+ crc = NVRAM_crc_update(crc, NVRAM_get_word(nvram, start + i));
+ }
+ if (odd) {
+ crc = NVRAM_crc_update(crc, NVRAM_get_byte(nvram, start + i) << 8);
+ }
+
+ return crc;
+}
+
+/* Format NVRAM for PREP target */
+static int NVRAM_prep_format (nvram_t *nvram)
+{
+#define NVRAM_PREP_OSAREA_SIZE 512
+#define NVRAM_PREP_CONFSIZE 1024
+ uint16_t crc;
+
+ /* NVRAM header */
+ /* 0x00: NVRAM size in kB */
+ NVRAM_set_word(nvram, 0x00, nvram->size >> 10);
+ /* 0x02: NVRAM version */
+ NVRAM_set_byte(nvram, 0x02, 0x01);
+ /* 0x03: NVRAM revision */
+ NVRAM_set_byte(nvram, 0x03, 0x01);
+ /* 0x08: last OS */
+ NVRAM_set_byte(nvram, 0x08, 0x00); /* Unknown */
+ /* 0x09: endian */
+ NVRAM_set_byte(nvram, 0x09, 'B'); /* Big-endian */
+ /* 0x0A: OSArea usage */
+ NVRAM_set_byte(nvram, 0x0A, 0x00); /* Empty */
+ /* 0x0B: PM mode */
+ NVRAM_set_byte(nvram, 0x0B, 0x00); /* Normal */
+ /* Restart block description record */
+ /* 0x0C: restart block version */
+ NVRAM_set_word(nvram, 0x0C, 0x01);
+ /* 0x0E: restart block revision */
+ NVRAM_set_word(nvram, 0x0E, 0x01);
+ /* 0x20: restart address */
+ NVRAM_set_lword(nvram, 0x20, 0x00);
+ /* 0x24: save area address */
+ NVRAM_set_lword(nvram, 0x24, 0x00);
+ /* 0x28: save area length */
+ NVRAM_set_lword(nvram, 0x28, 0x00);
+ /* 0x1C: checksum of restart block */
+ crc = NVRAM_compute_crc(nvram, 0x0C, 32);
+ NVRAM_set_word(nvram, 0x1C, crc);
+
+ /* Security section */
+ /* Set all to zero */
+ /* 0xC4: pointer to global environment area */
+ NVRAM_set_lword(nvram, 0xC4, 0x0100);
+ /* 0xC8: size of global environment area */
+ NVRAM_set_lword(nvram, 0xC8, nvram->size - NVRAM_PREP_OSAREA_SIZE -
+ NVRAM_PREP_CONFSIZE - 0x0100);
+ /* 0xD4: pointer to configuration area */
+ NVRAM_set_lword(nvram, 0xD4, nvram->size - NVRAM_PREP_CONFSIZE);
+ /* 0xD8: size of configuration area */
+ NVRAM_set_lword(nvram, 0xD8, NVRAM_PREP_CONFSIZE);
+ /* 0xE8: pointer to OS specific area */
+ NVRAM_set_lword(nvram, 0xE8, nvram->size - NVRAM_PREP_CONFSIZE
+ - NVRAM_PREP_OSAREA_SIZE);
+ /* 0xD8: size of OS specific area */
+ NVRAM_set_lword(nvram, 0xEC, NVRAM_PREP_OSAREA_SIZE);
+
+ /* Configuration area */
+
+ /* 0x04: checksum 0 => OS area */
+ crc = NVRAM_compute_crc(nvram, 0x00, nvram->size - NVRAM_PREP_CONFSIZE -
+ NVRAM_PREP_OSAREA_SIZE);
+ NVRAM_set_word(nvram, 0x04, crc);
+ /* 0x06: checksum of config area */
+ crc = NVRAM_compute_crc(nvram, nvram->size - NVRAM_PREP_CONFSIZE,
+ NVRAM_PREP_CONFSIZE);
+ NVRAM_set_word(nvram, 0x06, crc);
+
+ return 0;
+}
+
+static uint8_t NVRAM_chrp_chksum (nvram_t *nvram, uint16_t pos)
+{
+ uint16_t sum, end;
+
+ end = pos + 0x10;
+ sum = NVRAM_get_byte(nvram, pos);
+ for (pos += 2; pos < end; pos++) {
+ sum += NVRAM_get_byte(nvram, pos);
+ }
+ while (sum > 0xFF) {
+ sum = (sum & 0xFF) + (sum >> 8);
+ }
+
+ return sum;
+}
+
+static int NVRAM_chrp_format (unused nvram_t *nvram)
+{
+ uint8_t chksum;
+
+ /* Mark NVRAM as free */
+ NVRAM_set_byte(nvram, 0x00, 0x5A);
+ NVRAM_set_byte(nvram, 0x01, 0x00);
+ NVRAM_set_word(nvram, 0x02, 0x2000);
+ NVRAM_set_string(nvram, 0x04, "wwwwwwwwwwww", 12);
+ chksum = NVRAM_chrp_chksum(nvram, 0x00);
+ NVRAM_set_byte(nvram, 0x01, chksum);
+
+ return 0;
+}
+
+#if 0
+static uint16_t NVRAM_mac99_chksum (nvram_t *nvram,
+ uint16_t start, uint16_t len)
+ int cnt;
+ u32 low, high;
+
+ buffer += CORE99_ADLER_START;
+ low = 1;
+ high = 0;
+ for (cnt=0; cnt<(NVRAM_SIZE-CORE99_ADLER_START); cnt++) {
+ if ((cnt % 5000) == 0) {
+ high %= 65521UL;
+ high %= 65521UL;
+ }
+ low += buffer[cnt];
+ high += low;
+ }
+ low %= 65521UL;
+ high %= 65521UL;
+
+ return (high << 16) | low;
+{
+ uint16_t pos;
+ uint8_t tmp, sum;
+
+ sum = 0;
+ for (pos = start; pos < (start + len); pos++) {
+ tmp = sum + NVRAM_get_byte(nvram, pos);
+ if (tmp < sum)
+ tmp++;
+ sum = tmp;
+ }
+
+ return sum;
+}
+#endif
+
+static int NVRAM_mac99_format (nvram_t *nvram)
+{
+ uint8_t chksum;
+
+ /* Mark NVRAM as free */
+ NVRAM_set_byte(nvram, 0x00, 0x5A);
+ NVRAM_set_byte(nvram, 0x01, 0x00);
+ NVRAM_set_word(nvram, 0x02, 0x2000);
+ NVRAM_set_string(nvram, 0x04, "wwwwwwwwwwww", 12);
+ chksum = NVRAM_chrp_chksum(nvram, 0x00);
+ NVRAM_set_byte(nvram, 0x01, chksum);
+
+ return 0;
+}
+
+static int NVRAM_pop_format (unused nvram_t *nvram)
+{
+ /* TODO */
+ return -1;
+}
+
+/* Interface */
+uint8_t NVRAM_read (nvram_t *nvram, uint32_t addr)
+{
+ outb(nvram->io_base + 0x00, addr);
+ outb(nvram->io_base + 0x01, addr >> 8);
+
+ return inb(NVRAM_IO_BASE + 0x03);
+}
+
+void NVRAM_write (nvram_t *nvram, uint32_t addr, uint8_t value)
+{
+ outb(nvram->io_base + 0x00, addr);
+ outb(nvram->io_base + 0x01, addr >> 8);
+ outb(nvram->io_base + 0x03, value);
+}
+
+uint16_t NVRAM_get_size (nvram_t *nvram)
+{
+ return nvram->size;
+}
+
+int NVRAM_format (nvram_t *nvram)
+{
+ int ret;
+
+ {
+ uint16_t pos;
+
+ for (pos = 0; pos < nvram->size; pos += 4)
+ NVRAM_set_lword(nvram, pos, 0);
+ }
+ switch (arch) {
+ case ARCH_PREP:
+ ret = NVRAM_prep_format(nvram);
+ break;
+ case ARCH_CHRP:
+ ret = NVRAM_chrp_format(nvram);
+ break;
+ case ARCH_MAC99:
+ ret = NVRAM_mac99_format(nvram);
+ break;
+ case ARCH_POP:
+ ret = NVRAM_pop_format(nvram);
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+
+ return ret;
+}
+
+/* HACK... */
+extern int vga_width, vga_height, vga_depth;
+
+static nvram_t global_nvram;
+
+nvram_t *NVRAM_get_config (uint32_t *RAM_size, int *boot_device,
+ void **boot_image, uint32_t *boot_size,
+ void **cmdline, uint32_t *cmdline_size,
+ void **ramdisk, uint32_t *ramdisk_size)
+{
+ unsigned char sign[16];
+ nvram_t *nvram;
+ uint32_t lword;
+ uint16_t NVRAM_size, crc;
+ uint8_t byte;
+
+#if 0
+ nvram = malloc(sizeof(nvram_t));
+ if (nvram == NULL)
+ return NULL;
+#else
+ nvram = &global_nvram;
+#endif
+ nvram->io_base = NVRAM_IO_BASE;
+ /* Pre-initialised NVRAM is not supported any more */
+ if (NVRAM_get_string(nvram, sign, 0x00, 0x10) <= 0 ||
+ strcmp(sign, "QEMU_BIOS") != 0) {
+ ERROR("Wrong NVRAM signature %s\n", sign);
+ return NULL;
+ }
+ /* Check structure version */
+ lword = NVRAM_get_lword(nvram, 0x10);
+ if (lword != 0x00000002) {
+ ERROR("Wrong NVRAM structure version: %0x\n", lword);
+ return NULL;
+ }
+ /* Check CRC */
+ crc = NVRAM_compute_crc(nvram, 0x00, 0xF8);
+ if (NVRAM_get_word(nvram, 0xFC) != crc) {
+ ERROR("Invalid NVRAM structure CRC: %0x <=> %0x\n", crc,
+ NVRAM_get_word(nvram, 0xFC));
+ return NULL;
+ }
+ NVRAM_size = NVRAM_get_word(nvram, 0x14);
+ if ((NVRAM_size & 0x100) != 0x00 || NVRAM_size < 0x400 ||
+ NVRAM_size > 0x2000) {
+ ERROR("Invalid NVRAM size: %d\n", NVRAM_size);
+ return NULL;
+ }
+ nvram->size = NVRAM_size;
+ if (NVRAM_get_string(nvram, sign, 0x20, 0x10) < 0) {
+ ERROR("Unable to get architecture from NVRAM\n");
+ return NULL;
+ }
+ if (strcmp(sign, "PREP") == 0) {
+ arch = ARCH_PREP;
+ } else if (strcmp(sign, "CHRP") == 0) {
+ arch = ARCH_CHRP;
+ } else if (strcmp(sign, "MAC99") == 0) {
+ arch = ARCH_MAC99;
+ } else if (strcmp(sign, "POP") == 0) {
+ arch = ARCH_POP;
+ } else {
+ ERROR("Unknown PPC architecture: '%s'\n", sign);
+ return NULL;
+ }
+ /* HACK */
+ if (arch == ARCH_CHRP)
+ arch = ARCH_MAC99;
+ lword = NVRAM_get_lword(nvram, 0x30);
+ *RAM_size = lword;
+ byte = NVRAM_get_byte(nvram, 0x34);
+ *boot_device = byte;
+ /* Preloaded boot image */
+ lword = NVRAM_get_lword(nvram, 0x38);
+ *boot_image = (void *)lword;
+ lword = NVRAM_get_lword(nvram, 0x3C);
+ *boot_size = lword;
+ /* Preloaded cmdline */
+ lword = NVRAM_get_lword(nvram, 0x40);
+ *cmdline = (void *)lword;
+ lword = NVRAM_get_lword(nvram, 0x44);
+ *cmdline_size = lword;
+ /* Preloaded RAM disk */
+ lword = NVRAM_get_lword(nvram, 0x48);
+ *ramdisk = (void *)lword;
+ lword = NVRAM_get_lword(nvram, 0x4C);
+ *ramdisk_size = lword;
+ /* Preloaded NVRAM image */
+ lword = NVRAM_get_lword(nvram, 0x50);
+ /* Display init geometry */
+ lword = NVRAM_get_word(nvram, 0x54);
+ vga_width = lword;
+ lword = NVRAM_get_word(nvram, 0x56);
+ vga_height = lword;
+ lword = NVRAM_get_word(nvram, 0x58);
+ vga_depth = lword;
+ /* TODO: write it into NVRAM */
+
+ return nvram;
+}