1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * TDX host user interface driver
4 *
5 * Copyright (C) 2025 Intel Corporation
6 */
7
8 #include <linux/device/faux.h>
9 #include <linux/firmware.h>
10 #include <linux/module.h>
11 #include <linux/sysfs.h>
12
13 #include <asm/cpu_device_id.h>
14 #include <asm/seamldr.h>
15 #include <asm/tdx.h>
16
17 static const struct x86_cpu_id tdx_host_ids[] = {
18 X86_MATCH_FEATURE(X86_FEATURE_TDX_HOST_PLATFORM, NULL),
19 {}
20 };
21 MODULE_DEVICE_TABLE(x86cpu, tdx_host_ids);
22
version_show(struct device * dev,struct device_attribute * attr,char * buf)23 static ssize_t version_show(struct device *dev, struct device_attribute *attr,
24 char *buf)
25 {
26 const struct tdx_sys_info *tdx_sysinfo = tdx_get_sysinfo();
27 const struct tdx_sys_info_version *ver;
28 int ret;
29
30 if (!tdx_sysinfo)
31 return -ENXIO;
32
33 /*
34 * The version number can change during an update.
35 * Lock out updates while printing the version.
36 */
37 seamldr_lock_module_update();
38
39 ver = &tdx_sysinfo->version;
40 ret = sysfs_emit(buf, TDX_VERSION_FMT "\n", ver->major_version,
41 ver->minor_version,
42 ver->update_version);
43 seamldr_unlock_module_update();
44
45 return ret;
46 }
47 static DEVICE_ATTR_RO(version);
48
49 static struct attribute *tdx_host_attrs[] = {
50 &dev_attr_version.attr,
51 NULL,
52 };
53
54 static const struct attribute_group tdx_host_group = {
55 .attrs = tdx_host_attrs,
56 };
57
seamldr_version_show(struct device * dev,struct device_attribute * attr,char * buf)58 static ssize_t seamldr_version_show(struct device *dev, struct device_attribute *attr,
59 char *buf)
60 {
61 struct seamldr_info info;
62 int ret;
63
64 ret = seamldr_get_info(&info);
65 if (ret)
66 return ret;
67
68 return sysfs_emit(buf, TDX_VERSION_FMT "\n", info.major_version,
69 info.minor_version,
70 info.update_version);
71 }
72
num_remaining_updates_show(struct device * dev,struct device_attribute * attr,char * buf)73 static ssize_t num_remaining_updates_show(struct device *dev,
74 struct device_attribute *attr,
75 char *buf)
76 {
77 struct seamldr_info info;
78 int ret;
79
80 ret = seamldr_get_info(&info);
81 if (ret)
82 return ret;
83
84 return sysfs_emit(buf, "%u\n", info.num_remaining_updates);
85 }
86
87 /*
88 * These attributes are intended for managing TDX module updates. Reading
89 * them issues a slow, serialized P-SEAMLDR query, so keep them admin-only.
90 */
91 static DEVICE_ATTR_ADMIN_RO(seamldr_version);
92 static DEVICE_ATTR_ADMIN_RO(num_remaining_updates);
93
94 static struct attribute *seamldr_attrs[] = {
95 &dev_attr_seamldr_version.attr,
96 &dev_attr_num_remaining_updates.attr,
97 NULL,
98 };
99
supports_runtime_update(void)100 static bool supports_runtime_update(void)
101 {
102 const struct tdx_sys_info *sysinfo = tdx_get_sysinfo();
103
104 if (!sysinfo)
105 return false;
106
107 if (!tdx_supports_runtime_update(sysinfo))
108 return false;
109
110 /*
111 * This bug makes P-SEAMLDR calls clobber the current VMCS
112 * which breaks KVM. Avoid P-SEAMLDR calls by hiding all
113 * attributes if the CPU has this bug.
114 */
115 if (boot_cpu_has_bug(X86_BUG_SEAMRET_INVD_VMCS))
116 return false;
117
118 return true;
119 }
120
seamldr_group_visible(struct kobject * kobj,struct attribute * attr,int idx)121 static umode_t seamldr_group_visible(struct kobject *kobj, struct attribute *attr, int idx)
122 {
123 if (!supports_runtime_update())
124 return 0;
125
126 return attr->mode;
127 }
128
129 static const struct attribute_group seamldr_group = {
130 .attrs = seamldr_attrs,
131 .is_visible = seamldr_group_visible,
132 };
133
134 static const struct attribute_group *tdx_host_groups[] = {
135 &tdx_host_group,
136 &seamldr_group,
137 NULL,
138 };
139
tdx_fw_prepare(struct fw_upload * fwl,const u8 * data,u32 data_len)140 static enum fw_upload_err tdx_fw_prepare(struct fw_upload *fwl,
141 const u8 *data, u32 data_len)
142 {
143 return FW_UPLOAD_ERR_NONE;
144 }
145
tdx_fw_write(struct fw_upload * fwl,const u8 * data,u32 offset,u32 data_len,u32 * written)146 static enum fw_upload_err tdx_fw_write(struct fw_upload *fwl, const u8 *data,
147 u32 offset, u32 data_len, u32 *written)
148 {
149 int ret;
150
151 ret = seamldr_install_module(data, data_len);
152 switch (ret) {
153 case 0:
154 *written = data_len;
155 return FW_UPLOAD_ERR_NONE;
156 default:
157 return FW_UPLOAD_ERR_FW_INVALID;
158 }
159 }
160
tdx_fw_poll_complete(struct fw_upload * fwl)161 static enum fw_upload_err tdx_fw_poll_complete(struct fw_upload *fwl)
162 {
163 /*
164 * The upload completed during tdx_fw_write().
165 * Never poll for completion.
166 */
167 return FW_UPLOAD_ERR_NONE;
168 }
169
tdx_fw_cancel(struct fw_upload * fwl)170 static void tdx_fw_cancel(struct fw_upload *fwl)
171 {
172 /*
173 * TDX module updates are not cancellable.
174 * Provide a no-op callback to satisfy fw_upload_ops.
175 */
176 }
177
178 static const struct fw_upload_ops tdx_fw_ops = {
179 .prepare = tdx_fw_prepare,
180 .write = tdx_fw_write,
181 .poll_complete = tdx_fw_poll_complete,
182 .cancel = tdx_fw_cancel,
183 };
184
seamldr_deinit(void * tdx_fwl)185 static void seamldr_deinit(void *tdx_fwl)
186 {
187 firmware_upload_unregister(tdx_fwl);
188 }
189
seamldr_init(struct device * dev)190 static int seamldr_init(struct device *dev)
191 {
192 struct fw_upload *tdx_fwl;
193
194 if (!supports_runtime_update())
195 return 0;
196
197 tdx_fwl = firmware_upload_register(THIS_MODULE, dev, "tdx_module",
198 &tdx_fw_ops, NULL);
199 if (IS_ERR(tdx_fwl))
200 return PTR_ERR(tdx_fwl);
201
202 return devm_add_action_or_reset(dev, seamldr_deinit, tdx_fwl);
203 }
204
tdx_host_probe(struct faux_device * fdev)205 static int tdx_host_probe(struct faux_device *fdev)
206 {
207 return seamldr_init(&fdev->dev);
208 }
209
210 static const struct faux_device_ops tdx_host_ops = {
211 .probe = tdx_host_probe,
212 };
213
214 static struct faux_device *fdev;
215
tdx_host_init(void)216 static int __init tdx_host_init(void)
217 {
218 if (!x86_match_cpu(tdx_host_ids) || !tdx_get_sysinfo())
219 return -ENODEV;
220
221 fdev = faux_device_create_with_groups(KBUILD_MODNAME, NULL,
222 &tdx_host_ops,
223 tdx_host_groups);
224 if (!fdev)
225 return -ENODEV;
226
227 return 0;
228 }
229 module_init(tdx_host_init);
230
tdx_host_exit(void)231 static void __exit tdx_host_exit(void)
232 {
233 faux_device_destroy(fdev);
234 }
235 module_exit(tdx_host_exit);
236
237 MODULE_DESCRIPTION("TDX Host Services");
238 MODULE_LICENSE("GPL");
239