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