1172998Speter/*- 2172998Speter * Copyright (c) 2007 Peter Wemm 3172998Speter * All rights reserved. 4172998Speter * 5172998Speter * Redistribution and use in source and binary forms, with or without 6172998Speter * modification, are permitted provided that the following conditions 7172998Speter * are met: 8172998Speter * 1. Redistributions of source code must retain the above copyright 9172998Speter * notice, this list of conditions and the following disclaimer. 10172998Speter * 2. Redistributions in binary form must reproduce the above copyright 11172998Speter * notice, this list of conditions and the following disclaimer in the 12172998Speter * documentation and/or other materials provided with the distribution. 13172998Speter * 14172998Speter * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15172998Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16172998Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17172998Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18172998Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19172998Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20172998Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21172998Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22172998Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23172998Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24172998Speter * SUCH DAMAGE. 25172998Speter * 26172998Speter * $FreeBSD: releng/11.0/sys/dev/nvram/nvram.c 181132 2008-08-01 20:39:18Z jhb $ 27172998Speter */ 28172998Speter 29172998Speter#include <sys/param.h> 30172998Speter#include <sys/systm.h> 31172998Speter#include <sys/kernel.h> 32172998Speter#include <sys/conf.h> 33172998Speter#include <sys/fcntl.h> 34181132Sjhb#include <sys/lock.h> 35172998Speter#include <sys/proc.h> 36181132Sjhb#include <sys/sx.h> 37172998Speter#include <sys/uio.h> 38172998Speter#include <sys/module.h> 39172998Speter 40172998Speter#include <isa/rtc.h> 41172998Speter 42172998Speter/* 43172998Speter * Linux-style /dev/nvram driver 44172998Speter * 45172998Speter * cmos ram starts at bytes 14 through 128, for a total of 114 bytes. 46172998Speter * The driver exposes byte 14 as file offset 0. 47172998Speter * 48172998Speter * Offsets 2 through 31 are checksummed at offset 32, 33. 49172998Speter * In order to avoid the possibility of making the machine unbootable at the 50172998Speter * bios level (press F1 to continue!), we refuse to allow writes if we do 51172998Speter * not see a pre-existing valid checksum. If the existing sum is invalid, 52172998Speter * then presumably we do not know how to make a sum that the bios will accept. 53172998Speter */ 54172998Speter 55172998Speter#define NVRAM_FIRST RTC_DIAG /* 14 */ 56172998Speter#define NVRAM_LAST 128 57172998Speter 58172998Speter#define CKSUM_FIRST 2 59172998Speter#define CKSUM_LAST 31 60172998Speter#define CKSUM_MSB 32 61172998Speter#define CKSUM_LSB 33 62172998Speter 63172998Speterstatic d_open_t nvram_open; 64172998Speterstatic d_read_t nvram_read; 65172998Speterstatic d_write_t nvram_write; 66172998Speter 67172998Speterstatic struct cdev *nvram_dev; 68181132Sjhbstatic struct sx nvram_lock; 69172998Speter 70172998Speterstatic struct cdevsw nvram_cdevsw = { 71172998Speter .d_version = D_VERSION, 72172998Speter .d_open = nvram_open, 73172998Speter .d_read = nvram_read, 74172998Speter .d_write = nvram_write, 75172998Speter .d_name = "nvram", 76172998Speter}; 77172998Speter 78172998Speterstatic int 79172998Speternvram_open(struct cdev *dev __unused, int flags, int fmt __unused, 80172998Speter struct thread *td) 81172998Speter{ 82172998Speter int error = 0; 83172998Speter 84172998Speter if (flags & FWRITE) 85172998Speter error = securelevel_gt(td->td_ucred, 0); 86172998Speter 87172998Speter return (error); 88172998Speter} 89172998Speter 90172998Speterstatic int 91172998Speternvram_read(struct cdev *dev, struct uio *uio, int flags) 92172998Speter{ 93172998Speter int nv_off; 94172998Speter u_char v; 95172998Speter int error = 0; 96172998Speter 97172998Speter while (uio->uio_resid > 0 && error == 0) { 98172998Speter nv_off = uio->uio_offset + NVRAM_FIRST; 99172998Speter if (nv_off < NVRAM_FIRST || nv_off >= NVRAM_LAST) 100172998Speter return (0); /* Signal EOF */ 101172998Speter /* Single byte at a time */ 102172998Speter v = rtcin(nv_off); 103172998Speter error = uiomove(&v, 1, uio); 104172998Speter } 105172998Speter return (error); 106172998Speter 107172998Speter} 108172998Speter 109172998Speterstatic int 110172998Speternvram_write(struct cdev *dev, struct uio *uio, int flags) 111172998Speter{ 112172998Speter int nv_off; 113172998Speter u_char v; 114172998Speter int error = 0; 115172998Speter int i; 116172998Speter uint16_t sum; 117172998Speter 118181132Sjhb sx_xlock(&nvram_lock); 119181132Sjhb 120172998Speter /* Assert that we understand the existing checksum first! */ 121172998Speter sum = rtcin(NVRAM_FIRST + CKSUM_MSB) << 8 | 122172998Speter rtcin(NVRAM_FIRST + CKSUM_LSB); 123172998Speter for (i = CKSUM_FIRST; i <= CKSUM_LAST; i++) 124172998Speter sum -= rtcin(NVRAM_FIRST + i); 125181132Sjhb if (sum != 0) { 126181132Sjhb sx_xunlock(&nvram_lock); 127172998Speter return (EIO); 128181132Sjhb } 129172998Speter /* Bring in user data and write */ 130172998Speter while (uio->uio_resid > 0 && error == 0) { 131172998Speter nv_off = uio->uio_offset + NVRAM_FIRST; 132181132Sjhb if (nv_off < NVRAM_FIRST || nv_off >= NVRAM_LAST) { 133181132Sjhb sx_xunlock(&nvram_lock); 134172998Speter return (0); /* Signal EOF */ 135181132Sjhb } 136172998Speter /* Single byte at a time */ 137172998Speter error = uiomove(&v, 1, uio); 138172998Speter writertc(nv_off, v); 139172998Speter } 140172998Speter /* Recalculate checksum afterwards */ 141172998Speter sum = 0; 142172998Speter for (i = CKSUM_FIRST; i <= CKSUM_LAST; i++) 143172998Speter sum += rtcin(NVRAM_FIRST + i); 144172998Speter writertc(NVRAM_FIRST + CKSUM_MSB, sum >> 8); 145172998Speter writertc(NVRAM_FIRST + CKSUM_LSB, sum); 146181132Sjhb sx_xunlock(&nvram_lock); 147172998Speter return (error); 148172998Speter} 149172998Speter 150172998Speterstatic int 151172998Speternvram_modevent(module_t mod __unused, int type, void *data __unused) 152172998Speter{ 153172998Speter switch (type) { 154172998Speter case MOD_LOAD: 155181132Sjhb sx_init(&nvram_lock, "nvram"); 156172998Speter nvram_dev = make_dev(&nvram_cdevsw, 0, 157172998Speter UID_ROOT, GID_KMEM, 0640, "nvram"); 158172998Speter break; 159172998Speter case MOD_UNLOAD: 160172998Speter case MOD_SHUTDOWN: 161172998Speter destroy_dev(nvram_dev); 162181132Sjhb sx_destroy(&nvram_lock); 163172998Speter break; 164172998Speter default: 165172998Speter return (EOPNOTSUPP); 166172998Speter } 167172998Speter return (0); 168172998Speter} 169172998SpeterDEV_MODULE(nvram, nvram_modevent, NULL); 170