2  * drivers/base/power/trace.c
 
   4  * Copyright (C) 2006 Linus Torvalds
 
   6  * Trace facility for suspend/resume problems, when none of the
 
   7  * devices may be working.
 
  10 #include <linux/resume-trace.h>
 
  11 #include <linux/rtc.h>
 
  18  * Horrid, horrid, horrid.
 
  20  * It turns out that the _only_ piece of hardware that actually
 
  21  * keeps its value across a hard boot (and, more importantly, the
 
  22  * POST init sequence) is literally the realtime clock.
 
  24  * Never mind that an RTC chip has 114 bytes (and often a whole
 
  25  * other bank of an additional 128 bytes) of nice SRAM that is
 
  26  * _designed_ to keep data - the POST will clear it. So we literally
 
  27  * can just use the few bytes of actual time data, which means that
 
  28  * we're really limited.
 
  30  * It means, for example, that we can't use the seconds at all
 
  31  * (since the time between the hang and the boot might be more
 
  32  * than a minute), and we'd better not depend on the low bits of
 
  35  * There are the wday fields etc, but I wouldn't guarantee those
 
  36  * are dependable either. And if the date isn't valid, either the
 
  37  * hw or POST will do strange things.
 
  42  *  - day-of-month: 1-28
 
  46  * Giving us a total range of 0-16128000 (0xf61800), ie less
 
  47  * than 24 bits of actual data we can save across reboots.
 
  49  * And if your box can't boot in less than three minutes,
 
  52  * Now, almost 24 bits of data is pitifully small, so we need
 
  53  * to be pretty dense if we want to use it for anything nice.
 
  54  * What we do is that instead of saving off nice readable info,
 
  55  * we save off _hashes_ of information that we can hopefully
 
  56  * regenerate after the reboot.
 
  58  * In particular, this means that we might be unlucky, and hit
 
  59  * a case where we have a hash collision, and we end up not
 
  60  * being able to tell for certain exactly which case happened.
 
  61  * But that's hopefully unlikely.
 
  63  * What we do is to take the bits we can fit, and split them
 
  64  * into three parts (16*997*1009 = 16095568), and use the values
 
  66  *  - 0-15: user-settable
 
  67  *  - 0-996: file + line number
 
  71 #define FILEHASH (997)
 
  72 #define DEVHASH (1009)
 
  74 #define DEVSEED (7919)
 
  76 static unsigned int dev_hash_value;
 
  78 static int set_magic_time(unsigned int user, unsigned int file, unsigned int device)
 
  80         unsigned int n = user + USERHASH*(file + FILEHASH*device);
 
  83         static struct rtc_time time = {
 
  88                 .tm_mon = 5,    // June - counting from zero
 
  95         time.tm_year = (n % 100);
 
  97         time.tm_mon = (n % 12);
 
  99         time.tm_mday = (n % 28) + 1;
 
 101         time.tm_hour = (n % 24);
 
 103         time.tm_min = (n % 20) * 3;
 
 109 static unsigned int read_magic_time(void)
 
 111         struct rtc_time time;
 
 115         printk("Time: %2d:%02d:%02d  Date: %02d/%02d/%02d\n",
 
 116                 time.tm_hour, time.tm_min, time.tm_sec,
 
 117                 time.tm_mon + 1, time.tm_mday, time.tm_year % 100);
 
 118         val = time.tm_year;                             /* 100 years */
 
 121         val += time.tm_mon * 100;                       /* 12 months */
 
 122         val += (time.tm_mday-1) * 100 * 12;             /* 28 month-days */
 
 123         val += time.tm_hour * 100 * 12 * 28;            /* 24 hours */
 
 124         val += (time.tm_min / 3) * 100 * 12 * 28 * 24;  /* 20 3-minute intervals */
 
 129  * This is just the sdbm hash function with a user-supplied
 
 130  * seed and final size parameter.
 
 132 static unsigned int hash_string(unsigned int seed, const char *data, unsigned int mod)
 
 135         while ((c = *data++) != 0) {
 
 136                 seed = (seed << 16) + (seed << 6) - seed + c;
 
 141 void set_trace_device(struct device *dev)
 
 143         dev_hash_value = hash_string(DEVSEED, dev->bus_id, DEVHASH);
 
 145 EXPORT_SYMBOL(set_trace_device);
 
 148  * We could just take the "tracedata" index into the .tracedata
 
 149  * section instead. Generating a hash of the data gives us a
 
 150  * chance to work across kernel versions, and perhaps more
 
 151  * importantly it also gives us valid/invalid check (ie we will
 
 152  * likely not give totally bogus reports - if the hash matches,
 
 153  * it's not any guarantee, but it's a high _likelihood_ that
 
 154  * the match is valid).
 
 156 void generate_resume_trace(void *tracedata, unsigned int user)
 
 158         unsigned short lineno = *(unsigned short *)tracedata;
 
 159         const char *file = *(const char **)(tracedata + 2);
 
 160         unsigned int user_hash_value, file_hash_value;
 
 162         user_hash_value = user % USERHASH;
 
 163         file_hash_value = hash_string(lineno, file, FILEHASH);
 
 164         set_magic_time(user_hash_value, file_hash_value, dev_hash_value);
 
 166 EXPORT_SYMBOL(generate_resume_trace);
 
 168 extern char __tracedata_start, __tracedata_end;
 
 169 static int show_file_hash(unsigned int value)
 
 175         for (tracedata = &__tracedata_start ; tracedata < &__tracedata_end ;
 
 176                         tracedata += 2 + sizeof(unsigned long)) {
 
 177                 unsigned short lineno = *(unsigned short *)tracedata;
 
 178                 const char *file = *(const char **)(tracedata + 2);
 
 179                 unsigned int hash = hash_string(lineno, file, FILEHASH);
 
 182                 printk("  hash matches %s:%u\n", file, lineno);
 
 188 static int show_dev_hash(unsigned int value)
 
 191         struct list_head * entry = dpm_active.prev;
 
 193         while (entry != &dpm_active) {
 
 194                 struct device * dev = to_device(entry);
 
 195                 unsigned int hash = hash_string(DEVSEED, dev->bus_id, DEVHASH);
 
 197                         printk("  hash matches device %s\n", dev->bus_id);
 
 205 static unsigned int hash_value_early_read;
 
 207 static int early_resume_init(void)
 
 209         hash_value_early_read = read_magic_time();
 
 213 static int late_resume_init(void)
 
 215         unsigned int val = hash_value_early_read;
 
 216         unsigned int user, file, dev;
 
 218         user = val % USERHASH;
 
 219         val = val / USERHASH;
 
 220         file = val % FILEHASH;
 
 221         val = val / FILEHASH;
 
 222         dev = val /* % DEVHASH */;
 
 224         printk("  Magic number: %d:%d:%d\n", user, file, dev);
 
 225         show_file_hash(file);
 
 230 core_initcall(early_resume_init);
 
 231 late_initcall(late_resume_init);