Subject: kern/32342: OpenBSD firmware loading framework
To: None <kern-bug-people@netbsd.org, gnats-admin@netbsd.org,>
From: None <plunky@rya-online.net>
List: netbsd-bugs
Date: 12/19/2005 22:10:00
>Number:         32342
>Category:       kern
>Synopsis:       port of OpenBSD firmware loading framework
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Mon Dec 19 22:10:00 +0000 2005
>Originator:     Iain Hibbert
>Release:        NetBSD 3.99.14
>Organization:
>Environment:
	
	
System: NetBSD galant 3.99.14 NetBSD 3.99.14 (GALANT) #8: Mon Dec 19 21:34:33 GMT 2005 plunky@galant:/home/plunky/src/sys/arch/i386/compile/GALANT i386
Architecture: i386
Machine: i386
>Description:
	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

	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: