aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/tpm/tpm20.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/tpm/tpm20.c')
-rw-r--r--sys/dev/tpm/tpm20.c260
1 files changed, 260 insertions, 0 deletions
diff --git a/sys/dev/tpm/tpm20.c b/sys/dev/tpm/tpm20.c
new file mode 100644
index 000000000000..5ad30c864260
--- /dev/null
+++ b/sys/dev/tpm/tpm20.c
@@ -0,0 +1,260 @@
+/*-
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#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);
+static int tpm20_save_state(device_t dev, bool suspend);
+
+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);
+
+ 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)
+ 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_suspend(device_t dev)
+{
+ return (tpm20_save_state(dev, true));
+}
+
+int
+tpm20_shutdown(device_t dev)
+{
+ return (tpm20_save_state(dev, false));
+}
+
+static int
+tpm20_save_state(device_t dev, bool suspend)
+{
+ 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);
+
+ /*
+ * Inform the TPM whether we are going to suspend or reboot/shutdown.
+ */
+ if (suspend)
+ save_cmd[11] = 1; /* TPM_SU_STATE */
+
+ 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);
+}
+
+int32_t
+tpm20_get_timeout(uint32_t command)
+{
+ int32_t timeout;
+
+ switch (command) {
+ case TPM_CC_CreatePrimary:
+ case TPM_CC_Create:
+ case TPM_CC_CreateLoaded:
+ timeout = TPM_TIMEOUT_LONG;
+ break;
+ case TPM_CC_SequenceComplete:
+ case TPM_CC_Startup:
+ case TPM_CC_SequenceUpdate:
+ case TPM_CC_GetCapability:
+ case TPM_CC_PCR_Extend:
+ case TPM_CC_EventSequenceComplete:
+ case TPM_CC_HashSequenceStart:
+ timeout = TPM_TIMEOUT_C;
+ break;
+ default:
+ timeout = TPM_TIMEOUT_B;
+ break;
+ }
+ return timeout;
+}