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

scm.c (6766B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Recognize and maintain s390 storage class memory.
      4 *
      5 * Copyright IBM Corp. 2012
      6 * Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>
      7 */
      8
      9#include <linux/device.h>
     10#include <linux/module.h>
     11#include <linux/mutex.h>
     12#include <linux/slab.h>
     13#include <linux/init.h>
     14#include <linux/err.h>
     15#include <asm/eadm.h>
     16#include "chsc.h"
     17
     18static struct device *scm_root;
     19
     20#define to_scm_dev(n) container_of(n, struct scm_device, dev)
     21#define	to_scm_drv(d) container_of(d, struct scm_driver, drv)
     22
     23static int scmdev_probe(struct device *dev)
     24{
     25	struct scm_device *scmdev = to_scm_dev(dev);
     26	struct scm_driver *scmdrv = to_scm_drv(dev->driver);
     27
     28	return scmdrv->probe ? scmdrv->probe(scmdev) : -ENODEV;
     29}
     30
     31static void scmdev_remove(struct device *dev)
     32{
     33	struct scm_device *scmdev = to_scm_dev(dev);
     34	struct scm_driver *scmdrv = to_scm_drv(dev->driver);
     35
     36	if (scmdrv->remove)
     37		scmdrv->remove(scmdev);
     38}
     39
     40static int scmdev_uevent(struct device *dev, struct kobj_uevent_env *env)
     41{
     42	return add_uevent_var(env, "MODALIAS=scm:scmdev");
     43}
     44
     45static struct bus_type scm_bus_type = {
     46	.name  = "scm",
     47	.probe = scmdev_probe,
     48	.remove = scmdev_remove,
     49	.uevent = scmdev_uevent,
     50};
     51
     52/**
     53 * scm_driver_register() - register a scm driver
     54 * @scmdrv: driver to be registered
     55 */
     56int scm_driver_register(struct scm_driver *scmdrv)
     57{
     58	struct device_driver *drv = &scmdrv->drv;
     59
     60	drv->bus = &scm_bus_type;
     61
     62	return driver_register(drv);
     63}
     64EXPORT_SYMBOL_GPL(scm_driver_register);
     65
     66/**
     67 * scm_driver_unregister() - deregister a scm driver
     68 * @scmdrv: driver to be deregistered
     69 */
     70void scm_driver_unregister(struct scm_driver *scmdrv)
     71{
     72	driver_unregister(&scmdrv->drv);
     73}
     74EXPORT_SYMBOL_GPL(scm_driver_unregister);
     75
     76void scm_irq_handler(struct aob *aob, blk_status_t error)
     77{
     78	struct aob_rq_header *aobrq = (void *) aob->request.data;
     79	struct scm_device *scmdev = aobrq->scmdev;
     80	struct scm_driver *scmdrv = to_scm_drv(scmdev->dev.driver);
     81
     82	scmdrv->handler(scmdev, aobrq->data, error);
     83}
     84EXPORT_SYMBOL_GPL(scm_irq_handler);
     85
     86#define scm_attr(name)							\
     87static ssize_t show_##name(struct device *dev,				\
     88	       struct device_attribute *attr, char *buf)		\
     89{									\
     90	struct scm_device *scmdev = to_scm_dev(dev);			\
     91	int ret;							\
     92									\
     93	device_lock(dev);						\
     94	ret = sprintf(buf, "%u\n", scmdev->attrs.name);			\
     95	device_unlock(dev);						\
     96									\
     97	return ret;							\
     98}									\
     99static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);
    100
    101scm_attr(persistence);
    102scm_attr(oper_state);
    103scm_attr(data_state);
    104scm_attr(rank);
    105scm_attr(release);
    106scm_attr(res_id);
    107
    108static struct attribute *scmdev_attrs[] = {
    109	&dev_attr_persistence.attr,
    110	&dev_attr_oper_state.attr,
    111	&dev_attr_data_state.attr,
    112	&dev_attr_rank.attr,
    113	&dev_attr_release.attr,
    114	&dev_attr_res_id.attr,
    115	NULL,
    116};
    117
    118static struct attribute_group scmdev_attr_group = {
    119	.attrs = scmdev_attrs,
    120};
    121
    122static const struct attribute_group *scmdev_attr_groups[] = {
    123	&scmdev_attr_group,
    124	NULL,
    125};
    126
    127static void scmdev_release(struct device *dev)
    128{
    129	struct scm_device *scmdev = to_scm_dev(dev);
    130
    131	kfree(scmdev);
    132}
    133
    134static void scmdev_setup(struct scm_device *scmdev, struct sale *sale,
    135			 unsigned int size, unsigned int max_blk_count)
    136{
    137	dev_set_name(&scmdev->dev, "%016llx", (unsigned long long) sale->sa);
    138	scmdev->nr_max_block = max_blk_count;
    139	scmdev->address = sale->sa;
    140	scmdev->size = 1UL << size;
    141	scmdev->attrs.rank = sale->rank;
    142	scmdev->attrs.persistence = sale->p;
    143	scmdev->attrs.oper_state = sale->op_state;
    144	scmdev->attrs.data_state = sale->data_state;
    145	scmdev->attrs.rank = sale->rank;
    146	scmdev->attrs.release = sale->r;
    147	scmdev->attrs.res_id = sale->rid;
    148	scmdev->dev.parent = scm_root;
    149	scmdev->dev.bus = &scm_bus_type;
    150	scmdev->dev.release = scmdev_release;
    151	scmdev->dev.groups = scmdev_attr_groups;
    152}
    153
    154/*
    155 * Check for state-changes, notify the driver and userspace.
    156 */
    157static void scmdev_update(struct scm_device *scmdev, struct sale *sale)
    158{
    159	struct scm_driver *scmdrv;
    160	bool changed;
    161
    162	device_lock(&scmdev->dev);
    163	changed = scmdev->attrs.rank != sale->rank ||
    164		  scmdev->attrs.oper_state != sale->op_state;
    165	scmdev->attrs.rank = sale->rank;
    166	scmdev->attrs.oper_state = sale->op_state;
    167	if (!scmdev->dev.driver)
    168		goto out;
    169	scmdrv = to_scm_drv(scmdev->dev.driver);
    170	if (changed && scmdrv->notify)
    171		scmdrv->notify(scmdev, SCM_CHANGE);
    172out:
    173	device_unlock(&scmdev->dev);
    174	if (changed)
    175		kobject_uevent(&scmdev->dev.kobj, KOBJ_CHANGE);
    176}
    177
    178static int check_address(struct device *dev, const void *data)
    179{
    180	struct scm_device *scmdev = to_scm_dev(dev);
    181	const struct sale *sale = data;
    182
    183	return scmdev->address == sale->sa;
    184}
    185
    186static struct scm_device *scmdev_find(struct sale *sale)
    187{
    188	struct device *dev;
    189
    190	dev = bus_find_device(&scm_bus_type, NULL, sale, check_address);
    191
    192	return dev ? to_scm_dev(dev) : NULL;
    193}
    194
    195static int scm_add(struct chsc_scm_info *scm_info, size_t num)
    196{
    197	struct sale *sale, *scmal = scm_info->scmal;
    198	struct scm_device *scmdev;
    199	int ret;
    200
    201	for (sale = scmal; sale < scmal + num; sale++) {
    202		scmdev = scmdev_find(sale);
    203		if (scmdev) {
    204			scmdev_update(scmdev, sale);
    205			/* Release reference from scm_find(). */
    206			put_device(&scmdev->dev);
    207			continue;
    208		}
    209		scmdev = kzalloc(sizeof(*scmdev), GFP_KERNEL);
    210		if (!scmdev)
    211			return -ENODEV;
    212		scmdev_setup(scmdev, sale, scm_info->is, scm_info->mbc);
    213		ret = device_register(&scmdev->dev);
    214		if (ret) {
    215			/* Release reference from device_initialize(). */
    216			put_device(&scmdev->dev);
    217			return ret;
    218		}
    219	}
    220
    221	return 0;
    222}
    223
    224int scm_update_information(void)
    225{
    226	struct chsc_scm_info *scm_info;
    227	u64 token = 0;
    228	size_t num;
    229	int ret;
    230
    231	scm_info = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
    232	if (!scm_info)
    233		return -ENOMEM;
    234
    235	do {
    236		ret = chsc_scm_info(scm_info, token);
    237		if (ret)
    238			break;
    239
    240		num = (scm_info->response.length -
    241		       (offsetof(struct chsc_scm_info, scmal) -
    242			offsetof(struct chsc_scm_info, response))
    243		      ) / sizeof(struct sale);
    244
    245		ret = scm_add(scm_info, num);
    246		if (ret)
    247			break;
    248
    249		token = scm_info->restok;
    250	} while (token);
    251
    252	free_page((unsigned long)scm_info);
    253
    254	return ret;
    255}
    256
    257static int scm_dev_avail(struct device *dev, void *unused)
    258{
    259	struct scm_driver *scmdrv = to_scm_drv(dev->driver);
    260	struct scm_device *scmdev = to_scm_dev(dev);
    261
    262	if (dev->driver && scmdrv->notify)
    263		scmdrv->notify(scmdev, SCM_AVAIL);
    264
    265	return 0;
    266}
    267
    268int scm_process_availability_information(void)
    269{
    270	return bus_for_each_dev(&scm_bus_type, NULL, NULL, scm_dev_avail);
    271}
    272
    273static int __init scm_init(void)
    274{
    275	int ret;
    276
    277	ret = bus_register(&scm_bus_type);
    278	if (ret)
    279		return ret;
    280
    281	scm_root = root_device_register("scm");
    282	if (IS_ERR(scm_root)) {
    283		bus_unregister(&scm_bus_type);
    284		return PTR_ERR(scm_root);
    285	}
    286
    287	scm_update_information();
    288	return 0;
    289}
    290subsys_initcall_sync(scm_init);