Subject: Re: kern/32342: OpenBSD firmware loading framework
To: None <gnats-bugs@netbsd.org>
From: Jason Thorpe <thorpej@shagadelic.org>
List: netbsd-bugs
Date: 12/19/2005 14:20:53
On Dec 19, 2005, at 2:10 PM, plunky@rya-online.net wrote:

> 	I want to load driver firmware file from inside the kernel and there
> 	is no way currently to do that. Christos Zoulas mentioned on tech- 
> kern
> 	that OpenBSD had a framwork to handle this so I took a look. This is
> 	the result, please find below a shar archive containing two new  
> files:
>
> 		sys/dev/firmload.c
> 		share/man/man9/load_firmware.9

I'm not sure I like this:

- Tries to read it all in at once (into a wired kernel buffer  
allocated from kmem_map).  It would be better to allow a driver to  
read in pieces at a time, potentially into pageable memory.

- It should be possible to set the directory search path via sysctl,  
rather than hard-coding "/etc/firmware".

>
> 	which are exactly as in OpenBSD. Following this are patches for these
> 	two files to make them work for NetBSD. Following that is a cvs diff
> 	against the following files:
>
> 		sys/conf/files
> 		sys/sys/device.h
> 		share/man/man9/Makefile
>
> 	to integrate this patch with the -current version of NetBSD
>
>> How-To-Repeat:
> 	
>> Fix:
>
> # This is a shell archive.  Save it in a file, remove anything before
> # this line, and then unpack it by entering "sh file".  Note, it may
> # create directories; files and directories will be owned by you and
> # have default permissions.
> #
> # This archive contains:
> #
> #	sys/dev/firmload.c
> #	share/man/man9/load_firmware.9
> #
> echo x - sys/dev/firmload.c
> sed 's/^X//' >sys/dev/firmload.c << 'END-of-sys/dev/firmload.c'
> X/*	$OpenBSD: firmload.c,v 1.5 2005/08/01 08:15:02 deraadt Exp $	*/
> X
> X/*
> X * Copyright (c) 2004 Theo de Raadt <deraadt@openbsd.org>
> X *
> X * Permission to use, copy, modify, and distribute this software  
> for any
> X * purpose with or without fee is hereby granted, provided that  
> the above
> X * copyright notice and this permission notice appear in all copies.
> X *
> X * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL  
> WARRANTIES
> X * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> X * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE  
> LIABLE FOR
> X * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY  
> DAMAGES
> X * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER  
> IN AN
> X * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,  
> ARISING OUT OF
> X * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> X */
> X
> X#include <sys/param.h>
> X#include <sys/systm.h>
> X#include <sys/syslimits.h>
> X#include <sys/time.h>
> X#include <sys/namei.h>
> X#include <sys/vnode.h>
> X#include <sys/mount.h>
> X#include <sys/errno.h>
> X#include <sys/malloc.h>
> X#include <sys/proc.h>
> X#include <sys/device.h>
> X
> Xint
> Xloadfirmware(const char *name, u_char **bufp, size_t *buflen)
> X{
> X	struct proc *p = curproc;
> X	struct nameidata nid;
> X	char *path, *ptr;
> X	struct iovec iov;
> X	struct uio uio;
> X	struct vattr va;
> X	int error;
> X
> X	if (!rootvp || !vcount(rootvp))
> X		return (EIO);
> X
> X	path = malloc(MAXPATHLEN, M_TEMP, M_NOWAIT);
> X	if (path == NULL)
> X		return (ENOMEM);
> X		
> X	snprintf(path, MAXPATHLEN, "/etc/firmware/%s", name);
> X
> X	NDINIT(&nid, LOOKUP, NOFOLLOW|LOCKLEAF, UIO_SYSSPACE, path, p);
> X	error = namei(&nid);
> X	if (error)
> X		goto err;
> X	error = VOP_GETATTR(nid.ni_vp, &va, p->p_ucred, p);
> X	if (error)
> X		goto fail;
> X	if (va.va_size == 0) {
> X		error = EINVAL;
> X		goto fail;
> X	}
> X	if (va.va_size > FIRMWARE_MAX) {
> X		error = E2BIG;
> X		goto fail;
> X	}
> X	ptr = malloc(va.va_size, M_DEVBUF, M_NOWAIT);
> X	if (ptr == NULL) {
> X		error = ENOMEM;
> X		goto fail;
> X	}
> X
> X	iov.iov_base = ptr;
> X	iov.iov_len = va.va_size;
> X	uio.uio_iov = &iov;
> X	uio.uio_iovcnt = 1;
> X	uio.uio_offset = 0;
> X	uio.uio_resid = va.va_size;
> X	uio.uio_segflg = UIO_SYSSPACE;
> X	uio.uio_rw = UIO_READ;
> X	uio.uio_procp = p;
> X
> X	error = VOP_READ(nid.ni_vp, &uio, 0, NOCRED);
> X
> X	if (error == 0) {
> X		*bufp = ptr;
> X		*buflen = va.va_size;
> X	} else
> X		free(ptr, M_DEVBUF);
> X
> Xfail:
> X	vput(nid.ni_vp);
> Xerr:
> X	if (path)
> X		free(path, M_TEMP);
> X	return (error);
> X}
> END-of-sys/dev/firmload.c
> echo x - share/man/man9/load_firmware.9
> sed 's/^X//' >share/man/man9/load_firmware.9 << 'END-of-share/man/ 
> man9/load_firmware.9'
> X.\"	$OpenBSD: loadfirmware.9,v 1.2 2004/11/22 16:41:44 jmc Exp $
> X.\"
> X.\" Copyright (c) 2004 Theo de Raadt
> X.\" All rights reserved.
> X.\"
> X.\" Permission to use, copy, modify, and distribute this software  
> for any
> X.\" purpose with or without fee is hereby granted, provided that  
> the above
> X.\" copyright notice and this permission notice appear in all copies.
> X.\"
> X.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL  
> WARRANTIES
> X.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> X.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE  
> LIABLE FOR
> X.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY  
> DAMAGES
> X.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,  
> WHETHER IN AN
> X.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,  
> ARISING OUT OF
> X.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> X.\"
> X.Dd November 21, 2004
> X.Dt LOADFIRMWARE 9
> X.Os
> X.Sh NAME
> X.Nm loadfirmware
> X.Nd load a firmware file from the filesystem
> X.Sh SYNOPSIS
> X.Fd #include <sys/device.h>
> X.Ft int
> X.Fn loadfirmware "const char *filename" "u_char **buf" "size_t  
> *buflen"
> X.Sh DESCRIPTION
> XThe
> X.Fn loadfirmware
> Xfunction loads a firmware from the file specified by
> X.Ar filename
> Xin the directory
> X.Pa /etc/firmware .
> XMemory for the firmware is allocated using
> X.Xr malloc 9
> Xwith type
> X.Va M_DEVBUF
> Xas need be, within a reasonable size limit.
> X.Pp
> XIf no longer needed, the firmware buffer
> X.Va buf
> Xcan be freed using
> X.Xr free 9
> Xwith type
> X.Va M_DEVBUF .
> X.Sh RETURN VALUES
> XIf successful,
> X.Ar buf
> Xis set to point to the allocation and
> X.Ar buflen
> Xis set to the size of the firmware.
> XThen
> X.Fn loadfirmware
> Xreturns 0.
> XOtherwise, it returns an
> X.Va errno
> Xstyle error.
> END-of-share/man/man9/load_firmware.9
> exit
>
> --- sys/dev/firmload.c	2005-08-01 09:15:02.000000000 +0100
> +++ sys/dev/firmload.c	2005-12-19 21:46:02.000000000 +0000
> @@ -1,3 +1,4 @@
> +/*	$NetBSD: firmload.c$	*/
>  /*	$OpenBSD: firmload.c,v 1.5 2005/08/01 08:15:02 deraadt Exp $	*/
>
>  /*
> @@ -29,11 +30,12 @@
>  #include <sys/device.h>
>
>  int
> -loadfirmware(const char *name, u_char **bufp, size_t *buflen)
> +load_firmware(const char *name, uint8_t **bufp, size_t *buflen)
>  {
> -	struct proc *p = curproc;
> +	struct lwp *l = curlwp;
>  	struct nameidata nid;
> -	char *path, *ptr;
> +	char *path;
> +	uint8_t *ptr;
>  	struct iovec iov;
>  	struct uio uio;
>  	struct vattr va;
> @@ -48,11 +50,11 @@
>  		
>  	snprintf(path, MAXPATHLEN, "/etc/firmware/%s", name);
>
> -	NDINIT(&nid, LOOKUP, NOFOLLOW|LOCKLEAF, UIO_SYSSPACE, path, p);
> +	NDINIT(&nid, LOOKUP, NOFOLLOW|LOCKLEAF, UIO_SYSSPACE, path, l);
>  	error = namei(&nid);
>  	if (error)
>  		goto err;
> -	error = VOP_GETATTR(nid.ni_vp, &va, p->p_ucred, p);
> +	error = VOP_GETATTR(nid.ni_vp, &va, l->l_proc->p_ucred, l);
>  	if (error)
>  		goto fail;
>  	if (va.va_size == 0) {
> @@ -77,7 +79,7 @@
>  	uio.uio_resid = va.va_size;
>  	uio.uio_segflg = UIO_SYSSPACE;
>  	uio.uio_rw = UIO_READ;
> -	uio.uio_procp = p;
> +	uio.uio_lwp = l;
>
>  	error = VOP_READ(nid.ni_vp, &uio, 0, NOCRED);
>
> --- share/man/man9/load_firmware.9	2004-11-22 16:41:44.000000000 +0000
> +++ share/man/man9/load_firmware.9	2005-12-19 21:56:48.000000000 +0000
> @@ -1,3 +1,4 @@
> +.\"	$NetBSD: load_firmware.9$
>  .\"	$OpenBSD: loadfirmware.9,v 1.2 2004/11/22 16:41:44 jmc Exp $
>  .\"
>  .\" Copyright (c) 2004 Theo de Raadt
> @@ -16,18 +17,18 @@
>  .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
>  .\"
>  .Dd November 21, 2004
> -.Dt LOADFIRMWARE 9
> +.Dt LOAD_FIRMWARE 9
>  .Os
>  .Sh NAME
> -.Nm loadfirmware
> +.Nm load_firmware
>  .Nd load a firmware file from the filesystem
>  .Sh SYNOPSIS
>  .Fd #include <sys/device.h>
>  .Ft int
> -.Fn loadfirmware "const char *filename" "u_char **buf" "size_t  
> *buflen"
> +.Fn load_firmware "const char *filename" "u_char **buf" "size_t  
> *buflen"
>  .Sh DESCRIPTION
>  The
> -.Fn loadfirmware
> +.Fn load_firmware
>  function loads a firmware from the file specified by
>  .Ar filename
>  in the directory
> @@ -36,9 +37,10 @@
>  .Xr malloc 9
>  with type
>  .Va M_DEVBUF
> -as need be, within a reasonable size limit.
> +as need be, within a reasonable size limit defined by
> +.Va FIRMWARE_MAX .
>  .Pp
> -If no longer needed, the firmware buffer
> +When no longer needed, the firmware buffer
>  .Va buf
>  can be freed using
>  .Xr free 9
> @@ -51,7 +53,7 @@
>  .Ar buflen
>  is set to the size of the firmware.
>  Then
> -.Fn loadfirmware
> +.Fn load_firmware
>  returns 0.
>  Otherwise, it returns an
>  .Va errno
>
> Index: sys/conf/files
> ===================================================================
> RCS file: /cvsroot/src/sys/conf/files,v
> retrieving revision 1.746
> diff -u -r1.746 files
> --- sys/conf/files	7 Dec 2005 00:42:03 -0000	1.746
> +++ sys/conf/files	19 Dec 2005 21:54:52 -0000
> @@ -234,6 +235,9 @@
>  define 	pckbport	{[slot = -1]}
>  define	pckbport_machdep_cnattach
>
> +# filesystem firmware loading attribute
> +define firmload
> +
>  # audio device attributes
>  #
>  define	mulaw
> @@ -1169,6 +1170,7 @@
>  file	dev/dkwedge/dkwedge_bsdlabel.c	dkwedge_method_bsdlabel
>  file	dev/dkwedge/dkwedge_gpt.c	dkwedge_method_gpt
>  file	dev/dkwedge/dkwedge_mbr.c	dkwedge_method_mbr
> +file	dev/firmload.c			firmload
>  file	dev/fss.c			fss			needs-count
>  file	dev/md.c			md			needs-count
>  file	dev/midi.c			midi | midibus		needs-flag
> Index: sys/sys/device.h
> ===================================================================
> RCS file: /cvsroot/src/sys/sys/device.h,v
> retrieving revision 1.81
> diff -u -r1.81 device.h
> --- sys/sys/device.h	11 Dec 2005 12:25:20 -0000	1.81
> +++ sys/sys/device.h	19 Dec 2005 21:54:54 -0000
> @@ -417,6 +417,9 @@
>  #define	device_lookup(cfd, unit)					\
>  	(((unit) < (cfd)->cd_ndevs) ? (cfd)->cd_devs[(unit)] : NULL)
>
> +int	load_firmware(const char *, uint8_t **, size_t *);
> +#define FIRMWARE_MAX	(5*1024*1024)
> +
>  #ifdef DDB
>  void event_print(int, void (*)(const char *, ...));
>  #endif
> Index: share/man/man9/Makefile
> ===================================================================
> RCS file: /cvsroot/src/share/man/man9/Makefile,v
> retrieving revision 1.183
> diff -u -r1.183 Makefile
> --- share/man/man9/Makefile	24 Nov 2005 08:20:51 -0000	1.183
> +++ share/man/man9/Makefile	19 Dec 2005 21:54:56 -0000
> @@ -21,8 +21,8 @@
>  	ieee80211_radiotap.9 \
>  	in4_cksum.9 inittodr.9 intro.9 ioasic.9 ioctl.9 ipkdb.9 isa.9 \
>  	isapnp.9 itimerfix.9 kcopy.9 kcont.9 \
> -	kfilter_register.9 knote.9 \
> -	kprintf.9 kthread.9 linedisc.9 lock.9 log.9 ltsleep.9 \
> +	kfilter_register.9 knote.9 kprintf.9 kthread.9 linedisc.9 \
> +	load_firmware.9 lock.9 log.9 ltsleep.9 \
>  	malloc.9 mbuf.9 mca.9 memcmp.9 memcpy.9 memmove.9 memset.9 \
>  	microtime.9 mstohz.9 m_tag.9 namecache.9 namei.9 need_resched.9 \
>  	opencrypto.9 \
>
>> Unformatted:
>  	
>  	

-- thorpej