Changeset View
Changeset View
Standalone View
Standalone View
sys/dev/tpm/tpm20.c
- This file was added.
/*- | |||||
* Copyright (c) 2018 Stormshield. | |||||
* Copyright (c) 2018 Semihalf. | |||||
* All rights reserved. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | |||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | |||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||||
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, | |||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | |||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | |||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, | |||||
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN | |||||
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |||||
* POSSIBILITY OF SUCH DAMAGE. | |||||
*/ | |||||
#include "tpm20.h" | |||||
MALLOC_DECLARE(M_TPM20); | |||||
MALLOC_DEFINE(M_TPM20, "tpm_buffer", "buffer for tpm 2.0 driver"); | |||||
static void tpm20_discard_buffer(void *arg); | |||||
int | |||||
tpm20_read(struct cdev *dev, struct uio *uio, int flags) | |||||
{ | |||||
struct tpm_sc *sc; | |||||
size_t bytes_to_transfer; | |||||
int result = 0; | |||||
sc = (struct tpm_sc *)dev->si_drv1; | |||||
callout_stop(&sc->discard_buffer_callout); | |||||
sx_xlock(&sc->dev_lock); | |||||
cem: With make_dev_s (see below), these checks shouldn't be needed (or could be made assertions, if… | |||||
bytes_to_transfer = MIN(sc->pending_data_length, uio->uio_resid); | |||||
if (bytes_to_transfer > 0) { | |||||
result = uiomove((caddr_t) sc->buf, bytes_to_transfer, uio); | |||||
memset(sc->buf, 0, TPM_BUFSIZE); | |||||
sc->pending_data_length = 0; | |||||
cv_signal(&sc->buf_cv); | |||||
} else { | |||||
result = ETIMEDOUT; | |||||
} | |||||
sx_xunlock(&sc->dev_lock); | |||||
return (result); | |||||
} | |||||
int | |||||
tpm20_write(struct cdev *dev, struct uio *uio, int flags) | |||||
{ | |||||
struct tpm_sc *sc; | |||||
size_t byte_count; | |||||
int result = 0; | |||||
sc = (struct tpm_sc *)dev->si_drv1; | |||||
byte_count = uio->uio_resid; | |||||
if (byte_count < TPM_HEADER_SIZE) { | |||||
device_printf(sc->dev, | |||||
"Requested transfer is too small\n"); | |||||
return (EINVAL); | |||||
} | |||||
if (byte_count > TPM_BUFSIZE) { | |||||
device_printf(sc->dev, | |||||
"Requested transfer is too large\n"); | |||||
return (E2BIG); | |||||
} | |||||
sx_xlock(&sc->dev_lock); | |||||
while (sc->pending_data_length != 0) | |||||
cv_wait(&sc->buf_cv, &sc->dev_lock); | |||||
result = uiomove(sc->buf, byte_count, uio); | |||||
if (result != 0) { | |||||
sx_xunlock(&sc->dev_lock); | |||||
return (result); | |||||
} | |||||
result = sc->transmit(sc, byte_count); | |||||
if (result == 0) | |||||
callout_reset(&sc->discard_buffer_callout, | |||||
TPM_READ_TIMEOUT / tick, tpm20_discard_buffer, sc); | |||||
sx_xunlock(&sc->dev_lock); | |||||
return (result); | |||||
} | |||||
static void tpm20_discard_buffer(void *arg) | |||||
{ | |||||
struct tpm_sc *sc; | |||||
sc = (struct tpm_sc *)arg; | |||||
if (callout_pending(&sc->discard_buffer_callout)) | |||||
return; | |||||
sx_xlock(&sc->dev_lock); | |||||
memset(sc->buf, 0, TPM_BUFSIZE); | |||||
sc->pending_data_length = 0; | |||||
cv_signal(&sc->buf_cv); | |||||
sx_xunlock(&sc->dev_lock); | |||||
device_printf(sc->dev, | |||||
"User failed to read buffer in time\n"); | |||||
} | |||||
int | |||||
tpm20_open(struct cdev *dev, int flag, int mode, struct thread *td) | |||||
{ | |||||
return (0); | |||||
} | |||||
int | |||||
tpm20_close(struct cdev *dev, int flag, int mode, struct thread *td) | |||||
{ | |||||
return (0); | |||||
} | |||||
int | |||||
tpm20_ioctl(struct cdev *dev, u_long cmd, caddr_t data, | |||||
int flags, struct thread *td) | |||||
{ | |||||
return (ENOTTY); | |||||
} | |||||
int | |||||
tpm20_init(struct tpm_sc *sc) | |||||
{ | |||||
struct make_dev_args args; | |||||
int result; | |||||
sc->buf = malloc(TPM_BUFSIZE, M_TPM20, M_WAITOK); | |||||
sx_init(&sc->dev_lock, "TPM driver lock"); | |||||
cv_init(&sc->buf_cv, "TPM buffer cv"); | |||||
callout_init(&sc->discard_buffer_callout, 1); | |||||
sc->pending_data_length = 0; | |||||
make_dev_args_init(&args); | |||||
args.mda_devsw = &tpm_cdevsw; | |||||
args.mda_uid = UID_ROOT; | |||||
args.mda_gid = GID_WHEEL; | |||||
args.mda_mode = TPM_CDEV_PERM_FLAG; | |||||
args.mda_si_drv1 = sc; | |||||
result = make_dev_s(&args, &sc->sc_cdev, TPM_CDEV_NAME); | |||||
if (result != 0) | |||||
Done Inline ActionsI'd encourage using make_dev_s, which avoids the race condition between make_dev and si_drv1 being set. cem: I'd encourage using `make_dev_s`, which avoids the race condition between `make_dev` and… | |||||
tpm20_release(sc); | |||||
return (result); | |||||
} | |||||
void | |||||
tpm20_release(struct tpm_sc *sc) | |||||
{ | |||||
if (sc->buf != NULL) | |||||
free(sc->buf, M_TPM20); | |||||
sx_destroy(&sc->dev_lock); | |||||
cv_destroy(&sc->buf_cv); | |||||
if (sc->sc_cdev != NULL) | |||||
destroy_dev(sc->sc_cdev); | |||||
} | |||||
int | |||||
tpm20_save_state(device_t dev) | |||||
{ | |||||
struct tpm_sc *sc; | |||||
uint8_t save_cmd[] = { | |||||
0x80, 0x01, /* TPM_ST_NO_SESSIONS tag*/ | |||||
0x00, 0x00, 0x00, 0x0C, /* cmd length */ | |||||
0x00, 0x00, 0x01, 0x45, 0x00, 0x00 /* cmd TPM_CC_Shutdown */ | |||||
}; | |||||
sc = device_get_softc(dev); | |||||
if (sc == NULL || sc->buf == NULL) | |||||
return (0); | |||||
sx_xlock(&sc->dev_lock); | |||||
memcpy(sc->buf, save_cmd, sizeof(save_cmd)); | |||||
sc->transmit(sc, sizeof(save_cmd)); | |||||
sx_xunlock(&sc->dev_lock); | |||||
return (0); | |||||
} | |||||
With make_dev_s (see below), these checks shouldn't be needed (or could be made assertions, if you prefer). The devfs object lifetime is such that any threads in methods like these (d_read, etc) are drained before the object is destroyed. The logic is in destroy_devl().