summaryrefslogtreecommitdiff
path: root/sys/dev/pci/nofn.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/pci/nofn.c')
-rw-r--r--sys/dev/pci/nofn.c708
1 files changed, 708 insertions, 0 deletions
diff --git a/sys/dev/pci/nofn.c b/sys/dev/pci/nofn.c
new file mode 100644
index 00000000000..6ccab9c549e
--- /dev/null
+++ b/sys/dev/pci/nofn.c
@@ -0,0 +1,708 @@
+/* $OpenBSD: nofn.c,v 1.3 2002/05/15 21:33:22 jason Exp $ */
+
+/*
+ * Copyright (c) 2002 Jason L. Wright (jason@thought.net)
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Jason L. Wright
+ * 4. 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.
+ */
+
+/*
+ * Driver for the Hifn 7814/7851/7854 HIPP1 processor.
+ */
+
+#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/mbuf.h>
+#include <sys/device.h>
+#include <sys/queue.h>
+
+#include <uvm/uvm_extern.h>
+
+#include <crypto/cryptodev.h>
+#include <crypto/cryptosoft.h>
+#include <dev/rndvar.h>
+#include <sys/md5k.h>
+#include <crypto/sha1.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcidevs.h>
+
+#include <dev/pci/nofnreg.h>
+#include <dev/pci/nofnvar.h>
+
+int nofn_match(struct device *, void *, void *);
+void nofn_attach(struct device *, struct device *, void *);
+int nofn_intr(void *);
+
+void nofn_rng_enable(struct nofn_softc *);
+void nofn_rng_disable(struct nofn_softc *);
+void nofn_rng_tick(void *);
+int nofn_rng_intr(struct nofn_softc *);
+int nofn_rng_read(struct nofn_softc *);
+
+int nofn_pk_process(struct cryptkop *);
+void nofn_pk_enable(struct nofn_softc *);
+void nofn_pk_feed(struct nofn_softc *);
+struct nofn_softc *nofn_pk_find(struct cryptkop *);
+void nofn_pk_write_reg(struct nofn_softc *, int, union nofn_pk_reg *);
+void nofn_pk_read_reg(struct nofn_softc *, int, union nofn_pk_reg *);
+void nofn_pk_zero_reg(struct nofn_softc *, int);
+int nofn_modexp_start(struct nofn_softc *, struct nofn_pk_q *);
+void nofn_modexp_finish(struct nofn_softc *, struct nofn_pk_q *);
+int nofn_pk_sigbits(const u_int8_t *, u_int);
+
+struct cfattach nofn_ca = {
+ sizeof(struct nofn_softc), nofn_match, nofn_attach
+};
+
+struct cfdriver nofn_cd = {
+ 0, "nofn", DV_DULL
+};
+
+int
+nofn_match(parent, match, aux)
+ struct device *parent;
+ void *match, *aux;
+{
+ struct pci_attach_args *pa = (struct pci_attach_args *)aux;
+
+ if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_HIFN &&
+ PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_HIFN_78XX)
+ return (1);
+ return (0);
+}
+
+void
+nofn_attach(parent, self, aux)
+ struct device *parent, *self;
+ void *aux;
+{
+ struct nofn_softc *sc = (struct nofn_softc *)self;
+ struct pci_attach_args *pa = aux;
+ pci_chipset_tag_t pc = pa->pa_pc;
+ pci_intr_handle_t ih;
+ const char *intrstr = NULL;
+ bus_size_t bar0size = 0, bar3size = 0;
+ u_int32_t cmd;
+
+ sc->sc_dmat = pa->pa_dmat;
+
+ cmd = pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
+ cmd |= PCI_COMMAND_MEM_ENABLE | PCI_COMMAND_MASTER_ENABLE;
+ pci_conf_write(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, cmd);
+ cmd = pci_conf_read(pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
+
+ if (!(cmd & PCI_COMMAND_MEM_ENABLE)) {
+ printf(": failed to enable memory mapping\n");
+ goto fail;
+ }
+
+ if (!(cmd & PCI_COMMAND_MASTER_ENABLE)) {
+ printf(": failed to enable bus mastering\n");
+ goto fail;
+ }
+
+ if (pci_mapreg_map(pa, NOFN_BAR0_REGS, PCI_MAPREG_TYPE_MEM, 0,
+ &sc->sc_st, &sc->sc_sh, NULL, &bar0size, 0)) {
+ printf(": can't map bar0 regs\n");
+ goto fail;
+ }
+
+ if (pci_intr_map(pa, &ih)) {
+ printf(": couldn't map interrupt\n");
+ bus_space_unmap(sc->sc_st, sc->sc_sh, bar0size);
+ goto fail;
+ }
+
+ intrstr = pci_intr_string(pc, ih);
+ sc->sc_ih = pci_intr_establish(pc, ih, IPL_NET, nofn_intr, sc,
+ self->dv_xname);
+ if (sc->sc_ih == NULL) {
+ printf(": couldn't establish interrupt");
+ if (intrstr != NULL)
+ printf(" at %s", intrstr);
+ printf("\n");
+ goto fail;
+ }
+
+ sc->sc_revid = REG_READ_4(sc, NOFN_REVID);
+
+ switch (sc->sc_revid) {
+ case REVID_7814_7854_1:
+ case REVID_8154_1:/* XXX ? */
+ case REVID_8065_1:/* XXX ? */
+ case REVID_8165_1:/* XXX ? */
+ if (pci_mapreg_map(pa, NOFN_BAR3_PK, PCI_MAPREG_TYPE_MEM, 0,
+ &sc->sc_pk_t, &sc->sc_pk_h, NULL, &bar3size, 0)) {
+ printf(": can't map bar3 regs\n");
+ goto fail;
+ }
+ nofn_rng_enable(sc);
+ nofn_pk_enable(sc);
+ break;
+ case REVID_7851_1:
+ case REVID_7851_2:
+ break;
+ default:
+ printf(": unknown revid %x\n", sc->sc_revid);
+ break;
+ }
+
+ printf(": %s", intrstr);
+ if (sc->sc_flags & NOFN_FLAGS_PK)
+ printf(", pk");
+ if (sc->sc_flags & NOFN_FLAGS_RNG)
+ printf(", rng");
+ printf("\n");
+
+ REG_WRITE_4(sc, NOFN_PCI_INT_MASK, sc->sc_intrmask);
+
+ return;
+
+fail:
+ if (bar3size != 0)
+ bus_space_unmap(sc->sc_pk_t, sc->sc_pk_h, bar3size);
+ if (bar0size != 0)
+ bus_space_unmap(sc->sc_st, sc->sc_sh, bar0size);
+}
+
+int
+nofn_intr(vsc)
+ void *vsc;
+{
+ struct nofn_softc *sc = vsc;
+ u_int32_t stat;
+ int r = 0;
+
+ stat = REG_READ_4(sc, NOFN_PCI_INT_STAT) & sc->sc_intrmask;
+
+ if (stat & PCIINTSTAT_RNGRDY)
+ r |= nofn_rng_intr(sc);
+
+ if (stat & PCIINTSTAT_PK) {
+ struct nofn_pk_q *q;
+ u_int32_t sr;
+
+ r = 1;
+ sr = PK_READ_4(sc, NOFN_PK_SR);
+ if (sr & PK_SR_DONE && sc->sc_pk_current != NULL) {
+ q = sc->sc_pk_current;
+ sc->sc_pk_current = NULL;
+ q->q_finish(sc, q);
+ free(q, M_DEVBUF);
+ nofn_pk_feed(sc);
+ }
+ }
+
+ return (r);
+}
+
+int
+nofn_rng_read(sc)
+ struct nofn_softc *sc;
+{
+ u_int32_t buf[8], reg;
+ int ret = 0, i;
+
+ for (;;) {
+ reg = PK_READ_4(sc, NOFN_PK_SR);
+ if (reg & PK_SR_UFLOW) {
+ ret = -1;
+ printf("%s: rng underflow, disabling.\n");
+ nofn_rng_disable(sc);
+ break;
+ }
+
+ if ((reg & PK_SR_RRDY) == 0)
+ break;
+
+ ret = 1;
+ bus_space_read_region_4(sc->sc_pk_t, sc->sc_pk_h,
+ NOFN_PK_RNGFIFO_BEGIN, buf, 8);
+ if (sc->sc_rngskip > 0)
+ sc->sc_rngskip -= 8;
+ else
+ for (i = 0; i < 8; i++)
+ add_true_randomness(buf[i]);
+ }
+
+ return (ret);
+}
+
+int
+nofn_rng_intr(sc)
+ struct nofn_softc *sc;
+{
+ int r;
+
+ r = nofn_rng_read(sc);
+ if (r == 0)
+ return (0);
+ return (1);
+}
+
+void
+nofn_rng_tick(vsc)
+ void *vsc;
+{
+ struct nofn_softc *sc = vsc;
+ int s, r;
+
+ s = splnet();
+ r = nofn_rng_read(sc);
+ if (r != -1)
+ timeout_add(&sc->sc_rngto, sc->sc_rngtick);
+ splx(s);
+}
+
+void
+nofn_rng_disable(sc)
+ struct nofn_softc *sc;
+{
+ u_int32_t r;
+
+ /* disable rng unit */
+ r = PK_READ_4(sc, NOFN_PK_CFG2);
+ r &= PK_CFG2_ALU_ENA; /* preserve */
+ PK_WRITE_4(sc, NOFN_PK_CFG2, r);
+
+ switch (sc->sc_revid) {
+ case REVID_7814_7854_1:
+ if (timeout_pending(&sc->sc_rngto))
+ timeout_del(&sc->sc_rngto);
+ break;
+ case REVID_8154_1:
+ case REVID_8065_1:
+ case REVID_8165_1:
+ /* disable rng interrupts */
+ r = PK_READ_4(sc, NOFN_PK_IER);
+ r &= PK_IER_DONE; /* preserve */
+ PK_WRITE_4(sc, NOFN_PK_IER, r);
+
+ sc->sc_intrmask &= ~PCIINTMASK_RNGRDY;
+ REG_WRITE_4(sc, NOFN_PCI_INT_MASK, sc->sc_intrmask);
+ break;
+ default:
+ printf("%s: nofn_rng_disable: unknown rev %x\n", sc->sc_revid);
+ break;
+ }
+
+ sc->sc_flags &= ~NOFN_FLAGS_RNG;
+}
+
+void
+nofn_rng_enable(sc)
+ struct nofn_softc *sc;
+{
+ u_int32_t r;
+
+ /* setup scalar */
+ PK_WRITE_4(sc, NOFN_PK_RNC, PK_RNC_SCALER);
+
+ /* enable rng unit */
+ r = PK_READ_4(sc, NOFN_PK_CFG2);
+ r &= PK_CFG2_ALU_ENA; /* preserve */
+ r |= PK_CFG2_RNG_ENA;
+ PK_WRITE_4(sc, NOFN_PK_CFG2, r);
+
+ /* 78xx chips cannot use interrupts to gather rng's */
+ switch (sc->sc_revid) {
+ case REVID_7814_7854_1:
+ timeout_set(&sc->sc_rngto, nofn_rng_tick, sc);
+ if (hz < 100)
+ sc->sc_rngtick = 1;
+ else
+ sc->sc_rngtick = hz / 100;
+ timeout_add(&sc->sc_rngto, sc->sc_rngtick);
+ break;
+ case REVID_8154_1:
+ case REVID_8065_1:
+ case REVID_8165_1:
+ /* enable rng interrupts */
+ r = PK_READ_4(sc, NOFN_PK_IER);
+ r &= PK_IER_DONE; /* preserve */
+ r |= PK_IER_RRDY;
+ PK_WRITE_4(sc, NOFN_PK_IER, r);
+ sc->sc_intrmask |= PCIINTMASK_RNGRDY;
+ break;
+ default:
+ printf("%s: nofn_rng_enable: unknown rev %x\n", sc->sc_revid);
+ break;
+ }
+
+ sc->sc_flags |= NOFN_FLAGS_RNG;
+}
+
+void
+nofn_pk_enable(sc)
+ struct nofn_softc *sc;
+{
+ u_int32_t r;
+
+ if ((sc->sc_cid = crypto_get_driverid(0)) < 0) {
+ printf(": failed to register cid\n");
+ return;
+ }
+
+ SIMPLEQ_INIT(&sc->sc_pk_queue);
+ sc->sc_pk_current = NULL;
+
+ crypto_kregister(sc->sc_cid, CRK_MOD_EXP, 0, nofn_pk_process);
+
+ /* enable ALU */
+ r = PK_READ_4(sc, NOFN_PK_CFG2);
+ r &= PK_CFG2_RNG_ENA; /* preserve */
+ r |= PK_CFG2_ALU_ENA;
+ PK_WRITE_4(sc, NOFN_PK_CFG2, r);
+
+ sc->sc_intrmask |= PCIINTMASK_PK;
+ sc->sc_flags |= NOFN_FLAGS_PK;
+}
+
+void
+nofn_pk_feed(sc)
+ struct nofn_softc *sc;
+{
+ struct nofn_pk_q *q;
+ u_int32_t r;
+
+ /* Queue is empty and nothing being processed, turn off interrupt */
+ if (SIMPLEQ_EMPTY(&sc->sc_pk_queue) &&
+ sc->sc_pk_current == NULL) {
+ r = PK_READ_4(sc, NOFN_PK_IER);
+ r &= PK_IER_RRDY; /* preserve */
+ PK_WRITE_4(sc, NOFN_PK_IER, r);
+ return;
+ }
+
+ /* Operation already pending, wait. */
+ if (sc->sc_pk_current != NULL)
+ return;
+
+ while (!SIMPLEQ_EMPTY(&sc->sc_pk_queue)) {
+ q = SIMPLEQ_FIRST(&sc->sc_pk_queue);
+ if (q->q_start(sc, q) == 0) {
+ sc->sc_pk_current = q;
+ SIMPLEQ_REMOVE_HEAD(&sc->sc_pk_queue, q, q_next);
+
+ r = PK_READ_4(sc, NOFN_PK_IER);
+ r &= PK_IER_RRDY; /* preserve */
+ r |= PK_IER_DONE;
+ PK_WRITE_4(sc, NOFN_PK_IER, r);
+ break;
+ } else {
+ SIMPLEQ_REMOVE_HEAD(&sc->sc_pk_queue, q, q_next);
+ free(q, M_DEVBUF);
+ }
+ }
+}
+
+int
+nofn_pk_process(krp)
+ struct cryptkop *krp;
+{
+ struct nofn_softc *sc;
+ struct nofn_pk_q *q;
+ int s;
+
+ if (krp == NULL || krp->krp_callback == NULL)
+ return (EINVAL);
+ if ((sc = nofn_pk_find(krp)) == NULL) {
+ krp->krp_status = EINVAL;
+ crypto_kdone(krp);
+ return (0);
+ }
+
+ q = (struct nofn_pk_q *)malloc(sizeof(*q), M_DEVBUF, M_NOWAIT);
+ if (q == NULL) {
+ krp->krp_status = ENOMEM;
+ crypto_kdone(krp);
+ return (0);
+ }
+
+ switch (krp->krp_op) {
+ case CRK_MOD_EXP:
+ q->q_start = nofn_modexp_start;
+ q->q_finish = nofn_modexp_finish;
+ q->q_krp = krp;
+ s = splnet();
+ SIMPLEQ_INSERT_TAIL(&sc->sc_pk_queue, q, q_next);
+ nofn_pk_feed(sc);
+ splx(s);
+ return (0);
+ default:
+ printf("%s: kprocess: invalid op 0x%x\n",
+ sc->sc_dev.dv_xname, krp->krp_op);
+ krp->krp_status = EOPNOTSUPP;
+ crypto_kdone(krp);
+ free(q, M_DEVBUF);
+ return (0);
+ }
+}
+
+struct nofn_softc *
+nofn_pk_find(krp)
+ struct cryptkop *krp;
+{
+ struct nofn_softc *sc;
+ int i;
+
+ for (i = 0; i < nofn_cd.cd_ndevs; i++) {
+ sc = nofn_cd.cd_devs[i];
+ if (sc == NULL)
+ continue;
+ if (sc->sc_cid == krp->krp_hid)
+ return (sc);
+ }
+ return (NULL);
+}
+
+void
+nofn_pk_read_reg(sc, ridx, rp)
+ struct nofn_softc *sc;
+ int ridx;
+ union nofn_pk_reg *rp;
+{
+ bus_space_read_region_4(sc->sc_pk_t, sc->sc_pk_h,
+ NOFN_PK_REGADDR(NOFN_PK_WIN_2, ridx, 0), rp->w, 1024/32);
+}
+
+void
+nofn_pk_write_reg(sc, ridx, rp)
+ struct nofn_softc *sc;
+ int ridx;
+ union nofn_pk_reg *rp;
+{
+ bus_space_write_region_4(sc->sc_pk_t, sc->sc_pk_h,
+ NOFN_PK_REGADDR(NOFN_PK_WIN_2, ridx, 0), rp->w, 1024/32);
+}
+
+void
+nofn_pk_zero_reg(sc, ridx)
+ struct nofn_softc *sc;
+ int ridx;
+{
+ nofn_pk_write_reg(sc, ridx, &sc->sc_pk_zero);
+}
+
+int
+nofn_modexp_start(sc, q)
+ struct nofn_softc *sc;
+ struct nofn_pk_q *q;
+{
+ struct cryptkop *krp = q->q_krp;
+ int ip = 0, err = 0;
+ int mshift, eshift, nshift;
+ int mbits, ebits, nbits;
+
+ if (krp->krp_param[NOFN_MODEXP_PAR_M].crp_nbits > 1024) {
+ err = ERANGE;
+ goto errout;
+ }
+
+ /* Zero out registers. */
+ nofn_pk_zero_reg(sc, 0);
+ nofn_pk_zero_reg(sc, 1);
+ nofn_pk_zero_reg(sc, 2);
+ nofn_pk_zero_reg(sc, 3);
+
+ /* Write out N... */
+ nbits = nofn_pk_sigbits(krp->krp_param[NOFN_MODEXP_PAR_N].crp_p,
+ krp->krp_param[NOFN_MODEXP_PAR_N].crp_nbits);
+ if (nbits > 1024) {
+ err = E2BIG;
+ goto errout;
+ }
+ if (nbits < 5) {
+ err = ERANGE;
+ goto errout;
+ }
+ bzero(&sc->sc_pk_tmp, sizeof(sc->sc_pk_tmp));
+ bcopy(krp->krp_param[NOFN_MODEXP_PAR_N].crp_p, &sc->sc_pk_tmp,
+ (nbits + 7) / 8);
+ nofn_pk_write_reg(sc, 2, &sc->sc_pk_tmp);
+
+ nshift = 1024 - nbits;
+ PK_WRITE_4(sc, NOFN_PK_LENADDR(2), 1024);
+ if (nshift != 0) {
+ PK_WRITE_4(sc, NOFN_PK_INSTR_BEGIN + ip,
+ NOFN_PK_INSTR2(0, PK_OPCODE_SL, 2, 2, nshift));
+ ip += 4;
+
+ PK_WRITE_4(sc, NOFN_PK_INSTR_BEGIN + ip,
+ NOFN_PK_INSTR2(0, PK_OPCODE_TAG, 2, 2, nbits));
+ ip += 4;
+ }
+
+ /* Write out M... */
+ mbits = nofn_pk_sigbits(krp->krp_param[NOFN_MODEXP_PAR_M].crp_p,
+ krp->krp_param[NOFN_MODEXP_PAR_M].crp_nbits);
+ if (mbits > 1024 || mbits > nbits) {
+ err = E2BIG;
+ goto errout;
+ }
+ bzero(&sc->sc_pk_tmp, sizeof(sc->sc_pk_tmp));
+ bcopy(krp->krp_param[NOFN_MODEXP_PAR_M].crp_p, &sc->sc_pk_tmp,
+ (mbits + 7) / 8);
+ nofn_pk_write_reg(sc, 0, &sc->sc_pk_tmp);
+
+ mshift = 1024 - nbits;
+ PK_WRITE_4(sc, NOFN_PK_LENADDR(0), 1024);
+ if (mshift != 0) {
+ PK_WRITE_4(sc, NOFN_PK_INSTR_BEGIN + ip,
+ NOFN_PK_INSTR2(0, PK_OPCODE_SL, 0, 0, mshift));
+ ip += 4;
+
+ PK_WRITE_4(sc, NOFN_PK_INSTR_BEGIN + ip,
+ NOFN_PK_INSTR2(0, PK_OPCODE_TAG, 0, 0, nbits));
+ ip += 4;
+ }
+
+ /* Write out E... */
+ ebits = nofn_pk_sigbits(krp->krp_param[NOFN_MODEXP_PAR_E].crp_p,
+ krp->krp_param[NOFN_MODEXP_PAR_E].crp_nbits);
+ if (ebits > 1024 || ebits > nbits) {
+ err = E2BIG;
+ goto errout;
+ }
+ if (ebits < 1) {
+ err = ERANGE;
+ goto errout;
+ }
+ bzero(&sc->sc_pk_tmp, sizeof(sc->sc_pk_tmp));
+ bcopy(krp->krp_param[NOFN_MODEXP_PAR_E].crp_p, &sc->sc_pk_tmp,
+ (ebits + 7) / 8);
+ nofn_pk_write_reg(sc, 1, &sc->sc_pk_tmp);
+
+ eshift = 1024 - nbits;
+ PK_WRITE_4(sc, NOFN_PK_LENADDR(1), 1024);
+ if (eshift != 0) {
+ PK_WRITE_4(sc, NOFN_PK_INSTR_BEGIN + ip,
+ NOFN_PK_INSTR2(0, PK_OPCODE_SL, 1, 1, eshift));
+ ip += 4;
+
+ PK_WRITE_4(sc, NOFN_PK_INSTR_BEGIN + ip,
+ NOFN_PK_INSTR2(0, PK_OPCODE_TAG, 1, 1, nbits));
+ ip += 4;
+ }
+
+ if (nshift == 0) {
+ PK_WRITE_4(sc, NOFN_PK_INSTR_BEGIN + ip,
+ NOFN_PK_INSTR(PK_OP_DONE, PK_OPCODE_MODEXP, 3, 0, 1, 2));
+ ip += 4;
+ } else {
+ PK_WRITE_4(sc, NOFN_PK_INSTR_BEGIN + ip,
+ NOFN_PK_INSTR(0, PK_OPCODE_MODEXP, 3, 0, 1, 2));
+ ip += 4;
+
+ PK_WRITE_4(sc, NOFN_PK_INSTR_BEGIN + ip,
+ NOFN_PK_INSTR2(0, PK_OPCODE_SR, 3, 3, nshift));
+ ip += 4;
+
+ PK_WRITE_4(sc, NOFN_PK_INSTR_BEGIN + ip,
+ NOFN_PK_INSTR2(PK_OP_DONE, PK_OPCODE_TAG, 3, 3, nbits));
+ ip += 4;
+ }
+
+ /* Start microprogram */
+ PK_WRITE_4(sc, NOFN_PK_CR, 0 << PK_CR_OFFSET_S);
+
+ return (0);
+
+errout:
+ bzero(&sc->sc_pk_tmp, sizeof(sc->sc_pk_tmp));
+ nofn_pk_zero_reg(sc, 0);
+ nofn_pk_zero_reg(sc, 1);
+ nofn_pk_zero_reg(sc, 2);
+ nofn_pk_zero_reg(sc, 3);
+ krp->krp_status = err;
+ crypto_kdone(krp);
+ return (1);
+}
+
+void
+nofn_modexp_finish(sc, q)
+ struct nofn_softc *sc;
+ struct nofn_pk_q *q;
+{
+ struct cryptkop *krp = q->q_krp;
+ int reglen, crplen;
+
+ nofn_pk_read_reg(sc, 3, &sc->sc_pk_tmp);
+
+ reglen = ((PK_READ_4(sc, NOFN_PK_LENADDR(3)) & NOFN_PK_LENMASK) + 7)
+ / 8;
+ crplen = (krp->krp_param[NOFN_MODEXP_PAR_C].crp_nbits + 7) / 8;
+
+ if (crplen <= reglen)
+ bcopy(sc->sc_pk_tmp.b, krp->krp_param[NOFN_MODEXP_PAR_C].crp_p,
+ reglen);
+ else {
+ bcopy(sc->sc_pk_tmp.b, krp->krp_param[NOFN_MODEXP_PAR_C].crp_p,
+ reglen);
+ bzero(krp->krp_param[NOFN_MODEXP_PAR_C].crp_p + reglen,
+ crplen - reglen);
+ }
+ bzero(&sc->sc_pk_tmp, sizeof(sc->sc_pk_tmp));
+ nofn_pk_zero_reg(sc, 0);
+ nofn_pk_zero_reg(sc, 1);
+ nofn_pk_zero_reg(sc, 2);
+ nofn_pk_zero_reg(sc, 3);
+ crypto_kdone(krp);
+}
+
+/*
+ * Return the number of significant bits of a little endian big number.
+ */
+int
+nofn_pk_sigbits(p, pbits)
+ const u_int8_t *p;
+ u_int pbits;
+{
+ u_int plen = (pbits + 7) / 8;
+ int i, sig = plen * 8;
+ u_int8_t c;
+
+ for (i = plen - 1; i >= 0; i--) {
+ c = p[i];
+ if (c != 0) {
+ while ((c & 0x80) == 0) {
+ sig--;
+ c <<= 1;
+ }
+ break;
+ }
+ sig -= 8;
+ }
+ return (sig);
+}