#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/pool.h>
#include <crypto/cryptodev.h>
struct cryptocap *crypto_drivers;
int crypto_drivers_num = 0;
struct pool cryptop_pool;
int
crypto_newsession(u_int64_t *sid, struct cryptoini *cri, int hard)
{
u_int32_t hid, lid, hid2 = -1;
struct cryptocap *cpc;
struct cryptoini *cr;
int err, s, turn = 0;
if (crypto_drivers == NULL)
return EINVAL;
KERNEL_ASSERT_LOCKED();
s = splvm();
do {
for (hid = 0; hid < crypto_drivers_num; hid++) {
cpc = &crypto_drivers[hid];
if (cpc->cc_newsession == NULL ||
(cpc->cc_flags & CRYPTOCAP_F_CLEANUP))
continue;
if (cpc->cc_flags & CRYPTOCAP_F_SOFTWARE) {
if (turn == 0)
continue;
} else {
if (turn == 1)
continue;
}
for (cr = cri; cr; cr = cr->cri_next) {
if (cpc->cc_alg[cr->cri_alg] == 0)
break;
}
if (cr != NULL)
continue;
if (hid2 != -1) {
if (crypto_drivers[hid].cc_sessions <=
crypto_drivers[hid2].cc_sessions)
hid2 = hid;
} else {
hid2 = hid;
}
}
if (hid2 != -1)
break;
turn++;
} while (turn <= 2 && hard == 0);
hid = hid2;
if (hid == -1) {
splx(s);
return EINVAL;
}
lid = hid;
err = crypto_drivers[hid].cc_newsession(&lid, cri);
if (err == 0) {
(*sid) = hid;
(*sid) <<= 32;
(*sid) |= (lid & 0xffffffff);
crypto_drivers[hid].cc_sessions++;
}
splx(s);
return err;
}
int
crypto_freesession(u_int64_t sid)
{
int err = 0, s;
u_int32_t hid;
if (crypto_drivers == NULL)
return EINVAL;
hid = (sid >> 32) & 0xffffffff;
if (hid >= crypto_drivers_num)
return ENOENT;
KERNEL_ASSERT_LOCKED();
s = splvm();
if (crypto_drivers[hid].cc_sessions)
crypto_drivers[hid].cc_sessions--;
if (crypto_drivers[hid].cc_freesession)
err = crypto_drivers[hid].cc_freesession(sid);
if ((crypto_drivers[hid].cc_flags & CRYPTOCAP_F_CLEANUP) &&
crypto_drivers[hid].cc_sessions == 0)
explicit_bzero(&crypto_drivers[hid], sizeof(struct cryptocap));
splx(s);
return err;
}
int32_t
crypto_get_driverid(u_int8_t flags)
{
struct cryptocap *newdrv;
int i, s;
KERNEL_ASSERT_LOCKED();
s = splvm();
if (crypto_drivers_num == 0) {
crypto_drivers_num = CRYPTO_DRIVERS_INITIAL;
crypto_drivers = mallocarray(crypto_drivers_num,
sizeof(struct cryptocap), M_CRYPTO_DATA, M_NOWAIT | M_ZERO);
if (crypto_drivers == NULL) {
crypto_drivers_num = 0;
splx(s);
return -1;
}
}
for (i = 0; i < crypto_drivers_num; i++) {
if (crypto_drivers[i].cc_process == NULL &&
!(crypto_drivers[i].cc_flags & CRYPTOCAP_F_CLEANUP) &&
crypto_drivers[i].cc_sessions == 0) {
crypto_drivers[i].cc_sessions = 1;
crypto_drivers[i].cc_flags = flags;
splx(s);
return i;
}
}
if (crypto_drivers_num >= CRYPTO_DRIVERS_MAX) {
splx(s);
return -1;
}
newdrv = mallocarray(crypto_drivers_num,
2 * sizeof(struct cryptocap), M_CRYPTO_DATA, M_NOWAIT);
if (newdrv == NULL) {
splx(s);
return -1;
}
memcpy(newdrv, crypto_drivers,
crypto_drivers_num * sizeof(struct cryptocap));
bzero(&newdrv[crypto_drivers_num],
crypto_drivers_num * sizeof(struct cryptocap));
newdrv[i].cc_sessions = 1;
newdrv[i].cc_flags = flags;
free(crypto_drivers, M_CRYPTO_DATA,
crypto_drivers_num * sizeof(struct cryptocap));
crypto_drivers_num *= 2;
crypto_drivers = newdrv;
splx(s);
return i;
}
int
crypto_register(u_int32_t driverid, int *alg,
int (*newses)(u_int32_t *, struct cryptoini *),
int (*freeses)(u_int64_t), int (*process)(struct cryptop *))
{
int s, i;
if (driverid >= crypto_drivers_num || alg == NULL ||
crypto_drivers == NULL)
return EINVAL;
KERNEL_ASSERT_LOCKED();
s = splvm();
for (i = 0; i <= CRYPTO_ALGORITHM_MAX; i++) {
crypto_drivers[driverid].cc_alg[i] = alg[i];
}
crypto_drivers[driverid].cc_newsession = newses;
crypto_drivers[driverid].cc_process = process;
crypto_drivers[driverid].cc_freesession = freeses;
crypto_drivers[driverid].cc_sessions = 0;
splx(s);
return 0;
}
int
crypto_unregister(u_int32_t driverid, int alg)
{
int i = CRYPTO_ALGORITHM_MAX + 1, s;
u_int32_t ses;
KERNEL_ASSERT_LOCKED();
s = splvm();
if (driverid >= crypto_drivers_num || crypto_drivers == NULL ||
alg <= 0 || alg > (CRYPTO_ALGORITHM_MAX + 1)) {
splx(s);
return EINVAL;
}
if (alg != CRYPTO_ALGORITHM_MAX + 1) {
if (crypto_drivers[driverid].cc_alg[alg] == 0) {
splx(s);
return EINVAL;
}
crypto_drivers[driverid].cc_alg[alg] = 0;
for (i = 1; i <= CRYPTO_ALGORITHM_MAX; i++)
if (crypto_drivers[driverid].cc_alg[i] != 0)
break;
}
if (i == CRYPTO_ALGORITHM_MAX + 1 || alg == CRYPTO_ALGORITHM_MAX + 1) {
ses = crypto_drivers[driverid].cc_sessions;
bzero(&crypto_drivers[driverid], sizeof(struct cryptocap));
if (ses != 0) {
crypto_drivers[driverid].cc_flags |= CRYPTOCAP_F_CLEANUP;
crypto_drivers[driverid].cc_sessions = ses;
}
}
splx(s);
return 0;
}
int
crypto_invoke(struct cryptop *crp)
{
u_int64_t nid;
u_int32_t hid;
int error;
int s, i;
KASSERT(crp != NULL);
KERNEL_ASSERT_LOCKED();
s = splvm();
if (crp->crp_ndesc < 1 || crypto_drivers == NULL) {
error = EINVAL;
goto done;
}
hid = (crp->crp_sid >> 32) & 0xffffffff;
if (hid >= crypto_drivers_num)
goto migrate;
if (crypto_drivers[hid].cc_flags & CRYPTOCAP_F_CLEANUP) {
crypto_freesession(crp->crp_sid);
goto migrate;
}
if (crypto_drivers[hid].cc_process == NULL)
goto migrate;
crypto_drivers[hid].cc_operations++;
crypto_drivers[hid].cc_bytes += crp->crp_ilen;
error = crypto_drivers[hid].cc_process(crp);
if (error == ERESTART) {
crypto_unregister(hid, CRYPTO_ALGORITHM_MAX + 1);
goto migrate;
}
splx(s);
return error;
migrate:
for (i = 0; i < crp->crp_ndesc - 1; i++)
crp->crp_desc[i].CRD_INI.cri_next = &crp->crp_desc[i+1].CRD_INI;
crp->crp_desc[crp->crp_ndesc].CRD_INI.cri_next = NULL;
if (crypto_newsession(&nid, &(crp->crp_desc->CRD_INI), 0) == 0)
crp->crp_sid = nid;
error = EAGAIN;
done:
splx(s);
return error;
}
void
crypto_freereq(struct cryptop *crp)
{
if (crp == NULL)
return;
if (crp->crp_ndescalloc > 2)
free(crp->crp_desc, M_CRYPTO_DATA,
crp->crp_ndescalloc * sizeof(struct cryptodesc));
pool_put(&cryptop_pool, crp);
}
struct cryptop *
crypto_getreq(int num)
{
struct cryptop *crp;
crp = pool_get(&cryptop_pool, PR_NOWAIT | PR_ZERO);
if (crp == NULL)
return NULL;
crp->crp_desc = crp->crp_sdesc;
crp->crp_ndescalloc = crp->crp_ndesc = num;
if (num > 2) {
crp->crp_desc = mallocarray(num, sizeof(struct cryptodesc),
M_CRYPTO_DATA, M_NOWAIT | M_ZERO);
if (crp->crp_desc == NULL) {
pool_put(&cryptop_pool, crp);
return NULL;
}
}
return crp;
}
void
crypto_init(void)
{
pool_init(&cryptop_pool, sizeof(struct cryptop), 0, IPL_VM, 0,
"cryptop", NULL);
}