Submit #3041 ยป fw-remove-parent.patch
share/examples/kld/firmware/wrap-fw_module.sh | ||
---|---|---|
int error;
|
||
switch (type) {
|
||
case MOD_LOAD:
|
||
fp = firmware_register("${MODNAME}", _binary_${FWSYM}_start , (size_t)(_binary_${FWSYM}_end - _binary_${FWSYM}_start), 0, NULL);
|
||
fp = firmware_register("${MODNAME}", _binary_${FWSYM}_start , (size_t)(_binary_${FWSYM}_end - _binary_${FWSYM}_start), 0);
|
||
if (fp == NULL)
|
||
goto fail_0;
|
||
return (0);
|
share/man/man9/firmware.9 | ||
---|---|---|
.Fa "const void *data"
|
||
.Fa "size_t datasize"
|
||
.Fa "unsigned int version"
|
||
.Fa "const struct firmware *parent"
|
||
.Fc
|
||
.Ft int
|
||
.Fn firmware_unregister "const char *imagename"
|
||
... | ... | |
.Nm kern.module_path
|
||
which on most systems defaults to
|
||
.Nm /boot/kernel;/boot/modules .
|
||
.Pp
|
||
Note that in case a module contains multiple images,
|
||
the caller should first request a
|
||
.Fn firmware_get
|
||
for the first image contained in the module, followed by requests
|
||
for the other images.
|
||
.Sh BUILDING FIRMWARE LOADABLE MODULES
|
||
A firmware module is built by embedding the
|
||
.Nm firmware image
|
||
... | ... | |
.include <bsd.kmod.mk>
|
||
.Ed
|
||
where KMOD is the basename of the module; FIRMWS is a list of
|
||
colon-separated tuples indicating the image_file's to be embedded
|
||
in the module, the imagename and version of each firmware image.
|
||
where KMOD is the basename of the module; FIRMWS is a colon-separated
|
||
tuple indicating the image_file to be embedded in the module, the
|
||
imagename and version of the firmware image.
|
||
.Pp
|
||
If you need to embed firmware images into a system, you should write
|
||
appropriate entries in the
|
sys/dev/disk/ispfw/ispfw.c | ||
---|---|---|
break; \
|
||
if (firmware_register(#token, token##_risc_code, \
|
||
token##_risc_code[3] * sizeof(token##_risc_code[3]), \
|
||
ISPFW_VERSION, NULL) == NULL) { \
|
||
ISPFW_VERSION) == NULL) { \
|
||
kprintf("%s: unable to register firmware <%s>\n", \
|
||
MODULE_NAME, #token); \
|
||
break; \
|
sys/kern/subr_firmware.c | ||
---|---|---|
*
|
||
* In order for the above to work, the 'file' field must remain
|
||
* unchanged in firmware_unregister().
|
||
*
|
||
* Images residing in the same module are linked to each other
|
||
* through the 'parent' argument of firmware_register().
|
||
* One image (typically, one with the same name as the module to let
|
||
* the autoloading mechanism work) is considered the parent image for
|
||
* all other images in the same module. Children affect the refcount
|
||
* on the parent image preventing improper unloading of the image itself.
|
||
*/
|
||
struct priv_fw {
|
||
int refcnt; /* reference count */
|
||
/*
|
||
* parent entry, see above. Set on firmware_register(),
|
||
* cleared on firmware_unregister().
|
||
*/
|
||
struct priv_fw *parent;
|
||
int flags; /* record FIRMWARE_UNLOAD requests */
|
||
#define FW_UNLOAD 0x100
|
||
... | ... | |
/*
|
||
* Register a firmware image with the specified name. The
|
||
* image name must not already be registered. If this is a
|
||
* subimage then parent refers to a previously registered
|
||
* image that this should be associated with.
|
||
* image name must not already be registered.
|
||
*/
|
||
const struct firmware *
|
||
firmware_register(const char *imagename, const void *data, size_t datasize,
|
||
unsigned int version, const struct firmware *parent)
|
||
unsigned int version)
|
||
{
|
||
struct priv_fw *match, *frp;
|
||
... | ... | |
frp->fw.data = data;
|
||
frp->fw.datasize = datasize;
|
||
frp->fw.version = version;
|
||
if (parent != NULL) {
|
||
frp->parent = PRIV_FW(parent);
|
||
frp->parent->refcnt++;
|
||
}
|
||
lockmgr(&firmware_lock, LK_RELEASE);
|
||
if (bootverbose)
|
||
kprintf("firmware: '%s' version %u: %zu bytes loaded at %p\n",
|
||
... | ... | |
/*
|
||
* It is ok for the lookup to fail; this can happen
|
||
* when a module is unloaded on last reference and the
|
||
* module unload handler unregister's each of it's
|
||
* firmware images.
|
||
* module unload handler unregisters it's firmware
|
||
* image.
|
||
*/
|
||
err = 0;
|
||
} else if (fp->refcnt != 0) { /* cannot unregister */
|
||
... | ... | |
} else {
|
||
linker_file_t x = fp->file; /* save value */
|
||
if (fp->parent != NULL) /* release parent reference */
|
||
fp->parent->refcnt--;
|
||
/*
|
||
* Clear the whole entry with bzero to make sure we
|
||
* do not forget anything. Then restore 'file' which is
|
||
... | ... | |
static void
|
||
unloadentry(void *unused1, int unused2)
|
||
{
|
||
int limit = FIRMWARE_MAX;
|
||
int i; /* current cycle */
|
||
int i;
|
||
lockmgr(&firmware_lock, LK_EXCLUSIVE);
|
||
/*
|
||
* Scan the table. limit is set to make sure we make another
|
||
* full sweep after matching an entry that requires unloading.
|
||
*/
|
||
for (i = 0; i < limit; i++) {
|
||
/* Scan the table. */
|
||
for (i = 0; i < FIRMWARE_MAX; i++) {
|
||
struct priv_fw *fp;
|
||
int err;
|
||
fp = &firmware_table[i % FIRMWARE_MAX];
|
||
fp = &firmware_table[i];
|
||
if (fp->fw.name == NULL || fp->file == NULL ||
|
||
fp->refcnt != 0 || (fp->flags & FW_UNLOAD) == 0)
|
||
continue;
|
||
/*
|
||
* Found an entry. Now:
|
||
* 1. bump up limit to make sure we make another full round;
|
||
* 2. clear FW_UNLOAD so we don't try this entry again.
|
||
* 3. release the lock while trying to unload the module.
|
||
* 1. clear FW_UNLOAD so we don't try this entry again
|
||
* if unloading will be unsuccessful.
|
||
* 2. release the lock while trying to unload the module.
|
||
* 'file' remains set so that the entry cannot be reused
|
||
* in the meantime (it also means that fp->file will
|
||
* not change while we release the lock).
|
||
*/
|
||
limit = i + FIRMWARE_MAX; /* make another full round */
|
||
fp->flags &= ~FW_UNLOAD; /* do not try again */
|
||
lockmgr(&firmware_lock, LK_RELEASE);
|
sys/sys/firmware.h | ||
---|---|---|
* The firmware abstraction provides an interface for loading firmware
|
||
* images into the kernel and making them available to clients.
|
||
*
|
||
* Firmware images are usually embedded in kernel loadable modules that can
|
||
* be loaded on-demand or pre-loaded as desired. Modules may contain
|
||
* one or more firmware images that are stored as opaque data arrays
|
||
* and registered with a unique string name. Clients request
|
||
* firmware by name, and are returned a struct firmware * below on success.
|
||
* The kernel keeps track of references to firmware images to allow/prevent
|
||
* module/data unload.
|
||
* Firmware images are usually embedded in kernel loadable modules
|
||
* that can be loaded on-demand or pre-loaded as desired. Modules may
|
||
* contain one firmware image that is stored as opaque data array and
|
||
* registered with a unique string name. Clients request firmware by
|
||
* name, and are returned a struct firmware * below on success. The
|
||
* kernel keeps track of references to firmware images to
|
||
* allow/prevent module/data unload.
|
||
*
|
||
* When multiple images are stored in one module, the first image is
|
||
* treated as the master with the other images holding references
|
||
* to it. This means that to unload the module each dependent/subimage
|
||
* must first have its references removed.
|
||
* In order for automatic loading to work, the master image must have
|
||
* the same name as the module it is embedded into.
|
||
*/
|
||
... | ... | |
};
|
||
const struct firmware *firmware_register(const char *,
|
||
const void *, size_t, unsigned int, const struct firmware *);
|
||
const void *, size_t, unsigned int);
|
||
int firmware_unregister(const char *);
|
||
const struct firmware *firmware_get(const char *);
|
||
#define FIRMWARE_UNLOAD 0x0001 /* unload if unreferenced */
|
sys/tools/fw_stub.awk | ||
---|---|---|
function usage ()
|
||
{
|
||
print "usage: fw_stub <firmware:name>* [-l name] [-m modname] [-c outfile]";
|
||
print "usage: fw_stub <firmware:name> [-l name] [-m modname] [-c outfile]";
|
||
exit 1;
|
||
}
|
||
... | ... | |
} else
|
||
usage();
|
||
}
|
||
} else {
|
||
} else if (num_files == 0) {
|
||
split(ARGV[i], curr, ":");
|
||
filenames[num_files] = curr[1];
|
||
filename = curr[1];
|
||
if (length(curr[2]) > 0)
|
||
shortnames[num_files] = curr[2];
|
||
shortname = curr[2];
|
||
else
|
||
shortnames[num_files] = curr[1];
|
||
shortname = curr[1];
|
||
if (length(curr[3]) > 0)
|
||
versions[num_files] = int(curr[3]);
|
||
version = int(curr[3]);
|
||
else
|
||
versions[num_files] = 0;
|
||
version = 0;
|
||
num_files++;
|
||
} else {
|
||
# The argument is a firmware image specification
|
||
# but we already have one
|
||
usage();
|
||
}
|
||
}
|
||
... | ... | |
printc("static long " opt_l "_license_ack = 0;");
|
||
}
|
||
for (file_i = 0; file_i < num_files; file_i++) {
|
||
symb = filenames[file_i];
|
||
# '-', '.' and '/' are converted to '_' by ld/objcopy
|
||
gsub(/-|\.|\//, "_", symb);
|
||
printc("extern char _binary_" symb "_start[], _binary_" symb "_end[];");
|
||
}
|
||
symb = filename;
|
||
# '-', '.' and '/' are converted to '_' by ld/objcopy
|
||
gsub(/-|\.|\//, "_", symb);
|
||
printc("extern char _binary_" symb "_start[], _binary_" symb "_end[];");
|
||
printc("\nstatic int\n"\
|
||
modname "_fw_modevent(module_t mod, int type, void *unused)\
|
||
{\
|
||
const struct firmware *fp;");
|
||
if (num_files > 1)
|
||
printc("\tconst struct firmware *parent;");
|
||
printc("\tint error;\
|
||
switch (type) {\
|
||
case MOD_LOAD:\n");
|
||
... | ... | |
}\n");
|
||
}
|
||
for (file_i = 0; file_i < num_files; file_i++) {
|
||
short = shortnames[file_i];
|
||
symb = filenames[file_i];
|
||
version = versions[file_i];
|
||
# '-', '.' and '/' are converted to '_' by ld/objcopy
|
||
gsub(/-|\.|\//, "_", symb);
|
||
short = shortname;
|
||
symb = filename;
|
||
version = version;
|
||
# '-', '.' and '/' are converted to '_' by ld/objcopy
|
||
gsub(/-|\.|\//, "_", symb);
|
||
reg = "\t\tfp = ";
|
||
reg = reg "firmware_register(\"" short "\", _binary_" symb "_start , ";
|
||
reg = reg "(size_t)(_binary_" symb "_end - _binary_" symb "_start), ";
|
||
reg = reg version ", ";
|
||
reg = "\t\tfp = ";
|
||
reg = reg "firmware_register(\"" short "\", _binary_" symb "_start , ";
|
||
reg = reg "(size_t)(_binary_" symb "_end - _binary_" symb "_start), ";
|
||
reg = reg version ");";
|
||
if (file_i == 0)
|
||
reg = reg "NULL);";
|
||
else
|
||
reg = reg "parent);";
|
||
printc(reg);
|
||
printc(reg);
|
||
printc("\t\tif (fp == NULL)");
|
||
printc("\t\t\tgoto fail_" file_i ";");
|
||
if (file_i == 0 && num_files > 1)
|
||
printc("\t\tparent = fp;");
|
||
}
|
||
printc("\t\tif (fp == NULL)");
|
||
printc("\t\t\tgoto fail;");
|
||
printc("\t\treturn (0);");
|
||
for (file_i = num_files - 1; file_i > 0; file_i--) {
|
||
printc("fail_" file_i ":")
|
||
printc("\t\t(void)firmware_unregister(\"" shortnames[file_i - 1] "\");");
|
||
}
|
||
printc("\tfail_0:");
|
||
printc("\tfail:");
|
||
printc("\t\treturn (ENXIO);");
|
||
printc("\tcase MOD_UNLOAD:");
|
||
for (file_i = 1; file_i < num_files; file_i++) {
|
||
printc("\t\terror = firmware_unregister(\"" shortnames[file_i] "\");");
|
||
printc("\t\tif (error)");
|
||
printc("\t\t\treturn (error);");
|
||
}
|
||
printc("\t\terror = firmware_unregister(\"" shortnames[0] "\");");
|
||
printc("\t\terror = firmware_unregister(\"" shortname "\");");
|
||
printc("\t\treturn (error);\
|
||
}\
|