f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld/* $NetBSD: firmload.c,v 1.19 2014/03/25 16:19:13 christos Exp $ */
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld/*
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * Copyright 2016 Hans Rosenfeld <rosenfeld@grumpf.hope-2000.org>
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld */
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld/*
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * Copyright (c) 2005, 2006 The NetBSD Foundation, Inc.
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * All rights reserved.
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld *
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * This code is derived from software contributed to The NetBSD Foundation
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * by Jason R. Thorpe.
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld *
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * Redistribution and use in source and binary forms, with or without
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * modification, are permitted provided that the following conditions
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * are met:
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * 1. Redistributions of source code must retain the above copyright
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * notice, this list of conditions and the following disclaimer.
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * 2. Redistributions in binary form must reproduce the above copyright
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * notice, this list of conditions and the following disclaimer in the
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * documentation and/or other materials provided with the distribution.
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld *
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * POSSIBILITY OF SUCH DAMAGE.
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld */
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld/*
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * The firmload API provides an interface for device drivers to access
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * firmware images that must be loaded onto their devices.
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld */
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld#include <sys/param.h>
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld#include <sys/fcntl.h>
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld#include <sys/systm.h>
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld#include <sys/vnode.h>
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld#include <sys/lwp.h>
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld#include <sys/file.h>
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld#include <sys/cmn_err.h>
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld#include <sys/modctl.h>
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld#include <sys/kobj.h>
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld#include <sys/kobj_impl.h>
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld#include <sys/firmload.h>
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeldstruct firmware_handle {
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld struct _buf *fh_buf;
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld off_t fh_size;
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld};
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeldstatic firmware_handle_t
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeldfirmware_handle_alloc(void)
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld{
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld return (kmem_alloc(sizeof (struct firmware_handle), KM_SLEEP));
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld}
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeldstatic void
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeldfirmware_handle_free(firmware_handle_t fh)
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld{
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld kmem_free(fh, sizeof (struct firmware_handle));
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld}
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld/*
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * firmware_open:
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld *
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * Open a firmware image and return its handle.
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld */
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeldint
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeldfirmware_open(const char *drvname, const char *imgname, firmware_handle_t *fhp)
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld{
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld char *path;
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld firmware_handle_t fh;
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld int error;
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld if (drvname == NULL || imgname == NULL || fhp == NULL)
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld return (EINVAL);
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld path = kmem_asprintf("firmware/%s/%s", drvname, imgname);
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld fh = firmware_handle_alloc();
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld fh->fh_buf = kobj_open_path(path, 1, 0);
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld strfree(path);
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld if (fh->fh_buf == (struct _buf *)-1) {
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld firmware_handle_free(fh);
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld return (ENOENT);
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld }
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld error = kobj_get_filesize(fh->fh_buf, (uint64_t *)&fh->fh_size);
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld if (error != 0) {
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld kobj_close_file(fh->fh_buf);
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld firmware_handle_free(fh);
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld return (error);
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld }
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld *fhp = fh;
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld return (0);
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld}
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld/*
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * firmware_close:
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld *
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * Close a firmware image.
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld */
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeldint
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeldfirmware_close(firmware_handle_t fh)
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld{
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld if (fh != NULL) {
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld kobj_close_file(fh->fh_buf);
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld firmware_handle_free(fh);
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld }
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld return (0);
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld}
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld/*
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * firmware_get_size:
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld *
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * Return the total size of a firmware image.
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld */
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeldoff_t
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeldfirmware_get_size(firmware_handle_t fh)
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld{
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld ASSERT(fh != NULL);
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld return (fh->fh_size);
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld}
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld/*
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * firmware_read:
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld *
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * Read data from a firmware image at the specified offset into
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld * the provided buffer.
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld */
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeldint
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeldfirmware_read(firmware_handle_t fh, off_t offset, void *buf, size_t len)
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld{
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld ASSERT(fh != NULL);
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld if (kobj_read_file(fh->fh_buf, buf, len, offset) == -1)
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld return (-1);
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld return (0);
f73e0305a745f17c6a584c4470f99ea1e023657fHans Rosenfeld}