• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012 Intel Corporation
3  *    Author: Liu Jinsong <jinsong.liu@intel.com>
4  *    Author: Jiang Yunhong <yunhong.jiang@intel.com>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or (at
9  * your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
14  * NON INFRINGEMENT.  See the GNU General Public License for more
15  * details.
16  */
17 
18 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
19 
20 #include <linux/kernel.h>
21 #include <linux/module.h>
22 #include <linux/init.h>
23 #include <linux/types.h>
24 #include <linux/acpi.h>
25 #include <xen/acpi.h>
26 #include <xen/interface/platform.h>
27 #include <asm/xen/hypercall.h>
28 
29 #define PREFIX "ACPI:xen_memory_hotplug:"
30 
31 struct acpi_memory_info {
32 	struct list_head list;
33 	u64 start_addr;		/* Memory Range start physical addr */
34 	u64 length;		/* Memory Range length */
35 	unsigned short caching;	/* memory cache attribute */
36 	unsigned short write_protect;	/* memory read/write attribute */
37 				/* copied from buffer getting from _CRS */
38 	unsigned int enabled:1;
39 };
40 
41 struct acpi_memory_device {
42 	struct acpi_device *device;
43 	struct list_head res_list;
44 };
45 
46 static bool acpi_hotmem_initialized __read_mostly;
47 
xen_hotadd_memory(int pxm,struct acpi_memory_info * info)48 static int xen_hotadd_memory(int pxm, struct acpi_memory_info *info)
49 {
50 	int rc;
51 	struct xen_platform_op op;
52 
53 	op.cmd = XENPF_mem_hotadd;
54 	op.u.mem_add.spfn = info->start_addr >> PAGE_SHIFT;
55 	op.u.mem_add.epfn = (info->start_addr + info->length) >> PAGE_SHIFT;
56 	op.u.mem_add.pxm = pxm;
57 
58 	rc = HYPERVISOR_dom0_op(&op);
59 	if (rc)
60 		pr_err(PREFIX "Xen Hotplug Memory Add failed on "
61 			"0x%lx -> 0x%lx, _PXM: %d, error: %d\n",
62 			(unsigned long)info->start_addr,
63 			(unsigned long)(info->start_addr + info->length),
64 			pxm, rc);
65 
66 	return rc;
67 }
68 
xen_acpi_memory_enable_device(struct acpi_memory_device * mem_device)69 static int xen_acpi_memory_enable_device(struct acpi_memory_device *mem_device)
70 {
71 	int pxm, result;
72 	int num_enabled = 0;
73 	struct acpi_memory_info *info;
74 
75 	if (!mem_device)
76 		return -EINVAL;
77 
78 	pxm = xen_acpi_get_pxm(mem_device->device->handle);
79 	if (pxm < 0)
80 		return pxm;
81 
82 	list_for_each_entry(info, &mem_device->res_list, list) {
83 		if (info->enabled) { /* just sanity check...*/
84 			num_enabled++;
85 			continue;
86 		}
87 
88 		if (!info->length)
89 			continue;
90 
91 		result = xen_hotadd_memory(pxm, info);
92 		if (result)
93 			continue;
94 		info->enabled = 1;
95 		num_enabled++;
96 	}
97 
98 	if (!num_enabled)
99 		return -ENODEV;
100 
101 	return 0;
102 }
103 
104 static acpi_status
acpi_memory_get_resource(struct acpi_resource * resource,void * context)105 acpi_memory_get_resource(struct acpi_resource *resource, void *context)
106 {
107 	struct acpi_memory_device *mem_device = context;
108 	struct acpi_resource_address64 address64;
109 	struct acpi_memory_info *info, *new;
110 	acpi_status status;
111 
112 	status = acpi_resource_to_address64(resource, &address64);
113 	if (ACPI_FAILURE(status) ||
114 	    (address64.resource_type != ACPI_MEMORY_RANGE))
115 		return AE_OK;
116 
117 	list_for_each_entry(info, &mem_device->res_list, list) {
118 		if ((info->caching == address64.info.mem.caching) &&
119 		    (info->write_protect == address64.info.mem.write_protect) &&
120 		    (info->start_addr + info->length == address64.address.minimum)) {
121 			info->length += address64.address.address_length;
122 			return AE_OK;
123 		}
124 	}
125 
126 	new = kzalloc(sizeof(struct acpi_memory_info), GFP_KERNEL);
127 	if (!new)
128 		return AE_ERROR;
129 
130 	INIT_LIST_HEAD(&new->list);
131 	new->caching = address64.info.mem.caching;
132 	new->write_protect = address64.info.mem.write_protect;
133 	new->start_addr = address64.address.minimum;
134 	new->length = address64.address.address_length;
135 	list_add_tail(&new->list, &mem_device->res_list);
136 
137 	return AE_OK;
138 }
139 
140 static int
acpi_memory_get_device_resources(struct acpi_memory_device * mem_device)141 acpi_memory_get_device_resources(struct acpi_memory_device *mem_device)
142 {
143 	acpi_status status;
144 	struct acpi_memory_info *info, *n;
145 
146 	if (!list_empty(&mem_device->res_list))
147 		return 0;
148 
149 	status = acpi_walk_resources(mem_device->device->handle,
150 		METHOD_NAME__CRS, acpi_memory_get_resource, mem_device);
151 
152 	if (ACPI_FAILURE(status)) {
153 		list_for_each_entry_safe(info, n, &mem_device->res_list, list)
154 			kfree(info);
155 		INIT_LIST_HEAD(&mem_device->res_list);
156 		return -EINVAL;
157 	}
158 
159 	return 0;
160 }
161 
acpi_memory_get_device(acpi_handle handle,struct acpi_memory_device ** mem_device)162 static int acpi_memory_get_device(acpi_handle handle,
163 				  struct acpi_memory_device **mem_device)
164 {
165 	struct acpi_device *device = NULL;
166 	int result = 0;
167 
168 	acpi_scan_lock_acquire();
169 
170 	acpi_bus_get_device(handle, &device);
171 	if (acpi_device_enumerated(device))
172 		goto end;
173 
174 	/*
175 	 * Now add the notified device.  This creates the acpi_device
176 	 * and invokes .add function
177 	 */
178 	result = acpi_bus_scan(handle);
179 	if (result) {
180 		pr_warn(PREFIX "ACPI namespace scan failed\n");
181 		result = -EINVAL;
182 		goto out;
183 	}
184 	device = NULL;
185 	acpi_bus_get_device(handle, &device);
186 	if (!acpi_device_enumerated(device)) {
187 		pr_warn(PREFIX "Missing device object\n");
188 		result = -EINVAL;
189 		goto out;
190 	}
191 
192 end:
193 	*mem_device = acpi_driver_data(device);
194 	if (!(*mem_device)) {
195 		pr_err(PREFIX "driver data not found\n");
196 		result = -ENODEV;
197 		goto out;
198 	}
199 
200 out:
201 	acpi_scan_lock_release();
202 	return result;
203 }
204 
acpi_memory_check_device(struct acpi_memory_device * mem_device)205 static int acpi_memory_check_device(struct acpi_memory_device *mem_device)
206 {
207 	unsigned long long current_status;
208 
209 	/* Get device present/absent information from the _STA */
210 	if (ACPI_FAILURE(acpi_evaluate_integer(mem_device->device->handle,
211 				"_STA", NULL, &current_status)))
212 		return -ENODEV;
213 	/*
214 	 * Check for device status. Device should be
215 	 * present/enabled/functioning.
216 	 */
217 	if (!((current_status & ACPI_STA_DEVICE_PRESENT)
218 	      && (current_status & ACPI_STA_DEVICE_ENABLED)
219 	      && (current_status & ACPI_STA_DEVICE_FUNCTIONING)))
220 		return -ENODEV;
221 
222 	return 0;
223 }
224 
acpi_memory_disable_device(struct acpi_memory_device * mem_device)225 static int acpi_memory_disable_device(struct acpi_memory_device *mem_device)
226 {
227 	pr_debug(PREFIX "Xen does not support memory hotremove\n");
228 
229 	return -ENOSYS;
230 }
231 
acpi_memory_device_notify(acpi_handle handle,u32 event,void * data)232 static void acpi_memory_device_notify(acpi_handle handle, u32 event, void *data)
233 {
234 	struct acpi_memory_device *mem_device;
235 	struct acpi_device *device;
236 	u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */
237 
238 	switch (event) {
239 	case ACPI_NOTIFY_BUS_CHECK:
240 		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
241 			"\nReceived BUS CHECK notification for device\n"));
242 		/* Fall Through */
243 	case ACPI_NOTIFY_DEVICE_CHECK:
244 		if (event == ACPI_NOTIFY_DEVICE_CHECK)
245 			ACPI_DEBUG_PRINT((ACPI_DB_INFO,
246 			"\nReceived DEVICE CHECK notification for device\n"));
247 
248 		if (acpi_memory_get_device(handle, &mem_device)) {
249 			pr_err(PREFIX "Cannot find driver data\n");
250 			break;
251 		}
252 
253 		ost_code = ACPI_OST_SC_SUCCESS;
254 		break;
255 
256 	case ACPI_NOTIFY_EJECT_REQUEST:
257 		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
258 			"\nReceived EJECT REQUEST notification for device\n"));
259 
260 		acpi_scan_lock_acquire();
261 		if (acpi_bus_get_device(handle, &device)) {
262 			acpi_scan_lock_release();
263 			pr_err(PREFIX "Device doesn't exist\n");
264 			break;
265 		}
266 		mem_device = acpi_driver_data(device);
267 		if (!mem_device) {
268 			acpi_scan_lock_release();
269 			pr_err(PREFIX "Driver Data is NULL\n");
270 			break;
271 		}
272 
273 		/*
274 		 * TBD: implement acpi_memory_disable_device and invoke
275 		 * acpi_bus_remove if Xen support hotremove in the future
276 		 */
277 		acpi_memory_disable_device(mem_device);
278 		acpi_scan_lock_release();
279 		break;
280 
281 	default:
282 		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
283 				  "Unsupported event [0x%x]\n", event));
284 		/* non-hotplug event; possibly handled by other handler */
285 		return;
286 	}
287 
288 	(void) acpi_evaluate_ost(handle, event, ost_code, NULL);
289 	return;
290 }
291 
xen_acpi_memory_device_add(struct acpi_device * device)292 static int xen_acpi_memory_device_add(struct acpi_device *device)
293 {
294 	int result;
295 	struct acpi_memory_device *mem_device = NULL;
296 
297 
298 	if (!device)
299 		return -EINVAL;
300 
301 	mem_device = kzalloc(sizeof(struct acpi_memory_device), GFP_KERNEL);
302 	if (!mem_device)
303 		return -ENOMEM;
304 
305 	INIT_LIST_HEAD(&mem_device->res_list);
306 	mem_device->device = device;
307 	sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME);
308 	sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS);
309 	device->driver_data = mem_device;
310 
311 	/* Get the range from the _CRS */
312 	result = acpi_memory_get_device_resources(mem_device);
313 	if (result) {
314 		kfree(mem_device);
315 		return result;
316 	}
317 
318 	/*
319 	 * For booting existed memory devices, early boot code has recognized
320 	 * memory area by EFI/E820. If DSDT shows these memory devices on boot,
321 	 * hotplug is not necessary for them.
322 	 * For hot-added memory devices during runtime, it need hypercall to
323 	 * Xen hypervisor to add memory.
324 	 */
325 	if (!acpi_hotmem_initialized)
326 		return 0;
327 
328 	if (!acpi_memory_check_device(mem_device))
329 		result = xen_acpi_memory_enable_device(mem_device);
330 
331 	return result;
332 }
333 
xen_acpi_memory_device_remove(struct acpi_device * device)334 static int xen_acpi_memory_device_remove(struct acpi_device *device)
335 {
336 	struct acpi_memory_device *mem_device = NULL;
337 
338 	if (!device || !acpi_driver_data(device))
339 		return -EINVAL;
340 
341 	mem_device = acpi_driver_data(device);
342 	kfree(mem_device);
343 
344 	return 0;
345 }
346 
347 /*
348  * Helper function to check for memory device
349  */
is_memory_device(acpi_handle handle)350 static acpi_status is_memory_device(acpi_handle handle)
351 {
352 	char *hardware_id;
353 	acpi_status status;
354 	struct acpi_device_info *info;
355 
356 	status = acpi_get_object_info(handle, &info);
357 	if (ACPI_FAILURE(status))
358 		return status;
359 
360 	if (!(info->valid & ACPI_VALID_HID)) {
361 		kfree(info);
362 		return AE_ERROR;
363 	}
364 
365 	hardware_id = info->hardware_id.string;
366 	if ((hardware_id == NULL) ||
367 	    (strcmp(hardware_id, ACPI_MEMORY_DEVICE_HID)))
368 		status = AE_ERROR;
369 
370 	kfree(info);
371 	return status;
372 }
373 
374 static acpi_status
acpi_memory_register_notify_handler(acpi_handle handle,u32 level,void * ctxt,void ** retv)375 acpi_memory_register_notify_handler(acpi_handle handle,
376 				    u32 level, void *ctxt, void **retv)
377 {
378 	acpi_status status;
379 
380 	status = is_memory_device(handle);
381 	if (ACPI_FAILURE(status))
382 		return AE_OK;	/* continue */
383 
384 	status = acpi_install_notify_handler(handle, ACPI_SYSTEM_NOTIFY,
385 					     acpi_memory_device_notify, NULL);
386 	/* continue */
387 	return AE_OK;
388 }
389 
390 static acpi_status
acpi_memory_deregister_notify_handler(acpi_handle handle,u32 level,void * ctxt,void ** retv)391 acpi_memory_deregister_notify_handler(acpi_handle handle,
392 				      u32 level, void *ctxt, void **retv)
393 {
394 	acpi_status status;
395 
396 	status = is_memory_device(handle);
397 	if (ACPI_FAILURE(status))
398 		return AE_OK;	/* continue */
399 
400 	status = acpi_remove_notify_handler(handle,
401 					    ACPI_SYSTEM_NOTIFY,
402 					    acpi_memory_device_notify);
403 
404 	return AE_OK;	/* continue */
405 }
406 
407 static const struct acpi_device_id memory_device_ids[] = {
408 	{ACPI_MEMORY_DEVICE_HID, 0},
409 	{"", 0},
410 };
411 MODULE_DEVICE_TABLE(acpi, memory_device_ids);
412 
413 static struct acpi_driver xen_acpi_memory_device_driver = {
414 	.name = "acpi_memhotplug",
415 	.class = ACPI_MEMORY_DEVICE_CLASS,
416 	.ids = memory_device_ids,
417 	.ops = {
418 		.add = xen_acpi_memory_device_add,
419 		.remove = xen_acpi_memory_device_remove,
420 		},
421 };
422 
xen_acpi_memory_device_init(void)423 static int __init xen_acpi_memory_device_init(void)
424 {
425 	int result;
426 	acpi_status status;
427 
428 	if (!xen_initial_domain())
429 		return -ENODEV;
430 
431 	/* unregister the stub which only used to reserve driver space */
432 	xen_stub_memory_device_exit();
433 
434 	result = acpi_bus_register_driver(&xen_acpi_memory_device_driver);
435 	if (result < 0) {
436 		xen_stub_memory_device_init();
437 		return -ENODEV;
438 	}
439 
440 	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
441 				     ACPI_UINT32_MAX,
442 				     acpi_memory_register_notify_handler,
443 				     NULL, NULL, NULL);
444 
445 	if (ACPI_FAILURE(status)) {
446 		pr_warn(PREFIX "walk_namespace failed\n");
447 		acpi_bus_unregister_driver(&xen_acpi_memory_device_driver);
448 		xen_stub_memory_device_init();
449 		return -ENODEV;
450 	}
451 
452 	acpi_hotmem_initialized = true;
453 	return 0;
454 }
455 
xen_acpi_memory_device_exit(void)456 static void __exit xen_acpi_memory_device_exit(void)
457 {
458 	acpi_status status;
459 
460 	if (!xen_initial_domain())
461 		return;
462 
463 	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
464 				     ACPI_UINT32_MAX,
465 				     acpi_memory_deregister_notify_handler,
466 				     NULL, NULL, NULL);
467 	if (ACPI_FAILURE(status))
468 		pr_warn(PREFIX "walk_namespace failed\n");
469 
470 	acpi_bus_unregister_driver(&xen_acpi_memory_device_driver);
471 
472 	/*
473 	 * stub reserve space again to prevent any chance of native
474 	 * driver loading.
475 	 */
476 	xen_stub_memory_device_init();
477 	return;
478 }
479 
480 module_init(xen_acpi_memory_device_init);
481 module_exit(xen_acpi_memory_device_exit);
482 ACPI_MODULE_NAME("xen-acpi-memhotplug");
483 MODULE_AUTHOR("Liu Jinsong <jinsong.liu@intel.com>");
484 MODULE_DESCRIPTION("Xen Hotplug Mem Driver");
485 MODULE_LICENSE("GPL");
486