9c0e3d3a53
Some crypto consumers such as GELI and KTLS for file-backed sendfile need to store their output in a separate buffer from the input. Currently these consumers copy the contents of the input buffer into the output buffer and queue an in-place crypto operation on the output buffer. Using a separate output buffer avoids this copy. - Create a new 'struct crypto_buffer' describing a crypto buffer containing a type and type-specific fields. crp_ilen is gone, instead buffers that use a flat kernel buffer have a cb_buf_len field for their length. The length of other buffer types is inferred from the backing store (e.g. uio_resid for a uio). Requests now have two such structures: crp_buf for the input buffer, and crp_obuf for the output buffer. - Consumers now use helper functions (crypto_use_*, e.g. crypto_use_mbuf()) to configure the input buffer. If an output buffer is not configured, the request still modifies the input buffer in-place. A consumer uses a second set of helper functions (crypto_use_output_*) to configure an output buffer. - Consumers must request support for separate output buffers when creating a crypto session via the CSP_F_SEPARATE_OUTPUT flag and are only permitted to queue a request with a separate output buffer on sessions with this flag set. Existing drivers already reject sessions with unknown flags, so this permits drivers to be modified to support this extension without requiring all drivers to change. - Several data-related functions now have matching versions that operate on an explicit buffer (e.g. crypto_apply_buf, crypto_contiguous_subsegment_buf, bus_dma_load_crp_buf). - Most of the existing data-related functions operate on the input buffer. However crypto_copyback always writes to the output buffer if a request uses a separate output buffer. - For the regions in input/output buffers, the following conventions are followed: - AAD and IV are always present in input only and their fields are offsets into the input buffer. - payload is always present in both buffers. If a request uses a separate output buffer, it must set a new crp_payload_start_output field to the offset of the payload in the output buffer. - digest is in the input buffer for verify operations, and in the output buffer for compute operations. crp_digest_start is relative to the appropriate buffer. - Add a crypto buffer cursor abstraction. This is a more general form of some bits in the cryptosoft driver that tried to always use uio's. However, compared to the original code, this avoids rewalking the uio iovec array for requests with multiple vectors. It also avoids allocate an iovec array for mbufs and populating it by instead walking the mbuf chain directly. - Update the cryptosoft(4) driver to support separate output buffers making use of the cursor abstraction. Sponsored by: Netflix Differential Revision: https://reviews.freebsd.org/D24545
2744 lines
72 KiB
C
2744 lines
72 KiB
C
/* $OpenBSD: hifn7751.c,v 1.120 2002/05/17 00:33:34 deraadt Exp $ */
|
|
|
|
/*-
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*
|
|
* Invertex AEON / Hifn 7751 driver
|
|
* Copyright (c) 1999 Invertex Inc. All rights reserved.
|
|
* Copyright (c) 1999 Theo de Raadt
|
|
* Copyright (c) 2000-2001 Network Security Technologies, Inc.
|
|
* http://www.netsec.net
|
|
* Copyright (c) 2003 Hifn Inc.
|
|
*
|
|
* This driver is based on a previous driver by Invertex, for which they
|
|
* requested: Please send any comments, feedback, bug-fixes, or feature
|
|
* requests to software@invertex.com.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. The name of the author may not be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* Effort sponsored in part by the Defense Advanced Research Projects
|
|
* Agency (DARPA) and Air Force Research Laboratory, Air Force
|
|
* Materiel Command, USAF, under agreement number F30602-01-2-0537.
|
|
*/
|
|
|
|
#include <sys/cdefs.h>
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
/*
|
|
* Driver for various Hifn encryption processors.
|
|
*/
|
|
#include "opt_hifn.h"
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/systm.h>
|
|
#include <sys/proc.h>
|
|
#include <sys/errno.h>
|
|
#include <sys/malloc.h>
|
|
#include <sys/kernel.h>
|
|
#include <sys/module.h>
|
|
#include <sys/mbuf.h>
|
|
#include <sys/lock.h>
|
|
#include <sys/mutex.h>
|
|
#include <sys/sysctl.h>
|
|
#include <sys/uio.h>
|
|
|
|
#include <vm/vm.h>
|
|
#include <vm/pmap.h>
|
|
|
|
#include <machine/bus.h>
|
|
#include <machine/resource.h>
|
|
#include <sys/bus.h>
|
|
#include <sys/rman.h>
|
|
|
|
#include <opencrypto/cryptodev.h>
|
|
#include <opencrypto/xform_auth.h>
|
|
#include <sys/random.h>
|
|
#include <sys/kobj.h>
|
|
|
|
#include "cryptodev_if.h"
|
|
|
|
#include <dev/pci/pcivar.h>
|
|
#include <dev/pci/pcireg.h>
|
|
|
|
#ifdef HIFN_RNDTEST
|
|
#include <dev/rndtest/rndtest.h>
|
|
#endif
|
|
#include <dev/hifn/hifn7751reg.h>
|
|
#include <dev/hifn/hifn7751var.h>
|
|
|
|
#ifdef HIFN_VULCANDEV
|
|
#include <sys/conf.h>
|
|
#include <sys/uio.h>
|
|
|
|
static struct cdevsw vulcanpk_cdevsw; /* forward declaration */
|
|
#endif
|
|
|
|
/*
|
|
* Prototypes and count for the pci_device structure
|
|
*/
|
|
static int hifn_probe(device_t);
|
|
static int hifn_attach(device_t);
|
|
static int hifn_detach(device_t);
|
|
static int hifn_suspend(device_t);
|
|
static int hifn_resume(device_t);
|
|
static int hifn_shutdown(device_t);
|
|
|
|
static int hifn_probesession(device_t, const struct crypto_session_params *);
|
|
static int hifn_newsession(device_t, crypto_session_t,
|
|
const struct crypto_session_params *);
|
|
static int hifn_process(device_t, struct cryptop *, int);
|
|
|
|
static device_method_t hifn_methods[] = {
|
|
/* Device interface */
|
|
DEVMETHOD(device_probe, hifn_probe),
|
|
DEVMETHOD(device_attach, hifn_attach),
|
|
DEVMETHOD(device_detach, hifn_detach),
|
|
DEVMETHOD(device_suspend, hifn_suspend),
|
|
DEVMETHOD(device_resume, hifn_resume),
|
|
DEVMETHOD(device_shutdown, hifn_shutdown),
|
|
|
|
/* crypto device methods */
|
|
DEVMETHOD(cryptodev_probesession, hifn_probesession),
|
|
DEVMETHOD(cryptodev_newsession, hifn_newsession),
|
|
DEVMETHOD(cryptodev_process, hifn_process),
|
|
|
|
DEVMETHOD_END
|
|
};
|
|
static driver_t hifn_driver = {
|
|
"hifn",
|
|
hifn_methods,
|
|
sizeof (struct hifn_softc)
|
|
};
|
|
static devclass_t hifn_devclass;
|
|
|
|
DRIVER_MODULE(hifn, pci, hifn_driver, hifn_devclass, 0, 0);
|
|
MODULE_DEPEND(hifn, crypto, 1, 1, 1);
|
|
#ifdef HIFN_RNDTEST
|
|
MODULE_DEPEND(hifn, rndtest, 1, 1, 1);
|
|
#endif
|
|
|
|
static void hifn_reset_board(struct hifn_softc *, int);
|
|
static void hifn_reset_puc(struct hifn_softc *);
|
|
static void hifn_puc_wait(struct hifn_softc *);
|
|
static int hifn_enable_crypto(struct hifn_softc *);
|
|
static void hifn_set_retry(struct hifn_softc *sc);
|
|
static void hifn_init_dma(struct hifn_softc *);
|
|
static void hifn_init_pci_registers(struct hifn_softc *);
|
|
static int hifn_sramsize(struct hifn_softc *);
|
|
static int hifn_dramsize(struct hifn_softc *);
|
|
static int hifn_ramtype(struct hifn_softc *);
|
|
static void hifn_sessions(struct hifn_softc *);
|
|
static void hifn_intr(void *);
|
|
static u_int hifn_write_command(struct hifn_command *, u_int8_t *);
|
|
static u_int32_t hifn_next_signature(u_int32_t a, u_int cnt);
|
|
static void hifn_callback(struct hifn_softc *, struct hifn_command *, u_int8_t *);
|
|
static int hifn_crypto(struct hifn_softc *, struct hifn_command *, struct cryptop *, int);
|
|
static int hifn_readramaddr(struct hifn_softc *, int, u_int8_t *);
|
|
static int hifn_writeramaddr(struct hifn_softc *, int, u_int8_t *);
|
|
static int hifn_dmamap_load_src(struct hifn_softc *, struct hifn_command *);
|
|
static int hifn_dmamap_load_dst(struct hifn_softc *, struct hifn_command *);
|
|
static int hifn_init_pubrng(struct hifn_softc *);
|
|
static void hifn_rng(void *);
|
|
static void hifn_tick(void *);
|
|
static void hifn_abort(struct hifn_softc *);
|
|
static void hifn_alloc_slot(struct hifn_softc *, int *, int *, int *, int *);
|
|
|
|
static void hifn_write_reg_0(struct hifn_softc *, bus_size_t, u_int32_t);
|
|
static void hifn_write_reg_1(struct hifn_softc *, bus_size_t, u_int32_t);
|
|
|
|
static __inline u_int32_t
|
|
READ_REG_0(struct hifn_softc *sc, bus_size_t reg)
|
|
{
|
|
u_int32_t v = bus_space_read_4(sc->sc_st0, sc->sc_sh0, reg);
|
|
sc->sc_bar0_lastreg = (bus_size_t) -1;
|
|
return (v);
|
|
}
|
|
#define WRITE_REG_0(sc, reg, val) hifn_write_reg_0(sc, reg, val)
|
|
|
|
static __inline u_int32_t
|
|
READ_REG_1(struct hifn_softc *sc, bus_size_t reg)
|
|
{
|
|
u_int32_t v = bus_space_read_4(sc->sc_st1, sc->sc_sh1, reg);
|
|
sc->sc_bar1_lastreg = (bus_size_t) -1;
|
|
return (v);
|
|
}
|
|
#define WRITE_REG_1(sc, reg, val) hifn_write_reg_1(sc, reg, val)
|
|
|
|
static SYSCTL_NODE(_hw, OID_AUTO, hifn, CTLFLAG_RD | CTLFLAG_MPSAFE, 0,
|
|
"Hifn driver parameters");
|
|
|
|
#ifdef HIFN_DEBUG
|
|
static int hifn_debug = 0;
|
|
SYSCTL_INT(_hw_hifn, OID_AUTO, debug, CTLFLAG_RW, &hifn_debug,
|
|
0, "control debugging msgs");
|
|
#endif
|
|
|
|
static struct hifn_stats hifnstats;
|
|
SYSCTL_STRUCT(_hw_hifn, OID_AUTO, stats, CTLFLAG_RD, &hifnstats,
|
|
hifn_stats, "driver statistics");
|
|
static int hifn_maxbatch = 1;
|
|
SYSCTL_INT(_hw_hifn, OID_AUTO, maxbatch, CTLFLAG_RW, &hifn_maxbatch,
|
|
0, "max ops to batch w/o interrupt");
|
|
|
|
/*
|
|
* Probe for a supported device. The PCI vendor and device
|
|
* IDs are used to detect devices we know how to handle.
|
|
*/
|
|
static int
|
|
hifn_probe(device_t dev)
|
|
{
|
|
if (pci_get_vendor(dev) == PCI_VENDOR_INVERTEX &&
|
|
pci_get_device(dev) == PCI_PRODUCT_INVERTEX_AEON)
|
|
return (BUS_PROBE_DEFAULT);
|
|
if (pci_get_vendor(dev) == PCI_VENDOR_HIFN &&
|
|
(pci_get_device(dev) == PCI_PRODUCT_HIFN_7751 ||
|
|
pci_get_device(dev) == PCI_PRODUCT_HIFN_7951 ||
|
|
pci_get_device(dev) == PCI_PRODUCT_HIFN_7955 ||
|
|
pci_get_device(dev) == PCI_PRODUCT_HIFN_7956 ||
|
|
pci_get_device(dev) == PCI_PRODUCT_HIFN_7811))
|
|
return (BUS_PROBE_DEFAULT);
|
|
if (pci_get_vendor(dev) == PCI_VENDOR_NETSEC &&
|
|
pci_get_device(dev) == PCI_PRODUCT_NETSEC_7751)
|
|
return (BUS_PROBE_DEFAULT);
|
|
return (ENXIO);
|
|
}
|
|
|
|
static void
|
|
hifn_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error)
|
|
{
|
|
bus_addr_t *paddr = (bus_addr_t*) arg;
|
|
*paddr = segs->ds_addr;
|
|
}
|
|
|
|
static const char*
|
|
hifn_partname(struct hifn_softc *sc)
|
|
{
|
|
/* XXX sprintf numbers when not decoded */
|
|
switch (pci_get_vendor(sc->sc_dev)) {
|
|
case PCI_VENDOR_HIFN:
|
|
switch (pci_get_device(sc->sc_dev)) {
|
|
case PCI_PRODUCT_HIFN_6500: return "Hifn 6500";
|
|
case PCI_PRODUCT_HIFN_7751: return "Hifn 7751";
|
|
case PCI_PRODUCT_HIFN_7811: return "Hifn 7811";
|
|
case PCI_PRODUCT_HIFN_7951: return "Hifn 7951";
|
|
case PCI_PRODUCT_HIFN_7955: return "Hifn 7955";
|
|
case PCI_PRODUCT_HIFN_7956: return "Hifn 7956";
|
|
}
|
|
return "Hifn unknown-part";
|
|
case PCI_VENDOR_INVERTEX:
|
|
switch (pci_get_device(sc->sc_dev)) {
|
|
case PCI_PRODUCT_INVERTEX_AEON: return "Invertex AEON";
|
|
}
|
|
return "Invertex unknown-part";
|
|
case PCI_VENDOR_NETSEC:
|
|
switch (pci_get_device(sc->sc_dev)) {
|
|
case PCI_PRODUCT_NETSEC_7751: return "NetSec 7751";
|
|
}
|
|
return "NetSec unknown-part";
|
|
}
|
|
return "Unknown-vendor unknown-part";
|
|
}
|
|
|
|
static void
|
|
default_harvest(struct rndtest_state *rsp, void *buf, u_int count)
|
|
{
|
|
/* MarkM: FIX!! Check that this does not swamp the harvester! */
|
|
random_harvest_queue(buf, count, RANDOM_PURE_HIFN);
|
|
}
|
|
|
|
static u_int
|
|
checkmaxmin(device_t dev, const char *what, u_int v, u_int min, u_int max)
|
|
{
|
|
if (v > max) {
|
|
device_printf(dev, "Warning, %s %u out of range, "
|
|
"using max %u\n", what, v, max);
|
|
v = max;
|
|
} else if (v < min) {
|
|
device_printf(dev, "Warning, %s %u out of range, "
|
|
"using min %u\n", what, v, min);
|
|
v = min;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
/*
|
|
* Select PLL configuration for 795x parts. This is complicated in
|
|
* that we cannot determine the optimal parameters without user input.
|
|
* The reference clock is derived from an external clock through a
|
|
* multiplier. The external clock is either the host bus (i.e. PCI)
|
|
* or an external clock generator. When using the PCI bus we assume
|
|
* the clock is either 33 or 66 MHz; for an external source we cannot
|
|
* tell the speed.
|
|
*
|
|
* PLL configuration is done with a string: "pci" for PCI bus, or "ext"
|
|
* for an external source, followed by the frequency. We calculate
|
|
* the appropriate multiplier and PLL register contents accordingly.
|
|
* When no configuration is given we default to "pci66" since that
|
|
* always will allow the card to work. If a card is using the PCI
|
|
* bus clock and in a 33MHz slot then it will be operating at half
|
|
* speed until the correct information is provided.
|
|
*
|
|
* We use a default setting of "ext66" because according to Mike Ham
|
|
* of HiFn, almost every board in existence has an external crystal
|
|
* populated at 66Mhz. Using PCI can be a problem on modern motherboards,
|
|
* because PCI33 can have clocks from 0 to 33Mhz, and some have
|
|
* non-PCI-compliant spread-spectrum clocks, which can confuse the pll.
|
|
*/
|
|
static void
|
|
hifn_getpllconfig(device_t dev, u_int *pll)
|
|
{
|
|
const char *pllspec;
|
|
u_int freq, mul, fl, fh;
|
|
u_int32_t pllconfig;
|
|
char *nxt;
|
|
|
|
if (resource_string_value("hifn", device_get_unit(dev),
|
|
"pllconfig", &pllspec))
|
|
pllspec = "ext66";
|
|
fl = 33, fh = 66;
|
|
pllconfig = 0;
|
|
if (strncmp(pllspec, "ext", 3) == 0) {
|
|
pllspec += 3;
|
|
pllconfig |= HIFN_PLL_REF_SEL;
|
|
switch (pci_get_device(dev)) {
|
|
case PCI_PRODUCT_HIFN_7955:
|
|
case PCI_PRODUCT_HIFN_7956:
|
|
fl = 20, fh = 100;
|
|
break;
|
|
#ifdef notyet
|
|
case PCI_PRODUCT_HIFN_7954:
|
|
fl = 20, fh = 66;
|
|
break;
|
|
#endif
|
|
}
|
|
} else if (strncmp(pllspec, "pci", 3) == 0)
|
|
pllspec += 3;
|
|
freq = strtoul(pllspec, &nxt, 10);
|
|
if (nxt == pllspec)
|
|
freq = 66;
|
|
else
|
|
freq = checkmaxmin(dev, "frequency", freq, fl, fh);
|
|
/*
|
|
* Calculate multiplier. We target a Fck of 266 MHz,
|
|
* allowing only even values, possibly rounded down.
|
|
* Multipliers > 8 must set the charge pump current.
|
|
*/
|
|
mul = checkmaxmin(dev, "PLL divisor", (266 / freq) &~ 1, 2, 12);
|
|
pllconfig |= (mul / 2 - 1) << HIFN_PLL_ND_SHIFT;
|
|
if (mul > 8)
|
|
pllconfig |= HIFN_PLL_IS;
|
|
*pll = pllconfig;
|
|
}
|
|
|
|
/*
|
|
* Attach an interface that successfully probed.
|
|
*/
|
|
static int
|
|
hifn_attach(device_t dev)
|
|
{
|
|
struct hifn_softc *sc = device_get_softc(dev);
|
|
caddr_t kva;
|
|
int rseg, rid;
|
|
char rbase;
|
|
uint16_t rev;
|
|
|
|
sc->sc_dev = dev;
|
|
|
|
mtx_init(&sc->sc_mtx, device_get_nameunit(dev), "hifn driver", MTX_DEF);
|
|
|
|
/* XXX handle power management */
|
|
|
|
/*
|
|
* The 7951 and 795x have a random number generator and
|
|
* public key support; note this.
|
|
*/
|
|
if (pci_get_vendor(dev) == PCI_VENDOR_HIFN &&
|
|
(pci_get_device(dev) == PCI_PRODUCT_HIFN_7951 ||
|
|
pci_get_device(dev) == PCI_PRODUCT_HIFN_7955 ||
|
|
pci_get_device(dev) == PCI_PRODUCT_HIFN_7956))
|
|
sc->sc_flags = HIFN_HAS_RNG | HIFN_HAS_PUBLIC;
|
|
/*
|
|
* The 7811 has a random number generator and
|
|
* we also note it's identity 'cuz of some quirks.
|
|
*/
|
|
if (pci_get_vendor(dev) == PCI_VENDOR_HIFN &&
|
|
pci_get_device(dev) == PCI_PRODUCT_HIFN_7811)
|
|
sc->sc_flags |= HIFN_IS_7811 | HIFN_HAS_RNG;
|
|
|
|
/*
|
|
* The 795x parts support AES.
|
|
*/
|
|
if (pci_get_vendor(dev) == PCI_VENDOR_HIFN &&
|
|
(pci_get_device(dev) == PCI_PRODUCT_HIFN_7955 ||
|
|
pci_get_device(dev) == PCI_PRODUCT_HIFN_7956)) {
|
|
sc->sc_flags |= HIFN_IS_7956 | HIFN_HAS_AES;
|
|
/*
|
|
* Select PLL configuration. This depends on the
|
|
* bus and board design and must be manually configured
|
|
* if the default setting is unacceptable.
|
|
*/
|
|
hifn_getpllconfig(dev, &sc->sc_pllconfig);
|
|
}
|
|
|
|
/*
|
|
* Setup PCI resources. Note that we record the bus
|
|
* tag and handle for each register mapping, this is
|
|
* used by the READ_REG_0, WRITE_REG_0, READ_REG_1,
|
|
* and WRITE_REG_1 macros throughout the driver.
|
|
*/
|
|
pci_enable_busmaster(dev);
|
|
|
|
rid = HIFN_BAR0;
|
|
sc->sc_bar0res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
|
|
RF_ACTIVE);
|
|
if (sc->sc_bar0res == NULL) {
|
|
device_printf(dev, "cannot map bar%d register space\n", 0);
|
|
goto fail_pci;
|
|
}
|
|
sc->sc_st0 = rman_get_bustag(sc->sc_bar0res);
|
|
sc->sc_sh0 = rman_get_bushandle(sc->sc_bar0res);
|
|
sc->sc_bar0_lastreg = (bus_size_t) -1;
|
|
|
|
rid = HIFN_BAR1;
|
|
sc->sc_bar1res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
|
|
RF_ACTIVE);
|
|
if (sc->sc_bar1res == NULL) {
|
|
device_printf(dev, "cannot map bar%d register space\n", 1);
|
|
goto fail_io0;
|
|
}
|
|
sc->sc_st1 = rman_get_bustag(sc->sc_bar1res);
|
|
sc->sc_sh1 = rman_get_bushandle(sc->sc_bar1res);
|
|
sc->sc_bar1_lastreg = (bus_size_t) -1;
|
|
|
|
hifn_set_retry(sc);
|
|
|
|
/*
|
|
* Setup the area where the Hifn DMA's descriptors
|
|
* and associated data structures.
|
|
*/
|
|
if (bus_dma_tag_create(bus_get_dma_tag(dev), /* PCI parent */
|
|
1, 0, /* alignment,boundary */
|
|
BUS_SPACE_MAXADDR_32BIT, /* lowaddr */
|
|
BUS_SPACE_MAXADDR, /* highaddr */
|
|
NULL, NULL, /* filter, filterarg */
|
|
HIFN_MAX_DMALEN, /* maxsize */
|
|
MAX_SCATTER, /* nsegments */
|
|
HIFN_MAX_SEGLEN, /* maxsegsize */
|
|
BUS_DMA_ALLOCNOW, /* flags */
|
|
NULL, /* lockfunc */
|
|
NULL, /* lockarg */
|
|
&sc->sc_dmat)) {
|
|
device_printf(dev, "cannot allocate DMA tag\n");
|
|
goto fail_io1;
|
|
}
|
|
if (bus_dmamap_create(sc->sc_dmat, BUS_DMA_NOWAIT, &sc->sc_dmamap)) {
|
|
device_printf(dev, "cannot create dma map\n");
|
|
bus_dma_tag_destroy(sc->sc_dmat);
|
|
goto fail_io1;
|
|
}
|
|
if (bus_dmamem_alloc(sc->sc_dmat, (void**) &kva, BUS_DMA_NOWAIT, &sc->sc_dmamap)) {
|
|
device_printf(dev, "cannot alloc dma buffer\n");
|
|
bus_dmamap_destroy(sc->sc_dmat, sc->sc_dmamap);
|
|
bus_dma_tag_destroy(sc->sc_dmat);
|
|
goto fail_io1;
|
|
}
|
|
if (bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap, kva,
|
|
sizeof (*sc->sc_dma),
|
|
hifn_dmamap_cb, &sc->sc_dma_physaddr,
|
|
BUS_DMA_NOWAIT)) {
|
|
device_printf(dev, "cannot load dma map\n");
|
|
bus_dmamem_free(sc->sc_dmat, kva, sc->sc_dmamap);
|
|
bus_dma_tag_destroy(sc->sc_dmat);
|
|
goto fail_io1;
|
|
}
|
|
sc->sc_dma = (struct hifn_dma *)kva;
|
|
bzero(sc->sc_dma, sizeof(*sc->sc_dma));
|
|
|
|
KASSERT(sc->sc_st0 != 0, ("hifn_attach: null bar0 tag!"));
|
|
KASSERT(sc->sc_sh0 != 0, ("hifn_attach: null bar0 handle!"));
|
|
KASSERT(sc->sc_st1 != 0, ("hifn_attach: null bar1 tag!"));
|
|
KASSERT(sc->sc_sh1 != 0, ("hifn_attach: null bar1 handle!"));
|
|
|
|
/*
|
|
* Reset the board and do the ``secret handshake''
|
|
* to enable the crypto support. Then complete the
|
|
* initialization procedure by setting up the interrupt
|
|
* and hooking in to the system crypto support so we'll
|
|
* get used for system services like the crypto device,
|
|
* IPsec, RNG device, etc.
|
|
*/
|
|
hifn_reset_board(sc, 0);
|
|
|
|
if (hifn_enable_crypto(sc) != 0) {
|
|
device_printf(dev, "crypto enabling failed\n");
|
|
goto fail_mem;
|
|
}
|
|
hifn_reset_puc(sc);
|
|
|
|
hifn_init_dma(sc);
|
|
hifn_init_pci_registers(sc);
|
|
|
|
/* XXX can't dynamically determine ram type for 795x; force dram */
|
|
if (sc->sc_flags & HIFN_IS_7956)
|
|
sc->sc_drammodel = 1;
|
|
else if (hifn_ramtype(sc))
|
|
goto fail_mem;
|
|
|
|
if (sc->sc_drammodel == 0)
|
|
hifn_sramsize(sc);
|
|
else
|
|
hifn_dramsize(sc);
|
|
|
|
/*
|
|
* Workaround for NetSec 7751 rev A: half ram size because two
|
|
* of the address lines were left floating
|
|
*/
|
|
if (pci_get_vendor(dev) == PCI_VENDOR_NETSEC &&
|
|
pci_get_device(dev) == PCI_PRODUCT_NETSEC_7751 &&
|
|
pci_get_revid(dev) == 0x61) /*XXX???*/
|
|
sc->sc_ramsize >>= 1;
|
|
|
|
/*
|
|
* Arrange the interrupt line.
|
|
*/
|
|
rid = 0;
|
|
sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
|
|
RF_SHAREABLE|RF_ACTIVE);
|
|
if (sc->sc_irq == NULL) {
|
|
device_printf(dev, "could not map interrupt\n");
|
|
goto fail_mem;
|
|
}
|
|
/*
|
|
* NB: Network code assumes we are blocked with splimp()
|
|
* so make sure the IRQ is marked appropriately.
|
|
*/
|
|
if (bus_setup_intr(dev, sc->sc_irq, INTR_TYPE_NET | INTR_MPSAFE,
|
|
NULL, hifn_intr, sc, &sc->sc_intrhand)) {
|
|
device_printf(dev, "could not setup interrupt\n");
|
|
goto fail_intr2;
|
|
}
|
|
|
|
hifn_sessions(sc);
|
|
|
|
/*
|
|
* NB: Keep only the low 16 bits; this masks the chip id
|
|
* from the 7951.
|
|
*/
|
|
rev = READ_REG_1(sc, HIFN_1_REVID) & 0xffff;
|
|
|
|
rseg = sc->sc_ramsize / 1024;
|
|
rbase = 'K';
|
|
if (sc->sc_ramsize >= (1024 * 1024)) {
|
|
rbase = 'M';
|
|
rseg /= 1024;
|
|
}
|
|
device_printf(sc->sc_dev, "%s, rev %u, %d%cB %cram",
|
|
hifn_partname(sc), rev,
|
|
rseg, rbase, sc->sc_drammodel ? 'd' : 's');
|
|
if (sc->sc_flags & HIFN_IS_7956)
|
|
printf(", pll=0x%x<%s clk, %ux mult>",
|
|
sc->sc_pllconfig,
|
|
sc->sc_pllconfig & HIFN_PLL_REF_SEL ? "ext" : "pci",
|
|
2 + 2*((sc->sc_pllconfig & HIFN_PLL_ND) >> 11));
|
|
printf("\n");
|
|
|
|
WRITE_REG_0(sc, HIFN_0_PUCNFG,
|
|
READ_REG_0(sc, HIFN_0_PUCNFG) | HIFN_PUCNFG_CHIPID);
|
|
sc->sc_ena = READ_REG_0(sc, HIFN_0_PUSTAT) & HIFN_PUSTAT_CHIPENA;
|
|
|
|
switch (sc->sc_ena) {
|
|
case HIFN_PUSTAT_ENA_2:
|
|
case HIFN_PUSTAT_ENA_1:
|
|
sc->sc_cid = crypto_get_driverid(dev,
|
|
sizeof(struct hifn_session), CRYPTOCAP_F_HARDWARE);
|
|
if (sc->sc_cid < 0) {
|
|
device_printf(dev, "could not get crypto driver id\n");
|
|
goto fail_intr;
|
|
}
|
|
break;
|
|
}
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
|
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
|
|
|
if (sc->sc_flags & (HIFN_HAS_PUBLIC | HIFN_HAS_RNG))
|
|
hifn_init_pubrng(sc);
|
|
|
|
callout_init(&sc->sc_tickto, 1);
|
|
callout_reset(&sc->sc_tickto, hz, hifn_tick, sc);
|
|
|
|
return (0);
|
|
|
|
fail_intr:
|
|
bus_teardown_intr(dev, sc->sc_irq, sc->sc_intrhand);
|
|
fail_intr2:
|
|
/* XXX don't store rid */
|
|
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq);
|
|
fail_mem:
|
|
bus_dmamap_unload(sc->sc_dmat, sc->sc_dmamap);
|
|
bus_dmamem_free(sc->sc_dmat, sc->sc_dma, sc->sc_dmamap);
|
|
bus_dma_tag_destroy(sc->sc_dmat);
|
|
|
|
/* Turn off DMA polling */
|
|
WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MSTRESET |
|
|
HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE);
|
|
fail_io1:
|
|
bus_release_resource(dev, SYS_RES_MEMORY, HIFN_BAR1, sc->sc_bar1res);
|
|
fail_io0:
|
|
bus_release_resource(dev, SYS_RES_MEMORY, HIFN_BAR0, sc->sc_bar0res);
|
|
fail_pci:
|
|
mtx_destroy(&sc->sc_mtx);
|
|
return (ENXIO);
|
|
}
|
|
|
|
/*
|
|
* Detach an interface that successfully probed.
|
|
*/
|
|
static int
|
|
hifn_detach(device_t dev)
|
|
{
|
|
struct hifn_softc *sc = device_get_softc(dev);
|
|
|
|
KASSERT(sc != NULL, ("hifn_detach: null software carrier!"));
|
|
|
|
/* disable interrupts */
|
|
WRITE_REG_1(sc, HIFN_1_DMA_IER, 0);
|
|
|
|
/*XXX other resources */
|
|
callout_stop(&sc->sc_tickto);
|
|
callout_stop(&sc->sc_rngto);
|
|
#ifdef HIFN_RNDTEST
|
|
if (sc->sc_rndtest)
|
|
rndtest_detach(sc->sc_rndtest);
|
|
#endif
|
|
|
|
/* Turn off DMA polling */
|
|
WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MSTRESET |
|
|
HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE);
|
|
|
|
crypto_unregister_all(sc->sc_cid);
|
|
|
|
bus_generic_detach(dev); /*XXX should be no children, right? */
|
|
|
|
bus_teardown_intr(dev, sc->sc_irq, sc->sc_intrhand);
|
|
/* XXX don't store rid */
|
|
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq);
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, sc->sc_dmamap);
|
|
bus_dmamem_free(sc->sc_dmat, sc->sc_dma, sc->sc_dmamap);
|
|
bus_dma_tag_destroy(sc->sc_dmat);
|
|
|
|
bus_release_resource(dev, SYS_RES_MEMORY, HIFN_BAR1, sc->sc_bar1res);
|
|
bus_release_resource(dev, SYS_RES_MEMORY, HIFN_BAR0, sc->sc_bar0res);
|
|
|
|
mtx_destroy(&sc->sc_mtx);
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Stop all chip I/O so that the kernel's probe routines don't
|
|
* get confused by errant DMAs when rebooting.
|
|
*/
|
|
static int
|
|
hifn_shutdown(device_t dev)
|
|
{
|
|
#ifdef notyet
|
|
hifn_stop(device_get_softc(dev));
|
|
#endif
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Device suspend routine. Stop the interface and save some PCI
|
|
* settings in case the BIOS doesn't restore them properly on
|
|
* resume.
|
|
*/
|
|
static int
|
|
hifn_suspend(device_t dev)
|
|
{
|
|
struct hifn_softc *sc = device_get_softc(dev);
|
|
#ifdef notyet
|
|
hifn_stop(sc);
|
|
#endif
|
|
sc->sc_suspended = 1;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* Device resume routine. Restore some PCI settings in case the BIOS
|
|
* doesn't, re-enable busmastering, and restart the interface if
|
|
* appropriate.
|
|
*/
|
|
static int
|
|
hifn_resume(device_t dev)
|
|
{
|
|
struct hifn_softc *sc = device_get_softc(dev);
|
|
#ifdef notyet
|
|
/* reinitialize interface if necessary */
|
|
if (ifp->if_flags & IFF_UP)
|
|
rl_init(sc);
|
|
#endif
|
|
sc->sc_suspended = 0;
|
|
|
|
return (0);
|
|
}
|
|
|
|
static int
|
|
hifn_init_pubrng(struct hifn_softc *sc)
|
|
{
|
|
u_int32_t r;
|
|
int i;
|
|
|
|
#ifdef HIFN_RNDTEST
|
|
sc->sc_rndtest = rndtest_attach(sc->sc_dev);
|
|
if (sc->sc_rndtest)
|
|
sc->sc_harvest = rndtest_harvest;
|
|
else
|
|
sc->sc_harvest = default_harvest;
|
|
#else
|
|
sc->sc_harvest = default_harvest;
|
|
#endif
|
|
if ((sc->sc_flags & HIFN_IS_7811) == 0) {
|
|
/* Reset 7951 public key/rng engine */
|
|
WRITE_REG_1(sc, HIFN_1_PUB_RESET,
|
|
READ_REG_1(sc, HIFN_1_PUB_RESET) | HIFN_PUBRST_RESET);
|
|
|
|
for (i = 0; i < 100; i++) {
|
|
DELAY(1000);
|
|
if ((READ_REG_1(sc, HIFN_1_PUB_RESET) &
|
|
HIFN_PUBRST_RESET) == 0)
|
|
break;
|
|
}
|
|
|
|
if (i == 100) {
|
|
device_printf(sc->sc_dev, "public key init failed\n");
|
|
return (1);
|
|
}
|
|
}
|
|
|
|
/* Enable the rng, if available */
|
|
if (sc->sc_flags & HIFN_HAS_RNG) {
|
|
if (sc->sc_flags & HIFN_IS_7811) {
|
|
r = READ_REG_1(sc, HIFN_1_7811_RNGENA);
|
|
if (r & HIFN_7811_RNGENA_ENA) {
|
|
r &= ~HIFN_7811_RNGENA_ENA;
|
|
WRITE_REG_1(sc, HIFN_1_7811_RNGENA, r);
|
|
}
|
|
WRITE_REG_1(sc, HIFN_1_7811_RNGCFG,
|
|
HIFN_7811_RNGCFG_DEFL);
|
|
r |= HIFN_7811_RNGENA_ENA;
|
|
WRITE_REG_1(sc, HIFN_1_7811_RNGENA, r);
|
|
} else
|
|
WRITE_REG_1(sc, HIFN_1_RNG_CONFIG,
|
|
READ_REG_1(sc, HIFN_1_RNG_CONFIG) |
|
|
HIFN_RNGCFG_ENA);
|
|
|
|
sc->sc_rngfirst = 1;
|
|
if (hz >= 100)
|
|
sc->sc_rnghz = hz / 100;
|
|
else
|
|
sc->sc_rnghz = 1;
|
|
callout_init(&sc->sc_rngto, 1);
|
|
callout_reset(&sc->sc_rngto, sc->sc_rnghz, hifn_rng, sc);
|
|
}
|
|
|
|
/* Enable public key engine, if available */
|
|
if (sc->sc_flags & HIFN_HAS_PUBLIC) {
|
|
WRITE_REG_1(sc, HIFN_1_PUB_IEN, HIFN_PUBIEN_DONE);
|
|
sc->sc_dmaier |= HIFN_DMAIER_PUBDONE;
|
|
WRITE_REG_1(sc, HIFN_1_DMA_IER, sc->sc_dmaier);
|
|
#ifdef HIFN_VULCANDEV
|
|
sc->sc_pkdev = make_dev(&vulcanpk_cdevsw, 0,
|
|
UID_ROOT, GID_WHEEL, 0666,
|
|
"vulcanpk");
|
|
sc->sc_pkdev->si_drv1 = sc;
|
|
#endif
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
hifn_rng(void *vsc)
|
|
{
|
|
#define RANDOM_BITS(n) (n)*sizeof (u_int32_t), (n)*sizeof (u_int32_t)*NBBY, 0
|
|
struct hifn_softc *sc = vsc;
|
|
u_int32_t sts, num[2];
|
|
int i;
|
|
|
|
if (sc->sc_flags & HIFN_IS_7811) {
|
|
/* ONLY VALID ON 7811!!!! */
|
|
for (i = 0; i < 5; i++) {
|
|
sts = READ_REG_1(sc, HIFN_1_7811_RNGSTS);
|
|
if (sts & HIFN_7811_RNGSTS_UFL) {
|
|
device_printf(sc->sc_dev,
|
|
"RNG underflow: disabling\n");
|
|
return;
|
|
}
|
|
if ((sts & HIFN_7811_RNGSTS_RDY) == 0)
|
|
break;
|
|
|
|
/*
|
|
* There are at least two words in the RNG FIFO
|
|
* at this point.
|
|
*/
|
|
num[0] = READ_REG_1(sc, HIFN_1_7811_RNGDAT);
|
|
num[1] = READ_REG_1(sc, HIFN_1_7811_RNGDAT);
|
|
/* NB: discard first data read */
|
|
if (sc->sc_rngfirst)
|
|
sc->sc_rngfirst = 0;
|
|
else
|
|
(*sc->sc_harvest)(sc->sc_rndtest,
|
|
num, sizeof (num));
|
|
}
|
|
} else {
|
|
num[0] = READ_REG_1(sc, HIFN_1_RNG_DATA);
|
|
|
|
/* NB: discard first data read */
|
|
if (sc->sc_rngfirst)
|
|
sc->sc_rngfirst = 0;
|
|
else
|
|
(*sc->sc_harvest)(sc->sc_rndtest,
|
|
num, sizeof (num[0]));
|
|
}
|
|
|
|
callout_reset(&sc->sc_rngto, sc->sc_rnghz, hifn_rng, sc);
|
|
#undef RANDOM_BITS
|
|
}
|
|
|
|
static void
|
|
hifn_puc_wait(struct hifn_softc *sc)
|
|
{
|
|
int i;
|
|
int reg = HIFN_0_PUCTRL;
|
|
|
|
if (sc->sc_flags & HIFN_IS_7956) {
|
|
reg = HIFN_0_PUCTRL2;
|
|
}
|
|
|
|
for (i = 5000; i > 0; i--) {
|
|
DELAY(1);
|
|
if (!(READ_REG_0(sc, reg) & HIFN_PUCTRL_RESET))
|
|
break;
|
|
}
|
|
if (!i)
|
|
device_printf(sc->sc_dev, "proc unit did not reset\n");
|
|
}
|
|
|
|
/*
|
|
* Reset the processing unit.
|
|
*/
|
|
static void
|
|
hifn_reset_puc(struct hifn_softc *sc)
|
|
{
|
|
/* Reset processing unit */
|
|
int reg = HIFN_0_PUCTRL;
|
|
|
|
if (sc->sc_flags & HIFN_IS_7956) {
|
|
reg = HIFN_0_PUCTRL2;
|
|
}
|
|
WRITE_REG_0(sc, reg, HIFN_PUCTRL_DMAENA);
|
|
|
|
hifn_puc_wait(sc);
|
|
}
|
|
|
|
/*
|
|
* Set the Retry and TRDY registers; note that we set them to
|
|
* zero because the 7811 locks up when forced to retry (section
|
|
* 3.6 of "Specification Update SU-0014-04". Not clear if we
|
|
* should do this for all Hifn parts, but it doesn't seem to hurt.
|
|
*/
|
|
static void
|
|
hifn_set_retry(struct hifn_softc *sc)
|
|
{
|
|
/* NB: RETRY only responds to 8-bit reads/writes */
|
|
pci_write_config(sc->sc_dev, HIFN_RETRY_TIMEOUT, 0, 1);
|
|
pci_write_config(sc->sc_dev, HIFN_TRDY_TIMEOUT, 0, 1);
|
|
}
|
|
|
|
/*
|
|
* Resets the board. Values in the regesters are left as is
|
|
* from the reset (i.e. initial values are assigned elsewhere).
|
|
*/
|
|
static void
|
|
hifn_reset_board(struct hifn_softc *sc, int full)
|
|
{
|
|
u_int32_t reg;
|
|
|
|
/*
|
|
* Set polling in the DMA configuration register to zero. 0x7 avoids
|
|
* resetting the board and zeros out the other fields.
|
|
*/
|
|
WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MSTRESET |
|
|
HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE);
|
|
|
|
/*
|
|
* Now that polling has been disabled, we have to wait 1 ms
|
|
* before resetting the board.
|
|
*/
|
|
DELAY(1000);
|
|
|
|
/* Reset the DMA unit */
|
|
if (full) {
|
|
WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MODE);
|
|
DELAY(1000);
|
|
} else {
|
|
WRITE_REG_1(sc, HIFN_1_DMA_CNFG,
|
|
HIFN_DMACNFG_MODE | HIFN_DMACNFG_MSTRESET);
|
|
hifn_reset_puc(sc);
|
|
}
|
|
|
|
KASSERT(sc->sc_dma != NULL, ("hifn_reset_board: null DMA tag!"));
|
|
bzero(sc->sc_dma, sizeof(*sc->sc_dma));
|
|
|
|
/* Bring dma unit out of reset */
|
|
WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MSTRESET |
|
|
HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE);
|
|
|
|
hifn_puc_wait(sc);
|
|
hifn_set_retry(sc);
|
|
|
|
if (sc->sc_flags & HIFN_IS_7811) {
|
|
for (reg = 0; reg < 1000; reg++) {
|
|
if (READ_REG_1(sc, HIFN_1_7811_MIPSRST) &
|
|
HIFN_MIPSRST_CRAMINIT)
|
|
break;
|
|
DELAY(1000);
|
|
}
|
|
if (reg == 1000)
|
|
printf(": cram init timeout\n");
|
|
} else {
|
|
/* set up DMA configuration register #2 */
|
|
/* turn off all PK and BAR0 swaps */
|
|
WRITE_REG_1(sc, HIFN_1_DMA_CNFG2,
|
|
(3 << HIFN_DMACNFG2_INIT_WRITE_BURST_SHIFT)|
|
|
(3 << HIFN_DMACNFG2_INIT_READ_BURST_SHIFT)|
|
|
(2 << HIFN_DMACNFG2_TGT_WRITE_BURST_SHIFT)|
|
|
(2 << HIFN_DMACNFG2_TGT_READ_BURST_SHIFT));
|
|
}
|
|
|
|
}
|
|
|
|
static u_int32_t
|
|
hifn_next_signature(u_int32_t a, u_int cnt)
|
|
{
|
|
int i;
|
|
u_int32_t v;
|
|
|
|
for (i = 0; i < cnt; i++) {
|
|
|
|
/* get the parity */
|
|
v = a & 0x80080125;
|
|
v ^= v >> 16;
|
|
v ^= v >> 8;
|
|
v ^= v >> 4;
|
|
v ^= v >> 2;
|
|
v ^= v >> 1;
|
|
|
|
a = (v & 1) ^ (a << 1);
|
|
}
|
|
|
|
return a;
|
|
}
|
|
|
|
struct pci2id {
|
|
u_short pci_vendor;
|
|
u_short pci_prod;
|
|
char card_id[13];
|
|
};
|
|
static struct pci2id pci2id[] = {
|
|
{
|
|
PCI_VENDOR_HIFN,
|
|
PCI_PRODUCT_HIFN_7951,
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00 }
|
|
}, {
|
|
PCI_VENDOR_HIFN,
|
|
PCI_PRODUCT_HIFN_7955,
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00 }
|
|
}, {
|
|
PCI_VENDOR_HIFN,
|
|
PCI_PRODUCT_HIFN_7956,
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00 }
|
|
}, {
|
|
PCI_VENDOR_NETSEC,
|
|
PCI_PRODUCT_NETSEC_7751,
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00 }
|
|
}, {
|
|
PCI_VENDOR_INVERTEX,
|
|
PCI_PRODUCT_INVERTEX_AEON,
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00 }
|
|
}, {
|
|
PCI_VENDOR_HIFN,
|
|
PCI_PRODUCT_HIFN_7811,
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00 }
|
|
}, {
|
|
/*
|
|
* Other vendors share this PCI ID as well, such as
|
|
* http://www.powercrypt.com, and obviously they also
|
|
* use the same key.
|
|
*/
|
|
PCI_VENDOR_HIFN,
|
|
PCI_PRODUCT_HIFN_7751,
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00 }
|
|
},
|
|
};
|
|
|
|
/*
|
|
* Checks to see if crypto is already enabled. If crypto isn't enable,
|
|
* "hifn_enable_crypto" is called to enable it. The check is important,
|
|
* as enabling crypto twice will lock the board.
|
|
*/
|
|
static int
|
|
hifn_enable_crypto(struct hifn_softc *sc)
|
|
{
|
|
u_int32_t dmacfg, ramcfg, encl, addr, i;
|
|
char *offtbl = NULL;
|
|
|
|
for (i = 0; i < nitems(pci2id); i++) {
|
|
if (pci2id[i].pci_vendor == pci_get_vendor(sc->sc_dev) &&
|
|
pci2id[i].pci_prod == pci_get_device(sc->sc_dev)) {
|
|
offtbl = pci2id[i].card_id;
|
|
break;
|
|
}
|
|
}
|
|
if (offtbl == NULL) {
|
|
device_printf(sc->sc_dev, "Unknown card!\n");
|
|
return (1);
|
|
}
|
|
|
|
ramcfg = READ_REG_0(sc, HIFN_0_PUCNFG);
|
|
dmacfg = READ_REG_1(sc, HIFN_1_DMA_CNFG);
|
|
|
|
/*
|
|
* The RAM config register's encrypt level bit needs to be set before
|
|
* every read performed on the encryption level register.
|
|
*/
|
|
WRITE_REG_0(sc, HIFN_0_PUCNFG, ramcfg | HIFN_PUCNFG_CHIPID);
|
|
|
|
encl = READ_REG_0(sc, HIFN_0_PUSTAT) & HIFN_PUSTAT_CHIPENA;
|
|
|
|
/*
|
|
* Make sure we don't re-unlock. Two unlocks kills chip until the
|
|
* next reboot.
|
|
*/
|
|
if (encl == HIFN_PUSTAT_ENA_1 || encl == HIFN_PUSTAT_ENA_2) {
|
|
#ifdef HIFN_DEBUG
|
|
if (hifn_debug)
|
|
device_printf(sc->sc_dev,
|
|
"Strong crypto already enabled!\n");
|
|
#endif
|
|
goto report;
|
|
}
|
|
|
|
if (encl != 0 && encl != HIFN_PUSTAT_ENA_0) {
|
|
#ifdef HIFN_DEBUG
|
|
if (hifn_debug)
|
|
device_printf(sc->sc_dev,
|
|
"Unknown encryption level 0x%x\n", encl);
|
|
#endif
|
|
return 1;
|
|
}
|
|
|
|
WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_UNLOCK |
|
|
HIFN_DMACNFG_MSTRESET | HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE);
|
|
DELAY(1000);
|
|
addr = READ_REG_1(sc, HIFN_UNLOCK_SECRET1);
|
|
DELAY(1000);
|
|
WRITE_REG_1(sc, HIFN_UNLOCK_SECRET2, 0);
|
|
DELAY(1000);
|
|
|
|
for (i = 0; i <= 12; i++) {
|
|
addr = hifn_next_signature(addr, offtbl[i] + 0x101);
|
|
WRITE_REG_1(sc, HIFN_UNLOCK_SECRET2, addr);
|
|
|
|
DELAY(1000);
|
|
}
|
|
|
|
WRITE_REG_0(sc, HIFN_0_PUCNFG, ramcfg | HIFN_PUCNFG_CHIPID);
|
|
encl = READ_REG_0(sc, HIFN_0_PUSTAT) & HIFN_PUSTAT_CHIPENA;
|
|
|
|
#ifdef HIFN_DEBUG
|
|
if (hifn_debug) {
|
|
if (encl != HIFN_PUSTAT_ENA_1 && encl != HIFN_PUSTAT_ENA_2)
|
|
device_printf(sc->sc_dev, "Engine is permanently "
|
|
"locked until next system reset!\n");
|
|
else
|
|
device_printf(sc->sc_dev, "Engine enabled "
|
|
"successfully!\n");
|
|
}
|
|
#endif
|
|
|
|
report:
|
|
WRITE_REG_0(sc, HIFN_0_PUCNFG, ramcfg);
|
|
WRITE_REG_1(sc, HIFN_1_DMA_CNFG, dmacfg);
|
|
|
|
switch (encl) {
|
|
case HIFN_PUSTAT_ENA_1:
|
|
case HIFN_PUSTAT_ENA_2:
|
|
break;
|
|
case HIFN_PUSTAT_ENA_0:
|
|
default:
|
|
device_printf(sc->sc_dev, "disabled");
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Give initial values to the registers listed in the "Register Space"
|
|
* section of the HIFN Software Development reference manual.
|
|
*/
|
|
static void
|
|
hifn_init_pci_registers(struct hifn_softc *sc)
|
|
{
|
|
/* write fixed values needed by the Initialization registers */
|
|
WRITE_REG_0(sc, HIFN_0_PUCTRL, HIFN_PUCTRL_DMAENA);
|
|
WRITE_REG_0(sc, HIFN_0_FIFOCNFG, HIFN_FIFOCNFG_THRESHOLD);
|
|
WRITE_REG_0(sc, HIFN_0_PUIER, HIFN_PUIER_DSTOVER);
|
|
|
|
/* write all 4 ring address registers */
|
|
WRITE_REG_1(sc, HIFN_1_DMA_CRAR, sc->sc_dma_physaddr +
|
|
offsetof(struct hifn_dma, cmdr[0]));
|
|
WRITE_REG_1(sc, HIFN_1_DMA_SRAR, sc->sc_dma_physaddr +
|
|
offsetof(struct hifn_dma, srcr[0]));
|
|
WRITE_REG_1(sc, HIFN_1_DMA_DRAR, sc->sc_dma_physaddr +
|
|
offsetof(struct hifn_dma, dstr[0]));
|
|
WRITE_REG_1(sc, HIFN_1_DMA_RRAR, sc->sc_dma_physaddr +
|
|
offsetof(struct hifn_dma, resr[0]));
|
|
|
|
DELAY(2000);
|
|
|
|
/* write status register */
|
|
WRITE_REG_1(sc, HIFN_1_DMA_CSR,
|
|
HIFN_DMACSR_D_CTRL_DIS | HIFN_DMACSR_R_CTRL_DIS |
|
|
HIFN_DMACSR_S_CTRL_DIS | HIFN_DMACSR_C_CTRL_DIS |
|
|
HIFN_DMACSR_D_ABORT | HIFN_DMACSR_D_DONE | HIFN_DMACSR_D_LAST |
|
|
HIFN_DMACSR_D_WAIT | HIFN_DMACSR_D_OVER |
|
|
HIFN_DMACSR_R_ABORT | HIFN_DMACSR_R_DONE | HIFN_DMACSR_R_LAST |
|
|
HIFN_DMACSR_R_WAIT | HIFN_DMACSR_R_OVER |
|
|
HIFN_DMACSR_S_ABORT | HIFN_DMACSR_S_DONE | HIFN_DMACSR_S_LAST |
|
|
HIFN_DMACSR_S_WAIT |
|
|
HIFN_DMACSR_C_ABORT | HIFN_DMACSR_C_DONE | HIFN_DMACSR_C_LAST |
|
|
HIFN_DMACSR_C_WAIT |
|
|
HIFN_DMACSR_ENGINE |
|
|
((sc->sc_flags & HIFN_HAS_PUBLIC) ?
|
|
HIFN_DMACSR_PUBDONE : 0) |
|
|
((sc->sc_flags & HIFN_IS_7811) ?
|
|
HIFN_DMACSR_ILLW | HIFN_DMACSR_ILLR : 0));
|
|
|
|
sc->sc_d_busy = sc->sc_r_busy = sc->sc_s_busy = sc->sc_c_busy = 0;
|
|
sc->sc_dmaier |= HIFN_DMAIER_R_DONE | HIFN_DMAIER_C_ABORT |
|
|
HIFN_DMAIER_D_OVER | HIFN_DMAIER_R_OVER |
|
|
HIFN_DMAIER_S_ABORT | HIFN_DMAIER_D_ABORT | HIFN_DMAIER_R_ABORT |
|
|
((sc->sc_flags & HIFN_IS_7811) ?
|
|
HIFN_DMAIER_ILLW | HIFN_DMAIER_ILLR : 0);
|
|
sc->sc_dmaier &= ~HIFN_DMAIER_C_WAIT;
|
|
WRITE_REG_1(sc, HIFN_1_DMA_IER, sc->sc_dmaier);
|
|
|
|
|
|
if (sc->sc_flags & HIFN_IS_7956) {
|
|
u_int32_t pll;
|
|
|
|
WRITE_REG_0(sc, HIFN_0_PUCNFG, HIFN_PUCNFG_COMPSING |
|
|
HIFN_PUCNFG_TCALLPHASES |
|
|
HIFN_PUCNFG_TCDRVTOTEM | HIFN_PUCNFG_BUS32);
|
|
|
|
/* turn off the clocks and insure bypass is set */
|
|
pll = READ_REG_1(sc, HIFN_1_PLL);
|
|
pll = (pll &~ (HIFN_PLL_PK_CLK_SEL | HIFN_PLL_PE_CLK_SEL))
|
|
| HIFN_PLL_BP | HIFN_PLL_MBSET;
|
|
WRITE_REG_1(sc, HIFN_1_PLL, pll);
|
|
DELAY(10*1000); /* 10ms */
|
|
|
|
/* change configuration */
|
|
pll = (pll &~ HIFN_PLL_CONFIG) | sc->sc_pllconfig;
|
|
WRITE_REG_1(sc, HIFN_1_PLL, pll);
|
|
DELAY(10*1000); /* 10ms */
|
|
|
|
/* disable bypass */
|
|
pll &= ~HIFN_PLL_BP;
|
|
WRITE_REG_1(sc, HIFN_1_PLL, pll);
|
|
/* enable clocks with new configuration */
|
|
pll |= HIFN_PLL_PK_CLK_SEL | HIFN_PLL_PE_CLK_SEL;
|
|
WRITE_REG_1(sc, HIFN_1_PLL, pll);
|
|
} else {
|
|
WRITE_REG_0(sc, HIFN_0_PUCNFG, HIFN_PUCNFG_COMPSING |
|
|
HIFN_PUCNFG_DRFR_128 | HIFN_PUCNFG_TCALLPHASES |
|
|
HIFN_PUCNFG_TCDRVTOTEM | HIFN_PUCNFG_BUS32 |
|
|
(sc->sc_drammodel ? HIFN_PUCNFG_DRAM : HIFN_PUCNFG_SRAM));
|
|
}
|
|
|
|
WRITE_REG_0(sc, HIFN_0_PUISR, HIFN_PUISR_DSTOVER);
|
|
WRITE_REG_1(sc, HIFN_1_DMA_CNFG, HIFN_DMACNFG_MSTRESET |
|
|
HIFN_DMACNFG_DMARESET | HIFN_DMACNFG_MODE | HIFN_DMACNFG_LAST |
|
|
((HIFN_POLL_FREQUENCY << 16 ) & HIFN_DMACNFG_POLLFREQ) |
|
|
((HIFN_POLL_SCALAR << 8) & HIFN_DMACNFG_POLLINVAL));
|
|
}
|
|
|
|
/*
|
|
* The maximum number of sessions supported by the card
|
|
* is dependent on the amount of context ram, which
|
|
* encryption algorithms are enabled, and how compression
|
|
* is configured. This should be configured before this
|
|
* routine is called.
|
|
*/
|
|
static void
|
|
hifn_sessions(struct hifn_softc *sc)
|
|
{
|
|
u_int32_t pucnfg;
|
|
int ctxsize;
|
|
|
|
pucnfg = READ_REG_0(sc, HIFN_0_PUCNFG);
|
|
|
|
if (pucnfg & HIFN_PUCNFG_COMPSING) {
|
|
if (pucnfg & HIFN_PUCNFG_ENCCNFG)
|
|
ctxsize = 128;
|
|
else
|
|
ctxsize = 512;
|
|
/*
|
|
* 7955/7956 has internal context memory of 32K
|
|
*/
|
|
if (sc->sc_flags & HIFN_IS_7956)
|
|
sc->sc_maxses = 32768 / ctxsize;
|
|
else
|
|
sc->sc_maxses = 1 +
|
|
((sc->sc_ramsize - 32768) / ctxsize);
|
|
} else
|
|
sc->sc_maxses = sc->sc_ramsize / 16384;
|
|
|
|
if (sc->sc_maxses > 2048)
|
|
sc->sc_maxses = 2048;
|
|
}
|
|
|
|
/*
|
|
* Determine ram type (sram or dram). Board should be just out of a reset
|
|
* state when this is called.
|
|
*/
|
|
static int
|
|
hifn_ramtype(struct hifn_softc *sc)
|
|
{
|
|
u_int8_t data[8], dataexpect[8];
|
|
int i;
|
|
|
|
for (i = 0; i < sizeof(data); i++)
|
|
data[i] = dataexpect[i] = 0x55;
|
|
if (hifn_writeramaddr(sc, 0, data))
|
|
return (-1);
|
|
if (hifn_readramaddr(sc, 0, data))
|
|
return (-1);
|
|
if (bcmp(data, dataexpect, sizeof(data)) != 0) {
|
|
sc->sc_drammodel = 1;
|
|
return (0);
|
|
}
|
|
|
|
for (i = 0; i < sizeof(data); i++)
|
|
data[i] = dataexpect[i] = 0xaa;
|
|
if (hifn_writeramaddr(sc, 0, data))
|
|
return (-1);
|
|
if (hifn_readramaddr(sc, 0, data))
|
|
return (-1);
|
|
if (bcmp(data, dataexpect, sizeof(data)) != 0) {
|
|
sc->sc_drammodel = 1;
|
|
return (0);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
#define HIFN_SRAM_MAX (32 << 20)
|
|
#define HIFN_SRAM_STEP_SIZE 16384
|
|
#define HIFN_SRAM_GRANULARITY (HIFN_SRAM_MAX / HIFN_SRAM_STEP_SIZE)
|
|
|
|
static int
|
|
hifn_sramsize(struct hifn_softc *sc)
|
|
{
|
|
u_int32_t a;
|
|
u_int8_t data[8];
|
|
u_int8_t dataexpect[sizeof(data)];
|
|
int32_t i;
|
|
|
|
for (i = 0; i < sizeof(data); i++)
|
|
data[i] = dataexpect[i] = i ^ 0x5a;
|
|
|
|
for (i = HIFN_SRAM_GRANULARITY - 1; i >= 0; i--) {
|
|
a = i * HIFN_SRAM_STEP_SIZE;
|
|
bcopy(&i, data, sizeof(i));
|
|
hifn_writeramaddr(sc, a, data);
|
|
}
|
|
|
|
for (i = 0; i < HIFN_SRAM_GRANULARITY; i++) {
|
|
a = i * HIFN_SRAM_STEP_SIZE;
|
|
bcopy(&i, dataexpect, sizeof(i));
|
|
if (hifn_readramaddr(sc, a, data) < 0)
|
|
return (0);
|
|
if (bcmp(data, dataexpect, sizeof(data)) != 0)
|
|
return (0);
|
|
sc->sc_ramsize = a + HIFN_SRAM_STEP_SIZE;
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* XXX For dram boards, one should really try all of the
|
|
* HIFN_PUCNFG_DSZ_*'s. This just assumes that PUCNFG
|
|
* is already set up correctly.
|
|
*/
|
|
static int
|
|
hifn_dramsize(struct hifn_softc *sc)
|
|
{
|
|
u_int32_t cnfg;
|
|
|
|
if (sc->sc_flags & HIFN_IS_7956) {
|
|
/*
|
|
* 7955/7956 have a fixed internal ram of only 32K.
|
|
*/
|
|
sc->sc_ramsize = 32768;
|
|
} else {
|
|
cnfg = READ_REG_0(sc, HIFN_0_PUCNFG) &
|
|
HIFN_PUCNFG_DRAMMASK;
|
|
sc->sc_ramsize = 1 << ((cnfg >> 13) + 18);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static void
|
|
hifn_alloc_slot(struct hifn_softc *sc, int *cmdp, int *srcp, int *dstp, int *resp)
|
|
{
|
|
struct hifn_dma *dma = sc->sc_dma;
|
|
|
|
if (sc->sc_cmdi == HIFN_D_CMD_RSIZE) {
|
|
sc->sc_cmdi = 0;
|
|
dma->cmdr[HIFN_D_CMD_RSIZE].l = htole32(HIFN_D_VALID |
|
|
HIFN_D_JUMP | HIFN_D_MASKDONEIRQ);
|
|
HIFN_CMDR_SYNC(sc, HIFN_D_CMD_RSIZE,
|
|
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
|
|
}
|
|
*cmdp = sc->sc_cmdi++;
|
|
sc->sc_cmdk = sc->sc_cmdi;
|
|
|
|
if (sc->sc_srci == HIFN_D_SRC_RSIZE) {
|
|
sc->sc_srci = 0;
|
|
dma->srcr[HIFN_D_SRC_RSIZE].l = htole32(HIFN_D_VALID |
|
|
HIFN_D_JUMP | HIFN_D_MASKDONEIRQ);
|
|
HIFN_SRCR_SYNC(sc, HIFN_D_SRC_RSIZE,
|
|
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
|
|
}
|
|
*srcp = sc->sc_srci++;
|
|
sc->sc_srck = sc->sc_srci;
|
|
|
|
if (sc->sc_dsti == HIFN_D_DST_RSIZE) {
|
|
sc->sc_dsti = 0;
|
|
dma->dstr[HIFN_D_DST_RSIZE].l = htole32(HIFN_D_VALID |
|
|
HIFN_D_JUMP | HIFN_D_MASKDONEIRQ);
|
|
HIFN_DSTR_SYNC(sc, HIFN_D_DST_RSIZE,
|
|
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
|
|
}
|
|
*dstp = sc->sc_dsti++;
|
|
sc->sc_dstk = sc->sc_dsti;
|
|
|
|
if (sc->sc_resi == HIFN_D_RES_RSIZE) {
|
|
sc->sc_resi = 0;
|
|
dma->resr[HIFN_D_RES_RSIZE].l = htole32(HIFN_D_VALID |
|
|
HIFN_D_JUMP | HIFN_D_MASKDONEIRQ);
|
|
HIFN_RESR_SYNC(sc, HIFN_D_RES_RSIZE,
|
|
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
|
|
}
|
|
*resp = sc->sc_resi++;
|
|
sc->sc_resk = sc->sc_resi;
|
|
}
|
|
|
|
static int
|
|
hifn_writeramaddr(struct hifn_softc *sc, int addr, u_int8_t *data)
|
|
{
|
|
struct hifn_dma *dma = sc->sc_dma;
|
|
hifn_base_command_t wc;
|
|
const u_int32_t masks = HIFN_D_VALID | HIFN_D_LAST | HIFN_D_MASKDONEIRQ;
|
|
int r, cmdi, resi, srci, dsti;
|
|
|
|
wc.masks = htole16(3 << 13);
|
|
wc.session_num = htole16(addr >> 14);
|
|
wc.total_source_count = htole16(8);
|
|
wc.total_dest_count = htole16(addr & 0x3fff);
|
|
|
|
hifn_alloc_slot(sc, &cmdi, &srci, &dsti, &resi);
|
|
|
|
WRITE_REG_1(sc, HIFN_1_DMA_CSR,
|
|
HIFN_DMACSR_C_CTRL_ENA | HIFN_DMACSR_S_CTRL_ENA |
|
|
HIFN_DMACSR_D_CTRL_ENA | HIFN_DMACSR_R_CTRL_ENA);
|
|
|
|
/* build write command */
|
|
bzero(dma->command_bufs[cmdi], HIFN_MAX_COMMAND);
|
|
*(hifn_base_command_t *)dma->command_bufs[cmdi] = wc;
|
|
bcopy(data, &dma->test_src, sizeof(dma->test_src));
|
|
|
|
dma->srcr[srci].p = htole32(sc->sc_dma_physaddr
|
|
+ offsetof(struct hifn_dma, test_src));
|
|
dma->dstr[dsti].p = htole32(sc->sc_dma_physaddr
|
|
+ offsetof(struct hifn_dma, test_dst));
|
|
|
|
dma->cmdr[cmdi].l = htole32(16 | masks);
|
|
dma->srcr[srci].l = htole32(8 | masks);
|
|
dma->dstr[dsti].l = htole32(4 | masks);
|
|
dma->resr[resi].l = htole32(4 | masks);
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
|
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
|
|
|
for (r = 10000; r >= 0; r--) {
|
|
DELAY(10);
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
|
|
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
|
|
if ((dma->resr[resi].l & htole32(HIFN_D_VALID)) == 0)
|
|
break;
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
|
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
|
}
|
|
if (r == 0) {
|
|
device_printf(sc->sc_dev, "writeramaddr -- "
|
|
"result[%d](addr %d) still valid\n", resi, addr);
|
|
r = -1;
|
|
return (-1);
|
|
} else
|
|
r = 0;
|
|
|
|
WRITE_REG_1(sc, HIFN_1_DMA_CSR,
|
|
HIFN_DMACSR_C_CTRL_DIS | HIFN_DMACSR_S_CTRL_DIS |
|
|
HIFN_DMACSR_D_CTRL_DIS | HIFN_DMACSR_R_CTRL_DIS);
|
|
|
|
return (r);
|
|
}
|
|
|
|
static int
|
|
hifn_readramaddr(struct hifn_softc *sc, int addr, u_int8_t *data)
|
|
{
|
|
struct hifn_dma *dma = sc->sc_dma;
|
|
hifn_base_command_t rc;
|
|
const u_int32_t masks = HIFN_D_VALID | HIFN_D_LAST | HIFN_D_MASKDONEIRQ;
|
|
int r, cmdi, srci, dsti, resi;
|
|
|
|
rc.masks = htole16(2 << 13);
|
|
rc.session_num = htole16(addr >> 14);
|
|
rc.total_source_count = htole16(addr & 0x3fff);
|
|
rc.total_dest_count = htole16(8);
|
|
|
|
hifn_alloc_slot(sc, &cmdi, &srci, &dsti, &resi);
|
|
|
|
WRITE_REG_1(sc, HIFN_1_DMA_CSR,
|
|
HIFN_DMACSR_C_CTRL_ENA | HIFN_DMACSR_S_CTRL_ENA |
|
|
HIFN_DMACSR_D_CTRL_ENA | HIFN_DMACSR_R_CTRL_ENA);
|
|
|
|
bzero(dma->command_bufs[cmdi], HIFN_MAX_COMMAND);
|
|
*(hifn_base_command_t *)dma->command_bufs[cmdi] = rc;
|
|
|
|
dma->srcr[srci].p = htole32(sc->sc_dma_physaddr +
|
|
offsetof(struct hifn_dma, test_src));
|
|
dma->test_src = 0;
|
|
dma->dstr[dsti].p = htole32(sc->sc_dma_physaddr +
|
|
offsetof(struct hifn_dma, test_dst));
|
|
dma->test_dst = 0;
|
|
dma->cmdr[cmdi].l = htole32(8 | masks);
|
|
dma->srcr[srci].l = htole32(8 | masks);
|
|
dma->dstr[dsti].l = htole32(8 | masks);
|
|
dma->resr[resi].l = htole32(HIFN_MAX_RESULT | masks);
|
|
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
|
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
|
|
|
for (r = 10000; r >= 0; r--) {
|
|
DELAY(10);
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
|
|
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
|
|
if ((dma->resr[resi].l & htole32(HIFN_D_VALID)) == 0)
|
|
break;
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
|
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
|
}
|
|
if (r == 0) {
|
|
device_printf(sc->sc_dev, "readramaddr -- "
|
|
"result[%d](addr %d) still valid\n", resi, addr);
|
|
r = -1;
|
|
} else {
|
|
r = 0;
|
|
bcopy(&dma->test_dst, data, sizeof(dma->test_dst));
|
|
}
|
|
|
|
WRITE_REG_1(sc, HIFN_1_DMA_CSR,
|
|
HIFN_DMACSR_C_CTRL_DIS | HIFN_DMACSR_S_CTRL_DIS |
|
|
HIFN_DMACSR_D_CTRL_DIS | HIFN_DMACSR_R_CTRL_DIS);
|
|
|
|
return (r);
|
|
}
|
|
|
|
/*
|
|
* Initialize the descriptor rings.
|
|
*/
|
|
static void
|
|
hifn_init_dma(struct hifn_softc *sc)
|
|
{
|
|
struct hifn_dma *dma = sc->sc_dma;
|
|
int i;
|
|
|
|
hifn_set_retry(sc);
|
|
|
|
/* initialize static pointer values */
|
|
for (i = 0; i < HIFN_D_CMD_RSIZE; i++)
|
|
dma->cmdr[i].p = htole32(sc->sc_dma_physaddr +
|
|
offsetof(struct hifn_dma, command_bufs[i][0]));
|
|
for (i = 0; i < HIFN_D_RES_RSIZE; i++)
|
|
dma->resr[i].p = htole32(sc->sc_dma_physaddr +
|
|
offsetof(struct hifn_dma, result_bufs[i][0]));
|
|
|
|
dma->cmdr[HIFN_D_CMD_RSIZE].p =
|
|
htole32(sc->sc_dma_physaddr + offsetof(struct hifn_dma, cmdr[0]));
|
|
dma->srcr[HIFN_D_SRC_RSIZE].p =
|
|
htole32(sc->sc_dma_physaddr + offsetof(struct hifn_dma, srcr[0]));
|
|
dma->dstr[HIFN_D_DST_RSIZE].p =
|
|
htole32(sc->sc_dma_physaddr + offsetof(struct hifn_dma, dstr[0]));
|
|
dma->resr[HIFN_D_RES_RSIZE].p =
|
|
htole32(sc->sc_dma_physaddr + offsetof(struct hifn_dma, resr[0]));
|
|
|
|
sc->sc_cmdu = sc->sc_srcu = sc->sc_dstu = sc->sc_resu = 0;
|
|
sc->sc_cmdi = sc->sc_srci = sc->sc_dsti = sc->sc_resi = 0;
|
|
sc->sc_cmdk = sc->sc_srck = sc->sc_dstk = sc->sc_resk = 0;
|
|
}
|
|
|
|
/*
|
|
* Writes out the raw command buffer space. Returns the
|
|
* command buffer size.
|
|
*/
|
|
static u_int
|
|
hifn_write_command(struct hifn_command *cmd, u_int8_t *buf)
|
|
{
|
|
struct cryptop *crp;
|
|
u_int8_t *buf_pos;
|
|
hifn_base_command_t *base_cmd;
|
|
hifn_mac_command_t *mac_cmd;
|
|
hifn_crypt_command_t *cry_cmd;
|
|
int using_mac, using_crypt, ivlen;
|
|
u_int32_t dlen, slen;
|
|
|
|
crp = cmd->crp;
|
|
buf_pos = buf;
|
|
using_mac = cmd->base_masks & HIFN_BASE_CMD_MAC;
|
|
using_crypt = cmd->base_masks & HIFN_BASE_CMD_CRYPT;
|
|
|
|
base_cmd = (hifn_base_command_t *)buf_pos;
|
|
base_cmd->masks = htole16(cmd->base_masks);
|
|
slen = cmd->src_mapsize;
|
|
if (cmd->sloplen)
|
|
dlen = cmd->dst_mapsize - cmd->sloplen + sizeof(u_int32_t);
|
|
else
|
|
dlen = cmd->dst_mapsize;
|
|
base_cmd->total_source_count = htole16(slen & HIFN_BASE_CMD_LENMASK_LO);
|
|
base_cmd->total_dest_count = htole16(dlen & HIFN_BASE_CMD_LENMASK_LO);
|
|
dlen >>= 16;
|
|
slen >>= 16;
|
|
base_cmd->session_num = htole16(
|
|
((slen << HIFN_BASE_CMD_SRCLEN_S) & HIFN_BASE_CMD_SRCLEN_M) |
|
|
((dlen << HIFN_BASE_CMD_DSTLEN_S) & HIFN_BASE_CMD_DSTLEN_M));
|
|
buf_pos += sizeof(hifn_base_command_t);
|
|
|
|
if (using_mac) {
|
|
mac_cmd = (hifn_mac_command_t *)buf_pos;
|
|
dlen = crp->crp_aad_length + crp->crp_payload_length;
|
|
mac_cmd->source_count = htole16(dlen & 0xffff);
|
|
dlen >>= 16;
|
|
mac_cmd->masks = htole16(cmd->mac_masks |
|
|
((dlen << HIFN_MAC_CMD_SRCLEN_S) & HIFN_MAC_CMD_SRCLEN_M));
|
|
if (crp->crp_aad_length != 0)
|
|
mac_cmd->header_skip = htole16(crp->crp_aad_start);
|
|
else
|
|
mac_cmd->header_skip = htole16(crp->crp_payload_start);
|
|
mac_cmd->reserved = 0;
|
|
buf_pos += sizeof(hifn_mac_command_t);
|
|
}
|
|
|
|
if (using_crypt) {
|
|
cry_cmd = (hifn_crypt_command_t *)buf_pos;
|
|
dlen = crp->crp_payload_length;
|
|
cry_cmd->source_count = htole16(dlen & 0xffff);
|
|
dlen >>= 16;
|
|
cry_cmd->masks = htole16(cmd->cry_masks |
|
|
((dlen << HIFN_CRYPT_CMD_SRCLEN_S) & HIFN_CRYPT_CMD_SRCLEN_M));
|
|
cry_cmd->header_skip = htole16(crp->crp_payload_length);
|
|
cry_cmd->reserved = 0;
|
|
buf_pos += sizeof(hifn_crypt_command_t);
|
|
}
|
|
|
|
if (using_mac && cmd->mac_masks & HIFN_MAC_CMD_NEW_KEY) {
|
|
bcopy(cmd->mac, buf_pos, HIFN_MAC_KEY_LENGTH);
|
|
buf_pos += HIFN_MAC_KEY_LENGTH;
|
|
}
|
|
|
|
if (using_crypt && cmd->cry_masks & HIFN_CRYPT_CMD_NEW_KEY) {
|
|
switch (cmd->cry_masks & HIFN_CRYPT_CMD_ALG_MASK) {
|
|
case HIFN_CRYPT_CMD_ALG_AES:
|
|
/*
|
|
* AES keys are variable 128, 192 and
|
|
* 256 bits (16, 24 and 32 bytes).
|
|
*/
|
|
bcopy(cmd->ck, buf_pos, cmd->cklen);
|
|
buf_pos += cmd->cklen;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (using_crypt && cmd->cry_masks & HIFN_CRYPT_CMD_NEW_IV) {
|
|
switch (cmd->cry_masks & HIFN_CRYPT_CMD_ALG_MASK) {
|
|
case HIFN_CRYPT_CMD_ALG_AES:
|
|
ivlen = HIFN_AES_IV_LENGTH;
|
|
break;
|
|
default:
|
|
ivlen = HIFN_IV_LENGTH;
|
|
break;
|
|
}
|
|
bcopy(cmd->iv, buf_pos, ivlen);
|
|
buf_pos += ivlen;
|
|
}
|
|
|
|
if ((cmd->base_masks & (HIFN_BASE_CMD_MAC|HIFN_BASE_CMD_CRYPT)) == 0) {
|
|
bzero(buf_pos, 8);
|
|
buf_pos += 8;
|
|
}
|
|
|
|
return (buf_pos - buf);
|
|
}
|
|
|
|
static int
|
|
hifn_dmamap_aligned(struct hifn_operand *op)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < op->nsegs; i++) {
|
|
if (op->segs[i].ds_addr & 3)
|
|
return (0);
|
|
if ((i != (op->nsegs - 1)) && (op->segs[i].ds_len & 3))
|
|
return (0);
|
|
}
|
|
return (1);
|
|
}
|
|
|
|
static __inline int
|
|
hifn_dmamap_dstwrap(struct hifn_softc *sc, int idx)
|
|
{
|
|
struct hifn_dma *dma = sc->sc_dma;
|
|
|
|
if (++idx == HIFN_D_DST_RSIZE) {
|
|
dma->dstr[idx].l = htole32(HIFN_D_VALID | HIFN_D_JUMP |
|
|
HIFN_D_MASKDONEIRQ);
|
|
HIFN_DSTR_SYNC(sc, idx,
|
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
|
idx = 0;
|
|
}
|
|
return (idx);
|
|
}
|
|
|
|
static int
|
|
hifn_dmamap_load_dst(struct hifn_softc *sc, struct hifn_command *cmd)
|
|
{
|
|
struct hifn_dma *dma = sc->sc_dma;
|
|
struct hifn_operand *dst = &cmd->dst;
|
|
u_int32_t p, l;
|
|
int idx, used = 0, i;
|
|
|
|
idx = sc->sc_dsti;
|
|
for (i = 0; i < dst->nsegs - 1; i++) {
|
|
dma->dstr[idx].p = htole32(dst->segs[i].ds_addr);
|
|
dma->dstr[idx].l = htole32(HIFN_D_VALID |
|
|
HIFN_D_MASKDONEIRQ | dst->segs[i].ds_len);
|
|
HIFN_DSTR_SYNC(sc, idx,
|
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
|
used++;
|
|
|
|
idx = hifn_dmamap_dstwrap(sc, idx);
|
|
}
|
|
|
|
if (cmd->sloplen == 0) {
|
|
p = dst->segs[i].ds_addr;
|
|
l = HIFN_D_VALID | HIFN_D_MASKDONEIRQ | HIFN_D_LAST |
|
|
dst->segs[i].ds_len;
|
|
} else {
|
|
p = sc->sc_dma_physaddr +
|
|
offsetof(struct hifn_dma, slop[cmd->slopidx]);
|
|
l = HIFN_D_VALID | HIFN_D_MASKDONEIRQ | HIFN_D_LAST |
|
|
sizeof(u_int32_t);
|
|
|
|
if ((dst->segs[i].ds_len - cmd->sloplen) != 0) {
|
|
dma->dstr[idx].p = htole32(dst->segs[i].ds_addr);
|
|
dma->dstr[idx].l = htole32(HIFN_D_VALID |
|
|
HIFN_D_MASKDONEIRQ |
|
|
(dst->segs[i].ds_len - cmd->sloplen));
|
|
HIFN_DSTR_SYNC(sc, idx,
|
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
|
used++;
|
|
|
|
idx = hifn_dmamap_dstwrap(sc, idx);
|
|
}
|
|
}
|
|
dma->dstr[idx].p = htole32(p);
|
|
dma->dstr[idx].l = htole32(l);
|
|
HIFN_DSTR_SYNC(sc, idx, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
|
used++;
|
|
|
|
idx = hifn_dmamap_dstwrap(sc, idx);
|
|
|
|
sc->sc_dsti = idx;
|
|
sc->sc_dstu += used;
|
|
return (idx);
|
|
}
|
|
|
|
static __inline int
|
|
hifn_dmamap_srcwrap(struct hifn_softc *sc, int idx)
|
|
{
|
|
struct hifn_dma *dma = sc->sc_dma;
|
|
|
|
if (++idx == HIFN_D_SRC_RSIZE) {
|
|
dma->srcr[idx].l = htole32(HIFN_D_VALID |
|
|
HIFN_D_JUMP | HIFN_D_MASKDONEIRQ);
|
|
HIFN_SRCR_SYNC(sc, HIFN_D_SRC_RSIZE,
|
|
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
|
|
idx = 0;
|
|
}
|
|
return (idx);
|
|
}
|
|
|
|
static int
|
|
hifn_dmamap_load_src(struct hifn_softc *sc, struct hifn_command *cmd)
|
|
{
|
|
struct hifn_dma *dma = sc->sc_dma;
|
|
struct hifn_operand *src = &cmd->src;
|
|
int idx, i;
|
|
u_int32_t last = 0;
|
|
|
|
idx = sc->sc_srci;
|
|
for (i = 0; i < src->nsegs; i++) {
|
|
if (i == src->nsegs - 1)
|
|
last = HIFN_D_LAST;
|
|
|
|
dma->srcr[idx].p = htole32(src->segs[i].ds_addr);
|
|
dma->srcr[idx].l = htole32(src->segs[i].ds_len |
|
|
HIFN_D_VALID | HIFN_D_MASKDONEIRQ | last);
|
|
HIFN_SRCR_SYNC(sc, idx,
|
|
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
|
|
|
|
idx = hifn_dmamap_srcwrap(sc, idx);
|
|
}
|
|
sc->sc_srci = idx;
|
|
sc->sc_srcu += src->nsegs;
|
|
return (idx);
|
|
}
|
|
|
|
static void
|
|
hifn_op_cb(void* arg, bus_dma_segment_t *seg, int nsegs, int error)
|
|
{
|
|
struct hifn_operand *op = arg;
|
|
|
|
KASSERT(nsegs <= MAX_SCATTER,
|
|
("hifn_op_cb: too many DMA segments (%u > %u) "
|
|
"returned when mapping operand", nsegs, MAX_SCATTER));
|
|
op->nsegs = nsegs;
|
|
bcopy(seg, op->segs, nsegs * sizeof (seg[0]));
|
|
}
|
|
|
|
static int
|
|
hifn_crypto(
|
|
struct hifn_softc *sc,
|
|
struct hifn_command *cmd,
|
|
struct cryptop *crp,
|
|
int hint)
|
|
{
|
|
struct hifn_dma *dma = sc->sc_dma;
|
|
u_int32_t cmdlen, csr;
|
|
int cmdi, resi, err = 0;
|
|
|
|
/*
|
|
* need 1 cmd, and 1 res
|
|
*
|
|
* NB: check this first since it's easy.
|
|
*/
|
|
HIFN_LOCK(sc);
|
|
if ((sc->sc_cmdu + 1) > HIFN_D_CMD_RSIZE ||
|
|
(sc->sc_resu + 1) > HIFN_D_RES_RSIZE) {
|
|
#ifdef HIFN_DEBUG
|
|
if (hifn_debug) {
|
|
device_printf(sc->sc_dev,
|
|
"cmd/result exhaustion, cmdu %u resu %u\n",
|
|
sc->sc_cmdu, sc->sc_resu);
|
|
}
|
|
#endif
|
|
hifnstats.hst_nomem_cr++;
|
|
HIFN_UNLOCK(sc);
|
|
return (ERESTART);
|
|
}
|
|
|
|
if (bus_dmamap_create(sc->sc_dmat, BUS_DMA_NOWAIT, &cmd->src_map)) {
|
|
hifnstats.hst_nomem_map++;
|
|
HIFN_UNLOCK(sc);
|
|
return (ENOMEM);
|
|
}
|
|
|
|
if (bus_dmamap_load_crp(sc->sc_dmat, cmd->src_map, crp, hifn_op_cb,
|
|
&cmd->src, BUS_DMA_NOWAIT)) {
|
|
hifnstats.hst_nomem_load++;
|
|
err = ENOMEM;
|
|
goto err_srcmap1;
|
|
}
|
|
cmd->src_mapsize = crypto_buffer_len(&crp->crp_buf);
|
|
|
|
if (hifn_dmamap_aligned(&cmd->src)) {
|
|
cmd->sloplen = cmd->src_mapsize & 3;
|
|
cmd->dst = cmd->src;
|
|
} else if (crp->crp_buf.cb_type == CRYPTO_BUF_MBUF) {
|
|
int totlen, len;
|
|
struct mbuf *m, *m0, *mlast;
|
|
|
|
KASSERT(cmd->dst_m == NULL,
|
|
("hifn_crypto: dst_m initialized improperly"));
|
|
hifnstats.hst_unaligned++;
|
|
|
|
/*
|
|
* Source is not aligned on a longword boundary.
|
|
* Copy the data to insure alignment. If we fail
|
|
* to allocate mbufs or clusters while doing this
|
|
* we return ERESTART so the operation is requeued
|
|
* at the crypto later, but only if there are
|
|
* ops already posted to the hardware; otherwise we
|
|
* have no guarantee that we'll be re-entered.
|
|
*/
|
|
totlen = cmd->src_mapsize;
|
|
if (crp->crp_buf.cb_mbuf->m_flags & M_PKTHDR) {
|
|
len = MHLEN;
|
|
MGETHDR(m0, M_NOWAIT, MT_DATA);
|
|
if (m0 && !m_dup_pkthdr(m0, crp->crp_buf.cb_mbuf,
|
|
M_NOWAIT)) {
|
|
m_free(m0);
|
|
m0 = NULL;
|
|
}
|
|
} else {
|
|
len = MLEN;
|
|
MGET(m0, M_NOWAIT, MT_DATA);
|
|
}
|
|
if (m0 == NULL) {
|
|
hifnstats.hst_nomem_mbuf++;
|
|
err = sc->sc_cmdu ? ERESTART : ENOMEM;
|
|
goto err_srcmap;
|
|
}
|
|
if (totlen >= MINCLSIZE) {
|
|
if (!(MCLGET(m0, M_NOWAIT))) {
|
|
hifnstats.hst_nomem_mcl++;
|
|
err = sc->sc_cmdu ? ERESTART : ENOMEM;
|
|
m_freem(m0);
|
|
goto err_srcmap;
|
|
}
|
|
len = MCLBYTES;
|
|
}
|
|
totlen -= len;
|
|
m0->m_pkthdr.len = m0->m_len = len;
|
|
mlast = m0;
|
|
|
|
while (totlen > 0) {
|
|
MGET(m, M_NOWAIT, MT_DATA);
|
|
if (m == NULL) {
|
|
hifnstats.hst_nomem_mbuf++;
|
|
err = sc->sc_cmdu ? ERESTART : ENOMEM;
|
|
m_freem(m0);
|
|
goto err_srcmap;
|
|
}
|
|
len = MLEN;
|
|
if (totlen >= MINCLSIZE) {
|
|
if (!(MCLGET(m, M_NOWAIT))) {
|
|
hifnstats.hst_nomem_mcl++;
|
|
err = sc->sc_cmdu ? ERESTART : ENOMEM;
|
|
mlast->m_next = m;
|
|
m_freem(m0);
|
|
goto err_srcmap;
|
|
}
|
|
len = MCLBYTES;
|
|
}
|
|
|
|
m->m_len = len;
|
|
m0->m_pkthdr.len += len;
|
|
totlen -= len;
|
|
|
|
mlast->m_next = m;
|
|
mlast = m;
|
|
}
|
|
cmd->dst_m = m0;
|
|
|
|
if (bus_dmamap_create(sc->sc_dmat, BUS_DMA_NOWAIT,
|
|
&cmd->dst_map)) {
|
|
hifnstats.hst_nomem_map++;
|
|
err = ENOMEM;
|
|
goto err_srcmap;
|
|
}
|
|
|
|
if (bus_dmamap_load_mbuf_sg(sc->sc_dmat, cmd->dst_map, m0,
|
|
cmd->dst_segs, &cmd->dst_nsegs, 0)) {
|
|
hifnstats.hst_nomem_map++;
|
|
err = ENOMEM;
|
|
goto err_dstmap1;
|
|
}
|
|
cmd->dst_mapsize = m0->m_pkthdr.len;
|
|
} else {
|
|
err = EINVAL;
|
|
goto err_srcmap;
|
|
}
|
|
|
|
#ifdef HIFN_DEBUG
|
|
if (hifn_debug) {
|
|
device_printf(sc->sc_dev,
|
|
"Entering cmd: stat %8x ien %8x u %d/%d/%d/%d n %d/%d\n",
|
|
READ_REG_1(sc, HIFN_1_DMA_CSR),
|
|
READ_REG_1(sc, HIFN_1_DMA_IER),
|
|
sc->sc_cmdu, sc->sc_srcu, sc->sc_dstu, sc->sc_resu,
|
|
cmd->src_nsegs, cmd->dst_nsegs);
|
|
}
|
|
#endif
|
|
|
|
if (cmd->src_map == cmd->dst_map) {
|
|
bus_dmamap_sync(sc->sc_dmat, cmd->src_map,
|
|
BUS_DMASYNC_PREWRITE|BUS_DMASYNC_PREREAD);
|
|
} else {
|
|
bus_dmamap_sync(sc->sc_dmat, cmd->src_map,
|
|
BUS_DMASYNC_PREWRITE);
|
|
bus_dmamap_sync(sc->sc_dmat, cmd->dst_map,
|
|
BUS_DMASYNC_PREREAD);
|
|
}
|
|
|
|
/*
|
|
* need N src, and N dst
|
|
*/
|
|
if ((sc->sc_srcu + cmd->src_nsegs) > HIFN_D_SRC_RSIZE ||
|
|
(sc->sc_dstu + cmd->dst_nsegs + 1) > HIFN_D_DST_RSIZE) {
|
|
#ifdef HIFN_DEBUG
|
|
if (hifn_debug) {
|
|
device_printf(sc->sc_dev,
|
|
"src/dst exhaustion, srcu %u+%u dstu %u+%u\n",
|
|
sc->sc_srcu, cmd->src_nsegs,
|
|
sc->sc_dstu, cmd->dst_nsegs);
|
|
}
|
|
#endif
|
|
hifnstats.hst_nomem_sd++;
|
|
err = ERESTART;
|
|
goto err_dstmap;
|
|
}
|
|
|
|
if (sc->sc_cmdi == HIFN_D_CMD_RSIZE) {
|
|
sc->sc_cmdi = 0;
|
|
dma->cmdr[HIFN_D_CMD_RSIZE].l = htole32(HIFN_D_VALID |
|
|
HIFN_D_JUMP | HIFN_D_MASKDONEIRQ);
|
|
HIFN_CMDR_SYNC(sc, HIFN_D_CMD_RSIZE,
|
|
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
|
|
}
|
|
cmdi = sc->sc_cmdi++;
|
|
cmdlen = hifn_write_command(cmd, dma->command_bufs[cmdi]);
|
|
HIFN_CMD_SYNC(sc, cmdi, BUS_DMASYNC_PREWRITE);
|
|
|
|
/* .p for command/result already set */
|
|
dma->cmdr[cmdi].l = htole32(cmdlen | HIFN_D_VALID | HIFN_D_LAST |
|
|
HIFN_D_MASKDONEIRQ);
|
|
HIFN_CMDR_SYNC(sc, cmdi,
|
|
BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
|
|
sc->sc_cmdu++;
|
|
|
|
/*
|
|
* We don't worry about missing an interrupt (which a "command wait"
|
|
* interrupt salvages us from), unless there is more than one command
|
|
* in the queue.
|
|
*/
|
|
if (sc->sc_cmdu > 1) {
|
|
sc->sc_dmaier |= HIFN_DMAIER_C_WAIT;
|
|
WRITE_REG_1(sc, HIFN_1_DMA_IER, sc->sc_dmaier);
|
|
}
|
|
|
|
hifnstats.hst_ipackets++;
|
|
hifnstats.hst_ibytes += cmd->src_mapsize;
|
|
|
|
hifn_dmamap_load_src(sc, cmd);
|
|
|
|
/*
|
|
* Unlike other descriptors, we don't mask done interrupt from
|
|
* result descriptor.
|
|
*/
|
|
#ifdef HIFN_DEBUG
|
|
if (hifn_debug)
|
|
printf("load res\n");
|
|
#endif
|
|
if (sc->sc_resi == HIFN_D_RES_RSIZE) {
|
|
sc->sc_resi = 0;
|
|
dma->resr[HIFN_D_RES_RSIZE].l = htole32(HIFN_D_VALID |
|
|
HIFN_D_JUMP | HIFN_D_MASKDONEIRQ);
|
|
HIFN_RESR_SYNC(sc, HIFN_D_RES_RSIZE,
|
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
|
}
|
|
resi = sc->sc_resi++;
|
|
KASSERT(sc->sc_hifn_commands[resi] == NULL,
|
|
("hifn_crypto: command slot %u busy", resi));
|
|
sc->sc_hifn_commands[resi] = cmd;
|
|
HIFN_RES_SYNC(sc, resi, BUS_DMASYNC_PREREAD);
|
|
if ((hint & CRYPTO_HINT_MORE) && sc->sc_curbatch < hifn_maxbatch) {
|
|
dma->resr[resi].l = htole32(HIFN_MAX_RESULT |
|
|
HIFN_D_VALID | HIFN_D_LAST | HIFN_D_MASKDONEIRQ);
|
|
sc->sc_curbatch++;
|
|
if (sc->sc_curbatch > hifnstats.hst_maxbatch)
|
|
hifnstats.hst_maxbatch = sc->sc_curbatch;
|
|
hifnstats.hst_totbatch++;
|
|
} else {
|
|
dma->resr[resi].l = htole32(HIFN_MAX_RESULT |
|
|
HIFN_D_VALID | HIFN_D_LAST);
|
|
sc->sc_curbatch = 0;
|
|
}
|
|
HIFN_RESR_SYNC(sc, resi,
|
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
|
sc->sc_resu++;
|
|
|
|
if (cmd->sloplen)
|
|
cmd->slopidx = resi;
|
|
|
|
hifn_dmamap_load_dst(sc, cmd);
|
|
|
|
csr = 0;
|
|
if (sc->sc_c_busy == 0) {
|
|
csr |= HIFN_DMACSR_C_CTRL_ENA;
|
|
sc->sc_c_busy = 1;
|
|
}
|
|
if (sc->sc_s_busy == 0) {
|
|
csr |= HIFN_DMACSR_S_CTRL_ENA;
|
|
sc->sc_s_busy = 1;
|
|
}
|
|
if (sc->sc_r_busy == 0) {
|
|
csr |= HIFN_DMACSR_R_CTRL_ENA;
|
|
sc->sc_r_busy = 1;
|
|
}
|
|
if (sc->sc_d_busy == 0) {
|
|
csr |= HIFN_DMACSR_D_CTRL_ENA;
|
|
sc->sc_d_busy = 1;
|
|
}
|
|
if (csr)
|
|
WRITE_REG_1(sc, HIFN_1_DMA_CSR, csr);
|
|
|
|
#ifdef HIFN_DEBUG
|
|
if (hifn_debug) {
|
|
device_printf(sc->sc_dev, "command: stat %8x ier %8x\n",
|
|
READ_REG_1(sc, HIFN_1_DMA_CSR),
|
|
READ_REG_1(sc, HIFN_1_DMA_IER));
|
|
}
|
|
#endif
|
|
|
|
sc->sc_active = 5;
|
|
HIFN_UNLOCK(sc);
|
|
KASSERT(err == 0, ("hifn_crypto: success with error %u", err));
|
|
return (err); /* success */
|
|
|
|
err_dstmap:
|
|
if (cmd->src_map != cmd->dst_map)
|
|
bus_dmamap_unload(sc->sc_dmat, cmd->dst_map);
|
|
err_dstmap1:
|
|
if (cmd->src_map != cmd->dst_map)
|
|
bus_dmamap_destroy(sc->sc_dmat, cmd->dst_map);
|
|
err_srcmap:
|
|
if (crp->crp_buf.cb_type == CRYPTO_BUF_MBUF) {
|
|
if (cmd->dst_m != NULL)
|
|
m_freem(cmd->dst_m);
|
|
}
|
|
bus_dmamap_unload(sc->sc_dmat, cmd->src_map);
|
|
err_srcmap1:
|
|
bus_dmamap_destroy(sc->sc_dmat, cmd->src_map);
|
|
HIFN_UNLOCK(sc);
|
|
return (err);
|
|
}
|
|
|
|
static void
|
|
hifn_tick(void* vsc)
|
|
{
|
|
struct hifn_softc *sc = vsc;
|
|
|
|
HIFN_LOCK(sc);
|
|
if (sc->sc_active == 0) {
|
|
u_int32_t r = 0;
|
|
|
|
if (sc->sc_cmdu == 0 && sc->sc_c_busy) {
|
|
sc->sc_c_busy = 0;
|
|
r |= HIFN_DMACSR_C_CTRL_DIS;
|
|
}
|
|
if (sc->sc_srcu == 0 && sc->sc_s_busy) {
|
|
sc->sc_s_busy = 0;
|
|
r |= HIFN_DMACSR_S_CTRL_DIS;
|
|
}
|
|
if (sc->sc_dstu == 0 && sc->sc_d_busy) {
|
|
sc->sc_d_busy = 0;
|
|
r |= HIFN_DMACSR_D_CTRL_DIS;
|
|
}
|
|
if (sc->sc_resu == 0 && sc->sc_r_busy) {
|
|
sc->sc_r_busy = 0;
|
|
r |= HIFN_DMACSR_R_CTRL_DIS;
|
|
}
|
|
if (r)
|
|
WRITE_REG_1(sc, HIFN_1_DMA_CSR, r);
|
|
} else
|
|
sc->sc_active--;
|
|
HIFN_UNLOCK(sc);
|
|
callout_reset(&sc->sc_tickto, hz, hifn_tick, sc);
|
|
}
|
|
|
|
static void
|
|
hifn_intr(void *arg)
|
|
{
|
|
struct hifn_softc *sc = arg;
|
|
struct hifn_dma *dma;
|
|
u_int32_t dmacsr, restart;
|
|
int i, u;
|
|
|
|
dmacsr = READ_REG_1(sc, HIFN_1_DMA_CSR);
|
|
|
|
/* Nothing in the DMA unit interrupted */
|
|
if ((dmacsr & sc->sc_dmaier) == 0)
|
|
return;
|
|
|
|
HIFN_LOCK(sc);
|
|
|
|
dma = sc->sc_dma;
|
|
|
|
#ifdef HIFN_DEBUG
|
|
if (hifn_debug) {
|
|
device_printf(sc->sc_dev,
|
|
"irq: stat %08x ien %08x damier %08x i %d/%d/%d/%d k %d/%d/%d/%d u %d/%d/%d/%d\n",
|
|
dmacsr, READ_REG_1(sc, HIFN_1_DMA_IER), sc->sc_dmaier,
|
|
sc->sc_cmdi, sc->sc_srci, sc->sc_dsti, sc->sc_resi,
|
|
sc->sc_cmdk, sc->sc_srck, sc->sc_dstk, sc->sc_resk,
|
|
sc->sc_cmdu, sc->sc_srcu, sc->sc_dstu, sc->sc_resu);
|
|
}
|
|
#endif
|
|
|
|
WRITE_REG_1(sc, HIFN_1_DMA_CSR, dmacsr & sc->sc_dmaier);
|
|
|
|
if ((sc->sc_flags & HIFN_HAS_PUBLIC) &&
|
|
(dmacsr & HIFN_DMACSR_PUBDONE))
|
|
WRITE_REG_1(sc, HIFN_1_PUB_STATUS,
|
|
READ_REG_1(sc, HIFN_1_PUB_STATUS) | HIFN_PUBSTS_DONE);
|
|
|
|
restart = dmacsr & (HIFN_DMACSR_D_OVER | HIFN_DMACSR_R_OVER);
|
|
if (restart)
|
|
device_printf(sc->sc_dev, "overrun %x\n", dmacsr);
|
|
|
|
if (sc->sc_flags & HIFN_IS_7811) {
|
|
if (dmacsr & HIFN_DMACSR_ILLR)
|
|
device_printf(sc->sc_dev, "illegal read\n");
|
|
if (dmacsr & HIFN_DMACSR_ILLW)
|
|
device_printf(sc->sc_dev, "illegal write\n");
|
|
}
|
|
|
|
restart = dmacsr & (HIFN_DMACSR_C_ABORT | HIFN_DMACSR_S_ABORT |
|
|
HIFN_DMACSR_D_ABORT | HIFN_DMACSR_R_ABORT);
|
|
if (restart) {
|
|
device_printf(sc->sc_dev, "abort, resetting.\n");
|
|
hifnstats.hst_abort++;
|
|
hifn_abort(sc);
|
|
HIFN_UNLOCK(sc);
|
|
return;
|
|
}
|
|
|
|
if ((dmacsr & HIFN_DMACSR_C_WAIT) && (sc->sc_cmdu == 0)) {
|
|
/*
|
|
* If no slots to process and we receive a "waiting on
|
|
* command" interrupt, we disable the "waiting on command"
|
|
* (by clearing it).
|
|
*/
|
|
sc->sc_dmaier &= ~HIFN_DMAIER_C_WAIT;
|
|
WRITE_REG_1(sc, HIFN_1_DMA_IER, sc->sc_dmaier);
|
|
}
|
|
|
|
/* clear the rings */
|
|
i = sc->sc_resk; u = sc->sc_resu;
|
|
while (u != 0) {
|
|
HIFN_RESR_SYNC(sc, i,
|
|
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
|
|
if (dma->resr[i].l & htole32(HIFN_D_VALID)) {
|
|
HIFN_RESR_SYNC(sc, i,
|
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
|
break;
|
|
}
|
|
|
|
if (i != HIFN_D_RES_RSIZE) {
|
|
struct hifn_command *cmd;
|
|
u_int8_t *macbuf = NULL;
|
|
|
|
HIFN_RES_SYNC(sc, i, BUS_DMASYNC_POSTREAD);
|
|
cmd = sc->sc_hifn_commands[i];
|
|
KASSERT(cmd != NULL,
|
|
("hifn_intr: null command slot %u", i));
|
|
sc->sc_hifn_commands[i] = NULL;
|
|
|
|
if (cmd->base_masks & HIFN_BASE_CMD_MAC) {
|
|
macbuf = dma->result_bufs[i];
|
|
macbuf += 12;
|
|
}
|
|
|
|
hifn_callback(sc, cmd, macbuf);
|
|
hifnstats.hst_opackets++;
|
|
u--;
|
|
}
|
|
|
|
if (++i == (HIFN_D_RES_RSIZE + 1))
|
|
i = 0;
|
|
}
|
|
sc->sc_resk = i; sc->sc_resu = u;
|
|
|
|
i = sc->sc_srck; u = sc->sc_srcu;
|
|
while (u != 0) {
|
|
if (i == HIFN_D_SRC_RSIZE)
|
|
i = 0;
|
|
HIFN_SRCR_SYNC(sc, i,
|
|
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
|
|
if (dma->srcr[i].l & htole32(HIFN_D_VALID)) {
|
|
HIFN_SRCR_SYNC(sc, i,
|
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
|
break;
|
|
}
|
|
i++, u--;
|
|
}
|
|
sc->sc_srck = i; sc->sc_srcu = u;
|
|
|
|
i = sc->sc_cmdk; u = sc->sc_cmdu;
|
|
while (u != 0) {
|
|
HIFN_CMDR_SYNC(sc, i,
|
|
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
|
|
if (dma->cmdr[i].l & htole32(HIFN_D_VALID)) {
|
|
HIFN_CMDR_SYNC(sc, i,
|
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
|
break;
|
|
}
|
|
if (i != HIFN_D_CMD_RSIZE) {
|
|
u--;
|
|
HIFN_CMD_SYNC(sc, i, BUS_DMASYNC_POSTWRITE);
|
|
}
|
|
if (++i == (HIFN_D_CMD_RSIZE + 1))
|
|
i = 0;
|
|
}
|
|
sc->sc_cmdk = i; sc->sc_cmdu = u;
|
|
|
|
HIFN_UNLOCK(sc);
|
|
|
|
if (sc->sc_needwakeup) { /* XXX check high watermark */
|
|
int wakeup = sc->sc_needwakeup & (CRYPTO_SYMQ|CRYPTO_ASYMQ);
|
|
#ifdef HIFN_DEBUG
|
|
if (hifn_debug)
|
|
device_printf(sc->sc_dev,
|
|
"wakeup crypto (%x) u %d/%d/%d/%d\n",
|
|
sc->sc_needwakeup,
|
|
sc->sc_cmdu, sc->sc_srcu, sc->sc_dstu, sc->sc_resu);
|
|
#endif
|
|
sc->sc_needwakeup &= ~wakeup;
|
|
crypto_unblock(sc->sc_cid, wakeup);
|
|
}
|
|
}
|
|
|
|
static bool
|
|
hifn_auth_supported(struct hifn_softc *sc,
|
|
const struct crypto_session_params *csp)
|
|
{
|
|
|
|
switch (sc->sc_ena) {
|
|
case HIFN_PUSTAT_ENA_2:
|
|
case HIFN_PUSTAT_ENA_1:
|
|
break;
|
|
default:
|
|
return (false);
|
|
}
|
|
|
|
switch (csp->csp_auth_alg) {
|
|
case CRYPTO_SHA1:
|
|
break;
|
|
case CRYPTO_SHA1_HMAC:
|
|
if (csp->csp_auth_klen > HIFN_MAC_KEY_LENGTH)
|
|
return (false);
|
|
break;
|
|
default:
|
|
return (false);
|
|
}
|
|
|
|
return (true);
|
|
}
|
|
|
|
static bool
|
|
hifn_cipher_supported(struct hifn_softc *sc,
|
|
const struct crypto_session_params *csp)
|
|
{
|
|
|
|
if (csp->csp_cipher_klen == 0)
|
|
return (false);
|
|
if (csp->csp_ivlen > HIFN_MAX_IV_LENGTH)
|
|
return (false);
|
|
switch (sc->sc_ena) {
|
|
case HIFN_PUSTAT_ENA_2:
|
|
switch (csp->csp_cipher_alg) {
|
|
case CRYPTO_AES_CBC:
|
|
if ((sc->sc_flags & HIFN_HAS_AES) == 0)
|
|
return (false);
|
|
switch (csp->csp_cipher_klen) {
|
|
case 128:
|
|
case 192:
|
|
case 256:
|
|
break;
|
|
default:
|
|
return (false);
|
|
}
|
|
return (true);
|
|
}
|
|
}
|
|
return (false);
|
|
}
|
|
|
|
static int
|
|
hifn_probesession(device_t dev, const struct crypto_session_params *csp)
|
|
{
|
|
struct hifn_softc *sc;
|
|
|
|
sc = device_get_softc(dev);
|
|
if (csp->csp_flags != 0)
|
|
return (EINVAL);
|
|
switch (csp->csp_mode) {
|
|
case CSP_MODE_DIGEST:
|
|
if (!hifn_auth_supported(sc, csp))
|
|
return (EINVAL);
|
|
break;
|
|
case CSP_MODE_CIPHER:
|
|
if (!hifn_cipher_supported(sc, csp))
|
|
return (EINVAL);
|
|
break;
|
|
case CSP_MODE_ETA:
|
|
if (!hifn_auth_supported(sc, csp) ||
|
|
!hifn_cipher_supported(sc, csp))
|
|
return (EINVAL);
|
|
break;
|
|
default:
|
|
return (EINVAL);
|
|
}
|
|
|
|
return (CRYPTODEV_PROBE_HARDWARE);
|
|
}
|
|
|
|
/*
|
|
* Allocate a new 'session'.
|
|
*/
|
|
static int
|
|
hifn_newsession(device_t dev, crypto_session_t cses,
|
|
const struct crypto_session_params *csp)
|
|
{
|
|
struct hifn_session *ses;
|
|
|
|
ses = crypto_get_driver_session(cses);
|
|
|
|
if (csp->csp_auth_alg != 0) {
|
|
if (csp->csp_auth_mlen == 0)
|
|
ses->hs_mlen = crypto_auth_hash(csp)->hashsize;
|
|
else
|
|
ses->hs_mlen = csp->csp_auth_mlen;
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*
|
|
* XXX freesession routine should run a zero'd mac/encrypt key into context
|
|
* ram. to blow away any keys already stored there.
|
|
*/
|
|
|
|
static int
|
|
hifn_process(device_t dev, struct cryptop *crp, int hint)
|
|
{
|
|
const struct crypto_session_params *csp;
|
|
struct hifn_softc *sc = device_get_softc(dev);
|
|
struct hifn_command *cmd = NULL;
|
|
const void *mackey;
|
|
int err, keylen;
|
|
struct hifn_session *ses;
|
|
|
|
ses = crypto_get_driver_session(crp->crp_session);
|
|
|
|
cmd = malloc(sizeof(struct hifn_command), M_DEVBUF, M_NOWAIT | M_ZERO);
|
|
if (cmd == NULL) {
|
|
hifnstats.hst_nomem++;
|
|
err = ENOMEM;
|
|
goto errout;
|
|
}
|
|
|
|
csp = crypto_get_params(crp->crp_session);
|
|
|
|
/*
|
|
* The driver only supports ETA requests where there is no
|
|
* gap between the AAD and payload.
|
|
*/
|
|
if (csp->csp_mode == CSP_MODE_ETA && crp->crp_aad_length != 0 &&
|
|
crp->crp_aad_start + crp->crp_aad_length !=
|
|
crp->crp_payload_start) {
|
|
err = EINVAL;
|
|
goto errout;
|
|
}
|
|
|
|
switch (csp->csp_mode) {
|
|
case CSP_MODE_CIPHER:
|
|
case CSP_MODE_ETA:
|
|
if (!CRYPTO_OP_IS_ENCRYPT(crp->crp_op))
|
|
cmd->base_masks |= HIFN_BASE_CMD_DECODE;
|
|
cmd->base_masks |= HIFN_BASE_CMD_CRYPT;
|
|
switch (csp->csp_cipher_alg) {
|
|
case CRYPTO_AES_CBC:
|
|
cmd->cry_masks |= HIFN_CRYPT_CMD_ALG_AES |
|
|
HIFN_CRYPT_CMD_MODE_CBC |
|
|
HIFN_CRYPT_CMD_NEW_IV;
|
|
break;
|
|
default:
|
|
err = EINVAL;
|
|
goto errout;
|
|
}
|
|
crypto_read_iv(crp, cmd->iv);
|
|
|
|
if (crp->crp_cipher_key != NULL)
|
|
cmd->ck = crp->crp_cipher_key;
|
|
else
|
|
cmd->ck = csp->csp_cipher_key;
|
|
cmd->cklen = csp->csp_cipher_klen;
|
|
cmd->cry_masks |= HIFN_CRYPT_CMD_NEW_KEY;
|
|
|
|
/*
|
|
* Need to specify the size for the AES key in the masks.
|
|
*/
|
|
if ((cmd->cry_masks & HIFN_CRYPT_CMD_ALG_MASK) ==
|
|
HIFN_CRYPT_CMD_ALG_AES) {
|
|
switch (cmd->cklen) {
|
|
case 16:
|
|
cmd->cry_masks |= HIFN_CRYPT_CMD_KSZ_128;
|
|
break;
|
|
case 24:
|
|
cmd->cry_masks |= HIFN_CRYPT_CMD_KSZ_192;
|
|
break;
|
|
case 32:
|
|
cmd->cry_masks |= HIFN_CRYPT_CMD_KSZ_256;
|
|
break;
|
|
default:
|
|
err = EINVAL;
|
|
goto errout;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
switch (csp->csp_mode) {
|
|
case CSP_MODE_DIGEST:
|
|
case CSP_MODE_ETA:
|
|
cmd->base_masks |= HIFN_BASE_CMD_MAC;
|
|
|
|
switch (csp->csp_auth_alg) {
|
|
case CRYPTO_SHA1:
|
|
cmd->mac_masks |= HIFN_MAC_CMD_ALG_SHA1 |
|
|
HIFN_MAC_CMD_RESULT | HIFN_MAC_CMD_MODE_HASH |
|
|
HIFN_MAC_CMD_POS_IPSEC;
|
|
break;
|
|
case CRYPTO_SHA1_HMAC:
|
|
cmd->mac_masks |= HIFN_MAC_CMD_ALG_SHA1 |
|
|
HIFN_MAC_CMD_RESULT | HIFN_MAC_CMD_MODE_HMAC |
|
|
HIFN_MAC_CMD_POS_IPSEC | HIFN_MAC_CMD_TRUNC;
|
|
break;
|
|
}
|
|
|
|
if (csp->csp_auth_alg == CRYPTO_SHA1_HMAC) {
|
|
cmd->mac_masks |= HIFN_MAC_CMD_NEW_KEY;
|
|
if (crp->crp_auth_key != NULL)
|
|
mackey = crp->crp_auth_key;
|
|
else
|
|
mackey = csp->csp_auth_key;
|
|
keylen = csp->csp_auth_klen;
|
|
bcopy(mackey, cmd->mac, keylen);
|
|
bzero(cmd->mac + keylen, HIFN_MAC_KEY_LENGTH - keylen);
|
|
}
|
|
}
|
|
|
|
cmd->crp = crp;
|
|
cmd->session = ses;
|
|
cmd->softc = sc;
|
|
|
|
err = hifn_crypto(sc, cmd, crp, hint);
|
|
if (!err) {
|
|
return 0;
|
|
} else if (err == ERESTART) {
|
|
/*
|
|
* There weren't enough resources to dispatch the request
|
|
* to the part. Notify the caller so they'll requeue this
|
|
* request and resubmit it again soon.
|
|
*/
|
|
#ifdef HIFN_DEBUG
|
|
if (hifn_debug)
|
|
device_printf(sc->sc_dev, "requeue request\n");
|
|
#endif
|
|
free(cmd, M_DEVBUF);
|
|
sc->sc_needwakeup |= CRYPTO_SYMQ;
|
|
return (err);
|
|
}
|
|
|
|
errout:
|
|
if (cmd != NULL)
|
|
free(cmd, M_DEVBUF);
|
|
if (err == EINVAL)
|
|
hifnstats.hst_invalid++;
|
|
else
|
|
hifnstats.hst_nomem++;
|
|
crp->crp_etype = err;
|
|
crypto_done(crp);
|
|
return (err);
|
|
}
|
|
|
|
static void
|
|
hifn_abort(struct hifn_softc *sc)
|
|
{
|
|
struct hifn_dma *dma = sc->sc_dma;
|
|
struct hifn_command *cmd;
|
|
struct cryptop *crp;
|
|
int i, u;
|
|
|
|
i = sc->sc_resk; u = sc->sc_resu;
|
|
while (u != 0) {
|
|
cmd = sc->sc_hifn_commands[i];
|
|
KASSERT(cmd != NULL, ("hifn_abort: null command slot %u", i));
|
|
sc->sc_hifn_commands[i] = NULL;
|
|
crp = cmd->crp;
|
|
|
|
if ((dma->resr[i].l & htole32(HIFN_D_VALID)) == 0) {
|
|
/* Salvage what we can. */
|
|
u_int8_t *macbuf;
|
|
|
|
if (cmd->base_masks & HIFN_BASE_CMD_MAC) {
|
|
macbuf = dma->result_bufs[i];
|
|
macbuf += 12;
|
|
} else
|
|
macbuf = NULL;
|
|
hifnstats.hst_opackets++;
|
|
hifn_callback(sc, cmd, macbuf);
|
|
} else {
|
|
if (cmd->src_map == cmd->dst_map) {
|
|
bus_dmamap_sync(sc->sc_dmat, cmd->src_map,
|
|
BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE);
|
|
} else {
|
|
bus_dmamap_sync(sc->sc_dmat, cmd->src_map,
|
|
BUS_DMASYNC_POSTWRITE);
|
|
bus_dmamap_sync(sc->sc_dmat, cmd->dst_map,
|
|
BUS_DMASYNC_POSTREAD);
|
|
}
|
|
|
|
if (cmd->dst_m != NULL) {
|
|
m_freem(cmd->dst_m);
|
|
}
|
|
|
|
/* non-shared buffers cannot be restarted */
|
|
if (cmd->src_map != cmd->dst_map) {
|
|
/*
|
|
* XXX should be EAGAIN, delayed until
|
|
* after the reset.
|
|
*/
|
|
crp->crp_etype = ENOMEM;
|
|
bus_dmamap_unload(sc->sc_dmat, cmd->dst_map);
|
|
bus_dmamap_destroy(sc->sc_dmat, cmd->dst_map);
|
|
} else
|
|
crp->crp_etype = ENOMEM;
|
|
|
|
bus_dmamap_unload(sc->sc_dmat, cmd->src_map);
|
|
bus_dmamap_destroy(sc->sc_dmat, cmd->src_map);
|
|
|
|
free(cmd, M_DEVBUF);
|
|
if (crp->crp_etype != EAGAIN)
|
|
crypto_done(crp);
|
|
}
|
|
|
|
if (++i == HIFN_D_RES_RSIZE)
|
|
i = 0;
|
|
u--;
|
|
}
|
|
sc->sc_resk = i; sc->sc_resu = u;
|
|
|
|
hifn_reset_board(sc, 1);
|
|
hifn_init_dma(sc);
|
|
hifn_init_pci_registers(sc);
|
|
}
|
|
|
|
static void
|
|
hifn_callback(struct hifn_softc *sc, struct hifn_command *cmd, u_int8_t *macbuf)
|
|
{
|
|
struct hifn_dma *dma = sc->sc_dma;
|
|
struct cryptop *crp = cmd->crp;
|
|
uint8_t macbuf2[SHA1_HASH_LEN];
|
|
struct mbuf *m;
|
|
int totlen, i, u;
|
|
|
|
if (cmd->src_map == cmd->dst_map) {
|
|
bus_dmamap_sync(sc->sc_dmat, cmd->src_map,
|
|
BUS_DMASYNC_POSTWRITE | BUS_DMASYNC_POSTREAD);
|
|
} else {
|
|
bus_dmamap_sync(sc->sc_dmat, cmd->src_map,
|
|
BUS_DMASYNC_POSTWRITE);
|
|
bus_dmamap_sync(sc->sc_dmat, cmd->dst_map,
|
|
BUS_DMASYNC_POSTREAD);
|
|
}
|
|
|
|
if (crp->crp_buf.cb_type == CRYPTO_BUF_MBUF) {
|
|
if (cmd->dst_m != NULL) {
|
|
totlen = cmd->src_mapsize;
|
|
for (m = cmd->dst_m; m != NULL; m = m->m_next) {
|
|
if (totlen < m->m_len) {
|
|
m->m_len = totlen;
|
|
totlen = 0;
|
|
} else
|
|
totlen -= m->m_len;
|
|
}
|
|
cmd->dst_m->m_pkthdr.len =
|
|
crp->crp_buf.cb_mbuf->m_pkthdr.len;
|
|
m_freem(crp->crp_buf.cb_mbuf);
|
|
crp->crp_buf.cb_mbuf = cmd->dst_m;
|
|
}
|
|
}
|
|
|
|
if (cmd->sloplen != 0) {
|
|
crypto_copyback(crp, cmd->src_mapsize - cmd->sloplen,
|
|
cmd->sloplen, &dma->slop[cmd->slopidx]);
|
|
}
|
|
|
|
i = sc->sc_dstk; u = sc->sc_dstu;
|
|
while (u != 0) {
|
|
if (i == HIFN_D_DST_RSIZE)
|
|
i = 0;
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
|
|
BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
|
|
if (dma->dstr[i].l & htole32(HIFN_D_VALID)) {
|
|
bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap,
|
|
BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
|
|
break;
|
|
}
|
|
i++, u--;
|
|
}
|
|
sc->sc_dstk = i; sc->sc_dstu = u;
|
|
|
|
hifnstats.hst_obytes += cmd->dst_mapsize;
|
|
|
|
if (macbuf != NULL) {
|
|
if (crp->crp_op & CRYPTO_OP_VERIFY_DIGEST) {
|
|
crypto_copydata(crp, crp->crp_digest_start,
|
|
cmd->session->hs_mlen, macbuf2);
|
|
if (timingsafe_bcmp(macbuf, macbuf2,
|
|
cmd->session->hs_mlen) != 0)
|
|
crp->crp_etype = EBADMSG;
|
|
} else
|
|
crypto_copyback(crp, crp->crp_digest_start,
|
|
cmd->session->hs_mlen, macbuf);
|
|
}
|
|
|
|
if (cmd->src_map != cmd->dst_map) {
|
|
bus_dmamap_unload(sc->sc_dmat, cmd->dst_map);
|
|
bus_dmamap_destroy(sc->sc_dmat, cmd->dst_map);
|
|
}
|
|
bus_dmamap_unload(sc->sc_dmat, cmd->src_map);
|
|
bus_dmamap_destroy(sc->sc_dmat, cmd->src_map);
|
|
free(cmd, M_DEVBUF);
|
|
crypto_done(crp);
|
|
}
|
|
|
|
/*
|
|
* 7811 PB3 rev/2 parts lock-up on burst writes to Group 0
|
|
* and Group 1 registers; avoid conditions that could create
|
|
* burst writes by doing a read in between the writes.
|
|
*
|
|
* NB: The read we interpose is always to the same register;
|
|
* we do this because reading from an arbitrary (e.g. last)
|
|
* register may not always work.
|
|
*/
|
|
static void
|
|
hifn_write_reg_0(struct hifn_softc *sc, bus_size_t reg, u_int32_t val)
|
|
{
|
|
if (sc->sc_flags & HIFN_IS_7811) {
|
|
if (sc->sc_bar0_lastreg == reg - 4)
|
|
bus_space_read_4(sc->sc_st0, sc->sc_sh0, HIFN_0_PUCNFG);
|
|
sc->sc_bar0_lastreg = reg;
|
|
}
|
|
bus_space_write_4(sc->sc_st0, sc->sc_sh0, reg, val);
|
|
}
|
|
|
|
static void
|
|
hifn_write_reg_1(struct hifn_softc *sc, bus_size_t reg, u_int32_t val)
|
|
{
|
|
if (sc->sc_flags & HIFN_IS_7811) {
|
|
if (sc->sc_bar1_lastreg == reg - 4)
|
|
bus_space_read_4(sc->sc_st1, sc->sc_sh1, HIFN_1_REVID);
|
|
sc->sc_bar1_lastreg = reg;
|
|
}
|
|
bus_space_write_4(sc->sc_st1, sc->sc_sh1, reg, val);
|
|
}
|
|
|
|
#ifdef HIFN_VULCANDEV
|
|
/*
|
|
* this code provides support for mapping the PK engine's register
|
|
* into a userspace program.
|
|
*
|
|
*/
|
|
static int
|
|
vulcanpk_mmap(struct cdev *dev, vm_ooffset_t offset,
|
|
vm_paddr_t *paddr, int nprot, vm_memattr_t *memattr)
|
|
{
|
|
struct hifn_softc *sc;
|
|
vm_paddr_t pd;
|
|
void *b;
|
|
|
|
sc = dev->si_drv1;
|
|
|
|
pd = rman_get_start(sc->sc_bar1res);
|
|
b = rman_get_virtual(sc->sc_bar1res);
|
|
|
|
#if 0
|
|
printf("vpk mmap: %p(%016llx) offset=%lld\n", b,
|
|
(unsigned long long)pd, offset);
|
|
hexdump(b, HIFN_1_PUB_MEMEND, "vpk", 0);
|
|
#endif
|
|
|
|
if (offset == 0) {
|
|
*paddr = pd;
|
|
return (0);
|
|
}
|
|
return (-1);
|
|
}
|
|
|
|
static struct cdevsw vulcanpk_cdevsw = {
|
|
.d_version = D_VERSION,
|
|
.d_mmap = vulcanpk_mmap,
|
|
.d_name = "vulcanpk",
|
|
};
|
|
#endif /* HIFN_VULCANDEV */
|