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 char *name_unload = "UNLOADING"; 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 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, int keep_file) 101 { 102 KASSERT(fp->refcnt == 0, ("image %s refcnt %u", fp->name, fp->refcnt)); 103 if (keep_file && (fp->file != NULL)) 104 fp->name = name_unload; 105 else { 106 fp->name = NULL; 107 fp->file = NULL; 108 } 109 fp->data = NULL; 110 fp->datasize = 0; 111 fp->version = 0; 112 if (fp->parent != NULL) { /* release parent reference */ 113 fp->parent->refcnt--; 114 fp->parent = NULL; 115 } 116 } 117 118 static struct firmware * 119 lookup(const char *name) 120 { 121 int i; 122 123 for (i = 0; i < FIRMWARE_MAX; i++) { 124 struct firmware * fp = &firmware_table[i]; 125 if (fp->name != NULL && strcasecmp(name, fp->name) == 0) 126 return fp; 127 } 128 return NULL; 129 } 130 131 /* 132 * Unregister/remove a firmware image. If there are outstanding 133 * references an error is returned and the image is not removed 134 * from the registry. 135 */ 136 int 137 firmware_unregister(const char *imagename) 138 { 139 struct firmware *fp; 140 int refcnt = 0; 141 142 mtx_lock(&firmware_mtx); 143 /* 144 * NB: it is ok for the lookup to fail; this can happen 145 * when a module is unloaded on last reference and the 146 * module unload handler unregister's each of it's 147 * firmware images. 148 */ 149 fp = lookup(imagename); 150 if (fp != NULL) { 151 refcnt = fp->refcnt; 152 if (refcnt == 0) 153 clearentry(fp, 0); 154 } 155 mtx_unlock(&firmware_mtx); 156 return (refcnt != 0 ? EBUSY : 0); 157 } 158 159 /* 160 * Lookup and potentially load the specified firmware image. 161 * If the firmware is not found in the registry attempt to 162 * load a kernel module with the image name. If the firmware 163 * is located a reference is returned. The caller must release 164 * this reference for the image to be eligible for removal/unload. 165 */ 166 struct firmware * 167 firmware_get(const char *imagename) 168 { 169 struct thread *td; 170 struct firmware *fp; 171 linker_file_t result; 172 int requested_load = 0; 173 174 again: 175 mtx_lock(&firmware_mtx); 176 fp = lookup(imagename); 177 if (fp != NULL) { 178 if (requested_load) 179 fp->file = result; 180 fp->refcnt++; 181 mtx_unlock(&firmware_mtx); 182 return fp; 183 } 184 /* 185 * Image not present, try to load the module holding it 186 * or if we already tried give up. 187 */ 188 mtx_unlock(&firmware_mtx); 189 if (requested_load) { 190 printf("%s: failed to load firmware image %s\n", 191 __func__, imagename); 192 return NULL; 193 } 194 td = curthread; 195 if (suser(td) != 0 || 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 mtx_lock(&Giant); /* XXX */ 201 (void) linker_reference_module(imagename, NULL, &result); 202 mtx_unlock(&Giant); /* XXX */ 203 requested_load = 1; 204 goto again; /* sort of an Algol-style for loop */ 205 } 206 207 static void 208 unloadentry(void *unused1, int unused2) 209 { 210 struct firmware *fp; 211 212 mtx_lock(&firmware_mtx); 213 while ((fp = lookup(name_unload))) { 214 /* 215 * XXX: ugly, we should be able to lookup unlocked here if 216 * we properly lock around clearentry below to avoid double 217 * unload. Play it safe for now. 218 */ 219 mtx_unlock(&firmware_mtx); 220 221 linker_file_unload(fp->file, LINKER_UNLOAD_NORMAL); 222 223 mtx_lock(&firmware_mtx); 224 clearentry(fp, 0); 225 } 226 mtx_unlock(&firmware_mtx); 227 } 228 229 /* 230 * Release a reference to a firmware image returned by 231 * firmware_get. The reference is released and if this is 232 * the last reference to the firmware image the associated 233 * module may be released/unloaded. 234 */ 235 void 236 firmware_put(struct firmware *fp, int flags) 237 { 238 mtx_lock(&firmware_mtx); 239 fp->refcnt--; 240 if (fp->refcnt == 0 && (flags & FIRMWARE_UNLOAD)) 241 clearentry(fp, 1); 242 if (fp->file) 243 taskqueue_enqueue(taskqueue_thread, &firmware_task); 244 mtx_unlock(&firmware_mtx); 245 } 246 247 /* 248 * Module glue. 249 */ 250 static int 251 firmware_modevent(module_t mod, int type, void *unused) 252 { 253 switch (type) { 254 case MOD_LOAD: 255 TASK_INIT(&firmware_task, 0, unloadentry, NULL); 256 return 0; 257 case MOD_UNLOAD: 258 taskqueue_drain(taskqueue_thread, &firmware_task); 259 return 0; 260 } 261 return EINVAL; 262 } 263 264 static moduledata_t firmware_mod = { 265 "firmware", 266 firmware_modevent, 267 0 268 }; 269 DECLARE_MODULE(firmware, firmware_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); 270 MODULE_VERSION(firmware, 1); 271