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