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