xref: /freebsd/sys/crypto/via/padlock.c (revision ef0a6e20)
1ef0a6e20SPawel Jakub Dawidek /*-
2ef0a6e20SPawel Jakub Dawidek  * Copyright (c) 2005 Pawel Jakub Dawidek <pjd@FreeBSD.org>
3ef0a6e20SPawel Jakub Dawidek  * Copyright (c) 2004 Mark R V Murray
4ef0a6e20SPawel Jakub Dawidek  * All rights reserved.
5ef0a6e20SPawel Jakub Dawidek  *
6ef0a6e20SPawel Jakub Dawidek  * Redistribution and use in source and binary forms, with or without
7ef0a6e20SPawel Jakub Dawidek  * modification, are permitted provided that the following conditions
8ef0a6e20SPawel Jakub Dawidek  * are met:
9ef0a6e20SPawel Jakub Dawidek  * 1. Redistributions of source code must retain the above copyright
10ef0a6e20SPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer.
11ef0a6e20SPawel Jakub Dawidek  * 2. Redistributions in binary form must reproduce the above copyright
12ef0a6e20SPawel Jakub Dawidek  *    notice, this list of conditions and the following disclaimer in the
13ef0a6e20SPawel Jakub Dawidek  *    documentation and/or other materials provided with the distribution.
14ef0a6e20SPawel Jakub Dawidek  *
15ef0a6e20SPawel Jakub Dawidek  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
16ef0a6e20SPawel Jakub Dawidek  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17ef0a6e20SPawel Jakub Dawidek  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18ef0a6e20SPawel Jakub Dawidek  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
19ef0a6e20SPawel Jakub Dawidek  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20ef0a6e20SPawel Jakub Dawidek  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21ef0a6e20SPawel Jakub Dawidek  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22ef0a6e20SPawel Jakub Dawidek  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23ef0a6e20SPawel Jakub Dawidek  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24ef0a6e20SPawel Jakub Dawidek  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25ef0a6e20SPawel Jakub Dawidek  * SUCH DAMAGE.
26ef0a6e20SPawel Jakub Dawidek  */
27ef0a6e20SPawel Jakub Dawidek 
28ef0a6e20SPawel Jakub Dawidek /*	$OpenBSD: via.c,v 1.3 2004/06/15 23:36:55 deraadt Exp $	*/
29ef0a6e20SPawel Jakub Dawidek /*-
30ef0a6e20SPawel Jakub Dawidek  * Copyright (c) 2003 Jason Wright
31ef0a6e20SPawel Jakub Dawidek  * Copyright (c) 2003, 2004 Theo de Raadt
32ef0a6e20SPawel Jakub Dawidek  * All rights reserved.
33ef0a6e20SPawel Jakub Dawidek  *
34ef0a6e20SPawel Jakub Dawidek  * Permission to use, copy, modify, and distribute this software for any
35ef0a6e20SPawel Jakub Dawidek  * purpose with or without fee is hereby granted, provided that the above
36ef0a6e20SPawel Jakub Dawidek  * copyright notice and this permission notice appear in all copies.
37ef0a6e20SPawel Jakub Dawidek  *
38ef0a6e20SPawel Jakub Dawidek  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
39ef0a6e20SPawel Jakub Dawidek  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
40ef0a6e20SPawel Jakub Dawidek  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
41ef0a6e20SPawel Jakub Dawidek  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
42ef0a6e20SPawel Jakub Dawidek  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
43ef0a6e20SPawel Jakub Dawidek  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
44ef0a6e20SPawel Jakub Dawidek  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
45ef0a6e20SPawel Jakub Dawidek  */
46ef0a6e20SPawel Jakub Dawidek 
47ef0a6e20SPawel Jakub Dawidek #include <sys/cdefs.h>
48ef0a6e20SPawel Jakub Dawidek __FBSDID("$FreeBSD$");
49ef0a6e20SPawel Jakub Dawidek 
50ef0a6e20SPawel Jakub Dawidek #include <sys/param.h>
51ef0a6e20SPawel Jakub Dawidek #include <sys/systm.h>
52ef0a6e20SPawel Jakub Dawidek #include <sys/kernel.h>
53ef0a6e20SPawel Jakub Dawidek #include <sys/module.h>
54ef0a6e20SPawel Jakub Dawidek #include <sys/lock.h>
55ef0a6e20SPawel Jakub Dawidek #include <sys/mutex.h>
56ef0a6e20SPawel Jakub Dawidek #include <sys/malloc.h>
57ef0a6e20SPawel Jakub Dawidek #include <sys/libkern.h>
58ef0a6e20SPawel Jakub Dawidek #include <sys/mbuf.h>
59ef0a6e20SPawel Jakub Dawidek #include <sys/uio.h>
60ef0a6e20SPawel Jakub Dawidek #if defined(__i386__) && !defined(PC98)
61ef0a6e20SPawel Jakub Dawidek #include <machine/cpufunc.h>
62ef0a6e20SPawel Jakub Dawidek #include <machine/cputypes.h>
63ef0a6e20SPawel Jakub Dawidek #endif
64ef0a6e20SPawel Jakub Dawidek 
65ef0a6e20SPawel Jakub Dawidek #include <opencrypto/cryptodev.h>
66ef0a6e20SPawel Jakub Dawidek #include <crypto/rijndael/rijndael.h>
67ef0a6e20SPawel Jakub Dawidek 
68ef0a6e20SPawel Jakub Dawidek 
69ef0a6e20SPawel Jakub Dawidek #define	PADLOCK_ROUND_COUNT_AES128	10
70ef0a6e20SPawel Jakub Dawidek #define	PADLOCK_ROUND_COUNT_AES192	12
71ef0a6e20SPawel Jakub Dawidek #define	PADLOCK_ROUND_COUNT_AES256	14
72ef0a6e20SPawel Jakub Dawidek 
73ef0a6e20SPawel Jakub Dawidek #define	PADLOCK_ALGORITHM_TYPE_AES	0
74ef0a6e20SPawel Jakub Dawidek 
75ef0a6e20SPawel Jakub Dawidek #define	PADLOCK_KEY_GENERATION_HW	0
76ef0a6e20SPawel Jakub Dawidek #define	PADLOCK_KEY_GENERATION_SW	1
77ef0a6e20SPawel Jakub Dawidek 
78ef0a6e20SPawel Jakub Dawidek #define	PADLOCK_DIRECTION_ENCRYPT	0
79ef0a6e20SPawel Jakub Dawidek #define	PADLOCK_DIRECTION_DECRYPT	1
80ef0a6e20SPawel Jakub Dawidek 
81ef0a6e20SPawel Jakub Dawidek #define	PADLOCK_KEY_SIZE_128	0
82ef0a6e20SPawel Jakub Dawidek #define	PADLOCK_KEY_SIZE_192	1
83ef0a6e20SPawel Jakub Dawidek #define	PADLOCK_KEY_SIZE_256	2
84ef0a6e20SPawel Jakub Dawidek 
85ef0a6e20SPawel Jakub Dawidek union padlock_cw {
86ef0a6e20SPawel Jakub Dawidek 	uint64_t raw;
87ef0a6e20SPawel Jakub Dawidek 	struct {
88ef0a6e20SPawel Jakub Dawidek 		u_int round_count : 4;
89ef0a6e20SPawel Jakub Dawidek 		u_int algorithm_type : 3;
90ef0a6e20SPawel Jakub Dawidek 		u_int key_generation : 1;
91ef0a6e20SPawel Jakub Dawidek 		u_int intermediate : 1;
92ef0a6e20SPawel Jakub Dawidek 		u_int direction : 1;
93ef0a6e20SPawel Jakub Dawidek 		u_int key_size : 2;
94ef0a6e20SPawel Jakub Dawidek 		u_int filler0 : 20;
95ef0a6e20SPawel Jakub Dawidek 		u_int filler1 : 32;
96ef0a6e20SPawel Jakub Dawidek 		u_int filler2 : 32;
97ef0a6e20SPawel Jakub Dawidek 		u_int filler3 : 32;
98ef0a6e20SPawel Jakub Dawidek 	} __field;
99ef0a6e20SPawel Jakub Dawidek };
100ef0a6e20SPawel Jakub Dawidek #define	cw_round_count		__field.round_count
101ef0a6e20SPawel Jakub Dawidek #define	cw_algorithm_type	__field.algorithm_type
102ef0a6e20SPawel Jakub Dawidek #define	cw_key_generation	__field.key_generation
103ef0a6e20SPawel Jakub Dawidek #define	cw_intermediate		__field.intermediate
104ef0a6e20SPawel Jakub Dawidek #define	cw_direction		__field.direction
105ef0a6e20SPawel Jakub Dawidek #define	cw_key_size		__field.key_size
106ef0a6e20SPawel Jakub Dawidek #define	cw_filler0		__field.filler0
107ef0a6e20SPawel Jakub Dawidek #define	cw_filler1		__field.filler1
108ef0a6e20SPawel Jakub Dawidek #define	cw_filler2		__field.filler2
109ef0a6e20SPawel Jakub Dawidek #define	cw_filler3		__field.filler3
110ef0a6e20SPawel Jakub Dawidek 
111ef0a6e20SPawel Jakub Dawidek struct padlock_session {
112ef0a6e20SPawel Jakub Dawidek 	union padlock_cw ses_cw __aligned(16);
113ef0a6e20SPawel Jakub Dawidek 	uint32_t	ses_ekey[4 * (RIJNDAEL_MAXNR + 1) + 4] __aligned(16);	/* 128 bit aligned */
114ef0a6e20SPawel Jakub Dawidek 	uint32_t	ses_dkey[4 * (RIJNDAEL_MAXNR + 1) + 4] __aligned(16);	/* 128 bit aligned */
115ef0a6e20SPawel Jakub Dawidek 	uint8_t		ses_iv[16] __aligned(16);			/* 128 bit aligned */
116ef0a6e20SPawel Jakub Dawidek 	int		ses_used;
117ef0a6e20SPawel Jakub Dawidek 	uint32_t	ses_id;
118ef0a6e20SPawel Jakub Dawidek 	TAILQ_ENTRY(padlock_session) ses_next;
119ef0a6e20SPawel Jakub Dawidek };
120ef0a6e20SPawel Jakub Dawidek 
121ef0a6e20SPawel Jakub Dawidek struct padlock_softc {
122ef0a6e20SPawel Jakub Dawidek 	int32_t		sc_cid;
123ef0a6e20SPawel Jakub Dawidek 	uint32_t	sc_sid;
124ef0a6e20SPawel Jakub Dawidek 	TAILQ_HEAD(, padlock_session) sc_sessions;
125ef0a6e20SPawel Jakub Dawidek 	struct mtx	sc_sessions_mtx;
126ef0a6e20SPawel Jakub Dawidek };
127ef0a6e20SPawel Jakub Dawidek 
128ef0a6e20SPawel Jakub Dawidek static struct padlock_softc *padlock_sc;
129ef0a6e20SPawel Jakub Dawidek 
130ef0a6e20SPawel Jakub Dawidek static int padlock_newsession(void *arg __unused, uint32_t *sidp,
131ef0a6e20SPawel Jakub Dawidek     struct cryptoini *cri);
132ef0a6e20SPawel Jakub Dawidek static int padlock_freesession(void *arg __unused, uint64_t tid);
133ef0a6e20SPawel Jakub Dawidek static int padlock_process(void *arg __unused, struct cryptop *crp,
134ef0a6e20SPawel Jakub Dawidek     int hint __unused);
135ef0a6e20SPawel Jakub Dawidek 
136ef0a6e20SPawel Jakub Dawidek static __inline void
137ef0a6e20SPawel Jakub Dawidek padlock_cbc(void *in, void *out, size_t count, void *key, union padlock_cw *cw,
138ef0a6e20SPawel Jakub Dawidek     void *iv)
139ef0a6e20SPawel Jakub Dawidek {
140ef0a6e20SPawel Jakub Dawidek #ifdef __GNUCLIKE_ASM
141ef0a6e20SPawel Jakub Dawidek 	/* The .byte line is really VIA C3 "xcrypt-cbc" instruction */
142ef0a6e20SPawel Jakub Dawidek 	__asm __volatile(
143ef0a6e20SPawel Jakub Dawidek 		"pushf				\n\t"
144ef0a6e20SPawel Jakub Dawidek 		"popf				\n\t"
145ef0a6e20SPawel Jakub Dawidek 		"rep				\n\t"
146ef0a6e20SPawel Jakub Dawidek 		".byte	0x0f, 0xa7, 0xd0"
147ef0a6e20SPawel Jakub Dawidek 			: "+a" (iv), "+c" (count), "+D" (out), "+S" (in)
148ef0a6e20SPawel Jakub Dawidek 			: "b" (key), "d" (cw)
149ef0a6e20SPawel Jakub Dawidek 			: "cc", "memory"
150ef0a6e20SPawel Jakub Dawidek 		);
151ef0a6e20SPawel Jakub Dawidek #endif
152ef0a6e20SPawel Jakub Dawidek }
153ef0a6e20SPawel Jakub Dawidek 
154ef0a6e20SPawel Jakub Dawidek static int
155ef0a6e20SPawel Jakub Dawidek padlock_init(void)
156ef0a6e20SPawel Jakub Dawidek {
157ef0a6e20SPawel Jakub Dawidek 	struct padlock_softc *sc;
158ef0a6e20SPawel Jakub Dawidek #if defined(__i386__) && !defined(PC98)
159ef0a6e20SPawel Jakub Dawidek 	u_int regs[4];
160ef0a6e20SPawel Jakub Dawidek 	int has_ace = 0;
161ef0a6e20SPawel Jakub Dawidek 
162ef0a6e20SPawel Jakub Dawidek 	if (cpu_class < CPUCLASS_586)
163ef0a6e20SPawel Jakub Dawidek 		return (EINVAL);
164ef0a6e20SPawel Jakub Dawidek 	do_cpuid(1, regs);
165ef0a6e20SPawel Jakub Dawidek 	if ((regs[0] & 0xf) >= 3) {
166ef0a6e20SPawel Jakub Dawidek 		do_cpuid(0xc0000000, regs);
167ef0a6e20SPawel Jakub Dawidek 		if (regs[0] == 0xc0000001) {
168ef0a6e20SPawel Jakub Dawidek 			do_cpuid(0xc0000001, regs);
169ef0a6e20SPawel Jakub Dawidek 			if ((regs[3] & 0xc0) == 0xc0)
170ef0a6e20SPawel Jakub Dawidek 				has_ace = 1;
171ef0a6e20SPawel Jakub Dawidek 		}
172ef0a6e20SPawel Jakub Dawidek 	}
173ef0a6e20SPawel Jakub Dawidek 	if (!has_ace) {
174ef0a6e20SPawel Jakub Dawidek 		printf("PADLOCK: No ACE support.\n");
175ef0a6e20SPawel Jakub Dawidek 		return (EINVAL);
176ef0a6e20SPawel Jakub Dawidek 	}
177ef0a6e20SPawel Jakub Dawidek #else
178ef0a6e20SPawel Jakub Dawidek 	return (EINVAL);
179ef0a6e20SPawel Jakub Dawidek #endif
180ef0a6e20SPawel Jakub Dawidek 
181ef0a6e20SPawel Jakub Dawidek 	padlock_sc = sc = malloc(sizeof(*padlock_sc), M_DEVBUF,
182ef0a6e20SPawel Jakub Dawidek 	    M_NOWAIT | M_ZERO);
183ef0a6e20SPawel Jakub Dawidek 	if (padlock_sc == NULL) {
184ef0a6e20SPawel Jakub Dawidek 		printf("PADLOCK: Could not allocate memory.\n");
185ef0a6e20SPawel Jakub Dawidek 		return (ENOMEM);
186ef0a6e20SPawel Jakub Dawidek 	}
187ef0a6e20SPawel Jakub Dawidek 	TAILQ_INIT(&sc->sc_sessions);
188ef0a6e20SPawel Jakub Dawidek 	sc->sc_sid = 1;
189ef0a6e20SPawel Jakub Dawidek 
190ef0a6e20SPawel Jakub Dawidek 	sc->sc_cid = crypto_get_driverid(0);
191ef0a6e20SPawel Jakub Dawidek 	if (sc->sc_cid < 0) {
192ef0a6e20SPawel Jakub Dawidek 		printf("PADLOCK: Could not get crypto driver id.\n");
193ef0a6e20SPawel Jakub Dawidek 		free(padlock_sc, M_DEVBUF);
194ef0a6e20SPawel Jakub Dawidek 		padlock_sc = NULL;
195ef0a6e20SPawel Jakub Dawidek 		return (ENOMEM);
196ef0a6e20SPawel Jakub Dawidek 	}
197ef0a6e20SPawel Jakub Dawidek 
198ef0a6e20SPawel Jakub Dawidek 	mtx_init(&sc->sc_sessions_mtx, "padlock_mtx", NULL, MTX_DEF);
199ef0a6e20SPawel Jakub Dawidek 	crypto_register(sc->sc_cid, CRYPTO_AES_CBC, 0, 0, padlock_newsession,
200ef0a6e20SPawel Jakub Dawidek 	    padlock_freesession, padlock_process, NULL);
201ef0a6e20SPawel Jakub Dawidek 	return (0);
202ef0a6e20SPawel Jakub Dawidek }
203ef0a6e20SPawel Jakub Dawidek 
204ef0a6e20SPawel Jakub Dawidek static int
205ef0a6e20SPawel Jakub Dawidek padlock_destroy(void)
206ef0a6e20SPawel Jakub Dawidek {
207ef0a6e20SPawel Jakub Dawidek 	struct padlock_softc *sc = padlock_sc;
208ef0a6e20SPawel Jakub Dawidek 	struct padlock_session *ses;
209ef0a6e20SPawel Jakub Dawidek 	u_int active = 0;
210ef0a6e20SPawel Jakub Dawidek 
211ef0a6e20SPawel Jakub Dawidek 	if (sc == NULL)
212ef0a6e20SPawel Jakub Dawidek 		return (0);
213ef0a6e20SPawel Jakub Dawidek 	mtx_lock(&sc->sc_sessions_mtx);
214ef0a6e20SPawel Jakub Dawidek 	TAILQ_FOREACH(ses, &sc->sc_sessions, ses_next) {
215ef0a6e20SPawel Jakub Dawidek 		if (ses->ses_used)
216ef0a6e20SPawel Jakub Dawidek 			active++;
217ef0a6e20SPawel Jakub Dawidek 	}
218ef0a6e20SPawel Jakub Dawidek 	if (active > 0) {
219ef0a6e20SPawel Jakub Dawidek 		mtx_unlock(&sc->sc_sessions_mtx);
220ef0a6e20SPawel Jakub Dawidek 		printf("PADLOCK: Cannot destroy, %u sessions active.\n",
221ef0a6e20SPawel Jakub Dawidek 		    active);
222ef0a6e20SPawel Jakub Dawidek 		return (EBUSY);
223ef0a6e20SPawel Jakub Dawidek 	}
224ef0a6e20SPawel Jakub Dawidek 	padlock_sc = NULL;
225ef0a6e20SPawel Jakub Dawidek 	for (ses = TAILQ_FIRST(&sc->sc_sessions); ses != NULL;
226ef0a6e20SPawel Jakub Dawidek 	    ses = TAILQ_FIRST(&sc->sc_sessions)) {
227ef0a6e20SPawel Jakub Dawidek 		TAILQ_REMOVE(&sc->sc_sessions, ses, ses_next);
228ef0a6e20SPawel Jakub Dawidek 		free(ses, M_DEVBUF);
229ef0a6e20SPawel Jakub Dawidek 	}
230ef0a6e20SPawel Jakub Dawidek 	mtx_destroy(&sc->sc_sessions_mtx);
231ef0a6e20SPawel Jakub Dawidek 	crypto_unregister_all(sc->sc_cid);
232ef0a6e20SPawel Jakub Dawidek 	free(sc, M_DEVBUF);
233ef0a6e20SPawel Jakub Dawidek 	return (0);
234ef0a6e20SPawel Jakub Dawidek }
235ef0a6e20SPawel Jakub Dawidek 
236ef0a6e20SPawel Jakub Dawidek static int
237ef0a6e20SPawel Jakub Dawidek padlock_newsession(void *arg __unused, uint32_t *sidp, struct cryptoini *cri)
238ef0a6e20SPawel Jakub Dawidek {
239ef0a6e20SPawel Jakub Dawidek 	struct padlock_softc *sc = padlock_sc;
240ef0a6e20SPawel Jakub Dawidek 	struct padlock_session *ses = NULL;
241ef0a6e20SPawel Jakub Dawidek 	union padlock_cw *cw;
242ef0a6e20SPawel Jakub Dawidek 	int i;
243ef0a6e20SPawel Jakub Dawidek 
244ef0a6e20SPawel Jakub Dawidek 	if (sc == NULL || sidp == NULL || cri == NULL ||
245ef0a6e20SPawel Jakub Dawidek 	    cri->cri_next != NULL || cri->cri_alg != CRYPTO_AES_CBC) {
246ef0a6e20SPawel Jakub Dawidek 		return (EINVAL);
247ef0a6e20SPawel Jakub Dawidek 	}
248ef0a6e20SPawel Jakub Dawidek 	if (cri->cri_klen != 128 && cri->cri_klen != 192 &&
249ef0a6e20SPawel Jakub Dawidek 	    cri->cri_klen != 256) {
250ef0a6e20SPawel Jakub Dawidek 		return (EINVAL);
251ef0a6e20SPawel Jakub Dawidek 	}
252ef0a6e20SPawel Jakub Dawidek 
253ef0a6e20SPawel Jakub Dawidek 	/*
254ef0a6e20SPawel Jakub Dawidek 	 * Let's look for a free session structure.
255ef0a6e20SPawel Jakub Dawidek 	 */
256ef0a6e20SPawel Jakub Dawidek 	mtx_lock(&sc->sc_sessions_mtx);
257ef0a6e20SPawel Jakub Dawidek 	/*
258ef0a6e20SPawel Jakub Dawidek 	 * Free sessions goes first, so if first session is used, we need to
259ef0a6e20SPawel Jakub Dawidek 	 * allocate one.
260ef0a6e20SPawel Jakub Dawidek 	 */
261ef0a6e20SPawel Jakub Dawidek 	ses = TAILQ_FIRST(&sc->sc_sessions);
262ef0a6e20SPawel Jakub Dawidek 	if (ses == NULL || ses->ses_used)
263ef0a6e20SPawel Jakub Dawidek 		ses = NULL;
264ef0a6e20SPawel Jakub Dawidek 	else {
265ef0a6e20SPawel Jakub Dawidek 		TAILQ_REMOVE(&sc->sc_sessions, ses, ses_next);
266ef0a6e20SPawel Jakub Dawidek 		ses->ses_used = 1;
267ef0a6e20SPawel Jakub Dawidek 		TAILQ_INSERT_TAIL(&sc->sc_sessions, ses, ses_next);
268ef0a6e20SPawel Jakub Dawidek 	}
269ef0a6e20SPawel Jakub Dawidek 	mtx_unlock(&sc->sc_sessions_mtx);
270ef0a6e20SPawel Jakub Dawidek 	if (ses == NULL) {
271ef0a6e20SPawel Jakub Dawidek 		ses = malloc(sizeof(*ses), M_DEVBUF, M_NOWAIT | M_ZERO);
272ef0a6e20SPawel Jakub Dawidek 		if (ses == NULL)
273ef0a6e20SPawel Jakub Dawidek 			return (ENOMEM);
274ef0a6e20SPawel Jakub Dawidek 		ses->ses_used = 1;
275ef0a6e20SPawel Jakub Dawidek 		mtx_lock(&sc->sc_sessions_mtx);
276ef0a6e20SPawel Jakub Dawidek 		ses->ses_id = sc->sc_sid++;
277ef0a6e20SPawel Jakub Dawidek 		TAILQ_INSERT_TAIL(&sc->sc_sessions, ses, ses_next);
278ef0a6e20SPawel Jakub Dawidek 		mtx_unlock(&sc->sc_sessions_mtx);
279ef0a6e20SPawel Jakub Dawidek 	}
280ef0a6e20SPawel Jakub Dawidek 
281ef0a6e20SPawel Jakub Dawidek 	cw = &ses->ses_cw;
282ef0a6e20SPawel Jakub Dawidek 	bzero(cw, sizeof(*cw));
283ef0a6e20SPawel Jakub Dawidek 	cw->cw_algorithm_type = PADLOCK_ALGORITHM_TYPE_AES;
284ef0a6e20SPawel Jakub Dawidek 	cw->cw_key_generation = PADLOCK_KEY_GENERATION_SW;
285ef0a6e20SPawel Jakub Dawidek 	cw->cw_intermediate = 0;
286ef0a6e20SPawel Jakub Dawidek 	switch (cri->cri_klen) {
287ef0a6e20SPawel Jakub Dawidek 	case 128:
288ef0a6e20SPawel Jakub Dawidek 		cw->cw_round_count = PADLOCK_ROUND_COUNT_AES128;
289ef0a6e20SPawel Jakub Dawidek 		cw->cw_key_size = PADLOCK_KEY_SIZE_128;
290ef0a6e20SPawel Jakub Dawidek #ifdef HW_KEY_GENERATION
291ef0a6e20SPawel Jakub Dawidek 		/* This doesn't buy us much, that's why it is commented out. */
292ef0a6e20SPawel Jakub Dawidek 		cw->cw_key_generation = PADLOCK_KEY_GENERATION_HW;
293ef0a6e20SPawel Jakub Dawidek #endif
294ef0a6e20SPawel Jakub Dawidek 		break;
295ef0a6e20SPawel Jakub Dawidek 	case 192:
296ef0a6e20SPawel Jakub Dawidek 		cw->cw_round_count = PADLOCK_ROUND_COUNT_AES192;
297ef0a6e20SPawel Jakub Dawidek 		cw->cw_key_size = PADLOCK_KEY_SIZE_192;
298ef0a6e20SPawel Jakub Dawidek 		break;
299ef0a6e20SPawel Jakub Dawidek 	case 256:
300ef0a6e20SPawel Jakub Dawidek 		cw->cw_round_count = PADLOCK_ROUND_COUNT_AES256;
301ef0a6e20SPawel Jakub Dawidek 		cw->cw_key_size = PADLOCK_KEY_SIZE_256;
302ef0a6e20SPawel Jakub Dawidek 		break;
303ef0a6e20SPawel Jakub Dawidek 	}
304ef0a6e20SPawel Jakub Dawidek 
305ef0a6e20SPawel Jakub Dawidek 	arc4rand(ses->ses_iv, sizeof(ses->ses_iv), 0);
306ef0a6e20SPawel Jakub Dawidek 
307ef0a6e20SPawel Jakub Dawidek 	if (cw->cw_key_generation == PADLOCK_KEY_GENERATION_SW) {
308ef0a6e20SPawel Jakub Dawidek 		/* Build expanded keys for both directions */
309ef0a6e20SPawel Jakub Dawidek 		rijndaelKeySetupEnc(ses->ses_ekey, cri->cri_key, cri->cri_klen);
310ef0a6e20SPawel Jakub Dawidek 		rijndaelKeySetupDec(ses->ses_dkey, cri->cri_key, cri->cri_klen);
311ef0a6e20SPawel Jakub Dawidek 		for (i = 0; i < 4 * (RIJNDAEL_MAXNR + 1); i++) {
312ef0a6e20SPawel Jakub Dawidek 			ses->ses_ekey[i] = ntohl(ses->ses_ekey[i]);
313ef0a6e20SPawel Jakub Dawidek 			ses->ses_dkey[i] = ntohl(ses->ses_dkey[i]);
314ef0a6e20SPawel Jakub Dawidek 		}
315ef0a6e20SPawel Jakub Dawidek 	} else {
316ef0a6e20SPawel Jakub Dawidek 		bcopy(cri->cri_key, ses->ses_ekey, cri->cri_klen);
317ef0a6e20SPawel Jakub Dawidek 		bcopy(cri->cri_key, ses->ses_dkey, cri->cri_klen);
318ef0a6e20SPawel Jakub Dawidek 	}
319ef0a6e20SPawel Jakub Dawidek 
320ef0a6e20SPawel Jakub Dawidek 	*sidp = ses->ses_id;
321ef0a6e20SPawel Jakub Dawidek 	return (0);
322ef0a6e20SPawel Jakub Dawidek }
323ef0a6e20SPawel Jakub Dawidek 
324ef0a6e20SPawel Jakub Dawidek static int
325ef0a6e20SPawel Jakub Dawidek padlock_freesession(void *arg __unused, uint64_t tid)
326ef0a6e20SPawel Jakub Dawidek {
327ef0a6e20SPawel Jakub Dawidek 	struct padlock_softc *sc = padlock_sc;
328ef0a6e20SPawel Jakub Dawidek 	struct padlock_session *ses;
329ef0a6e20SPawel Jakub Dawidek 	uint32_t sid = ((uint32_t)tid) & 0xffffffff;
330ef0a6e20SPawel Jakub Dawidek 
331ef0a6e20SPawel Jakub Dawidek 	if (sc == NULL)
332ef0a6e20SPawel Jakub Dawidek 		return (EINVAL);
333ef0a6e20SPawel Jakub Dawidek 	mtx_lock(&sc->sc_sessions_mtx);
334ef0a6e20SPawel Jakub Dawidek 	TAILQ_FOREACH(ses, &sc->sc_sessions, ses_next) {
335ef0a6e20SPawel Jakub Dawidek 		if (ses->ses_id == sid)
336ef0a6e20SPawel Jakub Dawidek 			break;
337ef0a6e20SPawel Jakub Dawidek 	}
338ef0a6e20SPawel Jakub Dawidek 	if (ses == NULL) {
339ef0a6e20SPawel Jakub Dawidek 		mtx_unlock(&sc->sc_sessions_mtx);
340ef0a6e20SPawel Jakub Dawidek 		return (EINVAL);
341ef0a6e20SPawel Jakub Dawidek 	}
342ef0a6e20SPawel Jakub Dawidek 	TAILQ_REMOVE(&sc->sc_sessions, ses, ses_next);
343ef0a6e20SPawel Jakub Dawidek 	bzero(ses, sizeof(ses));
344ef0a6e20SPawel Jakub Dawidek 	ses->ses_used = 0;
345ef0a6e20SPawel Jakub Dawidek 	TAILQ_INSERT_TAIL(&sc->sc_sessions, ses, ses_next);
346ef0a6e20SPawel Jakub Dawidek 	mtx_unlock(&sc->sc_sessions_mtx);
347ef0a6e20SPawel Jakub Dawidek 	return (0);
348ef0a6e20SPawel Jakub Dawidek }
349ef0a6e20SPawel Jakub Dawidek 
350ef0a6e20SPawel Jakub Dawidek static int
351ef0a6e20SPawel Jakub Dawidek padlock_process(void *arg __unused, struct cryptop *crp, int hint __unused)
352ef0a6e20SPawel Jakub Dawidek {
353ef0a6e20SPawel Jakub Dawidek 	struct padlock_softc *sc = padlock_sc;
354ef0a6e20SPawel Jakub Dawidek 	struct padlock_session *ses;
355ef0a6e20SPawel Jakub Dawidek 	union padlock_cw *cw;
356ef0a6e20SPawel Jakub Dawidek 	struct cryptodesc *crd = NULL;
357ef0a6e20SPawel Jakub Dawidek 	uint32_t *key;
358ef0a6e20SPawel Jakub Dawidek 	u_char *buf, *abuf;
359ef0a6e20SPawel Jakub Dawidek 	int err = 0;
360ef0a6e20SPawel Jakub Dawidek 
361ef0a6e20SPawel Jakub Dawidek 	buf = NULL;
362ef0a6e20SPawel Jakub Dawidek 	if (crp == NULL || crp->crp_callback == NULL) {
363ef0a6e20SPawel Jakub Dawidek 		err = EINVAL;
364ef0a6e20SPawel Jakub Dawidek 		goto out;
365ef0a6e20SPawel Jakub Dawidek 	}
366ef0a6e20SPawel Jakub Dawidek 	crd = crp->crp_desc;
367ef0a6e20SPawel Jakub Dawidek 	if (crd == NULL || crd->crd_next != NULL ||
368ef0a6e20SPawel Jakub Dawidek 	    crd->crd_alg != CRYPTO_AES_CBC ||
369ef0a6e20SPawel Jakub Dawidek 	    (crd->crd_len % 16) != 0) {
370ef0a6e20SPawel Jakub Dawidek 		err = EINVAL;
371ef0a6e20SPawel Jakub Dawidek 		goto out;
372ef0a6e20SPawel Jakub Dawidek 	}
373ef0a6e20SPawel Jakub Dawidek 
374ef0a6e20SPawel Jakub Dawidek 	mtx_lock(&sc->sc_sessions_mtx);
375ef0a6e20SPawel Jakub Dawidek 	TAILQ_FOREACH(ses, &sc->sc_sessions, ses_next) {
376ef0a6e20SPawel Jakub Dawidek 		if (ses->ses_id == (crp->crp_sid & 0xffffffff))
377ef0a6e20SPawel Jakub Dawidek 			break;
378ef0a6e20SPawel Jakub Dawidek 	}
379ef0a6e20SPawel Jakub Dawidek 	mtx_unlock(&sc->sc_sessions_mtx);
380ef0a6e20SPawel Jakub Dawidek 	if (ses == NULL) {
381ef0a6e20SPawel Jakub Dawidek 		err = EINVAL;
382ef0a6e20SPawel Jakub Dawidek 		goto out;
383ef0a6e20SPawel Jakub Dawidek 	}
384ef0a6e20SPawel Jakub Dawidek 
385ef0a6e20SPawel Jakub Dawidek 	buf = malloc(crd->crd_len + 16, M_DEVBUF, M_NOWAIT);
386ef0a6e20SPawel Jakub Dawidek 	if (buf == NULL) {
387ef0a6e20SPawel Jakub Dawidek 		err = ENOMEM;
388ef0a6e20SPawel Jakub Dawidek 		goto out;
389ef0a6e20SPawel Jakub Dawidek 	}
390ef0a6e20SPawel Jakub Dawidek 	abuf = buf + 16 - ((uintptr_t)buf % 16);
391ef0a6e20SPawel Jakub Dawidek 
392ef0a6e20SPawel Jakub Dawidek 	cw = &ses->ses_cw;
393ef0a6e20SPawel Jakub Dawidek 	cw->cw_filler0 = 0;
394ef0a6e20SPawel Jakub Dawidek 	cw->cw_filler1 = 0;
395ef0a6e20SPawel Jakub Dawidek 	cw->cw_filler2 = 0;
396ef0a6e20SPawel Jakub Dawidek 	cw->cw_filler3 = 0;
397ef0a6e20SPawel Jakub Dawidek 	if ((crd->crd_flags & CRD_F_ENCRYPT) != 0) {
398ef0a6e20SPawel Jakub Dawidek 		cw->cw_direction = PADLOCK_DIRECTION_ENCRYPT;
399ef0a6e20SPawel Jakub Dawidek 		key = ses->ses_ekey;
400ef0a6e20SPawel Jakub Dawidek 		if ((crd->crd_flags & CRD_F_IV_EXPLICIT) != 0)
401ef0a6e20SPawel Jakub Dawidek 			bcopy(crd->crd_iv, ses->ses_iv, 16);
402ef0a6e20SPawel Jakub Dawidek 
403ef0a6e20SPawel Jakub Dawidek 		if ((crd->crd_flags & CRD_F_IV_PRESENT) == 0) {
404ef0a6e20SPawel Jakub Dawidek 			if ((crp->crp_flags & CRYPTO_F_IMBUF) != 0) {
405ef0a6e20SPawel Jakub Dawidek 				m_copyback((struct mbuf *)crp->crp_buf,
406ef0a6e20SPawel Jakub Dawidek 				    crd->crd_inject, 16, ses->ses_iv);
407ef0a6e20SPawel Jakub Dawidek 			} else if ((crp->crp_flags & CRYPTO_F_IOV) != 0) {
408ef0a6e20SPawel Jakub Dawidek 				cuio_copyback((struct uio *)crp->crp_buf,
409ef0a6e20SPawel Jakub Dawidek 				    crd->crd_inject, 16, ses->ses_iv);
410ef0a6e20SPawel Jakub Dawidek 			} else {
411ef0a6e20SPawel Jakub Dawidek 				bcopy(ses->ses_iv,
412ef0a6e20SPawel Jakub Dawidek 				    crp->crp_buf + crd->crd_inject, 16);
413ef0a6e20SPawel Jakub Dawidek 			}
414ef0a6e20SPawel Jakub Dawidek 		}
415ef0a6e20SPawel Jakub Dawidek 	} else {
416ef0a6e20SPawel Jakub Dawidek 		cw->cw_direction = PADLOCK_DIRECTION_DECRYPT;
417ef0a6e20SPawel Jakub Dawidek 		key = ses->ses_dkey;
418ef0a6e20SPawel Jakub Dawidek 		if ((crd->crd_flags & CRD_F_IV_EXPLICIT) != 0)
419ef0a6e20SPawel Jakub Dawidek 			bcopy(crd->crd_iv, ses->ses_iv, 16);
420ef0a6e20SPawel Jakub Dawidek 		else {
421ef0a6e20SPawel Jakub Dawidek 			if ((crp->crp_flags & CRYPTO_F_IMBUF) != 0) {
422ef0a6e20SPawel Jakub Dawidek 				m_copydata((struct mbuf *)crp->crp_buf,
423ef0a6e20SPawel Jakub Dawidek 				    crd->crd_inject, 16, ses->ses_iv);
424ef0a6e20SPawel Jakub Dawidek 			} else if ((crp->crp_flags & CRYPTO_F_IOV) != 0) {
425ef0a6e20SPawel Jakub Dawidek 				cuio_copydata((struct uio *)crp->crp_buf,
426ef0a6e20SPawel Jakub Dawidek 				    crd->crd_inject, 16, ses->ses_iv);
427ef0a6e20SPawel Jakub Dawidek 			} else {
428ef0a6e20SPawel Jakub Dawidek 				bcopy(crp->crp_buf + crd->crd_inject,
429ef0a6e20SPawel Jakub Dawidek 				    ses->ses_iv, 16);
430ef0a6e20SPawel Jakub Dawidek 			}
431ef0a6e20SPawel Jakub Dawidek 		}
432ef0a6e20SPawel Jakub Dawidek 	}
433ef0a6e20SPawel Jakub Dawidek 
434ef0a6e20SPawel Jakub Dawidek 	if ((crp->crp_flags & CRYPTO_F_IMBUF) != 0) {
435ef0a6e20SPawel Jakub Dawidek 		m_copydata((struct mbuf *)crp->crp_buf, crd->crd_skip,
436ef0a6e20SPawel Jakub Dawidek 		    crd->crd_len, abuf);
437ef0a6e20SPawel Jakub Dawidek 	} else if ((crp->crp_flags & CRYPTO_F_IOV) != 0) {
438ef0a6e20SPawel Jakub Dawidek 		cuio_copydata((struct uio *)crp->crp_buf, crd->crd_skip,
439ef0a6e20SPawel Jakub Dawidek 		    crd->crd_len, abuf);
440ef0a6e20SPawel Jakub Dawidek 	} else {
441ef0a6e20SPawel Jakub Dawidek 		bcopy(crp->crp_buf + crd->crd_skip, abuf, crd->crd_len);
442ef0a6e20SPawel Jakub Dawidek 	}
443ef0a6e20SPawel Jakub Dawidek 
444ef0a6e20SPawel Jakub Dawidek 	padlock_cbc(abuf, abuf, crd->crd_len / 16, key, cw, ses->ses_iv);
445ef0a6e20SPawel Jakub Dawidek 
446ef0a6e20SPawel Jakub Dawidek 	if ((crp->crp_flags & CRYPTO_F_IMBUF) != 0) {
447ef0a6e20SPawel Jakub Dawidek 		m_copyback((struct mbuf *)crp->crp_buf, crd->crd_skip,
448ef0a6e20SPawel Jakub Dawidek 		    crd->crd_len, abuf);
449ef0a6e20SPawel Jakub Dawidek 	} else if ((crp->crp_flags & CRYPTO_F_IOV) != 0) {
450ef0a6e20SPawel Jakub Dawidek 		cuio_copyback((struct uio *)crp->crp_buf, crd->crd_skip,
451ef0a6e20SPawel Jakub Dawidek 		    crd->crd_len, abuf);
452ef0a6e20SPawel Jakub Dawidek 	} else {
453ef0a6e20SPawel Jakub Dawidek 		bcopy(abuf, crp->crp_buf + crd->crd_skip, crd->crd_len);
454ef0a6e20SPawel Jakub Dawidek 	}
455ef0a6e20SPawel Jakub Dawidek 
456ef0a6e20SPawel Jakub Dawidek 	/* copy out last block for use as next session IV */
457ef0a6e20SPawel Jakub Dawidek 	if ((crd->crd_flags & CRD_F_ENCRYPT) != 0) {
458ef0a6e20SPawel Jakub Dawidek 		if ((crp->crp_flags & CRYPTO_F_IMBUF) != 0) {
459ef0a6e20SPawel Jakub Dawidek 			m_copydata((struct mbuf *)crp->crp_buf,
460ef0a6e20SPawel Jakub Dawidek 			    crd->crd_skip + crd->crd_len - 16, 16, ses->ses_iv);
461ef0a6e20SPawel Jakub Dawidek 		} else if ((crp->crp_flags & CRYPTO_F_IOV) != 0) {
462ef0a6e20SPawel Jakub Dawidek 			cuio_copydata((struct uio *)crp->crp_buf,
463ef0a6e20SPawel Jakub Dawidek 			    crd->crd_skip + crd->crd_len - 16, 16, ses->ses_iv);
464ef0a6e20SPawel Jakub Dawidek 		} else {
465ef0a6e20SPawel Jakub Dawidek 			bcopy(crp->crp_buf + crd->crd_skip + crd->crd_len - 16,
466ef0a6e20SPawel Jakub Dawidek 			    ses->ses_iv, 16);
467ef0a6e20SPawel Jakub Dawidek 		}
468ef0a6e20SPawel Jakub Dawidek 	}
469ef0a6e20SPawel Jakub Dawidek 
470ef0a6e20SPawel Jakub Dawidek out:
471ef0a6e20SPawel Jakub Dawidek 	if (buf != NULL) {
472ef0a6e20SPawel Jakub Dawidek 		bzero(buf, crd->crd_len + 16);
473ef0a6e20SPawel Jakub Dawidek 		free(buf, M_DEVBUF);
474ef0a6e20SPawel Jakub Dawidek 	}
475ef0a6e20SPawel Jakub Dawidek 	crp->crp_etype = err;
476ef0a6e20SPawel Jakub Dawidek 	crypto_done(crp);
477ef0a6e20SPawel Jakub Dawidek 	return (err);
478ef0a6e20SPawel Jakub Dawidek }
479ef0a6e20SPawel Jakub Dawidek 
480ef0a6e20SPawel Jakub Dawidek static int
481ef0a6e20SPawel Jakub Dawidek padlock_modevent(module_t mod, int type, void *unused __unused)
482ef0a6e20SPawel Jakub Dawidek {
483ef0a6e20SPawel Jakub Dawidek 	int error;
484ef0a6e20SPawel Jakub Dawidek 
485ef0a6e20SPawel Jakub Dawidek 	error = EOPNOTSUPP;
486ef0a6e20SPawel Jakub Dawidek 	switch (type) {
487ef0a6e20SPawel Jakub Dawidek 	case MOD_LOAD:
488ef0a6e20SPawel Jakub Dawidek 		error = padlock_init();
489ef0a6e20SPawel Jakub Dawidek 		break;
490ef0a6e20SPawel Jakub Dawidek 	case MOD_UNLOAD:
491ef0a6e20SPawel Jakub Dawidek 		error = padlock_destroy();
492ef0a6e20SPawel Jakub Dawidek 		break;
493ef0a6e20SPawel Jakub Dawidek 	}
494ef0a6e20SPawel Jakub Dawidek 	return (error);
495ef0a6e20SPawel Jakub Dawidek }
496ef0a6e20SPawel Jakub Dawidek 
497ef0a6e20SPawel Jakub Dawidek static moduledata_t padlock_mod = {
498ef0a6e20SPawel Jakub Dawidek 	"padlock",
499ef0a6e20SPawel Jakub Dawidek 	padlock_modevent,
500ef0a6e20SPawel Jakub Dawidek 	0
501ef0a6e20SPawel Jakub Dawidek };
502ef0a6e20SPawel Jakub Dawidek DECLARE_MODULE(padlock, padlock_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
503ef0a6e20SPawel Jakub Dawidek MODULE_VERSION(padlock, 1);
504ef0a6e20SPawel Jakub Dawidek MODULE_DEPEND(padlock, crypto, 1, 1, 1);
505