Home | History | Annotate | Line # | Download | only in marvell
mvcesa.c revision 1.3
      1 /*	$NetBSD: mvcesa.c,v 1.3 2020/06/14 23:29:23 riastradh Exp $	*/
      2 /*
      3  * Copyright (c) 2008 KIYOHARA Takashi
      4  * All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     18  * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
     19  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     20  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
     23  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
     24  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     25  * POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include <sys/cdefs.h>
     29 __KERNEL_RCSID(0, "$NetBSD: mvcesa.c,v 1.3 2020/06/14 23:29:23 riastradh Exp $");
     30 
     31 #include <sys/param.h>
     32 #include <sys/bus.h>
     33 #include <sys/cprng.h>
     34 #include <sys/device.h>
     35 #include <sys/endian.h>
     36 #include <sys/errno.h>
     37 #include <sys/mbuf.h>
     38 #include <sys/md5.h>
     39 #include <sys/uio.h>
     40 #include <sys/sha1.h>
     41 
     42 #include <opencrypto/cryptodev.h>
     43 #include <opencrypto/xform.h>
     44 
     45 #include <dev/marvell/marvellreg.h>
     46 #include <dev/marvell/marvellvar.h>
     47 #include <dev/marvell/mvcesareg.h>
     48 
     49 #include "locators.h"
     50 
     51 #define MVCESA_SESSION(sid)		((sid) & 0x0fffffff)
     52 #define MVCESA_SID(crd, sesn)		(((crd) << 28) | ((sesn) & 0x0fffffff))
     53 
     54 
     55 struct mvcesa_session {
     56 	int ses_used;
     57 
     58 	int ses_klen;
     59 	uint32_t ses_key[8];
     60 
     61 	uint32_t ses_hminner[5];	/* HMAC inner state */
     62 	uint32_t ses_hmouter[5];	/* HMAC outer state */
     63 };
     64 
     65 struct mvcesa_softc {
     66 	device_t sc_dev;
     67 
     68 	bus_space_tag_t sc_iot;
     69 	bus_space_handle_t sc_ioh;
     70 	bus_dma_tag_t sc_dmat;
     71 
     72 	int sc_cid;
     73 	int sc_nsessions;
     74 	struct mvcesa_session *sc_sessions;
     75 };
     76 
     77 static int mvcesa_match(device_t, cfdata_t, void *);
     78 static void mvcesa_attach(device_t, device_t, void *);
     79 
     80 static int mvcesa_intr(void *);
     81 
     82 static int mvcesa_newsession(void *, u_int32_t *, struct cryptoini *);
     83 static int mvcesa_freesession(void *, u_int64_t);
     84 static int mvcesa_process(void *, struct cryptop *, int);
     85 
     86 static int mvcesa_authentication(struct mvcesa_softc *, struct mvcesa_session *,
     87 				 uint32_t, uint32_t *, uint32_t *, uint64_t,
     88 				 int, int, char *, struct mbuf *, struct uio *);
     89 static int mvcesa_des_encdec(struct mvcesa_softc *, struct mvcesa_session *,
     90 			     uint32_t, uint32_t, uint32_t, uint32_t *, int, int,
     91 			     char *, struct mbuf *, struct uio *);
     92 
     93 
     94 CFATTACH_DECL_NEW(mvcesa_gt, sizeof(struct mvcesa_softc),
     95     mvcesa_match, mvcesa_attach, NULL, NULL);
     96 CFATTACH_DECL_NEW(mvcesa_mbus, sizeof(struct mvcesa_softc),
     97     mvcesa_match, mvcesa_attach, NULL, NULL);
     98 
     99 
    100 /* ARGSUSED */
    101 static int
    102 mvcesa_match(device_t parent, cfdata_t match, void *aux)
    103 {
    104 	struct marvell_attach_args *mva = aux;
    105 
    106 	if (strcmp(mva->mva_name, match->cf_name) != 0)
    107 		return 0;
    108 	if (mva->mva_offset == MVA_OFFSET_DEFAULT ||
    109 	    mva->mva_irq == MVA_IRQ_DEFAULT)
    110 		return 0;
    111 
    112 	mva->mva_size = MVCESA_SIZE;
    113 	return 1;
    114 }
    115 
    116 /* ARGSUSED */
    117 static void
    118 mvcesa_attach(device_t parent, device_t self, void *aux)
    119 {
    120 	struct mvcesa_softc *sc = device_private(self);
    121 	struct marvell_attach_args *mva = aux;
    122 
    123 	aprint_normal(
    124 	    ": Marvell Cryptographic Engines and Security Accelerator\n");
    125 	aprint_naive("\n");
    126 
    127 	sc->sc_dev = self;
    128 	sc->sc_iot = mva->mva_iot;
    129         /* Map I/O registers */
    130 	if (bus_space_subregion(mva->mva_iot, mva->mva_ioh, mva->mva_offset,
    131 	    mva->mva_size, &sc->sc_ioh)) {
    132 		aprint_error_dev(self, "can't map registers\n");
    133 		return;
    134 	}
    135 	sc->sc_dmat = mva->mva_dmat;
    136 
    137 	sc->sc_nsessions = 0;
    138 
    139 	/* Setup Opencrypto stuff */
    140 	sc->sc_cid = crypto_get_driverid(0);
    141 	if (sc->sc_cid < 0) {
    142 		aprint_error_dev(self, "couldn't get crypto driver id\n");
    143 		return;
    144 	}
    145 	crypto_register(sc->sc_cid, CRYPTO_DES_CBC, 0, 0,
    146 	    mvcesa_newsession, mvcesa_freesession, mvcesa_process, sc);
    147 	crypto_register(sc->sc_cid, CRYPTO_3DES_CBC, 0, 0,
    148 	    mvcesa_newsession, mvcesa_freesession, mvcesa_process, sc);
    149 #if __DMA_notyet__
    150 /*
    151  * Don't know how to process to AES CBC in PIO-mode.
    152  * I havn't found IV registers.
    153  */
    154 	crypto_register(sc->sc_cid, CRYPTO_AES_CBC, 0, 0,
    155 	    mvcesa_newsession, mvcesa_freesession, mvcesa_process, sc);
    156 #endif
    157 	crypto_register(sc->sc_cid, CRYPTO_SHA1, 0, 0,
    158 	    mvcesa_newsession, mvcesa_freesession, mvcesa_process, sc);
    159 	crypto_register(sc->sc_cid, CRYPTO_SHA1_HMAC, 0, 0,
    160 	    mvcesa_newsession, mvcesa_freesession, mvcesa_process, sc);
    161 	crypto_register(sc->sc_cid, CRYPTO_MD5, 0, 0,
    162 	    mvcesa_newsession, mvcesa_freesession, mvcesa_process, sc);
    163 	crypto_register(sc->sc_cid, CRYPTO_MD5_HMAC, 0, 0,
    164 	    mvcesa_newsession, mvcesa_freesession, mvcesa_process, sc);
    165 
    166 	/* Clear and establish interrupt */
    167 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVCESA_IC, 0);
    168 	marvell_intr_establish(mva->mva_irq, IPL_NET, mvcesa_intr, sc);
    169 
    170 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVCESA_IM, 0);
    171 }
    172 
    173 
    174 static int
    175 mvcesa_intr(void *arg)
    176 {
    177 #if 0
    178 	struct mvcesa_softc *sc = (struct mvcesa_softc *)arg;
    179 #endif
    180 	int handled = 0;
    181 
    182 	return handled;
    183 }
    184 
    185 
    186 /*
    187  * Opencrypto functions
    188  */
    189 /*
    190  * Allocate a new 'session' and return an encoded session id.  'sidp'
    191  * contains our registration id, and should contain an encoded session
    192  * id on successful allocation.
    193  */
    194 static int
    195 mvcesa_newsession(void *arg, u_int32_t *sidp, struct cryptoini *cri)
    196 {
    197 	struct mvcesa_softc *sc = (struct mvcesa_softc *)arg;
    198 	struct cryptoini *c;
    199 	struct mvcesa_session *ses = NULL;
    200 	int sesn, count, enc, mac, i;
    201 
    202 	KASSERT(sc != NULL /*, ("mvcesa_newsession: null softc")*/);
    203 	if (sidp == NULL || cri == NULL || sc == NULL)
    204 		return EINVAL;
    205 
    206 	for (sesn = 0; sesn < sc->sc_nsessions; sesn++)
    207 		if (sc->sc_sessions[sesn].ses_used == 0) {
    208 			ses = sc->sc_sessions + sesn;
    209 			break;
    210 		}
    211 
    212 	if (ses == NULL) {
    213 		sesn = sc->sc_nsessions;
    214 		ses = malloc((sesn + 1) * sizeof(*ses), M_DEVBUF, M_NOWAIT);
    215 		if (ses == NULL)
    216 			return ENOMEM;
    217 		if (sesn != 0) {
    218 			memcpy(ses, sc->sc_sessions, sesn * sizeof(*ses));
    219 			memset(sc->sc_sessions, 0, sesn * sizeof(*ses));
    220 			free(sc->sc_sessions, M_DEVBUF);
    221 		}
    222 		sc->sc_sessions = ses;
    223 		ses = sc->sc_sessions + sesn;
    224 		sc->sc_nsessions++;
    225 	}
    226 	memset(ses, 0, sizeof(*ses));
    227 
    228 	count = 0;
    229 	enc = mac = 0;
    230 	for (c = cri; c != NULL; c = c->cri_next) {
    231 		switch (c->cri_alg) {
    232 		case CRYPTO_DES_CBC:
    233 		case CRYPTO_3DES_CBC:
    234 			if (enc)
    235 				return EINVAL;
    236 			enc = 1;
    237 
    238 			/* Go ahead and compute key in CESA's byte order */
    239 			ses->ses_klen = c->cri_klen;
    240 			memcpy(ses->ses_key, c->cri_key, c->cri_klen / 8);
    241 			switch (c->cri_alg) {
    242 			case CRYPTO_3DES_CBC:
    243 				ses->ses_key[5] = htobe32(ses->ses_key[5]);
    244 				ses->ses_key[4] = htobe32(ses->ses_key[4]);
    245 				ses->ses_key[3] = htobe32(ses->ses_key[3]);
    246 				ses->ses_key[2] = htobe32(ses->ses_key[2]);
    247 
    248 				/* FALLTHROUGH */
    249 			case CRYPTO_DES_CBC:
    250 				ses->ses_key[1] = htobe32(ses->ses_key[1]);
    251 				ses->ses_key[0] = htobe32(ses->ses_key[0]);
    252 			}
    253 			break;
    254 
    255 		case CRYPTO_SHA1_HMAC:
    256 		case CRYPTO_MD5_HMAC:
    257 		{
    258 			MD5_CTX md5ctx;
    259 			SHA1_CTX sha1ctx;
    260 			int klen_bytes = c->cri_klen / 8;
    261 
    262 			KASSERT(c->cri_klen == 512);
    263 
    264 			for (i = 0; i < klen_bytes; i++)
    265 				c->cri_key[i] ^= HMAC_IPAD_VAL;
    266 			if (c->cri_alg == CRYPTO_MD5_HMAC_96) {
    267 				MD5Init(&md5ctx);
    268 				MD5Update(&md5ctx, c->cri_key, klen_bytes);
    269 				MD5Update(&md5ctx, hmac_ipad_buffer,
    270 				    HMAC_BLOCK_LEN - klen_bytes);
    271 				memcpy(ses->ses_hminner, md5ctx.state,
    272 				    sizeof(md5ctx.state));
    273 			} else {
    274 				SHA1Init(&sha1ctx);
    275 				SHA1Update(&sha1ctx, c->cri_key, klen_bytes);
    276 				SHA1Update(&sha1ctx, hmac_ipad_buffer,
    277 				    HMAC_BLOCK_LEN - klen_bytes);
    278 				memcpy(ses->ses_hminner, sha1ctx.state,
    279 				    sizeof(sha1ctx.state));
    280 			}
    281 
    282 			for (i = 0; i < klen_bytes; i++)
    283 				c->cri_key[i] ^=
    284 				    (HMAC_IPAD_VAL ^ HMAC_OPAD_VAL);
    285 			if (c->cri_alg == CRYPTO_MD5_HMAC_96) {
    286 				MD5Init(&md5ctx);
    287 				MD5Update(&md5ctx, c->cri_key, klen_bytes);
    288 				MD5Update(&md5ctx, hmac_opad_buffer,
    289 				    HMAC_BLOCK_LEN - klen_bytes);
    290 				memcpy(ses->ses_hmouter, md5ctx.state,
    291 				    sizeof(md5ctx.state));
    292 			} else {
    293 				SHA1Init(&sha1ctx);
    294 				SHA1Update(&sha1ctx, c->cri_key, klen_bytes);
    295 				SHA1Update(&sha1ctx, hmac_opad_buffer,
    296 				    HMAC_BLOCK_LEN - klen_bytes);
    297 				memcpy(ses->ses_hmouter, sha1ctx.state,
    298 				    sizeof(sha1ctx.state));
    299 			}
    300 
    301 			for (i = 0; i < klen_bytes; i++)
    302 				c->cri_key[i] ^= HMAC_OPAD_VAL;
    303 		}
    304 			/* FALLTHROUGH */
    305 
    306 		case CRYPTO_SHA1:
    307 		case CRYPTO_MD5:
    308 			if (mac)
    309 				return EINVAL;
    310 			mac = 1;
    311 		}
    312 		count++;
    313 	}
    314 	if (count > 2) {
    315 		mvcesa_freesession(sc, sesn);
    316 		return EINVAL;
    317 	}
    318 
    319 	*sidp = MVCESA_SID(device_unit(sc->sc_dev), sesn);
    320 	ses->ses_used = 1;
    321 
    322 	return 0;
    323 }
    324 
    325 /*
    326  * Deallocate a session.
    327  */
    328 static int
    329 mvcesa_freesession(void *arg, u_int64_t tid)
    330 {
    331 	struct mvcesa_softc *sc = (struct mvcesa_softc *)arg;
    332 	int session;
    333 	uint32_t sid = ((uint32_t)tid) & 0xffffffff;
    334 
    335 	KASSERT(sc != NULL /*, ("mvcesa_freesession: null softc")*/);
    336 
    337 	session = MVCESA_SESSION(sid);
    338 	if (session >= sc->sc_nsessions)
    339 		return EINVAL;
    340 
    341 	memset(&sc->sc_sessions[session], 0, sizeof(sc->sc_sessions[session]));
    342 	return (0);
    343 }
    344 
    345 static int
    346 mvcesa_process(void *arg, struct cryptop *crp, int hint)
    347 {
    348 	struct mvcesa_softc *sc = (struct mvcesa_softc *)arg;
    349 	struct mvcesa_session *ses;
    350 	struct cryptodesc *crd;
    351 	struct mbuf *m = NULL;
    352 	struct uio *uio = NULL;
    353 	int session;
    354 	char *buf = NULL;
    355 
    356 	KASSERT(sc != NULL /*, ("mvcesa_process: null softc")*/);
    357 
    358 	if (crp == NULL)
    359 		return EINVAL;
    360 	if (crp->crp_callback == NULL || sc == NULL) {
    361 		crp->crp_etype = EINVAL;
    362 		goto done;
    363 	}
    364 
    365 	session = MVCESA_SESSION(crp->crp_sid);
    366 	if (session >= sc->sc_nsessions) {
    367 		crp->crp_etype = ENOENT;
    368 		goto done;
    369 	}
    370 	ses = &sc->sc_sessions[session];
    371 
    372 	if (crp->crp_flags & CRYPTO_F_IMBUF)
    373 		m = (struct mbuf *)crp->crp_buf;
    374 	else if (crp->crp_flags & CRYPTO_F_IOV)
    375 		uio = (struct uio *)crp->crp_buf;
    376 	else
    377 		buf = (char *)crp->crp_buf;
    378 
    379 	if (0 /* DMA support */) {
    380 		/* not yet... */
    381 
    382 		goto done;
    383 	}
    384 
    385 	/* PIO operation */
    386 
    387 	for (crd = crp->crp_desc; crd; crd = crd->crd_next) {
    388 		switch (crd->crd_alg) {
    389 		case CRYPTO_DES_CBC:
    390 		case CRYPTO_3DES_CBC:
    391 		{
    392 			uint32_t alg, mode, dir, *iv, ivbuf[2];
    393 
    394 			mode = MVCESA_DESE_C_DESMODE_CBC;
    395 			if (crd->crd_alg == CRYPTO_DES_CBC)
    396 				alg = MVCESA_DESE_C_ALGORITHM_DES;
    397 			else {	/* CRYPTO_3DES_CBC */
    398 				alg = MVCESA_DESE_C_ALGORITHM_3DES;
    399 				mode |= MVCESA_DESE_C_3DESMODE_EDE;
    400 			}
    401 			if (crd->crd_flags & CRD_F_ENCRYPT) {
    402 				dir = MVCESA_DESE_C_DIRECTION_ENC;
    403 				if (crd->crd_flags & CRD_F_IV_EXPLICIT)
    404 					iv = (uint32_t *)crd->crd_iv;
    405 				else {
    406 					cprng_fast(ivbuf, sizeof(ivbuf));
    407 					iv = ivbuf;
    408 				}
    409 				if (!(crd->crd_flags & CRD_F_IV_PRESENT)) {
    410 					if (m != NULL)
    411 						m_copyback(m, crd->crd_inject,
    412 						    8, iv);
    413 					else if (uio != NULL)
    414 						cuio_copyback(uio,
    415 						    crd->crd_inject, 8, iv);
    416 				}
    417 			} else {
    418 				dir = MVCESA_DESE_C_DIRECTION_DEC;
    419 				if (crd->crd_flags & CRD_F_IV_EXPLICIT)
    420 					iv = (uint32_t *)crd->crd_iv;
    421 				else {
    422 					if (m != NULL)
    423 						m_copydata(m, crd->crd_inject,
    424 						    8, ivbuf);
    425 					else if (uio != NULL)
    426 						cuio_copydata(uio,
    427 						    crd->crd_inject, 8, ivbuf);
    428 					iv = ivbuf;
    429 				}
    430 			}
    431 
    432 			crp->crp_etype = mvcesa_des_encdec(sc, ses,
    433 			    alg, mode, dir, iv, crd->crd_skip, crd->crd_len,
    434 			    buf, m, uio);
    435 			break;
    436 		}
    437 
    438 		case CRYPTO_SHA1:
    439 		case CRYPTO_SHA1_HMAC:
    440 		case CRYPTO_MD5:
    441 		case CRYPTO_MD5_HMAC:
    442 		{
    443 			uint64_t bits;
    444 			uint32_t alg, *iv = NULL, digest[512 / 8 / 4], dlen;
    445 
    446 			if (crd->crd_alg == CRYPTO_SHA1 ||
    447 			    crd->crd_alg == CRYPTO_SHA1_HMAC) {
    448 				alg = MVCESA_SHA1MD5I_AC_ALGORITHM_SHA1;
    449 				dlen = 160;
    450 			} else {	/* CRYPTO_MD5 || CRYPTO_MD5_HMAC */
    451 				alg = MVCESA_SHA1MD5I_AC_ALGORITHM_MD5;
    452 				dlen = 128;
    453 			}
    454 			bits = crd->crd_len << 3;
    455 			if (crd->crd_alg == CRYPTO_SHA1_HMAC ||
    456 			    crd->crd_alg == CRYPTO_MD5_HMAC) {
    457 				iv = ses->ses_hminner;
    458 				bits += 512;
    459 			}
    460 
    461 			crp->crp_etype = mvcesa_authentication(sc, ses,
    462 			    alg, iv, digest, bits, crd->crd_skip, crd->crd_len,
    463 			    buf, m, uio);
    464 			if (crp->crp_etype != 0)
    465 				break;
    466 
    467 			if (crd->crd_alg == CRYPTO_SHA1_HMAC ||
    468 			    crd->crd_alg == CRYPTO_MD5_HMAC)
    469 				crp->crp_etype = mvcesa_authentication(sc,
    470 				    ses, alg, ses->ses_hmouter, digest,
    471 				    512 + dlen, 0, dlen, (char *)digest, NULL,
    472 				    NULL);
    473 			if (crp->crp_etype != 0)
    474 				break;
    475 
    476 			/* Inject the authentication data */
    477 			if (buf != NULL)
    478 				memcpy(buf + crd->crd_inject, digest, dlen / 8);
    479 			else if (m != NULL)
    480 				m_copyback(m, crd->crd_inject, dlen / 8,
    481 				    digest);
    482 			else if (uio != NULL)
    483 				memcpy(crp->crp_mac, digest, dlen / 8);
    484 		}
    485 		}
    486 		if (crp->crp_etype != 0)
    487 			break;
    488 	}
    489 
    490 done:
    491 	DPRINTF(("request %08x done\n", (uint32_t)crp));
    492 	crypto_done(crp);
    493 	return 0;
    494 }
    495 
    496 
    497 static int
    498 mvcesa_authentication(struct mvcesa_softc *sc, struct mvcesa_session *ses,
    499 		      uint32_t alg, uint32_t *iv, uint32_t *digest,
    500 		      uint64_t bits, int skip, int len, char *buf,
    501 		      struct mbuf *m, struct uio *uio)
    502 {
    503 	uint32_t cmd, bswp, data = 0;
    504 	int dlen, off, i, s;
    505 
    506 	/*
    507 	 * SHA/MD5 algorithms work in 512-bit chunks, equal to 16 words.
    508 	 */
    509 
    510 	KASSERT(!(len & (512 - 1)) || bits != 0);
    511 	KASSERT(buf != NULL || m != NULL || uio != NULL);
    512 
    513 	cmd = bus_space_read_4(sc->sc_iot, sc->sc_ioh, MVCESA_SHA1MD5I_AC);
    514 	if (!(cmd & MVCESA_SHA1MD5I_AC_TERMINATION))
    515 		return ERESTART;
    516 
    517 	bswp = 0;
    518 	if (alg == MVCESA_SHA1MD5I_AC_ALGORITHM_SHA1) {
    519 		dlen = 160;
    520 		bits = htobe64(bits);
    521 #if BYTE_ORDER == LITTLE_ENDIAN
    522 		bswp = MVCESA_SHA1MD5I_AC_DATABYTESWAP |
    523 		    MVCESA_SHA1MD5I_AC_IVBYTESWAP;
    524 #endif
    525 	} else {	/* MVCESA_SHA1MD5I_AC_ALGORITHM_MD5 */
    526 		dlen = 128;
    527 		bits = htole64(bits);
    528 #if BYTE_ORDER == BIG_ENDIAN
    529 		bswp = MVCESA_SHA1MD5I_AC_DATABYTESWAP |
    530 		    MVCESA_SHA1MD5I_AC_IVBYTESWAP;
    531 #endif
    532 	}
    533 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVCESA_SHA1MD5I_AC,
    534 	    alg | bswp | MVCESA_SHA1MD5I_AC_MODE_USEIV);
    535 
    536 	if (iv != NULL)
    537 		bus_space_write_region_4(sc->sc_iot, sc->sc_ioh,
    538 		    MVCESA_SHA1MD5I_IVDA, iv, dlen / 4);
    539 
    540 	off = i = 0;
    541 	while (1 /* CONSTCOND */) {
    542 		data = 0;
    543 		if (buf != NULL)
    544 			for (i = 0; i < 512 / 8 && off + i < len; i += s) {
    545 				s = uimin(sizeof(data), len - off - i);
    546 				memcpy(&data, buf + skip + off + i, s);
    547 				if (s == sizeof(data))
    548 					bus_space_write_4(sc->sc_iot,
    549 					    sc->sc_ioh, MVCESA_SHA1MD5I_DI,
    550 					    data);
    551 			}
    552 		else if (m != NULL)
    553 			for (i = 0; i < 512 / 8 && off + i < len; i += s) {
    554 				s = uimin(sizeof(data), len - off - i);
    555 				m_copydata(m, skip + off + i, s, &data);
    556 				if (s == sizeof(data))
    557 					bus_space_write_4(sc->sc_iot,
    558 					    sc->sc_ioh, MVCESA_SHA1MD5I_DI,
    559 					    data);
    560 			}
    561 		else if (uio != NULL)
    562 			for (i = 0; i < 512 / 8 && off + i < len; i += s) {
    563 				s = uimin(sizeof(data), len - off - i);
    564 				cuio_copydata(uio, skip + off + i, s, &data);
    565 				if (s == sizeof(data))
    566 					bus_space_write_4(sc->sc_iot,
    567 					    sc->sc_ioh, MVCESA_SHA1MD5I_DI,
    568 					    data);
    569 			}
    570 
    571 		off += i;
    572 		if (i < 512 / 8)
    573 			break;
    574 
    575 		do {
    576 			cmd = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
    577 			    MVCESA_SHA1MD5I_AC);
    578 		} while (!(cmd & MVCESA_SHA1MD5I_AC_TERMINATION));
    579 
    580 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVCESA_SHA1MD5I_AC,
    581 		    alg | bswp | MVCESA_SHA1MD5I_AC_MODE_CONTINUE);
    582 	}
    583 
    584 	if (i < 512 / 8) {
    585 		*((char *)&data + (i % 4)) = 0x80;
    586 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVCESA_SHA1MD5I_DI,
    587 		    data);
    588 		i = (i & ~3) + 4;
    589 
    590 		/* Do pad to 512 bits, if chunk size is more than 448 bits. */
    591 		if (i > 448 / 8) {
    592 			for (; i < 512 / 8; i += 4)
    593 				bus_space_write_4(sc->sc_iot, sc->sc_ioh,
    594 				    MVCESA_SHA1MD5I_DI, 0);
    595 			do {
    596 				cmd = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
    597 				    MVCESA_SHA1MD5I_AC);
    598 			} while (!(cmd & MVCESA_SHA1MD5I_AC_TERMINATION));
    599 			i = 0;
    600 		}
    601 		for (; i < 448 / 8; i += 4)
    602 			bus_space_write_4(sc->sc_iot, sc->sc_ioh,
    603 			    MVCESA_SHA1MD5I_DI, 0);
    604 
    605 		/* Set total bits */
    606 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVCESA_SHA1MD5I_BCL,
    607 		    bits & 0xffffffff);
    608 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVCESA_SHA1MD5I_BCH,
    609 		    bits >> 32);
    610 		do {
    611 			cmd = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
    612 			    MVCESA_SHA1MD5I_AC);
    613 		} while (!(cmd & MVCESA_SHA1MD5I_AC_TERMINATION));
    614 	}
    615 
    616 	if (digest != NULL) {
    617 		/* Read digest */
    618 		bus_space_read_region_4(sc->sc_iot, sc->sc_ioh,
    619 		    MVCESA_SHA1MD5I_IVDA, digest, dlen / 8 / 4);
    620 #if BYTE_ORDER == LITTLE_ENDIAN
    621 		if (alg == MVCESA_SHA1MD5I_AC_ALGORITHM_SHA1)
    622 			for (i = 0; i < dlen / 8 / 4; i++)
    623 				digest[i] = be32toh(digest[i]);
    624 #else
    625 		if (alg == MVCESA_SHA1MD5I_AC_ALGORITHM_MD5)
    626 			for (i = 0; i < dlen / 8 / 4; i++)
    627 				digest[i] = le32toh(digest[i]);
    628 #endif
    629 	}
    630 	return 0;
    631 }
    632 
    633 static int
    634 mvcesa_des_encdec(struct mvcesa_softc *sc, struct mvcesa_session *ses,
    635 		  uint32_t alg, uint32_t mode, uint32_t dir, uint32_t *iv,
    636 		  int skip, int len, char *buf, struct mbuf *m, struct uio *uio)
    637 {
    638 	uint64_t iblk, oblk;
    639 	uint32_t cmd, bswp = 0;
    640 	int i, o, s;
    641 
    642 	KASSERT(buf != NULL || m != NULL || uio != NULL);
    643 
    644 	cmd = bus_space_read_4(sc->sc_iot, sc->sc_ioh, MVCESA_DESE_C);
    645 	if (!(cmd & MVCESA_DESE_C_TERMINATION))
    646 		return ERESTART;
    647 
    648 #if BYTE_ORDER == LITTLE_ENDIAN
    649 	bswp = MVCESA_DESE_C_DATABYTESWAP | MVCESA_DESE_C_IVBYTESWAP   |
    650 	    MVCESA_DESE_C_OUTBYTESWAP;
    651 #endif
    652 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVCESA_DESE_C,
    653 	    dir | alg | mode | bswp | MVCESA_DESE_C_ALLTERMINATION);
    654 
    655 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVCESA_DESE_K0L,
    656 	    ses->ses_key[1]);
    657 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVCESA_DESE_K0H,
    658 	    ses->ses_key[0]);
    659 	if (alg == MVCESA_DESE_C_ALGORITHM_3DES) {
    660 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVCESA_DESE_K1L,
    661 		    ses->ses_key[3]);
    662 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVCESA_DESE_K1H,
    663 		    ses->ses_key[2]);
    664 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVCESA_DESE_K2L,
    665 		    ses->ses_key[5]);
    666 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVCESA_DESE_K2H,
    667 		    ses->ses_key[4]);
    668 	}
    669 
    670 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVCESA_DESE_IVL, iv[1]);
    671 	bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVCESA_DESE_IVH, iv[0]);
    672 
    673 	i = o = 0;
    674 	while (i < len) {
    675 		s = uimin(sizeof(iblk), len - i);
    676 		iblk = 0;
    677 
    678 		if (buf != NULL)
    679 			memcpy(&iblk, buf + skip + i, s);
    680 		else if (m != NULL)
    681 			m_copydata(m, skip + i, s, &iblk);
    682 		else if (uio != NULL)
    683 			cuio_copydata(uio, skip + i, s, &iblk);
    684 
    685 		/*
    686 		 * We have the pipeline that two data enters.
    687 		 */
    688 
    689 		while (1 /* CONSTCOND */) {
    690 			cmd = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
    691 			    MVCESA_DESE_C);
    692 			if (cmd & MVCESA_DESE_C_ALLTERMINATION)
    693 				/* Engine is ready.  Can write two data. */
    694 				break;
    695 			if (cmd & MVCESA_DESE_C_READALLOW) {
    696 				oblk = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
    697 				    MVCESA_DESE_DOH);
    698 				/* XXXX: needs barrier? */
    699 				oblk |= (uint64_t)bus_space_read_4(sc->sc_iot,
    700 				    sc->sc_ioh, MVCESA_DESE_DOL) << 32;
    701 
    702 				if (buf != NULL)
    703 					memcpy(buf + skip + o, &oblk,
    704 					    sizeof(oblk));
    705 				else if (m != NULL)
    706 					m_copydata(m, skip + o, sizeof(oblk),
    707 					    &oblk);
    708 				else if (uio != NULL)
    709 					cuio_copyback(uio, skip + o,
    710 					    sizeof(oblk), &oblk);
    711 				o += sizeof(oblk);
    712 
    713 				/* Can write one data */
    714 				break;
    715 			}
    716 		}
    717 
    718 		/*
    719 		 * Encryption/Decription calculation time is 9 cycles in DES
    720 		 * mode and 25 cycles in 3DES mode.
    721 		 */
    722 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVCESA_DESE_DBL,
    723 		    iblk >> 32);
    724 		/* XXXX: needs barrier? */
    725 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, MVCESA_DESE_DBH,
    726 		    iblk & 0xffffffff);
    727 		i += s;
    728 	}
    729 
    730 	while (1 /* CONSTCOND */) {
    731 		cmd = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
    732 		    MVCESA_DESE_C);
    733 		if (cmd & (MVCESA_DESE_C_READALLOW |
    734 					MVCESA_DESE_C_ALLTERMINATION)) {
    735 			oblk = bus_space_read_4(sc->sc_iot, sc->sc_ioh,
    736 			    MVCESA_DESE_DOH);
    737 			/* XXXX: needs barrier? */
    738 			oblk |= (uint64_t)bus_space_read_4(sc->sc_iot,
    739 			    sc->sc_ioh, MVCESA_DESE_DOL) << 32;
    740 
    741 			if (cmd & MVCESA_DESE_C_ALLTERMINATION) {
    742 				/* We can read IV from Data Out Registers. */
    743 				if (dir == MVCESA_DESE_C_DIRECTION_ENC)
    744 					o -= sizeof(oblk);
    745 				else
    746 					break;
    747 			}
    748 			if (buf != NULL)
    749 				memcpy(buf + skip + o, &oblk, sizeof(oblk));
    750 			else if (m != NULL)
    751 				m_copydata(m, skip + o, sizeof(oblk), &oblk);
    752 			else if (uio != NULL)
    753 				cuio_copyback(uio, skip + o, sizeof(oblk),
    754 				    &oblk);
    755 			o += sizeof(oblk);
    756 			if (cmd & MVCESA_DESE_C_ALLTERMINATION)
    757 				break;
    758 		}
    759 	}
    760 
    761 	return 0;
    762 }
    763