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