mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 09:36:22 +02:00
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>
239 lines
5.1 KiB
C
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");
|