1 /* $NetBSD: firmload.c,v 1.6 2006/11/16 01:32:45 christos Exp $ */
4 * Copyright (c) 2005, 2006 The NetBSD Foundation, Inc.
7 * This code is derived from software contributed to The NetBSD Foundation
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed by the NetBSD
21 * Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 * contributors may be used to endorse or promote products derived
24 * from this software without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
39 #include <sys/cdefs.h>
40 __KERNEL_RCSID(0, "$NetBSD: firmload.c,v 1.6 2006/11/16 01:32:45 christos Exp $");
43 * The firmload API provides an interface for device drivers to access
44 * firmware images that must be loaded onto their devices.
47 #include <sys/param.h>
48 #include <sys/fcntl.h>
49 #include <sys/filedesc.h>
50 #include <sys/malloc.h>
51 #include <sys/namei.h>
52 #include <sys/systm.h>
53 #include <sys/sysctl.h>
54 #include <sys/vnode.h>
55 #include <sys/kauth.h>
57 #include <dev/firmload.h>
59 static MALLOC_DEFINE(M_DEVFIRM
, "devfirm", "device firmware buffers");
61 struct firmware_handle
{
66 static firmware_handle_t
67 firmware_handle_alloc(void)
70 return (malloc(sizeof(struct firmware_handle
), M_DEVFIRM
, M_WAITOK
));
74 firmware_handle_free(firmware_handle_t fh
)
80 #if !defined(FIRMWARE_PATHS)
81 #define FIRMWARE_PATHS \
82 "/libdata/firmware:/usr/libdata/firmware:/usr/pkg/libdata/firmware:/usr/pkg/libdata"
85 static char firmware_paths
[PATH_MAX
+1] = FIRMWARE_PATHS
;
88 sysctl_hw_firmware_path(SYSCTLFN_ARGS
)
91 char newpath
[PATH_MAX
+1];
92 struct sysctlnode node
;
96 node
.sysctl_data
= &newpath
[0];
97 memcpy(node
.sysctl_data
, rnode
->sysctl_data
, PATH_MAX
+1);
98 error
= sysctl_lookup(SYSCTLFN_CALL(&node
));
99 if (error
|| newp
== NULL
)
103 * Make sure that all of the paths in the new path list are
106 * When sysctl_lookup() deals with a string, it's guaranteed
107 * to come back nul-terminated.
110 for (i
= 0; i
< PATH_MAX
+1; i
++) {
111 if (expected_char
!= 0 && newpath
[i
] != expected_char
)
113 if (newpath
[i
] == '\0')
115 else if (newpath
[i
] == ':')
121 memcpy(rnode
->sysctl_data
, node
.sysctl_data
, PATH_MAX
+1);
126 SYSCTL_SETUP_PROTO(sysctl_hw_firmware_setup
);
128 SYSCTL_SETUP(sysctl_hw_firmware_setup
, "sysctl hw.firmware subtree setup")
130 const struct sysctlnode
*firmware_node
;
132 if (sysctl_createv(clog
, 0, NULL
, NULL
,
134 CTLTYPE_NODE
, "hw", NULL
,
136 CTL_HW
, CTL_EOL
) != 0)
139 if (sysctl_createv(clog
, 0, NULL
, &firmware_node
,
141 CTLTYPE_NODE
, "firmware", NULL
,
143 CTL_HW
, CTL_CREATE
, CTL_EOL
) != 0)
146 sysctl_createv(clog
, 0, NULL
, NULL
,
148 CTLTYPE_STRING
, "path",
149 SYSCTL_DESCR("Device firmware loading path list"),
150 sysctl_hw_firmware_path
, 0, firmware_paths
, PATH_MAX
+1,
151 CTL_HW
, firmware_node
->sysctl_num
, CTL_CREATE
, CTL_EOL
);
155 firmware_path_next(const char *drvname
, const char *imgname
, char *pnbuf
,
158 char *prefix
= *prefixp
;
161 if (prefix
== NULL
/* terminated early */
162 || *prefix
== '\0' /* no more left */
163 || *prefix
!= '/') { /* not absolute */
169 * Compute the max path prefix based on the length of the provided
172 maxprefix
= MAXPATHLEN
-
177 + 1 /* terminating NUL */);
179 /* Check for underflow (size_t is unsigned). */
180 if (maxprefix
> MAXPATHLEN
) {
185 for (i
= 0; i
< maxprefix
; i
++) {
186 if (*prefix
== ':' || *prefix
== '\0')
188 pnbuf
[i
] = *prefix
++;
191 if (*prefix
!= ':' && *prefix
!= '\0') {
192 /* Path prefix was too long. */
202 * This sprintf() is safe because of the maxprefix calculation
205 sprintf(&pnbuf
[i
], "/%s/%s", drvname
, imgname
);
211 firmware_path_first(const char *drvname
, const char *imgname
, char *pnbuf
,
215 *prefixp
= firmware_paths
;
216 return (firmware_path_next(drvname
, imgname
, pnbuf
, prefixp
));
222 * Open a firmware image and return its handle.
225 firmware_open(const char *drvname
, const char *imgname
, firmware_handle_t
*fhp
)
229 char *pnbuf
, *path
, *prefix
;
230 firmware_handle_t fh
;
233 extern struct cwdinfo cwdi0
;
235 if (drvname
== NULL
|| imgname
== NULL
)
238 if (cwdi0
.cwdi_cdir
== NULL
) {
239 printf("firmware_open(%s/%s) called too early.\n",
245 KASSERT(pnbuf
!= NULL
);
247 fh
= firmware_handle_alloc();
251 for (path
= firmware_path_first(drvname
, imgname
, pnbuf
, &prefix
);
253 path
= firmware_path_next(drvname
, imgname
, pnbuf
, &prefix
)) {
254 NDINIT(&nd
, LOOKUP
, FOLLOW
, UIO_SYSSPACE
, path
, curlwp
);
255 error
= vn_open(&nd
, FREAD
, 0);
263 firmware_handle_free(fh
);
269 error
= VOP_GETATTR(vp
, &va
, kauth_cred_get(), curlwp
);
272 (void)vn_close(vp
, FREAD
, kauth_cred_get(), curlwp
);
273 firmware_handle_free(fh
);
277 if (va
.va_type
!= VREG
) {
279 (void)vn_close(vp
, FREAD
, kauth_cred_get(), curlwp
);
280 firmware_handle_free(fh
);
284 /* XXX Mark as busy text file. */
287 fh
->fh_size
= va
.va_size
;
298 * Close a firmware image.
301 firmware_close(firmware_handle_t fh
)
305 error
= vn_close(fh
->fh_vp
, FREAD
, kauth_cred_get(), curlwp
);
306 firmware_handle_free(fh
);
313 * Return the total size of a firmware image.
316 firmware_get_size(firmware_handle_t fh
)
319 return (fh
->fh_size
);
325 * Read data from a firmware image at the specified offset into
326 * the provided buffer.
329 firmware_read(firmware_handle_t fh
, off_t offset
, void *buf
, size_t len
)
332 return (vn_rdwr(UIO_READ
, fh
->fh_vp
, buf
, len
, offset
,
333 UIO_SYSSPACE
, 0, kauth_cred_get(), NULL
, curlwp
));
339 * Allocate a firmware buffer of the specified size.
341 * NOTE: This routine may block.
344 firmware_malloc(size_t size
)
347 return (malloc(size
, M_DEVFIRM
, M_WAITOK
));
353 * Free a previously allocated firmware buffer.
357 firmware_free(void *v
, size_t size
)