1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2020-2021 The FreeBSD Foundation 5 * Copyright (c) 2022 Bjoern A. Zeeb 6 * 7 * This software was developed by Björn Zeeb under sponsorship from 8 * the FreeBSD Foundation. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/param.h> 33 #include <sys/kernel.h> 34 #include <sys/types.h> 35 #include <sys/malloc.h> 36 #include <sys/firmware.h> 37 #include <sys/queue.h> 38 #include <sys/taskqueue.h> 39 40 #include <linux/types.h> 41 #include <linux/device.h> 42 43 #include <linux/firmware.h> 44 #undef firmware 45 46 MALLOC_DEFINE(M_LKPI_FW, "lkpifw", "LinuxKPI firmware"); 47 48 struct lkpi_fw_task { 49 /* Task and arguments for the "nowait" callback. */ 50 struct task fw_task; 51 gfp_t gfp; 52 const char *fw_name; 53 struct device *dev; 54 void *drv; 55 void(*cont)(const struct linuxkpi_firmware *, void *); 56 }; 57 58 static int 59 _linuxkpi_request_firmware(const char *fw_name, const struct linuxkpi_firmware **fw, 60 struct device *dev, gfp_t gfp __unused, bool enoentok, bool warn) 61 { 62 const struct firmware *fbdfw; 63 struct linuxkpi_firmware *lfw; 64 const char *fwimg; 65 char *p; 66 uint32_t flags; 67 68 if (fw_name == NULL || fw == NULL || dev == NULL) { 69 *fw = NULL; 70 return (-EINVAL); 71 } 72 73 /* Set independent on "warn". To debug, bootverbose is avail. */ 74 flags = FIRMWARE_GET_NOWARN; 75 76 KASSERT(gfp == GFP_KERNEL, ("%s: gfp %#x\n", __func__, gfp)); 77 lfw = malloc(sizeof(*lfw), M_LKPI_FW, M_WAITOK | M_ZERO); 78 79 /* 80 * Linux can have a path in the firmware which is hard to replicate 81 * for auto-firmware-module-loading. 82 * On FreeBSD, depending on what people do, the firmware will either 83 * be called "fw", or "dir_fw", or "modname_dir_fw". The latter the 84 * driver author has to deal with herself (requesting the special name). 85 * We also optionally flatten '/'s and '.'s as some firmware modules do. 86 * We probe in the least-of-work order avoiding memory operations. 87 * It will be preferred to build the firmware .ko in a well matching 88 * way rather than adding more name-mangling-hacks here in the future 89 * (though we could if needed). 90 */ 91 /* (1) Try any name removed of path. */ 92 fwimg = strrchr(fw_name, '/'); 93 if (fwimg != NULL) 94 fwimg++; 95 if (fwimg == NULL || *fwimg == '\0') 96 fwimg = fw_name; 97 fbdfw = firmware_get_flags(fwimg, flags); 98 /* (2) Try the original name if we have not yet. */ 99 if (fbdfw == NULL && fwimg != fw_name) { 100 fwimg = fw_name; 101 fbdfw = firmware_get_flags(fwimg, flags); 102 } 103 /* (3) Flatten '/', '.' and '-' to '_' and try with adjusted name. */ 104 if (fbdfw == NULL && 105 (strchr(fw_name, '/') != NULL || strchr(fw_name, '.') != NULL || 106 strchr(fw_name, '-'))) { 107 fwimg = strdup(fw_name, M_LKPI_FW); 108 if (fwimg != NULL) { 109 while ((p = strchr(fwimg, '/')) != NULL) 110 *p = '_'; 111 fbdfw = firmware_get_flags(fwimg, flags); 112 if (fbdfw == NULL) { 113 while ((p = strchr(fwimg, '.')) != NULL) 114 *p = '_'; 115 fbdfw = firmware_get_flags(fwimg, flags); 116 } 117 if (fbdfw == NULL) { 118 while ((p = strchr(fwimg, '-')) != NULL) 119 *p = '_'; 120 fbdfw = firmware_get_flags(fwimg, flags); 121 } 122 free(__DECONST(void *, fwimg), M_LKPI_FW); 123 } 124 } 125 if (fbdfw == NULL) { 126 if (enoentok) 127 *fw = lfw; 128 else { 129 free(lfw, M_LKPI_FW); 130 *fw = NULL; 131 } 132 if (warn) 133 device_printf(dev->bsddev, "could not load firmware " 134 "image '%s'\n", fw_name); 135 return (-ENOENT); 136 } 137 138 device_printf(dev->bsddev,"successfully loaded firmware image '%s'\n", 139 fw_name); 140 lfw->fbdfw = fbdfw; 141 lfw->data = (const uint8_t *)fbdfw->data; 142 lfw->size = fbdfw->datasize; 143 *fw = lfw; 144 return (0); 145 } 146 147 static void 148 lkpi_fw_task(void *ctx, int pending) 149 { 150 struct lkpi_fw_task *lfwt; 151 const struct linuxkpi_firmware *fw; 152 153 KASSERT(ctx != NULL && pending == 1, ("%s: lfwt %p, pending %d\n", 154 __func__, ctx, pending)); 155 156 lfwt = ctx; 157 if (lfwt->cont == NULL) 158 goto out; 159 160 _linuxkpi_request_firmware(lfwt->fw_name, &fw, lfwt->dev, 161 lfwt->gfp, true, true); 162 163 /* 164 * Linux seems to run the callback if it cannot find the firmware. 165 * We call it in all cases as it is the only feedback to the requester. 166 */ 167 lfwt->cont(fw, lfwt->drv); 168 /* Do not assume fw is still valid! */ 169 170 out: 171 free(lfwt, M_LKPI_FW); 172 } 173 174 int 175 linuxkpi_request_firmware_nowait(struct module *mod __unused, bool _t __unused, 176 const char *fw_name, struct device *dev, gfp_t gfp, void *drv, 177 void(*cont)(const struct linuxkpi_firmware *, void *)) 178 { 179 struct lkpi_fw_task *lfwt; 180 int error; 181 182 lfwt = malloc(sizeof(*lfwt), M_LKPI_FW, M_WAITOK | M_ZERO); 183 lfwt->gfp = gfp; 184 lfwt->fw_name = fw_name; 185 lfwt->dev = dev; 186 lfwt->drv = drv; 187 lfwt->cont = cont; 188 TASK_INIT(&lfwt->fw_task, 0, lkpi_fw_task, lfwt); 189 error = taskqueue_enqueue(taskqueue_thread, &lfwt->fw_task); 190 191 if (error) 192 return (-error); 193 return (0); 194 } 195 196 int 197 linuxkpi_request_firmware(const struct linuxkpi_firmware **fw, 198 const char *fw_name, struct device *dev) 199 { 200 201 return (_linuxkpi_request_firmware(fw_name, fw, dev, GFP_KERNEL, false, 202 true)); 203 } 204 205 int 206 linuxkpi_firmware_request_nowarn(const struct linuxkpi_firmware **fw, 207 const char *fw_name, struct device *dev) 208 { 209 210 return (_linuxkpi_request_firmware(fw_name, fw, dev, GFP_KERNEL, false, 211 false)); 212 } 213 214 void 215 linuxkpi_release_firmware(const struct linuxkpi_firmware *fw) 216 { 217 218 if (fw == NULL) 219 return; 220 221 if (fw->fbdfw) 222 firmware_put(fw->fbdfw, FIRMWARE_UNLOAD); 223 free(__DECONST(void *, fw), M_LKPI_FW); 224 } 225 226 int 227 linuxkpi_request_partial_firmware_into_buf(const struct linuxkpi_firmware **fw, 228 const char *fw_name, struct device *dev, uint8_t *buf, size_t buflen, 229 size_t offset) 230 { 231 const struct linuxkpi_firmware *lfw; 232 int error; 233 234 error = linuxkpi_request_firmware(fw, fw_name, dev); 235 if (error != 0) 236 return (error); 237 238 lfw = *fw; 239 if ((offset + buflen) >= lfw->size) { 240 linuxkpi_release_firmware(lfw); 241 return (-ERANGE); 242 } 243 244 memcpy(buf, lfw->data + offset, buflen); 245 246 return (0); 247 } 248