diff options
-rw-r--r-- | share/examples/kld/Makefile | 2 | ||||
-rw-r--r-- | share/examples/kld/firmware/Makefile | 5 | ||||
-rw-r--r-- | share/examples/kld/firmware/README | 18 | ||||
-rw-r--r-- | share/examples/kld/firmware/fwconsumer/Makefile | 6 | ||||
-rw-r--r-- | share/examples/kld/firmware/fwconsumer/fw_consumer.c | 78 | ||||
-rw-r--r-- | share/examples/kld/firmware/fwimage/Makefile | 6 | ||||
-rw-r--r-- | share/examples/kld/firmware/fwimage/firmware.img | bin | 0 -> 537 bytes | |||
-rw-r--r-- | share/man/man9/Makefile | 1 | ||||
-rw-r--r-- | share/man/man9/firmware.9 | 115 | ||||
-rw-r--r-- | sys/conf/NOTES | 1 | ||||
-rw-r--r-- | sys/conf/files | 1 | ||||
-rw-r--r-- | sys/conf/kmod.mk | 28 | ||||
-rw-r--r-- | sys/kern/subr_firmware.c | 270 | ||||
-rw-r--r-- | sys/modules/Makefile | 1 | ||||
-rw-r--r-- | sys/modules/firmware/Makefile | 8 | ||||
-rw-r--r-- | sys/sys/firmware.h | 61 | ||||
-rw-r--r-- | sys/tools/fw_stub.awk | 189 |
17 files changed, 789 insertions, 1 deletions
diff --git a/share/examples/kld/Makefile b/share/examples/kld/Makefile index 6c539bd50ac4..908f68e37aa9 100644 --- a/share/examples/kld/Makefile +++ b/share/examples/kld/Makefile @@ -67,6 +67,6 @@ # $FreeBSD$ # -SUBDIR= cdev syscall dyn_sysctl +SUBDIR= cdev dyn_sysctl firmware syscall .include <bsd.subdir.mk> diff --git a/share/examples/kld/firmware/Makefile b/share/examples/kld/firmware/Makefile new file mode 100644 index 000000000000..b4b733faae98 --- /dev/null +++ b/share/examples/kld/firmware/Makefile @@ -0,0 +1,5 @@ +# $FreeBSD$ + +SUBDIR= fwimage fwconsumer + +.include <bsd.subdir.mk> diff --git a/share/examples/kld/firmware/README b/share/examples/kld/firmware/README new file mode 100644 index 000000000000..075c5e5723b5 --- /dev/null +++ b/share/examples/kld/firmware/README @@ -0,0 +1,18 @@ +$FreeBSD$ + +This is a simple example of the firmware(9) system. It consists of two +parts: + +1) fwimage + This is the firmware image (the ascii art of beastie from the boot + menu). The Makefile lists the firmware file "firmware.img" and the + short handle for this firmware image "beastie". + Note that the module is called "beastie" as well so that it can be + loaded automatically if requested. + +2) fwconsumer + This module tries to get the a firmware image called "beastie", + checks if the data is '\0'-terminated and prints it to the console. + It keeps a reference to the firmware until it is unloaded. + +This is mainly to demonstrate how to construct firmware image modules. diff --git a/share/examples/kld/firmware/fwconsumer/Makefile b/share/examples/kld/firmware/fwconsumer/Makefile new file mode 100644 index 000000000000..1dea0c7eab69 --- /dev/null +++ b/share/examples/kld/firmware/fwconsumer/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= fw_consumer +SRCS= fw_consumer.c + +.include <bsd.kmod.mk> diff --git a/share/examples/kld/firmware/fwconsumer/fw_consumer.c b/share/examples/kld/firmware/fwconsumer/fw_consumer.c new file mode 100644 index 000000000000..97ab99aa0f7a --- /dev/null +++ b/share/examples/kld/firmware/fwconsumer/fw_consumer.c @@ -0,0 +1,78 @@ +/*- + * Copyright (c) 2006, Max Laier <mlaier@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/errno.h> +#include <sys/systm.h> +#include <sys/linker.h> +#include <sys/firmware.h> +#include <sys/proc.h> +#include <sys/module.h> + +static struct firmware *fp; + +static int +fw_consumer_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + fp = firmware_get("beastie"); + + if (fp == NULL) + return (ENOENT); + + if (((const char *)fp->data)[fp->datasize - 1] != '\0') { + firmware_put(fp, FIRMWARE_UNLOAD); + return (EINVAL); + } + printf("%s", (const char *)fp->data); + + return (0); + case MOD_UNLOAD: + printf("Bye!\n"); + + if (fp != NULL) { + printf("%s", (const char *)fp->data); + firmware_put(fp, FIRMWARE_UNLOAD); + } + + return (0); + } + return (EINVAL); +} + +static moduledata_t fw_consumer_mod = { + "fw_consumer", + fw_consumer_modevent, + 0 +}; +DECLARE_MODULE(fw_consumer, fw_consumer_mod, SI_SUB_DRIVERS, SI_ORDER_ANY); +MODULE_VERSION(fw_consumer, 1); +MODULE_DEPEND(fw_consumer, firmware, 1, 1, 1); diff --git a/share/examples/kld/firmware/fwimage/Makefile b/share/examples/kld/firmware/fwimage/Makefile new file mode 100644 index 000000000000..5c0ad7a8b49b --- /dev/null +++ b/share/examples/kld/firmware/fwimage/Makefile @@ -0,0 +1,6 @@ +# $FreeBSD$ + +KMOD= beastie +FIRMWS= firmware.img:beastie + +.include <bsd.kmod.mk> diff --git a/share/examples/kld/firmware/fwimage/firmware.img b/share/examples/kld/firmware/fwimage/firmware.img Binary files differnew file mode 100644 index 000000000000..afc3c23287e7 --- /dev/null +++ b/share/examples/kld/firmware/fwimage/firmware.img diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile index bde16291dae0..63eb6fccf06e 100644 --- a/share/man/man9/Makefile +++ b/share/man/man9/Makefile @@ -94,6 +94,7 @@ MAN= accept_filter.9 \ EVENTHANDLER.9 \ extattr.9 \ fetch.9 \ + firmware.9 \ g_access.9 \ g_attach.9 \ g_bio.9 \ diff --git a/share/man/man9/firmware.9 b/share/man/man9/firmware.9 new file mode 100644 index 000000000000..900c07cee33f --- /dev/null +++ b/share/man/man9/firmware.9 @@ -0,0 +1,115 @@ +.\" Copyright (c) 2006 Max Laier <mlaier@FreeBSD.org> +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd January 6, 2006 +.Os +.Dt FIRMWARE 9 +.Sh NAME +.Nm firmware_register , +.Nm firmware_unregister , +.Nm firmware_get , +.Nm firmware_put +.Nd firmware image loading and management +.Sh SYNOPSIS +.In sys/param.h +.In sys/linker.h +.In sys/firmware.h +.Bd -literal +struct firmware { + const char *name; /* system-wide name */ + const void *data; /* location of image */ + size_t datasize; /* size of image in bytes */ + unsigned int version; /* version of the image */ + int refcnt; /* held references */ + struct firmware *parent; /* not null if a subimage */ + linker_file_t file; /* loadable module */ +}; +.Ed +.Ft struct firmware * +.Fo firmware_register +.Fa "const char *imagename" +.Fa "const void *data" +.Fa "size_t datasize" +.Fa "unsigned int version" +.Fa "struct firmware *parent" +.Fc +.Ft int +.Fn firmware_unregister "const char *imagename" +.Ft struct firmware * +.Fn firmware_get "const char *imagename" +.Ft void +.Fn firmware_put "struct firmware *fp" "int flags" +.Sh DESCRIPTION +The firmware abstraction provides a convenient interface for loading firmware +images into the kernel. +Specially crafted kernel modules are used to hold the firmware images. +.Pp +The function +.Fn firmware_register +is used on load of such modules to register contained firmware images. +The arguments to +.Fn firmware_register +include a name that identifies the image for later requests to the firmware +system, a pointer to the actual image, the size of the image and an optional +parent image. +The parent image is used to keep track of references to a given module so that +it can be unloaded on last reference. +.Pp +The function +.Fn firmware_unregister +removes the firmware image identified by the name from the system if there +are no pending references or returns an error otherwise. +.Pp +The function +.Fn firmware_get +returns the requested firmware image. +If the image is not yet registered with the system +.Fn firmware_get +tries to load a module with the corresponding name. +This involves the linker subsystem and disk access which is why +.Fn firmware_get +must not be called with any locks (except for Giant). +On success +.Fn firmware_get +returns a pointer to the image description and increases the reference count +for this image. +.Pp +The function +.Fn firmware_put +is used to drop the reference to a firmware image. +The flags argument may be set to +.Dv FIRMWARE_UNLOAD +to indicate that the caller wishes to unload the corresponding module if the +image becomes unreferenced. +.Sh SEE ALSO +.Xr module 9 +.Pp +.Pa /usr/share/examples/kld +.Sh HISTORY +The firmware system was introduced in +.Fx 7.0 . +.Sh AUTHORS +This manual page was written by +.An Max Laier Aq mlaier@FreeBSD.org . diff --git a/sys/conf/NOTES b/sys/conf/NOTES index 65284c33a883..e6b139bd6c20 100644 --- a/sys/conf/NOTES +++ b/sys/conf/NOTES @@ -1187,6 +1187,7 @@ device nmdm #back-to-back tty devices device md #Memory/malloc disk device snp #Snoop device - to look at pty/vty/etc.. device ccd #Concatenated disk driver +device firmware #firmware(9) support # Kernel side iconv library options LIBICONV diff --git a/sys/conf/files b/sys/conf/files index b8832bfac782..243f110b4b19 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -1298,6 +1298,7 @@ kern/subr_clock.c optional genclock kern/subr_devstat.c standard kern/subr_disk.c standard kern/subr_eventhandler.c standard +kern/subr_firmware.c optional firmware kern/subr_hints.c standard kern/subr_kdb.c standard kern/subr_kobj.c standard diff --git a/sys/conf/kmod.mk b/sys/conf/kmod.mk index 90b79d841b74..5f90d0bcdbfc 100644 --- a/sys/conf/kmod.mk +++ b/sys/conf/kmod.mk @@ -36,6 +36,8 @@ # # SRCS List of source files. # +# FIRMWS List of firmware images in format filename:shortname:version +# # DESTDIR The tree where the module gets installed. [not set] # # +++ targets +++ @@ -119,6 +121,32 @@ CFLAGS+= -fno-omit-frame-pointer CFLAGS+= -mlongcall -fno-omit-frame-pointer .endif +.if defined(FIRMWS) +.if !exists(@) +${KMOD:S/$/.c/}: @ +.else +${KMOD:S/$/.c/}: @/tools/fw_stub.awk +.endif + ${AWK} -f @/tools/fw_stub.awk ${FIRMWS} -m${KMOD} -c${KMOD:S/$/.c/g} + +SRCS+= ${KMOD:S/$/.c/} +CLEANFILES+= ${KMOD:S/$/.c/} + +.for _firmw in ${FIRMWS} +${_firmw:C/\:.*$/.fwo/}: ${_firmw:C/\:.*$//} + @${ECHO} ${_firmw:C/\:.*$//} ${.ALLSRC:M*${_firmw:C/\:.*$//}} +.if !exists(${.CURDIR}/${_firmw:C/\:.*$//}) + ln -s ${.ALLSRC:M*${_firmw:C/\:.*$//}} ${_firmw:C/\:.*$//} + ${LD} -b binary ${LDFLAGS} -r -d -o ${.TARGET} ${_firmw:C/\:.*$//} + rm -f ${_firmw:C/\:.*$//} +.else + ${LD} -b binary ${LDFLAGS} -r -d -o ${.TARGET} ${_firmw:C/\:.*$//} +.endif + +OBJS+= ${_firmw:C/\:.*$/.fwo/} +.endfor +.endif + OBJS+= ${SRCS:N*.h:R:S/$/.o/g} .if !defined(PROG) diff --git a/sys/kern/subr_firmware.c b/sys/kern/subr_firmware.c new file mode 100644 index 000000000000..43ed1aee479a --- /dev/null +++ b/sys/kern/subr_firmware.c @@ -0,0 +1,270 @@ +/*- + * Copyright (c) 2005, Sam Leffler <sam@errno.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/queue.h> +#include <sys/taskqueue.h> +#include <sys/systm.h> +#include <sys/lock.h> +#include <sys/mutex.h> +#include <sys/errno.h> +#include <sys/linker.h> +#include <sys/firmware.h> +#include <sys/proc.h> +#include <sys/module.h> + +#define FIRMWARE_MAX 30 +static char *name_unload = "UNLOADING"; +static struct firmware firmware_table[FIRMWARE_MAX]; +struct task firmware_task; +struct mtx firmware_mtx; +MTX_SYSINIT(firmware, &firmware_mtx, "firmware table", MTX_DEF); + +/* + * 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. + */ +struct firmware * +firmware_register(const char *imagename, const void *data, size_t datasize, + unsigned int version, struct firmware *parent) +{ + struct firmware *frp = NULL; + int i; + + mtx_lock(&firmware_mtx); + for (i = 0; i < FIRMWARE_MAX; i++) { + struct firmware *fp = &firmware_table[i]; + + if (fp->name == NULL) { + if (frp == NULL) + frp = fp; + continue; + } + if (strcasecmp(imagename, fp->name) == 0) { + mtx_unlock(&firmware_mtx); + printf("%s: image %s already registered!\n", + __func__, imagename); + return NULL; + } + } + if (frp == NULL) { + mtx_unlock(&firmware_mtx); + printf("%s: cannot register image %s, firmware table full!\n", + __func__, imagename); + return NULL; + } + frp->name = imagename; + frp->data = data; + frp->datasize = datasize; + frp->version = version; + frp->refcnt = 0; + if (parent != NULL) + parent->refcnt++; + frp->parent = parent; + frp->file = NULL; + mtx_unlock(&firmware_mtx); + return frp; +} + +static void +clearentry(struct firmware *fp, int keep_file) +{ + KASSERT(fp->refcnt == 0, ("image %s refcnt %u", fp->name, fp->refcnt)); + if (keep_file && (fp->file != NULL)) + fp->name = name_unload; + else { + fp->name = NULL; + fp->file = NULL; + } + fp->data = NULL; + fp->datasize = 0; + fp->version = 0; + if (fp->parent != NULL) { /* release parent reference */ + fp->parent->refcnt--; + fp->parent = NULL; + } +} + +static struct firmware * +lookup(const char *name) +{ + int i; + + for (i = 0; i < FIRMWARE_MAX; i++) { + struct firmware * fp = &firmware_table[i]; + if (fp->name != NULL && strcasecmp(name, fp->name) == 0) + return fp; + } + return NULL; +} + +/* + * Unregister/remove a firmware image. If there are outstanding + * references an error is returned and the image is not removed + * from the registry. + */ +int +firmware_unregister(const char *imagename) +{ + struct firmware *fp; + int refcnt = 0; + + mtx_lock(&firmware_mtx); + /* + * NB: 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. + */ + fp = lookup(imagename); + if (fp != NULL) { + refcnt = fp->refcnt; + if (refcnt == 0) + clearentry(fp, 0); + } + mtx_unlock(&firmware_mtx); + return (refcnt != 0 ? EBUSY : 0); +} + +/* + * Lookup and potentially load the specified firmware image. + * If the firmware is not found in the registry attempt to + * load a kernel module with the image name. If the firmware + * is located a reference is returned. The caller must release + * this reference for the image to be eligible for removal/unload. + */ +struct firmware * +firmware_get(const char *imagename) +{ + struct thread *td; + struct firmware *fp; + linker_file_t result; + int requested_load = 0; + +again: + mtx_lock(&firmware_mtx); + fp = lookup(imagename); + if (fp != NULL) { + if (requested_load) + fp->file = result; + fp->refcnt++; + mtx_unlock(&firmware_mtx); + return fp; + } + /* + * Image not present, try to load the module holding it + * or if we already tried give up. + */ + mtx_unlock(&firmware_mtx); + if (requested_load) { + printf("%s: failed to load firmware image %s\n", + __func__, imagename); + return NULL; + } + td = curthread; + if (suser(td) != 0 || securelevel_gt(td->td_ucred, 0) != 0) { + printf("%s: insufficient privileges to " + "load firmware image %s\n", __func__, imagename); + return NULL; + } + mtx_lock(&Giant); /* XXX */ + (void) linker_reference_module(imagename, NULL, &result); + mtx_unlock(&Giant); /* XXX */ + requested_load = 1; + goto again; /* sort of an Algol-style for loop */ +} + +static void +unloadentry(void *unused1, int unused2) +{ + struct firmware *fp; + + mtx_lock(&firmware_mtx); + while ((fp = lookup(name_unload))) { + /* + * XXX: ugly, we should be able to lookup unlocked here if + * we properly lock around clearentry below to avoid double + * unload. Play it safe for now. + */ + mtx_unlock(&firmware_mtx); + + linker_file_unload(fp->file, LINKER_UNLOAD_NORMAL); + + mtx_lock(&firmware_mtx); + clearentry(fp, 0); + } + mtx_unlock(&firmware_mtx); +} + +/* + * Release a reference to a firmware image returned by + * firmware_get. The reference is released and if this is + * the last reference to the firmware image the associated + * module may be released/unloaded. + */ +void +firmware_put(struct firmware *fp, int flags) +{ + mtx_lock(&firmware_mtx); + fp->refcnt--; + if (fp->refcnt == 0 && (flags & FIRMWARE_UNLOAD)) + clearentry(fp, 1); + if (fp->file) + taskqueue_enqueue(taskqueue_thread, &firmware_task); + mtx_unlock(&firmware_mtx); +} + +/* + * Module glue. + */ +static int +firmware_modevent(module_t mod, int type, void *unused) +{ + switch (type) { + case MOD_LOAD: + TASK_INIT(&firmware_task, 0, unloadentry, NULL); + return 0; + case MOD_UNLOAD: + taskqueue_drain(taskqueue_thread, &firmware_task); + return 0; + } + return EINVAL; +} + +static moduledata_t firmware_mod = { + "firmware", + firmware_modevent, + 0 +}; +DECLARE_MODULE(firmware, firmware_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST); +MODULE_VERSION(firmware, 1); diff --git a/sys/modules/Makefile b/sys/modules/Makefile index 7bf42a123d72..c19fdf503152 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -77,6 +77,7 @@ SUBDIR= ${_3dfx} \ fdescfs \ ${_fe} \ firewire \ + firmware \ fxp \ ${_gem} \ geom \ diff --git a/sys/modules/firmware/Makefile b/sys/modules/firmware/Makefile new file mode 100644 index 000000000000..82ed1026e47e --- /dev/null +++ b/sys/modules/firmware/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../kern + +KMOD= firmware +SRCS= subr_firmware.c + +.include <bsd.kmod.mk> diff --git a/sys/sys/firmware.h b/sys/sys/firmware.h new file mode 100644 index 000000000000..4d84942b024f --- /dev/null +++ b/sys/sys/firmware.h @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 2005, Sam Leffler <sam@errno.com> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef _SYS_FIRMWARE_H_ +#define _SYS_FIRMWARE_H_ +/* + * Loadable firmware support. + * + * Firmware images are 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. Consumers request + * firmware by name with held references counted to use in disallowing + * module/data unload. + * + * When multiple images are stored in one module the one 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. + */ +struct firmware { + const char *name; /* system-wide name */ + const void *data; /* location of image */ + size_t datasize; /* size of image in bytes */ + unsigned int version; /* version of the image */ + int refcnt; /* held references */ + struct firmware *parent; /* not null if a subimage */ + linker_file_t file; /* loadable module */ +}; + +struct firmware *firmware_register(const char *, const void *, size_t, + unsigned int, struct firmware *); +int firmware_unregister(const char *); +struct firmware *firmware_get(const char *); +#define FIRMWARE_UNLOAD 0x0001 /* unload if unreferenced */ +void firmware_put(struct firmware *, int); +#endif /* _SYS_FIRMWARE_H_ */ diff --git a/sys/tools/fw_stub.awk b/sys/tools/fw_stub.awk new file mode 100644 index 000000000000..72383baf6dcd --- /dev/null +++ b/sys/tools/fw_stub.awk @@ -0,0 +1,189 @@ +#!/usr/bin/awk -f + +#- +# Copyright (c) 2006 Max Laier. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS `AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# $FreeBSD$ + +# +# Script to generate module .c file from a list of firmware images +# + +function usage () +{ + print "usage: fw_stub <firmware:name>* [-m modname] [-c outfile]"; + exit 1; +} + +# These are just for convenience ... +function printc(s) +{ + if (opt_c) + print s > ctmpfilename; + else + print s > "/dev/stdout"; +} + +BEGIN { + +# +# Process the command line. +# + +num_files = 0; + +for (i = 1; i < ARGC; i++) { + if (ARGV[i] ~ /^-/) { + # + # awk doesn't have getopt(), so we have to do it ourselves. + # This is a bit clumsy, but it works. + # + for (j = 2; j <= length(ARGV[i]); j++) { + o = substr(ARGV[i], j, 1); + if (o == "c") { + if (length(ARGV[i]) > j) { + opt_c = substr(ARGV[i], j + 1); + break; + } + else { + if (++i < ARGC) + opt_c = ARGV[i]; + else + usage(); + } + } else if (o == "m") { + if (length(ARGV[i]) > j) { + opt_m = substr(ARGV[i], j + 1); + break; + } + else { + if (++i < ARGC) + opt_m = ARGV[i]; + else + usage(); + } + } else + usage(); + } + } else { + split(ARGV[i], curr, ":"); + filenames[num_files] = curr[1]; + if (length(curr[2]) > 0) + shortnames[num_files] = curr[2]; + else + shortnames[num_files] = curr[2]; + if (length(curr[3]) > 0) + versions[num_files] = int(curr[3]); + else + versions[num_files] = 0; + num_files++; + } +} + +if (!num_files || !opt_m) + usage(); + +cfilename = opt_c; +ctmpfilename = cfilename ".tmp"; + +printc("#include <sys/param.h>\ +#include <sys/errno.h>\ +#include <sys/kernel.h>\ +#include <sys/module.h>\ +#include <sys/linker.h>\ +#include <sys/firmware.h>\n"); + +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[];"); +} + +printc("\nstatic int\n"\ +opt_m "_fw_modevent(module_t mod, int type, void *unused)\ +{\ + struct firmware *fp;\ + switch (type) {\ + case MOD_LOAD:"); + +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); + + if (file_i == 0) + reg = "\t\tfp = "; + else + reg = "\t\t(void)"; + + 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 "fp);"; + + printc(reg); +} + +printc("\t\treturn (0);\ + case MOD_UNLOAD:"); + +for (file_i = 1; file_i < num_files; file_i++) { + printc("\t\tfirmware_unregister(\"" shortnames[file_i] "\");"); +} + +printc("\t\tfirmware_unregister(\"" shortnames[0] "\");"); + +printc("\t\treturn (0);\ + }\ + return (EINVAL);\ +}\ +\ +static moduledata_t " opt_m "_fw_mod = {\ + \"" opt_m "_fw\",\ + " opt_m "_fw_modevent,\ + 0\ +};\ +DECLARE_MODULE(" opt_m "_fw, " opt_m "_fw_mod, SI_SUB_DRIVERS, SI_ORDER_FIRST);\ +MODULE_VERSION(" opt_m "_fw, 1);\ +MODULE_DEPEND(iwi_boot_fw, firmware, 1, 1, 1);\ +"); + +if (opt_c) + if ((rc = system("mv -f " ctmpfilename " " cfilename))) { + print "'mv -f " ctmpfilename " " cfilename "' failed: " rc \ + > "/dev/stderr"; + exit 1; + } + +exit 0; + +} |