xref: /linux/drivers/platform/x86/amd/hsmp/acpi.c (revision cdd30ebb1b9f36159d66f088b61aee264e649d7a)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * AMD HSMP Platform Driver
4  * Copyright (c) 2024, AMD.
5  * All Rights Reserved.
6  *
7  * This file provides an ACPI based driver implementation for HSMP interface.
8  */
9 
10 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11 
12 #include <asm/amd_hsmp.h>
13 #include <asm/amd_nb.h>
14 
15 #include <linux/acpi.h>
16 #include <linux/device.h>
17 #include <linux/dev_printk.h>
18 #include <linux/ioport.h>
19 #include <linux/kstrtox.h>
20 #include <linux/module.h>
21 #include <linux/platform_device.h>
22 #include <linux/sysfs.h>
23 #include <linux/uuid.h>
24 
25 #include <uapi/asm-generic/errno-base.h>
26 
27 #include "hsmp.h"
28 
29 #define DRIVER_NAME		"amd_hsmp"
30 #define DRIVER_VERSION		"2.3"
31 #define ACPI_HSMP_DEVICE_HID	"AMDI0097"
32 
33 /* These are the strings specified in ACPI table */
34 #define MSG_IDOFF_STR		"MsgIdOffset"
35 #define MSG_ARGOFF_STR		"MsgArgOffset"
36 #define MSG_RESPOFF_STR		"MsgRspOffset"
37 
38 static struct hsmp_plat_device *hsmp_pdev;
39 
amd_hsmp_acpi_rdwr(struct hsmp_socket * sock,u32 offset,u32 * value,bool write)40 static int amd_hsmp_acpi_rdwr(struct hsmp_socket *sock, u32 offset,
41 			      u32 *value, bool write)
42 {
43 	if (write)
44 		iowrite32(*value, sock->virt_base_addr + offset);
45 	else
46 		*value = ioread32(sock->virt_base_addr + offset);
47 
48 	return 0;
49 }
50 
51 /* This is the UUID used for HSMP */
52 static const guid_t acpi_hsmp_uuid = GUID_INIT(0xb74d619d, 0x5707, 0x48bd,
53 						0xa6, 0x9f, 0x4e, 0xa2,
54 						0x87, 0x1f, 0xc2, 0xf6);
55 
is_acpi_hsmp_uuid(union acpi_object * obj)56 static inline bool is_acpi_hsmp_uuid(union acpi_object *obj)
57 {
58 	if (obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == UUID_SIZE)
59 		return guid_equal((guid_t *)obj->buffer.pointer, &acpi_hsmp_uuid);
60 
61 	return false;
62 }
63 
hsmp_get_uid(struct device * dev,u16 * sock_ind)64 static inline int hsmp_get_uid(struct device *dev, u16 *sock_ind)
65 {
66 	char *uid;
67 
68 	/*
69 	 * UID (ID00, ID01..IDXX) is used for differentiating sockets,
70 	 * read it and strip the "ID" part of it and convert the remaining
71 	 * bytes to integer.
72 	 */
73 	uid = acpi_device_uid(ACPI_COMPANION(dev));
74 
75 	return kstrtou16(uid + 2, 10, sock_ind);
76 }
77 
hsmp_resource(struct acpi_resource * res,void * data)78 static acpi_status hsmp_resource(struct acpi_resource *res, void *data)
79 {
80 	struct hsmp_socket *sock = data;
81 	struct resource r;
82 
83 	switch (res->type) {
84 	case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
85 		if (!acpi_dev_resource_memory(res, &r))
86 			return AE_ERROR;
87 		if (!r.start || r.end < r.start || !(r.flags & IORESOURCE_MEM_WRITEABLE))
88 			return AE_ERROR;
89 		sock->mbinfo.base_addr = r.start;
90 		sock->mbinfo.size = resource_size(&r);
91 		break;
92 	case ACPI_RESOURCE_TYPE_END_TAG:
93 		break;
94 	default:
95 		return AE_ERROR;
96 	}
97 
98 	return AE_OK;
99 }
100 
hsmp_read_acpi_dsd(struct hsmp_socket * sock)101 static int hsmp_read_acpi_dsd(struct hsmp_socket *sock)
102 {
103 	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
104 	union acpi_object *guid, *mailbox_package;
105 	union acpi_object *dsd;
106 	acpi_status status;
107 	int ret = 0;
108 	int j;
109 
110 	status = acpi_evaluate_object_typed(ACPI_HANDLE(sock->dev), "_DSD", NULL,
111 					    &buf, ACPI_TYPE_PACKAGE);
112 	if (ACPI_FAILURE(status)) {
113 		dev_err(sock->dev, "Failed to read mailbox reg offsets from DSD table, err: %s\n",
114 			acpi_format_exception(status));
115 		return -ENODEV;
116 	}
117 
118 	dsd = buf.pointer;
119 
120 	/* HSMP _DSD property should contain 2 objects.
121 	 * 1. guid which is an acpi object of type ACPI_TYPE_BUFFER
122 	 * 2. mailbox which is an acpi object of type ACPI_TYPE_PACKAGE
123 	 *    This mailbox object contains 3 more acpi objects of type
124 	 *    ACPI_TYPE_PACKAGE for holding msgid, msgresp, msgarg offsets
125 	 *    these packages inturn contain 2 acpi objects of type
126 	 *    ACPI_TYPE_STRING and ACPI_TYPE_INTEGER
127 	 */
128 	if (!dsd || dsd->type != ACPI_TYPE_PACKAGE || dsd->package.count != 2) {
129 		ret = -EINVAL;
130 		goto free_buf;
131 	}
132 
133 	guid = &dsd->package.elements[0];
134 	mailbox_package = &dsd->package.elements[1];
135 	if (!is_acpi_hsmp_uuid(guid) || mailbox_package->type != ACPI_TYPE_PACKAGE) {
136 		dev_err(sock->dev, "Invalid hsmp _DSD table data\n");
137 		ret = -EINVAL;
138 		goto free_buf;
139 	}
140 
141 	for (j = 0; j < mailbox_package->package.count; j++) {
142 		union acpi_object *msgobj, *msgstr, *msgint;
143 
144 		msgobj	= &mailbox_package->package.elements[j];
145 		msgstr	= &msgobj->package.elements[0];
146 		msgint	= &msgobj->package.elements[1];
147 
148 		/* package should have 1 string and 1 integer object */
149 		if (msgobj->type != ACPI_TYPE_PACKAGE ||
150 		    msgstr->type != ACPI_TYPE_STRING ||
151 		    msgint->type != ACPI_TYPE_INTEGER) {
152 			ret = -EINVAL;
153 			goto free_buf;
154 		}
155 
156 		if (!strncmp(msgstr->string.pointer, MSG_IDOFF_STR,
157 			     msgstr->string.length)) {
158 			sock->mbinfo.msg_id_off = msgint->integer.value;
159 		} else if (!strncmp(msgstr->string.pointer, MSG_RESPOFF_STR,
160 				    msgstr->string.length)) {
161 			sock->mbinfo.msg_resp_off =  msgint->integer.value;
162 		} else if (!strncmp(msgstr->string.pointer, MSG_ARGOFF_STR,
163 				    msgstr->string.length)) {
164 			sock->mbinfo.msg_arg_off = msgint->integer.value;
165 		} else {
166 			ret = -ENOENT;
167 			goto free_buf;
168 		}
169 	}
170 
171 	if (!sock->mbinfo.msg_id_off || !sock->mbinfo.msg_resp_off ||
172 	    !sock->mbinfo.msg_arg_off)
173 		ret = -EINVAL;
174 
175 free_buf:
176 	ACPI_FREE(buf.pointer);
177 	return ret;
178 }
179 
hsmp_read_acpi_crs(struct hsmp_socket * sock)180 static int hsmp_read_acpi_crs(struct hsmp_socket *sock)
181 {
182 	acpi_status status;
183 
184 	status = acpi_walk_resources(ACPI_HANDLE(sock->dev), METHOD_NAME__CRS,
185 				     hsmp_resource, sock);
186 	if (ACPI_FAILURE(status)) {
187 		dev_err(sock->dev, "Failed to look up MP1 base address from CRS method, err: %s\n",
188 			acpi_format_exception(status));
189 		return -EINVAL;
190 	}
191 	if (!sock->mbinfo.base_addr || !sock->mbinfo.size)
192 		return -EINVAL;
193 
194 	/* The mapped region should be un-cached */
195 	sock->virt_base_addr = devm_ioremap_uc(sock->dev, sock->mbinfo.base_addr,
196 					       sock->mbinfo.size);
197 	if (!sock->virt_base_addr) {
198 		dev_err(sock->dev, "Failed to ioremap MP1 base address\n");
199 		return -ENOMEM;
200 	}
201 
202 	return 0;
203 }
204 
205 /* Parse the ACPI table to read the data */
hsmp_parse_acpi_table(struct device * dev,u16 sock_ind)206 static int hsmp_parse_acpi_table(struct device *dev, u16 sock_ind)
207 {
208 	struct hsmp_socket *sock = &hsmp_pdev->sock[sock_ind];
209 	int ret;
210 
211 	sock->sock_ind		= sock_ind;
212 	sock->dev		= dev;
213 	sock->amd_hsmp_rdwr	= amd_hsmp_acpi_rdwr;
214 
215 	sema_init(&sock->hsmp_sem, 1);
216 
217 	dev_set_drvdata(dev, sock);
218 
219 	/* Read MP1 base address from CRS method */
220 	ret = hsmp_read_acpi_crs(sock);
221 	if (ret)
222 		return ret;
223 
224 	/* Read mailbox offsets from DSD table */
225 	return hsmp_read_acpi_dsd(sock);
226 }
227 
hsmp_metric_tbl_acpi_read(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)228 static ssize_t hsmp_metric_tbl_acpi_read(struct file *filp, struct kobject *kobj,
229 					 struct bin_attribute *bin_attr, char *buf,
230 					 loff_t off, size_t count)
231 {
232 	struct device *dev = container_of(kobj, struct device, kobj);
233 	struct hsmp_socket *sock = dev_get_drvdata(dev);
234 
235 	return hsmp_metric_tbl_read(sock, buf, count);
236 }
237 
hsmp_is_sock_attr_visible(struct kobject * kobj,const struct bin_attribute * battr,int id)238 static umode_t hsmp_is_sock_attr_visible(struct kobject *kobj,
239 					 const struct bin_attribute *battr, int id)
240 {
241 	if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6)
242 		return battr->attr.mode;
243 
244 	return 0;
245 }
246 
init_acpi(struct device * dev)247 static int init_acpi(struct device *dev)
248 {
249 	u16 sock_ind;
250 	int ret;
251 
252 	ret = hsmp_get_uid(dev, &sock_ind);
253 	if (ret)
254 		return ret;
255 	if (sock_ind >= hsmp_pdev->num_sockets)
256 		return -EINVAL;
257 
258 	ret = hsmp_parse_acpi_table(dev, sock_ind);
259 	if (ret) {
260 		dev_err(dev, "Failed to parse ACPI table\n");
261 		return ret;
262 	}
263 
264 	/* Test the hsmp interface */
265 	ret = hsmp_test(sock_ind, 0xDEADBEEF);
266 	if (ret) {
267 		dev_err(dev, "HSMP test message failed on Fam:%x model:%x\n",
268 			boot_cpu_data.x86, boot_cpu_data.x86_model);
269 		dev_err(dev, "Is HSMP disabled in BIOS ?\n");
270 		return ret;
271 	}
272 
273 	ret = hsmp_cache_proto_ver(sock_ind);
274 	if (ret) {
275 		dev_err(dev, "Failed to read HSMP protocol version\n");
276 		return ret;
277 	}
278 
279 	if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6) {
280 		ret = hsmp_get_tbl_dram_base(sock_ind);
281 		if (ret)
282 			dev_err(dev, "Failed to init metric table\n");
283 	}
284 
285 	return ret;
286 }
287 
288 static struct bin_attribute  hsmp_metric_tbl_attr = {
289 	.attr = { .name = HSMP_METRICS_TABLE_NAME, .mode = 0444},
290 	.read = hsmp_metric_tbl_acpi_read,
291 	.size = sizeof(struct hsmp_metric_table),
292 };
293 
294 static struct bin_attribute *hsmp_attr_list[] = {
295 	&hsmp_metric_tbl_attr,
296 	NULL
297 };
298 
299 static struct attribute_group hsmp_attr_grp = {
300 	.bin_attrs = hsmp_attr_list,
301 	.is_bin_visible = hsmp_is_sock_attr_visible,
302 };
303 
304 static const struct attribute_group *hsmp_groups[] = {
305 	&hsmp_attr_grp,
306 	NULL
307 };
308 
309 static const struct acpi_device_id amd_hsmp_acpi_ids[] = {
310 	{ACPI_HSMP_DEVICE_HID, 0},
311 	{}
312 };
313 MODULE_DEVICE_TABLE(acpi, amd_hsmp_acpi_ids);
314 
hsmp_acpi_probe(struct platform_device * pdev)315 static int hsmp_acpi_probe(struct platform_device *pdev)
316 {
317 	int ret;
318 
319 	hsmp_pdev = get_hsmp_pdev();
320 	if (!hsmp_pdev)
321 		return -ENOMEM;
322 
323 	if (!hsmp_pdev->is_probed) {
324 		hsmp_pdev->num_sockets = amd_nb_num();
325 		if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_SOCKETS)
326 			return -ENODEV;
327 
328 		hsmp_pdev->sock = devm_kcalloc(&pdev->dev, hsmp_pdev->num_sockets,
329 					       sizeof(*hsmp_pdev->sock),
330 					       GFP_KERNEL);
331 		if (!hsmp_pdev->sock)
332 			return -ENOMEM;
333 	}
334 
335 	ret = init_acpi(&pdev->dev);
336 	if (ret) {
337 		dev_err(&pdev->dev, "Failed to initialize HSMP interface.\n");
338 		return ret;
339 	}
340 
341 	if (!hsmp_pdev->is_probed) {
342 		ret = hsmp_misc_register(&pdev->dev);
343 		if (ret)
344 			return ret;
345 		hsmp_pdev->is_probed = true;
346 	}
347 
348 	return 0;
349 }
350 
hsmp_acpi_remove(struct platform_device * pdev)351 static void hsmp_acpi_remove(struct platform_device *pdev)
352 {
353 	/*
354 	 * We register only one misc_device even on multi-socket system.
355 	 * So, deregister should happen only once.
356 	 */
357 	if (hsmp_pdev->is_probed) {
358 		hsmp_misc_deregister();
359 		hsmp_pdev->is_probed = false;
360 	}
361 }
362 
363 static struct platform_driver amd_hsmp_driver = {
364 	.probe		= hsmp_acpi_probe,
365 	.remove		= hsmp_acpi_remove,
366 	.driver		= {
367 		.name	= DRIVER_NAME,
368 		.acpi_match_table = amd_hsmp_acpi_ids,
369 		.dev_groups = hsmp_groups,
370 	},
371 };
372 
373 module_platform_driver(amd_hsmp_driver);
374 
375 MODULE_IMPORT_NS("AMD_HSMP");
376 MODULE_DESCRIPTION("AMD HSMP Platform Interface Driver");
377 MODULE_VERSION(DRIVER_VERSION);
378 MODULE_LICENSE("GPL");
379