summaryrefslogtreecommitdiffstats
path: root/sys/isa
diff options
context:
space:
mode:
authorpeter <peter@FreeBSD.org>2007-06-15 22:58:14 +0000
committerpeter <peter@FreeBSD.org>2007-06-15 22:58:14 +0000
commit7d427641bffb8a59a5be2e9fe2eb297d89427dbb (patch)
treedb3f86f1334e5f32597017085e6ae74e05a50e6b /sys/isa
parentd8964f8dc1a663e91dd1685e88682de1f9b9f242 (diff)
downloadFreeBSD-src-7d427641bffb8a59a5be2e9fe2eb297d89427dbb.zip
FreeBSD-src-7d427641bffb8a59a5be2e9fe2eb297d89427dbb.tar.gz
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.
Diffstat (limited to 'sys/isa')
-rw-r--r--sys/isa/atrtc.c99
1 files changed, 99 insertions, 0 deletions
diff --git a/sys/isa/atrtc.c b/sys/isa/atrtc.c
index 62c5764..f8e1c4e 100644
--- a/sys/isa/atrtc.c
+++ b/sys/isa/atrtc.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 */
OpenPOWER on IntegriCloud