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

suspend.c (3938B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3  * Copyright (C) 2010 Brian King IBM Corporation
      4  */
      5
      6#include <linux/cpu.h>
      7#include <linux/delay.h>
      8#include <linux/suspend.h>
      9#include <linux/stat.h>
     10#include <asm/firmware.h>
     11#include <asm/hvcall.h>
     12#include <asm/machdep.h>
     13#include <asm/mmu.h>
     14#include <asm/rtas.h>
     15#include <asm/topology.h>
     16
     17static struct device suspend_dev;
     18
     19/**
     20 * pseries_suspend_begin - First phase of hibernation
     21 *
     22 * Check to ensure we are in a valid state to hibernate
     23 *
     24 * Return value:
     25 * 	0 on success / other on failure
     26 **/
     27static int pseries_suspend_begin(u64 stream_id)
     28{
     29	long vasi_state, rc;
     30	unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
     31
     32	/* Make sure the state is valid */
     33	rc = plpar_hcall(H_VASI_STATE, retbuf, stream_id);
     34
     35	vasi_state = retbuf[0];
     36
     37	if (rc) {
     38		pr_err("pseries_suspend_begin: vasi_state returned %ld\n",rc);
     39		return rc;
     40	} else if (vasi_state == H_VASI_ENABLED) {
     41		return -EAGAIN;
     42	} else if (vasi_state != H_VASI_SUSPENDING) {
     43		pr_err("pseries_suspend_begin: vasi_state returned state %ld\n",
     44		       vasi_state);
     45		return -EIO;
     46	}
     47	return 0;
     48}
     49
     50/**
     51 * pseries_suspend_enter - Final phase of hibernation
     52 *
     53 * Return value:
     54 * 	0 on success / other on failure
     55 **/
     56static int pseries_suspend_enter(suspend_state_t state)
     57{
     58	return rtas_ibm_suspend_me(NULL);
     59}
     60
     61/**
     62 * store_hibernate - Initiate partition hibernation
     63 * @dev:		subsys root device
     64 * @attr:		device attribute struct
     65 * @buf:		buffer
     66 * @count:		buffer size
     67 *
     68 * Write the stream ID received from the HMC to this file
     69 * to trigger hibernating the partition
     70 *
     71 * Return value:
     72 * 	number of bytes printed to buffer / other on failure
     73 **/
     74static ssize_t store_hibernate(struct device *dev,
     75			       struct device_attribute *attr,
     76			       const char *buf, size_t count)
     77{
     78	u64 stream_id;
     79	int rc;
     80
     81	if (!capable(CAP_SYS_ADMIN))
     82		return -EPERM;
     83
     84	stream_id = simple_strtoul(buf, NULL, 16);
     85
     86	do {
     87		rc = pseries_suspend_begin(stream_id);
     88		if (rc == -EAGAIN)
     89			ssleep(1);
     90	} while (rc == -EAGAIN);
     91
     92	if (!rc)
     93		rc = pm_suspend(PM_SUSPEND_MEM);
     94
     95	if (!rc) {
     96		rc = count;
     97		post_mobility_fixup();
     98	}
     99
    100
    101	return rc;
    102}
    103
    104#define USER_DT_UPDATE	0
    105#define KERN_DT_UPDATE	1
    106
    107/**
    108 * show_hibernate - Report device tree update responsibilty
    109 * @dev:		subsys root device
    110 * @attr:		device attribute struct
    111 * @buf:		buffer
    112 *
    113 * Report whether a device tree update is performed by the kernel after a
    114 * resume, or if drmgr must coordinate the update from user space.
    115 *
    116 * Return value:
    117 *	0 if drmgr is to initiate update, and 1 otherwise
    118 **/
    119static ssize_t show_hibernate(struct device *dev,
    120			      struct device_attribute *attr,
    121			      char *buf)
    122{
    123	return sprintf(buf, "%d\n", KERN_DT_UPDATE);
    124}
    125
    126static DEVICE_ATTR(hibernate, 0644, show_hibernate, store_hibernate);
    127
    128static struct bus_type suspend_subsys = {
    129	.name = "power",
    130	.dev_name = "power",
    131};
    132
    133static const struct platform_suspend_ops pseries_suspend_ops = {
    134	.valid		= suspend_valid_only_mem,
    135	.enter		= pseries_suspend_enter,
    136};
    137
    138/**
    139 * pseries_suspend_sysfs_register - Register with sysfs
    140 *
    141 * Return value:
    142 * 	0 on success / other on failure
    143 **/
    144static int pseries_suspend_sysfs_register(struct device *dev)
    145{
    146	int rc;
    147
    148	if ((rc = subsys_system_register(&suspend_subsys, NULL)))
    149		return rc;
    150
    151	dev->id = 0;
    152	dev->bus = &suspend_subsys;
    153
    154	if ((rc = device_create_file(suspend_subsys.dev_root, &dev_attr_hibernate)))
    155		goto subsys_unregister;
    156
    157	return 0;
    158
    159subsys_unregister:
    160	bus_unregister(&suspend_subsys);
    161	return rc;
    162}
    163
    164/**
    165 * pseries_suspend_init - initcall for pSeries suspend
    166 *
    167 * Return value:
    168 * 	0 on success / other on failure
    169 **/
    170static int __init pseries_suspend_init(void)
    171{
    172	int rc;
    173
    174	if (!firmware_has_feature(FW_FEATURE_LPAR))
    175		return 0;
    176
    177	if ((rc = pseries_suspend_sysfs_register(&suspend_dev)))
    178		return rc;
    179
    180	suspend_set_ops(&pseries_suspend_ops);
    181	return 0;
    182}
    183machine_device_initcall(pseries, pseries_suspend_init);