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