Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F142471102
D43555.id133303.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
5 KB
Referenced Files
None
Subscribers
None
D43555.id133303.diff
View Options
diff --git a/sys/kern/subr_firmware.c b/sys/kern/subr_firmware.c
--- a/sys/kern/subr_firmware.c
+++ b/sys/kern/subr_firmware.c
@@ -29,6 +29,7 @@
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/eventhandler.h>
+#include <sys/fcntl.h>
#include <sys/firmware.h>
#include <sys/kernel.h>
#include <sys/linker.h>
@@ -36,9 +37,12 @@
#include <sys/malloc.h>
#include <sys/module.h>
#include <sys/mutex.h>
+#include <sys/namei.h>
#include <sys/priv.h>
#include <sys/proc.h>
#include <sys/queue.h>
+#include <sys/sbuf.h>
+#include <sys/sysctl.h>
#include <sys/systm.h>
#include <sys/taskqueue.h>
@@ -85,8 +89,9 @@
*/
struct priv_fw *parent;
- int flags; /* record FIRMWARE_UNLOAD requests */
-#define FW_UNLOAD 0x100
+ int flags;
+#define FW_DIRECT 0x080 /* Firmware directly loaded, file == NULL */
+#define FW_UNLOAD 0x100 /* record FIRMWARE_UNLOAD requests */
/*
* 'file' is private info managed by the autoload/unload code.
@@ -120,7 +125,8 @@
/*
* Firmware module operations are handled in a separate task as they
- * might sleep and they require directory context to do i/o.
+ * might sleep and they require directory context to do i/o. We also
+ * use this when loading director for the same reasons.
*/
static struct taskqueue *firmware_tq;
static struct task firmware_unload_task;
@@ -133,6 +139,11 @@
static MALLOC_DEFINE(M_FIRMWARE, "firmware", "device firmware images");
+static uint64_t firmware_max_size = 8u << 20; /* Default to 8MB cap */
+SYSCTL_U64(_debug, OID_AUTO, firmware_max_size,
+ CTLFLAG_RWTUN, &firmware_max_size, 0,
+ "Max size permitted for a firmware file.");
+
/*
* Helper function to lookup a name.
* As a side effect, it sets the pointer to a free slot, if any.
@@ -253,6 +264,80 @@
uint32_t flags;
};
+static const char *fw_path = "/boot/firmware/";
+
+static void
+try_fallback(const char *imagename, bool warn)
+{
+ struct nameidata nd;
+ struct thread *td = curthread; /* XXX */
+ struct ucred *cred = td ? td->td_ucred : NULL;
+ struct sbuf *sb;
+ struct priv_fw *fp;
+ const char *fn;
+ struct vattr vattr;
+ void *data = NULL;
+ const struct firmware *fw;
+ int flags;
+ size_t resid;
+ int error;
+
+ /*
+ * XXX TODO: Loop over some path instead of a single element path.
+ */
+ sb = sbuf_new_auto();
+ sbuf_printf(sb, "%s%s", fw_path, imagename);
+ sbuf_finish(sb);
+ fn = sbuf_data(sb);
+ printf("Trying to load %s\n", fn);
+
+ NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, fn);
+ flags = FREAD;
+ error = vn_open(&nd, &flags, 0, NULL);
+ if (error)
+ goto err;
+ NDFREE_PNBUF(&nd);
+ if (nd.ni_vp->v_type != VREG)
+ goto err2;
+ error = VOP_GETATTR(nd.ni_vp, &vattr, cred);
+ if (error)
+ goto err2;
+ /*
+ * Limit this to something sane, 8MB by default.
+ */
+ if (vattr.va_size > firmware_max_size) {
+ printf("Firmware of %ld bytes is longer than %ld max.\n",
+ vattr.va_size, firmware_max_size);
+ goto err2;
+ }
+ data = malloc(vattr.va_size, M_FIRMWARE, M_WAITOK);
+ error = vn_rdwr(UIO_READ, nd.ni_vp, (caddr_t)data, vattr.va_size, 0,
+ UIO_SYSSPACE, IO_NODELOCKED, cred, NOCRED, &resid, td);
+ /* XXX make data read only? */
+ VOP_UNLOCK(nd.ni_vp);
+ vn_close(nd.ni_vp, FREAD, cred, td);
+ nd.ni_vp = NULL;
+ if (error != 0 || resid != 0)
+ goto err;
+ fw = firmware_register(imagename, data, vattr.va_size, 0, NULL);
+ if (fw == NULL)
+ goto err;
+ fp = PRIV_FW(fw);
+ fp->flags |= FW_DIRECT; /* XXX NEED A LOCK? */
+ printf("%s: Loaded using %s\n", imagename, fn);
+ sbuf_delete(sb);
+ return;
+
+err2: /* cleanup in vn_open through vn_close */
+ VOP_UNLOCK(nd.ni_vp);
+ vn_close(nd.ni_vp, FREAD, cred, td);
+err:
+ free(data, M_FIRMWARE);
+ if (bootverbose || warn)
+ printf("%s: could not load %s either\n", imagename, fn);
+ sbuf_delete(sb);
+}
+
static void
loadimage(void *arg, int npending __unused)
{
@@ -266,6 +351,8 @@
if (bootverbose || (fwli->flags & FIRMWARE_GET_NOWARN) == 0)
printf("%s: could not load firmware image, error %d\n",
fwli->imagename, error);
+ try_fallback(fwli->imagename,
+ (fwli->flags & FIRMWARE_GET_NOWARN) == 0);
mtx_lock(&firmware_mtx);
goto done;
}
@@ -421,7 +508,7 @@
static void
unloadentry(void *unused1, int unused2)
{
- struct priv_fw *fp;
+ struct priv_fw *fp, *tmp;
mtx_lock(&firmware_mtx);
restart:
@@ -431,7 +518,22 @@
continue;
/*
- * Found an entry. Now:
+ * If we directly loaded the firmware, then we just need to
+ * remove the entry from the list and free the entry and go to
+ * the next one. There's no need for the indirection of the kld
+ * module case, we free memory and go to the next one.
+ */
+ if (fp->flags & FW_DIRECT) {
+ LIST_REMOVE(fp, link);
+ free(__DECONST(char *, fp->fw.data), M_FIRMWARE);
+ free(__DECONST(char *, fp->fw.name), M_FIRMWARE);
+ free(fp, M_FIRMWARE);
+ continue;
+ }
+
+ /*
+ * Found an entry. This is the kld case, so we have a more
+ * complex dance. Now:
* 1. make sure we scan the table again
* 2. clear FW_UNLOAD so we don't try this entry again.
* 3. release the lock while trying to unload the module.
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Jan 21, 6:02 AM (7 h, 7 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27793990
Default Alt Text
D43555.id133303.diff (5 KB)
Attached To
Mode
D43555: firmware: load binary firmware files
Attached
Detach File
Event Timeline
Log In to Comment