-
Notifications
You must be signed in to change notification settings - Fork 3
/
i915-jprobe.c
132 lines (111 loc) · 3.46 KB
/
i915-jprobe.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
/*
* Jprobes hack to disable the i915 lid notifier when the device
* is powered down.
*
* Copyright 2011 Red Hat, Inc
*
* Author: Alex Williamson <alex.williamson@redhat.com>
*
* This work is licensed under the terms of the GNU GPL, version 2. See
* the COPYING file in the top-level directory.
*/
#include <linux/module.h>
#include <linux/kprobes.h>
#include <linux/kallsyms.h>
#include <linux/notifier.h>
#include <linux/vga_switcheroo.h>
#include <linux/workqueue.h>
static struct notifier_block *i915_lid_nb;
static int (*i915_lid_notify)(struct notifier_block *, unsigned long , void *);
static int my_dummy_lid_notify(struct notifier_block *nb, unsigned long val,
void *unused)
{
return NOTIFY_OK;
}
static void my_i915_switcheroo_set_state(struct pci_dev *pdev,
enum vga_switcheroo_state state)
{
if (!i915_lid_nb) {
printk("Switching state, but no notifier block found\n");
goto done;
}
if (state == VGA_SWITCHEROO_ON) {
printk("Re-enabling i915 lid notifier\n");
i915_lid_nb->notifier_call = i915_lid_notify;
} else {
printk("Disabling i915 lid notifier\n");
i915_lid_nb->notifier_call = my_dummy_lid_notify;
}
done:
jprobe_return();
return; /* unreached */
}
static struct jprobe my_i915_switcheroo_set_state_jprobe = {
.entry = (kprobe_opcode_t *)my_i915_switcheroo_set_state
};
static void i915_register_jprobe(struct work_struct *work)
{
if (register_jprobe(&my_i915_switcheroo_set_state_jprobe) < 0) {
printk("Failed to register i915 jprobe\n");
my_i915_switcheroo_set_state_jprobe.kp.addr = NULL;
i915_lid_notify = NULL;
return;
}
printk("i915 jprobe registered\n");
}
static DECLARE_WORK(i915_jprobe_register_work, i915_register_jprobe);
static int my_acpi_lid_notifier_register(struct notifier_block *nb)
{
if (!i915_lid_notify) {
i915_lid_notify = (void *)kallsyms_lookup_name("intel_lid_notify");
if (!i915_lid_notify)
goto done;
my_i915_switcheroo_set_state_jprobe.kp.addr =
(kprobe_opcode_t *)kallsyms_lookup_name("i915_switcheroo_set_state");
if (!my_i915_switcheroo_set_state_jprobe.kp.addr) {
i915_lid_notify = NULL;
goto done;
}
schedule_work(&i915_jprobe_register_work);
}
if (nb->notifier_call == i915_lid_notify) {
printk("Matched i915 lid notifier block %p\n", nb);
i915_lid_nb = nb;
}
done:
jprobe_return();
return 0; /* unreached */
}
static struct jprobe my_acpi_lid_notifier_register_jprobe = {
.entry = (kprobe_opcode_t *)my_acpi_lid_notifier_register
};
int __init i915_jprobe_init(void)
{
int ret;
my_acpi_lid_notifier_register_jprobe.kp.addr =
(kprobe_opcode_t *)kallsyms_lookup_name("acpi_lid_notifier_register");
if (!my_acpi_lid_notifier_register_jprobe.kp.addr) {
printk("Couldn't find acpi_lid_notifier_register address\n");
return -1;
}
if ((ret = register_jprobe(&my_acpi_lid_notifier_register_jprobe)) < 0) {
printk("Failed register_jprobe for acpi_lid_notifier_register, %d\n",
ret);
return -1;
}
printk("Registered i915/lid jprobe\n");
return 0;
}
void __exit i915_jprobe_exit(void)
{
unregister_jprobe(&my_acpi_lid_notifier_register_jprobe);
if (my_i915_switcheroo_set_state_jprobe.kp.addr)
unregister_jprobe(&my_i915_switcheroo_set_state_jprobe);
printk("Unregistered i915/lid jprobe\n");
}
module_init(i915_jprobe_init);
module_exit(i915_jprobe_exit);
MODULE_AUTHOR("Alex Williamson <alex.williamson@redhat.com>");
MODULE_DESCRIPTION("Jprobe hack to fix i915 bugs when device is disabled");
MODULE_LICENSE("GPL v2");
MODULE_VERSION("0.1");