0001
0002
0003
0004
0005
0006
0007
0008
0009
0010 #define pr_fmt(fmt) "PM: " fmt
0011
0012 #include <linux/pm-trace.h>
0013 #include <linux/export.h>
0014 #include <linux/rtc.h>
0015 #include <linux/suspend.h>
0016 #include <linux/init.h>
0017
0018 #include <linux/mc146818rtc.h>
0019
0020 #include "power.h"
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058
0059
0060
0061
0062
0063
0064
0065
0066
0067
0068
0069
0070
0071
0072
0073
0074
0075 #define USERHASH (16)
0076 #define FILEHASH (997)
0077 #define DEVHASH (1009)
0078
0079 #define DEVSEED (7919)
0080
0081 bool pm_trace_rtc_abused __read_mostly;
0082 EXPORT_SYMBOL_GPL(pm_trace_rtc_abused);
0083
0084 static unsigned int dev_hash_value;
0085
0086 static int set_magic_time(unsigned int user, unsigned int file, unsigned int device)
0087 {
0088 unsigned int n = user + USERHASH*(file + FILEHASH*device);
0089
0090
0091 static struct rtc_time time = {
0092 .tm_sec = 0,
0093 .tm_min = 0,
0094 .tm_hour = 0,
0095 .tm_mday = 7,
0096 .tm_mon = 5,
0097 .tm_year = 106,
0098 .tm_wday = 3,
0099 .tm_yday = 160,
0100 .tm_isdst = 1
0101 };
0102
0103 time.tm_year = (n % 100);
0104 n /= 100;
0105 time.tm_mon = (n % 12);
0106 n /= 12;
0107 time.tm_mday = (n % 28) + 1;
0108 n /= 28;
0109 time.tm_hour = (n % 24);
0110 n /= 24;
0111 time.tm_min = (n % 20) * 3;
0112 n /= 20;
0113 mc146818_set_time(&time);
0114 pm_trace_rtc_abused = true;
0115 return n ? -1 : 0;
0116 }
0117
0118 static unsigned int read_magic_time(void)
0119 {
0120 struct rtc_time time;
0121 unsigned int val;
0122
0123 if (mc146818_get_time(&time) < 0) {
0124 pr_err("Unable to read current time from RTC\n");
0125 return 0;
0126 }
0127
0128 pr_info("RTC time: %ptRt, date: %ptRd\n", &time, &time);
0129 val = time.tm_year;
0130 if (val > 100)
0131 val -= 100;
0132 val += time.tm_mon * 100;
0133 val += (time.tm_mday-1) * 100 * 12;
0134 val += time.tm_hour * 100 * 12 * 28;
0135 val += (time.tm_min / 3) * 100 * 12 * 28 * 24;
0136 return val;
0137 }
0138
0139
0140
0141
0142
0143 static unsigned int hash_string(unsigned int seed, const char *data, unsigned int mod)
0144 {
0145 unsigned char c;
0146 while ((c = *data++) != 0) {
0147 seed = (seed << 16) + (seed << 6) - seed + c;
0148 }
0149 return seed % mod;
0150 }
0151
0152 void set_trace_device(struct device *dev)
0153 {
0154 dev_hash_value = hash_string(DEVSEED, dev_name(dev), DEVHASH);
0155 }
0156 EXPORT_SYMBOL(set_trace_device);
0157
0158
0159
0160
0161
0162
0163
0164
0165
0166
0167 void generate_pm_trace(const void *tracedata, unsigned int user)
0168 {
0169 unsigned short lineno = *(unsigned short *)tracedata;
0170 const char *file = *(const char **)(tracedata + 2);
0171 unsigned int user_hash_value, file_hash_value;
0172
0173 if (!x86_platform.legacy.rtc)
0174 return;
0175
0176 user_hash_value = user % USERHASH;
0177 file_hash_value = hash_string(lineno, file, FILEHASH);
0178 set_magic_time(user_hash_value, file_hash_value, dev_hash_value);
0179 }
0180 EXPORT_SYMBOL(generate_pm_trace);
0181
0182 extern char __tracedata_start[], __tracedata_end[];
0183 static int show_file_hash(unsigned int value)
0184 {
0185 int match;
0186 char *tracedata;
0187
0188 match = 0;
0189 for (tracedata = __tracedata_start ; tracedata < __tracedata_end ;
0190 tracedata += 2 + sizeof(unsigned long)) {
0191 unsigned short lineno = *(unsigned short *)tracedata;
0192 const char *file = *(const char **)(tracedata + 2);
0193 unsigned int hash = hash_string(lineno, file, FILEHASH);
0194 if (hash != value)
0195 continue;
0196 pr_info(" hash matches %s:%u\n", file, lineno);
0197 match++;
0198 }
0199 return match;
0200 }
0201
0202 static int show_dev_hash(unsigned int value)
0203 {
0204 int match = 0;
0205 struct list_head *entry;
0206
0207 device_pm_lock();
0208 entry = dpm_list.prev;
0209 while (entry != &dpm_list) {
0210 struct device * dev = to_device(entry);
0211 unsigned int hash = hash_string(DEVSEED, dev_name(dev), DEVHASH);
0212 if (hash == value) {
0213 dev_info(dev, "hash matches\n");
0214 match++;
0215 }
0216 entry = entry->prev;
0217 }
0218 device_pm_unlock();
0219 return match;
0220 }
0221
0222 static unsigned int hash_value_early_read;
0223
0224 int show_trace_dev_match(char *buf, size_t size)
0225 {
0226 unsigned int value = hash_value_early_read / (USERHASH * FILEHASH);
0227 int ret = 0;
0228 struct list_head *entry;
0229
0230
0231
0232
0233
0234 device_pm_lock();
0235 entry = dpm_list.prev;
0236 while (size && entry != &dpm_list) {
0237 struct device *dev = to_device(entry);
0238 unsigned int hash = hash_string(DEVSEED, dev_name(dev),
0239 DEVHASH);
0240 if (hash == value) {
0241 int len = snprintf(buf, size, "%s\n",
0242 dev_driver_string(dev));
0243 if (len > size)
0244 len = size;
0245 buf += len;
0246 ret += len;
0247 size -= len;
0248 }
0249 entry = entry->prev;
0250 }
0251 device_pm_unlock();
0252 return ret;
0253 }
0254
0255 static int
0256 pm_trace_notify(struct notifier_block *nb, unsigned long mode, void *_unused)
0257 {
0258 switch (mode) {
0259 case PM_POST_HIBERNATION:
0260 case PM_POST_SUSPEND:
0261 if (pm_trace_rtc_abused) {
0262 pm_trace_rtc_abused = false;
0263 pr_warn("Possible incorrect RTC due to pm_trace, please use 'ntpdate' or 'rdate' to reset it.\n");
0264 }
0265 break;
0266 default:
0267 break;
0268 }
0269 return 0;
0270 }
0271
0272 static struct notifier_block pm_trace_nb = {
0273 .notifier_call = pm_trace_notify,
0274 };
0275
0276 static int __init early_resume_init(void)
0277 {
0278 if (!x86_platform.legacy.rtc)
0279 return 0;
0280
0281 hash_value_early_read = read_magic_time();
0282 register_pm_notifier(&pm_trace_nb);
0283 return 0;
0284 }
0285
0286 static int __init late_resume_init(void)
0287 {
0288 unsigned int val = hash_value_early_read;
0289 unsigned int user, file, dev;
0290
0291 if (!x86_platform.legacy.rtc)
0292 return 0;
0293
0294 user = val % USERHASH;
0295 val = val / USERHASH;
0296 file = val % FILEHASH;
0297 val = val / FILEHASH;
0298 dev = val ;
0299
0300 pr_info(" Magic number: %d:%d:%d\n", user, file, dev);
0301 show_file_hash(file);
0302 show_dev_hash(dev);
0303 return 0;
0304 }
0305
0306 core_initcall(early_resume_init);
0307 late_initcall(late_resume_init);