linux/drivers/virt/coco/tdx-host/tdx-host.c
Uwe Kleine-König (The Capable Hub) 995832b2ce Replace <linux/mod_devicetable.h> by more specific <linux/device-id/*.h> (c files)
Replace the #include of <linux/mod_devicetable.h> by the more specific
<linux/device-id/*.h> where applicable. For most cases the include
can be dropped completely, only a few drivers need one or two headers
added.

Acked-by: Danilo Krummrich <dakr@kernel.org>
Acked-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Acked-by: Bjorn Helgaas <bhelgaas@google.com>
Link: https://patch.msgid.link/1a3f2007c5c5dcf555c09a4035ce3ae8ef1b6c49.1782808461.git.u.kleine-koenig@baylibre.com
Signed-off-by: Uwe Kleine-König (The Capable Hub) <u.kleine-koenig@baylibre.com>
2026-07-03 07:38:17 +02:00

239 lines
5.1 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* TDX host user interface driver
*
* Copyright (C) 2025 Intel Corporation
*/
#include <linux/device/faux.h>
#include <linux/firmware.h>
#include <linux/module.h>
#include <linux/sysfs.h>
#include <asm/cpu_device_id.h>
#include <asm/seamldr.h>
#include <asm/tdx.h>
static const struct x86_cpu_id tdx_host_ids[] = {
X86_MATCH_FEATURE(X86_FEATURE_TDX_HOST_PLATFORM, NULL),
{}
};
MODULE_DEVICE_TABLE(x86cpu, tdx_host_ids);
static ssize_t version_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
const struct tdx_sys_info *tdx_sysinfo = tdx_get_sysinfo();
const struct tdx_sys_info_version *ver;
int ret;
if (!tdx_sysinfo)
return -ENXIO;
/*
* The version number can change during an update.
* Lock out updates while printing the version.
*/
seamldr_lock_module_update();
ver = &tdx_sysinfo->version;
ret = sysfs_emit(buf, TDX_VERSION_FMT "\n", ver->major_version,
ver->minor_version,
ver->update_version);
seamldr_unlock_module_update();
return ret;
}
static DEVICE_ATTR_RO(version);
static struct attribute *tdx_host_attrs[] = {
&dev_attr_version.attr,
NULL,
};
static const struct attribute_group tdx_host_group = {
.attrs = tdx_host_attrs,
};
static ssize_t seamldr_version_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct seamldr_info info;
int ret;
ret = seamldr_get_info(&info);
if (ret)
return ret;
return sysfs_emit(buf, TDX_VERSION_FMT "\n", info.major_version,
info.minor_version,
info.update_version);
}
static ssize_t num_remaining_updates_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct seamldr_info info;
int ret;
ret = seamldr_get_info(&info);
if (ret)
return ret;
return sysfs_emit(buf, "%u\n", info.num_remaining_updates);
}
/*
* These attributes are intended for managing TDX module updates. Reading
* them issues a slow, serialized P-SEAMLDR query, so keep them admin-only.
*/
static DEVICE_ATTR_ADMIN_RO(seamldr_version);
static DEVICE_ATTR_ADMIN_RO(num_remaining_updates);
static struct attribute *seamldr_attrs[] = {
&dev_attr_seamldr_version.attr,
&dev_attr_num_remaining_updates.attr,
NULL,
};
static bool supports_runtime_update(void)
{
const struct tdx_sys_info *sysinfo = tdx_get_sysinfo();
if (!sysinfo)
return false;
if (!tdx_supports_runtime_update(sysinfo))
return false;
/*
* This bug makes P-SEAMLDR calls clobber the current VMCS
* which breaks KVM. Avoid P-SEAMLDR calls by hiding all
* attributes if the CPU has this bug.
*/
if (boot_cpu_has_bug(X86_BUG_SEAMRET_INVD_VMCS))
return false;
return true;
}
static umode_t seamldr_group_visible(struct kobject *kobj, struct attribute *attr, int idx)
{
if (!supports_runtime_update())
return 0;
return attr->mode;
}
static const struct attribute_group seamldr_group = {
.attrs = seamldr_attrs,
.is_visible = seamldr_group_visible,
};
static const struct attribute_group *tdx_host_groups[] = {
&tdx_host_group,
&seamldr_group,
NULL,
};
static enum fw_upload_err tdx_fw_prepare(struct fw_upload *fwl,
const u8 *data, u32 data_len)
{
return FW_UPLOAD_ERR_NONE;
}
static enum fw_upload_err tdx_fw_write(struct fw_upload *fwl, const u8 *data,
u32 offset, u32 data_len, u32 *written)
{
int ret;
ret = seamldr_install_module(data, data_len);
switch (ret) {
case 0:
*written = data_len;
return FW_UPLOAD_ERR_NONE;
default:
return FW_UPLOAD_ERR_FW_INVALID;
}
}
static enum fw_upload_err tdx_fw_poll_complete(struct fw_upload *fwl)
{
/*
* The upload completed during tdx_fw_write().
* Never poll for completion.
*/
return FW_UPLOAD_ERR_NONE;
}
static void tdx_fw_cancel(struct fw_upload *fwl)
{
/*
* TDX module updates are not cancellable.
* Provide a no-op callback to satisfy fw_upload_ops.
*/
}
static const struct fw_upload_ops tdx_fw_ops = {
.prepare = tdx_fw_prepare,
.write = tdx_fw_write,
.poll_complete = tdx_fw_poll_complete,
.cancel = tdx_fw_cancel,
};
static void seamldr_deinit(void *tdx_fwl)
{
firmware_upload_unregister(tdx_fwl);
}
static int seamldr_init(struct device *dev)
{
struct fw_upload *tdx_fwl;
if (!supports_runtime_update())
return 0;
tdx_fwl = firmware_upload_register(THIS_MODULE, dev, "tdx_module",
&tdx_fw_ops, NULL);
if (IS_ERR(tdx_fwl))
return PTR_ERR(tdx_fwl);
return devm_add_action_or_reset(dev, seamldr_deinit, tdx_fwl);
}
static int tdx_host_probe(struct faux_device *fdev)
{
return seamldr_init(&fdev->dev);
}
static const struct faux_device_ops tdx_host_ops = {
.probe = tdx_host_probe,
};
static struct faux_device *fdev;
static int __init tdx_host_init(void)
{
if (!x86_match_cpu(tdx_host_ids) || !tdx_get_sysinfo())
return -ENODEV;
fdev = faux_device_create_with_groups(KBUILD_MODNAME, NULL,
&tdx_host_ops,
tdx_host_groups);
if (!fdev)
return -ENODEV;
return 0;
}
module_init(tdx_host_init);
static void __exit tdx_host_exit(void)
{
faux_device_destroy(fdev);
}
module_exit(tdx_host_exit);
MODULE_DESCRIPTION("TDX Host Services");
MODULE_LICENSE("GPL");