openpromio.c revision 331722
1/*- 2 * Copyright (c) 2003 Jake Burkholder. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: stable/11/sys/dev/ofw/openpromio.c 331722 2018-03-29 02:50:57Z eadler $"); 30 31#include <sys/param.h> 32#include <sys/systm.h> 33#include <sys/conf.h> 34#include <sys/errno.h> 35#include <sys/fcntl.h> 36#include <sys/ioccom.h> 37#include <sys/kernel.h> 38#include <sys/malloc.h> 39#include <sys/module.h> 40 41#include <dev/ofw/openfirm.h> 42#include <dev/ofw/openpromio.h> 43 44/* 45 * This provides a Solaris compatible character device interface to 46 * Open Firmware. It exists entirely for compatibility with software 47 * like X11, and only the features that are actually needed for that 48 * are implemented. The interface sucks too much to actually use, 49 * new code should use the /dev/openfirm device. 50 */ 51 52static d_open_t openprom_open; 53static d_close_t openprom_close; 54static d_ioctl_t openprom_ioctl; 55 56static int openprom_modevent(module_t mode, int type, void *data); 57static int openprom_node_valid(phandle_t node); 58static int openprom_node_search(phandle_t root, phandle_t node); 59 60static struct cdevsw openprom_cdevsw = { 61 .d_version = D_VERSION, 62 .d_flags = D_NEEDGIANT, 63 .d_open = openprom_open, 64 .d_close = openprom_close, 65 .d_ioctl = openprom_ioctl, 66 .d_name = "openprom", 67}; 68 69static int openprom_is_open; 70static struct cdev *openprom_dev; 71static phandle_t openprom_node; 72 73static int 74openprom_open(struct cdev *dev, int oflags, int devtype, struct thread *td) 75{ 76 77 if (openprom_is_open != 0) 78 return (EBUSY); 79 openprom_is_open = 1; 80 return (0); 81} 82 83static int 84openprom_close(struct cdev *dev, int fflag, int devtype, struct thread *td) 85{ 86 87 openprom_is_open = 0; 88 return (0); 89} 90 91static int 92openprom_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, 93 struct thread *td) 94{ 95 struct openpromio *oprom; 96 phandle_t node; 97 uint32_t len; 98 size_t done; 99 int proplen; 100 char *prop; 101 char *buf; 102 int error; 103 104 if ((flags & FREAD) == 0) 105 return (EPERM); 106 107 prop = buf = NULL; 108 error = 0; 109 switch (cmd) { 110 case OPROMCHILD: 111 case OPROMNEXT: 112 if (data == NULL || *(void **)data == NULL) 113 return (EINVAL); 114 oprom = *(void **)data; 115 error = copyin(&oprom->oprom_size, &len, sizeof(len)); 116 if (error != 0) 117 break; 118 if (len != sizeof(node)) { 119 error = EINVAL; 120 break; 121 } 122 error = copyin(&oprom->oprom_array, &node, sizeof(node)); 123 if (error != 0) 124 break; 125 error = openprom_node_valid(node); 126 if (error != 0) 127 break; 128 switch (cmd) { 129 case OPROMCHILD: 130 node = OF_child(node); 131 break; 132 case OPROMNEXT: 133 node = OF_peer(node); 134 break; 135 } 136 error = copyout(&node, &oprom->oprom_array, sizeof(node)); 137 if (error != 0) 138 break; 139 openprom_node = node; 140 break; 141 case OPROMGETPROP: 142 case OPROMNXTPROP: 143 if (data == NULL || *(void **)data == NULL) 144 return (EINVAL); 145 oprom = *(void **)data; 146 error = copyin(&oprom->oprom_size, &len, sizeof(len)); 147 if (error != 0) 148 break; 149 if (len > OPROMMAXPARAM) { 150 error = EINVAL; 151 break; 152 } 153 prop = malloc(len, M_TEMP, M_WAITOK | M_ZERO); 154 if (prop == NULL) { 155 error = ENOMEM; 156 break; 157 } 158 error = copyinstr(&oprom->oprom_array, prop, len, &done); 159 if (error != 0) 160 break; 161 buf = malloc(OPROMMAXPARAM, M_TEMP, M_WAITOK | M_ZERO); 162 if (buf == NULL) { 163 error = ENOMEM; 164 break; 165 } 166 node = openprom_node; 167 switch (cmd) { 168 case OPROMGETPROP: 169 proplen = OF_getproplen(node, prop); 170 if (proplen > OPROMMAXPARAM) { 171 error = EINVAL; 172 break; 173 } 174 error = OF_getprop(node, prop, buf, proplen); 175 break; 176 case OPROMNXTPROP: 177 error = OF_nextprop(node, prop, buf, OPROMMAXPARAM); 178 proplen = strlen(buf); 179 break; 180 } 181 if (error != -1) { 182 error = copyout(&proplen, &oprom->oprom_size, 183 sizeof(proplen)); 184 if (error == 0) 185 error = copyout(buf, &oprom->oprom_array, 186 proplen + 1); 187 } else 188 error = EINVAL; 189 break; 190 default: 191 error = ENOIOCTL; 192 break; 193 } 194 195 if (prop != NULL) 196 free(prop, M_TEMP); 197 if (buf != NULL) 198 free(buf, M_TEMP); 199 200 return (error); 201} 202 203static int 204openprom_node_valid(phandle_t node) 205{ 206 207 if (node == 0) 208 return (0); 209 return (openprom_node_search(OF_peer(0), node)); 210} 211 212static int 213openprom_node_search(phandle_t root, phandle_t node) 214{ 215 phandle_t child; 216 217 if (root == node) 218 return (0); 219 for (child = OF_child(root); child != 0 && child != -1; 220 child = OF_peer(child)) 221 if (openprom_node_search(child, node) == 0) 222 return (0); 223 return (EINVAL); 224} 225 226static int 227openprom_modevent(module_t mode, int type, void *data) 228{ 229 230 switch (type) { 231 case MOD_LOAD: 232 openprom_dev = make_dev(&openprom_cdevsw, 0, UID_ROOT, 233 GID_WHEEL, 0600, "openprom"); 234 return (0); 235 case MOD_UNLOAD: 236 destroy_dev(openprom_dev); 237 return (0); 238 default: 239 return (EOPNOTSUPP); 240 } 241} 242 243DEV_MODULE(openprom, openprom_modevent, NULL); 244