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 * $FreeBSD$ 29 */ 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/kernel.h> 34 #include <sys/conf.h> 35 #include <sys/fcntl.h> 36 #include <sys/lock.h> 37 #include <sys/proc.h> 38 #include <sys/sx.h> 39 #include <sys/uio.h> 40 #include <sys/module.h> 41 42 #include <isa/rtc.h> 43 44 /* 45 * Linux-style /dev/nvram driver 46 * 47 * cmos ram starts at bytes 14 through 128, for a total of 114 bytes. 48 * The driver exposes byte 14 as file offset 0. 49 * 50 * Offsets 2 through 31 are checksummed at offset 32, 33. 51 * In order to avoid the possibility of making the machine unbootable at the 52 * bios level (press F1 to continue!), we refuse to allow writes if we do 53 * not see a pre-existing valid checksum. If the existing sum is invalid, 54 * then presumably we do not know how to make a sum that the bios will accept. 55 */ 56 57 #define NVRAM_FIRST RTC_DIAG /* 14 */ 58 #define NVRAM_LAST 128 59 60 #define CKSUM_FIRST 2 61 #define CKSUM_LAST 31 62 #define CKSUM_MSB 32 63 #define CKSUM_LSB 33 64 65 static d_open_t nvram_open; 66 static d_read_t nvram_read; 67 static d_write_t nvram_write; 68 69 static struct cdev *nvram_dev; 70 static struct sx nvram_lock; 71 72 static struct cdevsw nvram_cdevsw = { 73 .d_version = D_VERSION, 74 .d_open = nvram_open, 75 .d_read = nvram_read, 76 .d_write = nvram_write, 77 .d_name = "nvram", 78 }; 79 80 static int 81 nvram_open(struct cdev *dev __unused, int flags, int fmt __unused, 82 struct thread *td) 83 { 84 int error = 0; 85 86 if (flags & FWRITE) 87 error = securelevel_gt(td->td_ucred, 0); 88 89 return (error); 90 } 91 92 static int 93 nvram_read(struct cdev *dev, struct uio *uio, int flags) 94 { 95 int nv_off; 96 u_char v; 97 int error = 0; 98 99 while (uio->uio_resid > 0 && error == 0) { 100 nv_off = uio->uio_offset + NVRAM_FIRST; 101 if (nv_off < NVRAM_FIRST || nv_off >= NVRAM_LAST) 102 return (0); /* Signal EOF */ 103 /* Single byte at a time */ 104 v = rtcin(nv_off); 105 error = uiomove(&v, 1, uio); 106 } 107 return (error); 108 109 } 110 111 static int 112 nvram_write(struct cdev *dev, struct uio *uio, int flags) 113 { 114 int nv_off; 115 u_char v; 116 int error = 0; 117 int i; 118 uint16_t sum; 119 120 sx_xlock(&nvram_lock); 121 122 /* Assert that we understand the existing checksum first! */ 123 sum = rtcin(NVRAM_FIRST + CKSUM_MSB) << 8 | 124 rtcin(NVRAM_FIRST + CKSUM_LSB); 125 for (i = CKSUM_FIRST; i <= CKSUM_LAST; i++) 126 sum -= rtcin(NVRAM_FIRST + i); 127 if (sum != 0) { 128 sx_xunlock(&nvram_lock); 129 return (EIO); 130 } 131 /* Bring in user data and write */ 132 while (uio->uio_resid > 0 && error == 0) { 133 nv_off = uio->uio_offset + NVRAM_FIRST; 134 if (nv_off < NVRAM_FIRST || nv_off >= NVRAM_LAST) { 135 sx_xunlock(&nvram_lock); 136 return (0); /* Signal EOF */ 137 } 138 /* Single byte at a time */ 139 error = uiomove(&v, 1, uio); 140 writertc(nv_off, v); 141 } 142 /* Recalculate checksum afterwards */ 143 sum = 0; 144 for (i = CKSUM_FIRST; i <= CKSUM_LAST; i++) 145 sum += rtcin(NVRAM_FIRST + i); 146 writertc(NVRAM_FIRST + CKSUM_MSB, sum >> 8); 147 writertc(NVRAM_FIRST + CKSUM_LSB, sum); 148 sx_xunlock(&nvram_lock); 149 return (error); 150 } 151 152 static int 153 nvram_modevent(module_t mod __unused, int type, void *data __unused) 154 { 155 switch (type) { 156 case MOD_LOAD: 157 sx_init(&nvram_lock, "nvram"); 158 nvram_dev = make_dev(&nvram_cdevsw, 0, 159 UID_ROOT, GID_KMEM, 0640, "nvram"); 160 break; 161 case MOD_UNLOAD: 162 case MOD_SHUTDOWN: 163 destroy_dev(nvram_dev); 164 sx_destroy(&nvram_lock); 165 break; 166 default: 167 return (EOPNOTSUPP); 168 } 169 return (0); 170 } 171 DEV_MODULE(nvram, nvram_modevent, NULL); 172