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

gsc-me.c (5333B)


      1// SPDX-License-Identifier: GPL-2.0
      2/*
      3 * Copyright(c) 2019-2022, Intel Corporation. All rights reserved.
      4 *
      5 * Intel Management Engine Interface (Intel MEI) Linux driver
      6 */
      7
      8#include <linux/module.h>
      9#include <linux/mei_aux.h>
     10#include <linux/device.h>
     11#include <linux/irqreturn.h>
     12#include <linux/jiffies.h>
     13#include <linux/ktime.h>
     14#include <linux/delay.h>
     15#include <linux/pm_runtime.h>
     16
     17#include "mei_dev.h"
     18#include "hw-me.h"
     19#include "hw-me-regs.h"
     20
     21#include "mei-trace.h"
     22
     23#define MEI_GSC_RPM_TIMEOUT 500
     24
     25static int mei_gsc_read_hfs(const struct mei_device *dev, int where, u32 *val)
     26{
     27	struct mei_me_hw *hw = to_me_hw(dev);
     28
     29	*val = ioread32(hw->mem_addr + where + 0xC00);
     30
     31	return 0;
     32}
     33
     34static int mei_gsc_probe(struct auxiliary_device *aux_dev,
     35			 const struct auxiliary_device_id *aux_dev_id)
     36{
     37	struct mei_aux_device *adev = auxiliary_dev_to_mei_aux_dev(aux_dev);
     38	struct mei_device *dev;
     39	struct mei_me_hw *hw;
     40	struct device *device;
     41	const struct mei_cfg *cfg;
     42	int ret;
     43
     44	cfg = mei_me_get_cfg(aux_dev_id->driver_data);
     45	if (!cfg)
     46		return -ENODEV;
     47
     48	device = &aux_dev->dev;
     49
     50	dev = mei_me_dev_init(device, cfg);
     51	if (!dev) {
     52		ret = -ENOMEM;
     53		goto err;
     54	}
     55
     56	hw = to_me_hw(dev);
     57	hw->mem_addr = devm_ioremap_resource(device, &adev->bar);
     58	if (IS_ERR(hw->mem_addr)) {
     59		dev_err(device, "mmio not mapped\n");
     60		ret = PTR_ERR(hw->mem_addr);
     61		goto err;
     62	}
     63
     64	hw->irq = adev->irq;
     65	hw->read_fws = mei_gsc_read_hfs;
     66
     67	dev_set_drvdata(device, dev);
     68
     69	ret = devm_request_threaded_irq(device, hw->irq,
     70					mei_me_irq_quick_handler,
     71					mei_me_irq_thread_handler,
     72					IRQF_ONESHOT, KBUILD_MODNAME, dev);
     73	if (ret) {
     74		dev_err(device, "irq register failed %d\n", ret);
     75		goto err;
     76	}
     77
     78	pm_runtime_get_noresume(device);
     79	pm_runtime_set_active(device);
     80	pm_runtime_enable(device);
     81
     82	/* Continue to char device setup in spite of firmware handshake failure.
     83	 * In order to provide access to the firmware status registers to the user
     84	 * space via sysfs.
     85	 */
     86	if (mei_start(dev))
     87		dev_warn(device, "init hw failure.\n");
     88
     89	pm_runtime_set_autosuspend_delay(device, MEI_GSC_RPM_TIMEOUT);
     90	pm_runtime_use_autosuspend(device);
     91
     92	ret = mei_register(dev, device);
     93	if (ret)
     94		goto register_err;
     95
     96	pm_runtime_put_noidle(device);
     97	return 0;
     98
     99register_err:
    100	mei_stop(dev);
    101	devm_free_irq(device, hw->irq, dev);
    102
    103err:
    104	dev_err(device, "probe failed: %d\n", ret);
    105	dev_set_drvdata(device, NULL);
    106	return ret;
    107}
    108
    109static void mei_gsc_remove(struct auxiliary_device *aux_dev)
    110{
    111	struct mei_device *dev;
    112	struct mei_me_hw *hw;
    113
    114	dev = dev_get_drvdata(&aux_dev->dev);
    115	if (!dev)
    116		return;
    117
    118	hw = to_me_hw(dev);
    119
    120	mei_stop(dev);
    121
    122	mei_deregister(dev);
    123
    124	pm_runtime_disable(&aux_dev->dev);
    125
    126	mei_disable_interrupts(dev);
    127	devm_free_irq(&aux_dev->dev, hw->irq, dev);
    128}
    129
    130static int __maybe_unused mei_gsc_pm_suspend(struct device *device)
    131{
    132	struct mei_device *dev = dev_get_drvdata(device);
    133
    134	if (!dev)
    135		return -ENODEV;
    136
    137	mei_stop(dev);
    138
    139	mei_disable_interrupts(dev);
    140
    141	return 0;
    142}
    143
    144static int __maybe_unused mei_gsc_pm_resume(struct device *device)
    145{
    146	struct mei_device *dev = dev_get_drvdata(device);
    147	int err;
    148
    149	if (!dev)
    150		return -ENODEV;
    151
    152	err = mei_restart(dev);
    153	if (err)
    154		return err;
    155
    156	/* Start timer if stopped in suspend */
    157	schedule_delayed_work(&dev->timer_work, HZ);
    158
    159	return 0;
    160}
    161
    162static int __maybe_unused mei_gsc_pm_runtime_idle(struct device *device)
    163{
    164	struct mei_device *dev = dev_get_drvdata(device);
    165
    166	if (!dev)
    167		return -ENODEV;
    168	if (mei_write_is_idle(dev))
    169		pm_runtime_autosuspend(device);
    170
    171	return -EBUSY;
    172}
    173
    174static int  __maybe_unused mei_gsc_pm_runtime_suspend(struct device *device)
    175{
    176	struct mei_device *dev = dev_get_drvdata(device);
    177	struct mei_me_hw *hw;
    178	int ret;
    179
    180	if (!dev)
    181		return -ENODEV;
    182
    183	mutex_lock(&dev->device_lock);
    184
    185	if (mei_write_is_idle(dev)) {
    186		hw = to_me_hw(dev);
    187		hw->pg_state = MEI_PG_ON;
    188		ret = 0;
    189	} else {
    190		ret = -EAGAIN;
    191	}
    192
    193	mutex_unlock(&dev->device_lock);
    194
    195	return ret;
    196}
    197
    198static int __maybe_unused mei_gsc_pm_runtime_resume(struct device *device)
    199{
    200	struct mei_device *dev = dev_get_drvdata(device);
    201	struct mei_me_hw *hw;
    202	irqreturn_t irq_ret;
    203
    204	if (!dev)
    205		return -ENODEV;
    206
    207	mutex_lock(&dev->device_lock);
    208
    209	hw = to_me_hw(dev);
    210	hw->pg_state = MEI_PG_OFF;
    211
    212	mutex_unlock(&dev->device_lock);
    213
    214	irq_ret = mei_me_irq_thread_handler(1, dev);
    215	if (irq_ret != IRQ_HANDLED)
    216		dev_err(dev->dev, "thread handler fail %d\n", irq_ret);
    217
    218	return 0;
    219}
    220
    221static const struct dev_pm_ops mei_gsc_pm_ops = {
    222	SET_SYSTEM_SLEEP_PM_OPS(mei_gsc_pm_suspend,
    223				mei_gsc_pm_resume)
    224	SET_RUNTIME_PM_OPS(mei_gsc_pm_runtime_suspend,
    225			   mei_gsc_pm_runtime_resume,
    226			   mei_gsc_pm_runtime_idle)
    227};
    228
    229static const struct auxiliary_device_id mei_gsc_id_table[] = {
    230	{
    231		.name = "i915.mei-gsc",
    232		.driver_data = MEI_ME_GSC_CFG,
    233
    234	},
    235	{
    236		.name = "i915.mei-gscfi",
    237		.driver_data = MEI_ME_GSCFI_CFG,
    238	},
    239	{
    240		/* sentinel */
    241	}
    242};
    243MODULE_DEVICE_TABLE(auxiliary, mei_gsc_id_table);
    244
    245static struct auxiliary_driver mei_gsc_driver = {
    246	.probe	= mei_gsc_probe,
    247	.remove = mei_gsc_remove,
    248	.driver = {
    249		/* auxiliary_driver_register() sets .name to be the modname */
    250		.pm = &mei_gsc_pm_ops,
    251	},
    252	.id_table = mei_gsc_id_table
    253};
    254module_auxiliary_driver(mei_gsc_driver);
    255
    256MODULE_AUTHOR("Intel Corporation");
    257MODULE_ALIAS("auxiliary:i915.mei-gsc");
    258MODULE_ALIAS("auxiliary:i915.mei-gscfi");
    259MODULE_LICENSE("GPL");