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

evged.c (4663B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Generic Event Device for ACPI.
      4 *
      5 * Copyright (c) 2016, The Linux Foundation. All rights reserved.
      6 *
      7 * Generic Event Device allows platforms to handle interrupts in ACPI
      8 * ASL statements. It follows very similar to  _EVT method approach
      9 * from GPIO events. All interrupts are listed in _CRS and the handler
     10 * is written in _EVT method. Here is an example.
     11 *
     12 * Device (GED0)
     13 * {
     14 *
     15 *     Name (_HID, "ACPI0013")
     16 *     Name (_UID, 0)
     17 *     Method (_CRS, 0x0, Serialized)
     18 *     {
     19 *		Name (RBUF, ResourceTemplate ()
     20 *		{
     21 *		Interrupt(ResourceConsumer, Edge, ActiveHigh, Shared, , , )
     22 *		{123}
     23 *		}
     24 *     })
     25 *
     26 *     Method (_EVT, 1) {
     27 *             if (Lequal(123, Arg0))
     28 *             {
     29 *             }
     30 *     }
     31 * }
     32 */
     33
     34#include <linux/err.h>
     35#include <linux/init.h>
     36#include <linux/interrupt.h>
     37#include <linux/list.h>
     38#include <linux/platform_device.h>
     39#include <linux/acpi.h>
     40
     41#define MODULE_NAME	"acpi-ged"
     42
     43struct acpi_ged_device {
     44	struct device *dev;
     45	struct list_head event_list;
     46};
     47
     48struct acpi_ged_event {
     49	struct list_head node;
     50	struct device *dev;
     51	unsigned int gsi;
     52	unsigned int irq;
     53	acpi_handle handle;
     54};
     55
     56static irqreturn_t acpi_ged_irq_handler(int irq, void *data)
     57{
     58	struct acpi_ged_event *event = data;
     59	acpi_status acpi_ret;
     60
     61	acpi_ret = acpi_execute_simple_method(event->handle, NULL, event->gsi);
     62	if (ACPI_FAILURE(acpi_ret))
     63		dev_err_once(event->dev, "IRQ method execution failed\n");
     64
     65	return IRQ_HANDLED;
     66}
     67
     68static acpi_status acpi_ged_request_interrupt(struct acpi_resource *ares,
     69					      void *context)
     70{
     71	struct acpi_ged_event *event;
     72	unsigned int irq;
     73	unsigned int gsi;
     74	unsigned int irqflags = IRQF_ONESHOT;
     75	struct acpi_ged_device *geddev = context;
     76	struct device *dev = geddev->dev;
     77	acpi_handle handle = ACPI_HANDLE(dev);
     78	acpi_handle evt_handle;
     79	struct resource r;
     80	struct acpi_resource_irq *p = &ares->data.irq;
     81	struct acpi_resource_extended_irq *pext = &ares->data.extended_irq;
     82	char ev_name[5];
     83	u8 trigger;
     84
     85	if (ares->type == ACPI_RESOURCE_TYPE_END_TAG)
     86		return AE_OK;
     87
     88	if (!acpi_dev_resource_interrupt(ares, 0, &r)) {
     89		dev_err(dev, "unable to parse IRQ resource\n");
     90		return AE_ERROR;
     91	}
     92	if (ares->type == ACPI_RESOURCE_TYPE_IRQ) {
     93		gsi = p->interrupts[0];
     94		trigger = p->triggering;
     95	} else {
     96		gsi = pext->interrupts[0];
     97		trigger = pext->triggering;
     98	}
     99
    100	irq = r.start;
    101
    102	switch (gsi) {
    103	case 0 ... 255:
    104		sprintf(ev_name, "_%c%02X",
    105			trigger == ACPI_EDGE_SENSITIVE ? 'E' : 'L', gsi);
    106
    107		if (ACPI_SUCCESS(acpi_get_handle(handle, ev_name, &evt_handle)))
    108			break;
    109		fallthrough;
    110	default:
    111		if (ACPI_SUCCESS(acpi_get_handle(handle, "_EVT", &evt_handle)))
    112			break;
    113
    114		dev_err(dev, "cannot locate _EVT method\n");
    115		return AE_ERROR;
    116	}
    117
    118	event = devm_kzalloc(dev, sizeof(*event), GFP_KERNEL);
    119	if (!event)
    120		return AE_ERROR;
    121
    122	event->gsi = gsi;
    123	event->dev = dev;
    124	event->irq = irq;
    125	event->handle = evt_handle;
    126
    127	if (r.flags & IORESOURCE_IRQ_SHAREABLE)
    128		irqflags |= IRQF_SHARED;
    129
    130	if (request_threaded_irq(irq, NULL, acpi_ged_irq_handler,
    131				 irqflags, "ACPI:Ged", event)) {
    132		dev_err(dev, "failed to setup event handler for irq %u\n", irq);
    133		return AE_ERROR;
    134	}
    135
    136	dev_dbg(dev, "GED listening GSI %u @ IRQ %u\n", gsi, irq);
    137	list_add_tail(&event->node, &geddev->event_list);
    138	return AE_OK;
    139}
    140
    141static int ged_probe(struct platform_device *pdev)
    142{
    143	struct acpi_ged_device *geddev;
    144	acpi_status acpi_ret;
    145
    146	geddev = devm_kzalloc(&pdev->dev, sizeof(*geddev), GFP_KERNEL);
    147	if (!geddev)
    148		return -ENOMEM;
    149
    150	geddev->dev = &pdev->dev;
    151	INIT_LIST_HEAD(&geddev->event_list);
    152	acpi_ret = acpi_walk_resources(ACPI_HANDLE(&pdev->dev), "_CRS",
    153				       acpi_ged_request_interrupt, geddev);
    154	if (ACPI_FAILURE(acpi_ret)) {
    155		dev_err(&pdev->dev, "unable to parse the _CRS record\n");
    156		return -EINVAL;
    157	}
    158	platform_set_drvdata(pdev, geddev);
    159
    160	return 0;
    161}
    162
    163static void ged_shutdown(struct platform_device *pdev)
    164{
    165	struct acpi_ged_device *geddev = platform_get_drvdata(pdev);
    166	struct acpi_ged_event *event, *next;
    167
    168	list_for_each_entry_safe(event, next, &geddev->event_list, node) {
    169		free_irq(event->irq, event);
    170		list_del(&event->node);
    171		dev_dbg(geddev->dev, "GED releasing GSI %u @ IRQ %u\n",
    172			 event->gsi, event->irq);
    173	}
    174}
    175
    176static int ged_remove(struct platform_device *pdev)
    177{
    178	ged_shutdown(pdev);
    179	return 0;
    180}
    181
    182static const struct acpi_device_id ged_acpi_ids[] = {
    183	{"ACPI0013"},
    184	{},
    185};
    186
    187static struct platform_driver ged_driver = {
    188	.probe = ged_probe,
    189	.remove = ged_remove,
    190	.shutdown = ged_shutdown,
    191	.driver = {
    192		.name = MODULE_NAME,
    193		.acpi_match_table = ACPI_PTR(ged_acpi_ids),
    194	},
    195};
    196builtin_platform_driver(ged_driver);