xref: /freebsd/sys/crypto/via/padlock.c (revision 1b0909d5)
1 /*-
2  * Copyright (c) 2005-2008 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <sys/param.h>
31 #include <sys/systm.h>
32 #include <sys/kernel.h>
33 #include <sys/module.h>
34 #include <sys/lock.h>
35 #include <sys/rwlock.h>
36 #include <sys/malloc.h>
37 #include <sys/libkern.h>
38 #if defined(__amd64__) || defined(__i386__)
39 #include <machine/cpufunc.h>
40 #include <machine/cputypes.h>
41 #include <machine/md_var.h>
42 #include <machine/specialreg.h>
43 #endif
44 
45 #include <opencrypto/cryptodev.h>
46 
47 #include <crypto/via/padlock.h>
48 
49 #include <sys/kobj.h>
50 #include <sys/bus.h>
51 #include "cryptodev_if.h"
52 
53 /*
54  * Technical documentation about the PadLock engine can be found here:
55  *
56  * http://www.via.com.tw/en/downloads/whitepapers/initiatives/padlock/programming_guide.pdf
57  */
58 
59 struct padlock_softc {
60 	int32_t		sc_cid;
61 };
62 
63 static int padlock_newsession(device_t, crypto_session_t cses, struct cryptoini *cri);
64 static void padlock_freesession(device_t, crypto_session_t cses);
65 static void padlock_freesession_one(struct padlock_softc *sc,
66     struct padlock_session *ses);
67 static int padlock_process(device_t, struct cryptop *crp, int hint __unused);
68 
69 MALLOC_DEFINE(M_PADLOCK, "padlock_data", "PadLock Data");
70 
71 static void
72 padlock_identify(driver_t *drv, device_t parent)
73 {
74 	/* NB: order 10 is so we get attached after h/w devices */
75 	if (device_find_child(parent, "padlock", -1) == NULL &&
76 	    BUS_ADD_CHILD(parent, 10, "padlock", -1) == 0)
77 		panic("padlock: could not attach");
78 }
79 
80 static int
81 padlock_probe(device_t dev)
82 {
83 	char capp[256];
84 
85 #if defined(__amd64__) || defined(__i386__)
86 	/* If there is no AES support, we has nothing to do here. */
87 	if (!(via_feature_xcrypt & VIA_HAS_AES)) {
88 		device_printf(dev, "No ACE support.\n");
89 		return (EINVAL);
90 	}
91 	strlcpy(capp, "AES-CBC", sizeof(capp));
92 #if 0
93 	strlcat(capp, ",AES-EBC", sizeof(capp));
94 	strlcat(capp, ",AES-CFB", sizeof(capp));
95 	strlcat(capp, ",AES-OFB", sizeof(capp));
96 #endif
97 	if (via_feature_xcrypt & VIA_HAS_SHA) {
98 		strlcat(capp, ",SHA1", sizeof(capp));
99 		strlcat(capp, ",SHA256", sizeof(capp));
100 	}
101 #if 0
102 	if (via_feature_xcrypt & VIA_HAS_AESCTR)
103 		strlcat(capp, ",AES-CTR", sizeof(capp));
104 	if (via_feature_xcrypt & VIA_HAS_MM)
105 		strlcat(capp, ",RSA", sizeof(capp));
106 #endif
107 	device_set_desc_copy(dev, capp);
108 	return (0);
109 #else
110 	return (EINVAL);
111 #endif
112 }
113 
114 static int
115 padlock_attach(device_t dev)
116 {
117 	struct padlock_softc *sc = device_get_softc(dev);
118 
119 	sc->sc_cid = crypto_get_driverid(dev, sizeof(struct padlock_session),
120 	    CRYPTOCAP_F_HARDWARE);
121 	if (sc->sc_cid < 0) {
122 		device_printf(dev, "Could not get crypto driver id.\n");
123 		return (ENOMEM);
124 	}
125 
126 	crypto_register(sc->sc_cid, CRYPTO_AES_CBC, 0, 0);
127 	crypto_register(sc->sc_cid, CRYPTO_MD5_HMAC, 0, 0);
128 	crypto_register(sc->sc_cid, CRYPTO_SHA1_HMAC, 0, 0);
129 	crypto_register(sc->sc_cid, CRYPTO_RIPEMD160_HMAC, 0, 0);
130 	crypto_register(sc->sc_cid, CRYPTO_SHA2_256_HMAC, 0, 0);
131 	crypto_register(sc->sc_cid, CRYPTO_SHA2_384_HMAC, 0, 0);
132 	crypto_register(sc->sc_cid, CRYPTO_SHA2_512_HMAC, 0, 0);
133 	return (0);
134 }
135 
136 static int
137 padlock_detach(device_t dev)
138 {
139 	struct padlock_softc *sc = device_get_softc(dev);
140 
141 	crypto_unregister_all(sc->sc_cid);
142 	return (0);
143 }
144 
145 static int
146 padlock_newsession(device_t dev, crypto_session_t cses, struct cryptoini *cri)
147 {
148 	struct padlock_softc *sc = device_get_softc(dev);
149 	struct padlock_session *ses = NULL;
150 	struct cryptoini *encini, *macini;
151 	struct thread *td;
152 	int error;
153 
154 	if (cri == NULL)
155 		return (EINVAL);
156 
157 	encini = macini = NULL;
158 	for (; cri != NULL; cri = cri->cri_next) {
159 		switch (cri->cri_alg) {
160 		case CRYPTO_NULL_HMAC:
161 		case CRYPTO_MD5_HMAC:
162 		case CRYPTO_SHA1_HMAC:
163 		case CRYPTO_RIPEMD160_HMAC:
164 		case CRYPTO_SHA2_256_HMAC:
165 		case CRYPTO_SHA2_384_HMAC:
166 		case CRYPTO_SHA2_512_HMAC:
167 			if (macini != NULL)
168 				return (EINVAL);
169 			macini = cri;
170 			break;
171 		case CRYPTO_AES_CBC:
172 			if (encini != NULL)
173 				return (EINVAL);
174 			encini = cri;
175 			break;
176 		default:
177 			return (EINVAL);
178 		}
179 	}
180 
181 	/*
182 	 * We only support HMAC algorithms to be able to work with
183 	 * ipsec(4), so if we are asked only for authentication without
184 	 * encryption, don't pretend we can accellerate it.
185 	 */
186 	if (encini == NULL)
187 		return (EINVAL);
188 
189 	ses = crypto_get_driver_session(cses);
190 	ses->ses_fpu_ctx = fpu_kern_alloc_ctx(FPU_KERN_NORMAL);
191 
192 	error = padlock_cipher_setup(ses, encini);
193 	if (error != 0) {
194 		padlock_freesession_one(sc, ses);
195 		return (error);
196 	}
197 
198 	if (macini != NULL) {
199 		td = curthread;
200 		fpu_kern_enter(td, ses->ses_fpu_ctx, FPU_KERN_NORMAL |
201 		    FPU_KERN_KTHR);
202 		error = padlock_hash_setup(ses, macini);
203 		fpu_kern_leave(td, ses->ses_fpu_ctx);
204 		if (error != 0) {
205 			padlock_freesession_one(sc, ses);
206 			return (error);
207 		}
208 	}
209 
210 	return (0);
211 }
212 
213 static void
214 padlock_freesession_one(struct padlock_softc *sc, struct padlock_session *ses)
215 {
216 
217 	padlock_hash_free(ses);
218 	fpu_kern_free_ctx(ses->ses_fpu_ctx);
219 }
220 
221 static void
222 padlock_freesession(device_t dev, crypto_session_t cses)
223 {
224 	struct padlock_softc *sc = device_get_softc(dev);
225 	struct padlock_session *ses;
226 
227 	ses = crypto_get_driver_session(cses);
228 	padlock_freesession_one(sc, ses);
229 }
230 
231 static int
232 padlock_process(device_t dev, struct cryptop *crp, int hint __unused)
233 {
234 	struct padlock_session *ses = NULL;
235 	struct cryptodesc *crd, *enccrd, *maccrd;
236 	int error = 0;
237 
238 	enccrd = maccrd = NULL;
239 
240 	/* Sanity check. */
241 	if (crp == NULL)
242 		return (EINVAL);
243 
244 	if (crp->crp_callback == NULL || crp->crp_desc == NULL) {
245 		error = EINVAL;
246 		goto out;
247 	}
248 
249 	for (crd = crp->crp_desc; crd != NULL; crd = crd->crd_next) {
250 		switch (crd->crd_alg) {
251 		case CRYPTO_NULL_HMAC:
252 		case CRYPTO_MD5_HMAC:
253 		case CRYPTO_SHA1_HMAC:
254 		case CRYPTO_RIPEMD160_HMAC:
255 		case CRYPTO_SHA2_256_HMAC:
256 		case CRYPTO_SHA2_384_HMAC:
257 		case CRYPTO_SHA2_512_HMAC:
258 			if (maccrd != NULL) {
259 				error = EINVAL;
260 				goto out;
261 			}
262 			maccrd = crd;
263 			break;
264 		case CRYPTO_AES_CBC:
265 			if (enccrd != NULL) {
266 				error = EINVAL;
267 				goto out;
268 			}
269 			enccrd = crd;
270 			break;
271 		default:
272 			return (EINVAL);
273 		}
274 	}
275 	if (enccrd == NULL || (enccrd->crd_len % AES_BLOCK_LEN) != 0) {
276 		error = EINVAL;
277 		goto out;
278 	}
279 
280 	ses = crypto_get_driver_session(crp->crp_session);
281 
282 	/* Perform data authentication if requested before encryption. */
283 	if (maccrd != NULL && maccrd->crd_next == enccrd) {
284 		error = padlock_hash_process(ses, maccrd, crp);
285 		if (error != 0)
286 			goto out;
287 	}
288 
289 	error = padlock_cipher_process(ses, enccrd, crp);
290 	if (error != 0)
291 		goto out;
292 
293 	/* Perform data authentication if requested after encryption. */
294 	if (maccrd != NULL && enccrd->crd_next == maccrd) {
295 		error = padlock_hash_process(ses, maccrd, crp);
296 		if (error != 0)
297 			goto out;
298 	}
299 
300 out:
301 #if 0
302 	/*
303 	 * This code is not necessary, because contexts will be freed on next
304 	 * padlock_setup_mackey() call or at padlock_freesession() call.
305 	 */
306 	if (ses != NULL && maccrd != NULL &&
307 	    (maccrd->crd_flags & CRD_F_KEY_EXPLICIT) != 0) {
308 		padlock_free_ctx(ses->ses_axf, ses->ses_ictx);
309 		padlock_free_ctx(ses->ses_axf, ses->ses_octx);
310 	}
311 #endif
312 	crp->crp_etype = error;
313 	crypto_done(crp);
314 	return (error);
315 }
316 
317 static device_method_t padlock_methods[] = {
318 	DEVMETHOD(device_identify,	padlock_identify),
319 	DEVMETHOD(device_probe,		padlock_probe),
320 	DEVMETHOD(device_attach,	padlock_attach),
321 	DEVMETHOD(device_detach,	padlock_detach),
322 
323 	DEVMETHOD(cryptodev_newsession,	padlock_newsession),
324 	DEVMETHOD(cryptodev_freesession,padlock_freesession),
325 	DEVMETHOD(cryptodev_process,	padlock_process),
326 
327 	{0, 0},
328 };
329 
330 static driver_t padlock_driver = {
331 	"padlock",
332 	padlock_methods,
333 	sizeof(struct padlock_softc),
334 };
335 static devclass_t padlock_devclass;
336 
337 /* XXX where to attach */
338 DRIVER_MODULE(padlock, nexus, padlock_driver, padlock_devclass, 0, 0);
339 MODULE_VERSION(padlock, 1);
340 MODULE_DEPEND(padlock, crypto, 1, 1, 1);
341