aboutsummaryrefslogtreecommitdiff
path: root/sys/i386
diff options
context:
space:
mode:
authorPeter Wemm <peter@FreeBSD.org>2007-06-15 22:58:14 +0000
committerPeter Wemm <peter@FreeBSD.org>2007-06-15 22:58:14 +0000
commit5915fb72fb9e8f82720f2da747a39e6cdb0b134b (patch)
treedb3f86f1334e5f32597017085e6ae74e05a50e6b /sys/i386
parent37f878f56c07f3c298351acae5fe0c9afe936d79 (diff)
downloadsrc-5915fb72fb9e8f82720f2da747a39e6cdb0b134b.tar.gz
src-5915fb72fb9e8f82720f2da747a39e6cdb0b134b.zip
Prototype (but functional) Linux-ish /dev/nvram interface to the extra
114 bytes of cmos ram in the PC clock chip. The big difference between this and the Linux version is that we do not recalculate the checksums for bytes 16..31. We use this at work when cloning identical machines - we can copy the bios settings as well. Reading /dev/nvram gives 114 bytes of data but you can seek/read/write whichever bytes you like. Yes, this is a "foot, gun, fire!" type of device.
Notes
Notes: svn path=/head/; revision=170802
Diffstat (limited to 'sys/i386')
-rw-r--r--sys/i386/isa/clock.c99
1 files changed, 99 insertions, 0 deletions
diff --git a/sys/i386/isa/clock.c b/sys/i386/isa/clock.c
index 62c57646cf60..f8e1c4e39d07 100644
--- a/sys/i386/isa/clock.c
+++ b/sys/i386/isa/clock.c
@@ -56,12 +56,15 @@ __FBSDID("$FreeBSD$");
#include <sys/systm.h>
#include <sys/bus.h>
#include <sys/clock.h>
+#include <sys/conf.h>
+#include <sys/fcntl.h>
#include <sys/lock.h>
#include <sys/kdb.h>
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/time.h>
#include <sys/timetc.h>
+#include <sys/uio.h>
#include <sys/kernel.h>
#include <sys/limits.h>
#include <sys/module.h>
@@ -930,4 +933,100 @@ static devclass_t attimer_devclass;
DRIVER_MODULE(attimer, isa, attimer_driver, attimer_devclass, 0, 0);
DRIVER_MODULE(attimer, acpi, attimer_driver, attimer_devclass, 0, 0);
+
+/*
+ * Linux-style /dev/nvram driver
+ *
+ * cmos ram starts at bytes 14 through 128, for a total of 114 bytes.
+ * bytes 16 through 31 are checksummed at byte 32.
+ * Unlike Linux, you have to take care of the checksums yourself.
+ * The driver exposes byte 14 as file offset 0.
+ */
+
+#define NVRAM_FIRST RTC_DIAG /* 14 */
+#define NVRAM_LAST 128
+
+static d_open_t nvram_open;
+static d_read_t nvram_read;
+static d_write_t nvram_write;
+
+static struct cdev *nvram_dev;
+
+static struct cdevsw nvram_cdevsw = {
+ .d_version = D_VERSION,
+ .d_flags = D_NEEDGIANT,
+ .d_open = nvram_open,
+ .d_read = nvram_read,
+ .d_write = nvram_write,
+ .d_name = "nvram",
+};
+
+static int
+nvram_open(struct cdev *dev __unused, int flags, int fmt __unused,
+ struct thread *td)
+{
+ int error = 0;
+
+ if (flags & FWRITE)
+ error = securelevel_gt(td->td_ucred, 0);
+
+ return (error);
+}
+
+static int
+nvram_read(struct cdev *dev, struct uio *uio, int flags)
+{
+ int nv_off;
+ u_char v;
+ int error = 0;
+
+ while (uio->uio_resid > 0 && error == 0) {
+ nv_off = uio->uio_offset + NVRAM_FIRST;
+ if (nv_off < NVRAM_FIRST || nv_off >= NVRAM_LAST)
+ return (0); /* Signal EOF */
+ /* Single byte at a time */
+ v = rtcin(nv_off);
+ error = uiomove(&v, 1, uio);
+ }
+ return (error);
+
+}
+
+static int
+nvram_write(struct cdev *dev, struct uio *uio, int flags)
+{
+ int nv_off;
+ u_char v;
+ int error = 0;
+
+ while (uio->uio_resid > 0 && error == 0) {
+ nv_off = uio->uio_offset + NVRAM_FIRST;
+ if (nv_off < NVRAM_FIRST || nv_off >= NVRAM_LAST)
+ return (0); /* Signal EOF */
+ /* Single byte at a time */
+ error = uiomove(&v, 1, uio);
+ writertc(nv_off, v);
+ }
+ return (error);
+}
+
+static int
+nvram_modevent(module_t mod __unused, int type, void *data __unused)
+{
+ switch (type) {
+ case MOD_LOAD:
+ nvram_dev = make_dev(&nvram_cdevsw, 0,
+ UID_ROOT, GID_KMEM, 0640, "nvram");
+ break;
+ case MOD_UNLOAD:
+ case MOD_SHUTDOWN:
+ destroy_dev(nvram_dev);
+ break;
+ default:
+ return (EOPNOTSUPP);
+ }
+ return (0);
+}
+DEV_MODULE(nvram, nvram_modevent, NULL);
+
#endif /* DEV_ISA */