NetBSD Problem Report #37023

From martin@duskware.de  Mon Sep 24 19:17:47 2007
Return-Path: <martin@duskware.de>
Received: from mail.netbsd.org (mail.netbsd.org [204.152.190.11])
	by narn.NetBSD.org (Postfix) with ESMTP id 810EE63B8CE
	for <gnats-bugs@gnats.netbsd.org>; Mon, 24 Sep 2007 19:17:47 +0000 (UTC)
Message-Id: <20070924172022.1DB5E63B8CE@narn.NetBSD.org>
Date: Mon, 24 Sep 2007 17:20:22 +0000 (UTC)
From: morth@morth.org
Reply-To: morth@morth.org
To: netbsd-bugs-owner@NetBSD.org
Subject: better nvram support
X-Send-Pr-Version: www-1.0

>Number:         37023
>Category:       port-macppc
>Synopsis:       better nvram support
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    port-macppc-maintainer
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Mon Sep 24 19:20:01 +0000 2007
>Originator:     Pelle Johansson
>Release:        diff is for -current
>Organization:
>Environment:
NetBSD kaninen.morth.org 3.1.1_PATCH NetBSD 3.1.1_PATCH (kaninen) #0: Mon Jul 23 11:24:37 CEST 2007  morth@kaninen.morth.org:/mnt/netbsd/netbsd-3.1/obj.kaninen/sys/arch/macppc/compile/kaninen macppc
>Description:
I've had this sitting on my comp for quite a while. It improves the nvram driver by separating it into patitions, much in the way Apple designed it. Also contained is a nvram tool to access the variables stored, so you can for example use it to configure what device to boot, without dropping into OF. The syntax is based on the same command in Darwin. Since I wasn't quite sure where to put the tool, I'll just include the source file (it should probably be installed into /sbin/).

I've only tested it on my PMac G4 yikes (new world), but it "should" work even on old world (though it remains to see if reality follows the documentation).

It's not completely complete, there's no man page, and I haven't figured out quite how MAKEDEV works in the source tree, but after installing this patch, you should make nvram devices like this (usually only nvram2 will be used):

crw-r--r--  1 root  wheel  33, 0 Jul 15  2006 /dev/nvram0
crw-r--r--  1 root  wheel  33, 1 Jul 22  2006 /dev/nvram1
crw-r--r--  1 root  wheel  33, 2 Jul 23  2006 /dev/nvram2
crw-r--r--  1 root  wheel  33, 3 Jul 22  2006 /dev/nvram3
crw-r--r--  1 root  wheel  33, 4 Jul 22  2006 /dev/nvram4
crw-r--r--  1 root  wheel  33, 5 Jul 22  2006 /dev/nvram5
crw-r--r--  1 root  wheel  33, 6 Jul 22  2006 /dev/nvram6
crw-r--r--  1 root  wheel  33, 7 Jul 22  2006 /dev/nvram7
crw-r--r--  1 root  wheel  33, 8 Jul 22  2006 /dev/nvram8
crw-r--r--  1 root  wheel  33, 9 Jul 22  2006 /dev/nvram9

In addition, I don't know what kernel locking method to use. It would be _bad_ if the computer crashed while writing the nvram.
>How-To-Repeat:

>Fix:
Index: src/sys/arch/macppc/dev/nvram.c
===================================================================
RCS file: /cvsroot/src/sys/arch/macppc/dev/nvram.c,v
retrieving revision 1.11
diff -u -r1.11 nvram.c
--- src/sys/arch/macppc/dev/nvram.c	24 Jan 2007 13:08:12 -0000	1.11
+++ src/sys/arch/macppc/dev/nvram.c	24 Sep 2007 17:02:18 -0000
@@ -1,6 +1,7 @@
 /*	$NetBSD: nvram.c,v 1.11 2007/01/24 13:08:12 hubertf Exp $	*/

 /*-
+ * Copyright (C) 2006   Pelle Johansson
  * Copyright (C) 1998	Internet Research Institute, Inc.
  * All rights reserved.
  *
@@ -31,6 +32,13 @@
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  */

+/*
+ * OpenFirmware NVRAM driver.
+ * The NVRAM comes in two versions: fixed offsets and partitioned.
+ * We try to support both by using the unreserved part of the
+ * fixed offset NVRAM for partitions.
+ */
+
 #include <sys/cdefs.h>
 __KERNEL_RCSID(0, "$NetBSD: nvram.c,v 1.11 2007/01/24 13:08:12 hubertf Exp $");

@@ -42,9 +50,11 @@
 #include <sys/device.h>
 #include <sys/malloc.h>
 #include <sys/event.h>
+#include <sys/fcntl.h>

 #include <machine/autoconf.h>
 #include <machine/pio.h>
+#include <machine/nvram.h>

 #define NVRAM_NONE  0
 #define NVRAM_IOMEM 1
@@ -55,25 +65,48 @@
 static void nvram_attach __P((struct device *, struct device *, void *));
 static int nvram_match __P((struct device *, struct cfdata *, void *));

+/*
+ * Old OF PowerMacs had fixed offsets to different parts of nvram.
+ * We support this by making them part 0 - 2.
+ * This has the additional advantage that common will always be nvram2.
+ */
+const struct nvram_fixed_part {
+	const char *name;
+	u_int off;
+	u_int sz;
+} nvram_fixed_parts[] = {
+	{"diagnostics", 0x1000, 0x300},
+	{"APL,MacOS75", 0x1300, 0x500},
+	{"common",      0x1800, 0x800},
+};
+#define NVRAM_NUM_FIXED_PARTS (sizeof(nvram_fixed_parts) / sizeof(struct nvram_fixed_part))
+
 struct nvram_softc {
 	struct device sc_dev;
 	int nv_type;
 	char *nv_port;
+	char *nv_mem;
 	char *nv_data;
+	int need_put;
+	struct nvram_fixed_part nv_fixed[NVRAM_NUM_FIXED_PARTS];
 };

+static int nvramget __P((struct nvram_softc *));
+static int nvramput __P((struct nvram_softc *));
+static int nvrampart __P((struct nvram_softc *, u_int, u_int *, u_int *));
+
 CFATTACH_DECL(nvram, sizeof(struct nvram_softc),
     nvram_match, nvram_attach, NULL, NULL);

 extern struct cfdriver nvram_cd;

-dev_type_read(nvramread);
-dev_type_write(nvramwrite);
-dev_type_mmap(nvrammmap);
+dev_type_close(nvramclose);
+dev_type_read(nvramio);
+dev_type_ioctl(nvramioctl);

 const struct cdevsw nvram_cdevsw = {
-	nullopen, nullclose, nvramread, nvramwrite, noioctl,
-	nostop, notty, nopoll, nvrammmap, nokqfilter,
+	nullopen, nvramclose, nvramio, nvramio, nvramioctl,
+	nostop, notty, nopoll, nommap, nokqfilter,
 };

 int
@@ -102,19 +135,20 @@
 	struct confargs *ca = aux;
 	int *reg = ca->ca_reg;

+	sc->nv_data = NULL;
 	printf("\n");

 	switch (ca->ca_nreg) {

 	case 8:						/* untested */
 		sc->nv_type = NVRAM_IOMEM;
-		sc->nv_data = mapiodev(ca->ca_baseaddr + reg[0], reg[1]);
+		sc->nv_mem = mapiodev(ca->ca_baseaddr + reg[0], reg[1]);
 		break;

 	case 16:
 		sc->nv_type = NVRAM_PORT;
 		sc->nv_port = mapiodev(ca->ca_baseaddr + reg[0], reg[1]);
-		sc->nv_data = mapiodev(ca->ca_baseaddr + reg[2], reg[3]);
+		sc->nv_mem = mapiodev(ca->ca_baseaddr + reg[2], reg[3]);
 		break;

 	case 0:
@@ -125,80 +159,256 @@
 }

 int
-nvramread(dev, uio, flag)
-	dev_t dev;
-	struct uio *uio;
-	int flag;
-{
+nvramget(sc)
 	struct nvram_softc *sc;
-	u_int off, cnt;
+{
 	int i;
-	int error = 0;
-	char *buf;
+	int j;
+	u_int sz;
+	
+	if (sc->nv_data)
+		return 0;

-	sc = nvram_cd.cd_devs[0];
+	sc->nv_data= malloc(NVRAM_SIZE, M_DEVBUF, M_WAITOK);
+	if (sc->nv_data == NULL) {
+		return EAGAIN;
+	}

-	off = uio->uio_offset;
-	cnt = uio->uio_resid;
+	/* mutex start */
+	switch (sc->nv_type) {
+
+	case NVRAM_IOMEM:
+		for (i = 0; i < NVRAM_SIZE; i++)
+			sc->nv_data[i] = sc->nv_mem[i * 16];
+
+		break;

-	if (off > NVRAM_SIZE || cnt > NVRAM_SIZE)
-		return EFAULT;
+	case NVRAM_PORT:
+		for (i = 0; i < NVRAM_SIZE; i += 32) {

-	if (off + cnt > NVRAM_SIZE)
-		cnt = NVRAM_SIZE - off;
+			out8(sc->nv_port, i / 32);
+			for (j = 0; j < 32; j++) {
+				sc->nv_data[i + j] = sc->nv_mem[j * 16];
+			}
+		}
+		break;

-	buf = malloc(NVRAM_SIZE, M_DEVBUF, M_WAITOK);
-	if (buf == NULL) {
-		error = EAGAIN;
-		goto out;
+	default:
+		/* mutex end 1 */
+		return ENXIO;
 	}
+	for (j = 0; j < NVRAM_NUM_FIXED_PARTS; j++) {
+		sc->nv_fixed[j].name = nvram_fixed_parts[j].name;
+		sc->nv_fixed[j].off = NVRAM_SIZE;
+	}
+	for (i = 0; ; i += sz) {
+		sz = *(u_int16_t*)(sc->nv_data + i + 2) * 16;
+		if (i + sz >= NVRAM_SIZE)
+			break;
+
+		for (j = 0; j < NVRAM_NUM_FIXED_PARTS; j++) {
+			if (!strncmp(sc->nv_data + i + 4, nvram_fixed_parts[j].name, 12)) {
+				sc->nv_fixed[j].off = i;
+				sc->nv_fixed[j].sz = sz;
+				break;
+			}
+		}
+	}
+	sc->need_put = 0;
+	/* mutex end 2 */
+	
+	return 0;
+}
+
+int
+nvramput(sc)
+	struct nvram_softc *sc;
+{
+	int i;
+	
+	if (!sc->need_put)
+		return 0;
+	if (!sc->nv_data)
+		panic("No NVRAM data!");

 	switch (sc->nv_type) {

 	case NVRAM_IOMEM:
+		/* important start */
 		for (i = 0; i < NVRAM_SIZE; i++)
-			buf[i] = sc->nv_data[i * 16];
-
+			out8(sc->nv_mem + i * 16, sc->nv_data[i]);
+		/* important end */
 		break;

 	case NVRAM_PORT:
+		/* mutex + important start */
 		for (i = 0; i < NVRAM_SIZE; i += 32) {
 			int j;
-
+			
 			out8(sc->nv_port, i / 32);
-			for (j = 0; j < 32; j++) {
-				buf[i + j] = sc->nv_data[j * 16];
-			}
+			for (j = 0; j < 32; j++)
+				out8(sc->nv_mem + j * 16, sc->nv_data[i + j]);
 		}
+		/* mutex + important end */
 		break;

 	default:
-		goto out;
+		return ENXIO;
 	}
+	sc->need_put = 0;

-	error = uiomove(buf + off, cnt, uio);
+	return 0;
+}

-out:
-	if (buf)
-		free(buf, M_DEVBUF);
+int
+nvrampart(sc, part, off, sz)
+	struct nvram_softc *sc;
+	u_int part;
+	u_int *off;
+	u_int *sz;
+{
+	int error;
+	struct nvram_fixed_part *currfixed;

-	return error;
+	/*
+	 * Each part is
+	 * 1 byte signature
+	 * 1 byte checksum
+	 * 2 byte length in 16 byte tuples, including header
+	 * 12 byte name
+	 * data
+	 */
+	
+	error = nvramget(sc);
+	if (error)
+		return error;
+	
+	if (part < NVRAM_NUM_FIXED_PARTS) {
+		*off = sc->nv_fixed[part].off;
+		if (*off >= NVRAM_SIZE)
+			return ENXIO;
+		*sz = sc->nv_fixed[part].sz;
+		return 0;
+	}
+	part -= NVRAM_NUM_FIXED_PARTS;
+	currfixed = sc->nv_fixed;
+
+	*off = 0;
+	while (part--) {
+		*off += *(u_int16_t*)(sc->nv_data + *off + 2) * 16;
+		if (*off >= NVRAM_SIZE)
+			return ENXIO;
+		if (currfixed && *off == currfixed->off) {
+			/* Skip past the fixed parts. */
+			part++;
+			if (++currfixed - sc->nv_fixed >= NVRAM_NUM_FIXED_PARTS)
+				currfixed = NULL;
+		}
+	}
+	*sz = *(u_int16_t*)(sc->nv_data + *off + 2) * 16;
+	if (*off + *sz > NVRAM_SIZE)
+		return ENXIO;
+	
+	return 0;
 }

 int
-nvramwrite(dev, uio, flag)
+nvramclose(dev, flag, mode, p)
+	dev_t dev;
+	int flag;
+	int mode;
+	struct proc *p;
+{
+	return nvramput(nvram_cd.cd_devs[0]);
+}
+
+int
+nvramio(dev, uio, flag)
 	dev_t dev;
 	struct uio *uio;
 	int flag;
 {
-	return ENXIO;
+	struct nvram_softc *sc;
+	u_int off, cnt;
+	int error = 0;
+	u_int part;
+	u_int poff;
+	u_int psz;
+
+	sc = nvram_cd.cd_devs[0];
+
+	off = uio->uio_offset;
+	cnt = uio->uio_resid;
+	
+	part = minor(dev);
+	error = nvrampart(sc, part, &poff, &psz);
+	if (error)
+		return error;
+	/* Skip header */
+	poff += 16;
+	psz -= 16;
+
+	if (off > psz)
+		return ENXIO;
+	if (off + cnt > psz)
+		cnt = psz - off;
+
+	error = uiomove(sc->nv_data + poff + off, cnt, uio);
+	if (!error && uio->uio_rw == UIO_WRITE && cnt > 0)
+		sc->need_put = 1;
+	return error;
 }

-paddr_t
-nvrammmap(dev, off, prot)
-        dev_t dev;
-        off_t off;
-	int prot;
+int
+nvramioctl(dev, cmd, data, flag, p)
+	dev_t dev;
+	u_long cmd;
+	caddr_t data;
+	int flag;
+	struct proc *p;
 {
-	return -1;
+	struct nvram_softc *sc;
+	int error;
+	u_int part;
+	u_int poff;
+	u_int psz;
+
+	sc = nvram_cd.cd_devs[0];
+	
+	part = minor(dev);
+	error = nvrampart(sc, part, &poff, &psz);
+	if (error)
+		return error;
+	
+	switch (cmd) {
+	case NVRAM_IOC_GETSIGN:
+		*(char*)data = sc->nv_data[poff];
+		break;
+
+	case NVRAM_IOC_SETSIGN:
+		if (!(flag & FWRITE) || part < NVRAM_NUM_FIXED_PARTS)
+			error = EPERM;
+		else
+			sc->nv_data[poff] = *(char*)data;
+		break;
+
+	case NVRAM_IOC_GETNAME:
+		memcpy(data, sc->nv_data + poff + 4, 12);
+		break;
+
+	case NVRAM_IOC_SETNAME:
+		if (!(flag & FWRITE) || part < NVRAM_NUM_FIXED_PARTS)
+			error = EPERM;
+		else {
+			memcpy(sc->nv_data + poff + 4, data, 12);
+			sc->need_put = 1;
+		}
+		break;
+
+	default:
+		error = EPASSTHROUGH;
+		break;
+	}
+	return error;
 }
+
Index: src/sys/arch/macppc/include/Makefile
===================================================================
RCS file: /cvsroot/src/sys/arch/macppc/include/Makefile,v
retrieving revision 1.19
diff -u -r1.19 Makefile
--- src/sys/arch/macppc/include/Makefile	11 Dec 2005 12:18:06 -0000	1.19
+++ src/sys/arch/macppc/include/Makefile	24 Sep 2007 17:11:52 -0000
@@ -9,6 +9,7 @@
 	grfioctl.h \
 	intr.h \
 	keyboard.h \
-	vmparam.h
+	vmparam.h \
+	nvram.h

 .include "../../powerpc/include/Makefile"
--- /dev/null	2007-09-24 19:11:09.000000000 +0200
+++ src/sys/arch/macppc/include/nvram.h	2007-06-29 09:58:56.000000000 +0200
@@ -0,0 +1,41 @@
+/*	$NetBSD$	*/
+
+/*
+ *  Copyright (c) 2006 Pelle Johansson
+ *  All rights reserved.
+ * 
+ *  Redistribution and use in source and binary forms, with or without
+ *  modification, are permitted provided that the following conditions
+ *  are met:
+ *  1. Redistributions of source code must retain the above copyright
+ *     notice, this list of conditions and the following disclaimer.
+ *  2. Redistributions in binary form must reproduce the above copyright
+ *     notice, this list of conditions and the following disclaimer in the
+ *     documentation and/or other materials provided with the distribution.
+ *  3. The name of the author may not be used to endorse or promote products
+ *     derived from this software without specific prior written permission.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * 
+ */
+#ifndef _MACPPC_NVRAM_H_
+#define _MACPPC_NVRAM_H_
+
+#include <sys/ioccom.h>
+
+#define	NVRAM_IOC_GETSIGN	_IOR('N', 0, char)
+#define	NVRAM_IOC_SETSIGN	_IOW('N', 1, char)
+#define	NVRAM_IOC_GETNAME	_IOR('N', 2, char[12])
+#define	NVRAM_IOC_SETNAME	_IOW('N', 3, char[12])
+
+#endif /* _MACPPC_NVRAM_H_ */


nvram tool:

/* $NetBSD$	*/

/*
 * Copyright (c) 2006 Pelle Johansson
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#include <sys/cdefs.h>

#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <stdio.h>

#include <machine/nvram.h>

const char *devfile = "/dev/nvram2";

extern char *optarg;
extern int optind;

int
main(int argc, char *argv[])
{
	int printall = 0;
	int ch;
	int fd;
	char name[13];
	int i;
	char buf[4097];
	char *nv, *eq, *aeq;
	int written = 0;
	int len, vlen;

	while ((ch = getopt (argc, argv, "pf:")) != -1)
	{
		switch (ch)
		{
		case 'p':
			printall = 1;
			break;

		case 'f':
			devfile = optarg;
			break;
		}
	}

	argc -= optind;
	argv += optind;

	fd = open(devfile, O_RDONLY);
	if (fd < 0)
	{
		perror(devfile);
		return 1;
	}

	if ((len = read(fd, buf, 4096)) < 4096) {
		perror ("read");
		return 1;
	}
	close (fd);
	buf[4096] = 0;
	for (nv = buf; nv[0] && nv - buf < 4096; nv += vlen) {
		int print = printall;

		vlen = strlen (nv) + 1;
		eq = strchr (nv, '=');
		for (i = 0; i < argc; i++)
		{
			aeq = strchr (argv[i], '=');
			if (aeq && eq && aeq - argv[i] == eq - nv && !strncmp (argv[i], nv, eq - nv))
			{
				len -= vlen;
				memmove (nv, nv + vlen, len - (nv - buf));
				memset (buf + len, 4096 - len, 0);
				vlen = 0;
				break;
			}
			else if (!aeq && eq && eq - nv == strlen (argv[i]) && !strncmp (argv[i], nv, eq - nv))
				print = 1;
		}
		if (print)
			printf ("%s\n", nv);
	}
	for (i = 0; i < argc; i++)
	{
		if (strchr(argv[i], '='))
		{
			vlen = strlen(argv[i]);
			if (nv - buf + vlen < 4096) {
				strcpy (nv, argv[i]);
				nv += vlen + 1;
				written = 1;
				if (printall)
					printf(argv[i]);
			}
		}
	}
	if (written)
	{
		memset(nv, 4096 - (nv - buf), 0);
		fd = open(devfile, O_WRONLY);
		if (fd < 0)
		{
			perror (devfile);
			return 1;
		}
		if (write(fd, buf, 4096) < 4096)
		{
			perror ("write");
			return 1;
		}
		close(fd);
	}

	return 0;
}

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.39 2013/11/01 18:47:49 spz Exp $
$NetBSD: gnats_config.sh,v 1.8 2006/05/07 09:23:38 tsutsui Exp $
Copyright © 1994-2007 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.