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