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__) && !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		fpu_kern_free_ctx(ses->ses_fpu_ctx);
160		free(ses, M_PADLOCK);
161	}
162	rw_destroy(&sc->sc_sessions_lock);
163	crypto_unregister_all(sc->sc_cid);
164	return (0);
165}
166
167static int
168padlock_newsession(device_t dev, uint32_t *sidp, struct cryptoini *cri)
169{
170	struct padlock_softc *sc = device_get_softc(dev);
171	struct padlock_session *ses = NULL;
172	struct cryptoini *encini, *macini;
173	struct thread *td;
174	int error, saved_ctx;
175
176	if (sidp == NULL || cri == NULL)
177		return (EINVAL);
178
179	encini = macini = NULL;
180	for (; cri != NULL; cri = cri->cri_next) {
181		switch (cri->cri_alg) {
182		case CRYPTO_NULL_HMAC:
183		case CRYPTO_MD5_HMAC:
184		case CRYPTO_SHA1_HMAC:
185		case CRYPTO_RIPEMD160_HMAC:
186		case CRYPTO_SHA2_256_HMAC:
187		case CRYPTO_SHA2_384_HMAC:
188		case CRYPTO_SHA2_512_HMAC:
189			if (macini != NULL)
190				return (EINVAL);
191			macini = cri;
192			break;
193		case CRYPTO_AES_CBC:
194			if (encini != NULL)
195				return (EINVAL);
196			encini = cri;
197			break;
198		default:
199			return (EINVAL);
200		}
201	}
202
203	/*
204	 * We only support HMAC algorithms to be able to work with
205	 * ipsec(4), so if we are asked only for authentication without
206	 * encryption, don't pretend we can accellerate it.
207	 */
208	if (encini == NULL)
209		return (EINVAL);
210
211	/*
212	 * Let's look for a free session structure.
213	 */
214	rw_wlock(&sc->sc_sessions_lock);
215	/*
216	 * Free sessions goes first, so if first session is used, we need to
217	 * allocate one.
218	 */
219	ses = TAILQ_FIRST(&sc->sc_sessions);
220	if (ses == NULL || ses->ses_used) {
221		ses = malloc(sizeof(*ses), M_PADLOCK, M_NOWAIT | M_ZERO);
222		if (ses == NULL) {
223			rw_wunlock(&sc->sc_sessions_lock);
224			return (ENOMEM);
225		}
226		ses->ses_fpu_ctx = fpu_kern_alloc_ctx(FPU_KERN_NORMAL |
227		    FPU_KERN_NOWAIT);
228		if (ses->ses_fpu_ctx == NULL) {
229			free(ses, M_PADLOCK);
230			rw_wunlock(&sc->sc_sessions_lock);
231			return (ENOMEM);
232		}
233		ses->ses_id = sc->sc_sid++;
234	} else {
235		TAILQ_REMOVE(&sc->sc_sessions, ses, ses_next);
236	}
237	ses->ses_used = 1;
238	TAILQ_INSERT_TAIL(&sc->sc_sessions, ses, ses_next);
239	rw_wunlock(&sc->sc_sessions_lock);
240
241	error = padlock_cipher_setup(ses, encini);
242	if (error != 0) {
243		padlock_freesession_one(sc, ses, 0);
244		return (error);
245	}
246
247	if (macini != NULL) {
248		td = curthread;
249		if (!is_fpu_kern_thread(0)) {
250			error = fpu_kern_enter(td, ses->ses_fpu_ctx,
251			    FPU_KERN_NORMAL);
252			saved_ctx = 1;
253		} else {
254			error = 0;
255			saved_ctx = 0;
256		}
257		if (error == 0) {
258			error = padlock_hash_setup(ses, macini);
259			if (saved_ctx)
260				fpu_kern_leave(td, ses->ses_fpu_ctx);
261		}
262		if (error != 0) {
263			padlock_freesession_one(sc, ses, 0);
264			return (error);
265		}
266	}
267
268	*sidp = ses->ses_id;
269	return (0);
270}
271
272static void
273padlock_freesession_one(struct padlock_softc *sc, struct padlock_session *ses,
274    int locked)
275{
276	struct fpu_kern_ctx *ctx;
277	uint32_t sid = ses->ses_id;
278
279	if (!locked)
280		rw_wlock(&sc->sc_sessions_lock);
281	TAILQ_REMOVE(&sc->sc_sessions, ses, ses_next);
282	padlock_hash_free(ses);
283	ctx = ses->ses_fpu_ctx;
284	bzero(ses, sizeof(*ses));
285	ses->ses_used = 0;
286	ses->ses_id = sid;
287	ses->ses_fpu_ctx = ctx;
288	TAILQ_INSERT_HEAD(&sc->sc_sessions, ses, ses_next);
289	if (!locked)
290		rw_wunlock(&sc->sc_sessions_lock);
291}
292
293static int
294padlock_freesession(device_t dev, uint64_t tid)
295{
296	struct padlock_softc *sc = device_get_softc(dev);
297	struct padlock_session *ses;
298	uint32_t sid = ((uint32_t)tid) & 0xffffffff;
299
300	rw_wlock(&sc->sc_sessions_lock);
301	TAILQ_FOREACH_REVERSE(ses, &sc->sc_sessions, padlock_sessions_head,
302	    ses_next) {
303		if (ses->ses_id == sid)
304			break;
305	}
306	if (ses == NULL) {
307		rw_wunlock(&sc->sc_sessions_lock);
308		return (EINVAL);
309	}
310	padlock_freesession_one(sc, ses, 1);
311	rw_wunlock(&sc->sc_sessions_lock);
312	return (0);
313}
314
315static int
316padlock_process(device_t dev, struct cryptop *crp, int hint __unused)
317{
318	struct padlock_softc *sc = device_get_softc(dev);
319	struct padlock_session *ses = NULL;
320	struct cryptodesc *crd, *enccrd, *maccrd;
321	int error = 0;
322
323	enccrd = maccrd = NULL;
324
325	/* Sanity check. */
326	if (crp == NULL)
327		return (EINVAL);
328
329	if (crp->crp_callback == NULL || crp->crp_desc == NULL) {
330		error = EINVAL;
331		goto out;
332	}
333
334	for (crd = crp->crp_desc; crd != NULL; crd = crd->crd_next) {
335		switch (crd->crd_alg) {
336		case CRYPTO_NULL_HMAC:
337		case CRYPTO_MD5_HMAC:
338		case CRYPTO_SHA1_HMAC:
339		case CRYPTO_RIPEMD160_HMAC:
340		case CRYPTO_SHA2_256_HMAC:
341		case CRYPTO_SHA2_384_HMAC:
342		case CRYPTO_SHA2_512_HMAC:
343			if (maccrd != NULL) {
344				error = EINVAL;
345				goto out;
346			}
347			maccrd = crd;
348			break;
349		case CRYPTO_AES_CBC:
350			if (enccrd != NULL) {
351				error = EINVAL;
352				goto out;
353			}
354			enccrd = crd;
355			break;
356		default:
357			return (EINVAL);
358		}
359	}
360	if (enccrd == NULL || (enccrd->crd_len % AES_BLOCK_LEN) != 0) {
361		error = EINVAL;
362		goto out;
363	}
364
365	rw_rlock(&sc->sc_sessions_lock);
366	TAILQ_FOREACH_REVERSE(ses, &sc->sc_sessions, padlock_sessions_head,
367	    ses_next) {
368		if (ses->ses_id == (crp->crp_sid & 0xffffffff))
369			break;
370	}
371	rw_runlock(&sc->sc_sessions_lock);
372	if (ses == NULL) {
373		error = EINVAL;
374		goto out;
375	}
376
377	/* Perform data authentication if requested before encryption. */
378	if (maccrd != NULL && maccrd->crd_next == enccrd) {
379		error = padlock_hash_process(ses, maccrd, crp);
380		if (error != 0)
381			goto out;
382	}
383
384	error = padlock_cipher_process(ses, enccrd, crp);
385	if (error != 0)
386		goto out;
387
388	/* Perform data authentication if requested after encryption. */
389	if (maccrd != NULL && enccrd->crd_next == maccrd) {
390		error = padlock_hash_process(ses, maccrd, crp);
391		if (error != 0)
392			goto out;
393	}
394
395out:
396#if 0
397	/*
398	 * This code is not necessary, because contexts will be freed on next
399	 * padlock_setup_mackey() call or at padlock_freesession() call.
400	 */
401	if (ses != NULL && maccrd != NULL &&
402	    (maccrd->crd_flags & CRD_F_KEY_EXPLICIT) != 0) {
403		padlock_free_ctx(ses->ses_axf, ses->ses_ictx);
404		padlock_free_ctx(ses->ses_axf, ses->ses_octx);
405	}
406#endif
407	crp->crp_etype = error;
408	crypto_done(crp);
409	return (error);
410}
411
412static device_method_t padlock_methods[] = {
413	DEVMETHOD(device_identify,	padlock_identify),
414	DEVMETHOD(device_probe,		padlock_probe),
415	DEVMETHOD(device_attach,	padlock_attach),
416	DEVMETHOD(device_detach,	padlock_detach),
417
418	DEVMETHOD(cryptodev_newsession,	padlock_newsession),
419	DEVMETHOD(cryptodev_freesession,padlock_freesession),
420	DEVMETHOD(cryptodev_process,	padlock_process),
421
422	{0, 0},
423};
424
425static driver_t padlock_driver = {
426	"padlock",
427	padlock_methods,
428	sizeof(struct padlock_softc),
429};
430static devclass_t padlock_devclass;
431
432/* XXX where to attach */
433DRIVER_MODULE(padlock, nexus, padlock_driver, padlock_devclass, 0, 0);
434MODULE_VERSION(padlock, 1);
435MODULE_DEPEND(padlock, crypto, 1, 1, 1);
436