1 /* $NetBSD: firmload.c,v 1.10 2008/03/21 21:54:59 ad 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.
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
32 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: firmload.c,v 1.10 2008/03/21 21:54:59 ad Exp $");
36 * The firmload API provides an interface for device drivers to access
37 * firmware images that must be loaded onto their devices.
40 #include <sys/param.h>
41 #include <sys/fcntl.h>
42 #include <sys/filedesc.h>
43 #include <sys/malloc.h>
44 #include <sys/namei.h>
45 #include <sys/systm.h>
46 #include <sys/sysctl.h>
47 #include <sys/vnode.h>
48 #include <sys/kauth.h>
50 #include <dev/firmload.h>
52 static MALLOC_DEFINE(M_DEVFIRM
, "devfirm", "device firmware buffers");
54 struct firmware_handle
{
59 static firmware_handle_t
60 firmware_handle_alloc(void)
63 return (malloc(sizeof(struct firmware_handle
), M_DEVFIRM
, M_WAITOK
));
67 firmware_handle_free(firmware_handle_t fh
)
73 #if !defined(FIRMWARE_PATHS)
74 #define FIRMWARE_PATHS \
75 "/libdata/firmware:/usr/libdata/firmware:/usr/pkg/libdata/firmware:/usr/pkg/libdata"
78 static char firmware_paths
[PATH_MAX
+1] = FIRMWARE_PATHS
;
81 sysctl_hw_firmware_path(SYSCTLFN_ARGS
)
84 char newpath
[PATH_MAX
+1];
85 struct sysctlnode node
;
89 node
.sysctl_data
= &newpath
[0];
90 memcpy(node
.sysctl_data
, rnode
->sysctl_data
, PATH_MAX
+1);
91 error
= sysctl_lookup(SYSCTLFN_CALL(&node
));
92 if (error
|| newp
== NULL
)
96 * Make sure that all of the paths in the new path list are
99 * When sysctl_lookup() deals with a string, it's guaranteed
100 * to come back nul-terminated.
103 for (i
= 0; i
< PATH_MAX
+1; i
++) {
104 if (expected_char
!= 0 && newpath
[i
] != expected_char
)
106 if (newpath
[i
] == '\0')
108 else if (newpath
[i
] == ':')
114 memcpy(rnode
->sysctl_data
, node
.sysctl_data
, PATH_MAX
+1);
119 SYSCTL_SETUP_PROTO(sysctl_hw_firmware_setup
);
121 SYSCTL_SETUP(sysctl_hw_firmware_setup
, "sysctl hw.firmware subtree setup")
123 const struct sysctlnode
*firmware_node
;
125 if (sysctl_createv(clog
, 0, NULL
, NULL
,
127 CTLTYPE_NODE
, "hw", NULL
,
129 CTL_HW
, CTL_EOL
) != 0)
132 if (sysctl_createv(clog
, 0, NULL
, &firmware_node
,
134 CTLTYPE_NODE
, "firmware", NULL
,
136 CTL_HW
, CTL_CREATE
, CTL_EOL
) != 0)
139 sysctl_createv(clog
, 0, NULL
, NULL
,
141 CTLTYPE_STRING
, "path",
142 SYSCTL_DESCR("Device firmware loading path list"),
143 sysctl_hw_firmware_path
, 0, firmware_paths
, PATH_MAX
+1,
144 CTL_HW
, firmware_node
->sysctl_num
, CTL_CREATE
, CTL_EOL
);
148 firmware_path_next(const char *drvname
, const char *imgname
, char *pnbuf
,
151 char *prefix
= *prefixp
;
154 if (prefix
== NULL
/* terminated early */
155 || *prefix
== '\0' /* no more left */
156 || *prefix
!= '/') { /* not absolute */
162 * Compute the max path prefix based on the length of the provided
165 maxprefix
= MAXPATHLEN
-
170 + 1 /* terminating NUL */);
172 /* Check for underflow (size_t is unsigned). */
173 if (maxprefix
> MAXPATHLEN
) {
178 for (i
= 0; i
< maxprefix
; i
++) {
179 if (*prefix
== ':' || *prefix
== '\0')
181 pnbuf
[i
] = *prefix
++;
184 if (*prefix
!= ':' && *prefix
!= '\0') {
185 /* Path prefix was too long. */
195 * This sprintf() is safe because of the maxprefix calculation
198 sprintf(&pnbuf
[i
], "/%s/%s", drvname
, imgname
);
204 firmware_path_first(const char *drvname
, const char *imgname
, char *pnbuf
,
208 *prefixp
= firmware_paths
;
209 return (firmware_path_next(drvname
, imgname
, pnbuf
, prefixp
));
215 * Open a firmware image and return its handle.
218 firmware_open(const char *drvname
, const char *imgname
, firmware_handle_t
*fhp
)
222 char *pnbuf
, *path
, *prefix
;
223 firmware_handle_t fh
;
226 extern struct cwdinfo cwdi0
;
228 if (drvname
== NULL
|| imgname
== NULL
)
231 if (cwdi0
.cwdi_cdir
== NULL
) {
232 printf("firmware_open(%s/%s) called too early.\n",
238 KASSERT(pnbuf
!= NULL
);
240 fh
= firmware_handle_alloc();
244 for (path
= firmware_path_first(drvname
, imgname
, pnbuf
, &prefix
);
246 path
= firmware_path_next(drvname
, imgname
, pnbuf
, &prefix
)) {
247 NDINIT(&nd
, LOOKUP
, FOLLOW
, UIO_SYSSPACE
, path
);
248 error
= vn_open(&nd
, FREAD
, 0);
256 firmware_handle_free(fh
);
262 error
= VOP_GETATTR(vp
, &va
, kauth_cred_get());
265 (void)vn_close(vp
, FREAD
, kauth_cred_get());
266 firmware_handle_free(fh
);
270 if (va
.va_type
!= VREG
) {
272 (void)vn_close(vp
, FREAD
, kauth_cred_get());
273 firmware_handle_free(fh
);
277 /* XXX Mark as busy text file. */
280 fh
->fh_size
= va
.va_size
;
291 * Close a firmware image.
294 firmware_close(firmware_handle_t fh
)
298 error
= vn_close(fh
->fh_vp
, FREAD
, kauth_cred_get());
299 firmware_handle_free(fh
);
306 * Return the total size of a firmware image.
309 firmware_get_size(firmware_handle_t fh
)
312 return (fh
->fh_size
);
318 * Read data from a firmware image at the specified offset into
319 * the provided buffer.
322 firmware_read(firmware_handle_t fh
, off_t offset
, void *buf
, size_t len
)
325 return (vn_rdwr(UIO_READ
, fh
->fh_vp
, buf
, len
, offset
,
326 UIO_SYSSPACE
, 0, kauth_cred_get(), NULL
, curlwp
));
332 * Allocate a firmware buffer of the specified size.
334 * NOTE: This routine may block.
337 firmware_malloc(size_t size
)
340 return (malloc(size
, M_DEVFIRM
, M_WAITOK
));
346 * Free a previously allocated firmware buffer.
350 firmware_free(void *v
, size_t size
)