xref: /linux/drivers/virt/coco/tdx-host/tdx-host.c (revision d2c9a99135da931377240942d44f3dea104cedb8)
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