cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

s2idle.c (15271B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Architecture-specific ACPI-based support for suspend-to-idle.
      4 *
      5 * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
      6 * Author: Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>
      7 * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
      8 *
      9 * On platforms supporting the Low Power S0 Idle interface there is an ACPI
     10 * device object with the PNP0D80 compatible device ID (System Power Management
     11 * Controller) and a specific _DSM method under it.  That method, if present,
     12 * can be used to indicate to the platform that the OS is transitioning into a
     13 * low-power state in which certain types of activity are not desirable or that
     14 * it is leaving such a state, which allows the platform to adjust its operation
     15 * mode accordingly.
     16 */
     17
     18#include <linux/acpi.h>
     19#include <linux/device.h>
     20#include <linux/suspend.h>
     21
     22#include "../sleep.h"
     23
     24#ifdef CONFIG_SUSPEND
     25
     26static bool sleep_no_lps0 __read_mostly;
     27module_param(sleep_no_lps0, bool, 0644);
     28MODULE_PARM_DESC(sleep_no_lps0, "Do not use the special LPS0 device interface");
     29
     30static const struct acpi_device_id lps0_device_ids[] = {
     31	{"PNP0D80", },
     32	{"", },
     33};
     34
     35/* Microsoft platform agnostic UUID */
     36#define ACPI_LPS0_DSM_UUID_MICROSOFT      "11e00d56-ce64-47ce-837b-1f898f9aa461"
     37
     38#define ACPI_LPS0_DSM_UUID	"c4eb40a0-6cd2-11e2-bcfd-0800200c9a66"
     39
     40#define ACPI_LPS0_GET_DEVICE_CONSTRAINTS	1
     41#define ACPI_LPS0_SCREEN_OFF	3
     42#define ACPI_LPS0_SCREEN_ON	4
     43#define ACPI_LPS0_ENTRY		5
     44#define ACPI_LPS0_EXIT		6
     45#define ACPI_LPS0_MS_ENTRY      7
     46#define ACPI_LPS0_MS_EXIT       8
     47
     48/* AMD */
     49#define ACPI_LPS0_DSM_UUID_AMD      "e3f32452-febc-43ce-9039-932122d37721"
     50#define ACPI_LPS0_ENTRY_AMD         2
     51#define ACPI_LPS0_EXIT_AMD          3
     52#define ACPI_LPS0_SCREEN_OFF_AMD    4
     53#define ACPI_LPS0_SCREEN_ON_AMD     5
     54
     55static acpi_handle lps0_device_handle;
     56static guid_t lps0_dsm_guid;
     57static int lps0_dsm_func_mask;
     58
     59static guid_t lps0_dsm_guid_microsoft;
     60static int lps0_dsm_func_mask_microsoft;
     61
     62/* Device constraint entry structure */
     63struct lpi_device_info {
     64	char *name;
     65	int enabled;
     66	union acpi_object *package;
     67};
     68
     69/* Constraint package structure */
     70struct lpi_device_constraint {
     71	int uid;
     72	int min_dstate;
     73	int function_states;
     74};
     75
     76struct lpi_constraints {
     77	acpi_handle handle;
     78	int min_dstate;
     79};
     80
     81/* AMD Constraint package structure */
     82struct lpi_device_constraint_amd {
     83	char *name;
     84	int enabled;
     85	int function_states;
     86	int min_dstate;
     87};
     88
     89static LIST_HEAD(lps0_s2idle_devops_head);
     90
     91static struct lpi_constraints *lpi_constraints_table;
     92static int lpi_constraints_table_size;
     93static int rev_id;
     94
     95static void lpi_device_get_constraints_amd(void)
     96{
     97	union acpi_object *out_obj;
     98	int i, j, k;
     99
    100	out_obj = acpi_evaluate_dsm_typed(lps0_device_handle, &lps0_dsm_guid,
    101					  rev_id, ACPI_LPS0_GET_DEVICE_CONSTRAINTS,
    102					  NULL, ACPI_TYPE_PACKAGE);
    103
    104	acpi_handle_debug(lps0_device_handle, "_DSM function 1 eval %s\n",
    105			  out_obj ? "successful" : "failed");
    106
    107	if (!out_obj)
    108		return;
    109
    110	for (i = 0; i < out_obj->package.count; i++) {
    111		union acpi_object *package = &out_obj->package.elements[i];
    112
    113		if (package->type == ACPI_TYPE_PACKAGE) {
    114			lpi_constraints_table = kcalloc(package->package.count,
    115							sizeof(*lpi_constraints_table),
    116							GFP_KERNEL);
    117
    118			if (!lpi_constraints_table)
    119				goto free_acpi_buffer;
    120
    121			acpi_handle_debug(lps0_device_handle,
    122					  "LPI: constraints list begin:\n");
    123
    124			for (j = 0; j < package->package.count; ++j) {
    125				union acpi_object *info_obj = &package->package.elements[j];
    126				struct lpi_device_constraint_amd dev_info = {};
    127				struct lpi_constraints *list;
    128				acpi_status status;
    129
    130				for (k = 0; k < info_obj->package.count; ++k) {
    131					union acpi_object *obj = &info_obj->package.elements[k];
    132
    133					list = &lpi_constraints_table[lpi_constraints_table_size];
    134					list->min_dstate = -1;
    135
    136					switch (k) {
    137					case 0:
    138						dev_info.enabled = obj->integer.value;
    139						break;
    140					case 1:
    141						dev_info.name = obj->string.pointer;
    142						break;
    143					case 2:
    144						dev_info.function_states = obj->integer.value;
    145						break;
    146					case 3:
    147						dev_info.min_dstate = obj->integer.value;
    148						break;
    149					}
    150
    151					if (!dev_info.enabled || !dev_info.name ||
    152					    !dev_info.min_dstate)
    153						continue;
    154
    155					status = acpi_get_handle(NULL, dev_info.name,
    156								 &list->handle);
    157					if (ACPI_FAILURE(status))
    158						continue;
    159
    160					acpi_handle_debug(lps0_device_handle,
    161							  "Name:%s\n", dev_info.name);
    162
    163					list->min_dstate = dev_info.min_dstate;
    164
    165					if (list->min_dstate < 0) {
    166						acpi_handle_debug(lps0_device_handle,
    167								  "Incomplete constraint defined\n");
    168						continue;
    169					}
    170				}
    171				lpi_constraints_table_size++;
    172			}
    173		}
    174	}
    175
    176	acpi_handle_debug(lps0_device_handle, "LPI: constraints list end\n");
    177
    178free_acpi_buffer:
    179	ACPI_FREE(out_obj);
    180}
    181
    182static void lpi_device_get_constraints(void)
    183{
    184	union acpi_object *out_obj;
    185	int i;
    186
    187	out_obj = acpi_evaluate_dsm_typed(lps0_device_handle, &lps0_dsm_guid,
    188					  1, ACPI_LPS0_GET_DEVICE_CONSTRAINTS,
    189					  NULL, ACPI_TYPE_PACKAGE);
    190
    191	acpi_handle_debug(lps0_device_handle, "_DSM function 1 eval %s\n",
    192			  out_obj ? "successful" : "failed");
    193
    194	if (!out_obj)
    195		return;
    196
    197	lpi_constraints_table = kcalloc(out_obj->package.count,
    198					sizeof(*lpi_constraints_table),
    199					GFP_KERNEL);
    200	if (!lpi_constraints_table)
    201		goto free_acpi_buffer;
    202
    203	acpi_handle_debug(lps0_device_handle, "LPI: constraints list begin:\n");
    204
    205	for (i = 0; i < out_obj->package.count; i++) {
    206		struct lpi_constraints *constraint;
    207		acpi_status status;
    208		union acpi_object *package = &out_obj->package.elements[i];
    209		struct lpi_device_info info = { };
    210		int package_count = 0, j;
    211
    212		if (!package)
    213			continue;
    214
    215		for (j = 0; j < package->package.count; ++j) {
    216			union acpi_object *element =
    217					&(package->package.elements[j]);
    218
    219			switch (element->type) {
    220			case ACPI_TYPE_INTEGER:
    221				info.enabled = element->integer.value;
    222				break;
    223			case ACPI_TYPE_STRING:
    224				info.name = element->string.pointer;
    225				break;
    226			case ACPI_TYPE_PACKAGE:
    227				package_count = element->package.count;
    228				info.package = element->package.elements;
    229				break;
    230			}
    231		}
    232
    233		if (!info.enabled || !info.package || !info.name)
    234			continue;
    235
    236		constraint = &lpi_constraints_table[lpi_constraints_table_size];
    237
    238		status = acpi_get_handle(NULL, info.name, &constraint->handle);
    239		if (ACPI_FAILURE(status))
    240			continue;
    241
    242		acpi_handle_debug(lps0_device_handle,
    243				  "index:%d Name:%s\n", i, info.name);
    244
    245		constraint->min_dstate = -1;
    246
    247		for (j = 0; j < package_count; ++j) {
    248			union acpi_object *info_obj = &info.package[j];
    249			union acpi_object *cnstr_pkg;
    250			union acpi_object *obj;
    251			struct lpi_device_constraint dev_info;
    252
    253			switch (info_obj->type) {
    254			case ACPI_TYPE_INTEGER:
    255				/* version */
    256				break;
    257			case ACPI_TYPE_PACKAGE:
    258				if (info_obj->package.count < 2)
    259					break;
    260
    261				cnstr_pkg = info_obj->package.elements;
    262				obj = &cnstr_pkg[0];
    263				dev_info.uid = obj->integer.value;
    264				obj = &cnstr_pkg[1];
    265				dev_info.min_dstate = obj->integer.value;
    266
    267				acpi_handle_debug(lps0_device_handle,
    268					"uid:%d min_dstate:%s\n",
    269					dev_info.uid,
    270					acpi_power_state_string(dev_info.min_dstate));
    271
    272				constraint->min_dstate = dev_info.min_dstate;
    273				break;
    274			}
    275		}
    276
    277		if (constraint->min_dstate < 0) {
    278			acpi_handle_debug(lps0_device_handle,
    279					  "Incomplete constraint defined\n");
    280			continue;
    281		}
    282
    283		lpi_constraints_table_size++;
    284	}
    285
    286	acpi_handle_debug(lps0_device_handle, "LPI: constraints list end\n");
    287
    288free_acpi_buffer:
    289	ACPI_FREE(out_obj);
    290}
    291
    292static void lpi_check_constraints(void)
    293{
    294	int i;
    295
    296	for (i = 0; i < lpi_constraints_table_size; ++i) {
    297		acpi_handle handle = lpi_constraints_table[i].handle;
    298		struct acpi_device *adev = acpi_fetch_acpi_dev(handle);
    299
    300		if (!adev)
    301			continue;
    302
    303		acpi_handle_debug(handle,
    304			"LPI: required min power state:%s current power state:%s\n",
    305			acpi_power_state_string(lpi_constraints_table[i].min_dstate),
    306			acpi_power_state_string(adev->power.state));
    307
    308		if (!adev->flags.power_manageable) {
    309			acpi_handle_info(handle, "LPI: Device not power manageable\n");
    310			lpi_constraints_table[i].handle = NULL;
    311			continue;
    312		}
    313
    314		if (adev->power.state < lpi_constraints_table[i].min_dstate)
    315			acpi_handle_info(handle,
    316				"LPI: Constraint not met; min power state:%s current power state:%s\n",
    317				acpi_power_state_string(lpi_constraints_table[i].min_dstate),
    318				acpi_power_state_string(adev->power.state));
    319	}
    320}
    321
    322static void acpi_sleep_run_lps0_dsm(unsigned int func, unsigned int func_mask, guid_t dsm_guid)
    323{
    324	union acpi_object *out_obj;
    325
    326	if (!(func_mask & (1 << func)))
    327		return;
    328
    329	out_obj = acpi_evaluate_dsm(lps0_device_handle, &dsm_guid,
    330					rev_id, func, NULL);
    331	ACPI_FREE(out_obj);
    332
    333	acpi_handle_debug(lps0_device_handle, "_DSM function %u evaluation %s\n",
    334			  func, out_obj ? "successful" : "failed");
    335}
    336
    337static bool acpi_s2idle_vendor_amd(void)
    338{
    339	return boot_cpu_data.x86_vendor == X86_VENDOR_AMD;
    340}
    341
    342static int validate_dsm(acpi_handle handle, const char *uuid, int rev, guid_t *dsm_guid)
    343{
    344	union acpi_object *obj;
    345	int ret = -EINVAL;
    346
    347	guid_parse(uuid, dsm_guid);
    348	obj = acpi_evaluate_dsm(handle, dsm_guid, rev, 0, NULL);
    349
    350	/* Check if the _DSM is present and as expected. */
    351	if (!obj || obj->type != ACPI_TYPE_BUFFER || obj->buffer.length == 0 ||
    352	    obj->buffer.length > sizeof(u32)) {
    353		acpi_handle_debug(handle,
    354				"_DSM UUID %s rev %d function 0 evaluation failed\n", uuid, rev);
    355		goto out;
    356	}
    357
    358	ret = *(int *)obj->buffer.pointer;
    359	acpi_handle_debug(handle, "_DSM UUID %s rev %d function mask: 0x%x\n", uuid, rev, ret);
    360
    361out:
    362	ACPI_FREE(obj);
    363	return ret;
    364}
    365
    366static int lps0_device_attach(struct acpi_device *adev,
    367			      const struct acpi_device_id *not_used)
    368{
    369	if (lps0_device_handle)
    370		return 0;
    371
    372	if (!(acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0))
    373		return 0;
    374
    375	if (acpi_s2idle_vendor_amd()) {
    376		/* AMD0004, AMD0005, AMDI0005:
    377		 * - Should use rev_id 0x0
    378		 * - function mask > 0x3: Should use AMD method, but has off by one bug
    379		 * - function mask = 0x3: Should use Microsoft method
    380		 * AMDI0006:
    381		 * - should use rev_id 0x0
    382		 * - function mask = 0x3: Should use Microsoft method
    383		 * AMDI0007:
    384		 * - Should use rev_id 0x2
    385		 * - Should only use AMD method
    386		 */
    387		const char *hid = acpi_device_hid(adev);
    388		rev_id = strcmp(hid, "AMDI0007") ? 0 : 2;
    389		lps0_dsm_func_mask = validate_dsm(adev->handle,
    390					ACPI_LPS0_DSM_UUID_AMD, rev_id, &lps0_dsm_guid);
    391		lps0_dsm_func_mask_microsoft = validate_dsm(adev->handle,
    392					ACPI_LPS0_DSM_UUID_MICROSOFT, 0,
    393					&lps0_dsm_guid_microsoft);
    394		if (lps0_dsm_func_mask > 0x3 && (!strcmp(hid, "AMD0004") ||
    395						 !strcmp(hid, "AMD0005") ||
    396						 !strcmp(hid, "AMDI0005"))) {
    397			lps0_dsm_func_mask = (lps0_dsm_func_mask << 1) | 0x1;
    398			acpi_handle_debug(adev->handle, "_DSM UUID %s: Adjusted function mask: 0x%x\n",
    399					  ACPI_LPS0_DSM_UUID_AMD, lps0_dsm_func_mask);
    400		} else if (lps0_dsm_func_mask_microsoft > 0 && !strcmp(hid, "AMDI0007")) {
    401			lps0_dsm_func_mask_microsoft = -EINVAL;
    402			acpi_handle_debug(adev->handle, "_DSM Using AMD method\n");
    403		}
    404	} else {
    405		rev_id = 1;
    406		lps0_dsm_func_mask = validate_dsm(adev->handle,
    407					ACPI_LPS0_DSM_UUID, rev_id, &lps0_dsm_guid);
    408		lps0_dsm_func_mask_microsoft = -EINVAL;
    409	}
    410
    411	if (lps0_dsm_func_mask < 0 && lps0_dsm_func_mask_microsoft < 0)
    412		return 0; //function evaluation failed
    413
    414	lps0_device_handle = adev->handle;
    415
    416	if (acpi_s2idle_vendor_amd())
    417		lpi_device_get_constraints_amd();
    418	else
    419		lpi_device_get_constraints();
    420
    421	/*
    422	 * Use suspend-to-idle by default if the default suspend mode was not
    423	 * set from the command line.
    424	 */
    425	if (mem_sleep_default > PM_SUSPEND_MEM && !acpi_sleep_default_s3)
    426		mem_sleep_current = PM_SUSPEND_TO_IDLE;
    427
    428	/*
    429	 * Some LPS0 systems, like ASUS Zenbook UX430UNR/i7-8550U, require the
    430	 * EC GPE to be enabled while suspended for certain wakeup devices to
    431	 * work, so mark it as wakeup-capable.
    432	 */
    433	acpi_ec_mark_gpe_for_wake();
    434
    435	return 0;
    436}
    437
    438static struct acpi_scan_handler lps0_handler = {
    439	.ids = lps0_device_ids,
    440	.attach = lps0_device_attach,
    441};
    442
    443int acpi_s2idle_prepare_late(void)
    444{
    445	struct acpi_s2idle_dev_ops *handler;
    446
    447	if (!lps0_device_handle || sleep_no_lps0)
    448		return 0;
    449
    450	if (pm_debug_messages_on)
    451		lpi_check_constraints();
    452
    453	/* Screen off */
    454	if (lps0_dsm_func_mask > 0)
    455		acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ?
    456					ACPI_LPS0_SCREEN_OFF_AMD :
    457					ACPI_LPS0_SCREEN_OFF,
    458					lps0_dsm_func_mask, lps0_dsm_guid);
    459
    460	if (lps0_dsm_func_mask_microsoft > 0)
    461		acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_OFF,
    462				lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
    463
    464	/* LPS0 entry */
    465	if (lps0_dsm_func_mask > 0)
    466		acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ?
    467					ACPI_LPS0_ENTRY_AMD :
    468					ACPI_LPS0_ENTRY,
    469					lps0_dsm_func_mask, lps0_dsm_guid);
    470	if (lps0_dsm_func_mask_microsoft > 0) {
    471		acpi_sleep_run_lps0_dsm(ACPI_LPS0_ENTRY,
    472				lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
    473		/* modern standby entry */
    474		acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_ENTRY,
    475				lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
    476	}
    477
    478	list_for_each_entry(handler, &lps0_s2idle_devops_head, list_node) {
    479		if (handler->prepare)
    480			handler->prepare();
    481	}
    482
    483	return 0;
    484}
    485
    486void acpi_s2idle_restore_early(void)
    487{
    488	struct acpi_s2idle_dev_ops *handler;
    489
    490	if (!lps0_device_handle || sleep_no_lps0)
    491		return;
    492
    493	list_for_each_entry(handler, &lps0_s2idle_devops_head, list_node)
    494		if (handler->restore)
    495			handler->restore();
    496
    497	/* Modern standby exit */
    498	if (lps0_dsm_func_mask_microsoft > 0)
    499		acpi_sleep_run_lps0_dsm(ACPI_LPS0_MS_EXIT,
    500				lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
    501
    502	/* LPS0 exit */
    503	if (lps0_dsm_func_mask > 0)
    504		acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ?
    505					ACPI_LPS0_EXIT_AMD :
    506					ACPI_LPS0_EXIT,
    507					lps0_dsm_func_mask, lps0_dsm_guid);
    508	if (lps0_dsm_func_mask_microsoft > 0)
    509		acpi_sleep_run_lps0_dsm(ACPI_LPS0_EXIT,
    510				lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
    511
    512	/* Screen on */
    513	if (lps0_dsm_func_mask_microsoft > 0)
    514		acpi_sleep_run_lps0_dsm(ACPI_LPS0_SCREEN_ON,
    515				lps0_dsm_func_mask_microsoft, lps0_dsm_guid_microsoft);
    516	if (lps0_dsm_func_mask > 0)
    517		acpi_sleep_run_lps0_dsm(acpi_s2idle_vendor_amd() ?
    518					ACPI_LPS0_SCREEN_ON_AMD :
    519					ACPI_LPS0_SCREEN_ON,
    520					lps0_dsm_func_mask, lps0_dsm_guid);
    521}
    522
    523static const struct platform_s2idle_ops acpi_s2idle_ops_lps0 = {
    524	.begin = acpi_s2idle_begin,
    525	.prepare = acpi_s2idle_prepare,
    526	.prepare_late = acpi_s2idle_prepare_late,
    527	.wake = acpi_s2idle_wake,
    528	.restore_early = acpi_s2idle_restore_early,
    529	.restore = acpi_s2idle_restore,
    530	.end = acpi_s2idle_end,
    531};
    532
    533void acpi_s2idle_setup(void)
    534{
    535	acpi_scan_add_handler(&lps0_handler);
    536	s2idle_set_ops(&acpi_s2idle_ops_lps0);
    537}
    538
    539int acpi_register_lps0_dev(struct acpi_s2idle_dev_ops *arg)
    540{
    541	if (!lps0_device_handle || sleep_no_lps0)
    542		return -ENODEV;
    543
    544	lock_system_sleep();
    545	list_add(&arg->list_node, &lps0_s2idle_devops_head);
    546	unlock_system_sleep();
    547
    548	return 0;
    549}
    550EXPORT_SYMBOL_GPL(acpi_register_lps0_dev);
    551
    552void acpi_unregister_lps0_dev(struct acpi_s2idle_dev_ops *arg)
    553{
    554	if (!lps0_device_handle || sleep_no_lps0)
    555		return;
    556
    557	lock_system_sleep();
    558	list_del(&arg->list_node);
    559	unlock_system_sleep();
    560}
    561EXPORT_SYMBOL_GPL(acpi_unregister_lps0_dev);
    562
    563#endif /* CONFIG_SUSPEND */