/*- * Copyright (c) 2003-2012 Broadcom Corporation * 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. * * THIS SOFTWARE IS PROVIDED BY BROADCOM ``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 BROADCOM OR CONTRIBUTORS 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "cryptodev_if.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef NLM_RSA_DEBUG int print_krp_params(struct cryptkop *krp); #endif static int xlp_rsa_init(struct xlp_rsa_softc *sc, int node); static int xlp_rsa_newsession(device_t , uint32_t *, struct cryptoini *); static int xlp_rsa_freesession(device_t , uint64_t); static int xlp_rsa_kprocess(device_t , struct cryptkop *, int); static int xlp_get_rsa_opsize(struct xlp_rsa_command *cmd, unsigned int bits); static void xlp_free_cmd_params(struct xlp_rsa_command *cmd); static int xlp_rsa_inp2hwformat(uint8_t *src, uint8_t *dst, uint32_t paramsize, uint8_t result); static int xlp_rsa_probe(device_t); static int xlp_rsa_attach(device_t); static int xlp_rsa_detach(device_t); static device_method_t xlp_rsa_methods[] = { /* device interface */ DEVMETHOD(device_probe, xlp_rsa_probe), DEVMETHOD(device_attach, xlp_rsa_attach), DEVMETHOD(device_detach, xlp_rsa_detach), /* bus interface */ DEVMETHOD(bus_print_child, bus_generic_print_child), DEVMETHOD(bus_driver_added, bus_generic_driver_added), /* crypto device methods */ DEVMETHOD(cryptodev_newsession, xlp_rsa_newsession), DEVMETHOD(cryptodev_freesession,xlp_rsa_freesession), DEVMETHOD(cryptodev_kprocess, xlp_rsa_kprocess), DEVMETHOD_END }; static driver_t xlp_rsa_driver = { "nlmrsa", xlp_rsa_methods, sizeof(struct xlp_rsa_softc) }; static devclass_t xlp_rsa_devclass; DRIVER_MODULE(nlmrsa, pci, xlp_rsa_driver, xlp_rsa_devclass, 0, 0); MODULE_DEPEND(nlmrsa, crypto, 1, 1, 1); void nlm_xlprsaecc_msgring_handler(int vc, int size, int code, int src_id, struct nlm_fmn_msg *msg, void *data); #ifdef NLM_RSA_DEBUG int print_krp_params(struct cryptkop *krp) { int i; printf("krp->krp_op :%d\n",krp->krp_op); printf("krp->krp_status :%d\n",krp->krp_status); printf("krp->krp_iparams:%d\n",krp->krp_iparams); printf("krp->krp_oparams:%d\n",krp->krp_oparams); for (i=0;ikrp_iparams+krp->krp_oparams;i++) { printf("krp->krp_param[%d].crp_p :0x%llx\n",i, (unsigned long long)krp->krp_param[i].crp_p); printf("krp->krp_param[%d].crp_nbits :%d\n",i, krp->krp_param[i].crp_nbits); printf("krp->krp_param[%d].crp_nbytes :%d\n",i, (krp->krp_param[i].crp_nbits+7)/8); } return 0; } #endif static int xlp_rsa_init(struct xlp_rsa_softc *sc, int node) { struct xlp_rsa_command *cmd = NULL; uint32_t size, fbvc, dstvc, endsel, regval; struct nlm_fmn_msg m; int err, ret, i; uint64_t base; /* Register interrupt handler for the RSA/ECC CMS messages */ if (register_msgring_handler(sc->rsaecc_vc_start, sc->rsaecc_vc_end, nlm_xlprsaecc_msgring_handler, sc) != 0) { err = -1; printf("Couldn't register rsa/ecc msgring handler\n"); goto errout; } m.msg[0] = m.msg[1] = m.msg[2] = m.msg[3] = 0; fbvc = nlm_cpuid() / CMS_MAX_VCPU_VC; /* Do the CMS credit initialization */ /* Currently it is configured by default to 50 when kernel comes up */ if ((cmd = malloc(sizeof(struct xlp_rsa_command), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { err = ENOMEM; printf("Failed to allocate mem for cmd\n"); goto errout; } size = sizeof(nlm_rsa_ucode_data); if ((cmd->rsasrc = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { err = ENOMEM; printf("Failed to allocate mem for cmd->rsasrc\n"); goto errout; } if (((uintptr_t)cmd->rsasrc & (XLP_L2L3_CACHELINE_SIZE - 1))) { err = EINVAL; printf("cmd->rsasrc is not cacheline aligned\n"); goto errout; } memcpy(cmd->rsasrc, (uint8_t *)nlm_rsa_ucode_data, size); m.msg[0] = nlm_crypto_form_rsa_ecc_fmn_entry0(1, 0x70, 0, vtophys(cmd->rsasrc)); m.msg[1] = nlm_crypto_form_rsa_ecc_fmn_entry1(0, 1, fbvc, vtophys(cmd->rsasrc)); /* Software scratch pad */ m.msg[2] = (uintptr_t)cmd; for (dstvc = sc->rsaecc_vc_start; dstvc <= sc->rsaecc_vc_end; dstvc++) { ret = nlm_fmn_msgsend(dstvc, 3, FMN_SWCODE_RSA, &m); if (ret != 0) { err = -1; printf("%s: msgsnd failed (%x)\n", __func__, ret); goto errout; } } /* Configure so that all VCs send request to all RSA pipes */ base = nlm_get_rsa_regbase(node); if (nlm_is_xlp3xx()) { endsel = 1; regval = 0xFFFF; } else { endsel = 3; regval = 0x07FFFFFF; } for (i = 0; i < endsel; i++) nlm_write_rsa_reg(base, RSA_ENG_SEL_0 + i, regval); return (0); errout: xlp_free_cmd_params(cmd); return (err); } /* This function is called from an interrupt handler */ void nlm_xlprsaecc_msgring_handler(int vc, int size, int code, int src_id, struct nlm_fmn_msg *msg, void *data) { struct xlp_rsa_command *cmd = NULL; struct xlp_rsa_softc *sc = NULL; KASSERT(code == FMN_SWCODE_RSA, ("%s: bad code = %d, expected code = %d\n", __FUNCTION__, code, FMN_SWCODE_RSA)); sc = (struct xlp_rsa_softc *)data; KASSERT(src_id >= sc->rsaecc_vc_start && src_id <= sc->rsaecc_vc_end, ("%s: bad src_id = %d, expect %d - %d\n", __FUNCTION__, src_id, sc->rsaecc_vc_start, sc->rsaecc_vc_end)); cmd = (struct xlp_rsa_command *)(uintptr_t)msg->msg[1]; KASSERT(cmd != NULL, ("%s:cmd not received properly\n", __FUNCTION__)); KASSERT(RSA_ERROR(msg->msg[0]) == 0, ("%s: Message rcv msg0 %llx msg1 %llx err %x \n", __FUNCTION__, (unsigned long long)msg->msg[0], (unsigned long long)msg->msg[1], (int)RSA_ERROR(msg->msg[0]))); xlp_rsa_inp2hwformat(((uint8_t *)cmd->rsasrc+ (cmd->rsaopsize*cmd->krp->krp_iparams)), cmd->krp->krp_param[cmd->krp->krp_iparams].crp_p, ((cmd->krp->krp_param[cmd->krp->krp_iparams].crp_nbits+7)/8), 1); if (cmd->krp != NULL) crypto_kdone(cmd->krp); xlp_free_cmd_params(cmd); return; } static int xlp_rsa_probe(device_t dev) { struct xlp_rsa_softc *sc; if (pci_get_vendor(dev) == PCI_VENDOR_NETLOGIC && pci_get_device(dev) == PCI_DEVICE_ID_NLM_RSA) { sc = device_get_softc(dev); return (BUS_PROBE_DEFAULT); } return (ENXIO); } /* * Attach an interface that successfully probed. */ static int xlp_rsa_attach(device_t dev) { struct xlp_rsa_softc *sc = device_get_softc(dev); uint64_t base; int qstart, qnum; int freq, node; sc->sc_dev = dev; node = nlm_get_device_node(pci_get_slot(dev)); freq = nlm_set_device_frequency(node, DFS_DEVICE_RSA, 250); if (bootverbose) device_printf(dev, "RSA Freq: %dMHz\n", freq); if(pci_get_device(dev) == PCI_DEVICE_ID_NLM_RSA) { device_set_desc(dev, "XLP RSA/ECC Accelerator"); if ((sc->sc_cid = crypto_get_driverid(dev, CRYPTOCAP_F_HARDWARE)) < 0) { printf("xlp_rsaecc-err:couldn't get the driver id\n"); goto error_exit; } if (crypto_kregister(sc->sc_cid, CRK_MOD_EXP, 0) != 0) printf("register failed for CRK_MOD_EXP\n"); base = nlm_get_rsa_pcibase(node); qstart = nlm_qidstart(base); qnum = nlm_qnum(base); sc->rsaecc_vc_start = qstart; sc->rsaecc_vc_end = qstart + qnum - 1; } if (xlp_rsa_init(sc, node) != 0) goto error_exit; device_printf(dev, "RSA Initialization complete!\n"); return (0); error_exit: return (ENXIO); } /* * Detach an interface that successfully probed. */ static int xlp_rsa_detach(device_t dev) { return (0); } /* * Allocate a new 'session' and return an encoded session id. 'sidp' * contains our registration id, and should contain an encoded session * id on successful allocation. */ static int xlp_rsa_newsession(device_t dev, u_int32_t *sidp, struct cryptoini *cri) { struct xlp_rsa_softc *sc = device_get_softc(dev); int sesn; struct xlp_rsa_session *ses = NULL; if (sidp == NULL || cri == NULL || sc == NULL) return (EINVAL); if (sc->sc_sessions == NULL) { ses = sc->sc_sessions = malloc(sizeof(struct xlp_rsa_session), 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 = malloc((sesn + 1)*sizeof(struct xlp_rsa_session), M_DEVBUF, M_NOWAIT); if (ses == NULL) return (ENOMEM); bcopy(sc->sc_sessions, ses, sesn * sizeof(*ses)); bzero(sc->sc_sessions, sesn * sizeof(*ses)); free(sc->sc_sessions, M_DEVBUF); sc->sc_sessions = ses; ses = &sc->sc_sessions[sesn]; sc->sc_nsessions++; } } bzero(ses, sizeof(*ses)); ses->sessionid = sesn; ses->hs_used = 1; *sidp = XLP_RSA_SID(device_get_unit(sc->sc_dev), sesn); return (0); } /* * Deallocate a session. * XXX this routine should run a zero'd mac/encrypt key into context ram. * XXX to blow away any keys already stored there. */ static int xlp_rsa_freesession(device_t dev, u_int64_t tid) { struct xlp_rsa_softc *sc = device_get_softc(dev); int session; u_int32_t sid = CRYPTO_SESID2LID(tid); if (sc == NULL) return (EINVAL); session = XLP_RSA_SESSION(sid); if (session >= sc->sc_nsessions) return (EINVAL); sc->sc_sessions[session].hs_used = 0; return (0); } static void xlp_free_cmd_params(struct xlp_rsa_command *cmd) { if (cmd->rsasrc != NULL) free(cmd->rsasrc, M_DEVBUF); if (cmd != NULL) free(cmd, M_DEVBUF); return; } static int xlp_get_rsa_opsize(struct xlp_rsa_command *cmd, unsigned int bits) { if (bits == 0) return (-1); /* XLP hardware expects always a fixed size with unused bytes * zeroed out in the input data */ if (bits <= 512) { cmd->rsatype = 0x40; cmd->rsaopsize = (512/8); return (0); } else if ((bits > 512) && (bits <= 1024)) { cmd->rsatype = 0x41; cmd->rsaopsize = (1024/8); return (0); } else if ((bits > 1024) && (bits <= 2048)) { cmd->rsatype = 0x42; cmd->rsaopsize = (2048/8); return (0); } else if ((bits > 2048) && (bits <= 4096)) { cmd->rsatype = 0x43; cmd->rsaopsize = (4096/8); return (0); } else if ((bits > 4096) && (bits <= 8192)) { cmd->rsatype = 0x44; cmd->rsaopsize = (8192/8); return (0); } else { return (-1); } } static int xlp_rsa_inp2hwformat(uint8_t *src, uint8_t *dst, uint32_t paramsize, uint8_t result) { uint32_t pdwords, pbytes; int i=0, j=0, k=0; pdwords = (paramsize / 8); pbytes = (paramsize % 8); for (i = 0, k = 0; i < pdwords; i++) { /* copy dwords of inp/hw to hw/out format */ for (j = 7; j >= 0; j--, k++) dst[(i*8)+j] = src[k]; } if (pbytes) { if (!result) { /* copy rem bytes of input data to hw format */ for (j = 7; k < paramsize; j--, k++) dst[(i*8)+j] = src[k]; } else { /* copy rem bytes of hw data to exp output format */ for (j = 7; k < paramsize; j--, k++) dst[k] = src[(i*8)+j]; } } return (0); } static int nlm_crypto_complete_rsa_request(struct xlp_rsa_softc *sc, struct xlp_rsa_command *cmd) { unsigned int fbvc; struct nlm_fmn_msg m; int ret; fbvc = nlm_cpuid() / CMS_MAX_VCPU_VC; m.msg[0] = m.msg[1] = m.msg[2] = m.msg[3] = 0; m.msg[0] = nlm_crypto_form_rsa_ecc_fmn_entry0(1, cmd->rsatype, cmd->rsafn, vtophys(cmd->rsasrc)); m.msg[1] = nlm_crypto_form_rsa_ecc_fmn_entry1(0, 1, fbvc, vtophys(cmd->rsasrc + (cmd->rsaopsize * cmd->krp->krp_iparams))); /* Software scratch pad */ m.msg[2] = (uintptr_t)cmd; /* Send the message to rsa engine vc */ ret = nlm_fmn_msgsend(sc->rsaecc_vc_start, 3, FMN_SWCODE_RSA, &m); if (ret != 0) { #ifdef NLM_SEC_DEBUG printf("%s: msgsnd failed (%x)\n", __func__, ret); #endif return (ERESTART); } return (0); } static int xlp_rsa_kprocess(device_t dev, struct cryptkop *krp, int hint) { struct xlp_rsa_softc *sc = device_get_softc(dev); struct xlp_rsa_command *cmd = NULL; int err = -1, i; if (krp == NULL || krp->krp_callback == NULL) return (EINVAL); if ((cmd = malloc(sizeof(struct xlp_rsa_command), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { err = ENOMEM; goto errout; } cmd->krp = krp; #ifdef NLM_RSA_DEBUG print_krp_params(krp); #endif switch (krp->krp_op) { case CRK_MOD_EXP: if (krp->krp_iparams == 3 && krp->krp_oparams == 1) break; goto errout; default: printf("Op:%d not yet supported\n", krp->krp_op); goto errout; } if ((xlp_get_rsa_opsize(cmd, krp->krp_param[krp->krp_iparams-1].crp_nbits)) != 0) { err = EINVAL; goto errout; } cmd->rsafn = 0; /* Mod Exp */ if ((cmd->rsasrc = malloc((cmd->rsaopsize * (krp->krp_iparams+krp->krp_oparams)), M_DEVBUF, M_NOWAIT | M_ZERO)) == NULL) { err = ENOMEM; goto errout; } if (((uintptr_t)cmd->rsasrc & (XLP_L2L3_CACHELINE_SIZE - 1))) { err = EINVAL; goto errout; } for (i=0;ikrp_iparams;i++) { KASSERT(krp->krp_param[i].crp_nbits != 0, ("%s: parameter[%d]'s length is zero\n", __FUNCTION__, i)); xlp_rsa_inp2hwformat(krp->krp_param[i].crp_p, ((uint8_t *)cmd->rsasrc+(i*cmd->rsaopsize)), ((krp->krp_param[i].crp_nbits+7)/8), 0); } if (nlm_crypto_complete_rsa_request(sc, cmd) != 0) goto errout; return (0); errout: xlp_free_cmd_params(cmd); krp->krp_status = err; crypto_kdone(krp); return (err); }