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 98 if (bootverbose) 99 printf("firmware: '%s' version %u: %zu bytes loaded at %p\n", 100 imagename, version, datasize, data); 101 102 return frp; 103 } 104 105 static void 106 clearentry(struct firmware *fp) 107 { 108 KASSERT(fp->refcnt == 0, ("image %s refcnt %u", fp->name, fp->refcnt)); 109 fp->name = NULL; 110 fp->file = NULL; 111 fp->data = NULL; 112 fp->datasize = 0; 113 fp->version = 0; 114 fp->flags = 0; 115 if (fp->parent != NULL) { /* release parent reference */ 116 fp->parent->refcnt--; 117 fp->parent = NULL; 118 } 119 } 120 121 static struct firmware * 122 lookup(const char *name) 123 { 124 struct firmware *fp; 125 int i; 126 127 for (i = 0; i < FIRMWARE_MAX; i++) { 128 fp = &firmware_table[i]; 129 if (fp->name != NULL && strcasecmp(name, fp->name) == 0) 130 return fp; 131 } 132 return NULL; 133 } 134 135 /* 136 * Unregister/remove a firmware image. If there are outstanding 137 * references an error is returned and the image is not removed 138 * from the registry. 139 */ 140 int 141 firmware_unregister(const char *imagename) 142 { 143 struct firmware *fp; 144 int refcnt = 0; 145 146 mtx_lock(&firmware_mtx); 147 /* 148 * NB: it is ok for the lookup to fail; this can happen 149 * when a module is unloaded on last reference and the 150 * module unload handler unregister's each of it's 151 * firmware images. 152 */ 153 fp = lookup(imagename); 154 if (fp != NULL) { 155 refcnt = fp->refcnt; 156 if (refcnt == 0) 157 clearentry(fp); 158 } 159 mtx_unlock(&firmware_mtx); 160 return (refcnt != 0 ? EBUSY : 0); 161 } 162 163 /* 164 * Lookup and potentially load the specified firmware image. 165 * If the firmware is not found in the registry attempt to 166 * load a kernel module with the image name. If the firmware 167 * is located a reference is returned. The caller must release 168 * this reference for the image to be eligible for removal/unload. 169 */ 170 struct firmware * 171 firmware_get(const char *imagename) 172 { 173 struct thread *td; 174 struct firmware *fp; 175 linker_file_t result; 176 int requested_load = 0; 177 178 again: 179 mtx_lock(&firmware_mtx); 180 fp = lookup(imagename); 181 if (fp != NULL) { 182 if (requested_load) 183 fp->file = result; 184 fp->refcnt++; 185 mtx_unlock(&firmware_mtx); 186 return fp; 187 } 188 /* 189 * Image not present, try to load the module holding it 190 * or if we already tried give up. 191 */ 192 mtx_unlock(&firmware_mtx); 193 if (requested_load) { 194 printf("%s: failed to load firmware image %s\n", 195 __func__, imagename); 196 (void) linker_release_module(imagename, NULL, NULL); 197 return NULL; 198 } 199 td = curthread; 200 if (priv_check(td, PRIV_FIRMWARE_LOAD) != 0 || 201 securelevel_gt(td->td_ucred, 0) != 0) { 202 printf("%s: insufficient privileges to " 203 "load firmware image %s\n", __func__, imagename); 204 return NULL; 205 } 206 (void) linker_reference_module(imagename, NULL, &result); 207 requested_load = 1; 208 goto again; /* sort of an Algol-style for loop */ 209 } 210 211 static void 212 unloadentry(void *unused1, int unused2) 213 { 214 struct firmware *fp; 215 linker_file_t file; 216 int i, err; 217 218 mtx_lock(&firmware_mtx); 219 for (;;) { 220 /* Look for an unwanted entry that we explicitly loaded. */ 221 for (i = 0; i < FIRMWARE_MAX; i++) { 222 fp = &firmware_table[i]; 223 if (fp->name != NULL && fp->file != NULL && 224 fp->refcnt == 0 && 225 (fp->flags & FIRMWAREFLAG_KEEPKLDREF) == 0) 226 break; 227 fp = NULL; 228 } 229 if (fp == NULL) 230 break; 231 file = fp->file; 232 /* No longer explicitly loaded. */ 233 fp->file = NULL; 234 mtx_unlock(&firmware_mtx); 235 236 err = linker_release_module(NULL, NULL, file); 237 238 mtx_lock(&firmware_mtx); 239 if (err) { 240 /* 241 * If linker_release_module() failed then we still 242 * hold a reference on the module so it should not be 243 * possible for it to go away or be re-registered. 244 */ 245 KASSERT(fp->file == NULL, 246 ("firmware entry reused while referenced!")); 247 fp->file = file; 248 } 249 } 250 mtx_unlock(&firmware_mtx); 251 } 252 253 /* 254 * Release a reference to a firmware image returned by 255 * firmware_get. The reference is released and if this is 256 * the last reference to the firmware image the associated 257 * module may be released/unloaded. 258 */ 259 void 260 firmware_put(struct firmware *fp, int flags) 261 { 262 mtx_lock(&firmware_mtx); 263 fp->refcnt--; 264 if (fp->refcnt == 0) { 265 if ((flags & FIRMWARE_UNLOAD) == 0) 266 fp->flags |= FIRMWAREFLAG_KEEPKLDREF; 267 } 268 if (fp->file) 269 taskqueue_enqueue(taskqueue_thread, &firmware_task); 270 mtx_unlock(&firmware_mtx); 271 } 272 273 /* 274 * Module glue. 275 */ 276 static int 277 firmware_modevent(module_t mod, int type, void *unused) 278 { 279 struct firmware *fp; 280 int i; 281 282 switch (type) { 283 case MOD_LOAD: 284 TASK_INIT(&firmware_task, 0, unloadentry, NULL); 285 return 0; 286 case MOD_UNLOAD: 287 for (i = 0; i < FIRMWARE_MAX; i++) { 288 fp = &firmware_table[i]; 289 fp->flags &= ~FIRMWAREFLAG_KEEPKLDREF; 290 } 291 taskqueue_enqueue(taskqueue_thread, &firmware_task); 292 taskqueue_drain(taskqueue_thread, &firmware_task); 293 return 0; 294 } 295 return EINVAL; 296 } 297 298 static moduledata_t firmware_mod = { 299 "firmware", 300 firmware_modevent, 301 0 302 }; 303 DECLARE_MODULE(firmware, firmware_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); 304 MODULE_VERSION(firmware, 1); 305