1
2
3
4
5
6
7
8
9
10#define pr_fmt(fmt) "PM: " fmt
11
12#include <linux/pm-trace.h>
13#include <linux/export.h>
14#include <linux/rtc.h>
15#include <linux/suspend.h>
16#include <linux/init.h>
17
18#include <linux/mc146818rtc.h>
19
20#include "power.h"
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#define USERHASH (16)
76#define FILEHASH (997)
77#define DEVHASH (1009)
78
79#define DEVSEED (7919)
80
81bool pm_trace_rtc_abused __read_mostly;
82EXPORT_SYMBOL_GPL(pm_trace_rtc_abused);
83
84static unsigned int dev_hash_value;
85
86static int set_magic_time(unsigned int user, unsigned int file, unsigned int device)
87{
88 unsigned int n = user + USERHASH*(file + FILEHASH*device);
89
90
91 static struct rtc_time time = {
92 .tm_sec = 0,
93 .tm_min = 0,
94 .tm_hour = 0,
95 .tm_mday = 7,
96 .tm_mon = 5,
97 .tm_year = 106,
98 .tm_wday = 3,
99 .tm_yday = 160,
100 .tm_isdst = 1
101 };
102
103 time.tm_year = (n % 100);
104 n /= 100;
105 time.tm_mon = (n % 12);
106 n /= 12;
107 time.tm_mday = (n % 28) + 1;
108 n /= 28;
109 time.tm_hour = (n % 24);
110 n /= 24;
111 time.tm_min = (n % 20) * 3;
112 n /= 20;
113 mc146818_set_time(&time);
114 pm_trace_rtc_abused = true;
115 return n ? -1 : 0;
116}
117
118static unsigned int read_magic_time(void)
119{
120 struct rtc_time time;
121 unsigned int val;
122
123 mc146818_get_time(&time);
124 pr_info("RTC time: %ptRt, date: %ptRd\n", &time, &time);
125 val = time.tm_year;
126 if (val > 100)
127 val -= 100;
128 val += time.tm_mon * 100;
129 val += (time.tm_mday-1) * 100 * 12;
130 val += time.tm_hour * 100 * 12 * 28;
131 val += (time.tm_min / 3) * 100 * 12 * 28 * 24;
132 return val;
133}
134
135
136
137
138
139static unsigned int hash_string(unsigned int seed, const char *data, unsigned int mod)
140{
141 unsigned char c;
142 while ((c = *data++) != 0) {
143 seed = (seed << 16) + (seed << 6) - seed + c;
144 }
145 return seed % mod;
146}
147
148void set_trace_device(struct device *dev)
149{
150 dev_hash_value = hash_string(DEVSEED, dev_name(dev), DEVHASH);
151}
152EXPORT_SYMBOL(set_trace_device);
153
154
155
156
157
158
159
160
161
162
163void generate_pm_trace(const void *tracedata, unsigned int user)
164{
165 unsigned short lineno = *(unsigned short *)tracedata;
166 const char *file = *(const char **)(tracedata + 2);
167 unsigned int user_hash_value, file_hash_value;
168
169 if (!x86_platform.legacy.rtc)
170 return;
171
172 user_hash_value = user % USERHASH;
173 file_hash_value = hash_string(lineno, file, FILEHASH);
174 set_magic_time(user_hash_value, file_hash_value, dev_hash_value);
175}
176EXPORT_SYMBOL(generate_pm_trace);
177
178extern char __tracedata_start[], __tracedata_end[];
179static int show_file_hash(unsigned int value)
180{
181 int match;
182 char *tracedata;
183
184 match = 0;
185 for (tracedata = __tracedata_start ; tracedata < __tracedata_end ;
186 tracedata += 2 + sizeof(unsigned long)) {
187 unsigned short lineno = *(unsigned short *)tracedata;
188 const char *file = *(const char **)(tracedata + 2);
189 unsigned int hash = hash_string(lineno, file, FILEHASH);
190 if (hash != value)
191 continue;
192 pr_info(" hash matches %s:%u\n", file, lineno);
193 match++;
194 }
195 return match;
196}
197
198static int show_dev_hash(unsigned int value)
199{
200 int match = 0;
201 struct list_head *entry;
202
203 device_pm_lock();
204 entry = dpm_list.prev;
205 while (entry != &dpm_list) {
206 struct device * dev = to_device(entry);
207 unsigned int hash = hash_string(DEVSEED, dev_name(dev), DEVHASH);
208 if (hash == value) {
209 dev_info(dev, "hash matches\n");
210 match++;
211 }
212 entry = entry->prev;
213 }
214 device_pm_unlock();
215 return match;
216}
217
218static unsigned int hash_value_early_read;
219
220int show_trace_dev_match(char *buf, size_t size)
221{
222 unsigned int value = hash_value_early_read / (USERHASH * FILEHASH);
223 int ret = 0;
224 struct list_head *entry;
225
226
227
228
229
230 device_pm_lock();
231 entry = dpm_list.prev;
232 while (size && entry != &dpm_list) {
233 struct device *dev = to_device(entry);
234 unsigned int hash = hash_string(DEVSEED, dev_name(dev),
235 DEVHASH);
236 if (hash == value) {
237 int len = snprintf(buf, size, "%s\n",
238 dev_driver_string(dev));
239 if (len > size)
240 len = size;
241 buf += len;
242 ret += len;
243 size -= len;
244 }
245 entry = entry->prev;
246 }
247 device_pm_unlock();
248 return ret;
249}
250
251static int
252pm_trace_notify(struct notifier_block *nb, unsigned long mode, void *_unused)
253{
254 switch (mode) {
255 case PM_POST_HIBERNATION:
256 case PM_POST_SUSPEND:
257 if (pm_trace_rtc_abused) {
258 pm_trace_rtc_abused = false;
259 pr_warn("Possible incorrect RTC due to pm_trace, please use 'ntpdate' or 'rdate' to reset it.\n");
260 }
261 break;
262 default:
263 break;
264 }
265 return 0;
266}
267
268static struct notifier_block pm_trace_nb = {
269 .notifier_call = pm_trace_notify,
270};
271
272static int __init early_resume_init(void)
273{
274 if (!x86_platform.legacy.rtc)
275 return 0;
276
277 hash_value_early_read = read_magic_time();
278 register_pm_notifier(&pm_trace_nb);
279 return 0;
280}
281
282static int __init late_resume_init(void)
283{
284 unsigned int val = hash_value_early_read;
285 unsigned int user, file, dev;
286
287 if (!x86_platform.legacy.rtc)
288 return 0;
289
290 user = val % USERHASH;
291 val = val / USERHASH;
292 file = val % FILEHASH;
293 val = val / FILEHASH;
294 dev = val ;
295
296 pr_info(" Magic number: %d:%d:%d\n", user, file, dev);
297 show_file_hash(file);
298 show_dev_hash(dev);
299 return 0;
300}
301
302core_initcall(early_resume_init);
303late_initcall(late_resume_init);
304