xref: /OK3568_Linux_fs/kernel/drivers/base/power/trace.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * drivers/base/power/trace.c
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (C) 2006 Linus Torvalds
6*4882a593Smuzhiyun  *
7*4882a593Smuzhiyun  * Trace facility for suspend/resume problems, when none of the
8*4882a593Smuzhiyun  * devices may be working.
9*4882a593Smuzhiyun  */
10*4882a593Smuzhiyun #define pr_fmt(fmt) "PM: " fmt
11*4882a593Smuzhiyun 
12*4882a593Smuzhiyun #include <linux/pm-trace.h>
13*4882a593Smuzhiyun #include <linux/export.h>
14*4882a593Smuzhiyun #include <linux/rtc.h>
15*4882a593Smuzhiyun #include <linux/suspend.h>
16*4882a593Smuzhiyun #include <linux/init.h>
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #include <linux/mc146818rtc.h>
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #include "power.h"
21*4882a593Smuzhiyun 
22*4882a593Smuzhiyun /*
23*4882a593Smuzhiyun  * Horrid, horrid, horrid.
24*4882a593Smuzhiyun  *
25*4882a593Smuzhiyun  * It turns out that the _only_ piece of hardware that actually
26*4882a593Smuzhiyun  * keeps its value across a hard boot (and, more importantly, the
27*4882a593Smuzhiyun  * POST init sequence) is literally the realtime clock.
28*4882a593Smuzhiyun  *
29*4882a593Smuzhiyun  * Never mind that an RTC chip has 114 bytes (and often a whole
30*4882a593Smuzhiyun  * other bank of an additional 128 bytes) of nice SRAM that is
31*4882a593Smuzhiyun  * _designed_ to keep data - the POST will clear it. So we literally
32*4882a593Smuzhiyun  * can just use the few bytes of actual time data, which means that
33*4882a593Smuzhiyun  * we're really limited.
34*4882a593Smuzhiyun  *
35*4882a593Smuzhiyun  * It means, for example, that we can't use the seconds at all
36*4882a593Smuzhiyun  * (since the time between the hang and the boot might be more
37*4882a593Smuzhiyun  * than a minute), and we'd better not depend on the low bits of
38*4882a593Smuzhiyun  * the minutes either.
39*4882a593Smuzhiyun  *
40*4882a593Smuzhiyun  * There are the wday fields etc, but I wouldn't guarantee those
41*4882a593Smuzhiyun  * are dependable either. And if the date isn't valid, either the
42*4882a593Smuzhiyun  * hw or POST will do strange things.
43*4882a593Smuzhiyun  *
44*4882a593Smuzhiyun  * So we're left with:
45*4882a593Smuzhiyun  *  - year: 0-99
46*4882a593Smuzhiyun  *  - month: 0-11
47*4882a593Smuzhiyun  *  - day-of-month: 1-28
48*4882a593Smuzhiyun  *  - hour: 0-23
49*4882a593Smuzhiyun  *  - min: (0-30)*2
50*4882a593Smuzhiyun  *
51*4882a593Smuzhiyun  * Giving us a total range of 0-16128000 (0xf61800), ie less
52*4882a593Smuzhiyun  * than 24 bits of actual data we can save across reboots.
53*4882a593Smuzhiyun  *
54*4882a593Smuzhiyun  * And if your box can't boot in less than three minutes,
55*4882a593Smuzhiyun  * you're screwed.
56*4882a593Smuzhiyun  *
57*4882a593Smuzhiyun  * Now, almost 24 bits of data is pitifully small, so we need
58*4882a593Smuzhiyun  * to be pretty dense if we want to use it for anything nice.
59*4882a593Smuzhiyun  * What we do is that instead of saving off nice readable info,
60*4882a593Smuzhiyun  * we save off _hashes_ of information that we can hopefully
61*4882a593Smuzhiyun  * regenerate after the reboot.
62*4882a593Smuzhiyun  *
63*4882a593Smuzhiyun  * In particular, this means that we might be unlucky, and hit
64*4882a593Smuzhiyun  * a case where we have a hash collision, and we end up not
65*4882a593Smuzhiyun  * being able to tell for certain exactly which case happened.
66*4882a593Smuzhiyun  * But that's hopefully unlikely.
67*4882a593Smuzhiyun  *
68*4882a593Smuzhiyun  * What we do is to take the bits we can fit, and split them
69*4882a593Smuzhiyun  * into three parts (16*997*1009 = 16095568), and use the values
70*4882a593Smuzhiyun  * for:
71*4882a593Smuzhiyun  *  - 0-15: user-settable
72*4882a593Smuzhiyun  *  - 0-996: file + line number
73*4882a593Smuzhiyun  *  - 0-1008: device
74*4882a593Smuzhiyun  */
75*4882a593Smuzhiyun #define USERHASH (16)
76*4882a593Smuzhiyun #define FILEHASH (997)
77*4882a593Smuzhiyun #define DEVHASH (1009)
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun #define DEVSEED (7919)
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun bool pm_trace_rtc_abused __read_mostly;
82*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(pm_trace_rtc_abused);
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun static unsigned int dev_hash_value;
85*4882a593Smuzhiyun 
set_magic_time(unsigned int user,unsigned int file,unsigned int device)86*4882a593Smuzhiyun static int set_magic_time(unsigned int user, unsigned int file, unsigned int device)
87*4882a593Smuzhiyun {
88*4882a593Smuzhiyun 	unsigned int n = user + USERHASH*(file + FILEHASH*device);
89*4882a593Smuzhiyun 
90*4882a593Smuzhiyun 	// June 7th, 2006
91*4882a593Smuzhiyun 	static struct rtc_time time = {
92*4882a593Smuzhiyun 		.tm_sec = 0,
93*4882a593Smuzhiyun 		.tm_min = 0,
94*4882a593Smuzhiyun 		.tm_hour = 0,
95*4882a593Smuzhiyun 		.tm_mday = 7,
96*4882a593Smuzhiyun 		.tm_mon = 5,	// June - counting from zero
97*4882a593Smuzhiyun 		.tm_year = 106,
98*4882a593Smuzhiyun 		.tm_wday = 3,
99*4882a593Smuzhiyun 		.tm_yday = 160,
100*4882a593Smuzhiyun 		.tm_isdst = 1
101*4882a593Smuzhiyun 	};
102*4882a593Smuzhiyun 
103*4882a593Smuzhiyun 	time.tm_year = (n % 100);
104*4882a593Smuzhiyun 	n /= 100;
105*4882a593Smuzhiyun 	time.tm_mon = (n % 12);
106*4882a593Smuzhiyun 	n /= 12;
107*4882a593Smuzhiyun 	time.tm_mday = (n % 28) + 1;
108*4882a593Smuzhiyun 	n /= 28;
109*4882a593Smuzhiyun 	time.tm_hour = (n % 24);
110*4882a593Smuzhiyun 	n /= 24;
111*4882a593Smuzhiyun 	time.tm_min = (n % 20) * 3;
112*4882a593Smuzhiyun 	n /= 20;
113*4882a593Smuzhiyun 	mc146818_set_time(&time);
114*4882a593Smuzhiyun 	pm_trace_rtc_abused = true;
115*4882a593Smuzhiyun 	return n ? -1 : 0;
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun 
read_magic_time(void)118*4882a593Smuzhiyun static unsigned int read_magic_time(void)
119*4882a593Smuzhiyun {
120*4882a593Smuzhiyun 	struct rtc_time time;
121*4882a593Smuzhiyun 	unsigned int val;
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	if (mc146818_get_time(&time) < 0) {
124*4882a593Smuzhiyun 		pr_err("Unable to read current time from RTC\n");
125*4882a593Smuzhiyun 		return 0;
126*4882a593Smuzhiyun 	}
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 	pr_info("RTC time: %ptRt, date: %ptRd\n", &time, &time);
129*4882a593Smuzhiyun 	val = time.tm_year;				/* 100 years */
130*4882a593Smuzhiyun 	if (val > 100)
131*4882a593Smuzhiyun 		val -= 100;
132*4882a593Smuzhiyun 	val += time.tm_mon * 100;			/* 12 months */
133*4882a593Smuzhiyun 	val += (time.tm_mday-1) * 100 * 12;		/* 28 month-days */
134*4882a593Smuzhiyun 	val += time.tm_hour * 100 * 12 * 28;		/* 24 hours */
135*4882a593Smuzhiyun 	val += (time.tm_min / 3) * 100 * 12 * 28 * 24;	/* 20 3-minute intervals */
136*4882a593Smuzhiyun 	return val;
137*4882a593Smuzhiyun }
138*4882a593Smuzhiyun 
139*4882a593Smuzhiyun /*
140*4882a593Smuzhiyun  * This is just the sdbm hash function with a user-supplied
141*4882a593Smuzhiyun  * seed and final size parameter.
142*4882a593Smuzhiyun  */
hash_string(unsigned int seed,const char * data,unsigned int mod)143*4882a593Smuzhiyun static unsigned int hash_string(unsigned int seed, const char *data, unsigned int mod)
144*4882a593Smuzhiyun {
145*4882a593Smuzhiyun 	unsigned char c;
146*4882a593Smuzhiyun 	while ((c = *data++) != 0) {
147*4882a593Smuzhiyun 		seed = (seed << 16) + (seed << 6) - seed + c;
148*4882a593Smuzhiyun 	}
149*4882a593Smuzhiyun 	return seed % mod;
150*4882a593Smuzhiyun }
151*4882a593Smuzhiyun 
set_trace_device(struct device * dev)152*4882a593Smuzhiyun void set_trace_device(struct device *dev)
153*4882a593Smuzhiyun {
154*4882a593Smuzhiyun 	dev_hash_value = hash_string(DEVSEED, dev_name(dev), DEVHASH);
155*4882a593Smuzhiyun }
156*4882a593Smuzhiyun EXPORT_SYMBOL(set_trace_device);
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun /*
159*4882a593Smuzhiyun  * We could just take the "tracedata" index into the .tracedata
160*4882a593Smuzhiyun  * section instead. Generating a hash of the data gives us a
161*4882a593Smuzhiyun  * chance to work across kernel versions, and perhaps more
162*4882a593Smuzhiyun  * importantly it also gives us valid/invalid check (ie we will
163*4882a593Smuzhiyun  * likely not give totally bogus reports - if the hash matches,
164*4882a593Smuzhiyun  * it's not any guarantee, but it's a high _likelihood_ that
165*4882a593Smuzhiyun  * the match is valid).
166*4882a593Smuzhiyun  */
generate_pm_trace(const void * tracedata,unsigned int user)167*4882a593Smuzhiyun void generate_pm_trace(const void *tracedata, unsigned int user)
168*4882a593Smuzhiyun {
169*4882a593Smuzhiyun 	unsigned short lineno = *(unsigned short *)tracedata;
170*4882a593Smuzhiyun 	const char *file = *(const char **)(tracedata + 2);
171*4882a593Smuzhiyun 	unsigned int user_hash_value, file_hash_value;
172*4882a593Smuzhiyun 
173*4882a593Smuzhiyun 	if (!x86_platform.legacy.rtc)
174*4882a593Smuzhiyun 		return;
175*4882a593Smuzhiyun 
176*4882a593Smuzhiyun 	user_hash_value = user % USERHASH;
177*4882a593Smuzhiyun 	file_hash_value = hash_string(lineno, file, FILEHASH);
178*4882a593Smuzhiyun 	set_magic_time(user_hash_value, file_hash_value, dev_hash_value);
179*4882a593Smuzhiyun }
180*4882a593Smuzhiyun EXPORT_SYMBOL(generate_pm_trace);
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun extern char __tracedata_start[], __tracedata_end[];
show_file_hash(unsigned int value)183*4882a593Smuzhiyun static int show_file_hash(unsigned int value)
184*4882a593Smuzhiyun {
185*4882a593Smuzhiyun 	int match;
186*4882a593Smuzhiyun 	char *tracedata;
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun 	match = 0;
189*4882a593Smuzhiyun 	for (tracedata = __tracedata_start ; tracedata < __tracedata_end ;
190*4882a593Smuzhiyun 			tracedata += 2 + sizeof(unsigned long)) {
191*4882a593Smuzhiyun 		unsigned short lineno = *(unsigned short *)tracedata;
192*4882a593Smuzhiyun 		const char *file = *(const char **)(tracedata + 2);
193*4882a593Smuzhiyun 		unsigned int hash = hash_string(lineno, file, FILEHASH);
194*4882a593Smuzhiyun 		if (hash != value)
195*4882a593Smuzhiyun 			continue;
196*4882a593Smuzhiyun 		pr_info("  hash matches %s:%u\n", file, lineno);
197*4882a593Smuzhiyun 		match++;
198*4882a593Smuzhiyun 	}
199*4882a593Smuzhiyun 	return match;
200*4882a593Smuzhiyun }
201*4882a593Smuzhiyun 
show_dev_hash(unsigned int value)202*4882a593Smuzhiyun static int show_dev_hash(unsigned int value)
203*4882a593Smuzhiyun {
204*4882a593Smuzhiyun 	int match = 0;
205*4882a593Smuzhiyun 	struct list_head *entry;
206*4882a593Smuzhiyun 
207*4882a593Smuzhiyun 	device_pm_lock();
208*4882a593Smuzhiyun 	entry = dpm_list.prev;
209*4882a593Smuzhiyun 	while (entry != &dpm_list) {
210*4882a593Smuzhiyun 		struct device * dev = to_device(entry);
211*4882a593Smuzhiyun 		unsigned int hash = hash_string(DEVSEED, dev_name(dev), DEVHASH);
212*4882a593Smuzhiyun 		if (hash == value) {
213*4882a593Smuzhiyun 			dev_info(dev, "hash matches\n");
214*4882a593Smuzhiyun 			match++;
215*4882a593Smuzhiyun 		}
216*4882a593Smuzhiyun 		entry = entry->prev;
217*4882a593Smuzhiyun 	}
218*4882a593Smuzhiyun 	device_pm_unlock();
219*4882a593Smuzhiyun 	return match;
220*4882a593Smuzhiyun }
221*4882a593Smuzhiyun 
222*4882a593Smuzhiyun static unsigned int hash_value_early_read;
223*4882a593Smuzhiyun 
show_trace_dev_match(char * buf,size_t size)224*4882a593Smuzhiyun int show_trace_dev_match(char *buf, size_t size)
225*4882a593Smuzhiyun {
226*4882a593Smuzhiyun 	unsigned int value = hash_value_early_read / (USERHASH * FILEHASH);
227*4882a593Smuzhiyun 	int ret = 0;
228*4882a593Smuzhiyun 	struct list_head *entry;
229*4882a593Smuzhiyun 
230*4882a593Smuzhiyun 	/*
231*4882a593Smuzhiyun 	 * It's possible that multiple devices will match the hash and we can't
232*4882a593Smuzhiyun 	 * tell which is the culprit, so it's best to output them all.
233*4882a593Smuzhiyun 	 */
234*4882a593Smuzhiyun 	device_pm_lock();
235*4882a593Smuzhiyun 	entry = dpm_list.prev;
236*4882a593Smuzhiyun 	while (size && entry != &dpm_list) {
237*4882a593Smuzhiyun 		struct device *dev = to_device(entry);
238*4882a593Smuzhiyun 		unsigned int hash = hash_string(DEVSEED, dev_name(dev),
239*4882a593Smuzhiyun 						DEVHASH);
240*4882a593Smuzhiyun 		if (hash == value) {
241*4882a593Smuzhiyun 			int len = snprintf(buf, size, "%s\n",
242*4882a593Smuzhiyun 					    dev_driver_string(dev));
243*4882a593Smuzhiyun 			if (len > size)
244*4882a593Smuzhiyun 				len = size;
245*4882a593Smuzhiyun 			buf += len;
246*4882a593Smuzhiyun 			ret += len;
247*4882a593Smuzhiyun 			size -= len;
248*4882a593Smuzhiyun 		}
249*4882a593Smuzhiyun 		entry = entry->prev;
250*4882a593Smuzhiyun 	}
251*4882a593Smuzhiyun 	device_pm_unlock();
252*4882a593Smuzhiyun 	return ret;
253*4882a593Smuzhiyun }
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun static int
pm_trace_notify(struct notifier_block * nb,unsigned long mode,void * _unused)256*4882a593Smuzhiyun pm_trace_notify(struct notifier_block *nb, unsigned long mode, void *_unused)
257*4882a593Smuzhiyun {
258*4882a593Smuzhiyun 	switch (mode) {
259*4882a593Smuzhiyun 	case PM_POST_HIBERNATION:
260*4882a593Smuzhiyun 	case PM_POST_SUSPEND:
261*4882a593Smuzhiyun 		if (pm_trace_rtc_abused) {
262*4882a593Smuzhiyun 			pm_trace_rtc_abused = false;
263*4882a593Smuzhiyun 			pr_warn("Possible incorrect RTC due to pm_trace, please use 'ntpdate' or 'rdate' to reset it.\n");
264*4882a593Smuzhiyun 		}
265*4882a593Smuzhiyun 		break;
266*4882a593Smuzhiyun 	default:
267*4882a593Smuzhiyun 		break;
268*4882a593Smuzhiyun 	}
269*4882a593Smuzhiyun 	return 0;
270*4882a593Smuzhiyun }
271*4882a593Smuzhiyun 
272*4882a593Smuzhiyun static struct notifier_block pm_trace_nb = {
273*4882a593Smuzhiyun 	.notifier_call = pm_trace_notify,
274*4882a593Smuzhiyun };
275*4882a593Smuzhiyun 
early_resume_init(void)276*4882a593Smuzhiyun static int __init early_resume_init(void)
277*4882a593Smuzhiyun {
278*4882a593Smuzhiyun 	if (!x86_platform.legacy.rtc)
279*4882a593Smuzhiyun 		return 0;
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	hash_value_early_read = read_magic_time();
282*4882a593Smuzhiyun 	register_pm_notifier(&pm_trace_nb);
283*4882a593Smuzhiyun 	return 0;
284*4882a593Smuzhiyun }
285*4882a593Smuzhiyun 
late_resume_init(void)286*4882a593Smuzhiyun static int __init late_resume_init(void)
287*4882a593Smuzhiyun {
288*4882a593Smuzhiyun 	unsigned int val = hash_value_early_read;
289*4882a593Smuzhiyun 	unsigned int user, file, dev;
290*4882a593Smuzhiyun 
291*4882a593Smuzhiyun 	if (!x86_platform.legacy.rtc)
292*4882a593Smuzhiyun 		return 0;
293*4882a593Smuzhiyun 
294*4882a593Smuzhiyun 	user = val % USERHASH;
295*4882a593Smuzhiyun 	val = val / USERHASH;
296*4882a593Smuzhiyun 	file = val % FILEHASH;
297*4882a593Smuzhiyun 	val = val / FILEHASH;
298*4882a593Smuzhiyun 	dev = val /* % DEVHASH */;
299*4882a593Smuzhiyun 
300*4882a593Smuzhiyun 	pr_info("  Magic number: %d:%d:%d\n", user, file, dev);
301*4882a593Smuzhiyun 	show_file_hash(file);
302*4882a593Smuzhiyun 	show_dev_hash(dev);
303*4882a593Smuzhiyun 	return 0;
304*4882a593Smuzhiyun }
305*4882a593Smuzhiyun 
306*4882a593Smuzhiyun core_initcall(early_resume_init);
307*4882a593Smuzhiyun late_initcall(late_resume_init);
308