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.
This commit is contained in:
Peter Wemm 2007-06-15 22:58:14 +00:00
parent 37f878f56c
commit 5915fb72fb
3 changed files with 297 additions and 0 deletions

View File

@ -53,12 +53,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>
@ -931,4 +934,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 */

View File

@ -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 */

View File

@ -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 */