Project

General

Profile

Actions

Bug #790

closed

newer HIFN support 7955/7956

Added by andrew_atrens over 14 years ago. Updated over 12 years ago.

Status:
Closed
Priority:
Low
Assignee:
-
Category:
-
Target version:
-
Start date:
Due date:
% Done:

0%

Estimated time:

Description

more stuff from my tree.. originally from FreeBSD/OpenBSD - it's had 18+ months of soak .. hopefully
I haven't missed any dependencies :(

Index: cryptodev.h ===================================================================
RCS file: /usr/dfly/repo/src/sys/opencrypto/cryptodev.h,v
retrieving revision 1.2
diff u -r1.2 cryptodev.h
--
cryptodev.h 17 Jun 2003 04:28:54 -0000 1.2
++ cryptodev.h 11 Apr 2007 03:41:59 -0000
@ -229,6 +229,8 @
#define CRD_F_IV_EXPLICIT 0x04 /* IV explicitly provided /
#define CRD_F_DSA_SHA_NEEDED 0x08 /
Compute SHA-1 of buffer for DSA /
#define CRD_F_COMP 0x0f /
Set when doing compression */
#define CRD_F_KEY_EXPLICIT 0x10
+ ===================================================================
RCS file: /usr/dfly/repo/src/sys/opencrypto/cryptosoft.c,v
retrieving revision 1.5
diff u -r1.5 cryptosoft.c
--
cryptosoft.c 5 Sep 2006 03:48:13 -0000 1.5
+++ cryptosoft.c 11 Apr 2007 03:41:59 -0000
@ -154,6 +154,16 @
}
}

struct cryptoini        CRD_INI; /* Initialization/context data */
#define crd_iv CRD_INI.cri_iv
Index: cryptosoft.c

+ if (crd->crd_flags & CRD_F_KEY_EXPLICIT) {
+ int error;

if (sw->sw_kschedule)
+ exf->zerokey(&(sw->sw_kschedule));
+ error = exf->setkey(&sw->sw_kschedule,
+ crd->crd_key, crd->crd_klen / 8);
+ if (error)
+ return (error);
+ }
ivp = iv;

if (outtype  CRYPTO_BUF_CONTIG) {

Index: hifn7751.c
=================================================================
RCS file: /usr/dfly/repo/src/sys/dev/crypto/hifn/hifn7751.c,v
retrieving revision 1.13
diff u -r1.13 hifn7751.c
--
hifn7751.c 22 Dec 2006 23:26:15 -0000 1.13
+++ hifn7751.c 11 Apr 2007 03:41:10 -0000
@ -2,12 +2,13 @
/* $DragonFly: src/sys/dev/crypto/hifn/hifn7751.c,v 1.13 2006/12/22 23:26:15 swildner Exp $ /
/
$OpenBSD: hifn7751.c,v 1.120 2002/05/17 00:33:34 deraadt Exp $ */

/*
+/*
* 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
@ -43,7 +44,7 @
*/

/*
- * Driver for the Hifn 7751 encryption processor.
+ * Driver for various Hifn encryption processors.
*/
#include "opt_hifn.h"

@ -60,6 +61,9 @
#include <sys/random.h>
#include <sys/thread2.h>

#include <sys/bus.h>
#include <sys/uio.h>
+
#include <vm/vm.h>
#include <vm/pmap.h>

@ -194,11 +198,17 @
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 (0);
if (pci_get_vendor(dev) PCI_VENDOR_NETSEC &&
pci_get_device(dev) PCI_PRODUCT_NETSEC_7751)
return (0);
+ if (pci_get_vendor(dev) == PCI_VENDOR_HIFN) {
+ device_printf(dev,"device id = 0x%x\n", pci_get_device(dev) );
+ return (0);
+ }
return (ENXIO);
}

@ -220,6 +230,8 @
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:
@ -244,6 +256,84 @
add_true_randomness(*p++);
}

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.
+ */
static void
+hifn_getpllconfig(device_t dev, u_int *pll)
{
+ 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 = "pci66";
+ 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) x%x~ 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.
/
@ -264,11 +354,13 @
/
XXX handle power management */

/*
- * The 7951 has a random number generator and
+ * 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_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
@ -279,6 +371,21 @
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);
+ }

/
* Configure support for memory-mapped access to * registers and for DMA operations.
*/
@ -368,10 +475,10 @
sc->sc_dma = (struct hifn_dma *)kva;
bzero(sc->sc_dma, sizeof(*sc->sc_dma));

- KASSERT);
- KASSERT);
- KASSERT);
- KASSERT);
+ KASSERT);
+ KASSERT);
+ KASSERT);
+ KASSERT);

/*
 * Reset the board and do the ``secret handshake''
@ -392,7 +499,10 @
hifn_init_dma(sc);
hifn_init_pci_registers(sc);

- if (hifn_ramtype(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)
@ -423,7 +533,7 @ * 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, 0,
+ if (bus_setup_intr(dev, sc->sc_irq, INTR_FAST,
hifn_intr, sc,
&sc->sc_intrhand, NULL)) {
device_printf(dev, "could not setup interrupt\n");
@ -448,6 +558,12 @
hifn_partname(sc), rev,
rseg, rbase, sc->sc_drammodel ? 'd' : 's',
sc->sc_maxses);
+ if (sc->sc_flags & HIFN_IS_7956)
+ kprintf(", pll=0x%x<%s clk, ux mult>",
+ sc->sc_pllconfig,
+ sc->sc_pllconfig x%x
HIFN_PLL_REF_SEL ? "ext" : "pci",
+ 2 + 2
((sc->sc_pllconfig & HIFN_PLL_ND) >> 11));
+ kprintf("\n");
sc->sc_cid = crypto_get_driverid(0);
if (sc->sc_cid < 0) {
@ -465,6 +581,10 @
hifn_newsession, hifn_freesession, hifn_process, sc);
crypto_register(sc->sc_cid, CRYPTO_ARC4, 0, 0,
hifn_newsession, hifn_freesession, hifn_process, sc);
+ if (sc->sc_flags & HIFN_HAS_AES)
+ crypto_register(sc->sc_cid, CRYPTO_AES_CBC, 0, 0,
+ hifn_newsession, hifn_freesession,
+ hifn_process, sc);
/*FALLTHROUGH*/
case HIFN_PUSTAT_ENA_1:
crypto_register(sc->sc_cid, CRYPTO_MD5, 0, 0,
@ -524,6 +644,9 @
KASSERT(sc != NULL, ("hifn_detach: null software carrier!"));

+ /* disable interrupts */
+ WRITE_REG_1(sc, HIFN_1_DMA_IER, 0);
+
crit_enter();
/*XXX other resources */
callout_stop(&sc->sc_tickto);
@ -875,6 +998,16 @ { 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,
@ -1055,10 +1188,36 @
sc->sc_dmaier &= ~HIFN_DMAIER_C_WAIT;
WRITE_REG_1(sc, HIFN_1_DMA_IER, sc->sc_dmaier);

- 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));

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;
+ WRITE_REG_1(sc, HIFN_1_PLL, pll);
+ DELAY; /
10ms /
+ /
change configuration /
+ pll = (pll &~ HIFN_PLL_CONFIG) | sc->sc_pllconfig;
+ WRITE_REG_1(sc, HIFN_1_PLL, pll);
+ DELAY; /
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 |
@ -1087,8 +1246,14 @
ctxsize = 128;
else
ctxsize = 512;
- sc->sc_maxses = 1
- ((sc->sc_ramsize - 32768) / ctxsize);
/*
+ * 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;

@ -1175,9 +1340,16 @ {
u_int32_t cnfg;

- cnfg = READ_REG_0(sc, HIFN_0_PUCNFG) &
- HIFN_PUCNFG_DRAMMASK;
- sc->sc_ramsize = 1 << ((cnfg >> 13) + 18);
+ 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);
}

@ -1393,7 +1565,7 @
hifn_base_command_t *base_cmd;
hifn_mac_command_t *mac_cmd;
hifn_crypt_command_t *cry_cmd;
- int using_mac, using_crypt, len;
+ int using_mac, using_crypt, len, ivlen;
u_int32_t dlen, slen;

buf_pos = buf;
@ -1411,7 +1583,11 @
base_cmd->total_dest_count = htole16(dlen & HIFN_BASE_CMD_LENMASK_LO);
dlen >>= 16;
slen >>= 16;
#if 0
base_cmd->session_num = htole16(cmd->session_num |
#else
+ base_cmd->session_num = htole16(
#endif
((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);
@ -1453,7 +1629,7 @
break;
case HIFN_CRYPT_CMD_ALG_DES:
bcopy(cmd->ck, buf_pos, HIFN_DES_KEY_LENGTH);
- buf_pos += cmd->cklen;
buf_pos = HIFN_DES_KEY_LENGTH;
break;
case HIFN_CRYPT_CMD_ALG_RC4:
len = 256;
@ -1468,12 +1644,28 @
bzero(buf_pos, 4);
buf_pos += 4;
break;
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) {
- bcopy(cmd->iv, buf_pos, HIFN_IV_LENGTH);
- buf_pos = HIFN_IV_LENGTH;
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) {
@ -1658,6 +1850,7 @
goto err_srcmap1;
}
} else if (crp->crp_flags & CRYPTO_F_IOV) {
+ cmd->src_io->uio_segflg = UIO_USERSPACE;
if (bus_dmamap_load_uio(sc->sc_dmat, cmd->src_map,
cmd->src_io, hifn_op_cb, &cmd->src, BUS_DMA_NOWAIT)) {
hifnstats.hst_nomem_load++;
@ -1769,6 +1962,7 @
goto err_dstmap1;
}
} else if (crp->crp_flags & CRYPTO_F_IOV) {
+ cmd->dst_io->uio_segflg |= UIO_USERSPACE;
if (bus_dmamap_load_uio(sc->sc_dmat, cmd->dst_map,
cmd->dst_io, hifn_op_cb, &cmd->dst, BUS_DMA_NOWAIT)) {
hifnstats.hst_nomem_load++;
@ -1979,10 +2173,16 @
u_int32_t dmacsr, restart;
int i, u;

- dma = sc->sc_dma;
-
dmacsr = READ_REG_1(sc, HIFN_1_DMA_CSR);

+ /* Nothing in the DMA unit interrupted */
+ if ((dmacsr & sc->sc_dmaier) == 0) {
+ hifnstats.hst_noirq++;
+ return;
+ }

dma = sc->sc_dma;
+
#ifdef HIFN_DEBUG
if (hifn_debug) {
device_printf(sc->sc_dev,
@ -1994,12 +2194,6 @
}
#endif

- /* Nothing in the DMA unit interrupted */
- if ((dmacsr & sc->sc_dmaier) == 0) {
- hifnstats.hst_noirq++;
- return;
- }

WRITE_REG_1(sc, HIFN_1_DMA_CSR, dmacsr & sc
>sc_dmaier);

if ((sc->sc_flags & HIFN_HAS_PUBLIC) &&
@ -2130,17 +2324,44 @ {
struct cryptoini *c;
struct hifn_softc *sc = arg;
- int i, mac = 0, cry = 0;
+ int mac = 0, cry = 0, sesn;
+ struct hifn_session *ses = NULL;
KASSERT(sc != NULL, ("hifn_newsession: null softc"));
if (sidp NULL || cri NULL || sc == NULL)
return (EINVAL);

- for (i = 0; i < sc->sc_maxses; i++)
- if (sc->sc_sessions[i].hs_state HS_STATE_FREE)
- break;
- if (i sc->sc_maxses)
- return (ENOMEM);
+ if (sc->sc_sessions == NULL) {
+ ses = sc->sc_sessions = (struct hifn_session *)kmalloc(
+ sizeof(*ses), M_DEVBUF, M_NOWAIT);
+ if (ses == NULL)
+ return (ENOMEM);
+ sesn = 0;
+ sc->sc_nsessions = 1;
+ } else {
+ for (sesn = 0; sesn < sc->sc_nsessions; sesn++) {
+ if (!sc->sc_sessions[sesn].hs_used) {
+ ses = &sc->sc_sessions[sesn];
+ break;
+ }
+ }

if (ses == NULL) {
+ sesn = sc->sc_nsessions;
+ ses = (struct hifn_session *)kmalloc((sesn + 1) *
+ sizeof(*ses), M_DEVBUF, M_NOWAIT);
+ if (ses == NULL)
+ return (ENOMEM);
+ bcopy(sc->sc_sessions, ses, sesn * sizeof(*ses));
+ bzero(sc->sc_sessions, sesn * sizeof(*ses));
+ kfree(sc->sc_sessions, M_DEVBUF);
+ sc->sc_sessions = ses;
+ ses = &sc->sc_sessions[sesn];
+ sc->sc_nsessions++;
+ }
+ }
+ bzero(ses, sizeof(*ses));
+ ses->hs_used = 1;

for (c = cri; c != NULL; c = c->cri_next) {
switch (c->cri_alg) {
@ -2154,8 +2375,11 @
break;
case CRYPTO_DES_CBC:
case CRYPTO_3DES_CBC:
+ case CRYPTO_AES_CBC:
/* XXX this may read fewer, does it matter? /
- read_random(sc->sc_sessions[i].hs_iv, HIFN_IV_LENGTH);
+ read_random(ses->hs_iv,
+ c->cri_alg CRYPTO_AES_CBC ?
+ HIFN_AES_IV_LENGTH : HIFN_IV_LENGTH);
/*FALLTHROUGH
/
case CRYPTO_ARC4:
if (cry)
@ -2169,8 +2393,7 @
if (mac 0 && cry == 0)
return (EINVAL);

- *sidp = HIFN_SID(device_get_unit(sc->sc_dev), i);
- sc->sc_sessions[i].hs_state = HS_STATE_USED;
+ *sidp = HIFN_SID(device_get_unit(sc->sc_dev), sesn);

return (0);
}
@ -2180,19 +2403,21 @ * XXX this routine should run a zero'd mac/encrypt key into context ram. * XXX to blow away any keys already stored there.
*/
#define CRYPTO_SESID2LID(_sid) (((u_int32_t) (_sid)) & 0xffffffff)

static int
hifn_freesession(void *arg, u_int64_t tid) {
struct hifn_softc *sc = arg;
int session;
- u_int32_t sid = ((u_int32_t) tid) & 0xffffffff;
+ u_int32_t sid = CRYPTO_SESID2LID(tid); {
struct hifn_softc *sc = arg;
struct hifn_command *cmd = NULL;
- int session, err;
+ int session, err, ivlen;
struct cryptodesc *crd1, *crd2, *maccrd, *enccrd;
if (crp  NULL || crp->crp_callback  NULL) {
@ -2213,12 +2438,19 @
}
session = HIFN_SESSION(crp->crp_sid);

- if (sc == NULL || session >= sc->sc_maxses) {
+ if (sc == NULL || session >= sc->sc_nsessions) {
+
err = EINVAL;
goto errout;
}

cmd = kmalloc(sizeof(struct hifn_command), M_DEVBUF, M_INTWAIT | M_ZERO);
+ if (cmd == NULL) {
+ hifnstats.hst_nomem++;
+ err = ENOMEM;
+ goto errout;
+ }
+
if (crp->crp_flags & CRYPTO_F_IMBUF) {
cmd->src_m = (struct mbuf *)crp->crp_buf;
@ -2247,6 +2479,7 @
enccrd = NULL;
} else if (crd1->crd_alg CRYPTO_DES_CBC ||
crd1->crd_alg CRYPTO_3DES_CBC ||
+ crd1->crd_alg CRYPTO_AES_CBC ||
crd1->crd_alg CRYPTO_ARC4) {
if ((crd1->crd_flags & CRD_F_ENCRYPT) == 0)
cmd->base_masks |= HIFN_BASE_CMD_DECODE;
@ -2263,6 +2496,7 @
crd1->crd_alg CRYPTO_SHA1) &&
(crd2->crd_alg CRYPTO_DES_CBC ||
crd2->crd_alg CRYPTO_3DES_CBC ||
+ crd2->crd_alg CRYPTO_AES_CBC ||
crd2->crd_alg CRYPTO_ARC4) &&
((crd2->crd_flags & CRD_F_ENCRYPT) 0)) {
cmd->base_masks = HIFN_BASE_CMD_DECODE;
@ -2270,7 +2504,8 @
enccrd = crd2;
} else if ((crd1->crd_alg CRYPTO_DES_CBC ||
crd1->crd_alg CRYPTO_ARC4 ||
- crd1->crd_alg CRYPTO_3DES_CBC) &&
+ crd1->crd_alg CRYPTO_3DES_CBC ||
+ crd1->crd_alg CRYPTO_AES_CBC) &&
(crd2->crd_alg CRYPTO_MD5_HMAC ||
crd2->crd_alg CRYPTO_SHA1_HMAC ||
crd2->crd_alg CRYPTO_MD5 ||
@ -2293,10 +2528,6 @
switch (enccrd->crd_alg) {
case CRYPTO_ARC4:
cmd->cry_masks |= HIFN_CRYPT_CMD_ALG_RC4;
- if ((enccrd->crd_flags & CRD_F_ENCRYPT)
- != sc->sc_sessions[session].hs_prev_op)
- sc->sc_sessions[session].hs_state =
- HS_STATE_USED;
break;
case CRYPTO_DES_CBC:
cmd->cry_masks |= HIFN_CRYPT_CMD_ALG_DES |
@ -2308,50 +2539,74 @
HIFN_CRYPT_CMD_MODE_CBC |
HIFN_CRYPT_CMD_NEW_IV;
break;
+ 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;
}
if (enccrd->crd_alg != CRYPTO_ARC4) {
+ ivlen = ((enccrd->crd_alg CRYPTO_AES_CBC) ?
+ HIFN_AES_IV_LENGTH : HIFN_IV_LENGTH);
if (enccrd->crd_flags & CRD_F_ENCRYPT) {
if (enccrd->crd_flags & CRD_F_IV_EXPLICIT)
- bcopy(enccrd->crd_iv, cmd->iv,
- HIFN_IV_LENGTH);
+ bcopy(enccrd->crd_iv, cmd->iv, ivlen);
else
bcopy(sc->sc_sessions[session].hs_iv,
- cmd->iv, HIFN_IV_LENGTH);
+ cmd->iv, ivlen);
if ((enccrd->crd_flags & CRD_F_IV_PRESENT)
0) {
if (crp->crp_flags & CRYPTO_F_IMBUF)
m_copyback(cmd->src_m,
enccrd->crd_inject,
- HIFN_IV_LENGTH, cmd->iv);
+ ivlen, cmd->iv);
else if (crp->crp_flags & CRYPTO_F_IOV)
cuio_copyback(cmd->src_io,
enccrd->crd_inject,
- HIFN_IV_LENGTH, cmd->iv);
+ ivlen, cmd->iv);
}
} else {
if (enccrd->crd_flags & CRD_F_IV_EXPLICIT)
- bcopy(enccrd->crd_iv, cmd->iv,
- HIFN_IV_LENGTH);
+ bcopy(enccrd->crd_iv, cmd->iv, ivlen);
else if (crp->crp_flags & CRYPTO_F_IMBUF)
m_copydata(cmd->src_m,
- enccrd->crd_inject,
- HIFN_IV_LENGTH, cmd->iv);
+ enccrd->crd_inject, ivlen, cmd->iv);
else if (crp->crp_flags & CRYPTO_F_IOV)
cuio_copydata(cmd->src_io,
- enccrd->crd_inject,
- HIFN_IV_LENGTH, cmd->iv);
+ enccrd->crd_inject, ivlen, cmd->iv);
}
}

+ if (enccrd->crd_flags & CRD_F_KEY_EXPLICIT)
+ cmd->cry_masks |= HIFN_CRYPT_CMD_NEW_KEY;
cmd->ck = enccrd->crd_key;
cmd->cklen = enccrd->crd_klen >> 3;
+ cmd->cry_masks |= HIFN_CRYPT_CMD_NEW_KEY;

- if (sc->sc_sessions[session].hs_state == HS_STATE_USED)
- 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;
+ }
+ }
}

if (maccrd) {
@ -2381,9 +2636,8 @
break;
}

- if ((maccrd->crd_alg CRYPTO_SHA1_HMAC ||
- maccrd->crd_alg CRYPTO_MD5_HMAC) &&
- sc->sc_sessions[session].hs_state HS_STATE_USED) {
+ if (maccrd->crd_alg CRYPTO_SHA1_HMAC ||
+ maccrd->crd_alg == CRYPTO_MD5_HMAC) {
cmd->mac_masks |= HIFN_MAC_CMD_NEW_KEY;
bcopy(maccrd->crd_key, cmd->mac, maccrd->crd_klen >> 3);
bzero(cmd->mac + (maccrd->crd_klen >> 3),
@ -2397,11 +2651,6 @

err = hifn_crypto(sc, cmd, crp, hint);
if (!err) {
- if (enccrd)
- sc->sc_sessions[session].hs_prev_op =
- enccrd->crd_flags & CRD_F_ENCRYPT;
- if (sc->sc_sessions[session].hs_state == HS_STATE_USED)
- sc->sc_sessions[session].hs_state = HS_STATE_KEY;
return 0;
} else if (err == ERESTART) {
/*
@ -2498,11 +2747,6 @
}
dma->resk = i; dma->resu = u;

- /* Force upload of key next time */
- for (i = 0; i < sc->sc_maxses; i++)
- if (sc->sc_sessions[i].hs_state == HS_STATE_KEY)
- sc->sc_sessions[i].hs_state = HS_STATE_USED;
-
hifn_reset_board(sc, 1);
hifn_init_dma(sc);
hifn_init_pci_registers(sc);
@ -2515,7 +2759,7 @
struct cryptop *crp = cmd->crp;
struct cryptodesc *crd;
struct mbuf *m;
- int totlen, i, u;
+ int totlen, i, u, ivlen;

if (cmd->src_map == cmd->dst_map) {
bus_dmamap_sync(sc->sc_dmat, cmd->src_map,
@ -2575,17 +2819,18 @
HIFN_BASE_CMD_CRYPT) {
for (crd = crp->crp_desc; crd; crd = crd->crd_next) {
if (crd->crd_alg != CRYPTO_DES_CBC &&
- crd->crd_alg != CRYPTO_3DES_CBC)
+ crd->crd_alg != CRYPTO_3DES_CBC &&
+ crd->crd_alg != CRYPTO_AES_CBC)
continue;
+ ivlen = ((crd->crd_alg CRYPTO_AES_CBC) ?
+ HIFN_AES_IV_LENGTH : HIFN_IV_LENGTH);
if (crp->crp_flags & CRYPTO_F_IMBUF)
m_copydata((struct mbuf )crp->crp_buf,
- crd->crd_skip + crd->crd_len - HIFN_IV_LENGTH,
- HIFN_IV_LENGTH,
+ crd->crd_skip + crd->crd_len - ivlen, ivlen,
cmd->softc->sc_sessions[cmd->session_num].hs_iv);
else if (crp->crp_flags & CRYPTO_F_IOV) {
cuio_copydata((struct uio *)crp->crp_buf,
- crd->crd_skip + crd->crd_len - HIFN_IV_LENGTH,
- HIFN_IV_LENGTH,
+ crd->crd_skip + crd->crd_len - ivlen, ivlen,
cmd->softc->sc_sessions[cmd->session_num].hs_iv);
}
break;
Index: hifn7751reg.h
=================================================================
RCS file: /usr/dfly/repo/src/sys/dev/crypto/hifn/hifn7751reg.h,v
retrieving revision 1.2
diff u -r1.2 hifn7751reg.h
--
hifn7751reg.h 17 Jun 2003 04:28:27 -0000 1.2
+++ hifn7751reg.h 11 Apr 2007 03:41:10 -0000
@ -2,7 +2,7 @
/
$DragonFly: src/sys/dev/crypto/hifn/hifn7751reg.h,v 1.2 2003/06/17 04:28:27 dillon Exp $ /
/
$OpenBSD: hifn7751reg.h,v 1.35 2002/04/08 17:49:42 jason Exp $ */

/*
+/*
* Invertex AEON / Hifn 7751 driver * Copyright (c) 1999 Invertex Inc. All rights reserved. * Copyright (c) 1999 Theo de Raadt
@ -50,8 +50,13 @ * Some PCI configuration space offset defines. The names were made * identical to the names used by the Linux kernel.
/
#ifdef notyet
#define HIFN_BAR0 (PCIR_MAPS+0x0) /
PUC register map /
#define HIFN_BAR1 (PCIR_MAPS+0x4) /
DMA register map /
#else
#define HIFN_BAR0 PCIR_BAR(0) /
PUC register map /
#define HIFN_BAR1 PCIR_BAR(1) /
DMA register map */
+#endif
#define HIFN_TRDY_TIMEOUT 0x40
#define HIFN_RETRY_TIMEOUT 0x41

@ -64,6 +69,8 @
#define PCI_PRODUCT_HIFN_6500 0x0006 /* 6500 /
#define PCI_PRODUCT_HIFN_7811 0x0007 /
7811 /
#define PCI_PRODUCT_HIFN_7951 0x0012 /
7951 /
#define PCI_PRODUCT_HIFN_7955 0x0020 /
7954/7955 /
#define PCI_PRODUCT_HIFN_7956 0x001d /
7956 */

#define    PCI_VENDOR_INVERTEX    0x14e1        /* Invertex /
#define PCI_PRODUCT_INVERTEX_AEON 0x0005 /
AEON /
@ -211,6 +218,7 @
#define HIFN_1_DMA_CSR 0x40 /
DMA Status and Control /
#define HIFN_1_DMA_IER 0x44 /
DMA Interrupt Enable /
#define HIFN_1_DMA_CNFG 0x48 /
DMA Configuration /
+#define HIFN_1_PLL 0x4c /
7955/7956: PLL config /
#define HIFN_1_7811_RNGENA 0x60 /
7811: rng enable /
#define HIFN_1_7811_RNGCFG 0x64 /
7811: rng config /
#define HIFN_1_7811_RNGDAT 0x68 /
7811: rng data */
@ -370,6 +378,41 @
#define HIFN_UNLOCK_SECRET1 0xf4
#define HIFN_UNLOCK_SECRET2 0xfc

/*
* PLL config register
+
+ * This register is present only on 7954/7955/7956 parts. It must be
+ * programmed according to the bus interface method used by the h/w.
+ * Note that the parts require a stable clock. Since the PCI clock
+ * may vary the reference clock must usually be used. To avoid
+ * overclocking the core logic, setup must be done carefully, refer
+ * to the driver for details. The exact multiplier required varies
+ * by part and system configuration; refer to the Hifn documentation.
+ */
#define HIFN_PLL_REF_SEL 0x00000001 /
REF/HBI clk selection /
#define HIFN_PLL_BP 0x00000002 /
bypass (used during setup) /
/
bit 2 reserved /
#define HIFN_PLL_PK_CLK_SEL 0x00000008 /
public key clk select /
#define HIFN_PLL_PE_CLK_SEL 0x00000010 /
packet engine clk select /
/
bits 5-9 reserved /
#define HIFN_PLL_MBSET 0x00000400 /
must be set to 1 /
#define HIFN_PLL_ND 0x00003800 /
Fpll_ref multiplier select /
#define HIFN_PLL_ND_SHIFT 11
#define HIFN_PLL_ND_2 0x00000000 /
2x /
#define HIFN_PLL_ND_4 0x00000800 /
4x /
#define HIFN_PLL_ND_6 0x00001000 /
6x /
#define HIFN_PLL_ND_8 0x00001800 /
8x /
#define HIFN_PLL_ND_10 0x00002000 /
10x /
#define HIFN_PLL_ND_12 0x00002800 /
12x /
/
bits 14-15 reserved /
#define HIFN_PLL_IS 0x00010000 /
charge pump current select /
/
bits 17-31 reserved /

/

+ * Board configuration specifies only these bits.
+ /
#define HIFN_PLL_CONFIG (HIFN_PLL_IS|HIFN_PLL_ND|HIFN_PLL_REF_SEL)

/
******************************************************************** * Structs for board commands *
@ -385,6 +428,8 @
volatile u_int16_t total_dest_count;
} hifn_base_command_t;

#define HIFN_BASE_CMD_COMP 0x0100 /* enable compression engine /
#define HIFN_BASE_CMD_PAD 0x0200 / enable padding engine /
#define HIFN_BASE_CMD_MAC 0x0400
#define HIFN_BASE_CMD_CRYPT 0x0800
#define HIFN_BASE_CMD_DECODE 0x2000
@ -409,7 +454,8 @
#define HIFN_CRYPT_CMD_ALG_DES 0x0000 /
DES /
#define HIFN_CRYPT_CMD_ALG_3DES 0x0001 /
3DES /
#define HIFN_CRYPT_CMD_ALG_RC4 0x0002 /
RC4 /
-#define HIFN_CRYPT_CMD_MODE_MASK 0x0018 /
DES mode: /
#define HIFN_CRYPT_CMD_ALG_AES 0x0003 /
AES /
#define HIFN_CRYPT_CMD_MODE_MASK 0x0018 /
Encrypt/DES mode: /
#define HIFN_CRYPT_CMD_MODE_ECB 0x0000 /
ECB /
#define HIFN_CRYPT_CMD_MODE_CBC 0x0008 /
CBC /
#define HIFN_CRYPT_CMD_MODE_CFB 0x0010 /
CFB */
@ -421,6 +467,11 @
#define HIFN_CRYPT_CMD_SRCLEN_M 0xc000
#define HIFN_CRYPT_CMD_SRCLEN_S 14

#define HIFN_CRYPT_CMD_KSZ_MASK 0x0600 /* AES key size: /
#define HIFN_CRYPT_CMD_KSZ_128 0x0000 / 128 bit /
#define HIFN_CRYPT_CMD_KSZ_192 0x0200 /
192 bit /
#define HIFN_CRYPT_CMD_KSZ_256 0x0400 /
256 bit /
+
/
* Structure to help build up the command data structure.
*/
@ -452,6 +503,64 @
#define HIFN_MAC_CMD_POS_IPSEC 0x0200
#define HIFN_MAC_CMD_NEW_KEY 0x0800

struct hifn_comp_command {
volatile u_int16_t masks;
+ volatile u_int16_t header_skip;
+ volatile u_int16_t source_count;
+ volatile u_int16_t reserved;
};

#define HIFN_COMP_CMD_SRCLEN_M 0xc000
#define HIFN_COMP_CMD_SRCLEN_S 14
#define HIFN_COMP_CMD_ONE 0x0100 /* must be one /
#define HIFN_COMP_CMD_CLEARHIST 0x0010 / clear history /
#define HIFN_COMP_CMD_UPDATEHIST 0x0008 /
update history /
#define HIFN_COMP_CMD_LZS_STRIP0 0x0004 /
LZS: strip zero /
#define HIFN_COMP_CMD_MPPC_RESTART 0x0004 /
MPPC: restart /
#define HIFN_COMP_CMD_ALG_MASK 0x0001 /
compression mode: /
#define HIFN_COMP_CMD_ALG_MPPC 0x0001 /
MPPC /
#define HIFN_COMP_CMD_ALG_LZS 0x0000 /
LZS /

+struct hifn_base_result {
volatile u_int16_t flags;
+ volatile u_int16_t session;
+ volatile u_int16_t src_cnt; /
15:0 of source count /
+ volatile u_int16_t dst_cnt; /
15:0 of dest count /
};

#define HIFN_BASE_RES_DSTOVERRUN 0x0200 /
destination overrun /
#define HIFN_BASE_RES_SRCLEN_M 0xc000 /
17:16 of source count /
#define HIFN_BASE_RES_SRCLEN_S 14
#define HIFN_BASE_RES_DSTLEN_M 0x3000 /
17:16 of dest count /
#define HIFN_BASE_RES_DSTLEN_S 12

struct hifn_comp_result {
volatile u_int16_t flags;
+ volatile u_int16_t crc;
};

#define HIFN_COMP_RES_LCB_M 0xff00 /
longitudinal check byte /
#define HIFN_COMP_RES_LCB_S 8
#define HIFN_COMP_RES_RESTART 0x0004 /
MPPC: restart /
#define HIFN_COMP_RES_ENDMARKER 0x0002 /
LZS: end marker seen /
#define HIFN_COMP_RES_SRC_NOTZERO 0x0001 /
source expired /

struct hifn_mac_result {
volatile u_int16_t flags;
+ volatile u_int16_t reserved;
+ /
followed by 0, 6, 8, or 10 u_int16_t's of the MAC, then crypt /
};

#define HIFN_MAC_RES_MISCOMPARE 0x0002 /
compare failed /
#define HIFN_MAC_RES_SRC_NOTZERO 0x0001 /
source expired /

+struct hifn_crypt_result {
volatile u_int16_t flags;
+ volatile u_int16_t reserved;
};

#define HIFN_CRYPT_RES_SRC_NOTZERO 0x0001 /
source expired /

/
* The poll frequency and poll scalar defines are unshifted values used * to set fields in the DMA Configuration Register.
Index: hifn7751var.h ===================================================================
RCS file: /usr/dfly/repo/src/sys/dev/crypto/hifn/hifn7751var.h,v
retrieving revision 1.2
diff u -r1.2 hifn7751var.h
--
hifn7751var.h 17 Jun 2003 04:28:27 -0000 1.2
+++ hifn7751var.h 11 Apr 2007 03:41:10 -0000
@ -2,7 +2,7 @
/* $DragonFly: src/sys/dev/crypto/hifn/hifn7751var.h,v 1.2 2003/06/17 04:28:27 dillon Exp $ /
/
$OpenBSD: hifn7751var.h,v 1.42 2002/04/08 17:49:42 jason Exp $ */

/*
+/*
* Invertex AEON / Hifn 7751 driver * Copyright (c) 1999 Invertex Inc. All rights reserved. * Copyright (c) 1999 Theo de Raadt
@ -68,6 +68,8 @
#define HIFN_3DES_KEY_LENGTH 24
#define HIFN_MAX_CRYPT_KEY_LENGTH HIFN_3DES_KEY_LENGTH
#define HIFN_IV_LENGTH 8
#define HIFN_AES_IV_LENGTH 16
#define HIFN_MAX_IV_LENGTH HIFN_AES_IV_LENGTH

/*
 *  Length values for authentication
@ -110,9 +112,8 @
};
struct hifn_session {
- int hs_state;
- int hs_prev_op; /* XXX collapse into hs_flags? */
- u_int8_t hs_iv[HIFN_IV_LENGTH];
+ int hs_used;
+ u_int8_t hs_iv[HIFN_MAX_IV_LENGTH];
};
#define    HIFN_RING_SYNC(sc, r, i, f)                    \
@ -137,8 +138,7 @
/* * Holds data specific to a single HIFN board.
/
struct hifn_softc {
device_t sc_dev; /
device backpointer /
+struct hifn_softc { device_t sc_dev; /
device backpointer /
bus_dma_tag_t sc_dmat; /
parent DMA tag decriptor /
struct resource *sc_bar0res;
bus_space_handle_t sc_sh0; /
bar0 bus space handle */
@ -153,7 +153,7 @

u_int32_t sc_dmaier;
u_int32_t sc_drammodel; /* 1=dram, 0=sram /
-
+ u_int32_t sc_pllconfig; /
7954/7955/7956 PLL config */

struct hifn_dma sc_dma;
bus_dmamap_t sc_dmamap;
@ -162,11 +162,15 @
int sc_dmansegs;
int32_t sc_cid;
int sc_maxses;
+ int sc_nsessions;
+ struct hifn_session *sc_sessions;
int sc_ramsize;
int sc_flags;
#define HIFN_HAS_RNG 0x1 /
includes random number generator /
#define HIFN_HAS_PUBLIC 0x2 /
includes public key support /
#define HIFN_IS_7811 0x4 /
Hifn 7811 part /
#define HIFN_HAS_AES 0x4 /
includes AES support /
#define HIFN_IS_7811 0x8 /
Hifn 7811 part /
+#define HIFN_IS_7956 0x10 /
Hifn 7956/7955 don't have SDRAM /
struct callout sc_rngto; /
for polling RNG /
struct callout sc_tickto; /
for managing DMA /
int sc_rngfirst;
@ -182,7 +186,6 @
int sc_needwakeup; /
ops q'd wating on resources /
int sc_curbatch; /
# ops submitted w/o int */
int sc_suspended;
struct hifn_session sc_sessions2048;
};

/*
@ -263,7 +266,7 @
struct hifn_command {
u_int16_t session_num;
u_int16_t base_masks, cry_masks, mac_masks;
- u_int8_t iv[HIFN_IV_LENGTH], *ck, mac[HIFN_MAC_KEY_LENGTH];
+ u_int8_t iv[HIFN_MAX_IV_LENGTH], *ck, mac[HIFN_MAC_KEY_LENGTH];
int cklen;
int sloplen, slopidx;
Actions #1

Updated by dillon over 14 years ago

:more stuff from my tree.. originally from FreeBSD/OpenBSD - it's had 18+ months of soak .. hopefully
:I haven't missed any dependencies :(

This one is too complex to apply manually.  Could you rediff it against
HEAD and mime-encode the patch ?
-Matt
Actions #2

Updated by hasso almost 14 years ago

Committed, thanks. I had to merge this manually and although it's briefly
tested, please test and let me know if I broke something.

Actions #3

Updated by andrew_atrens almost 14 years ago

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Thanks, I should be able to re-test this week.

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.4 (FreeBSD)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFHVXkD8It2CaCdeMwRAmwOAJ4qUXyo02dQRvBN31kISlN7oWXyeACcDR0H
BhwI7wczQjpf9qtcDoQQXIM=
=IiIq
-----END PGP SIGNATURE-----

Actions

Also available in: Atom PDF