1105398Stmm/* $NetBSD: openfirmio.c,v 1.4 2002/09/06 13:23:19 gehenna Exp $ */ 2105398Stmm 3119418Sobrien#include <sys/cdefs.h> 4119418Sobrien__FBSDID("$FreeBSD$"); 5119418Sobrien 6139749Simp/*- 7105398Stmm * Copyright (c) 1992, 1993 8105398Stmm * The Regents of the University of California. All rights reserved. 9105398Stmm * 10105398Stmm * This software was developed by the Computer Systems Engineering group 11105398Stmm * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 12105398Stmm * contributed to Berkeley. 13105398Stmm * 14105398Stmm * All advertising materials mentioning features or use of this software 15105398Stmm * must display the following acknowledgement: 16105398Stmm * This product includes software developed by the University of 17105398Stmm * California, Lawrence Berkeley Laboratory. 18105398Stmm * 19105398Stmm * Redistribution and use in source and binary forms, with or without 20105398Stmm * modification, are permitted provided that the following conditions 21105398Stmm * are met: 22105398Stmm * 1. Redistributions of source code must retain the above copyright 23105398Stmm * notice, this list of conditions and the following disclaimer. 24105398Stmm * 2. Redistributions in binary form must reproduce the above copyright 25105398Stmm * notice, this list of conditions and the following disclaimer in the 26105398Stmm * documentation and/or other materials provided with the distribution. 27105398Stmm * 4. Neither the name of the University nor the names of its contributors 28105398Stmm * may be used to endorse or promote products derived from this software 29105398Stmm * without specific prior written permission. 30105398Stmm * 31105398Stmm * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 32105398Stmm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 33105398Stmm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 34105398Stmm * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 35105398Stmm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 36105398Stmm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 37105398Stmm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 38105398Stmm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 39105398Stmm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 40105398Stmm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 41105398Stmm * SUCH DAMAGE. 42105398Stmm * 43105398Stmm * @(#)openfirm.c 8.1 (Berkeley) 6/11/93 44105398Stmm * 45105398Stmm */ 46105398Stmm 47105398Stmm#include <sys/param.h> 48105398Stmm#include <sys/systm.h> 49105398Stmm#include <sys/conf.h> 50105398Stmm#include <sys/errno.h> 51105398Stmm#include <sys/fcntl.h> 52105398Stmm#include <sys/ioccom.h> 53105398Stmm#include <sys/kernel.h> 54105398Stmm#include <sys/malloc.h> 55105398Stmm#include <sys/module.h> 56105398Stmm 57105398Stmm#include <dev/ofw/openfirmio.h> 58105398Stmm 59130585Sphkstatic struct cdev *openfirm_dev; 60105398Stmm 61105398Stmmstatic d_ioctl_t openfirm_ioctl; 62105398Stmm 63105398Stmm#define OPENFIRM_MINOR 0 64105398Stmm 65105398Stmmstatic struct cdevsw openfirm_cdevsw = { 66126080Sphk .d_version = D_VERSION, 67126080Sphk .d_flags = D_NEEDGIANT, 68111815Sphk .d_ioctl = openfirm_ioctl, 69111815Sphk .d_name = "openfirm", 70105398Stmm}; 71105398Stmm 72105398Stmmstatic phandle_t lastnode; /* speed hack */ 73105398Stmm 74105398Stmmstatic int openfirm_checkid(phandle_t, phandle_t); 75105398Stmmstatic int openfirm_getstr(int, const char *, char **); 76105398Stmm 77105398Stmm/* 78105398Stmm * Verify target ID is valid (exists in the OPENPROM tree), as 79105398Stmm * listed from node ID sid forward. 80105398Stmm */ 81105398Stmmstatic int 82105398Stmmopenfirm_checkid(phandle_t sid, phandle_t tid) 83105398Stmm{ 84105398Stmm 85105398Stmm for (; sid != 0; sid = OF_peer(sid)) 86105398Stmm if (sid == tid || openfirm_checkid(OF_child(sid), tid)) 87105398Stmm return (1); 88105398Stmm 89105398Stmm return (0); 90105398Stmm} 91105398Stmm 92105398Stmmstatic int 93105398Stmmopenfirm_getstr(int len, const char *user, char **cpp) 94105398Stmm{ 95105398Stmm int error; 96105398Stmm char *cp; 97105398Stmm 98194138Smarius /* Reject obvious bogus requests. */ 99129587Smarius if ((u_int)len > OFIOCMAXNAME) 100105398Stmm return (ENAMETOOLONG); 101105398Stmm 102111119Simp *cpp = cp = malloc(len + 1, M_TEMP, M_WAITOK); 103105398Stmm if (cp == NULL) 104105398Stmm return (ENOMEM); 105105398Stmm error = copyin(user, cp, len); 106105398Stmm cp[len] = '\0'; 107105398Stmm return (error); 108105398Stmm} 109105398Stmm 110105398Stmmint 111130585Sphkopenfirm_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, 112105398Stmm struct thread *td) 113105398Stmm{ 114105398Stmm struct ofiocdesc *of; 115105398Stmm phandle_t node; 116105398Stmm int len, ok, error; 117105398Stmm char *name, *value; 118105398Stmm char newname[32]; 119105398Stmm 120116210Stmm if ((flags & FREAD) == 0) 121116210Stmm return (EBADF); 122116210Stmm 123105398Stmm of = (struct ofiocdesc *)data; 124105398Stmm switch (cmd) { 125105398Stmm case OFIOCGETOPTNODE: 126105398Stmm *(phandle_t *) data = OF_finddevice("/options"); 127105398Stmm return (0); 128105398Stmm case OFIOCGET: 129105398Stmm case OFIOCSET: 130105398Stmm case OFIOCNEXTPROP: 131105398Stmm case OFIOCFINDDEVICE: 132116210Stmm case OFIOCGETPROPLEN: 133105398Stmm node = of->of_nodeid; 134105398Stmm break; 135105398Stmm case OFIOCGETNEXT: 136105398Stmm case OFIOCGETCHILD: 137105398Stmm node = *(phandle_t *)data; 138105398Stmm break; 139105398Stmm default: 140116210Stmm return (ENOIOCTL); 141105398Stmm } 142105398Stmm 143105398Stmm if (node != 0 && node != lastnode) { 144194138Smarius /* Not an easy one, we must search for it. */ 145105398Stmm ok = openfirm_checkid(OF_peer(0), node); 146105398Stmm if (!ok) 147105398Stmm return (EINVAL); 148105398Stmm lastnode = node; 149105398Stmm } 150105398Stmm 151105398Stmm name = value = NULL; 152105398Stmm error = 0; 153105398Stmm switch (cmd) { 154105398Stmm 155105398Stmm case OFIOCGET: 156116210Stmm case OFIOCGETPROPLEN: 157105398Stmm if (node == 0) 158105398Stmm return (EINVAL); 159105398Stmm error = openfirm_getstr(of->of_namelen, of->of_name, &name); 160105398Stmm if (error) 161105398Stmm break; 162105398Stmm len = OF_getproplen(node, name); 163116210Stmm if (cmd == OFIOCGETPROPLEN) { 164116210Stmm of->of_buflen = len; 165116210Stmm break; 166116210Stmm } 167105398Stmm if (len > of->of_buflen) { 168105398Stmm error = ENOMEM; 169105398Stmm break; 170105398Stmm } 171105398Stmm of->of_buflen = len; 172194138Smarius /* -1 means no entry; 0 means no value. */ 173105398Stmm if (len <= 0) 174105398Stmm break; 175111119Simp value = malloc(len, M_TEMP, M_WAITOK); 176105398Stmm if (value == NULL) { 177105398Stmm error = ENOMEM; 178105398Stmm break; 179105398Stmm } 180105398Stmm len = OF_getprop(node, name, (void *)value, len); 181105398Stmm error = copyout(value, of->of_buf, len); 182105398Stmm break; 183105398Stmm 184105398Stmm case OFIOCSET: 185129587Smarius /* 186129587Smarius * Note: Text string values for at least the /options node 187161836Smarius * have to be null-terminated and the length parameter must 188194138Smarius * include this terminating null. However, like OF_getprop(), 189161836Smarius * OF_setprop() will return the actual length of the text 190129587Smarius * string, i.e. omitting the terminating null. 191129587Smarius */ 192105398Stmm if ((flags & FWRITE) == 0) 193105398Stmm return (EBADF); 194105398Stmm if (node == 0) 195105398Stmm return (EINVAL); 196129587Smarius if ((u_int)of->of_buflen > OFIOCMAXVALUE) 197129587Smarius return (ENAMETOOLONG); 198105398Stmm error = openfirm_getstr(of->of_namelen, of->of_name, &name); 199105398Stmm if (error) 200105398Stmm break; 201129587Smarius value = malloc(of->of_buflen, M_TEMP, M_WAITOK); 202129587Smarius if (value == NULL) { 203129587Smarius error = ENOMEM; 204129587Smarius break; 205129587Smarius } 206129587Smarius error = copyin(of->of_buf, value, of->of_buflen); 207105398Stmm if (error) 208105398Stmm break; 209105398Stmm len = OF_setprop(node, name, value, of->of_buflen); 210129587Smarius if (len < 0) 211105398Stmm error = EINVAL; 212129587Smarius of->of_buflen = len; 213105398Stmm break; 214105398Stmm 215105398Stmm case OFIOCNEXTPROP: 216105398Stmm if (node == 0 || of->of_buflen < 0) 217105398Stmm return (EINVAL); 218105398Stmm if (of->of_namelen != 0) { 219105398Stmm error = openfirm_getstr(of->of_namelen, of->of_name, 220105398Stmm &name); 221105398Stmm if (error) 222105398Stmm break; 223105398Stmm } 224186347Snwhitehorn ok = OF_nextprop(node, name, newname, sizeof(newname)); 225105398Stmm if (ok == 0) { 226105398Stmm error = ENOENT; 227105398Stmm break; 228105398Stmm } 229105398Stmm if (ok == -1) { 230105398Stmm error = EINVAL; 231105398Stmm break; 232105398Stmm } 233105398Stmm len = strlen(newname) + 1; 234105398Stmm if (len > of->of_buflen) 235105398Stmm len = of->of_buflen; 236105398Stmm else 237105398Stmm of->of_buflen = len; 238105398Stmm error = copyout(newname, of->of_buf, len); 239105398Stmm break; 240105398Stmm 241105398Stmm case OFIOCGETNEXT: 242105398Stmm node = OF_peer(node); 243105398Stmm *(phandle_t *)data = lastnode = node; 244105398Stmm break; 245105398Stmm 246105398Stmm case OFIOCGETCHILD: 247105398Stmm if (node == 0) 248105398Stmm return (EINVAL); 249105398Stmm node = OF_child(node); 250105398Stmm *(phandle_t *)data = lastnode = node; 251105398Stmm break; 252105398Stmm 253105398Stmm case OFIOCFINDDEVICE: 254105398Stmm error = openfirm_getstr(of->of_namelen, of->of_name, &name); 255105398Stmm if (error) 256105398Stmm break; 257105398Stmm node = OF_finddevice(name); 258105398Stmm if (node == 0 || node == -1) { 259105398Stmm error = ENOENT; 260105398Stmm break; 261105398Stmm } 262105398Stmm of->of_nodeid = lastnode = node; 263105398Stmm break; 264105398Stmm } 265105398Stmm 266105398Stmm if (name != NULL) 267105398Stmm free(name, M_TEMP); 268105398Stmm if (value != NULL) 269105398Stmm free(value, M_TEMP); 270105398Stmm 271105398Stmm return (error); 272105398Stmm} 273105398Stmm 274105398Stmmstatic int 275105398Stmmopenfirm_modevent(module_t mod, int type, void *data) 276105398Stmm{ 277194138Smarius 278105398Stmm switch(type) { 279105398Stmm case MOD_LOAD: 280105398Stmm if (bootverbose) 281133862Smarius printf("openfirm: <Open Firmware control device>\n"); 282105398Stmm /* 283105398Stmm * Allow only root access by default; this device may allow 284105398Stmm * users to peek into firmware passwords, and likely to crash 285105398Stmm * the machine on some boxen due to firmware quirks. 286105398Stmm */ 287105398Stmm openfirm_dev = make_dev(&openfirm_cdevsw, OPENFIRM_MINOR, 288105398Stmm UID_ROOT, GID_WHEEL, 0600, "openfirm"); 289105398Stmm return 0; 290105398Stmm 291105398Stmm case MOD_UNLOAD: 292105398Stmm destroy_dev(openfirm_dev); 293105398Stmm return 0; 294105398Stmm 295105398Stmm case MOD_SHUTDOWN: 296105398Stmm return 0; 297105398Stmm 298105398Stmm default: 299105398Stmm return EOPNOTSUPP; 300105398Stmm } 301105398Stmm} 302105398Stmm 303105398StmmDEV_MODULE(openfirm, openfirm_modevent, NULL); 304