1 /*
2  * Octeon Crypto for OCF
3  *
4  * Written by David McCullough <[email protected]>
5  * Copyright (C) 2009 David McCullough
6  *
7  * LICENSE TERMS
8  *
9  * The free distribution and use of this software in both source and binary
10  * form is allowed (with or without changes) provided that:
11  *
12  *   1. distributions of this source code include the above copyright
13  *      notice, this list of conditions and the following disclaimer;
14  *
15  *   2. distributions in binary form include the above copyright
16  *      notice, this list of conditions and the following disclaimer
17  *      in the documentation and/or other associated materials;
18  *
19  *   3. the copyright holder's name is not used to endorse products
20  *      built using this software without specific written permission.
21  *
22  * DISCLAIMER
23  *
24  * This software is provided 'as is' with no explicit or implied warranties
25  * in respect of its properties, including, but not limited to, correctness
26  * and/or fitness for purpose.
27  * ---------------------------------------------------------------------------
28  */
29 
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32 
33 #include <sys/param.h>
34 #include <sys/systm.h>
35 #include <sys/bus.h>
36 #include <sys/kernel.h>
37 #include <sys/module.h>
38 #include <sys/malloc.h>
39 #include <sys/mbuf.h>
40 #include <sys/uio.h>
41 
42 #include <opencrypto/cryptodev.h>
43 
44 #include <contrib/octeon-sdk/cvmx.h>
45 
46 #include <mips/cavium/cryptocteon/cryptocteonvar.h>
47 
48 #include "cryptodev_if.h"
49 
50 struct cryptocteon_softc {
51 	int32_t			sc_cid;		/* opencrypto id */
52 };
53 
54 int cryptocteon_debug = 0;
55 TUNABLE_INT("hw.cryptocteon.debug", &cryptocteon_debug);
56 
57 static void cryptocteon_identify(driver_t *, device_t);
58 static int cryptocteon_probe(device_t);
59 static int cryptocteon_attach(device_t);
60 
61 static int cryptocteon_process(device_t, struct cryptop *, int);
62 static int cryptocteon_newsession(device_t, crypto_session_t, struct cryptoini *);
63 
64 static void
cryptocteon_identify(driver_t * drv,device_t parent)65 cryptocteon_identify(driver_t *drv, device_t parent)
66 {
67 	if (octeon_has_feature(OCTEON_FEATURE_CRYPTO))
68 		BUS_ADD_CHILD(parent, 0, "cryptocteon", 0);
69 }
70 
71 static int
cryptocteon_probe(device_t dev)72 cryptocteon_probe(device_t dev)
73 {
74 	device_set_desc(dev, "Octeon Secure Coprocessor");
75 	return (0);
76 }
77 
78 static int
cryptocteon_attach(device_t dev)79 cryptocteon_attach(device_t dev)
80 {
81 	struct cryptocteon_softc *sc;
82 
83 	sc = device_get_softc(dev);
84 
85 	sc->sc_cid = crypto_get_driverid(dev, sizeof(struct octo_sess),
86 	    CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SYNC);
87 	if (sc->sc_cid < 0) {
88 		device_printf(dev, "crypto_get_driverid ret %d\n", sc->sc_cid);
89 		return (ENXIO);
90 	}
91 
92 	crypto_register(sc->sc_cid, CRYPTO_MD5_HMAC, 0, 0);
93 	crypto_register(sc->sc_cid, CRYPTO_SHA1_HMAC, 0, 0);
94 	crypto_register(sc->sc_cid, CRYPTO_DES_CBC, 0, 0);
95 	crypto_register(sc->sc_cid, CRYPTO_3DES_CBC, 0, 0);
96 	crypto_register(sc->sc_cid, CRYPTO_AES_CBC, 0, 0);
97 
98 	return (0);
99 }
100 
101 /*
102  * Generate a new octo session.  We artifically limit it to a single
103  * hash/cipher or hash-cipher combo just to make it easier, most callers
104  * do not expect more than this anyway.
105  */
106 static int
cryptocteon_newsession(device_t dev,crypto_session_t cses,struct cryptoini * cri)107 cryptocteon_newsession(device_t dev, crypto_session_t cses,
108     struct cryptoini *cri)
109 {
110 	struct cryptoini *c, *encini = NULL, *macini = NULL;
111 	struct cryptocteon_softc *sc;
112 	struct octo_sess *ocd;
113 	int i;
114 
115 	sc = device_get_softc(dev);
116 
117 	if (cri == NULL || sc == NULL)
118 		return (EINVAL);
119 
120 	/*
121 	 * To keep it simple, we only handle hash, cipher or hash/cipher in a
122 	 * session,  you cannot currently do multiple ciphers/hashes in one
123 	 * session even though it would be possibel to code this driver to
124 	 * handle it.
125 	 */
126 	for (i = 0, c = cri; c && i < 2; i++) {
127 		if (c->cri_alg == CRYPTO_MD5_HMAC ||
128 		    c->cri_alg == CRYPTO_SHA1_HMAC ||
129 		    c->cri_alg == CRYPTO_NULL_HMAC) {
130 			if (macini) {
131 				break;
132 			}
133 			macini = c;
134 		}
135 		if (c->cri_alg == CRYPTO_DES_CBC ||
136 		    c->cri_alg == CRYPTO_3DES_CBC ||
137 		    c->cri_alg == CRYPTO_AES_CBC ||
138 		    c->cri_alg == CRYPTO_NULL_CBC) {
139 			if (encini) {
140 				break;
141 			}
142 			encini = c;
143 		}
144 		c = c->cri_next;
145 	}
146 	if (!macini && !encini) {
147 		dprintf("%s,%d - EINVAL bad cipher/hash or combination\n",
148 				__FILE__, __LINE__);
149 		return EINVAL;
150 	}
151 	if (c) {
152 		dprintf("%s,%d - EINVAL cannot handle chained cipher/hash combos\n",
153 				__FILE__, __LINE__);
154 		return EINVAL;
155 	}
156 
157 	/*
158 	 * So we have something we can do, lets setup the session
159 	 */
160 	ocd = crypto_get_driver_session(cses);
161 
162 	if (encini && encini->cri_key) {
163 		ocd->octo_encklen = (encini->cri_klen + 7) / 8;
164 		memcpy(ocd->octo_enckey, encini->cri_key, ocd->octo_encklen);
165 	}
166 
167 	if (macini && macini->cri_key) {
168 		ocd->octo_macklen = (macini->cri_klen + 7) / 8;
169 		memcpy(ocd->octo_mackey, macini->cri_key, ocd->octo_macklen);
170 	}
171 
172 	ocd->octo_mlen = 0;
173 	if (encini && encini->cri_mlen)
174 		ocd->octo_mlen = encini->cri_mlen;
175 	else if (macini && macini->cri_mlen)
176 		ocd->octo_mlen = macini->cri_mlen;
177 	else
178 		ocd->octo_mlen = 12;
179 
180 	/*
181 	 * point c at the enc if it exists, otherwise the mac
182 	 */
183 	c = encini ? encini : macini;
184 
185 	switch (c->cri_alg) {
186 	case CRYPTO_DES_CBC:
187 	case CRYPTO_3DES_CBC:
188 		ocd->octo_ivsize  = 8;
189 		switch (macini ? macini->cri_alg : -1) {
190 		case CRYPTO_MD5_HMAC:
191 			ocd->octo_encrypt = octo_des_cbc_md5_encrypt;
192 			ocd->octo_decrypt = octo_des_cbc_md5_decrypt;
193 			octo_calc_hash(0, macini->cri_key, ocd->octo_hminner,
194 					ocd->octo_hmouter);
195 			break;
196 		case CRYPTO_SHA1_HMAC:
197 			ocd->octo_encrypt = octo_des_cbc_sha1_encrypt;
198 			ocd->octo_decrypt = octo_des_cbc_sha1_encrypt;
199 			octo_calc_hash(1, macini->cri_key, ocd->octo_hminner,
200 					ocd->octo_hmouter);
201 			break;
202 		case -1:
203 			ocd->octo_encrypt = octo_des_cbc_encrypt;
204 			ocd->octo_decrypt = octo_des_cbc_decrypt;
205 			break;
206 		default:
207 			dprintf("%s,%d: EINVALn", __FILE__, __LINE__);
208 			return EINVAL;
209 		}
210 		break;
211 	case CRYPTO_AES_CBC:
212 		ocd->octo_ivsize  = 16;
213 		switch (macini ? macini->cri_alg : -1) {
214 		case CRYPTO_MD5_HMAC:
215 			ocd->octo_encrypt = octo_aes_cbc_md5_encrypt;
216 			ocd->octo_decrypt = octo_aes_cbc_md5_decrypt;
217 			octo_calc_hash(0, macini->cri_key, ocd->octo_hminner,
218 					ocd->octo_hmouter);
219 			break;
220 		case CRYPTO_SHA1_HMAC:
221 			ocd->octo_encrypt = octo_aes_cbc_sha1_encrypt;
222 			ocd->octo_decrypt = octo_aes_cbc_sha1_decrypt;
223 			octo_calc_hash(1, macini->cri_key, ocd->octo_hminner,
224 					ocd->octo_hmouter);
225 			break;
226 		case -1:
227 			ocd->octo_encrypt = octo_aes_cbc_encrypt;
228 			ocd->octo_decrypt = octo_aes_cbc_decrypt;
229 			break;
230 		default:
231 			dprintf("%s,%d: EINVALn", __FILE__, __LINE__);
232 			return EINVAL;
233 		}
234 		break;
235 	case CRYPTO_MD5_HMAC:
236 		ocd->octo_encrypt = octo_null_md5_encrypt;
237 		ocd->octo_decrypt = octo_null_md5_encrypt;
238 		octo_calc_hash(0, macini->cri_key, ocd->octo_hminner,
239 				ocd->octo_hmouter);
240 		break;
241 	case CRYPTO_SHA1_HMAC:
242 		ocd->octo_encrypt = octo_null_sha1_encrypt;
243 		ocd->octo_decrypt = octo_null_sha1_encrypt;
244 		octo_calc_hash(1, macini->cri_key, ocd->octo_hminner,
245 				ocd->octo_hmouter);
246 		break;
247 	default:
248 		dprintf("%s,%d: EINVALn", __FILE__, __LINE__);
249 		return EINVAL;
250 	}
251 
252 	ocd->octo_encalg = encini ? encini->cri_alg : -1;
253 	ocd->octo_macalg = macini ? macini->cri_alg : -1;
254 
255 	return (0);
256 }
257 
258 /*
259  * Process a request.
260  */
261 static int
cryptocteon_process(device_t dev,struct cryptop * crp,int hint)262 cryptocteon_process(device_t dev, struct cryptop *crp, int hint)
263 {
264 	struct cryptodesc *crd;
265 	struct octo_sess *od;
266 	size_t iovcnt, iovlen;
267 	struct mbuf *m = NULL;
268 	struct uio *uiop = NULL;
269 	struct cryptodesc *enccrd = NULL, *maccrd = NULL;
270 	unsigned char *ivp = NULL;
271 	unsigned char iv_data[HASH_MAX_LEN];
272 	int auth_off = 0, auth_len = 0, crypt_off = 0, crypt_len = 0, icv_off = 0;
273 	struct cryptocteon_softc *sc;
274 
275 	sc = device_get_softc(dev);
276 
277 	if (sc == NULL || crp == NULL)
278 		return EINVAL;
279 
280 	crp->crp_etype = 0;
281 
282 	if (crp->crp_desc == NULL || crp->crp_buf == NULL) {
283 		dprintf("%s,%d: EINVAL\n", __FILE__, __LINE__);
284 		crp->crp_etype = EINVAL;
285 		goto done;
286 	}
287 
288 	od = crypto_get_driver_session(crp->crp_session);
289 
290 	/*
291 	 * do some error checking outside of the loop for m and IOV processing
292 	 * this leaves us with valid m or uiop pointers for later
293 	 */
294 	if (crp->crp_flags & CRYPTO_F_IMBUF) {
295 		unsigned frags;
296 
297 		m = (struct mbuf *) crp->crp_buf;
298 		for (frags = 0; m != NULL; frags++)
299 			m = m->m_next;
300 
301 		if (frags >= UIO_MAXIOV) {
302 			printf("%s,%d: %d frags > UIO_MAXIOV", __FILE__, __LINE__, frags);
303 			goto done;
304 		}
305 
306 		m = (struct mbuf *) crp->crp_buf;
307 	} else if (crp->crp_flags & CRYPTO_F_IOV) {
308 		uiop = (struct uio *) crp->crp_buf;
309 		if (uiop->uio_iovcnt > UIO_MAXIOV) {
310 			printf("%s,%d: %d uio_iovcnt > UIO_MAXIOV", __FILE__, __LINE__,
311 			       uiop->uio_iovcnt);
312 			goto done;
313 		}
314 	}
315 
316 	/* point our enccrd and maccrd appropriately */
317 	crd = crp->crp_desc;
318 	if (crd->crd_alg == od->octo_encalg)
319 		enccrd = crd;
320 	if (crd->crd_alg == od->octo_macalg)
321 		maccrd = crd;
322 	crd = crd->crd_next;
323 	if (crd) {
324 		if (crd->crd_alg == od->octo_encalg)
325 			enccrd = crd;
326 		if (crd->crd_alg == od->octo_macalg)
327 			maccrd = crd;
328 		crd = crd->crd_next;
329 	}
330 	if (crd) {
331 		crp->crp_etype = EINVAL;
332 		dprintf("%s,%d: ENOENT - descriptors do not match session\n",
333 				__FILE__, __LINE__);
334 		goto done;
335 	}
336 
337 	if (enccrd) {
338 		if (enccrd->crd_flags & CRD_F_IV_EXPLICIT) {
339 			ivp = enccrd->crd_iv;
340 		} else {
341 			ivp = iv_data;
342 			crypto_copydata(crp->crp_flags, crp->crp_buf,
343 					enccrd->crd_inject, od->octo_ivsize, (caddr_t) ivp);
344 		}
345 
346 		if (maccrd) {
347 			auth_off = maccrd->crd_skip;
348 			auth_len = maccrd->crd_len;
349 			icv_off  = maccrd->crd_inject;
350 		}
351 
352 		crypt_off = enccrd->crd_skip;
353 		crypt_len = enccrd->crd_len;
354 	} else { /* if (maccrd) */
355 		auth_off = maccrd->crd_skip;
356 		auth_len = maccrd->crd_len;
357 		icv_off  = maccrd->crd_inject;
358 	}
359 
360 	/*
361 	 * setup the I/O vector to cover the buffer
362 	 */
363 	if (crp->crp_flags & CRYPTO_F_IMBUF) {
364 		iovcnt = 0;
365 		iovlen = 0;
366 
367 		while (m != NULL) {
368 			od->octo_iov[iovcnt].iov_base = mtod(m, void *);
369 			od->octo_iov[iovcnt].iov_len = m->m_len;
370 
371 			m = m->m_next;
372 			iovlen += od->octo_iov[iovcnt++].iov_len;
373 		}
374 	} else if (crp->crp_flags & CRYPTO_F_IOV) {
375 		iovlen = 0;
376 		for (iovcnt = 0; iovcnt < uiop->uio_iovcnt; iovcnt++) {
377 			od->octo_iov[iovcnt].iov_base = uiop->uio_iov[iovcnt].iov_base;
378 			od->octo_iov[iovcnt].iov_len = uiop->uio_iov[iovcnt].iov_len;
379 
380 			iovlen += od->octo_iov[iovcnt].iov_len;
381 		}
382 	} else {
383 		iovlen = crp->crp_ilen;
384 		od->octo_iov[0].iov_base = crp->crp_buf;
385 		od->octo_iov[0].iov_len = crp->crp_ilen;
386 		iovcnt = 1;
387 	}
388 
389 
390 	/*
391 	 * setup a new explicit key
392 	 */
393 	if (enccrd) {
394 		if (enccrd->crd_flags & CRD_F_KEY_EXPLICIT) {
395 			od->octo_encklen = (enccrd->crd_klen + 7) / 8;
396 			memcpy(od->octo_enckey, enccrd->crd_key, od->octo_encklen);
397 		}
398 	}
399 	if (maccrd) {
400 		if (maccrd->crd_flags & CRD_F_KEY_EXPLICIT) {
401 			od->octo_macklen = (maccrd->crd_klen + 7) / 8;
402 			memcpy(od->octo_mackey, maccrd->crd_key, od->octo_macklen);
403 			od->octo_mackey_set = 0;
404 		}
405 		if (!od->octo_mackey_set) {
406 			octo_calc_hash(maccrd->crd_alg == CRYPTO_MD5_HMAC ? 0 : 1,
407 				maccrd->crd_key, od->octo_hminner, od->octo_hmouter);
408 			od->octo_mackey_set = 1;
409 		}
410 	}
411 
412 
413 	if (!enccrd || (enccrd->crd_flags & CRD_F_ENCRYPT))
414 		(*od->octo_encrypt)(od, od->octo_iov, iovcnt, iovlen,
415 				auth_off, auth_len, crypt_off, crypt_len, icv_off, ivp);
416 	else
417 		(*od->octo_decrypt)(od, od->octo_iov, iovcnt, iovlen,
418 				auth_off, auth_len, crypt_off, crypt_len, icv_off, ivp);
419 
420 done:
421 	crypto_done(crp);
422 	return (0);
423 }
424 
425 static device_method_t cryptocteon_methods[] = {
426 	/* device methods */
427 	DEVMETHOD(device_identify,	cryptocteon_identify),
428 	DEVMETHOD(device_probe,		cryptocteon_probe),
429 	DEVMETHOD(device_attach,	cryptocteon_attach),
430 
431 	/* crypto device methods */
432 	DEVMETHOD(cryptodev_newsession,	cryptocteon_newsession),
433 	DEVMETHOD(cryptodev_process,	cryptocteon_process),
434 
435 	{ 0, 0 }
436 };
437 
438 static driver_t cryptocteon_driver = {
439 	"cryptocteon",
440 	cryptocteon_methods,
441 	sizeof (struct cryptocteon_softc),
442 };
443 static devclass_t cryptocteon_devclass;
444 DRIVER_MODULE(cryptocteon, nexus, cryptocteon_driver, cryptocteon_devclass, 0, 0);
445