#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;
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);
}
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;
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)
{
return FW_UPLOAD_ERR_NONE;
}
static void tdx_fw_cancel(struct fw_upload *fwl)
{
}
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");