// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) 2005, 2012 IBM Corporation * * Authors: * Kent Yoder * Seiji Munetoh * Stefan Berger * Reiner Sailer * Kylene Hall * Nayna Jain * * Access to the event log created by a system's firmware / BIOS */ #include #include #include #include #include #include "../tpm.h" #include "common.h" static int tpm_bios_measurements_open(struct inode *inode, struct file *file) { int err; struct seq_file *seq; struct tpm_chip_seqops *chip_seqops; const struct seq_operations *seqops; struct tpm_chip *chip; inode_lock(inode); if (!inode->i_private) { inode_unlock(inode); return -ENODEV; } chip_seqops = inode->i_private; seqops = chip_seqops->seqops; chip = chip_seqops->chip; get_device(&chip->dev); inode_unlock(inode); /* now register seq file */ err = seq_open(file, seqops); if (!err) { seq = file->private_data; seq->private = chip; } return err; } static int tpm_bios_measurements_release(struct inode *inode, struct file *file) { struct seq_file *seq = file->private_data; struct tpm_chip *chip = seq->private; put_device(&chip->dev); return seq_release(inode, file); } static const struct file_operations tpm_bios_measurements_ops = { .owner = THIS_MODULE, .open = tpm_bios_measurements_open, .read = seq_read, .llseek = seq_lseek, .release = tpm_bios_measurements_release, }; static int tpm_read_log(struct tpm_chip *chip) { int rc; if (chip->log.bios_event_log != NULL) { dev_dbg(&chip->dev, "%s: ERROR - event log already initialized\n", __func__); return -EFAULT; } rc = tpm_read_log_acpi(chip); if (rc != -ENODEV) return rc; rc = tpm_read_log_efi(chip); if (rc != -ENODEV) return rc; return tpm_read_log_of(chip); } /* * tpm_bios_log_setup() - Read the event log from the firmware * @chip: TPM chip to use. * * If an event log is found then the securityfs files are setup to * export it to userspace, otherwise nothing is done. */ void tpm_bios_log_setup(struct tpm_chip *chip) { const char *name = dev_name(&chip->dev); unsigned int cnt; int log_version; int rc = 0; if (chip->flags & TPM_CHIP_FLAG_VIRTUAL) return; rc = tpm_read_log(chip); if (rc < 0) return; log_version = rc; cnt = 0; chip->bios_dir[cnt] = securityfs_create_dir(name, NULL); /* NOTE: securityfs_create_dir can return ENODEV if securityfs is * compiled out. The caller should ignore the ENODEV return code. */ if (IS_ERR(chip->bios_dir[cnt])) goto err; cnt++; chip->bin_log_seqops.chip = chip; if (log_version == EFI_TCG2_EVENT_LOG_FORMAT_TCG_2) chip->bin_log_seqops.seqops = &tpm2_binary_b_measurements_seqops; else chip->bin_log_seqops.seqops = &tpm1_binary_b_measurements_seqops; chip->bios_dir[cnt] = securityfs_create_file("binary_bios_measurements", 0440, chip->bios_dir[0], (void *)&chip->bin_log_seqops, &tpm_bios_measurements_ops); if (IS_ERR(chip->bios_dir[cnt])) goto err; cnt++; if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) { chip->ascii_log_seqops.chip = chip; chip->ascii_log_seqops.seqops = &tpm1_ascii_b_measurements_seqops; chip->bios_dir[cnt] = securityfs_create_file("ascii_bios_measurements", 0440, chip->bios_dir[0], (void *)&chip->ascii_log_seqops, &tpm_bios_measurements_ops); if (IS_ERR(chip->bios_dir[cnt])) goto err; cnt++; } return; err: chip->bios_dir[cnt] = NULL; tpm_bios_log_teardown(chip); return; } void tpm_bios_log_teardown(struct tpm_chip *chip) { int i; struct inode *inode; /* securityfs_remove currently doesn't take care of handling sync * between removal and opening of pseudo files. To handle this, a * workaround is added by making i_private = NULL here during removal * and to check it during open(), both within inode_lock()/unlock(). * This design ensures that open() either safely gets kref or fails. */ for (i = (TPM_NUM_EVENT_LOG_FILES - 1); i >= 0; i--) { if (chip->bios_dir[i]) { inode = d_inode(chip->bios_dir[i]); inode_lock(inode); inode->i_private = NULL; inode_unlock(inode); securityfs_remove(chip->bios_dir[i]); } } }