tpm-chip.c 5.0 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
/*
 * Copyright (C) 2004 IBM Corporation
 * Copyright (C) 2014 Intel Corporation
 *
 * Authors:
 * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com>
 * Leendert van Doorn <leendert@watson.ibm.com>
 * Dave Safford <safford@watson.ibm.com>
 * Reiner Sailer <sailer@watson.ibm.com>
 * Kylene Hall <kjhall@us.ibm.com>
 *
 * Maintained by: <tpmdd-devel@lists.sourceforge.net>
 *
 * TPM chip management routines.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, version 2 of the
 * License.
 *
 */

#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/freezer.h>
#include "tpm.h"
#include "tpm_eventlog.h"

static DECLARE_BITMAP(dev_mask, TPM_NUM_DEVICES);
static LIST_HEAD(tpm_chip_list);
static DEFINE_SPINLOCK(driver_lock);

/*
 * tpm_chip_find_get - return tpm_chip for a given chip number
 * @chip_num the device number for the chip
 */
struct tpm_chip *tpm_chip_find_get(int chip_num)
{
	struct tpm_chip *pos, *chip = NULL;

	rcu_read_lock();
	list_for_each_entry_rcu(pos, &tpm_chip_list, list) {
		if (chip_num != TPM_ANY_NUM && chip_num != pos->dev_num)
			continue;

48
		if (try_module_get(pos->pdev->driver->owner)) {
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
			chip = pos;
			break;
		}
	}
	rcu_read_unlock();
	return chip;
}

/**
 * tpmm_chip_remove() - free chip memory and device number
 * @data: points to struct tpm_chip instance
 *
 * This is used internally by tpmm_chip_alloc() and called by devres
 * when the device is released. This function does the opposite of
 * tpmm_chip_alloc() freeing memory and the device number.
 */
static void tpmm_chip_remove(void *data)
{
	struct tpm_chip *chip = (struct tpm_chip *) data;

	spin_lock(&driver_lock);
	clear_bit(chip->dev_num, dev_mask);
	spin_unlock(&driver_lock);
	kfree(chip);
}

/**
 * tpmm_chip_alloc() - allocate a new struct tpm_chip instance
 * @dev: device to which the chip is associated
 * @ops: struct tpm_class_ops instance
 *
 * Allocates a new struct tpm_chip instance and assigns a free
 * device number for it. Caller does not have to worry about
 * freeing the allocated resources. When the devices is removed
 * devres calls tpmm_chip_remove() to do the job.
 */
struct tpm_chip *tpmm_chip_alloc(struct device *dev,
				 const struct tpm_class_ops *ops)
{
	struct tpm_chip *chip;

	chip = kzalloc(sizeof(*chip), GFP_KERNEL);
	if (chip == NULL)
		return ERR_PTR(-ENOMEM);

	mutex_init(&chip->tpm_mutex);
	INIT_LIST_HEAD(&chip->list);

	chip->ops = ops;

	spin_lock(&driver_lock);
	chip->dev_num = find_first_zero_bit(dev_mask, TPM_NUM_DEVICES);
	spin_unlock(&driver_lock);

	if (chip->dev_num >= TPM_NUM_DEVICES) {
		dev_err(dev, "No available tpm device numbers\n");
		kfree(chip);
		return ERR_PTR(-ENOMEM);
	}

	set_bit(chip->dev_num, dev_mask);

	scnprintf(chip->devname, sizeof(chip->devname), "tpm%d", chip->dev_num);

113
	chip->pdev = dev;
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
	devm_add_action(dev, tpmm_chip_remove, chip);
	dev_set_drvdata(dev, chip);

	return chip;
}
EXPORT_SYMBOL_GPL(tpmm_chip_alloc);

/*
 * tpm_chip_register() - create a misc driver for the TPM chip
 * @chip: TPM chip to use.
 *
 * Creates a misc driver for the TPM chip and adds sysfs interfaces for
 * the device, PPI and TCPA. As the last step this function adds the
 * chip to the list of TPM chips available for use.
 *
 * NOTE: This function should be only called after the chip initialization
 * is complete.
 *
 * Called from tpm_<specific>.c probe function only for devices
 * the driver has determined it should claim.  Prior to calling
 * this function the specific probe function has called pci_enable_device
 * upon errant exit from this function specific probe function should call
 * pci_disable_device
 */
int tpm_chip_register(struct tpm_chip *chip)
{
	int rc;

	rc = tpm_dev_add_device(chip);
	if (rc)
		return rc;

	rc = tpm_sysfs_add_device(chip);
	if (rc)
		goto del_misc;

150
	rc = tpm_add_ppi(chip);
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
	if (rc)
		goto del_sysfs;

	chip->bios_dir = tpm_bios_log_setup(chip->devname);

	/* Make the chip available. */
	spin_lock(&driver_lock);
	list_add_rcu(&chip->list, &tpm_chip_list);
	spin_unlock(&driver_lock);

	chip->flags |= TPM_CHIP_FLAG_REGISTERED;

	return 0;
del_sysfs:
	tpm_sysfs_del_device(chip);
del_misc:
	tpm_dev_del_device(chip);
	return rc;
}
EXPORT_SYMBOL_GPL(tpm_chip_register);

/*
 * tpm_chip_unregister() - release the TPM driver
 * @chip: TPM chip to use.
 *
 * Takes the chip first away from the list of available TPM chips and then
 * cleans up all the resources reserved by tpm_chip_register().
 *
 * NOTE: This function should be only called before deinitializing chip
 * resources.
 */
void tpm_chip_unregister(struct tpm_chip *chip)
{
	if (!(chip->flags & TPM_CHIP_FLAG_REGISTERED))
		return;

	spin_lock(&driver_lock);
	list_del_rcu(&chip->list);
	spin_unlock(&driver_lock);
	synchronize_rcu();

	if (chip->bios_dir)
		tpm_bios_log_teardown(chip->bios_dir);
194
	tpm_remove_ppi(chip);
195 196 197 198 199
	tpm_sysfs_del_device(chip);

	tpm_dev_del_device(chip);
}
EXPORT_SYMBOL_GPL(tpm_chip_unregister);