Commit | Line | Data |
---|---|---|
6861d27c MC |
1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | /* | |
3 | * Pvpanic Device Support | |
4 | * | |
5 | * Copyright (C) 2013 Fujitsu. | |
6 | * Copyright (C) 2018 ZTE. | |
7 | * Copyright (C) 2021 Oracle. | |
8 | */ | |
9 | ||
10 | #include <linux/io.h> | |
11 | #include <linux/kernel.h> | |
12 | #include <linux/kexec.h> | |
13 | #include <linux/mod_devicetable.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/platform_device.h> | |
f39650de | 16 | #include <linux/panic_notifier.h> |
6861d27c | 17 | #include <linux/types.h> |
b3c0f877 MC |
18 | #include <linux/cdev.h> |
19 | #include <linux/list.h> | |
6861d27c MC |
20 | |
21 | #include <uapi/misc/pvpanic.h> | |
22 | ||
23 | #include "pvpanic.h" | |
24 | ||
25 | MODULE_AUTHOR("Mihai Carabas <mihai.carabas@oracle.com>"); | |
84b0f12a | 26 | MODULE_DESCRIPTION("pvpanic device driver"); |
6861d27c MC |
27 | MODULE_LICENSE("GPL"); |
28 | ||
391e2415 Y |
29 | static struct list_head pvpanic_list; |
30 | static spinlock_t pvpanic_lock; | |
6861d27c MC |
31 | |
32 | static void | |
33 | pvpanic_send_event(unsigned int event) | |
34 | { | |
b3c0f877 MC |
35 | struct pvpanic_instance *pi_cur; |
36 | ||
e918c102 GP |
37 | if (!spin_trylock(&pvpanic_lock)) |
38 | return; | |
39 | ||
b3c0f877 MC |
40 | list_for_each_entry(pi_cur, &pvpanic_list, list) { |
41 | if (event & pi_cur->capability & pi_cur->events) | |
42 | iowrite8(event, pi_cur->base); | |
43 | } | |
44 | spin_unlock(&pvpanic_lock); | |
6861d27c MC |
45 | } |
46 | ||
47 | static int | |
84b0f12a | 48 | pvpanic_panic_notify(struct notifier_block *nb, unsigned long code, void *unused) |
6861d27c MC |
49 | { |
50 | unsigned int event = PVPANIC_PANICKED; | |
51 | ||
52 | if (kexec_crash_loaded()) | |
53 | event = PVPANIC_CRASH_LOADED; | |
54 | ||
55 | pvpanic_send_event(event); | |
56 | ||
57 | return NOTIFY_DONE; | |
58 | } | |
59 | ||
e918c102 GP |
60 | /* |
61 | * Call our notifier very early on panic, deferring the | |
62 | * action taken to the hypervisor. | |
63 | */ | |
6861d27c MC |
64 | static struct notifier_block pvpanic_panic_nb = { |
65 | .notifier_call = pvpanic_panic_notify, | |
e918c102 | 66 | .priority = INT_MAX, |
6861d27c MC |
67 | }; |
68 | ||
394febc9 | 69 | static void pvpanic_remove(void *param) |
6861d27c | 70 | { |
b3c0f877 | 71 | struct pvpanic_instance *pi_cur, *pi_next; |
394febc9 | 72 | struct pvpanic_instance *pi = param; |
b3c0f877 MC |
73 | |
74 | spin_lock(&pvpanic_lock); | |
75 | list_for_each_entry_safe(pi_cur, pi_next, &pvpanic_list, list) { | |
76 | if (pi_cur == pi) { | |
77 | list_del(&pi_cur->list); | |
78 | break; | |
79 | } | |
80 | } | |
81 | spin_unlock(&pvpanic_lock); | |
6861d27c | 82 | } |
394febc9 CJ |
83 | |
84 | int devm_pvpanic_probe(struct device *dev, struct pvpanic_instance *pi) | |
85 | { | |
86 | if (!pi || !pi->base) | |
87 | return -EINVAL; | |
88 | ||
89 | spin_lock(&pvpanic_lock); | |
90 | list_add(&pi->list, &pvpanic_list); | |
91 | spin_unlock(&pvpanic_lock); | |
92 | ||
a99009bc MC |
93 | dev_set_drvdata(dev, pi); |
94 | ||
394febc9 CJ |
95 | return devm_add_action_or_reset(dev, pvpanic_remove, pi); |
96 | } | |
97 | EXPORT_SYMBOL_GPL(devm_pvpanic_probe); | |
6861d27c | 98 | |
b3c0f877 | 99 | static int pvpanic_init(void) |
6861d27c | 100 | { |
b3c0f877 MC |
101 | INIT_LIST_HEAD(&pvpanic_list); |
102 | spin_lock_init(&pvpanic_lock); | |
103 | ||
84b0f12a | 104 | atomic_notifier_chain_register(&panic_notifier_list, &pvpanic_panic_nb); |
b3c0f877 MC |
105 | |
106 | return 0; | |
6861d27c | 107 | } |
33a43041 | 108 | module_init(pvpanic_init); |
b3c0f877 MC |
109 | |
110 | static void pvpanic_exit(void) | |
111 | { | |
84b0f12a | 112 | atomic_notifier_chain_unregister(&panic_notifier_list, &pvpanic_panic_nb); |
b3c0f877 MC |
113 | |
114 | } | |
b3c0f877 | 115 | module_exit(pvpanic_exit); |