padlock.c revision 188171
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: head/sys/crypto/via/padlock.c 188171 2009-02-05 19:30:28Z imp $");
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__) && !defined(PC98))
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
59struct padlock_softc {
60	int32_t		sc_cid;
61	uint32_t	sc_sid;
62	TAILQ_HEAD(padlock_sessions_head, padlock_session) sc_sessions;
63	struct rwlock	sc_sessions_lock;
64};
65
66static int padlock_newsession(device_t, uint32_t *sidp, struct cryptoini *cri);
67static int padlock_freesession(device_t, uint64_t tid);
68static void padlock_freesession_one(struct padlock_softc *sc,
69    struct padlock_session *ses, int locked);
70static int padlock_process(device_t, struct cryptop *crp, int hint __unused);
71
72MALLOC_DEFINE(M_PADLOCK, "padlock_data", "PadLock Data");
73
74static void
75padlock_identify(driver_t *drv, device_t parent)
76{
77	/* NB: order 10 is so we get attached after h/w devices */
78	if (device_find_child(parent, "padlock", -1) == NULL &&
79	    BUS_ADD_CHILD(parent, 10, "padlock", -1) == 0)
80		panic("padlock: could not attach");
81}
82
83static int
84padlock_probe(device_t dev)
85{
86	char capp[256];
87
88#if defined(__amd64__) || (defined(__i386__) && !defined(PC98))
89	/* If there is no AES support, we has nothing to do here. */
90	if (!(via_feature_xcrypt & VIA_HAS_AES)) {
91		device_printf(dev, "No ACE support.\n");
92		return (EINVAL);
93	}
94	strlcpy(capp, "AES-CBC", sizeof(capp));
95#if 0
96	strlcat(capp, ",AES-EBC", sizeof(capp));
97	strlcat(capp, ",AES-CFB", sizeof(capp));
98	strlcat(capp, ",AES-OFB", sizeof(capp));
99#endif
100	if (via_feature_xcrypt & VIA_HAS_SHA) {
101		strlcat(capp, ",SHA1", sizeof(capp));
102		strlcat(capp, ",SHA256", sizeof(capp));
103	}
104#if 0
105	if (via_feature_xcrypt & VIA_HAS_AESCTR)
106		strlcat(capp, ",AES-CTR", sizeof(capp));
107	if (via_feature_xcrypt & VIA_HAS_MM)
108		strlcat(capp, ",RSA", sizeof(capp));
109#endif
110	device_set_desc_copy(dev, capp);
111	return (0);
112#else
113	return (EINVAL);
114#endif
115}
116
117static int
118padlock_attach(device_t dev)
119{
120	struct padlock_softc *sc = device_get_softc(dev);
121
122	TAILQ_INIT(&sc->sc_sessions);
123	sc->sc_sid = 1;
124
125	sc->sc_cid = crypto_get_driverid(dev, CRYPTOCAP_F_HARDWARE);
126	if (sc->sc_cid < 0) {
127		device_printf(dev, "Could not get crypto driver id.\n");
128		return (ENOMEM);
129	}
130
131	rw_init(&sc->sc_sessions_lock, "padlock_lock");
132	crypto_register(sc->sc_cid, CRYPTO_AES_CBC, 0, 0);
133	crypto_register(sc->sc_cid, CRYPTO_MD5_HMAC, 0, 0);
134	crypto_register(sc->sc_cid, CRYPTO_SHA1_HMAC, 0, 0);
135	crypto_register(sc->sc_cid, CRYPTO_RIPEMD160_HMAC, 0, 0);
136	crypto_register(sc->sc_cid, CRYPTO_SHA2_256_HMAC, 0, 0);
137	crypto_register(sc->sc_cid, CRYPTO_SHA2_384_HMAC, 0, 0);
138	crypto_register(sc->sc_cid, CRYPTO_SHA2_512_HMAC, 0, 0);
139	return (0);
140}
141
142static int
143padlock_detach(device_t dev)
144{
145	struct padlock_softc *sc = device_get_softc(dev);
146	struct padlock_session *ses;
147
148	rw_wlock(&sc->sc_sessions_lock);
149	TAILQ_FOREACH(ses, &sc->sc_sessions, ses_next) {
150		if (ses->ses_used) {
151			rw_wunlock(&sc->sc_sessions_lock);
152			device_printf(dev,
153			    "Cannot detach, sessions still active.\n");
154			return (EBUSY);
155		}
156	}
157	while ((ses = TAILQ_FIRST(&sc->sc_sessions)) != NULL) {
158		TAILQ_REMOVE(&sc->sc_sessions, ses, ses_next);
159		free(ses, M_PADLOCK);
160	}
161	rw_destroy(&sc->sc_sessions_lock);
162	crypto_unregister_all(sc->sc_cid);
163	return (0);
164}
165
166static int
167padlock_newsession(device_t dev, uint32_t *sidp, struct cryptoini *cri)
168{
169	struct padlock_softc *sc = device_get_softc(dev);
170	struct padlock_session *ses = NULL;
171	struct cryptoini *encini, *macini;
172	int error;
173
174	if (sidp == NULL || cri == NULL)
175		return (EINVAL);
176
177	encini = macini = NULL;
178	for (; cri != NULL; cri = cri->cri_next) {
179		switch (cri->cri_alg) {
180		case CRYPTO_NULL_HMAC:
181		case CRYPTO_MD5_HMAC:
182		case CRYPTO_SHA1_HMAC:
183		case CRYPTO_RIPEMD160_HMAC:
184		case CRYPTO_SHA2_256_HMAC:
185		case CRYPTO_SHA2_384_HMAC:
186		case CRYPTO_SHA2_512_HMAC:
187			if (macini != NULL)
188				return (EINVAL);
189			macini = cri;
190			break;
191		case CRYPTO_AES_CBC:
192			if (encini != NULL)
193				return (EINVAL);
194			encini = cri;
195			break;
196		default:
197			return (EINVAL);
198		}
199	}
200
201	/*
202	 * We only support HMAC algorithms to be able to work with
203	 * ipsec(4), so if we are asked only for authentication without
204	 * encryption, don't pretend we can accellerate it.
205	 */
206	if (encini == NULL)
207		return (EINVAL);
208
209	/*
210	 * Let's look for a free session structure.
211	 */
212	rw_wlock(&sc->sc_sessions_lock);
213	/*
214	 * Free sessions goes first, so if first session is used, we need to
215	 * allocate one.
216	 */
217	ses = TAILQ_FIRST(&sc->sc_sessions);
218	if (ses == NULL || ses->ses_used) {
219		ses = malloc(sizeof(*ses), M_PADLOCK, M_NOWAIT | M_ZERO);
220		if (ses == NULL) {
221			rw_wunlock(&sc->sc_sessions_lock);
222			return (ENOMEM);
223		}
224		ses->ses_id = sc->sc_sid++;
225	} else {
226		TAILQ_REMOVE(&sc->sc_sessions, ses, ses_next);
227	}
228	ses->ses_used = 1;
229	TAILQ_INSERT_TAIL(&sc->sc_sessions, ses, ses_next);
230	rw_wunlock(&sc->sc_sessions_lock);
231
232	error = padlock_cipher_setup(ses, encini);
233	if (error != 0) {
234		padlock_freesession_one(sc, ses, 0);
235		return (error);
236	}
237
238	if (macini != NULL) {
239		error = padlock_hash_setup(ses, macini);
240		if (error != 0) {
241			padlock_freesession_one(sc, ses, 0);
242			return (error);
243		}
244	}
245
246	*sidp = ses->ses_id;
247	return (0);
248}
249
250static void
251padlock_freesession_one(struct padlock_softc *sc, struct padlock_session *ses,
252    int locked)
253{
254	uint32_t sid = ses->ses_id;
255
256	if (!locked)
257		rw_wlock(&sc->sc_sessions_lock);
258	TAILQ_REMOVE(&sc->sc_sessions, ses, ses_next);
259	padlock_hash_free(ses);
260	bzero(ses, sizeof(*ses));
261	ses->ses_used = 0;
262	ses->ses_id = sid;
263	TAILQ_INSERT_HEAD(&sc->sc_sessions, ses, ses_next);
264	if (!locked)
265		rw_wunlock(&sc->sc_sessions_lock);
266}
267
268static int
269padlock_freesession(device_t dev, uint64_t tid)
270{
271	struct padlock_softc *sc = device_get_softc(dev);
272	struct padlock_session *ses;
273	uint32_t sid = ((uint32_t)tid) & 0xffffffff;
274
275	rw_wlock(&sc->sc_sessions_lock);
276	TAILQ_FOREACH_REVERSE(ses, &sc->sc_sessions, padlock_sessions_head,
277	    ses_next) {
278		if (ses->ses_id == sid)
279			break;
280	}
281	if (ses == NULL) {
282		rw_wunlock(&sc->sc_sessions_lock);
283		return (EINVAL);
284	}
285	padlock_freesession_one(sc, ses, 1);
286	rw_wunlock(&sc->sc_sessions_lock);
287	return (0);
288}
289
290static int
291padlock_process(device_t dev, struct cryptop *crp, int hint __unused)
292{
293	struct padlock_softc *sc = device_get_softc(dev);
294	struct padlock_session *ses = NULL;
295	struct cryptodesc *crd, *enccrd, *maccrd;
296	int error = 0;
297
298	enccrd = maccrd = NULL;
299
300	/* Sanity check. */
301	if (crp == NULL)
302		return (EINVAL);
303
304	if (crp->crp_callback == NULL || crp->crp_desc == NULL) {
305		error = EINVAL;
306		goto out;
307	}
308
309	for (crd = crp->crp_desc; crd != NULL; crd = crd->crd_next) {
310		switch (crd->crd_alg) {
311		case CRYPTO_NULL_HMAC:
312		case CRYPTO_MD5_HMAC:
313		case CRYPTO_SHA1_HMAC:
314		case CRYPTO_RIPEMD160_HMAC:
315		case CRYPTO_SHA2_256_HMAC:
316		case CRYPTO_SHA2_384_HMAC:
317		case CRYPTO_SHA2_512_HMAC:
318			if (maccrd != NULL) {
319				error = EINVAL;
320				goto out;
321			}
322			maccrd = crd;
323			break;
324		case CRYPTO_AES_CBC:
325			if (enccrd != NULL) {
326				error = EINVAL;
327				goto out;
328			}
329			enccrd = crd;
330			break;
331		default:
332			return (EINVAL);
333		}
334	}
335	if (enccrd == NULL || (enccrd->crd_len % AES_BLOCK_LEN) != 0) {
336		error = EINVAL;
337		goto out;
338	}
339
340	rw_rlock(&sc->sc_sessions_lock);
341	TAILQ_FOREACH_REVERSE(ses, &sc->sc_sessions, padlock_sessions_head,
342	    ses_next) {
343		if (ses->ses_id == (crp->crp_sid & 0xffffffff))
344			break;
345	}
346	rw_runlock(&sc->sc_sessions_lock);
347	if (ses == NULL) {
348		error = EINVAL;
349		goto out;
350	}
351
352	/* Perform data authentication if requested before encryption. */
353	if (maccrd != NULL && maccrd->crd_next == enccrd) {
354		error = padlock_hash_process(ses, maccrd, crp);
355		if (error != 0)
356			goto out;
357	}
358
359	error = padlock_cipher_process(ses, enccrd, crp);
360	if (error != 0)
361		goto out;
362
363	/* Perform data authentication if requested after encryption. */
364	if (maccrd != NULL && enccrd->crd_next == maccrd) {
365		error = padlock_hash_process(ses, maccrd, crp);
366		if (error != 0)
367			goto out;
368	}
369
370out:
371#if 0
372	/*
373	 * This code is not necessary, because contexts will be freed on next
374	 * padlock_setup_mackey() call or at padlock_freesession() call.
375	 */
376	if (ses != NULL && maccrd != NULL &&
377	    (maccrd->crd_flags & CRD_F_KEY_EXPLICIT) != 0) {
378		padlock_free_ctx(ses->ses_axf, ses->ses_ictx);
379		padlock_free_ctx(ses->ses_axf, ses->ses_octx);
380	}
381#endif
382	crp->crp_etype = error;
383	crypto_done(crp);
384	return (error);
385}
386
387static device_method_t padlock_methods[] = {
388	DEVMETHOD(device_identify,	padlock_identify),
389	DEVMETHOD(device_probe,		padlock_probe),
390	DEVMETHOD(device_attach,	padlock_attach),
391	DEVMETHOD(device_detach,	padlock_detach),
392
393	DEVMETHOD(cryptodev_newsession,	padlock_newsession),
394	DEVMETHOD(cryptodev_freesession,padlock_freesession),
395	DEVMETHOD(cryptodev_process,	padlock_process),
396
397	{0, 0},
398};
399
400static driver_t padlock_driver = {
401	"padlock",
402	padlock_methods,
403	sizeof(struct padlock_softc),
404};
405static devclass_t padlock_devclass;
406
407/* XXX where to attach */
408DRIVER_MODULE(padlock, nexus, padlock_driver, padlock_devclass, 0, 0);
409MODULE_VERSION(padlock, 1);
410MODULE_DEPEND(padlock, crypto, 1, 1, 1);
411