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