diff --git a/share/man/man9/firmware.9 b/share/man/man9/firmware.9 --- a/share/man/man9/firmware.9 +++ b/share/man/man9/firmware.9 @@ -23,13 +23,14 @@ .\" .\" $FreeBSD$ .\" -.Dd March 14, 2019 +.Dd January 27, 2021 .Dt FIRMWARE 9 .Os .Sh NAME .Nm firmware_register , .Nm firmware_unregister , .Nm firmware_get , +.Nm firmware_get_flags , .Nm firmware_put .Nd firmware image loading and management .Sh SYNOPSIS @@ -57,6 +58,8 @@ .Fn firmware_unregister "const char *imagename" .Ft "const struct firmware *" .Fn firmware_get "const char *imagename" +.Ft "const struct firmware *" +.Fn firmware_get_flags "const char *imagename" "uint32_t flags" .Ft void .Fn firmware_put "const struct firmware *fp" "int flags" .Sh DESCRIPTION @@ -97,7 +100,13 @@ .Fn firmware_get with the .Nm imagename -they want as an argument. +they want as an argument, or by calling +.Fn firmware_get_flags +with the +.Nm imagename +and +.Nm flags +they want as an arguments. If a matching image is not already registered, the firmware subsystem will try to load it using the mechanisms specified below (typically, a kernel module @@ -134,11 +143,22 @@ it does not unregister the image and returns EBUSY. .Pp .Fn firmware_get -returns the requested firmware image. +and +.Fn firmware_get_flags +return the requested firmware image. +The +.Fa flags +argument may be set to +.Dv FIRMWARE_GET_NOWARN +to indicate that errors on firmware load or registration should +only be logged in case of +.Nm booverbose . If the image is not yet registered with the system, -the function tries to load it. +the functions try to load it. This involves the linker subsystem and disk access, so .Fn firmware_get +or +.Fn firmware_get_flags must not be called with any locks (except for .Va Giant ) . Note also that if the firmware image is loaded from a filesystem @@ -149,9 +169,11 @@ .Pp On success, .Fn firmware_get -returns a pointer to the image description and increases the reference count +and +.Fn firmware_get_flags +return a pointer to the image description and increase the reference count for this image. -On failure, the function returns NULL. +On failure, the functions return NULL. .Pp .Fn firmware_put drops a reference to a firmware image. @@ -183,10 +205,12 @@ .Pp When .Fn firmware_get +or +.Fn firmware_get_flags does not find the requested image, it tries to load it using one of the available loading mechanisms. At the moment, there is only one, namely -.Nm Loadable kernel modules : +.Nm Loadable kernel modules . .Pp A firmware image named .Nm foo @@ -203,6 +227,8 @@ Note that in case a module contains multiple images, the caller should first request a .Fn firmware_get +or +.Fn firmware_get_flags for the first image contained in the module, followed by requests for the other images. .Sh BUILDING FIRMWARE LOADABLE MODULES 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 @@ -238,36 +238,42 @@ return (err); } +struct fw_loadimage { + const char *imagename; + uint32_t flags; +}; + static void -loadimage(void *arg, int npending) +loadimage(void *arg, int npending __unused) { - char *imagename = arg; + struct fw_loadimage *fwli = arg; struct priv_fw *fp; linker_file_t result; int error; - error = linker_reference_module(imagename, NULL, &result); + error = linker_reference_module(fwli->imagename, NULL, &result); if (error != 0) { - printf("%s: could not load firmware image, error %d\n", - imagename, error); + if (bootverbose || (fwli->flags & FIRMWARE_GET_NOWARN) == 0) + printf("%s: could not load firmware image, error %d\n", + fwli->imagename, error); mtx_lock(&firmware_mtx); goto done; } mtx_lock(&firmware_mtx); - fp = lookup(imagename); + fp = lookup(fwli->imagename); if (fp == NULL || fp->file != NULL) { mtx_unlock(&firmware_mtx); if (fp == NULL) printf("%s: firmware image loaded, " - "but did not register\n", imagename); - (void) linker_release_module(imagename, NULL, NULL); + "but did not register\n", fwli->imagename); + (void) linker_release_module(fwli->imagename, NULL, NULL); mtx_lock(&firmware_mtx); goto done; } fp->file = result; /* record the module identity */ done: - wakeup_one(imagename); + wakeup_one(arg); mtx_unlock(&firmware_mtx); } @@ -279,7 +285,7 @@ * release this reference for the image to be eligible for removal/unload. */ const struct firmware * -firmware_get(const char *imagename) +firmware_get_flags(const char *imagename, uint32_t flags) { struct task fwload_task; struct thread *td; @@ -306,11 +312,15 @@ * Also we must not hold any mtx's over this call which is problematic. */ if (!cold) { - TASK_INIT(&fwload_task, 0, loadimage, __DECONST(void *, - imagename)); + struct fw_loadimage fwli; + + fwli.imagename = imagename; + fwli.flags = flags; + TASK_INIT(&fwload_task, 0, loadimage, (void *)&fwli); taskqueue_enqueue(firmware_tq, &fwload_task); - msleep(__DECONST(void *, imagename), &firmware_mtx, 0, - "fwload", 0); + PHOLD(curproc); + msleep((void *)&fwli, &firmware_mtx, 0, "fwload", 0); + PRELE(curproc); } /* * After attempting to load the module, see if the image is registered. @@ -328,6 +338,13 @@ return &fp->fw; } +const struct firmware * +firmware_get(const char *imagename) +{ + + return (firmware_get_flags(imagename, 0)); +} + /* * Release a reference to a firmware image returned by firmware_get. * The caller may specify, with the FIRMWARE_UNLOAD flag, its desire diff --git a/sys/sys/firmware.h b/sys/sys/firmware.h --- a/sys/sys/firmware.h +++ b/sys/sys/firmware.h @@ -60,7 +60,12 @@ const struct firmware *firmware_register(const char *, const void *, size_t, unsigned int, const struct firmware *); int firmware_unregister(const char *); + +#define FIRMWARE_GET_NOWARN 0x0001 /* Do not warn if firmware not found. */ +const struct firmware *firmware_get_flags(const char *, uint32_t flags); const struct firmware *firmware_get(const char *); + #define FIRMWARE_UNLOAD 0x0001 /* unload if unreferenced */ void firmware_put(const struct firmware *, int); + #endif /* _SYS_FIRMWARE_H_ */