18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2004 IBM Corporation 48c2ecf20Sopenharmony_ci * Copyright (C) 2014 Intel Corporation 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Authors: 78c2ecf20Sopenharmony_ci * Jarkko Sakkinen <jarkko.sakkinen@linux.intel.com> 88c2ecf20Sopenharmony_ci * Leendert van Doorn <leendert@watson.ibm.com> 98c2ecf20Sopenharmony_ci * Dave Safford <safford@watson.ibm.com> 108c2ecf20Sopenharmony_ci * Reiner Sailer <sailer@watson.ibm.com> 118c2ecf20Sopenharmony_ci * Kylene Hall <kjhall@us.ibm.com> 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Maintained by: <tpmdd-devel@lists.sourceforge.net> 148c2ecf20Sopenharmony_ci * 158c2ecf20Sopenharmony_ci * TPM chip management routines. 168c2ecf20Sopenharmony_ci */ 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/poll.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/mutex.h> 218c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 228c2ecf20Sopenharmony_ci#include <linux/freezer.h> 238c2ecf20Sopenharmony_ci#include <linux/major.h> 248c2ecf20Sopenharmony_ci#include <linux/tpm_eventlog.h> 258c2ecf20Sopenharmony_ci#include <linux/hw_random.h> 268c2ecf20Sopenharmony_ci#include "tpm.h" 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ciDEFINE_IDR(dev_nums_idr); 298c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(idr_lock); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistruct class *tpm_class; 328c2ecf20Sopenharmony_cistruct class *tpmrm_class; 338c2ecf20Sopenharmony_cidev_t tpm_devt; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic int tpm_request_locality(struct tpm_chip *chip) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci int rc; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci if (!chip->ops->request_locality) 408c2ecf20Sopenharmony_ci return 0; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci rc = chip->ops->request_locality(chip, 0); 438c2ecf20Sopenharmony_ci if (rc < 0) 448c2ecf20Sopenharmony_ci return rc; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci chip->locality = rc; 478c2ecf20Sopenharmony_ci return 0; 488c2ecf20Sopenharmony_ci} 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_cistatic void tpm_relinquish_locality(struct tpm_chip *chip) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci int rc; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_ci if (!chip->ops->relinquish_locality) 558c2ecf20Sopenharmony_ci return; 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci rc = chip->ops->relinquish_locality(chip, chip->locality); 588c2ecf20Sopenharmony_ci if (rc) 598c2ecf20Sopenharmony_ci dev_err(&chip->dev, "%s: : error %d\n", __func__, rc); 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci chip->locality = -1; 628c2ecf20Sopenharmony_ci} 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_cistatic int tpm_cmd_ready(struct tpm_chip *chip) 658c2ecf20Sopenharmony_ci{ 668c2ecf20Sopenharmony_ci if (!chip->ops->cmd_ready) 678c2ecf20Sopenharmony_ci return 0; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci return chip->ops->cmd_ready(chip); 708c2ecf20Sopenharmony_ci} 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic int tpm_go_idle(struct tpm_chip *chip) 738c2ecf20Sopenharmony_ci{ 748c2ecf20Sopenharmony_ci if (!chip->ops->go_idle) 758c2ecf20Sopenharmony_ci return 0; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci return chip->ops->go_idle(chip); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic void tpm_clk_enable(struct tpm_chip *chip) 818c2ecf20Sopenharmony_ci{ 828c2ecf20Sopenharmony_ci if (chip->ops->clk_enable) 838c2ecf20Sopenharmony_ci chip->ops->clk_enable(chip, true); 848c2ecf20Sopenharmony_ci} 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_cistatic void tpm_clk_disable(struct tpm_chip *chip) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci if (chip->ops->clk_enable) 898c2ecf20Sopenharmony_ci chip->ops->clk_enable(chip, false); 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/** 938c2ecf20Sopenharmony_ci * tpm_chip_start() - power on the TPM 948c2ecf20Sopenharmony_ci * @chip: a TPM chip to use 958c2ecf20Sopenharmony_ci * 968c2ecf20Sopenharmony_ci * Return: 978c2ecf20Sopenharmony_ci * * The response length - OK 988c2ecf20Sopenharmony_ci * * -errno - A system error 998c2ecf20Sopenharmony_ci */ 1008c2ecf20Sopenharmony_ciint tpm_chip_start(struct tpm_chip *chip) 1018c2ecf20Sopenharmony_ci{ 1028c2ecf20Sopenharmony_ci int ret; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci tpm_clk_enable(chip); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci if (chip->locality == -1) { 1078c2ecf20Sopenharmony_ci ret = tpm_request_locality(chip); 1088c2ecf20Sopenharmony_ci if (ret) { 1098c2ecf20Sopenharmony_ci tpm_clk_disable(chip); 1108c2ecf20Sopenharmony_ci return ret; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci ret = tpm_cmd_ready(chip); 1158c2ecf20Sopenharmony_ci if (ret) { 1168c2ecf20Sopenharmony_ci tpm_relinquish_locality(chip); 1178c2ecf20Sopenharmony_ci tpm_clk_disable(chip); 1188c2ecf20Sopenharmony_ci return ret; 1198c2ecf20Sopenharmony_ci } 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return 0; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tpm_chip_start); 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci/** 1268c2ecf20Sopenharmony_ci * tpm_chip_stop() - power off the TPM 1278c2ecf20Sopenharmony_ci * @chip: a TPM chip to use 1288c2ecf20Sopenharmony_ci * 1298c2ecf20Sopenharmony_ci * Return: 1308c2ecf20Sopenharmony_ci * * The response length - OK 1318c2ecf20Sopenharmony_ci * * -errno - A system error 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_civoid tpm_chip_stop(struct tpm_chip *chip) 1348c2ecf20Sopenharmony_ci{ 1358c2ecf20Sopenharmony_ci tpm_go_idle(chip); 1368c2ecf20Sopenharmony_ci tpm_relinquish_locality(chip); 1378c2ecf20Sopenharmony_ci tpm_clk_disable(chip); 1388c2ecf20Sopenharmony_ci} 1398c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tpm_chip_stop); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci/** 1428c2ecf20Sopenharmony_ci * tpm_try_get_ops() - Get a ref to the tpm_chip 1438c2ecf20Sopenharmony_ci * @chip: Chip to ref 1448c2ecf20Sopenharmony_ci * 1458c2ecf20Sopenharmony_ci * The caller must already have some kind of locking to ensure that chip is 1468c2ecf20Sopenharmony_ci * valid. This function will lock the chip so that the ops member can be 1478c2ecf20Sopenharmony_ci * accessed safely. The locking prevents tpm_chip_unregister from 1488c2ecf20Sopenharmony_ci * completing, so it should not be held for long periods. 1498c2ecf20Sopenharmony_ci * 1508c2ecf20Sopenharmony_ci * Returns -ERRNO if the chip could not be got. 1518c2ecf20Sopenharmony_ci */ 1528c2ecf20Sopenharmony_ciint tpm_try_get_ops(struct tpm_chip *chip) 1538c2ecf20Sopenharmony_ci{ 1548c2ecf20Sopenharmony_ci int rc = -EIO; 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci get_device(&chip->dev); 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci down_read(&chip->ops_sem); 1598c2ecf20Sopenharmony_ci if (!chip->ops) 1608c2ecf20Sopenharmony_ci goto out_ops; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci mutex_lock(&chip->tpm_mutex); 1638c2ecf20Sopenharmony_ci rc = tpm_chip_start(chip); 1648c2ecf20Sopenharmony_ci if (rc) 1658c2ecf20Sopenharmony_ci goto out_lock; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci return 0; 1688c2ecf20Sopenharmony_ciout_lock: 1698c2ecf20Sopenharmony_ci mutex_unlock(&chip->tpm_mutex); 1708c2ecf20Sopenharmony_ciout_ops: 1718c2ecf20Sopenharmony_ci up_read(&chip->ops_sem); 1728c2ecf20Sopenharmony_ci put_device(&chip->dev); 1738c2ecf20Sopenharmony_ci return rc; 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tpm_try_get_ops); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci/** 1788c2ecf20Sopenharmony_ci * tpm_put_ops() - Release a ref to the tpm_chip 1798c2ecf20Sopenharmony_ci * @chip: Chip to put 1808c2ecf20Sopenharmony_ci * 1818c2ecf20Sopenharmony_ci * This is the opposite pair to tpm_try_get_ops(). After this returns chip may 1828c2ecf20Sopenharmony_ci * be kfree'd. 1838c2ecf20Sopenharmony_ci */ 1848c2ecf20Sopenharmony_civoid tpm_put_ops(struct tpm_chip *chip) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci tpm_chip_stop(chip); 1878c2ecf20Sopenharmony_ci mutex_unlock(&chip->tpm_mutex); 1888c2ecf20Sopenharmony_ci up_read(&chip->ops_sem); 1898c2ecf20Sopenharmony_ci put_device(&chip->dev); 1908c2ecf20Sopenharmony_ci} 1918c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tpm_put_ops); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci/** 1948c2ecf20Sopenharmony_ci * tpm_default_chip() - find a TPM chip and get a reference to it 1958c2ecf20Sopenharmony_ci */ 1968c2ecf20Sopenharmony_cistruct tpm_chip *tpm_default_chip(void) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci struct tpm_chip *chip, *res = NULL; 1998c2ecf20Sopenharmony_ci int chip_num = 0; 2008c2ecf20Sopenharmony_ci int chip_prev; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci mutex_lock(&idr_lock); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci do { 2058c2ecf20Sopenharmony_ci chip_prev = chip_num; 2068c2ecf20Sopenharmony_ci chip = idr_get_next(&dev_nums_idr, &chip_num); 2078c2ecf20Sopenharmony_ci if (chip) { 2088c2ecf20Sopenharmony_ci get_device(&chip->dev); 2098c2ecf20Sopenharmony_ci res = chip; 2108c2ecf20Sopenharmony_ci break; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci } while (chip_prev != chip_num); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci mutex_unlock(&idr_lock); 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return res; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tpm_default_chip); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci/** 2218c2ecf20Sopenharmony_ci * tpm_find_get_ops() - find and reserve a TPM chip 2228c2ecf20Sopenharmony_ci * @chip: a &struct tpm_chip instance, %NULL for the default chip 2238c2ecf20Sopenharmony_ci * 2248c2ecf20Sopenharmony_ci * Finds a TPM chip and reserves its class device and operations. The chip must 2258c2ecf20Sopenharmony_ci * be released with tpm_put_ops() after use. 2268c2ecf20Sopenharmony_ci * This function is for internal use only. It supports existing TPM callers 2278c2ecf20Sopenharmony_ci * by accepting NULL, but those callers should be converted to pass in a chip 2288c2ecf20Sopenharmony_ci * directly. 2298c2ecf20Sopenharmony_ci * 2308c2ecf20Sopenharmony_ci * Return: 2318c2ecf20Sopenharmony_ci * A reserved &struct tpm_chip instance. 2328c2ecf20Sopenharmony_ci * %NULL if a chip is not found. 2338c2ecf20Sopenharmony_ci * %NULL if the chip is not available. 2348c2ecf20Sopenharmony_ci */ 2358c2ecf20Sopenharmony_cistruct tpm_chip *tpm_find_get_ops(struct tpm_chip *chip) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci int rc; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci if (chip) { 2408c2ecf20Sopenharmony_ci if (!tpm_try_get_ops(chip)) 2418c2ecf20Sopenharmony_ci return chip; 2428c2ecf20Sopenharmony_ci return NULL; 2438c2ecf20Sopenharmony_ci } 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci chip = tpm_default_chip(); 2468c2ecf20Sopenharmony_ci if (!chip) 2478c2ecf20Sopenharmony_ci return NULL; 2488c2ecf20Sopenharmony_ci rc = tpm_try_get_ops(chip); 2498c2ecf20Sopenharmony_ci /* release additional reference we got from tpm_default_chip() */ 2508c2ecf20Sopenharmony_ci put_device(&chip->dev); 2518c2ecf20Sopenharmony_ci if (rc) 2528c2ecf20Sopenharmony_ci return NULL; 2538c2ecf20Sopenharmony_ci return chip; 2548c2ecf20Sopenharmony_ci} 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci/** 2578c2ecf20Sopenharmony_ci * tpm_dev_release() - free chip memory and the device number 2588c2ecf20Sopenharmony_ci * @dev: the character device for the TPM chip 2598c2ecf20Sopenharmony_ci * 2608c2ecf20Sopenharmony_ci * This is used as the release function for the character device. 2618c2ecf20Sopenharmony_ci */ 2628c2ecf20Sopenharmony_cistatic void tpm_dev_release(struct device *dev) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci struct tpm_chip *chip = container_of(dev, struct tpm_chip, dev); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci mutex_lock(&idr_lock); 2678c2ecf20Sopenharmony_ci idr_remove(&dev_nums_idr, chip->dev_num); 2688c2ecf20Sopenharmony_ci mutex_unlock(&idr_lock); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci kfree(chip->log.bios_event_log); 2718c2ecf20Sopenharmony_ci kfree(chip->work_space.context_buf); 2728c2ecf20Sopenharmony_ci kfree(chip->work_space.session_buf); 2738c2ecf20Sopenharmony_ci kfree(chip->allocated_banks); 2748c2ecf20Sopenharmony_ci kfree(chip); 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci/** 2788c2ecf20Sopenharmony_ci * tpm_class_shutdown() - prepare the TPM device for loss of power. 2798c2ecf20Sopenharmony_ci * @dev: device to which the chip is associated. 2808c2ecf20Sopenharmony_ci * 2818c2ecf20Sopenharmony_ci * Issues a TPM2_Shutdown command prior to loss of power, as required by the 2828c2ecf20Sopenharmony_ci * TPM 2.0 spec. Then, calls bus- and device- specific shutdown code. 2838c2ecf20Sopenharmony_ci * 2848c2ecf20Sopenharmony_ci * Return: always 0 (i.e. success) 2858c2ecf20Sopenharmony_ci */ 2868c2ecf20Sopenharmony_cistatic int tpm_class_shutdown(struct device *dev) 2878c2ecf20Sopenharmony_ci{ 2888c2ecf20Sopenharmony_ci struct tpm_chip *chip = container_of(dev, struct tpm_chip, dev); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci down_write(&chip->ops_sem); 2918c2ecf20Sopenharmony_ci if (chip->flags & TPM_CHIP_FLAG_TPM2) { 2928c2ecf20Sopenharmony_ci if (!tpm_chip_start(chip)) { 2938c2ecf20Sopenharmony_ci tpm2_shutdown(chip, TPM2_SU_CLEAR); 2948c2ecf20Sopenharmony_ci tpm_chip_stop(chip); 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci chip->ops = NULL; 2988c2ecf20Sopenharmony_ci up_write(&chip->ops_sem); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci return 0; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci/** 3048c2ecf20Sopenharmony_ci * tpm_chip_alloc() - allocate a new struct tpm_chip instance 3058c2ecf20Sopenharmony_ci * @pdev: device to which the chip is associated 3068c2ecf20Sopenharmony_ci * At this point pdev mst be initialized, but does not have to 3078c2ecf20Sopenharmony_ci * be registered 3088c2ecf20Sopenharmony_ci * @ops: struct tpm_class_ops instance 3098c2ecf20Sopenharmony_ci * 3108c2ecf20Sopenharmony_ci * Allocates a new struct tpm_chip instance and assigns a free 3118c2ecf20Sopenharmony_ci * device number for it. Must be paired with put_device(&chip->dev). 3128c2ecf20Sopenharmony_ci */ 3138c2ecf20Sopenharmony_cistruct tpm_chip *tpm_chip_alloc(struct device *pdev, 3148c2ecf20Sopenharmony_ci const struct tpm_class_ops *ops) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci struct tpm_chip *chip; 3178c2ecf20Sopenharmony_ci int rc; 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci chip = kzalloc(sizeof(*chip), GFP_KERNEL); 3208c2ecf20Sopenharmony_ci if (chip == NULL) 3218c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci mutex_init(&chip->tpm_mutex); 3248c2ecf20Sopenharmony_ci init_rwsem(&chip->ops_sem); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci chip->ops = ops; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci mutex_lock(&idr_lock); 3298c2ecf20Sopenharmony_ci rc = idr_alloc(&dev_nums_idr, NULL, 0, TPM_NUM_DEVICES, GFP_KERNEL); 3308c2ecf20Sopenharmony_ci mutex_unlock(&idr_lock); 3318c2ecf20Sopenharmony_ci if (rc < 0) { 3328c2ecf20Sopenharmony_ci dev_err(pdev, "No available tpm device numbers\n"); 3338c2ecf20Sopenharmony_ci kfree(chip); 3348c2ecf20Sopenharmony_ci return ERR_PTR(rc); 3358c2ecf20Sopenharmony_ci } 3368c2ecf20Sopenharmony_ci chip->dev_num = rc; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci device_initialize(&chip->dev); 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci chip->dev.class = tpm_class; 3418c2ecf20Sopenharmony_ci chip->dev.class->shutdown_pre = tpm_class_shutdown; 3428c2ecf20Sopenharmony_ci chip->dev.release = tpm_dev_release; 3438c2ecf20Sopenharmony_ci chip->dev.parent = pdev; 3448c2ecf20Sopenharmony_ci chip->dev.groups = chip->groups; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (chip->dev_num == 0) 3478c2ecf20Sopenharmony_ci chip->dev.devt = MKDEV(MISC_MAJOR, TPM_MINOR); 3488c2ecf20Sopenharmony_ci else 3498c2ecf20Sopenharmony_ci chip->dev.devt = MKDEV(MAJOR(tpm_devt), chip->dev_num); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci rc = dev_set_name(&chip->dev, "tpm%d", chip->dev_num); 3528c2ecf20Sopenharmony_ci if (rc) 3538c2ecf20Sopenharmony_ci goto out; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (!pdev) 3568c2ecf20Sopenharmony_ci chip->flags |= TPM_CHIP_FLAG_VIRTUAL; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci cdev_init(&chip->cdev, &tpm_fops); 3598c2ecf20Sopenharmony_ci chip->cdev.owner = THIS_MODULE; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci rc = tpm2_init_space(&chip->work_space, TPM2_SPACE_BUFFER_SIZE); 3628c2ecf20Sopenharmony_ci if (rc) { 3638c2ecf20Sopenharmony_ci rc = -ENOMEM; 3648c2ecf20Sopenharmony_ci goto out; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci chip->locality = -1; 3688c2ecf20Sopenharmony_ci return chip; 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ciout: 3718c2ecf20Sopenharmony_ci put_device(&chip->dev); 3728c2ecf20Sopenharmony_ci return ERR_PTR(rc); 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tpm_chip_alloc); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci/** 3778c2ecf20Sopenharmony_ci * tpmm_chip_alloc() - allocate a new struct tpm_chip instance 3788c2ecf20Sopenharmony_ci * @pdev: parent device to which the chip is associated 3798c2ecf20Sopenharmony_ci * @ops: struct tpm_class_ops instance 3808c2ecf20Sopenharmony_ci * 3818c2ecf20Sopenharmony_ci * Same as tpm_chip_alloc except devm is used to do the put_device 3828c2ecf20Sopenharmony_ci */ 3838c2ecf20Sopenharmony_cistruct tpm_chip *tpmm_chip_alloc(struct device *pdev, 3848c2ecf20Sopenharmony_ci const struct tpm_class_ops *ops) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci struct tpm_chip *chip; 3878c2ecf20Sopenharmony_ci int rc; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci chip = tpm_chip_alloc(pdev, ops); 3908c2ecf20Sopenharmony_ci if (IS_ERR(chip)) 3918c2ecf20Sopenharmony_ci return chip; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci rc = devm_add_action_or_reset(pdev, 3948c2ecf20Sopenharmony_ci (void (*)(void *)) put_device, 3958c2ecf20Sopenharmony_ci &chip->dev); 3968c2ecf20Sopenharmony_ci if (rc) 3978c2ecf20Sopenharmony_ci return ERR_PTR(rc); 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci dev_set_drvdata(pdev, chip); 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci return chip; 4028c2ecf20Sopenharmony_ci} 4038c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tpmm_chip_alloc); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic int tpm_add_char_device(struct tpm_chip *chip) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci int rc; 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci rc = cdev_device_add(&chip->cdev, &chip->dev); 4108c2ecf20Sopenharmony_ci if (rc) { 4118c2ecf20Sopenharmony_ci dev_err(&chip->dev, 4128c2ecf20Sopenharmony_ci "unable to cdev_device_add() %s, major %d, minor %d, err=%d\n", 4138c2ecf20Sopenharmony_ci dev_name(&chip->dev), MAJOR(chip->dev.devt), 4148c2ecf20Sopenharmony_ci MINOR(chip->dev.devt), rc); 4158c2ecf20Sopenharmony_ci return rc; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci if (chip->flags & TPM_CHIP_FLAG_TPM2) { 4198c2ecf20Sopenharmony_ci rc = tpm_devs_add(chip); 4208c2ecf20Sopenharmony_ci if (rc) 4218c2ecf20Sopenharmony_ci goto err_del_cdev; 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_ci /* Make the chip available. */ 4258c2ecf20Sopenharmony_ci mutex_lock(&idr_lock); 4268c2ecf20Sopenharmony_ci idr_replace(&dev_nums_idr, chip, chip->dev_num); 4278c2ecf20Sopenharmony_ci mutex_unlock(&idr_lock); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci return 0; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_cierr_del_cdev: 4328c2ecf20Sopenharmony_ci cdev_device_del(&chip->cdev, &chip->dev); 4338c2ecf20Sopenharmony_ci return rc; 4348c2ecf20Sopenharmony_ci} 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_cistatic void tpm_del_char_device(struct tpm_chip *chip) 4378c2ecf20Sopenharmony_ci{ 4388c2ecf20Sopenharmony_ci cdev_device_del(&chip->cdev, &chip->dev); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci /* Make the chip unavailable. */ 4418c2ecf20Sopenharmony_ci mutex_lock(&idr_lock); 4428c2ecf20Sopenharmony_ci idr_replace(&dev_nums_idr, NULL, chip->dev_num); 4438c2ecf20Sopenharmony_ci mutex_unlock(&idr_lock); 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci /* Make the driver uncallable. */ 4468c2ecf20Sopenharmony_ci down_write(&chip->ops_sem); 4478c2ecf20Sopenharmony_ci if (chip->flags & TPM_CHIP_FLAG_TPM2) { 4488c2ecf20Sopenharmony_ci if (!tpm_chip_start(chip)) { 4498c2ecf20Sopenharmony_ci tpm2_shutdown(chip, TPM2_SU_CLEAR); 4508c2ecf20Sopenharmony_ci tpm_chip_stop(chip); 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci chip->ops = NULL; 4548c2ecf20Sopenharmony_ci up_write(&chip->ops_sem); 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cistatic void tpm_del_legacy_sysfs(struct tpm_chip *chip) 4588c2ecf20Sopenharmony_ci{ 4598c2ecf20Sopenharmony_ci struct attribute **i; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (chip->flags & (TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL)) 4628c2ecf20Sopenharmony_ci return; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci sysfs_remove_link(&chip->dev.parent->kobj, "ppi"); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci for (i = chip->groups[0]->attrs; *i != NULL; ++i) 4678c2ecf20Sopenharmony_ci sysfs_remove_link(&chip->dev.parent->kobj, (*i)->name); 4688c2ecf20Sopenharmony_ci} 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci/* For compatibility with legacy sysfs paths we provide symlinks from the 4718c2ecf20Sopenharmony_ci * parent dev directory to selected names within the tpm chip directory. Old 4728c2ecf20Sopenharmony_ci * kernel versions created these files directly under the parent. 4738c2ecf20Sopenharmony_ci */ 4748c2ecf20Sopenharmony_cistatic int tpm_add_legacy_sysfs(struct tpm_chip *chip) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci struct attribute **i; 4778c2ecf20Sopenharmony_ci int rc; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci if (chip->flags & (TPM_CHIP_FLAG_TPM2 | TPM_CHIP_FLAG_VIRTUAL)) 4808c2ecf20Sopenharmony_ci return 0; 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci rc = compat_only_sysfs_link_entry_to_kobj( 4838c2ecf20Sopenharmony_ci &chip->dev.parent->kobj, &chip->dev.kobj, "ppi", NULL); 4848c2ecf20Sopenharmony_ci if (rc && rc != -ENOENT) 4858c2ecf20Sopenharmony_ci return rc; 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci /* All the names from tpm-sysfs */ 4888c2ecf20Sopenharmony_ci for (i = chip->groups[0]->attrs; *i != NULL; ++i) { 4898c2ecf20Sopenharmony_ci rc = compat_only_sysfs_link_entry_to_kobj( 4908c2ecf20Sopenharmony_ci &chip->dev.parent->kobj, &chip->dev.kobj, (*i)->name, NULL); 4918c2ecf20Sopenharmony_ci if (rc) { 4928c2ecf20Sopenharmony_ci tpm_del_legacy_sysfs(chip); 4938c2ecf20Sopenharmony_ci return rc; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci return 0; 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic int tpm_hwrng_read(struct hwrng *rng, void *data, size_t max, bool wait) 5018c2ecf20Sopenharmony_ci{ 5028c2ecf20Sopenharmony_ci struct tpm_chip *chip = container_of(rng, struct tpm_chip, hwrng); 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci return tpm_get_random(chip, data, max); 5058c2ecf20Sopenharmony_ci} 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_cistatic int tpm_add_hwrng(struct tpm_chip *chip) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_HW_RANDOM_TPM)) 5108c2ecf20Sopenharmony_ci return 0; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci snprintf(chip->hwrng_name, sizeof(chip->hwrng_name), 5138c2ecf20Sopenharmony_ci "tpm-rng-%d", chip->dev_num); 5148c2ecf20Sopenharmony_ci chip->hwrng.name = chip->hwrng_name; 5158c2ecf20Sopenharmony_ci chip->hwrng.read = tpm_hwrng_read; 5168c2ecf20Sopenharmony_ci return hwrng_register(&chip->hwrng); 5178c2ecf20Sopenharmony_ci} 5188c2ecf20Sopenharmony_ci 5198c2ecf20Sopenharmony_cistatic int tpm_get_pcr_allocation(struct tpm_chip *chip) 5208c2ecf20Sopenharmony_ci{ 5218c2ecf20Sopenharmony_ci int rc; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci rc = (chip->flags & TPM_CHIP_FLAG_TPM2) ? 5248c2ecf20Sopenharmony_ci tpm2_get_pcr_allocation(chip) : 5258c2ecf20Sopenharmony_ci tpm1_get_pcr_allocation(chip); 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci if (rc > 0) 5288c2ecf20Sopenharmony_ci return -ENODEV; 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci return rc; 5318c2ecf20Sopenharmony_ci} 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci/* 5348c2ecf20Sopenharmony_ci * tpm_chip_register() - create a character device for the TPM chip 5358c2ecf20Sopenharmony_ci * @chip: TPM chip to use. 5368c2ecf20Sopenharmony_ci * 5378c2ecf20Sopenharmony_ci * Creates a character device for the TPM chip and adds sysfs attributes for 5388c2ecf20Sopenharmony_ci * the device. As the last step this function adds the chip to the list of TPM 5398c2ecf20Sopenharmony_ci * chips available for in-kernel use. 5408c2ecf20Sopenharmony_ci * 5418c2ecf20Sopenharmony_ci * This function should be only called after the chip initialization is 5428c2ecf20Sopenharmony_ci * complete. 5438c2ecf20Sopenharmony_ci */ 5448c2ecf20Sopenharmony_ciint tpm_chip_register(struct tpm_chip *chip) 5458c2ecf20Sopenharmony_ci{ 5468c2ecf20Sopenharmony_ci int rc; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci rc = tpm_chip_start(chip); 5498c2ecf20Sopenharmony_ci if (rc) 5508c2ecf20Sopenharmony_ci return rc; 5518c2ecf20Sopenharmony_ci rc = tpm_auto_startup(chip); 5528c2ecf20Sopenharmony_ci if (rc) { 5538c2ecf20Sopenharmony_ci tpm_chip_stop(chip); 5548c2ecf20Sopenharmony_ci return rc; 5558c2ecf20Sopenharmony_ci } 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci rc = tpm_get_pcr_allocation(chip); 5588c2ecf20Sopenharmony_ci tpm_chip_stop(chip); 5598c2ecf20Sopenharmony_ci if (rc) 5608c2ecf20Sopenharmony_ci return rc; 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci tpm_sysfs_add_device(chip); 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci tpm_bios_log_setup(chip); 5658c2ecf20Sopenharmony_ci 5668c2ecf20Sopenharmony_ci tpm_add_ppi(chip); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci rc = tpm_add_hwrng(chip); 5698c2ecf20Sopenharmony_ci if (rc) 5708c2ecf20Sopenharmony_ci goto out_ppi; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci rc = tpm_add_char_device(chip); 5738c2ecf20Sopenharmony_ci if (rc) 5748c2ecf20Sopenharmony_ci goto out_hwrng; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci rc = tpm_add_legacy_sysfs(chip); 5778c2ecf20Sopenharmony_ci if (rc) { 5788c2ecf20Sopenharmony_ci tpm_chip_unregister(chip); 5798c2ecf20Sopenharmony_ci return rc; 5808c2ecf20Sopenharmony_ci } 5818c2ecf20Sopenharmony_ci 5828c2ecf20Sopenharmony_ci return 0; 5838c2ecf20Sopenharmony_ci 5848c2ecf20Sopenharmony_ciout_hwrng: 5858c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_HW_RANDOM_TPM)) 5868c2ecf20Sopenharmony_ci hwrng_unregister(&chip->hwrng); 5878c2ecf20Sopenharmony_ciout_ppi: 5888c2ecf20Sopenharmony_ci tpm_bios_log_teardown(chip); 5898c2ecf20Sopenharmony_ci 5908c2ecf20Sopenharmony_ci return rc; 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tpm_chip_register); 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_ci/* 5958c2ecf20Sopenharmony_ci * tpm_chip_unregister() - release the TPM driver 5968c2ecf20Sopenharmony_ci * @chip: TPM chip to use. 5978c2ecf20Sopenharmony_ci * 5988c2ecf20Sopenharmony_ci * Takes the chip first away from the list of available TPM chips and then 5998c2ecf20Sopenharmony_ci * cleans up all the resources reserved by tpm_chip_register(). 6008c2ecf20Sopenharmony_ci * 6018c2ecf20Sopenharmony_ci * Once this function returns the driver call backs in 'op's will not be 6028c2ecf20Sopenharmony_ci * running and will no longer start. 6038c2ecf20Sopenharmony_ci * 6048c2ecf20Sopenharmony_ci * NOTE: This function should be only called before deinitializing chip 6058c2ecf20Sopenharmony_ci * resources. 6068c2ecf20Sopenharmony_ci */ 6078c2ecf20Sopenharmony_civoid tpm_chip_unregister(struct tpm_chip *chip) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci tpm_del_legacy_sysfs(chip); 6108c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_HW_RANDOM_TPM)) 6118c2ecf20Sopenharmony_ci hwrng_unregister(&chip->hwrng); 6128c2ecf20Sopenharmony_ci tpm_bios_log_teardown(chip); 6138c2ecf20Sopenharmony_ci if (chip->flags & TPM_CHIP_FLAG_TPM2) 6148c2ecf20Sopenharmony_ci tpm_devs_remove(chip); 6158c2ecf20Sopenharmony_ci tpm_del_char_device(chip); 6168c2ecf20Sopenharmony_ci} 6178c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(tpm_chip_unregister); 618