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