162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * efi_secret module
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2022 IBM Corporation
662306a36Sopenharmony_ci * Author: Dov Murik <dovmurik@linux.ibm.com>
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci
962306a36Sopenharmony_ci/**
1062306a36Sopenharmony_ci * DOC: efi_secret: Allow reading EFI confidential computing (coco) secret area
1162306a36Sopenharmony_ci * via securityfs interface.
1262306a36Sopenharmony_ci *
1362306a36Sopenharmony_ci * When the module is loaded (and securityfs is mounted, typically under
1462306a36Sopenharmony_ci * /sys/kernel/security), a "secrets/coco" directory is created in securityfs.
1562306a36Sopenharmony_ci * In it, a file is created for each secret entry.  The name of each such file
1662306a36Sopenharmony_ci * is the GUID of the secret entry, and its content is the secret data.
1762306a36Sopenharmony_ci */
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <linux/platform_device.h>
2062306a36Sopenharmony_ci#include <linux/seq_file.h>
2162306a36Sopenharmony_ci#include <linux/fs.h>
2262306a36Sopenharmony_ci#include <linux/kernel.h>
2362306a36Sopenharmony_ci#include <linux/init.h>
2462306a36Sopenharmony_ci#include <linux/module.h>
2562306a36Sopenharmony_ci#include <linux/io.h>
2662306a36Sopenharmony_ci#include <linux/security.h>
2762306a36Sopenharmony_ci#include <linux/efi.h>
2862306a36Sopenharmony_ci#include <linux/cacheflush.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci#define EFI_SECRET_NUM_FILES 64
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_cistruct efi_secret {
3362306a36Sopenharmony_ci	struct dentry *secrets_dir;
3462306a36Sopenharmony_ci	struct dentry *fs_dir;
3562306a36Sopenharmony_ci	struct dentry *fs_files[EFI_SECRET_NUM_FILES];
3662306a36Sopenharmony_ci	void __iomem *secret_data;
3762306a36Sopenharmony_ci	u64 secret_data_len;
3862306a36Sopenharmony_ci};
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci/*
4162306a36Sopenharmony_ci * Structure of the EFI secret area
4262306a36Sopenharmony_ci *
4362306a36Sopenharmony_ci * Offset   Length
4462306a36Sopenharmony_ci * (bytes)  (bytes)  Usage
4562306a36Sopenharmony_ci * -------  -------  -----
4662306a36Sopenharmony_ci *       0       16  Secret table header GUID (must be 1e74f542-71dd-4d66-963e-ef4287ff173b)
4762306a36Sopenharmony_ci *      16        4  Length of bytes of the entire secret area
4862306a36Sopenharmony_ci *
4962306a36Sopenharmony_ci *      20       16  First secret entry's GUID
5062306a36Sopenharmony_ci *      36        4  First secret entry's length in bytes (= 16 + 4 + x)
5162306a36Sopenharmony_ci *      40        x  First secret entry's data
5262306a36Sopenharmony_ci *
5362306a36Sopenharmony_ci *    40+x       16  Second secret entry's GUID
5462306a36Sopenharmony_ci *    56+x        4  Second secret entry's length in bytes (= 16 + 4 + y)
5562306a36Sopenharmony_ci *    60+x        y  Second secret entry's data
5662306a36Sopenharmony_ci *
5762306a36Sopenharmony_ci * (... and so on for additional entries)
5862306a36Sopenharmony_ci *
5962306a36Sopenharmony_ci * The GUID of each secret entry designates the usage of the secret data.
6062306a36Sopenharmony_ci */
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/**
6362306a36Sopenharmony_ci * struct secret_header - Header of entire secret area; this should be followed
6462306a36Sopenharmony_ci * by instances of struct secret_entry.
6562306a36Sopenharmony_ci * @guid:	Must be EFI_SECRET_TABLE_HEADER_GUID
6662306a36Sopenharmony_ci * @len:	Length in bytes of entire secret area, including header
6762306a36Sopenharmony_ci */
6862306a36Sopenharmony_cistruct secret_header {
6962306a36Sopenharmony_ci	efi_guid_t guid;
7062306a36Sopenharmony_ci	u32 len;
7162306a36Sopenharmony_ci} __attribute((packed));
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/**
7462306a36Sopenharmony_ci * struct secret_entry - Holds one secret entry
7562306a36Sopenharmony_ci * @guid:	Secret-specific GUID (or NULL_GUID if this secret entry was deleted)
7662306a36Sopenharmony_ci * @len:	Length of secret entry, including its guid and len fields
7762306a36Sopenharmony_ci * @data:	The secret data (full of zeros if this secret entry was deleted)
7862306a36Sopenharmony_ci */
7962306a36Sopenharmony_cistruct secret_entry {
8062306a36Sopenharmony_ci	efi_guid_t guid;
8162306a36Sopenharmony_ci	u32 len;
8262306a36Sopenharmony_ci	u8 data[];
8362306a36Sopenharmony_ci} __attribute((packed));
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic size_t secret_entry_data_len(struct secret_entry *e)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	return e->len - sizeof(*e);
8862306a36Sopenharmony_ci}
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_cistatic struct efi_secret the_efi_secret;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_cistatic inline struct efi_secret *efi_secret_get(void)
9362306a36Sopenharmony_ci{
9462306a36Sopenharmony_ci	return &the_efi_secret;
9562306a36Sopenharmony_ci}
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_cistatic int efi_secret_bin_file_show(struct seq_file *file, void *data)
9862306a36Sopenharmony_ci{
9962306a36Sopenharmony_ci	struct secret_entry *e = file->private;
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	if (e)
10262306a36Sopenharmony_ci		seq_write(file, e->data, secret_entry_data_len(e));
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	return 0;
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(efi_secret_bin_file);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci/*
10962306a36Sopenharmony_ci * Overwrite memory content with zeroes, and ensure that dirty cache lines are
11062306a36Sopenharmony_ci * actually written back to memory, to clear out the secret.
11162306a36Sopenharmony_ci */
11262306a36Sopenharmony_cistatic void wipe_memory(void *addr, size_t size)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	memzero_explicit(addr, size);
11562306a36Sopenharmony_ci#ifdef CONFIG_X86
11662306a36Sopenharmony_ci	clflush_cache_range(addr, size);
11762306a36Sopenharmony_ci#endif
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_cistatic int efi_secret_unlink(struct inode *dir, struct dentry *dentry)
12162306a36Sopenharmony_ci{
12262306a36Sopenharmony_ci	struct efi_secret *s = efi_secret_get();
12362306a36Sopenharmony_ci	struct inode *inode = d_inode(dentry);
12462306a36Sopenharmony_ci	struct secret_entry *e = (struct secret_entry *)inode->i_private;
12562306a36Sopenharmony_ci	int i;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	if (e) {
12862306a36Sopenharmony_ci		/* Zero out the secret data */
12962306a36Sopenharmony_ci		wipe_memory(e->data, secret_entry_data_len(e));
13062306a36Sopenharmony_ci		e->guid = NULL_GUID;
13162306a36Sopenharmony_ci	}
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	inode->i_private = NULL;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	for (i = 0; i < EFI_SECRET_NUM_FILES; i++)
13662306a36Sopenharmony_ci		if (s->fs_files[i] == dentry)
13762306a36Sopenharmony_ci			s->fs_files[i] = NULL;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	/*
14062306a36Sopenharmony_ci	 * securityfs_remove tries to lock the directory's inode, but we reach
14162306a36Sopenharmony_ci	 * the unlink callback when it's already locked
14262306a36Sopenharmony_ci	 */
14362306a36Sopenharmony_ci	inode_unlock(dir);
14462306a36Sopenharmony_ci	securityfs_remove(dentry);
14562306a36Sopenharmony_ci	inode_lock(dir);
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	return 0;
14862306a36Sopenharmony_ci}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_cistatic const struct inode_operations efi_secret_dir_inode_operations = {
15162306a36Sopenharmony_ci	.lookup         = simple_lookup,
15262306a36Sopenharmony_ci	.unlink         = efi_secret_unlink,
15362306a36Sopenharmony_ci};
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic int efi_secret_map_area(struct platform_device *dev)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	int ret;
15862306a36Sopenharmony_ci	struct efi_secret *s = efi_secret_get();
15962306a36Sopenharmony_ci	struct linux_efi_coco_secret_area *secret_area;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	if (efi.coco_secret == EFI_INVALID_TABLE_ADDR) {
16262306a36Sopenharmony_ci		dev_err(&dev->dev, "Secret area address is not available\n");
16362306a36Sopenharmony_ci		return -EINVAL;
16462306a36Sopenharmony_ci	}
16562306a36Sopenharmony_ci
16662306a36Sopenharmony_ci	secret_area = memremap(efi.coco_secret, sizeof(*secret_area), MEMREMAP_WB);
16762306a36Sopenharmony_ci	if (secret_area == NULL) {
16862306a36Sopenharmony_ci		dev_err(&dev->dev, "Could not map secret area EFI config entry\n");
16962306a36Sopenharmony_ci		return -ENOMEM;
17062306a36Sopenharmony_ci	}
17162306a36Sopenharmony_ci	if (!secret_area->base_pa || secret_area->size < sizeof(struct secret_header)) {
17262306a36Sopenharmony_ci		dev_err(&dev->dev,
17362306a36Sopenharmony_ci			"Invalid secret area memory location (base_pa=0x%llx size=0x%llx)\n",
17462306a36Sopenharmony_ci			secret_area->base_pa, secret_area->size);
17562306a36Sopenharmony_ci		ret = -EINVAL;
17662306a36Sopenharmony_ci		goto unmap;
17762306a36Sopenharmony_ci	}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	s->secret_data = ioremap_encrypted(secret_area->base_pa, secret_area->size);
18062306a36Sopenharmony_ci	if (s->secret_data == NULL) {
18162306a36Sopenharmony_ci		dev_err(&dev->dev, "Could not map secret area\n");
18262306a36Sopenharmony_ci		ret = -ENOMEM;
18362306a36Sopenharmony_ci		goto unmap;
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	s->secret_data_len = secret_area->size;
18762306a36Sopenharmony_ci	ret = 0;
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ciunmap:
19062306a36Sopenharmony_ci	memunmap(secret_area);
19162306a36Sopenharmony_ci	return ret;
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic void efi_secret_securityfs_teardown(struct platform_device *dev)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	struct efi_secret *s = efi_secret_get();
19762306a36Sopenharmony_ci	int i;
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	for (i = (EFI_SECRET_NUM_FILES - 1); i >= 0; i--) {
20062306a36Sopenharmony_ci		securityfs_remove(s->fs_files[i]);
20162306a36Sopenharmony_ci		s->fs_files[i] = NULL;
20262306a36Sopenharmony_ci	}
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	securityfs_remove(s->fs_dir);
20562306a36Sopenharmony_ci	s->fs_dir = NULL;
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ci	securityfs_remove(s->secrets_dir);
20862306a36Sopenharmony_ci	s->secrets_dir = NULL;
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	dev_dbg(&dev->dev, "Removed securityfs entries\n");
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic int efi_secret_securityfs_setup(struct platform_device *dev)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	struct efi_secret *s = efi_secret_get();
21662306a36Sopenharmony_ci	int ret = 0, i = 0, bytes_left;
21762306a36Sopenharmony_ci	unsigned char *ptr;
21862306a36Sopenharmony_ci	struct secret_header *h;
21962306a36Sopenharmony_ci	struct secret_entry *e;
22062306a36Sopenharmony_ci	struct dentry *dent;
22162306a36Sopenharmony_ci	char guid_str[EFI_VARIABLE_GUID_LEN + 1];
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	ptr = (void __force *)s->secret_data;
22462306a36Sopenharmony_ci	h = (struct secret_header *)ptr;
22562306a36Sopenharmony_ci	if (efi_guidcmp(h->guid, EFI_SECRET_TABLE_HEADER_GUID)) {
22662306a36Sopenharmony_ci		/*
22762306a36Sopenharmony_ci		 * This is not an error: it just means that EFI defines secret
22862306a36Sopenharmony_ci		 * area but it was not populated by the Guest Owner.
22962306a36Sopenharmony_ci		 */
23062306a36Sopenharmony_ci		dev_dbg(&dev->dev, "EFI secret area does not start with correct GUID\n");
23162306a36Sopenharmony_ci		return -ENODEV;
23262306a36Sopenharmony_ci	}
23362306a36Sopenharmony_ci	if (h->len < sizeof(*h)) {
23462306a36Sopenharmony_ci		dev_err(&dev->dev, "EFI secret area reported length is too small\n");
23562306a36Sopenharmony_ci		return -EINVAL;
23662306a36Sopenharmony_ci	}
23762306a36Sopenharmony_ci	if (h->len > s->secret_data_len) {
23862306a36Sopenharmony_ci		dev_err(&dev->dev, "EFI secret area reported length is too big\n");
23962306a36Sopenharmony_ci		return -EINVAL;
24062306a36Sopenharmony_ci	}
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	s->secrets_dir = NULL;
24362306a36Sopenharmony_ci	s->fs_dir = NULL;
24462306a36Sopenharmony_ci	memset(s->fs_files, 0, sizeof(s->fs_files));
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_ci	dent = securityfs_create_dir("secrets", NULL);
24762306a36Sopenharmony_ci	if (IS_ERR(dent)) {
24862306a36Sopenharmony_ci		dev_err(&dev->dev, "Error creating secrets securityfs directory entry err=%ld\n",
24962306a36Sopenharmony_ci			PTR_ERR(dent));
25062306a36Sopenharmony_ci		return PTR_ERR(dent);
25162306a36Sopenharmony_ci	}
25262306a36Sopenharmony_ci	s->secrets_dir = dent;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	dent = securityfs_create_dir("coco", s->secrets_dir);
25562306a36Sopenharmony_ci	if (IS_ERR(dent)) {
25662306a36Sopenharmony_ci		dev_err(&dev->dev, "Error creating coco securityfs directory entry err=%ld\n",
25762306a36Sopenharmony_ci			PTR_ERR(dent));
25862306a36Sopenharmony_ci		return PTR_ERR(dent);
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci	d_inode(dent)->i_op = &efi_secret_dir_inode_operations;
26162306a36Sopenharmony_ci	s->fs_dir = dent;
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	bytes_left = h->len - sizeof(*h);
26462306a36Sopenharmony_ci	ptr += sizeof(*h);
26562306a36Sopenharmony_ci	while (bytes_left >= (int)sizeof(*e) && i < EFI_SECRET_NUM_FILES) {
26662306a36Sopenharmony_ci		e = (struct secret_entry *)ptr;
26762306a36Sopenharmony_ci		if (e->len < sizeof(*e) || e->len > (unsigned int)bytes_left) {
26862306a36Sopenharmony_ci			dev_err(&dev->dev, "EFI secret area is corrupted\n");
26962306a36Sopenharmony_ci			ret = -EINVAL;
27062306a36Sopenharmony_ci			goto err_cleanup;
27162306a36Sopenharmony_ci		}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci		/* Skip deleted entries (which will have NULL_GUID) */
27462306a36Sopenharmony_ci		if (efi_guidcmp(e->guid, NULL_GUID)) {
27562306a36Sopenharmony_ci			efi_guid_to_str(&e->guid, guid_str);
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci			dent = securityfs_create_file(guid_str, 0440, s->fs_dir, (void *)e,
27862306a36Sopenharmony_ci						      &efi_secret_bin_file_fops);
27962306a36Sopenharmony_ci			if (IS_ERR(dent)) {
28062306a36Sopenharmony_ci				dev_err(&dev->dev, "Error creating efi_secret securityfs entry\n");
28162306a36Sopenharmony_ci				ret = PTR_ERR(dent);
28262306a36Sopenharmony_ci				goto err_cleanup;
28362306a36Sopenharmony_ci			}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci			s->fs_files[i++] = dent;
28662306a36Sopenharmony_ci		}
28762306a36Sopenharmony_ci		ptr += e->len;
28862306a36Sopenharmony_ci		bytes_left -= e->len;
28962306a36Sopenharmony_ci	}
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	dev_info(&dev->dev, "Created %d entries in securityfs secrets/coco\n", i);
29262306a36Sopenharmony_ci	return 0;
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_cierr_cleanup:
29562306a36Sopenharmony_ci	efi_secret_securityfs_teardown(dev);
29662306a36Sopenharmony_ci	return ret;
29762306a36Sopenharmony_ci}
29862306a36Sopenharmony_ci
29962306a36Sopenharmony_cistatic void efi_secret_unmap_area(void)
30062306a36Sopenharmony_ci{
30162306a36Sopenharmony_ci	struct efi_secret *s = efi_secret_get();
30262306a36Sopenharmony_ci
30362306a36Sopenharmony_ci	if (s->secret_data) {
30462306a36Sopenharmony_ci		iounmap(s->secret_data);
30562306a36Sopenharmony_ci		s->secret_data = NULL;
30662306a36Sopenharmony_ci		s->secret_data_len = 0;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci}
30962306a36Sopenharmony_ci
31062306a36Sopenharmony_cistatic int efi_secret_probe(struct platform_device *dev)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	int ret;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	ret = efi_secret_map_area(dev);
31562306a36Sopenharmony_ci	if (ret)
31662306a36Sopenharmony_ci		return ret;
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	ret = efi_secret_securityfs_setup(dev);
31962306a36Sopenharmony_ci	if (ret)
32062306a36Sopenharmony_ci		goto err_unmap;
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_ci	return ret;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_cierr_unmap:
32562306a36Sopenharmony_ci	efi_secret_unmap_area();
32662306a36Sopenharmony_ci	return ret;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic int efi_secret_remove(struct platform_device *dev)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	efi_secret_securityfs_teardown(dev);
33262306a36Sopenharmony_ci	efi_secret_unmap_area();
33362306a36Sopenharmony_ci	return 0;
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic struct platform_driver efi_secret_driver = {
33762306a36Sopenharmony_ci	.probe = efi_secret_probe,
33862306a36Sopenharmony_ci	.remove = efi_secret_remove,
33962306a36Sopenharmony_ci	.driver = {
34062306a36Sopenharmony_ci		.name = "efi_secret",
34162306a36Sopenharmony_ci	},
34262306a36Sopenharmony_ci};
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_cimodule_platform_driver(efi_secret_driver);
34562306a36Sopenharmony_ci
34662306a36Sopenharmony_ciMODULE_DESCRIPTION("Confidential computing EFI secret area access");
34762306a36Sopenharmony_ciMODULE_AUTHOR("IBM");
34862306a36Sopenharmony_ciMODULE_LICENSE("GPL");
34962306a36Sopenharmony_ciMODULE_ALIAS("platform:efi_secret");
350