1172998Speter/*-
2172998Speter * Copyright (c) 2007 Peter Wemm
3172998Speter * All rights reserved.
4172998Speter *
5172998Speter * Redistribution and use in source and binary forms, with or without
6172998Speter * modification, are permitted provided that the following conditions
7172998Speter * are met:
8172998Speter * 1. Redistributions of source code must retain the above copyright
9172998Speter *    notice, this list of conditions and the following disclaimer.
10172998Speter * 2. Redistributions in binary form must reproduce the above copyright
11172998Speter *    notice, this list of conditions and the following disclaimer in the
12172998Speter *    documentation and/or other materials provided with the distribution.
13172998Speter *
14172998Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15172998Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16172998Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17172998Speter * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18172998Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19172998Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20172998Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21172998Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22172998Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23172998Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24172998Speter * SUCH DAMAGE.
25172998Speter *
26172998Speter * $FreeBSD: releng/11.0/sys/dev/nvram/nvram.c 181132 2008-08-01 20:39:18Z jhb $
27172998Speter */
28172998Speter
29172998Speter#include <sys/param.h>
30172998Speter#include <sys/systm.h>
31172998Speter#include <sys/kernel.h>
32172998Speter#include <sys/conf.h>
33172998Speter#include <sys/fcntl.h>
34181132Sjhb#include <sys/lock.h>
35172998Speter#include <sys/proc.h>
36181132Sjhb#include <sys/sx.h>
37172998Speter#include <sys/uio.h>
38172998Speter#include <sys/module.h>
39172998Speter
40172998Speter#include <isa/rtc.h>
41172998Speter
42172998Speter/*
43172998Speter * Linux-style /dev/nvram driver
44172998Speter *
45172998Speter * cmos ram starts at bytes 14 through 128, for a total of 114 bytes.
46172998Speter * The driver exposes byte 14 as file offset 0.
47172998Speter *
48172998Speter * Offsets 2 through 31 are checksummed at offset 32, 33.
49172998Speter * In order to avoid the possibility of making the machine unbootable at the
50172998Speter * bios level (press F1 to continue!), we refuse to allow writes if we do
51172998Speter * not see a pre-existing valid checksum.  If the existing sum is invalid,
52172998Speter * then presumably we do not know how to make a sum that the bios will accept.
53172998Speter */
54172998Speter
55172998Speter#define NVRAM_FIRST	RTC_DIAG	/* 14 */
56172998Speter#define NVRAM_LAST	128
57172998Speter
58172998Speter#define CKSUM_FIRST	2
59172998Speter#define CKSUM_LAST	31
60172998Speter#define CKSUM_MSB	32
61172998Speter#define CKSUM_LSB	33
62172998Speter
63172998Speterstatic d_open_t		nvram_open;
64172998Speterstatic d_read_t		nvram_read;
65172998Speterstatic d_write_t	nvram_write;
66172998Speter
67172998Speterstatic struct cdev *nvram_dev;
68181132Sjhbstatic struct sx nvram_lock;
69172998Speter
70172998Speterstatic struct cdevsw nvram_cdevsw = {
71172998Speter	.d_version =	D_VERSION,
72172998Speter	.d_open =	nvram_open,
73172998Speter	.d_read =	nvram_read,
74172998Speter	.d_write =	nvram_write,
75172998Speter	.d_name =	"nvram",
76172998Speter};
77172998Speter
78172998Speterstatic int
79172998Speternvram_open(struct cdev *dev __unused, int flags, int fmt __unused,
80172998Speter    struct thread *td)
81172998Speter{
82172998Speter	int error = 0;
83172998Speter
84172998Speter	if (flags & FWRITE)
85172998Speter		error = securelevel_gt(td->td_ucred, 0);
86172998Speter
87172998Speter	return (error);
88172998Speter}
89172998Speter
90172998Speterstatic int
91172998Speternvram_read(struct cdev *dev, struct uio *uio, int flags)
92172998Speter{
93172998Speter	int nv_off;
94172998Speter	u_char v;
95172998Speter	int error = 0;
96172998Speter
97172998Speter	while (uio->uio_resid > 0 && error == 0) {
98172998Speter		nv_off = uio->uio_offset + NVRAM_FIRST;
99172998Speter		if (nv_off < NVRAM_FIRST || nv_off >= NVRAM_LAST)
100172998Speter			return (0);	/* Signal EOF */
101172998Speter		/* Single byte at a time */
102172998Speter		v = rtcin(nv_off);
103172998Speter		error = uiomove(&v, 1, uio);
104172998Speter	}
105172998Speter	return (error);
106172998Speter
107172998Speter}
108172998Speter
109172998Speterstatic int
110172998Speternvram_write(struct cdev *dev, struct uio *uio, int flags)
111172998Speter{
112172998Speter	int nv_off;
113172998Speter	u_char v;
114172998Speter	int error = 0;
115172998Speter	int i;
116172998Speter	uint16_t sum;
117172998Speter
118181132Sjhb	sx_xlock(&nvram_lock);
119181132Sjhb
120172998Speter	/* Assert that we understand the existing checksum first!  */
121172998Speter	sum = rtcin(NVRAM_FIRST + CKSUM_MSB) << 8 |
122172998Speter	      rtcin(NVRAM_FIRST + CKSUM_LSB);
123172998Speter	for (i = CKSUM_FIRST; i <= CKSUM_LAST; i++)
124172998Speter		sum -= rtcin(NVRAM_FIRST + i);
125181132Sjhb	if (sum != 0) {
126181132Sjhb		sx_xunlock(&nvram_lock);
127172998Speter		return (EIO);
128181132Sjhb	}
129172998Speter	/* Bring in user data and write */
130172998Speter	while (uio->uio_resid > 0 && error == 0) {
131172998Speter		nv_off = uio->uio_offset + NVRAM_FIRST;
132181132Sjhb		if (nv_off < NVRAM_FIRST || nv_off >= NVRAM_LAST) {
133181132Sjhb			sx_xunlock(&nvram_lock);
134172998Speter			return (0);	/* Signal EOF */
135181132Sjhb		}
136172998Speter		/* Single byte at a time */
137172998Speter		error = uiomove(&v, 1, uio);
138172998Speter		writertc(nv_off, v);
139172998Speter	}
140172998Speter	/* Recalculate checksum afterwards */
141172998Speter	sum = 0;
142172998Speter	for (i = CKSUM_FIRST; i <= CKSUM_LAST; i++)
143172998Speter		sum += rtcin(NVRAM_FIRST + i);
144172998Speter	writertc(NVRAM_FIRST + CKSUM_MSB, sum >> 8);
145172998Speter	writertc(NVRAM_FIRST + CKSUM_LSB, sum);
146181132Sjhb	sx_xunlock(&nvram_lock);
147172998Speter	return (error);
148172998Speter}
149172998Speter
150172998Speterstatic int
151172998Speternvram_modevent(module_t mod __unused, int type, void *data __unused)
152172998Speter{
153172998Speter	switch (type) {
154172998Speter	case MOD_LOAD:
155181132Sjhb		sx_init(&nvram_lock, "nvram");
156172998Speter		nvram_dev = make_dev(&nvram_cdevsw, 0,
157172998Speter		    UID_ROOT, GID_KMEM, 0640, "nvram");
158172998Speter		break;
159172998Speter	case MOD_UNLOAD:
160172998Speter	case MOD_SHUTDOWN:
161172998Speter		destroy_dev(nvram_dev);
162181132Sjhb		sx_destroy(&nvram_lock);
163172998Speter		break;
164172998Speter	default:
165172998Speter		return (EOPNOTSUPP);
166172998Speter	}
167172998Speter	return (0);
168172998Speter}
169172998SpeterDEV_MODULE(nvram, nvram_modevent, NULL);
170