Submitted By: Ken Moffat <ken@linuxfromscratch.org>
Date: 2005-11-23
Initial Package Version: 1.1.3
Upstream Status: Package is maintained by debian, this cherry-picks
Origin: From debian 1.1.3-18.diff, edited
Description: Makes it work on New World, fixes compile error, updated to
 allow -m32 to be passed via EXTRACFLAGS in multilib.

 Some of the debian powerpc-utils_1.1.3-18.diff, but only the parts relevant
to nvsetenv and nvsetvol - the remainder may be useful on an Old World Mac,
but they are no use on New World.  The -D_GNU_SOURCE apparently helps on
powerpc64.  I've also removed bogus messages advising you to update your
headers when IOC_NVRAM_SYNC isn't defined : it hasn't existed for a long
time.  Finally, I ran the nvsetenv.sgml through docbook2txt -man to get a
proper man page, because most people won't have docbook utils installed. 

diff -Nuar pmac-utils.orig/Makefile pmac-utils/Makefile
--- pmac-utils.orig/Makefile	1998-07-21 14:22:28.000000000 +0100
+++ pmac-utils/Makefile	2005-11-17 15:16:52.000000000 +0000
@@ -22,8 +22,8 @@
 
 SGMLMAN	= sgml2txt -man
 CC	= gcc -Wall -Wstrict-prototypes
-CFLAGS	= -O2 -fsigned-char 
-LDFLAGS = -s
+CFLAGS	= $(EXTRACFLAGS) -O2 -fsigned-char -D_GNU_SOURCE
+LDFLAGS =
 INSTALL	= /usr/bin/install -c
 SOUND_INC = -I.
 
@@ -42,8 +42,12 @@
 mousemode:
 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $@.c
 
-nvsetenv:
+nvsetenv: nvsetenv.c nwnvsetenv.c
+	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $@.c nwnvsetenv.c
+
+nvsetvol:
 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $@.c
+ 
 
 nvvideo:
 	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $@.c
diff -Nuar pmac-utils.orig/nvsetenv.8 pmac-utils/nvsetenv.8
--- pmac-utils.orig/nvsetenv.8	1970-01-01 01:00:00.000000000 +0100
+++ pmac-utils/nvsetenv.8	2005-11-17 20:39:47.000000000 +0000
@@ -0,0 +1,256 @@
+.if n .ds Q \&"
+.if t .ds Q ``
+.if n .ds U \&"
+.if t .ds U ''
+.TH "NVSETENV" 8 
+.tr \&
+.nr bi 0
+.nr ll 0
+.nr el 0
+.de DS
+..
+.de DE
+..
+.de Pp
+.ie \\n(ll>0 \{\
+.ie \\n(bi=1 \{\
+.nr bi 0
+.if \\n(t\\n(ll=0 \{.IP \\(bu\}
+.if \\n(t\\n(ll=1 \{.IP \\n+(e\\n(el.\}
+.\}
+.el .sp 
+.\}
+.el \{\
+.ie \\nh=1 \{\
+.LP
+.nr h 0
+.\}
+.el .PP 
+.\}
+..
+.SH NAME
+
+.Pp
+\fBnvsetenv\fP - change/view Open Firmware environment variables 
+.Pp
+.SH SYNOPSIS
+
+.Pp
+\fBnvsetenv 
+\f(CR[\fP\fIvariable\fP \f(CR[\fP\fIvalue\fP\f(CR\fB]]\fP\fP\fP
+.Pp
+.SH DESCRIPTION
+
+.Pp
+\fBnvsetenv\fP is a program to adjust or view the Open Firmware (OF)
+boot parameters stored in non-volatile (battery-powered) RAM.
+\fBnvsetenv\fP will show the current values of all OF's environment
+variables when no parameters are given.
+.Pp
+.SH OPTIONS
+
+.Pp
+.nr ll +1
+.nr t\n(ll 2
+.if \n(ll>1 .RS
+.IP "\fIvariable\fP"
+.nr bi 1
+.Pp
+nvsetenv will show current value of an OF's
+variable, if no value is given
+.IP "\fIvariable value\fP"
+.nr bi 1
+.Pp
+nvsetenv will set \fIvariable\fP to
+\fIvalue\fP
+.if \n(ll>1 .RE
+.nr ll -1
+.Pp
+.SH EXAMPLES
+
+.Pp
+This example will set the boot device to the first SCSI disk on the
+internal SCSI bus, using /vmlinux as boot image, trying to
+use the third partition as root partition.
+.DS
+.sp 
+.ft RR
+.nf
+        > nvsetenv boot-device  \&"scsi-int/sd@0:0\&"
+        > nvsetenv boot-file    \&" /vmlinux root=/dev/sda3\&"
+.DE
+.fi 
+.ec
+.ft P
+.sp
+.Pp
+Alternatives boot devices are: 
+.DS
+.sp 
+.ft RR
+.nf
+        scsi/sd@1:0             SCSI disk at ID 1
+        ata/ata-disk@0:0        Internal IDE disk
+.DE
+.fi 
+.ec
+.ft P
+.sp
+.Pp
+You can also boot from a floppy, you need a XCOFF-format kernel image
+(in this example: vmlinux.coff), copied to a HFS format high-density
+(1.44Mb) floppy.
+.DS
+.sp 
+.ft RR
+.nf
+        > nvsetenv boot-device  \&"fd:vmlinux.coff\&"
+        > nvsetenv boot-file    \&" root=/dev/sda3\&"
+.DE
+.fi 
+.ec
+.ft P
+.sp
+
+To return to MacOS, do:
+.DS
+.sp 
+.ft RR
+.nf
+        > nvsetenv boot-device  \&"/AAPL,ROM\&"
+.DE
+.fi 
+.ec
+.ft P
+.sp
+.Pp
+Valid values for \&"input-devices\&" are:
+.DS
+.sp 
+.ft RR
+.nf
+        ttya                    Modem serial port
+        ttyb                    Printer serial port
+        kbd                     Keyboard
+        enet                    Ethernet interface
+.DE
+.fi 
+.ec
+.ft P
+.sp
+.Pp
+Valid values for \&"output-devices\&" are (machine and/or OF dependent):
+.DS
+.sp 
+.ft RR
+.nf
+        screen                  Screen display (newer machines)
+        /chaos/control          Screen display (7500, 7600 and 8500)
+        /bandit/ATY,264GT-B     Screen display (6500)
+.DE
+.fi 
+.ec
+.ft P
+.sp
+.Pp
+OF is not designed to wait for your hard disk to spin up
+(remember MacOS boots from ROM).
+Use the following setting to have OF retry to boot from your disk 
+until is has spun up:
+.DS
+.sp 
+.ft RR
+.nf
+        > nvsetenv boot-command \&"begin ['] boot catch 1000 ms cr again\&"
+.DE
+.fi 
+.ec
+.ft P
+.sp
+.Pp
+You only have to append an \&"S\&" to the \&"boot-file\&" variable to boot
+Linux in single user mode.
+.Pp
+You can use install your own nvramrc patch using the following command:
+.DS
+.sp 
+.ft RR
+.nf
+        > nvsetenv nvramrc \&"`cat file.patch`\&"
+.DE
+.fi 
+.ec
+.ft P
+.sp
+
+(please note the backticks!), or: 
+.DS
+.sp 
+.ft RR
+.nf
+        > nvsetenv nvramrc \&"$(cat file.patch)\&"
+.DE
+.fi 
+.ec
+.ft P
+.sp
+.Pp
+.SH FILES
+
+.Pp
+.nr ll +1
+.nr t\n(ll 2
+.if \n(ll>1 .RS
+.IP "\fI/dev/nvram\fP"
+.nr bi 1
+.Pp
+character device with major number 10
+and minor number 144
+.IP "\fI/proc/cpuinfo\fP"
+.nr bi 1
+.Pp
+to identify New/Old-World machines
+.if \n(ll>1 .RE
+.nr ll -1
+.Pp
+.SH SEE ALSO
+
+.Pp
+macos(8)
+.Pp
+.SH AUTHORS
+
+.Pp
+.DS
+.sp 
+.ft RR
+.nf
+Paul Mackerras <paulus@cs.anu.edu.au> (program)
+.DE
+.fi 
+.ec
+.ft P
+.sp
+
+.DS
+.sp 
+.ft RR
+.nf
+Richard van Hees <R.M.vanHees@phys.uu.nl> (documentation)
+.DE
+.fi 
+.ec
+.ft P
+.sp
+
+.DS
+.sp 
+.ft RR
+.nf
+Klaus Halfmann  <halfmann@libra.de> (NewWorld code)
+.DE
+.fi 
+.ec
+.ft P
+.sp
+.Pp
diff -Nuar pmac-utils.orig/nvsetenv.c pmac-utils/nvsetenv.c
--- pmac-utils.orig/nvsetenv.c	1997-04-28 14:29:05.000000000 +0100
+++ pmac-utils/nvsetenv.c	2005-11-17 14:57:57.000000000 +0000
@@ -1,15 +1,32 @@
+/*	nvsetnv.c
+
+	used to set the Envenrioment variables in power macs NVram.
+	Decides via /proc/cpuinfo which type of machine is used.
+
+		Copyright (C) 1996-1998 by Paul Mackerras.
+	nwcode: Copyright (C) 2000	by Klaus Halfmann
+
+	see README for details
+*/
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
+#include <sys/ioctl.h>
+#include <asm/nvram.h>
+#ifndef IOC_NVRAM_SYNC
+#define IOC_NVRAM_SYNC _IO('p', 0x43)
+#endif
 
-#define NVSTART	0x1800
-#define NVSIZE	0x800
+#define NVSTART		0x1800 	// Start of the NVRam OF partition
+#define NVSIZE		0x800	// Size of of the NVRam
 #define MXSTRING        128
 #define N_NVVARS	(int)(sizeof(nvvars) / sizeof(nvvars[0]))
+#define NVMAGIC		0x1275
 
-static int nvfd, nvstr_used;
+static int  nvstr_used;
 static char nvstrbuf[NVSIZE];
 
 struct nvram {
@@ -24,12 +41,13 @@
     unsigned long  vals[1];
 };
 
-union {
-    struct nvram nv;
-    char c[NVSIZE];
-    unsigned short s[NVSIZE/2];
-} nvbuf;
+typedef union {
+    struct nvram 	nv;
+    char 		c[NVSIZE];
+    unsigned short 	s[NVSIZE/2];
+} nvbuftype;
 
+nvbuftype nvbuf;
 
 enum nvtype {
     boolean,
@@ -70,16 +88,21 @@
     {"boot-command", string},
 };
 
+	// Calculated number of variables
+#define N_NVVARS	(int)(sizeof(nvvars) / sizeof(nvvars[0]))
+
 union nvval {
-    unsigned long word_val;
-    char *str_val;
+    unsigned long 	word_val;
+    char 		*str_val;
 } nvvals[32];
 
-int
-nvcsum( void )
+extern int checkNewWorld(void); 		// from nwnvsetenv
+extern int nvNew(int ac, char** av, int nfd); 	// from nwnvsetenv
+
+int nvcsum( void )
 {
-    int i;
-    unsigned c;
+    int 	i;
+    unsigned	c;
     
     c = 0;
     for (i = 0; i < NVSIZE/2; ++i)
@@ -89,28 +112,29 @@
     return c & 0xffff;
 }
 
-void
-nvload( void )
+static void nvload( int nvfd )
 {
     int s;
 
     if (lseek(nvfd, NVSTART, 0) < 0
 	|| read(nvfd, &nvbuf, NVSIZE) != NVSIZE) {
-	perror("Error reading /dev/nvram");
+	perror("Error reading nvram device");
 	exit(EXIT_FAILURE);
     }
+    if (nvbuf.nv.magic != NVMAGIC)
+	(void) fprintf(stderr, "Warning: Bad magic number %x\n",
+			 nvbuf.nv.magic);
     s = nvcsum();
     if (s != 0xffff)
        (void) fprintf(stderr, "Warning: checksum error (%x) on nvram\n", 
 		      s ^ 0xffff);
 }
 
-void
-nvstore(void)
+static void nvstore(int nvfd)
 {
     if (lseek(nvfd, NVSTART, 0) < 0
 	|| write(nvfd, &nvbuf, NVSIZE) != NVSIZE) {
-	perror("Error writing /dev/nvram");
+	perror("Error writing nvram device");
 	exit(EXIT_FAILURE);
     }
 }
@@ -145,8 +169,7 @@
     }
 }
 
-void
-nvpack( void )
+void nvpack( void )
 {
     int     i, vi;
     size_t  off, len;
@@ -184,7 +207,7 @@
     nvbuf.nv.cksum = (unsigned short int) (~nvcsum());
 }
 
-void
+static void
 print_var(int i, int indent)
 {
     char *p;
@@ -207,8 +230,7 @@
     (void) printf("\n");
 }
 
-void
-parse_val(int i, char *str)
+void parse_val(int i, char *str)
 {
     char *endp;
 
@@ -238,12 +260,38 @@
     }
 }
 
-int 
-main(int ac, char **av)
+
+void nvOld(int ac, char** av, int i, int nvfd)
+{
+    nvload(nvfd);
+    nvunpack();
+
+    switch (ac) {
+    case 1:
+	for (i = 0; i < N_NVVARS; ++i) {
+	    (void) printf("%-16s", nvvars[i].name);
+	    print_var(i, 16);
+	}
+	break;
+
+    case 2:
+	print_var(i, 0);
+	break;
+	
+    case 3:
+	parse_val(i, av[2]);
+	nvpack();
+	nvstore(nvfd);
+	break;
+    }
+}
+
+int main(int ac, char **av)
 {
-    int i = 0, l, print;
+    int i = 0, l, print, nvfd, newWorld;
 
     l = (int) strlen(av[0]);
+    // print when no value is set OR we are aclled as <xxx>printenv
     print = (int) (ac <= 2 || 
 		   (l > 8 && strcmp(&av[0][l-8], "printenv") == 0));
     if (print != 0 && ac > 2) {
@@ -255,7 +303,9 @@
 	exit(EXIT_FAILURE);
     }
 
-    if (ac >= 2) {
+    newWorld = checkNewWorld();
+    
+    if (!newWorld && ac >= 2) {
 	for (i = 0; i < N_NVVARS; ++i)
 	    if (strcmp(av[1], nvvars[i].name) == 0)
 		break;
@@ -266,33 +316,21 @@
 	}
     }
 
-    nvfd = open("/dev/nvram", ac <= 2 ? O_RDONLY: O_RDWR);
+    nvfd = open("/dev/misc/nvram", ac <= 2 ? O_RDONLY: O_RDWR);
     if (nvfd < 0) {
-	perror("Couldn't open /dev/nvram");
+      nvfd = open("/dev/nvram", ac <= 2 ? O_RDONLY: O_RDWR);
+      if (nvfd < 0) {
+	perror("Couldn't open nvram device");
 	exit(EXIT_FAILURE);
-    }
-    nvload();
-    nvunpack();
-
-    switch (ac) {
-    case 1:
-	for (i = 0; i < N_NVVARS; ++i) {
-	    (void) printf("%-16s", nvvars[i].name);
-	    print_var(i, 16);
-	}
-	break;
-
-    case 2:
-	print_var(i, 0);
-	break;
-	
-    case 3:
-	parse_val(i, av[2]);
-	nvpack();
-	nvstore();
-	break;
+      }
     }
 
+    if (newWorld)
+    	nvNew(ac, av, nvfd);
+    else
+    	nvOld(ac, av, i, nvfd);
+    
+    (void) ioctl(nvfd, IOC_NVRAM_SYNC);
     (void) close(nvfd);
     exit(EXIT_SUCCESS);
 }
diff -Nuar pmac-utils.orig/nvsetvol.8 pmac-utils/nvsetvol.8
--- pmac-utils.orig/nvsetvol.8	1970-01-01 01:00:00.000000000 +0100
+++ pmac-utils/nvsetvol.8	2005-11-17 21:37:20.000000000 +0000
@@ -0,0 +1,27 @@
+.TH NVSETVOL 8 "16th February 2003"
+
+.SH NAME
+nvsetvol \- tool for changing startup volume of PowerMac machines
+
+.SH SYNOPSIS
+.B nvsetvol
+[
+.B volume
+]
+
+.SH DESCRIPTION
+.B nvsetvol
+queries and sets the base volume of a PowerMac machine.  With no options,
+.B nvsetvol
+displays the current volume; with a numerical argument, it sets the volume.
+The
+.B volume
+argument should be a number from 0 to 255.  0 will turn off the annoying
+startup chime.
+
+.SH AUTHOR
+.B nvsetvol
+was written by Matteo Frigo <athena@fftw.org>.
+
+This manual page was written by Daniel Jacobowitz <dan@debian.org> for the
+Debian GNU/Linux distribution.
diff -Nuar pmac-utils.orig/nvsetvol.c pmac-utils/nvsetvol.c
--- pmac-utils.orig/nvsetvol.c	1970-01-01 01:00:00.000000000 +0100
+++ pmac-utils/nvsetvol.c	2005-11-17 21:43:16.000000000 +0000
@@ -0,0 +1,110 @@
+/* set volume of the startup chime in the PowerMac nvram. 
+
+   Written by Matteo Frigo <athena@fftw.org>.
+   $Id: nvsetvol.c,v 1.1 2003/01/11 11:26:30 athena Exp $
+
+   This program is in the public domain.
+*/
+
+/* The volume is stored as a byte at address 8 in the parameter ram. */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <asm/nvram.h>
+#ifndef IOC_NVRAM_SYNC
+#define IOC_NVRAM_SYNC _IO('p', 0x43)
+#endif
+
+typedef struct {
+     unsigned char sig;
+     unsigned char cksum;
+     unsigned short len;
+     char name[12];
+} header;
+
+void die(const char *s) __attribute__((noreturn));
+void die(const char *s)
+{
+     perror(s);
+     exit(1);
+}
+
+void seek_pram(int fd, int offset)
+{
+     if (lseek(fd, offset, SEEK_SET) < 0)
+	  die("lseek");
+}
+
+// MSch: only works for newworld - oldworld have XPRAM at offset 0x1300 fixed. 
+
+int find_pram(int fd, int *size)
+{
+     header buf;
+     int offset = 0;
+     int rc = 0;
+     while((rc = read(fd, &buf, sizeof(header))) == sizeof(header)) {
+	  int sz = buf.len * 16;
+	  // what's the sig in char?
+	  // if (buf.sig == 0x1275) fprintf(stderr, "sig at offset %d rc %d buf.len %d buf.name %s \n", offset, rc, buf.len, buf.name);
+	  // if (buf.sig == 0x5a) fprintf(stderr, "sig at offset %x rc %d buf.len %x buf.name %s \n", offset, rc, buf.len, buf.name);
+	  // if (sz) fprintf(stderr, "offset %x rc %d sig %x buf.len %x buf.name %s \n", offset, rc, buf.sig, buf.len, buf.name);
+	  if (!strncmp(buf.name, "APL,MacOS75", 11)) {
+	       *size = sz - sizeof(header);
+	       // fprintf(stderr, "XPRAM offset %x size %x\n", offset + sizeof(header), *size); 
+	       return offset + sizeof(header);
+	  }
+	  if (sz)
+	    offset += sz;
+          else
+            offset += sizeof(header);
+	  seek_pram(fd, offset);
+     }
+     // no Core99 style PRAM found - just assume hard coded values as set in kernel:
+     // XPRAM 0x1300
+     // NR    0x1400
+     // OF    0x1800
+     // TODO: use ioctl to get PRAM offset
+     // fprintf(stderr, "no NW PRAM found, assuming OW XPRAM offset 0x1300 size 0x100\n");
+     *size = 0x100;
+     return (0x1300);
+     die("no PRAM found");
+}
+
+#define VOLADDR 8
+
+int main(int argc, char *argv[])
+{
+     int fd, offset, size;
+     char *buf;
+
+     fd = open("/dev/nvram", O_RDWR);
+     if (fd < 0) die("can't open /dev/nvram");
+     
+     offset = find_pram(fd, &size);
+     buf = malloc(size);
+     if (!buf) die("can't malloc()");
+
+     seek_pram(fd, offset);
+     if (read(fd, buf, size) != size)
+	  die("error reading /dev/nvram");
+
+     printf("current volume is %d\n", (unsigned char) buf[VOLADDR]);
+     
+     if (argc > 1) {
+	  buf[VOLADDR] = atoi(argv[1]);
+
+	  seek_pram(fd, offset);
+	  if (write(fd, buf, size) != size)
+	       die("error writing /dev/nvram");
+	  printf("new volume is %d\n", (unsigned char) buf[VOLADDR]);
+     }
+     ioctl(fd, IOC_NVRAM_SYNC);
+     close(fd);
+     return 0;
+}
diff -Nuar pmac-utils.orig/nwnvsetenv.c pmac-utils/nwnvsetenv.c
--- pmac-utils.orig/nwnvsetenv.c	1970-01-01 01:00:00.000000000 +0100
+++ pmac-utils/nwnvsetenv.c	2005-11-17 21:37:27.000000000 +0000
@@ -0,0 +1,261 @@
+/*	nwnvsetnv.c
+
+	used to set the Envenrioment variables in power macs NVram.
+	This is for the NewWorld nvram only
+
+	nwcode: Copyright (C) 2000	by Klaus Halfmann
+
+	see README for details
+*/
+#include <stdio.h>
+#include <stdlib.h>
+
+#define __USE_GNU	// need strnlen
+
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+// #define MXSTRING        128:
+// #define N_NVVARS	(int)(sizeof(nvvars) / sizeof(nvvars[0]))
+// #define NVMAGIC		0x1275
+
+/* CHRP NVRAM header */
+typedef struct {
+  unsigned char		signature;
+  unsigned char		cksum;
+  unsigned short	len;
+  char			name[12];
+  // unsigned char data[0];
+} chrp_header;
+
+/* Check in proc/cpuinfo if this is a new world machine */
+
+int checkNewWorld(void)
+{
+    FILE* cpuf = fopen("/proc/cpuinfo","r");
+    char  buf[256]; // "pmac-generation    : NewWolrd|OldWorld 
+    int	  result = 0, found = 0;
+
+    if (!cpuf) {
+	perror("Couldn't open /proc/cpuinfo");
+	exit(EXIT_FAILURE);
+    }
+
+    while (NULL != fgets(buf, 255, cpuf))
+    {
+	if (!strncmp(buf, "pmac-generation" ,15))
+	{
+	    char* index = strchr(buf, ':') + 2;
+	    if (!index) // ???
+		continue;
+	    result = !strncmp(index,"NewWorld",8);
+	    found = 1;
+	    break;
+	}
+    }
+    fclose(cpuf);
+
+    /* Some kernels don't have pmac-generation ( <= 2.2.15 and earlier 2.3.x ) */
+    /* Look for AAPL in /proc/device-tree/compatible instead. */
+    if (!found) {
+	cpuf = fopen("/proc/device-tree/compatible", "r");
+	if(!cpuf)
+	    return 0;
+	
+	fgets(buf, 255, cpuf);
+	fclose(cpuf);
+	if (strncmp(buf, "AAPL", 4) != 0)
+	    result = 1;
+    }
+
+    return result; // assume OldWorld
+}
+
+/* seek NVRAM until common (OF) part 
+   return the lenght of the part in case of sucess,
+   	  0 otherwise.
+   chrph is set to the value of the "coommon" blocek eventually found
+   *nvstart is set to the seekpoint where common block starts.
+*/
+   
+int nvscan(int nvfd, chrp_header* chrph, int* nvstart)
+{
+    int		start = 0;
+    
+    while (read(nvfd, chrph, sizeof(chrp_header)) == sizeof(chrp_header))
+    {
+	int size = chrph->len * 0x10 - sizeof(chrp_header);    	
+	if (!strncmp(chrph->name, "common", 7))
+	{
+	    *nvstart = start;
+	    return size; // seeked upto start
+	}
+	if (lseek(nvfd, size, SEEK_CUR) < 0)
+	   break;
+	start += size + sizeof(chrp_header);
+    }
+    fprintf(stderr,"No common Block found\n");
+    exit(EXIT_FAILURE);
+}
+
+static char* nvload( int nvfd , int nvsize)
+{
+    char* nvbuf = malloc(nvsize);
+
+    if (!nvbuf) {
+	perror("Error allocating buffer");
+	exit(EXIT_FAILURE);
+    }
+    if (read(nvfd, nvbuf, nvsize) != nvsize) {
+	perror("Error reading /dev/nvram");
+	exit(EXIT_FAILURE);
+    }
+    return nvbuf;
+}
+
+static void
+print_vars(char* nvbuf, int nvsize)
+{
+    int i = 0;
+
+    while (i < nvsize)
+    {
+ 	int size = strnlen(nvbuf, nvsize);
+	if (size == 0)
+	    break;
+	printf("%s\n",nvbuf);
+	nvbuf += (size + 1);	// count 0-byte, too
+    }
+}
+
+/* move memory around to insert the value.
+ *
+ * @param nvbufend 	byte AFTER the end of the buffer
+ * @param varsize 	length of the variable name
+ * @param buf		byte where varaible NAME starts
+ * @param newval	new value to replace old one
+ * @param foundsize 	lenght of varible + '=' + value
+ * @param equalpos 	position relative to buf where '=' was found
+ */
+static void 
+insert_val (char* nvbufend, int varsize,   char* buf, 
+	    char* newval,   int foundsize, int equalpos)
+{
+    int 	oldlen 	= foundsize - equalpos -1; // account for the '='
+    int		newlen 	= strlen(newval);
+    char* 	valpos 	= buf + varsize + 1;
+    int 	delta 	= newlen - oldlen;
+
+    if (delta > 0) // expand mem
+	memmove(valpos + newlen,
+		valpos + oldlen, (nvbufend - valpos - newlen));
+    else if (delta < 0) // shrink mem
+    {
+	memmove(valpos + newlen, 
+		valpos + oldlen, (nvbufend - valpos - oldlen));
+	memset (nvbufend + delta, 0, -delta );
+    }
+    strncpy(valpos, newval, newlen);
+}
+
+/* return position where variable is found,
+ * newval may be null.
+ */
+
+static char* set_var(char* nvbuf, int nvsize, char* varname, char* newval)
+{
+    int i =0;
+    int varsize = strlen(varname);
+    int equalpos;
+    while (i < nvsize)
+    {
+	char* buf = &nvbuf[i];
+ 	int foundsize = strnlen(buf, nvsize -i);
+	if (foundsize == 0)
+	    break;
+	equalpos = (int) (strchr(buf, '=') - buf);
+	if (equalpos == varsize &&
+	    !strncmp(buf, varname, equalpos))
+	{
+	    if (newval)	// set the value 
+		insert_val(nvbuf + nvsize, varsize, buf, 
+			   newval, foundsize, equalpos);
+	    return buf;
+	}
+	i += foundsize + 1;	// count 0-byte, too
+    }
+    return NULL;
+}
+
+static void
+print_var(char* nvbuf, int nvsize, char* varname)
+{
+    char* buf = set_var(nvbuf, nvsize, varname, NULL);
+    if (buf)
+	printf("%s\n",buf);
+}
+
+/* This fucntion is not used here, it is left
+   her for the curious */
+
+unsigned short chrp_checksum(chrp_header* hdr, char* nvbuf, int nvsize)
+{
+    unsigned char* 	ptr = (unsigned char*) &hdr->len;
+    unsigned char* 	end = ptr + sizeof(chrp_header);
+    unsigned short 	sum = hdr->signature;
+	// this in fact skips the checksum
+    for (; ptr < end; ptr++)
+	sum += *ptr;
+    while (sum > 0xFF)
+	sum = (sum & 0xFF) + (sum>>8);
+    return sum;
+}                                                                                                 
+
+static void
+nvstore(int nvfd, chrp_header* chrph, char* nvbuf, int nvstart, int nvsize)
+{
+    // mmh, the checksum is calculated for the header only
+    // since we did not modify the header we can just ignore it.
+    ssize_t written;
+    ssize_t seek =  nvstart + sizeof(chrp_header);
+    written = lseek(nvfd, seek, SEEK_SET);
+    if (written != seek)
+    {
+    	fprintf(stderr,"Error seeking /dev/nvram\n");
+	exit(EXIT_FAILURE);
+    }
+    written = write(nvfd, nvbuf, nvsize);
+    if (written != nvsize)
+    {
+    	fprintf(stderr,"Error writing /dev/nvram %x %x\n", nvsize, seek);
+	exit(EXIT_FAILURE);
+    }
+}
+
+/* print / set the New World NVRAM */
+
+void nvNew(int ac, char** av, int nvfd)
+{
+    int 		nvsize, nvstart;
+    chrp_header		chrph;
+    char*		nvbuf;
+
+    nvsize = nvscan(nvfd, &chrph, &nvstart);
+    nvbuf  = nvload(nvfd, nvsize);
+
+    switch (ac) {
+    case 1:
+	print_vars(nvbuf, nvsize);
+	break;
+
+    case 2:
+	print_var(nvbuf, nvsize, av[1]);
+	break;
+	
+    case 3:
+	set_var(nvbuf, nvsize, av[1], av[2]);
+	nvstore(nvfd, &chrph, nvbuf, nvstart, nvsize);
+	break;
+    }
+}
