Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F104035230
D43555.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.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_BINARY 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 binaries directly.
*/
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.
@@ -240,6 +251,85 @@
uint32_t flags;
};
+static const char *fw_path = "/boot/firmware/";
+
+static void
+try_binary_file(const char *imagename, uint32_t flags)
+{
+ struct nameidata nd;
+ struct thread *td = curthread;
+ 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;
+ bool warn = flags & FIRMWARE_GET_NOWARN;
+
+ /*
+ * XXX TODO: Loop over some path instead of a single element path.
+ * and fetch this path from the 'firmware_path' kenv the loader sets.
+ */
+ sb = sbuf_new_auto();
+ sbuf_printf(sb, "%s%s", fw_path, imagename);
+ sbuf_finish(sb);
+ fn = sbuf_data(sb);
+ if (bootverbose)
+ printf("Trying to load binary firmware from %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 %s is too big: %ld bytes, %ld bytes max.\n",
+ fn, 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(fn, data, vattr.va_size, 0, NULL);
+ if (fw == NULL)
+ goto err;
+ fp = PRIV_FW(fw);
+ fp->flags |= FW_BINARY;
+ if (bootverbose)
+ printf("%s: Loaded binary firmware 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 binary firmware %s either\n", imagename, fn);
+ sbuf_delete(sb);
+}
+
static void
loadimage(void *arg, int npending __unused)
{
@@ -253,6 +343,7 @@
if (bootverbose || (fwli->flags & FIRMWARE_GET_NOWARN) == 0)
printf("%s: could not load firmware image, error %d\n",
fwli->imagename, error);
+ try_binary_file(fwli->imagename, fwli->flags);
mtx_lock(&firmware_mtx);
goto done;
}
@@ -408,17 +499,32 @@
static void
unloadentry(void *unused1, int unused2)
{
- struct priv_fw *fp;
+ struct priv_fw *fp, *tmp;
mtx_lock(&firmware_mtx);
restart:
- LIST_FOREACH(fp, &firmware_table, link) {
- if (fp->file == NULL || fp->refcnt != 0 ||
- (fp->flags & FW_UNLOAD) == 0)
+ LIST_FOREACH_SAFE(fp, &firmware_table, link, tmp) {
+ if (((fp->flags & FW_BINARY) == 0 && fp->file == NULL) ||
+ fp->refcnt != 0 || (fp->flags & FW_UNLOAD) == 0)
+ continue;
+
+ /*
+ * 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_BINARY) != 0) {
+ 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. Now:
+ * 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
Tue, Dec 3, 5:10 PM (20 h, 50 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15010304
Default Alt Text
D43555.diff (5 KB)
Attached To
Mode
D43555: firmware: load binary firmware files
Attached
Detach File
Event Timeline
Log In to Comment