xref: /freebsd/sys/dev/nvram/nvram.c (revision 721351876cd4d3a8a700f62d2061331fa951a488)
1 /*-
2  * Copyright (c) 2007 Peter Wemm
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 
29 #include <sys/param.h>
30 #include <sys/systm.h>
31 #include <sys/kernel.h>
32 #include <sys/conf.h>
33 #include <sys/fcntl.h>
34 #include <sys/proc.h>
35 #include <sys/uio.h>
36 #include <sys/module.h>
37 
38 #include <isa/rtc.h>
39 
40 /*
41  * Linux-style /dev/nvram driver
42  *
43  * cmos ram starts at bytes 14 through 128, for a total of 114 bytes.
44  * The driver exposes byte 14 as file offset 0.
45  *
46  * Offsets 2 through 31 are checksummed at offset 32, 33.
47  * In order to avoid the possibility of making the machine unbootable at the
48  * bios level (press F1 to continue!), we refuse to allow writes if we do
49  * not see a pre-existing valid checksum.  If the existing sum is invalid,
50  * then presumably we do not know how to make a sum that the bios will accept.
51  */
52 
53 #define NVRAM_FIRST	RTC_DIAG	/* 14 */
54 #define NVRAM_LAST	128
55 
56 #define CKSUM_FIRST	2
57 #define CKSUM_LAST	31
58 #define CKSUM_MSB	32
59 #define CKSUM_LSB	33
60 
61 static d_open_t		nvram_open;
62 static d_read_t		nvram_read;
63 static d_write_t	nvram_write;
64 
65 static struct cdev *nvram_dev;
66 
67 static struct cdevsw nvram_cdevsw = {
68 	.d_version =	D_VERSION,
69 	.d_flags =	D_NEEDGIANT,
70 	.d_open =	nvram_open,
71 	.d_read =	nvram_read,
72 	.d_write =	nvram_write,
73 	.d_name =	"nvram",
74 };
75 
76 static int
77 nvram_open(struct cdev *dev __unused, int flags, int fmt __unused,
78     struct thread *td)
79 {
80 	int error = 0;
81 
82 	if (flags & FWRITE)
83 		error = securelevel_gt(td->td_ucred, 0);
84 
85 	return (error);
86 }
87 
88 static int
89 nvram_read(struct cdev *dev, struct uio *uio, int flags)
90 {
91 	int nv_off;
92 	u_char v;
93 	int error = 0;
94 
95 	while (uio->uio_resid > 0 && error == 0) {
96 		nv_off = uio->uio_offset + NVRAM_FIRST;
97 		if (nv_off < NVRAM_FIRST || nv_off >= NVRAM_LAST)
98 			return (0);	/* Signal EOF */
99 		/* Single byte at a time */
100 		v = rtcin(nv_off);
101 		error = uiomove(&v, 1, uio);
102 	}
103 	return (error);
104 
105 }
106 
107 static int
108 nvram_write(struct cdev *dev, struct uio *uio, int flags)
109 {
110 	int nv_off;
111 	u_char v;
112 	int error = 0;
113 	int i;
114 	uint16_t sum;
115 
116 	/* Assert that we understand the existing checksum first!  */
117 	sum = rtcin(NVRAM_FIRST + CKSUM_MSB) << 8 |
118 	      rtcin(NVRAM_FIRST + CKSUM_LSB);
119 	for (i = CKSUM_FIRST; i <= CKSUM_LAST; i++)
120 		sum -= rtcin(NVRAM_FIRST + i);
121 	if (sum != 0)
122 		return (EIO);
123 	/* Bring in user data and write */
124 	while (uio->uio_resid > 0 && error == 0) {
125 		nv_off = uio->uio_offset + NVRAM_FIRST;
126 		if (nv_off < NVRAM_FIRST || nv_off >= NVRAM_LAST)
127 			return (0);	/* Signal EOF */
128 		/* Single byte at a time */
129 		error = uiomove(&v, 1, uio);
130 		writertc(nv_off, v);
131 	}
132 	/* Recalculate checksum afterwards */
133 	sum = 0;
134 	for (i = CKSUM_FIRST; i <= CKSUM_LAST; i++)
135 		sum += rtcin(NVRAM_FIRST + i);
136 	writertc(NVRAM_FIRST + CKSUM_MSB, sum >> 8);
137 	writertc(NVRAM_FIRST + CKSUM_LSB, sum);
138 	return (error);
139 }
140 
141 static int
142 nvram_modevent(module_t mod __unused, int type, void *data __unused)
143 {
144 	switch (type) {
145 	case MOD_LOAD:
146 		nvram_dev = make_dev(&nvram_cdevsw, 0,
147 		    UID_ROOT, GID_KMEM, 0640, "nvram");
148 		break;
149 	case MOD_UNLOAD:
150 	case MOD_SHUTDOWN:
151 		destroy_dev(nvram_dev);
152 		break;
153 	default:
154 		return (EOPNOTSUPP);
155 	}
156 	return (0);
157 }
158 DEV_MODULE(nvram, nvram_modevent, NULL);
159