Subject: Re: cmos ram access?
To: None <current-users@netbsd.org>
From: Takahiro Kambe <taca@back-street.net>
List: current-users
Date: 03/28/2003 10:59:57
OK, Perry, Werner.  I attached diff here.

- This diff is to 1.6.1_RC3 generated from our local CVS repogitry.

- sys/arch/i386/i386/machdep.c is completely extra one, notifying user
  that it is now safe to power off.

- /dev/cmos provide read and write to BIOS NVRAM's 128 byte data.
  When writing data, 

	* check sum for basic and extend BIOS data is calculated by
          this driver.

	* RTC clock information will not be changed.

	* Writing wrong data cause reset all NVRAM's data or something
          unexpected result.  *CAUTION* !!

- /dev/bios simply provides read first 256 byte of BIOS ROM.  This is
  intended to identify different vendor or version of BIOS calculating
  MD5 (or SHA1) value.  256 is experimental value enough to do it.

- These codes are "works for me" quality.  Basic codes were written
  and tested on FreeBSD 2.2.8 first and ported NetBSD 1.5 last year.
  It was written as part of my real job.

- Those code are undeer BSD style license.

Since BIOS NVRAM's data is depends on vendor or version of BIOS, we
needed to check what version of BIOS was and created /dev/bios device.

In message <3E822A0A.B447E771@bit-1.de>
	on Wed, 26 Mar 2003 23:30:34 +0100,
	Werner Backes <werner@bit-1.de> wrote:
> is no production machine and I just want to do some testing with
> RTC alarm auto-wakeup.
One difficulty is such data exists as extended BIOS data and its
address(offset) might be vendor depend.

P.S.
If any question, please ask me.  But I might forget why I wrote such
code fragment.  :-)

-- 
Takahiro Kambe <taca@back-street.net>


Index: sys/arch/i386/conf/files.i386
===================================================================
RCS file: /usr/pkg/libdata/cvs/src/sys/arch/i386/conf/files.i386,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.2.1
diff -d -u -p -r1.1.1.1 -r1.1.1.1.2.1
--- sys/arch/i386/conf/files.i386	2003/02/24 02:25:26	1.1.1.1
+++ sys/arch/i386/conf/files.i386	2003/02/24 07:35:14	1.1.1.1.2.1
@@ -308,6 +308,13 @@ attach	apm at mainbus
 file	arch/i386/i386/apm.c		apm needs-count
 file	arch/i386/i386/apmcall.s	apm
 
+device bios
+attach bios at isa
+file	arch/i386/isa/bios.c		bios needs-flag
+
+defpseudo cmos
+file	arch/i386/isa/cmos.c		cmos needs-flag
+
 #
 # Compatibility modules
 #
Index: sys/arch/i386/i386/conf.c
===================================================================
RCS file: /usr/pkg/libdata/cvs/src/sys/arch/i386/i386/conf.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.2.1
diff -d -u -p -r1.1.1.1 -r1.1.1.1.2.1
--- sys/arch/i386/i386/conf.c	2003/02/24 02:25:27	1.1.1.1
+++ sys/arch/i386/i386/conf.c	2003/02/24 07:35:20	1.1.1.1.2.1
@@ -105,6 +105,20 @@ struct bdevsw	bdevsw[] =
 };
 int	nblkdev = sizeof(bdevsw) / sizeof(bdevsw[0]);
 
+/* open, close, read */
+#define cdev_bios_init(c,n) { \
+	dev_init(c,n,open), dev_init(c,n,close), dev_init(c,n,read), \
+	(dev_type_write((*))) enodev, (dev_type_ioctl((*))) enodev, \
+	(dev_type_stop((*))) enodev, 0, seltrue, \
+	(dev_type_mmap((*))) enodev }
+
+/* open, close, read, write */
+#define cdev_cmos_init(c,n) { \
+	dev_init(c,n,open), dev_init(c,n,close), dev_init(c,n,read), \
+	dev_init(c,n,write), (dev_type_ioctl((*))) enodev, \
+	(dev_type_stop((*))) enodev, 0, seltrue, \
+	(dev_type_mmap((*))) enodev }
+
 #include <dev/sysmon/sysmonconf.h>
 cdev_decl(sysmon);
 
@@ -195,6 +209,10 @@ cdev_decl(uscanner);
 cdev_decl(vc_nb_);
 #include "netsmb.h"
 cdev_decl(nsmb_dev_);
+#include "bios.h"
+cdev_decl(bios);
+#include "cmos.h"
+cdev_decl(cmos);
 
 #include "ipfilter.h"
 #include "satlink.h"
@@ -296,8 +314,8 @@ struct cdevsw	cdevsw[] =
 	cdev_lkm_dummy(),		/* 30 */
 	cdev_lkm_dummy(),		/* 31 */
 	cdev_lkm_dummy(),		/* 32 */
-	cdev_lkm_dummy(),		/* 33 */
-	cdev_lkm_dummy(),		/* 34 */
+	cdev_bios_init(NBIOS,bios),	/* 33: BIOS ROM */
+	cdev_cmos_init(NCMOS,cmos),	/* 34: BIOS CMOS data */
 	cdev_mouse_init(NOMMS,mms),	/* 35: Microsoft mouse */
 	cdev_mouse_init(NOLMS,lms),	/* 36: Logitech mouse */
 	cdev_notdef(),			/* 37: was: opms (PS/2 mouse) */
Index: sys/arch/i386/i386/machdep.c
===================================================================
RCS file: /usr/pkg/libdata/cvs/src/sys/arch/i386/i386/machdep.c,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.2.1
diff -d -u -p -r1.1.1.1 -r1.1.1.1.2.1
--- sys/arch/i386/i386/machdep.c	2003/02/24 02:25:27	1.1.1.1
+++ sys/arch/i386/i386/machdep.c	2003/02/24 07:35:20	1.1.1.1.2.1
@@ -2238,6 +2238,9 @@ cpu_reboot(howto, bootstr)
 haltsys:
 	doshutdownhooks();
 
+	if ((howto & RB_HALT) == RB_HALT)
+		printf("\n\nYou can power off now.\n");
+
 	if ((howto & RB_POWERDOWN) == RB_POWERDOWN) {
 #if NAPM > 0 && !defined(APM_NO_POWEROFF)
 		/* turn off, if we can.  But try to turn disk off and
Index: sys/arch/i386/include/conf.h
===================================================================
RCS file: /usr/pkg/libdata/cvs/src/sys/arch/i386/include/conf.h,v
retrieving revision 1.1.1.1
retrieving revision 1.1.1.1.2.1
diff -d -u -p -r1.1.1.1 -r1.1.1.1.2.1
--- sys/arch/i386/include/conf.h	2003/02/24 02:25:28	1.1.1.1
+++ sys/arch/i386/include/conf.h	2003/02/24 07:35:23	1.1.1.1.2.1
@@ -50,3 +50,7 @@ cdev_decl(mms);
 cdev_decl(lms);
 
 cdev_decl(joy);
+
+cdev_decl(bios);
+
+cdev_decl(cmos);
Index: sys/arch/i386/isa/bios.c
===================================================================
RCS file: bios.c
diff -N bios.c
--- /dev/null	Thu Mar 27 03:20:24 2003
+++ /tmp/cvs01693gw	Thu Mar 27 21:29:19 2003
@@ -0,0 +1,189 @@
+/* $Id: bios.c,v 1.1.2.2 2003/03/26 06:53:24 taca Exp $ */
+
+/*
+ * Copyright (C) 2003  JONE System Co., Inc.
+ * All right reserved.
+ * 
+ * Copyright (C) 1999, 2000, 2001, 2002 JEPRO Co., Ltd.
+ * All right 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. Neither the name of JEPRO Co., Ltd. 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 AUTHOR 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 AUTHOR 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/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/select.h>
+#include <sys/proc.h>
+#include <sys/device.h>
+
+#include <machine/bus.h>
+#include <machine/conf.h>
+
+#include <dev/isa/isavar.h>
+
+#define BIOS_ID_OFFSET	0x1f80
+#define BIOS_BASE	0xfe000
+#define BIOS_SIZE	256
+
+struct bios_softc {			/* driver status information */
+	struct device sc_dev;
+
+	int status;
+	int flag;
+#define BIOS_FREE	0
+#define BIOS_OPENED	1
+#define BIOS_READ	2
+	bus_space_tag_t memt;		/* bus memory space identifier */
+	bus_space_handle_t memh;	/* bus memory handle */
+
+	int size;
+	unsigned char *buf;
+};
+
+extern struct cfdriver bios_cd;
+
+#ifdef BIOS_DEBUG
+int bios_debug = 0;
+#endif
+
+int biosprobe __P((struct device *, struct cfdata *, void *));
+void biosattach __P((struct device *, struct device *, void *));
+
+struct cfattach bios_ca = {
+	sizeof(struct bios_softc), biosprobe, biosattach
+};
+
+int
+biosprobe(parent, match, aux)
+	struct device *parent;
+	struct cfdata *match;
+	void *aux;
+{
+	struct isa_attach_args *ia = aux;
+	bus_space_tag_t memt = ia->ia_memt;
+	bus_space_handle_t memh;
+	int id, rv;
+	
+	if (bus_space_map(memt, BIOS_BASE, BIOS_SIZE, 0, &memh))
+		return 0;
+
+	id = bus_space_read_1(memt, memh, BIOS_ID_OFFSET);
+	rv = (id < 0xff && id >= 0xf8)? 1: 0;
+	bus_space_unmap(memt, memh, BIOS_SIZE);
+	if (rv) {
+		ia->ia_nio = 0;
+		ia->ia_iomem[0].ir_addr = BIOS_BASE;
+		ia->ia_iomem[0].ir_size = BIOS_SIZE;
+	}
+
+	return rv;
+}
+
+void
+biosattach(parent, self, aux)
+	struct device *parent, *self;
+	void *aux;
+{
+	struct bios_softc *sc = (void *)self;
+	struct isa_attach_args *ia = aux;
+	bus_space_tag_t memt = ia->ia_memt;
+	bus_space_handle_t memh;
+
+	printf("\n");
+
+	if (bus_space_map(memt, BIOS_BASE, BIOS_SIZE, 0, &memh)) {
+		printf("%s: can't map memory space\n", sc->sc_dev.dv_xname);
+		return;
+	}
+
+	sc->memt = memt;
+	sc->memh = memh;
+	sc->status = 0;
+	sc->flag = BIOS_FREE;
+	sc->size = BIOS_SIZE;
+	sc->buf = malloc(sc->size, M_DEVBUF, 0);
+}
+
+int
+biosopen(dev, flag, mode, p)
+	dev_t dev;
+	int flag;
+	int mode;
+	struct proc *p;
+{
+	int unit = minor(dev);
+	struct bios_softc *sc;
+
+	if (unit >= bios_cd.cd_ndevs)
+		return ENXIO;
+	sc = bios_cd.cd_devs[unit];
+	if (!sc)
+		return ENXIO;
+	if (sc->flag != BIOS_FREE)
+		return EBUSY;
+	sc->flag = BIOS_OPENED;
+
+	return 0;
+}
+
+int
+biosclose(dev, flag, mode, p)
+	dev_t dev;
+	int flag;
+	int mode;
+	struct proc *p;
+{
+	struct bios_softc *sc = bios_cd.cd_devs[minor(dev)];
+
+	sc->flag = BIOS_FREE;
+	return 0;
+}
+
+int
+biosread(dev, uio, flag)
+	dev_t dev;
+	struct uio *uio;
+	int flag;
+{
+	struct bios_softc *sc = bios_cd.cd_devs[minor(dev)];
+	int error = 0;
+
+	if (sc->flag == BIOS_READ)
+		return error;
+
+	if (sc->status == 0) {
+		bus_space_read_region_1(sc->memt, sc->memh, 0, sc->buf,
+					BIOS_SIZE);
+		sc->status++;
+	}
+
+	/* Copy the data to the user process. */
+	error = uiomove(sc->buf, sc->size, uio);
+	sc->flag = BIOS_READ;
+	return error;
+}
Index: sys/arch/i386/isa/cmos.c
===================================================================
RCS file: cmos.c
diff -N cmos.c
--- /dev/null	Thu Mar 27 03:20:24 2003
+++ /tmp/cvs01693gx	Thu Mar 27 21:29:19 2003
@@ -0,0 +1,267 @@
+/* $Id: cmos.c,v 1.1.2.2 2003/03/26 06:53:24 taca Exp $ */
+
+/*
+ * Copyright (C) 2003 JONE System Co., Inc.
+ * All right reserved.
+ *
+ * Copyright (C) 1999, 2000, 2001, 2002 JEPRO Co., Ltd.
+ * All right 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. Neither the name of JEPRO Co., Ltd. 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 AUTHOR 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 AUTHOR 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/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/ioctl.h>
+#include <sys/proc.h>
+#include <sys/device.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/isa/isareg.h>
+#include <dev/isa/isavar.h>
+#include <dev/ic/mc146818reg.h>
+#include <i386/isa/nvram.h>
+
+#define CMOS_SUM	32
+#define CMOS_COUNTER	43
+#define CMOS_XSTART	50
+#define CMOS_XSUM	111
+
+#define NVRAM_SUMH	(MC_NVRAM_START + CMOS_SUM)
+#define NVRAM_COUNTER	(MC_NVRAM_START + CMOS_COUNTER)
+#define NVRAM_XSTART	(MC_NVRAM_START + CMOS_XSTART)
+#define NVRAM_XSUMH	(MC_NVRAM_START + CMOS_XSUM)
+
+#define	CMOS_SIZE	128
+
+struct cmos_softc {			/* driver status information */
+	int sc_state;
+#define CMOS_NONE	0
+#define CMOS_INITED	1
+#define	CMOS_OPENED	2
+#define	CMOS_READ	4
+#define	CMOS_WROTE	8
+	unsigned char *sc_buf;
+} cmossoftc;
+
+#ifdef CMOS_DEBUG
+int cmos_debug = 0;
+#endif
+
+void cmosattach(int);
+int cmosopen(dev_t, int, int, struct proc *);
+int cmosclose(dev_t, int, int, struct proc *);
+int cmosread(dev_t, struct uio *, int);
+int cmoswrite(dev_t, struct uio *, int);
+static void cmossum(unsigned char *, int, int, int);
+#ifdef CMOS_DEBUG
+static void cmos_dump(unsigned char *);
+#endif
+
+extern __inline u_int mc146818_read(void *, u_int);
+extern __inline void mc146818_write(void *, u_int, u_int);
+
+void
+cmosattach(n)
+	int n;
+{
+	struct cmos_softc *sc = &cmossoftc;
+
+	printf("cmos: attached.\n");
+	sc->sc_state = CMOS_NONE;
+}
+
+int
+cmosopen(dev, flags, ifmt, p)
+	dev_t dev;
+	int flags, ifmt;
+	struct proc *p;
+{
+	struct cmos_softc *sc = &cmossoftc;
+	int error;
+
+	if ((error = suser(p->p_ucred, &p->p_acflag)))
+		return error;
+
+	if ((sc->sc_state & CMOS_INITED) != CMOS_INITED) {
+		cmossoftc.sc_buf = malloc(CMOS_SIZE, M_DEVBUF, M_WAITOK);
+		sc->sc_state = CMOS_INITED;
+	}
+	sc->sc_state |= CMOS_OPENED;
+#ifdef CMOS_DEBUG
+	printf("cmos: opened\n");
+#endif
+	return 0;
+}
+
+int
+cmosclose(dev, flags, ifmt, p)
+	dev_t dev;
+	int flags, ifmt;
+	struct proc *p;
+{
+	struct cmos_softc *sc = &cmossoftc;
+
+	sc->sc_state = CMOS_INITED;
+#ifdef CMOS_DEBUG
+	printf("cmos: closed\n");
+#endif
+	return 0;
+}
+
+int
+cmosread(dev, uio, ioflag)
+	dev_t dev;
+	struct uio *uio;
+	int ioflag;
+{
+	struct cmos_softc *sc = &cmossoftc;
+	int i, s;
+	unsigned char *p;
+	unsigned char c;
+	int error = 0;
+
+	if (sc->sc_state & CMOS_READ)
+		return (0);
+
+	if (uio->uio_resid < CMOS_SIZE)
+		return (EINVAL);
+
+#ifdef CMOS_DEBUG
+	printf("cmos: try to read to %d\n", CMOS_SIZE);
+#endif
+	p = sc->sc_buf;
+	s = splclock();
+	for (i = 0; i < CMOS_SIZE; i++) {
+		if (i >= MC_REGA && i <= MC_REGD)
+			c = 0;
+		else
+			c = mc146818_read(sc, i);
+		*p++ = c;
+	}
+	splx(s);
+
+	sc->sc_state |= CMOS_READ;
+	error = uiomove((caddr_t)sc->sc_buf, CMOS_SIZE, uio);
+	return (error);
+}
+
+int
+cmoswrite(dev, uio, ioflag)
+	dev_t dev;
+	struct uio *uio;
+	int ioflag;
+{
+	struct cmos_softc *sc = &cmossoftc;
+	int i, l;
+	int error = 0;
+	int s;
+
+	if (sc->sc_state & CMOS_WROTE)
+		return (0);
+
+	sc->sc_state |= CMOS_WROTE;
+	l = min(CMOS_SIZE, uio->uio_resid);
+	error = uiomove((caddr_t)sc->sc_buf, l, uio);
+#ifdef CMOS_DEBUG
+	printf("write %d\n", l);
+	if (cmos_debug) {
+		cmos_dump(sc->sc_buf);
+	}
+#endif
+	if (error)
+		return error;
+
+	cmossum(sc->sc_buf, NVRAM_DISKETTE, NVRAM_SUMH, CMOS_SUM);
+	cmossum(sc->sc_buf, NVRAM_XSTART, NVRAM_XSUMH, CMOS_XSUM);
+
+#ifdef CMOS_DEBUG
+	if (cmos_debug) {
+		cmos_dump(sc->sc_buf);
+		return (0);
+	}
+#endif
+	s = splclock();
+	for (i = NVRAM_DISKETTE; i < CMOS_SIZE; i++) {
+		if (i != NVRAM_COUNTER)
+			mc146818_write(sc, i, sc->sc_buf[i]);
+	}
+	splx(s);
+
+	return (error);
+}
+
+static void
+cmossum(p, from, to, offset)
+	unsigned char *p;
+	int from;
+	int to;
+	int offset;
+{
+	int i;
+	unsigned short u;
+
+#ifdef CMOS_DEBUG
+	printf("cmossum: from %d to %d and store %d\n", from, to, offset);
+#endif
+	u = 0;
+	offset += MC_NVRAM_START;
+	for (i = from; i < to; i++) {
+		u += p[i];
+	}
+	p[offset++] = (u / 256);
+	p[offset] = (u & 0xff);
+}
+
+#ifdef CMOS_DEBUG
+static void
+cmos_dump(p)
+	unsigned char *p;
+{
+	static char buf[80];
+	int i;
+	char *t;
+
+	for (i = 0; i < CMOS_SIZE; i++) {
+		if (i % 16 == 0) {
+			sprintf(buf, "%02x:", i);
+			t = strchr(buf, '\0');
+		}
+		sprintf(t, " %02x", p[i]);
+		t += 3;
+		if (i % 16 == 15) {
+			*t++ = '\n';
+			*t = '\0';
+			printf(buf);
+		}
+	}
+	printf("\n");
+}
+#endif