1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
22
23#include <linux/kernel.h>
24#include <linux/module.h>
25#include <linux/init.h>
26#include <linux/types.h>
27#include <acpi/acpi_bus.h>
28#include <acpi/acpi_drivers.h>
29
30MODULE_AUTHOR("Hu Tao <hutao@cn.fujitsu.com>");
31MODULE_DESCRIPTION("pvpanic device driver");
32MODULE_LICENSE("GPL");
33
34static int pvpanic_add(struct acpi_device *device);
35static int pvpanic_remove(struct acpi_device *device);
36
37static const struct acpi_device_id pvpanic_device_ids[] = {
38 { "QEMU0001", 0 },
39 { "", 0 },
40};
41MODULE_DEVICE_TABLE(acpi, pvpanic_device_ids);
42
43#define PVPANIC_PANICKED (1 << 0)
44
45static u16 port;
46
47static struct acpi_driver pvpanic_driver = {
48 .name = "pvpanic",
49 .class = "QEMU",
50 .ids = pvpanic_device_ids,
51 .ops = {
52 .add = pvpanic_add,
53 .remove = pvpanic_remove,
54 },
55 .owner = THIS_MODULE,
56};
57
58static void
59pvpanic_send_event(unsigned int event)
60{
61 outb(event, port);
62}
63
64static int
65pvpanic_panic_notify(struct notifier_block *nb, unsigned long code,
66 void *unused)
67{
68 pvpanic_send_event(PVPANIC_PANICKED);
69 return NOTIFY_DONE;
70}
71
72static struct notifier_block pvpanic_panic_nb = {
73 .notifier_call = pvpanic_panic_notify,
74 .priority = 1,
75};
76
77
78static acpi_status
79pvpanic_walk_resources(struct acpi_resource *res, void *context)
80{
81 switch (res->type) {
82 case ACPI_RESOURCE_TYPE_END_TAG:
83 return AE_OK;
84
85 case ACPI_RESOURCE_TYPE_IO:
86 port = res->data.io.minimum;
87 return AE_OK;
88
89 default:
90 return AE_ERROR;
91 }
92}
93
94static int pvpanic_add(struct acpi_device *device)
95{
96 acpi_status status;
97 u64 ret;
98
99 status = acpi_evaluate_integer(device->handle, "_STA", NULL,
100 &ret);
101
102 if (ACPI_FAILURE(status) || (ret & 0x0B) != 0x0B)
103 return -ENODEV;
104
105 acpi_walk_resources(device->handle, METHOD_NAME__CRS,
106 pvpanic_walk_resources, NULL);
107
108 if (!port)
109 return -ENODEV;
110
111 atomic_notifier_chain_register(&panic_notifier_list,
112 &pvpanic_panic_nb);
113
114 return 0;
115}
116
117static int pvpanic_remove(struct acpi_device *device)
118{
119
120 atomic_notifier_chain_unregister(&panic_notifier_list,
121 &pvpanic_panic_nb);
122 return 0;
123}
124
125module_acpi_driver(pvpanic_driver);
126