1 /*- 2 * Copyright (c) 2005, Sam Leffler <sam@errno.com> 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 unmodified, this list of conditions, and the following 10 * disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27 #include <sys/cdefs.h> 28 __FBSDID("$FreeBSD$"); 29 30 #include <sys/param.h> 31 #include <sys/kernel.h> 32 #include <sys/malloc.h> 33 #include <sys/queue.h> 34 #include <sys/taskqueue.h> 35 #include <sys/systm.h> 36 #include <sys/lock.h> 37 #include <sys/mutex.h> 38 #include <sys/errno.h> 39 #include <sys/linker.h> 40 #include <sys/firmware.h> 41 #include <sys/priv.h> 42 #include <sys/proc.h> 43 #include <sys/module.h> 44 45 #define FIRMWARE_MAX 30 46 static struct firmware firmware_table[FIRMWARE_MAX]; 47 struct task firmware_task; 48 struct mtx firmware_mtx; 49 MTX_SYSINIT(firmware, &firmware_mtx, "firmware table", MTX_DEF); 50 51 /* 52 * Register a firmware image with the specified name. The 53 * image name must not already be registered. If this is a 54 * subimage then parent refers to a previously registered 55 * image that this should be associated with. 56 */ 57 struct firmware * 58 firmware_register(const char *imagename, const void *data, size_t datasize, 59 unsigned int version, struct firmware *parent) 60 { 61 struct firmware *frp = NULL; 62 int i; 63 64 mtx_lock(&firmware_mtx); 65 for (i = 0; i < FIRMWARE_MAX; i++) { 66 struct firmware *fp = &firmware_table[i]; 67 68 if (fp->name == NULL) { 69 if (frp == NULL) 70 frp = fp; 71 continue; 72 } 73 if (strcasecmp(imagename, fp->name) == 0) { 74 mtx_unlock(&firmware_mtx); 75 printf("%s: image %s already registered!\n", 76 __func__, imagename); 77 return NULL; 78 } 79 } 80 if (frp == NULL) { 81 mtx_unlock(&firmware_mtx); 82 printf("%s: cannot register image %s, firmware table full!\n", 83 __func__, imagename); 84 return NULL; 85 } 86 frp->name = imagename; 87 frp->data = data; 88 frp->datasize = datasize; 89 frp->version = version; 90 frp->refcnt = 0; 91 frp->flags = 0; 92 if (parent != NULL) 93 parent->refcnt++; 94 frp->parent = parent; 95 frp->file = NULL; 96 mtx_unlock(&firmware_mtx); 97 return frp; 98 } 99 100 static void 101 clearentry(struct firmware *fp) 102 { 103 KASSERT(fp->refcnt == 0, ("image %s refcnt %u", fp->name, fp->refcnt)); 104 fp->name = NULL; 105 fp->file = NULL; 106 fp->data = NULL; 107 fp->datasize = 0; 108 fp->version = 0; 109 fp->flags = 0; 110 if (fp->parent != NULL) { /* release parent reference */ 111 fp->parent->refcnt--; 112 fp->parent = NULL; 113 } 114 } 115 116 static struct firmware * 117 lookup(const char *name) 118 { 119 struct firmware *fp; 120 int i; 121 122 for (i = 0; i < FIRMWARE_MAX; i++) { 123 fp = &firmware_table[i]; 124 if (fp->name != NULL && strcasecmp(name, fp->name) == 0) 125 return fp; 126 } 127 return NULL; 128 } 129 130 /* 131 * Unregister/remove a firmware image. If there are outstanding 132 * references an error is returned and the image is not removed 133 * from the registry. 134 */ 135 int 136 firmware_unregister(const char *imagename) 137 { 138 struct firmware *fp; 139 int refcnt = 0; 140 141 mtx_lock(&firmware_mtx); 142 /* 143 * NB: it is ok for the lookup to fail; this can happen 144 * when a module is unloaded on last reference and the 145 * module unload handler unregister's each of it's 146 * firmware images. 147 */ 148 fp = lookup(imagename); 149 if (fp != NULL) { 150 refcnt = fp->refcnt; 151 if (refcnt == 0) 152 clearentry(fp); 153 } 154 mtx_unlock(&firmware_mtx); 155 return (refcnt != 0 ? EBUSY : 0); 156 } 157 158 /* 159 * Lookup and potentially load the specified firmware image. 160 * If the firmware is not found in the registry attempt to 161 * load a kernel module with the image name. If the firmware 162 * is located a reference is returned. The caller must release 163 * this reference for the image to be eligible for removal/unload. 164 */ 165 struct firmware * 166 firmware_get(const char *imagename) 167 { 168 struct thread *td; 169 struct firmware *fp; 170 linker_file_t result; 171 int requested_load = 0; 172 173 again: 174 mtx_lock(&firmware_mtx); 175 fp = lookup(imagename); 176 if (fp != NULL) { 177 if (requested_load) 178 fp->file = result; 179 fp->refcnt++; 180 mtx_unlock(&firmware_mtx); 181 return fp; 182 } 183 /* 184 * Image not present, try to load the module holding it 185 * or if we already tried give up. 186 */ 187 mtx_unlock(&firmware_mtx); 188 if (requested_load) { 189 printf("%s: failed to load firmware image %s\n", 190 __func__, imagename); 191 return NULL; 192 } 193 td = curthread; 194 if (priv_check(td, PRIV_FIRMWARE_LOAD) != 0 || 195 securelevel_gt(td->td_ucred, 0) != 0) { 196 printf("%s: insufficient privileges to " 197 "load firmware image %s\n", __func__, imagename); 198 return NULL; 199 } 200 (void) linker_reference_module(imagename, NULL, &result); 201 requested_load = 1; 202 goto again; /* sort of an Algol-style for loop */ 203 } 204 205 static void 206 unloadentry(void *unused1, int unused2) 207 { 208 struct firmware *fp; 209 linker_file_t file; 210 int i, err; 211 212 mtx_lock(&firmware_mtx); 213 for (;;) { 214 /* Look for an unwanted entry that we explicitly loaded. */ 215 for (i = 0; i < FIRMWARE_MAX; i++) { 216 fp = &firmware_table[i]; 217 if (fp->name != NULL && fp->file != NULL && 218 fp->refcnt == 0 && 219 (fp->flags & FIRMWAREFLAG_KEEPKLDREF) == 0) 220 break; 221 fp = NULL; 222 } 223 if (fp == NULL) 224 break; 225 file = fp->file; 226 /* No longer explicitly loaded. */ 227 fp->file = NULL; 228 mtx_unlock(&firmware_mtx); 229 230 err = linker_release_module(NULL, NULL, file); 231 232 mtx_lock(&firmware_mtx); 233 if (err) { 234 /* 235 * If linker_release_module() failed then we still 236 * hold a reference on the module so it should not be 237 * possible for it to go away or be re-registered. 238 */ 239 KASSERT(fp->file == NULL, 240 ("firmware entry reused while referenced!")); 241 fp->file = file; 242 } 243 } 244 mtx_unlock(&firmware_mtx); 245 } 246 247 /* 248 * Release a reference to a firmware image returned by 249 * firmware_get. The reference is released and if this is 250 * the last reference to the firmware image the associated 251 * module may be released/unloaded. 252 */ 253 void 254 firmware_put(struct firmware *fp, int flags) 255 { 256 mtx_lock(&firmware_mtx); 257 fp->refcnt--; 258 if (fp->refcnt == 0) { 259 if ((flags & FIRMWARE_UNLOAD) == 0) 260 fp->flags |= FIRMWAREFLAG_KEEPKLDREF; 261 } 262 if (fp->file) 263 taskqueue_enqueue(taskqueue_thread, &firmware_task); 264 mtx_unlock(&firmware_mtx); 265 } 266 267 /* 268 * Module glue. 269 */ 270 static int 271 firmware_modevent(module_t mod, int type, void *unused) 272 { 273 struct firmware *fp; 274 int i; 275 276 switch (type) { 277 case MOD_LOAD: 278 TASK_INIT(&firmware_task, 0, unloadentry, NULL); 279 return 0; 280 case MOD_UNLOAD: 281 for (i = 0; i < FIRMWARE_MAX; i++) { 282 fp = &firmware_table[i]; 283 fp->flags &= ~FIRMWAREFLAG_KEEPKLDREF; 284 } 285 taskqueue_enqueue(taskqueue_thread, &firmware_task); 286 taskqueue_drain(taskqueue_thread, &firmware_task); 287 return 0; 288 } 289 return EINVAL; 290 } 291 292 static moduledata_t firmware_mod = { 293 "firmware", 294 firmware_modevent, 295 0 296 }; 297 DECLARE_MODULE(firmware, firmware_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); 298 MODULE_VERSION(firmware, 1); 299